1 /* NetHack 3.6 save.c $NHDT-Date: 1448241784 2015/11/23 01:23:04 $ $NHDT-Branch: master $:$NHDT-Revision: 1.95 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
5 /* JNetHack Copyright */
6 /* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000 */
7 /* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2016 */
8 /* JNetHack may be freely redistributed. See license for details. */
16 #if !defined(LSC) && !defined(O_WRONLY) && !defined(AZTEC_C)
22 static int count_only;
26 int dotcnt, dotrow; /* also used in restore */
29 STATIC_DCL void FDECL(savelevchn, (int, int));
30 STATIC_DCL void FDECL(savedamage, (int, int));
31 STATIC_DCL void FDECL(saveobj, (int, struct obj *));
32 STATIC_DCL void FDECL(saveobjchn, (int, struct obj *, int));
33 STATIC_DCL void FDECL(savemon, (int, struct monst *));
34 STATIC_DCL void FDECL(savemonchn, (int, struct monst *, int));
35 STATIC_DCL void FDECL(savetrapchn, (int, struct trap *, int));
36 STATIC_DCL void FDECL(savegamestate, (int, int));
37 STATIC_OVL void FDECL(save_msghistory, (int, int));
39 STATIC_DCL void FDECL(savelev0, (int, XCHAR_P, int));
40 STATIC_DCL boolean NDECL(swapout_oldest);
41 STATIC_DCL void FDECL(copyfile, (char *, char *));
43 STATIC_DCL void FDECL(savelevl, (int fd, BOOLEAN_P));
44 STATIC_DCL void FDECL(def_bufon, (int));
45 STATIC_DCL void FDECL(def_bufoff, (int));
46 STATIC_DCL void FDECL(def_bflush, (int));
47 STATIC_DCL void FDECL(def_bwrite, (int, genericptr_t, unsigned int));
49 STATIC_DCL void FDECL(zerocomp_bufon, (int));
50 STATIC_DCL void FDECL(zerocomp_bufoff, (int));
51 STATIC_DCL void FDECL(zerocomp_bflush, (int));
52 STATIC_DCL void FDECL(zerocomp_bwrite, (int, genericptr_t, unsigned int));
53 STATIC_DCL void FDECL(zerocomp_bputc, (int));
56 static struct save_procs {
58 void FDECL((*save_bufon), (int));
59 void FDECL((*save_bufoff), (int));
60 void FDECL((*save_bflush), (int));
61 void FDECL((*save_bwrite), (int, genericptr_t, unsigned int));
62 void FDECL((*save_bclose), (int));
64 #if !defined(ZEROCOMP) || (defined(COMPRESS) || defined(ZLIB_COMP))
65 "externalcomp", def_bufon, def_bufoff, def_bflush, def_bwrite, def_bclose,
67 "zerocomp", zerocomp_bufon, zerocomp_bufoff,
68 zerocomp_bflush, zerocomp_bwrite, zerocomp_bclose,
72 static long nulls[sizeof(struct trap) + sizeof(struct fruit)];
74 #if defined(UNIX) || defined(VMS) || defined(__EMX__) || defined(WIN32)
75 #define HUP if (!program_state.done_hup)
80 /* need to preserve these during save to avoid accessing freed memory */
81 static unsigned ustuck_id = 0, usteed_id = 0;
86 clear_nhwindow(WIN_MESSAGE);
88 if (yn("Really save?") == 'n') {
90 if(yn("
\96{
\93\96\82É
\95Û
\91¶
\82·
\82é
\81H") == 'n') {
91 clear_nhwindow(WIN_MESSAGE);
95 clear_nhwindow(WIN_MESSAGE);
99 pline("
\95Û
\91¶
\92\86\81D
\81D
\81D");
100 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
101 program_state.done_hup = 0;
104 u.uhp = -1; /* universal game's over indicator */
105 /* make sure they see the Saving message */
106 display_nhwindow(WIN_MESSAGE, TRUE);
108 exit_nhwindows("Be seeing you...");
110 exit_nhwindows("
\82Ü
\82½
\89ï
\82¢
\82Ü
\82µ
\82å
\82¤
\81D
\81D
\81D");
111 terminate(EXIT_SUCCESS);
118 /* returns 1 if save successful */
123 register int fd, ofd;
128 /* we may get here via hangup signal, in which case we want to fix up
129 a few of things before saving so that they won't be restored in
130 an improper state; these will be no-ops for normal save sequence */
132 if (iflags.save_uinwater)
133 u.uinwater = 1, iflags.save_uinwater = 0;
134 if (iflags.save_uburied)
135 u.uburied = 1, iflags.save_uburied = 0;
137 if (!program_state.something_worth_saving || !SAVEF[0])
139 fq_save = fqname(SAVEF, SAVEPREFIX, 1); /* level files take 0 */
141 #if defined(UNIX) || defined(VMS)
142 sethanguphandler((void FDECL((*), (int) )) SIG_IGN);
145 (void) signal(SIGINT, SIG_IGN);
148 #if defined(MICRO) && defined(MFLOPPY)
149 if (!saveDiskPrompt(0))
153 HUP if (iflags.window_inited)
155 nh_uncompress(fq_save);
156 fd = open_savefile();
159 clear_nhwindow(WIN_MESSAGE);
161 There("seems to be an old save file.");
163 pline("
\91O
\82É
\83Z
\81[
\83u
\82µ
\82½
\83t
\83@
\83C
\83\8b\82ª
\82 \82è
\82Ü
\82·
\81D");
165 if (yn("Overwrite the old file?") == 'n') {
167 if (yn("
\8cÃ
\82¢
\83t
\83@
\83C
\83\8b\82É
\8fã
\8f\91\82«
\82µ
\82Ü
\82·
\82©
\81H") == 'n') {
168 nh_compress(fq_save);
174 HUP mark_synch(); /* flush any buffered screen output */
176 fd = create_savefile();
178 HUP pline("Cannot open save file.");
179 (void) delete_savefile(); /* ab@unido */
183 vision_recalc(2); /* shut down vision to prevent problems
184 in the event of an impossible() call */
186 /* undo date-dependent luck adjustments made at startup time */
187 if (flags.moonphase == FULL_MOON) /* ut-sally!fletcher */
188 change_luck(-1); /* and unido!ab */
191 if (iflags.window_inited)
192 HUP clear_nhwindow(WIN_MESSAGE);
198 if (strncmpi("X11", windowprocs.name, 3))
199 putstr(WIN_MAP, 0, "Saving:");
202 /* make sure there is enough disk space */
203 if (iflags.checkspace) {
206 savelev(fd, ledger_no(&u.uz), COUNT_SAVE);
207 savegamestate(fd, COUNT_SAVE);
208 needed = bytes_counted;
210 for (ltmp = 1; ltmp <= maxledgerno(); ltmp++)
211 if (ltmp != ledger_no(&u.uz) && level_info[ltmp].where)
212 needed += level_info[ltmp].size + (sizeof ltmp);
213 fds = freediskspace(fq_save);
217 There("is insufficient space on SAVE disk.");
218 pline("Require %ld bytes but only have %ld.", needed, fds);
222 (void) delete_savefile();
231 store_savefileinfo(fd);
232 store_plname_in_file(fd);
233 ustuck_id = (u.ustuck ? u.ustuck->m_id : 0);
234 usteed_id = (u.usteed ? u.usteed->m_id : 0);
235 savelev(fd, ledger_no(&u.uz), WRITE_SAVE | FREE_SAVE);
236 savegamestate(fd, WRITE_SAVE | FREE_SAVE);
238 /* While copying level files around, zero out u.uz to keep
239 * parts of the restore code from completely initializing all
240 * in-core data structures, since all we're doing is copying.
241 * This also avoids at least one nasty core dump.
244 u.uz.dnum = u.uz.dlevel = 0;
245 /* these pointers are no longer valid, and at least u.usteed
246 * may mislead place_monster() on other levels
248 u.ustuck = (struct monst *) 0;
249 u.usteed = (struct monst *) 0;
251 for (ltmp = (xchar) 1; ltmp <= maxledgerno(); ltmp++) {
252 if (ltmp == ledger_no(&uz_save))
254 if (!(level_info[ltmp].flags & LFILE_EXISTS))
257 curs(WIN_MAP, 1 + dotcnt++, dotrow);
258 if (dotcnt >= (COLNO - 1)) {
262 if (strncmpi("X11", windowprocs.name, 3)) {
263 putstr(WIN_MAP, 0, ".");
267 ofd = open_levelfile(ltmp, whynot);
271 (void) delete_savefile();
272 HUP Strcpy(killer.name, whynot);
276 minit(); /* ZEROCOMP */
277 getlev(ofd, hackpid, ltmp, FALSE);
279 bwrite(fd, (genericptr_t) <mp, sizeof ltmp); /* level number*/
280 savelev(fd, ltmp, WRITE_SAVE | FREE_SAVE); /* actual level*/
281 delete_levelfile(ltmp);
287 /* get rid of current level --jgm */
288 delete_levelfile(ledger_no(&u.uz));
290 nh_compress(fq_save);
291 /* this should probably come sooner... */
292 program_state.something_worth_saving = 0;
297 savegamestate(fd, mode)
298 register int fd, mode;
303 count_only = (mode & COUNT_SAVE);
305 uid = (unsigned long) getuid();
306 bwrite(fd, (genericptr_t) &uid, sizeof uid);
307 bwrite(fd, (genericptr_t) &context, sizeof(struct context_info));
308 bwrite(fd, (genericptr_t) &flags, sizeof(struct flag));
310 bwrite(fd, (genericptr_t) &sysflags, sizeof(struct sysflag));
312 urealtime.finish_time = getnow();
313 urealtime.realtime += (long) (urealtime.finish_time
314 - urealtime.start_timing);
315 bwrite(fd, (genericptr_t) &u, sizeof(struct you));
316 bwrite(fd, yyyymmddhhmmss(ubirthday), 14);
317 bwrite(fd, (genericptr_t) &urealtime.realtime, sizeof urealtime.realtime);
318 bwrite(fd, yyyymmddhhmmss(urealtime.start_timing), 14); /** Why? **/
319 /* this is the value to use for the next update of urealtime.realtime */
320 urealtime.start_timing = urealtime.finish_time;
321 save_killers(fd, mode);
323 /* must come before migrating_objs and migrating_mons are freed */
324 save_timers(fd, mode, RANGE_GLOBAL);
325 save_light_sources(fd, mode, RANGE_GLOBAL);
327 saveobjchn(fd, invent, mode);
329 /* prevent loss of ball & chain when swallowed */
330 uball->nobj = uchain;
331 uchain->nobj = (struct obj *) 0;
332 saveobjchn(fd, uball, mode);
334 saveobjchn(fd, (struct obj *) 0, mode);
337 saveobjchn(fd, migrating_objs, mode);
338 savemonchn(fd, migrating_mons, mode);
339 if (release_data(mode)) {
344 bwrite(fd, (genericptr_t) mvitals, sizeof(mvitals));
346 save_dungeon(fd, (boolean) !!perform_bwrite(mode),
347 (boolean) !!release_data(mode));
348 savelevchn(fd, mode);
349 bwrite(fd, (genericptr_t) &moves, sizeof moves);
350 bwrite(fd, (genericptr_t) &monstermoves, sizeof monstermoves);
351 bwrite(fd, (genericptr_t) &quest_status, sizeof(struct q_score));
352 bwrite(fd, (genericptr_t) spl_book,
353 sizeof(struct spell) * (MAXSPELL + 1));
355 save_oracles(fd, mode);
357 bwrite(fd, (genericptr_t) &ustuck_id, sizeof ustuck_id);
359 bwrite(fd, (genericptr_t) &usteed_id, sizeof usteed_id);
360 bwrite(fd, (genericptr_t) pl_character, sizeof pl_character);
361 bwrite(fd, (genericptr_t) pl_fruit, sizeof pl_fruit);
362 savefruitchn(fd, mode);
364 save_waterlevel(fd, mode);
365 save_msghistory(fd, mode);
370 tricked_fileremoved(fd, whynot)
376 pline("Probably someone removed it.");
377 Strcpy(killer.name, whynot);
389 static boolean havestate = TRUE;
392 /* When checkpointing is on, the full state needs to be written
393 * on each checkpoint. When checkpointing is off, only the pid
394 * needs to be in the level.0 file, so it does not need to be
395 * constantly rewritten. When checkpointing is turned off during
396 * a game, however, the file has to be rewritten once to truncate
397 * it and avoid restoring from outdated information.
399 * Restricting havestate to this routine means that an additional
400 * noop pid rewriting will take place on the first "checkpoint" after
401 * the game is started or restored, if checkpointing is off.
403 if (flags.ins_chkpt || havestate) {
404 /* save the rest of the current game state in the lock file,
405 * following the original int pid, the current level number,
406 * and the current savefile name, which should not be subject
407 * to any internal compression schemes since they must be
408 * readable by an external utility
410 fd = open_levelfile(0, whynot);
411 if (tricked_fileremoved(fd, whynot))
414 #pragma GCC diagnostic push
415 #pragma GCC diagnostic ignored "-Wunused-result"
416 (void) read(fd, (genericptr_t) &hpid, sizeof(hpid));
417 #pragma GCC diagnostic pop
418 if (hackpid != hpid) {
419 Sprintf(whynot, "Level #0 pid (%d) doesn't match ours (%d)!",
422 Strcpy(killer.name, whynot);
427 fd = create_levelfile(0, whynot);
430 Strcpy(killer.name, whynot);
434 #pragma GCC diagnostic push
435 #pragma GCC diagnostic ignored "-Wunused-result"
436 (void) write(fd, (genericptr_t) &hackpid, sizeof(hackpid));
437 #pragma GCC diagnostic pop
438 if (flags.ins_chkpt) {
439 int currlev = ledger_no(&u.uz);
441 #pragma GCC diagnostic push
442 #pragma GCC diagnostic ignored "-Wunused-result"
443 (void) write(fd, (genericptr_t) &currlev, sizeof(currlev));
444 #pragma GCC diagnostic pop
445 save_savefile_name(fd);
447 store_savefileinfo(fd);
448 store_plname_in_file(fd);
450 ustuck_id = (u.ustuck ? u.ustuck->m_id : 0);
451 usteed_id = (u.usteed ? u.usteed->m_id : 0);
452 savegamestate(fd, WRITE_SAVE);
456 havestate = flags.ins_chkpt;
462 savelev(fd, lev, mode)
467 if (mode & COUNT_SAVE) {
469 savelev0(fd, lev, COUNT_SAVE);
470 /* probably bytes_counted will be filled in again by an
471 * immediately following WRITE_SAVE anyway, but we'll
472 * leave it out of checkspace just in case */
473 if (iflags.checkspace) {
474 while (bytes_counted > freediskspace(levels))
475 if (!swapout_oldest())
479 if (mode & (WRITE_SAVE | FREE_SAVE)) {
481 savelev0(fd, lev, mode);
483 if (mode != FREE_SAVE) {
484 level_info[lev].where = ACTIVE;
485 level_info[lev].time = moves;
486 level_info[lev].size = bytes_counted;
492 savelev0(fd, lev, mode)
495 savelev(fd, lev, mode)
505 /* if we're tearing down the current level without saving anything
506 (which happens upon entrance to the endgame or after an aborted
507 restore attempt) then we don't want to do any actual I/O */
508 if (mode == FREE_SAVE)
511 /* purge any dead monsters (necessary if we're starting
512 a panic save rather than a normal one, or sometimes
513 when changing levels without taking time -- e.g.
514 create statue trap then immediately level teleport) */
515 if (iflags.purge_monsters)
519 panic("Save on bad file!"); /* impossible */
521 count_only = (mode & COUNT_SAVE);
523 if (lev >= 0 && lev <= maxledgerno())
524 level_info[lev].flags |= VISITED;
525 bwrite(fd, (genericptr_t) &hackpid, sizeof(hackpid));
529 bwrite(fd, (genericptr_t) &tlev, sizeof(tlev));
531 bwrite(fd, (genericptr_t) &lev, sizeof(lev));
533 savecemetery(fd, mode, &level.bonesinfo);
535 (boolean) ((sfsaveinfo.sfi1 & SFI1_RLECOMP) == SFI1_RLECOMP));
536 bwrite(fd, (genericptr_t) lastseentyp, sizeof(lastseentyp));
537 bwrite(fd, (genericptr_t) &monstermoves, sizeof(monstermoves));
538 bwrite(fd, (genericptr_t) &upstair, sizeof(stairway));
539 bwrite(fd, (genericptr_t) &dnstair, sizeof(stairway));
540 bwrite(fd, (genericptr_t) &upladder, sizeof(stairway));
541 bwrite(fd, (genericptr_t) &dnladder, sizeof(stairway));
542 bwrite(fd, (genericptr_t) &sstairs, sizeof(stairway));
543 bwrite(fd, (genericptr_t) &updest, sizeof(dest_area));
544 bwrite(fd, (genericptr_t) &dndest, sizeof(dest_area));
545 bwrite(fd, (genericptr_t) &level.flags, sizeof(level.flags));
546 bwrite(fd, (genericptr_t) doors, sizeof(doors));
547 save_rooms(fd); /* no dynamic memory to reclaim */
549 /* from here on out, saving also involves allocated memory cleanup */
551 /* this comes before the map, so need cleanup here if we skipped */
552 if (mode == FREE_SAVE)
553 savecemetery(fd, mode, &level.bonesinfo);
554 /* must be saved before mons, objs, and buried objs */
555 save_timers(fd, mode, RANGE_LEVEL);
556 save_light_sources(fd, mode, RANGE_LEVEL);
558 savemonchn(fd, fmon, mode);
559 save_worm(fd, mode); /* save worm information */
560 savetrapchn(fd, ftrap, mode);
561 saveobjchn(fd, fobj, mode);
562 saveobjchn(fd, level.buriedobjlist, mode);
563 saveobjchn(fd, billobjs, mode);
564 if (release_data(mode)) {
568 level.buriedobjlist = 0;
570 /* level.bonesinfo = 0; -- handled by savecemetery() */
572 save_engravings(fd, mode);
573 savedamage(fd, mode);
574 save_regions(fd, mode);
575 if (mode != FREE_SAVE)
580 savelevl(fd, rlecomp)
585 struct rm *prm, *rgrm;
590 /* perform run-length encoding of rm structs */
592 rgrm = &levl[0][0]; /* start matching at first rm */
595 for (y = 0; y < ROWNO; y++) {
596 for (x = 0; x < COLNO; x++) {
598 if (prm->glyph == rgrm->glyph && prm->typ == rgrm->typ
599 && prm->seenv == rgrm->seenv
600 && prm->horizontal == rgrm->horizontal
601 && prm->flags == rgrm->flags && prm->lit == rgrm->lit
602 && prm->waslit == rgrm->waslit
603 && prm->roomno == rgrm->roomno && prm->edge == rgrm->edge
604 && prm->candig == rgrm->candig) {
607 match = 254; /* undo this match */
611 /* the run has been broken,
612 * write out run-length encoding */
614 bwrite(fd, (genericptr_t) &match, sizeof(uchar));
615 bwrite(fd, (genericptr_t) rgrm, sizeof(struct rm));
616 /* start encoding again. we have at least 1 rm
617 * in the next run, viz. this one. */
624 bwrite(fd, (genericptr_t) &match, sizeof(uchar));
625 bwrite(fd, (genericptr_t) rgrm, sizeof(struct rm));
631 #endif /* ?RLECOMP */
632 bwrite(fd, (genericptr_t) levl, sizeof levl);
640 (*saveprocs.save_bufon)(fd);
649 (*saveprocs.save_bufoff)(fd);
653 /* flush run and buffer */
658 (*saveprocs.save_bflush)(fd);
666 register unsigned num;
668 (*saveprocs.save_bwrite)(fd, loc, num);
676 (*saveprocs.save_bclose)(fd);
680 static int bw_fd = -1;
681 static FILE *bw_FILE = 0;
682 static boolean buffering = FALSE;
691 panic("double buffering unexpected");
693 if ((bw_FILE = fdopen(fd, "w")) == 0)
694 panic("buffering of file %d failed", fd);
714 if (fflush(bw_FILE) == EOF)
715 panic("flush of savefile failed!");
722 def_bwrite(fd, loc, num)
724 register genericptr_t loc;
725 register unsigned num;
730 bytes_counted += num;
738 panic("unbuffered write to fd %d (!= %d)", fd, bw_fd);
740 failed = (fwrite(loc, (int) num, 1, bw_FILE) != 1);
744 /* lint wants 3rd arg of write to be an int; lint -p an unsigned */
745 #if defined(BSD) || defined(ULTRIX) || defined(WIN32) || defined(_MSC_VER)
746 failed = ((long) write(fd, loc, (int) num) != (long) num);
747 #else /* e.g. SYSV, __TURBOC__ */
748 failed = ((long) write(fd, loc, num) != (long) num);
753 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
754 if (program_state.done_hup)
755 terminate(EXIT_FAILURE);
758 panic("cannot write %u bytes to file #%d", num, fd);
769 (void) fclose(bw_FILE);
779 /* The runs of zero-run compression are flushed after the game state or a
780 * level is written out. This adds a couple bytes to a save file, where
781 * the runs could be mashed together, but it allows gluing together game
782 * state and level files to form a save file, and it means the flushing
783 * does not need to be specifically called for every other time a level
784 * file is written out.
787 #define RLESC '\0' /* Leading character for run of LRESC's */
788 #define flushoutrun(ln) (zerocomp_bputc(RLESC), zerocomp_bputc(ln), ln = -1)
790 #ifndef ZEROCOMP_BUFSIZ
791 #define ZEROCOMP_BUFSIZ BUFSZ
793 static NEARDATA unsigned char outbuf[ZEROCOMP_BUFSIZ];
794 static NEARDATA unsigned short outbufp = 0;
795 static NEARDATA short outrunlength = -1;
796 static NEARDATA int bwritefd;
797 static NEARDATA boolean compressing = FALSE;
801 HUP printf("outbufp %d outrunlength %d\n", outbufp,outrunlength);
813 if (outbufp >= sizeof outbuf) {
814 (void) write(bwritefd, outbuf, sizeof outbuf);
817 outbuf[outbufp++] = (unsigned char) c;
836 panic("closing file with buffered data still unwritten");
843 /* flush run and buffer */
849 if (outrunlength >= 0) { /* flush run */
850 flushoutrun(outrunlength);
858 if (write(fd, outbuf, outbufp) != outbufp) {
859 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
860 if (program_state.done_hup)
861 terminate(EXIT_FAILURE);
864 zerocomp_bclose(fd); /* panic (outbufp != 0) */
871 zerocomp_bwrite(fd, loc, num)
874 register unsigned num;
876 register unsigned char *bp = (unsigned char *) loc;
880 bytes_counted += num;
884 if ((unsigned) write(fd, loc, num) != num) {
885 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
886 if (program_state.done_hup)
887 terminate(EXIT_FAILURE);
890 panic("cannot write %u bytes to file #%d", num, fd);
894 for (; num; num--, bp++) {
895 if (*bp == RLESC) { /* One more char in run */
896 if (++outrunlength == 0xFF) {
897 flushoutrun(outrunlength);
899 } else { /* end of run */
900 if (outrunlength >= 0) { /* flush run */
901 flushoutrun(outrunlength);
917 #endif /* ZEROCOMP */
921 register int fd, mode;
923 s_level *tmplev, *tmplev2;
926 for (tmplev = sp_levchn; tmplev; tmplev = tmplev->next)
928 if (perform_bwrite(mode))
929 bwrite(fd, (genericptr_t) &cnt, sizeof(int));
931 for (tmplev = sp_levchn; tmplev; tmplev = tmplev2) {
932 tmplev2 = tmplev->next;
933 if (perform_bwrite(mode))
934 bwrite(fd, (genericptr_t) tmplev, sizeof(s_level));
935 if (release_data(mode))
936 free((genericptr_t) tmplev);
938 if (release_data(mode))
942 /* used when saving a level and also when saving dungeon overview data */
944 savecemetery(fd, mode, cemeteryaddr)
947 struct cemetery **cemeteryaddr;
949 struct cemetery *thisbones, *nextbones;
952 flag = *cemeteryaddr ? 0 : -1;
953 if (perform_bwrite(mode))
954 bwrite(fd, (genericptr_t) &flag, sizeof flag);
955 nextbones = *cemeteryaddr;
956 while ((thisbones = nextbones) != 0) {
957 nextbones = thisbones->next;
958 if (perform_bwrite(mode))
959 bwrite(fd, (genericptr_t) thisbones, sizeof *thisbones);
960 if (release_data(mode))
961 free((genericptr_t) thisbones);
963 if (release_data(mode))
969 register int fd, mode;
971 register struct damage *damageptr, *tmp_dam;
974 damageptr = level.damagelist;
975 for (tmp_dam = damageptr; tmp_dam; tmp_dam = tmp_dam->next)
977 if (perform_bwrite(mode))
978 bwrite(fd, (genericptr_t) &xl, sizeof(xl));
981 if (perform_bwrite(mode))
982 bwrite(fd, (genericptr_t) damageptr, sizeof(*damageptr));
984 damageptr = damageptr->next;
985 if (release_data(mode))
986 free((genericptr_t) tmp_dam);
988 if (release_data(mode))
989 level.damagelist = 0;
997 int buflen, zerobuf = 0;
999 buflen = sizeof(struct obj);
1000 bwrite(fd, (genericptr_t) &buflen, sizeof(int));
1001 bwrite(fd, (genericptr_t) otmp, buflen);
1004 buflen = strlen(ONAME(otmp)) + 1;
1007 bwrite(fd, (genericptr_t) &buflen, sizeof buflen);
1009 bwrite(fd, (genericptr_t) ONAME(otmp), buflen);
1011 /* defer to savemon() for this one */
1013 savemon(fd, OMONST(otmp));
1015 bwrite(fd, (genericptr_t) &zerobuf, sizeof zerobuf);
1018 buflen = sizeof(unsigned);
1021 bwrite(fd, (genericptr_t) &buflen, sizeof buflen);
1023 bwrite(fd, (genericptr_t) OMID(otmp), buflen);
1026 buflen = sizeof(long);
1029 bwrite(fd, (genericptr_t) &buflen, sizeof buflen);
1031 bwrite(fd, (genericptr_t) OLONG(otmp), buflen);
1034 buflen = strlen(OMAILCMD(otmp)) + 1;
1037 bwrite(fd, (genericptr_t) &buflen, sizeof buflen);
1039 bwrite(fd, (genericptr_t) OMAILCMD(otmp), buflen);
1044 saveobjchn(fd, otmp, mode)
1045 register int fd, mode;
1046 register struct obj *otmp;
1048 register struct obj *otmp2;
1053 if (perform_bwrite(mode)) {
1056 if (Has_contents(otmp))
1057 saveobjchn(fd, otmp->cobj, mode);
1058 if (release_data(mode)) {
1059 /* if (otmp->oclass == FOOD_CLASS)
1060 * food_disappears(otmp);
1063 * If these are on the floor, the discarding could
1064 * be because of a game save, or we could just be changing levels.
1065 * Always invalidate the pointer, but ensure that we have
1066 * the o_id in order to restore the pointer on reload.
1068 if (otmp == context.victual.piece) {
1069 /* Store the o_id of the victual if mismatched */
1070 if (context.victual.o_id != otmp->o_id)
1071 context.victual.o_id = otmp->o_id;
1072 /* invalidate the pointer; on reload it will get restored */
1073 context.victual.piece = (struct obj *) 0;
1075 if (otmp == context.tin.tin) {
1076 /* Store the o_id of your tin */
1077 if (context.tin.o_id != otmp->o_id)
1078 context.tin.o_id = otmp->o_id;
1079 /* invalidate the pointer; on reload it will get restored */
1080 context.tin.tin = (struct obj *) 0;
1082 /* if (otmp->oclass == SPBOOK_CLASS)
1083 * book_disappears(otmp);
1085 if (otmp == context.spbook.book) {
1086 /* Store the o_id of your spellbook */
1087 if (context.spbook.o_id != otmp->o_id)
1088 context.spbook.o_id = otmp->o_id;
1089 /* invalidate the pointer; on reload it will get restored */
1090 context.spbook.book = (struct obj *) 0;
1092 otmp->where = OBJ_FREE; /* set to free so dealloc will work */
1093 otmp->nobj = NULL; /* nobj saved into otmp2 */
1094 otmp->cobj = NULL; /* contents handled above */
1095 otmp->timed = 0; /* not timed any more */
1096 otmp->lamplit = 0; /* caller handled lights */
1101 if (perform_bwrite(mode))
1102 bwrite(fd, (genericptr_t) &minusone, sizeof(int));
1112 buflen = sizeof(struct monst);
1113 bwrite(fd, (genericptr_t) &buflen, sizeof(int));
1114 bwrite(fd, (genericptr_t) mtmp, buflen);
1117 buflen = strlen(MNAME(mtmp)) + 1;
1120 bwrite(fd, (genericptr_t) &buflen, sizeof buflen);
1122 bwrite(fd, (genericptr_t) MNAME(mtmp), buflen);
1125 buflen = sizeof(struct egd);
1128 bwrite(fd, (genericptr_t) &buflen, sizeof(int));
1130 bwrite(fd, (genericptr_t) EGD(mtmp), buflen);
1133 buflen = sizeof(struct epri);
1136 bwrite(fd, (genericptr_t) &buflen, sizeof(int));
1138 bwrite(fd, (genericptr_t) EPRI(mtmp), buflen);
1141 buflen = sizeof(struct eshk);
1144 bwrite(fd, (genericptr_t) &buflen, sizeof(int));
1146 bwrite(fd, (genericptr_t) ESHK(mtmp), buflen);
1149 buflen = sizeof(struct emin);
1152 bwrite(fd, (genericptr_t) &buflen, sizeof(int));
1154 bwrite(fd, (genericptr_t) EMIN(mtmp), buflen);
1157 buflen = sizeof(struct edog);
1160 bwrite(fd, (genericptr_t) &buflen, sizeof(int));
1162 bwrite(fd, (genericptr_t) EDOG(mtmp), buflen);
1164 /* mcorpsenm is inline int rather than pointer to something,
1165 so doesn't need to be preceded by a length field */
1166 bwrite(fd, (genericptr_t) &MCORPSENM(mtmp), sizeof MCORPSENM(mtmp));
1171 savemonchn(fd, mtmp, mode)
1172 register int fd, mode;
1173 register struct monst *mtmp;
1175 register struct monst *mtmp2;
1180 if (perform_bwrite(mode)) {
1181 mtmp->mnum = monsndx(mtmp->data);
1183 forget_temple_entry(mtmp); /* EPRI() */
1187 saveobjchn(fd, mtmp->minvent, mode);
1188 if (release_data(mode)) {
1189 if (mtmp == context.polearm.hitmon) {
1190 context.polearm.m_id = mtmp->m_id;
1191 context.polearm.hitmon = NULL;
1193 mtmp->nmon = NULL; /* nmon saved into mtmp2 */
1194 dealloc_monst(mtmp);
1198 if (perform_bwrite(mode))
1199 bwrite(fd, (genericptr_t) &minusone, sizeof(int));
1203 savetrapchn(fd, trap, mode)
1204 register int fd, mode;
1205 register struct trap *trap;
1207 register struct trap *trap2;
1210 trap2 = trap->ntrap;
1211 if (perform_bwrite(mode))
1212 bwrite(fd, (genericptr_t) trap, sizeof(struct trap));
1213 if (release_data(mode))
1217 if (perform_bwrite(mode))
1218 bwrite(fd, (genericptr_t) nulls, sizeof(struct trap));
1221 /* save all the fruit names and ID's; this is used only in saving whole games
1222 * (not levels) and in saving bones levels. When saving a bones level,
1223 * we only want to save the fruits which exist on the bones level; the bones
1224 * level routine marks nonexistent fruits by making the fid negative.
1227 savefruitchn(fd, mode)
1228 register int fd, mode;
1230 register struct fruit *f2, *f1;
1235 if (f1->fid >= 0 && perform_bwrite(mode))
1236 bwrite(fd, (genericptr_t) f1, sizeof(struct fruit));
1237 if (release_data(mode))
1241 if (perform_bwrite(mode))
1242 bwrite(fd, (genericptr_t) nulls, sizeof(struct fruit));
1243 if (release_data(mode))
1248 store_plname_in_file(fd)
1251 int plsiztmp = PL_NSIZ;
1253 /* bwrite() before bufon() uses plain write() */
1254 bwrite(fd, (genericptr_t) &plsiztmp, sizeof(plsiztmp));
1255 bwrite(fd, (genericptr_t) plname, plsiztmp);
1261 save_msghistory(fd, mode)
1265 int msgcount = 0, msglen;
1267 boolean init = TRUE;
1269 if (perform_bwrite(mode)) {
1270 /* ask window port for each message in sequence */
1271 while ((msg = getmsghistory(init)) != 0) {
1273 msglen = strlen(msg);
1274 /* sanity: truncate if necessary (shouldn't happen);
1275 no need to modify msg[] since terminator isn't written */
1276 if (msglen > BUFSZ - 1)
1278 bwrite(fd, (genericptr_t) &msglen, sizeof(msglen));
1279 bwrite(fd, (genericptr_t) msg, msglen);
1282 bwrite(fd, (genericptr_t) &minusone, sizeof(int));
1284 debugpline1("Stored %d messages into savefile.", msgcount);
1285 /* note: we don't attempt to handle release_data() here */
1289 store_savefileinfo(fd)
1292 /* sfcap (decl.c) describes the savefile feature capabilities
1293 * that are supported by this port/platform build.
1295 * sfsaveinfo (decl.c) describes the savefile info that actually
1296 * gets written into the savefile, and is used to determine the
1297 * save file being written.
1299 * sfrestinfo (decl.c) describes the savefile info that is
1300 * being used to read the information from an existing savefile.
1305 /* bwrite() before bufon() uses plain write() */
1306 bwrite(fd, (genericptr_t) &sfsaveinfo, (unsigned) (sizeof sfsaveinfo));
1312 set_savepref(suitename)
1313 const char *suitename;
1315 if (!strcmpi(suitename, "externalcomp")) {
1316 saveprocs.name = "externalcomp";
1317 saveprocs.save_bufon = def_bufon;
1318 saveprocs.save_bufoff = def_bufoff;
1319 saveprocs.save_bflush = def_bflush;
1320 saveprocs.save_bwrite = def_bwrite;
1321 saveprocs.save_bclose = def_bclose;
1322 sfsaveinfo.sfi1 |= SFI1_EXTERNALCOMP;
1323 sfsaveinfo.sfi1 &= ~SFI1_ZEROCOMP;
1325 if (!strcmpi(suitename, "!rlecomp")) {
1326 sfsaveinfo.sfi1 &= ~SFI1_RLECOMP;
1329 if (!strcmpi(suitename, "zerocomp")) {
1330 saveprocs.name = "zerocomp";
1331 saveprocs.save_bufon = zerocomp_bufon;
1332 saveprocs.save_bufoff = zerocomp_bufoff;
1333 saveprocs.save_bflush = zerocomp_bflush;
1334 saveprocs.save_bwrite = zerocomp_bwrite;
1335 saveprocs.save_bclose = zerocomp_bclose;
1336 sfsaveinfo.sfi1 |= SFI1_ZEROCOMP;
1337 sfsaveinfo.sfi1 &= ~SFI1_EXTERNALCOMP;
1341 if (!strcmpi(suitename, "rlecomp")) {
1342 sfsaveinfo.sfi1 |= SFI1_RLECOMP;
1347 /* also called by prscore(); this probably belongs in dungeon.c... */
1351 #ifdef FREE_ALL_MEMORY
1352 savelevchn(0, FREE_SAVE);
1353 save_dungeon(0, FALSE, TRUE);
1362 free_menu_coloring();
1363 free_invbuf(); /* let_to_name (invent.c) */
1364 free_youbuf(); /* You_buf,&c (pline.c) */
1366 tmp_at(DISP_FREEMEM, 0); /* temporary display effects */
1367 #ifdef FREE_ALL_MEMORY
1368 #define freeobjchn(X) (saveobjchn(0, X, FREE_SAVE), X = 0)
1369 #define freemonchn(X) (savemonchn(0, X, FREE_SAVE), X = 0)
1370 #define freetrapchn(X) (savetrapchn(0, X, FREE_SAVE), X = 0)
1371 #define freefruitchn() savefruitchn(0, FREE_SAVE)
1372 #define freenames() savenames(0, FREE_SAVE)
1373 #define free_killers() save_killers(0, FREE_SAVE)
1374 #define free_oracles() save_oracles(0, FREE_SAVE)
1375 #define free_waterlevel() save_waterlevel(0, FREE_SAVE)
1376 #define free_worm() save_worm(0, FREE_SAVE)
1377 #define free_timers(R) save_timers(0, FREE_SAVE, R)
1378 #define free_light_sources(R) save_light_sources(0, FREE_SAVE, R);
1379 #define free_engravings() save_engravings(0, FREE_SAVE)
1380 #define freedamage() savedamage(0, FREE_SAVE)
1381 #define free_animals() mon_animal_list(FALSE)
1383 /* move-specific data */
1384 dmonsfree(); /* release dead monsters */
1386 /* level-specific data */
1387 free_timers(RANGE_LEVEL);
1388 free_light_sources(RANGE_LEVEL);
1391 free_worm(); /* release worm segment information */
1394 freeobjchn(level.buriedobjlist);
1395 freeobjchn(billobjs);
1399 /* game-state data */
1401 free_timers(RANGE_GLOBAL);
1402 free_light_sources(RANGE_GLOBAL);
1404 freeobjchn(migrating_objs);
1405 freemonchn(migrating_mons);
1406 freemonchn(mydogs); /* ascension or dungeon escape */
1407 /* freelevchn(); -- [folded into free_dungeons()] */
1415 /* some pointers in iflags */
1416 if (iflags.wc_font_map)
1417 free(iflags.wc_font_map);
1418 if (iflags.wc_font_message)
1419 free(iflags.wc_font_message);
1420 if (iflags.wc_font_text)
1421 free(iflags.wc_font_text);
1422 if (iflags.wc_font_menu)
1423 free(iflags.wc_font_menu);
1424 if (iflags.wc_font_status)
1425 free(iflags.wc_font_status);
1426 if (iflags.wc_tile_file)
1427 free(iflags.wc_tile_file);
1428 free_autopickup_exceptions();
1431 /* free_pickinv_cache(); -- now done from really_done()... */
1433 #endif /* FREE_ALL_MEMORY */
1434 #ifdef STATUS_VIA_WINDOWPORT
1438 /* last, because it frees data that might be used by panic() to provide
1439 feedback to the user; conceivably other freeing might trigger panic */
1440 sysopt_release(); /* SYSCF strings */
1449 char to[PATHLEN], from[PATHLEN];
1451 Sprintf(from, "%s%s", permbones, alllevels);
1452 Sprintf(to, "%s%s", levels, alllevels);
1453 set_levelfile_name(from, lev);
1454 set_levelfile_name(to, lev);
1455 if (iflags.checkspace) {
1456 while (level_info[lev].size > freediskspace(to))
1457 if (!swapout_oldest())
1461 pline("Swapping in `%s'.", from);
1465 (void) unlink(from);
1466 level_info[lev].where = ACTIVE;
1473 char to[PATHLEN], from[PATHLEN];
1479 for (i = 1, oldtime = 0, oldest = 0; i <= maxledgerno(); i++)
1480 if (level_info[i].where == ACTIVE
1481 && (!oldtime || level_info[i].time < oldtime)) {
1483 oldtime = level_info[i].time;
1487 Sprintf(from, "%s%s", levels, alllevels);
1488 Sprintf(to, "%s%s", permbones, alllevels);
1489 set_levelfile_name(from, oldest);
1490 set_levelfile_name(to, oldest);
1492 pline("Swapping out `%s'.", from);
1496 (void) unlink(from);
1497 level_info[oldest].where = SWAPPED;
1506 if (_copyfile(from, to))
1507 panic("Can't copy %s to %s", from, to);
1509 char buf[BUFSIZ]; /* this is system interaction, therefore
1510 * BUFSIZ instead of NetHack's BUFSZ */
1511 int nfrom, nto, fdfrom, fdto;
1513 if ((fdfrom = open(from, O_RDONLY | O_BINARY, FCMASK)) < 0)
1514 panic("Can't copy from %s !?", from);
1515 if ((fdto = open(to, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, FCMASK)) < 0)
1516 panic("Can't copy to %s", to);
1518 nfrom = read(fdfrom, buf, BUFSIZ);
1519 nto = write(fdto, buf, nfrom);
1521 panic("Copyfile failed!");
1522 } while (nfrom == BUFSIZ);
1523 (void) nhclose(fdfrom);
1524 (void) nhclose(fdto);
1528 /* see comment in bones.c */
1536 #endif /* MFLOPPY */