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