OSDN Git Service

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