chris@umcp-cs.UUCP (Chris Torek) (12/21/85)
Index: src/hack.save.c src/hack.lev.c Hack_1.0.3 Fix_(proposed)
Description:
The save file format makes the assumption that the `permonst'
pointers in monster chains are always at a constant offset
from &mons[0]. Unfortunately, after changes to the source,
this is not always true. The bug manifests as dogs or
other `special' monsters being displayed with the wrong
symbol, and a segmentation fault soon afterward, as invalid
pointers are soon followed.
Repeat-By:
Somewhat difficult. I invoked the problem entirely by
accident when adding a global variable.
Fix:
(proposed)
The save file format should be changed. Instead of each
monster's permonst pointer, an index value should be saved.
For `normal' monsters this can simply be the index in
`mons'. For `special' monsters this should be a special
value for each.
An easy way to get the required information into the save
file would be to add the index to the permonst structure
itself, so that the save and restore code changes to
something like this:
mtmp->data = (struct permonst *)(msave->data->index);
bwrite(fd, (char *)mtmp, sizeof *mtmp);
free(mtmp);
and
mread(fd, mtmp, sizeof *mtmp);
index = (int)mtmp->data;
if (index < CMNUM)
mtmp->data = mons[index];
else
switch (index) {
case <cases for each `special' monster>
.
.
.
}
Alternatively, the current scheme could continue to be used
if the `special' monster permonst data were moved into the
`mons' array, so that the (rather hacky) relocation trick
works for these as well. This would appear to require
changes in many modules.
At the same time as any change is made, the save file format
should be modified to include a `version' or `magic number',
so that the format can later be changed and the recover code
augmented to recognize an `old format' file. Unfortunately,
no provision exists for this at the moment. However, a
reasonably safe hack is to note that current save files
start with the saved level, which starts with `hackpid';
this is always a nonnegative integer. If the `magic numbers'
or versions are made negative, hack can still recognize old
format files.
For those that have held on this long, here is a revised
version of restmonchn() that is a little shorter than the
original. (I felt I should post at least one *real* change
:-).)
struct monst *
restmonchn(fd)
register int fd;
{
register struct monst *mtmp, **p;
register long differ;
struct monst *first = 0;
struct permonst *monbegin;
int xl;
mread(fd, (char *)&monbegin, sizeof monbegin);
differ = (char *)&mons[0] - (char *)monbegin;
p = &first;
while (1) {
mread(fd, (char *)&xl, sizeof xl);
if (xl == -1)
break;
mtmp = newmonst(xl);
mread(fd, (char *)mtmp, (unsigned)xl + sizeof (struct monst));
if (!mtmp->m_id)
mtmp->m_id = flags.ident++;
mtmp->data = (struct permonst *)((char *)mtmp->data + differ);
if (mtmp->minvent)
mtmp->minvent = restobjchn(fd);
*p = mtmp;
p = &mtmp->nmon;
}
if (*p) {
impossible("Restmonchn: error reading monchn.");
*p = 0;
}
return (first);
}
--
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 4251)
UUCP: seismo!umcp-cs!chris
CSNet: chris@umcp-cs ARPA: chris@mimsy.umd.edu