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
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
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
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
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
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
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
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
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
577  | bufon(fd)
578  | int fd;
579  | {
580  |     compressing = TRUE;
581  |     return;
582  | }
583  | 
584  | /*ARGSUSED*/
585  | void
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
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
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
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
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
691  | bufoff(fd)
692  | int fd;
693  | {
694  |     bflush(fd);
695  |     buffering = FALSE;
696  | }
697  | 
698  | void
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
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
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
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
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
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
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
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
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
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
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
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
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
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
1077 | co_false()	    /* see comment in bones.c */
1078 | {
1079 |     count_only = FALSE;
1080 |     return;
1081 | }
1082 | 
1083 | #endif /* MFLOPPY */
1084 | 
1085 | /*save.c*/