-/* NetHack 3.7 save.c $NHDT-Date: 1596498207 2020/08/03 23:43:27 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.160 $ */
+/* NetHack 3.7 save.c $NHDT-Date: 1606919257 2020/12/02 14:27:37 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.163 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Michael Allison, 2009. */
/* NetHack may be freely redistributed. See license for details. */
static void FDECL(savemon, (NHFILE *,struct monst *));
static void FDECL(savelevl, (NHFILE *,BOOLEAN_P));
static void FDECL(save_stairs, (NHFILE *));
-static void FDECL(saveobjchn, (NHFILE *,struct obj *));
+static void FDECL(saveobjchn, (NHFILE *,struct obj **));
static void FDECL(savemonchn, (NHFILE *,struct monst *));
static void FDECL(savetrapchn, (NHFILE *,struct trap *));
static void FDECL(savegamestate, (NHFILE *));
d_level uz_save;
char whynot[BUFSZ];
NHFILE *nhfp, *onhfp;
+ int res = 0;
+ g.program_state.saving++; /* inhibit status and perm_invent updates */
/* we may get here via hangup signal, in which case we want to fix up
a few of things before saving so that they won't be restored in
an improper state; these will be no-ops for normal save sequence */
u.uburied = 1, iflags.save_uburied = 0;
if (!g.program_state.something_worth_saving || !g.SAVEF[0])
- return 0;
- fq_save = fqname(g.SAVEF, SAVEPREFIX, 1); /* level files take 0 */
+ goto done;
+ fq_save = fqname(g.SAVEF, SAVEPREFIX, 1); /* level files take 0 */
#ifndef NO_SIGNAL
#if defined(UNIX) || defined(VMS)
sethanguphandler((void FDECL((*), (int) )) SIG_IGN);
There("seems to be an old save file.");
if (yn("Overwrite the old file?") == 'n') {
nh_compress(fq_save);
- return 0;
+ goto done;
}
}
}
if (!nhfp) {
HUP pline("Cannot open save file.");
(void) delete_savefile(); /* ab@unido */
- return 0;
+ goto done;
}
vision_recalc(2); /* shut down vision to prevent problems
store_plname_in_file(nhfp);
g.ustuck_id = (u.ustuck ? u.ustuck->m_id : 0);
g.usteed_id = (u.usteed ? u.usteed->m_id : 0);
+ /* savelev() might save uball and uchain, releasing their memory if
+ FREEING, so we need to check their status now; if hero is swallowed,
+ uball and uchain will persist beyond saving map floor and inventory
+ so these copies of their pointers will be valid and savegamestate()
+ will know to save them separately (from floor and invent); when not
+ swallowed, uchain will be stale by then, and uball will be too if
+ ball is on the floor rather than carried */
+ g.looseball = BALL_IN_MON ? uball : 0;
+ g.loosechain = CHAIN_IN_MON ? uchain : 0;
savelev(nhfp, ledger_no(&u.uz));
savegamestate(nhfp);
(void) delete_savefile();
HUP Strcpy(g.killer.name, whynot);
HUP done(TRICKED);
- return 0;
+ goto done;
}
minit(); /* ZEROCOMP */
getlev(onhfp, g.hackpid, ltmp);
nh_compress(fq_save);
/* this should probably come sooner... */
g.program_state.something_worth_saving = 0;
- return 1;
+ res = 1;
+
+ done:
+ g.program_state.saving--;
+ return res;
}
static void
NHFILE *nhfp;
{
unsigned long uid;
- struct obj * bc_objs = (struct obj *)0;
+ struct obj *bc_objs = (struct obj *)0;
+
+ if (!g.program_state.saving)
+ impossible("savegamestate called when not saving or changing levels?");
uid = (unsigned long) getuid();
if (nhfp->structlevel) {
save_timers(nhfp, RANGE_GLOBAL);
save_light_sources(nhfp, RANGE_GLOBAL);
- saveobjchn(nhfp, g.invent);
-
- /* save ball and chain if they are currently dangling free (i.e. not on
- floor or in inventory) */
- if (CHAIN_IN_MON) {
- uchain->nobj = bc_objs;
- bc_objs = uchain;
+ /* when FREEING, deletes objects in invent and sets invent to Null;
+ pointers into invent (uwep, uarmg, uamul, &c) are set to Null too */
+ saveobjchn(nhfp, &g.invent);
+
+ /* save ball and chain if they are currently dangling free (i.e. not
+ on floor or in inventory); 'looseball' and 'loosechain' have been
+ set up in caller because ball and chain will be gone by now if on
+ floor, or ball gone if carried; caveat: uball and uchain pointers
+ will be non-Null but stale (point to freed memory) in those cases */
+ bc_objs = (struct obj *) 0;
+ if (g.loosechain) {
+ g.loosechain->nobj = bc_objs; /* uchain */
+ bc_objs = g.loosechain;
}
- if (BALL_IN_MON) {
- uball->nobj = bc_objs;
- bc_objs = uball;
+ if (g.looseball) {
+ g.looseball->nobj = bc_objs;
+ bc_objs = g.looseball;
}
- saveobjchn(nhfp, bc_objs);
+ saveobjchn(nhfp, &bc_objs); /* frees objs in list, sets bc_objs to Null */
+ g.looseball = g.loosechain = (struct obj *) 0;
- saveobjchn(nhfp, g.migrating_objs);
+ saveobjchn(nhfp, &g.migrating_objs); /* frees objs and sets to Null */
savemonchn(nhfp, g.migrating_mons);
- if (release_data(nhfp)) {
- g.invent = 0;
- g.migrating_objs = 0;
- g.migrating_mons = 0;
- }
+ if (release_data(nhfp))
+ g.migrating_mons = (struct monst *) 0;
if (nhfp->structlevel)
bwrite(nhfp->fd, (genericptr_t) g.mvitals, sizeof g.mvitals);
save_dungeon(nhfp, (boolean) !!perform_bwrite(nhfp),
char whynot[BUFSZ];
NHFILE *nhfp;
+ g.program_state.saving++; /* inhibit status and perm_invent updates */
/* When checkpointing is on, the full state needs to be written
* on each checkpoint. When checkpointing is off, only the pid
* needs to be in the level.0 file, so it does not need to be
if (g.hackpid != hpid) {
Sprintf(whynot, "Level #0 pid (%d) doesn't match ours (%d)!",
hpid, g.hackpid);
- pline1(whynot);
- Strcpy(g.killer.name, whynot);
- done(TRICKED);
+ goto giveup;
}
close_nhfile(nhfp);
nhfp = create_levelfile(0, whynot);
if (!nhfp) {
pline1(whynot);
+ giveup:
Strcpy(g.killer.name, whynot);
+ /* done(TRICKED) will return when running in wizard mode;
+ clear the display-update-suppression flag before rather
+ than after so that screen updating behaves normally;
+ game data shouldn't be inconsistent yet, unlike it would
+ become midway through saving */
+ g.program_state.saving--;
done(TRICKED);
return;
}
g.ustuck_id = (u.ustuck ? u.ustuck->m_id : 0);
g.usteed_id = (u.usteed ? u.usteed->m_id : 0);
+ g.looseball = BALL_IN_MON ? uball : 0;
+ g.loosechain = CHAIN_IN_MON ? uchain : 0;
savegamestate(nhfp);
}
close_nhfile(nhfp);
}
+ g.program_state.saving--;
g.havestate = flags.ins_chkpt;
}
#endif
short tlev;
#endif
+ g.program_state.saving++; /* even if current mode is FREEING */
/*
* Level file contents:
* version info (handled by caller);
if (nhfp->mode == FREEING) /* see above */
goto skip_lots;
- savelevl(nhfp, (boolean) ((sfsaveinfo.sfi1 & SFI1_RLECOMP) == SFI1_RLECOMP));
+ savelevl(nhfp,
+ (boolean) ((sfsaveinfo.sfi1 & SFI1_RLECOMP) == SFI1_RLECOMP));
if (nhfp->structlevel) {
bwrite(nhfp->fd, (genericptr_t) g.lastseentyp, sizeof g.lastseentyp);
bwrite(nhfp->fd, (genericptr_t) &g.monstermoves, sizeof g.monstermoves);
savemonchn(nhfp, fmon);
save_worm(nhfp); /* save worm information */
savetrapchn(nhfp, g.ftrap);
- saveobjchn(nhfp, fobj);
- saveobjchn(nhfp, g.level.buriedobjlist);
- saveobjchn(nhfp, g.billobjs);
+ saveobjchn(nhfp, &fobj);
+ saveobjchn(nhfp, &g.level.buriedobjlist);
+ saveobjchn(nhfp, &g.billobjs);
if (release_data(nhfp)) {
int x,y;
/* TODO: maybe use clear_level_structures() */
if (nhfp->structlevel)
bflush(nhfp->fd);
}
+ g.program_state.saving--;
}
static void
}
static void
-saveobjchn(nhfp, otmp)
+saveobjchn(nhfp, obj_p)
NHFILE *nhfp;
-register struct obj *otmp;
+struct obj **obj_p;
{
- register struct obj *otmp2;
+ register struct obj *otmp = *obj_p;
+ struct obj *otmp2;
+ boolean is_invent = (otmp && otmp == g.invent);
int minusone = -1;
while (otmp) {
saveobj(nhfp, otmp);
}
if (Has_contents(otmp))
- saveobjchn(nhfp, otmp->cobj);
+ saveobjchn(nhfp, &otmp->cobj);
if (release_data(nhfp)) {
/*
* If these are on the floor, the discarding could be
otmp->cobj = NULL; /* contents handled above */
otmp->timed = 0; /* not timed any more */
otmp->lamplit = 0; /* caller handled lights */
+ otmp->leashmon = 0; /* mon->mleashed could still be set;
+ * unfortunately, we can't clear that
+ * until after the monster is saved */
+ otmp->owornmask = 0L; /* no longer care */
dealloc_obj(otmp);
}
otmp = otmp2;
if (nhfp->structlevel)
bwrite(nhfp->fd, (genericptr_t) &minusone, sizeof (int));
}
+ if (release_data(nhfp)) {
+ if (is_invent)
+ allunworn(); /* clear uwep, uarm, uball, &c pointers */
+ *obj_p = (struct obj *) 0;
+ }
}
static void
savemon(nhfp, mtmp);
}
if (mtmp->minvent)
- saveobjchn(nhfp, mtmp->minvent);
+ saveobjchn(nhfp, &mtmp->minvent);
if (release_data(nhfp)) {
if (mtmp == g.context.polearm.hitmon) {
g.context.polearm.m_id = mtmp->m_id;
tmp_at(DISP_FREEMEM, 0); /* temporary display effects */
#ifdef FREE_ALL_MEMORY
#define free_current_level() savelev(&tnhfp, -1)
-#define freeobjchn(X) (saveobjchn(&tnhfp, X), X = 0)
+#define freeobjchn(X) (saveobjchn(&tnhfp, &X), X = 0)
#define freemonchn(X) (savemonchn(&tnhfp, X), X = 0)
#define freefruitchn() savefruitchn(&tnhfp)
#define freenames() savenames(&tnhfp)