1 /* SCCS Id: @(#)save.c 3.4 2003/11/14 */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
4
5 #include "hack.h"
6 #include "lev.h"
7 #include "quest.h"
8
9 #ifndef NO_SIGNAL
10 #include <signal.h>
11 #endif
12 #if !defined(LSC) && !defined(O_WRONLY) && !defined(AZTEC_C)
13 #include <fcntl.h>
14 #endif
15
16 #ifdef MFLOPPY
17 long bytes_counted;
18 static int count_only;
19 #endif
20
21 #ifdef MICRO
22 int dotcnt, dotrow; /* also used in restore */
23 #endif
24
25 #ifdef ZEROCOMP
26 STATIC_DCL void FDECL(bputc, (int));
27 #endif
28 STATIC_DCL void FDECL(savelevchn, (int,int));
29 STATIC_DCL void FDECL(savedamage, (int,int));
30 STATIC_DCL void FDECL(saveobjchn, (int,struct obj *,int));
31 STATIC_DCL void FDECL(savemonchn, (int,struct monst *,int));
32 STATIC_DCL void FDECL(savetrapchn, (int,struct trap *,int));
33 STATIC_DCL void FDECL(savegamestate, (int,int));
34 void FDECL(save_mongen_override, (int,struct mon_gen_override *, int));
35 void FDECL(save_lvl_sounds, (int,struct lvl_sounds *, int));
36 #ifdef MFLOPPY
37 STATIC_DCL void FDECL(savelev0, (int,XCHAR_P,int));
38 STATIC_DCL boolean NDECL(swapout_oldest);
39 STATIC_DCL void FDECL(copyfile, (char *,char *));
40 #endif /* MFLOPPY */
41 #ifdef GCC_WARN
42 static long nulls[10];
43 #else
44 #define nulls nul
45 #endif
46
47 #if defined(UNIX) || defined(VMS) || defined(__EMX__) || defined(WIN32)
48 #define HUP if (!program_state.done_hup)
49 #else
50 #define HUP
51 #endif
52
53 #ifdef MENU_COLOR
54 extern struct menucoloring *menu_colorings;
55 #endif
56
57 #if defined(STATUS_COLORS) && defined(TEXTCOLOR)
58 extern const struct percent_color_option *hp_colors;
59 extern const struct percent_color_option *pw_colors;
60 extern const struct text_color_option *text_colors;
61 #endif
62
63 #ifdef USE_MERSENNE_TWISTER
64 extern gsl_rng *rng_state;
65 #endif
66
67 /* need to preserve these during save to avoid accessing freed memory */
68 static unsigned ustuck_id = 0, usteed_id = 0;
69
70 int
dosave()71 dosave()
72 {
73 clear_nhwindow(WIN_MESSAGE);
74 if(yn("Really save?") == 'n') {
75 clear_nhwindow(WIN_MESSAGE);
76 if(multi > 0) nomul(0, 0);
77 } else {
78 clear_nhwindow(WIN_MESSAGE);
79 pline("Saving...");
80 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
81 program_state.done_hup = 0;
82 #endif
83 if(dosave0()) {
84 #ifdef LIVELOGFILE
85 livelog_game_action("saved");
86 #endif
87 program_state.something_worth_saving = 0;
88 u.uhp = -1; /* universal game's over indicator */
89 /* make sure they see the Saving message */
90 display_nhwindow(WIN_MESSAGE, TRUE);
91 exit_nhwindows("Be seeing you...");
92 terminate(EXIT_SUCCESS);
93 } else (void)doredraw();
94 }
95 return 0;
96 }
97
98
99 #if defined(UNIX) || defined(VMS) || defined (__EMX__) || defined(WIN32)
100 /*ARGSUSED*/
101 void
hangup(sig_unused)102 hangup(sig_unused) /* called as signal() handler, so sent at least one arg */
103 int sig_unused;
104 {
105 # ifdef NOSAVEONHANGUP
106 (void) signal(SIGINT, SIG_IGN);
107 clearlocks();
108 # ifndef VMS
109 terminate(EXIT_FAILURE);
110 # endif
111 # else /* SAVEONHANGUP */
112 if (!program_state.done_hup++) {
113 if (program_state.something_worth_saving)
114 (void) dosave0();
115 # ifdef VMS
116 /* don't call exit when already within an exit handler;
117 that would cancel any other pending user-mode handlers */
118 if (!program_state.exiting)
119 # endif
120 {
121 clearlocks();
122 terminate(EXIT_FAILURE);
123 }
124 }
125 # endif
126 return;
127 }
128 #endif
129
130 /* returns 1 if save successful */
131 int
dosave0()132 dosave0()
133 {
134 const char *fq_save;
135 register int fd, ofd;
136 xchar ltmp;
137 d_level uz_save;
138 char whynot[BUFSZ];
139
140 #ifdef WHEREIS_FILE
141 delete_whereis();
142 #endif
143
144 if (!SAVEF[0])
145 return 0;
146 fq_save = fqname(SAVEF, SAVEPREFIX, 1); /* level files take 0 */
147
148 #if defined(UNIX) || defined(VMS)
149 (void) signal(SIGHUP, SIG_IGN);
150 #endif
151 #ifndef NO_SIGNAL
152 (void) signal(SIGINT, SIG_IGN);
153 #endif
154
155 #if defined(MICRO) && defined(MFLOPPY)
156 if (!saveDiskPrompt(0)) return 0;
157 #endif
158
159 HUP if (iflags.window_inited) {
160 uncompress(fq_save);
161 fd = open_savefile();
162 if (fd > 0) {
163 (void) close(fd);
164 clear_nhwindow(WIN_MESSAGE);
165 There("seems to be an old save file.");
166 if (yn("Overwrite the old file?") == 'n') {
167 compress(fq_save);
168 return 0;
169 }
170 }
171 }
172
173 HUP mark_synch(); /* flush any buffered screen output */
174
175 fd = create_savefile();
176 if(fd < 0) {
177 HUP pline("Cannot open save file.");
178 (void) delete_savefile(); /* ab@unido */
179 return(0);
180 }
181
182 vision_recalc(2); /* shut down vision to prevent problems
183 in the event of an impossible() call */
184
185 /* undo date-dependent luck adjustments made at startup time */
186 if(flags.moonphase == FULL_MOON) /* ut-sally!fletcher */
187 change_luck(-1); /* and unido!ab */
188 if(flags.friday13)
189 change_luck(1);
190 if(iflags.window_inited)
191 HUP clear_nhwindow(WIN_MESSAGE);
192
193 #ifdef MICRO
194 dotcnt = 0;
195 dotrow = 2;
196 curs(WIN_MAP, 1, 1);
197 if (strncmpi("X11", windowprocs.name, 3))
198 putstr(WIN_MAP, 0, "Saving:");
199 #endif
200 #ifdef MFLOPPY
201 /* make sure there is enough disk space */
202 if (iflags.checkspace) {
203 long fds, needed;
204
205 savelev(fd, ledger_no(&u.uz), COUNT_SAVE);
206 savegamestate(fd, COUNT_SAVE);
207 needed = bytes_counted;
208
209 for (ltmp = 1; ltmp <= maxledgerno(); ltmp++)
210 if (ltmp != ledger_no(&u.uz) && level_info[ltmp].where)
211 needed += level_info[ltmp].size + (sizeof ltmp);
212 fds = freediskspace(fq_save);
213 if (needed > fds) {
214 HUP {
215 There("is insufficient space on SAVE disk.");
216 pline("Require %ld bytes but only have %ld.", needed, fds);
217 }
218 flushout();
219 (void) close(fd);
220 (void) delete_savefile();
221 return 0;
222 }
223
224 co_false();
225 }
226 #endif /* MFLOPPY */
227
228 store_version(fd);
229 #ifdef STORE_PLNAME_IN_FILE
230 bwrite(fd, (genericptr_t) plname, PL_NSIZ);
231 #endif
232 ustuck_id = (u.ustuck ? u.ustuck->m_id : 0);
233 #ifdef STEED
234 usteed_id = (u.usteed ? u.usteed->m_id : 0);
235 #endif
236
237 savelev(fd, ledger_no(&u.uz), WRITE_SAVE | FREE_SAVE);
238 savegamestate(fd, WRITE_SAVE | FREE_SAVE);
239
240 /* While copying level files around, zero out u.uz to keep
241 * parts of the restore code from completely initializing all
242 * in-core data structures, since all we're doing is copying.
243 * This also avoids at least one nasty core dump.
244 */
245 uz_save = u.uz;
246 u.uz.dnum = u.uz.dlevel = 0;
247 /* these pointers are no longer valid, and at least u.usteed
248 * may mislead place_monster() on other levels
249 */
250 u.ustuck = (struct monst *)0;
251 #ifdef STEED
252 u.usteed = (struct monst *)0;
253 #endif
254
255 for(ltmp = (xchar)1; ltmp <= maxledgerno(); ltmp++) {
256 if (ltmp == ledger_no(&uz_save)) continue;
257 if (!(level_info[ltmp].flags & LFILE_EXISTS)) continue;
258 #ifdef MICRO
259 curs(WIN_MAP, 1 + dotcnt++, dotrow);
260 if (dotcnt >= (COLNO - 1)) {
261 dotrow++;
262 dotcnt = 0;
263 }
264 if (strncmpi("X11", windowprocs.name, 3)){
265 putstr(WIN_MAP, 0, ".");
266 }
267 mark_synch();
268 #endif
269 ofd = open_levelfile(ltmp, whynot);
270 if (ofd < 0) {
271 HUP pline("%s", whynot);
272 (void) close(fd);
273 (void) delete_savefile();
274 HUP killer = whynot;
275 HUP done(TRICKED);
276 return(0);
277 }
278 minit(); /* ZEROCOMP */
279 getlev(ofd, hackpid, ltmp, FALSE);
280 (void) close(ofd);
281 bwrite(fd, (genericptr_t) <mp, sizeof ltmp); /* level number*/
282 savelev(fd, ltmp, WRITE_SAVE | FREE_SAVE); /* actual level*/
283 delete_levelfile(ltmp);
284 }
285 bclose(fd);
286
287 u.uz = uz_save;
288
289 /* get rid of current level --jgm */
290 delete_levelfile(ledger_no(&u.uz));
291 delete_levelfile(0);
292 compress(fq_save);
293 return(1);
294 }
295
296 STATIC_OVL void
savegamestate(fd,mode)297 savegamestate(fd, mode)
298 register int fd, mode;
299 {
300 int uid;
301 #if defined(RECORD_REALTIME) || defined(REALTIME_ON_BOTL)
302 time_t realtime;
303 #endif
304
305
306 #ifdef MFLOPPY
307 count_only = (mode & COUNT_SAVE);
308 #endif
309 uid = getuid();
310 bwrite(fd, (genericptr_t) &uid, sizeof uid);
311 bwrite(fd, (genericptr_t) &flags, sizeof(struct flag));
312 bwrite(fd, (genericptr_t) &u, sizeof(struct you));
313
314 /* must come before migrating_objs and migrating_mons are freed */
315 save_timers(fd, mode, RANGE_GLOBAL);
316 save_light_sources(fd, mode, RANGE_GLOBAL);
317
318 saveobjchn(fd, invent, mode);
319 saveobjchn(fd, migrating_objs, mode);
320 savemonchn(fd, migrating_mons, mode);
321 if (release_data(mode)) {
322 invent = 0;
323 migrating_objs = 0;
324 migrating_mons = 0;
325 }
326 bwrite(fd, (genericptr_t) mvitals, sizeof(mvitals));
327
328 save_dungeon(fd, (boolean)!!perform_bwrite(mode),
329 (boolean)!!release_data(mode));
330 savelevchn(fd, mode);
331 bwrite(fd, (genericptr_t) &moves, sizeof moves);
332 bwrite(fd, (genericptr_t) &monstermoves, sizeof monstermoves);
333 bwrite(fd, (genericptr_t) &quest_status, sizeof(struct q_score));
334 bwrite(fd, (genericptr_t) spl_book,
335 sizeof(struct spell) * (MAXSPELL + 1));
336 save_artifacts(fd);
337 save_oracles(fd, mode);
338 if(ustuck_id)
339 bwrite(fd, (genericptr_t) &ustuck_id, sizeof ustuck_id);
340 #ifdef STEED
341 if(usteed_id)
342 bwrite(fd, (genericptr_t) &usteed_id, sizeof usteed_id);
343 #endif
344 bwrite(fd, (genericptr_t) pl_tutorial, sizeof pl_tutorial);
345 bwrite(fd, (genericptr_t) pl_character, sizeof pl_character);
346 bwrite(fd, (genericptr_t) pl_fruit, sizeof pl_fruit);
347 bwrite(fd, (genericptr_t) ¤t_fruit, sizeof current_fruit);
348 savefruitchn(fd, mode);
349 savenames(fd, mode);
350 save_waterlevel(fd, mode);
351
352 #ifdef RECORD_ACHIEVE
353 bwrite(fd, (genericptr_t) &achieve, sizeof achieve);
354 #endif
355 #if defined(RECORD_REALTIME) || defined(REALTIME_ON_BOTL)
356 realtime = get_realtime();
357 bwrite(fd, (genericptr_t) &realtime, sizeof realtime);
358 #endif
359
360 bflush(fd);
361 }
362
363 #ifdef INSURANCE
364 void
savestateinlock()365 savestateinlock()
366 {
367 int fd, hpid;
368 static boolean havestate = TRUE;
369 char whynot[BUFSZ];
370
371 /* When checkpointing is on, the full state needs to be written
372 * on each checkpoint. When checkpointing is off, only the pid
373 * needs to be in the level.0 file, so it does not need to be
374 * constantly rewritten. When checkpointing is turned off during
375 * a game, however, the file has to be rewritten once to truncate
376 * it and avoid restoring from outdated information.
377 *
378 * Restricting havestate to this routine means that an additional
379 * noop pid rewriting will take place on the first "checkpoint" after
380 * the game is started or restored, if checkpointing is off.
381 */
382 if (flags.ins_chkpt || havestate) {
383 /* save the rest of the current game state in the lock file,
384 * following the original int pid, the current level number,
385 * and the current savefile name, which should not be subject
386 * to any internal compression schemes since they must be
387 * readable by an external utility
388 */
389 fd = open_levelfile(0, whynot);
390 if (fd < 0) {
391 pline("%s", whynot);
392 pline("Probably someone removed it.");
393 killer = whynot;
394 done(TRICKED);
395 return;
396 }
397
398 (void) read(fd, (genericptr_t) &hpid, sizeof(hpid));
399 if (hackpid != hpid) {
400 Sprintf(whynot,
401 "Level #0 pid (%d) doesn't match ours (%d)!",
402 hpid, hackpid);
403 pline("%s", whynot);
404 killer = whynot;
405 done(TRICKED);
406 }
407 (void) close(fd);
408
409 fd = create_levelfile(0, whynot);
410 if (fd < 0) {
411 pline("%s", whynot);
412 killer = whynot;
413 done(TRICKED);
414 return;
415 }
416 (void) write(fd, (genericptr_t) &hackpid, sizeof(hackpid));
417 if (flags.ins_chkpt) {
418 int currlev = ledger_no(&u.uz);
419
420 (void) write(fd, (genericptr_t) &currlev, sizeof(currlev));
421 save_savefile_name(fd);
422 store_version(fd);
423 #ifdef STORE_PLNAME_IN_FILE
424 bwrite(fd, (genericptr_t) plname, PL_NSIZ);
425 #endif
426 ustuck_id = (u.ustuck ? u.ustuck->m_id : 0);
427 #ifdef STEED
428 usteed_id = (u.usteed ? u.usteed->m_id : 0);
429 #endif
430 savegamestate(fd, WRITE_SAVE);
431 }
432 bclose(fd);
433 }
434 havestate = flags.ins_chkpt;
435 }
436 #endif
437
438 #ifdef MFLOPPY
439 boolean
savelev(fd,lev,mode)440 savelev(fd, lev, mode)
441 int fd;
442 xchar lev;
443 int mode;
444 {
445 if (mode & COUNT_SAVE) {
446 bytes_counted = 0;
447 savelev0(fd, lev, COUNT_SAVE);
448 /* probably bytes_counted will be filled in again by an
449 * immediately following WRITE_SAVE anyway, but we'll
450 * leave it out of checkspace just in case */
451 if (iflags.checkspace) {
452 while (bytes_counted > freediskspace(levels))
453 if (!swapout_oldest())
454 return FALSE;
455 }
456 }
457 if (mode & (WRITE_SAVE | FREE_SAVE)) {
458 bytes_counted = 0;
459 savelev0(fd, lev, mode);
460 }
461 if (mode != FREE_SAVE) {
462 level_info[lev].where = ACTIVE;
463 level_info[lev].time = moves;
464 level_info[lev].size = bytes_counted;
465 }
466 return TRUE;
467 }
468
469 STATIC_OVL void
savelev0(fd,lev,mode)470 savelev0(fd,lev,mode)
471 #else
472 void
473 savelev(fd,lev,mode)
474 #endif
475 int fd;
476 xchar lev;
477 int mode;
478 {
479 #ifdef TOS
480 short tlev;
481 #endif
482
483 /* if we're tearing down the current level without saving anything
484 (which happens upon entrance to the endgame or after an aborted
485 restore attempt) then we don't want to do any actual I/O */
486 if (mode == FREE_SAVE) goto skip_lots;
487 if (iflags.purge_monsters) {
488 /* purge any dead monsters (necessary if we're starting
489 * a panic save rather than a normal one, or sometimes
490 * when changing levels without taking time -- e.g.
491 * create statue trap then immediately level teleport) */
492 dmonsfree();
493 }
494
495 if(fd < 0) panic("Save on bad file!"); /* impossible */
496 #ifdef MFLOPPY
497 count_only = (mode & COUNT_SAVE);
498 #endif
499 if (lev >= 0 && lev <= maxledgerno())
500 level_info[lev].flags |= VISITED;
501 bwrite(fd,(genericptr_t) &hackpid,sizeof(hackpid));
502 #ifdef TOS
503 tlev=lev; tlev &= 0x00ff;
504 bwrite(fd,(genericptr_t) &tlev,sizeof(tlev));
505 #else
506 bwrite(fd,(genericptr_t) &lev,sizeof(lev));
507 #endif
508 #ifdef RLECOMP
509 {
510 /* perform run-length encoding of rm structs */
511 struct rm *prm, *rgrm;
512 int x, y;
513 uchar match;
514
515 rgrm = &levl[0][0]; /* start matching at first rm */
516 match = 0;
517
518 for (y = 0; y < ROWNO; y++) {
519 for (x = 0; x < COLNO; x++) {
520 prm = &levl[x][y];
521 if (prm->glyph == rgrm->glyph
522 && prm->typ == rgrm->typ
523 && prm->seenv == rgrm->seenv
524 && prm->horizontal == rgrm->horizontal
525 && prm->flags == rgrm->flags
526 && prm->lit == rgrm->lit
527 && prm->waslit == rgrm->waslit
528 && prm->roomno == rgrm->roomno
529 && prm->edge == rgrm->edge) {
530 match++;
531 if (match > 254) {
532 match = 254; /* undo this match */
533 goto writeout;
534 }
535 } else {
536 /* the run has been broken,
537 * write out run-length encoding */
538 writeout:
539 bwrite(fd, (genericptr_t)&match, sizeof(uchar));
540 bwrite(fd, (genericptr_t)rgrm, sizeof(struct rm));
541 /* start encoding again. we have at least 1 rm
542 * in the next run, viz. this one. */
543 match = 1;
544 rgrm = prm;
545 }
546 }
547 }
548 if (match > 0) {
549 bwrite(fd, (genericptr_t)&match, sizeof(uchar));
550 bwrite(fd, (genericptr_t)rgrm, sizeof(struct rm));
551 }
552 }
553 #else
554 bwrite(fd,(genericptr_t) levl,sizeof(levl));
555 #endif /* RLECOMP */
556
557 bwrite(fd,(genericptr_t) &monstermoves,sizeof(monstermoves));
558 bwrite(fd,(genericptr_t) &upstair,sizeof(stairway));
559 bwrite(fd,(genericptr_t) &dnstair,sizeof(stairway));
560 bwrite(fd,(genericptr_t) &upladder,sizeof(stairway));
561 bwrite(fd,(genericptr_t) &dnladder,sizeof(stairway));
562 bwrite(fd,(genericptr_t) &sstairs,sizeof(stairway));
563 bwrite(fd,(genericptr_t) &updest,sizeof(dest_area));
564 bwrite(fd,(genericptr_t) &dndest,sizeof(dest_area));
565 bwrite(fd,(genericptr_t) &level.flags,sizeof(level.flags));
566 bwrite(fd, (genericptr_t) doors, sizeof(doors));
567 save_rooms(fd); /* no dynamic memory to reclaim */
568
569 /* from here on out, saving also involves allocated memory cleanup */
570 skip_lots:
571 /* must be saved before mons, objs, and buried objs */
572 save_timers(fd, mode, RANGE_LEVEL);
573 save_light_sources(fd, mode, RANGE_LEVEL);
574
575 savemonchn(fd, fmon, mode);
576 save_worm(fd, mode); /* save worm information */
577 savetrapchn(fd, ftrap, mode);
578 saveobjchn(fd, fobj, mode);
579 saveobjchn(fd, level.buriedobjlist, mode);
580 saveobjchn(fd, billobjs, mode);
581 save_mongen_override(fd, level.mon_gen, mode);
582 save_lvl_sounds(fd, level.sounds, mode);
583 if (release_data(mode)) {
584 fmon = 0;
585 ftrap = 0;
586 fobj = 0;
587 level.buriedobjlist = 0;
588 billobjs = 0;
589 free(level.mon_gen);
590 level.mon_gen = NULL;
591 level.sounds = NULL;
592 }
593 save_engravings(fd, mode);
594 savedamage(fd, mode);
595 save_regions(fd, mode);
596 if (mode != FREE_SAVE) bflush(fd);
597 }
598
599 #ifdef ZEROCOMP
600 /* The runs of zero-run compression are flushed after the game state or a
601 * level is written out. This adds a couple bytes to a save file, where
602 * the runs could be mashed together, but it allows gluing together game
603 * state and level files to form a save file, and it means the flushing
604 * does not need to be specifically called for every other time a level
605 * file is written out.
606 */
607
608 #define RLESC '\0' /* Leading character for run of LRESC's */
609 #define flushoutrun(ln) (bputc(RLESC), bputc(ln), ln = -1)
610
611 #ifndef ZEROCOMP_BUFSIZ
612 # define ZEROCOMP_BUFSIZ BUFSZ
613 #endif
614 static NEARDATA unsigned char outbuf[ZEROCOMP_BUFSIZ];
615 static NEARDATA unsigned short outbufp = 0;
616 static NEARDATA short outrunlength = -1;
617 static NEARDATA int bwritefd;
618 static NEARDATA boolean compressing = FALSE;
619
620 /*dbg()
621 {
622 HUP printf("outbufp %d outrunlength %d\n", outbufp,outrunlength);
623 }*/
624
625 STATIC_OVL void
bputc(c)626 bputc(c)
627 int c;
628 {
629 #ifdef MFLOPPY
630 bytes_counted++;
631 if (count_only)
632 return;
633 #endif
634 if (outbufp >= sizeof outbuf) {
635 (void) write(bwritefd, outbuf, sizeof outbuf);
636 outbufp = 0;
637 }
638 outbuf[outbufp++] = (unsigned char)c;
639 }
640
641 /*ARGSUSED*/
642 void
bufon(fd)643 bufon(fd)
644 int fd;
645 {
646 compressing = TRUE;
647 return;
648 }
649
650 /*ARGSUSED*/
651 void
bufoff(fd)652 bufoff(fd)
653 int fd;
654 {
655 if (outbufp) {
656 outbufp = 0;
657 panic("closing file with buffered data still unwritten");
658 }
659 outrunlength = -1;
660 compressing = FALSE;
661 return;
662 }
663
664 void
bflush(fd)665 bflush(fd) /* flush run and buffer */
666 register int fd;
667 {
668 bwritefd = fd;
669 if (outrunlength >= 0) { /* flush run */
670 flushoutrun(outrunlength);
671 }
672 #ifdef MFLOPPY
673 if (count_only) outbufp = 0;
674 #endif
675
676 if (outbufp) {
677 if (write(fd, outbuf, outbufp) != outbufp) {
678 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
679 if (program_state.done_hup)
680 terminate(EXIT_FAILURE);
681 else
682 #endif
683 bclose(fd); /* panic (outbufp != 0) */
684 }
685 outbufp = 0;
686 }
687 }
688
689 void
bwrite(fd,loc,num)690 bwrite(fd, loc, num)
691 int fd;
692 genericptr_t loc;
693 register unsigned num;
694 {
695 register unsigned char *bp = (unsigned char *)loc;
696
697 if (!compressing) {
698 #ifdef MFLOPPY
699 bytes_counted += num;
700 if (count_only) return;
701 #endif
702 if ((unsigned) write(fd, loc, num) != num) {
703 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
704 if (program_state.done_hup)
705 terminate(EXIT_FAILURE);
706 else
707 #endif
708 panic("cannot write %u bytes to file #%d", num, fd);
709 }
710 } else {
711 bwritefd = fd;
712 for (; num; num--, bp++) {
713 if (*bp == RLESC) { /* One more char in run */
714 if (++outrunlength == 0xFF) {
715 flushoutrun(outrunlength);
716 }
717 } else { /* end of run */
718 if (outrunlength >= 0) { /* flush run */
719 flushoutrun(outrunlength);
720 }
721 bputc(*bp);
722 }
723 }
724 }
725 }
726
727 void
bclose(fd)728 bclose(fd)
729 int fd;
730 {
731 bufoff(fd);
732 (void) close(fd);
733 return;
734 }
735
736 #else /* ZEROCOMP */
737
738 static int bw_fd = -1;
739 static FILE *bw_FILE = 0;
740 static boolean buffering = FALSE;
741
742 void
bufon(fd)743 bufon(fd)
744 int fd;
745 {
746 #ifdef UNIX
747 if(bw_fd >= 0)
748 panic("double buffering unexpected");
749 bw_fd = fd;
750 if((bw_FILE = fdopen(fd, "w")) == 0)
751 panic("buffering of file %d failed", fd);
752 #endif
753 buffering = TRUE;
754 }
755
756 void
bufoff(fd)757 bufoff(fd)
758 int fd;
759 {
760 bflush(fd);
761 buffering = FALSE;
762 }
763
764 void
bflush(fd)765 bflush(fd)
766 int fd;
767 {
768 #ifdef UNIX
769 if(fd == bw_fd) {
770 if(fflush(bw_FILE) == EOF)
771 panic("flush of savefile failed!");
772 }
773 #endif
774 return;
775 }
776
777 void
bwrite(fd,loc,num)778 bwrite(fd,loc,num)
779 register int fd;
780 register genericptr_t loc;
781 register unsigned num;
782 {
783 boolean failed;
784
785 #ifdef MFLOPPY
786 bytes_counted += num;
787 if (count_only) return;
788 #endif
789
790 #ifdef UNIX
791 if (buffering) {
792 if(fd != bw_fd)
793 panic("unbuffered write to fd %d (!= %d)", fd, bw_fd);
794
795 failed = (fwrite(loc, (int)num, 1, bw_FILE) != 1);
796 } else
797 #endif /* UNIX */
798 {
799 /* lint wants the 3rd arg of write to be an int; lint -p an unsigned */
800 #if defined(BSD) || defined(ULTRIX)
801 failed = (write(fd, loc, (int)num) != (int)num);
802 #else /* e.g. SYSV, __TURBOC__ */
803 failed = (write(fd, loc, num) != num);
804 #endif
805 }
806
807 if (failed) {
808 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
809 if (program_state.done_hup)
810 terminate(EXIT_FAILURE);
811 else
812 #endif
813 panic("cannot write %u bytes to file #%d", num, fd);
814 }
815 }
816
817 void
bclose(fd)818 bclose(fd)
819 int fd;
820 {
821 bufoff(fd);
822 #ifdef UNIX
823 if (fd == bw_fd) {
824 (void) fclose(bw_FILE);
825 bw_fd = -1;
826 bw_FILE = 0;
827 } else
828 #endif
829 (void) close(fd);
830 return;
831 }
832 #endif /* ZEROCOMP */
833
834 STATIC_OVL void
savelevchn(fd,mode)835 savelevchn(fd, mode)
836 register int fd, mode;
837 {
838 s_level *tmplev, *tmplev2;
839 int cnt = 0;
840
841 for (tmplev = sp_levchn; tmplev; tmplev = tmplev->next) cnt++;
842 if (perform_bwrite(mode))
843 bwrite(fd, (genericptr_t) &cnt, sizeof(int));
844
845 for (tmplev = sp_levchn; tmplev; tmplev = tmplev2) {
846 tmplev2 = tmplev->next;
847 if (perform_bwrite(mode))
848 bwrite(fd, (genericptr_t) tmplev, sizeof(s_level));
849 if (release_data(mode))
850 free((genericptr_t) tmplev);
851 }
852 if (release_data(mode))
853 sp_levchn = 0;
854 }
855
856 STATIC_OVL void
savedamage(fd,mode)857 savedamage(fd, mode)
858 register int fd, mode;
859 {
860 register struct damage *damageptr, *tmp_dam;
861 unsigned int xl = 0;
862
863 damageptr = level.damagelist;
864 for (tmp_dam = damageptr; tmp_dam; tmp_dam = tmp_dam->next)
865 xl++;
866 if (perform_bwrite(mode))
867 bwrite(fd, (genericptr_t) &xl, sizeof(xl));
868
869 while (xl--) {
870 if (perform_bwrite(mode))
871 bwrite(fd, (genericptr_t) damageptr, sizeof(*damageptr));
872 tmp_dam = damageptr;
873 damageptr = damageptr->next;
874 if (release_data(mode))
875 free((genericptr_t)tmp_dam);
876 }
877 if (release_data(mode))
878 level.damagelist = 0;
879 }
880
881 void
save_mongen_override(fd,or,mode)882 save_mongen_override(fd, or, mode)
883 register int fd, mode;
884 register struct mon_gen_override *or;
885 {
886 struct mon_gen_tuple *mt;
887 struct mon_gen_tuple *prev;
888 int marker = 0;
889
890 if (!or) {
891 if (perform_bwrite(mode)) {
892 marker = 0;
893 bwrite(fd, (genericptr_t) &marker, sizeof(marker));
894 }
895 } else {
896 if (perform_bwrite(mode)) {
897 marker = 1;
898 bwrite(fd, (genericptr_t) &marker, sizeof(marker));
899 bwrite(fd, (genericptr_t) or, sizeof(struct mon_gen_override));
900 }
901 mt = or->gen_chances;
902 while (mt) {
903 if (perform_bwrite(mode)) {
904 bwrite(fd, (genericptr_t) mt, sizeof(struct mon_gen_tuple));
905 }
906 prev = mt;
907 mt = mt->next;
908 if (release_data(mode))
909 free(prev);
910 }
911 if (release_data(mode))
912 or->gen_chances = NULL;
913 }
914 }
915
916 void
save_lvl_sounds(fd,or,mode)917 save_lvl_sounds(fd, or, mode)
918 register int fd, mode;
919 register struct lvl_sounds *or;
920 {
921 struct lvl_sound_bite *mt;
922 int marker = 0;
923 int i;
924 int len;
925
926 if (!or) {
927 if (perform_bwrite(mode)) {
928 marker = 0;
929 bwrite(fd, (genericptr_t) &marker, sizeof(marker));
930 }
931 } else {
932 if (perform_bwrite(mode)) {
933 marker = 1;
934 bwrite(fd, (genericptr_t) &marker, sizeof(marker));
935 bwrite(fd, (genericptr_t) or, sizeof(struct lvl_sounds));
936
937 for (i = 0; i < or->n_sounds; i++) {
938 bwrite(fd, (genericptr_t)&(or->sounds[i].flags), sizeof(or->sounds[i].flags));
939 len = strlen(or->sounds[i].msg)+1;
940 bwrite(fd, (genericptr_t)&len, sizeof(len));
941 bwrite(fd, (genericptr_t)or->sounds[i].msg, len);
942 }
943 }
944 if (release_data(mode)) {
945 for (i = 0; i < or->n_sounds; i++)
946 free(or->sounds[i].msg);
947 free(or->sounds);
948 or->sounds = NULL;
949 or->n_sounds = 0;
950 }
951 }
952 }
953
954
955 STATIC_OVL void
saveobjchn(fd,otmp,mode)956 saveobjchn(fd, otmp, mode)
957 register int fd, mode;
958 register struct obj *otmp;
959 {
960 register struct obj *otmp2;
961 unsigned int xl;
962 int minusone = -1;
963
964 while(otmp) {
965 otmp2 = otmp->nobj;
966 if (perform_bwrite(mode)) {
967 xl = otmp->oxlth + otmp->onamelth;
968 bwrite(fd, (genericptr_t) &xl, sizeof(int));
969 bwrite(fd, (genericptr_t) otmp, xl + sizeof(struct obj));
970 }
971 if (Has_contents(otmp))
972 saveobjchn(fd,otmp->cobj,mode);
973 if (release_data(mode)) {
974 if (otmp->oclass == FOOD_CLASS) food_disappears(otmp);
975 if (otmp->oclass == SPBOOK_CLASS) book_disappears(otmp);
976 otmp->where = OBJ_FREE; /* set to free so dealloc will work */
977 otmp->timed = 0; /* not timed any more */
978 otmp->lamplit = 0; /* caller handled lights */
979 dealloc_obj(otmp);
980 }
981 otmp = otmp2;
982 }
983 if (perform_bwrite(mode))
984 bwrite(fd, (genericptr_t) &minusone, sizeof(int));
985 }
986
987 STATIC_OVL void
savemonchn(fd,mtmp,mode)988 savemonchn(fd, mtmp, mode)
989 register int fd, mode;
990 register struct monst *mtmp;
991 {
992 register struct monst *mtmp2;
993 unsigned int xl;
994 int minusone = -1;
995 struct permonst *monbegin = &mons[0];
996
997 if (perform_bwrite(mode))
998 bwrite(fd, (genericptr_t) &monbegin, sizeof(monbegin));
999
1000 while (mtmp) {
1001 mtmp2 = mtmp->nmon;
1002 if (perform_bwrite(mode)) {
1003 xl = mtmp->mxlth + mtmp->mnamelth;
1004 bwrite(fd, (genericptr_t) &xl, sizeof(int));
1005 bwrite(fd, (genericptr_t) mtmp, xl + sizeof(struct monst));
1006 }
1007 if (mtmp->minvent)
1008 saveobjchn(fd,mtmp->minvent,mode);
1009 if (release_data(mode))
1010 dealloc_monst(mtmp);
1011 mtmp = mtmp2;
1012 }
1013 if (perform_bwrite(mode))
1014 bwrite(fd, (genericptr_t) &minusone, sizeof(int));
1015 }
1016
1017 STATIC_OVL void
savetrapchn(fd,trap,mode)1018 savetrapchn(fd, trap, mode)
1019 register int fd, mode;
1020 register struct trap *trap;
1021 {
1022 register struct trap *trap2;
1023
1024 while (trap) {
1025 trap2 = trap->ntrap;
1026 if (perform_bwrite(mode))
1027 bwrite(fd, (genericptr_t) trap, sizeof(struct trap));
1028 if (release_data(mode))
1029 dealloc_trap(trap);
1030 trap = trap2;
1031 }
1032 if (perform_bwrite(mode))
1033 bwrite(fd, (genericptr_t)nulls, sizeof(struct trap));
1034 }
1035
1036 /* save all the fruit names and ID's; this is used only in saving whole games
1037 * (not levels) and in saving bones levels. When saving a bones level,
1038 * we only want to save the fruits which exist on the bones level; the bones
1039 * level routine marks nonexistent fruits by making the fid negative.
1040 */
1041 void
savefruitchn(fd,mode)1042 savefruitchn(fd, mode)
1043 register int fd, mode;
1044 {
1045 register struct fruit *f2, *f1;
1046
1047 f1 = ffruit;
1048 while (f1) {
1049 f2 = f1->nextf;
1050 if (f1->fid >= 0 && perform_bwrite(mode))
1051 bwrite(fd, (genericptr_t) f1, sizeof(struct fruit));
1052 if (release_data(mode))
1053 dealloc_fruit(f1);
1054 f1 = f2;
1055 }
1056 if (perform_bwrite(mode))
1057 bwrite(fd, (genericptr_t)nulls, sizeof(struct fruit));
1058 if (release_data(mode))
1059 ffruit = 0;
1060 }
1061
1062 #if defined(STATUS_COLORS) && defined(TEXTCOLOR)
1063
1064 void
free_percent_color_options(list_head)1065 free_percent_color_options(list_head)
1066 const struct percent_color_option *list_head;
1067 {
1068 if (list_head == NULL) return;
1069 free_percent_color_options(list_head->next);
1070 free((genericptr_t)list_head);
1071 }
1072
1073 void
free_text_color_options(list_head)1074 free_text_color_options(list_head)
1075 const struct text_color_option *list_head;
1076 {
1077 if (list_head == NULL) return;
1078 free_text_color_options(list_head->next);
1079 free((genericptr_t)list_head->text);
1080 free((genericptr_t)list_head);
1081 }
1082
1083 void
free_status_colors()1084 free_status_colors()
1085 {
1086 free_percent_color_options(hp_colors); hp_colors = NULL;
1087 free_percent_color_options(pw_colors); pw_colors = NULL;
1088 free_text_color_options(text_colors); text_colors = NULL;
1089 }
1090 #endif
1091
1092 /* also called by prscore(); this probably belongs in dungeon.c... */
1093 void
free_dungeons()1094 free_dungeons()
1095 {
1096 #ifdef FREE_ALL_MEMORY
1097 savelevchn(0, FREE_SAVE);
1098 save_dungeon(0, FALSE, TRUE);
1099 #endif
1100 return;
1101 }
1102
1103 #ifdef MENU_COLOR
1104 void
free_menu_coloring()1105 free_menu_coloring()
1106 {
1107 struct menucoloring *tmp = menu_colorings;
1108
1109 while (tmp) {
1110 struct menucoloring *tmp2 = tmp->next;
1111 # ifdef MENU_COLOR_REGEX
1112 (void) regfree(&tmp->match);
1113 # else
1114 free(tmp->match);
1115 # endif
1116 free(tmp);
1117 tmp = tmp2;
1118 }
1119 }
1120 #endif /* MENU_COLOR */
1121
1122 void
freedynamicdata()1123 freedynamicdata()
1124 {
1125 #if defined(STATUS_COLORS) && defined(TEXTCOLOR)
1126 free_status_colors();
1127 #endif
1128 unload_qtlist();
1129 free_invbuf(); /* let_to_name (invent.c) */
1130 free_youbuf(); /* You_buf,&c (pline.c) */
1131 #ifdef MENU_COLOR
1132 free_menu_coloring();
1133 #endif
1134 tmp_at(DISP_FREEMEM, 0); /* temporary display effects */
1135 #ifdef FREE_ALL_MEMORY
1136 # define freeobjchn(X) (saveobjchn(0, X, FREE_SAVE), X = 0)
1137 # define freemonchn(X) (savemonchn(0, X, FREE_SAVE), X = 0)
1138 # define freetrapchn(X) (savetrapchn(0, X, FREE_SAVE), X = 0)
1139 # define freefruitchn() savefruitchn(0, FREE_SAVE)
1140 # define freenames() savenames(0, FREE_SAVE)
1141 # define free_oracles() save_oracles(0, FREE_SAVE)
1142 # define free_waterlevel() save_waterlevel(0, FREE_SAVE)
1143 # define free_worm() save_worm(0, FREE_SAVE)
1144 # define free_timers(R) save_timers(0, FREE_SAVE, R)
1145 # define free_light_sources(R) save_light_sources(0, FREE_SAVE, R);
1146 # define free_engravings() save_engravings(0, FREE_SAVE)
1147 # define freedamage() savedamage(0, FREE_SAVE)
1148 # define free_animals() mon_animal_list(FALSE)
1149
1150 /* move-specific data */
1151 dmonsfree(); /* release dead monsters */
1152
1153 /* level-specific data */
1154 free_timers(RANGE_LEVEL);
1155 free_light_sources(RANGE_LEVEL);
1156 freemonchn(fmon);
1157 free_worm(); /* release worm segment information */
1158 freetrapchn(ftrap);
1159 freeobjchn(fobj);
1160 freeobjchn(level.buriedobjlist);
1161 freeobjchn(billobjs);
1162 free_engravings();
1163 freedamage();
1164
1165 /* game-state data */
1166 free_timers(RANGE_GLOBAL);
1167 free_light_sources(RANGE_GLOBAL);
1168 freeobjchn(invent);
1169 freeobjchn(migrating_objs);
1170 freemonchn(migrating_mons);
1171 freemonchn(mydogs); /* ascension or dungeon escape */
1172 /* freelevchn(); [folded into free_dungeons()] */
1173 free_animals();
1174 free_oracles();
1175 freefruitchn();
1176 freenames();
1177 free_waterlevel();
1178 free_dungeons();
1179
1180 /* some pointers in iflags */
1181 if (iflags.wc_font_map) free(iflags.wc_font_map);
1182 if (iflags.wc_font_message) free(iflags.wc_font_message);
1183 if (iflags.wc_font_text) free(iflags.wc_font_text);
1184 if (iflags.wc_font_menu) free(iflags.wc_font_menu);
1185 if (iflags.wc_font_status) free(iflags.wc_font_status);
1186 if (iflags.wc_tile_file) free(iflags.wc_tile_file);
1187 #ifdef AUTOPICKUP_EXCEPTIONS
1188 free_autopickup_exceptions();
1189 #endif
1190
1191 #endif /* FREE_ALL_MEMORY */
1192 #ifdef USE_MERSENNE_TWISTER
1193 gsl_rng_free(rng_state);
1194 #endif
1195 return;
1196 }
1197
1198 #ifdef MFLOPPY
1199 boolean
swapin_file(lev)1200 swapin_file(lev)
1201 int lev;
1202 {
1203 char to[PATHLEN], from[PATHLEN];
1204
1205 Sprintf(from, "%s%s", permbones, alllevels);
1206 Sprintf(to, "%s%s", levels, alllevels);
1207 set_levelfile_name(from, lev);
1208 set_levelfile_name(to, lev);
1209 if (iflags.checkspace) {
1210 while (level_info[lev].size > freediskspace(to))
1211 if (!swapout_oldest())
1212 return FALSE;
1213 }
1214 # ifdef WIZARD
1215 if (wizard) {
1216 pline("Swapping in `%s'.", from);
1217 wait_synch();
1218 }
1219 # endif
1220 copyfile(from, to);
1221 (void) unlink(from);
1222 level_info[lev].where = ACTIVE;
1223 return TRUE;
1224 }
1225
1226 STATIC_OVL boolean
swapout_oldest()1227 swapout_oldest() {
1228 char to[PATHLEN], from[PATHLEN];
1229 int i, oldest;
1230 long oldtime;
1231
1232 if (!ramdisk)
1233 return FALSE;
1234 for (i = 1, oldtime = 0, oldest = 0; i <= maxledgerno(); i++)
1235 if (level_info[i].where == ACTIVE
1236 && (!oldtime || level_info[i].time < oldtime)) {
1237 oldest = i;
1238 oldtime = level_info[i].time;
1239 }
1240 if (!oldest)
1241 return FALSE;
1242 Sprintf(from, "%s%s", levels, alllevels);
1243 Sprintf(to, "%s%s", permbones, alllevels);
1244 set_levelfile_name(from, oldest);
1245 set_levelfile_name(to, oldest);
1246 # ifdef WIZARD
1247 if (wizard) {
1248 pline("Swapping out `%s'.", from);
1249 wait_synch();
1250 }
1251 # endif
1252 copyfile(from, to);
1253 (void) unlink(from);
1254 level_info[oldest].where = SWAPPED;
1255 return TRUE;
1256 }
1257
1258 STATIC_OVL void
copyfile(from,to)1259 copyfile(from, to)
1260 char *from, *to;
1261 {
1262 # ifdef TOS
1263
1264 if (_copyfile(from, to))
1265 panic("Can't copy %s to %s", from, to);
1266 # else
1267 char buf[BUFSIZ]; /* this is system interaction, therefore
1268 * BUFSIZ instead of NetHack's BUFSZ */
1269 int nfrom, nto, fdfrom, fdto;
1270
1271 if ((fdfrom = open(from, O_RDONLY | O_BINARY, FCMASK)) < 0)
1272 panic("Can't copy from %s !?", from);
1273 if ((fdto = open(to, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, FCMASK)) < 0)
1274 panic("Can't copy to %s", to);
1275 do {
1276 nfrom = read(fdfrom, buf, BUFSIZ);
1277 nto = write(fdto, buf, nfrom);
1278 if (nto != nfrom)
1279 panic("Copyfile failed!");
1280 } while (nfrom == BUFSIZ);
1281 (void) close(fdfrom);
1282 (void) close(fdto);
1283 # endif /* TOS */
1284 }
1285
1286 void
co_false()1287 co_false() /* see comment in bones.c */
1288 {
1289 count_only = FALSE;
1290 return;
1291 }
1292
1293 #endif /* MFLOPPY */
1294
1295 /*save.c*/
1296