OSDN Git Service

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