Read the entire buffer at once, then remove the carriage returns and set
a null terminator. This reduces the number of calls to PHYSFS_read from
len to 1.
It reads uninitialized values, so it was barely functional before
morph_data became a dynamic allocation, and it is broken now. Disable
it to avoid crashing the game. The demo code should have initialized
the morph_data structure properly, but does not.
Reported-by: tycho <https://github.com/dxx-rebirth/dxx-rebirth/issues/496>
Fixes: cac5f1da56 ("Move morph_data into dynamic allocations")
When building with AddressSanitizer, gcc warns that `i` may not fit,
because the compiler fails to adequately constrain the possible values
for `i`. Add a control-flow hint to inform gcc about valid values.
These variables are used, but optimized builds eliminate all reference
to their address, so the build normally succeeds. Unoptimized builds
retain references to the address, so a definition is required for them.
Reported-by: tycho <https://github.com/dxx-rebirth/dxx-rebirth/issues/496>
Fixes: 595c3ca086 ("Move Boss_cloak_interval to d_level_shared_boss_state")
Fixes: c61dee7e6a ("Move Boss_teleport_interval to d_level_shared_boss_state")
Returning from a secret level clears the player's `hostages_on_board` to
0 as part of initializing a "new" ship, but this is not desirable
behavior. Copy the counter out before and back afterward.
```
Thread 1 "d2x-rebirth-edi" hit Hardware watchpoint 1: -location d2x::LevelUniqueObjectState.Objects._M_elems[0].ctype.player_info.mission.hostages_on_board
Old value = 5 '\005'
New value = 0 '\000'
#0 d2x::init_player_stats_ship (GameTime64=<optimized out>, plrobj=...) at similar/main/gameseq.cpp:613
#1 d2x::init_player_stats_level (secret_flag=d2x::secret_restore::survived, plrobj=..., plr=...) at similar/main/gameseq.cpp:615
#2 d2x::StartNewLevelSub (level_num=level_num@entry=3, page_in_textures=page_in_textures@entry=1, secret_flag=secret_flag@entry=d2x::secret_restore::survived) at similar/main/gameseq.cpp:1901
#3 d2x::state_restore_all_sub (LevelSharedDestructibleLightState=..., secret=d2x::secret_restore::survived, filename=<optimized out>) at similar/main/state.cpp:1670
#4 d2x::state_restore_all (in_game=in_game@entry=1, secret=secret@entry=d2x::secret_restore::survived, filename_override=<optimized out>, blind=blind@entry=dcx::blind_save::no) at similar/main/state.cpp:1490
#5 d2x::ExitSecretLevel ()
```
Reported-by: teratorn <https://github.com/dxx-rebirth/dxx-rebirth/issues/495>
gcc before gcc-7 failed to build valptridx:
```
common/include/valptridx.h: In instantiation of 'valptridx<managed_type>::ptridx<policy>::ptridx(const valptridx<managed_type>::ptridx<rpolicy>&) [with rpolicy =
valptridx<dcx::segment>::vm; typename std::enable_if<(policy:: allow_nullptr || (! rpolicy:: allow_nullptr)), int>::type <anonymous> = 0; policy = valptridx<dcx::segment>::vc; managed_type = dcx::segment]':
similar/main/endlevel.cpp:586:105: required from here
common/include/valptridx.h:686:14: error: 'using vptr_type = class valptridx<dcx::segment>::ptr<valptridx<dcx::segment>::vm>' is protected within this context
vptr_type(static_cast<const typename ptridx<rpolicy>::vptr_type &>(rhs)),
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
common/include/valptridx.h:666:31: note: declared protected here
using vptr_type = ptr<policy>;
```
This cannot be fixed in the obvious way because the error message is
wrong:
```
664 using containing_type = valptridx<managed_type>;
665 public:
666 using vptr_type = ptr<policy>;
667 using vidx_type = idx<policy>;
668 using typename vidx_type::array_managed_type;
```
The type declarations _already are_ *public*, not *protected* as the
message states. Relaxing the protection on the containing class
resolves the error. This looks bad from an encapsulation perspective,
but does not make the code incorrect.
Commit d879fc7f6b changed the type of `newpath` and updated all uses
in common code. A Windows-only block was skipped. Update that now.
Fixes: d879fc7f6b ("Convert file selection to use array<char, N>, not char[N]")
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`.