o Add support for zlib compression via ZLIB_COMP in config.h (ZLIB_COMP and COMPRESS are mutually exclusive). o rlecomp and zerocomp are run time options available if RLECOMP and ZEROCOMP are defined, but not turned on by default if either COMPRESS or ZLIB_COMP are defined. o Add information to the save file about internal compression options used when writing the save file, particularly rlecomp and zerocomp support. o Automatically adjust rlecomp and zerocomp (if support compiled in) when reading in an existing savefile that was saved with those options turned on. Still allows writing out of savefile in preferred format. o In order to support zlib and not conflict with compress and uncompress routines there, the NetHack internal functions were changed to nh_uncompress and nh_compress as done in the zlib contribution received in 1999 from <Someone>. I tagged the sources NETHACK_3_5_0_PREZLIB prior to applying these changes.
1365 lines
34 KiB
C
1365 lines
34 KiB
C
/* SCCS Id: @(#)restore.c 3.5 2005/01/04 */
|
|
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
|
|
/* NetHack may be freely redistributed. See license for details. */
|
|
|
|
#include "hack.h"
|
|
#include "lev.h"
|
|
#include "tcap.h" /* for TERMLIB and ASCIIGRAPH */
|
|
|
|
#if defined(MICRO)
|
|
extern int dotcnt; /* shared with save */
|
|
extern int dotrow; /* shared with save */
|
|
#endif
|
|
|
|
#ifdef USE_TILES
|
|
extern void FDECL(substitute_tiles, (d_level *)); /* from tile.c */
|
|
#endif
|
|
|
|
#ifdef ZEROCOMP
|
|
STATIC_DCL void NDECL(zerocomp_minit);
|
|
STATIC_DCL void FDECL(zerocomp_mread, (int,genericptr_t,unsigned int));
|
|
STATIC_DCL int NDECL(zerocomp_mgetc);
|
|
#endif
|
|
|
|
STATIC_DCL void NDECL(def_minit);
|
|
STATIC_DCL void FDECL(def_mread, (int,genericptr_t,unsigned int));
|
|
|
|
STATIC_DCL void NDECL(find_lev_obj);
|
|
STATIC_DCL void FDECL(restlevchn, (int));
|
|
STATIC_DCL void FDECL(restdamage, (int,BOOLEAN_P));
|
|
STATIC_DCL struct obj *FDECL(restobjchn, (int,BOOLEAN_P,BOOLEAN_P));
|
|
STATIC_DCL struct monst *FDECL(restmonchn, (int,BOOLEAN_P));
|
|
STATIC_DCL struct fruit *FDECL(loadfruitchn, (int));
|
|
STATIC_DCL void FDECL(freefruitchn, (struct fruit *));
|
|
STATIC_DCL void FDECL(ghostfruit, (struct obj *));
|
|
STATIC_DCL boolean FDECL(restgamestate, (int, unsigned int *, unsigned int *));
|
|
STATIC_DCL void FDECL(restlevelstate, (unsigned int, unsigned int));
|
|
STATIC_DCL int FDECL(restlevelfile, (int,XCHAR_P));
|
|
STATIC_OVL void FDECL(restore_msghistory, (int));
|
|
STATIC_DCL void FDECL(reset_oattached_mids, (BOOLEAN_P));
|
|
#ifndef GOLDOBJ
|
|
STATIC_DCL struct obj *FDECL(gold_in, (struct obj *));
|
|
#endif
|
|
STATIC_DCL void FDECL(rest_levl, (int,BOOLEAN_P));
|
|
|
|
static struct restore_procs {
|
|
const char *name;
|
|
int mread_flags;
|
|
void NDECL((*restore_minit));
|
|
void FDECL((*restore_mread), (int,genericptr_t,unsigned int));
|
|
void FDECL((*restore_bclose), (int));
|
|
} restoreprocs = {
|
|
#if !defined(ZEROCOMP) || (defined(COMPRESS) || defined(ZLIB_COMP))
|
|
"externalcomp", 0,
|
|
def_minit,
|
|
def_mread,
|
|
def_bclose,
|
|
#else
|
|
"zerocomp", 0,
|
|
zerocomp_minit,
|
|
zerocomp_mread,
|
|
zerocomp_bclose,
|
|
#endif
|
|
};
|
|
|
|
/*
|
|
* Save a mapping of IDs from ghost levels to the current level. This
|
|
* map is used by the timer routines when restoring ghost levels.
|
|
*/
|
|
#define N_PER_BUCKET 64
|
|
struct bucket {
|
|
struct bucket *next;
|
|
struct {
|
|
unsigned gid; /* ghost ID */
|
|
unsigned nid; /* new ID */
|
|
} map[N_PER_BUCKET];
|
|
};
|
|
|
|
STATIC_DCL void NDECL(clear_id_mapping);
|
|
STATIC_DCL void FDECL(add_id_mapping, (unsigned, unsigned));
|
|
|
|
static int n_ids_mapped = 0;
|
|
static struct bucket *id_map = 0;
|
|
|
|
|
|
#ifdef AMII_GRAPHICS
|
|
void FDECL( amii_setpens, (int) ); /* use colors from save file */
|
|
extern int amii_numcolors;
|
|
#endif
|
|
|
|
#include "quest.h"
|
|
|
|
boolean restoring = FALSE;
|
|
static NEARDATA struct fruit *oldfruit;
|
|
static NEARDATA long omoves;
|
|
|
|
#define Is_IceBox(o) ((o)->otyp == ICE_BOX ? TRUE : FALSE)
|
|
|
|
/* Recalculate level.objects[x][y], since this info was not saved. */
|
|
STATIC_OVL void
|
|
find_lev_obj()
|
|
{
|
|
register struct obj *fobjtmp = (struct obj *)0;
|
|
register struct obj *otmp;
|
|
int x,y;
|
|
|
|
for(x=0; x<COLNO; x++) for(y=0; y<ROWNO; y++)
|
|
level.objects[x][y] = (struct obj *)0;
|
|
|
|
/*
|
|
* Reverse the entire fobj chain, which is necessary so that we can
|
|
* place the objects in the proper order. Make all obj in chain
|
|
* OBJ_FREE so place_object will work correctly.
|
|
*/
|
|
while ((otmp = fobj) != 0) {
|
|
fobj = otmp->nobj;
|
|
otmp->nobj = fobjtmp;
|
|
otmp->where = OBJ_FREE;
|
|
fobjtmp = otmp;
|
|
}
|
|
/* fobj should now be empty */
|
|
|
|
/* Set level.objects (as well as reversing the chain back again) */
|
|
while ((otmp = fobjtmp) != 0) {
|
|
fobjtmp = otmp->nobj;
|
|
place_object(otmp, otmp->ox, otmp->oy);
|
|
}
|
|
}
|
|
|
|
/* Things that were marked "in_use" when the game was saved (ex. via the
|
|
* infamous "HUP" cheat) get used up here.
|
|
*/
|
|
void
|
|
inven_inuse(quietly)
|
|
boolean quietly;
|
|
{
|
|
register struct obj *otmp, *otmp2;
|
|
|
|
for (otmp = invent; otmp; otmp = otmp2) {
|
|
otmp2 = otmp->nobj;
|
|
#ifndef GOLDOBJ
|
|
if (otmp->oclass == COIN_CLASS) {
|
|
/* in_use gold is created by some menu operations */
|
|
if (!otmp->in_use) {
|
|
impossible("inven_inuse: !in_use gold in inventory");
|
|
}
|
|
extract_nobj(otmp, &invent);
|
|
otmp->in_use = FALSE;
|
|
dealloc_obj(otmp);
|
|
} else
|
|
#endif /* GOLDOBJ */
|
|
if (otmp->in_use) {
|
|
if (!quietly) pline("Finishing off %s...", xname(otmp));
|
|
useup(otmp);
|
|
}
|
|
}
|
|
}
|
|
|
|
STATIC_OVL void
|
|
restlevchn(fd)
|
|
register int fd;
|
|
{
|
|
int cnt;
|
|
s_level *tmplev, *x;
|
|
|
|
sp_levchn = (s_level *) 0;
|
|
mread(fd, (genericptr_t) &cnt, sizeof(int));
|
|
for(; cnt > 0; cnt--) {
|
|
|
|
tmplev = (s_level *)alloc(sizeof(s_level));
|
|
mread(fd, (genericptr_t) tmplev, sizeof(s_level));
|
|
if(!sp_levchn) sp_levchn = tmplev;
|
|
else {
|
|
|
|
for(x = sp_levchn; x->next; x = x->next);
|
|
x->next = tmplev;
|
|
}
|
|
tmplev->next = (s_level *)0;
|
|
}
|
|
}
|
|
|
|
STATIC_OVL void
|
|
restdamage(fd, ghostly)
|
|
int fd;
|
|
boolean ghostly;
|
|
{
|
|
int counter;
|
|
struct damage *tmp_dam;
|
|
|
|
mread(fd, (genericptr_t) &counter, sizeof(counter));
|
|
if (!counter)
|
|
return;
|
|
tmp_dam = (struct damage *)alloc(sizeof(struct damage));
|
|
while (--counter >= 0) {
|
|
char damaged_shops[5], *shp = (char *)0;
|
|
|
|
mread(fd, (genericptr_t) tmp_dam, sizeof(*tmp_dam));
|
|
if (ghostly)
|
|
tmp_dam->when += (monstermoves - omoves);
|
|
Strcpy(damaged_shops,
|
|
in_rooms(tmp_dam->place.x, tmp_dam->place.y, SHOPBASE));
|
|
if (u.uz.dlevel) {
|
|
/* when restoring, there are two passes over the current
|
|
* level. the first time, u.uz isn't set, so neither is
|
|
* shop_keeper(). just wait and process the damage on
|
|
* the second pass.
|
|
*/
|
|
for (shp = damaged_shops; *shp; shp++) {
|
|
struct monst *shkp = shop_keeper(*shp);
|
|
|
|
if (shkp && inhishop(shkp) &&
|
|
repair_damage(shkp, tmp_dam, TRUE))
|
|
break;
|
|
}
|
|
}
|
|
if (!shp || !*shp) {
|
|
tmp_dam->next = level.damagelist;
|
|
level.damagelist = tmp_dam;
|
|
tmp_dam = (struct damage *)alloc(sizeof(*tmp_dam));
|
|
}
|
|
}
|
|
free((genericptr_t)tmp_dam);
|
|
}
|
|
|
|
STATIC_OVL struct obj *
|
|
restobjchn(fd, ghostly, frozen)
|
|
register int fd;
|
|
boolean ghostly, frozen;
|
|
{
|
|
register struct obj *otmp, *otmp2 = 0;
|
|
register struct obj *first = (struct obj *)0;
|
|
int xl;
|
|
|
|
while(1) {
|
|
mread(fd, (genericptr_t) &xl, sizeof(xl));
|
|
if(xl == -1) break;
|
|
otmp = newobj(xl);
|
|
if(!first) first = otmp;
|
|
else otmp2->nobj = otmp;
|
|
mread(fd, (genericptr_t) otmp,
|
|
(unsigned) xl + sizeof(struct obj));
|
|
if (ghostly) {
|
|
unsigned nid = context.ident++;
|
|
add_id_mapping(otmp->o_id, nid);
|
|
otmp->o_id = nid;
|
|
}
|
|
if (ghostly && otmp->otyp == SLIME_MOLD) ghostfruit(otmp);
|
|
/* Ghost levels get object age shifted from old player's clock
|
|
* to new player's clock. Assumption: new player arrived
|
|
* immediately after old player died.
|
|
*/
|
|
if (ghostly && !frozen && !age_is_relative(otmp))
|
|
otmp->age = monstermoves - omoves + otmp->age;
|
|
|
|
/* get contents of a container or statue */
|
|
if (Has_contents(otmp)) {
|
|
struct obj *otmp3;
|
|
otmp->cobj = restobjchn(fd, ghostly, Is_IceBox(otmp));
|
|
/* restore container back pointers */
|
|
for (otmp3 = otmp->cobj; otmp3; otmp3 = otmp3->nobj)
|
|
otmp3->ocontainer = otmp;
|
|
}
|
|
if (otmp->bypass) otmp->bypass = 0;
|
|
if (!ghostly) {
|
|
/* fix the pointers */
|
|
if (otmp->o_id == context.victual.o_id)
|
|
context.victual.piece = otmp;
|
|
if (otmp->o_id == context.tin.o_id)
|
|
context.tin.tin = otmp;
|
|
if (otmp->o_id == context.spbook.o_id)
|
|
context.spbook.book = otmp;
|
|
}
|
|
otmp2 = otmp;
|
|
}
|
|
if(first && otmp2->nobj){
|
|
impossible("Restobjchn: error reading objchn.");
|
|
otmp2->nobj = 0;
|
|
}
|
|
|
|
return(first);
|
|
}
|
|
|
|
STATIC_OVL struct monst *
|
|
restmonchn(fd, ghostly)
|
|
register int fd;
|
|
boolean ghostly;
|
|
{
|
|
register struct monst *mtmp, *mtmp2 = 0;
|
|
register struct monst *first = (struct monst *)0;
|
|
int xl, offset;
|
|
|
|
while(1) {
|
|
mread(fd, (genericptr_t) &xl, sizeof(xl));
|
|
if(xl == -1) break;
|
|
mtmp = newmonst(xl);
|
|
if(!first) first = mtmp;
|
|
else mtmp2->nmon = mtmp;
|
|
mread(fd, (genericptr_t) mtmp, (unsigned) xl + sizeof(struct monst));
|
|
if (ghostly) {
|
|
unsigned nid = context.ident++;
|
|
add_id_mapping(mtmp->m_id, nid);
|
|
mtmp->m_id = nid;
|
|
}
|
|
offset = mtmp->mnum;
|
|
mtmp->data = &mons[offset];
|
|
if (ghostly) {
|
|
int mndx = monsndx(mtmp->data);
|
|
if (propagate(mndx, TRUE, ghostly) == 0) {
|
|
/* cookie to trigger purge in getbones() */
|
|
mtmp->mhpmax = DEFUNCT_MONSTER;
|
|
}
|
|
}
|
|
if(mtmp->minvent) {
|
|
struct obj *obj;
|
|
mtmp->minvent = restobjchn(fd, ghostly, FALSE);
|
|
/* restore monster back pointer */
|
|
for (obj = mtmp->minvent; obj; obj = obj->nobj)
|
|
obj->ocarry = mtmp;
|
|
#ifndef GOLDOBJ
|
|
put_gold_back(&mtmp->minvent, &mtmp->mgold);
|
|
#endif
|
|
}
|
|
if (mtmp->mw) {
|
|
struct obj *obj;
|
|
|
|
for(obj = mtmp->minvent; obj; obj = obj->nobj)
|
|
if (obj->owornmask & W_WEP) break;
|
|
if (obj) mtmp->mw = obj;
|
|
else {
|
|
MON_NOWEP(mtmp);
|
|
impossible("bad monster weapon restore");
|
|
}
|
|
}
|
|
|
|
if (mtmp->isshk) restshk(mtmp, ghostly);
|
|
if (mtmp->ispriest) restpriest(mtmp, ghostly);
|
|
|
|
mtmp2 = mtmp;
|
|
}
|
|
if(first && mtmp2->nmon){
|
|
impossible("Restmonchn: error reading monchn.");
|
|
mtmp2->nmon = 0;
|
|
}
|
|
return(first);
|
|
}
|
|
|
|
STATIC_OVL struct fruit *
|
|
loadfruitchn(fd)
|
|
int fd;
|
|
{
|
|
register struct fruit *flist, *fnext;
|
|
|
|
flist = 0;
|
|
while (fnext = newfruit(),
|
|
mread(fd, (genericptr_t)fnext, sizeof *fnext),
|
|
fnext->fid != 0) {
|
|
fnext->nextf = flist;
|
|
flist = fnext;
|
|
}
|
|
dealloc_fruit(fnext);
|
|
return flist;
|
|
}
|
|
|
|
STATIC_OVL void
|
|
freefruitchn(flist)
|
|
register struct fruit *flist;
|
|
{
|
|
register struct fruit *fnext;
|
|
|
|
while (flist) {
|
|
fnext = flist->nextf;
|
|
dealloc_fruit(flist);
|
|
flist = fnext;
|
|
}
|
|
}
|
|
|
|
STATIC_OVL void
|
|
ghostfruit(otmp)
|
|
register struct obj *otmp;
|
|
{
|
|
register struct fruit *oldf;
|
|
|
|
for (oldf = oldfruit; oldf; oldf = oldf->nextf)
|
|
if (oldf->fid == otmp->spe) break;
|
|
|
|
if (!oldf) impossible("no old fruit?");
|
|
else otmp->spe = fruitadd(oldf->fname);
|
|
}
|
|
|
|
STATIC_OVL
|
|
boolean
|
|
restgamestate(fd, stuckid, steedid)
|
|
register int fd;
|
|
unsigned int *stuckid, *steedid; /* STEED */
|
|
{
|
|
/* discover is actually flags.explore */
|
|
boolean remember_discover = discover;
|
|
struct obj *otmp;
|
|
int uid;
|
|
|
|
mread(fd, (genericptr_t) &uid, sizeof uid);
|
|
if (uid != getuid()) { /* strange ... */
|
|
/* for wizard mode, issue a reminder; for others, treat it
|
|
as an attempt to cheat and refuse to restore this file */
|
|
pline("Saved game was not yours.");
|
|
#ifdef WIZARD
|
|
if (!wizard)
|
|
#endif
|
|
return FALSE;
|
|
}
|
|
mread(fd, (genericptr_t) &context, sizeof(struct context_info));
|
|
if (context.warntype.speciesidx)
|
|
context.warntype.species = &mons[context.warntype.speciesidx];
|
|
|
|
mread(fd, (genericptr_t) &flags, sizeof(struct flag));
|
|
if (remember_discover) discover = remember_discover;
|
|
#ifdef SYSFLAGS
|
|
mread(fd, (genericptr_t) &sysflags, sizeof(struct sysflag));
|
|
#endif
|
|
|
|
role_init(); /* Reset the initial role, race, gender, and alignment */
|
|
#ifdef AMII_GRAPHICS
|
|
amii_setpens(amii_numcolors); /* use colors from save file */
|
|
#endif
|
|
mread(fd, (genericptr_t) &u, sizeof(struct you));
|
|
set_uasmon();
|
|
#ifdef CLIPPING
|
|
cliparound(u.ux, u.uy);
|
|
#endif
|
|
if(u.uhp <= 0 && (!Upolyd || u.mh <= 0)) {
|
|
u.ux = u.uy = 0; /* affects pline() [hence You()] */
|
|
You("were not healthy enough to survive restoration.");
|
|
/* wiz1_level.dlevel is used by mklev.c to see if lots of stuff is
|
|
* uninitialized, so we only have to set it and not the other stuff.
|
|
*/
|
|
wiz1_level.dlevel = 0;
|
|
u.uz.dnum = 0;
|
|
u.uz.dlevel = 1;
|
|
return(FALSE);
|
|
}
|
|
|
|
/* this stuff comes after potential aborted restore attempts */
|
|
restore_killers(fd);
|
|
restore_timers(fd, RANGE_GLOBAL, FALSE, 0L);
|
|
restore_light_sources(fd);
|
|
invent = restobjchn(fd, FALSE, FALSE);
|
|
#ifndef GOLDOBJ
|
|
put_gold_back(&invent, &u.ugold);
|
|
#endif
|
|
migrating_objs = restobjchn(fd, FALSE, FALSE);
|
|
migrating_mons = restmonchn(fd, FALSE);
|
|
mread(fd, (genericptr_t) mvitals, sizeof(mvitals));
|
|
|
|
/*
|
|
* There are some things after this that can have unintended display
|
|
* side-effects too early in the game.
|
|
* Disable see_monsters() here, re-enable it at the top of moveloop()
|
|
*/
|
|
defer_see_monsters = TRUE;
|
|
|
|
/* this comes after inventory has been loaded */
|
|
for(otmp = invent; otmp; otmp = otmp->nobj)
|
|
if(otmp->owornmask)
|
|
setworn(otmp, otmp->owornmask);
|
|
/* reset weapon so that player will get a reminder about "bashing"
|
|
during next fight when bare-handed or wielding an unconventional
|
|
item; for pick-axe, we aren't able to distinguish between having
|
|
applied or wielded it, so be conservative and assume the former */
|
|
otmp = uwep; /* `uwep' usually init'd by setworn() in loop above */
|
|
uwep = 0; /* clear it and have setuwep() reinit */
|
|
setuwep(otmp); /* (don't need any null check here) */
|
|
if (!uwep || uwep->otyp == PICK_AXE || uwep->otyp == GRAPPLING_HOOK)
|
|
unweapon = TRUE;
|
|
|
|
restore_dungeon(fd);
|
|
restlevchn(fd);
|
|
mread(fd, (genericptr_t) &moves, sizeof moves);
|
|
mread(fd, (genericptr_t) &monstermoves, sizeof monstermoves);
|
|
mread(fd, (genericptr_t) &quest_status, sizeof(struct q_score));
|
|
mread(fd, (genericptr_t) spl_book,
|
|
sizeof(struct spell) * (MAXSPELL + 1));
|
|
restore_artifacts(fd);
|
|
restore_oracles(fd);
|
|
if (u.ustuck)
|
|
mread(fd, (genericptr_t) stuckid, sizeof (*stuckid));
|
|
#ifdef STEED
|
|
if (u.usteed)
|
|
mread(fd, (genericptr_t) steedid, sizeof (*steedid));
|
|
#endif
|
|
mread(fd, (genericptr_t) pl_character, sizeof pl_character);
|
|
|
|
mread(fd, (genericptr_t) pl_fruit, sizeof pl_fruit);
|
|
mread(fd, (genericptr_t) ¤t_fruit, sizeof current_fruit);
|
|
freefruitchn(ffruit); /* clean up fruit(s) made by initoptions() */
|
|
ffruit = loadfruitchn(fd);
|
|
|
|
restnames(fd);
|
|
restore_waterlevel(fd);
|
|
restore_msghistory(fd);
|
|
/* must come after all mons & objs are restored */
|
|
relink_timers(FALSE);
|
|
relink_light_sources(FALSE);
|
|
return(TRUE);
|
|
}
|
|
|
|
/* update game state pointers to those valid for the current level (so we
|
|
* don't dereference a wild u.ustuck when saving the game state, for instance)
|
|
*/
|
|
STATIC_OVL void
|
|
restlevelstate(stuckid, steedid)
|
|
unsigned int stuckid, steedid; /* STEED */
|
|
{
|
|
register struct monst *mtmp;
|
|
|
|
if (stuckid) {
|
|
for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
|
|
if (mtmp->m_id == stuckid) break;
|
|
if (!mtmp) panic("Cannot find the monster ustuck.");
|
|
u.ustuck = mtmp;
|
|
}
|
|
#ifdef STEED
|
|
if (steedid) {
|
|
for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
|
|
if (mtmp->m_id == steedid) break;
|
|
if (!mtmp) panic("Cannot find the monster usteed.");
|
|
u.usteed = mtmp;
|
|
remove_monster(mtmp->mx, mtmp->my);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*ARGSUSED*/ /* fd used in MFLOPPY only */
|
|
STATIC_OVL int
|
|
restlevelfile(fd, ltmp)
|
|
register int fd;
|
|
xchar ltmp;
|
|
#if defined(macintosh) && (defined(__SC__) || defined(__MRC__))
|
|
# pragma unused(fd)
|
|
#endif
|
|
{
|
|
register int nfd;
|
|
char whynot[BUFSZ];
|
|
|
|
nfd = create_levelfile(ltmp, whynot);
|
|
if (nfd < 0) {
|
|
/* BUG: should suppress any attempt to write a panic
|
|
save file if file creation is now failing... */
|
|
panic("restlevelfile: %s", whynot);
|
|
}
|
|
#ifdef MFLOPPY
|
|
if (!savelev(nfd, ltmp, COUNT_SAVE)) {
|
|
|
|
/* The savelev can't proceed because the size required
|
|
* is greater than the available disk space.
|
|
*/
|
|
pline("Not enough space on `%s' to restore your game.",
|
|
levels);
|
|
|
|
/* Remove levels and bones that may have been created.
|
|
*/
|
|
(void) close(nfd);
|
|
# ifdef AMIGA
|
|
clearlocks();
|
|
# else
|
|
eraseall(levels, alllevels);
|
|
eraseall(levels, allbones);
|
|
|
|
/* Perhaps the person would like to play without a
|
|
* RAMdisk.
|
|
*/
|
|
if (ramdisk) {
|
|
/* PlaywoRAMdisk may not return, but if it does
|
|
* it is certain that ramdisk will be 0.
|
|
*/
|
|
playwoRAMdisk();
|
|
/* Rewind save file and try again */
|
|
(void) lseek(fd, (off_t)0, 0);
|
|
(void) validate(fd, (char *)0); /* skip version etc */
|
|
return dorecover(fd); /* 0 or 1 */
|
|
} else {
|
|
# endif
|
|
pline("Be seeing you...");
|
|
terminate(EXIT_SUCCESS);
|
|
# ifndef AMIGA
|
|
}
|
|
# endif
|
|
}
|
|
#endif
|
|
bufon(nfd);
|
|
savelev(nfd, ltmp, WRITE_SAVE | FREE_SAVE);
|
|
bclose(nfd);
|
|
return(2);
|
|
}
|
|
|
|
int
|
|
dorecover(fd)
|
|
register int fd;
|
|
{
|
|
unsigned int stuckid = 0, steedid = 0; /* not a register */
|
|
xchar ltmp;
|
|
int rtmp;
|
|
struct obj *otmp;
|
|
|
|
restoring = TRUE;
|
|
get_plname_from_file(fd, plname);
|
|
getlev(fd, 0, (xchar)0, FALSE);
|
|
if (!restgamestate(fd, &stuckid, &steedid)) {
|
|
display_nhwindow(WIN_MESSAGE, TRUE);
|
|
savelev(-1, 0, FREE_SAVE); /* discard current level */
|
|
(void) close(fd);
|
|
(void) delete_savefile();
|
|
restoring = FALSE;
|
|
return(0);
|
|
}
|
|
restlevelstate(stuckid, steedid);
|
|
#ifdef INSURANCE
|
|
savestateinlock();
|
|
#endif
|
|
rtmp = restlevelfile(fd, ledger_no(&u.uz));
|
|
if (rtmp < 2) return(rtmp); /* dorecover called recursively */
|
|
|
|
/* these pointers won't be valid while we're processing the
|
|
* other levels, but they'll be reset again by restlevelstate()
|
|
* afterwards, and in the meantime at least u.usteed may mislead
|
|
* place_monster() on other levels
|
|
*/
|
|
u.ustuck = (struct monst *)0;
|
|
#ifdef STEED
|
|
u.usteed = (struct monst *)0;
|
|
#endif
|
|
|
|
#ifdef MICRO
|
|
# ifdef AMII_GRAPHICS
|
|
{
|
|
extern struct window_procs amii_procs;
|
|
if(windowprocs.win_init_nhwindows== amii_procs.win_init_nhwindows){
|
|
extern winid WIN_BASE;
|
|
clear_nhwindow(WIN_BASE); /* hack until there's a hook for this */
|
|
}
|
|
}
|
|
# else
|
|
clear_nhwindow(WIN_MAP);
|
|
# endif
|
|
clear_nhwindow(WIN_MESSAGE);
|
|
You("return to level %d in %s%s.",
|
|
depth(&u.uz), dungeons[u.uz.dnum].dname,
|
|
flags.debug ? " while in debug mode" :
|
|
flags.explore ? " while in explore mode" : "");
|
|
curs(WIN_MAP, 1, 1);
|
|
dotcnt = 0;
|
|
dotrow = 2;
|
|
if (strncmpi("X11", windowprocs.name, 3))
|
|
putstr(WIN_MAP, 0, "Restoring:");
|
|
#endif
|
|
restoreprocs.mread_flags = 1; /* return despite error */
|
|
while(1) {
|
|
mread(fd, (genericptr_t) <mp, sizeof ltmp);
|
|
if (restoreprocs.mread_flags == -1)
|
|
break;
|
|
getlev(fd, 0, ltmp, FALSE);
|
|
#ifdef MICRO
|
|
curs(WIN_MAP, 1+dotcnt++, dotrow);
|
|
if (dotcnt >= (COLNO - 1)) {
|
|
dotrow++;
|
|
dotcnt = 0;
|
|
}
|
|
if (strncmpi("X11", windowprocs.name, 3)){
|
|
putstr(WIN_MAP, 0, ".");
|
|
}
|
|
mark_synch();
|
|
#endif
|
|
rtmp = restlevelfile(fd, ltmp);
|
|
if (rtmp < 2) return(rtmp); /* dorecover called recursively */
|
|
}
|
|
restoreprocs.mread_flags = 0;
|
|
|
|
#ifdef BSD
|
|
(void) lseek(fd, 0L, 0);
|
|
#else
|
|
(void) lseek(fd, (off_t)0, 0);
|
|
#endif
|
|
(void) validate(fd, (char *)0); /* skip version and savefile info */
|
|
get_plname_from_file(fd, plname);
|
|
|
|
getlev(fd, 0, (xchar)0, FALSE);
|
|
(void) close(fd);
|
|
|
|
/* Now set the restore settings to match the
|
|
* settings used by the save file output routines
|
|
*/
|
|
reset_restpref();
|
|
|
|
if (!wizard && !discover)
|
|
(void) delete_savefile();
|
|
#ifdef REINCARNATION
|
|
if (Is_rogue_level(&u.uz)) assign_rogue_graphics(TRUE);
|
|
#endif
|
|
#ifdef USE_TILES
|
|
substitute_tiles(&u.uz);
|
|
#endif
|
|
restlevelstate(stuckid, steedid);
|
|
#ifdef MFLOPPY
|
|
gameDiskPrompt();
|
|
#endif
|
|
max_rank_sz(); /* to recompute mrank_sz (botl.c) */
|
|
/* take care of iron ball & chain */
|
|
for(otmp = fobj; otmp; otmp = otmp->nobj)
|
|
if(otmp->owornmask)
|
|
setworn(otmp, otmp->owornmask);
|
|
|
|
/* in_use processing must be after:
|
|
* + The inventory has been read so that freeinv() works.
|
|
* + The current level has been restored so billing information
|
|
* is available.
|
|
*/
|
|
inven_inuse(FALSE);
|
|
|
|
load_qtlist(); /* re-load the quest text info */
|
|
/* Set up the vision internals, after levl[] data is loaded */
|
|
/* but before docrt(). */
|
|
vision_reset();
|
|
vision_full_recalc = 1; /* recompute vision (not saved) */
|
|
|
|
run_timers(); /* expire all timers that have gone off while away */
|
|
docrt();
|
|
restoring = FALSE;
|
|
clear_nhwindow(WIN_MESSAGE);
|
|
program_state.something_worth_saving++; /* useful data now exists */
|
|
|
|
/* Success! */
|
|
welcome(FALSE);
|
|
return(1);
|
|
}
|
|
|
|
STATIC_OVL void
|
|
rest_levl(fd, rlecomp)
|
|
int fd;
|
|
boolean rlecomp;
|
|
{
|
|
#ifdef RLECOMP
|
|
short i, j;
|
|
uchar len;
|
|
struct rm r;
|
|
|
|
if (rlecomp) {
|
|
# if defined(MAC)
|
|
/* Suppress warning about used before set */
|
|
(void) memset((genericptr_t) &r, 0, sizeof(r));
|
|
# endif
|
|
i = 0; j = 0; len = 0;
|
|
while(i < ROWNO) {
|
|
while(j < COLNO) {
|
|
if(len > 0) {
|
|
levl[j][i] = r;
|
|
len -= 1;
|
|
j += 1;
|
|
} else {
|
|
mread(fd, (genericptr_t)&len, sizeof(uchar));
|
|
mread(fd, (genericptr_t)&r, sizeof(struct rm));
|
|
}
|
|
}
|
|
j = 0;
|
|
i += 1;
|
|
}
|
|
} else
|
|
#endif /* RLECOMP */
|
|
mread(fd, (genericptr_t) levl, sizeof(levl));
|
|
}
|
|
|
|
void
|
|
trickery(reason)
|
|
char *reason;
|
|
{
|
|
pline("Strange, this map is not as I remember it.");
|
|
pline("Somebody is trying some trickery here...");
|
|
pline("This game is void.");
|
|
Strcpy(killer.name, reason ? reason : "");
|
|
done(TRICKED);
|
|
}
|
|
|
|
void
|
|
getlev(fd, pid, lev, ghostly)
|
|
int fd, pid;
|
|
xchar lev;
|
|
boolean ghostly;
|
|
{
|
|
register struct trap *trap;
|
|
register struct monst *mtmp;
|
|
branch *br;
|
|
int hpid;
|
|
xchar dlvl;
|
|
int x, y;
|
|
#ifdef TOS
|
|
short tlev;
|
|
#endif
|
|
|
|
if (ghostly)
|
|
clear_id_mapping();
|
|
|
|
#if defined(MSDOS) || defined(OS2)
|
|
setmode(fd, O_BINARY);
|
|
#endif
|
|
/* Load the old fruit info. We have to do it first, so the
|
|
* information is available when restoring the objects.
|
|
*/
|
|
if (ghostly) oldfruit = loadfruitchn(fd);
|
|
|
|
/* First some sanity checks */
|
|
mread(fd, (genericptr_t) &hpid, sizeof(hpid));
|
|
/* CHECK: This may prevent restoration */
|
|
#ifdef TOS
|
|
mread(fd, (genericptr_t) &tlev, sizeof(tlev));
|
|
dlvl=tlev&0x00ff;
|
|
#else
|
|
mread(fd, (genericptr_t) &dlvl, sizeof(dlvl));
|
|
#endif
|
|
if ((pid && pid != hpid) || (lev && dlvl != lev)) {
|
|
char trickbuf[BUFSZ];
|
|
|
|
if (pid && pid != hpid)
|
|
Sprintf(trickbuf, "PID (%d) doesn't match saved PID (%d)!",
|
|
hpid, pid);
|
|
else
|
|
Sprintf(trickbuf, "This is level %d, not %d!", dlvl, lev);
|
|
#ifdef WIZARD
|
|
if (wizard) pline(trickbuf);
|
|
#endif
|
|
trickery(trickbuf);
|
|
}
|
|
rest_levl(fd, (boolean)((sfrestinfo.sfi1 & SFI1_RLECOMP) == SFI1_RLECOMP));
|
|
mread(fd, (genericptr_t)&omoves, sizeof(omoves));
|
|
mread(fd, (genericptr_t)&upstair, sizeof(stairway));
|
|
mread(fd, (genericptr_t)&dnstair, sizeof(stairway));
|
|
mread(fd, (genericptr_t)&upladder, sizeof(stairway));
|
|
mread(fd, (genericptr_t)&dnladder, sizeof(stairway));
|
|
mread(fd, (genericptr_t)&sstairs, sizeof(stairway));
|
|
mread(fd, (genericptr_t)&updest, sizeof(dest_area));
|
|
mread(fd, (genericptr_t)&dndest, sizeof(dest_area));
|
|
mread(fd, (genericptr_t)&level.flags, sizeof(level.flags));
|
|
mread(fd, (genericptr_t)doors, sizeof(doors));
|
|
rest_rooms(fd); /* No joke :-) */
|
|
if (nroom)
|
|
doorindex = rooms[nroom - 1].fdoor + rooms[nroom - 1].doorct;
|
|
else
|
|
doorindex = 0;
|
|
|
|
restore_timers(fd, RANGE_LEVEL, ghostly, monstermoves - omoves);
|
|
restore_light_sources(fd);
|
|
fmon = restmonchn(fd, ghostly);
|
|
|
|
/* regenerate animals while on another level */
|
|
if (u.uz.dlevel) {
|
|
register struct monst *mtmp2;
|
|
|
|
for (mtmp = fmon; mtmp; mtmp = mtmp2) {
|
|
mtmp2 = mtmp->nmon;
|
|
if (ghostly) {
|
|
/* reset peaceful/malign relative to new character */
|
|
/* shopkeepers will reset based on name */
|
|
if(!mtmp->isshk) {
|
|
if (is_unicorn(mtmp->data) &&
|
|
sgn(u.ualign.type) == sgn(mtmp->data->maligntyp))
|
|
mtmp->mpeaceful = TRUE;
|
|
else
|
|
mtmp->mpeaceful = peace_minded(mtmp->data);
|
|
}
|
|
set_malign(mtmp);
|
|
} else if (monstermoves > omoves)
|
|
mon_catchup_elapsed_time(mtmp, monstermoves - omoves);
|
|
|
|
/* update shape-changers in case protection against
|
|
them is different now than when the level was saved */
|
|
restore_cham(mtmp);
|
|
}
|
|
}
|
|
|
|
rest_worm(fd); /* restore worm information */
|
|
ftrap = 0;
|
|
while (trap = newtrap(),
|
|
mread(fd, (genericptr_t)trap, sizeof(struct trap)),
|
|
trap->tx != 0) { /* need "!= 0" to work around DICE 3.0 bug */
|
|
trap->ntrap = ftrap;
|
|
ftrap = trap;
|
|
}
|
|
dealloc_trap(trap);
|
|
fobj = restobjchn(fd, ghostly, FALSE);
|
|
find_lev_obj();
|
|
/* restobjchn()'s `frozen' argument probably ought to be a callback
|
|
routine so that we can check for objects being buried under ice */
|
|
level.buriedobjlist = restobjchn(fd, ghostly, FALSE);
|
|
billobjs = restobjchn(fd, ghostly, FALSE);
|
|
rest_engravings(fd);
|
|
|
|
/* reset level.monsters for new level */
|
|
for (x = 0; x < COLNO; x++)
|
|
for (y = 0; y < ROWNO; y++)
|
|
level.monsters[x][y] = (struct monst *) 0;
|
|
for (mtmp = level.monlist; mtmp; mtmp = mtmp->nmon) {
|
|
if (mtmp->isshk)
|
|
set_residency(mtmp, FALSE);
|
|
place_monster(mtmp, mtmp->mx, mtmp->my);
|
|
if (mtmp->wormno) place_wsegs(mtmp);
|
|
}
|
|
restdamage(fd, ghostly);
|
|
|
|
rest_regions(fd, ghostly);
|
|
if (ghostly) {
|
|
/* Now get rid of all the temp fruits... */
|
|
freefruitchn(oldfruit), oldfruit = 0;
|
|
|
|
if (lev > ledger_no(&medusa_level) &&
|
|
lev < ledger_no(&stronghold_level) && xdnstair == 0) {
|
|
coord cc;
|
|
|
|
mazexy(&cc);
|
|
xdnstair = cc.x;
|
|
ydnstair = cc.y;
|
|
levl[cc.x][cc.y].typ = STAIRS;
|
|
}
|
|
|
|
br = Is_branchlev(&u.uz);
|
|
if (br && u.uz.dlevel == 1) {
|
|
d_level ltmp;
|
|
|
|
if (on_level(&u.uz, &br->end1))
|
|
assign_level(<mp, &br->end2);
|
|
else
|
|
assign_level(<mp, &br->end1);
|
|
|
|
switch(br->type) {
|
|
case BR_STAIR:
|
|
case BR_NO_END1:
|
|
case BR_NO_END2: /* OK to assign to sstairs if it's not used */
|
|
assign_level(&sstairs.tolev, <mp);
|
|
break;
|
|
case BR_PORTAL: /* max of 1 portal per level */
|
|
{
|
|
register struct trap *ttmp;
|
|
for(ttmp = ftrap; ttmp; ttmp = ttmp->ntrap)
|
|
if (ttmp->ttyp == MAGIC_PORTAL)
|
|
break;
|
|
if (!ttmp) panic("getlev: need portal but none found");
|
|
assign_level(&ttmp->dst, <mp);
|
|
}
|
|
break;
|
|
}
|
|
} else if (!br) {
|
|
/* Remove any dangling portals. */
|
|
register struct trap *ttmp;
|
|
for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap)
|
|
if (ttmp->ttyp == MAGIC_PORTAL) {
|
|
deltrap(ttmp);
|
|
break; /* max of 1 portal/level */
|
|
}
|
|
}
|
|
}
|
|
|
|
/* must come after all mons & objs are restored */
|
|
relink_timers(ghostly);
|
|
relink_light_sources(ghostly);
|
|
reset_oattached_mids(ghostly);
|
|
|
|
if (ghostly)
|
|
clear_id_mapping();
|
|
}
|
|
|
|
void
|
|
get_plname_from_file(fd, plbuf)
|
|
int fd;
|
|
char *plbuf;
|
|
{
|
|
int rlen, pltmpsiz = 0;
|
|
rlen = read(fd, (genericptr_t) &pltmpsiz, sizeof(pltmpsiz));
|
|
rlen = read(fd, (genericptr_t) plbuf, pltmpsiz);
|
|
return;
|
|
}
|
|
|
|
STATIC_OVL void
|
|
restore_msghistory(fd)
|
|
register int fd;
|
|
{
|
|
int msgsize, msgcount = 0;
|
|
char msg[BUFSZ];
|
|
|
|
while(1) {
|
|
mread(fd, (genericptr_t) &msgsize, sizeof(msgsize));
|
|
if(msgsize == -1) break;
|
|
if (msgsize > (BUFSZ - 1))
|
|
panic("restore_msghistory: msg too big (%d)", msgsize);
|
|
mread(fd, (genericptr_t)msg, msgsize);
|
|
msg[msgsize] = '\0';
|
|
putmsghistory(msg);
|
|
++msgcount;
|
|
}
|
|
#ifdef DEBUG_MSGCOUNT
|
|
pline("Read %d messages from savefile.", msgcount);
|
|
#endif
|
|
}
|
|
|
|
/* Clear all structures for object and monster ID mapping. */
|
|
STATIC_OVL void
|
|
clear_id_mapping()
|
|
{
|
|
struct bucket *curr;
|
|
|
|
while ((curr = id_map) != 0) {
|
|
id_map = curr->next;
|
|
free((genericptr_t) curr);
|
|
}
|
|
n_ids_mapped = 0;
|
|
}
|
|
|
|
/* Add a mapping to the ID map. */
|
|
STATIC_OVL void
|
|
add_id_mapping(gid, nid)
|
|
unsigned gid, nid;
|
|
{
|
|
int idx;
|
|
|
|
idx = n_ids_mapped % N_PER_BUCKET;
|
|
/* idx is zero on first time through, as well as when a new bucket is */
|
|
/* needed */
|
|
if (idx == 0) {
|
|
struct bucket *gnu = (struct bucket *) alloc(sizeof(struct bucket));
|
|
gnu->next = id_map;
|
|
id_map = gnu;
|
|
}
|
|
|
|
id_map->map[idx].gid = gid;
|
|
id_map->map[idx].nid = nid;
|
|
n_ids_mapped++;
|
|
}
|
|
|
|
/*
|
|
* Global routine to look up a mapping. If found, return TRUE and fill
|
|
* in the new ID value. Otherwise, return false and return -1 in the new
|
|
* ID.
|
|
*/
|
|
boolean
|
|
lookup_id_mapping(gid, nidp)
|
|
unsigned gid, *nidp;
|
|
{
|
|
int i;
|
|
struct bucket *curr;
|
|
|
|
if (n_ids_mapped)
|
|
for (curr = id_map; curr; curr = curr->next) {
|
|
/* first bucket might not be totally full */
|
|
if (curr == id_map) {
|
|
i = n_ids_mapped % N_PER_BUCKET;
|
|
if (i == 0) i = N_PER_BUCKET;
|
|
} else
|
|
i = N_PER_BUCKET;
|
|
|
|
while (--i >= 0)
|
|
if (gid == curr->map[i].gid) {
|
|
*nidp = curr->map[i].nid;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
STATIC_OVL void
|
|
reset_oattached_mids(ghostly)
|
|
boolean ghostly;
|
|
{
|
|
struct obj *otmp;
|
|
unsigned oldid, nid;
|
|
for (otmp = fobj; otmp; otmp = otmp->nobj) {
|
|
if (ghostly && otmp->oattached == OATTACHED_MONST && otmp->oxlth) {
|
|
struct monst *mtmp = (struct monst *)otmp->oextra;
|
|
|
|
mtmp->m_id = 0;
|
|
mtmp->mpeaceful = mtmp->mtame = 0; /* pet's owner died! */
|
|
}
|
|
if (ghostly && otmp->oattached == OATTACHED_M_ID) {
|
|
(void) memcpy((genericptr_t)&oldid, (genericptr_t)otmp->oextra,
|
|
sizeof(oldid));
|
|
if (lookup_id_mapping(oldid, &nid))
|
|
(void) memcpy((genericptr_t)otmp->oextra, (genericptr_t)&nid,
|
|
sizeof(nid));
|
|
else
|
|
otmp->oattached = OATTACHED_NOTHING;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
minit()
|
|
{
|
|
(*restoreprocs.restore_minit)();
|
|
return;
|
|
}
|
|
|
|
void
|
|
mread(fd, buf, len)
|
|
register int fd;
|
|
register genericptr_t buf;
|
|
register unsigned int len;
|
|
{
|
|
(*restoreprocs.restore_mread)(fd, buf, len);
|
|
return;
|
|
}
|
|
|
|
/* examine the version info and the savefile_info data
|
|
that immediately follows it.
|
|
Return 0 if it passed the checks.
|
|
Return 1 if it failed the version check.
|
|
Return 2 if it failed the savefile feature check.
|
|
Return -1 if it failed for some unknown reason.
|
|
*/
|
|
int
|
|
validate(fd, name)
|
|
int fd;
|
|
const char *name;
|
|
{
|
|
int rlen;
|
|
struct savefile_info sfi;
|
|
unsigned long compatible;
|
|
boolean verbose = name ? TRUE : FALSE, reslt = FALSE;
|
|
|
|
if (!(reslt = uptodate(fd, name))) return 1;
|
|
|
|
rlen = read(fd, (genericptr_t) &sfi, sizeof sfi);
|
|
minit(); /* ZEROCOMP */
|
|
if (rlen == 0) {
|
|
if (verbose) {
|
|
pline("File \"%s\" is empty during save file feature check?", name);
|
|
wait_synch();
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
compatible = (sfi.sfi1 & sfcap.sfi1);
|
|
|
|
if ((sfi.sfi1 & SFI1_ZEROCOMP) == SFI1_ZEROCOMP) {
|
|
if ((compatible & SFI1_ZEROCOMP) != SFI1_ZEROCOMP) {
|
|
if (verbose) {
|
|
pline("File \"%s\" has incompatible ZEROCOMP compression.", name);
|
|
wait_synch();
|
|
}
|
|
return 2;
|
|
} else if ((sfrestinfo.sfi1 & SFI1_ZEROCOMP) != SFI1_ZEROCOMP) {
|
|
set_restpref("zerocomp");
|
|
}
|
|
}
|
|
|
|
if ((sfi.sfi1 & SFI1_EXTERNALCOMP) == SFI1_EXTERNALCOMP) {
|
|
if ((compatible & SFI1_EXTERNALCOMP) != SFI1_EXTERNALCOMP) {
|
|
if (verbose) {
|
|
pline("File \"%s\" lacks required internal compression.", name);
|
|
wait_synch();
|
|
}
|
|
return 2;
|
|
} else if ((sfrestinfo.sfi1 & SFI1_EXTERNALCOMP) != SFI1_EXTERNALCOMP) {
|
|
set_restpref("externalcomp");
|
|
}
|
|
}
|
|
|
|
/* RLECOMP check must be last, after ZEROCOMP or INTERNALCOMP adjustments */
|
|
if ((sfi.sfi1 & SFI1_RLECOMP) == SFI1_RLECOMP) {
|
|
if ((compatible & SFI1_RLECOMP) != SFI1_RLECOMP) {
|
|
if (verbose) {
|
|
pline("File \"%s\" has incompatible run-length compression.", name);
|
|
wait_synch();
|
|
}
|
|
return 2;
|
|
} else if ((sfrestinfo.sfi1 & SFI1_RLECOMP) != SFI1_RLECOMP) {
|
|
set_restpref("rlecomp");
|
|
}
|
|
}
|
|
/* savefile does not have RLECOMP level location compression, so adjust */
|
|
else set_restpref("!rlecomp");
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
reset_restpref()
|
|
{
|
|
#ifdef ZEROCOMP
|
|
if (iflags.zerocomp)
|
|
set_restpref("zerocomp");
|
|
else
|
|
#endif
|
|
set_restpref("externalcomp");
|
|
#ifdef RLECOMP
|
|
if (iflags.rlecomp)
|
|
set_restpref("rlecomp");
|
|
else
|
|
#endif
|
|
set_restpref("!rlecomp");
|
|
}
|
|
|
|
void
|
|
set_restpref(suitename)
|
|
const char *suitename;
|
|
{
|
|
if (!strcmpi(suitename, "externalcomp")) {
|
|
restoreprocs.name = "externalcomp";
|
|
restoreprocs.restore_mread = def_mread;
|
|
restoreprocs.restore_minit = def_minit;
|
|
sfrestinfo.sfi1 |= SFI1_EXTERNALCOMP;
|
|
sfrestinfo.sfi1 &= ~SFI1_ZEROCOMP;
|
|
def_minit();
|
|
}
|
|
if (!strcmpi(suitename, "!rlecomp")) {
|
|
sfrestinfo.sfi1 &= ~SFI1_RLECOMP;
|
|
}
|
|
#ifdef ZEROCOMP
|
|
if (!strcmpi(suitename, "zerocomp")) {
|
|
restoreprocs.name = "zerocomp";
|
|
restoreprocs.restore_mread = zerocomp_mread;
|
|
restoreprocs.restore_minit = zerocomp_minit;
|
|
sfrestinfo.sfi1 |= SFI1_ZEROCOMP;
|
|
sfrestinfo.sfi1 &= ~SFI1_EXTERNALCOMP;
|
|
zerocomp_minit();
|
|
}
|
|
#endif
|
|
#ifdef RLECOMP
|
|
if (!strcmpi(suitename, "rlecomp")) {
|
|
sfrestinfo.sfi1 |= SFI1_RLECOMP;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef ZEROCOMP
|
|
#define RLESC '\0' /* Leading character for run of RLESC's */
|
|
|
|
#ifndef ZEROCOMP_BUFSIZ
|
|
#define ZEROCOMP_BUFSIZ BUFSZ
|
|
#endif
|
|
static NEARDATA unsigned char inbuf[ZEROCOMP_BUFSIZ];
|
|
static NEARDATA unsigned short inbufp = 0;
|
|
static NEARDATA unsigned short inbufsz = 0;
|
|
static NEARDATA short inrunlength = -1;
|
|
static NEARDATA int mreadfd;
|
|
|
|
STATIC_OVL int
|
|
zerocomp_mgetc()
|
|
{
|
|
if (inbufp >= inbufsz) {
|
|
inbufsz = read(mreadfd, (genericptr_t)inbuf, sizeof inbuf);
|
|
if (!inbufsz) {
|
|
if (inbufp > sizeof inbuf)
|
|
error("EOF on file #%d.\n", mreadfd);
|
|
inbufp = 1 + sizeof inbuf; /* exactly one warning :-) */
|
|
return -1;
|
|
}
|
|
inbufp = 0;
|
|
}
|
|
return inbuf[inbufp++];
|
|
}
|
|
|
|
STATIC_OVL void
|
|
zerocomp_minit()
|
|
{
|
|
inbufsz = 0;
|
|
inbufp = 0;
|
|
inrunlength = -1;
|
|
}
|
|
|
|
STATIC_OVL void
|
|
zerocomp_mread(fd, buf, len)
|
|
int fd;
|
|
genericptr_t buf;
|
|
register unsigned len;
|
|
{
|
|
/*register int readlen = 0;*/
|
|
if (fd < 0) error("Restore error; mread attempting to read file %d.", fd);
|
|
mreadfd = fd;
|
|
while (len--) {
|
|
if (inrunlength > 0) {
|
|
inrunlength--;
|
|
*(*((char **)&buf))++ = '\0';
|
|
} else {
|
|
register short ch = zerocomp_mgetc();
|
|
if (ch < 0) {
|
|
restoreprocs.mread_flags = -1;
|
|
return;
|
|
}
|
|
if ((*(*(char **)&buf)++ = (char)ch) == RLESC) {
|
|
inrunlength = zerocomp_mgetc();
|
|
}
|
|
}
|
|
/*readlen++;*/
|
|
}
|
|
}
|
|
#endif /* ZEROCOMP */
|
|
|
|
STATIC_OVL void
|
|
def_minit()
|
|
{
|
|
return;
|
|
}
|
|
|
|
STATIC_OVL void
|
|
def_mread(fd, buf, len)
|
|
register int fd;
|
|
register genericptr_t buf;
|
|
register unsigned int len;
|
|
{
|
|
register int rlen;
|
|
|
|
#if defined(BSD) || defined(ULTRIX)
|
|
rlen = read(fd, buf, (int) len);
|
|
if(rlen != len){
|
|
#else /* e.g. SYSV, __TURBOC__ */
|
|
rlen = read(fd, buf, (unsigned) len);
|
|
if((unsigned)rlen != len){
|
|
#endif
|
|
if (restoreprocs.mread_flags == 1) { /* means "return anyway" */
|
|
restoreprocs.mread_flags = -1;
|
|
return;
|
|
} else {
|
|
pline("Read %d instead of %u bytes.", rlen, len);
|
|
if(restoring) {
|
|
(void) close(fd);
|
|
(void) delete_savefile();
|
|
error("Error restoring old game.");
|
|
}
|
|
panic("Error reading level file.");
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifndef GOLDOBJ
|
|
/*
|
|
* Takes all of the gold objects out of the invent or
|
|
* mtmp->minvent chain and puts it into either
|
|
* u.ugold or mtmp->mgold.
|
|
*/
|
|
void
|
|
put_gold_back(head_ptr, goldptr)
|
|
struct obj **head_ptr;
|
|
long *goldptr;
|
|
{
|
|
struct obj *goldobj;
|
|
long gld = 0L;
|
|
|
|
while ((goldobj = gold_in(*head_ptr))) {
|
|
gld += goldobj->quan;
|
|
extract_nobj(goldobj, head_ptr);
|
|
dealloc_obj(goldobj);
|
|
}
|
|
if (gld) *goldptr += gld;
|
|
}
|
|
|
|
STATIC_OVL struct obj *
|
|
gold_in(chn)
|
|
struct obj *chn;
|
|
{
|
|
struct obj *otmp;
|
|
|
|
for(otmp = chn; otmp; otmp = otmp->nobj)
|
|
if(otmp->otyp == GOLD_PIECE)
|
|
return(otmp);
|
|
return((struct obj *) 0);
|
|
}
|
|
#endif /*GOLDOBJ*/
|
|
|
|
/*restore.c*/
|