1 /* NetHack 3.6 restore.c $NHDT-Date: 1451082255 2015/12/25 22:24:15 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.103 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
7 #include "tcap.h" /* for TERMLIB and ASCIIGRAPH */
10 extern int dotcnt
; /* shared with save */
11 extern int dotrow
; /* shared with save */
15 extern void FDECL(substitute_tiles
, (d_level
*)); /* from tile.c */
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
);
24 STATIC_DCL
void NDECL(def_minit
);
25 STATIC_DCL
void FDECL(def_mread
, (int, genericptr_t
, unsigned int));
27 STATIC_DCL
void NDECL(find_lev_obj
);
28 STATIC_DCL
void FDECL(restlevchn
, (int));
29 STATIC_DCL
void FDECL(restdamage
, (int, BOOLEAN_P
));
30 STATIC_DCL
void FDECL(restobj
, (int, struct obj
*));
31 STATIC_DCL
struct obj
*FDECL(restobjchn
, (int, BOOLEAN_P
, BOOLEAN_P
));
32 STATIC_OVL
void FDECL(restmon
, (int, struct monst
*));
33 STATIC_DCL
struct monst
*FDECL(restmonchn
, (int, BOOLEAN_P
));
34 STATIC_DCL
struct fruit
*FDECL(loadfruitchn
, (int));
35 STATIC_DCL
void FDECL(freefruitchn
, (struct fruit
*));
36 STATIC_DCL
void FDECL(ghostfruit
, (struct obj
*));
38 FDECL(restgamestate
, (int, unsigned int *, unsigned int *));
39 STATIC_DCL
void FDECL(restlevelstate
, (unsigned int, unsigned int));
40 STATIC_DCL
int FDECL(restlevelfile
, (int, XCHAR_P
));
41 STATIC_OVL
void FDECL(restore_msghistory
, (int));
42 STATIC_DCL
void FDECL(reset_oattached_mids
, (BOOLEAN_P
));
43 STATIC_DCL
void FDECL(rest_levl
, (int, BOOLEAN_P
));
45 static struct restore_procs
{
48 void NDECL((*restore_minit
));
49 void FDECL((*restore_mread
), (int, genericptr_t
, unsigned int));
50 void FDECL((*restore_bclose
), (int));
52 #if !defined(ZEROCOMP) || (defined(COMPRESS) || defined(ZLIB_COMP))
53 "externalcomp", 0, def_minit
, def_mread
, def_bclose
,
55 "zerocomp", 0, zerocomp_minit
, zerocomp_mread
, zerocomp_bclose
,
60 * Save a mapping of IDs from ghost levels to the current level. This
61 * map is used by the timer routines when restoring ghost levels.
63 #define N_PER_BUCKET 64
67 unsigned gid
; /* ghost ID */
68 unsigned nid
; /* new ID */
72 STATIC_DCL
void NDECL(clear_id_mapping
);
73 STATIC_DCL
void FDECL(add_id_mapping
, (unsigned, unsigned));
75 static int n_ids_mapped
= 0;
76 static struct bucket
*id_map
= 0;
79 void FDECL(amii_setpens
, (int)); /* use colors from save file */
80 extern int amii_numcolors
;
85 boolean restoring
= FALSE
;
86 static NEARDATA
struct fruit
*oldfruit
;
87 static NEARDATA
long omoves
;
89 #define Is_IceBox(o) ((o)->otyp == ICE_BOX ? TRUE : FALSE)
91 /* Recalculate level.objects[x][y], since this info was not saved. */
95 register struct obj
*fobjtmp
= (struct obj
*) 0;
96 register struct obj
*otmp
;
99 for (x
= 0; x
< COLNO
; x
++)
100 for (y
= 0; y
< ROWNO
; y
++)
101 level
.objects
[x
][y
] = (struct obj
*) 0;
104 * Reverse the entire fobj chain, which is necessary so that we can
105 * place the objects in the proper order. Make all obj in chain
106 * OBJ_FREE so place_object will work correctly.
108 while ((otmp
= fobj
) != 0) {
110 otmp
->nobj
= fobjtmp
;
111 otmp
->where
= OBJ_FREE
;
114 /* fobj should now be empty */
116 /* Set level.objects (as well as reversing the chain back again) */
117 while ((otmp
= fobjtmp
) != 0) {
118 fobjtmp
= otmp
->nobj
;
119 place_object(otmp
, otmp
->ox
, otmp
->oy
);
123 /* Things that were marked "in_use" when the game was saved (ex. via the
124 * infamous "HUP" cheat) get used up here.
130 register struct obj
*otmp
, *otmp2
;
132 for (otmp
= invent
; otmp
; otmp
= otmp2
) {
136 pline("Finishing off %s...", xname(otmp
));
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
));
157 for (x
= sp_levchn
; x
->next
; x
= x
->next
)
161 tmplev
->next
= (s_level
*) 0;
166 restdamage(fd
, ghostly
)
171 struct damage
*tmp_dam
;
173 mread(fd
, (genericptr_t
) &counter
, sizeof(counter
));
176 tmp_dam
= (struct damage
*) alloc(sizeof(struct damage
));
177 while (--counter
>= 0) {
178 char damaged_shops
[5], *shp
= (char *) 0;
180 mread(fd
, (genericptr_t
) tmp_dam
, sizeof(*tmp_dam
));
182 tmp_dam
->when
+= (monstermoves
- omoves
);
183 Strcpy(damaged_shops
,
184 in_rooms(tmp_dam
->place
.x
, tmp_dam
->place
.y
, SHOPBASE
));
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
191 for (shp
= damaged_shops
; *shp
; shp
++) {
192 struct monst
*shkp
= shop_keeper(*shp
);
194 if (shkp
&& inhishop(shkp
)
195 && repair_damage(shkp
, tmp_dam
, TRUE
))
200 tmp_dam
->next
= level
.damagelist
;
201 level
.damagelist
= tmp_dam
;
202 tmp_dam
= (struct damage
*) alloc(sizeof(*tmp_dam
));
205 free((genericptr_t
) tmp_dam
);
208 /* restore one object */
216 mread(fd
, (genericptr_t
) otmp
, sizeof(struct obj
));
218 /* next object pointers are invalid; otmp->cobj needs to be left
219 as is--being non-null is key to restoring container contents */
220 otmp
->nobj
= otmp
->nexthere
= (struct obj
*) 0;
221 /* non-null oextra needs to be reconstructed */
223 otmp
->oextra
= newoextra();
225 /* oname - object's name */
226 mread(fd
, (genericptr_t
) &buflen
, sizeof(buflen
));
227 if (buflen
> 0) { /* includes terminating '\0' */
228 new_oname(otmp
, buflen
);
229 mread(fd
, (genericptr_t
) ONAME(otmp
), buflen
);
231 /* omonst - corpse or statue might retain full monster details */
232 mread(fd
, (genericptr_t
) &buflen
, sizeof(buflen
));
235 /* this is actually a monst struct, so we
236 can just defer to restmon() here */
237 restmon(fd
, OMONST(otmp
));
239 /* omid - monster id number, connecting corpse to ghost */
240 mread(fd
, (genericptr_t
) &buflen
, sizeof(buflen
));
243 mread(fd
, (genericptr_t
) OMID(otmp
), buflen
);
245 /* olong - temporary gold */
246 mread(fd
, (genericptr_t
) &buflen
, sizeof(buflen
));
249 mread(fd
, (genericptr_t
) OLONG(otmp
), buflen
);
251 /* omailcmd - feedback mechanism for scroll of mail */
252 mread(fd
, (genericptr_t
) &buflen
, sizeof(buflen
));
254 char *omailcmd
= (char *) alloc(buflen
);
256 mread(fd
, (genericptr_t
) omailcmd
, buflen
);
257 new_omailcmd(otmp
, omailcmd
);
258 free((genericptr_t
) omailcmd
);
263 STATIC_OVL
struct obj
*
264 restobjchn(fd
, ghostly
, frozen
)
266 boolean ghostly
, frozen
;
268 register struct obj
*otmp
, *otmp2
= 0;
269 register struct obj
*first
= (struct obj
*) 0;
273 mread(fd
, (genericptr_t
) &buflen
, sizeof buflen
);
285 unsigned nid
= context
.ident
++;
286 add_id_mapping(otmp
->o_id
, nid
);
289 if (ghostly
&& otmp
->otyp
== SLIME_MOLD
)
291 /* Ghost levels get object age shifted from old player's clock
292 * to new player's clock. Assumption: new player arrived
293 * immediately after old player died.
295 if (ghostly
&& !frozen
&& !age_is_relative(otmp
))
296 otmp
->age
= monstermoves
- omoves
+ otmp
->age
;
298 /* get contents of a container or statue */
299 if (Has_contents(otmp
)) {
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
;
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
;
319 if (first
&& otmp2
->nobj
) {
320 impossible("Restobjchn: error reading objchn.");
327 /* restore one monster */
335 mread(fd
, (genericptr_t
) mtmp
, sizeof(struct monst
));
337 /* next monster pointer is invalid */
338 mtmp
->nmon
= (struct monst
*) 0;
339 /* non-null mextra needs to be reconstructed */
341 mtmp
->mextra
= newmextra();
343 /* mname - monster's name */
344 mread(fd
, (genericptr_t
) &buflen
, sizeof(buflen
));
345 if (buflen
> 0) { /* includes terminating '\0' */
346 new_mname(mtmp
, buflen
);
347 mread(fd
, (genericptr_t
) MNAME(mtmp
), buflen
);
349 /* egd - vault guard */
350 mread(fd
, (genericptr_t
) &buflen
, sizeof(buflen
));
353 mread(fd
, (genericptr_t
) EGD(mtmp
), sizeof(struct egd
));
355 /* epri - temple priest */
356 mread(fd
, (genericptr_t
) &buflen
, sizeof(buflen
));
359 mread(fd
, (genericptr_t
) EPRI(mtmp
), sizeof(struct epri
));
361 /* eshk - shopkeeper */
362 mread(fd
, (genericptr_t
) &buflen
, sizeof(buflen
));
365 mread(fd
, (genericptr_t
) ESHK(mtmp
), sizeof(struct eshk
));
368 mread(fd
, (genericptr_t
) &buflen
, sizeof(buflen
));
371 mread(fd
, (genericptr_t
) EMIN(mtmp
), sizeof(struct emin
));
374 mread(fd
, (genericptr_t
) &buflen
, sizeof(buflen
));
377 mread(fd
, (genericptr_t
) EDOG(mtmp
), sizeof(struct edog
));
379 /* mcorpsenm - obj->corpsenm for mimic posing as corpse or
380 statue (inline int rather than pointer to something) */
381 mread(fd
, (genericptr_t
) &MCORPSENM(mtmp
), sizeof MCORPSENM(mtmp
));
385 STATIC_OVL
struct monst
*
386 restmonchn(fd
, ghostly
)
390 register struct monst
*mtmp
, *mtmp2
= 0;
391 register struct monst
*first
= (struct monst
*) 0;
395 mread(fd
, (genericptr_t
) &buflen
, sizeof(buflen
));
407 unsigned nid
= context
.ident
++;
408 add_id_mapping(mtmp
->m_id
, nid
);
412 mtmp
->data
= &mons
[offset
];
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
;
422 mtmp
->minvent
= restobjchn(fd
, ghostly
, FALSE
);
423 /* restore monster back pointer */
424 for (obj
= mtmp
->minvent
; obj
; obj
= obj
->nobj
)
430 for (obj
= mtmp
->minvent
; obj
; obj
= obj
->nobj
)
431 if (obj
->owornmask
& W_WEP
)
437 impossible("bad monster weapon restore");
442 restshk(mtmp
, ghostly
);
444 restpriest(mtmp
, ghostly
);
447 if (mtmp
->m_id
== context
.polearm
.m_id
)
448 context
.polearm
.hitmon
= mtmp
;
452 if (first
&& mtmp2
->nmon
) {
453 impossible("Restmonchn: error reading monchn.");
459 STATIC_OVL
struct fruit
*
463 register struct fruit
*flist
, *fnext
;
466 while (fnext
= newfruit(), mread(fd
, (genericptr_t
) fnext
, sizeof *fnext
),
468 fnext
->nextf
= flist
;
471 dealloc_fruit(fnext
);
477 register struct fruit
*flist
;
479 register struct fruit
*fnext
;
482 fnext
= flist
->nextf
;
483 dealloc_fruit(flist
);
490 register struct obj
*otmp
;
492 register struct fruit
*oldf
;
494 for (oldf
= oldfruit
; oldf
; oldf
= oldf
->nextf
)
495 if (oldf
->fid
== otmp
->spe
)
499 impossible("no old fruit?");
501 otmp
->spe
= fruitadd(oldf
->fname
, (struct fruit
*) 0);
505 #define SYSOPT_CHECK_SAVE_UID sysopt.check_save_uid
507 #define SYSOPT_CHECK_SAVE_UID TRUE
512 restgamestate(fd
, stuckid
, steedid
)
514 unsigned int *stuckid
, *steedid
;
516 struct flag newgameflags
;
518 struct sysflag newgamesysflags
;
520 struct obj
*otmp
, *tmp_bc
;
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.");
533 mread(fd
, (genericptr_t
) &context
, sizeof(struct context_info
));
534 if (context
.warntype
.speciesidx
>= LOW_PM
)
535 context
.warntype
.species
= &mons
[context
.warntype
.speciesidx
];
537 /* we want to be able to revert to command line/environment/config
538 file option values instead of keeping old save file option values
539 if partial restore fails and we resort to starting a new game */
540 newgameflags
= flags
;
541 mread(fd
, (genericptr_t
) &flags
, sizeof(struct flag
));
542 /* wizard and discover are actually flags.debug and flags.explore;
543 player might be overriding the save file values for them;
544 in the discover case, we don't want to set that for a normal
545 game until after the save file has been removed */
546 iflags
.deferred_X
= (newgameflags
.explore
&& !discover
);
547 if (newgameflags
.debug
) {
548 /* authorized by startup code; wizard mode exists and is allowed */
549 wizard
= TRUE
, discover
= iflags
.deferred_X
= FALSE
;
551 /* specified by save file; check authorization now */
555 newgamesysflags
= sysflags
;
556 mread(fd
, (genericptr_t
) &sysflags
, sizeof(struct sysflag
));
559 role_init(); /* Reset the initial role, race, gender, and alignment */
561 amii_setpens(amii_numcolors
); /* use colors from save file */
563 mread(fd
, (genericptr_t
) &u
, sizeof(struct you
));
565 #define ReadTimebuf(foo) \
566 mread(fd, (genericptr_t) timebuf, 14); \
567 timebuf[14] = '\0'; \
568 foo = time_from_yyyymmddhhmmss(timebuf);
570 ReadTimebuf(ubirthday
);
571 mread(fd
, &urealtime
.realtime
, sizeof urealtime
.realtime
);
572 ReadTimebuf(urealtime
.start_timing
); /** [not used] **/
573 /* current time is the time to use for next urealtime.realtime update */
574 urealtime
.start_timing
= getnow();
578 cliparound(u
.ux
, u
.uy
);
580 if (u
.uhp
<= 0 && (!Upolyd
|| u
.mh
<= 0)) {
581 u
.ux
= u
.uy
= 0; /* affects pline() [hence You()] */
582 You("were not healthy enough to survive restoration.");
583 /* wiz1_level.dlevel is used by mklev.c to see if lots of stuff is
584 * uninitialized, so we only have to set it and not the other stuff.
586 wiz1_level
.dlevel
= 0;
589 /* revert to pre-restore option settings */
590 iflags
.deferred_X
= FALSE
;
591 flags
= newgameflags
;
593 sysflags
= newgamesysflags
;
597 /* in case hangup save occurred in midst of level change */
598 assign_level(&u
.uz0
, &u
.uz
);
600 /* this stuff comes after potential aborted restore attempts */
602 restore_timers(fd
, RANGE_GLOBAL
, FALSE
, 0L);
603 restore_light_sources(fd
);
604 invent
= restobjchn(fd
, FALSE
, FALSE
);
605 /* tmp_bc only gets set here if the ball & chain were orphaned
606 because you were swallowed; otherwise they will be on the floor
607 or in your inventory */
608 tmp_bc
= restobjchn(fd
, FALSE
, FALSE
);
610 for (otmp
= tmp_bc
; otmp
; otmp
= otmp
->nobj
) {
612 setworn(otmp
, otmp
->owornmask
);
614 if (!uball
|| !uchain
)
615 impossible("restgamestate: lost ball & chain");
618 migrating_objs
= restobjchn(fd
, FALSE
, FALSE
);
619 migrating_mons
= restmonchn(fd
, FALSE
);
620 mread(fd
, (genericptr_t
) mvitals
, sizeof(mvitals
));
623 * There are some things after this that can have unintended display
624 * side-effects too early in the game.
625 * Disable see_monsters() here, re-enable it at the top of moveloop()
627 defer_see_monsters
= TRUE
;
629 /* this comes after inventory has been loaded */
630 for (otmp
= invent
; otmp
; otmp
= otmp
->nobj
)
632 setworn(otmp
, otmp
->owornmask
);
633 /* reset weapon so that player will get a reminder about "bashing"
634 during next fight when bare-handed or wielding an unconventional
635 item; for pick-axe, we aren't able to distinguish between having
636 applied or wielded it, so be conservative and assume the former */
637 otmp
= uwep
; /* `uwep' usually init'd by setworn() in loop above */
638 uwep
= 0; /* clear it and have setuwep() reinit */
639 setuwep(otmp
); /* (don't need any null check here) */
640 if (!uwep
|| uwep
->otyp
== PICK_AXE
|| uwep
->otyp
== GRAPPLING_HOOK
)
645 mread(fd
, (genericptr_t
) &moves
, sizeof moves
);
646 mread(fd
, (genericptr_t
) &monstermoves
, sizeof monstermoves
);
647 mread(fd
, (genericptr_t
) &quest_status
, sizeof(struct q_score
));
648 mread(fd
, (genericptr_t
) spl_book
, sizeof(struct spell
) * (MAXSPELL
+ 1));
649 restore_artifacts(fd
);
652 mread(fd
, (genericptr_t
) stuckid
, sizeof(*stuckid
));
654 mread(fd
, (genericptr_t
) steedid
, sizeof(*steedid
));
655 mread(fd
, (genericptr_t
) pl_character
, sizeof pl_character
);
657 mread(fd
, (genericptr_t
) pl_fruit
, sizeof pl_fruit
);
658 freefruitchn(ffruit
); /* clean up fruit(s) made by initoptions() */
659 ffruit
= loadfruitchn(fd
);
662 restore_waterlevel(fd
);
663 restore_msghistory(fd
);
664 /* must come after all mons & objs are restored */
665 relink_timers(FALSE
);
666 relink_light_sources(FALSE
);
670 /* update game state pointers to those valid for the current level (so we
671 * don't dereference a wild u.ustuck when saving the game state, for instance)
674 restlevelstate(stuckid
, steedid
)
675 unsigned int stuckid
, steedid
;
677 register struct monst
*mtmp
;
680 for (mtmp
= fmon
; mtmp
; mtmp
= mtmp
->nmon
)
681 if (mtmp
->m_id
== stuckid
)
684 panic("Cannot find the monster ustuck.");
688 for (mtmp
= fmon
; mtmp
; mtmp
= mtmp
->nmon
)
689 if (mtmp
->m_id
== steedid
)
692 panic("Cannot find the monster usteed.");
694 remove_monster(mtmp
->mx
, mtmp
->my
);
700 restlevelfile(fd
, ltmp
)
701 int fd
; /* fd used in MFLOPPY only */
710 nfd
= create_levelfile(ltmp
, whynot
);
712 /* BUG: should suppress any attempt to write a panic
713 save file if file creation is now failing... */
714 panic("restlevelfile: %s", whynot
);
717 if (!savelev(nfd
, ltmp
, COUNT_SAVE
)) {
718 /* The savelev can't proceed because the size required
719 * is greater than the available disk space.
721 pline("Not enough space on `%s' to restore your game.", levels
);
723 /* Remove levels and bones that may have been created.
729 eraseall(levels
, alllevels
);
730 eraseall(levels
, allbones
);
732 /* Perhaps the person would like to play without a
736 /* PlaywoRAMdisk may not return, but if it does
737 * it is certain that ramdisk will be 0.
740 /* Rewind save file and try again */
741 (void) lseek(fd
, (off_t
) 0, 0);
742 (void) validate(fd
, (char *) 0); /* skip version etc */
743 return dorecover(fd
); /* 0 or 1 */
746 pline("Be seeing you...");
747 terminate(EXIT_SUCCESS
);
751 savelev(nfd
, ltmp
, WRITE_SAVE
| FREE_SAVE
);
760 unsigned int stuckid
= 0, steedid
= 0; /* not a register */
766 get_plname_from_file(fd
, plname
);
767 getlev(fd
, 0, (xchar
) 0, FALSE
);
768 if (!restgamestate(fd
, &stuckid
, &steedid
)) {
769 display_nhwindow(WIN_MESSAGE
, TRUE
);
770 savelev(-1, 0, FREE_SAVE
); /* discard current level */
772 (void) delete_savefile();
776 restlevelstate(stuckid
, steedid
);
780 rtmp
= restlevelfile(fd
, ledger_no(&u
.uz
));
782 return rtmp
; /* dorecover called recursively */
784 /* these pointers won't be valid while we're processing the
785 * other levels, but they'll be reset again by restlevelstate()
786 * afterwards, and in the meantime at least u.usteed may mislead
787 * place_monster() on other levels
789 u
.ustuck
= (struct monst
*) 0;
790 u
.usteed
= (struct monst
*) 0;
795 extern struct window_procs amii_procs
;
796 if (windowprocs
.win_init_nhwindows
== amii_procs
.win_init_nhwindows
) {
797 extern winid WIN_BASE
;
798 clear_nhwindow(WIN_BASE
); /* hack until there's a hook for this */
802 clear_nhwindow(WIN_MAP
);
804 clear_nhwindow(WIN_MESSAGE
);
805 You("return to level %d in %s%s.", depth(&u
.uz
),
806 dungeons
[u
.uz
.dnum
].dname
,
807 flags
.debug
? " while in debug mode"
808 : flags
.explore
? " while in explore mode" : "");
812 if (strncmpi("X11", windowprocs
.name
, 3))
813 putstr(WIN_MAP
, 0, "Restoring:");
815 restoreprocs
.mread_flags
= 1; /* return despite error */
817 mread(fd
, (genericptr_t
) <mp
, sizeof ltmp
);
818 if (restoreprocs
.mread_flags
== -1)
820 getlev(fd
, 0, ltmp
, FALSE
);
822 curs(WIN_MAP
, 1 + dotcnt
++, dotrow
);
823 if (dotcnt
>= (COLNO
- 1)) {
827 if (strncmpi("X11", windowprocs
.name
, 3)) {
828 putstr(WIN_MAP
, 0, ".");
832 rtmp
= restlevelfile(fd
, ltmp
);
834 return rtmp
; /* dorecover called recursively */
836 restoreprocs
.mread_flags
= 0;
839 (void) lseek(fd
, 0L, 0);
841 (void) lseek(fd
, (off_t
) 0, 0);
843 (void) validate(fd
, (char *) 0); /* skip version and savefile info */
844 get_plname_from_file(fd
, plname
);
846 getlev(fd
, 0, (xchar
) 0, FALSE
);
849 /* Now set the restore settings to match the
850 * settings used by the save file output routines
854 restlevelstate(stuckid
, steedid
);
855 program_state
.something_worth_saving
= 1; /* useful data now exists */
857 if (!wizard
&& !discover
)
858 (void) delete_savefile();
859 if (Is_rogue_level(&u
.uz
))
860 assign_graphics(ROGUESET
);
862 substitute_tiles(&u
.uz
);
867 max_rank_sz(); /* to recompute mrank_sz (botl.c) */
868 /* take care of iron ball & chain */
869 for (otmp
= fobj
; otmp
; otmp
= otmp
->nobj
)
871 setworn(otmp
, otmp
->owornmask
);
873 /* in_use processing must be after:
874 * + The inventory has been read so that freeinv() works.
875 * + The current level has been restored so billing information
880 load_qtlist(); /* re-load the quest text info */
881 /* Set up the vision internals, after levl[] data is loaded
882 but before docrt(). */
885 vision_full_recalc
= 1; /* recompute vision (not saved) */
887 run_timers(); /* expire all timers that have gone off while away */
890 clear_nhwindow(WIN_MESSAGE
);
894 check_special_room(FALSE
);
899 restcemetery(fd
, cemeteryaddr
)
901 struct cemetery
**cemeteryaddr
;
903 struct cemetery
*bonesinfo
, **bonesaddr
;
906 mread(fd
, (genericptr_t
) &flag
, sizeof flag
);
908 bonesaddr
= cemeteryaddr
;
910 bonesinfo
= (struct cemetery
*) alloc(sizeof *bonesinfo
);
911 mread(fd
, (genericptr_t
) bonesinfo
, sizeof *bonesinfo
);
912 *bonesaddr
= bonesinfo
;
913 bonesaddr
= &(*bonesaddr
)->next
;
914 } while (*bonesaddr
);
922 rest_levl(fd
, rlecomp
)
932 (void) memset((genericptr_t
) &r
, 0, sizeof(r
));
943 mread(fd
, (genericptr_t
) &len
, sizeof(uchar
));
944 mread(fd
, (genericptr_t
) &r
, sizeof(struct rm
));
954 #endif /* ?RLECOMP */
955 mread(fd
, (genericptr_t
) levl
, sizeof levl
);
962 pline("Strange, this map is not as I remember it.");
963 pline("Somebody is trying some trickery here...");
964 pline("This game is void.");
965 Strcpy(killer
.name
, reason
? reason
: "");
970 getlev(fd
, pid
, lev
, ghostly
)
975 register struct trap
*trap
;
976 register struct monst
*mtmp
;
989 #if defined(MSDOS) || defined(OS2)
990 setmode(fd
, O_BINARY
);
992 /* Load the old fruit info. We have to do it first, so the
993 * information is available when restoring the objects.
996 oldfruit
= loadfruitchn(fd
);
998 /* First some sanity checks */
999 mread(fd
, (genericptr_t
) &hpid
, sizeof(hpid
));
1000 /* CHECK: This may prevent restoration */
1002 mread(fd
, (genericptr_t
) &tlev
, sizeof(tlev
));
1003 dlvl
= tlev
& 0x00ff;
1005 mread(fd
, (genericptr_t
) &dlvl
, sizeof(dlvl
));
1007 if ((pid
&& pid
!= hpid
) || (lev
&& dlvl
!= lev
)) {
1008 char trickbuf
[BUFSZ
];
1010 if (pid
&& pid
!= hpid
)
1011 Sprintf(trickbuf
, "PID (%d) doesn't match saved PID (%d)!", hpid
,
1014 Sprintf(trickbuf
, "This is level %d, not %d!", dlvl
, lev
);
1019 restcemetery(fd
, &level
.bonesinfo
);
1021 (boolean
) ((sfrestinfo
.sfi1
& SFI1_RLECOMP
) == SFI1_RLECOMP
));
1022 mread(fd
, (genericptr_t
) lastseentyp
, sizeof(lastseentyp
));
1023 mread(fd
, (genericptr_t
) &omoves
, sizeof(omoves
));
1024 elapsed
= monstermoves
- omoves
;
1025 mread(fd
, (genericptr_t
) &upstair
, sizeof(stairway
));
1026 mread(fd
, (genericptr_t
) &dnstair
, sizeof(stairway
));
1027 mread(fd
, (genericptr_t
) &upladder
, sizeof(stairway
));
1028 mread(fd
, (genericptr_t
) &dnladder
, sizeof(stairway
));
1029 mread(fd
, (genericptr_t
) &sstairs
, sizeof(stairway
));
1030 mread(fd
, (genericptr_t
) &updest
, sizeof(dest_area
));
1031 mread(fd
, (genericptr_t
) &dndest
, sizeof(dest_area
));
1032 mread(fd
, (genericptr_t
) &level
.flags
, sizeof(level
.flags
));
1033 mread(fd
, (genericptr_t
) doors
, sizeof(doors
));
1034 rest_rooms(fd
); /* No joke :-) */
1036 doorindex
= rooms
[nroom
- 1].fdoor
+ rooms
[nroom
- 1].doorct
;
1040 restore_timers(fd
, RANGE_LEVEL
, ghostly
, elapsed
);
1041 restore_light_sources(fd
);
1042 fmon
= restmonchn(fd
, ghostly
);
1044 rest_worm(fd
); /* restore worm information */
1046 while (trap
= newtrap(),
1047 mread(fd
, (genericptr_t
) trap
, sizeof(struct trap
)),
1048 trap
->tx
!= 0) { /* need "!= 0" to work around DICE 3.0 bug */
1049 trap
->ntrap
= ftrap
;
1053 fobj
= restobjchn(fd
, ghostly
, FALSE
);
1055 /* restobjchn()'s `frozen' argument probably ought to be a callback
1056 routine so that we can check for objects being buried under ice */
1057 level
.buriedobjlist
= restobjchn(fd
, ghostly
, FALSE
);
1058 billobjs
= restobjchn(fd
, ghostly
, FALSE
);
1059 rest_engravings(fd
);
1061 /* reset level.monsters for new level */
1062 for (x
= 0; x
< COLNO
; x
++)
1063 for (y
= 0; y
< ROWNO
; y
++)
1064 level
.monsters
[x
][y
] = (struct monst
*) 0;
1065 for (mtmp
= fmon
; mtmp
; mtmp
= mtmp
->nmon
) {
1067 set_residency(mtmp
, FALSE
);
1068 place_monster(mtmp
, mtmp
->mx
, mtmp
->my
);
1072 /* regenerate monsters while on another level */
1076 /* reset peaceful/malign relative to new character;
1077 shopkeepers will reset based on name */
1080 (is_unicorn(mtmp
->data
)
1081 && sgn(u
.ualign
.type
) == sgn(mtmp
->data
->maligntyp
))
1083 : peace_minded(mtmp
->data
);
1085 } else if (elapsed
> 0L) {
1086 mon_catchup_elapsed_time(mtmp
, elapsed
);
1088 /* update shape-changers in case protection against
1089 them is different now than when the level was saved */
1091 /* give hiders a chance to hide before their next move */
1092 if (ghostly
|| elapsed
> (long) rnd(10))
1096 restdamage(fd
, ghostly
);
1097 rest_regions(fd
, ghostly
);
1099 /* Now get rid of all the temp fruits... */
1100 freefruitchn(oldfruit
), oldfruit
= 0;
1102 if (lev
> ledger_no(&medusa_level
)
1103 && lev
< ledger_no(&stronghold_level
) && xdnstair
== 0) {
1109 levl
[cc
.x
][cc
.y
].typ
= STAIRS
;
1112 br
= Is_branchlev(&u
.uz
);
1113 if (br
&& u
.uz
.dlevel
== 1) {
1116 if (on_level(&u
.uz
, &br
->end1
))
1117 assign_level(<mp
, &br
->end2
);
1119 assign_level(<mp
, &br
->end1
);
1124 case BR_NO_END2
: /* OK to assign to sstairs if it's not used */
1125 assign_level(&sstairs
.tolev
, <mp
);
1127 case BR_PORTAL
: /* max of 1 portal per level */
1128 for (trap
= ftrap
; trap
; trap
= trap
->ntrap
)
1129 if (trap
->ttyp
== MAGIC_PORTAL
)
1132 panic("getlev: need portal but none found");
1133 assign_level(&trap
->dst
, <mp
);
1137 struct trap
*ttmp
= 0;
1139 /* Remove any dangling portals. */
1140 for (trap
= ftrap
; trap
; trap
= ttmp
) {
1142 if (trap
->ttyp
== MAGIC_PORTAL
)
1148 /* must come after all mons & objs are restored */
1149 relink_timers(ghostly
);
1150 relink_light_sources(ghostly
);
1151 reset_oattached_mids(ghostly
);
1158 get_plname_from_file(fd
, plbuf
)
1163 (void) read(fd
, (genericptr_t
) &pltmpsiz
, sizeof(pltmpsiz
));
1164 (void) read(fd
, (genericptr_t
) plbuf
, pltmpsiz
);
1169 restore_msghistory(fd
)
1172 int msgsize
, msgcount
= 0;
1176 mread(fd
, (genericptr_t
) &msgsize
, sizeof(msgsize
));
1179 if (msgsize
> (BUFSZ
- 1))
1180 panic("restore_msghistory: msg too big (%d)", msgsize
);
1181 mread(fd
, (genericptr_t
) msg
, msgsize
);
1182 msg
[msgsize
] = '\0';
1183 putmsghistory(msg
, TRUE
);
1187 putmsghistory((char *) 0, TRUE
);
1188 debugpline1("Read %d messages from savefile.", msgcount
);
1191 /* Clear all structures for object and monster ID mapping. */
1195 struct bucket
*curr
;
1197 while ((curr
= id_map
) != 0) {
1198 id_map
= curr
->next
;
1199 free((genericptr_t
) curr
);
1204 /* Add a mapping to the ID map. */
1206 add_id_mapping(gid
, nid
)
1211 idx
= n_ids_mapped
% N_PER_BUCKET
;
1212 /* idx is zero on first time through, as well as when a new bucket is */
1215 struct bucket
*gnu
= (struct bucket
*) alloc(sizeof(struct bucket
));
1220 id_map
->map
[idx
].gid
= gid
;
1221 id_map
->map
[idx
].nid
= nid
;
1226 * Global routine to look up a mapping. If found, return TRUE and fill
1227 * in the new ID value. Otherwise, return false and return -1 in the new
1231 lookup_id_mapping(gid
, nidp
)
1232 unsigned gid
, *nidp
;
1235 struct bucket
*curr
;
1238 for (curr
= id_map
; curr
; curr
= curr
->next
) {
1239 /* first bucket might not be totally full */
1240 if (curr
== id_map
) {
1241 i
= n_ids_mapped
% N_PER_BUCKET
;
1248 if (gid
== curr
->map
[i
].gid
) {
1249 *nidp
= curr
->map
[i
].nid
;
1258 reset_oattached_mids(ghostly
)
1262 unsigned oldid
, nid
;
1263 for (otmp
= fobj
; otmp
; otmp
= otmp
->nobj
) {
1264 if (ghostly
&& has_omonst(otmp
)) {
1265 struct monst
*mtmp
= OMONST(otmp
);
1268 mtmp
->mpeaceful
= mtmp
->mtame
= 0; /* pet's owner died! */
1270 if (ghostly
&& has_omid(otmp
)) {
1271 (void) memcpy((genericptr_t
) &oldid
, (genericptr_t
) OMID(otmp
),
1273 if (lookup_id_mapping(oldid
, &nid
))
1274 (void) memcpy((genericptr_t
) OMID(otmp
), (genericptr_t
) &nid
,
1283 /* put up a menu listing each character from this player's saved games;
1284 returns 1: use plname[], 0: new game, -1: quit */
1286 restore_menu(bannerwin
)
1287 winid bannerwin
; /* if not WIN_ERR, clear window and show copyright in menu */
1292 menu_item
*chosen_game
= (menu_item
*) 0;
1293 int k
, clet
, ch
= 0; /* ch: 0 => new game */
1296 saved
= get_saved_games(); /* array of character names */
1297 if (saved
&& *saved
) {
1298 tmpwin
= create_nhwindow(NHW_MENU
);
1300 any
= zeroany
; /* no selection */
1301 if (bannerwin
!= WIN_ERR
) {
1302 /* for tty; erase copyright notice and redo it in the menu */
1303 clear_nhwindow(bannerwin
);
1304 /* COPYRIGHT_BANNER_[ABCD] */
1305 for (k
= 1; k
<= 4; ++k
)
1306 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
,
1307 copyright_banner_line(k
), MENU_UNSELECTED
);
1308 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, "",
1311 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
,
1312 "Select one of your saved games", MENU_UNSELECTED
);
1313 for (k
= 0; saved
[k
]; ++k
) {
1315 add_menu(tmpwin
, NO_GLYPH
, &any
, 0, 0, ATR_NONE
, saved
[k
],
1318 clet
= (k
<= 'n' - 'a') ? 'n' : 0; /* new game */
1319 any
.a_int
= -1; /* not >= 0 */
1320 add_menu(tmpwin
, NO_GLYPH
, &any
, clet
, 0, ATR_NONE
,
1321 "Start a new character", MENU_UNSELECTED
);
1322 clet
= (k
+ 1 <= 'q' - 'a') ? 'q' : 0; /* quit */
1324 add_menu(tmpwin
, NO_GLYPH
, &any
, clet
, 0, ATR_NONE
,
1325 "Never mind (quit)", MENU_SELECTED
);
1326 /* no prompt on end_menu, as we've done our own at the top */
1327 end_menu(tmpwin
, (char *) 0);
1328 if (select_menu(tmpwin
, PICK_ONE
, &chosen_game
) > 0) {
1329 ch
= chosen_game
->item
.a_int
;
1331 Strcpy(plname
, saved
[ch
- 1]);
1333 ++ch
; /* -1 -> 0 (new game), -2 -> -1 (quit) */
1334 free((genericptr_t
) chosen_game
);
1336 ch
= -1; /* quit menu without making a selection => quit */
1338 destroy_nhwindow(tmpwin
);
1339 if (bannerwin
!= WIN_ERR
) {
1340 /* for tty; clear the menu away and put subset of copyright back
1342 clear_nhwindow(bannerwin
);
1343 /* COPYRIGHT_BANNER_A, preceding "Who are you?" prompt */
1345 putstr(bannerwin
, 0, copyright_banner_line(1));
1348 free_saved_games(saved
);
1349 return (ch
> 0) ? 1 : ch
;
1351 #endif /* SELECTSAVED */
1356 (*restoreprocs
.restore_minit
)();
1363 register genericptr_t buf
;
1364 register unsigned int len
;
1366 (*restoreprocs
.restore_mread
)(fd
, buf
, len
);
1370 /* examine the version info and the savefile_info data
1371 that immediately follows it.
1372 Return 0 if it passed the checks.
1373 Return 1 if it failed the version check.
1374 Return 2 if it failed the savefile feature check.
1375 Return -1 if it failed for some unknown reason.
1383 struct savefile_info sfi
;
1384 unsigned long compatible
;
1385 boolean verbose
= name
? TRUE
: FALSE
, reslt
= FALSE
;
1387 if (!(reslt
= uptodate(fd
, name
)))
1390 rlen
= read(fd
, (genericptr_t
) &sfi
, sizeof sfi
);
1391 minit(); /* ZEROCOMP */
1394 pline("File \"%s\" is empty during save file feature check?",
1401 compatible
= (sfi
.sfi1
& sfcap
.sfi1
);
1403 if ((sfi
.sfi1
& SFI1_ZEROCOMP
) == SFI1_ZEROCOMP
) {
1404 if ((compatible
& SFI1_ZEROCOMP
) != SFI1_ZEROCOMP
) {
1406 pline("File \"%s\" has incompatible ZEROCOMP compression.",
1411 } else if ((sfrestinfo
.sfi1
& SFI1_ZEROCOMP
) != SFI1_ZEROCOMP
) {
1412 set_restpref("zerocomp");
1416 if ((sfi
.sfi1
& SFI1_EXTERNALCOMP
) == SFI1_EXTERNALCOMP
) {
1417 if ((compatible
& SFI1_EXTERNALCOMP
) != SFI1_EXTERNALCOMP
) {
1419 pline("File \"%s\" lacks required internal compression.",
1424 } else if ((sfrestinfo
.sfi1
& SFI1_EXTERNALCOMP
)
1425 != SFI1_EXTERNALCOMP
) {
1426 set_restpref("externalcomp");
1430 /* RLECOMP check must be last, after ZEROCOMP or INTERNALCOMP adjustments
1432 if ((sfi
.sfi1
& SFI1_RLECOMP
) == SFI1_RLECOMP
) {
1433 if ((compatible
& SFI1_RLECOMP
) != SFI1_RLECOMP
) {
1435 pline("File \"%s\" has incompatible run-length compression.",
1440 } else if ((sfrestinfo
.sfi1
& SFI1_RLECOMP
) != SFI1_RLECOMP
) {
1441 set_restpref("rlecomp");
1444 /* savefile does not have RLECOMP level location compression, so adjust */
1446 set_restpref("!rlecomp");
1455 if (iflags
.zerocomp
)
1456 set_restpref("zerocomp");
1459 set_restpref("externalcomp");
1462 set_restpref("rlecomp");
1465 set_restpref("!rlecomp");
1469 set_restpref(suitename
)
1470 const char *suitename
;
1472 if (!strcmpi(suitename
, "externalcomp")) {
1473 restoreprocs
.name
= "externalcomp";
1474 restoreprocs
.restore_mread
= def_mread
;
1475 restoreprocs
.restore_minit
= def_minit
;
1476 sfrestinfo
.sfi1
|= SFI1_EXTERNALCOMP
;
1477 sfrestinfo
.sfi1
&= ~SFI1_ZEROCOMP
;
1480 if (!strcmpi(suitename
, "!rlecomp")) {
1481 sfrestinfo
.sfi1
&= ~SFI1_RLECOMP
;
1484 if (!strcmpi(suitename
, "zerocomp")) {
1485 restoreprocs
.name
= "zerocomp";
1486 restoreprocs
.restore_mread
= zerocomp_mread
;
1487 restoreprocs
.restore_minit
= zerocomp_minit
;
1488 sfrestinfo
.sfi1
|= SFI1_ZEROCOMP
;
1489 sfrestinfo
.sfi1
&= ~SFI1_EXTERNALCOMP
;
1494 if (!strcmpi(suitename
, "rlecomp")) {
1495 sfrestinfo
.sfi1
|= SFI1_RLECOMP
;
1501 #define RLESC '\0' /* Leading character for run of RLESC's */
1503 #ifndef ZEROCOMP_BUFSIZ
1504 #define ZEROCOMP_BUFSIZ BUFSZ
1506 static NEARDATA
unsigned char inbuf
[ZEROCOMP_BUFSIZ
];
1507 static NEARDATA
unsigned short inbufp
= 0;
1508 static NEARDATA
unsigned short inbufsz
= 0;
1509 static NEARDATA
short inrunlength
= -1;
1510 static NEARDATA
int mreadfd
;
1515 if (inbufp
>= inbufsz
) {
1516 inbufsz
= read(mreadfd
, (genericptr_t
) inbuf
, sizeof inbuf
);
1518 if (inbufp
> sizeof inbuf
)
1519 error("EOF on file #%d.\n", mreadfd
);
1520 inbufp
= 1 + sizeof inbuf
; /* exactly one warning :-) */
1525 return inbuf
[inbufp
++];
1537 zerocomp_mread(fd
, buf
, len
)
1540 register unsigned len
;
1542 /*register int readlen = 0;*/
1544 error("Restore error; mread attempting to read file %d.", fd
);
1547 if (inrunlength
> 0) {
1549 *(*((char **) &buf
))++ = '\0';
1551 register short ch
= zerocomp_mgetc();
1553 restoreprocs
.mread_flags
= -1;
1556 if ((*(*(char **) &buf
)++ = (char) ch
) == RLESC
) {
1557 inrunlength
= zerocomp_mgetc();
1563 #endif /* ZEROCOMP */
1572 def_mread(fd
, buf
, len
)
1574 register genericptr_t buf
;
1575 register unsigned int len
;
1578 #if defined(BSD) || defined(ULTRIX)
1579 #define readLenType int
1580 #else /* e.g. SYSV, __TURBOC__ */
1581 #define readLenType unsigned
1584 rlen
= read(fd
, buf
, (readLenType
) len
);
1585 if ((readLenType
) rlen
!= (readLenType
) len
) {
1586 if (restoreprocs
.mread_flags
== 1) { /* means "return anyway" */
1587 restoreprocs
.mread_flags
= -1;
1590 pline("Read %d instead of %u bytes.", rlen
, len
);
1593 (void) delete_savefile();
1594 error("Error restoring old game.");
1596 panic("Error reading level file.");