Past releases had a debug-only assertion that 25 was sufficient, but
then dynamically allocated enough storage for larger models as needed.
Commit 22a34809ee ("Move interpreter g3s_lrgb onto stack") added a
hard limit of 25, but did not detect attempts to exceed this. Custom
models that exceeded 25 would cause a stack buffer overwrite and likely
crash. Raise the limit to 64 and add sanity checking to refuse to
render models that exceed MAX_POINTS_PER_POLY.
Fixes: 22a34809ee ("Move interpreter g3s_lrgb onto stack")
For switch cases where existing comments or code flow logic obviously
intended to fall through, add a gcc-7 /*-fallthrough*/ comment to
silence warnings about this. Some cases which are less obvious are not
converted, so the code does not yet compile clean with
-Wimplicit-fallthrough.
Reported-by: parkerlreed <https://github.com/dxx-rebirth/dxx-rebirth/issues/338>
The dump logger probes for the end of the stack, then rounds down to the
nearest paragraph boundary to simplify the logic in the hexdump routine.
The termination condition in the hexdump code assumed that there would
exist an integer N such that (`start` + (16 * N) == `end`). Since `end`
is rounded to a multiple of 16, this held if and only if `start` is also
a multiple of 16. In practice, this tended to happen, but it was not
guaranteed by the code. If it ever failed to happen, then the hexdump
routine would not terminate and would instead perform an invalid read
beyond the edge of the stack.
Modify the hexdump routine to round `start` to a multiple of 16 so that
the termination condition works as intended. This has the useful side
effect that hex dumps now always start paragraph aligned. When the
stack was not paragraph aligned, this change will cause the hexdump to
show bytes below the stack pointer at the time of the fault. However,
the stack requirements of the handler itself ensure that these bytes
will be valid.
Hat labels reserve an extra character for the arrow, which partially
masked this error. When used buttons requires more characters than (1
+ used hats), the buffer had insufficient space and the label was
truncated.
x86_64-w64-mingw32-g++ -Wformat handling misparses the std::size_t
format string, causing a spurious error.
common/main/valptridx.tcc: In function 'void untyped_index_mismatch_exception::prepare_report(const char*, unsigned int, const void*, long int, const void*, const void*, char (&)[229], std::size_t)':
common/main/valptridx.tcc:36:182: error: format '%u' expects argument of type 'unsigned int', but argument 7 has type 'std::size_t {aka long long unsigned int}' [-Werror=format=]
This occurs even though the processed text uses %I64u (which is correct
for a `long long unsigned int`), not %u as shown in the error message.
static void prepare_report(const char *const filename, const unsigned lineno, const void *const array_base, const long supplied_index, const void *const expected_pointer, const void *const actual_pointer, char (&buf)[report_buffer_size], const std::size_t array_size)
{
snprintf(buf, sizeof(buf), "%s:%u: " "pointer/index mismatch:" " base=%p size=%" "I64u" " index=%li expected=%p actual=%p", filename, lineno, array_base, array_size, supplied_index, expected_pointer, actual_pointer);
}
In practice, all such sizes will fit in `unsigned int` because they are
the dimension of an array of large structures. Switch all platforms to
use `unsigned long`, which works everywhere and is at least as big as
`unsigned int`. Using `unsigned long` produces the same size as
`std::size_t` on all platforms other than Win64, where `unsigned long`
is only 32 bits due to the strange LLP64 model Microsoft picked.
Commit b32298df5a ("Rewrite powerup cap
code to centralize logic") centralized powerup cap code in the
powerup_cap_state class.
Commit 901a554e96 ("New powerup management
code: Addeed functions and packet type to ...") removed all use of
powerup_cap_state, but left the dead implementation present.
Commit 479f5ed584 ("Fix 'format specifies
type 'unsigned short' but the argument has type 'unsigned char''
warning") switched the already dead (but still compiled) code from %hu
to %hhu to fix a warning on OS X. Although the commit was written by
Chris, it was my suggestion to use %hhu. I neglected to test Windows
before suggesting it, so the change went in even though Windows does not
accept %hhu; this broke the Windows build. Fortunately, the code had
been dead for 11 months when the change was made, so the fix for Windows
is to remove the long dead code.
Descent 1 has no headlight powerup. Code to read headlights was
incorrectly added in 357e1b0144, but no code was added to write
headlights, so the Descent 1 lighting code checked for headlights that
never existed. Guard the headlight global variables and the associated
logic with if(D2).
Fixes: 357e1b0144 ("Made lighting code work with actual RGB values and added feature to let certain objects emit colored dynamic light as well as let mine flash red when control center destroyed (OpenGL-only at the moment)")
Type `SyncGLMethod` was defined conditional on DXX_USE_OGL, but header
`common/include/ogl_sync.h` used `SyncGLMethod` unconditionally. This
works fine in the normal build since SDL builds never include
`common/include/ogl_sync.h`, but broke the check_header_includes=1 test
since that compiles `common/include/ogl_sync.h` even for SDL builds.
Wrap type `ogl_sync` in `#if DXX_USE_OGL` to hide it from the
check_header_includes=1 test in SDL mode.
Expose sphere_intersects_wall and call it directly from
boss_fits_in_seg, so that boss_fits_in_seg does not need to modify the
position and segment of the boss during the test.
If boss has teleported before, even before a loaded state was saved, play the boss looping sound immediately when loading the saved game (if near the boss). Resolves issue #326.
clang becomes confused trying to determine which vm_distance_squared
constructor to use for a literal input of 0x7fffffffffffffff, even
though the size of the input requires it to be `long` and only one
constructor can take a `long`. Switch from an explicit
0x7fffffffffffffff to the symbolic constant INT64_MAX, which has the
same value, but a platform-appropriate suffix to force the compiler to
pick the right type.
For general clarity, switch some other instances of integer maximum
literals to symbolic constants of the same value.
This commit has no effect on the generated code (except for changes to
line numbers).
Reported-by: kreatordxx <https://github.com/dxx-rebirth/dxx-rebirth/pull/324>
Fixes: 17208cca79 ("Disallow int for vm_distance_squared")
When objp is nullptr, ai_door_is_openable assumes Buddy_objnum is a
valid object number, but this assumption is not guaranteed to be true.
When it is false, the game crashes. This crash can be triggered by a
brain robot trying to make nearby robots snipe.
Fix the crash by passing the robot of interest.
Reported-by: Yarn <http://www.dxx-rebirth.com/frm/index.php/topic,2165.0.html>
When using `gcc -fsanitize=undefined`, the compiler proves trivial
results, so `DXX_CONSTANT_TRUE` is defined. It then fails to prove that
`DXX_CONSTANT_TRUE(m_state == checked)` is false, causing a compile-time
error. Relax the check to occur only when it can prove `m_state` equal
to a disallowed value, rather than when it cannot prove `m_state` equal
to an allowed value.
Move the preprocessor guard so that the runtime check is always visible.
Optimizing compilers can still eliminate that check at compile-time when
it provably never fails.
Macro LINE_SPACING previously used global grd_curcanv implicitly.
Change it to take a canvas argument. Change all callers to pass
grd_curcanv, so that usage is explicit.
As a macro, it always refers to the global grd_curcanv. This interferes
with converting canvas handling to be an argument. Expand GHEIGHT so
that uses of grd_curcanv can be changed individually.
As a macro, it always refers to the global grd_curcanv. This interferes
with converting canvas handling to be an argument. Expand GWIDTH so
that uses of grd_curcanv can be changed individually.
Movement handling has an ugly hack that tries to grab powerups near the
console player, but it reuses general collision handling and fails to
check whether the player is alive.
Add a liveness check. Place the check so that it happens once, before
the objects are scanned, rather than being needlessly repeated for every
object.
Refactor the collision code to let the movement hack skip the parts it
does not need.
Reported-by: ryusei117 <https://github.com/dxx-rebirth/dxx-rebirth/issues/302#issuecomment-275816259>
PCH mode causes the Valgrind-wrapper header to be included in places
it is not meant to be used, which leads to spurious compiler errors.
PCH mode causes the Valgrind-wrapper __real_* stub functions to see
the physfsx.h PHYSFS macros, which also causes errors.
Fortunately, both cases can be easily fixed.
Add preprocessor guards to the Valgrind-wrapper header to cause it to
compile out when the PCH phase runs. It has no include guard, so it
will be rescanned with the proper macros when the two consuming files
are built normally.
Modify the Valgrind-wrapper __real_* stub functions to use
the standard macro avoidance trick so that they do not call the
physfsx.h PHYSFS macros.
Kreator proposed restoring the Descent 2 cheat that grants homing
capability to all weapons. This commit implements that proposition,
with some changes to the implementation details.
Based-on-patch-by: Chris Taylor <chris@icculus.org>
Requested-by: Chris Taylor <https://github.com/dxx-rebirth/dxx-rebirth/pull/318>
By design, valptridx will throw an exception on invalid input. This is
better than silently permitting invalid input to corrupt program state.
Past releases blindly trusted that multiplayer peers would not send
invalid input. Conversion to the valptridx design eliminated the
undefined behavior when peers send invalid input, but still allowed
multiplayer peers to remotely crash the game by sending invalid inputs.
Add a mechanism to trap invalid inputs and gracefully ignore those
messages. This may cause game consistency issues, but will not allow
data corruption.
Contains 2 calls - to obj_create and init_ai_object. For safety reasons and tidiness compared to using obj_create directly. The call to init_ai_object in recreate_thief was already redundant.
All releases to date have a bug where they treat certain segment number
fields as an int, not a segment number. Storing segment_none (0xffff)
into the save file causes affected releases to crash in various places
because it fails to recognize that this is segment_none.
Current code correctly treats segment_none as a non-segment and works
correctly without this hack. The hack is only required to get past
releases to work correctly after loading a saved game written by current
code.
Use std::equal_range to find the upper and lower bounds in a single
binary search, rather than relying on a linear search to find the first
sought element.
Some bitblt code had guards of the form:
#if A
xxx
#if !A
yyy
#endif
zzz
#endif
If A is true, !A is false, so the inner block can never be included.
Delete it.
In multi_do_frame(), replace call to window_close(Game_wind) with returning window_event_result::close whenever multi_quit_game is true. Only using this return value where multi_do_frame() is directly called by GameProcessFrame(). multi_quit_game will only be set back to 0 when a new multi game is started.
Closing a window within its handler is problematic - it can result in an unstable state.
Replace call to window_close(Game_wind) with returning window_event_result::close to game_handler. Applies to when there is a failure in net_udp_level_sync(). Closing a window within its handler is problematic - it can result in an unstable state.
Replace call to window_close(Game_wind) with returning window_event_result::close to game_handler. Applies to whenever newdemo_stop_playback() is called. Closing a window within its handler is problematic - it can result in an unstable state.
`struct object_rw` is poisoned prior to initializing and sending it.
However, some fields are legitimately unininitialized (other than their
memset or poison value) at send time. Add and use a poison variant that
can clear those fields, without marking them unreadable.
Replace calls to window_close(Game_wind) with returning window_event_result::close to game handler. Applies to when DoEndGame() is called, DoGameOver() is called, aborting in the kmatrix screen (multiplayer game) during AdvanceLevel() and playing one demo frame causes playback to stop in GameProcessFrame(). Closing a window within its handler is problematic - it can result in an unstable state.
Clang rejects a declared (but never defined and never used) `static`
function. Change the declaration to be the value of a macro that will
be expanded only when the function is both defined and used.
Reported-by: kreatordxx <https://github.com/dxx-rebirth/dxx-rebirth/issues/295>
Fixes: 55822d0b4d ("Add wrappers for more precise Valgrind physfs diagnostics")
Valgrind warns when writing uninitialized data to a file. The Descent
savegame format requires writing certain fields that are no longer used.
Set those fields to 0 to prevent leaking stack data into the file.
zicodxx reports that f7d0c85 made the thief bot passive and timid.
His analysis suggests that the problem is because f7d0c85 changed
find_connected_distance to return vm_distance::maximum_value() in places
where it previously returned magic values that were not maximum (caching
a distance of F1_0*1000 and returning a distance of -1). Rather than
try to fix the underlying code that relied on these magic values, revert
those return paths to return these unusual values. Move the unusual
values to named constants in file scope so that they are easier to find
and correlate.
Reported-by: zicodxx <https://github.com/dxx-rebirth/dxx-rebirth/issues/286>
Analyzed-by: zicodxx
Fixes: f7d0c853ba ("Use special types for distance/magnitude")
init_player_stats_new_ship used select_primary_weapon and
select_secondary_weapon to assign the player's weapons. However, those
functions read the current weapon and jumped according to its value. A
new ship has no defined value for current weapons, so the jump triggered
an uninitialized value warning from Valgrind.
Add new functions set_primary_weapon, set_secondary_weapon that work
like the previous select_* functions, but always take the path used
for weapons not equal, without checking. This prevents the warnings
from Valgrind, as well as a theoretical risk of initializing the ship
improperly.
The declaration of valptridx_specialized_types needed to be found by
Argument Dependent Lookup, but this was inconvenient for some types.
Split the declaration of valptridx_specialized_types out from the
definition of valptridx global subtype.
Past releases, when rendering an invalid primary texture, would
Int3() and then reset the texture to zero. Commit d767f7c changed the
logic to return without resetting the texture, since the reset seemed to
be unnecessary. Unfortunately, it is necessary. Some levels, including
those shipped with the retail game data, specify bogus primary textures
on some surfaces. After d767f7c, rendering a surface with an invalid
primary texture causes the surface to be invisible, even if it has a
valid secondary texture.
Remove the return statement added in d767f7c. Extend
validate_segment_side to validate the primary texture on the tested
side. When an invalid texture is found, reset it and log a diagnostic.
For built-in levels, log at level CON_VERBOSE since players cannot
readily fix the level. For external levels, log at level CON_URGENT so
that level authors know to fix their level before releasing it.
Fixes: d767f7cd5e ("Pass vcsegptridx to render_face")
Headers included by SConstruct tests cannot rely on the existence of
"dxxsconf.h"; instead, SConstruct must define the symbols that would
have been in it.
Previously:
constexpr vTYPEptr{};
constexpr vcTYPEptr{};
Now:
__attribute_unused static vTYPEptr{};
constexpr vcTYPEptr{}; // unchanged from above
This is necessary for future work. It should have no user observable
effects for now.
Use a compound statement to cache the success condition as a local
boolean, then reference the local in the macro expansions. This should
hint to the optimizer that this is always the same expression, which
should encourage it not to repeat the test in the generated code.
Actual results vary. x86_64-pc-linux-gnu-g++-5.4.0 generates code that
is bigger, but uses fewer instructions.
This preserves virtually all game information, including active fire. The saved game 'gamesave.sge' can also be loaded with File->Restore Game State. The level can still be saved as usual (but it warns the user if there's an unsaved game state). This functionality may be useful for testing (or cheating! ;) )