OSDN Git Service

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