1 /* NetHack 3.6 save.c $NHDT-Date: 1450231175 2015/12/16 01:59:35 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.98 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
11 #if !defined(LSC) && !defined(O_WRONLY) && !defined(AZTEC_C)
17 static int count_only
;
21 int dotcnt
, dotrow
; /* also used in restore */
24 STATIC_DCL
void FDECL(savelevchn
, (int, int));
25 STATIC_DCL
void FDECL(savedamage
, (int, int));
26 STATIC_DCL
void FDECL(saveobj
, (int, struct obj
*));
27 STATIC_DCL
void FDECL(saveobjchn
, (int, struct obj
*, int));
28 STATIC_DCL
void FDECL(savemon
, (int, struct monst
*));
29 STATIC_DCL
void FDECL(savemonchn
, (int, struct monst
*, int));
30 STATIC_DCL
void FDECL(savetrapchn
, (int, struct trap
*, int));
31 STATIC_DCL
void FDECL(savegamestate
, (int, int));
32 STATIC_OVL
void FDECL(save_msghistory
, (int, int));
34 STATIC_DCL
void FDECL(savelev0
, (int, XCHAR_P
, int));
35 STATIC_DCL boolean
NDECL(swapout_oldest
);
36 STATIC_DCL
void FDECL(copyfile
, (char *, char *));
38 STATIC_DCL
void FDECL(savelevl
, (int fd
, BOOLEAN_P
));
39 STATIC_DCL
void FDECL(def_bufon
, (int));
40 STATIC_DCL
void FDECL(def_bufoff
, (int));
41 STATIC_DCL
void FDECL(def_bflush
, (int));
42 STATIC_DCL
void FDECL(def_bwrite
, (int, genericptr_t
, unsigned int));
44 STATIC_DCL
void FDECL(zerocomp_bufon
, (int));
45 STATIC_DCL
void FDECL(zerocomp_bufoff
, (int));
46 STATIC_DCL
void FDECL(zerocomp_bflush
, (int));
47 STATIC_DCL
void FDECL(zerocomp_bwrite
, (int, genericptr_t
, unsigned int));
48 STATIC_DCL
void FDECL(zerocomp_bputc
, (int));
51 static struct save_procs
{
53 void FDECL((*save_bufon
), (int));
54 void FDECL((*save_bufoff
), (int));
55 void FDECL((*save_bflush
), (int));
56 void FDECL((*save_bwrite
), (int, genericptr_t
, unsigned int));
57 void FDECL((*save_bclose
), (int));
59 #if !defined(ZEROCOMP) || (defined(COMPRESS) || defined(ZLIB_COMP))
60 "externalcomp", def_bufon
, def_bufoff
, def_bflush
, def_bwrite
, def_bclose
,
62 "zerocomp", zerocomp_bufon
, zerocomp_bufoff
,
63 zerocomp_bflush
, zerocomp_bwrite
, zerocomp_bclose
,
67 static long nulls
[sizeof(struct trap
) + sizeof(struct fruit
)];
69 #if defined(UNIX) || defined(VMS) || defined(__EMX__) || defined(WIN32)
70 #define HUP if (!program_state.done_hup)
75 /* need to preserve these during save to avoid accessing freed memory */
76 static unsigned ustuck_id
= 0, usteed_id
= 0;
81 clear_nhwindow(WIN_MESSAGE
);
82 if (yn("Really save?") == 'n') {
83 clear_nhwindow(WIN_MESSAGE
);
87 clear_nhwindow(WIN_MESSAGE
);
89 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
90 program_state
.done_hup
= 0;
93 u
.uhp
= -1; /* universal game's over indicator */
94 /* make sure they see the Saving message */
95 display_nhwindow(WIN_MESSAGE
, TRUE
);
96 exit_nhwindows("Be seeing you...");
97 terminate(EXIT_SUCCESS
);
104 /* returns 1 if save successful */
109 register int fd
, ofd
;
114 /* we may get here via hangup signal, in which case we want to fix up
115 a few of things before saving so that they won't be restored in
116 an improper state; these will be no-ops for normal save sequence */
118 if (iflags
.save_uinwater
)
119 u
.uinwater
= 1, iflags
.save_uinwater
= 0;
120 if (iflags
.save_uburied
)
121 u
.uburied
= 1, iflags
.save_uburied
= 0;
123 if (!program_state
.something_worth_saving
|| !SAVEF
[0])
125 fq_save
= fqname(SAVEF
, SAVEPREFIX
, 1); /* level files take 0 */
127 #if defined(UNIX) || defined(VMS)
128 sethanguphandler((void FDECL((*), (int) )) SIG_IGN
);
131 (void) signal(SIGINT
, SIG_IGN
);
134 #if defined(MICRO) && defined(MFLOPPY)
135 if (!saveDiskPrompt(0))
139 HUP
if (iflags
.window_inited
)
141 nh_uncompress(fq_save
);
142 fd
= open_savefile();
145 clear_nhwindow(WIN_MESSAGE
);
146 There("seems to be an old save file.");
147 if (yn("Overwrite the old file?") == 'n') {
148 nh_compress(fq_save
);
154 HUP
mark_synch(); /* flush any buffered screen output */
156 fd
= create_savefile();
158 HUP
pline("Cannot open save file.");
159 (void) delete_savefile(); /* ab@unido */
163 vision_recalc(2); /* shut down vision to prevent problems
164 in the event of an impossible() call */
166 /* undo date-dependent luck adjustments made at startup time */
167 if (flags
.moonphase
== FULL_MOON
) /* ut-sally!fletcher */
168 change_luck(-1); /* and unido!ab */
171 if (iflags
.window_inited
)
172 HUP
clear_nhwindow(WIN_MESSAGE
);
178 if (strncmpi("X11", windowprocs
.name
, 3))
179 putstr(WIN_MAP
, 0, "Saving:");
182 /* make sure there is enough disk space */
183 if (iflags
.checkspace
) {
186 savelev(fd
, ledger_no(&u
.uz
), COUNT_SAVE
);
187 savegamestate(fd
, COUNT_SAVE
);
188 needed
= bytes_counted
;
190 for (ltmp
= 1; ltmp
<= maxledgerno(); ltmp
++)
191 if (ltmp
!= ledger_no(&u
.uz
) && level_info
[ltmp
].where
)
192 needed
+= level_info
[ltmp
].size
+ (sizeof ltmp
);
193 fds
= freediskspace(fq_save
);
197 There("is insufficient space on SAVE disk.");
198 pline("Require %ld bytes but only have %ld.", needed
, fds
);
202 (void) delete_savefile();
211 store_savefileinfo(fd
);
212 store_plname_in_file(fd
);
213 ustuck_id
= (u
.ustuck
? u
.ustuck
->m_id
: 0);
214 usteed_id
= (u
.usteed
? u
.usteed
->m_id
: 0);
215 savelev(fd
, ledger_no(&u
.uz
), WRITE_SAVE
| FREE_SAVE
);
216 savegamestate(fd
, WRITE_SAVE
| FREE_SAVE
);
218 /* While copying level files around, zero out u.uz to keep
219 * parts of the restore code from completely initializing all
220 * in-core data structures, since all we're doing is copying.
221 * This also avoids at least one nasty core dump.
224 u
.uz
.dnum
= u
.uz
.dlevel
= 0;
225 /* these pointers are no longer valid, and at least u.usteed
226 * may mislead place_monster() on other levels
228 u
.ustuck
= (struct monst
*) 0;
229 u
.usteed
= (struct monst
*) 0;
231 for (ltmp
= (xchar
) 1; ltmp
<= maxledgerno(); ltmp
++) {
232 if (ltmp
== ledger_no(&uz_save
))
234 if (!(level_info
[ltmp
].flags
& LFILE_EXISTS
))
237 curs(WIN_MAP
, 1 + dotcnt
++, dotrow
);
238 if (dotcnt
>= (COLNO
- 1)) {
242 if (strncmpi("X11", windowprocs
.name
, 3)) {
243 putstr(WIN_MAP
, 0, ".");
247 ofd
= open_levelfile(ltmp
, whynot
);
251 (void) delete_savefile();
252 HUP
Strcpy(killer
.name
, whynot
);
256 minit(); /* ZEROCOMP */
257 getlev(ofd
, hackpid
, ltmp
, FALSE
);
259 bwrite(fd
, (genericptr_t
) <mp
, sizeof ltmp
); /* level number*/
260 savelev(fd
, ltmp
, WRITE_SAVE
| FREE_SAVE
); /* actual level*/
261 delete_levelfile(ltmp
);
267 /* get rid of current level --jgm */
268 delete_levelfile(ledger_no(&u
.uz
));
270 nh_compress(fq_save
);
271 /* this should probably come sooner... */
272 program_state
.something_worth_saving
= 0;
277 savegamestate(fd
, mode
)
278 register int fd
, mode
;
283 count_only
= (mode
& COUNT_SAVE
);
285 uid
= (unsigned long) getuid();
286 bwrite(fd
, (genericptr_t
) &uid
, sizeof uid
);
287 bwrite(fd
, (genericptr_t
) &context
, sizeof(struct context_info
));
288 bwrite(fd
, (genericptr_t
) &flags
, sizeof(struct flag
));
290 bwrite(fd
, (genericptr_t
) &sysflags
, sizeof(struct sysflag
));
292 urealtime
.finish_time
= getnow();
293 urealtime
.realtime
+= (long) (urealtime
.finish_time
294 - urealtime
.start_timing
);
295 bwrite(fd
, (genericptr_t
) &u
, sizeof(struct you
));
296 bwrite(fd
, yyyymmddhhmmss(ubirthday
), 14);
297 bwrite(fd
, (genericptr_t
) &urealtime
.realtime
, sizeof urealtime
.realtime
);
298 bwrite(fd
, yyyymmddhhmmss(urealtime
.start_timing
), 14); /** Why? **/
299 /* this is the value to use for the next update of urealtime.realtime */
300 urealtime
.start_timing
= urealtime
.finish_time
;
301 save_killers(fd
, mode
);
303 /* must come before migrating_objs and migrating_mons are freed */
304 save_timers(fd
, mode
, RANGE_GLOBAL
);
305 save_light_sources(fd
, mode
, RANGE_GLOBAL
);
307 saveobjchn(fd
, invent
, mode
);
309 /* prevent loss of ball & chain when swallowed */
310 uball
->nobj
= uchain
;
311 uchain
->nobj
= (struct obj
*) 0;
312 saveobjchn(fd
, uball
, mode
);
314 saveobjchn(fd
, (struct obj
*) 0, mode
);
317 saveobjchn(fd
, migrating_objs
, mode
);
318 savemonchn(fd
, migrating_mons
, mode
);
319 if (release_data(mode
)) {
324 bwrite(fd
, (genericptr_t
) mvitals
, sizeof(mvitals
));
326 save_dungeon(fd
, (boolean
) !!perform_bwrite(mode
),
327 (boolean
) !!release_data(mode
));
328 savelevchn(fd
, mode
);
329 bwrite(fd
, (genericptr_t
) &moves
, sizeof moves
);
330 bwrite(fd
, (genericptr_t
) &monstermoves
, sizeof monstermoves
);
331 bwrite(fd
, (genericptr_t
) &quest_status
, sizeof(struct q_score
));
332 bwrite(fd
, (genericptr_t
) spl_book
,
333 sizeof(struct spell
) * (MAXSPELL
+ 1));
335 save_oracles(fd
, mode
);
337 bwrite(fd
, (genericptr_t
) &ustuck_id
, sizeof ustuck_id
);
339 bwrite(fd
, (genericptr_t
) &usteed_id
, sizeof usteed_id
);
340 bwrite(fd
, (genericptr_t
) pl_character
, sizeof pl_character
);
341 bwrite(fd
, (genericptr_t
) pl_fruit
, sizeof pl_fruit
);
342 savefruitchn(fd
, mode
);
344 save_waterlevel(fd
, mode
);
345 save_msghistory(fd
, mode
);
350 tricked_fileremoved(fd
, whynot
)
356 pline("Probably someone removed it.");
357 Strcpy(killer
.name
, whynot
);
369 static boolean havestate
= TRUE
;
372 /* When checkpointing is on, the full state needs to be written
373 * on each checkpoint. When checkpointing is off, only the pid
374 * needs to be in the level.0 file, so it does not need to be
375 * constantly rewritten. When checkpointing is turned off during
376 * a game, however, the file has to be rewritten once to truncate
377 * it and avoid restoring from outdated information.
379 * Restricting havestate to this routine means that an additional
380 * noop pid rewriting will take place on the first "checkpoint" after
381 * the game is started or restored, if checkpointing is off.
383 if (flags
.ins_chkpt
|| havestate
) {
384 /* save the rest of the current game state in the lock file,
385 * following the original int pid, the current level number,
386 * and the current savefile name, which should not be subject
387 * to any internal compression schemes since they must be
388 * readable by an external utility
390 fd
= open_levelfile(0, whynot
);
391 if (tricked_fileremoved(fd
, whynot
))
394 (void) read(fd
, (genericptr_t
) &hpid
, sizeof(hpid
));
395 if (hackpid
!= hpid
) {
396 Sprintf(whynot
, "Level #0 pid (%d) doesn't match ours (%d)!",
399 Strcpy(killer
.name
, whynot
);
404 fd
= create_levelfile(0, whynot
);
407 Strcpy(killer
.name
, whynot
);
411 (void) write(fd
, (genericptr_t
) &hackpid
, sizeof(hackpid
));
412 if (flags
.ins_chkpt
) {
413 int currlev
= ledger_no(&u
.uz
);
415 (void) write(fd
, (genericptr_t
) &currlev
, sizeof(currlev
));
416 save_savefile_name(fd
);
418 store_savefileinfo(fd
);
419 store_plname_in_file(fd
);
421 ustuck_id
= (u
.ustuck
? u
.ustuck
->m_id
: 0);
422 usteed_id
= (u
.usteed
? u
.usteed
->m_id
: 0);
423 savegamestate(fd
, WRITE_SAVE
);
427 havestate
= flags
.ins_chkpt
;
433 savelev(fd
, lev
, mode
)
438 if (mode
& COUNT_SAVE
) {
440 savelev0(fd
, lev
, COUNT_SAVE
);
441 /* probably bytes_counted will be filled in again by an
442 * immediately following WRITE_SAVE anyway, but we'll
443 * leave it out of checkspace just in case */
444 if (iflags
.checkspace
) {
445 while (bytes_counted
> freediskspace(levels
))
446 if (!swapout_oldest())
450 if (mode
& (WRITE_SAVE
| FREE_SAVE
)) {
452 savelev0(fd
, lev
, mode
);
454 if (mode
!= FREE_SAVE
) {
455 level_info
[lev
].where
= ACTIVE
;
456 level_info
[lev
].time
= moves
;
457 level_info
[lev
].size
= bytes_counted
;
463 savelev0(fd
, lev
, mode
)
466 savelev(fd
, lev
, mode
)
476 /* if we're tearing down the current level without saving anything
477 (which happens upon entrance to the endgame or after an aborted
478 restore attempt) then we don't want to do any actual I/O */
479 if (mode
== FREE_SAVE
)
482 /* purge any dead monsters (necessary if we're starting
483 a panic save rather than a normal one, or sometimes
484 when changing levels without taking time -- e.g.
485 create statue trap then immediately level teleport) */
486 if (iflags
.purge_monsters
)
490 panic("Save on bad file!"); /* impossible */
492 count_only
= (mode
& COUNT_SAVE
);
494 if (lev
>= 0 && lev
<= maxledgerno())
495 level_info
[lev
].flags
|= VISITED
;
496 bwrite(fd
, (genericptr_t
) &hackpid
, sizeof(hackpid
));
500 bwrite(fd
, (genericptr_t
) &tlev
, sizeof(tlev
));
502 bwrite(fd
, (genericptr_t
) &lev
, sizeof(lev
));
504 savecemetery(fd
, mode
, &level
.bonesinfo
);
506 (boolean
) ((sfsaveinfo
.sfi1
& SFI1_RLECOMP
) == SFI1_RLECOMP
));
507 bwrite(fd
, (genericptr_t
) lastseentyp
, sizeof(lastseentyp
));
508 bwrite(fd
, (genericptr_t
) &monstermoves
, sizeof(monstermoves
));
509 bwrite(fd
, (genericptr_t
) &upstair
, sizeof(stairway
));
510 bwrite(fd
, (genericptr_t
) &dnstair
, sizeof(stairway
));
511 bwrite(fd
, (genericptr_t
) &upladder
, sizeof(stairway
));
512 bwrite(fd
, (genericptr_t
) &dnladder
, sizeof(stairway
));
513 bwrite(fd
, (genericptr_t
) &sstairs
, sizeof(stairway
));
514 bwrite(fd
, (genericptr_t
) &updest
, sizeof(dest_area
));
515 bwrite(fd
, (genericptr_t
) &dndest
, sizeof(dest_area
));
516 bwrite(fd
, (genericptr_t
) &level
.flags
, sizeof(level
.flags
));
517 bwrite(fd
, (genericptr_t
) doors
, sizeof(doors
));
518 save_rooms(fd
); /* no dynamic memory to reclaim */
520 /* from here on out, saving also involves allocated memory cleanup */
522 /* this comes before the map, so need cleanup here if we skipped */
523 if (mode
== FREE_SAVE
)
524 savecemetery(fd
, mode
, &level
.bonesinfo
);
525 /* must be saved before mons, objs, and buried objs */
526 save_timers(fd
, mode
, RANGE_LEVEL
);
527 save_light_sources(fd
, mode
, RANGE_LEVEL
);
529 savemonchn(fd
, fmon
, mode
);
530 save_worm(fd
, mode
); /* save worm information */
531 savetrapchn(fd
, ftrap
, mode
);
532 saveobjchn(fd
, fobj
, mode
);
533 saveobjchn(fd
, level
.buriedobjlist
, mode
);
534 saveobjchn(fd
, billobjs
, mode
);
535 if (release_data(mode
)) {
539 level
.buriedobjlist
= 0;
541 /* level.bonesinfo = 0; -- handled by savecemetery() */
543 save_engravings(fd
, mode
);
544 savedamage(fd
, mode
);
545 save_regions(fd
, mode
);
546 if (mode
!= FREE_SAVE
)
551 savelevl(fd
, rlecomp
)
556 struct rm
*prm
, *rgrm
;
561 /* perform run-length encoding of rm structs */
563 rgrm
= &levl
[0][0]; /* start matching at first rm */
566 for (y
= 0; y
< ROWNO
; y
++) {
567 for (x
= 0; x
< COLNO
; x
++) {
569 if (prm
->glyph
== rgrm
->glyph
&& prm
->typ
== rgrm
->typ
570 && prm
->seenv
== rgrm
->seenv
571 && prm
->horizontal
== rgrm
->horizontal
572 && prm
->flags
== rgrm
->flags
&& prm
->lit
== rgrm
->lit
573 && prm
->waslit
== rgrm
->waslit
574 && prm
->roomno
== rgrm
->roomno
&& prm
->edge
== rgrm
->edge
575 && prm
->candig
== rgrm
->candig
) {
578 match
= 254; /* undo this match */
582 /* the run has been broken,
583 * write out run-length encoding */
585 bwrite(fd
, (genericptr_t
) &match
, sizeof(uchar
));
586 bwrite(fd
, (genericptr_t
) rgrm
, sizeof(struct rm
));
587 /* start encoding again. we have at least 1 rm
588 * in the next run, viz. this one. */
595 bwrite(fd
, (genericptr_t
) &match
, sizeof(uchar
));
596 bwrite(fd
, (genericptr_t
) rgrm
, sizeof(struct rm
));
602 #endif /* ?RLECOMP */
603 bwrite(fd
, (genericptr_t
) levl
, sizeof levl
);
611 (*saveprocs
.save_bufon
)(fd
);
620 (*saveprocs
.save_bufoff
)(fd
);
624 /* flush run and buffer */
629 (*saveprocs
.save_bflush
)(fd
);
637 register unsigned num
;
639 (*saveprocs
.save_bwrite
)(fd
, loc
, num
);
647 (*saveprocs
.save_bclose
)(fd
);
651 static int bw_fd
= -1;
652 static FILE *bw_FILE
= 0;
653 static boolean buffering
= FALSE
;
662 panic("double buffering unexpected");
664 if ((bw_FILE
= fdopen(fd
, "w")) == 0)
665 panic("buffering of file %d failed", fd
);
685 if (fflush(bw_FILE
) == EOF
)
686 panic("flush of savefile failed!");
693 def_bwrite(fd
, loc
, num
)
695 register genericptr_t loc
;
696 register unsigned num
;
701 bytes_counted
+= num
;
709 panic("unbuffered write to fd %d (!= %d)", fd
, bw_fd
);
711 failed
= (fwrite(loc
, (int) num
, 1, bw_FILE
) != 1);
715 /* lint wants 3rd arg of write to be an int; lint -p an unsigned */
716 #if defined(BSD) || defined(ULTRIX) || defined(WIN32) || defined(_MSC_VER)
717 failed
= ((long) write(fd
, loc
, (int) num
) != (long) num
);
718 #else /* e.g. SYSV, __TURBOC__ */
719 failed
= ((long) write(fd
, loc
, num
) != (long) num
);
724 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
725 if (program_state
.done_hup
)
726 terminate(EXIT_FAILURE
);
729 panic("cannot write %u bytes to file #%d", num
, fd
);
740 (void) fclose(bw_FILE
);
750 /* The runs of zero-run compression are flushed after the game state or a
751 * level is written out. This adds a couple bytes to a save file, where
752 * the runs could be mashed together, but it allows gluing together game
753 * state and level files to form a save file, and it means the flushing
754 * does not need to be specifically called for every other time a level
755 * file is written out.
758 #define RLESC '\0' /* Leading character for run of LRESC's */
759 #define flushoutrun(ln) (zerocomp_bputc(RLESC), zerocomp_bputc(ln), ln = -1)
761 #ifndef ZEROCOMP_BUFSIZ
762 #define ZEROCOMP_BUFSIZ BUFSZ
764 static NEARDATA
unsigned char outbuf
[ZEROCOMP_BUFSIZ
];
765 static NEARDATA
unsigned short outbufp
= 0;
766 static NEARDATA
short outrunlength
= -1;
767 static NEARDATA
int bwritefd
;
768 static NEARDATA boolean compressing
= FALSE
;
772 HUP printf("outbufp %d outrunlength %d\n", outbufp,outrunlength);
784 if (outbufp
>= sizeof outbuf
) {
785 (void) write(bwritefd
, outbuf
, sizeof outbuf
);
788 outbuf
[outbufp
++] = (unsigned char) c
;
807 panic("closing file with buffered data still unwritten");
814 /* flush run and buffer */
820 if (outrunlength
>= 0) { /* flush run */
821 flushoutrun(outrunlength
);
829 if (write(fd
, outbuf
, outbufp
) != outbufp
) {
830 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
831 if (program_state
.done_hup
)
832 terminate(EXIT_FAILURE
);
835 zerocomp_bclose(fd
); /* panic (outbufp != 0) */
842 zerocomp_bwrite(fd
, loc
, num
)
845 register unsigned num
;
847 register unsigned char *bp
= (unsigned char *) loc
;
851 bytes_counted
+= num
;
855 if ((unsigned) write(fd
, loc
, num
) != num
) {
856 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
857 if (program_state
.done_hup
)
858 terminate(EXIT_FAILURE
);
861 panic("cannot write %u bytes to file #%d", num
, fd
);
865 for (; num
; num
--, bp
++) {
866 if (*bp
== RLESC
) { /* One more char in run */
867 if (++outrunlength
== 0xFF) {
868 flushoutrun(outrunlength
);
870 } else { /* end of run */
871 if (outrunlength
>= 0) { /* flush run */
872 flushoutrun(outrunlength
);
888 #endif /* ZEROCOMP */
892 register int fd
, mode
;
894 s_level
*tmplev
, *tmplev2
;
897 for (tmplev
= sp_levchn
; tmplev
; tmplev
= tmplev
->next
)
899 if (perform_bwrite(mode
))
900 bwrite(fd
, (genericptr_t
) &cnt
, sizeof(int));
902 for (tmplev
= sp_levchn
; tmplev
; tmplev
= tmplev2
) {
903 tmplev2
= tmplev
->next
;
904 if (perform_bwrite(mode
))
905 bwrite(fd
, (genericptr_t
) tmplev
, sizeof(s_level
));
906 if (release_data(mode
))
907 free((genericptr_t
) tmplev
);
909 if (release_data(mode
))
913 /* used when saving a level and also when saving dungeon overview data */
915 savecemetery(fd
, mode
, cemeteryaddr
)
918 struct cemetery
**cemeteryaddr
;
920 struct cemetery
*thisbones
, *nextbones
;
923 flag
= *cemeteryaddr
? 0 : -1;
924 if (perform_bwrite(mode
))
925 bwrite(fd
, (genericptr_t
) &flag
, sizeof flag
);
926 nextbones
= *cemeteryaddr
;
927 while ((thisbones
= nextbones
) != 0) {
928 nextbones
= thisbones
->next
;
929 if (perform_bwrite(mode
))
930 bwrite(fd
, (genericptr_t
) thisbones
, sizeof *thisbones
);
931 if (release_data(mode
))
932 free((genericptr_t
) thisbones
);
934 if (release_data(mode
))
940 register int fd
, mode
;
942 register struct damage
*damageptr
, *tmp_dam
;
945 damageptr
= level
.damagelist
;
946 for (tmp_dam
= damageptr
; tmp_dam
; tmp_dam
= tmp_dam
->next
)
948 if (perform_bwrite(mode
))
949 bwrite(fd
, (genericptr_t
) &xl
, sizeof(xl
));
952 if (perform_bwrite(mode
))
953 bwrite(fd
, (genericptr_t
) damageptr
, sizeof(*damageptr
));
955 damageptr
= damageptr
->next
;
956 if (release_data(mode
))
957 free((genericptr_t
) tmp_dam
);
959 if (release_data(mode
))
960 level
.damagelist
= 0;
968 int buflen
, zerobuf
= 0;
970 buflen
= sizeof(struct obj
);
971 bwrite(fd
, (genericptr_t
) &buflen
, sizeof(int));
972 bwrite(fd
, (genericptr_t
) otmp
, buflen
);
975 buflen
= strlen(ONAME(otmp
)) + 1;
978 bwrite(fd
, (genericptr_t
) &buflen
, sizeof buflen
);
980 bwrite(fd
, (genericptr_t
) ONAME(otmp
), buflen
);
982 /* defer to savemon() for this one */
984 savemon(fd
, OMONST(otmp
));
986 bwrite(fd
, (genericptr_t
) &zerobuf
, sizeof zerobuf
);
989 buflen
= sizeof(unsigned);
992 bwrite(fd
, (genericptr_t
) &buflen
, sizeof buflen
);
994 bwrite(fd
, (genericptr_t
) OMID(otmp
), buflen
);
997 buflen
= sizeof(long);
1000 bwrite(fd
, (genericptr_t
) &buflen
, sizeof buflen
);
1002 bwrite(fd
, (genericptr_t
) OLONG(otmp
), buflen
);
1005 buflen
= strlen(OMAILCMD(otmp
)) + 1;
1008 bwrite(fd
, (genericptr_t
) &buflen
, sizeof buflen
);
1010 bwrite(fd
, (genericptr_t
) OMAILCMD(otmp
), buflen
);
1015 saveobjchn(fd
, otmp
, mode
)
1016 register int fd
, mode
;
1017 register struct obj
*otmp
;
1019 register struct obj
*otmp2
;
1024 if (perform_bwrite(mode
)) {
1027 if (Has_contents(otmp
))
1028 saveobjchn(fd
, otmp
->cobj
, mode
);
1029 if (release_data(mode
)) {
1030 /* if (otmp->oclass == FOOD_CLASS)
1031 * food_disappears(otmp);
1034 * If these are on the floor, the discarding could
1035 * be because of a game save, or we could just be changing levels.
1036 * Always invalidate the pointer, but ensure that we have
1037 * the o_id in order to restore the pointer on reload.
1039 if (otmp
== context
.victual
.piece
) {
1040 /* Store the o_id of the victual if mismatched */
1041 if (context
.victual
.o_id
!= otmp
->o_id
)
1042 context
.victual
.o_id
= otmp
->o_id
;
1043 /* invalidate the pointer; on reload it will get restored */
1044 context
.victual
.piece
= (struct obj
*) 0;
1046 if (otmp
== context
.tin
.tin
) {
1047 /* Store the o_id of your tin */
1048 if (context
.tin
.o_id
!= otmp
->o_id
)
1049 context
.tin
.o_id
= otmp
->o_id
;
1050 /* invalidate the pointer; on reload it will get restored */
1051 context
.tin
.tin
= (struct obj
*) 0;
1053 /* if (otmp->oclass == SPBOOK_CLASS)
1054 * book_disappears(otmp);
1056 if (otmp
== context
.spbook
.book
) {
1057 /* Store the o_id of your spellbook */
1058 if (context
.spbook
.o_id
!= otmp
->o_id
)
1059 context
.spbook
.o_id
= otmp
->o_id
;
1060 /* invalidate the pointer; on reload it will get restored */
1061 context
.spbook
.book
= (struct obj
*) 0;
1063 otmp
->where
= OBJ_FREE
; /* set to free so dealloc will work */
1064 otmp
->nobj
= NULL
; /* nobj saved into otmp2 */
1065 otmp
->cobj
= NULL
; /* contents handled above */
1066 otmp
->timed
= 0; /* not timed any more */
1067 otmp
->lamplit
= 0; /* caller handled lights */
1072 if (perform_bwrite(mode
))
1073 bwrite(fd
, (genericptr_t
) &minusone
, sizeof(int));
1083 buflen
= sizeof(struct monst
);
1084 bwrite(fd
, (genericptr_t
) &buflen
, sizeof(int));
1085 bwrite(fd
, (genericptr_t
) mtmp
, buflen
);
1088 buflen
= strlen(MNAME(mtmp
)) + 1;
1091 bwrite(fd
, (genericptr_t
) &buflen
, sizeof buflen
);
1093 bwrite(fd
, (genericptr_t
) MNAME(mtmp
), buflen
);
1096 buflen
= sizeof(struct egd
);
1099 bwrite(fd
, (genericptr_t
) &buflen
, sizeof(int));
1101 bwrite(fd
, (genericptr_t
) EGD(mtmp
), buflen
);
1104 buflen
= sizeof(struct epri
);
1107 bwrite(fd
, (genericptr_t
) &buflen
, sizeof(int));
1109 bwrite(fd
, (genericptr_t
) EPRI(mtmp
), buflen
);
1112 buflen
= sizeof(struct eshk
);
1115 bwrite(fd
, (genericptr_t
) &buflen
, sizeof(int));
1117 bwrite(fd
, (genericptr_t
) ESHK(mtmp
), buflen
);
1120 buflen
= sizeof(struct emin
);
1123 bwrite(fd
, (genericptr_t
) &buflen
, sizeof(int));
1125 bwrite(fd
, (genericptr_t
) EMIN(mtmp
), buflen
);
1128 buflen
= sizeof(struct edog
);
1131 bwrite(fd
, (genericptr_t
) &buflen
, sizeof(int));
1133 bwrite(fd
, (genericptr_t
) EDOG(mtmp
), buflen
);
1135 /* mcorpsenm is inline int rather than pointer to something,
1136 so doesn't need to be preceded by a length field */
1137 bwrite(fd
, (genericptr_t
) &MCORPSENM(mtmp
), sizeof MCORPSENM(mtmp
));
1142 savemonchn(fd
, mtmp
, mode
)
1143 register int fd
, mode
;
1144 register struct monst
*mtmp
;
1146 register struct monst
*mtmp2
;
1151 if (perform_bwrite(mode
)) {
1152 mtmp
->mnum
= monsndx(mtmp
->data
);
1154 forget_temple_entry(mtmp
); /* EPRI() */
1158 saveobjchn(fd
, mtmp
->minvent
, mode
);
1159 if (release_data(mode
)) {
1160 if (mtmp
== context
.polearm
.hitmon
) {
1161 context
.polearm
.m_id
= mtmp
->m_id
;
1162 context
.polearm
.hitmon
= NULL
;
1164 mtmp
->nmon
= NULL
; /* nmon saved into mtmp2 */
1165 dealloc_monst(mtmp
);
1169 if (perform_bwrite(mode
))
1170 bwrite(fd
, (genericptr_t
) &minusone
, sizeof(int));
1174 savetrapchn(fd
, trap
, mode
)
1175 register int fd
, mode
;
1176 register struct trap
*trap
;
1178 register struct trap
*trap2
;
1181 trap2
= trap
->ntrap
;
1182 if (perform_bwrite(mode
))
1183 bwrite(fd
, (genericptr_t
) trap
, sizeof(struct trap
));
1184 if (release_data(mode
))
1188 if (perform_bwrite(mode
))
1189 bwrite(fd
, (genericptr_t
) nulls
, sizeof(struct trap
));
1192 /* save all the fruit names and ID's; this is used only in saving whole games
1193 * (not levels) and in saving bones levels. When saving a bones level,
1194 * we only want to save the fruits which exist on the bones level; the bones
1195 * level routine marks nonexistent fruits by making the fid negative.
1198 savefruitchn(fd
, mode
)
1199 register int fd
, mode
;
1201 register struct fruit
*f2
, *f1
;
1206 if (f1
->fid
>= 0 && perform_bwrite(mode
))
1207 bwrite(fd
, (genericptr_t
) f1
, sizeof(struct fruit
));
1208 if (release_data(mode
))
1212 if (perform_bwrite(mode
))
1213 bwrite(fd
, (genericptr_t
) nulls
, sizeof(struct fruit
));
1214 if (release_data(mode
))
1219 store_plname_in_file(fd
)
1222 int plsiztmp
= PL_NSIZ
;
1224 /* bwrite() before bufon() uses plain write() */
1225 bwrite(fd
, (genericptr_t
) &plsiztmp
, sizeof(plsiztmp
));
1226 bwrite(fd
, (genericptr_t
) plname
, plsiztmp
);
1232 save_msghistory(fd
, mode
)
1236 int msgcount
= 0, msglen
;
1238 boolean init
= TRUE
;
1240 if (perform_bwrite(mode
)) {
1241 /* ask window port for each message in sequence */
1242 while ((msg
= getmsghistory(init
)) != 0) {
1244 msglen
= strlen(msg
);
1245 /* sanity: truncate if necessary (shouldn't happen);
1246 no need to modify msg[] since terminator isn't written */
1247 if (msglen
> BUFSZ
- 1)
1249 bwrite(fd
, (genericptr_t
) &msglen
, sizeof(msglen
));
1250 bwrite(fd
, (genericptr_t
) msg
, msglen
);
1253 bwrite(fd
, (genericptr_t
) &minusone
, sizeof(int));
1255 debugpline1("Stored %d messages into savefile.", msgcount
);
1256 /* note: we don't attempt to handle release_data() here */
1260 store_savefileinfo(fd
)
1263 /* sfcap (decl.c) describes the savefile feature capabilities
1264 * that are supported by this port/platform build.
1266 * sfsaveinfo (decl.c) describes the savefile info that actually
1267 * gets written into the savefile, and is used to determine the
1268 * save file being written.
1270 * sfrestinfo (decl.c) describes the savefile info that is
1271 * being used to read the information from an existing savefile.
1276 /* bwrite() before bufon() uses plain write() */
1277 bwrite(fd
, (genericptr_t
) &sfsaveinfo
, (unsigned) (sizeof sfsaveinfo
));
1283 set_savepref(suitename
)
1284 const char *suitename
;
1286 if (!strcmpi(suitename
, "externalcomp")) {
1287 saveprocs
.name
= "externalcomp";
1288 saveprocs
.save_bufon
= def_bufon
;
1289 saveprocs
.save_bufoff
= def_bufoff
;
1290 saveprocs
.save_bflush
= def_bflush
;
1291 saveprocs
.save_bwrite
= def_bwrite
;
1292 saveprocs
.save_bclose
= def_bclose
;
1293 sfsaveinfo
.sfi1
|= SFI1_EXTERNALCOMP
;
1294 sfsaveinfo
.sfi1
&= ~SFI1_ZEROCOMP
;
1296 if (!strcmpi(suitename
, "!rlecomp")) {
1297 sfsaveinfo
.sfi1
&= ~SFI1_RLECOMP
;
1300 if (!strcmpi(suitename
, "zerocomp")) {
1301 saveprocs
.name
= "zerocomp";
1302 saveprocs
.save_bufon
= zerocomp_bufon
;
1303 saveprocs
.save_bufoff
= zerocomp_bufoff
;
1304 saveprocs
.save_bflush
= zerocomp_bflush
;
1305 saveprocs
.save_bwrite
= zerocomp_bwrite
;
1306 saveprocs
.save_bclose
= zerocomp_bclose
;
1307 sfsaveinfo
.sfi1
|= SFI1_ZEROCOMP
;
1308 sfsaveinfo
.sfi1
&= ~SFI1_EXTERNALCOMP
;
1312 if (!strcmpi(suitename
, "rlecomp")) {
1313 sfsaveinfo
.sfi1
|= SFI1_RLECOMP
;
1318 /* also called by prscore(); this probably belongs in dungeon.c... */
1322 #ifdef FREE_ALL_MEMORY
1323 savelevchn(0, FREE_SAVE
);
1324 save_dungeon(0, FALSE
, TRUE
);
1333 free_menu_coloring();
1334 free_invbuf(); /* let_to_name (invent.c) */
1335 free_youbuf(); /* You_buf,&c (pline.c) */
1337 tmp_at(DISP_FREEMEM
, 0); /* temporary display effects */
1338 #ifdef FREE_ALL_MEMORY
1339 #define freeobjchn(X) (saveobjchn(0, X, FREE_SAVE), X = 0)
1340 #define freemonchn(X) (savemonchn(0, X, FREE_SAVE), X = 0)
1341 #define freetrapchn(X) (savetrapchn(0, X, FREE_SAVE), X = 0)
1342 #define freefruitchn() savefruitchn(0, FREE_SAVE)
1343 #define freenames() savenames(0, FREE_SAVE)
1344 #define free_killers() save_killers(0, FREE_SAVE)
1345 #define free_oracles() save_oracles(0, FREE_SAVE)
1346 #define free_waterlevel() save_waterlevel(0, FREE_SAVE)
1347 #define free_worm() save_worm(0, FREE_SAVE)
1348 #define free_timers(R) save_timers(0, FREE_SAVE, R)
1349 #define free_light_sources(R) save_light_sources(0, FREE_SAVE, R);
1350 #define free_engravings() save_engravings(0, FREE_SAVE)
1351 #define freedamage() savedamage(0, FREE_SAVE)
1352 #define free_animals() mon_animal_list(FALSE)
1354 /* move-specific data */
1355 dmonsfree(); /* release dead monsters */
1357 /* level-specific data */
1358 free_timers(RANGE_LEVEL
);
1359 free_light_sources(RANGE_LEVEL
);
1362 free_worm(); /* release worm segment information */
1365 freeobjchn(level
.buriedobjlist
);
1366 freeobjchn(billobjs
);
1370 /* game-state data */
1372 free_timers(RANGE_GLOBAL
);
1373 free_light_sources(RANGE_GLOBAL
);
1375 freeobjchn(migrating_objs
);
1376 freemonchn(migrating_mons
);
1377 freemonchn(mydogs
); /* ascension or dungeon escape */
1378 /* freelevchn(); -- [folded into free_dungeons()] */
1386 /* some pointers in iflags */
1387 if (iflags
.wc_font_map
)
1388 free(iflags
.wc_font_map
);
1389 if (iflags
.wc_font_message
)
1390 free(iflags
.wc_font_message
);
1391 if (iflags
.wc_font_text
)
1392 free(iflags
.wc_font_text
);
1393 if (iflags
.wc_font_menu
)
1394 free(iflags
.wc_font_menu
);
1395 if (iflags
.wc_font_status
)
1396 free(iflags
.wc_font_status
);
1397 if (iflags
.wc_tile_file
)
1398 free(iflags
.wc_tile_file
);
1399 free_autopickup_exceptions();
1402 /* free_pickinv_cache(); -- now done from really_done()... */
1404 #endif /* FREE_ALL_MEMORY */
1405 #ifdef STATUS_VIA_WINDOWPORT
1409 /* last, because it frees data that might be used by panic() to provide
1410 feedback to the user; conceivably other freeing might trigger panic */
1411 sysopt_release(); /* SYSCF strings */
1420 char to
[PATHLEN
], from
[PATHLEN
];
1422 Sprintf(from
, "%s%s", permbones
, alllevels
);
1423 Sprintf(to
, "%s%s", levels
, alllevels
);
1424 set_levelfile_name(from
, lev
);
1425 set_levelfile_name(to
, lev
);
1426 if (iflags
.checkspace
) {
1427 while (level_info
[lev
].size
> freediskspace(to
))
1428 if (!swapout_oldest())
1432 pline("Swapping in `%s'.", from
);
1436 (void) unlink(from
);
1437 level_info
[lev
].where
= ACTIVE
;
1444 char to
[PATHLEN
], from
[PATHLEN
];
1450 for (i
= 1, oldtime
= 0, oldest
= 0; i
<= maxledgerno(); i
++)
1451 if (level_info
[i
].where
== ACTIVE
1452 && (!oldtime
|| level_info
[i
].time
< oldtime
)) {
1454 oldtime
= level_info
[i
].time
;
1458 Sprintf(from
, "%s%s", levels
, alllevels
);
1459 Sprintf(to
, "%s%s", permbones
, alllevels
);
1460 set_levelfile_name(from
, oldest
);
1461 set_levelfile_name(to
, oldest
);
1463 pline("Swapping out `%s'.", from
);
1467 (void) unlink(from
);
1468 level_info
[oldest
].where
= SWAPPED
;
1477 if (_copyfile(from
, to
))
1478 panic("Can't copy %s to %s", from
, to
);
1480 char buf
[BUFSIZ
]; /* this is system interaction, therefore
1481 * BUFSIZ instead of NetHack's BUFSZ */
1482 int nfrom
, nto
, fdfrom
, fdto
;
1484 if ((fdfrom
= open(from
, O_RDONLY
| O_BINARY
, FCMASK
)) < 0)
1485 panic("Can't copy from %s !?", from
);
1486 if ((fdto
= open(to
, O_WRONLY
| O_BINARY
| O_CREAT
| O_TRUNC
, FCMASK
)) < 0)
1487 panic("Can't copy to %s", to
);
1489 nfrom
= read(fdfrom
, buf
, BUFSIZ
);
1490 nto
= write(fdto
, buf
, nfrom
);
1492 panic("Copyfile failed!");
1493 } while (nfrom
== BUFSIZ
);
1494 (void) nhclose(fdfrom
);
1495 (void) nhclose(fdto
);
1499 /* see comment in bones.c */
1507 #endif /* MFLOPPY */