1/*	SCCS Id: @(#)restore.c	3.4	2003/09/06	*/
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 "tcap.h" /* for TERMLIB and ASCIIGRAPH */
8
9#if defined(MICRO)
10extern int dotcnt;	/* shared with save */
11extern int dotrow;	/* shared with save */
12#endif
13
14#ifdef USE_TILES
15extern void FDECL(substitute_tiles, (d_level *));       /* from tile.c */
16#endif
17
18#ifdef ZEROCOMP
19static int NDECL(mgetc);
20#endif
21STATIC_DCL void NDECL(find_lev_obj);
22STATIC_DCL void FDECL(restlevchn, (int));
23STATIC_DCL void FDECL(restdamage, (int,BOOLEAN_P));
24STATIC_DCL struct obj *FDECL(restobjchn, (int,BOOLEAN_P,BOOLEAN_P));
25STATIC_DCL struct monst *FDECL(restmonchn, (int,BOOLEAN_P));
26STATIC_DCL struct fruit *FDECL(loadfruitchn, (int));
27STATIC_DCL void FDECL(freefruitchn, (struct fruit *));
28STATIC_DCL void FDECL(ghostfruit, (struct obj *));
29STATIC_DCL boolean FDECL(restgamestate, (int, unsigned int *, unsigned int *));
30STATIC_DCL void FDECL(restlevelstate, (unsigned int, unsigned int));
31STATIC_DCL int FDECL(restlevelfile, (int,XCHAR_P));
32STATIC_DCL void FDECL(reset_oattached_mids, (BOOLEAN_P));
33
34/*
35 * Save a mapping of IDs from ghost levels to the current level.  This
36 * map is used by the timer routines when restoring ghost levels.
37 */
38#define N_PER_BUCKET 64
39struct bucket {
40    struct bucket *next;
41    struct {
42	unsigned gid;	/* ghost ID */
43	unsigned nid;	/* new ID */
44    } map[N_PER_BUCKET];
45};
46
47STATIC_DCL void NDECL(clear_id_mapping);
48STATIC_DCL void FDECL(add_id_mapping, (unsigned, unsigned));
49
50static int n_ids_mapped = 0;
51static struct bucket *id_map = 0;
52
53
54#ifdef AMII_GRAPHICS
55void FDECL( amii_setpens, (int) );	/* use colors from save file */
56extern int amii_numcolors;
57#endif
58
59#include "quest.h"
60
61boolean restoring = FALSE;
62static NEARDATA struct fruit *oldfruit;
63static NEARDATA long omoves;
64
65#define Is_IceBox(o) ((o)->otyp == ICE_BOX ? TRUE : FALSE)
66
67/* Recalculate level.objects[x][y], since this info was not saved. */
68STATIC_OVL void
69find_lev_obj()
70{
71	register struct obj *fobjtmp = (struct obj *)0;
72	register struct obj *otmp;
73	int x,y;
74
75	for(x=0; x<COLNO; x++) for(y=0; y<ROWNO; y++)
76		level.objects[x][y] = (struct obj *)0;
77
78	/*
79	 * Reverse the entire fobj chain, which is necessary so that we can
80	 * place the objects in the proper order.  Make all obj in chain
81	 * OBJ_FREE so place_object will work correctly.
82	 */
83	while ((otmp = fobj) != 0) {
84		fobj = otmp->nobj;
85		otmp->nobj = fobjtmp;
86		otmp->where = OBJ_FREE;
87		fobjtmp = otmp;
88	}
89	/* fobj should now be empty */
90
91	/* Set level.objects (as well as reversing the chain back again) */
92	while ((otmp = fobjtmp) != 0) {
93		fobjtmp = otmp->nobj;
94		place_object(otmp, otmp->ox, otmp->oy);
95	}
96}
97
98/* Things that were marked "in_use" when the game was saved (ex. via the
99 * infamous "HUP" cheat) get used up here.
100 */
101void
102inven_inuse(quietly)
103boolean quietly;
104{
105	register struct obj *otmp, *otmp2;
106
107	for (otmp = invent; otmp; otmp = otmp2) {
108	    otmp2 = otmp->nobj;
109#ifndef GOLDOBJ
110	    if (otmp->oclass == COIN_CLASS) {
111		/* in_use gold is created by some menu operations */
112		if (!otmp->in_use) {
113		    impossible("inven_inuse: !in_use gold in inventory");
114		}
115		extract_nobj(otmp, &invent);
116		otmp->in_use = FALSE;
117		dealloc_obj(otmp);
118	    } else
119#endif /* GOLDOBJ */
120	    if (otmp->in_use) {
121		if (!quietly) pline("Finishing off %s...", xname(otmp));
122		useup(otmp);
123	    }
124	}
125}
126
127STATIC_OVL void
128restlevchn(fd)
129register int fd;
130{
131	int cnt;
132	s_level	*tmplev, *x;
133
134	sp_levchn = (s_level *) 0;
135	mread(fd, (genericptr_t) &cnt, sizeof(int));
136	for(; cnt > 0; cnt--) {
137
138	    tmplev = (s_level *)alloc(sizeof(s_level));
139	    mread(fd, (genericptr_t) tmplev, sizeof(s_level));
140	    if(!sp_levchn) sp_levchn = tmplev;
141	    else {
142
143		for(x = sp_levchn; x->next; x = x->next);
144		x->next = tmplev;
145	    }
146	    tmplev->next = (s_level *)0;
147	}
148}
149
150STATIC_OVL void
151restdamage(fd, ghostly)
152int fd;
153boolean ghostly;
154{
155	int counter;
156	struct damage *tmp_dam;
157
158	mread(fd, (genericptr_t) &counter, sizeof(counter));
159	if (!counter)
160	    return;
161	tmp_dam = (struct damage *)alloc(sizeof(struct damage));
162	while (--counter >= 0) {
163	    char damaged_shops[5], *shp = (char *)0;
164
165	    mread(fd, (genericptr_t) tmp_dam, sizeof(*tmp_dam));
166	    if (ghostly)
167		tmp_dam->when += (monstermoves - omoves);
168	    Strcpy(damaged_shops,
169		   in_rooms(tmp_dam->place.x, tmp_dam->place.y, SHOPBASE));
170	    if (u.uz.dlevel) {
171		/* when restoring, there are two passes over the current
172		 * level.  the first time, u.uz isn't set, so neither is
173		 * shop_keeper().  just wait and process the damage on
174		 * the second pass.
175		 */
176		for (shp = damaged_shops; *shp; shp++) {
177		    struct monst *shkp = shop_keeper(*shp);
178
179		    if (shkp && inhishop(shkp) &&
180			    repair_damage(shkp, tmp_dam, TRUE))
181			break;
182		}
183	    }
184	    if (!shp || !*shp) {
185		tmp_dam->next = level.damagelist;
186		level.damagelist = tmp_dam;
187		tmp_dam = (struct damage *)alloc(sizeof(*tmp_dam));
188	    }
189	}
190	free((genericptr_t)tmp_dam);
191}
192
193STATIC_OVL struct obj *
194restobjchn(fd, ghostly, frozen)
195register int fd;
196boolean ghostly, frozen;
197{
198	register struct obj *otmp, *otmp2 = 0;
199	register struct obj *first = (struct obj *)0;
200	int xl;
201
202	while(1) {
203		mread(fd, (genericptr_t) &xl, sizeof(xl));
204		if(xl == -1) break;
205		otmp = newobj(xl);
206		if(!first) first = otmp;
207		else otmp2->nobj = otmp;
208		mread(fd, (genericptr_t) otmp,
209					(unsigned) xl + sizeof(struct obj));
210		if (ghostly) {
211		    unsigned nid = flags.ident++;
212		    add_id_mapping(otmp->o_id, nid);
213		    otmp->o_id = nid;
214		}
215		if (ghostly && otmp->otyp == SLIME_MOLD) ghostfruit(otmp);
216		/* Ghost levels get object age shifted from old player's clock
217		 * to new player's clock.  Assumption: new player arrived
218		 * immediately after old player died.
219		 */
220		if (ghostly && !frozen && !age_is_relative(otmp))
221		    otmp->age = monstermoves - omoves + otmp->age;
222
223		/* get contents of a container or statue */
224		if (Has_contents(otmp)) {
225		    struct obj *otmp3;
226		    otmp->cobj = restobjchn(fd, ghostly, Is_IceBox(otmp));
227		    /* restore container back pointers */
228		    for (otmp3 = otmp->cobj; otmp3; otmp3 = otmp3->nobj)
229			otmp3->ocontainer = otmp;
230		}
231		if (otmp->bypass) otmp->bypass = 0;
232
233		otmp2 = otmp;
234	}
235	if(first && otmp2->nobj){
236		impossible("Restobjchn: error reading objchn.");
237		otmp2->nobj = 0;
238	}
239
240	return(first);
241}
242
243STATIC_OVL struct monst *
244restmonchn(fd, ghostly)
245register int fd;
246boolean ghostly;
247{
248	register struct monst *mtmp, *mtmp2 = 0;
249	register struct monst *first = (struct monst *)0;
250	int xl;
251	struct permonst *monbegin;
252	boolean moved;
253
254	/* get the original base address */
255	mread(fd, (genericptr_t)&monbegin, sizeof(monbegin));
256	moved = (monbegin != mons);
257
258	while(1) {
259		mread(fd, (genericptr_t) &xl, sizeof(xl));
260		if(xl == -1) break;
261		mtmp = newmonst(xl);
262		if(!first) first = mtmp;
263		else mtmp2->nmon = mtmp;
264		mread(fd, (genericptr_t) mtmp, (unsigned) xl + sizeof(struct monst));
265		if (ghostly) {
266			unsigned nid = flags.ident++;
267			add_id_mapping(mtmp->m_id, nid);
268			mtmp->m_id = nid;
269		}
270		if (moved && mtmp->data) {
271			int offset = mtmp->data - monbegin;	/*(ptrdiff_t)*/
272			mtmp->data = mons + offset;  /* new permonst location */
273		}
274		if (ghostly) {
275			int mndx = monsndx(mtmp->data);
276			if (propagate(mndx, TRUE, ghostly) == 0) {
277				/* cookie to trigger purge in getbones() */
278				mtmp->mhpmax = DEFUNCT_MONSTER;
279			}
280		}
281		if(mtmp->minvent) {
282			struct obj *obj;
283			mtmp->minvent = restobjchn(fd, ghostly, FALSE);
284			/* restore monster back pointer */
285			for (obj = mtmp->minvent; obj; obj = obj->nobj)
286				obj->ocarry = mtmp;
287		}
288		if (mtmp->mw) {
289			struct obj *obj;
290
291			for(obj = mtmp->minvent; obj; obj = obj->nobj)
292				if (obj->owornmask & W_WEP) break;
293			if (obj) mtmp->mw = obj;
294			else {
295				MON_NOWEP(mtmp);
296				impossible("bad monster weapon restore");
297			}
298		}
299
300		if (mtmp->isshk) restshk(mtmp, ghostly);
301		if (mtmp->ispriest) restpriest(mtmp, ghostly);
302
303		mtmp2 = mtmp;
304	}
305	if(first && mtmp2->nmon){
306		impossible("Restmonchn: error reading monchn.");
307		mtmp2->nmon = 0;
308	}
309	return(first);
310}
311
312STATIC_OVL struct fruit *
313loadfruitchn(fd)
314int fd;
315{
316	register struct fruit *flist, *fnext;
317
318	flist = 0;
319	while (fnext = newfruit(),
320	       mread(fd, (genericptr_t)fnext, sizeof *fnext),
321	       fnext->fid != 0) {
322		fnext->nextf = flist;
323		flist = fnext;
324	}
325	dealloc_fruit(fnext);
326	return flist;
327}
328
329STATIC_OVL void
330freefruitchn(flist)
331register struct fruit *flist;
332{
333	register struct fruit *fnext;
334
335	while (flist) {
336	    fnext = flist->nextf;
337	    dealloc_fruit(flist);
338	    flist = fnext;
339	}
340}
341
342STATIC_OVL void
343ghostfruit(otmp)
344register struct obj *otmp;
345{
346	register struct fruit *oldf;
347
348	for (oldf = oldfruit; oldf; oldf = oldf->nextf)
349		if (oldf->fid == otmp->spe) break;
350
351	if (!oldf) impossible("no old fruit?");
352	else otmp->spe = fruitadd(oldf->fname);
353}
354
355STATIC_OVL
356boolean
357restgamestate(fd, stuckid, steedid)
358register int fd;
359unsigned int *stuckid, *steedid;	/* STEED */
360{
361	/* discover is actually flags.explore */
362	boolean remember_discover = discover;
363	struct obj *otmp;
364	int uid;
365
366	mread(fd, (genericptr_t) &uid, sizeof uid);
367	if (uid != getuid()) {		/* strange ... */
368	    /* for wizard mode, issue a reminder; for others, treat it
369	       as an attempt to cheat and refuse to restore this file */
370	    pline("Saved game was not yours.");
371#ifdef WIZARD
372	    if (!wizard)
373#endif
374		return FALSE;
375	}
376
377	mread(fd, (genericptr_t) &flags, sizeof(struct flag));
378	flags.bypasses = 0;	/* never use the saved value of bypasses */
379	if (remember_discover) discover = remember_discover;
380
381	role_init();	/* Reset the initial role, race, gender, and alignment */
382#ifdef AMII_GRAPHICS
383	amii_setpens(amii_numcolors);	/* use colors from save file */
384#endif
385	mread(fd, (genericptr_t) &u, sizeof(struct you));
386	set_uasmon();
387#ifdef CLIPPING
388	cliparound(u.ux, u.uy);
389#endif
390	if(u.uhp <= 0 && (!Upolyd || u.mh <= 0)) {
391	    u.ux = u.uy = 0;	/* affects pline() [hence You()] */
392	    You("were not healthy enough to survive restoration.");
393	    /* wiz1_level.dlevel is used by mklev.c to see if lots of stuff is
394	     * uninitialized, so we only have to set it and not the other stuff.
395	     */
396	    wiz1_level.dlevel = 0;
397	    u.uz.dnum = 0;
398	    u.uz.dlevel = 1;
399	    return(FALSE);
400	}
401
402	/* this stuff comes after potential aborted restore attempts */
403	restore_timers(fd, RANGE_GLOBAL, FALSE, 0L);
404	restore_light_sources(fd);
405	invent = restobjchn(fd, FALSE, FALSE);
406	migrating_objs = restobjchn(fd, FALSE, FALSE);
407	migrating_mons = restmonchn(fd, FALSE);
408	mread(fd, (genericptr_t) mvitals, sizeof(mvitals));
409
410	/* this comes after inventory has been loaded */
411	for(otmp = invent; otmp; otmp = otmp->nobj)
412		if(otmp->owornmask)
413			setworn(otmp, otmp->owornmask);
414	/* reset weapon so that player will get a reminder about "bashing"
415	   during next fight when bare-handed or wielding an unconventional
416	   item; for pick-axe, we aren't able to distinguish between having
417	   applied or wielded it, so be conservative and assume the former */
418	otmp = uwep;	/* `uwep' usually init'd by setworn() in loop above */
419	uwep = 0;	/* clear it and have setuwep() reinit */
420	setuwep(otmp);	/* (don't need any null check here) */
421	if (!uwep || uwep->otyp == PICK_AXE || uwep->otyp == GRAPPLING_HOOK)
422	    unweapon = TRUE;
423
424	restore_dungeon(fd);
425	restlevchn(fd);
426	mread(fd, (genericptr_t) &moves, sizeof moves);
427	mread(fd, (genericptr_t) &monstermoves, sizeof monstermoves);
428	mread(fd, (genericptr_t) &quest_status, sizeof(struct q_score));
429	mread(fd, (genericptr_t) spl_book,
430				sizeof(struct spell) * (MAXSPELL + 1));
431	restore_artifacts(fd);
432	restore_oracles(fd);
433	if (u.ustuck)
434		mread(fd, (genericptr_t) stuckid, sizeof (*stuckid));
435#ifdef STEED
436	if (u.usteed)
437		mread(fd, (genericptr_t) steedid, sizeof (*steedid));
438#endif
439	mread(fd, (genericptr_t) pl_character, sizeof pl_character);
440
441	mread(fd, (genericptr_t) pl_fruit, sizeof pl_fruit);
442	mread(fd, (genericptr_t) &current_fruit, sizeof current_fruit);
443	freefruitchn(ffruit);	/* clean up fruit(s) made by initoptions() */
444	ffruit = loadfruitchn(fd);
445
446	restnames(fd);
447	restore_waterlevel(fd);
448	/* must come after all mons & objs are restored */
449	relink_timers(FALSE);
450	relink_light_sources(FALSE);
451	return(TRUE);
452}
453
454/* update game state pointers to those valid for the current level (so we
455 * don't dereference a wild u.ustuck when saving the game state, for instance)
456 */
457STATIC_OVL void
458restlevelstate(stuckid, steedid)
459unsigned int stuckid, steedid;	/* STEED */
460{
461	register struct monst *mtmp;
462
463	if (stuckid) {
464		for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
465			if (mtmp->m_id == stuckid) break;
466		if (!mtmp) panic("Cannot find the monster ustuck.");
467		u.ustuck = mtmp;
468	}
469#ifdef STEED
470	if (steedid) {
471		for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
472			if (mtmp->m_id == steedid) break;
473		if (!mtmp) panic("Cannot find the monster usteed.");
474		u.usteed = mtmp;
475		remove_monster(mtmp->mx, mtmp->my);
476	}
477#endif
478}
479
480/*ARGSUSED*/	/* fd used in MFLOPPY only */
481STATIC_OVL int
482restlevelfile(fd, ltmp)
483register int fd;
484xchar ltmp;
485#if defined(macintosh) && (defined(__SC__) || defined(__MRC__))
486# pragma unused(fd)
487#endif
488{
489	register int nfd;
490	char whynot[BUFSZ];
491
492	nfd = create_levelfile(ltmp, whynot);
493	if (nfd < 0) {
494		/* BUG: should suppress any attempt to write a panic
495		   save file if file creation is now failing... */
496		panic("restlevelfile: %s", whynot);
497	}
498#ifdef MFLOPPY
499	if (!savelev(nfd, ltmp, COUNT_SAVE)) {
500
501		/* The savelev can't proceed because the size required
502		 * is greater than the available disk space.
503		 */
504		pline("Not enough space on `%s' to restore your game.",
505			levels);
506
507		/* Remove levels and bones that may have been created.
508		 */
509		(void) close(nfd);
510# ifdef AMIGA
511		clearlocks();
512# else
513		eraseall(levels, alllevels);
514		eraseall(levels, allbones);
515
516		/* Perhaps the person would like to play without a
517		 * RAMdisk.
518		 */
519		if (ramdisk) {
520			/* PlaywoRAMdisk may not return, but if it does
521			 * it is certain that ramdisk will be 0.
522			 */
523			playwoRAMdisk();
524			/* Rewind save file and try again */
525			(void) lseek(fd, (off_t)0, 0);
526			(void) uptodate(fd, (char *)0);	/* skip version */
527			return dorecover(fd);	/* 0 or 1 */
528		} else {
529# endif
530			pline("Be seeing you...");
531			terminate(EXIT_SUCCESS);
532# ifndef AMIGA
533		}
534# endif
535	}
536#endif
537	bufon(nfd);
538	savelev(nfd, ltmp, WRITE_SAVE | FREE_SAVE);
539	bclose(nfd);
540	return(2);
541}
542
543int
544dorecover(fd)
545register int fd;
546{
547	unsigned int stuckid = 0, steedid = 0;	/* not a register */
548	xchar ltmp;
549	int rtmp;
550	struct obj *otmp;
551
552#ifdef STORE_PLNAME_IN_FILE
553	mread(fd, (genericptr_t) plname, PL_NSIZ);
554#endif
555
556	restoring = TRUE;
557	getlev(fd, 0, (xchar)0, FALSE);
558	if (!restgamestate(fd, &stuckid, &steedid)) {
559		display_nhwindow(WIN_MESSAGE, TRUE);
560		savelev(-1, 0, FREE_SAVE);	/* discard current level */
561		(void) close(fd);
562		(void) delete_savefile();
563		restoring = FALSE;
564		return(0);
565	}
566	restlevelstate(stuckid, steedid);
567#ifdef INSURANCE
568	savestateinlock();
569#endif
570	rtmp = restlevelfile(fd, ledger_no(&u.uz));
571	if (rtmp < 2) return(rtmp);  /* dorecover called recursively */
572
573	/* these pointers won't be valid while we're processing the
574	 * other levels, but they'll be reset again by restlevelstate()
575	 * afterwards, and in the meantime at least u.usteed may mislead
576	 * place_monster() on other levels
577	 */
578	u.ustuck = (struct monst *)0;
579#ifdef STEED
580	u.usteed = (struct monst *)0;
581#endif
582
583#ifdef MICRO
584# ifdef AMII_GRAPHICS
585	{
586	extern struct window_procs amii_procs;
587	if(windowprocs.win_init_nhwindows== amii_procs.win_init_nhwindows){
588	    extern winid WIN_BASE;
589	    clear_nhwindow(WIN_BASE);	/* hack until there's a hook for this */
590	}
591	}
592# else
593	clear_nhwindow(WIN_MAP);
594# endif
595	clear_nhwindow(WIN_MESSAGE);
596	You("return to level %d in %s%s.",
597		depth(&u.uz), dungeons[u.uz.dnum].dname,
598		flags.debug ? " while in debug mode" :
599		flags.explore ? " while in explore mode" : "");
600	curs(WIN_MAP, 1, 1);
601	dotcnt = 0;
602	dotrow = 2;
603	if (strncmpi("X11", windowprocs.name, 3))
604    	  putstr(WIN_MAP, 0, "Restoring:");
605#endif
606	while(1) {
607#ifdef ZEROCOMP
608		if(mread(fd, (genericptr_t) &ltmp, sizeof ltmp) < 0)
609#else
610		if(read(fd, (genericptr_t) &ltmp, sizeof ltmp) != sizeof ltmp)
611#endif
612			break;
613		getlev(fd, 0, ltmp, FALSE);
614#ifdef MICRO
615		curs(WIN_MAP, 1+dotcnt++, dotrow);
616		if (dotcnt >= (COLNO - 1)) {
617			dotrow++;
618			dotcnt = 0;
619		}
620		if (strncmpi("X11", windowprocs.name, 3)){
621		  putstr(WIN_MAP, 0, ".");
622		}
623		mark_synch();
624#endif
625		rtmp = restlevelfile(fd, ltmp);
626		if (rtmp < 2) return(rtmp);  /* dorecover called recursively */
627	}
628
629#ifdef BSD
630	(void) lseek(fd, 0L, 0);
631#else
632	(void) lseek(fd, (off_t)0, 0);
633#endif
634	(void) uptodate(fd, (char *)0);		/* skip version info */
635#ifdef STORE_PLNAME_IN_FILE
636	mread(fd, (genericptr_t) plname, PL_NSIZ);
637#endif
638	getlev(fd, 0, (xchar)0, FALSE);
639	(void) close(fd);
640
641	if (!wizard && !discover)
642		(void) delete_savefile();
643#ifdef REINCARNATION
644	if (Is_rogue_level(&u.uz)) assign_rogue_graphics(TRUE);
645#endif
646#ifdef USE_TILES
647	substitute_tiles(&u.uz);
648#endif
649	restlevelstate(stuckid, steedid);
650#ifdef MFLOPPY
651	gameDiskPrompt();
652#endif
653	max_rank_sz(); /* to recompute mrank_sz (botl.c) */
654	/* take care of iron ball & chain */
655	for(otmp = fobj; otmp; otmp = otmp->nobj)
656		if(otmp->owornmask)
657			setworn(otmp, otmp->owornmask);
658
659	/* in_use processing must be after:
660	 *    + The inventory has been read so that freeinv() works.
661	 *    + The current level has been restored so billing information
662	 *	is available.
663	 */
664	inven_inuse(FALSE);
665
666	load_qtlist();	/* re-load the quest text info */
667	reset_attribute_clock();
668	/* Set up the vision internals, after levl[] data is loaded */
669	/* but before docrt().					    */
670	vision_reset();
671	vision_full_recalc = 1;	/* recompute vision (not saved) */
672
673	run_timers();	/* expire all timers that have gone off while away */
674	docrt();
675	restoring = FALSE;
676	clear_nhwindow(WIN_MESSAGE);
677	program_state.something_worth_saving++;	/* useful data now exists */
678
679	/* Success! */
680	welcome(FALSE);
681	return(1);
682}
683
684void
685trickery(reason)
686char *reason;
687{
688	pline("Strange, this map is not as I remember it.");
689	pline("Somebody is trying some trickery here...");
690	pline("This game is void.");
691	killer = reason;
692	done(TRICKED);
693}
694
695void
696getlev(fd, pid, lev, ghostly)
697int fd, pid;
698xchar lev;
699boolean ghostly;
700{
701	register struct trap *trap;
702	register struct monst *mtmp;
703	branch *br;
704	int hpid;
705	xchar dlvl;
706	int x, y;
707#ifdef TOS
708	short tlev;
709#endif
710
711	if (ghostly)
712	    clear_id_mapping();
713
714#if defined(MSDOS) || defined(OS2)
715	setmode(fd, O_BINARY);
716#endif
717	/* Load the old fruit info.  We have to do it first, so the
718	 * information is available when restoring the objects.
719	 */
720	if (ghostly) oldfruit = loadfruitchn(fd);
721
722	/* First some sanity checks */
723	mread(fd, (genericptr_t) &hpid, sizeof(hpid));
724/* CHECK:  This may prevent restoration */
725#ifdef TOS
726	mread(fd, (genericptr_t) &tlev, sizeof(tlev));
727	dlvl=tlev&0x00ff;
728#else
729	mread(fd, (genericptr_t) &dlvl, sizeof(dlvl));
730#endif
731	if ((pid && pid != hpid) || (lev && dlvl != lev)) {
732	    char trickbuf[BUFSZ];
733
734	    if (pid && pid != hpid)
735		Sprintf(trickbuf, "PID (%d) doesn't match saved PID (%d)!",
736			hpid, pid);
737	    else
738		Sprintf(trickbuf, "This is level %d, not %d!", dlvl, lev);
739#ifdef WIZARD
740	    if (wizard) pline(trickbuf);
741#endif
742	    trickery(trickbuf);
743	}
744
745#ifdef RLECOMP
746	{
747		short	i, j;
748		uchar	len;
749		struct rm r;
750
751#if defined(MAC)
752		/* Suppress warning about used before set */
753		(void) memset((genericptr_t) &r, 0, sizeof(r));
754#endif
755		i = 0; j = 0; len = 0;
756		while(i < ROWNO) {
757		    while(j < COLNO) {
758			if(len > 0) {
759			    levl[j][i] = r;
760			    len -= 1;
761			    j += 1;
762			} else {
763			    mread(fd, (genericptr_t)&len, sizeof(uchar));
764			    mread(fd, (genericptr_t)&r, sizeof(struct rm));
765			}
766		    }
767		    j = 0;
768		    i += 1;
769		}
770	}
771#else
772	mread(fd, (genericptr_t) levl, sizeof(levl));
773#endif	/* RLECOMP */
774
775	mread(fd, (genericptr_t)&omoves, sizeof(omoves));
776	mread(fd, (genericptr_t)&upstair, sizeof(stairway));
777	mread(fd, (genericptr_t)&dnstair, sizeof(stairway));
778	mread(fd, (genericptr_t)&upladder, sizeof(stairway));
779	mread(fd, (genericptr_t)&dnladder, sizeof(stairway));
780	mread(fd, (genericptr_t)&sstairs, sizeof(stairway));
781	mread(fd, (genericptr_t)&updest, sizeof(dest_area));
782	mread(fd, (genericptr_t)&dndest, sizeof(dest_area));
783	mread(fd, (genericptr_t)&level.flags, sizeof(level.flags));
784	mread(fd, (genericptr_t)doors, sizeof(doors));
785	rest_rooms(fd);		/* No joke :-) */
786	if (nroom)
787	    doorindex = rooms[nroom - 1].fdoor + rooms[nroom - 1].doorct;
788	else
789	    doorindex = 0;
790
791	restore_timers(fd, RANGE_LEVEL, ghostly, monstermoves - omoves);
792	restore_light_sources(fd);
793	fmon = restmonchn(fd, ghostly);
794
795	/* regenerate animals while on another level */
796	if (u.uz.dlevel) {
797	    register struct monst *mtmp2;
798
799	    for (mtmp = fmon; mtmp; mtmp = mtmp2) {
800		mtmp2 = mtmp->nmon;
801		if (ghostly) {
802			/* reset peaceful/malign relative to new character */
803			if(!mtmp->isshk)
804				/* shopkeepers will reset based on name */
805				mtmp->mpeaceful = peace_minded(mtmp->data);
806			set_malign(mtmp);
807		} else if (monstermoves > omoves)
808			mon_catchup_elapsed_time(mtmp, monstermoves - omoves);
809
810		/* update shape-changers in case protection against
811		   them is different now than when the level was saved */
812		restore_cham(mtmp);
813	    }
814	}
815
816	rest_worm(fd);	/* restore worm information */
817	ftrap = 0;
818	while (trap = newtrap(),
819	       mread(fd, (genericptr_t)trap, sizeof(struct trap)),
820	       trap->tx != 0) {	/* need "!= 0" to work around DICE 3.0 bug */
821		trap->ntrap = ftrap;
822		ftrap = trap;
823	}
824	dealloc_trap(trap);
825	fobj = restobjchn(fd, ghostly, FALSE);
826	find_lev_obj();
827	/* restobjchn()'s `frozen' argument probably ought to be a callback
828	   routine so that we can check for objects being buried under ice */
829	level.buriedobjlist = restobjchn(fd, ghostly, FALSE);
830	billobjs = restobjchn(fd, ghostly, FALSE);
831	rest_engravings(fd);
832
833	/* reset level.monsters for new level */
834	for (x = 0; x < COLNO; x++)
835	    for (y = 0; y < ROWNO; y++)
836		level.monsters[x][y] = (struct monst *) 0;
837	for (mtmp = level.monlist; mtmp; mtmp = mtmp->nmon) {
838	    if (mtmp->isshk)
839		set_residency(mtmp, FALSE);
840	    place_monster(mtmp, mtmp->mx, mtmp->my);
841	    if (mtmp->wormno) place_wsegs(mtmp);
842	}
843	restdamage(fd, ghostly);
844
845	rest_regions(fd, ghostly);
846	if (ghostly) {
847	    /* Now get rid of all the temp fruits... */
848	    freefruitchn(oldfruit),  oldfruit = 0;
849
850	    if (lev > ledger_no(&medusa_level) &&
851			lev < ledger_no(&stronghold_level) && xdnstair == 0) {
852		coord cc;
853
854		mazexy(&cc);
855		xdnstair = cc.x;
856		ydnstair = cc.y;
857		levl[cc.x][cc.y].typ = STAIRS;
858	    }
859
860	    br = Is_branchlev(&u.uz);
861	    if (br && u.uz.dlevel == 1) {
862		d_level ltmp;
863
864		if (on_level(&u.uz, &br->end1))
865		    assign_level(&ltmp, &br->end2);
866		else
867		    assign_level(&ltmp, &br->end1);
868
869		switch(br->type) {
870		case BR_STAIR:
871		case BR_NO_END1:
872		case BR_NO_END2: /* OK to assign to sstairs if it's not used */
873		    assign_level(&sstairs.tolev, &ltmp);
874		    break;
875		case BR_PORTAL: /* max of 1 portal per level */
876		    {
877			register struct trap *ttmp;
878			for(ttmp = ftrap; ttmp; ttmp = ttmp->ntrap)
879			    if (ttmp->ttyp == MAGIC_PORTAL)
880				break;
881			if (!ttmp) panic("getlev: need portal but none found");
882			assign_level(&ttmp->dst, &ltmp);
883		    }
884		    break;
885		}
886	    } else if (!br) {
887		/* Remove any dangling portals. */
888		register struct trap *ttmp;
889		for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap)
890		    if (ttmp->ttyp == MAGIC_PORTAL) {
891			deltrap(ttmp);
892			break; /* max of 1 portal/level */
893		    }
894	    }
895	}
896
897	/* must come after all mons & objs are restored */
898	relink_timers(ghostly);
899	relink_light_sources(ghostly);
900	reset_oattached_mids(ghostly);
901
902	if (ghostly)
903	    clear_id_mapping();
904}
905
906
907/* Clear all structures for object and monster ID mapping. */
908STATIC_OVL void
909clear_id_mapping()
910{
911    struct bucket *curr;
912
913    while ((curr = id_map) != 0) {
914	id_map = curr->next;
915	free((genericptr_t) curr);
916    }
917    n_ids_mapped = 0;
918}
919
920/* Add a mapping to the ID map. */
921STATIC_OVL void
922add_id_mapping(gid, nid)
923    unsigned gid, nid;
924{
925    int idx;
926
927    idx = n_ids_mapped % N_PER_BUCKET;
928    /* idx is zero on first time through, as well as when a new bucket is */
929    /* needed */
930    if (idx == 0) {
931	struct bucket *gnu = (struct bucket *) alloc(sizeof(struct bucket));
932	gnu->next = id_map;
933	id_map = gnu;
934    }
935
936    id_map->map[idx].gid = gid;
937    id_map->map[idx].nid = nid;
938    n_ids_mapped++;
939}
940
941/*
942 * Global routine to look up a mapping.  If found, return TRUE and fill
943 * in the new ID value.  Otherwise, return false and return -1 in the new
944 * ID.
945 */
946boolean
947lookup_id_mapping(gid, nidp)
948    unsigned gid, *nidp;
949{
950    int i;
951    struct bucket *curr;
952
953    if (n_ids_mapped)
954	for (curr = id_map; curr; curr = curr->next) {
955	    /* first bucket might not be totally full */
956	    if (curr == id_map) {
957		i = n_ids_mapped % N_PER_BUCKET;
958		if (i == 0) i = N_PER_BUCKET;
959	    } else
960		i = N_PER_BUCKET;
961
962	    while (--i >= 0)
963		if (gid == curr->map[i].gid) {
964		    *nidp = curr->map[i].nid;
965		    return TRUE;
966		}
967	}
968
969    return FALSE;
970}
971
972STATIC_OVL void
973reset_oattached_mids(ghostly)
974boolean ghostly;
975{
976    struct obj *otmp;
977    unsigned oldid, nid;
978    for (otmp = fobj; otmp; otmp = otmp->nobj) {
979	if (ghostly && otmp->oattached == OATTACHED_MONST && otmp->oxlth) {
980	    struct monst *mtmp = (struct monst *)otmp->oextra;
981
982	    mtmp->m_id = 0;
983	    mtmp->mpeaceful = mtmp->mtame = 0;	/* pet's owner died! */
984	}
985	if (ghostly && otmp->oattached == OATTACHED_M_ID) {
986	    (void) memcpy((genericptr_t)&oldid, (genericptr_t)otmp->oextra,
987								sizeof(oldid));
988	    if (lookup_id_mapping(oldid, &nid))
989		(void) memcpy((genericptr_t)otmp->oextra, (genericptr_t)&nid,
990								sizeof(nid));
991	    else
992		otmp->oattached = OATTACHED_NOTHING;
993	}
994    }
995}
996
997
998#ifdef ZEROCOMP
999#define RLESC '\0'	/* Leading character for run of RLESC's */
1000
1001#ifndef ZEROCOMP_BUFSIZ
1002#define ZEROCOMP_BUFSIZ BUFSZ
1003#endif
1004static NEARDATA unsigned char inbuf[ZEROCOMP_BUFSIZ];
1005static NEARDATA unsigned short inbufp = 0;
1006static NEARDATA unsigned short inbufsz = 0;
1007static NEARDATA short inrunlength = -1;
1008static NEARDATA int mreadfd;
1009
1010static int
1011mgetc()
1012{
1013    if (inbufp >= inbufsz) {
1014	inbufsz = read(mreadfd, (genericptr_t)inbuf, sizeof inbuf);
1015	if (!inbufsz) {
1016	    if (inbufp > sizeof inbuf)
1017		error("EOF on file #%d.\n", mreadfd);
1018	    inbufp = 1 + sizeof inbuf;  /* exactly one warning :-) */
1019	    return -1;
1020	}
1021	inbufp = 0;
1022    }
1023    return inbuf[inbufp++];
1024}
1025
1026void
1027minit()
1028{
1029    inbufsz = 0;
1030    inbufp = 0;
1031    inrunlength = -1;
1032}
1033
1034int
1035mread(fd, buf, len)
1036int fd;
1037genericptr_t buf;
1038register unsigned len;
1039{
1040    /*register int readlen = 0;*/
1041    if (fd < 0) error("Restore error; mread attempting to read file %d.", fd);
1042    mreadfd = fd;
1043    while (len--) {
1044	if (inrunlength > 0) {
1045	    inrunlength--;
1046	    *(*((char **)&buf))++ = '\0';
1047	} else {
1048	    register short ch = mgetc();
1049	    if (ch < 0) return -1; /*readlen;*/
1050	    if ((*(*(char **)&buf)++ = (char)ch) == RLESC) {
1051		inrunlength = mgetc();
1052	    }
1053	}
1054	/*readlen++;*/
1055    }
1056    return 0; /*readlen;*/
1057}
1058
1059#else /* ZEROCOMP */
1060
1061void
1062minit()
1063{
1064    return;
1065}
1066
1067void
1068mread(fd, buf, len)
1069register int fd;
1070register genericptr_t buf;
1071register unsigned int len;
1072{
1073	register int rlen;
1074
1075#if defined(BSD) || defined(ULTRIX)
1076	rlen = read(fd, buf, (int) len);
1077	if(rlen != len){
1078#else /* e.g. SYSV, __TURBOC__ */
1079	rlen = read(fd, buf, (unsigned) len);
1080	if((unsigned)rlen != len){
1081#endif
1082		pline("Read %d instead of %u bytes.", rlen, len);
1083		if(restoring) {
1084			(void) close(fd);
1085			(void) delete_savefile();
1086			error("Error restoring old game.");
1087		}
1088		panic("Error reading level file.");
1089	}
1090}
1091#endif /* ZEROCOMP */
1092
1093/*restore.c*/
1094