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