The next_obj field is a pointer which the top item in a stack uses to
keep a list of the other items. When removing an item from the stack,
rem_obj() failed to set next_obj to NULL, which can cause items in
monster inventory to point to items earlier in the inventory list.
That causes infinite co-recursion when saving or restoring.
Inventory letters are based on "identifiers" stored in objects' o_ident
field. Identifiers are allocated by get_ident(), which keeps a list of
objects that have them, to avoid giving the same identifier to multiple
objects.
The list is not stored in the savefile, so after restore, get_ident()
was not aware of existing identifiers. This resulted in picked-up
objects having the same inventory letters as objects restored from the
file.
The restore code now adds all objects with identifiers to the list.
Some games' implementation of md_hasclreol() poked around in ncurses
internals, which does not work for some ncurses build configuration.
Most games did not actually call md_hasclreol(), so it was removed.
There is a standard terminfo function which can retrieve the value of
the clr_eol capability, so this was used for rogue5.
Functions in memory.c tracked and instrumented memory allocation. This
mechanism logged information to a text file and also added a 16KB guard
area to every allocation. Neither of these are desirable in a multi-
user environment, so the whole memory tracking subsystem has been
disabled.
The behavior can be enabled with a configure flag, but it would be a
better idea to use Valgrind to deal with memory bugs.
In md_shellescape(), signal handlers for SIGINT and SIGQUIT were saved
and restored, but exchanged in the process. This was fixed in the
other games by commit 600873555ec0.
Deleting md_shellescape() might have been a better fix. It is
apparently unused.
The function wrapped the standard putchar(), doing nothing beside
discarding the return value. That could cause problems with tputs(),
which expects an int to be returned.
In some games, restore() passes the result of ctime() to mvprintw() or
some other variadic message-formatting function. If ctime() has not
been declared properly, its return type is inferred to be int instead
of char *. This does not cause a warning because the compiler does not
know the correct type of variadic arguments.
On platforms where ints and pointers are not the same size, this can,
probably depending on alignment, result in a segfault that is not easy
to trace.
Including time.h fixes the problem. Some games manually declared
ctime() and avoided the bug. These declarations have also been
replaced with the include.
Marking non-magic items caused segfaults because item_color was set to
NULL. item_type could also be used as an out-of-bounds index. These
problems have been fixed by only using these variables when the mark
argument is false, in which case they are properly initialized.
A fall-through case statement was also fixed.
It is possible for getpwuid() to return NULL. Such a failure will no
longer cause a segfault. However, the call to getpwuid() may normally
not be reachable.
When using the -n option, UltraRogue will look for character files in a
single location, similar to save files.
The location is chosen by defining CHRDIR in getplay.c, at least until
UltraRogue gets integrated with the build systems.
The functions for restoring saved games failed to properly correct the
t_chasee pointer of monsters chasing the player. Such monsters would
attempt to chase NULL instead, with predictable results.
A for loop had no braces around its body, which was a single if-else
statement. In Advanced Rogue 5, another statement had been added,
accidentally removing the if-else from the loop. This could have
resulted in an out-of-bounds access to the options array.
In the other games, the added braces are only for clarity.
The r_flags field in struct room was being written as an int and read
as a short. This caused the restore functions to receive the wrong
data, usually an impossible string length, and abort.
This breaks save compatibility, though the save files had problems
anyway: the r_fires field should have been used, instead of reading and
writing r_flags twice.
The new function md_random_seed() has replaced time() + getpid() and
similar methods. Putting everything in mdport.c slightly reduces the
warnings and workarounds.
Rogue V3 allowed the player to gain perpetual haste by quaffing a
potion of haste while already hasted. This is supposed to remove the
haste effect and cause temporary paralysis.
Super-Rogue removed haste correctly, but gave confusing messages.
Like an earlier bug with detecting monsters, the fuses responsible for
ending levitation and hallucination were not recognized by
rs_write_daemons(). They got left out of the savefile, so after
restoring, the effect never wore off.
I think this is the first bugfix I've ever made that reduced the game's
difficulty.