When presented with an overlong string, throw std::runtime_error instead
of corrupting memory. Either way, the result is a crash, but this crash
is safe, deterministic, and probably easier to debug.
The historical savegame format cannot support finding a mission in a
subdirectory. Add a backwards-incompatible modification to store the
full path in the savegame, and store it in a way that old versions will
fail gracefully.[1] When loading demos, or legacy savegames, search for
the mission in all available directories. Demos are still written with
an unqualified path because the demo loading code would crash if given
an oversized path. Mission names sent over the network as part of
multiplayer use the guess logic now, so that guests do not need to have
the mission in the same path as the host.
[1] Versions affected by issue #486 may fail ungracefully.
Reported-by: AlumiuN <https://github.com/dxx-rebirth/dxx-rebirth/issues/491>
- In D2X, do not accept Descent2-specific directives from Descent 1
`.msn` files.
- Set the descent_version field correctly in the `mle`. Previously,
`.msn` was set to descent1 and all `.mn2` were set to descent2,
regardless of whether the `.mn2` used `name`, `xname`, `zname`, or
`!name`.
- Avoid rewinding the file and rereading the same line while checking
the possible name types.
- Avoid recomputing end-of-string when it is already known.
- Avoid re-reading the mission file's version when the mission is
chosen. Instead, use the version that was recorded when the mission
was loaded into the mission list. This also fixes a bug where Descent
1 `.msn` files would be classified as descent_version_type::descent2
since both use `name =`, but that string has a different meaning
depending on whether the file is `.msn` or `.mn2`.
Some levels have robots with n_joints=0 and an invalid offset. If the
number of joints is zero, the offset is irrelevant. Reset it to 0, so
that a later call to partial_range will not trap.
Reported-by: kakhome1 <https://github.com/dxx-rebirth/dxx-rebirth/issues/485>
TORRaven reported an issue where `Descent: First Strike` level 4 in
cooperative games would cause the player 0 ship to be unable to move.
On investigation, this occurs when the host has enabled extra player
starts, and the resulting synthesis of new player start positions moves
the player 0 start out of its origin segment. The physics engine reacts
poorly to this situation, causing the object to be unable to move and to
report HIT_BAD_P0 in debug builds. Add a test to cancel the
displacement if get_seg_masks reports that the center point would move
outside the origin segment. More clever logic might be appropriate, but
since this is the first report of problems with the feature since its
introduction in 5a64ee5132, this commit just eliminates the
immediate problem. Possible future improvements if the existing
displacement causes collisions:
- Reduce the magnitude of the existing displacement, instead of
abandoning it entirely.
- Move the player start to the segment's center before displacing it.
- Add logic to move the player into a nearby logically connected
segment.
Reported-by: TORRaven <https://github.com/dxx-rebirth/dxx-rebirth/issues/483>
Fixes: 5a64ee5132 ("Add experimental support for larger cooperative games")
AlumiuN reported a crash when a save game is unable to load the
underlying mission. The crash is because the game proceeds to load the
savegame onto whatever level was loaded before this mission, which will
usually result in fatal inconsistencies in the data.
This commit does not fix the cause of the inability to load the mission,
but instead fixes the logic so that the user gets a reasonable error
message advising that the mission failed to load. This was
unintentionally broken in db80a88ad2 when the sense of the return
value was inverted, and the fallthrough case was not adjusted. This
impacts all uses of `load_mission_by_name`, though in practice restoring
from a savegame is the most obvious way to hit the problem.
Reported-by: AlumiuN <https://github.com/dxx-rebirth/dxx-rebirth/issues/486> (indirectly)
Fixes: db80a88ad2 ("Improve error message on failure to load mission")
arch_close calls various library shutdown routines, some of which may
not be in good order after atexit hooks begin executing. Call it before
returning from main, so that the libraries are still fully initialized.
Extended identifiers have proved to cause more problems than they solve.
Switch to base32 which, while less commonly supported, is available in
Python and uses an alphabet that is almost a subset of the C identifier
alphabet. Padding characters are still a problem, but can be remapped
to a valid C identifier that is not a valid base32 character.
endlevel wants to freeze the console player's last in-mine position.
Instead of copying the position out and back, refactor the flow to let
endlevel skip the update of the position.
Only the console player's last position needs to be remembered across
frames. Copy the console player's position out before processsing
object movement. For all other objects, retain a temporary for use by
the position recovery code.
Special macros are used to pass information to
`generate-kconfig-udlr.py`. Provide default definitions for these
macros in `kconfig.ui-table.cpp`, and override them in the one build
where they need to be special. This enables `kconfig.ui-table.cpp` to
build cleanly when invoked from the command line in the compilation
database.
obj_rw is a packed structure, so configurations where reset_hitobj takes
its parameter by reference (or where its parameter requires passing
through a constructor that takes its input by reference) fail because
gcc refuses to bind a reference to a member of a packed structure. Copy
the member variable into a local, which gcc will bind.
In some configurations, gcc (but not clang) instantiate
valptridx<segment>::array_managed_type too eagerly, then fail if the
sizeof(segment) is unknown. Include segment.h so that the size is
available.
Historically, Descent ignored any trailing unconverted characters. The
logic in Descent 1 was tightened in 3368390f37, and then the logic in
Descent 2 was made to mirror Descent 1 in 19699037ce. Restore the lax
parsing logic in both games.
Reported-by: InsanityBringer <https://github.com/dxx-rebirth/dxx-rebirth/issues/464> (issue 5)
Fixes: 3368390f37 ("Check for valid SuperX number") (for Descent 1)
Fixes: 19699037ce ("Move d2x-rebirth/main/bmread.cpp -> similar/main/bmread.cpp") (for Descent 2)
In Descent 2, `type` is initialized at declaration time, then never
rewritten. At the end of the function, `Error` is called if `type` has
not been rewritten. Originally, `type` was hard reset to
`OL_CONTROL_CENTER`, but this reinitialization was removed when
`ObjType` and related data were removed from Descent 2.
Reported-by: InsanityBringer <https://github.com/dxx-rebirth/dxx-rebirth/issues/464> (issue 3)
Fixes: ea7ba7ae87 ("remove ObjType, ObjId, ObjStrength and OL_ constants; use "object" instead of "robot" in some places; draw and place reactors with correct modelnum")
Mission sorting is handled in a top-level function after the missions
have been found, so that the special case to promote built-in missions
is only applied at the top level. Unfortunately, this meant that
subdirectories were not sorted. Add an explicit sort in a path specific
to subdirectory handling.
Reported-by: AlumiuN <https://github.com/dxx-rebirth/dxx-rebirth/issues/472>
If object creation fails, the counter is not decreased. This creates an
imbalance by over counting the number of robots in existence. Avoid
this imbalance by deferring the counter update until after the robot is
created.
When a robot drops a robot, the dropped robot is not added to the
level's running accumulated_robots counter, but when the player destroys
that robot, the destruction will be counted. This imbalance allows the
expression counting the number of not-yet-destroyed robots to underflow,
which then confuses the matcen logic into not creating new robots.
Fix this by incrementing the accumulated_robots count as each dropped
robot is created.
Reported-by: ziplantil <https://github.com/dxx-rebirth/dxx-rebirth/issues/466>
This change was proposed by an external user as a claimed fix for a
failure to build. This change has not been tested by the core team.
However, it is a simple change and at worst will break the build.
The change is based in part on advice from recurring contributor
@MaddTheSane. Neither @MaddTheSane nor @kreatordxx have commented on
the correctness of these changes, though the original author of the
commit, @Sottises, is listed as the origin of the most recent prebuilt
OS X package offered from
<https://www.dxx-rebirth.com/download-dxx-rebirth/>.
Reported-by: Sottises <https://github.com/dxx-rebirth/dxx-rebirth/issues/455>
If music is configured as built-in, and no secret songs are configured,
then BIMSecretSongs is set to a non-null pointer to a zero-element
array. This triggers a divide-by-zero when the array length is used in
a modulus operation.
Fix this by adding a special case that initializing from an empty vector
calls `reset()`, which causes the underlying unique_ptr to store
`nullptr` instead of `new bim_song_info[0]`.
Reported-by: Daniel-Leontiev <https://github.com/dxx-rebirth/dxx-rebirth/issues/465>
Fixes: c355e207fe ("Refactor song loading")
Descent 2 has a hack, present as far back as I can trace, that
suppresses starting sounds during level load. The original reason was
not recorded, but this hack has the useful side effect that it avoids
using uninitialized data when set_sound_sources tries to use a Viewer
that has not been reset for the objects of the new level.
Descent 1 lacks this hack, so an invalid Viewer is used, which may
trigger a valptridx trap if the undefined data has an invalid segment
number, and could cause memory corruption in builds which do not
validate the segment index. The valptridx trap:
```
terminate called after throwing an instance of 'valptridx<dcx::segment>::index_range_exception'
what(): similar/main/digiobj.cpp:389: invalid index used in array subscript: base=(nil) size=9000 index=65021
```
The backtrace leading to the trap:
```
d1x::digi_link_sound_common (viewer=..., so=..., pos=..., forever=<optimized out>, max_volume=<optimized out>, max_distance=..., soundnum=42, segnum=...) at similar/main/digiobj.cpp:389
0x00005555555a4e2d in d1x::digi_link_sound_to_pos2 (vcobjptr=..., max_distance=..., max_volume=32768, forever=1, pos=..., sidenum=4, segnum=..., org_soundnum=121) at similar/main/digiobj.cpp:483
d1x::digi_link_sound_to_pos (soundnum=soundnum@entry=121, segnum=..., sidenum=sidenum@entry=4, pos=..., forever=forever@entry=1, max_volume=32768) at similar/main/digiobj.cpp:490
0x00005555555c140d in d1x::set_sound_sources (vcsegptridx=..., vcvertptr=...) at similar/main/gameseq.cpp:817
d1x::LoadLevel (level_num=<optimized out>, page_in_textures=1) at similar/main/gameseq.cpp:1022
0x00005555555c2654 in d1x::StartNewLevelSub (level_num=-1, page_in_textures=<optimized out>) at similar/main/gameseq.cpp:1865
```
Backport this hack into Descent 1. Ultimately, the hack should go away
and data should be loaded in an order that does not access undefined
memory.
Reported-by: Spacecpp <https://github.com/dxx-rebirth/dxx-rebirth/issues/463>
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSGeometry.h:10:9: fatal error:
'CoreGraphics/CGBase.h' file not found
#import <CoreGraphics/CGBase.h>
^~~~~~~~~~~~~~~~~~~~~~~
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSGeometry.h:10:9: note: did not
find header 'CGBase.h' in framework 'CoreGraphics' (loaded from
'/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks')
similar/misc/physfsx.cpp:20:10: fatal error: 'HIServices/Processes.h' file not found
#include <HIServices/Processes.h>
Since this is an OS X problem, this has only been tested by the original
reporter.
Reported-by: Sottises <https://github.com/dxx-rebirth/dxx-rebirth/issues/455>
Suggested-by: MaddTheSane <https://github.com/dxx-rebirth/dxx-rebirth/issues/455#issuecomment-536164089> # SConstruct part
Commit 0df57f5b0f changed markers' movement type from MT_NONE to
MT_SPINNING, since retail code assigned a spin rate, suggesting markers
were meant to spin. However, in some anarchy games, markers are used as
player-positioned cameras. Spinning the cameras makes them hard to use.
Resolve this by preventing markers from spinning in such games.
Fixes: 0df57f5b0f ("Fix retail bug that prevented marker spinning")
Reported-by: flpduarte <https://github.com/dxx-rebirth/dxx-rebirth/issues/456>
Each axis can act as two buttons in both ways.
For example, a player might map slide left and slide right to J1 -A1 and J1 +A1 as button presses instead of the slide L/R axis.
This is mostly to fix XBox 360 Controller Left and Right triggers. But it can work on every axis if the player wishes to bind them.
Defer decreasing energy/vulcan until the object is confirmed to be
created. This avoids charging the player for a shot that cannot be
created due to object exhaustion.
Defer updating Next_laser_fire_time, so that a player can immediately
try firing again.
Defer decreasing energy (for flares) or secondary weapon count (for
secondaries) until the object is confirmed to be created. This avoids
charging the player for a shot that cannot be created due to object
exhaustion.
Otherwise, unless FrameTime exceeds Final_boss_countdown_time in a
single frame, the time never advances and the level never ends.
Fixes: 215252b8ca ("Fold Final_boss_is_dead into Final_boss_countdown_time")
The prior commit fixed the problem of a blank pilot name for new games,
but loading an existing save game uses a different path and still did
not set the player's callsign. Set it on load, too.
Fixes: 0c7de10512 ("Track chosen pilot name outside Players[]")
Single player games relied on the quirk that the pilot's name was always
kept in Players[], even outside a game. Commit 0c7de10512 removed
that quirk, which had the unintended consequence that single player
games are now played by the anonymous pilot. Set the player's callsign
during game setup so that the high score entry can be attributed
correctly.
Fixes: 0c7de10512 ("Track chosen pilot name outside Players[]")
Reported-by: Neurochild247 <https://github.com/dxx-rebirth/dxx-rebirth/issues/443>
Bisected-by: zicodxx <https://github.com/dxx-rebirth/dxx-rebirth/issues/443#issuecomment-523827234>
Commit e6169f17f9 switched from searching `Data/` to searching
`data/`. For users on case-insensitive filesystems, this is a no-op and
the instructions that reference the `Data` directory continue to work.
For users on case-sensitive filesystems, the change broke accessing the
data directory, and caused the instructions to mislead. Given the age
of the commit, fixing the capitalization now will likely cause more
problems than it solves. Instead, fix the capitalization of the
instructions, so that users who follow the documentation get a working
setup.
Reported-by: krux02 <https://github.com/dxx-rebirth/dxx-rebirth/issues/447>
Fixes: e6169f17f9 ("Add 'Data' subdir as a searchpath, so D2X will find descent.pig and to simplify a lot of file opening/checking/closing code")
If the level was not the most recently played, a save is forced so that
it can be marked as most recently played. However, the logic to force
the save also forced an update of the highest-level field, even if that
reduced it.
- Rename the parameter to clarify its meaning.
- Add comments explaining the logic to force the save.
- Only update the highest-level field when the update would increase the saved value
If a guided missile is in flight, and its owning player dies, the
player's type is changed to OBJ_GHOST. The damage that killed the
player should have put the guided missile into autonomous mode, so for
this purpose, return that the missile is not actively guided.
Reported-by: Johnsondr80 <https://github.com/dxx-rebirth/dxx-rebirth/issues/437>
The subcanvas is positioned based on the dimensions of the containing
window, so it must be repositioned if the outer window is resized.
Reported-by: vLKp <https://github.com/dxx-rebirth/dxx-rebirth/issues/394>
Fixes: f491059ed7 ("Enable building with SDL2")
exit_segnum is set during load_endlevel_data, which currently runs too
early for this to depend on the exit used, and thus to be player-unique.
That data should be loaded at need, when the level ends, rather than
during level setup. This can be addressed later, when support for
multiple exits is improved.
transition_segnum is a function of how the player exited the mine, since
there could be multiple exit tunnels, although current code appears not
to handle that well in other places. Therefore, it needs to be
per-player data, not part of the level data, where it would not depend
on the player exiting.
This could be a shared variable instead, since it cannot be directly
influenced by gameplay. However, it is influenced by the game mode, and
keeping shared variables that are mode-specific would be more complexity
for very little savings.
Zico's commit 38fabd7c49 open-coded a test for a robot smart mine. So
far, this is the only place it is needed, but others might arise. Move
the test and its comment out to a static helper function.
As documented in zico's commit 38fabd7c49, robot smart mines have a
different ID number than player smart mines. Rename the test functions
to clarify that they only recognize player smart mines, but not robot
smart mines.
git grep -l is_proximity_bomb_or_smart_mine | xargs sed -i -e 's/is_proximity_bomb_or_smart_mine/is_proximity_bomb_or_player_smart_mine/g'