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) ¤t_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) <mp, sizeof ltmp) < 0)
569 | #else
570 | if(read(fd, (genericptr_t) <mp, 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(<mp, &br->end2);
811 | else
812 | assign_level(<mp, &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, <mp);
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, <mp);
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*/