OSDN Git Service

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