OSDN Git Service

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