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