Skip to content

Commit

Permalink
Add -aditionaldir option
Browse files Browse the repository at this point in the history
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>

Or, if the user is loading a mod that replaces .hog files:

  Descent3 -setdir <path-to-base-game-data> -additionaldir <path-to-mod-files>

[1]: <#373 (comment)>
  • Loading branch information
Jayman2000 committed Aug 20, 2024
1 parent 5761b5d commit 6894792
Show file tree
Hide file tree
Showing 29 changed files with 291 additions and 150 deletions.
3 changes: 1 addition & 2 deletions Descent3/Game2DLL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -581,8 +581,7 @@ bool InitGameModule(const char *name, module *mod) {

// Open the hog file
if (!cf_OpenLibrary(lib_name)) {
ddio_MakePath(tmp_dll_name, Base_directory.u8string().c_str(), "netgames", name, NULL);
strcat(tmp_dll_name, ".d3m");
strncpy(tmp_dll_name, gd_LocatePath(lib_name).u8string().c_str(), sizeof(tmp_dll_name));
Multi_game_dll_name[0] = '\0';
goto loaddll;
}
Expand Down
2 changes: 1 addition & 1 deletion Descent3/ambient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ void WriteAmbientData() {
CFILE *ofile;

#ifndef NEWEDITOR
ddio_MakePath(filename, Base_directory.u8string().c_str(), "data", "misc", AMBIENT_FILE_NAME, NULL);
ddio_MakePath(filename, gd_GetWritableBaseDirectory().u8string().c_str(), "data", "misc", AMBIENT_FILE_NAME, NULL);
#else
ddio_MakePath(filename, D3HogDir, "data", "misc", AMBIENT_FILE_NAME, NULL);
#endif
Expand Down
4 changes: 2 additions & 2 deletions Descent3/demofile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ void DemoToggleRecording() {
if (stricmp(szfile + (strlen(szfile) - 4), ".dem") != 0) {
strcat(szfile, ".dem");
}
Demo_fname = Base_directory / "demo" / szfile;
Demo_fname = gd_GetWritableBaseDirectory() / "demo" / szfile;
mprintf(0, "Saving demo to file: %s\n", Demo_fname.u8string().c_str());
// Try to create the file
Demo_cfp = cfopen(Demo_fname, "wb");
Expand Down Expand Up @@ -1424,7 +1424,7 @@ bool LoadDemoDialog() {
// return false;
// #else

std::filesystem::path file = Base_directory / "demo";
std::filesystem::path file = gd_GetWritableBaseDirectory() / "demo";

if (DoPathFileDialog(false, file, TXT_VIEWDEMO, {"*.dem"}, PFDF_FILEMUSTEXIST)) {
Demo_fname = file;
Expand Down
1 change: 1 addition & 0 deletions Descent3/descent.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@

#include <cstdlib>
#include <filesystem>
#include <vector>

#include "application.h"

Expand Down
2 changes: 1 addition & 1 deletion Descent3/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1299,7 +1299,7 @@ void DoScreenshot() {
count = 1;
while (!done) {
snprintf(str, sizeof(str), "Screenshot%.3d.png", count);
ddio_MakePath(filename, Base_directory.u8string().c_str(), str, NULL);
ddio_MakePath(filename, gd_GetWritableBaseDirectory().u8string().c_str(), str, NULL);
infile = (CFILE *)cfopen(filename, "rb");
if (infile == NULL) {
done = 1;
Expand Down
8 changes: 4 additions & 4 deletions Descent3/gamesave.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ void QuickSaveGame() {
i = Quicksave_game_slot;

snprintf(filename, sizeof(filename), "saveg00%d", i);
ddio_MakePath(pathname, Base_directory.u8string().c_str(), "savegame", filename, NULL);
ddio_MakePath(pathname, gd_GetWritableBaseDirectory().u8string().c_str(), "savegame", filename, NULL);

fp = fopen(pathname, "rb");
if (fp) {
Expand Down Expand Up @@ -394,7 +394,7 @@ void SaveGameDialog() {
#endif

// setup paths.
ddio_MakePath(savegame_dir, Base_directory.u8string().c_str(), "savegame", NULL);
ddio_MakePath(savegame_dir, gd_GetWritableBaseDirectory().u8string().c_str(), "savegame", NULL);
// ddio_MakePath(pathname, savegame_dir, "*.sav", NULL); -unused

// create savegame directory if it didn't exist before.
Expand Down Expand Up @@ -546,7 +546,7 @@ void __cdecl LoadGameDialogCB(newuiTiledWindow *wnd, void *data)

mprintf(0, "savegame slot=%d\n", id - SAVE_HOTSPOT_ID);

ddio_MakePath(savegame_dir, Base_directory.u8string().c_str(), "savegame", NULL);
ddio_MakePath(savegame_dir, gd_GetWritableBaseDirectory().u8string().c_str(), "savegame", NULL);
snprintf(filename, sizeof(filename), "saveg00%d", (id - SAVE_HOTSPOT_ID));
ddio_MakePath(pathname, savegame_dir, filename, NULL);

Expand Down Expand Up @@ -589,7 +589,7 @@ bool LoadGameDialog() {
}

// setup paths.
ddio_MakePath(savegame_dir, Base_directory.u8string().c_str(), "savegame", NULL);
ddio_MakePath(savegame_dir, gd_GetWritableBaseDirectory().u8string().c_str(), "savegame", NULL);
ddio_MakePath(pathname, savegame_dir, "*.sav", NULL);

// create savegame directory if it didn't exist before.
Expand Down
34 changes: 25 additions & 9 deletions Descent3/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1392,13 +1392,14 @@ void LoadGameSettings() {
*/
void InitIOSystems(bool editor) {
ddio_init_info io_info;
std::vector<std::filesystem::path> initial_base_directories = {};

// Set the base directory
// Set the writable base directory
int dirarg = FindArg("-setdir");
int exedirarg = FindArg("-useexedir");
std::filesystem::path initial_base_directory;
std::filesystem::path writable_base_directory;
if (dirarg) {
initial_base_directory = GameArgs[dirarg + 1];
writable_base_directory = GameArgs[dirarg + 1];
} else if (exedirarg) {
char exec_path[_MAX_PATH];
memset(exec_path, 0, sizeof(exec_path));
Expand All @@ -1407,15 +1408,30 @@ void InitIOSystems(bool editor) {
Error("Failed to get executable path\n");
} else {
std::filesystem::path executablePath(exec_path);
initial_base_directory = executablePath.parent_path();
mprintf(0, "Using working directory of %s\n", Base_directory);
writable_base_directory = executablePath.parent_path();
mprintf(0, "Using working directory of %s\n", writable_base_directory);
}
} else {
initial_base_directory = std::filesystem::current_path();
writable_base_directory = std::filesystem::current_path();
}

gd_Init(initial_base_directory);
ddio_SetWorkingDir(Base_directory.u8string().c_str());
ddio_SetWorkingDir(writable_base_directory.u8string().c_str());
initial_base_directories.insert(initial_base_directories.begin(), writable_base_directory);

// Set any additional base directories
auto additionaldirarg = 0;
while (0 != (additionaldirarg = FindArg("-additionaldir", additionaldirarg))) {
const auto dir_to_add = GetArg(additionaldirarg + 1);
if (dir_to_add == NULL) {
mprintf(0, "-additionaldir was at the end of the argument list. It should never be at the end of the argument list.\n");
break;
} else {
initial_base_directories.insert(initial_base_directories.begin(), std::filesystem::path(dir_to_add));
additionaldirarg += 2;
}
}

gd_Init(initial_base_directories);

Descent->set_defer_handler(D3DeferHandler);

Expand Down Expand Up @@ -2039,7 +2055,7 @@ void SetupTempDirectory(void) {
exit(1);
}
// restore working dir
ddio_SetWorkingDir(Base_directory.u8string().c_str());
ddio_SetWorkingDir(gd_GetWritableBaseDirectory().u8string().c_str());
}

void DeleteTempFiles() {
Expand Down
77 changes: 42 additions & 35 deletions Descent3/menu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,8 @@
bool Directplay_lobby_launched_game = false;
#endif

#include "gamedata.h"

#define IDV_QUIT 0xff
// Menu Item Defines
#define IDV_NEWGAME 10
Expand Down Expand Up @@ -1060,51 +1062,55 @@ bool ProcessCommandLine() {
#define TRAINING_MISSION_NAME "Pilot Training"

/**
* Count singleplayer missions in directory. Mission should have .mn3 extension.
* @param missions where to search missions. Should be a valid directory.
* Count singleplayer missions in directories. Mission should have .mn3 extension.
* @param missions_directories where to search missions. Should be a list of valid directories.
* @return count of found missions
*/
static inline int count_missions(const std::filesystem::path &missions_directory) {
static inline int count_missions(const std::vector<std::filesystem::path> &missions_directories) {
int c = 0;

ddio_DoForeachFile(missions_directory, std::regex(".*\\.mn3"), [&c](const std::filesystem::path &path) {
if (stricmp(path.filename().u8string().c_str(), "d3_2.mn3") == 0)
return;
mprintf(0, "Mission path: %s\n", path.u8string().c_str());
tMissionInfo msninfo{};
GetMissionInfo(path.filename().u8string().c_str(), &msninfo);
for (auto missions_directory : missions_directories) {
ddio_DoForeachFile(missions_directory, std::regex(".*\\.mn3"), [&c](const std::filesystem::path &path) {
if (stricmp(path.filename().u8string().c_str(), "d3_2.mn3") == 0)
return;
mprintf(0, "Mission path: %s\n", path.u8string().c_str());
tMissionInfo msninfo{};
GetMissionInfo(path.filename().u8string().c_str(), &msninfo);

if (msninfo.name[0] && msninfo.single) {
mprintf(0, "Name: %s\n", msninfo.name);
c++;
if (!(c % 2))
DoWaitMessage(true);
} else {
mprintf(0, "Illegal or multiplayer mission: %s\n", path.u8string().c_str());
}
});
if (msninfo.name[0] && msninfo.single) {
mprintf(0, "Name: %s\n", msninfo.name);
c++;
if (!(c % 2))
DoWaitMessage(true);
} else {
mprintf(0, "Illegal or multiplayer mission: %s\n", path.u8string().c_str());
}
});
}

return c;
}

static inline int generate_mission_listbox(newuiListBox *lb, int n_maxfiles, char **filelist,
const std::filesystem::path &missions_directory) {
const std::vector<std::filesystem::path> &missions_directories) {
int c = 0;
ddio_DoForeachFile(
missions_directory, std::regex(".*\\.mn3"), [&c, &lb, &n_maxfiles, &filelist](const std::filesystem::path &path) {
tMissionInfo msninfo{};
if (c < n_maxfiles) {
if (stricmp(path.filename().u8string().c_str(), "d3_2.mn3") == 0)
return;
if (GetMissionInfo(path.filename().u8string().c_str(), &msninfo) && msninfo.name[0] && msninfo.single) {
filelist[c] = mem_strdup(path.filename().u8string().c_str());
lb->AddItem(msninfo.name);
c++;
if (!(c % 2))
DoWaitMessage(true);
for (auto missions_directory : missions_directories) {
ddio_DoForeachFile(
missions_directory, std::regex(".*\\.mn3"), [&c, &lb, &n_maxfiles, &filelist](const std::filesystem::path &path) {
tMissionInfo msninfo{};
if (c < n_maxfiles) {
if (stricmp(path.filename().u8string().c_str(), "d3_2.mn3") == 0)
return;
if (GetMissionInfo(path.filename().u8string().c_str(), &msninfo) && msninfo.name[0] && msninfo.single) {
filelist[c] = mem_strdup(path.filename().u8string().c_str());
lb->AddItem(msninfo.name);
c++;
if (!(c % 2))
DoWaitMessage(true);
}
}
}
});
});
}

return c;
}
Expand Down Expand Up @@ -1181,8 +1187,9 @@ bool MenuNewGame() {
// add mission names to listbox
// count valid mission files.
// add a please wait dialog here.
auto missions_directories = gd_LocateMultiplePaths("missions");
n_missions = 0;
n_missions += count_missions(D3MissionsDir);
n_missions += count_missions(missions_directories);
if (n_missions) {
// allocate extra mission slot because of check below which adds a name to the filelist.
filelist = (char **)mem_malloc(sizeof(char *) * (n_missions + 1));
Expand All @@ -1195,7 +1202,7 @@ bool MenuNewGame() {
goto missions_fail;
}
// generate real listbox now.
generate_mission_listbox(msn_lb, n_missions, filelist, D3MissionsDir);
generate_mission_listbox(msn_lb, n_missions, filelist, missions_directories);
// #ifdef RELEASE
int k;
for (k = 0; k < n_missions; k++) {
Expand Down
10 changes: 5 additions & 5 deletions Descent3/multi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8095,23 +8095,23 @@ char *GetFileNameFromPlayerAndID(int16_t playernum, int16_t id) {
break;
case NETFILE_ID_SHIP_TEX:
if (NetPlayers[playernum].ship_logo[0])
ddio_MakePath(rval, Base_directory.u8string().c_str(), "custom", "graphics", NetPlayers[playernum].ship_logo, NULL);
ddio_MakePath(rval, gd_GetWritableBaseDirectory().u8string().c_str(), "custom", "graphics", NetPlayers[playernum].ship_logo, NULL);
break;
case NETFILE_ID_VOICE_TAUNT1:
if (NetPlayers[playernum].voice_taunt1[0])
ddio_MakePath(rval, Base_directory.u8string().c_str(), "custom", "sounds", NetPlayers[playernum].voice_taunt1, NULL);
ddio_MakePath(rval, gd_GetWritableBaseDirectory().u8string().c_str(), "custom", "sounds", NetPlayers[playernum].voice_taunt1, NULL);
break;
case NETFILE_ID_VOICE_TAUNT2:
if (NetPlayers[playernum].voice_taunt2[0])
ddio_MakePath(rval, Base_directory.u8string().c_str(), "custom", "sounds", NetPlayers[playernum].voice_taunt2, NULL);
ddio_MakePath(rval, gd_GetWritableBaseDirectory().u8string().c_str(), "custom", "sounds", NetPlayers[playernum].voice_taunt2, NULL);
break;
case NETFILE_ID_VOICE_TAUNT3:
if (NetPlayers[playernum].voice_taunt3[0])
ddio_MakePath(rval, Base_directory.u8string().c_str(), "custom", "sounds", NetPlayers[playernum].voice_taunt3, NULL);
ddio_MakePath(rval, gd_GetWritableBaseDirectory().u8string().c_str(), "custom", "sounds", NetPlayers[playernum].voice_taunt3, NULL);
break;
case NETFILE_ID_VOICE_TAUNT4:
if (NetPlayers[playernum].voice_taunt4[0])
ddio_MakePath(rval, Base_directory.u8string().c_str(), "custom", "sounds", NetPlayers[playernum].voice_taunt4, NULL);
ddio_MakePath(rval, gd_GetWritableBaseDirectory().u8string().c_str(), "custom", "sounds", NetPlayers[playernum].voice_taunt4, NULL);
break;
default:
mprintf(0, "Unknown id (%d) passed to GetFileNameFromPlayerAndID()\n", id);
Expand Down
12 changes: 8 additions & 4 deletions Descent3/multi_dll_mgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -521,14 +521,17 @@ void GetMultiAPI(multi_api *api) {
api->fp[109] = (int *)GetRankIndex;
api->fp[110] = (int *)CheckGetD3M;
api->fp[111] = (int *)ddio_DoForeachFile;
api->fp[112] = (int *)gd_LocatePath;
api->fp[113] = (int *)gd_LocateMultiplePaths;
api->fp[114] = (int *)gd_GetWritableBaseDirectory;

// Variable pointers
api->vp[0] = (int *)&Player_num;
api->vp[1] = (int *)Tracker_id;
api->vp[2] = (int *)&Game_is_master_tracker_game;
api->vp[3] = (int *)&Game_mode;
api->vp[4] = (int *)NULL; // Current_pilot; no longer a struct
api->vp[5] = (int *)&Base_directory;
api->vp[5] = (int *)&Base_directories;
api->vp[6] = (int *)&MultiDLLGameStarting;
api->vp[7] = (int *)MTPilotinfo;
api->vp[8] = (int *)&Num_network_games_known;
Expand Down Expand Up @@ -598,7 +601,7 @@ int LoadMultiDLL(const char *name) {
if (MultiDLLHandle.handle)
FreeMultiDLL();

std::filesystem::path dll_path_name = Base_directory / "online";
std::filesystem::path dll_path_name = gd_GetWritableBaseDirectory() / "online";
ddio_DoForeachFile(dll_path_name, std::regex(".+\\.tmp"), [](const std::filesystem::path& path, ...) {
std::error_code ec;
std::filesystem::remove(path, ec);
Expand All @@ -621,8 +624,9 @@ int LoadMultiDLL(const char *name) {

// Open the hog file
if (!cf_OpenLibrary(lib_name)) {
ddio_MakePath(tmp_dll_name, Base_directory.u8string().c_str(), "online", name, NULL);
strcat(tmp_dll_name, ".d3c");
std::filesystem::path tmp_dll_path = std::filesystem::path("online") / name;
tmp_dll_path += ".d3c";
strncpy(tmp_dll_name, tmp_dll_path.u8string().c_str(), sizeof(tmp_dll_name));
Multi_conn_dll_name[0] = 0;
goto loaddll;
}
Expand Down
18 changes: 9 additions & 9 deletions Descent3/multi_ui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -442,11 +442,12 @@ int MainMultiplayerMenu() {

std::vector<std::string> dllnames;

ddio_DoForeachFile(Base_directory / "online", std::regex(".*\\.d3c"),
[&dllnames](const std::filesystem::path &path) {
std::string filename = path.stem().string();
for (auto online_directory : gd_LocateMultiplePaths("online")) {
ddio_DoForeachFile(online_directory, std::regex(".*\\.d3c"),
[&dllnames](const std::filesystem::path &path) {
std::string filename = path.stem().string();

std::replace(filename.begin(), filename.end(), '~', '/');
std::replace(filename.begin(), filename.end(), '~', '/');

// Place PXO_NAME first in list
if (stricmp(filename.c_str(), PXO_NAME) == 0) {
Expand All @@ -455,6 +456,7 @@ int MainMultiplayerMenu() {
dllnames.push_back(filename);
}
});
}

for (auto const &name : dllnames) {
lists->AddItem(name.c_str());
Expand All @@ -470,9 +472,7 @@ int MainMultiplayerMenu() {
exit_menu = 1;
menu_wnd.Destroy();
return 0;
}

if (dftidx != -1) {
} else {
lists->SetCurrentItem(sznetgame); //>SetCurrentIndex(dftidx);
}

Expand Down Expand Up @@ -989,15 +989,15 @@ void DoMultiAllowed(void) {
}

void MultiDoConfigSave() {
std::filesystem::path file = Base_directory / "custom" / "settings";
std::filesystem::path file = gd_GetWritableBaseDirectory() / "custom" / "settings";
if (DoPathFileDialog(true, file, TXT_MULTISAVESET, {"*.mps"}, 0)) {
file.replace_extension(".mps");
MultiSaveSettings(file);
}
}

void MultiDoConfigLoad() {
std::filesystem::path file = Base_directory / "custom" / "settings";
std::filesystem::path file = gd_GetWritableBaseDirectory() / "custom" / "settings";
if (DoPathFileDialog(false, file, TXT_MULTILOADSET, {"*.mps"}, PFDF_FILEMUSTEXIST))
MultiLoadSettings(file);
}
Expand Down
Loading

0 comments on commit 6894792

Please sign in to comment.