Commit d185ab9514 broke the
pointer-moving logic. When the allweapons cheat is executed or when
e.g. the afterburner is picked up, ASAN terminates the program with:
```
==8330==ERROR: AddressSanitizer: heap-use-after-free on address 0x50f00007ab60
at pc 0x7f23334f6843 bp 0x7ffe724d2b10 sp 0x7ffe724d22d0
READ of size 3 at 0x50f00007ab60 thread T0
f0 strdup
f1 Inventory::AddCounterMeasure(int, int, int, int, char const*) Descent3/Inventory.cpp:575
f2 Inventory::Add(int, int, object*, int, int, int, char const*) Descent3/Inventory.cpp:520
f3 DemoCheats(int) Descent3/GameCheat.cpp:606
f4 ProcessKeys() Descent3/GameLoop.cpp:2420
f5 GameFrame() Descent3/GameLoop.cpp:2956
f6 GameSequencer() Descent3/gamesequence.cpp:1212
f7 PlayGame() Descent3/game.cpp:826
f8 MainLoop() Descent3/descent.cpp:554
f9 Descent3() Descent3/descent.cpp:507
f10 oeD3LnxApp::run() Descent3/sdlmain.cpp:142
f11 main Descent3/sdlmain.cpp:323
0x50f00007ab60 is located 0 bytes inside of 175-byte region [0x50f00007ab60,0x50f00007ac0f)
freed by thread T0 here:
f1 mng_LoadNetGenericPage(CFILE*, bool) manage/generic.cpp:2216
f2 mng_LoadNetPages(int) manage/manage.cpp:1281
f3 mng_LoadTableFiles(int) manage/manage.cpp:648
f4 InitD3Systems2(bool) Descent3/init.cpp:1891
f5 Descent3() Descent3/descent.cpp:503
f6 oeD3LnxApp::run() Descent3/sdlmain.cpp:142
f7 main Descent3/sdlmain.cpp:323
previously allocated by thread T0 here:
f0 malloc
f1 mem_rmalloc<char> mem/mem.h:138
f2 mng_ReadNewGenericPage(CFILE*, mngs_generic_page*) manage/generic.cpp:1145
f3 mng_LoadNetGenericPage(CFILE*, bool) manage/generic.cpp:2196
f4 mng_LoadNetPages(int) manage/manage.cpp:1281
f5 mng_LoadTableFiles(int) manage/manage.cpp:648
f6 InitD3Systems2(bool) Descent3/init.cpp:1891
f7 Descent3() Descent3/descent.cpp:503
f8 oeD3LnxApp::run() Descent3/sdlmain.cpp:142
f9 main Descent3/sdlmain.cpp:323
```
The pointer value of mngs_generic_page::description was copied to
object_info::description (by function
``mng_AssignGenericPageToObjInfo``) and then the page was freed in
``mng_LoadNetGenericPage``, leaving object_info::description non-NULL
and dangling.
Fixes: d185ab9514
Before this change, Descent 3 would look for all of its game data files
in a single directory. This change allows users to spread out Descent
3’s game data over multiple directories.
Building Descent 3 produces multiple files that can be freely
redistributed (Descent3, d3-linux.hog, online/Direct TCP~IP.d3c, etc.).
Running Descent 3 requires those files and several additional files that
cannot be freely redistributed. Before this change, the files that were
redistributable had to be in the same directory as the files that were
not redistributable. This change makes it so that they can be in
separate directories.
The main motivation behind this change is to allow people to package
Descent 3 for Linux in a reasonable manner. For the most part, binary
packages for Descent 3 will contain all of the freely redistributable
components. Package managers will copy those components into system
directories that are owned by root and that users probably shouldn’t
edit manually. Users will then create a new directory and copy the game
data from their copy of Descent 3 into that new directory. Users will
then be able to run:
Descent3 -setdir <path-to-proprietary-files> -additionaldir <path-to-open-source-files>
The -additionaldir option can also be used to support more complicated
scenarios. For example, if the user is using Debian’s
game-data-packager [1], then they would do something like this:
Descent3 -setdir <path-to-writable-directory> -additionaldir <path-to-gdp-directory> -additionaldir <path-to-open-source-files>
The -additionaldir option can also be used to load a mod that replaces
.hog files:
Descent3 -setdir <path-to-base-game-data> -additionaldir <path-to-mod-files>
[1]: <https://github.com/DescentDevelopers/Descent3/issues/373#issuecomment-2120330650>
Before this change, Base_directory was a char array. In general, it’s
better to use an std::filesystem::path to represent paths. Here’s why:
• char arrays have a limited size. If a user creates a file or directory
that has a very long path, then it might not be possible to store that
path in a given char array. You can try to make the char array long
enough to store the longest possible path, but future operating systems
may increase that limit. With std::filesystem::paths, you don’t have
to worry about path length limits.
• char arrays cannot necessarily represent all paths. We try our best to
use UTF-8 for all char arrays [1], but unfortunately, it’s possible to
create a path that cannot be represent using UTF-8 [2]. With an
std::filesystem::paths, stuff like that is less likely to happen because
std::filesystem::path::value_type is platform-specific.
• char arrays don’t have any built-in mechanisms for manipulating paths.
Before this commit, the code would work around this problem in two
different ways:
1. It would use Descent 3–specific functions that implement path
operations on char arrays/pointers (for example, ddio_MakePath()).
Once all paths use std::filesystem::path, we’ll be able to simplify
the code by removing those functions.
2. It would temporarily convert Base_directory into an
std::filesystem::path. This commit simplifies the code by removing
those temporary conversions because they are no longer necessary.
[1]: 7cd79025 (Merge pull request #494 from Jayman2000/encoding-improvements, 2024-07-20)
[2]: <https://github.com/rust-lang/rust/issues/12056>
Remove ddio_CreateDir(), ddio_RemoveDir(), ddio_SaveWorkingDir(), ddio_RestoreWorkingDir(), ddio_DirExists(). Replace them in code with std::fs alternatives.