savefile changes - part 1

This is the first of several savefile-related changes to
follow later. This one is groundwork for those later changes.

Remove internal compression schemes (RLECOMP and ZEROCOMP)
and discard the savefile_info struct that was primarily used to
convey which internal compression schemes had been in use.

Relocate some struct definitions into appropriate header files
for use by code to come in later changes.

Remove the two struct size-related fields from version_info and
from the nmakedefs_s. Instead, include a series of bytes near the
beginning of the savefile, representing the size of each
struct or base data type that impacts the historical savefile
content. Those are referred to as the "critical bytes".
(Related note: the "you" struct required two bytes, low and high,
due to its size).

Compare those critical bytes in a savefile against the NetHack
build that is reading the savefile. This allows mismatch detection
early in the savefile-reading process, and a clean exit, rather than
proceeding to read nonsensical values from the file. Include some
feedback on what the first mismatch was when encountering
one.

For arrays stored in the savefile, use loop-logic in the core
to write/read the array elements one at a time, rather than in
a single blob. This will be required for changes to follow later.
(impacts artiexist[], artidisco[], svd.dungeons[], svl.level_info[],
svl.level.locations[][], msrooms[] field of mapseen, svb.bases[],
svb.disco[] objects[], svm.mvitals[], svs.spl_book[], svd.doors[],
go.oracle_loc[], utrack[], wgrowtime[])

This also adds data model to the long version information.

This invalidates existing save and bones files due to the changes in
the information at the start of the file.
This commit is contained in:
nhmall
2025-04-15 15:35:17 -04:00
parent af3e601ff7
commit a3e12550ea
32 changed files with 962 additions and 752 deletions

View File

@@ -11,12 +11,6 @@ extern int dotcnt; /* shared with save */
extern int dotrow; /* shared with save */
#endif
#ifdef ZEROCOMP
staticfn void zerocomp_minit(void);
staticfn void zerocomp_mread(int, genericptr_t, unsigned int);
staticfn int zerocomp_mgetc(void);
#endif
staticfn void find_lev_obj(void);
staticfn void restlevchn(NHFILE *);
staticfn void restdamage(NHFILE *);
@@ -26,16 +20,19 @@ staticfn void restmon(NHFILE *, struct monst *);
staticfn struct monst *restmonchn(NHFILE *);
staticfn struct fruit *loadfruitchn(NHFILE *);
staticfn void freefruitchn(struct fruit *);
staticfn void rest_levl(NHFILE *);
staticfn void rest_stairs(NHFILE *);
staticfn void ghostfruit(struct obj *);
staticfn boolean restgamestate(NHFILE *);
staticfn void restlevelstate(void);
staticfn int restlevelfile(xint8);
staticfn void rest_bubbles(NHFILE *);
staticfn void restore_gamelog(NHFILE *);
staticfn void restore_msghistory(NHFILE *);
staticfn void reset_oattached_mids(boolean);
staticfn void rest_levl(NHFILE *, boolean);
staticfn void rest_stairs(NHFILE *);
staticfn boolean restgamestate(NHFILE *);
staticfn void rest_bubbles(NHFILE *);
staticfn void restore_gamelog(NHFILE *);
staticfn void restore_msghistory(NHFILE *);
/*
* Save a mapping of IDs from ghost levels to the current level. This
@@ -132,13 +129,14 @@ restlevchn(NHFILE *nhfp)
s_level *tmplev, *x;
svs.sp_levchn = (s_level *) 0;
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, &cnt, sizeof cnt);
}
for (; cnt > 0; cnt--) {
tmplev = (s_level *) alloc(sizeof(s_level));
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, tmplev, sizeof *tmplev);
}
if (!svs.sp_levchn)
svs.sp_levchn = tmplev;
@@ -159,16 +157,18 @@ restdamage(NHFILE *nhfp)
struct damage *tmp_dam;
boolean ghostly = (nhfp->ftype == NHF_BONESFILE);
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, &dmgcount, sizeof dmgcount);
}
counter = (int) dmgcount;
if (!counter)
return;
do {
tmp_dam = (struct damage *) alloc(sizeof *tmp_dam);
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, tmp_dam, sizeof *tmp_dam);
}
if (ghostly)
tmp_dam->when += (svm.moves - go.omoves);
@@ -184,8 +184,9 @@ restobj(NHFILE *nhfp, struct obj *otmp)
{
int buflen = 0;
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, otmp, sizeof *otmp);
}
otmp->lua_ref_cnt = 0;
/* next object pointers are invalid; otmp->cobj needs to be left
@@ -196,18 +197,20 @@ restobj(NHFILE *nhfp, struct obj *otmp)
otmp->oextra = newoextra();
/* oname - object's name */
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, &buflen, sizeof buflen);
}
if (buflen > 0) { /* includes terminating '\0' */
new_oname(otmp, buflen);
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, ONAME(otmp), buflen);
}
}
/* omonst - corpse or statue might retain full monster details */
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, &buflen, sizeof buflen);
}
if (buflen > 0) {
newomonst(otmp);
/* this is actually a monst struct, so we
@@ -216,21 +219,24 @@ restobj(NHFILE *nhfp, struct obj *otmp)
}
/* omailcmd - feedback mechanism for scroll of mail */
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, &buflen, sizeof buflen);
}
if (buflen > 0) {
char *omailcmd = (char *) alloc(buflen);
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, omailcmd, buflen);
}
new_omailcmd(otmp, omailcmd);
free((genericptr_t) omailcmd);
}
/* omid - monster id number, connecting corpse to ghost */
newomid(otmp); /* superfluous; we're already allocated otmp->oextra */
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, &OMID(otmp), sizeof OMID(otmp));
}
}
}
@@ -243,9 +249,9 @@ restobjchn(NHFILE *nhfp, boolean frozen)
boolean ghostly = (nhfp->ftype == NHF_BONESFILE);
while (1) {
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, &buflen, sizeof buflen);
}
if (buflen == -1)
break;
@@ -306,10 +312,11 @@ restobjchn(NHFILE *nhfp, boolean frozen)
staticfn void
restmon(NHFILE *nhfp, struct monst *mtmp)
{
int buflen = 0;
int buflen = 0, mc = 0;
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, mtmp, sizeof *mtmp);
}
/* next monster pointer is invalid */
mtmp->nmon = (struct monst *) 0;
@@ -318,71 +325,85 @@ restmon(NHFILE *nhfp, struct monst *mtmp)
mtmp->mextra = newmextra();
/* mgivenname - monster's name */
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, &buflen, sizeof buflen);
}
if (buflen > 0) { /* includes terminating '\0' */
new_mgivenname(mtmp, buflen);
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, MGIVENNAME(mtmp), buflen);
}
}
/* egd - vault guard */
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, &buflen, sizeof buflen);
}
if (buflen > 0) {
newegd(mtmp);
if (nhfp->structlevel)
Mread(nhfp->fd, EGD(mtmp), sizeof (struct egd));
if (nhfp->structlevel) {
Mread(nhfp->fd, EGD(mtmp), sizeof(struct egd));
}
}
/* epri - temple priest */
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, &buflen, sizeof buflen);
}
if (buflen > 0) {
newepri(mtmp);
if (nhfp->structlevel)
Mread(nhfp->fd, EPRI(mtmp), sizeof (struct epri));
if (nhfp->structlevel) {
Mread(nhfp->fd, EPRI(mtmp), sizeof(struct epri));
}
}
/* eshk - shopkeeper */
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, &buflen, sizeof buflen);
}
if (buflen > 0) {
neweshk(mtmp);
if (nhfp->structlevel)
Mread(nhfp->fd, ESHK(mtmp), sizeof (struct eshk));
if (nhfp->structlevel) {
Mread(nhfp->fd, ESHK(mtmp), sizeof(struct eshk));
}
}
/* emin - minion */
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, &buflen, sizeof buflen);
}
if (buflen > 0) {
newemin(mtmp);
if (nhfp->structlevel)
Mread(nhfp->fd, EMIN(mtmp), sizeof (struct emin));
if (nhfp->structlevel) {
Mread(nhfp->fd, EMIN(mtmp), sizeof(struct emin));
}
}
/* edog - pet */
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, &buflen, sizeof buflen);
}
if (buflen > 0) {
newedog(mtmp);
if (nhfp->structlevel)
Mread(nhfp->fd, EDOG(mtmp), sizeof (struct edog));
if (nhfp->structlevel) {
Mread(nhfp->fd, EDOG(mtmp), sizeof(struct edog));
}
/* sanity check to prevent rn2(0) */
if (EDOG(mtmp)->apport <= 0) {
EDOG(mtmp)->apport = 1;
}
}
/* ebones */
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, &buflen, sizeof buflen);
}
if (buflen > 0) {
newebones(mtmp);
if (nhfp->structlevel)
Mread(nhfp->fd, EBONES(mtmp),
sizeof (struct ebones));
if (nhfp->structlevel) {
Mread(nhfp->fd, EBONES(mtmp), sizeof(struct ebones));
}
}
/* mcorpsenm - obj->corpsenm for mimic posing as corpse or
statue (inline int rather than pointer to something) */
if (nhfp->structlevel)
Mread(nhfp->fd, &MCORPSENM(mtmp), sizeof MCORPSENM(mtmp));
if (nhfp->structlevel) {
Mread(nhfp->fd, &mc, sizeof mc);
}
MCORPSENM(mtmp) = mc;
} /* mextra */
}
@@ -395,8 +416,9 @@ restmonchn(NHFILE *nhfp)
boolean ghostly = (nhfp->ftype == NHF_BONESFILE);
while (1) {
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, &buflen, sizeof buflen);
}
if (buflen == -1)
break;
@@ -472,8 +494,9 @@ loadfruitchn(NHFILE *nhfp)
flist = 0;
for (;;) {
fnext = newfruit();
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, fnext, sizeof *fnext);
}
if (fnext->fid != 0) {
fnext->nextf = flist;
flist = fnext;
@@ -520,6 +543,7 @@ ghostfruit(struct obj *otmp)
staticfn boolean
restgamestate(NHFILE *nhfp)
{
int i;
struct flag newgameflags;
struct context_info newgamecontext; /* all 0, but has some pointers */
struct obj *otmp;
@@ -528,8 +552,9 @@ restgamestate(NHFILE *nhfp)
unsigned long uid = 0;
boolean defer_perm_invent, restoring_special;
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, &uid, sizeof uid);
}
if (SYSOPT_CHECK_SAVE_UID
&& uid != (unsigned long) getuid()) { /* strange ... */
@@ -541,8 +566,9 @@ restgamestate(NHFILE *nhfp)
}
newgamecontext = svc.context; /* copy statically init'd context */
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, &svc.context, sizeof svc.context);
}
svc.context.warntype.species = (ismnum(svc.context.warntype.speciesidx))
? &mons[svc.context.warntype.speciesidx]
: (struct permonst *) 0;
@@ -554,8 +580,9 @@ restgamestate(NHFILE *nhfp)
file option values instead of keeping old save file option values
if partial restore fails and we resort to starting a new game */
newgameflags = flags;
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, &flags, sizeof flags);
}
/* avoid keeping permanent inventory window up to date during restore
(setworn() calls update_inventory); attempting to include the cost
@@ -583,8 +610,9 @@ restgamestate(NHFILE *nhfp)
#ifdef AMII_GRAPHICS
amii_setpens(amii_numcolors); /* use colors from save file */
#endif
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, &u, sizeof u);
}
gy.youmonst.cham = u.mcham;
if (restoring_special && iflags.explore_error_flag) {
@@ -594,15 +622,17 @@ restgamestate(NHFILE *nhfp)
u.uhp = 0;
}
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, timebuf, 14);
}
timebuf[14] = '\0';
ubirthday = time_from_yyyymmddhhmmss(timebuf);
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, &urealtime.realtime, sizeof urealtime.realtime);
if (nhfp->structlevel)
}
if (nhfp->structlevel) {
Mread(nhfp->fd, timebuf, 14);
}
timebuf[14] = '\0';
urealtime.start_timing = time_from_yyyymmddhhmmss(timebuf);
@@ -653,10 +683,11 @@ restgamestate(NHFILE *nhfp)
gm.migrating_objs = restobjchn(nhfp, FALSE);
gm.migrating_mons = restmonchn(nhfp);
if (nhfp->structlevel) {
Mread(nhfp->fd, &svm.mvitals[0], sizeof svm.mvitals);
for (i = 0; i < NUMMONS; ++i) {
if (nhfp->structlevel) {
Mread(nhfp->fd, &svm.mvitals[i], sizeof (struct mvitals));
}
}
/*
* There are some things after this that can have unintended display
* side-effects too early in the game.
@@ -683,10 +714,17 @@ restgamestate(NHFILE *nhfp)
restlevchn(nhfp);
if (nhfp->structlevel) {
Mread(nhfp->fd, &svm.moves, sizeof svm.moves);
/* hero_seq isn't saved and restored because it can be recalculated */
gh.hero_seq = svm.moves << 3; /* normally handled in moveloop() */
}
/* hero_seq isn't saved and restored because it can be recalculated */
gh.hero_seq = svm.moves << 3; /* normally handled in moveloop() */
if (nhfp->structlevel) {
Mread(nhfp->fd, &svq.quest_status, sizeof svq.quest_status);
Mread(nhfp->fd, svs.spl_book, (MAXSPELL + 1) * sizeof (struct spell));
}
for (i = 0; i < (MAXSPELL + 1); ++i) {
if (nhfp->structlevel) {
Mread(nhfp->fd, &svs.spl_book[i],
sizeof (struct spell));
}
}
restore_artifacts(nhfp);
restore_oracles(nhfp);
@@ -819,9 +857,9 @@ dorecover(NHFILE *nhfp)
while (1) {
if (nhfp->structlevel) {
Mread(nhfp->fd, &ltmp, sizeof ltmp);
if (restoreinfo.mread_flags == -1)
break;
}
if (restoreinfo.mread_flags == -1)
break;
getlev(nhfp, 0, ltmp);
#ifdef MICRO
curs(WIN_MAP, 1 + dotcnt++, dotrow);
@@ -839,6 +877,7 @@ dorecover(NHFILE *nhfp)
return rtmp; /* dorecover called recursively */
}
restoreinfo.mread_flags = 0;
rewind_nhfile(nhfp); /* return to beginning of file */
(void) validate(nhfp, (char *) 0, FALSE);
get_plname_from_file(nhfp, svp.plname, TRUE);
@@ -939,14 +978,16 @@ restcemetery(NHFILE *nhfp, struct cemetery **cemeteryaddr)
struct cemetery *bonesinfo, **bonesaddr;
int cflag = 0;
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, &cflag, sizeof cflag);
}
if (cflag == 0) {
bonesaddr = cemeteryaddr;
do {
bonesinfo = (struct cemetery *) alloc(sizeof *bonesinfo);
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, bonesinfo, sizeof *bonesinfo);
}
*bonesaddr = bonesinfo;
bonesaddr = &(*bonesaddr)->next;
} while (*bonesaddr);
@@ -957,46 +998,16 @@ restcemetery(NHFILE *nhfp, struct cemetery **cemeteryaddr)
/*ARGSUSED*/
staticfn void
rest_levl(
NHFILE *nhfp,
#ifdef RLECOMP
boolean rlecomp
#else
boolean rlecomp UNUSED
#endif
)
rest_levl(NHFILE *nhfp)
{
#ifdef RLECOMP
short i, j;
uchar len;
struct rm r;
int c, r;
if (rlecomp) {
(void) memset((genericptr_t) &r, 0, sizeof(r));
i = 0;
j = 0;
len = 0;
while (i < ROWNO) {
while (j < COLNO) {
if (len > 0) {
levl[j][i] = r;
len -= 1;
j += 1;
} else {
if (nhfp->structlevel) {
Mread(nhfp->fd, &len, sizeof len);
Mread(nhfp->fd, &r, sizeof r);
}
}
for (c = 0; c < COLNO; ++c) {
for (r = 0; r < ROWNO; ++r) {
if (nhfp->structlevel) {
Mread(nhfp->fd, &levl[c][r], sizeof (struct rm));
}
j = 0;
i += 1;
}
return;
}
#endif /* RLECOMP */
if (nhfp->structlevel) {
Mread(nhfp->fd, levl, sizeof levl);
}
}
@@ -1015,12 +1026,14 @@ getlev(NHFILE *nhfp, int pid, xint8 lev)
{
struct trap *trap;
struct monst *mtmp;
long elapsed;
branch *br;
int x, y;
long elapsed = 0L;
int hpid = 0;
xint8 dlvl = 0;
int x, y;
int i, c, r;
boolean ghostly = (nhfp->ftype == NHF_BONESFILE);
coord *tmpc = 0;
#ifdef TOS
short tlev;
#endif
@@ -1041,18 +1054,13 @@ getlev(NHFILE *nhfp, int pid, xint8 lev)
go.oldfruit = loadfruitchn(nhfp);
/* First some sanity checks */
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, &hpid, sizeof hpid);
}
/* CHECK: This may prevent restoration */
#ifdef TOS
if (nhfp->structlevel)
Mread(nhfp->fd, &tlev, sizeof tlev);
dlvl = tlev & 0x00ff;
#else
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, &dlvl, sizeof dlvl);
#endif
}
if ((pid && pid != hpid) || (lev && dlvl != lev)) {
char trickbuf[BUFSZ];
@@ -1066,48 +1074,58 @@ getlev(NHFILE *nhfp, int pid, xint8 lev)
trickery(trickbuf);
}
restcemetery(nhfp, &svl.level.bonesinfo);
rest_levl(nhfp,
(boolean) ((sfrestinfo.sfi1 & SFI1_RLECOMP) == SFI1_RLECOMP));
if (nhfp->structlevel) {
Mread(nhfp->fd, svl.lastseentyp, sizeof svl.lastseentyp);
Mread(nhfp->fd, &go.omoves, sizeof go.omoves);
}
rest_levl(nhfp);
for (c = 0; c < COLNO; ++c)
for (r = 0; r < ROWNO; ++r) {
if (nhfp->structlevel) {
Mread(nhfp->fd, &svl.lastseentyp[c][r], sizeof (schar));
}
}
Mread(nhfp->fd, &go.omoves, sizeof go.omoves);
elapsed = svm.moves - go.omoves;
rest_stairs(nhfp);
if (nhfp->structlevel) {
rest_stairs(nhfp);
Mread(nhfp->fd, &svu.updest, sizeof svu.updest);
Mread(nhfp->fd, &svd.dndest, sizeof svd.dndest);
Mread(nhfp->fd, &svl.level.flags, sizeof svl.level.flags);
if (svd.doors) {
free(svd.doors);
svd.doors = 0;
}
}
if (svd.doors) {
free(svd.doors);
svd.doors = 0;
}
if (nhfp->structlevel) {
Mread(nhfp->fd, &svd.doors_alloc, sizeof svd.doors_alloc);
if (svd.doors_alloc) { /* avoid pointless alloc(0) */
svd.doors = (coord *) alloc(svd.doors_alloc * sizeof (coord));
Mread(nhfp->fd, svd.doors, svd.doors_alloc * sizeof (coord));
}
if (svd.doors_alloc) { /* avoid pointless alloc(0) */
svd.doors = (coord *) alloc(svd.doors_alloc * sizeof(coord));
tmpc = svd.doors;
for (i = 0; i < svd.doors_alloc; ++i) {
if (nhfp->structlevel) {
Mread(nhfp->fd, tmpc, sizeof (coord));
}
tmpc++;
}
}
rest_rooms(nhfp); /* No joke :-) */
if (svn.nroom)
if (svn.nroom) {
gd.doorindex = svr.rooms[svn.nroom - 1].fdoor
+ svr.rooms[svn.nroom - 1].doorct;
else
} else {
gd.doorindex = 0;
}
restore_timers(nhfp, RANGE_LEVEL, elapsed);
restore_light_sources(nhfp);
fmon = restmonchn(nhfp);
/* rest_worm(fd); */ /* restore worm information */
rest_worm(nhfp); /* restore worm information */
gf.ftrap = 0;
for (;;) {
trap = newtrap();
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, trap, sizeof *trap);
}
if (trap->tx != 0) {
if (program_state.restoring != REST_GSTATE
&& trap->dst.dnum == u.uz.dnum) {
@@ -1298,8 +1316,9 @@ rest_bubbles(NHFILE *nhfp)
clouds are present is recorded during save so that we don't have to
know what level is being restored */
bbubbly = 0;
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, &bbubbly, sizeof bbubbly);
}
if (bbubbly)
restore_waterlevel(nhfp);
@@ -1310,21 +1329,24 @@ restore_gamelog(NHFILE *nhfp)
{
int slen = 0;
char msg[BUFSZ*2];
struct gamelog_line tmp;
struct gamelog_line tmp = { 0 };
while (1) {
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, &slen, sizeof slen);
}
if (slen == -1)
break;
if (slen > ((BUFSZ*2) - 1))
panic("restore_gamelog: msg too big (%d)", slen);
if (nhfp->structlevel) {
Mread(nhfp->fd, msg, slen);
Mread(nhfp->fd, &tmp, sizeof tmp);
msg[slen] = '\0';
gamelog_add(tmp.flags, tmp.turn, msg);
}
msg[slen] = '\0';
if (nhfp->structlevel) {
Mread(nhfp->fd, &tmp, sizeof tmp);
}
gamelog_add(tmp.flags, tmp.turn, msg);
}
}
@@ -1335,14 +1357,16 @@ restore_msghistory(NHFILE *nhfp)
char msg[BUFSZ];
while (1) {
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, &msgsize, sizeof msgsize);
}
if (msgsize == -1)
break;
if (msgsize > BUFSZ - 1)
panic("restore_msghistory: msg too big (%d)", msgsize);
if (nhfp->structlevel)
if (nhfp->structlevel) {
Mread(nhfp->fd, msg, msgsize);
}
msg[msgsize] = '\0';
putmsghistory(msg, TRUE);
++msgcount;
@@ -1531,10 +1555,8 @@ restore_menu(
int
validate(NHFILE *nhfp, const char *name, boolean without_waitsynch_perfile)
{
readLenType rlen = 0;
struct savefile_info sfi;
unsigned long utdflags = 0L;
boolean verbose = name ? TRUE : FALSE, reslt = FALSE;
boolean reslt = FALSE;
if (nhfp->structlevel)
utdflags |= UTD_CHECKSIZES;
@@ -1543,24 +1565,6 @@ validate(NHFILE *nhfp, const char *name, boolean without_waitsynch_perfile)
if (!(reslt = uptodate(nhfp, name, utdflags)))
return 1;
if ((nhfp->mode & WRITING) == 0) {
if (nhfp->structlevel)
rlen = (readLenType) read(nhfp->fd, (genericptr_t) &sfi,
sizeof sfi);
} else {
if (nhfp->structlevel)
rlen = (readLenType) read(nhfp->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;
}
}
return 0;
}