OSDN Git Service

fix #36659
[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 /* JNetHack Copyright */
6 /* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000  */
7 /* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2016            */
8 /* JNetHack may be freely redistributed.  See license for details. */
9
10 #include "hack.h"
11 #include "lev.h"
12
13 #ifndef NO_SIGNAL
14 #include <signal.h>
15 #endif
16 #if !defined(LSC) && !defined(O_WRONLY) && !defined(AZTEC_C)
17 #include <fcntl.h>
18 #endif
19
20 #ifdef MFLOPPY
21 long bytes_counted;
22 static int count_only;
23 #endif
24
25 #ifdef MICRO
26 int dotcnt, dotrow; /* also used in restore */
27 #endif
28
29 STATIC_DCL void FDECL(savelevchn, (int, int));
30 STATIC_DCL void FDECL(savedamage, (int, int));
31 STATIC_DCL void FDECL(saveobj, (int, struct obj *));
32 STATIC_DCL void FDECL(saveobjchn, (int, struct obj *, int));
33 STATIC_DCL void FDECL(savemon, (int, struct monst *));
34 STATIC_DCL void FDECL(savemonchn, (int, struct monst *, int));
35 STATIC_DCL void FDECL(savetrapchn, (int, struct trap *, int));
36 STATIC_DCL void FDECL(savegamestate, (int, int));
37 STATIC_OVL void FDECL(save_msghistory, (int, int));
38 #ifdef MFLOPPY
39 STATIC_DCL void FDECL(savelev0, (int, XCHAR_P, int));
40 STATIC_DCL boolean NDECL(swapout_oldest);
41 STATIC_DCL void FDECL(copyfile, (char *, char *));
42 #endif /* MFLOPPY */
43 STATIC_DCL void FDECL(savelevl, (int fd, BOOLEAN_P));
44 STATIC_DCL void FDECL(def_bufon, (int));
45 STATIC_DCL void FDECL(def_bufoff, (int));
46 STATIC_DCL void FDECL(def_bflush, (int));
47 STATIC_DCL void FDECL(def_bwrite, (int, genericptr_t, unsigned int));
48 #ifdef ZEROCOMP
49 STATIC_DCL void FDECL(zerocomp_bufon, (int));
50 STATIC_DCL void FDECL(zerocomp_bufoff, (int));
51 STATIC_DCL void FDECL(zerocomp_bflush, (int));
52 STATIC_DCL void FDECL(zerocomp_bwrite, (int, genericptr_t, unsigned int));
53 STATIC_DCL void FDECL(zerocomp_bputc, (int));
54 #endif
55
56 static struct save_procs {
57     const char *name;
58     void FDECL((*save_bufon), (int));
59     void FDECL((*save_bufoff), (int));
60     void FDECL((*save_bflush), (int));
61     void FDECL((*save_bwrite), (int, genericptr_t, unsigned int));
62     void FDECL((*save_bclose), (int));
63 } saveprocs = {
64 #if !defined(ZEROCOMP) || (defined(COMPRESS) || defined(ZLIB_COMP))
65     "externalcomp", def_bufon, def_bufoff, def_bflush, def_bwrite, def_bclose,
66 #else
67     "zerocomp",      zerocomp_bufon,  zerocomp_bufoff,
68     zerocomp_bflush, zerocomp_bwrite, zerocomp_bclose,
69 #endif
70 };
71
72 static long nulls[sizeof(struct trap) + sizeof(struct fruit)];
73
74 #if defined(UNIX) || defined(VMS) || defined(__EMX__) || defined(WIN32)
75 #define HUP if (!program_state.done_hup)
76 #else
77 #define HUP
78 #endif
79
80 /* need to preserve these during save to avoid accessing freed memory */
81 static unsigned ustuck_id = 0, usteed_id = 0;
82
83 int
84 dosave()
85 {
86     clear_nhwindow(WIN_MESSAGE);
87 /*JP
88     if (yn("Really save?") == 'n') {
89 */
90     if(yn("\96{\93\96\82É\95Û\91\82·\82é\81H") == 'n') {
91         clear_nhwindow(WIN_MESSAGE);
92         if (multi > 0)
93             nomul(0);
94     } else {
95         clear_nhwindow(WIN_MESSAGE);
96 /*JP
97         pline("Saving...");
98 */
99         pline("\95Û\91\92\86\81D\81D\81D");
100 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
101         program_state.done_hup = 0;
102 #endif
103         if (dosave0()) {
104             u.uhp = -1; /* universal game's over indicator */
105             /* make sure they see the Saving message */
106             display_nhwindow(WIN_MESSAGE, TRUE);
107 /*JP
108             exit_nhwindows("Be seeing you...");
109 */
110             exit_nhwindows("\82Ü\82½\89ï\82¢\82Ü\82µ\82å\82¤\81D\81D\81D");
111             terminate(EXIT_SUCCESS);
112         } else
113             (void) doredraw();
114     }
115     return 0;
116 }
117
118 /* returns 1 if save successful */
119 int
120 dosave0()
121 {
122     const char *fq_save;
123     register int fd, ofd;
124     xchar ltmp;
125     d_level uz_save;
126     char whynot[BUFSZ];
127
128     /* we may get here via hangup signal, in which case we want to fix up
129        a few of things before saving so that they won't be restored in
130        an improper state; these will be no-ops for normal save sequence */
131     u.uinvulnerable = 0;
132     if (iflags.save_uinwater)
133         u.uinwater = 1, iflags.save_uinwater = 0;
134     if (iflags.save_uburied)
135         u.uburied = 1, iflags.save_uburied = 0;
136
137     if (!program_state.something_worth_saving || !SAVEF[0])
138         return 0;
139     fq_save = fqname(SAVEF, SAVEPREFIX, 1); /* level files take 0 */
140
141 #if defined(UNIX) || defined(VMS)
142     sethanguphandler((void FDECL((*), (int) )) SIG_IGN);
143 #endif
144 #ifndef NO_SIGNAL
145     (void) signal(SIGINT, SIG_IGN);
146 #endif
147
148 #if defined(MICRO) && defined(MFLOPPY)
149     if (!saveDiskPrompt(0))
150         return 0;
151 #endif
152
153     HUP if (iflags.window_inited)
154     {
155         nh_uncompress(fq_save);
156         fd = open_savefile();
157         if (fd > 0) {
158             (void) nhclose(fd);
159             clear_nhwindow(WIN_MESSAGE);
160 /*JP
161             There("seems to be an old save file.");
162 */
163             pline("\91O\82É\83Z\81[\83u\82µ\82½\83t\83@\83C\83\8b\82ª\82 \82è\82Ü\82·\81D");
164 /*JP
165             if (yn("Overwrite the old file?") == 'n') {
166 */
167             if (yn("\8cÃ\82¢\83t\83@\83C\83\8b\82É\8fã\8f\91\82«\82µ\82Ü\82·\82©\81H") == 'n') {
168                 nh_compress(fq_save);
169                 return 0;
170             }
171         }
172     }
173
174     HUP mark_synch(); /* flush any buffered screen output */
175
176     fd = create_savefile();
177     if (fd < 0) {
178         HUP pline("Cannot open save file.");
179         (void) delete_savefile(); /* ab@unido */
180         return 0;
181     }
182
183     vision_recalc(2); /* shut down vision to prevent problems
184                          in the event of an impossible() call */
185
186     /* undo date-dependent luck adjustments made at startup time */
187     if (flags.moonphase == FULL_MOON) /* ut-sally!fletcher */
188         change_luck(-1);              /* and unido!ab */
189     if (flags.friday13)
190         change_luck(1);
191     if (iflags.window_inited)
192         HUP clear_nhwindow(WIN_MESSAGE);
193
194 #ifdef MICRO
195     dotcnt = 0;
196     dotrow = 2;
197     curs(WIN_MAP, 1, 1);
198     if (strncmpi("X11", windowprocs.name, 3))
199         putstr(WIN_MAP, 0, "Saving:");
200 #endif
201 #ifdef MFLOPPY
202     /* make sure there is enough disk space */
203     if (iflags.checkspace) {
204         long fds, needed;
205
206         savelev(fd, ledger_no(&u.uz), COUNT_SAVE);
207         savegamestate(fd, COUNT_SAVE);
208         needed = bytes_counted;
209
210         for (ltmp = 1; ltmp <= maxledgerno(); ltmp++)
211             if (ltmp != ledger_no(&u.uz) && level_info[ltmp].where)
212                 needed += level_info[ltmp].size + (sizeof ltmp);
213         fds = freediskspace(fq_save);
214         if (needed > fds) {
215             HUP
216             {
217                 There("is insufficient space on SAVE disk.");
218                 pline("Require %ld bytes but only have %ld.", needed, fds);
219             }
220             flushout();
221             (void) nhclose(fd);
222             (void) delete_savefile();
223             return 0;
224         }
225
226         co_false();
227     }
228 #endif /* MFLOPPY */
229
230     store_version(fd);
231     store_savefileinfo(fd);
232     store_plname_in_file(fd);
233     ustuck_id = (u.ustuck ? u.ustuck->m_id : 0);
234     usteed_id = (u.usteed ? u.usteed->m_id : 0);
235     savelev(fd, ledger_no(&u.uz), WRITE_SAVE | FREE_SAVE);
236     savegamestate(fd, WRITE_SAVE | FREE_SAVE);
237
238     /* While copying level files around, zero out u.uz to keep
239      * parts of the restore code from completely initializing all
240      * in-core data structures, since all we're doing is copying.
241      * This also avoids at least one nasty core dump.
242      */
243     uz_save = u.uz;
244     u.uz.dnum = u.uz.dlevel = 0;
245     /* these pointers are no longer valid, and at least u.usteed
246      * may mislead place_monster() on other levels
247      */
248     u.ustuck = (struct monst *) 0;
249     u.usteed = (struct monst *) 0;
250
251     for (ltmp = (xchar) 1; ltmp <= maxledgerno(); ltmp++) {
252         if (ltmp == ledger_no(&uz_save))
253             continue;
254         if (!(level_info[ltmp].flags & LFILE_EXISTS))
255             continue;
256 #ifdef MICRO
257         curs(WIN_MAP, 1 + dotcnt++, dotrow);
258         if (dotcnt >= (COLNO - 1)) {
259             dotrow++;
260             dotcnt = 0;
261         }
262         if (strncmpi("X11", windowprocs.name, 3)) {
263             putstr(WIN_MAP, 0, ".");
264         }
265         mark_synch();
266 #endif
267         ofd = open_levelfile(ltmp, whynot);
268         if (ofd < 0) {
269             HUP pline1(whynot);
270             (void) nhclose(fd);
271             (void) delete_savefile();
272             HUP Strcpy(killer.name, whynot);
273             HUP done(TRICKED);
274             return 0;
275         }
276         minit(); /* ZEROCOMP */
277         getlev(ofd, hackpid, ltmp, FALSE);
278         (void) nhclose(ofd);
279         bwrite(fd, (genericptr_t) &ltmp, sizeof ltmp); /* level number*/
280         savelev(fd, ltmp, WRITE_SAVE | FREE_SAVE);     /* actual level*/
281         delete_levelfile(ltmp);
282     }
283     bclose(fd);
284
285     u.uz = uz_save;
286
287     /* get rid of current level --jgm */
288     delete_levelfile(ledger_no(&u.uz));
289     delete_levelfile(0);
290     nh_compress(fq_save);
291     /* this should probably come sooner... */
292     program_state.something_worth_saving = 0;
293     return 1;
294 }
295
296 STATIC_OVL void
297 savegamestate(fd, mode)
298 register int fd, mode;
299 {
300     unsigned long uid;
301
302 #ifdef MFLOPPY
303     count_only = (mode & COUNT_SAVE);
304 #endif
305     uid = (unsigned long) getuid();
306     bwrite(fd, (genericptr_t) &uid, sizeof uid);
307     bwrite(fd, (genericptr_t) &context, sizeof(struct context_info));
308     bwrite(fd, (genericptr_t) &flags, sizeof(struct flag));
309 #ifdef SYSFLAGS
310     bwrite(fd, (genericptr_t) &sysflags, sizeof(struct sysflag));
311 #endif
312     urealtime.finish_time = getnow();
313     urealtime.realtime += (long) (urealtime.finish_time
314                                   - urealtime.start_timing);
315     bwrite(fd, (genericptr_t) &u, sizeof(struct you));
316     bwrite(fd, yyyymmddhhmmss(ubirthday), 14);
317     bwrite(fd, (genericptr_t) &urealtime.realtime, sizeof urealtime.realtime);
318     bwrite(fd, yyyymmddhhmmss(urealtime.start_timing), 14);  /** Why? **/
319     /* this is the value to use for the next update of urealtime.realtime */
320     urealtime.start_timing = urealtime.finish_time;
321     save_killers(fd, mode);
322
323     /* must come before migrating_objs and migrating_mons are freed */
324     save_timers(fd, mode, RANGE_GLOBAL);
325     save_light_sources(fd, mode, RANGE_GLOBAL);
326
327     saveobjchn(fd, invent, mode);
328     if (BALL_IN_MON) {
329         /* prevent loss of ball & chain when swallowed */
330         uball->nobj = uchain;
331         uchain->nobj = (struct obj *) 0;
332         saveobjchn(fd, uball, mode);
333     } else {
334         saveobjchn(fd, (struct obj *) 0, mode);
335     }
336
337     saveobjchn(fd, migrating_objs, mode);
338     savemonchn(fd, migrating_mons, mode);
339     if (release_data(mode)) {
340         invent = 0;
341         migrating_objs = 0;
342         migrating_mons = 0;
343     }
344     bwrite(fd, (genericptr_t) mvitals, sizeof(mvitals));
345
346     save_dungeon(fd, (boolean) !!perform_bwrite(mode),
347                  (boolean) !!release_data(mode));
348     savelevchn(fd, mode);
349     bwrite(fd, (genericptr_t) &moves, sizeof moves);
350     bwrite(fd, (genericptr_t) &monstermoves, sizeof monstermoves);
351     bwrite(fd, (genericptr_t) &quest_status, sizeof(struct q_score));
352     bwrite(fd, (genericptr_t) spl_book,
353            sizeof(struct spell) * (MAXSPELL + 1));
354     save_artifacts(fd);
355     save_oracles(fd, mode);
356     if (ustuck_id)
357         bwrite(fd, (genericptr_t) &ustuck_id, sizeof ustuck_id);
358     if (usteed_id)
359         bwrite(fd, (genericptr_t) &usteed_id, sizeof usteed_id);
360     bwrite(fd, (genericptr_t) pl_character, sizeof pl_character);
361     bwrite(fd, (genericptr_t) pl_fruit, sizeof pl_fruit);
362     savefruitchn(fd, mode);
363     savenames(fd, mode);
364     save_waterlevel(fd, mode);
365     save_msghistory(fd, mode);
366     bflush(fd);
367 }
368
369 boolean
370 tricked_fileremoved(fd, whynot)
371 int fd;
372 char *whynot;
373 {
374     if (fd < 0) {
375         pline1(whynot);
376         pline("Probably someone removed it.");
377         Strcpy(killer.name, whynot);
378         done(TRICKED);
379         return TRUE;
380     }
381     return FALSE;
382 }
383
384 #ifdef INSURANCE
385 void
386 savestateinlock()
387 {
388     int fd, hpid;
389     static boolean havestate = TRUE;
390     char whynot[BUFSZ];
391
392     /* When checkpointing is on, the full state needs to be written
393      * on each checkpoint.  When checkpointing is off, only the pid
394      * needs to be in the level.0 file, so it does not need to be
395      * constantly rewritten.  When checkpointing is turned off during
396      * a game, however, the file has to be rewritten once to truncate
397      * it and avoid restoring from outdated information.
398      *
399      * Restricting havestate to this routine means that an additional
400      * noop pid rewriting will take place on the first "checkpoint" after
401      * the game is started or restored, if checkpointing is off.
402      */
403     if (flags.ins_chkpt || havestate) {
404         /* save the rest of the current game state in the lock file,
405          * following the original int pid, the current level number,
406          * and the current savefile name, which should not be subject
407          * to any internal compression schemes since they must be
408          * readable by an external utility
409          */
410         fd = open_levelfile(0, whynot);
411         if (tricked_fileremoved(fd, whynot))
412             return;
413
414 #pragma GCC diagnostic push
415 #pragma GCC diagnostic ignored "-Wunused-result"
416         (void) read(fd, (genericptr_t) &hpid, sizeof(hpid));
417 #pragma GCC diagnostic pop
418         if (hackpid != hpid) {
419             Sprintf(whynot, "Level #0 pid (%d) doesn't match ours (%d)!",
420                     hpid, hackpid);
421             pline1(whynot);
422             Strcpy(killer.name, whynot);
423             done(TRICKED);
424         }
425         (void) nhclose(fd);
426
427         fd = create_levelfile(0, whynot);
428         if (fd < 0) {
429             pline1(whynot);
430             Strcpy(killer.name, whynot);
431             done(TRICKED);
432             return;
433         }
434 #pragma GCC diagnostic push
435 #pragma GCC diagnostic ignored "-Wunused-result"
436         (void) write(fd, (genericptr_t) &hackpid, sizeof(hackpid));
437 #pragma GCC diagnostic pop
438         if (flags.ins_chkpt) {
439             int currlev = ledger_no(&u.uz);
440
441 #pragma GCC diagnostic push
442 #pragma GCC diagnostic ignored "-Wunused-result"
443             (void) write(fd, (genericptr_t) &currlev, sizeof(currlev));
444 #pragma GCC diagnostic pop
445             save_savefile_name(fd);
446             store_version(fd);
447             store_savefileinfo(fd);
448             store_plname_in_file(fd);
449
450             ustuck_id = (u.ustuck ? u.ustuck->m_id : 0);
451             usteed_id = (u.usteed ? u.usteed->m_id : 0);
452             savegamestate(fd, WRITE_SAVE);
453         }
454         bclose(fd);
455     }
456     havestate = flags.ins_chkpt;
457 }
458 #endif
459
460 #ifdef MFLOPPY
461 boolean
462 savelev(fd, lev, mode)
463 int fd;
464 xchar lev;
465 int mode;
466 {
467     if (mode & COUNT_SAVE) {
468         bytes_counted = 0;
469         savelev0(fd, lev, COUNT_SAVE);
470         /* probably bytes_counted will be filled in again by an
471          * immediately following WRITE_SAVE anyway, but we'll
472          * leave it out of checkspace just in case */
473         if (iflags.checkspace) {
474             while (bytes_counted > freediskspace(levels))
475                 if (!swapout_oldest())
476                     return FALSE;
477         }
478     }
479     if (mode & (WRITE_SAVE | FREE_SAVE)) {
480         bytes_counted = 0;
481         savelev0(fd, lev, mode);
482     }
483     if (mode != FREE_SAVE) {
484         level_info[lev].where = ACTIVE;
485         level_info[lev].time = moves;
486         level_info[lev].size = bytes_counted;
487     }
488     return TRUE;
489 }
490
491 STATIC_OVL void
492 savelev0(fd, lev, mode)
493 #else
494 void
495 savelev(fd, lev, mode)
496 #endif
497 int fd;
498 xchar lev;
499 int mode;
500 {
501 #ifdef TOS
502     short tlev;
503 #endif
504
505     /* if we're tearing down the current level without saving anything
506        (which happens upon entrance to the endgame or after an aborted
507        restore attempt) then we don't want to do any actual I/O */
508     if (mode == FREE_SAVE)
509         goto skip_lots;
510
511     /* purge any dead monsters (necessary if we're starting
512        a panic save rather than a normal one, or sometimes
513        when changing levels without taking time -- e.g.
514        create statue trap then immediately level teleport) */
515     if (iflags.purge_monsters)
516         dmonsfree();
517
518     if (fd < 0)
519         panic("Save on bad file!"); /* impossible */
520 #ifdef MFLOPPY
521     count_only = (mode & COUNT_SAVE);
522 #endif
523     if (lev >= 0 && lev <= maxledgerno())
524         level_info[lev].flags |= VISITED;
525     bwrite(fd, (genericptr_t) &hackpid, sizeof(hackpid));
526 #ifdef TOS
527     tlev = lev;
528     tlev &= 0x00ff;
529     bwrite(fd, (genericptr_t) &tlev, sizeof(tlev));
530 #else
531     bwrite(fd, (genericptr_t) &lev, sizeof(lev));
532 #endif
533     savecemetery(fd, mode, &level.bonesinfo);
534     savelevl(fd,
535              (boolean) ((sfsaveinfo.sfi1 & SFI1_RLECOMP) == SFI1_RLECOMP));
536     bwrite(fd, (genericptr_t) lastseentyp, sizeof(lastseentyp));
537     bwrite(fd, (genericptr_t) &monstermoves, sizeof(monstermoves));
538     bwrite(fd, (genericptr_t) &upstair, sizeof(stairway));
539     bwrite(fd, (genericptr_t) &dnstair, sizeof(stairway));
540     bwrite(fd, (genericptr_t) &upladder, sizeof(stairway));
541     bwrite(fd, (genericptr_t) &dnladder, sizeof(stairway));
542     bwrite(fd, (genericptr_t) &sstairs, sizeof(stairway));
543     bwrite(fd, (genericptr_t) &updest, sizeof(dest_area));
544     bwrite(fd, (genericptr_t) &dndest, sizeof(dest_area));
545     bwrite(fd, (genericptr_t) &level.flags, sizeof(level.flags));
546     bwrite(fd, (genericptr_t) doors, sizeof(doors));
547     save_rooms(fd); /* no dynamic memory to reclaim */
548
549 /* from here on out, saving also involves allocated memory cleanup */
550 skip_lots:
551     /* this comes before the map, so need cleanup here if we skipped */
552     if (mode == FREE_SAVE)
553         savecemetery(fd, mode, &level.bonesinfo);
554     /* must be saved before mons, objs, and buried objs */
555     save_timers(fd, mode, RANGE_LEVEL);
556     save_light_sources(fd, mode, RANGE_LEVEL);
557
558     savemonchn(fd, fmon, mode);
559     save_worm(fd, mode); /* save worm information */
560     savetrapchn(fd, ftrap, mode);
561     saveobjchn(fd, fobj, mode);
562     saveobjchn(fd, level.buriedobjlist, mode);
563     saveobjchn(fd, billobjs, mode);
564     if (release_data(mode)) {
565         fmon = 0;
566         ftrap = 0;
567         fobj = 0;
568         level.buriedobjlist = 0;
569         billobjs = 0;
570         /* level.bonesinfo = 0; -- handled by savecemetery() */
571     }
572     save_engravings(fd, mode);
573     savedamage(fd, mode);
574     save_regions(fd, mode);
575     if (mode != FREE_SAVE)
576         bflush(fd);
577 }
578
579 STATIC_OVL void
580 savelevl(fd, rlecomp)
581 int fd;
582 boolean rlecomp;
583 {
584 #ifdef RLECOMP
585     struct rm *prm, *rgrm;
586     int x, y;
587     uchar match;
588
589     if (rlecomp) {
590         /* perform run-length encoding of rm structs */
591
592         rgrm = &levl[0][0]; /* start matching at first rm */
593         match = 0;
594
595         for (y = 0; y < ROWNO; y++) {
596             for (x = 0; x < COLNO; x++) {
597                 prm = &levl[x][y];
598                 if (prm->glyph == rgrm->glyph && prm->typ == rgrm->typ
599                     && prm->seenv == rgrm->seenv
600                     && prm->horizontal == rgrm->horizontal
601                     && prm->flags == rgrm->flags && prm->lit == rgrm->lit
602                     && prm->waslit == rgrm->waslit
603                     && prm->roomno == rgrm->roomno && prm->edge == rgrm->edge
604                     && prm->candig == rgrm->candig) {
605                     match++;
606                     if (match > 254) {
607                         match = 254; /* undo this match */
608                         goto writeout;
609                     }
610                 } else {
611                 /* the run has been broken,
612                  * write out run-length encoding */
613                 writeout:
614                     bwrite(fd, (genericptr_t) &match, sizeof(uchar));
615                     bwrite(fd, (genericptr_t) rgrm, sizeof(struct rm));
616                     /* start encoding again. we have at least 1 rm
617                      * in the next run, viz. this one. */
618                     match = 1;
619                     rgrm = prm;
620                 }
621             }
622         }
623         if (match > 0) {
624             bwrite(fd, (genericptr_t) &match, sizeof(uchar));
625             bwrite(fd, (genericptr_t) rgrm, sizeof(struct rm));
626         }
627         return;
628     }
629 #else /* !RLECOMP */
630     nhUse(rlecomp);
631 #endif /* ?RLECOMP */
632     bwrite(fd, (genericptr_t) levl, sizeof levl);
633 }
634
635 /*ARGSUSED*/
636 void
637 bufon(fd)
638 int fd;
639 {
640     (*saveprocs.save_bufon)(fd);
641     return;
642 }
643
644 /*ARGSUSED*/
645 void
646 bufoff(fd)
647 int fd;
648 {
649     (*saveprocs.save_bufoff)(fd);
650     return;
651 }
652
653 /* flush run and buffer */
654 void
655 bflush(fd)
656 register int fd;
657 {
658     (*saveprocs.save_bflush)(fd);
659     return;
660 }
661
662 void
663 bwrite(fd, loc, num)
664 int fd;
665 genericptr_t loc;
666 register unsigned num;
667 {
668     (*saveprocs.save_bwrite)(fd, loc, num);
669     return;
670 }
671
672 void
673 bclose(fd)
674 int fd;
675 {
676     (*saveprocs.save_bclose)(fd);
677     return;
678 }
679
680 static int bw_fd = -1;
681 static FILE *bw_FILE = 0;
682 static boolean buffering = FALSE;
683
684 STATIC_OVL void
685 def_bufon(fd)
686 int fd;
687 {
688 #ifdef UNIX
689     if (bw_fd != fd) {
690         if (bw_fd >= 0)
691             panic("double buffering unexpected");
692         bw_fd = fd;
693         if ((bw_FILE = fdopen(fd, "w")) == 0)
694             panic("buffering of file %d failed", fd);
695     }
696 #endif
697     buffering = TRUE;
698 }
699
700 STATIC_OVL void
701 def_bufoff(fd)
702 int fd;
703 {
704     def_bflush(fd);
705     buffering = FALSE;
706 }
707
708 STATIC_OVL void
709 def_bflush(fd)
710 int fd;
711 {
712 #ifdef UNIX
713     if (fd == bw_fd) {
714         if (fflush(bw_FILE) == EOF)
715             panic("flush of savefile failed!");
716     }
717 #endif
718     return;
719 }
720
721 STATIC_OVL void
722 def_bwrite(fd, loc, num)
723 register int fd;
724 register genericptr_t loc;
725 register unsigned num;
726 {
727     boolean failed;
728
729 #ifdef MFLOPPY
730     bytes_counted += num;
731     if (count_only)
732         return;
733 #endif
734
735 #ifdef UNIX
736     if (buffering) {
737         if (fd != bw_fd)
738             panic("unbuffered write to fd %d (!= %d)", fd, bw_fd);
739
740         failed = (fwrite(loc, (int) num, 1, bw_FILE) != 1);
741     } else
742 #endif /* UNIX */
743     {
744         /* lint wants 3rd arg of write to be an int; lint -p an unsigned */
745 #if defined(BSD) || defined(ULTRIX) || defined(WIN32) || defined(_MSC_VER)
746         failed = ((long) write(fd, loc, (int) num) != (long) num);
747 #else /* e.g. SYSV, __TURBOC__ */
748         failed = ((long) write(fd, loc, num) != (long) num);
749 #endif
750     }
751
752     if (failed) {
753 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
754         if (program_state.done_hup)
755             terminate(EXIT_FAILURE);
756         else
757 #endif
758             panic("cannot write %u bytes to file #%d", num, fd);
759     }
760 }
761
762 void
763 def_bclose(fd)
764 int fd;
765 {
766     bufoff(fd);
767 #ifdef UNIX
768     if (fd == bw_fd) {
769         (void) fclose(bw_FILE);
770         bw_fd = -1;
771         bw_FILE = 0;
772     } else
773 #endif
774         (void) nhclose(fd);
775     return;
776 }
777
778 #ifdef ZEROCOMP
779 /* The runs of zero-run compression are flushed after the game state or a
780  * level is written out.  This adds a couple bytes to a save file, where
781  * the runs could be mashed together, but it allows gluing together game
782  * state and level files to form a save file, and it means the flushing
783  * does not need to be specifically called for every other time a level
784  * file is written out.
785  */
786
787 #define RLESC '\0' /* Leading character for run of LRESC's */
788 #define flushoutrun(ln) (zerocomp_bputc(RLESC), zerocomp_bputc(ln), ln = -1)
789
790 #ifndef ZEROCOMP_BUFSIZ
791 #define ZEROCOMP_BUFSIZ BUFSZ
792 #endif
793 static NEARDATA unsigned char outbuf[ZEROCOMP_BUFSIZ];
794 static NEARDATA unsigned short outbufp = 0;
795 static NEARDATA short outrunlength = -1;
796 static NEARDATA int bwritefd;
797 static NEARDATA boolean compressing = FALSE;
798
799 /*dbg()
800 {
801     HUP printf("outbufp %d outrunlength %d\n", outbufp,outrunlength);
802 }*/
803
804 STATIC_OVL void
805 zerocomp_bputc(c)
806 int c;
807 {
808 #ifdef MFLOPPY
809     bytes_counted++;
810     if (count_only)
811         return;
812 #endif
813     if (outbufp >= sizeof outbuf) {
814         (void) write(bwritefd, outbuf, sizeof outbuf);
815         outbufp = 0;
816     }
817     outbuf[outbufp++] = (unsigned char) c;
818 }
819
820 /*ARGSUSED*/
821 void STATIC_OVL
822 zerocomp_bufon(fd)
823 int fd;
824 {
825     compressing = TRUE;
826     return;
827 }
828
829 /*ARGSUSED*/
830 STATIC_OVL void
831 zerocomp_bufoff(fd)
832 int fd;
833 {
834     if (outbufp) {
835         outbufp = 0;
836         panic("closing file with buffered data still unwritten");
837     }
838     outrunlength = -1;
839     compressing = FALSE;
840     return;
841 }
842
843 /* flush run and buffer */
844 STATIC_OVL void
845 zerocomp_bflush(fd)
846 register int fd;
847 {
848     bwritefd = fd;
849     if (outrunlength >= 0) { /* flush run */
850         flushoutrun(outrunlength);
851     }
852 #ifdef MFLOPPY
853     if (count_only)
854         outbufp = 0;
855 #endif
856
857     if (outbufp) {
858         if (write(fd, outbuf, outbufp) != outbufp) {
859 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
860             if (program_state.done_hup)
861                 terminate(EXIT_FAILURE);
862             else
863 #endif
864                 zerocomp_bclose(fd); /* panic (outbufp != 0) */
865         }
866         outbufp = 0;
867     }
868 }
869
870 STATIC_OVL void
871 zerocomp_bwrite(fd, loc, num)
872 int fd;
873 genericptr_t loc;
874 register unsigned num;
875 {
876     register unsigned char *bp = (unsigned char *) loc;
877
878     if (!compressing) {
879 #ifdef MFLOPPY
880         bytes_counted += num;
881         if (count_only)
882             return;
883 #endif
884         if ((unsigned) write(fd, loc, num) != num) {
885 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
886             if (program_state.done_hup)
887                 terminate(EXIT_FAILURE);
888             else
889 #endif
890                 panic("cannot write %u bytes to file #%d", num, fd);
891         }
892     } else {
893         bwritefd = fd;
894         for (; num; num--, bp++) {
895             if (*bp == RLESC) { /* One more char in run */
896                 if (++outrunlength == 0xFF) {
897                     flushoutrun(outrunlength);
898                 }
899             } else {                     /* end of run */
900                 if (outrunlength >= 0) { /* flush run */
901                     flushoutrun(outrunlength);
902                 }
903                 zerocomp_bputc(*bp);
904             }
905         }
906     }
907 }
908
909 void
910 zerocomp_bclose(fd)
911 int fd;
912 {
913     zerocomp_bufoff(fd);
914     (void) nhclose(fd);
915     return;
916 }
917 #endif /* ZEROCOMP */
918
919 STATIC_OVL void
920 savelevchn(fd, mode)
921 register int fd, mode;
922 {
923     s_level *tmplev, *tmplev2;
924     int cnt = 0;
925
926     for (tmplev = sp_levchn; tmplev; tmplev = tmplev->next)
927         cnt++;
928     if (perform_bwrite(mode))
929         bwrite(fd, (genericptr_t) &cnt, sizeof(int));
930
931     for (tmplev = sp_levchn; tmplev; tmplev = tmplev2) {
932         tmplev2 = tmplev->next;
933         if (perform_bwrite(mode))
934             bwrite(fd, (genericptr_t) tmplev, sizeof(s_level));
935         if (release_data(mode))
936             free((genericptr_t) tmplev);
937     }
938     if (release_data(mode))
939         sp_levchn = 0;
940 }
941
942 /* used when saving a level and also when saving dungeon overview data */
943 void
944 savecemetery(fd, mode, cemeteryaddr)
945 int fd;
946 int mode;
947 struct cemetery **cemeteryaddr;
948 {
949     struct cemetery *thisbones, *nextbones;
950     int flag;
951
952     flag = *cemeteryaddr ? 0 : -1;
953     if (perform_bwrite(mode))
954         bwrite(fd, (genericptr_t) &flag, sizeof flag);
955     nextbones = *cemeteryaddr;
956     while ((thisbones = nextbones) != 0) {
957         nextbones = thisbones->next;
958         if (perform_bwrite(mode))
959             bwrite(fd, (genericptr_t) thisbones, sizeof *thisbones);
960         if (release_data(mode))
961             free((genericptr_t) thisbones);
962     }
963     if (release_data(mode))
964         *cemeteryaddr = 0;
965 }
966
967 STATIC_OVL void
968 savedamage(fd, mode)
969 register int fd, mode;
970 {
971     register struct damage *damageptr, *tmp_dam;
972     unsigned int xl = 0;
973
974     damageptr = level.damagelist;
975     for (tmp_dam = damageptr; tmp_dam; tmp_dam = tmp_dam->next)
976         xl++;
977     if (perform_bwrite(mode))
978         bwrite(fd, (genericptr_t) &xl, sizeof(xl));
979
980     while (xl--) {
981         if (perform_bwrite(mode))
982             bwrite(fd, (genericptr_t) damageptr, sizeof(*damageptr));
983         tmp_dam = damageptr;
984         damageptr = damageptr->next;
985         if (release_data(mode))
986             free((genericptr_t) tmp_dam);
987     }
988     if (release_data(mode))
989         level.damagelist = 0;
990 }
991
992 STATIC_OVL void
993 saveobj(fd, otmp)
994 int fd;
995 struct obj *otmp;
996 {
997     int buflen, zerobuf = 0;
998
999     buflen = sizeof(struct obj);
1000     bwrite(fd, (genericptr_t) &buflen, sizeof(int));
1001     bwrite(fd, (genericptr_t) otmp, buflen);
1002     if (otmp->oextra) {
1003         if (ONAME(otmp))
1004             buflen = strlen(ONAME(otmp)) + 1;
1005         else
1006             buflen = 0;
1007         bwrite(fd, (genericptr_t) &buflen, sizeof buflen);
1008         if (buflen > 0)
1009             bwrite(fd, (genericptr_t) ONAME(otmp), buflen);
1010
1011         /* defer to savemon() for this one */
1012         if (OMONST(otmp))
1013             savemon(fd, OMONST(otmp));
1014         else
1015             bwrite(fd, (genericptr_t) &zerobuf, sizeof zerobuf);
1016
1017         if (OMID(otmp))
1018             buflen = sizeof(unsigned);
1019         else
1020             buflen = 0;
1021         bwrite(fd, (genericptr_t) &buflen, sizeof buflen);
1022         if (buflen > 0)
1023             bwrite(fd, (genericptr_t) OMID(otmp), buflen);
1024
1025         if (OLONG(otmp))
1026             buflen = sizeof(long);
1027         else
1028             buflen = 0;
1029         bwrite(fd, (genericptr_t) &buflen, sizeof buflen);
1030         if (buflen > 0)
1031             bwrite(fd, (genericptr_t) OLONG(otmp), buflen);
1032
1033         if (OMAILCMD(otmp))
1034             buflen = strlen(OMAILCMD(otmp)) + 1;
1035         else
1036             buflen = 0;
1037         bwrite(fd, (genericptr_t) &buflen, sizeof buflen);
1038         if (buflen > 0)
1039             bwrite(fd, (genericptr_t) OMAILCMD(otmp), buflen);
1040     }
1041 }
1042
1043 STATIC_OVL void
1044 saveobjchn(fd, otmp, mode)
1045 register int fd, mode;
1046 register struct obj *otmp;
1047 {
1048     register struct obj *otmp2;
1049     int minusone = -1;
1050
1051     while (otmp) {
1052         otmp2 = otmp->nobj;
1053         if (perform_bwrite(mode)) {
1054             saveobj(fd, otmp);
1055         }
1056         if (Has_contents(otmp))
1057             saveobjchn(fd, otmp->cobj, mode);
1058         if (release_data(mode)) {
1059             /*  if (otmp->oclass == FOOD_CLASS)
1060              *      food_disappears(otmp);
1061              */
1062             /*
1063              * If these are on the floor, the discarding could
1064              * be because of a game save, or we could just be changing levels.
1065              * Always invalidate the pointer, but ensure that we have
1066              * the o_id in order to restore the pointer on reload.
1067              */
1068             if (otmp == context.victual.piece) {
1069                 /* Store the o_id of the victual if mismatched */
1070                 if (context.victual.o_id != otmp->o_id)
1071                     context.victual.o_id = otmp->o_id;
1072                 /* invalidate the pointer; on reload it will get restored */
1073                 context.victual.piece = (struct obj *) 0;
1074             }
1075             if (otmp == context.tin.tin) {
1076                 /* Store the o_id of your tin */
1077                 if (context.tin.o_id != otmp->o_id)
1078                     context.tin.o_id = otmp->o_id;
1079                 /* invalidate the pointer; on reload it will get restored */
1080                 context.tin.tin = (struct obj *) 0;
1081             }
1082             /*  if (otmp->oclass == SPBOOK_CLASS)
1083              *      book_disappears(otmp);
1084              */
1085             if (otmp == context.spbook.book) {
1086                 /* Store the o_id of your spellbook */
1087                 if (context.spbook.o_id != otmp->o_id)
1088                     context.spbook.o_id = otmp->o_id;
1089                 /* invalidate the pointer; on reload it will get restored */
1090                 context.spbook.book = (struct obj *) 0;
1091             }
1092             otmp->where = OBJ_FREE; /* set to free so dealloc will work */
1093             otmp->nobj = NULL;      /* nobj saved into otmp2 */
1094             otmp->cobj = NULL;      /* contents handled above */
1095             otmp->timed = 0;        /* not timed any more */
1096             otmp->lamplit = 0;      /* caller handled lights */
1097             dealloc_obj(otmp);
1098         }
1099         otmp = otmp2;
1100     }
1101     if (perform_bwrite(mode))
1102         bwrite(fd, (genericptr_t) &minusone, sizeof(int));
1103 }
1104
1105 STATIC_OVL void
1106 savemon(fd, mtmp)
1107 int fd;
1108 struct monst *mtmp;
1109 {
1110     int buflen;
1111
1112     buflen = sizeof(struct monst);
1113     bwrite(fd, (genericptr_t) &buflen, sizeof(int));
1114     bwrite(fd, (genericptr_t) mtmp, buflen);
1115     if (mtmp->mextra) {
1116         if (MNAME(mtmp))
1117             buflen = strlen(MNAME(mtmp)) + 1;
1118         else
1119             buflen = 0;
1120         bwrite(fd, (genericptr_t) &buflen, sizeof buflen);
1121         if (buflen > 0)
1122             bwrite(fd, (genericptr_t) MNAME(mtmp), buflen);
1123
1124         if (EGD(mtmp))
1125             buflen = sizeof(struct egd);
1126         else
1127             buflen = 0;
1128         bwrite(fd, (genericptr_t) &buflen, sizeof(int));
1129         if (buflen > 0)
1130             bwrite(fd, (genericptr_t) EGD(mtmp), buflen);
1131
1132         if (EPRI(mtmp))
1133             buflen = sizeof(struct epri);
1134         else
1135             buflen = 0;
1136         bwrite(fd, (genericptr_t) &buflen, sizeof(int));
1137         if (buflen > 0)
1138             bwrite(fd, (genericptr_t) EPRI(mtmp), buflen);
1139
1140         if (ESHK(mtmp))
1141             buflen = sizeof(struct eshk);
1142         else
1143             buflen = 0;
1144         bwrite(fd, (genericptr_t) &buflen, sizeof(int));
1145         if (buflen > 0)
1146             bwrite(fd, (genericptr_t) ESHK(mtmp), buflen);
1147
1148         if (EMIN(mtmp))
1149             buflen = sizeof(struct emin);
1150         else
1151             buflen = 0;
1152         bwrite(fd, (genericptr_t) &buflen, sizeof(int));
1153         if (buflen > 0)
1154             bwrite(fd, (genericptr_t) EMIN(mtmp), buflen);
1155
1156         if (EDOG(mtmp))
1157             buflen = sizeof(struct edog);
1158         else
1159             buflen = 0;
1160         bwrite(fd, (genericptr_t) &buflen, sizeof(int));
1161         if (buflen > 0)
1162             bwrite(fd, (genericptr_t) EDOG(mtmp), buflen);
1163
1164         /* mcorpsenm is inline int rather than pointer to something,
1165            so doesn't need to be preceded by a length field */
1166         bwrite(fd, (genericptr_t) &MCORPSENM(mtmp), sizeof MCORPSENM(mtmp));
1167     }
1168 }
1169
1170 STATIC_OVL void
1171 savemonchn(fd, mtmp, mode)
1172 register int fd, mode;
1173 register struct monst *mtmp;
1174 {
1175     register struct monst *mtmp2;
1176     int minusone = -1;
1177
1178     while (mtmp) {
1179         mtmp2 = mtmp->nmon;
1180         if (perform_bwrite(mode)) {
1181             mtmp->mnum = monsndx(mtmp->data);
1182             if (mtmp->ispriest)
1183                 forget_temple_entry(mtmp); /* EPRI() */
1184             savemon(fd, mtmp);
1185         }
1186         if (mtmp->minvent)
1187             saveobjchn(fd, mtmp->minvent, mode);
1188         if (release_data(mode)) {
1189             if (mtmp == context.polearm.hitmon) {
1190                 context.polearm.m_id = mtmp->m_id;
1191                 context.polearm.hitmon = NULL;
1192             }
1193             mtmp->nmon = NULL;  /* nmon saved into mtmp2 */
1194             dealloc_monst(mtmp);
1195         }
1196         mtmp = mtmp2;
1197     }
1198     if (perform_bwrite(mode))
1199         bwrite(fd, (genericptr_t) &minusone, sizeof(int));
1200 }
1201
1202 STATIC_OVL void
1203 savetrapchn(fd, trap, mode)
1204 register int fd, mode;
1205 register struct trap *trap;
1206 {
1207     register struct trap *trap2;
1208
1209     while (trap) {
1210         trap2 = trap->ntrap;
1211         if (perform_bwrite(mode))
1212             bwrite(fd, (genericptr_t) trap, sizeof(struct trap));
1213         if (release_data(mode))
1214             dealloc_trap(trap);
1215         trap = trap2;
1216     }
1217     if (perform_bwrite(mode))
1218         bwrite(fd, (genericptr_t) nulls, sizeof(struct trap));
1219 }
1220
1221 /* save all the fruit names and ID's; this is used only in saving whole games
1222  * (not levels) and in saving bones levels.  When saving a bones level,
1223  * we only want to save the fruits which exist on the bones level; the bones
1224  * level routine marks nonexistent fruits by making the fid negative.
1225  */
1226 void
1227 savefruitchn(fd, mode)
1228 register int fd, mode;
1229 {
1230     register struct fruit *f2, *f1;
1231
1232     f1 = ffruit;
1233     while (f1) {
1234         f2 = f1->nextf;
1235         if (f1->fid >= 0 && perform_bwrite(mode))
1236             bwrite(fd, (genericptr_t) f1, sizeof(struct fruit));
1237         if (release_data(mode))
1238             dealloc_fruit(f1);
1239         f1 = f2;
1240     }
1241     if (perform_bwrite(mode))
1242         bwrite(fd, (genericptr_t) nulls, sizeof(struct fruit));
1243     if (release_data(mode))
1244         ffruit = 0;
1245 }
1246
1247 void
1248 store_plname_in_file(fd)
1249 int fd;
1250 {
1251     int plsiztmp = PL_NSIZ;
1252     bufoff(fd);
1253     /* bwrite() before bufon() uses plain write() */
1254     bwrite(fd, (genericptr_t) &plsiztmp, sizeof(plsiztmp));
1255     bwrite(fd, (genericptr_t) plname, plsiztmp);
1256     bufon(fd);
1257     return;
1258 }
1259
1260 STATIC_OVL void
1261 save_msghistory(fd, mode)
1262 int fd, mode;
1263 {
1264     char *msg;
1265     int msgcount = 0, msglen;
1266     int minusone = -1;
1267     boolean init = TRUE;
1268
1269     if (perform_bwrite(mode)) {
1270         /* ask window port for each message in sequence */
1271         while ((msg = getmsghistory(init)) != 0) {
1272             init = FALSE;
1273             msglen = strlen(msg);
1274             /* sanity: truncate if necessary (shouldn't happen);
1275                no need to modify msg[] since terminator isn't written */
1276             if (msglen > BUFSZ - 1)
1277                 msglen = BUFSZ - 1;
1278             bwrite(fd, (genericptr_t) &msglen, sizeof(msglen));
1279             bwrite(fd, (genericptr_t) msg, msglen);
1280             ++msgcount;
1281         }
1282         bwrite(fd, (genericptr_t) &minusone, sizeof(int));
1283     }
1284     debugpline1("Stored %d messages into savefile.", msgcount);
1285     /* note: we don't attempt to handle release_data() here */
1286 }
1287
1288 void
1289 store_savefileinfo(fd)
1290 int fd;
1291 {
1292     /* sfcap (decl.c) describes the savefile feature capabilities
1293      * that are supported by this port/platform build.
1294      *
1295      * sfsaveinfo (decl.c) describes the savefile info that actually
1296      * gets written into the savefile, and is used to determine the
1297      * save file being written.
1298
1299      * sfrestinfo (decl.c) describes the savefile info that is
1300      * being used to read the information from an existing savefile.
1301      *
1302      */
1303
1304     bufoff(fd);
1305     /* bwrite() before bufon() uses plain write() */
1306     bwrite(fd, (genericptr_t) &sfsaveinfo, (unsigned) (sizeof sfsaveinfo));
1307     bufon(fd);
1308     return;
1309 }
1310
1311 void
1312 set_savepref(suitename)
1313 const char *suitename;
1314 {
1315     if (!strcmpi(suitename, "externalcomp")) {
1316         saveprocs.name = "externalcomp";
1317         saveprocs.save_bufon = def_bufon;
1318         saveprocs.save_bufoff = def_bufoff;
1319         saveprocs.save_bflush = def_bflush;
1320         saveprocs.save_bwrite = def_bwrite;
1321         saveprocs.save_bclose = def_bclose;
1322         sfsaveinfo.sfi1 |= SFI1_EXTERNALCOMP;
1323         sfsaveinfo.sfi1 &= ~SFI1_ZEROCOMP;
1324     }
1325     if (!strcmpi(suitename, "!rlecomp")) {
1326         sfsaveinfo.sfi1 &= ~SFI1_RLECOMP;
1327     }
1328 #ifdef ZEROCOMP
1329     if (!strcmpi(suitename, "zerocomp")) {
1330         saveprocs.name = "zerocomp";
1331         saveprocs.save_bufon = zerocomp_bufon;
1332         saveprocs.save_bufoff = zerocomp_bufoff;
1333         saveprocs.save_bflush = zerocomp_bflush;
1334         saveprocs.save_bwrite = zerocomp_bwrite;
1335         saveprocs.save_bclose = zerocomp_bclose;
1336         sfsaveinfo.sfi1 |= SFI1_ZEROCOMP;
1337         sfsaveinfo.sfi1 &= ~SFI1_EXTERNALCOMP;
1338     }
1339 #endif
1340 #ifdef RLECOMP
1341     if (!strcmpi(suitename, "rlecomp")) {
1342         sfsaveinfo.sfi1 |= SFI1_RLECOMP;
1343     }
1344 #endif
1345 }
1346
1347 /* also called by prscore(); this probably belongs in dungeon.c... */
1348 void
1349 free_dungeons()
1350 {
1351 #ifdef FREE_ALL_MEMORY
1352     savelevchn(0, FREE_SAVE);
1353     save_dungeon(0, FALSE, TRUE);
1354 #endif
1355     return;
1356 }
1357
1358 void
1359 freedynamicdata()
1360 {
1361     unload_qtlist();
1362     free_menu_coloring();
1363     free_invbuf();           /* let_to_name (invent.c) */
1364     free_youbuf();           /* You_buf,&c (pline.c) */
1365     msgtype_free();
1366     tmp_at(DISP_FREEMEM, 0); /* temporary display effects */
1367 #ifdef FREE_ALL_MEMORY
1368 #define freeobjchn(X) (saveobjchn(0, X, FREE_SAVE), X = 0)
1369 #define freemonchn(X) (savemonchn(0, X, FREE_SAVE), X = 0)
1370 #define freetrapchn(X) (savetrapchn(0, X, FREE_SAVE), X = 0)
1371 #define freefruitchn() savefruitchn(0, FREE_SAVE)
1372 #define freenames() savenames(0, FREE_SAVE)
1373 #define free_killers() save_killers(0, FREE_SAVE)
1374 #define free_oracles() save_oracles(0, FREE_SAVE)
1375 #define free_waterlevel() save_waterlevel(0, FREE_SAVE)
1376 #define free_worm() save_worm(0, FREE_SAVE)
1377 #define free_timers(R) save_timers(0, FREE_SAVE, R)
1378 #define free_light_sources(R) save_light_sources(0, FREE_SAVE, R);
1379 #define free_engravings() save_engravings(0, FREE_SAVE)
1380 #define freedamage() savedamage(0, FREE_SAVE)
1381 #define free_animals() mon_animal_list(FALSE)
1382
1383     /* move-specific data */
1384     dmonsfree(); /* release dead monsters */
1385
1386     /* level-specific data */
1387     free_timers(RANGE_LEVEL);
1388     free_light_sources(RANGE_LEVEL);
1389     clear_regions();
1390     freemonchn(fmon);
1391     free_worm(); /* release worm segment information */
1392     freetrapchn(ftrap);
1393     freeobjchn(fobj);
1394     freeobjchn(level.buriedobjlist);
1395     freeobjchn(billobjs);
1396     free_engravings();
1397     freedamage();
1398
1399     /* game-state data */
1400     free_killers();
1401     free_timers(RANGE_GLOBAL);
1402     free_light_sources(RANGE_GLOBAL);
1403     freeobjchn(invent);
1404     freeobjchn(migrating_objs);
1405     freemonchn(migrating_mons);
1406     freemonchn(mydogs); /* ascension or dungeon escape */
1407     /* freelevchn();  --  [folded into free_dungeons()] */
1408     free_animals();
1409     free_oracles();
1410     freefruitchn();
1411     freenames();
1412     free_waterlevel();
1413     free_dungeons();
1414
1415     /* some pointers in iflags */
1416     if (iflags.wc_font_map)
1417         free(iflags.wc_font_map);
1418     if (iflags.wc_font_message)
1419         free(iflags.wc_font_message);
1420     if (iflags.wc_font_text)
1421         free(iflags.wc_font_text);
1422     if (iflags.wc_font_menu)
1423         free(iflags.wc_font_menu);
1424     if (iflags.wc_font_status)
1425         free(iflags.wc_font_status);
1426     if (iflags.wc_tile_file)
1427         free(iflags.wc_tile_file);
1428     free_autopickup_exceptions();
1429
1430     /* miscellaneous */
1431     /* free_pickinv_cache();  --  now done from really_done()... */
1432     free_symsets();
1433 #endif /* FREE_ALL_MEMORY */
1434 #ifdef STATUS_VIA_WINDOWPORT
1435     status_finish();
1436 #endif
1437
1438     /* last, because it frees data that might be used by panic() to provide
1439        feedback to the user; conceivably other freeing might trigger panic */
1440     sysopt_release(); /* SYSCF strings */
1441     return;
1442 }
1443
1444 #ifdef MFLOPPY
1445 boolean
1446 swapin_file(lev)
1447 int lev;
1448 {
1449     char to[PATHLEN], from[PATHLEN];
1450
1451     Sprintf(from, "%s%s", permbones, alllevels);
1452     Sprintf(to, "%s%s", levels, alllevels);
1453     set_levelfile_name(from, lev);
1454     set_levelfile_name(to, lev);
1455     if (iflags.checkspace) {
1456         while (level_info[lev].size > freediskspace(to))
1457             if (!swapout_oldest())
1458                 return FALSE;
1459     }
1460     if (wizard) {
1461         pline("Swapping in `%s'.", from);
1462         wait_synch();
1463     }
1464     copyfile(from, to);
1465     (void) unlink(from);
1466     level_info[lev].where = ACTIVE;
1467     return TRUE;
1468 }
1469
1470 STATIC_OVL boolean
1471 swapout_oldest()
1472 {
1473     char to[PATHLEN], from[PATHLEN];
1474     int i, oldest;
1475     long oldtime;
1476
1477     if (!ramdisk)
1478         return FALSE;
1479     for (i = 1, oldtime = 0, oldest = 0; i <= maxledgerno(); i++)
1480         if (level_info[i].where == ACTIVE
1481             && (!oldtime || level_info[i].time < oldtime)) {
1482             oldest = i;
1483             oldtime = level_info[i].time;
1484         }
1485     if (!oldest)
1486         return FALSE;
1487     Sprintf(from, "%s%s", levels, alllevels);
1488     Sprintf(to, "%s%s", permbones, alllevels);
1489     set_levelfile_name(from, oldest);
1490     set_levelfile_name(to, oldest);
1491     if (wizard) {
1492         pline("Swapping out `%s'.", from);
1493         wait_synch();
1494     }
1495     copyfile(from, to);
1496     (void) unlink(from);
1497     level_info[oldest].where = SWAPPED;
1498     return TRUE;
1499 }
1500
1501 STATIC_OVL void
1502 copyfile(from, to)
1503 char *from, *to;
1504 {
1505 #ifdef TOS
1506     if (_copyfile(from, to))
1507         panic("Can't copy %s to %s", from, to);
1508 #else
1509     char buf[BUFSIZ]; /* this is system interaction, therefore
1510                        * BUFSIZ instead of NetHack's BUFSZ */
1511     int nfrom, nto, fdfrom, fdto;
1512
1513     if ((fdfrom = open(from, O_RDONLY | O_BINARY, FCMASK)) < 0)
1514         panic("Can't copy from %s !?", from);
1515     if ((fdto = open(to, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, FCMASK)) < 0)
1516         panic("Can't copy to %s", to);
1517     do {
1518         nfrom = read(fdfrom, buf, BUFSIZ);
1519         nto = write(fdto, buf, nfrom);
1520         if (nto != nfrom)
1521             panic("Copyfile failed!");
1522     } while (nfrom == BUFSIZ);
1523     (void) nhclose(fdfrom);
1524     (void) nhclose(fdto);
1525 #endif /* TOS */
1526 }
1527
1528 /* see comment in bones.c */
1529 void
1530 co_false()
1531 {
1532     count_only = FALSE;
1533     return;
1534 }
1535
1536 #endif /* MFLOPPY */
1537
1538 /*save.c*/