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) &ltmp, 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) &current_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