OSDN Git Service

551eadc701c851af1ca00d628b31405e9523a639
[jnethack/source.git] / src / save.c
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. */
4
5 #include "hack.h"
6 #include "lev.h"
7
8 #ifndef NO_SIGNAL
9 #include <signal.h>
10 #endif
11 #if !defined(LSC) && !defined(O_WRONLY) && !defined(AZTEC_C)
12 #include <fcntl.h>
13 #endif
14
15 #ifdef MFLOPPY
16 long bytes_counted;
17 static int count_only;
18 #endif
19
20 #ifdef MICRO
21 int dotcnt, dotrow; /* also used in restore */
22 #endif
23
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));
33 #ifdef MFLOPPY
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 *));
37 #endif /* MFLOPPY */
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));
43 #ifdef ZEROCOMP
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));
49 #endif
50
51 static struct save_procs {
52     const char *name;
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));
58 } saveprocs = {
59 #if !defined(ZEROCOMP) || (defined(COMPRESS) || defined(ZLIB_COMP))
60     "externalcomp", def_bufon, def_bufoff, def_bflush, def_bwrite, def_bclose,
61 #else
62     "zerocomp",      zerocomp_bufon,  zerocomp_bufoff,
63     zerocomp_bflush, zerocomp_bwrite, zerocomp_bclose,
64 #endif
65 };
66
67 static long nulls[sizeof(struct trap) + sizeof(struct fruit)];
68
69 #if defined(UNIX) || defined(VMS) || defined(__EMX__) || defined(WIN32)
70 #define HUP if (!program_state.done_hup)
71 #else
72 #define HUP
73 #endif
74
75 /* need to preserve these during save to avoid accessing freed memory */
76 static unsigned ustuck_id = 0, usteed_id = 0;
77
78 int
79 dosave()
80 {
81     clear_nhwindow(WIN_MESSAGE);
82     if (yn("Really save?") == 'n') {
83         clear_nhwindow(WIN_MESSAGE);
84         if (multi > 0)
85             nomul(0);
86     } else {
87         clear_nhwindow(WIN_MESSAGE);
88         pline("Saving...");
89 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
90         program_state.done_hup = 0;
91 #endif
92         if (dosave0()) {
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);
98         } else
99             (void) doredraw();
100     }
101     return 0;
102 }
103
104 /* returns 1 if save successful */
105 int
106 dosave0()
107 {
108     const char *fq_save;
109     register int fd, ofd;
110     xchar ltmp;
111     d_level uz_save;
112     char whynot[BUFSZ];
113
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 */
117     u.uinvulnerable = 0;
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;
122
123     if (!program_state.something_worth_saving || !SAVEF[0])
124         return 0;
125     fq_save = fqname(SAVEF, SAVEPREFIX, 1); /* level files take 0 */
126
127 #if defined(UNIX) || defined(VMS)
128     sethanguphandler((void FDECL((*), (int) )) SIG_IGN);
129 #endif
130 #ifndef NO_SIGNAL
131     (void) signal(SIGINT, SIG_IGN);
132 #endif
133
134 #if defined(MICRO) && defined(MFLOPPY)
135     if (!saveDiskPrompt(0))
136         return 0;
137 #endif
138
139     HUP if (iflags.window_inited)
140     {
141         nh_uncompress(fq_save);
142         fd = open_savefile();
143         if (fd > 0) {
144             (void) nhclose(fd);
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);
149                 return 0;
150             }
151         }
152     }
153
154     HUP mark_synch(); /* flush any buffered screen output */
155
156     fd = create_savefile();
157     if (fd < 0) {
158         HUP pline("Cannot open save file.");
159         (void) delete_savefile(); /* ab@unido */
160         return 0;
161     }
162
163     vision_recalc(2); /* shut down vision to prevent problems
164                          in the event of an impossible() call */
165
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 */
169     if (flags.friday13)
170         change_luck(1);
171     if (iflags.window_inited)
172         HUP clear_nhwindow(WIN_MESSAGE);
173
174 #ifdef MICRO
175     dotcnt = 0;
176     dotrow = 2;
177     curs(WIN_MAP, 1, 1);
178     if (strncmpi("X11", windowprocs.name, 3))
179         putstr(WIN_MAP, 0, "Saving:");
180 #endif
181 #ifdef MFLOPPY
182     /* make sure there is enough disk space */
183     if (iflags.checkspace) {
184         long fds, needed;
185
186         savelev(fd, ledger_no(&u.uz), COUNT_SAVE);
187         savegamestate(fd, COUNT_SAVE);
188         needed = bytes_counted;
189
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);
194         if (needed > fds) {
195             HUP
196             {
197                 There("is insufficient space on SAVE disk.");
198                 pline("Require %ld bytes but only have %ld.", needed, fds);
199             }
200             flushout();
201             (void) nhclose(fd);
202             (void) delete_savefile();
203             return 0;
204         }
205
206         co_false();
207     }
208 #endif /* MFLOPPY */
209
210     store_version(fd);
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);
217
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.
222      */
223     uz_save = u.uz;
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
227      */
228     u.ustuck = (struct monst *) 0;
229     u.usteed = (struct monst *) 0;
230
231     for (ltmp = (xchar) 1; ltmp <= maxledgerno(); ltmp++) {
232         if (ltmp == ledger_no(&uz_save))
233             continue;
234         if (!(level_info[ltmp].flags & LFILE_EXISTS))
235             continue;
236 #ifdef MICRO
237         curs(WIN_MAP, 1 + dotcnt++, dotrow);
238         if (dotcnt >= (COLNO - 1)) {
239             dotrow++;
240             dotcnt = 0;
241         }
242         if (strncmpi("X11", windowprocs.name, 3)) {
243             putstr(WIN_MAP, 0, ".");
244         }
245         mark_synch();
246 #endif
247         ofd = open_levelfile(ltmp, whynot);
248         if (ofd < 0) {
249             HUP pline1(whynot);
250             (void) nhclose(fd);
251             (void) delete_savefile();
252             HUP Strcpy(killer.name, whynot);
253             HUP done(TRICKED);
254             return 0;
255         }
256         minit(); /* ZEROCOMP */
257         getlev(ofd, hackpid, ltmp, FALSE);
258         (void) nhclose(ofd);
259         bwrite(fd, (genericptr_t) &ltmp, sizeof ltmp); /* level number*/
260         savelev(fd, ltmp, WRITE_SAVE | FREE_SAVE);     /* actual level*/
261         delete_levelfile(ltmp);
262     }
263     bclose(fd);
264
265     u.uz = uz_save;
266
267     /* get rid of current level --jgm */
268     delete_levelfile(ledger_no(&u.uz));
269     delete_levelfile(0);
270     nh_compress(fq_save);
271     /* this should probably come sooner... */
272     program_state.something_worth_saving = 0;
273     return 1;
274 }
275
276 STATIC_OVL void
277 savegamestate(fd, mode)
278 register int fd, mode;
279 {
280     unsigned long uid;
281
282 #ifdef MFLOPPY
283     count_only = (mode & COUNT_SAVE);
284 #endif
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));
289 #ifdef SYSFLAGS
290     bwrite(fd, (genericptr_t) &sysflags, sizeof(struct sysflag));
291 #endif
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);
302
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);
306
307     saveobjchn(fd, invent, mode);
308     if (BALL_IN_MON) {
309         /* prevent loss of ball & chain when swallowed */
310         uball->nobj = uchain;
311         uchain->nobj = (struct obj *) 0;
312         saveobjchn(fd, uball, mode);
313     } else {
314         saveobjchn(fd, (struct obj *) 0, mode);
315     }
316
317     saveobjchn(fd, migrating_objs, mode);
318     savemonchn(fd, migrating_mons, mode);
319     if (release_data(mode)) {
320         invent = 0;
321         migrating_objs = 0;
322         migrating_mons = 0;
323     }
324     bwrite(fd, (genericptr_t) mvitals, sizeof(mvitals));
325
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));
334     save_artifacts(fd);
335     save_oracles(fd, mode);
336     if (ustuck_id)
337         bwrite(fd, (genericptr_t) &ustuck_id, sizeof ustuck_id);
338     if (usteed_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);
343     savenames(fd, mode);
344     save_waterlevel(fd, mode);
345     save_msghistory(fd, mode);
346     bflush(fd);
347 }
348
349 boolean
350 tricked_fileremoved(fd, whynot)
351 int fd;
352 char *whynot;
353 {
354     if (fd < 0) {
355         pline1(whynot);
356         pline("Probably someone removed it.");
357         Strcpy(killer.name, whynot);
358         done(TRICKED);
359         return TRUE;
360     }
361     return FALSE;
362 }
363
364 #ifdef INSURANCE
365 void
366 savestateinlock()
367 {
368     int fd, hpid;
369     static boolean havestate = TRUE;
370     char whynot[BUFSZ];
371
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.
378      *
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.
382      */
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
389          */
390         fd = open_levelfile(0, whynot);
391         if (tricked_fileremoved(fd, whynot))
392             return;
393
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)!",
397                     hpid, hackpid);
398             pline1(whynot);
399             Strcpy(killer.name, whynot);
400             done(TRICKED);
401         }
402         (void) nhclose(fd);
403
404         fd = create_levelfile(0, whynot);
405         if (fd < 0) {
406             pline1(whynot);
407             Strcpy(killer.name, whynot);
408             done(TRICKED);
409             return;
410         }
411         (void) write(fd, (genericptr_t) &hackpid, sizeof(hackpid));
412         if (flags.ins_chkpt) {
413             int currlev = ledger_no(&u.uz);
414
415             (void) write(fd, (genericptr_t) &currlev, sizeof(currlev));
416             save_savefile_name(fd);
417             store_version(fd);
418             store_savefileinfo(fd);
419             store_plname_in_file(fd);
420
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);
424         }
425         bclose(fd);
426     }
427     havestate = flags.ins_chkpt;
428 }
429 #endif
430
431 #ifdef MFLOPPY
432 boolean
433 savelev(fd, lev, mode)
434 int fd;
435 xchar lev;
436 int mode;
437 {
438     if (mode & COUNT_SAVE) {
439         bytes_counted = 0;
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())
447                     return FALSE;
448         }
449     }
450     if (mode & (WRITE_SAVE | FREE_SAVE)) {
451         bytes_counted = 0;
452         savelev0(fd, lev, mode);
453     }
454     if (mode != FREE_SAVE) {
455         level_info[lev].where = ACTIVE;
456         level_info[lev].time = moves;
457         level_info[lev].size = bytes_counted;
458     }
459     return TRUE;
460 }
461
462 STATIC_OVL void
463 savelev0(fd, lev, mode)
464 #else
465 void
466 savelev(fd, lev, mode)
467 #endif
468 int fd;
469 xchar lev;
470 int mode;
471 {
472 #ifdef TOS
473     short tlev;
474 #endif
475
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)
480         goto skip_lots;
481
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)
487         dmonsfree();
488
489     if (fd < 0)
490         panic("Save on bad file!"); /* impossible */
491 #ifdef MFLOPPY
492     count_only = (mode & COUNT_SAVE);
493 #endif
494     if (lev >= 0 && lev <= maxledgerno())
495         level_info[lev].flags |= VISITED;
496     bwrite(fd, (genericptr_t) &hackpid, sizeof(hackpid));
497 #ifdef TOS
498     tlev = lev;
499     tlev &= 0x00ff;
500     bwrite(fd, (genericptr_t) &tlev, sizeof(tlev));
501 #else
502     bwrite(fd, (genericptr_t) &lev, sizeof(lev));
503 #endif
504     savecemetery(fd, mode, &level.bonesinfo);
505     savelevl(fd,
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 */
519
520 /* from here on out, saving also involves allocated memory cleanup */
521 skip_lots:
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);
528
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)) {
536         fmon = 0;
537         ftrap = 0;
538         fobj = 0;
539         level.buriedobjlist = 0;
540         billobjs = 0;
541         /* level.bonesinfo = 0; -- handled by savecemetery() */
542     }
543     save_engravings(fd, mode);
544     savedamage(fd, mode);
545     save_regions(fd, mode);
546     if (mode != FREE_SAVE)
547         bflush(fd);
548 }
549
550 STATIC_OVL void
551 savelevl(fd, rlecomp)
552 int fd;
553 boolean rlecomp;
554 {
555 #ifdef RLECOMP
556     struct rm *prm, *rgrm;
557     int x, y;
558     uchar match;
559
560     if (rlecomp) {
561         /* perform run-length encoding of rm structs */
562
563         rgrm = &levl[0][0]; /* start matching at first rm */
564         match = 0;
565
566         for (y = 0; y < ROWNO; y++) {
567             for (x = 0; x < COLNO; x++) {
568                 prm = &levl[x][y];
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) {
576                     match++;
577                     if (match > 254) {
578                         match = 254; /* undo this match */
579                         goto writeout;
580                     }
581                 } else {
582                 /* the run has been broken,
583                  * write out run-length encoding */
584                 writeout:
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. */
589                     match = 1;
590                     rgrm = prm;
591                 }
592             }
593         }
594         if (match > 0) {
595             bwrite(fd, (genericptr_t) &match, sizeof(uchar));
596             bwrite(fd, (genericptr_t) rgrm, sizeof(struct rm));
597         }
598         return;
599     }
600 #else /* !RLECOMP */
601     nhUse(rlecomp);
602 #endif /* ?RLECOMP */
603     bwrite(fd, (genericptr_t) levl, sizeof levl);
604 }
605
606 /*ARGSUSED*/
607 void
608 bufon(fd)
609 int fd;
610 {
611     (*saveprocs.save_bufon)(fd);
612     return;
613 }
614
615 /*ARGSUSED*/
616 void
617 bufoff(fd)
618 int fd;
619 {
620     (*saveprocs.save_bufoff)(fd);
621     return;
622 }
623
624 /* flush run and buffer */
625 void
626 bflush(fd)
627 register int fd;
628 {
629     (*saveprocs.save_bflush)(fd);
630     return;
631 }
632
633 void
634 bwrite(fd, loc, num)
635 int fd;
636 genericptr_t loc;
637 register unsigned num;
638 {
639     (*saveprocs.save_bwrite)(fd, loc, num);
640     return;
641 }
642
643 void
644 bclose(fd)
645 int fd;
646 {
647     (*saveprocs.save_bclose)(fd);
648     return;
649 }
650
651 static int bw_fd = -1;
652 static FILE *bw_FILE = 0;
653 static boolean buffering = FALSE;
654
655 STATIC_OVL void
656 def_bufon(fd)
657 int fd;
658 {
659 #ifdef UNIX
660     if (bw_fd != fd) {
661         if (bw_fd >= 0)
662             panic("double buffering unexpected");
663         bw_fd = fd;
664         if ((bw_FILE = fdopen(fd, "w")) == 0)
665             panic("buffering of file %d failed", fd);
666     }
667 #endif
668     buffering = TRUE;
669 }
670
671 STATIC_OVL void
672 def_bufoff(fd)
673 int fd;
674 {
675     def_bflush(fd);
676     buffering = FALSE;
677 }
678
679 STATIC_OVL void
680 def_bflush(fd)
681 int fd;
682 {
683 #ifdef UNIX
684     if (fd == bw_fd) {
685         if (fflush(bw_FILE) == EOF)
686             panic("flush of savefile failed!");
687     }
688 #endif
689     return;
690 }
691
692 STATIC_OVL void
693 def_bwrite(fd, loc, num)
694 register int fd;
695 register genericptr_t loc;
696 register unsigned num;
697 {
698     boolean failed;
699
700 #ifdef MFLOPPY
701     bytes_counted += num;
702     if (count_only)
703         return;
704 #endif
705
706 #ifdef UNIX
707     if (buffering) {
708         if (fd != bw_fd)
709             panic("unbuffered write to fd %d (!= %d)", fd, bw_fd);
710
711         failed = (fwrite(loc, (int) num, 1, bw_FILE) != 1);
712     } else
713 #endif /* UNIX */
714     {
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);
720 #endif
721     }
722
723     if (failed) {
724 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
725         if (program_state.done_hup)
726             terminate(EXIT_FAILURE);
727         else
728 #endif
729             panic("cannot write %u bytes to file #%d", num, fd);
730     }
731 }
732
733 void
734 def_bclose(fd)
735 int fd;
736 {
737     bufoff(fd);
738 #ifdef UNIX
739     if (fd == bw_fd) {
740         (void) fclose(bw_FILE);
741         bw_fd = -1;
742         bw_FILE = 0;
743     } else
744 #endif
745         (void) nhclose(fd);
746     return;
747 }
748
749 #ifdef ZEROCOMP
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.
756  */
757
758 #define RLESC '\0' /* Leading character for run of LRESC's */
759 #define flushoutrun(ln) (zerocomp_bputc(RLESC), zerocomp_bputc(ln), ln = -1)
760
761 #ifndef ZEROCOMP_BUFSIZ
762 #define ZEROCOMP_BUFSIZ BUFSZ
763 #endif
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;
769
770 /*dbg()
771 {
772     HUP printf("outbufp %d outrunlength %d\n", outbufp,outrunlength);
773 }*/
774
775 STATIC_OVL void
776 zerocomp_bputc(c)
777 int c;
778 {
779 #ifdef MFLOPPY
780     bytes_counted++;
781     if (count_only)
782         return;
783 #endif
784     if (outbufp >= sizeof outbuf) {
785         (void) write(bwritefd, outbuf, sizeof outbuf);
786         outbufp = 0;
787     }
788     outbuf[outbufp++] = (unsigned char) c;
789 }
790
791 /*ARGSUSED*/
792 void STATIC_OVL
793 zerocomp_bufon(fd)
794 int fd;
795 {
796     compressing = TRUE;
797     return;
798 }
799
800 /*ARGSUSED*/
801 STATIC_OVL void
802 zerocomp_bufoff(fd)
803 int fd;
804 {
805     if (outbufp) {
806         outbufp = 0;
807         panic("closing file with buffered data still unwritten");
808     }
809     outrunlength = -1;
810     compressing = FALSE;
811     return;
812 }
813
814 /* flush run and buffer */
815 STATIC_OVL void
816 zerocomp_bflush(fd)
817 register int fd;
818 {
819     bwritefd = fd;
820     if (outrunlength >= 0) { /* flush run */
821         flushoutrun(outrunlength);
822     }
823 #ifdef MFLOPPY
824     if (count_only)
825         outbufp = 0;
826 #endif
827
828     if (outbufp) {
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);
833             else
834 #endif
835                 zerocomp_bclose(fd); /* panic (outbufp != 0) */
836         }
837         outbufp = 0;
838     }
839 }
840
841 STATIC_OVL void
842 zerocomp_bwrite(fd, loc, num)
843 int fd;
844 genericptr_t loc;
845 register unsigned num;
846 {
847     register unsigned char *bp = (unsigned char *) loc;
848
849     if (!compressing) {
850 #ifdef MFLOPPY
851         bytes_counted += num;
852         if (count_only)
853             return;
854 #endif
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);
859             else
860 #endif
861                 panic("cannot write %u bytes to file #%d", num, fd);
862         }
863     } else {
864         bwritefd = fd;
865         for (; num; num--, bp++) {
866             if (*bp == RLESC) { /* One more char in run */
867                 if (++outrunlength == 0xFF) {
868                     flushoutrun(outrunlength);
869                 }
870             } else {                     /* end of run */
871                 if (outrunlength >= 0) { /* flush run */
872                     flushoutrun(outrunlength);
873                 }
874                 zerocomp_bputc(*bp);
875             }
876         }
877     }
878 }
879
880 void
881 zerocomp_bclose(fd)
882 int fd;
883 {
884     zerocomp_bufoff(fd);
885     (void) nhclose(fd);
886     return;
887 }
888 #endif /* ZEROCOMP */
889
890 STATIC_OVL void
891 savelevchn(fd, mode)
892 register int fd, mode;
893 {
894     s_level *tmplev, *tmplev2;
895     int cnt = 0;
896
897     for (tmplev = sp_levchn; tmplev; tmplev = tmplev->next)
898         cnt++;
899     if (perform_bwrite(mode))
900         bwrite(fd, (genericptr_t) &cnt, sizeof(int));
901
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);
908     }
909     if (release_data(mode))
910         sp_levchn = 0;
911 }
912
913 /* used when saving a level and also when saving dungeon overview data */
914 void
915 savecemetery(fd, mode, cemeteryaddr)
916 int fd;
917 int mode;
918 struct cemetery **cemeteryaddr;
919 {
920     struct cemetery *thisbones, *nextbones;
921     int flag;
922
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);
933     }
934     if (release_data(mode))
935         *cemeteryaddr = 0;
936 }
937
938 STATIC_OVL void
939 savedamage(fd, mode)
940 register int fd, mode;
941 {
942     register struct damage *damageptr, *tmp_dam;
943     unsigned int xl = 0;
944
945     damageptr = level.damagelist;
946     for (tmp_dam = damageptr; tmp_dam; tmp_dam = tmp_dam->next)
947         xl++;
948     if (perform_bwrite(mode))
949         bwrite(fd, (genericptr_t) &xl, sizeof(xl));
950
951     while (xl--) {
952         if (perform_bwrite(mode))
953             bwrite(fd, (genericptr_t) damageptr, sizeof(*damageptr));
954         tmp_dam = damageptr;
955         damageptr = damageptr->next;
956         if (release_data(mode))
957             free((genericptr_t) tmp_dam);
958     }
959     if (release_data(mode))
960         level.damagelist = 0;
961 }
962
963 STATIC_OVL void
964 saveobj(fd, otmp)
965 int fd;
966 struct obj *otmp;
967 {
968     int buflen, zerobuf = 0;
969
970     buflen = sizeof(struct obj);
971     bwrite(fd, (genericptr_t) &buflen, sizeof(int));
972     bwrite(fd, (genericptr_t) otmp, buflen);
973     if (otmp->oextra) {
974         if (ONAME(otmp))
975             buflen = strlen(ONAME(otmp)) + 1;
976         else
977             buflen = 0;
978         bwrite(fd, (genericptr_t) &buflen, sizeof buflen);
979         if (buflen > 0)
980             bwrite(fd, (genericptr_t) ONAME(otmp), buflen);
981
982         /* defer to savemon() for this one */
983         if (OMONST(otmp))
984             savemon(fd, OMONST(otmp));
985         else
986             bwrite(fd, (genericptr_t) &zerobuf, sizeof zerobuf);
987
988         if (OMID(otmp))
989             buflen = sizeof(unsigned);
990         else
991             buflen = 0;
992         bwrite(fd, (genericptr_t) &buflen, sizeof buflen);
993         if (buflen > 0)
994             bwrite(fd, (genericptr_t) OMID(otmp), buflen);
995
996         if (OLONG(otmp))
997             buflen = sizeof(long);
998         else
999             buflen = 0;
1000         bwrite(fd, (genericptr_t) &buflen, sizeof buflen);
1001         if (buflen > 0)
1002             bwrite(fd, (genericptr_t) OLONG(otmp), buflen);
1003
1004         if (OMAILCMD(otmp))
1005             buflen = strlen(OMAILCMD(otmp)) + 1;
1006         else
1007             buflen = 0;
1008         bwrite(fd, (genericptr_t) &buflen, sizeof buflen);
1009         if (buflen > 0)
1010             bwrite(fd, (genericptr_t) OMAILCMD(otmp), buflen);
1011     }
1012 }
1013
1014 STATIC_OVL void
1015 saveobjchn(fd, otmp, mode)
1016 register int fd, mode;
1017 register struct obj *otmp;
1018 {
1019     register struct obj *otmp2;
1020     int minusone = -1;
1021
1022     while (otmp) {
1023         otmp2 = otmp->nobj;
1024         if (perform_bwrite(mode)) {
1025             saveobj(fd, otmp);
1026         }
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);
1032              */
1033             /*
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.
1038              */
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;
1045             }
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;
1052             }
1053             /*  if (otmp->oclass == SPBOOK_CLASS)
1054              *      book_disappears(otmp);
1055              */
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;
1062             }
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 */
1068             dealloc_obj(otmp);
1069         }
1070         otmp = otmp2;
1071     }
1072     if (perform_bwrite(mode))
1073         bwrite(fd, (genericptr_t) &minusone, sizeof(int));
1074 }
1075
1076 STATIC_OVL void
1077 savemon(fd, mtmp)
1078 int fd;
1079 struct monst *mtmp;
1080 {
1081     int buflen;
1082
1083     buflen = sizeof(struct monst);
1084     bwrite(fd, (genericptr_t) &buflen, sizeof(int));
1085     bwrite(fd, (genericptr_t) mtmp, buflen);
1086     if (mtmp->mextra) {
1087         if (MNAME(mtmp))
1088             buflen = strlen(MNAME(mtmp)) + 1;
1089         else
1090             buflen = 0;
1091         bwrite(fd, (genericptr_t) &buflen, sizeof buflen);
1092         if (buflen > 0)
1093             bwrite(fd, (genericptr_t) MNAME(mtmp), buflen);
1094
1095         if (EGD(mtmp))
1096             buflen = sizeof(struct egd);
1097         else
1098             buflen = 0;
1099         bwrite(fd, (genericptr_t) &buflen, sizeof(int));
1100         if (buflen > 0)
1101             bwrite(fd, (genericptr_t) EGD(mtmp), buflen);
1102
1103         if (EPRI(mtmp))
1104             buflen = sizeof(struct epri);
1105         else
1106             buflen = 0;
1107         bwrite(fd, (genericptr_t) &buflen, sizeof(int));
1108         if (buflen > 0)
1109             bwrite(fd, (genericptr_t) EPRI(mtmp), buflen);
1110
1111         if (ESHK(mtmp))
1112             buflen = sizeof(struct eshk);
1113         else
1114             buflen = 0;
1115         bwrite(fd, (genericptr_t) &buflen, sizeof(int));
1116         if (buflen > 0)
1117             bwrite(fd, (genericptr_t) ESHK(mtmp), buflen);
1118
1119         if (EMIN(mtmp))
1120             buflen = sizeof(struct emin);
1121         else
1122             buflen = 0;
1123         bwrite(fd, (genericptr_t) &buflen, sizeof(int));
1124         if (buflen > 0)
1125             bwrite(fd, (genericptr_t) EMIN(mtmp), buflen);
1126
1127         if (EDOG(mtmp))
1128             buflen = sizeof(struct edog);
1129         else
1130             buflen = 0;
1131         bwrite(fd, (genericptr_t) &buflen, sizeof(int));
1132         if (buflen > 0)
1133             bwrite(fd, (genericptr_t) EDOG(mtmp), buflen);
1134
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));
1138     }
1139 }
1140
1141 STATIC_OVL void
1142 savemonchn(fd, mtmp, mode)
1143 register int fd, mode;
1144 register struct monst *mtmp;
1145 {
1146     register struct monst *mtmp2;
1147     int minusone = -1;
1148
1149     while (mtmp) {
1150         mtmp2 = mtmp->nmon;
1151         if (perform_bwrite(mode)) {
1152             mtmp->mnum = monsndx(mtmp->data);
1153             if (mtmp->ispriest)
1154                 forget_temple_entry(mtmp); /* EPRI() */
1155             savemon(fd, mtmp);
1156         }
1157         if (mtmp->minvent)
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;
1163             }
1164             mtmp->nmon = NULL;  /* nmon saved into mtmp2 */
1165             dealloc_monst(mtmp);
1166         }
1167         mtmp = mtmp2;
1168     }
1169     if (perform_bwrite(mode))
1170         bwrite(fd, (genericptr_t) &minusone, sizeof(int));
1171 }
1172
1173 STATIC_OVL void
1174 savetrapchn(fd, trap, mode)
1175 register int fd, mode;
1176 register struct trap *trap;
1177 {
1178     register struct trap *trap2;
1179
1180     while (trap) {
1181         trap2 = trap->ntrap;
1182         if (perform_bwrite(mode))
1183             bwrite(fd, (genericptr_t) trap, sizeof(struct trap));
1184         if (release_data(mode))
1185             dealloc_trap(trap);
1186         trap = trap2;
1187     }
1188     if (perform_bwrite(mode))
1189         bwrite(fd, (genericptr_t) nulls, sizeof(struct trap));
1190 }
1191
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.
1196  */
1197 void
1198 savefruitchn(fd, mode)
1199 register int fd, mode;
1200 {
1201     register struct fruit *f2, *f1;
1202
1203     f1 = ffruit;
1204     while (f1) {
1205         f2 = f1->nextf;
1206         if (f1->fid >= 0 && perform_bwrite(mode))
1207             bwrite(fd, (genericptr_t) f1, sizeof(struct fruit));
1208         if (release_data(mode))
1209             dealloc_fruit(f1);
1210         f1 = f2;
1211     }
1212     if (perform_bwrite(mode))
1213         bwrite(fd, (genericptr_t) nulls, sizeof(struct fruit));
1214     if (release_data(mode))
1215         ffruit = 0;
1216 }
1217
1218 void
1219 store_plname_in_file(fd)
1220 int fd;
1221 {
1222     int plsiztmp = PL_NSIZ;
1223     bufoff(fd);
1224     /* bwrite() before bufon() uses plain write() */
1225     bwrite(fd, (genericptr_t) &plsiztmp, sizeof(plsiztmp));
1226     bwrite(fd, (genericptr_t) plname, plsiztmp);
1227     bufon(fd);
1228     return;
1229 }
1230
1231 STATIC_OVL void
1232 save_msghistory(fd, mode)
1233 int fd, mode;
1234 {
1235     char *msg;
1236     int msgcount = 0, msglen;
1237     int minusone = -1;
1238     boolean init = TRUE;
1239
1240     if (perform_bwrite(mode)) {
1241         /* ask window port for each message in sequence */
1242         while ((msg = getmsghistory(init)) != 0) {
1243             init = FALSE;
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)
1248                 msglen = BUFSZ - 1;
1249             bwrite(fd, (genericptr_t) &msglen, sizeof(msglen));
1250             bwrite(fd, (genericptr_t) msg, msglen);
1251             ++msgcount;
1252         }
1253         bwrite(fd, (genericptr_t) &minusone, sizeof(int));
1254     }
1255     debugpline1("Stored %d messages into savefile.", msgcount);
1256     /* note: we don't attempt to handle release_data() here */
1257 }
1258
1259 void
1260 store_savefileinfo(fd)
1261 int fd;
1262 {
1263     /* sfcap (decl.c) describes the savefile feature capabilities
1264      * that are supported by this port/platform build.
1265      *
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.
1269
1270      * sfrestinfo (decl.c) describes the savefile info that is
1271      * being used to read the information from an existing savefile.
1272      *
1273      */
1274
1275     bufoff(fd);
1276     /* bwrite() before bufon() uses plain write() */
1277     bwrite(fd, (genericptr_t) &sfsaveinfo, (unsigned) (sizeof sfsaveinfo));
1278     bufon(fd);
1279     return;
1280 }
1281
1282 void
1283 set_savepref(suitename)
1284 const char *suitename;
1285 {
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;
1295     }
1296     if (!strcmpi(suitename, "!rlecomp")) {
1297         sfsaveinfo.sfi1 &= ~SFI1_RLECOMP;
1298     }
1299 #ifdef ZEROCOMP
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;
1309     }
1310 #endif
1311 #ifdef RLECOMP
1312     if (!strcmpi(suitename, "rlecomp")) {
1313         sfsaveinfo.sfi1 |= SFI1_RLECOMP;
1314     }
1315 #endif
1316 }
1317
1318 /* also called by prscore(); this probably belongs in dungeon.c... */
1319 void
1320 free_dungeons()
1321 {
1322 #ifdef FREE_ALL_MEMORY
1323     savelevchn(0, FREE_SAVE);
1324     save_dungeon(0, FALSE, TRUE);
1325 #endif
1326     return;
1327 }
1328
1329 void
1330 freedynamicdata()
1331 {
1332     unload_qtlist();
1333     free_menu_coloring();
1334     free_invbuf();           /* let_to_name (invent.c) */
1335     free_youbuf();           /* You_buf,&c (pline.c) */
1336     msgtype_free();
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)
1353
1354     /* move-specific data */
1355     dmonsfree(); /* release dead monsters */
1356
1357     /* level-specific data */
1358     free_timers(RANGE_LEVEL);
1359     free_light_sources(RANGE_LEVEL);
1360     clear_regions();
1361     freemonchn(fmon);
1362     free_worm(); /* release worm segment information */
1363     freetrapchn(ftrap);
1364     freeobjchn(fobj);
1365     freeobjchn(level.buriedobjlist);
1366     freeobjchn(billobjs);
1367     free_engravings();
1368     freedamage();
1369
1370     /* game-state data */
1371     free_killers();
1372     free_timers(RANGE_GLOBAL);
1373     free_light_sources(RANGE_GLOBAL);
1374     freeobjchn(invent);
1375     freeobjchn(migrating_objs);
1376     freemonchn(migrating_mons);
1377     freemonchn(mydogs); /* ascension or dungeon escape */
1378     /* freelevchn();  --  [folded into free_dungeons()] */
1379     free_animals();
1380     free_oracles();
1381     freefruitchn();
1382     freenames();
1383     free_waterlevel();
1384     free_dungeons();
1385
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();
1400
1401     /* miscellaneous */
1402     /* free_pickinv_cache();  --  now done from really_done()... */
1403     free_symsets();
1404 #endif /* FREE_ALL_MEMORY */
1405 #ifdef STATUS_VIA_WINDOWPORT
1406     status_finish();
1407 #endif
1408
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 */
1412     return;
1413 }
1414
1415 #ifdef MFLOPPY
1416 boolean
1417 swapin_file(lev)
1418 int lev;
1419 {
1420     char to[PATHLEN], from[PATHLEN];
1421
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())
1429                 return FALSE;
1430     }
1431     if (wizard) {
1432         pline("Swapping in `%s'.", from);
1433         wait_synch();
1434     }
1435     copyfile(from, to);
1436     (void) unlink(from);
1437     level_info[lev].where = ACTIVE;
1438     return TRUE;
1439 }
1440
1441 STATIC_OVL boolean
1442 swapout_oldest()
1443 {
1444     char to[PATHLEN], from[PATHLEN];
1445     int i, oldest;
1446     long oldtime;
1447
1448     if (!ramdisk)
1449         return FALSE;
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)) {
1453             oldest = i;
1454             oldtime = level_info[i].time;
1455         }
1456     if (!oldest)
1457         return FALSE;
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);
1462     if (wizard) {
1463         pline("Swapping out `%s'.", from);
1464         wait_synch();
1465     }
1466     copyfile(from, to);
1467     (void) unlink(from);
1468     level_info[oldest].where = SWAPPED;
1469     return TRUE;
1470 }
1471
1472 STATIC_OVL void
1473 copyfile(from, to)
1474 char *from, *to;
1475 {
1476 #ifdef TOS
1477     if (_copyfile(from, to))
1478         panic("Can't copy %s to %s", from, to);
1479 #else
1480     char buf[BUFSIZ]; /* this is system interaction, therefore
1481                        * BUFSIZ instead of NetHack's BUFSZ */
1482     int nfrom, nto, fdfrom, fdto;
1483
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);
1488     do {
1489         nfrom = read(fdfrom, buf, BUFSIZ);
1490         nto = write(fdto, buf, nfrom);
1491         if (nto != nfrom)
1492             panic("Copyfile failed!");
1493     } while (nfrom == BUFSIZ);
1494     (void) nhclose(fdfrom);
1495     (void) nhclose(fdto);
1496 #endif /* TOS */
1497 }
1498
1499 /* see comment in bones.c */
1500 void
1501 co_false()
1502 {
1503     count_only = FALSE;
1504     return;
1505 }
1506
1507 #endif /* MFLOPPY */
1508
1509 /*save.c*/