OSDN Git Service

use _pragma
[jnethack/source.git] / src / restore.c
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 /*-Copyright (c) Michael Allison, 2009. */
4 /* NetHack may be freely redistributed.  See license for details. */
5
6 /* JNetHack Copyright */
7 /* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000  */
8 /* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2018            */
9 /* JNetHack may be freely redistributed.  See license for details. */
10
11 #include "hack.h"
12 #include "lev.h"
13 #include "tcap.h" /* for TERMLIB and ASCIIGRAPH */
14
15 #if defined(MICRO)
16 extern int dotcnt; /* shared with save */
17 extern int dotrow; /* shared with save */
18 #endif
19
20 #ifdef USE_TILES
21 extern void FDECL(substitute_tiles, (d_level *)); /* from tile.c */
22 #endif
23
24 #ifdef ZEROCOMP
25 STATIC_DCL void NDECL(zerocomp_minit);
26 STATIC_DCL void FDECL(zerocomp_mread, (int, genericptr_t, unsigned int));
27 STATIC_DCL int NDECL(zerocomp_mgetc);
28 #endif
29
30 STATIC_DCL void NDECL(def_minit);
31 STATIC_DCL void FDECL(def_mread, (int, genericptr_t, unsigned int));
32
33 STATIC_DCL void NDECL(find_lev_obj);
34 STATIC_DCL void FDECL(restlevchn, (int));
35 STATIC_DCL void FDECL(restdamage, (int, BOOLEAN_P));
36 STATIC_DCL void FDECL(restobj, (int, struct obj *));
37 STATIC_DCL struct obj *FDECL(restobjchn, (int, BOOLEAN_P, BOOLEAN_P));
38 STATIC_OVL void FDECL(restmon, (int, struct monst *));
39 STATIC_DCL struct monst *FDECL(restmonchn, (int, BOOLEAN_P));
40 STATIC_DCL struct fruit *FDECL(loadfruitchn, (int));
41 STATIC_DCL void FDECL(freefruitchn, (struct fruit *));
42 STATIC_DCL void FDECL(ghostfruit, (struct obj *));
43 STATIC_DCL boolean
44 FDECL(restgamestate, (int, unsigned int *, unsigned int *));
45 STATIC_DCL void FDECL(restlevelstate, (unsigned int, unsigned int));
46 STATIC_DCL int FDECL(restlevelfile, (int, XCHAR_P));
47 STATIC_OVL void FDECL(restore_msghistory, (int));
48 STATIC_DCL void FDECL(reset_oattached_mids, (BOOLEAN_P));
49 STATIC_DCL void FDECL(rest_levl, (int, BOOLEAN_P));
50
51 static struct restore_procs {
52     const char *name;
53     int mread_flags;
54     void NDECL((*restore_minit));
55     void FDECL((*restore_mread), (int, genericptr_t, unsigned int));
56     void FDECL((*restore_bclose), (int));
57 } restoreprocs = {
58 #if !defined(ZEROCOMP) || (defined(COMPRESS) || defined(ZLIB_COMP))
59     "externalcomp", 0, def_minit, def_mread, def_bclose,
60 #else
61     "zerocomp", 0, zerocomp_minit, zerocomp_mread, zerocomp_bclose,
62 #endif
63 };
64
65 /*
66  * Save a mapping of IDs from ghost levels to the current level.  This
67  * map is used by the timer routines when restoring ghost levels.
68  */
69 #define N_PER_BUCKET 64
70 struct bucket {
71     struct bucket *next;
72     struct {
73         unsigned gid; /* ghost ID */
74         unsigned nid; /* new ID */
75     } map[N_PER_BUCKET];
76 };
77
78 STATIC_DCL void NDECL(clear_id_mapping);
79 STATIC_DCL void FDECL(add_id_mapping, (unsigned, unsigned));
80
81 static int n_ids_mapped = 0;
82 static struct bucket *id_map = 0;
83
84 #ifdef AMII_GRAPHICS
85 void FDECL(amii_setpens, (int)); /* use colors from save file */
86 extern int amii_numcolors;
87 #endif
88
89 #include "display.h"
90
91 boolean restoring = FALSE;
92 static NEARDATA struct fruit *oldfruit;
93 static NEARDATA long omoves;
94
95 #define Is_IceBox(o) ((o)->otyp == ICE_BOX ? TRUE : FALSE)
96
97 /* Recalculate level.objects[x][y], since this info was not saved. */
98 STATIC_OVL void
99 find_lev_obj()
100 {
101     register struct obj *fobjtmp = (struct obj *) 0;
102     register struct obj *otmp;
103     int x, y;
104
105     for (x = 0; x < COLNO; x++)
106         for (y = 0; y < ROWNO; y++)
107             level.objects[x][y] = (struct obj *) 0;
108
109     /*
110      * Reverse the entire fobj chain, which is necessary so that we can
111      * place the objects in the proper order.  Make all obj in chain
112      * OBJ_FREE so place_object will work correctly.
113      */
114     while ((otmp = fobj) != 0) {
115         fobj = otmp->nobj;
116         otmp->nobj = fobjtmp;
117         otmp->where = OBJ_FREE;
118         fobjtmp = otmp;
119     }
120     /* fobj should now be empty */
121
122     /* Set level.objects (as well as reversing the chain back again) */
123     while ((otmp = fobjtmp) != 0) {
124         fobjtmp = otmp->nobj;
125         place_object(otmp, otmp->ox, otmp->oy);
126     }
127 }
128
129 /* Things that were marked "in_use" when the game was saved (ex. via the
130  * infamous "HUP" cheat) get used up here.
131  */
132 void
133 inven_inuse(quietly)
134 boolean quietly;
135 {
136     register struct obj *otmp, *otmp2;
137
138     for (otmp = invent; otmp; otmp = otmp2) {
139         otmp2 = otmp->nobj;
140         if (otmp->in_use) {
141             if (!quietly)
142 /*JP
143                 pline("Finishing off %s...", xname(otmp));
144 */
145                 pline("%s\82ð\8eg\82¢\8fI\82¦\82½\81D\81D\81D", xname(otmp));
146             useup(otmp);
147         }
148     }
149 }
150
151 STATIC_OVL void
152 restlevchn(fd)
153 register int fd;
154 {
155     int cnt;
156     s_level *tmplev, *x;
157
158     sp_levchn = (s_level *) 0;
159     mread(fd, (genericptr_t) &cnt, sizeof(int));
160     for (; cnt > 0; cnt--) {
161         tmplev = (s_level *) alloc(sizeof(s_level));
162         mread(fd, (genericptr_t) tmplev, sizeof(s_level));
163         if (!sp_levchn)
164             sp_levchn = tmplev;
165         else {
166             for (x = sp_levchn; x->next; x = x->next)
167                 ;
168             x->next = tmplev;
169         }
170         tmplev->next = (s_level *) 0;
171     }
172 }
173
174 STATIC_OVL void
175 restdamage(fd, ghostly)
176 int fd;
177 boolean ghostly;
178 {
179     int counter;
180     struct damage *tmp_dam;
181
182     mread(fd, (genericptr_t) &counter, sizeof(counter));
183     if (!counter)
184         return;
185     tmp_dam = (struct damage *) alloc(sizeof(struct damage));
186     while (--counter >= 0) {
187         char damaged_shops[5], *shp = (char *) 0;
188
189         mread(fd, (genericptr_t) tmp_dam, sizeof(*tmp_dam));
190         if (ghostly)
191             tmp_dam->when += (monstermoves - omoves);
192         Strcpy(damaged_shops,
193                in_rooms(tmp_dam->place.x, tmp_dam->place.y, SHOPBASE));
194         if (u.uz.dlevel) {
195             /* when restoring, there are two passes over the current
196              * level.  the first time, u.uz isn't set, so neither is
197              * shop_keeper().  just wait and process the damage on
198              * the second pass.
199              */
200             for (shp = damaged_shops; *shp; shp++) {
201                 struct monst *shkp = shop_keeper(*shp);
202
203                 if (shkp && inhishop(shkp)
204                     && repair_damage(shkp, tmp_dam, TRUE))
205                     break;
206             }
207         }
208         if (!shp || !*shp) {
209             tmp_dam->next = level.damagelist;
210             level.damagelist = tmp_dam;
211             tmp_dam = (struct damage *) alloc(sizeof(*tmp_dam));
212         }
213     }
214     free((genericptr_t) tmp_dam);
215 }
216
217 /* restore one object */
218 STATIC_OVL void
219 restobj(fd, otmp)
220 int fd;
221 struct obj *otmp;
222 {
223     int buflen;
224
225     mread(fd, (genericptr_t) otmp, sizeof(struct obj));
226
227     /* next object pointers are invalid; otmp->cobj needs to be left
228        as is--being non-null is key to restoring container contents */
229     otmp->nobj = otmp->nexthere = (struct obj *) 0;
230     /* non-null oextra needs to be reconstructed */
231     if (otmp->oextra) {
232         otmp->oextra = newoextra();
233
234         /* oname - object's name */
235         mread(fd, (genericptr_t) &buflen, sizeof(buflen));
236         if (buflen > 0) { /* includes terminating '\0' */
237             new_oname(otmp, buflen);
238             mread(fd, (genericptr_t) ONAME(otmp), buflen);
239         }
240         /* omonst - corpse or statue might retain full monster details */
241         mread(fd, (genericptr_t) &buflen, sizeof(buflen));
242         if (buflen > 0) {
243             newomonst(otmp);
244             /* this is actually a monst struct, so we
245                can just defer to restmon() here */
246             restmon(fd, OMONST(otmp));
247         }
248         /* omid - monster id number, connecting corpse to ghost */
249         mread(fd, (genericptr_t) &buflen, sizeof(buflen));
250         if (buflen > 0) {
251             newomid(otmp);
252             mread(fd, (genericptr_t) OMID(otmp), buflen);
253         }
254         /* olong - temporary gold */
255         mread(fd, (genericptr_t) &buflen, sizeof(buflen));
256         if (buflen > 0) {
257             newolong(otmp);
258             mread(fd, (genericptr_t) OLONG(otmp), buflen);
259         }
260         /* omailcmd - feedback mechanism for scroll of mail */
261         mread(fd, (genericptr_t) &buflen, sizeof(buflen));
262         if (buflen > 0) {
263             char *omailcmd = (char *) alloc(buflen);
264
265             mread(fd, (genericptr_t) omailcmd, buflen);
266             new_omailcmd(otmp, omailcmd);
267             free((genericptr_t) omailcmd);
268         }
269     }
270 }
271
272 STATIC_OVL struct obj *
273 restobjchn(fd, ghostly, frozen)
274 register int fd;
275 boolean ghostly, frozen;
276 {
277     register struct obj *otmp, *otmp2 = 0;
278     register struct obj *first = (struct obj *) 0;
279     int buflen;
280
281     while (1) {
282         mread(fd, (genericptr_t) &buflen, sizeof buflen);
283         if (buflen == -1)
284             break;
285
286         otmp = newobj();
287         restobj(fd, otmp);
288         if (!first)
289             first = otmp;
290         else
291             otmp2->nobj = otmp;
292
293         if (ghostly) {
294             unsigned nid = context.ident++;
295             add_id_mapping(otmp->o_id, nid);
296             otmp->o_id = nid;
297         }
298         if (ghostly && otmp->otyp == SLIME_MOLD)
299             ghostfruit(otmp);
300         /* Ghost levels get object age shifted from old player's clock
301          * to new player's clock.  Assumption: new player arrived
302          * immediately after old player died.
303          */
304         if (ghostly && !frozen && !age_is_relative(otmp))
305             otmp->age = monstermoves - omoves + otmp->age;
306
307         /* get contents of a container or statue */
308         if (Has_contents(otmp)) {
309             struct obj *otmp3;
310             otmp->cobj = restobjchn(fd, ghostly, Is_IceBox(otmp));
311             /* restore container back pointers */
312             for (otmp3 = otmp->cobj; otmp3; otmp3 = otmp3->nobj)
313                 otmp3->ocontainer = otmp;
314         }
315         if (otmp->bypass)
316             otmp->bypass = 0;
317         if (!ghostly) {
318             /* fix the pointers */
319             if (otmp->o_id == context.victual.o_id)
320                 context.victual.piece = otmp;
321             if (otmp->o_id == context.tin.o_id)
322                 context.tin.tin = otmp;
323             if (otmp->o_id == context.spbook.o_id)
324                 context.spbook.book = otmp;
325         }
326         otmp2 = otmp;
327     }
328     if (first && otmp2->nobj) {
329         impossible("Restobjchn: error reading objchn.");
330         otmp2->nobj = 0;
331     }
332
333     return first;
334 }
335
336 /* restore one monster */
337 STATIC_OVL void
338 restmon(fd, mtmp)
339 int fd;
340 struct monst *mtmp;
341 {
342     int buflen;
343
344     mread(fd, (genericptr_t) mtmp, sizeof(struct monst));
345
346     /* next monster pointer is invalid */
347     mtmp->nmon = (struct monst *) 0;
348     /* non-null mextra needs to be reconstructed */
349     if (mtmp->mextra) {
350         mtmp->mextra = newmextra();
351
352         /* mname - monster's name */
353         mread(fd, (genericptr_t) &buflen, sizeof(buflen));
354         if (buflen > 0) { /* includes terminating '\0' */
355             new_mname(mtmp, buflen);
356             mread(fd, (genericptr_t) MNAME(mtmp), buflen);
357         }
358         /* egd - vault guard */
359         mread(fd, (genericptr_t) &buflen, sizeof(buflen));
360         if (buflen > 0) {
361             newegd(mtmp);
362             mread(fd, (genericptr_t) EGD(mtmp), sizeof(struct egd));
363         }
364         /* epri - temple priest */
365         mread(fd, (genericptr_t) &buflen, sizeof(buflen));
366         if (buflen > 0) {
367             newepri(mtmp);
368             mread(fd, (genericptr_t) EPRI(mtmp), sizeof(struct epri));
369         }
370         /* eshk - shopkeeper */
371         mread(fd, (genericptr_t) &buflen, sizeof(buflen));
372         if (buflen > 0) {
373             neweshk(mtmp);
374             mread(fd, (genericptr_t) ESHK(mtmp), sizeof(struct eshk));
375         }
376         /* emin - minion */
377         mread(fd, (genericptr_t) &buflen, sizeof(buflen));
378         if (buflen > 0) {
379             newemin(mtmp);
380             mread(fd, (genericptr_t) EMIN(mtmp), sizeof(struct emin));
381         }
382         /* edog - pet */
383         mread(fd, (genericptr_t) &buflen, sizeof(buflen));
384         if (buflen > 0) {
385             newedog(mtmp);
386             mread(fd, (genericptr_t) EDOG(mtmp), sizeof(struct edog));
387         }
388         /* mcorpsenm - obj->corpsenm for mimic posing as corpse or
389            statue (inline int rather than pointer to something) */
390         mread(fd, (genericptr_t) &MCORPSENM(mtmp), sizeof MCORPSENM(mtmp));
391     } /* mextra */
392 }
393
394 STATIC_OVL struct monst *
395 restmonchn(fd, ghostly)
396 register int fd;
397 boolean ghostly;
398 {
399     register struct monst *mtmp, *mtmp2 = 0;
400     register struct monst *first = (struct monst *) 0;
401     int offset, buflen;
402
403     while (1) {
404         mread(fd, (genericptr_t) &buflen, sizeof(buflen));
405         if (buflen == -1)
406             break;
407
408         mtmp = newmonst();
409         restmon(fd, mtmp);
410         if (!first)
411             first = mtmp;
412         else
413             mtmp2->nmon = mtmp;
414
415         if (ghostly) {
416             unsigned nid = context.ident++;
417             add_id_mapping(mtmp->m_id, nid);
418             mtmp->m_id = nid;
419         }
420         offset = mtmp->mnum;
421         mtmp->data = &mons[offset];
422         if (ghostly) {
423             int mndx = monsndx(mtmp->data);
424             if (propagate(mndx, TRUE, ghostly) == 0) {
425                 /* cookie to trigger purge in getbones() */
426                 mtmp->mhpmax = DEFUNCT_MONSTER;
427             }
428         }
429         if (mtmp->minvent) {
430             struct obj *obj;
431             mtmp->minvent = restobjchn(fd, ghostly, FALSE);
432             /* restore monster back pointer */
433             for (obj = mtmp->minvent; obj; obj = obj->nobj)
434                 obj->ocarry = mtmp;
435         }
436         if (mtmp->mw) {
437             struct obj *obj;
438
439             for (obj = mtmp->minvent; obj; obj = obj->nobj)
440                 if (obj->owornmask & W_WEP)
441                     break;
442             if (obj)
443                 mtmp->mw = obj;
444             else {
445                 MON_NOWEP(mtmp);
446                 impossible("bad monster weapon restore");
447             }
448         }
449
450         if (mtmp->isshk)
451             restshk(mtmp, ghostly);
452         if (mtmp->ispriest)
453             restpriest(mtmp, ghostly);
454
455         if (!ghostly) {
456             if (mtmp->m_id == context.polearm.m_id)
457                 context.polearm.hitmon = mtmp;
458         }
459         mtmp2 = mtmp;
460     }
461     if (first && mtmp2->nmon) {
462         impossible("Restmonchn: error reading monchn.");
463         mtmp2->nmon = 0;
464     }
465     return first;
466 }
467
468 STATIC_OVL struct fruit *
469 loadfruitchn(fd)
470 int fd;
471 {
472     register struct fruit *flist, *fnext;
473
474     flist = 0;
475     while (fnext = newfruit(), mread(fd, (genericptr_t) fnext, sizeof *fnext),
476            fnext->fid != 0) {
477         fnext->nextf = flist;
478         flist = fnext;
479     }
480     dealloc_fruit(fnext);
481     return flist;
482 }
483
484 STATIC_OVL void
485 freefruitchn(flist)
486 register struct fruit *flist;
487 {
488     register struct fruit *fnext;
489
490     while (flist) {
491         fnext = flist->nextf;
492         dealloc_fruit(flist);
493         flist = fnext;
494     }
495 }
496
497 STATIC_OVL void
498 ghostfruit(otmp)
499 register struct obj *otmp;
500 {
501     register struct fruit *oldf;
502
503     for (oldf = oldfruit; oldf; oldf = oldf->nextf)
504         if (oldf->fid == otmp->spe)
505             break;
506
507     if (!oldf)
508         impossible("no old fruit?");
509     else
510         otmp->spe = fruitadd(oldf->fname, (struct fruit *) 0);
511 }
512
513 #ifdef SYSCF
514 #define SYSOPT_CHECK_SAVE_UID sysopt.check_save_uid
515 #else
516 #define SYSOPT_CHECK_SAVE_UID TRUE
517 #endif
518
519 STATIC_OVL
520 boolean
521 restgamestate(fd, stuckid, steedid)
522 register int fd;
523 unsigned int *stuckid, *steedid;
524 {
525     struct flag newgameflags;
526 #ifdef SYSFLAGS
527     struct sysflag newgamesysflags;
528 #endif
529     struct obj *otmp, *tmp_bc;
530     char timebuf[15];
531     unsigned long uid;
532
533     mread(fd, (genericptr_t) &uid, sizeof uid);
534     if (SYSOPT_CHECK_SAVE_UID
535         && uid != (unsigned long) getuid()) { /* strange ... */
536         /* for wizard mode, issue a reminder; for others, treat it
537            as an attempt to cheat and refuse to restore this file */
538 /*JP
539         pline("Saved game was not yours.");
540 */
541         pline("\83Z\81[\83u\82³\82ê\82½\83Q\81[\83\80\82Í\82 \82È\82½\82Ì\82à\82Ì\82Å\82Í\82È\82¢\81D");
542         if (!wizard)
543             return FALSE;
544     }
545     mread(fd, (genericptr_t) &context, sizeof(struct context_info));
546     if (context.warntype.speciesidx >= LOW_PM)
547         context.warntype.species = &mons[context.warntype.speciesidx];
548
549     /* we want to be able to revert to command line/environment/config
550        file option values instead of keeping old save file option values
551        if partial restore fails and we resort to starting a new game */
552     newgameflags = flags;
553     mread(fd, (genericptr_t) &flags, sizeof(struct flag));
554     /* wizard and discover are actually flags.debug and flags.explore;
555        player might be overriding the save file values for them;
556        in the discover case, we don't want to set that for a normal
557        game until after the save file has been removed */
558     iflags.deferred_X = (newgameflags.explore && !discover);
559     if (newgameflags.debug) {
560         /* authorized by startup code; wizard mode exists and is allowed */
561         wizard = TRUE, discover = iflags.deferred_X = FALSE;
562     } else if (wizard) {
563         /* specified by save file; check authorization now */
564         set_playmode();
565     }
566 #ifdef SYSFLAGS
567     newgamesysflags = sysflags;
568     mread(fd, (genericptr_t) &sysflags, sizeof(struct sysflag));
569 #endif
570
571     role_init(); /* Reset the initial role, race, gender, and alignment */
572 #ifdef AMII_GRAPHICS
573     amii_setpens(amii_numcolors); /* use colors from save file */
574 #endif
575     mread(fd, (genericptr_t) &u, sizeof(struct you));
576
577 #define ReadTimebuf(foo)                   \
578     mread(fd, (genericptr_t) timebuf, 14); \
579     timebuf[14] = '\0';                    \
580     foo = time_from_yyyymmddhhmmss(timebuf);
581
582     ReadTimebuf(ubirthday);
583     mread(fd, &urealtime.realtime, sizeof urealtime.realtime);
584     ReadTimebuf(urealtime.start_timing); /** [not used] **/
585     /* current time is the time to use for next urealtime.realtime update */
586     urealtime.start_timing = getnow();
587
588     set_uasmon();
589 #ifdef CLIPPING
590     cliparound(u.ux, u.uy);
591 #endif
592     if (u.uhp <= 0 && (!Upolyd || u.mh <= 0)) {
593         u.ux = u.uy = 0; /* affects pline() [hence You()] */
594 #if 0 /*JP*/
595         You("were not healthy enough to survive restoration.");
596 #else
597         You("\8dÄ\8aJ\82Å\82«\82é\82Ù\82Ç\8c\92\8dN\82Å\82Í\82È\82©\82Á\82½\81D");
598 #endif
599         /* wiz1_level.dlevel is used by mklev.c to see if lots of stuff is
600          * uninitialized, so we only have to set it and not the other stuff.
601          */
602         wiz1_level.dlevel = 0;
603         u.uz.dnum = 0;
604         u.uz.dlevel = 1;
605         /* revert to pre-restore option settings */
606         iflags.deferred_X = FALSE;
607         flags = newgameflags;
608 #ifdef SYSFLAGS
609         sysflags = newgamesysflags;
610 #endif
611         return FALSE;
612     }
613     /* in case hangup save occurred in midst of level change */
614     assign_level(&u.uz0, &u.uz);
615
616     /* this stuff comes after potential aborted restore attempts */
617     restore_killers(fd);
618     restore_timers(fd, RANGE_GLOBAL, FALSE, 0L);
619     restore_light_sources(fd);
620     invent = restobjchn(fd, FALSE, FALSE);
621     /* tmp_bc only gets set here if the ball & chain were orphaned
622        because you were swallowed; otherwise they will be on the floor
623        or in your inventory */
624     tmp_bc = restobjchn(fd, FALSE, FALSE);
625     if (tmp_bc) {
626         for (otmp = tmp_bc; otmp; otmp = otmp->nobj) {
627             if (otmp->owornmask)
628                 setworn(otmp, otmp->owornmask);
629         }
630         if (!uball || !uchain)
631             impossible("restgamestate: lost ball & chain");
632     }
633
634     migrating_objs = restobjchn(fd, FALSE, FALSE);
635     migrating_mons = restmonchn(fd, FALSE);
636     mread(fd, (genericptr_t) mvitals, sizeof(mvitals));
637
638     /*
639      * There are some things after this that can have unintended display
640      * side-effects too early in the game.
641      * Disable see_monsters() here, re-enable it at the top of moveloop()
642      */
643     defer_see_monsters = TRUE;
644
645     /* this comes after inventory has been loaded */
646     for (otmp = invent; otmp; otmp = otmp->nobj)
647         if (otmp->owornmask)
648             setworn(otmp, otmp->owornmask);
649     /* reset weapon so that player will get a reminder about "bashing"
650        during next fight when bare-handed or wielding an unconventional
651        item; for pick-axe, we aren't able to distinguish between having
652        applied or wielded it, so be conservative and assume the former */
653     otmp = uwep;   /* `uwep' usually init'd by setworn() in loop above */
654     uwep = 0;      /* clear it and have setuwep() reinit */
655     setuwep(otmp); /* (don't need any null check here) */
656     if (!uwep || uwep->otyp == PICK_AXE || uwep->otyp == GRAPPLING_HOOK)
657         unweapon = TRUE;
658
659     restore_dungeon(fd);
660     restlevchn(fd);
661     mread(fd, (genericptr_t) &moves, sizeof moves);
662     mread(fd, (genericptr_t) &monstermoves, sizeof monstermoves);
663     mread(fd, (genericptr_t) &quest_status, sizeof(struct q_score));
664     mread(fd, (genericptr_t) spl_book, sizeof(struct spell) * (MAXSPELL + 1));
665     restore_artifacts(fd);
666     restore_oracles(fd);
667     if (u.ustuck)
668         mread(fd, (genericptr_t) stuckid, sizeof(*stuckid));
669     if (u.usteed)
670         mread(fd, (genericptr_t) steedid, sizeof(*steedid));
671     mread(fd, (genericptr_t) pl_character, sizeof pl_character);
672
673     mread(fd, (genericptr_t) pl_fruit, sizeof pl_fruit);
674     freefruitchn(ffruit); /* clean up fruit(s) made by initoptions() */
675     ffruit = loadfruitchn(fd);
676
677     restnames(fd);
678     restore_waterlevel(fd);
679     restore_msghistory(fd);
680     /* must come after all mons & objs are restored */
681     relink_timers(FALSE);
682     relink_light_sources(FALSE);
683     return TRUE;
684 }
685
686 /* update game state pointers to those valid for the current level (so we
687  * don't dereference a wild u.ustuck when saving the game state, for instance)
688  */
689 STATIC_OVL void
690 restlevelstate(stuckid, steedid)
691 unsigned int stuckid, steedid;
692 {
693     register struct monst *mtmp;
694
695     if (stuckid) {
696         for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
697             if (mtmp->m_id == stuckid)
698                 break;
699         if (!mtmp)
700             panic("Cannot find the monster ustuck.");
701         u.ustuck = mtmp;
702     }
703     if (steedid) {
704         for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
705             if (mtmp->m_id == steedid)
706                 break;
707         if (!mtmp)
708             panic("Cannot find the monster usteed.");
709         u.usteed = mtmp;
710         remove_monster(mtmp->mx, mtmp->my);
711     }
712 }
713
714 /*ARGSUSED*/
715 STATIC_OVL int
716 restlevelfile(fd, ltmp)
717 int fd; /* fd used in MFLOPPY only */
718 xchar ltmp;
719 {
720     int nfd;
721     char whynot[BUFSZ];
722
723 #ifndef MFLOPPY
724     nhUse(fd);
725 #endif
726     nfd = create_levelfile(ltmp, whynot);
727     if (nfd < 0) {
728         /* BUG: should suppress any attempt to write a panic
729            save file if file creation is now failing... */
730         panic("restlevelfile: %s", whynot);
731     }
732 #ifdef MFLOPPY
733     if (!savelev(nfd, ltmp, COUNT_SAVE)) {
734         /* The savelev can't proceed because the size required
735          * is greater than the available disk space.
736          */
737 /*JP
738         pline("Not enough space on `%s' to restore your game.", levels);
739 */
740         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);
741
742         /* Remove levels and bones that may have been created.
743          */
744         (void) nhclose(nfd);
745 #ifdef AMIGA
746         clearlocks();
747 #else /* !AMIGA */
748         eraseall(levels, alllevels);
749         eraseall(levels, allbones);
750
751         /* Perhaps the person would like to play without a
752          * RAMdisk.
753          */
754         if (ramdisk) {
755             /* PlaywoRAMdisk may not return, but if it does
756              * it is certain that ramdisk will be 0.
757              */
758             playwoRAMdisk();
759             /* Rewind save file and try again */
760             (void) lseek(fd, (off_t) 0, 0);
761             (void) validate(fd, (char *) 0); /* skip version etc */
762             return dorecover(fd);            /* 0 or 1 */
763         }
764 #endif /* ?AMIGA */
765 /*JP
766         pline("Be seeing you...");
767 */
768         pline("\82Ü\82½\89ï\82¢\82Ü\82µ\82å\82¤\81D\81D\81D");
769         nh_terminate(EXIT_SUCCESS);
770     }
771 #endif /* MFLOPPY */
772     bufon(nfd);
773     savelev(nfd, ltmp, WRITE_SAVE | FREE_SAVE);
774     bclose(nfd);
775     return 2;
776 }
777
778 int
779 dorecover(fd)
780 register int fd;
781 {
782     unsigned int stuckid = 0, steedid = 0; /* not a register */
783     xchar ltmp;
784     int rtmp;
785     struct obj *otmp;
786
787     restoring = TRUE;
788     get_plname_from_file(fd, plname);
789     getlev(fd, 0, (xchar) 0, FALSE);
790     if (!restgamestate(fd, &stuckid, &steedid)) {
791         display_nhwindow(WIN_MESSAGE, TRUE);
792         savelev(-1, 0, FREE_SAVE); /* discard current level */
793         (void) nhclose(fd);
794         (void) delete_savefile();
795         restoring = FALSE;
796         return 0;
797     }
798     restlevelstate(stuckid, steedid);
799 #ifdef INSURANCE
800     savestateinlock();
801 #endif
802     rtmp = restlevelfile(fd, ledger_no(&u.uz));
803     if (rtmp < 2)
804         return rtmp; /* dorecover called recursively */
805
806     /* these pointers won't be valid while we're processing the
807      * other levels, but they'll be reset again by restlevelstate()
808      * afterwards, and in the meantime at least u.usteed may mislead
809      * place_monster() on other levels
810      */
811     u.ustuck = (struct monst *) 0;
812     u.usteed = (struct monst *) 0;
813
814 #ifdef MICRO
815 #ifdef AMII_GRAPHICS
816     {
817         extern struct window_procs amii_procs;
818         if (windowprocs.win_init_nhwindows == amii_procs.win_init_nhwindows) {
819             extern winid WIN_BASE;
820             clear_nhwindow(WIN_BASE); /* hack until there's a hook for this */
821         }
822     }
823 #else
824         clear_nhwindow(WIN_MAP);
825 #endif
826     clear_nhwindow(WIN_MESSAGE);
827 #if 0 /*JP*/
828     You("return to level %d in %s%s.", depth(&u.uz),
829         dungeons[u.uz.dnum].dname,
830         flags.debug ? " while in debug mode"
831                     : flags.explore ? " while in explore mode" : "");
832 #else
833     You("%s%s\82Ì\92n\89º%d\8aK\82É\96ß\82Á\82Ä\82«\82½\81D",
834         flags.debug ? "\83E\83B\83U\81[\83h\83\82\81[\83h\92\86\82Ì"
835                     : flags.explore ? "\92T\8c\9f\83\82\81[\83h\92\86\82Ì" : "",
836         dungeons[u.uz.dnum].dname, depth(&u.uz));
837 #endif
838     curs(WIN_MAP, 1, 1);
839     dotcnt = 0;
840     dotrow = 2;
841     if (strncmpi("X11", windowprocs.name, 3))
842         putstr(WIN_MAP, 0, "Restoring:");
843 #endif
844     restoreprocs.mread_flags = 1; /* return despite error */
845     while (1) {
846         mread(fd, (genericptr_t) &ltmp, sizeof ltmp);
847         if (restoreprocs.mread_flags == -1)
848             break;
849         getlev(fd, 0, ltmp, FALSE);
850 #ifdef MICRO
851         curs(WIN_MAP, 1 + dotcnt++, dotrow);
852         if (dotcnt >= (COLNO - 1)) {
853             dotrow++;
854             dotcnt = 0;
855         }
856         if (strncmpi("X11", windowprocs.name, 3)) {
857             putstr(WIN_MAP, 0, ".");
858         }
859         mark_synch();
860 #endif
861         rtmp = restlevelfile(fd, ltmp);
862         if (rtmp < 2)
863             return rtmp; /* dorecover called recursively */
864     }
865     restoreprocs.mread_flags = 0;
866
867 #ifdef BSD
868     (void) lseek(fd, 0L, 0);
869 #else
870     (void) lseek(fd, (off_t) 0, 0);
871 #endif
872     (void) validate(fd, (char *) 0); /* skip version and savefile info */
873     get_plname_from_file(fd, plname);
874
875     getlev(fd, 0, (xchar) 0, FALSE);
876     (void) nhclose(fd);
877
878     /* Now set the restore settings to match the
879      * settings used by the save file output routines
880      */
881     reset_restpref();
882
883     restlevelstate(stuckid, steedid);
884     program_state.something_worth_saving = 1; /* useful data now exists */
885
886     if (!wizard && !discover)
887         (void) delete_savefile();
888     if (Is_rogue_level(&u.uz))
889         assign_graphics(ROGUESET);
890 #ifdef USE_TILES
891     substitute_tiles(&u.uz);
892 #endif
893 #ifdef MFLOPPY
894     gameDiskPrompt();
895 #endif
896     max_rank_sz(); /* to recompute mrank_sz (botl.c) */
897     /* take care of iron ball & chain */
898     for (otmp = fobj; otmp; otmp = otmp->nobj)
899         if (otmp->owornmask)
900             setworn(otmp, otmp->owornmask);
901
902     /* in_use processing must be after:
903      *    + The inventory has been read so that freeinv() works.
904      *    + The current level has been restored so billing information
905      *      is available.
906      */
907     inven_inuse(FALSE);
908
909     load_qtlist(); /* re-load the quest text info */
910     /* Set up the vision internals, after levl[] data is loaded
911        but before docrt(). */
912     reglyph_darkroom();
913     vision_reset();
914     vision_full_recalc = 1; /* recompute vision (not saved) */
915
916     run_timers(); /* expire all timers that have gone off while away */
917     docrt();
918     restoring = FALSE;
919     clear_nhwindow(WIN_MESSAGE);
920
921     /* Success! */
922     welcome(FALSE);
923     check_special_room(FALSE);
924     return 1;
925 }
926
927 void
928 restcemetery(fd, cemeteryaddr)
929 int fd;
930 struct cemetery **cemeteryaddr;
931 {
932     struct cemetery *bonesinfo, **bonesaddr;
933     int flag;
934
935     mread(fd, (genericptr_t) &flag, sizeof flag);
936     if (flag == 0) {
937         bonesaddr = cemeteryaddr;
938         do {
939             bonesinfo = (struct cemetery *) alloc(sizeof *bonesinfo);
940             mread(fd, (genericptr_t) bonesinfo, sizeof *bonesinfo);
941             *bonesaddr = bonesinfo;
942             bonesaddr = &(*bonesaddr)->next;
943         } while (*bonesaddr);
944     } else {
945         *cemeteryaddr = 0;
946     }
947 }
948
949 /*ARGSUSED*/
950 STATIC_OVL void
951 rest_levl(fd, rlecomp)
952 int fd;
953 boolean rlecomp;
954 {
955 #ifdef RLECOMP
956     short i, j;
957     uchar len;
958     struct rm r;
959
960     if (rlecomp) {
961         (void) memset((genericptr_t) &r, 0, sizeof(r));
962         i = 0;
963         j = 0;
964         len = 0;
965         while (i < ROWNO) {
966             while (j < COLNO) {
967                 if (len > 0) {
968                     levl[j][i] = r;
969                     len -= 1;
970                     j += 1;
971                 } else {
972                     mread(fd, (genericptr_t) &len, sizeof(uchar));
973                     mread(fd, (genericptr_t) &r, sizeof(struct rm));
974                 }
975             }
976             j = 0;
977             i += 1;
978         }
979         return;
980     }
981 #else /* !RLECOMP */
982     nhUse(rlecomp);
983 #endif /* ?RLECOMP */
984     mread(fd, (genericptr_t) levl, sizeof levl);
985 }
986
987 void
988 trickery(reason)
989 char *reason;
990 {
991 /*JP
992     pline("Strange, this map is not as I remember it.");
993 */
994     pline("\96­\82¾\81C\82±\82Ì\92n\90}\82Í\8e\84\82ª\8ao\82¦\82Ä\82¢\82½\82à\82Ì\82Æ\88á\82¤\81D");
995 /*JP
996     pline("Somebody is trying some trickery here...");
997 */
998     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");
999 /*JP
1000     pline("This game is void.");
1001 */
1002     pline("\82±\82Ì\83Q\81[\83\80\82Í\96³\8cø\82Æ\82È\82é\81D");
1003     Strcpy(killer.name, reason ? reason : "");
1004     done(TRICKED);
1005 }
1006
1007 void
1008 getlev(fd, pid, lev, ghostly)
1009 int fd, pid;
1010 xchar lev;
1011 boolean ghostly;
1012 {
1013     register struct trap *trap;
1014     register struct monst *mtmp;
1015     long elapsed;
1016     branch *br;
1017     int hpid;
1018     xchar dlvl;
1019     int x, y;
1020 #ifdef TOS
1021     short tlev;
1022 #endif
1023
1024     if (ghostly)
1025         clear_id_mapping();
1026
1027 #if defined(MSDOS) || defined(OS2)
1028     setmode(fd, O_BINARY);
1029 #endif
1030     /* Load the old fruit info.  We have to do it first, so the
1031      * information is available when restoring the objects.
1032      */
1033     if (ghostly)
1034         oldfruit = loadfruitchn(fd);
1035
1036     /* First some sanity checks */
1037     mread(fd, (genericptr_t) &hpid, sizeof(hpid));
1038 /* CHECK:  This may prevent restoration */
1039 #ifdef TOS
1040     mread(fd, (genericptr_t) &tlev, sizeof(tlev));
1041     dlvl = tlev & 0x00ff;
1042 #else
1043     mread(fd, (genericptr_t) &dlvl, sizeof(dlvl));
1044 #endif
1045     if ((pid && pid != hpid) || (lev && dlvl != lev)) {
1046         char trickbuf[BUFSZ];
1047
1048         if (pid && pid != hpid)
1049             Sprintf(trickbuf, "PID (%d) doesn't match saved PID (%d)!", hpid,
1050                     pid);
1051         else
1052             Sprintf(trickbuf, "This is level %d, not %d!", dlvl, lev);
1053         if (wizard)
1054             pline1(trickbuf);
1055         trickery(trickbuf);
1056     }
1057     restcemetery(fd, &level.bonesinfo);
1058     rest_levl(fd,
1059               (boolean) ((sfrestinfo.sfi1 & SFI1_RLECOMP) == SFI1_RLECOMP));
1060     mread(fd, (genericptr_t) lastseentyp, sizeof(lastseentyp));
1061     mread(fd, (genericptr_t) &omoves, sizeof(omoves));
1062     elapsed = monstermoves - omoves;
1063     mread(fd, (genericptr_t) &upstair, sizeof(stairway));
1064     mread(fd, (genericptr_t) &dnstair, sizeof(stairway));
1065     mread(fd, (genericptr_t) &upladder, sizeof(stairway));
1066     mread(fd, (genericptr_t) &dnladder, sizeof(stairway));
1067     mread(fd, (genericptr_t) &sstairs, sizeof(stairway));
1068     mread(fd, (genericptr_t) &updest, sizeof(dest_area));
1069     mread(fd, (genericptr_t) &dndest, sizeof(dest_area));
1070     mread(fd, (genericptr_t) &level.flags, sizeof(level.flags));
1071     mread(fd, (genericptr_t) doors, sizeof(doors));
1072     rest_rooms(fd); /* No joke :-) */
1073     if (nroom)
1074         doorindex = rooms[nroom - 1].fdoor + rooms[nroom - 1].doorct;
1075     else
1076         doorindex = 0;
1077
1078     restore_timers(fd, RANGE_LEVEL, ghostly, elapsed);
1079     restore_light_sources(fd);
1080     fmon = restmonchn(fd, ghostly);
1081
1082     rest_worm(fd); /* restore worm information */
1083     ftrap = 0;
1084     while (trap = newtrap(),
1085            mread(fd, (genericptr_t) trap, sizeof(struct trap)),
1086            trap->tx != 0) { /* need "!= 0" to work around DICE 3.0 bug */
1087         trap->ntrap = ftrap;
1088         ftrap = trap;
1089     }
1090     dealloc_trap(trap);
1091     fobj = restobjchn(fd, ghostly, FALSE);
1092     find_lev_obj();
1093     /* restobjchn()'s `frozen' argument probably ought to be a callback
1094        routine so that we can check for objects being buried under ice */
1095     level.buriedobjlist = restobjchn(fd, ghostly, FALSE);
1096     billobjs = restobjchn(fd, ghostly, FALSE);
1097     rest_engravings(fd);
1098
1099     /* reset level.monsters for new level */
1100     for (x = 0; x < COLNO; x++)
1101         for (y = 0; y < ROWNO; y++)
1102             level.monsters[x][y] = (struct monst *) 0;
1103     for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
1104         if (mtmp->isshk)
1105             set_residency(mtmp, FALSE);
1106         place_monster(mtmp, mtmp->mx, mtmp->my);
1107         if (mtmp->wormno)
1108             place_wsegs(mtmp);
1109
1110         /* regenerate monsters while on another level */
1111         if (!u.uz.dlevel)
1112             continue;
1113         if (ghostly) {
1114             /* reset peaceful/malign relative to new character;
1115                shopkeepers will reset based on name */
1116             if (!mtmp->isshk)
1117                 mtmp->mpeaceful =
1118                     (is_unicorn(mtmp->data)
1119                      && sgn(u.ualign.type) == sgn(mtmp->data->maligntyp))
1120                         ? TRUE
1121                         : peace_minded(mtmp->data);
1122             set_malign(mtmp);
1123         } else if (elapsed > 0L) {
1124             mon_catchup_elapsed_time(mtmp, elapsed);
1125         }
1126         /* update shape-changers in case protection against
1127            them is different now than when the level was saved */
1128         restore_cham(mtmp);
1129         /* give hiders a chance to hide before their next move */
1130         if (ghostly || elapsed > (long) rnd(10))
1131             hide_monst(mtmp);
1132     }
1133
1134     restdamage(fd, ghostly);
1135     rest_regions(fd, ghostly);
1136     if (ghostly) {
1137         /* Now get rid of all the temp fruits... */
1138         freefruitchn(oldfruit), oldfruit = 0;
1139
1140         if (lev > ledger_no(&medusa_level)
1141             && lev < ledger_no(&stronghold_level) && xdnstair == 0) {
1142             coord cc;
1143
1144             mazexy(&cc);
1145             xdnstair = cc.x;
1146             ydnstair = cc.y;
1147             levl[cc.x][cc.y].typ = STAIRS;
1148         }
1149
1150         br = Is_branchlev(&u.uz);
1151         if (br && u.uz.dlevel == 1) {
1152             d_level ltmp;
1153
1154             if (on_level(&u.uz, &br->end1))
1155                 assign_level(&ltmp, &br->end2);
1156             else
1157                 assign_level(&ltmp, &br->end1);
1158
1159             switch (br->type) {
1160             case BR_STAIR:
1161             case BR_NO_END1:
1162             case BR_NO_END2: /* OK to assign to sstairs if it's not used */
1163                 assign_level(&sstairs.tolev, &ltmp);
1164                 break;
1165             case BR_PORTAL: /* max of 1 portal per level */
1166                 for (trap = ftrap; trap; trap = trap->ntrap)
1167                     if (trap->ttyp == MAGIC_PORTAL)
1168                         break;
1169                 if (!trap)
1170                     panic("getlev: need portal but none found");
1171                 assign_level(&trap->dst, &ltmp);
1172                 break;
1173             }
1174         } else if (!br) {
1175             struct trap *ttmp = 0;
1176
1177             /* Remove any dangling portals. */
1178             for (trap = ftrap; trap; trap = ttmp) {
1179                 ttmp = trap->ntrap;
1180                 if (trap->ttyp == MAGIC_PORTAL)
1181                     deltrap(trap);
1182             }
1183         }
1184     }
1185
1186     /* must come after all mons & objs are restored */
1187     relink_timers(ghostly);
1188     relink_light_sources(ghostly);
1189     reset_oattached_mids(ghostly);
1190
1191     if (ghostly)
1192         clear_id_mapping();
1193 }
1194
1195 void
1196 get_plname_from_file(fd, plbuf)
1197 int fd;
1198 char *plbuf;
1199 {
1200     int pltmpsiz = 0;
1201 _pragma_ignore(-Wunused-result)
1202     (void) read(fd, (genericptr_t) &pltmpsiz, sizeof(pltmpsiz));
1203     (void) read(fd, (genericptr_t) plbuf, pltmpsiz);
1204 _pragma_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*/