OSDN Git Service

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