NetHack->aNetHack
[aNetHack.git] / src / restore.c
blob509ff6ed918c11ee4166a46d031bd33b54cc9ea5
1 /* NetHack 3.6 restore.c $NHDT-Date: 1451082255 2015/12/25 22:24:15 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.103 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
5 #include "hack.h"
6 #include "lev.h"
7 #include "tcap.h" /* for TERMLIB and ASCIIGRAPH */
9 #if defined(MICRO)
10 extern int dotcnt; /* shared with save */
11 extern int dotrow; /* shared with save */
12 #endif
14 #ifdef USE_TILES
15 extern void FDECL(substitute_tiles, (d_level *)); /* from tile.c */
16 #endif
18 #ifdef ZEROCOMP
19 STATIC_DCL void NDECL(zerocomp_minit);
20 STATIC_DCL void FDECL(zerocomp_mread, (int, genericptr_t, unsigned int));
21 STATIC_DCL int NDECL(zerocomp_mgetc);
22 #endif
24 STATIC_DCL void NDECL(def_minit);
25 STATIC_DCL void FDECL(def_mread, (int, genericptr_t, unsigned int));
27 STATIC_DCL void NDECL(find_lev_obj);
28 STATIC_DCL void FDECL(restlevchn, (int));
29 STATIC_DCL void FDECL(restdamage, (int, BOOLEAN_P));
30 STATIC_DCL void FDECL(restobj, (int, struct obj *));
31 STATIC_DCL struct obj *FDECL(restobjchn, (int, BOOLEAN_P, BOOLEAN_P));
32 STATIC_OVL void FDECL(restmon, (int, struct monst *));
33 STATIC_DCL struct monst *FDECL(restmonchn, (int, BOOLEAN_P));
34 STATIC_DCL struct fruit *FDECL(loadfruitchn, (int));
35 STATIC_DCL void FDECL(freefruitchn, (struct fruit *));
36 STATIC_DCL void FDECL(ghostfruit, (struct obj *));
37 STATIC_DCL boolean
38 FDECL(restgamestate, (int, unsigned int *, unsigned int *));
39 STATIC_DCL void FDECL(restlevelstate, (unsigned int, unsigned int));
40 STATIC_DCL int FDECL(restlevelfile, (int, XCHAR_P));
41 STATIC_OVL void FDECL(restore_msghistory, (int));
42 STATIC_DCL void FDECL(reset_oattached_mids, (BOOLEAN_P));
43 STATIC_DCL void FDECL(rest_levl, (int, BOOLEAN_P));
45 static struct restore_procs {
46 const char *name;
47 int mread_flags;
48 void NDECL((*restore_minit));
49 void FDECL((*restore_mread), (int, genericptr_t, unsigned int));
50 void FDECL((*restore_bclose), (int));
51 } restoreprocs = {
52 #if !defined(ZEROCOMP) || (defined(COMPRESS) || defined(ZLIB_COMP))
53 "externalcomp", 0, def_minit, def_mread, def_bclose,
54 #else
55 "zerocomp", 0, zerocomp_minit, zerocomp_mread, zerocomp_bclose,
56 #endif
60 * Save a mapping of IDs from ghost levels to the current level. This
61 * map is used by the timer routines when restoring ghost levels.
63 #define N_PER_BUCKET 64
64 struct bucket {
65 struct bucket *next;
66 struct {
67 unsigned gid; /* ghost ID */
68 unsigned nid; /* new ID */
69 } map[N_PER_BUCKET];
72 STATIC_DCL void NDECL(clear_id_mapping);
73 STATIC_DCL void FDECL(add_id_mapping, (unsigned, unsigned));
75 static int n_ids_mapped = 0;
76 static struct bucket *id_map = 0;
78 #ifdef AMII_GRAPHICS
79 void FDECL(amii_setpens, (int)); /* use colors from save file */
80 extern int amii_numcolors;
81 #endif
83 #include "display.h"
85 boolean restoring = FALSE;
86 static NEARDATA struct fruit *oldfruit;
87 static NEARDATA long omoves;
89 #define Is_IceBox(o) ((o)->otyp == ICE_BOX ? TRUE : FALSE)
91 /* Recalculate level.objects[x][y], since this info was not saved. */
92 STATIC_OVL void
93 find_lev_obj()
95 register struct obj *fobjtmp = (struct obj *) 0;
96 register struct obj *otmp;
97 int x, y;
99 for (x = 0; x < COLNO; x++)
100 for (y = 0; y < ROWNO; y++)
101 level.objects[x][y] = (struct obj *) 0;
104 * Reverse the entire fobj chain, which is necessary so that we can
105 * place the objects in the proper order. Make all obj in chain
106 * OBJ_FREE so place_object will work correctly.
108 while ((otmp = fobj) != 0) {
109 fobj = otmp->nobj;
110 otmp->nobj = fobjtmp;
111 otmp->where = OBJ_FREE;
112 fobjtmp = otmp;
114 /* fobj should now be empty */
116 /* Set level.objects (as well as reversing the chain back again) */
117 while ((otmp = fobjtmp) != 0) {
118 fobjtmp = otmp->nobj;
119 place_object(otmp, otmp->ox, otmp->oy);
123 /* Things that were marked "in_use" when the game was saved (ex. via the
124 * infamous "HUP" cheat) get used up here.
126 void
127 inven_inuse(quietly)
128 boolean quietly;
130 register struct obj *otmp, *otmp2;
132 for (otmp = invent; otmp; otmp = otmp2) {
133 otmp2 = otmp->nobj;
134 if (otmp->in_use) {
135 if (!quietly)
136 pline("Finishing off %s...", xname(otmp));
137 useup(otmp);
142 STATIC_OVL void
143 restlevchn(fd)
144 register int fd;
146 int cnt;
147 s_level *tmplev, *x;
149 sp_levchn = (s_level *) 0;
150 mread(fd, (genericptr_t) &cnt, sizeof(int));
151 for (; cnt > 0; cnt--) {
152 tmplev = (s_level *) alloc(sizeof(s_level));
153 mread(fd, (genericptr_t) tmplev, sizeof(s_level));
154 if (!sp_levchn)
155 sp_levchn = tmplev;
156 else {
157 for (x = sp_levchn; x->next; x = x->next)
159 x->next = tmplev;
161 tmplev->next = (s_level *) 0;
165 STATIC_OVL void
166 restdamage(fd, ghostly)
167 int fd;
168 boolean ghostly;
170 int counter;
171 struct damage *tmp_dam;
173 mread(fd, (genericptr_t) &counter, sizeof(counter));
174 if (!counter)
175 return;
176 tmp_dam = (struct damage *) alloc(sizeof(struct damage));
177 while (--counter >= 0) {
178 char damaged_shops[5], *shp = (char *) 0;
180 mread(fd, (genericptr_t) tmp_dam, sizeof(*tmp_dam));
181 if (ghostly)
182 tmp_dam->when += (monstermoves - omoves);
183 Strcpy(damaged_shops,
184 in_rooms(tmp_dam->place.x, tmp_dam->place.y, SHOPBASE));
185 if (u.uz.dlevel) {
186 /* when restoring, there are two passes over the current
187 * level. the first time, u.uz isn't set, so neither is
188 * shop_keeper(). just wait and process the damage on
189 * the second pass.
191 for (shp = damaged_shops; *shp; shp++) {
192 struct monst *shkp = shop_keeper(*shp);
194 if (shkp && inhishop(shkp)
195 && repair_damage(shkp, tmp_dam, TRUE))
196 break;
199 if (!shp || !*shp) {
200 tmp_dam->next = level.damagelist;
201 level.damagelist = tmp_dam;
202 tmp_dam = (struct damage *) alloc(sizeof(*tmp_dam));
205 free((genericptr_t) tmp_dam);
208 /* restore one object */
209 STATIC_OVL void
210 restobj(fd, otmp)
211 int fd;
212 struct obj *otmp;
214 int buflen;
216 mread(fd, (genericptr_t) otmp, sizeof(struct obj));
218 /* next object pointers are invalid; otmp->cobj needs to be left
219 as is--being non-null is key to restoring container contents */
220 otmp->nobj = otmp->nexthere = (struct obj *) 0;
221 /* non-null oextra needs to be reconstructed */
222 if (otmp->oextra) {
223 otmp->oextra = newoextra();
225 /* oname - object's name */
226 mread(fd, (genericptr_t) &buflen, sizeof(buflen));
227 if (buflen > 0) { /* includes terminating '\0' */
228 new_oname(otmp, buflen);
229 mread(fd, (genericptr_t) ONAME(otmp), buflen);
231 /* omonst - corpse or statue might retain full monster details */
232 mread(fd, (genericptr_t) &buflen, sizeof(buflen));
233 if (buflen > 0) {
234 newomonst(otmp);
235 /* this is actually a monst struct, so we
236 can just defer to restmon() here */
237 restmon(fd, OMONST(otmp));
239 /* omid - monster id number, connecting corpse to ghost */
240 mread(fd, (genericptr_t) &buflen, sizeof(buflen));
241 if (buflen > 0) {
242 newomid(otmp);
243 mread(fd, (genericptr_t) OMID(otmp), buflen);
245 /* olong - temporary gold */
246 mread(fd, (genericptr_t) &buflen, sizeof(buflen));
247 if (buflen > 0) {
248 newolong(otmp);
249 mread(fd, (genericptr_t) OLONG(otmp), buflen);
251 /* omailcmd - feedback mechanism for scroll of mail */
252 mread(fd, (genericptr_t) &buflen, sizeof(buflen));
253 if (buflen > 0) {
254 char *omailcmd = (char *) alloc(buflen);
256 mread(fd, (genericptr_t) omailcmd, buflen);
257 new_omailcmd(otmp, omailcmd);
258 free((genericptr_t) omailcmd);
263 STATIC_OVL struct obj *
264 restobjchn(fd, ghostly, frozen)
265 register int fd;
266 boolean ghostly, frozen;
268 register struct obj *otmp, *otmp2 = 0;
269 register struct obj *first = (struct obj *) 0;
270 int buflen;
272 while (1) {
273 mread(fd, (genericptr_t) &buflen, sizeof buflen);
274 if (buflen == -1)
275 break;
277 otmp = newobj();
278 restobj(fd, otmp);
279 if (!first)
280 first = otmp;
281 else
282 otmp2->nobj = otmp;
284 if (ghostly) {
285 unsigned nid = context.ident++;
286 add_id_mapping(otmp->o_id, nid);
287 otmp->o_id = nid;
289 if (ghostly && otmp->otyp == SLIME_MOLD)
290 ghostfruit(otmp);
291 /* Ghost levels get object age shifted from old player's clock
292 * to new player's clock. Assumption: new player arrived
293 * immediately after old player died.
295 if (ghostly && !frozen && !age_is_relative(otmp))
296 otmp->age = monstermoves - omoves + otmp->age;
298 /* get contents of a container or statue */
299 if (Has_contents(otmp)) {
300 struct obj *otmp3;
301 otmp->cobj = restobjchn(fd, ghostly, Is_IceBox(otmp));
302 /* restore container back pointers */
303 for (otmp3 = otmp->cobj; otmp3; otmp3 = otmp3->nobj)
304 otmp3->ocontainer = otmp;
306 if (otmp->bypass)
307 otmp->bypass = 0;
308 if (!ghostly) {
309 /* fix the pointers */
310 if (otmp->o_id == context.victual.o_id)
311 context.victual.piece = otmp;
312 if (otmp->o_id == context.tin.o_id)
313 context.tin.tin = otmp;
314 if (otmp->o_id == context.spbook.o_id)
315 context.spbook.book = otmp;
317 otmp2 = otmp;
319 if (first && otmp2->nobj) {
320 impossible("Restobjchn: error reading objchn.");
321 otmp2->nobj = 0;
324 return first;
327 /* restore one monster */
328 STATIC_OVL void
329 restmon(fd, mtmp)
330 int fd;
331 struct monst *mtmp;
333 int buflen;
335 mread(fd, (genericptr_t) mtmp, sizeof(struct monst));
337 /* next monster pointer is invalid */
338 mtmp->nmon = (struct monst *) 0;
339 /* non-null mextra needs to be reconstructed */
340 if (mtmp->mextra) {
341 mtmp->mextra = newmextra();
343 /* mname - monster's name */
344 mread(fd, (genericptr_t) &buflen, sizeof(buflen));
345 if (buflen > 0) { /* includes terminating '\0' */
346 new_mname(mtmp, buflen);
347 mread(fd, (genericptr_t) MNAME(mtmp), buflen);
349 /* egd - vault guard */
350 mread(fd, (genericptr_t) &buflen, sizeof(buflen));
351 if (buflen > 0) {
352 newegd(mtmp);
353 mread(fd, (genericptr_t) EGD(mtmp), sizeof(struct egd));
355 /* epri - temple priest */
356 mread(fd, (genericptr_t) &buflen, sizeof(buflen));
357 if (buflen > 0) {
358 newepri(mtmp);
359 mread(fd, (genericptr_t) EPRI(mtmp), sizeof(struct epri));
361 /* eshk - shopkeeper */
362 mread(fd, (genericptr_t) &buflen, sizeof(buflen));
363 if (buflen > 0) {
364 neweshk(mtmp);
365 mread(fd, (genericptr_t) ESHK(mtmp), sizeof(struct eshk));
367 /* emin - minion */
368 mread(fd, (genericptr_t) &buflen, sizeof(buflen));
369 if (buflen > 0) {
370 newemin(mtmp);
371 mread(fd, (genericptr_t) EMIN(mtmp), sizeof(struct emin));
373 /* edog - pet */
374 mread(fd, (genericptr_t) &buflen, sizeof(buflen));
375 if (buflen > 0) {
376 newedog(mtmp);
377 mread(fd, (genericptr_t) EDOG(mtmp), sizeof(struct edog));
379 /* mcorpsenm - obj->corpsenm for mimic posing as corpse or
380 statue (inline int rather than pointer to something) */
381 mread(fd, (genericptr_t) &MCORPSENM(mtmp), sizeof MCORPSENM(mtmp));
382 } /* mextra */
385 STATIC_OVL struct monst *
386 restmonchn(fd, ghostly)
387 register int fd;
388 boolean ghostly;
390 register struct monst *mtmp, *mtmp2 = 0;
391 register struct monst *first = (struct monst *) 0;
392 int offset, buflen;
394 while (1) {
395 mread(fd, (genericptr_t) &buflen, sizeof(buflen));
396 if (buflen == -1)
397 break;
399 mtmp = newmonst();
400 restmon(fd, mtmp);
401 if (!first)
402 first = mtmp;
403 else
404 mtmp2->nmon = mtmp;
406 if (ghostly) {
407 unsigned nid = context.ident++;
408 add_id_mapping(mtmp->m_id, nid);
409 mtmp->m_id = nid;
411 offset = mtmp->mnum;
412 mtmp->data = &mons[offset];
413 if (ghostly) {
414 int mndx = monsndx(mtmp->data);
415 if (propagate(mndx, TRUE, ghostly) == 0) {
416 /* cookie to trigger purge in getbones() */
417 mtmp->mhpmax = DEFUNCT_MONSTER;
420 if (mtmp->minvent) {
421 struct obj *obj;
422 mtmp->minvent = restobjchn(fd, ghostly, FALSE);
423 /* restore monster back pointer */
424 for (obj = mtmp->minvent; obj; obj = obj->nobj)
425 obj->ocarry = mtmp;
427 if (mtmp->mw) {
428 struct obj *obj;
430 for (obj = mtmp->minvent; obj; obj = obj->nobj)
431 if (obj->owornmask & W_WEP)
432 break;
433 if (obj)
434 mtmp->mw = obj;
435 else {
436 MON_NOWEP(mtmp);
437 impossible("bad monster weapon restore");
441 if (mtmp->isshk)
442 restshk(mtmp, ghostly);
443 if (mtmp->ispriest)
444 restpriest(mtmp, ghostly);
446 if (!ghostly) {
447 if (mtmp->m_id == context.polearm.m_id)
448 context.polearm.hitmon = mtmp;
450 mtmp2 = mtmp;
452 if (first && mtmp2->nmon) {
453 impossible("Restmonchn: error reading monchn.");
454 mtmp2->nmon = 0;
456 return first;
459 STATIC_OVL struct fruit *
460 loadfruitchn(fd)
461 int fd;
463 register struct fruit *flist, *fnext;
465 flist = 0;
466 while (fnext = newfruit(), mread(fd, (genericptr_t) fnext, sizeof *fnext),
467 fnext->fid != 0) {
468 fnext->nextf = flist;
469 flist = fnext;
471 dealloc_fruit(fnext);
472 return flist;
475 STATIC_OVL void
476 freefruitchn(flist)
477 register struct fruit *flist;
479 register struct fruit *fnext;
481 while (flist) {
482 fnext = flist->nextf;
483 dealloc_fruit(flist);
484 flist = fnext;
488 STATIC_OVL void
489 ghostfruit(otmp)
490 register struct obj *otmp;
492 register struct fruit *oldf;
494 for (oldf = oldfruit; oldf; oldf = oldf->nextf)
495 if (oldf->fid == otmp->spe)
496 break;
498 if (!oldf)
499 impossible("no old fruit?");
500 else
501 otmp->spe = fruitadd(oldf->fname, (struct fruit *) 0);
504 #ifdef SYSCF
505 #define SYSOPT_CHECK_SAVE_UID sysopt.check_save_uid
506 #else
507 #define SYSOPT_CHECK_SAVE_UID TRUE
508 #endif
510 STATIC_OVL
511 boolean
512 restgamestate(fd, stuckid, steedid)
513 register int fd;
514 unsigned int *stuckid, *steedid;
516 struct flag newgameflags;
517 #ifdef SYSFLAGS
518 struct sysflag newgamesysflags;
519 #endif
520 struct obj *otmp, *tmp_bc;
521 char timebuf[15];
522 unsigned long uid;
524 mread(fd, (genericptr_t) &uid, sizeof uid);
525 if (SYSOPT_CHECK_SAVE_UID
526 && uid != (unsigned long) getuid()) { /* strange ... */
527 /* for wizard mode, issue a reminder; for others, treat it
528 as an attempt to cheat and refuse to restore this file */
529 pline("Saved game was not yours.");
530 if (!wizard)
531 return FALSE;
533 mread(fd, (genericptr_t) &context, sizeof(struct context_info));
534 if (context.warntype.speciesidx >= LOW_PM)
535 context.warntype.species = &mons[context.warntype.speciesidx];
537 /* we want to be able to revert to command line/environment/config
538 file option values instead of keeping old save file option values
539 if partial restore fails and we resort to starting a new game */
540 newgameflags = flags;
541 mread(fd, (genericptr_t) &flags, sizeof(struct flag));
542 /* wizard and discover are actually flags.debug and flags.explore;
543 player might be overriding the save file values for them;
544 in the discover case, we don't want to set that for a normal
545 game until after the save file has been removed */
546 iflags.deferred_X = (newgameflags.explore && !discover);
547 if (newgameflags.debug) {
548 /* authorized by startup code; wizard mode exists and is allowed */
549 wizard = TRUE, discover = iflags.deferred_X = FALSE;
550 } else if (wizard) {
551 /* specified by save file; check authorization now */
552 set_playmode();
554 #ifdef SYSFLAGS
555 newgamesysflags = sysflags;
556 mread(fd, (genericptr_t) &sysflags, sizeof(struct sysflag));
557 #endif
559 role_init(); /* Reset the initial role, race, gender, and alignment */
560 #ifdef AMII_GRAPHICS
561 amii_setpens(amii_numcolors); /* use colors from save file */
562 #endif
563 mread(fd, (genericptr_t) &u, sizeof(struct you));
565 #define ReadTimebuf(foo) \
566 mread(fd, (genericptr_t) timebuf, 14); \
567 timebuf[14] = '\0'; \
568 foo = time_from_yyyymmddhhmmss(timebuf);
570 ReadTimebuf(ubirthday);
571 mread(fd, &urealtime.realtime, sizeof urealtime.realtime);
572 ReadTimebuf(urealtime.start_timing); /** [not used] **/
573 /* current time is the time to use for next urealtime.realtime update */
574 urealtime.start_timing = getnow();
576 set_uasmon();
577 #ifdef CLIPPING
578 cliparound(u.ux, u.uy);
579 #endif
580 if (u.uhp <= 0 && (!Upolyd || u.mh <= 0)) {
581 u.ux = u.uy = 0; /* affects pline() [hence You()] */
582 You("were not healthy enough to survive restoration.");
583 /* wiz1_level.dlevel is used by mklev.c to see if lots of stuff is
584 * uninitialized, so we only have to set it and not the other stuff.
586 wiz1_level.dlevel = 0;
587 u.uz.dnum = 0;
588 u.uz.dlevel = 1;
589 /* revert to pre-restore option settings */
590 iflags.deferred_X = FALSE;
591 flags = newgameflags;
592 #ifdef SYSFLAGS
593 sysflags = newgamesysflags;
594 #endif
595 return FALSE;
597 /* in case hangup save occurred in midst of level change */
598 assign_level(&u.uz0, &u.uz);
600 /* this stuff comes after potential aborted restore attempts */
601 restore_killers(fd);
602 restore_timers(fd, RANGE_GLOBAL, FALSE, 0L);
603 restore_light_sources(fd);
604 invent = restobjchn(fd, FALSE, FALSE);
605 /* tmp_bc only gets set here if the ball & chain were orphaned
606 because you were swallowed; otherwise they will be on the floor
607 or in your inventory */
608 tmp_bc = restobjchn(fd, FALSE, FALSE);
609 if (tmp_bc) {
610 for (otmp = tmp_bc; otmp; otmp = otmp->nobj) {
611 if (otmp->owornmask)
612 setworn(otmp, otmp->owornmask);
614 if (!uball || !uchain)
615 impossible("restgamestate: lost ball & chain");
618 migrating_objs = restobjchn(fd, FALSE, FALSE);
619 migrating_mons = restmonchn(fd, FALSE);
620 mread(fd, (genericptr_t) mvitals, sizeof(mvitals));
623 * There are some things after this that can have unintended display
624 * side-effects too early in the game.
625 * Disable see_monsters() here, re-enable it at the top of moveloop()
627 defer_see_monsters = TRUE;
629 /* this comes after inventory has been loaded */
630 for (otmp = invent; otmp; otmp = otmp->nobj)
631 if (otmp->owornmask)
632 setworn(otmp, otmp->owornmask);
633 /* reset weapon so that player will get a reminder about "bashing"
634 during next fight when bare-handed or wielding an unconventional
635 item; for pick-axe, we aren't able to distinguish between having
636 applied or wielded it, so be conservative and assume the former */
637 otmp = uwep; /* `uwep' usually init'd by setworn() in loop above */
638 uwep = 0; /* clear it and have setuwep() reinit */
639 setuwep(otmp); /* (don't need any null check here) */
640 if (!uwep || uwep->otyp == PICK_AXE || uwep->otyp == GRAPPLING_HOOK)
641 unweapon = TRUE;
643 restore_dungeon(fd);
644 restlevchn(fd);
645 mread(fd, (genericptr_t) &moves, sizeof moves);
646 mread(fd, (genericptr_t) &monstermoves, sizeof monstermoves);
647 mread(fd, (genericptr_t) &quest_status, sizeof(struct q_score));
648 mread(fd, (genericptr_t) spl_book, sizeof(struct spell) * (MAXSPELL + 1));
649 restore_artifacts(fd);
650 restore_oracles(fd);
651 if (u.ustuck)
652 mread(fd, (genericptr_t) stuckid, sizeof(*stuckid));
653 if (u.usteed)
654 mread(fd, (genericptr_t) steedid, sizeof(*steedid));
655 mread(fd, (genericptr_t) pl_character, sizeof pl_character);
657 mread(fd, (genericptr_t) pl_fruit, sizeof pl_fruit);
658 freefruitchn(ffruit); /* clean up fruit(s) made by initoptions() */
659 ffruit = loadfruitchn(fd);
661 restnames(fd);
662 restore_waterlevel(fd);
663 restore_msghistory(fd);
664 /* must come after all mons & objs are restored */
665 relink_timers(FALSE);
666 relink_light_sources(FALSE);
667 return TRUE;
670 /* update game state pointers to those valid for the current level (so we
671 * don't dereference a wild u.ustuck when saving the game state, for instance)
673 STATIC_OVL void
674 restlevelstate(stuckid, steedid)
675 unsigned int stuckid, steedid;
677 register struct monst *mtmp;
679 if (stuckid) {
680 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
681 if (mtmp->m_id == stuckid)
682 break;
683 if (!mtmp)
684 panic("Cannot find the monster ustuck.");
685 u.ustuck = mtmp;
687 if (steedid) {
688 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
689 if (mtmp->m_id == steedid)
690 break;
691 if (!mtmp)
692 panic("Cannot find the monster usteed.");
693 u.usteed = mtmp;
694 remove_monster(mtmp->mx, mtmp->my);
698 /*ARGSUSED*/
699 STATIC_OVL int
700 restlevelfile(fd, ltmp)
701 int fd; /* fd used in MFLOPPY only */
702 xchar ltmp;
704 int nfd;
705 char whynot[BUFSZ];
707 #ifndef MFLOPPY
708 nhUse(fd);
709 #endif
710 nfd = create_levelfile(ltmp, whynot);
711 if (nfd < 0) {
712 /* BUG: should suppress any attempt to write a panic
713 save file if file creation is now failing... */
714 panic("restlevelfile: %s", whynot);
716 #ifdef MFLOPPY
717 if (!savelev(nfd, ltmp, COUNT_SAVE)) {
718 /* The savelev can't proceed because the size required
719 * is greater than the available disk space.
721 pline("Not enough space on `%s' to restore your game.", levels);
723 /* Remove levels and bones that may have been created.
725 (void) nhclose(nfd);
726 #ifdef AMIGA
727 clearlocks();
728 #else /* !AMIGA */
729 eraseall(levels, alllevels);
730 eraseall(levels, allbones);
732 /* Perhaps the person would like to play without a
733 * RAMdisk.
735 if (ramdisk) {
736 /* PlaywoRAMdisk may not return, but if it does
737 * it is certain that ramdisk will be 0.
739 playwoRAMdisk();
740 /* Rewind save file and try again */
741 (void) lseek(fd, (off_t) 0, 0);
742 (void) validate(fd, (char *) 0); /* skip version etc */
743 return dorecover(fd); /* 0 or 1 */
745 #endif /* ?AMIGA */
746 pline("Be seeing you...");
747 terminate(EXIT_SUCCESS);
749 #endif /* MFLOPPY */
750 bufon(nfd);
751 savelev(nfd, ltmp, WRITE_SAVE | FREE_SAVE);
752 bclose(nfd);
753 return 2;
757 dorecover(fd)
758 register int fd;
760 unsigned int stuckid = 0, steedid = 0; /* not a register */
761 xchar ltmp;
762 int rtmp;
763 struct obj *otmp;
765 restoring = TRUE;
766 get_plname_from_file(fd, plname);
767 getlev(fd, 0, (xchar) 0, FALSE);
768 if (!restgamestate(fd, &stuckid, &steedid)) {
769 display_nhwindow(WIN_MESSAGE, TRUE);
770 savelev(-1, 0, FREE_SAVE); /* discard current level */
771 (void) nhclose(fd);
772 (void) delete_savefile();
773 restoring = FALSE;
774 return 0;
776 restlevelstate(stuckid, steedid);
777 #ifdef INSURANCE
778 savestateinlock();
779 #endif
780 rtmp = restlevelfile(fd, ledger_no(&u.uz));
781 if (rtmp < 2)
782 return rtmp; /* dorecover called recursively */
784 /* these pointers won't be valid while we're processing the
785 * other levels, but they'll be reset again by restlevelstate()
786 * afterwards, and in the meantime at least u.usteed may mislead
787 * place_monster() on other levels
789 u.ustuck = (struct monst *) 0;
790 u.usteed = (struct monst *) 0;
792 #ifdef MICRO
793 #ifdef AMII_GRAPHICS
795 extern struct window_procs amii_procs;
796 if (windowprocs.win_init_nhwindows == amii_procs.win_init_nhwindows) {
797 extern winid WIN_BASE;
798 clear_nhwindow(WIN_BASE); /* hack until there's a hook for this */
801 #else
802 clear_nhwindow(WIN_MAP);
803 #endif
804 clear_nhwindow(WIN_MESSAGE);
805 You("return to level %d in %s%s.", depth(&u.uz),
806 dungeons[u.uz.dnum].dname,
807 flags.debug ? " while in debug mode"
808 : flags.explore ? " while in explore mode" : "");
809 curs(WIN_MAP, 1, 1);
810 dotcnt = 0;
811 dotrow = 2;
812 if (strncmpi("X11", windowprocs.name, 3))
813 putstr(WIN_MAP, 0, "Restoring:");
814 #endif
815 restoreprocs.mread_flags = 1; /* return despite error */
816 while (1) {
817 mread(fd, (genericptr_t) &ltmp, sizeof ltmp);
818 if (restoreprocs.mread_flags == -1)
819 break;
820 getlev(fd, 0, ltmp, FALSE);
821 #ifdef MICRO
822 curs(WIN_MAP, 1 + dotcnt++, dotrow);
823 if (dotcnt >= (COLNO - 1)) {
824 dotrow++;
825 dotcnt = 0;
827 if (strncmpi("X11", windowprocs.name, 3)) {
828 putstr(WIN_MAP, 0, ".");
830 mark_synch();
831 #endif
832 rtmp = restlevelfile(fd, ltmp);
833 if (rtmp < 2)
834 return rtmp; /* dorecover called recursively */
836 restoreprocs.mread_flags = 0;
838 #ifdef BSD
839 (void) lseek(fd, 0L, 0);
840 #else
841 (void) lseek(fd, (off_t) 0, 0);
842 #endif
843 (void) validate(fd, (char *) 0); /* skip version and savefile info */
844 get_plname_from_file(fd, plname);
846 getlev(fd, 0, (xchar) 0, FALSE);
847 (void) nhclose(fd);
849 /* Now set the restore settings to match the
850 * settings used by the save file output routines
852 reset_restpref();
854 restlevelstate(stuckid, steedid);
855 program_state.something_worth_saving = 1; /* useful data now exists */
857 if (!wizard && !discover)
858 (void) delete_savefile();
859 if (Is_rogue_level(&u.uz))
860 assign_graphics(ROGUESET);
861 #ifdef USE_TILES
862 substitute_tiles(&u.uz);
863 #endif
864 #ifdef MFLOPPY
865 gameDiskPrompt();
866 #endif
867 max_rank_sz(); /* to recompute mrank_sz (botl.c) */
868 /* take care of iron ball & chain */
869 for (otmp = fobj; otmp; otmp = otmp->nobj)
870 if (otmp->owornmask)
871 setworn(otmp, otmp->owornmask);
873 /* in_use processing must be after:
874 * + The inventory has been read so that freeinv() works.
875 * + The current level has been restored so billing information
876 * is available.
878 inven_inuse(FALSE);
880 load_qtlist(); /* re-load the quest text info */
881 /* Set up the vision internals, after levl[] data is loaded
882 but before docrt(). */
883 reglyph_darkroom();
884 vision_reset();
885 vision_full_recalc = 1; /* recompute vision (not saved) */
887 run_timers(); /* expire all timers that have gone off while away */
888 docrt();
889 restoring = FALSE;
890 clear_nhwindow(WIN_MESSAGE);
892 /* Success! */
893 welcome(FALSE);
894 check_special_room(FALSE);
895 return 1;
898 void
899 restcemetery(fd, cemeteryaddr)
900 int fd;
901 struct cemetery **cemeteryaddr;
903 struct cemetery *bonesinfo, **bonesaddr;
904 int flag;
906 mread(fd, (genericptr_t) &flag, sizeof flag);
907 if (flag == 0) {
908 bonesaddr = cemeteryaddr;
909 do {
910 bonesinfo = (struct cemetery *) alloc(sizeof *bonesinfo);
911 mread(fd, (genericptr_t) bonesinfo, sizeof *bonesinfo);
912 *bonesaddr = bonesinfo;
913 bonesaddr = &(*bonesaddr)->next;
914 } while (*bonesaddr);
915 } else {
916 *cemeteryaddr = 0;
920 /*ARGSUSED*/
921 STATIC_OVL void
922 rest_levl(fd, rlecomp)
923 int fd;
924 boolean rlecomp;
926 #ifdef RLECOMP
927 short i, j;
928 uchar len;
929 struct rm r;
931 if (rlecomp) {
932 (void) memset((genericptr_t) &r, 0, sizeof(r));
933 i = 0;
934 j = 0;
935 len = 0;
936 while (i < ROWNO) {
937 while (j < COLNO) {
938 if (len > 0) {
939 levl[j][i] = r;
940 len -= 1;
941 j += 1;
942 } else {
943 mread(fd, (genericptr_t) &len, sizeof(uchar));
944 mread(fd, (genericptr_t) &r, sizeof(struct rm));
947 j = 0;
948 i += 1;
950 return;
952 #else /* !RLECOMP */
953 nhUse(rlecomp);
954 #endif /* ?RLECOMP */
955 mread(fd, (genericptr_t) levl, sizeof levl);
958 void
959 trickery(reason)
960 char *reason;
962 pline("Strange, this map is not as I remember it.");
963 pline("Somebody is trying some trickery here...");
964 pline("This game is void.");
965 Strcpy(killer.name, reason ? reason : "");
966 done(TRICKED);
969 void
970 getlev(fd, pid, lev, ghostly)
971 int fd, pid;
972 xchar lev;
973 boolean ghostly;
975 register struct trap *trap;
976 register struct monst *mtmp;
977 long elapsed;
978 branch *br;
979 int hpid;
980 xchar dlvl;
981 int x, y;
982 #ifdef TOS
983 short tlev;
984 #endif
986 if (ghostly)
987 clear_id_mapping();
989 #if defined(MSDOS) || defined(OS2)
990 setmode(fd, O_BINARY);
991 #endif
992 /* Load the old fruit info. We have to do it first, so the
993 * information is available when restoring the objects.
995 if (ghostly)
996 oldfruit = loadfruitchn(fd);
998 /* First some sanity checks */
999 mread(fd, (genericptr_t) &hpid, sizeof(hpid));
1000 /* CHECK: This may prevent restoration */
1001 #ifdef TOS
1002 mread(fd, (genericptr_t) &tlev, sizeof(tlev));
1003 dlvl = tlev & 0x00ff;
1004 #else
1005 mread(fd, (genericptr_t) &dlvl, sizeof(dlvl));
1006 #endif
1007 if ((pid && pid != hpid) || (lev && dlvl != lev)) {
1008 char trickbuf[BUFSZ];
1010 if (pid && pid != hpid)
1011 Sprintf(trickbuf, "PID (%d) doesn't match saved PID (%d)!", hpid,
1012 pid);
1013 else
1014 Sprintf(trickbuf, "This is level %d, not %d!", dlvl, lev);
1015 if (wizard)
1016 pline1(trickbuf);
1017 trickery(trickbuf);
1019 restcemetery(fd, &level.bonesinfo);
1020 rest_levl(fd,
1021 (boolean) ((sfrestinfo.sfi1 & SFI1_RLECOMP) == SFI1_RLECOMP));
1022 mread(fd, (genericptr_t) lastseentyp, sizeof(lastseentyp));
1023 mread(fd, (genericptr_t) &omoves, sizeof(omoves));
1024 elapsed = monstermoves - omoves;
1025 mread(fd, (genericptr_t) &upstair, sizeof(stairway));
1026 mread(fd, (genericptr_t) &dnstair, sizeof(stairway));
1027 mread(fd, (genericptr_t) &upladder, sizeof(stairway));
1028 mread(fd, (genericptr_t) &dnladder, sizeof(stairway));
1029 mread(fd, (genericptr_t) &sstairs, sizeof(stairway));
1030 mread(fd, (genericptr_t) &updest, sizeof(dest_area));
1031 mread(fd, (genericptr_t) &dndest, sizeof(dest_area));
1032 mread(fd, (genericptr_t) &level.flags, sizeof(level.flags));
1033 mread(fd, (genericptr_t) doors, sizeof(doors));
1034 rest_rooms(fd); /* No joke :-) */
1035 if (nroom)
1036 doorindex = rooms[nroom - 1].fdoor + rooms[nroom - 1].doorct;
1037 else
1038 doorindex = 0;
1040 restore_timers(fd, RANGE_LEVEL, ghostly, elapsed);
1041 restore_light_sources(fd);
1042 fmon = restmonchn(fd, ghostly);
1044 rest_worm(fd); /* restore worm information */
1045 ftrap = 0;
1046 while (trap = newtrap(),
1047 mread(fd, (genericptr_t) trap, sizeof(struct trap)),
1048 trap->tx != 0) { /* need "!= 0" to work around DICE 3.0 bug */
1049 trap->ntrap = ftrap;
1050 ftrap = trap;
1052 dealloc_trap(trap);
1053 fobj = restobjchn(fd, ghostly, FALSE);
1054 find_lev_obj();
1055 /* restobjchn()'s `frozen' argument probably ought to be a callback
1056 routine so that we can check for objects being buried under ice */
1057 level.buriedobjlist = restobjchn(fd, ghostly, FALSE);
1058 billobjs = restobjchn(fd, ghostly, FALSE);
1059 rest_engravings(fd);
1061 /* reset level.monsters for new level */
1062 for (x = 0; x < COLNO; x++)
1063 for (y = 0; y < ROWNO; y++)
1064 level.monsters[x][y] = (struct monst *) 0;
1065 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
1066 if (mtmp->isshk)
1067 set_residency(mtmp, FALSE);
1068 place_monster(mtmp, mtmp->mx, mtmp->my);
1069 if (mtmp->wormno)
1070 place_wsegs(mtmp);
1072 /* regenerate monsters while on another level */
1073 if (!u.uz.dlevel)
1074 continue;
1075 if (ghostly) {
1076 /* reset peaceful/malign relative to new character;
1077 shopkeepers will reset based on name */
1078 if (!mtmp->isshk)
1079 mtmp->mpeaceful =
1080 (is_unicorn(mtmp->data)
1081 && sgn(u.ualign.type) == sgn(mtmp->data->maligntyp))
1082 ? TRUE
1083 : peace_minded(mtmp->data);
1084 set_malign(mtmp);
1085 } else if (elapsed > 0L) {
1086 mon_catchup_elapsed_time(mtmp, elapsed);
1088 /* update shape-changers in case protection against
1089 them is different now than when the level was saved */
1090 restore_cham(mtmp);
1091 /* give hiders a chance to hide before their next move */
1092 if (ghostly || elapsed > (long) rnd(10))
1093 hide_monst(mtmp);
1096 restdamage(fd, ghostly);
1097 rest_regions(fd, ghostly);
1098 if (ghostly) {
1099 /* Now get rid of all the temp fruits... */
1100 freefruitchn(oldfruit), oldfruit = 0;
1102 if (lev > ledger_no(&medusa_level)
1103 && lev < ledger_no(&stronghold_level) && xdnstair == 0) {
1104 coord cc;
1106 mazexy(&cc);
1107 xdnstair = cc.x;
1108 ydnstair = cc.y;
1109 levl[cc.x][cc.y].typ = STAIRS;
1112 br = Is_branchlev(&u.uz);
1113 if (br && u.uz.dlevel == 1) {
1114 d_level ltmp;
1116 if (on_level(&u.uz, &br->end1))
1117 assign_level(&ltmp, &br->end2);
1118 else
1119 assign_level(&ltmp, &br->end1);
1121 switch (br->type) {
1122 case BR_STAIR:
1123 case BR_NO_END1:
1124 case BR_NO_END2: /* OK to assign to sstairs if it's not used */
1125 assign_level(&sstairs.tolev, &ltmp);
1126 break;
1127 case BR_PORTAL: /* max of 1 portal per level */
1128 for (trap = ftrap; trap; trap = trap->ntrap)
1129 if (trap->ttyp == MAGIC_PORTAL)
1130 break;
1131 if (!trap)
1132 panic("getlev: need portal but none found");
1133 assign_level(&trap->dst, &ltmp);
1134 break;
1136 } else if (!br) {
1137 struct trap *ttmp = 0;
1139 /* Remove any dangling portals. */
1140 for (trap = ftrap; trap; trap = ttmp) {
1141 ttmp = trap->ntrap;
1142 if (trap->ttyp == MAGIC_PORTAL)
1143 deltrap(trap);
1148 /* must come after all mons & objs are restored */
1149 relink_timers(ghostly);
1150 relink_light_sources(ghostly);
1151 reset_oattached_mids(ghostly);
1153 if (ghostly)
1154 clear_id_mapping();
1157 void
1158 get_plname_from_file(fd, plbuf)
1159 int fd;
1160 char *plbuf;
1162 int pltmpsiz = 0;
1163 (void) read(fd, (genericptr_t) &pltmpsiz, sizeof(pltmpsiz));
1164 (void) read(fd, (genericptr_t) plbuf, pltmpsiz);
1165 return;
1168 STATIC_OVL void
1169 restore_msghistory(fd)
1170 register int fd;
1172 int msgsize, msgcount = 0;
1173 char msg[BUFSZ];
1175 while (1) {
1176 mread(fd, (genericptr_t) &msgsize, sizeof(msgsize));
1177 if (msgsize == -1)
1178 break;
1179 if (msgsize > (BUFSZ - 1))
1180 panic("restore_msghistory: msg too big (%d)", msgsize);
1181 mread(fd, (genericptr_t) msg, msgsize);
1182 msg[msgsize] = '\0';
1183 putmsghistory(msg, TRUE);
1184 ++msgcount;
1186 if (msgcount)
1187 putmsghistory((char *) 0, TRUE);
1188 debugpline1("Read %d messages from savefile.", msgcount);
1191 /* Clear all structures for object and monster ID mapping. */
1192 STATIC_OVL void
1193 clear_id_mapping()
1195 struct bucket *curr;
1197 while ((curr = id_map) != 0) {
1198 id_map = curr->next;
1199 free((genericptr_t) curr);
1201 n_ids_mapped = 0;
1204 /* Add a mapping to the ID map. */
1205 STATIC_OVL void
1206 add_id_mapping(gid, nid)
1207 unsigned gid, nid;
1209 int idx;
1211 idx = n_ids_mapped % N_PER_BUCKET;
1212 /* idx is zero on first time through, as well as when a new bucket is */
1213 /* needed */
1214 if (idx == 0) {
1215 struct bucket *gnu = (struct bucket *) alloc(sizeof(struct bucket));
1216 gnu->next = id_map;
1217 id_map = gnu;
1220 id_map->map[idx].gid = gid;
1221 id_map->map[idx].nid = nid;
1222 n_ids_mapped++;
1226 * Global routine to look up a mapping. If found, return TRUE and fill
1227 * in the new ID value. Otherwise, return false and return -1 in the new
1228 * ID.
1230 boolean
1231 lookup_id_mapping(gid, nidp)
1232 unsigned gid, *nidp;
1234 int i;
1235 struct bucket *curr;
1237 if (n_ids_mapped)
1238 for (curr = id_map; curr; curr = curr->next) {
1239 /* first bucket might not be totally full */
1240 if (curr == id_map) {
1241 i = n_ids_mapped % N_PER_BUCKET;
1242 if (i == 0)
1243 i = N_PER_BUCKET;
1244 } else
1245 i = N_PER_BUCKET;
1247 while (--i >= 0)
1248 if (gid == curr->map[i].gid) {
1249 *nidp = curr->map[i].nid;
1250 return TRUE;
1254 return FALSE;
1257 STATIC_OVL void
1258 reset_oattached_mids(ghostly)
1259 boolean ghostly;
1261 struct obj *otmp;
1262 unsigned oldid, nid;
1263 for (otmp = fobj; otmp; otmp = otmp->nobj) {
1264 if (ghostly && has_omonst(otmp)) {
1265 struct monst *mtmp = OMONST(otmp);
1267 mtmp->m_id = 0;
1268 mtmp->mpeaceful = mtmp->mtame = 0; /* pet's owner died! */
1270 if (ghostly && has_omid(otmp)) {
1271 (void) memcpy((genericptr_t) &oldid, (genericptr_t) OMID(otmp),
1272 sizeof(oldid));
1273 if (lookup_id_mapping(oldid, &nid))
1274 (void) memcpy((genericptr_t) OMID(otmp), (genericptr_t) &nid,
1275 sizeof(nid));
1276 else
1277 free_omid(otmp);
1282 #ifdef SELECTSAVED
1283 /* put up a menu listing each character from this player's saved games;
1284 returns 1: use plname[], 0: new game, -1: quit */
1286 restore_menu(bannerwin)
1287 winid bannerwin; /* if not WIN_ERR, clear window and show copyright in menu */
1289 winid tmpwin;
1290 anything any;
1291 char **saved;
1292 menu_item *chosen_game = (menu_item *) 0;
1293 int k, clet, ch = 0; /* ch: 0 => new game */
1295 *plname = '\0';
1296 saved = get_saved_games(); /* array of character names */
1297 if (saved && *saved) {
1298 tmpwin = create_nhwindow(NHW_MENU);
1299 start_menu(tmpwin);
1300 any = zeroany; /* no selection */
1301 if (bannerwin != WIN_ERR) {
1302 /* for tty; erase copyright notice and redo it in the menu */
1303 clear_nhwindow(bannerwin);
1304 /* COPYRIGHT_BANNER_[ABCD] */
1305 for (k = 1; k <= 4; ++k)
1306 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
1307 copyright_banner_line(k), MENU_UNSELECTED);
1308 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "",
1309 MENU_UNSELECTED);
1311 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
1312 "Select one of your saved games", MENU_UNSELECTED);
1313 for (k = 0; saved[k]; ++k) {
1314 any.a_int = k + 1;
1315 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, saved[k],
1316 MENU_UNSELECTED);
1318 clet = (k <= 'n' - 'a') ? 'n' : 0; /* new game */
1319 any.a_int = -1; /* not >= 0 */
1320 add_menu(tmpwin, NO_GLYPH, &any, clet, 0, ATR_NONE,
1321 "Start a new character", MENU_UNSELECTED);
1322 clet = (k + 1 <= 'q' - 'a') ? 'q' : 0; /* quit */
1323 any.a_int = -2;
1324 add_menu(tmpwin, NO_GLYPH, &any, clet, 0, ATR_NONE,
1325 "Never mind (quit)", MENU_SELECTED);
1326 /* no prompt on end_menu, as we've done our own at the top */
1327 end_menu(tmpwin, (char *) 0);
1328 if (select_menu(tmpwin, PICK_ONE, &chosen_game) > 0) {
1329 ch = chosen_game->item.a_int;
1330 if (ch > 0)
1331 Strcpy(plname, saved[ch - 1]);
1332 else if (ch < 0)
1333 ++ch; /* -1 -> 0 (new game), -2 -> -1 (quit) */
1334 free((genericptr_t) chosen_game);
1335 } else {
1336 ch = -1; /* quit menu without making a selection => quit */
1338 destroy_nhwindow(tmpwin);
1339 if (bannerwin != WIN_ERR) {
1340 /* for tty; clear the menu away and put subset of copyright back
1342 clear_nhwindow(bannerwin);
1343 /* COPYRIGHT_BANNER_A, preceding "Who are you?" prompt */
1344 if (ch == 0)
1345 putstr(bannerwin, 0, copyright_banner_line(1));
1348 free_saved_games(saved);
1349 return (ch > 0) ? 1 : ch;
1351 #endif /* SELECTSAVED */
1353 void
1354 minit()
1356 (*restoreprocs.restore_minit)();
1357 return;
1360 void
1361 mread(fd, buf, len)
1362 register int fd;
1363 register genericptr_t buf;
1364 register unsigned int len;
1366 (*restoreprocs.restore_mread)(fd, buf, len);
1367 return;
1370 /* examine the version info and the savefile_info data
1371 that immediately follows it.
1372 Return 0 if it passed the checks.
1373 Return 1 if it failed the version check.
1374 Return 2 if it failed the savefile feature check.
1375 Return -1 if it failed for some unknown reason.
1378 validate(fd, name)
1379 int fd;
1380 const char *name;
1382 int rlen;
1383 struct savefile_info sfi;
1384 unsigned long compatible;
1385 boolean verbose = name ? TRUE : FALSE, reslt = FALSE;
1387 if (!(reslt = uptodate(fd, name)))
1388 return 1;
1390 rlen = read(fd, (genericptr_t) &sfi, sizeof sfi);
1391 minit(); /* ZEROCOMP */
1392 if (rlen == 0) {
1393 if (verbose) {
1394 pline("File \"%s\" is empty during save file feature check?",
1395 name);
1396 wait_synch();
1398 return -1;
1401 compatible = (sfi.sfi1 & sfcap.sfi1);
1403 if ((sfi.sfi1 & SFI1_ZEROCOMP) == SFI1_ZEROCOMP) {
1404 if ((compatible & SFI1_ZEROCOMP) != SFI1_ZEROCOMP) {
1405 if (verbose) {
1406 pline("File \"%s\" has incompatible ZEROCOMP compression.",
1407 name);
1408 wait_synch();
1410 return 2;
1411 } else if ((sfrestinfo.sfi1 & SFI1_ZEROCOMP) != SFI1_ZEROCOMP) {
1412 set_restpref("zerocomp");
1416 if ((sfi.sfi1 & SFI1_EXTERNALCOMP) == SFI1_EXTERNALCOMP) {
1417 if ((compatible & SFI1_EXTERNALCOMP) != SFI1_EXTERNALCOMP) {
1418 if (verbose) {
1419 pline("File \"%s\" lacks required internal compression.",
1420 name);
1421 wait_synch();
1423 return 2;
1424 } else if ((sfrestinfo.sfi1 & SFI1_EXTERNALCOMP)
1425 != SFI1_EXTERNALCOMP) {
1426 set_restpref("externalcomp");
1430 /* RLECOMP check must be last, after ZEROCOMP or INTERNALCOMP adjustments
1432 if ((sfi.sfi1 & SFI1_RLECOMP) == SFI1_RLECOMP) {
1433 if ((compatible & SFI1_RLECOMP) != SFI1_RLECOMP) {
1434 if (verbose) {
1435 pline("File \"%s\" has incompatible run-length compression.",
1436 name);
1437 wait_synch();
1439 return 2;
1440 } else if ((sfrestinfo.sfi1 & SFI1_RLECOMP) != SFI1_RLECOMP) {
1441 set_restpref("rlecomp");
1444 /* savefile does not have RLECOMP level location compression, so adjust */
1445 else
1446 set_restpref("!rlecomp");
1448 return 0;
1451 void
1452 reset_restpref()
1454 #ifdef ZEROCOMP
1455 if (iflags.zerocomp)
1456 set_restpref("zerocomp");
1457 else
1458 #endif
1459 set_restpref("externalcomp");
1460 #ifdef RLECOMP
1461 if (iflags.rlecomp)
1462 set_restpref("rlecomp");
1463 else
1464 #endif
1465 set_restpref("!rlecomp");
1468 void
1469 set_restpref(suitename)
1470 const char *suitename;
1472 if (!strcmpi(suitename, "externalcomp")) {
1473 restoreprocs.name = "externalcomp";
1474 restoreprocs.restore_mread = def_mread;
1475 restoreprocs.restore_minit = def_minit;
1476 sfrestinfo.sfi1 |= SFI1_EXTERNALCOMP;
1477 sfrestinfo.sfi1 &= ~SFI1_ZEROCOMP;
1478 def_minit();
1480 if (!strcmpi(suitename, "!rlecomp")) {
1481 sfrestinfo.sfi1 &= ~SFI1_RLECOMP;
1483 #ifdef ZEROCOMP
1484 if (!strcmpi(suitename, "zerocomp")) {
1485 restoreprocs.name = "zerocomp";
1486 restoreprocs.restore_mread = zerocomp_mread;
1487 restoreprocs.restore_minit = zerocomp_minit;
1488 sfrestinfo.sfi1 |= SFI1_ZEROCOMP;
1489 sfrestinfo.sfi1 &= ~SFI1_EXTERNALCOMP;
1490 zerocomp_minit();
1492 #endif
1493 #ifdef RLECOMP
1494 if (!strcmpi(suitename, "rlecomp")) {
1495 sfrestinfo.sfi1 |= SFI1_RLECOMP;
1497 #endif
1500 #ifdef ZEROCOMP
1501 #define RLESC '\0' /* Leading character for run of RLESC's */
1503 #ifndef ZEROCOMP_BUFSIZ
1504 #define ZEROCOMP_BUFSIZ BUFSZ
1505 #endif
1506 static NEARDATA unsigned char inbuf[ZEROCOMP_BUFSIZ];
1507 static NEARDATA unsigned short inbufp = 0;
1508 static NEARDATA unsigned short inbufsz = 0;
1509 static NEARDATA short inrunlength = -1;
1510 static NEARDATA int mreadfd;
1512 STATIC_OVL int
1513 zerocomp_mgetc()
1515 if (inbufp >= inbufsz) {
1516 inbufsz = read(mreadfd, (genericptr_t) inbuf, sizeof inbuf);
1517 if (!inbufsz) {
1518 if (inbufp > sizeof inbuf)
1519 error("EOF on file #%d.\n", mreadfd);
1520 inbufp = 1 + sizeof inbuf; /* exactly one warning :-) */
1521 return -1;
1523 inbufp = 0;
1525 return inbuf[inbufp++];
1528 STATIC_OVL void
1529 zerocomp_minit()
1531 inbufsz = 0;
1532 inbufp = 0;
1533 inrunlength = -1;
1536 STATIC_OVL void
1537 zerocomp_mread(fd, buf, len)
1538 int fd;
1539 genericptr_t buf;
1540 register unsigned len;
1542 /*register int readlen = 0;*/
1543 if (fd < 0)
1544 error("Restore error; mread attempting to read file %d.", fd);
1545 mreadfd = fd;
1546 while (len--) {
1547 if (inrunlength > 0) {
1548 inrunlength--;
1549 *(*((char **) &buf))++ = '\0';
1550 } else {
1551 register short ch = zerocomp_mgetc();
1552 if (ch < 0) {
1553 restoreprocs.mread_flags = -1;
1554 return;
1556 if ((*(*(char **) &buf)++ = (char) ch) == RLESC) {
1557 inrunlength = zerocomp_mgetc();
1560 /*readlen++;*/
1563 #endif /* ZEROCOMP */
1565 STATIC_OVL void
1566 def_minit()
1568 return;
1571 STATIC_OVL void
1572 def_mread(fd, buf, len)
1573 register int fd;
1574 register genericptr_t buf;
1575 register unsigned int len;
1577 register int rlen;
1578 #if defined(BSD) || defined(ULTRIX)
1579 #define readLenType int
1580 #else /* e.g. SYSV, __TURBOC__ */
1581 #define readLenType unsigned
1582 #endif
1584 rlen = read(fd, buf, (readLenType) len);
1585 if ((readLenType) rlen != (readLenType) len) {
1586 if (restoreprocs.mread_flags == 1) { /* means "return anyway" */
1587 restoreprocs.mread_flags = -1;
1588 return;
1589 } else {
1590 pline("Read %d instead of %u bytes.", rlen, len);
1591 if (restoring) {
1592 (void) nhclose(fd);
1593 (void) delete_savefile();
1594 error("Error restoring old game.");
1596 panic("Error reading level file.");
1601 /*restore.c*/