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