1 /* NetHack 3.6 save.c $NHDT-Date: 1489192905 2017/03/11 00:41:45 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.101 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Michael Allison, 2009. */
4 /* NetHack may be freely redistributed. See license for details. */
6 /* JNetHack Copyright */
7 /* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000 */
8 /* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2018 */
9 /* JNetHack may be freely redistributed. See license for details. */
17 #if !defined(LSC) && !defined(O_WRONLY) && !defined(AZTEC_C)
23 static int count_only;
27 int dotcnt, dotrow; /* also used in restore */
30 STATIC_DCL void FDECL(savelevchn, (int, int));
31 STATIC_DCL void FDECL(savedamage, (int, int));
32 STATIC_DCL void FDECL(saveobj, (int, struct obj *));
33 STATIC_DCL void FDECL(saveobjchn, (int, struct obj *, int));
34 STATIC_DCL void FDECL(savemon, (int, struct monst *));
35 STATIC_DCL void FDECL(savemonchn, (int, struct monst *, int));
36 STATIC_DCL void FDECL(savetrapchn, (int, struct trap *, int));
37 STATIC_DCL void FDECL(savegamestate, (int, int));
38 STATIC_OVL void FDECL(save_msghistory, (int, int));
40 STATIC_DCL void FDECL(savelev0, (int, XCHAR_P, int));
41 STATIC_DCL boolean NDECL(swapout_oldest);
42 STATIC_DCL void FDECL(copyfile, (char *, char *));
44 STATIC_DCL void FDECL(savelevl, (int fd, BOOLEAN_P));
45 STATIC_DCL void FDECL(def_bufon, (int));
46 STATIC_DCL void FDECL(def_bufoff, (int));
47 STATIC_DCL void FDECL(def_bflush, (int));
48 STATIC_DCL void FDECL(def_bwrite, (int, genericptr_t, unsigned int));
50 STATIC_DCL void FDECL(zerocomp_bufon, (int));
51 STATIC_DCL void FDECL(zerocomp_bufoff, (int));
52 STATIC_DCL void FDECL(zerocomp_bflush, (int));
53 STATIC_DCL void FDECL(zerocomp_bwrite, (int, genericptr_t, unsigned int));
54 STATIC_DCL void FDECL(zerocomp_bputc, (int));
57 static struct save_procs {
59 void FDECL((*save_bufon), (int));
60 void FDECL((*save_bufoff), (int));
61 void FDECL((*save_bflush), (int));
62 void FDECL((*save_bwrite), (int, genericptr_t, unsigned int));
63 void FDECL((*save_bclose), (int));
65 #if !defined(ZEROCOMP) || (defined(COMPRESS) || defined(ZLIB_COMP))
66 "externalcomp", def_bufon, def_bufoff, def_bflush, def_bwrite, def_bclose,
68 "zerocomp", zerocomp_bufon, zerocomp_bufoff,
69 zerocomp_bflush, zerocomp_bwrite, zerocomp_bclose,
73 #if defined(UNIX) || defined(VMS) || defined(__EMX__) || defined(WIN32)
74 #define HUP if (!program_state.done_hup)
79 /* need to preserve these during save to avoid accessing freed memory */
80 static unsigned ustuck_id = 0, usteed_id = 0;
85 clear_nhwindow(WIN_MESSAGE);
87 if (yn("Really save?") == 'n') {
89 if(yn("
\96{
\93\96\82É
\95Û
\91¶
\82·
\82é
\81H") == 'n') {
90 clear_nhwindow(WIN_MESSAGE);
94 clear_nhwindow(WIN_MESSAGE);
98 pline("
\95Û
\91¶
\92\86\81D
\81D
\81D");
99 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
100 program_state.done_hup = 0;
103 u.uhp = -1; /* universal game's over indicator */
104 /* make sure they see the Saving message */
105 display_nhwindow(WIN_MESSAGE, TRUE);
107 exit_nhwindows("Be seeing you...");
109 exit_nhwindows("
\82Ü
\82½
\89ï
\82¢
\82Ü
\82µ
\82å
\82¤
\81D
\81D
\81D");
110 nh_terminate(EXIT_SUCCESS);
117 /* returns 1 if save successful */
122 register int fd, ofd;
127 /* we may get here via hangup signal, in which case we want to fix up
128 a few of things before saving so that they won't be restored in
129 an improper state; these will be no-ops for normal save sequence */
131 if (iflags.save_uswallow)
132 u.uswallow = 1, iflags.save_uswallow = 0;
133 if (iflags.save_uinwater)
134 u.uinwater = 1, iflags.save_uinwater = 0;
135 if (iflags.save_uburied)
136 u.uburied = 1, iflags.save_uburied = 0;
138 if (!program_state.something_worth_saving || !SAVEF[0])
140 fq_save = fqname(SAVEF, SAVEPREFIX, 1); /* level files take 0 */
142 #if defined(UNIX) || defined(VMS)
143 sethanguphandler((void FDECL((*), (int) )) SIG_IGN);
146 (void) signal(SIGINT, SIG_IGN);
149 #if defined(MICRO) && defined(MFLOPPY)
150 if (!saveDiskPrompt(0))
154 HUP if (iflags.window_inited)
156 nh_uncompress(fq_save);
157 fd = open_savefile();
160 clear_nhwindow(WIN_MESSAGE);
162 There("seems to be an old save file.");
164 pline("
\91O
\82É
\83Z
\81[
\83u
\82µ
\82½
\83t
\83@
\83C
\83\8b\82ª
\82 \82è
\82Ü
\82·
\81D");
166 if (yn("Overwrite the old file?") == 'n') {
168 if (yn("
\8cÃ
\82¢
\83t
\83@
\83C
\83\8b\82É
\8fã
\8f\91\82«
\82µ
\82Ü
\82·
\82©
\81H") == 'n') {
169 nh_compress(fq_save);
175 HUP mark_synch(); /* flush any buffered screen output */
177 fd = create_savefile();
179 HUP pline("Cannot open save file.");
180 (void) delete_savefile(); /* ab@unido */
184 vision_recalc(2); /* shut down vision to prevent problems
185 in the event of an impossible() call */
187 /* undo date-dependent luck adjustments made at startup time */
188 if (flags.moonphase == FULL_MOON) /* ut-sally!fletcher */
189 change_luck(-1); /* and unido!ab */
192 if (iflags.window_inited)
193 HUP clear_nhwindow(WIN_MESSAGE);
199 if (strncmpi("X11", windowprocs.name, 3))
200 putstr(WIN_MAP, 0, "Saving:");
203 /* make sure there is enough disk space */
204 if (iflags.checkspace) {
207 savelev(fd, ledger_no(&u.uz), COUNT_SAVE);
208 savegamestate(fd, COUNT_SAVE);
209 needed = bytes_counted;
211 for (ltmp = 1; ltmp <= maxledgerno(); ltmp++)
212 if (ltmp != ledger_no(&u.uz) && level_info[ltmp].where)
213 needed += level_info[ltmp].size + (sizeof ltmp);
214 fds = freediskspace(fq_save);
218 There("is insufficient space on SAVE disk.");
219 pline("Require %ld bytes but only have %ld.", needed, fds);
223 (void) delete_savefile();
232 store_savefileinfo(fd);
233 store_plname_in_file(fd);
234 ustuck_id = (u.ustuck ? u.ustuck->m_id : 0);
235 usteed_id = (u.usteed ? u.usteed->m_id : 0);
236 savelev(fd, ledger_no(&u.uz), WRITE_SAVE | FREE_SAVE);
237 savegamestate(fd, WRITE_SAVE | FREE_SAVE);
239 /* While copying level files around, zero out u.uz to keep
240 * parts of the restore code from completely initializing all
241 * in-core data structures, since all we're doing is copying.
242 * This also avoids at least one nasty core dump.
245 u.uz.dnum = u.uz.dlevel = 0;
246 /* these pointers are no longer valid, and at least u.usteed
247 * may mislead place_monster() on other levels
249 u.ustuck = (struct monst *) 0;
250 u.usteed = (struct monst *) 0;
252 for (ltmp = (xchar) 1; ltmp <= maxledgerno(); ltmp++) {
253 if (ltmp == ledger_no(&uz_save))
255 if (!(level_info[ltmp].flags & LFILE_EXISTS))
258 curs(WIN_MAP, 1 + dotcnt++, dotrow);
259 if (dotcnt >= (COLNO - 1)) {
263 if (strncmpi("X11", windowprocs.name, 3)) {
264 putstr(WIN_MAP, 0, ".");
268 ofd = open_levelfile(ltmp, whynot);
272 (void) delete_savefile();
273 HUP Strcpy(killer.name, whynot);
277 minit(); /* ZEROCOMP */
278 getlev(ofd, hackpid, ltmp, FALSE);
280 bwrite(fd, (genericptr_t) <mp, sizeof ltmp); /* level number*/
281 savelev(fd, ltmp, WRITE_SAVE | FREE_SAVE); /* actual level*/
282 delete_levelfile(ltmp);
288 /* get rid of current level --jgm */
289 delete_levelfile(ledger_no(&u.uz));
291 nh_compress(fq_save);
292 /* this should probably come sooner... */
293 program_state.something_worth_saving = 0;
298 savegamestate(fd, mode)
299 register int fd, mode;
304 count_only = (mode & COUNT_SAVE);
306 uid = (unsigned long) getuid();
307 bwrite(fd, (genericptr_t) &uid, sizeof uid);
308 bwrite(fd, (genericptr_t) &context, sizeof(struct context_info));
309 bwrite(fd, (genericptr_t) &flags, sizeof(struct flag));
311 bwrite(fd, (genericptr_t) &sysflags, sizeof(struct sysflag));
313 urealtime.finish_time = getnow();
314 urealtime.realtime += (long) (urealtime.finish_time
315 - urealtime.start_timing);
316 bwrite(fd, (genericptr_t) &u, sizeof(struct you));
317 bwrite(fd, yyyymmddhhmmss(ubirthday), 14);
318 bwrite(fd, (genericptr_t) &urealtime.realtime, sizeof urealtime.realtime);
319 bwrite(fd, yyyymmddhhmmss(urealtime.start_timing), 14); /** Why? **/
320 /* this is the value to use for the next update of urealtime.realtime */
321 urealtime.start_timing = urealtime.finish_time;
322 save_killers(fd, mode);
324 /* must come before migrating_objs and migrating_mons are freed */
325 save_timers(fd, mode, RANGE_GLOBAL);
326 save_light_sources(fd, mode, RANGE_GLOBAL);
328 saveobjchn(fd, invent, mode);
330 /* prevent loss of ball & chain when swallowed */
331 uball->nobj = uchain;
332 uchain->nobj = (struct obj *) 0;
333 saveobjchn(fd, uball, mode);
335 saveobjchn(fd, (struct obj *) 0, mode);
338 saveobjchn(fd, migrating_objs, mode);
339 savemonchn(fd, migrating_mons, mode);
340 if (release_data(mode)) {
345 bwrite(fd, (genericptr_t) mvitals, sizeof(mvitals));
347 save_dungeon(fd, (boolean) !!perform_bwrite(mode),
348 (boolean) !!release_data(mode));
349 savelevchn(fd, mode);
350 bwrite(fd, (genericptr_t) &moves, sizeof moves);
351 bwrite(fd, (genericptr_t) &monstermoves, sizeof monstermoves);
352 bwrite(fd, (genericptr_t) &quest_status, sizeof(struct q_score));
353 bwrite(fd, (genericptr_t) spl_book,
354 sizeof(struct spell) * (MAXSPELL + 1));
356 save_oracles(fd, mode);
358 bwrite(fd, (genericptr_t) &ustuck_id, sizeof ustuck_id);
360 bwrite(fd, (genericptr_t) &usteed_id, sizeof usteed_id);
361 bwrite(fd, (genericptr_t) pl_character, sizeof pl_character);
362 bwrite(fd, (genericptr_t) pl_fruit, sizeof pl_fruit);
363 savefruitchn(fd, mode);
365 save_waterlevel(fd, mode);
366 save_msghistory(fd, mode);
371 tricked_fileremoved(fd, whynot)
377 pline("Probably someone removed it.");
378 Strcpy(killer.name, whynot);
390 static boolean havestate = TRUE;
393 /* When checkpointing is on, the full state needs to be written
394 * on each checkpoint. When checkpointing is off, only the pid
395 * needs to be in the level.0 file, so it does not need to be
396 * constantly rewritten. When checkpointing is turned off during
397 * a game, however, the file has to be rewritten once to truncate
398 * it and avoid restoring from outdated information.
400 * Restricting havestate to this routine means that an additional
401 * noop pid rewriting will take place on the first "checkpoint" after
402 * the game is started or restored, if checkpointing is off.
404 if (flags.ins_chkpt || havestate) {
405 /* save the rest of the current game state in the lock file,
406 * following the original int pid, the current level number,
407 * and the current savefile name, which should not be subject
408 * to any internal compression schemes since they must be
409 * readable by an external utility
411 fd = open_levelfile(0, whynot);
412 if (tricked_fileremoved(fd, whynot))
415 #pragma GCC diagnostic push
416 #pragma GCC diagnostic ignored "-Wunused-result"
417 (void) read(fd, (genericptr_t) &hpid, sizeof(hpid));
418 #pragma GCC diagnostic pop
419 if (hackpid != hpid) {
420 Sprintf(whynot, "Level #0 pid (%d) doesn't match ours (%d)!",
423 Strcpy(killer.name, whynot);
428 fd = create_levelfile(0, whynot);
431 Strcpy(killer.name, whynot);
435 #pragma GCC diagnostic push
436 #pragma GCC diagnostic ignored "-Wunused-result"
437 (void) write(fd, (genericptr_t) &hackpid, sizeof(hackpid));
438 #pragma GCC diagnostic pop
439 if (flags.ins_chkpt) {
440 int currlev = ledger_no(&u.uz);
442 #pragma GCC diagnostic push
443 #pragma GCC diagnostic ignored "-Wunused-result"
444 (void) write(fd, (genericptr_t) &currlev, sizeof(currlev));
445 #pragma GCC diagnostic pop
446 save_savefile_name(fd);
448 store_savefileinfo(fd);
449 store_plname_in_file(fd);
451 ustuck_id = (u.ustuck ? u.ustuck->m_id : 0);
452 usteed_id = (u.usteed ? u.usteed->m_id : 0);
453 savegamestate(fd, WRITE_SAVE);
457 havestate = flags.ins_chkpt;
463 savelev(fd, lev, mode)
468 if (mode & COUNT_SAVE) {
470 savelev0(fd, lev, COUNT_SAVE);
471 /* probably bytes_counted will be filled in again by an
472 * immediately following WRITE_SAVE anyway, but we'll
473 * leave it out of checkspace just in case */
474 if (iflags.checkspace) {
475 while (bytes_counted > freediskspace(levels))
476 if (!swapout_oldest())
480 if (mode & (WRITE_SAVE | FREE_SAVE)) {
482 savelev0(fd, lev, mode);
484 if (mode != FREE_SAVE) {
485 level_info[lev].where = ACTIVE;
486 level_info[lev].time = moves;
487 level_info[lev].size = bytes_counted;
493 savelev0(fd, lev, mode)
496 savelev(fd, lev, mode)
506 /* if we're tearing down the current level without saving anything
507 (which happens upon entrance to the endgame or after an aborted
508 restore attempt) then we don't want to do any actual I/O */
509 if (mode == FREE_SAVE)
512 /* purge any dead monsters (necessary if we're starting
513 a panic save rather than a normal one, or sometimes
514 when changing levels without taking time -- e.g.
515 create statue trap then immediately level teleport) */
516 if (iflags.purge_monsters)
520 panic("Save on bad file!"); /* impossible */
522 count_only = (mode & COUNT_SAVE);
524 if (lev >= 0 && lev <= maxledgerno())
525 level_info[lev].flags |= VISITED;
526 bwrite(fd, (genericptr_t) &hackpid, sizeof(hackpid));
530 bwrite(fd, (genericptr_t) &tlev, sizeof(tlev));
532 bwrite(fd, (genericptr_t) &lev, sizeof(lev));
534 savecemetery(fd, mode, &level.bonesinfo);
536 (boolean) ((sfsaveinfo.sfi1 & SFI1_RLECOMP) == SFI1_RLECOMP));
537 bwrite(fd, (genericptr_t) lastseentyp, sizeof(lastseentyp));
538 bwrite(fd, (genericptr_t) &monstermoves, sizeof(monstermoves));
539 bwrite(fd, (genericptr_t) &upstair, sizeof(stairway));
540 bwrite(fd, (genericptr_t) &dnstair, sizeof(stairway));
541 bwrite(fd, (genericptr_t) &upladder, sizeof(stairway));
542 bwrite(fd, (genericptr_t) &dnladder, sizeof(stairway));
543 bwrite(fd, (genericptr_t) &sstairs, sizeof(stairway));
544 bwrite(fd, (genericptr_t) &updest, sizeof(dest_area));
545 bwrite(fd, (genericptr_t) &dndest, sizeof(dest_area));
546 bwrite(fd, (genericptr_t) &level.flags, sizeof(level.flags));
547 bwrite(fd, (genericptr_t) doors, sizeof(doors));
548 save_rooms(fd); /* no dynamic memory to reclaim */
550 /* from here on out, saving also involves allocated memory cleanup */
552 /* this comes before the map, so need cleanup here if we skipped */
553 if (mode == FREE_SAVE)
554 savecemetery(fd, mode, &level.bonesinfo);
555 /* must be saved before mons, objs, and buried objs */
556 save_timers(fd, mode, RANGE_LEVEL);
557 save_light_sources(fd, mode, RANGE_LEVEL);
559 savemonchn(fd, fmon, mode);
560 save_worm(fd, mode); /* save worm information */
561 savetrapchn(fd, ftrap, mode);
562 saveobjchn(fd, fobj, mode);
563 saveobjchn(fd, level.buriedobjlist, mode);
564 saveobjchn(fd, billobjs, mode);
565 if (release_data(mode)) {
569 level.buriedobjlist = 0;
571 /* level.bonesinfo = 0; -- handled by savecemetery() */
573 save_engravings(fd, mode);
574 savedamage(fd, mode);
575 save_regions(fd, mode);
576 if (mode != FREE_SAVE)
581 savelevl(fd, rlecomp)
586 struct rm *prm, *rgrm;
591 /* perform run-length encoding of rm structs */
593 rgrm = &levl[0][0]; /* start matching at first rm */
596 for (y = 0; y < ROWNO; y++) {
597 for (x = 0; x < COLNO; x++) {
599 if (prm->glyph == rgrm->glyph && prm->typ == rgrm->typ
600 && prm->seenv == rgrm->seenv
601 && prm->horizontal == rgrm->horizontal
602 && prm->flags == rgrm->flags && prm->lit == rgrm->lit
603 && prm->waslit == rgrm->waslit
604 && prm->roomno == rgrm->roomno && prm->edge == rgrm->edge
605 && prm->candig == rgrm->candig) {
608 match = 254; /* undo this match */
612 /* the run has been broken,
613 * write out run-length encoding */
615 bwrite(fd, (genericptr_t) &match, sizeof(uchar));
616 bwrite(fd, (genericptr_t) rgrm, sizeof(struct rm));
617 /* start encoding again. we have at least 1 rm
618 * in the next run, viz. this one. */
625 bwrite(fd, (genericptr_t) &match, sizeof(uchar));
626 bwrite(fd, (genericptr_t) rgrm, sizeof(struct rm));
632 #endif /* ?RLECOMP */
633 bwrite(fd, (genericptr_t) levl, sizeof levl);
641 (*saveprocs.save_bufon)(fd);
650 (*saveprocs.save_bufoff)(fd);
654 /* flush run and buffer */
659 (*saveprocs.save_bflush)(fd);
667 register unsigned num;
669 (*saveprocs.save_bwrite)(fd, loc, num);
677 (*saveprocs.save_bclose)(fd);
681 static int bw_fd = -1;
682 static FILE *bw_FILE = 0;
683 static boolean buffering = FALSE;
692 panic("double buffering unexpected");
694 if ((bw_FILE = fdopen(fd, "w")) == 0)
695 panic("buffering of file %d failed", fd);
715 if (fflush(bw_FILE) == EOF)
716 panic("flush of savefile failed!");
723 def_bwrite(fd, loc, num)
725 register genericptr_t loc;
726 register unsigned num;
731 bytes_counted += num;
739 panic("unbuffered write to fd %d (!= %d)", fd, bw_fd);
741 failed = (fwrite(loc, (int) num, 1, bw_FILE) != 1);
745 /* lint wants 3rd arg of write to be an int; lint -p an unsigned */
746 #if defined(BSD) || defined(ULTRIX) || defined(WIN32) || defined(_MSC_VER)
747 failed = ((long) write(fd, loc, (int) num) != (long) num);
748 #else /* e.g. SYSV, __TURBOC__ */
749 failed = ((long) write(fd, loc, num) != (long) num);
754 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
755 if (program_state.done_hup)
756 nh_terminate(EXIT_FAILURE);
759 panic("cannot write %u bytes to file #%d", num, fd);
770 (void) fclose(bw_FILE);
780 /* The runs of zero-run compression are flushed after the game state or a
781 * level is written out. This adds a couple bytes to a save file, where
782 * the runs could be mashed together, but it allows gluing together game
783 * state and level files to form a save file, and it means the flushing
784 * does not need to be specifically called for every other time a level
785 * file is written out.
788 #define RLESC '\0' /* Leading character for run of LRESC's */
789 #define flushoutrun(ln) (zerocomp_bputc(RLESC), zerocomp_bputc(ln), ln = -1)
791 #ifndef ZEROCOMP_BUFSIZ
792 #define ZEROCOMP_BUFSIZ BUFSZ
794 static NEARDATA unsigned char outbuf[ZEROCOMP_BUFSIZ];
795 static NEARDATA unsigned short outbufp = 0;
796 static NEARDATA short outrunlength = -1;
797 static NEARDATA int bwritefd;
798 static NEARDATA boolean compressing = FALSE;
802 HUP printf("outbufp %d outrunlength %d\n", outbufp,outrunlength);
814 if (outbufp >= sizeof outbuf) {
815 (void) write(bwritefd, outbuf, sizeof outbuf);
818 outbuf[outbufp++] = (unsigned char) c;
837 panic("closing file with buffered data still unwritten");
844 /* flush run and buffer */
850 if (outrunlength >= 0) { /* flush run */
851 flushoutrun(outrunlength);
859 if (write(fd, outbuf, outbufp) != outbufp) {
860 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
861 if (program_state.done_hup)
862 nh_terminate(EXIT_FAILURE);
865 zerocomp_bclose(fd); /* panic (outbufp != 0) */
872 zerocomp_bwrite(fd, loc, num)
875 register unsigned num;
877 register unsigned char *bp = (unsigned char *) loc;
881 bytes_counted += num;
885 if ((unsigned) write(fd, loc, num) != num) {
886 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
887 if (program_state.done_hup)
888 nh_terminate(EXIT_FAILURE);
891 panic("cannot write %u bytes to file #%d", num, fd);
895 for (; num; num--, bp++) {
896 if (*bp == RLESC) { /* One more char in run */
897 if (++outrunlength == 0xFF) {
898 flushoutrun(outrunlength);
900 } else { /* end of run */
901 if (outrunlength >= 0) { /* flush run */
902 flushoutrun(outrunlength);
918 #endif /* ZEROCOMP */
922 register int fd, mode;
924 s_level *tmplev, *tmplev2;
927 for (tmplev = sp_levchn; tmplev; tmplev = tmplev->next)
929 if (perform_bwrite(mode))
930 bwrite(fd, (genericptr_t) &cnt, sizeof(int));
932 for (tmplev = sp_levchn; tmplev; tmplev = tmplev2) {
933 tmplev2 = tmplev->next;
934 if (perform_bwrite(mode))
935 bwrite(fd, (genericptr_t) tmplev, sizeof(s_level));
936 if (release_data(mode))
937 free((genericptr_t) tmplev);
939 if (release_data(mode))
943 /* used when saving a level and also when saving dungeon overview data */
945 savecemetery(fd, mode, cemeteryaddr)
948 struct cemetery **cemeteryaddr;
950 struct cemetery *thisbones, *nextbones;
953 flag = *cemeteryaddr ? 0 : -1;
954 if (perform_bwrite(mode))
955 bwrite(fd, (genericptr_t) &flag, sizeof flag);
956 nextbones = *cemeteryaddr;
957 while ((thisbones = nextbones) != 0) {
958 nextbones = thisbones->next;
959 if (perform_bwrite(mode))
960 bwrite(fd, (genericptr_t) thisbones, sizeof *thisbones);
961 if (release_data(mode))
962 free((genericptr_t) thisbones);
964 if (release_data(mode))
970 register int fd, mode;
972 register struct damage *damageptr, *tmp_dam;
975 damageptr = level.damagelist;
976 for (tmp_dam = damageptr; tmp_dam; tmp_dam = tmp_dam->next)
978 if (perform_bwrite(mode))
979 bwrite(fd, (genericptr_t) &xl, sizeof(xl));
982 if (perform_bwrite(mode))
983 bwrite(fd, (genericptr_t) damageptr, sizeof(*damageptr));
985 damageptr = damageptr->next;
986 if (release_data(mode))
987 free((genericptr_t) tmp_dam);
989 if (release_data(mode))
990 level.damagelist = 0;
998 int buflen, zerobuf = 0;
1000 buflen = sizeof(struct obj);
1001 bwrite(fd, (genericptr_t) &buflen, sizeof(int));
1002 bwrite(fd, (genericptr_t) otmp, buflen);
1005 buflen = strlen(ONAME(otmp)) + 1;
1008 bwrite(fd, (genericptr_t) &buflen, sizeof buflen);
1010 bwrite(fd, (genericptr_t) ONAME(otmp), buflen);
1012 /* defer to savemon() for this one */
1014 savemon(fd, OMONST(otmp));
1016 bwrite(fd, (genericptr_t) &zerobuf, sizeof zerobuf);
1019 buflen = sizeof(unsigned);
1022 bwrite(fd, (genericptr_t) &buflen, sizeof buflen);
1024 bwrite(fd, (genericptr_t) OMID(otmp), buflen);
1027 buflen = sizeof(long);
1030 bwrite(fd, (genericptr_t) &buflen, sizeof buflen);
1032 bwrite(fd, (genericptr_t) OLONG(otmp), buflen);
1035 buflen = strlen(OMAILCMD(otmp)) + 1;
1038 bwrite(fd, (genericptr_t) &buflen, sizeof buflen);
1040 bwrite(fd, (genericptr_t) OMAILCMD(otmp), buflen);
1045 saveobjchn(fd, otmp, mode)
1046 register int fd, mode;
1047 register struct obj *otmp;
1049 register struct obj *otmp2;
1054 if (perform_bwrite(mode)) {
1057 if (Has_contents(otmp))
1058 saveobjchn(fd, otmp->cobj, mode);
1059 if (release_data(mode)) {
1060 /* if (otmp->oclass == FOOD_CLASS)
1061 * food_disappears(otmp);
1064 * If these are on the floor, the discarding could
1065 * be because of a game save, or we could just be changing levels.
1066 * Always invalidate the pointer, but ensure that we have
1067 * the o_id in order to restore the pointer on reload.
1069 if (otmp == context.victual.piece) {
1070 /* Store the o_id of the victual if mismatched */
1071 if (context.victual.o_id != otmp->o_id)
1072 context.victual.o_id = otmp->o_id;
1073 /* invalidate the pointer; on reload it will get restored */
1074 context.victual.piece = (struct obj *) 0;
1076 if (otmp == context.tin.tin) {
1077 /* Store the o_id of your tin */
1078 if (context.tin.o_id != otmp->o_id)
1079 context.tin.o_id = otmp->o_id;
1080 /* invalidate the pointer; on reload it will get restored */
1081 context.tin.tin = (struct obj *) 0;
1083 /* if (otmp->oclass == SPBOOK_CLASS)
1084 * book_disappears(otmp);
1086 if (otmp == context.spbook.book) {
1087 /* Store the o_id of your spellbook */
1088 if (context.spbook.o_id != otmp->o_id)
1089 context.spbook.o_id = otmp->o_id;
1090 /* invalidate the pointer; on reload it will get restored */
1091 context.spbook.book = (struct obj *) 0;
1093 otmp->where = OBJ_FREE; /* set to free so dealloc will work */
1094 otmp->nobj = NULL; /* nobj saved into otmp2 */
1095 otmp->cobj = NULL; /* contents handled above */
1096 otmp->timed = 0; /* not timed any more */
1097 otmp->lamplit = 0; /* caller handled lights */
1102 if (perform_bwrite(mode))
1103 bwrite(fd, (genericptr_t) &minusone, sizeof(int));
1113 buflen = sizeof(struct monst);
1114 bwrite(fd, (genericptr_t) &buflen, sizeof(int));
1115 bwrite(fd, (genericptr_t) mtmp, buflen);
1118 buflen = strlen(MNAME(mtmp)) + 1;
1121 bwrite(fd, (genericptr_t) &buflen, sizeof buflen);
1123 bwrite(fd, (genericptr_t) MNAME(mtmp), buflen);
1126 buflen = sizeof(struct egd);
1129 bwrite(fd, (genericptr_t) &buflen, sizeof(int));
1131 bwrite(fd, (genericptr_t) EGD(mtmp), buflen);
1134 buflen = sizeof(struct epri);
1137 bwrite(fd, (genericptr_t) &buflen, sizeof(int));
1139 bwrite(fd, (genericptr_t) EPRI(mtmp), buflen);
1142 buflen = sizeof(struct eshk);
1145 bwrite(fd, (genericptr_t) &buflen, sizeof(int));
1147 bwrite(fd, (genericptr_t) ESHK(mtmp), buflen);
1150 buflen = sizeof(struct emin);
1153 bwrite(fd, (genericptr_t) &buflen, sizeof(int));
1155 bwrite(fd, (genericptr_t) EMIN(mtmp), buflen);
1158 buflen = sizeof(struct edog);
1161 bwrite(fd, (genericptr_t) &buflen, sizeof(int));
1163 bwrite(fd, (genericptr_t) EDOG(mtmp), buflen);
1165 /* mcorpsenm is inline int rather than pointer to something,
1166 so doesn't need to be preceded by a length field */
1167 bwrite(fd, (genericptr_t) &MCORPSENM(mtmp), sizeof MCORPSENM(mtmp));
1172 savemonchn(fd, mtmp, mode)
1173 register int fd, mode;
1174 register struct monst *mtmp;
1176 register struct monst *mtmp2;
1181 if (perform_bwrite(mode)) {
1182 mtmp->mnum = monsndx(mtmp->data);
1184 forget_temple_entry(mtmp); /* EPRI() */
1188 saveobjchn(fd, mtmp->minvent, mode);
1189 if (release_data(mode)) {
1190 if (mtmp == context.polearm.hitmon) {
1191 context.polearm.m_id = mtmp->m_id;
1192 context.polearm.hitmon = NULL;
1194 mtmp->nmon = NULL; /* nmon saved into mtmp2 */
1195 dealloc_monst(mtmp);
1199 if (perform_bwrite(mode))
1200 bwrite(fd, (genericptr_t) &minusone, sizeof(int));
1203 /* save traps; ftrap is the only trap chain so the 2nd arg is superfluous */
1205 savetrapchn(fd, trap, mode)
1207 register struct trap *trap;
1210 static struct trap zerotrap;
1211 register struct trap *trap2;
1214 trap2 = trap->ntrap;
1215 if (perform_bwrite(mode))
1216 bwrite(fd, (genericptr_t) trap, sizeof (struct trap));
1217 if (release_data(mode))
1221 if (perform_bwrite(mode))
1222 bwrite(fd, (genericptr_t) &zerotrap, sizeof zerotrap);
1225 /* save all the fruit names and ID's; this is used only in saving whole games
1226 * (not levels) and in saving bones levels. When saving a bones level,
1227 * we only want to save the fruits which exist on the bones level; the bones
1228 * level routine marks nonexistent fruits by making the fid negative.
1231 savefruitchn(fd, mode)
1234 static struct fruit zerofruit;
1235 register struct fruit *f2, *f1;
1240 if (f1->fid >= 0 && perform_bwrite(mode))
1241 bwrite(fd, (genericptr_t) f1, sizeof (struct fruit));
1242 if (release_data(mode))
1246 if (perform_bwrite(mode))
1247 bwrite(fd, (genericptr_t) &zerofruit, sizeof zerofruit);
1248 if (release_data(mode))
1253 store_plname_in_file(fd)
1256 int plsiztmp = PL_NSIZ;
1259 /* bwrite() before bufon() uses plain write() */
1260 bwrite(fd, (genericptr_t) &plsiztmp, sizeof(plsiztmp));
1261 bwrite(fd, (genericptr_t) plname, plsiztmp);
1267 save_msghistory(fd, mode)
1271 int msgcount = 0, msglen;
1273 boolean init = TRUE;
1275 if (perform_bwrite(mode)) {
1276 /* ask window port for each message in sequence */
1277 while ((msg = getmsghistory(init)) != 0) {
1279 msglen = strlen(msg);
1280 /* sanity: truncate if necessary (shouldn't happen);
1281 no need to modify msg[] since terminator isn't written */
1282 if (msglen > BUFSZ - 1)
1284 bwrite(fd, (genericptr_t) &msglen, sizeof(msglen));
1285 bwrite(fd, (genericptr_t) msg, msglen);
1288 bwrite(fd, (genericptr_t) &minusone, sizeof(int));
1290 debugpline1("Stored %d messages into savefile.", msgcount);
1291 /* note: we don't attempt to handle release_data() here */
1295 store_savefileinfo(fd)
1298 /* sfcap (decl.c) describes the savefile feature capabilities
1299 * that are supported by this port/platform build.
1301 * sfsaveinfo (decl.c) describes the savefile info that actually
1302 * gets written into the savefile, and is used to determine the
1303 * save file being written.
1305 * sfrestinfo (decl.c) describes the savefile info that is
1306 * being used to read the information from an existing savefile.
1311 /* bwrite() before bufon() uses plain write() */
1312 bwrite(fd, (genericptr_t) &sfsaveinfo, (unsigned) (sizeof sfsaveinfo));
1318 set_savepref(suitename)
1319 const char *suitename;
1321 if (!strcmpi(suitename, "externalcomp")) {
1322 saveprocs.name = "externalcomp";
1323 saveprocs.save_bufon = def_bufon;
1324 saveprocs.save_bufoff = def_bufoff;
1325 saveprocs.save_bflush = def_bflush;
1326 saveprocs.save_bwrite = def_bwrite;
1327 saveprocs.save_bclose = def_bclose;
1328 sfsaveinfo.sfi1 |= SFI1_EXTERNALCOMP;
1329 sfsaveinfo.sfi1 &= ~SFI1_ZEROCOMP;
1331 if (!strcmpi(suitename, "!rlecomp")) {
1332 sfsaveinfo.sfi1 &= ~SFI1_RLECOMP;
1335 if (!strcmpi(suitename, "zerocomp")) {
1336 saveprocs.name = "zerocomp";
1337 saveprocs.save_bufon = zerocomp_bufon;
1338 saveprocs.save_bufoff = zerocomp_bufoff;
1339 saveprocs.save_bflush = zerocomp_bflush;
1340 saveprocs.save_bwrite = zerocomp_bwrite;
1341 saveprocs.save_bclose = zerocomp_bclose;
1342 sfsaveinfo.sfi1 |= SFI1_ZEROCOMP;
1343 sfsaveinfo.sfi1 &= ~SFI1_EXTERNALCOMP;
1347 if (!strcmpi(suitename, "rlecomp")) {
1348 sfsaveinfo.sfi1 |= SFI1_RLECOMP;
1353 /* also called by prscore(); this probably belongs in dungeon.c... */
1357 #ifdef FREE_ALL_MEMORY
1358 savelevchn(0, FREE_SAVE);
1359 save_dungeon(0, FALSE, TRUE);
1367 #if defined(UNIX) && defined(MAIL)
1371 free_menu_coloring();
1372 free_invbuf(); /* let_to_name (invent.c) */
1373 free_youbuf(); /* You_buf,&c (pline.c) */
1375 tmp_at(DISP_FREEMEM, 0); /* temporary display effects */
1376 #ifdef FREE_ALL_MEMORY
1377 #define freeobjchn(X) (saveobjchn(0, X, FREE_SAVE), X = 0)
1378 #define freemonchn(X) (savemonchn(0, X, FREE_SAVE), X = 0)
1379 #define freetrapchn(X) (savetrapchn(0, X, FREE_SAVE), X = 0)
1380 #define freefruitchn() savefruitchn(0, FREE_SAVE)
1381 #define freenames() savenames(0, FREE_SAVE)
1382 #define free_killers() save_killers(0, FREE_SAVE)
1383 #define free_oracles() save_oracles(0, FREE_SAVE)
1384 #define free_waterlevel() save_waterlevel(0, FREE_SAVE)
1385 #define free_worm() save_worm(0, FREE_SAVE)
1386 #define free_timers(R) save_timers(0, FREE_SAVE, R)
1387 #define free_light_sources(R) save_light_sources(0, FREE_SAVE, R);
1388 #define free_engravings() save_engravings(0, FREE_SAVE)
1389 #define freedamage() savedamage(0, FREE_SAVE)
1390 #define free_animals() mon_animal_list(FALSE)
1392 /* move-specific data */
1393 dmonsfree(); /* release dead monsters */
1395 /* level-specific data */
1396 free_timers(RANGE_LEVEL);
1397 free_light_sources(RANGE_LEVEL);
1400 free_worm(); /* release worm segment information */
1403 freeobjchn(level.buriedobjlist);
1404 freeobjchn(billobjs);
1408 /* game-state data */
1410 free_timers(RANGE_GLOBAL);
1411 free_light_sources(RANGE_GLOBAL);
1413 freeobjchn(migrating_objs);
1414 freemonchn(migrating_mons);
1415 freemonchn(mydogs); /* ascension or dungeon escape */
1416 /* freelevchn(); -- [folded into free_dungeons()] */
1424 /* some pointers in iflags */
1425 if (iflags.wc_font_map)
1426 free(iflags.wc_font_map);
1427 if (iflags.wc_font_message)
1428 free(iflags.wc_font_message);
1429 if (iflags.wc_font_text)
1430 free(iflags.wc_font_text);
1431 if (iflags.wc_font_menu)
1432 free(iflags.wc_font_menu);
1433 if (iflags.wc_font_status)
1434 free(iflags.wc_font_status);
1435 if (iflags.wc_tile_file)
1436 free(iflags.wc_tile_file);
1437 free_autopickup_exceptions();
1440 /* free_pickinv_cache(); -- now done from really_done()... */
1442 #endif /* FREE_ALL_MEMORY */
1443 #ifdef STATUS_HILITES
1447 dumplogfreemessages();
1450 /* last, because it frees data that might be used by panic() to provide
1451 feedback to the user; conceivably other freeing might trigger panic */
1452 sysopt_release(); /* SYSCF strings */
1461 char to[PATHLEN], from[PATHLEN];
1463 Sprintf(from, "%s%s", permbones, alllevels);
1464 Sprintf(to, "%s%s", levels, alllevels);
1465 set_levelfile_name(from, lev);
1466 set_levelfile_name(to, lev);
1467 if (iflags.checkspace) {
1468 while (level_info[lev].size > freediskspace(to))
1469 if (!swapout_oldest())
1473 pline("Swapping in `%s'.", from);
1477 (void) unlink(from);
1478 level_info[lev].where = ACTIVE;
1485 char to[PATHLEN], from[PATHLEN];
1491 for (i = 1, oldtime = 0, oldest = 0; i <= maxledgerno(); i++)
1492 if (level_info[i].where == ACTIVE
1493 && (!oldtime || level_info[i].time < oldtime)) {
1495 oldtime = level_info[i].time;
1499 Sprintf(from, "%s%s", levels, alllevels);
1500 Sprintf(to, "%s%s", permbones, alllevels);
1501 set_levelfile_name(from, oldest);
1502 set_levelfile_name(to, oldest);
1504 pline("Swapping out `%s'.", from);
1508 (void) unlink(from);
1509 level_info[oldest].where = SWAPPED;
1518 if (_copyfile(from, to))
1519 panic("Can't copy %s to %s", from, to);
1521 char buf[BUFSIZ]; /* this is system interaction, therefore
1522 * BUFSIZ instead of NetHack's BUFSZ */
1523 int nfrom, nto, fdfrom, fdto;
1525 if ((fdfrom = open(from, O_RDONLY | O_BINARY, FCMASK)) < 0)
1526 panic("Can't copy from %s !?", from);
1527 if ((fdto = open(to, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, FCMASK)) < 0)
1528 panic("Can't copy to %s", to);
1530 nfrom = read(fdfrom, buf, BUFSIZ);
1531 nto = write(fdto, buf, nfrom);
1533 panic("Copyfile failed!");
1534 } while (nfrom == BUFSIZ);
1535 (void) nhclose(fdfrom);
1536 (void) nhclose(fdto);
1540 /* see comment in bones.c */
1548 #endif /* MFLOPPY */