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