/*	SCCS Id: @(#)restore.c	3.1	93/04/06	*/
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed.  See license for details. */

#include "hack.h"
#include "lev.h"
#include "termcap.h" /* for TERMLIB and ASCIIGRAPH */

#ifdef MICRO
extern int dotcnt;	/* shared with save */
#endif

#ifdef ZEROCOMP
static int NDECL(mgetc);
#endif
static void NDECL(find_lev_obj);
#ifndef NO_SIGNAL
static void NDECL(inven_inuse);
#endif
static void FDECL(restlevchn, (int));
static void FDECL(restdamage, (int,BOOLEAN_P));
static struct obj * FDECL(restobjchn, (int,BOOLEAN_P));
static struct monst * FDECL(restmonchn, (int,BOOLEAN_P));
static void FDECL(restgenoinfo, (int));
static boolean FDECL(restgamestate, (int, unsigned int *));
static int FDECL(restlevelfile, (int,XCHAR_P));

#ifdef AMII_GRAPHICS
void NDECL( amii_setpens );		/* use colors from save file */
#endif

#ifdef MULDGN
#include "quest.h"
#endif

boolean restoring = FALSE;
#ifdef TUTTI_FRUTTI
static NEARDATA struct fruit *oldfruit;
#endif
static NEARDATA long omoves;

/* Recalculate level.objects[x][y], since this info was not saved. */
static void
find_lev_obj()
{
	register struct obj *fobjtmp = (struct obj *)0;
	register struct obj *otmp;
	int x,y;

	for(x=0; x<COLNO; x++) for(y=0; y<ROWNO; y++)
		level.objects[x][y] = (struct obj *)0;

	/* Reverse the entire fobj chain, which is necessary so that we can
	 * place the objects in the proper order.
	 */
	while ((otmp = fobj) != 0) {
		fobj = otmp->nobj;
		otmp->nobj = fobjtmp;
		fobjtmp = otmp;
	}
	/* Set level.objects (as well as reversing the chain back again) */
	while ((otmp = fobjtmp) != 0) {
		place_object(otmp, otmp->ox, otmp->oy);
		fobjtmp = otmp->nobj;
		otmp->nobj = fobj;
		fobj = otmp;
	}
}

#ifndef NO_SIGNAL
static void
inven_inuse()
/* Things that were marked "in_use" when the game was saved (ex. via the
 * infamous "HUP" cheat) get used up here.
 */
{
	register struct obj *otmp, *otmp2;

	for(otmp = invent; otmp; otmp = otmp2) {
		otmp2 = otmp->nobj;
		if(otmp->in_use) {
			/* in_use and oldcorpse share a bit, but we don't
			 * want nasty messages for old corpses --
			 * remove_cadavers() will clean them up nicely
			 */
			if (otmp->otyp == CORPSE &&
					mons[otmp->corpsenm].mlet == S_TROLL)
				continue;
			pline("Finishing off %s...", xname(otmp));
			useup(otmp);
		}
	}
}
#endif

static void
restlevchn(fd)
register int fd;
{
	int cnt;
	s_level	*tmplev, *x;

	sp_levchn = (s_level *) 0;
	mread(fd, (genericptr_t) &cnt, sizeof(int));
	for(; cnt > 0; cnt--) {

	    tmplev = (s_level *)alloc(sizeof(s_level));
	    mread(fd, (genericptr_t) tmplev, sizeof(s_level));
	    if(!sp_levchn) sp_levchn = tmplev;
	    else {

		for(x = sp_levchn; x->next; x = x->next);
		x->next = tmplev;
	    }
	    tmplev->next = (s_level *)0;
	}
}

static void
restdamage(fd, ghostly)
int fd;
boolean ghostly;
{
	int counter;
	struct damage *tmp_dam;

	mread(fd, (genericptr_t) &counter, sizeof(counter));
	if (!counter)
	    return;
	tmp_dam = (struct damage *)alloc(sizeof(struct damage));
	while (1) {
	    char damaged_shops[5], *shp = NULL;

	    mread(fd, (genericptr_t) tmp_dam, sizeof(*tmp_dam));
	    if (ghostly)
		tmp_dam->when += (monstermoves - omoves);
	    Strcpy(damaged_shops,
		   in_rooms(tmp_dam->place.x, tmp_dam->place.y, SHOPBASE));
	    if (u.uz.dlevel) {
		/* when restoring, there are two passes over the current
		 * level.  the first time, u.uz isn't set, so neither is
		 * shop_keeper().  just wait and process the damage on
		 * the second pass.
		 */
		for (shp = damaged_shops; *shp; shp++) {
		    struct monst *shkp = shop_keeper(*shp);

		    if (shkp && inhishop(shkp) && repair_damage(shkp, tmp_dam))
			break;
		}
	    }
	    if (!shp || !*shp) {
		tmp_dam->next = level.damagelist;
		level.damagelist = tmp_dam;
		tmp_dam = (struct damage *)alloc(sizeof(*tmp_dam));
	    }
	    if (!(--counter)) {
		free((genericptr_t)tmp_dam);
		return;
	    }
	}
}

static struct obj *
restobjchn(fd, ghostly)
register int fd;
boolean ghostly;
{
	register struct obj *otmp, *otmp2 = 0;
	register struct obj *first = (struct obj *)0;
#ifdef TUTTI_FRUTTI
	register struct fruit *oldf;
#endif
	int xl;

	while(1) {
		mread(fd, (genericptr_t) &xl, sizeof(xl));
		if(xl == -1) break;
		otmp = newobj(xl);
		if(!first) first = otmp;
		else otmp2->nobj = otmp;
		mread(fd, (genericptr_t) otmp,
					(unsigned) xl + sizeof(struct obj));
		if(!otmp->o_id) otmp->o_id = flags.ident++;
#ifdef TUTTI_FRUTTI
		if(ghostly && otmp->otyp == SLIME_MOLD) {
			for(oldf=oldfruit; oldf; oldf=oldf->nextf)
				if (oldf->fid == otmp->spe) break;
			if(!oldf) impossible("no old fruit?");
			else otmp->spe = fruitadd(oldf->fname);
		}
#endif
		/* Ghost levels get object age shifted from old player's clock
		 * to new player's clock.  Assumption: new player arrived
		 * immediately after old player died.
		 */
		if (ghostly && otmp->otyp != OIL_LAMP
				&& otmp->otyp != BRASS_LANTERN
				&& otmp->otyp != CANDELABRUM_OF_INVOCATION
				&& !Is_candle(otmp))
			otmp->age = monstermoves-omoves+otmp->age;

		/* get contents of a container or statue */
		if (Has_contents(otmp))
		    otmp->cobj = restobjchn(fd,ghostly);

		otmp2 = otmp;
	}
	if(first && otmp2->nobj){
		impossible("Restobjchn: error reading objchn.");
		otmp2->nobj = 0;
	}

	return(first);
}

static struct monst *
restmonchn(fd, ghostly)
register int fd;
boolean ghostly;
{
	register struct monst *mtmp, *mtmp2 = 0;
	register struct monst *first = (struct monst *)0;
	int xl;
	struct permonst *monbegin;
	boolean moved;

	/* get the original base address */
	mread(fd, (genericptr_t)&monbegin, sizeof(monbegin));
	moved = (monbegin != mons);

	while(1) {
		mread(fd, (genericptr_t) &xl, sizeof(xl));
		if(xl == -1) break;
		mtmp = newmonst(xl);
		if(!first) first = mtmp;
		else mtmp2->nmon = mtmp;
		mread(fd, (genericptr_t) mtmp, (unsigned) xl + sizeof(struct monst));
		if(!mtmp->m_id)
			mtmp->m_id = flags.ident++;
		if (moved && mtmp->data) {
			int offset = mtmp->data - monbegin;	/*(ptrdiff_t)*/
			mtmp->data = mons + offset;  /* new permonst location */
		}
		if(mtmp->minvent)
			mtmp->minvent = restobjchn(fd, ghostly);
#ifdef MUSE
		if (mtmp->mw) {
			struct obj *obj;

			for(obj = mtmp->minvent; obj; obj = obj->nobj)
				if (obj->owornmask & W_WEP) break;
			if (obj) mtmp->mw = obj;
			else {
				MON_NOWEP(mtmp);
/* [tom]			impossible("bad monster weapon restore"); */
			}
		}
#endif
		if (mtmp->isshk) restshk(mtmp, ghostly);
		if (mtmp->ispriest) restpriest(mtmp, ghostly);

		mtmp2 = mtmp;
	}
	if(first && mtmp2->nmon){
		impossible("Restmonchn: error reading monchn.");
		mtmp2->nmon = 0;
	}
	return(first);
}

static void
restgenoinfo(fd)
register int fd;
{
	register int i;
	unsigned genolist[NUMMONS];

	mread(fd, (genericptr_t) genolist, sizeof(genolist));

	for (i = 0; i < NUMMONS; i++)
		mons[i].geno = genolist[i];
}

static
boolean
restgamestate(fd, mid)
register int fd;
unsigned int *mid;
{
	struct obj *otmp;
	int tmp;		/* not a register ! */
	struct flag oldflags;
#ifdef TUTTI_FRUTTI
	struct fruit *fruit;
#endif

	invent = restobjchn(fd, FALSE);
	migrating_objs = restobjchn(fd, FALSE);
	migrating_mons = restmonchn(fd, FALSE);
	restgenoinfo(fd);

	mread(fd, (genericptr_t) &tmp, sizeof tmp);
#ifdef WIZARD
	if(!wizard)
#endif
	    if(tmp != getuid()) {		/* strange ... */
		pline("Saved game was not yours.");
		return(FALSE);
	    }

	oldflags = flags;
	mread(fd, (genericptr_t) &flags, sizeof(struct flag));
	/* Some config file and command line OPTIONS take precedence over
	 * those in save file.
	 */
#ifdef TERMLIB
	flags.DECgraphics = oldflags.DECgraphics;
#endif
#ifdef ASCIIGRAPH
	flags.IBMgraphics = oldflags.IBMgraphics;
#endif
#ifdef MICRO
	flags.rawio = oldflags.rawio;
	flags.BIOS = oldflags.BIOS;
#endif
#ifdef TEXTCOLOR
	flags.use_color = oldflags.use_color;
	flags.hilite_pet = oldflags.hilite_pet;
#endif
#ifdef MAC_GRAPHICS_ENV
	flags.MACgraphics = oldflags.MACgraphics;
	flags.large_font = oldflags.large_font;
#endif
	/* these come from the current environment; ignore saved values */
	flags.window_inited = oldflags.window_inited;
	flags.msg_history = oldflags.msg_history;
	flags.echo = oldflags.echo;
	flags.cbreak = oldflags.cbreak;
#ifdef NEWS
	flags.news = oldflags.news;
#endif
#ifdef AMII_GRAPHICS
	amii_setpens();		/* use colors from save file */
#endif
	mread(fd, (genericptr_t) &u, sizeof(struct you));
	if(u.uhp <= 0) {
	    You("were not healthy enough to survive restoration.");
	    /* wiz1_level.dlevel is used by mklev.c to see if lots of stuff is
	     * uninitialized, so we only have to set it and not the other stuff.
	     */
	    wiz1_level.dlevel = 0;
	    u.uz.dnum = 0;
	    u.uz.dlevel = 1;
	    return(FALSE);
	}

	/* don't do this earlier to avoid complicating abort above */
	for(otmp = invent; otmp; otmp = otmp->nobj)
		if(otmp->owornmask)
			setworn(otmp, otmp->owornmask);

	restore_dungeon(fd);
	mread(fd, (genericptr_t) &inv_pos, sizeof inv_pos);
	restlevchn(fd);
	mread(fd, (genericptr_t) &moves, sizeof moves);
	mread(fd, (genericptr_t) &monstermoves, sizeof monstermoves);
#ifdef MULDGN
	mread(fd, (genericptr_t) &quest_status, sizeof(struct q_score));
#endif
	mread(fd, (genericptr_t) spl_book,
				sizeof(struct spell) * (MAXSPELL + 1));
	restore_artifacts(fd);
	restore_oracles(fd);
	if(u.ustuck)
		mread(fd, (genericptr_t) mid, sizeof (*mid));
	mread(fd, (genericptr_t) pl_character, sizeof pl_character);

#ifdef TUTTI_FRUTTI
	mread(fd, (genericptr_t) pl_fruit, sizeof pl_fruit);
	mread(fd, (genericptr_t) &current_fruit, sizeof current_fruit);
	ffruit = 0;
	while (fruit = newfruit(),
	       mread(fd, (genericptr_t)fruit, sizeof(struct fruit)),
	       fruit->fid) {
		fruit->nextf = ffruit;
		ffruit = fruit;
	}
	dealloc_fruit(fruit);
#endif
	restnames(fd);
	restore_waterlevel(fd);
	return(TRUE);
}

/*ARGSUSED*/	/* fd used in MFLOPPY only */
static int
restlevelfile(fd, ltmp)
register int fd;
xchar ltmp;
#ifdef applec
# pragma unused(fd)
#endif
{
	register int nfd;

	nfd = create_levelfile(ltmp);

	if (nfd < 0)	panic("Cannot open temp level %d!", ltmp);
#ifdef MFLOPPY
	if (!savelev(nfd, ltmp, COUNT_SAVE)) {

		/* The savelev can't proceed because the size required
		 * is greater than the available disk space.
		 */
		pline("Not enough space on `%s' to restore your game.",
			levels);

		/* Remove levels and bones that may have been created.
		 */
		(void) close(nfd);
		eraseall(levels, alllevels);
# ifndef AMIGA
		eraseall(levels, allbones);

		/* Perhaps the person would like to play without a
		 * RAMdisk.
		 */
		/* Maybe not [Tom] */
/*		if (ramdisk) {
			/* PlaywoRAMdisk may not return, but if it does
			 * it is certain that ramdisk will be 0.
			 */
/*			playwoRAMdisk();
			/* Rewind save file and try again */
/*			(void) lseek(fd, (off_t)0, 0);
			return dorecover(fd);	/* 0 or 1 */
/*		} else */{
# endif
			pline("Be seeing you...");
			terminate(0);
# ifndef AMIGA
		}
# endif
	}
#endif
	bufon(nfd);
	savelev(nfd, ltmp, WRITE_SAVE | FREE_SAVE);
	bclose(nfd);
	return(2);
}

int
dorecover(fd)
register int fd;
{
	unsigned int mid;		/* not a register */
	xchar ltmp;
	int rtmp;
	struct obj *otmp;

	minit();	/* ZEROCOMP */
	restoring = TRUE;
	getlev(fd, 0, (xchar)0, FALSE);
	if (!restgamestate(fd, &mid)) {
		(void) close(fd);
		(void) delete_savefile();
		restoring = FALSE;
		return(0);
	}
#ifdef MULDGN
	quest_init();
#endif
#ifdef INSURANCE
	savestateinlock();
#endif
	rtmp = restlevelfile(fd, ledger_no(&u.uz));
	if (rtmp < 2) return(rtmp);  /* dorecover called recursively */

#ifdef MICRO
# ifdef AMII_GRAPHICS
	{
	extern struct window_procs amii_procs;
	if(windowprocs.win_init_nhwindows== amii_procs.win_init_nhwindows){
	    extern winid WIN_BASE;
	    clear_nhwindow(WIN_BASE);	/* hack until there's a hook for this */
	}
	}
# else
	clear_nhwindow(WIN_MAP);
# endif
	clear_nhwindow(WIN_MESSAGE);
	You("return to level %d in %s%s.",
		depth(&u.uz), dungeons[u.uz.dnum].dname,
		flags.debug ? " while in debug mode" :
		flags.explore ? " while in explore mode" : "");
	curs(WIN_MAP, 1, 1);
	dotcnt = 0;
	putstr(WIN_MAP, 0, "Restoring:");
#endif
	while(1) {
#ifdef ZEROCOMP
		if(mread(fd, (genericptr_t) &ltmp, sizeof ltmp) < 0)
#else
		if(read(fd, (genericptr_t) &ltmp, sizeof ltmp) != sizeof ltmp)
#endif
			break;
		getlev(fd, 0, ltmp, FALSE);
#ifdef MICRO
		curs(WIN_MAP, 1+dotcnt++, 2);
		putstr(WIN_MAP, 0, ".");
		mark_synch();
#endif
		rtmp = restlevelfile(fd, ltmp);
		if (rtmp < 2) return(rtmp);  /* dorecover called recursively */
	}

#ifdef BSD
	(void) lseek(fd, 0L, 0);
#else
	(void) lseek(fd, 0L, 0);
/*	(void) lseek(fd, (off_t)0, 0); */
#endif
	minit();	/* ZEROCOMP */
	getlev(fd, 0, (xchar)0, FALSE);
	(void) close(fd);

#if defined(WIZARD) || defined(EXPLORE_MODE)
	if(
# ifdef WIZARD
	   !wizard
#  ifdef EXPLORE_MODE
		   &&
#  endif
# endif
# ifdef EXPLORE_MODE
		      !discover
# endif
				)
#endif
		(void) delete_savefile();
#ifdef REINCARNATION
	if (Is_rogue_level(&u.uz)) assign_rogue_graphics(TRUE);
#endif
	if(u.ustuck) {
		register struct monst *mtmp;

		for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
			if(mtmp->m_id == mid) goto monfnd;
		panic("Cannot find the monster ustuck.");
	monfnd:
		u.ustuck = mtmp;
	}
#ifdef MFLOPPY
	gameDiskPrompt();
#endif
	max_rank_sz(); /* to recompute mrank_sz (botl.c) */
#ifdef POLYSELF
	set_uasmon();
#endif
	/* take care of iron ball & chain */
	for(otmp = fobj; otmp; otmp = otmp->nobj)
		if(otmp->owornmask)
			setworn(otmp, otmp->owornmask);
#ifndef NO_SIGNAL
	/* in_use processing must be after:
	 *    + The inventory has been read so that freeinv() works.
	 *    + The current level has been restored so billing information
	 *	is available.
	 */
	inven_inuse();
#endif
#ifdef MULDGN
	load_qtlist();	/* re-load the quest text info */
#endif
	reset_attribute_clock();
	/* Set up the vision internals, after levl[] data is loaded */
	/* but before docrt().					    */
	vision_reset();
	vision_full_recalc = 1;	/* recompute vision (not saved) */
	docrt();
	restoring = FALSE;
	clear_nhwindow(WIN_MESSAGE);
	return(1);
}

void
trickery()
{
	pline("Strange, this map is not as I remember it.");
	pline("Somebody is trying some trickery here...");
	pline("This game is void.");
	done(TRICKED);
}

void
getlev(fd, pid, lev, ghostly)
int fd, pid;
xchar lev;
boolean ghostly;
{
	register struct trap *trap;
	register struct monst *mtmp;
	branch *br;
	int hpid;
	xchar dlvl;
	int x, y;
#ifdef TOS
	short tlev;
#endif

#if defined(MSDOS) || defined(OS2)
	setmode(fd, O_BINARY);
#endif
#ifdef TUTTI_FRUTTI
	/* Load the old fruit info.  We have to do it first, so the
	 * information is available when restoring the objects.
	 */
	if (ghostly) {
		struct fruit *fruit;

		oldfruit = 0;
		while (fruit = newfruit(),
		       mread(fd, (genericptr_t)fruit, sizeof(struct fruit)),
		       fruit->fid) {
			fruit->nextf = oldfruit;
			oldfruit = fruit;
		}
		dealloc_fruit(fruit);
	}
#endif

	/* First some sanity checks */
	mread(fd, (genericptr_t) &hpid, sizeof(hpid));
/* CHECK:  This may prevent restoration */
#ifdef TOS
	mread(fd, (genericptr_t) &tlev, sizeof(tlev));
	dlvl=tlev&0x00ff;
#else
	mread(fd, (genericptr_t) &dlvl, sizeof(dlvl));
#endif
	if((pid && pid != hpid) || (lev && dlvl != lev)) {
#ifdef WIZARD
		if (wizard) {
			if (pid && pid != hpid)
				pline("PID (%d) doesn't match saved PID (%d)!", hpid, pid);
			else if (lev && dlvl != lev)
				pline("This is level %d, not %d!", dlvl, lev);
		}
#endif
		trickery();
	}

#ifdef RLECOMP
	{
		short	i, j;
		uchar	len;
		struct rm r;
		
#if defined(MAC)
		memset ( & r , 0 , sizeof ( r ) ) ; /* Suppress warning about used before set */
#endif
		i = 0; j = 0; len = 0;
		while(i < ROWNO) {
		    while(j < COLNO) {
			if(len > 0) {
			    levl[j][i] = r;
			    len -= 1;
			    j += 1;
			} else {
			    mread(fd, (genericptr_t)&len, sizeof(uchar));
			    mread(fd, (genericptr_t)&r, sizeof(struct rm));
			}
		    }
		    j = 0;
		    i += 1;
		}
	}
#else
	mread(fd, (genericptr_t) levl, sizeof(levl));
#endif	/* RLECOMP */

	mread(fd, (genericptr_t)&omoves, sizeof(omoves));
	mread(fd, (genericptr_t)&upstair, sizeof(stairway));
	mread(fd, (genericptr_t)&dnstair, sizeof(stairway));
	mread(fd, (genericptr_t)&upladder, sizeof(stairway));
	mread(fd, (genericptr_t)&dnladder, sizeof(stairway));
	mread(fd, (genericptr_t)&sstairs, sizeof(stairway));
	mread(fd, (genericptr_t)&updest, sizeof(dest_area));
	mread(fd, (genericptr_t)&dndest, sizeof(dest_area));
	mread(fd, (genericptr_t)&level.flags, sizeof(level.flags));

	fmon = restmonchn(fd, ghostly);

	/* regenerate animals while on another level */
	{ long tmoves = (monstermoves > omoves) ? monstermoves-omoves : 0;
	  register struct monst *mtmp2;

	  for(mtmp = fmon; mtmp; mtmp = mtmp2) {
		mtmp2 = mtmp->nmon;
		if(mtmp->data->geno & G_GENOD) {
			/* mondead() would try to link the monster's objects
			 * into fobj and the appropriate nexthere chain.
			 * unfortunately, such things will not have sane
			 * values until after find_lev_obj() well below
			 * here, so we'd go chasing random pointers if we
			 * tried that.  we could save the monster's objects
			 * in another chain and insert them in the level
			 * later, but that's a lot of work for very little
			 * gain.  hence, just throw the objects away via
			 * mongone() and pretend the monster wandered off
			 * somewhere private before the genocide.
			 */
			mongone(mtmp);
			continue;
		}

		if (ghostly) {
			/* reset peaceful/malign relative to new character */
			if(!mtmp->isshk)
				/* shopkeepers will reset based on name */
				mtmp->mpeaceful = peace_minded(mtmp->data);
			set_malign(mtmp);
		} else if (mtmp->mtame && tmoves > 250)
			mtmp->mtame = mtmp->mpeaceful = 0;

		/* restore shape changers - Maarten Jan Huisjes */
		if (mtmp->data == &mons[PM_CHAMELEON]
		    && !Protection_from_shape_changers
		    && !mtmp->cham)
			mtmp->cham = 1;
		else if(Protection_from_shape_changers) {
			if (mtmp->cham) {
				mtmp->cham = 0;
				(void) newcham(mtmp, &mons[PM_CHAMELEON]);
			} else if(is_were(mtmp->data) && !is_human(mtmp->data))
				new_were(mtmp);
		}

		if (!ghostly) {
			long nhp = mtmp->mhp +
				(regenerates(mtmp->data) ? tmoves : tmoves/20);

			if(!mtmp->mcansee && mtmp->mblinded) {
				if ((long) mtmp->mblinded <= tmoves) {
					mtmp->mblinded = 0;
					mtmp->mcansee = 1;
				} else mtmp->mblinded -= tmoves;
			}
			if(!mtmp->mcanmove && mtmp->mfrozen) {
				if ((long) mtmp->mfrozen <= tmoves) {
					mtmp->mfrozen = 0;
					mtmp->mcanmove = 1;
				} else mtmp->mfrozen -= tmoves;
			}
			if(mtmp->mflee && mtmp->mfleetim) {
				if ((long) mtmp->mfleetim <= tmoves) {
					mtmp->mfleetim = 0;
					mtmp->mflee = 0;
				} else mtmp->mfleetim -= tmoves;
			}
			if(nhp >= mtmp->mhpmax)
				mtmp->mhp = mtmp->mhpmax;
			else
				mtmp->mhp = nhp;
		}
	  }
	}

	rest_worm(fd);	/* restore worm information */
	ftrap = 0;
	while (trap = newtrap(),
	       mread(fd, (genericptr_t)trap, sizeof(struct trap)),
	       trap->tx) {
		trap->ntrap = ftrap;
		ftrap = trap;
	}
	dealloc_trap(trap);
	fobj = restobjchn(fd, ghostly);
	find_lev_obj();
	level.buriedobjlist = restobjchn(fd, ghostly);
	billobjs = restobjchn(fd, ghostly);
	rest_engravings(fd);
	rest_rooms(fd);		/* No joke :-) */
	mread(fd, (genericptr_t)doors, sizeof(doors));

	/* reset level.monsters for new level */
	for (x = 0; x < COLNO; x++)
	    for (y = 0; y < ROWNO; y++)
		level.monsters[x][y] = (struct monst *) 0;
	for (mtmp = level.monlist; mtmp; mtmp = mtmp->nmon) {
	    if (mtmp->isshk)
		set_residency(mtmp, FALSE);
	    place_monster(mtmp, mtmp->mx, mtmp->my);
	    if (mtmp->wormno) place_wsegs(mtmp);
	}
	restdamage(fd, ghostly);


#ifdef TUTTI_FRUTTI
	/* Now get rid of all the temp fruits... */
	if (ghostly) {
		struct fruit *fruit;

		while(oldfruit) {
			fruit = oldfruit->nextf;
			dealloc_fruit(oldfruit);
			oldfruit = fruit;
		}
	}
#endif
	if (ghostly && lev > ledger_no(&medusa_level) &&
			lev < ledger_no(&stronghold_level) && xdnstair == 0) {
		coord cc;

		mazexy(&cc);
		xdnstair = cc.x;
		ydnstair = cc.y;
		levl[cc.x][cc.y].typ = STAIRS;
	}
	if (ghostly && (br = Is_branchlev(&u.uz)) && u.uz.dlevel == 1) {
	    d_level ltmp;

	    if (on_level(&u.uz, &br->end1))
		assign_level(&ltmp, &br->end2);
	    else
		assign_level(&ltmp, &br->end1);

	    switch(br->type) {
	    case BR_STAIR:
	    case BR_NO_END1:
	    case BR_NO_END2: /* OK to assign to sstairs if it's not used */
		assign_level(&sstairs.tolev, &ltmp);
		break;		
	    case BR_PORTAL: /* max of 1 portal per level */
		{
		    register struct trap *ttmp;
		    for(ttmp = ftrap; ttmp; ttmp = ttmp->ntrap)
			if (ttmp->ttyp == MAGIC_PORTAL)
			    break;
		    if (!ttmp) panic("getlev: need portal but none found");
		    assign_level(&ttmp->dst, &ltmp);
		}
		break;
	    }
	} else if (ghostly && !Is_branchlev(&u.uz)) {
	    /* Make sure there are no dangling portals.  If so, remove them */
	    register struct trap *ttmp;
	    for(ttmp = ftrap; ttmp; ttmp = ttmp->ntrap)
		if (ttmp->ttyp == MAGIC_PORTAL) {
		    deltrap(ttmp);
		    break; /* max of 1 portal/level */
		}
	}
}

#ifdef ZEROCOMP
#define RLESC '\0'	/* Leading character for run of RLESC's */

#ifndef ZEROCOMP_BUFSIZ
#define ZEROCOMP_BUFSIZ BUFSZ
#endif
static NEARDATA unsigned char inbuf[ZEROCOMP_BUFSIZ];
static NEARDATA unsigned short inbufp = 0;
static NEARDATA unsigned short inbufsz = 0;
static NEARDATA short inrunlength = -1;
static NEARDATA int mreadfd;

static int
mgetc()
{
    if (inbufp >= inbufsz) {
	inbufsz = read(mreadfd, (genericptr_t)inbuf, sizeof inbuf);
	if (!inbufsz) {
	    if (inbufp > sizeof inbuf)
		error("EOF on file #%d.\n", mreadfd);
	    inbufp = 1 + sizeof inbuf;  /* exactly one warning :-) */
	    return -1;
	}
	inbufp = 0;
    }
    return inbuf[inbufp++];
}

void
minit()
{
    inbufsz = 0;
    inbufp = 0;
    inrunlength = -1;
}

int
mread(fd, buf, len)
int fd;
genericptr_t buf;
register unsigned len;
{
    /*register int readlen = 0;*/
    mreadfd = fd;
    while (len--) {
	if (inrunlength > 0) {
	    inrunlength--;
	    *(*((char **)&buf))++ = '\0';
	} else {
	    register short ch = mgetc();
	    if (ch < 0) return -1; /*readlen;*/
	    if ((*(*(char **)&buf)++ = ch) == RLESC) {
		inrunlength = mgetc();
	    }
	}
	/*readlen++;*/
    }
    return 0; /*readlen;*/
}

#else /* ZEROCOMP */

void
minit()
{
    return;
}

void
mread(fd, buf, len)
register int fd;
register genericptr_t buf;
register unsigned int len;
{
	register int rlen;

#if defined(BSD) || defined(ULTRIX)
	rlen = read(fd, buf, (int) len);
	if(rlen != len){
#else /* e.g. SYSV, __TURBOC__ */
	rlen = read(fd, buf, (unsigned) len);
	if((unsigned)rlen != len){
#endif
		pline("Read %d instead of %u bytes.", rlen, len);
		if(restoring) {
			(void) close(fd);
			(void) delete_savefile();
			error("Error restoring old game.");
		}
		panic("Error reading level file.");
	}
}
#endif /* ZEROCOMP */

/*restore.c*/
