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