OSDN Git Service

rearrange main dungeon
[nethackexpress/trunk.git] / src / save.c
1 /*      SCCS Id: @(#)save.c     3.4     2003/11/14      */
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 #include "quest.h"
8
9 #ifndef NO_SIGNAL
10 #include <signal.h>
11 #endif
12 #if !defined(LSC) && !defined(O_WRONLY) && !defined(AZTEC_C)
13 #include <fcntl.h>
14 #endif
15
16 #ifdef MFLOPPY
17 long bytes_counted;
18 static int count_only;
19 #endif
20
21 #ifdef MICRO
22 int dotcnt, dotrow;     /* also used in restore */
23 #endif
24
25 #ifdef ZEROCOMP
26 STATIC_DCL void FDECL(bputc, (int));
27 #endif
28 STATIC_DCL void FDECL(savelevchn, (int,int));
29 STATIC_DCL void FDECL(savedamage, (int,int));
30 STATIC_DCL void FDECL(saveobjchn, (int,struct obj *,int));
31 STATIC_DCL void FDECL(savemonchn, (int,struct monst *,int));
32 STATIC_DCL void FDECL(savetrapchn, (int,struct trap *,int));
33 STATIC_DCL void FDECL(savegamestate, (int,int));
34 #ifdef MFLOPPY
35 STATIC_DCL void FDECL(savelev0, (int,XCHAR_P,int));
36 STATIC_DCL boolean NDECL(swapout_oldest);
37 STATIC_DCL void FDECL(copyfile, (char *,char *));
38 #endif /* MFLOPPY */
39 #ifdef GCC_WARN
40 static long nulls[10];
41 #else
42 #define nulls nul
43 #endif
44
45 #if defined(UNIX) || defined(VMS) || defined(__EMX__) || defined(WIN32)
46 #define HUP     if (!program_state.done_hup)
47 #else
48 #define HUP
49 #endif
50
51 /* need to preserve these during save to avoid accessing freed memory */
52 static unsigned ustuck_id = 0, usteed_id = 0;
53
54 int
55 dosave()
56 {
57         clear_nhwindow(WIN_MESSAGE);
58         if(yn("Really save?") == 'n') {
59                 clear_nhwindow(WIN_MESSAGE);
60                 if(multi > 0) nomul(0);
61         } else {
62                 clear_nhwindow(WIN_MESSAGE);
63                 pline("Saving...");
64 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
65                 program_state.done_hup = 0;
66 #endif
67                 if(dosave0()) {
68                         program_state.something_worth_saving = 0;
69                         u.uhp = -1;             /* universal game's over indicator */
70                         /* make sure they see the Saving message */
71                         display_nhwindow(WIN_MESSAGE, TRUE);
72                         exit_nhwindows("Be seeing you...");
73                         terminate(EXIT_SUCCESS);
74                 } else (void)doredraw();
75         }
76         return 0;
77 }
78
79
80 #if defined(UNIX) || defined(VMS) || defined (__EMX__) || defined(WIN32)
81 /*ARGSUSED*/
82 void
83 hangup(sig_unused)  /* called as signal() handler, so sent at least one arg */
84 int sig_unused;
85 {
86 # ifdef NOSAVEONHANGUP
87         (void) signal(SIGINT, SIG_IGN);
88         clearlocks();
89 #  ifndef VMS
90         terminate(EXIT_FAILURE);
91 #  endif
92 # else  /* SAVEONHANGUP */
93         if (!program_state.done_hup++) {
94             if (program_state.something_worth_saving)
95                 (void) dosave0();
96 #  ifdef VMS
97             /* don't call exit when already within an exit handler;
98                that would cancel any other pending user-mode handlers */
99             if (!program_state.exiting)
100 #  endif
101             {
102                 clearlocks();
103                 terminate(EXIT_FAILURE);
104             }
105         }
106 # endif
107         return;
108 }
109 #endif
110
111 /* returns 1 if save successful */
112 int
113 dosave0()
114 {
115         const char *fq_save;
116         register int fd, ofd;
117         xchar ltmp;
118         d_level uz_save;
119         char whynot[BUFSZ];
120
121         if (!SAVEF[0])
122                 return 0;
123         fq_save = fqname(SAVEF, SAVEPREFIX, 1); /* level files take 0 */
124
125 #if defined(UNIX) || defined(VMS)
126         (void) signal(SIGHUP, SIG_IGN);
127 #endif
128 #ifndef NO_SIGNAL
129         (void) signal(SIGINT, SIG_IGN);
130 #endif
131
132 #if defined(MICRO) && defined(MFLOPPY)
133         if (!saveDiskPrompt(0)) return 0;
134 #endif
135
136         HUP if (iflags.window_inited) {
137             uncompress(fq_save);
138             fd = open_savefile();
139             if (fd > 0) {
140                 (void) close(fd);
141                 clear_nhwindow(WIN_MESSAGE);
142                 There("seems to be an old save file.");
143                 if (yn("Overwrite the old file?") == 'n') {
144                     compress(fq_save);
145                     return 0;
146                 }
147             }
148         }
149
150         HUP mark_synch();       /* flush any buffered screen output */
151
152         fd = create_savefile();
153         if(fd < 0) {
154                 HUP pline("Cannot open save file.");
155                 (void) delete_savefile();       /* ab@unido */
156                 return(0);
157         }
158
159         vision_recalc(2);       /* shut down vision to prevent problems
160                                    in the event of an impossible() call */
161         
162         /* undo date-dependent luck adjustments made at startup time */
163         if(flags.moonphase == FULL_MOON)        /* ut-sally!fletcher */
164                 change_luck(-1);                /* and unido!ab */
165         if(flags.friday13)
166                 change_luck(1);
167         if(iflags.window_inited)
168             HUP clear_nhwindow(WIN_MESSAGE);
169
170 #ifdef MICRO
171         dotcnt = 0;
172         dotrow = 2;
173         curs(WIN_MAP, 1, 1);
174         if (strncmpi("X11", windowprocs.name, 3))
175           putstr(WIN_MAP, 0, "Saving:");
176 #endif
177 #ifdef MFLOPPY
178         /* make sure there is enough disk space */
179         if (iflags.checkspace) {
180             long fds, needed;
181
182             savelev(fd, ledger_no(&u.uz), COUNT_SAVE);
183             savegamestate(fd, COUNT_SAVE);
184             needed = bytes_counted;
185
186             for (ltmp = 1; ltmp <= maxledgerno(); ltmp++)
187                 if (ltmp != ledger_no(&u.uz) && level_info[ltmp].where)
188                     needed += level_info[ltmp].size + (sizeof ltmp);
189             fds = freediskspace(fq_save);
190             if (needed > fds) {
191                 HUP {
192                     There("is insufficient space on SAVE disk.");
193                     pline("Require %ld bytes but only have %ld.", needed, fds);
194                 }
195                 flushout();
196                 (void) close(fd);
197                 (void) delete_savefile();
198                 return 0;
199             }
200
201             co_false();
202         }
203 #endif /* MFLOPPY */
204
205         store_version(fd);
206 #ifdef STORE_PLNAME_IN_FILE
207         bwrite(fd, (genericptr_t) plname, PL_NSIZ);
208 #endif
209         ustuck_id = (u.ustuck ? u.ustuck->m_id : 0);
210 #ifdef STEED
211         usteed_id = (u.usteed ? u.usteed->m_id : 0);
212 #endif
213         savelev(fd, ledger_no(&u.uz), WRITE_SAVE | FREE_SAVE);
214         savegamestate(fd, WRITE_SAVE | FREE_SAVE);
215
216         /* While copying level files around, zero out u.uz to keep
217          * parts of the restore code from completely initializing all
218          * in-core data structures, since all we're doing is copying.
219          * This also avoids at least one nasty core dump.
220          */
221         uz_save = u.uz;
222         u.uz.dnum = u.uz.dlevel = 0;
223         /* these pointers are no longer valid, and at least u.usteed
224          * may mislead place_monster() on other levels
225          */
226         u.ustuck = (struct monst *)0;
227 #ifdef STEED
228         u.usteed = (struct monst *)0;
229 #endif
230
231         for(ltmp = (xchar)1; ltmp <= maxledgerno(); ltmp++) {
232                 if (ltmp == ledger_no(&uz_save)) continue;
233                 if (!(level_info[ltmp].flags & LFILE_EXISTS)) continue;
234 #ifdef MICRO
235                 curs(WIN_MAP, 1 + dotcnt++, dotrow);
236                 if (dotcnt >= (COLNO - 1)) {
237                         dotrow++;
238                         dotcnt = 0;
239                 }
240                 if (strncmpi("X11", windowprocs.name, 3)){
241                   putstr(WIN_MAP, 0, ".");
242                 }
243                 mark_synch();
244 #endif
245                 ofd = open_levelfile(ltmp, whynot);
246                 if (ofd < 0) {
247                     HUP pline("%s", whynot);
248                     (void) close(fd);
249                     (void) delete_savefile();
250                     HUP killer = whynot;
251                     HUP done(TRICKED);
252                     return(0);
253                 }
254                 minit();        /* ZEROCOMP */
255                 getlev(ofd, hackpid, ltmp, FALSE);
256                 (void) close(ofd);
257                 bwrite(fd, (genericptr_t) &ltmp, sizeof ltmp); /* level number*/
258                 savelev(fd, ltmp, WRITE_SAVE | FREE_SAVE);     /* actual level*/
259                 delete_levelfile(ltmp);
260         }
261         bclose(fd);
262
263         u.uz = uz_save;
264
265         /* get rid of current level --jgm */
266         delete_levelfile(ledger_no(&u.uz));
267         delete_levelfile(0);
268         compress(fq_save);
269         return(1);
270 }
271
272 STATIC_OVL void
273 savegamestate(fd, mode)
274 register int fd, mode;
275 {
276         int uid;
277
278 #ifdef MFLOPPY
279         count_only = (mode & COUNT_SAVE);
280 #endif
281         uid = getuid();
282         bwrite(fd, (genericptr_t) &uid, sizeof uid);
283         bwrite(fd, (genericptr_t) &flags, sizeof(struct flag));
284         bwrite(fd, (genericptr_t) &u, sizeof(struct you));
285
286         /* must come before migrating_objs and migrating_mons are freed */
287         save_timers(fd, mode, RANGE_GLOBAL);
288         save_light_sources(fd, mode, RANGE_GLOBAL);
289
290         saveobjchn(fd, invent, mode);
291         saveobjchn(fd, migrating_objs, mode);
292         savemonchn(fd, migrating_mons, mode);
293         if (release_data(mode)) {
294             invent = 0;
295             migrating_objs = 0;
296             migrating_mons = 0;
297         }
298         bwrite(fd, (genericptr_t) mvitals, sizeof(mvitals));
299
300         save_dungeon(fd, (boolean)!!perform_bwrite(mode),
301                          (boolean)!!release_data(mode));
302         savelevchn(fd, mode);
303         bwrite(fd, (genericptr_t) &moves, sizeof moves);
304         bwrite(fd, (genericptr_t) &monstermoves, sizeof monstermoves);
305         bwrite(fd, (genericptr_t) &quest_status, sizeof(struct q_score));
306         bwrite(fd, (genericptr_t) spl_book,
307                                 sizeof(struct spell) * (MAXSPELL + 1));
308         save_artifacts(fd);
309         save_oracles(fd, mode);
310         if(ustuck_id)
311             bwrite(fd, (genericptr_t) &ustuck_id, sizeof ustuck_id);
312 #ifdef STEED
313         if(usteed_id)
314             bwrite(fd, (genericptr_t) &usteed_id, sizeof usteed_id);
315 #endif
316         bwrite(fd, (genericptr_t) pl_character, sizeof pl_character);
317         bwrite(fd, (genericptr_t) pl_fruit, sizeof pl_fruit);
318         bwrite(fd, (genericptr_t) &current_fruit, sizeof current_fruit);
319         savefruitchn(fd, mode);
320         savenames(fd, mode);
321         save_waterlevel(fd, mode);
322         bflush(fd);
323 }
324
325 #ifdef INSURANCE
326 void
327 savestateinlock()
328 {
329         int fd, hpid;
330         static boolean havestate = TRUE;
331         char whynot[BUFSZ];
332
333         /* When checkpointing is on, the full state needs to be written
334          * on each checkpoint.  When checkpointing is off, only the pid
335          * needs to be in the level.0 file, so it does not need to be
336          * constantly rewritten.  When checkpointing is turned off during
337          * a game, however, the file has to be rewritten once to truncate
338          * it and avoid restoring from outdated information.
339          *
340          * Restricting havestate to this routine means that an additional
341          * noop pid rewriting will take place on the first "checkpoint" after
342          * the game is started or restored, if checkpointing is off.
343          */
344         if (flags.ins_chkpt || havestate) {
345                 /* save the rest of the current game state in the lock file,
346                  * following the original int pid, the current level number,
347                  * and the current savefile name, which should not be subject
348                  * to any internal compression schemes since they must be
349                  * readable by an external utility
350                  */
351                 fd = open_levelfile(0, whynot);
352                 if (fd < 0) {
353                     pline("%s", whynot);
354                     pline("Probably someone removed it.");
355                     killer = whynot;
356                     done(TRICKED);
357                     return;
358                 }
359
360                 (void) read(fd, (genericptr_t) &hpid, sizeof(hpid));
361                 if (hackpid != hpid) {
362                     Sprintf(whynot,
363                             "Level #0 pid (%d) doesn't match ours (%d)!",
364                             hpid, hackpid);
365                     pline("%s", whynot);
366                     killer = whynot;
367                     done(TRICKED);
368                 }
369                 (void) close(fd);
370
371                 fd = create_levelfile(0, whynot);
372                 if (fd < 0) {
373                     pline("%s", whynot);
374                     killer = whynot;
375                     done(TRICKED);
376                     return;
377                 }
378                 (void) write(fd, (genericptr_t) &hackpid, sizeof(hackpid));
379                 if (flags.ins_chkpt) {
380                     int currlev = ledger_no(&u.uz);
381
382                     (void) write(fd, (genericptr_t) &currlev, sizeof(currlev));
383                     save_savefile_name(fd);
384                     store_version(fd);
385 #ifdef STORE_PLNAME_IN_FILE
386                     bwrite(fd, (genericptr_t) plname, PL_NSIZ);
387 #endif
388                     ustuck_id = (u.ustuck ? u.ustuck->m_id : 0);
389 #ifdef STEED
390                     usteed_id = (u.usteed ? u.usteed->m_id : 0);
391 #endif
392                     savegamestate(fd, WRITE_SAVE);
393                 }
394                 bclose(fd);
395         }
396         havestate = flags.ins_chkpt;
397 }
398 #endif
399
400 #ifdef MFLOPPY
401 boolean
402 savelev(fd, lev, mode)
403 int fd;
404 xchar lev;
405 int mode;
406 {
407         if (mode & COUNT_SAVE) {
408                 bytes_counted = 0;
409                 savelev0(fd, lev, COUNT_SAVE);
410                 /* probably bytes_counted will be filled in again by an
411                  * immediately following WRITE_SAVE anyway, but we'll
412                  * leave it out of checkspace just in case */
413                 if (iflags.checkspace) {
414                         while (bytes_counted > freediskspace(levels))
415                                 if (!swapout_oldest())
416                                         return FALSE;
417                 }
418         }
419         if (mode & (WRITE_SAVE | FREE_SAVE)) {
420                 bytes_counted = 0;
421                 savelev0(fd, lev, mode);
422         }
423         if (mode != FREE_SAVE) {
424                 level_info[lev].where = ACTIVE;
425                 level_info[lev].time = moves;
426                 level_info[lev].size = bytes_counted;
427         }
428         return TRUE;
429 }
430
431 STATIC_OVL void
432 savelev0(fd,lev,mode)
433 #else
434 void
435 savelev(fd,lev,mode)
436 #endif
437 int fd;
438 xchar lev;
439 int mode;
440 {
441 #ifdef TOS
442         short tlev;
443 #endif
444
445         /* if we're tearing down the current level without saving anything
446            (which happens upon entrance to the endgame or after an aborted
447            restore attempt) then we don't want to do any actual I/O */
448         if (mode == FREE_SAVE) goto skip_lots;
449         if (iflags.purge_monsters) {
450                 /* purge any dead monsters (necessary if we're starting
451                  * a panic save rather than a normal one, or sometimes
452                  * when changing levels without taking time -- e.g.
453                  * create statue trap then immediately level teleport) */
454                 dmonsfree();
455         }
456
457         if(fd < 0) panic("Save on bad file!");  /* impossible */
458 #ifdef MFLOPPY
459         count_only = (mode & COUNT_SAVE);
460 #endif
461         if (lev >= 0 && lev <= maxledgerno())
462             level_info[lev].flags |= VISITED;
463         bwrite(fd,(genericptr_t) &hackpid,sizeof(hackpid));
464 #ifdef TOS
465         tlev=lev; tlev &= 0x00ff;
466         bwrite(fd,(genericptr_t) &tlev,sizeof(tlev));
467 #else
468         bwrite(fd,(genericptr_t) &lev,sizeof(lev));
469 #endif
470 #ifdef RLECOMP
471         {
472             /* perform run-length encoding of rm structs */
473             struct rm *prm, *rgrm;
474             int x, y;
475             uchar match;
476
477             rgrm = &levl[0][0];         /* start matching at first rm */
478             match = 0;
479
480             for (y = 0; y < ROWNO; y++) {
481                 for (x = 0; x < COLNO; x++) {
482                     prm = &levl[x][y];
483                     if (prm->glyph == rgrm->glyph
484                         && prm->typ == rgrm->typ
485                         && prm->seenv == rgrm->seenv
486                         && prm->horizontal == rgrm->horizontal
487                         && prm->flags == rgrm->flags
488                         && prm->lit == rgrm->lit
489                         && prm->waslit == rgrm->waslit
490                         && prm->roomno == rgrm->roomno
491                         && prm->edge == rgrm->edge) {
492                         match++;
493                         if (match > 254) {
494                             match = 254;        /* undo this match */
495                             goto writeout;
496                         }
497                     } else {
498                         /* the run has been broken,
499                          * write out run-length encoding */
500                     writeout:
501                         bwrite(fd, (genericptr_t)&match, sizeof(uchar));
502                         bwrite(fd, (genericptr_t)rgrm, sizeof(struct rm));
503                         /* start encoding again. we have at least 1 rm
504                          * in the next run, viz. this one. */
505                         match = 1;
506                         rgrm = prm;
507                     }
508                 }
509             }
510             if (match > 0) {
511                 bwrite(fd, (genericptr_t)&match, sizeof(uchar));
512                 bwrite(fd, (genericptr_t)rgrm, sizeof(struct rm));
513             }
514         }
515 #else
516         bwrite(fd,(genericptr_t) levl,sizeof(levl));
517 #endif /* RLECOMP */
518
519         bwrite(fd,(genericptr_t) &monstermoves,sizeof(monstermoves));
520         bwrite(fd,(genericptr_t) &upstair,sizeof(stairway));
521         bwrite(fd,(genericptr_t) &dnstair,sizeof(stairway));
522         bwrite(fd,(genericptr_t) &upladder,sizeof(stairway));
523         bwrite(fd,(genericptr_t) &dnladder,sizeof(stairway));
524         bwrite(fd,(genericptr_t) &sstairs,sizeof(stairway));
525         bwrite(fd,(genericptr_t) &updest,sizeof(dest_area));
526         bwrite(fd,(genericptr_t) &dndest,sizeof(dest_area));
527         bwrite(fd,(genericptr_t) &level.flags,sizeof(level.flags));
528         bwrite(fd, (genericptr_t) doors, sizeof(doors));
529         save_rooms(fd); /* no dynamic memory to reclaim */
530
531         /* from here on out, saving also involves allocated memory cleanup */
532  skip_lots:
533         /* must be saved before mons, objs, and buried objs */
534         save_timers(fd, mode, RANGE_LEVEL);
535         save_light_sources(fd, mode, RANGE_LEVEL);
536
537         savemonchn(fd, fmon, mode);
538         save_worm(fd, mode);    /* save worm information */
539         savetrapchn(fd, ftrap, mode);
540         saveobjchn(fd, fobj, mode);
541         saveobjchn(fd, level.buriedobjlist, mode);
542         saveobjchn(fd, billobjs, mode);
543         if (release_data(mode)) {
544             fmon = 0;
545             ftrap = 0;
546             fobj = 0;
547             level.buriedobjlist = 0;
548             billobjs = 0;
549         }
550         save_engravings(fd, mode);
551         savedamage(fd, mode);
552         save_regions(fd, mode);
553         if (mode != FREE_SAVE) bflush(fd);
554 }
555
556 #ifdef ZEROCOMP
557 /* The runs of zero-run compression are flushed after the game state or a
558  * level is written out.  This adds a couple bytes to a save file, where
559  * the runs could be mashed together, but it allows gluing together game
560  * state and level files to form a save file, and it means the flushing
561  * does not need to be specifically called for every other time a level
562  * file is written out.
563  */
564
565 #define RLESC '\0'    /* Leading character for run of LRESC's */
566 #define flushoutrun(ln) (bputc(RLESC), bputc(ln), ln = -1)
567
568 #ifndef ZEROCOMP_BUFSIZ
569 # define ZEROCOMP_BUFSIZ BUFSZ
570 #endif
571 static NEARDATA unsigned char outbuf[ZEROCOMP_BUFSIZ];
572 static NEARDATA unsigned short outbufp = 0;
573 static NEARDATA short outrunlength = -1;
574 static NEARDATA int bwritefd;
575 static NEARDATA boolean compressing = FALSE;
576
577 /*dbg()
578 {
579     HUP printf("outbufp %d outrunlength %d\n", outbufp,outrunlength);
580 }*/
581
582 STATIC_OVL void
583 bputc(c)
584 int c;
585 {
586 #ifdef MFLOPPY
587     bytes_counted++;
588     if (count_only)
589       return;
590 #endif
591     if (outbufp >= sizeof outbuf) {
592         (void) write(bwritefd, outbuf, sizeof outbuf);
593         outbufp = 0;
594     }
595     outbuf[outbufp++] = (unsigned char)c;
596 }
597
598 /*ARGSUSED*/
599 void
600 bufon(fd)
601 int fd;
602 {
603     compressing = TRUE;
604     return;
605 }
606
607 /*ARGSUSED*/
608 void
609 bufoff(fd)
610 int fd;
611 {
612     if (outbufp) {
613         outbufp = 0;
614         panic("closing file with buffered data still unwritten");
615     }
616     outrunlength = -1;
617     compressing = FALSE;
618     return;
619 }
620
621 void
622 bflush(fd)  /* flush run and buffer */
623 register int fd;
624 {
625     bwritefd = fd;
626     if (outrunlength >= 0) {    /* flush run */
627         flushoutrun(outrunlength);
628     }
629 #ifdef MFLOPPY
630     if (count_only) outbufp = 0;
631 #endif
632
633     if (outbufp) {
634         if (write(fd, outbuf, outbufp) != outbufp) {
635 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
636             if (program_state.done_hup)
637                 terminate(EXIT_FAILURE);
638             else
639 #endif
640                 bclose(fd);     /* panic (outbufp != 0) */
641         }
642         outbufp = 0;
643     }
644 }
645
646 void
647 bwrite(fd, loc, num)
648 int fd;
649 genericptr_t loc;
650 register unsigned num;
651 {
652     register unsigned char *bp = (unsigned char *)loc;
653
654     if (!compressing) {
655 #ifdef MFLOPPY
656         bytes_counted += num;
657         if (count_only) return;
658 #endif
659         if ((unsigned) write(fd, loc, num) != num) {
660 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
661             if (program_state.done_hup)
662                 terminate(EXIT_FAILURE);
663             else
664 #endif
665                 panic("cannot write %u bytes to file #%d", num, fd);
666         }
667     } else {
668         bwritefd = fd;
669         for (; num; num--, bp++) {
670             if (*bp == RLESC) { /* One more char in run */
671                 if (++outrunlength == 0xFF) {
672                     flushoutrun(outrunlength);
673                 }
674             } else {            /* end of run */
675                 if (outrunlength >= 0) {        /* flush run */
676                     flushoutrun(outrunlength);
677                 }
678                 bputc(*bp);
679             }
680         }
681     }
682 }
683
684 void
685 bclose(fd)
686 int fd;
687 {
688     bufoff(fd);
689     (void) close(fd);
690     return;
691 }
692
693 #else /* ZEROCOMP */
694
695 static int bw_fd = -1;
696 static FILE *bw_FILE = 0;
697 static boolean buffering = FALSE;
698
699 void
700 bufon(fd)
701     int fd;
702 {
703 #ifdef UNIX
704     if(bw_fd >= 0)
705         panic("double buffering unexpected");
706     bw_fd = fd;
707     if((bw_FILE = fdopen(fd, "w")) == 0)
708         panic("buffering of file %d failed", fd);
709 #endif
710     buffering = TRUE;
711 }
712
713 void
714 bufoff(fd)
715 int fd;
716 {
717     bflush(fd);
718     buffering = FALSE;
719 }
720
721 void
722 bflush(fd)
723     int fd;
724 {
725 #ifdef UNIX
726     if(fd == bw_fd) {
727         if(fflush(bw_FILE) == EOF)
728             panic("flush of savefile failed!");
729     }
730 #endif
731     return;
732 }
733
734 void
735 bwrite(fd,loc,num)
736 register int fd;
737 register genericptr_t loc;
738 register unsigned num;
739 {
740         boolean failed;
741
742 #ifdef MFLOPPY
743         bytes_counted += num;
744         if (count_only) return;
745 #endif
746
747 #ifdef UNIX
748         if (buffering) {
749             if(fd != bw_fd)
750                 panic("unbuffered write to fd %d (!= %d)", fd, bw_fd);
751
752             failed = (fwrite(loc, (int)num, 1, bw_FILE) != 1);
753         } else
754 #endif /* UNIX */
755         {
756 /* lint wants the 3rd arg of write to be an int; lint -p an unsigned */
757 #if defined(BSD) || defined(ULTRIX)
758             failed = (write(fd, loc, (int)num) != (int)num);
759 #else /* e.g. SYSV, __TURBOC__ */
760             failed = (write(fd, loc, num) != num);
761 #endif
762         }
763
764         if (failed) {
765 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
766             if (program_state.done_hup)
767                 terminate(EXIT_FAILURE);
768             else
769 #endif
770                 panic("cannot write %u bytes to file #%d", num, fd);
771         }
772 }
773
774 void
775 bclose(fd)
776     int fd;
777 {
778     bufoff(fd);
779 #ifdef UNIX
780     if (fd == bw_fd) {
781         (void) fclose(bw_FILE);
782         bw_fd = -1;
783         bw_FILE = 0;
784     } else
785 #endif
786         (void) close(fd);
787     return;
788 }
789 #endif /* ZEROCOMP */
790
791 STATIC_OVL void
792 savelevchn(fd, mode)
793 register int fd, mode;
794 {
795         s_level *tmplev, *tmplev2;
796         int cnt = 0;
797
798         for (tmplev = sp_levchn; tmplev; tmplev = tmplev->next) cnt++;
799         if (perform_bwrite(mode))
800             bwrite(fd, (genericptr_t) &cnt, sizeof(int));
801
802         for (tmplev = sp_levchn; tmplev; tmplev = tmplev2) {
803             tmplev2 = tmplev->next;
804             if (perform_bwrite(mode))
805                 bwrite(fd, (genericptr_t) tmplev, sizeof(s_level));
806             if (release_data(mode))
807                 free((genericptr_t) tmplev);
808         }
809         if (release_data(mode))
810             sp_levchn = 0;
811 }
812
813 STATIC_OVL void
814 savedamage(fd, mode)
815 register int fd, mode;
816 {
817         register struct damage *damageptr, *tmp_dam;
818         unsigned int xl = 0;
819
820         damageptr = level.damagelist;
821         for (tmp_dam = damageptr; tmp_dam; tmp_dam = tmp_dam->next)
822             xl++;
823         if (perform_bwrite(mode))
824             bwrite(fd, (genericptr_t) &xl, sizeof(xl));
825
826         while (xl--) {
827             if (perform_bwrite(mode))
828                 bwrite(fd, (genericptr_t) damageptr, sizeof(*damageptr));
829             tmp_dam = damageptr;
830             damageptr = damageptr->next;
831             if (release_data(mode))
832                 free((genericptr_t)tmp_dam);
833         }
834         if (release_data(mode))
835             level.damagelist = 0;
836 }
837
838 STATIC_OVL void
839 saveobjchn(fd, otmp, mode)
840 register int fd, mode;
841 register struct obj *otmp;
842 {
843         register struct obj *otmp2;
844         unsigned int xl;
845         int minusone = -1;
846
847         while(otmp) {
848             otmp2 = otmp->nobj;
849             if (perform_bwrite(mode)) {
850                 xl = otmp->oxlth + otmp->onamelth;
851                 bwrite(fd, (genericptr_t) &xl, sizeof(int));
852                 bwrite(fd, (genericptr_t) otmp, xl + sizeof(struct obj));
853             }
854             if (Has_contents(otmp))
855                 saveobjchn(fd,otmp->cobj,mode);
856             if (release_data(mode)) {
857                 if (otmp->oclass == FOOD_CLASS) food_disappears(otmp);
858                 if (otmp->oclass == SPBOOK_CLASS) book_disappears(otmp);
859                 otmp->where = OBJ_FREE; /* set to free so dealloc will work */
860                 otmp->timed = 0;        /* not timed any more */
861                 otmp->lamplit = 0;      /* caller handled lights */
862                 dealloc_obj(otmp);
863             }
864             otmp = otmp2;
865         }
866         if (perform_bwrite(mode))
867             bwrite(fd, (genericptr_t) &minusone, sizeof(int));
868 }
869
870 STATIC_OVL void
871 savemonchn(fd, mtmp, mode)
872 register int fd, mode;
873 register struct monst *mtmp;
874 {
875         register struct monst *mtmp2;
876         unsigned int xl;
877         int minusone = -1;
878         struct permonst *monbegin = &mons[0];
879
880         if (perform_bwrite(mode))
881             bwrite(fd, (genericptr_t) &monbegin, sizeof(monbegin));
882
883         while (mtmp) {
884             mtmp2 = mtmp->nmon;
885             if (perform_bwrite(mode)) {
886                 xl = mtmp->mxlth + mtmp->mnamelth;
887                 bwrite(fd, (genericptr_t) &xl, sizeof(int));
888                 bwrite(fd, (genericptr_t) mtmp, xl + sizeof(struct monst));
889             }
890             if (mtmp->minvent)
891                 saveobjchn(fd,mtmp->minvent,mode);
892             if (release_data(mode))
893                 dealloc_monst(mtmp);
894             mtmp = mtmp2;
895         }
896         if (perform_bwrite(mode))
897             bwrite(fd, (genericptr_t) &minusone, sizeof(int));
898 }
899
900 STATIC_OVL void
901 savetrapchn(fd, trap, mode)
902 register int fd, mode;
903 register struct trap *trap;
904 {
905         register struct trap *trap2;
906
907         while (trap) {
908             trap2 = trap->ntrap;
909             if (perform_bwrite(mode))
910                 bwrite(fd, (genericptr_t) trap, sizeof(struct trap));
911             if (release_data(mode))
912                 dealloc_trap(trap);
913             trap = trap2;
914         }
915         if (perform_bwrite(mode))
916             bwrite(fd, (genericptr_t)nulls, sizeof(struct trap));
917 }
918
919 /* save all the fruit names and ID's; this is used only in saving whole games
920  * (not levels) and in saving bones levels.  When saving a bones level,
921  * we only want to save the fruits which exist on the bones level; the bones
922  * level routine marks nonexistent fruits by making the fid negative.
923  */
924 void
925 savefruitchn(fd, mode)
926 register int fd, mode;
927 {
928         register struct fruit *f2, *f1;
929
930         f1 = ffruit;
931         while (f1) {
932             f2 = f1->nextf;
933             if (f1->fid >= 0 && perform_bwrite(mode))
934                 bwrite(fd, (genericptr_t) f1, sizeof(struct fruit));
935             if (release_data(mode))
936                 dealloc_fruit(f1);
937             f1 = f2;
938         }
939         if (perform_bwrite(mode))
940             bwrite(fd, (genericptr_t)nulls, sizeof(struct fruit));
941         if (release_data(mode))
942             ffruit = 0;
943 }
944
945 /* also called by prscore(); this probably belongs in dungeon.c... */
946 void
947 free_dungeons()
948 {
949 #ifdef FREE_ALL_MEMORY
950         savelevchn(0, FREE_SAVE);
951         save_dungeon(0, FALSE, TRUE);
952 #endif
953         return;
954 }
955
956 void
957 freedynamicdata()
958 {
959         unload_qtlist();
960         free_invbuf();  /* let_to_name (invent.c) */
961         free_youbuf();  /* You_buf,&c (pline.c) */
962         tmp_at(DISP_FREEMEM, 0);        /* temporary display effects */
963 #ifdef FREE_ALL_MEMORY
964 # define freeobjchn(X)  (saveobjchn(0, X, FREE_SAVE),  X = 0)
965 # define freemonchn(X)  (savemonchn(0, X, FREE_SAVE),  X = 0)
966 # define freetrapchn(X) (savetrapchn(0, X, FREE_SAVE), X = 0)
967 # define freefruitchn()  savefruitchn(0, FREE_SAVE)
968 # define freenames()     savenames(0, FREE_SAVE)
969 # define free_oracles() save_oracles(0, FREE_SAVE)
970 # define free_waterlevel() save_waterlevel(0, FREE_SAVE)
971 # define free_worm()     save_worm(0, FREE_SAVE)
972 # define free_timers(R)  save_timers(0, FREE_SAVE, R)
973 # define free_light_sources(R) save_light_sources(0, FREE_SAVE, R);
974 # define free_engravings() save_engravings(0, FREE_SAVE)
975 # define freedamage()    savedamage(0, FREE_SAVE)
976 # define free_animals()  mon_animal_list(FALSE)
977
978         /* move-specific data */
979         dmonsfree();            /* release dead monsters */
980
981         /* level-specific data */
982         free_timers(RANGE_LEVEL);
983         free_light_sources(RANGE_LEVEL);
984         freemonchn(fmon);
985         free_worm();            /* release worm segment information */
986         freetrapchn(ftrap);
987         freeobjchn(fobj);
988         freeobjchn(level.buriedobjlist);
989         freeobjchn(billobjs);
990         free_engravings();
991         freedamage();
992
993         /* game-state data */
994         free_timers(RANGE_GLOBAL);
995         free_light_sources(RANGE_GLOBAL);
996         freeobjchn(invent);
997         freeobjchn(migrating_objs);
998         freemonchn(migrating_mons);
999         freemonchn(mydogs);             /* ascension or dungeon escape */
1000      /* freelevchn();   [folded into free_dungeons()] */
1001         free_animals();
1002         free_oracles();
1003         freefruitchn();
1004         freenames();
1005         free_waterlevel();
1006         free_dungeons();
1007
1008         /* some pointers in iflags */
1009         if (iflags.wc_font_map) free(iflags.wc_font_map);
1010         if (iflags.wc_font_message) free(iflags.wc_font_message);
1011         if (iflags.wc_font_text) free(iflags.wc_font_text);
1012         if (iflags.wc_font_menu) free(iflags.wc_font_menu);
1013         if (iflags.wc_font_status) free(iflags.wc_font_status);
1014         if (iflags.wc_tile_file) free(iflags.wc_tile_file);
1015 #ifdef AUTOPICKUP_EXCEPTIONS
1016         free_autopickup_exceptions();
1017 #endif
1018
1019 #endif  /* FREE_ALL_MEMORY */
1020         return;
1021 }
1022
1023 #ifdef MFLOPPY
1024 boolean
1025 swapin_file(lev)
1026 int lev;
1027 {
1028         char to[PATHLEN], from[PATHLEN];
1029
1030         Sprintf(from, "%s%s", permbones, alllevels);
1031         Sprintf(to, "%s%s", levels, alllevels);
1032         set_levelfile_name(from, lev);
1033         set_levelfile_name(to, lev);
1034         if (iflags.checkspace) {
1035                 while (level_info[lev].size > freediskspace(to))
1036                         if (!swapout_oldest())
1037                                 return FALSE;
1038         }
1039 # ifdef WIZARD
1040         if (wizard) {
1041                 pline("Swapping in `%s'.", from);
1042                 wait_synch();
1043         }
1044 # endif
1045         copyfile(from, to);
1046         (void) unlink(from);
1047         level_info[lev].where = ACTIVE;
1048         return TRUE;
1049 }
1050
1051 STATIC_OVL boolean
1052 swapout_oldest() {
1053         char to[PATHLEN], from[PATHLEN];
1054         int i, oldest;
1055         long oldtime;
1056
1057         if (!ramdisk)
1058                 return FALSE;
1059         for (i = 1, oldtime = 0, oldest = 0; i <= maxledgerno(); i++)
1060                 if (level_info[i].where == ACTIVE
1061                 && (!oldtime || level_info[i].time < oldtime)) {
1062                         oldest = i;
1063                         oldtime = level_info[i].time;
1064                 }
1065         if (!oldest)
1066                 return FALSE;
1067         Sprintf(from, "%s%s", levels, alllevels);
1068         Sprintf(to, "%s%s", permbones, alllevels);
1069         set_levelfile_name(from, oldest);
1070         set_levelfile_name(to, oldest);
1071 # ifdef WIZARD
1072         if (wizard) {
1073                 pline("Swapping out `%s'.", from);
1074                 wait_synch();
1075         }
1076 # endif
1077         copyfile(from, to);
1078         (void) unlink(from);
1079         level_info[oldest].where = SWAPPED;
1080         return TRUE;
1081 }
1082
1083 STATIC_OVL void
1084 copyfile(from, to)
1085 char *from, *to;
1086 {
1087 # ifdef TOS
1088
1089         if (_copyfile(from, to))
1090                 panic("Can't copy %s to %s", from, to);
1091 # else
1092         char buf[BUFSIZ];       /* this is system interaction, therefore
1093                                  * BUFSIZ instead of NetHack's BUFSZ */
1094         int nfrom, nto, fdfrom, fdto;
1095
1096         if ((fdfrom = open(from, O_RDONLY | O_BINARY, FCMASK)) < 0)
1097                 panic("Can't copy from %s !?", from);
1098         if ((fdto = open(to, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, FCMASK)) < 0)
1099                 panic("Can't copy to %s", to);
1100         do {
1101                 nfrom = read(fdfrom, buf, BUFSIZ);
1102                 nto = write(fdto, buf, nfrom);
1103                 if (nto != nfrom)
1104                         panic("Copyfile failed!");
1105         } while (nfrom == BUFSIZ);
1106         (void) close(fdfrom);
1107         (void) close(fdto);
1108 # endif /* TOS */
1109 }
1110
1111 void
1112 co_false()          /* see comment in bones.c */
1113 {
1114     count_only = FALSE;
1115     return;
1116 }
1117
1118 #endif /* MFLOPPY */
1119
1120 /*save.c*/