diff --git a/Descent3/Game2DLL.cpp b/Descent3/Game2DLL.cpp index 3eafc2954..cf080a73e 100644 --- a/Descent3/Game2DLL.cpp +++ b/Descent3/Game2DLL.cpp @@ -565,7 +565,7 @@ bool InitGameModule(const char *name, module *mod) { std::filesystem::path dll_name; std::filesystem::path tmp_dll_name; // Make the hog filename - lib_name = std::filesystem::path(Base_directory) / "netgames" / name; + lib_name = std::filesystem::path("netgames") / name; lib_name.replace_extension(".d3m"); // Make the dll filename dll_name = name; @@ -573,8 +573,7 @@ bool InitGameModule(const char *name, module *mod) { // Open the hog file if (!cf_OpenLibrary(lib_name)) { - tmp_dll_name = std::filesystem::path(Base_directory) / "netgames" / name; - tmp_dll_name.replace_extension(".d3m"); + tmp_dll_name = cf_LocatePath(lib_name).u8string().c_str(); Multi_game_dll_name.clear(); goto loaddll; } diff --git a/Descent3/Mission.cpp b/Descent3/Mission.cpp index dcde32a16..00e8cbe2e 100644 --- a/Descent3/Mission.cpp +++ b/Descent3/Mission.cpp @@ -921,7 +921,7 @@ bool LoadMission(const char *mssn) { if (IS_MN3_FILE(mssn)) { strcpy(mission, mssn); - ddio_MakePath(pathname, D3MissionsDir, mission, NULL); + ddio_MakePath(pathname, "missions", mission, NULL); } else { strcpy(mission, mssn); strcpy(pathname, mssn); @@ -1645,9 +1645,7 @@ void DoMissionMovie(const char *movie) { return; #endif if (movie && *movie) { - char mpath[_MAX_PATH]; - ddio_MakePath(mpath, LocalD3Dir, "movies", movie, NULL); - PlayMovie(mpath); + PlayMovie(movie); } } @@ -1835,11 +1833,11 @@ bool mn3_Open(const char *mn3file) { char voice_hog[_MAX_PATH*2]; if ((stricmp(filename, "d3") == 0) || (stricmp(filename, "training") == 0)) { // Open audio hog file - ddio_MakePath(voice_hog, D3MissionsDir, "d3voice1.hog", nullptr); // Audio for levels 1-4 + ddio_MakePath(voice_hog, "missions", "d3voice1.hog", nullptr); // Audio for levels 1-4 Mission_voice_hog_handle = cf_OpenLibrary(voice_hog); } else if (stricmp(filename, "d3_2") == 0) { // Open audio hog file - ddio_MakePath(voice_hog, D3MissionsDir, "d3voice2.hog", nullptr); // Audio for levels 5-17 + ddio_MakePath(voice_hog, "missions", "d3voice2.hog", nullptr); // Audio for levels 5-17 Mission_voice_hog_handle = cf_OpenLibrary(voice_hog); } strcat(filename, ".gam"); @@ -1855,7 +1853,7 @@ bool mn3_GetInfo(const char *mn3file, tMissionInfo *msn) { char pathname[_MAX_PATH]; char filename[PSFILENAME_LEN + 1]; - ddio_MakePath(pathname, D3MissionsDir, mn3file, nullptr); + ddio_MakePath(pathname, "missions", mn3file, nullptr); handle = cf_OpenLibrary(pathname); if (handle == 0) { LOG_ERROR << "MISSION: MN3 failed to open."; diff --git a/Descent3/PilotPicsAPI.cpp b/Descent3/PilotPicsAPI.cpp index 7d8de7cca..520254d71 100644 --- a/Descent3/PilotPicsAPI.cpp +++ b/Descent3/PilotPicsAPI.cpp @@ -132,9 +132,7 @@ bool PPic_InitDatabase(void) { // attempt to open the hog database // -------------------------------- - char fullpath[_MAX_PATH]; - ddio_MakePath(fullpath, LocalD3Dir, PILOTPIC_DATABASE_HOG, NULL); - PilotPic_database_hog_handle = cf_OpenLibrary(fullpath); + PilotPic_database_hog_handle = cf_OpenLibrary(PILOTPIC_DATABASE_HOG); if (PilotPic_database_hog_handle == 0) { // there was an error opening the hog database diff --git a/Descent3/ambient.cpp b/Descent3/ambient.cpp index ade9f295f..518ba27dd 100644 --- a/Descent3/ambient.cpp +++ b/Descent3/ambient.cpp @@ -293,7 +293,7 @@ void WriteAmbientData() { CFILE *ofile; #ifndef NEWEDITOR - ddio_MakePath(filename, Base_directory, "data", "misc", AMBIENT_FILE_NAME, NULL); + ddio_MakePath(filename, cf_GetWritableBaseDirectory().u8string().c_str(), "data", "misc", AMBIENT_FILE_NAME, NULL); #else ddio_MakePath(filename, D3HogDir, "data", "misc", AMBIENT_FILE_NAME, NULL); #endif diff --git a/Descent3/args.cpp b/Descent3/args.cpp index f3a381932..a7501edd9 100644 --- a/Descent3/args.cpp +++ b/Descent3/args.cpp @@ -104,7 +104,7 @@ const char *SkipArgPrefix(const char *arg) { return arg; } -int FindArg(const char *which) { +int FindArg(const char *which, int start) { if (which == nullptr) return 0; @@ -112,14 +112,18 @@ int FindArg(const char *which) { return strcasecmp(which, SkipArgPrefix(arg)) == 0; }; - for (int i = 1; i <= TotalArgs; i++) { + for (int i = start; i <= TotalArgs; i++) { if (which_matches(GameArgs[i])) { LOG_INFO.printf("FindArg: Found [%s] at argument index (%d).", which, i); return i; } } - LOG_VERBOSE.printf("FindArg: Did not find [%s] on command line.", which); + if (start == 1) { + LOG_VERBOSE.printf("FindArg: Did not find [%s] on command line.", which); + } else { + LOG_VERBOSE.printf("FindArg: Did not find [%s] on command line at index %i or after index %i.", which, start, start); + } return 0; } diff --git a/Descent3/args.h b/Descent3/args.h index 5794af5ee..1e84b3d4f 100644 --- a/Descent3/args.h +++ b/Descent3/args.h @@ -28,7 +28,7 @@ void GatherArgs(const char *str); void GatherArgs(char **argv); // Returns index of argument sought, or 0 if not found -int FindArg(const char *which); +int FindArg(const char *which, int start = 1); int FindArgChar(const char *which, char singleCharArg); const char *GetArg(int index); diff --git a/Descent3/cinematics.cpp b/Descent3/cinematics.cpp index 06d0b7520..141de09c7 100644 --- a/Descent3/cinematics.cpp +++ b/Descent3/cinematics.cpp @@ -45,10 +45,7 @@ bool InitCinematics() { return true; } - char path[_MAX_PATH]; - ddio_MakePath(path, Base_directory, "movies", NULL); - - if (mve_Init(path, Sound_card_name) != MVELIB_NOERROR) + if (mve_Init() != MVELIB_NOERROR) return false; mve_SetCallback(CinematicCallback); diff --git a/Descent3/d3movie.cpp b/Descent3/d3movie.cpp index 1d627343a..3a123c2ad 100644 --- a/Descent3/d3movie.cpp +++ b/Descent3/d3movie.cpp @@ -53,8 +53,7 @@ static bool mve_InitSound(); static void mve_CloseSound(); #endif -// sets the directory where movies are stored -int mve_Init(const char *dir, const char *sndcard) { +int mve_Init() { #ifndef NO_MOVIES return MVELIB_NOERROR; #else @@ -72,47 +71,11 @@ void mve_SetCallback(MovieFrameCallback_fp callBack) { // used to tell movie library how to render movies. void mve_SetRenderProperties(int16_t x, int16_t y, int16_t w, int16_t h, renderer_type type, bool hicolor) {} -#if defined(POSIX) -// locates the case-sensitive movie file name -std::filesystem::path mve_FindMovieFileRealName(const std::filesystem::path &movie) { - // split into directory and file... - std::filesystem::path t_file = movie.filename(); - std::filesystem::path t_dir = movie.parent_path(); - std::filesystem::path t_out; - - // found a directory? - if (!t_dir.empty()) { - // map the bits (or fail) - t_out = cf_FindRealFileNameCaseInsensitive(t_file, t_dir); - if (t_out.empty()) - return t_out; - // re-assemble - return (t_dir / t_out); - } else { - // just a file, map that - t_out = cf_FindRealFileNameCaseInsensitive(t_file); - if (t_out.empty()) - return t_out; - // re-assemble - return t_out; - } -} -#endif - // plays a movie using the current screen. int mve_PlayMovie(const std::filesystem::path &pMovieName, oeApplication *pApp) { #ifndef NO_MOVIES // first, find that movie.. - std::filesystem::path real_name; -#if defined(POSIX) - real_name = mve_FindMovieFileRealName(pMovieName); - if (real_name.empty()) { - LOG_WARNING.printf("MOVIE: No such file %s", pMovieName.u8string().c_str()); - return MVELIB_FILE_ERROR; - } -#else - real_name = pMovieName; -#endif + std::filesystem::path real_name = cf_LocatePath("movies" / pMovieName); // open movie file. FILE *hFile = fopen(real_name.u8string().c_str(), "rb"); if (hFile == nullptr) { @@ -355,17 +318,7 @@ void CallbackShowFrameNoFlip(unsigned char *buf, unsigned int bufw, unsigned int intptr_t mve_SequenceStart(const char *mvename, void *fhandle, oeApplication *app, bool looping) { #ifndef NO_MOVIES // first, find that movie.. - std::filesystem::path real_name; -#if defined(POSIX) - real_name = mve_FindMovieFileRealName(mvename); - if (real_name.empty()) { - LOG_WARNING.printf("MOVIE: No such file %s", mvename); - fhandle = nullptr; - return 0; - } -#else - real_name = mvename; -#endif + std::filesystem::path real_name = cf_LocatePath(std::filesystem::path("movies") / mvename); fhandle = fopen(real_name.u8string().c_str(), "rb"); if (fhandle == nullptr) { diff --git a/Descent3/d3movie.h b/Descent3/d3movie.h index 558cc3dc4..4cd354435 100644 --- a/Descent3/d3movie.h +++ b/Descent3/d3movie.h @@ -33,7 +33,7 @@ #define MVELIB_PLAYBACK_ERROR (-5) #define MVELIB_PLAYBACK_ABORTED (-6) -int mve_Init(const char *dir, const char *sndcard); +int mve_Init(); // simply plays a movie. int mve_PlayMovie(const std::filesystem::path &pMovieName, oeApplication *pApp); diff --git a/Descent3/demofile.cpp b/Descent3/demofile.cpp index 2942c208e..7ba81f4bb 100644 --- a/Descent3/demofile.cpp +++ b/Descent3/demofile.cpp @@ -359,7 +359,7 @@ void DemoToggleRecording() { if (stricmp(szfile + (strlen(szfile) - 4), ".dem") != 0) { strcat(szfile, ".dem"); } - Demo_fname = std::filesystem::path(Base_directory) / "demo" / szfile; + Demo_fname = cf_GetWritableBaseDirectory() / "demo" / szfile; LOG_INFO.printf("Saving demo to file: %s", Demo_fname.u8string().c_str()); // Try to create the file Demo_cfp = cfopen(Demo_fname, "wb"); @@ -1408,7 +1408,7 @@ bool LoadDemoDialog() { // return false; // #else - std::filesystem::path file = std::filesystem::path(Base_directory) / "demo"; + std::filesystem::path file = cf_GetWritableBaseDirectory() / "demo"; if (DoPathFileDialog(false, file, TXT_VIEWDEMO, {"*.dem"}, PFDF_FILEMUSTEXIST)) { Demo_fname = file; diff --git a/Descent3/descent.cpp b/Descent3/descent.cpp index 80f223848..46cabed32 100644 --- a/Descent3/descent.cpp +++ b/Descent3/descent.cpp @@ -490,7 +490,7 @@ void Descent3() { }; for (auto const &intro : intros) { - PlayMovie(std::filesystem::path(Base_directory) / "movies" / intro); + PlayMovie(intro); } } diff --git a/Descent3/descent.h b/Descent3/descent.h index 9ea4f976f..68b0cb5cb 100644 --- a/Descent3/descent.h +++ b/Descent3/descent.h @@ -181,9 +181,6 @@ extern bool Descent_overrided_intro; // How long the a mission name can be #define MSN_NAMELEN 32 -// The "root" directory of the D3 file tree -extern char Base_directory[]; - // Variable to preserve current path. TODO: redundant? extern std::filesystem::path orig_pwd; diff --git a/Descent3/game.cpp b/Descent3/game.cpp index 14ad58c86..9a74c0a3d 100644 --- a/Descent3/game.cpp +++ b/Descent3/game.cpp @@ -1279,7 +1279,7 @@ void DoScreenshot() { count = 1; while (!done) { snprintf(str, sizeof(str), "Screenshot%.3d.png", count); - ddio_MakePath(filename, Base_directory, str, NULL); + ddio_MakePath(filename, cf_GetWritableBaseDirectory().u8string().c_str(), str, NULL); infile = (CFILE *)cfopen(filename, "rb"); if (infile == NULL) { done = 1; diff --git a/Descent3/gamesave.cpp b/Descent3/gamesave.cpp index 7209d2925..bd44e1ed6 100644 --- a/Descent3/gamesave.cpp +++ b/Descent3/gamesave.cpp @@ -348,7 +348,7 @@ void QuickSaveGame() { i = Quicksave_game_slot; snprintf(filename, sizeof(filename), "saveg00%d", i); - ddio_MakePath(pathname, Base_directory, "savegame", filename, NULL); + ddio_MakePath(pathname, cf_GetWritableBaseDirectory().u8string().c_str(), "savegame", filename, NULL); fp = fopen(pathname, "rb"); if (fp) { @@ -391,7 +391,7 @@ void SaveGameDialog() { #endif // setup paths. - ddio_MakePath(savegame_dir, Base_directory, "savegame", NULL); + ddio_MakePath(savegame_dir, cf_GetWritableBaseDirectory().u8string().c_str(), "savegame", NULL); // ddio_MakePath(pathname, savegame_dir, "*.sav", NULL); -unused // create savegame directory if it didn't exist before. @@ -543,7 +543,7 @@ void __cdecl LoadGameDialogCB(newuiTiledWindow *wnd, void *data) LOG_DEBUG.printf("savegame slot=%d", id - SAVE_HOTSPOT_ID); - ddio_MakePath(savegame_dir, Base_directory, "savegame", NULL); + ddio_MakePath(savegame_dir, cf_GetWritableBaseDirectory().u8string().c_str(), "savegame", NULL); snprintf(filename, sizeof(filename), "saveg00%d", (id - SAVE_HOTSPOT_ID)); ddio_MakePath(pathname, savegame_dir, filename, NULL); @@ -586,7 +586,7 @@ bool LoadGameDialog() { } // setup paths. - ddio_MakePath(savegame_dir, Base_directory, "savegame", NULL); + ddio_MakePath(savegame_dir, cf_GetWritableBaseDirectory().u8string().c_str(), "savegame", NULL); ddio_MakePath(pathname, savegame_dir, "*.sav", NULL); // create savegame directory if it didn't exist before. diff --git a/Descent3/gamesequence.cpp b/Descent3/gamesequence.cpp index d94a799af..fd8a26598 100644 --- a/Descent3/gamesequence.cpp +++ b/Descent3/gamesequence.cpp @@ -1295,31 +1295,28 @@ bool GameSequencer() { // Make sure we have the correct hogfile void CheckHogfile() { - char hogpath[_MAX_PATH * 2]; + const char *new_mn3; LOG_DEBUG << "Checking to see if we need to open another hog off of disk or CDROM"; if (Current_mission.filename && (stricmp(Current_mission.filename, "d3.mn3") == 0) && (Current_mission.cur_level > 4)) { - // close the mission hog file and open d3_2.mn3 - mn3_Close(); - ddio_MakePath(hogpath, D3MissionsDir, "d3_2.mn3", nullptr); - if (cfexist(hogpath)) { - mn3_Open(hogpath); - mem_free(Current_mission.filename); - Current_mission.filename = mem_strdup("d3_2.mn3"); - } else { - SetFunctionMode(MENU_MODE); - } + new_mn3 = "d3_2.mn3"; } else if (Current_mission.filename && (stricmp(Current_mission.filename, "d3_2.mn3") == 0) && (Current_mission.cur_level <= 4)) { - // Part 2 of the mission is d3_2.mn3 - // close the mission hog file and open d3.mn3 + new_mn3 = "d3.mn3"; + } else { + new_mn3 = NULL; + } + + if (new_mn3) { + // close the mission hog file and open the new one mn3_Close(); - ddio_MakePath(hogpath, D3MissionsDir, "d3.mn3", nullptr); - if (cfexist(hogpath)) { - mn3_Open(hogpath); + auto relative_path = std::filesystem::path("missions") / new_mn3; + auto absolute_path = cf_LocatePath(relative_path); + if (std::filesystem::exists(absolute_path)) { + mn3_Open(relative_path.u8string().c_str()); mem_free(Current_mission.filename); - Current_mission.filename = mem_strdup("d3.mn3"); + Current_mission.filename = mem_strdup(new_mn3); } else { SetFunctionMode(MENU_MODE); } @@ -1628,35 +1625,9 @@ void StartLevel() { // Loads a level and starts everything up bool LoadAndStartCurrentLevel() { - char hogpath[_MAX_PATH * 2]; // This is a bit redundant because we just did it in most cases, but we need to be sure that it always happens, // and this code is here for weird systems, like save/load and demo, etc. - if (Current_mission.filename && (stricmp(Current_mission.filename, "d3.mn3") == 0) && - (Current_mission.cur_level > 4)) { - // close the mission hog file and open d3_2.mn3 - mn3_Close(); - ddio_MakePath(hogpath, D3MissionsDir, "d3_2.mn3", nullptr); - if (cfexist(hogpath)) { - mn3_Open(hogpath); - mem_free(Current_mission.filename); - Current_mission.filename = mem_strdup("d3_2.mn3"); - } else { - SetFunctionMode(MENU_MODE); - } - } else if (Current_mission.filename && (stricmp(Current_mission.filename, "d3_2.mn3") == 0) && - (Current_mission.cur_level <= 4)) { - // Part 2 of the mission is d3_2.mn3 - // close the mission hog file and open d3.mn3 - mn3_Close(); - ddio_MakePath(hogpath, D3MissionsDir, "d3.mn3", nullptr); - if (cfexist(hogpath)) { - mn3_Open(hogpath); - mem_free(Current_mission.filename); - Current_mission.filename = mem_strdup("d3.mn3"); - } else { - SetFunctionMode(MENU_MODE); - } - } + CheckHogfile(); // load the level. if fails, then bail out // ShowProgressScreen (TXT_LOADINGLEVEL); diff --git a/Descent3/gamespy.cpp b/Descent3/gamespy.cpp index 29d0f5474..1eaa747cc 100644 --- a/Descent3/gamespy.cpp +++ b/Descent3/gamespy.cpp @@ -124,7 +124,7 @@ int gspy_Init() { } // Read the config, resolve the name if needed and setup the server addresses - cfgpath = std::filesystem::path(Base_directory) / gspy_cfgfilename; + cfgpath = cf_LocatePath(gspy_cfgfilename); gspy_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); diff --git a/Descent3/init.cpp b/Descent3/init.cpp index 2df70ff07..fcec5fce7 100644 --- a/Descent3/init.cpp +++ b/Descent3/init.cpp @@ -1026,9 +1026,6 @@ static bool Graphics_init = false; static bool Title_bitmap_init = false; uint8_t Use_motion_blur = 0; -// The "root" directory of the D3 file tree -char Base_directory[_MAX_PATH]; - extern int Min_allowed_frametime; extern bool Render_powerup_sparkles; @@ -1393,12 +1390,12 @@ void LoadGameSettings() { void InitIOSystems(bool editor) { ddio_init_info io_info; - // Set the base directory + // Set the writable base directory int dirarg = FindArg("-setdir"); int exedirarg = FindArg("-useexedir"); + std::filesystem::path writable_base_directory; if (dirarg) { - strncpy(Base_directory, GameArgs[dirarg + 1], sizeof(Base_directory) - 1); - Base_directory[sizeof(Base_directory) - 1] = '\0'; + writable_base_directory = GameArgs[dirarg + 1]; } else if (exedirarg) { char exec_path[_MAX_PATH]; memset(exec_path, 0, sizeof(exec_path)); @@ -1407,16 +1404,28 @@ void InitIOSystems(bool editor) { Error("Failed to get executable path\n"); } else { std::filesystem::path executablePath(exec_path); - std::string baseDirectoryString = executablePath.parent_path().string(); - strncpy(Base_directory, baseDirectoryString.c_str(), sizeof(Base_directory) - 1); - Base_directory[sizeof(Base_directory) - 1] = '\0'; - LOG_INFO << "Using working directory of " << Base_directory; + writable_base_directory = executablePath.parent_path(); + LOG_INFO << "Using working directory of " << writable_base_directory; } } else { - ddio_GetWorkingDir(Base_directory, sizeof(Base_directory)); + writable_base_directory = std::filesystem::current_path(); } - ddio_SetWorkingDir(Base_directory); + ddio_SetWorkingDir(writable_base_directory.u8string().c_str()); + cf_AddBaseDirectory(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) { + LOG_WARNING << "-additionaldir was at the end of the argument list. It should never be at the end of the argument list."; + break; + } else { + cf_AddBaseDirectory(std::filesystem::path(dir_to_add)); + additionaldirarg += 2; + } + } Descent->set_defer_handler(D3DeferHandler); @@ -1469,42 +1478,36 @@ void InitIOSystems(bool editor) { // Init hogfiles INIT_MESSAGE(("Checking for HOG files.")); int d3_hid, extra_hid, sys_hid, extra13_hid; - char fullname[_MAX_PATH]; + std::filesystem::path hog_name; #ifdef DEMO // DAJ d3_hid = cf_OpenLibrary("d3demo.hog"); - ddio_MakePath(fullname, LocalD3Dir, "d3demo.hog", NULL); + hog_name = "d3demo.hog"; #else - ddio_MakePath(fullname, LocalD3Dir, "d3.hog", NULL); + hog_name = "d3.hog"; #endif - d3_hid = cf_OpenLibrary(fullname); + d3_hid = cf_OpenLibrary(hog_name); // JC: Steam release uses extra1.hog instead of extra.hog, so try loading it first // Open this file if it's present for stuff we might add later - ddio_MakePath(fullname, LocalD3Dir, "extra1.hog", NULL); - extra_hid = cf_OpenLibrary(fullname); + extra_hid = cf_OpenLibrary("extra1.hog"); if (extra_hid == 0) { - ddio_MakePath(fullname, LocalD3Dir, "extra.hog", NULL); - extra_hid = cf_OpenLibrary(fullname); + extra_hid = cf_OpenLibrary("extra.hog"); } // JC: Steam release uses extra.hog instead of merc.hog, so try loading it last (so we don't conflict with the above) // Open mercenary hog if it exists - ddio_MakePath(fullname, LocalD3Dir, "merc.hog", NULL); - merc_hid = cf_OpenLibrary(fullname); + merc_hid = cf_OpenLibrary("merc.hog"); if (merc_hid == 0) { - ddio_MakePath(fullname, LocalD3Dir, "extra.hog", NULL); - merc_hid = cf_OpenLibrary(fullname); + merc_hid = cf_OpenLibrary("extra.hog"); } // Open this for extra 1.3 code (Black Pyro, etc) - ddio_MakePath(fullname, LocalD3Dir, "extra13.hog", NULL); - extra13_hid = cf_OpenLibrary(fullname); + extra13_hid = cf_OpenLibrary("extra13.hog"); // last library opened is the first to be searched for dynamic libs, so put // this one at the end to find our newly build script libraries first - ddio_MakePath(fullname, LocalD3Dir, PRIMARY_HOG, NULL); - sys_hid = cf_OpenLibrary(fullname); + sys_hid = cf_OpenLibrary(PRIMARY_HOG); // Check to see if there is a -mission command line option // if there is, attempt to open that hog/mn3 so it can override such @@ -2025,7 +2028,7 @@ void SetupTempDirectory(void) { exit(1); } // restore working dir - ddio_SetWorkingDir(Base_directory); + ddio_SetWorkingDir(cf_GetWritableBaseDirectory().u8string().c_str()); } void DeleteTempFiles() { diff --git a/Descent3/menu.cpp b/Descent3/menu.cpp index 1c0a5e2cd..417cd3800 100644 --- a/Descent3/menu.cpp +++ b/Descent3/menu.cpp @@ -1061,51 +1061,55 @@ bool ProcessCommandLine() { #define TRAINING_MISSION_NAME "Pilot Training" /** - * Count singleplayer missions in directory. Mission should have .mn3 extension. - * @param base_directory 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 &base_directory) { +static inline int count_missions(const std::vector &missions_directories) { int c = 0; - ddio_DoForeachFile(base_directory, std::regex(".*\\.mn3"), [&c](const std::filesystem::path &path) { - if (stricmp(path.filename().u8string().c_str(), "d3_2.mn3") == 0) - return; - LOG_DEBUG.printf("Mission path: %s", path.u8string().c_str()); - tMissionInfo msninfo{}; - GetMissionInfo(path.filename().u8string().c_str(), &msninfo); + for (const 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; + LOG_DEBUG.printf("Mission path: %s", path.u8string().c_str()); + tMissionInfo msninfo{}; + GetMissionInfo(path.filename().u8string().c_str(), &msninfo); - if (msninfo.name[0] && msninfo.single) { - LOG_DEBUG.printf("Name: %s", msninfo.name); - c++; - if (!(c % 2)) - DoWaitMessage(true); - } else { - LOG_DEBUG.printf("Illegal or multiplayer mission: %s", path.u8string().c_str()); - } - }); + if (msninfo.name[0] && msninfo.single) { + LOG_DEBUG.printf("Name: %s", msninfo.name); + c++; + if (!(c % 2)) + DoWaitMessage(true); + } else { + LOG_DEBUG.printf("Illegal or multiplayer mission: %s", path.u8string().c_str()); + } + }); + } return c; } static inline int generate_mission_listbox(newuiListBox *lb, int n_maxfiles, char **filelist, - const std::filesystem::path &base_directory) { + const std::vector &missions_directories) { int c = 0; - ddio_DoForeachFile( - base_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 (const 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; } @@ -1182,8 +1186,9 @@ bool MenuNewGame() { // add mission names to listbox // count valid mission files. // add a please wait dialog here. + auto missions_directories = cf_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)); @@ -1196,7 +1201,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++) { diff --git a/Descent3/mmItem.cpp b/Descent3/mmItem.cpp index e5a692ef6..34292cdfe 100644 --- a/Descent3/mmItem.cpp +++ b/Descent3/mmItem.cpp @@ -330,9 +330,7 @@ void mmInterface::Create() { m_movie = NULL; static_menu_background = true; } else { - char filename[_MAX_PATH]; - ddio_MakePath(filename, Base_directory, "movies", "mainmenu", NULL); - m_movie = StartMovie(filename, true); + m_movie = StartMovie("mainmenu", true); if (!m_movie) //[ISB] Didn't find the menu movie? { diff --git a/Descent3/multi.cpp b/Descent3/multi.cpp index 25b39c870..543d298ed 100644 --- a/Descent3/multi.cpp +++ b/Descent3/multi.cpp @@ -8010,23 +8010,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, "custom", "graphics", NetPlayers[playernum].ship_logo, NULL); + ddio_MakePath(rval, cf_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, "custom", "sounds", NetPlayers[playernum].voice_taunt1, NULL); + ddio_MakePath(rval, cf_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, "custom", "sounds", NetPlayers[playernum].voice_taunt2, NULL); + ddio_MakePath(rval, cf_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, "custom", "sounds", NetPlayers[playernum].voice_taunt3, NULL); + ddio_MakePath(rval, cf_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, "custom", "sounds", NetPlayers[playernum].voice_taunt4, NULL); + ddio_MakePath(rval, cf_GetWritableBaseDirectory().u8string().c_str(), "custom", "sounds", NetPlayers[playernum].voice_taunt4, NULL); break; default: LOG_FATAL.printf("Unknown id (%d) passed to GetFileNameFromPlayerAndID()", id); diff --git a/Descent3/multi_dll_mgr.cpp b/Descent3/multi_dll_mgr.cpp index 1dff4f69e..d7e3d961d 100644 --- a/Descent3/multi_dll_mgr.cpp +++ b/Descent3/multi_dll_mgr.cpp @@ -520,6 +520,9 @@ 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 *)cf_LocatePath; + api->fp[113] = (int *)cf_LocateMultiplePaths; + api->fp[114] = (int *)cf_GetWritableBaseDirectory; // Variable pointers api->vp[0] = (int *)&Player_num; @@ -527,7 +530,7 @@ void GetMultiAPI(multi_api *api) { 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 *)NULL; // Base_directory; no longer exists api->vp[6] = (int *)&MultiDLLGameStarting; api->vp[7] = (int *)MTPilotinfo; api->vp[8] = (int *)&Num_network_games_known; @@ -598,7 +601,7 @@ int LoadMultiDLL(const char *name) { if (MultiDLLHandle.handle) FreeMultiDLL(); - std::filesystem::path dll_path_name = std::filesystem::path(Base_directory) / "online"; + std::filesystem::path dll_path_name = cf_GetWritableBaseDirectory() / "online"; ddio_DoForeachFile(dll_path_name, std::regex(".+\\.tmp"), [](const std::filesystem::path& path, ...) { std::error_code ec; std::filesystem::remove(path, ec); @@ -608,7 +611,7 @@ int LoadMultiDLL(const char *name) { }); // Make the hog filename - lib_name = std::filesystem::path(Base_directory) / "online" / name; + lib_name = std::filesystem::path("online") / name; lib_name.replace_extension(".d3c"); // Make the dll filename dll_name = name; @@ -616,7 +619,7 @@ int LoadMultiDLL(const char *name) { // Open the hog file if (!cf_OpenLibrary(lib_name)) { - tmp_dll_name = std::filesystem::path(Base_directory) / "online" / name; + tmp_dll_name = std::filesystem::path("online") / name; tmp_dll_name.replace_extension(".d3c"); Multi_conn_dll_name.clear(); goto loaddll; diff --git a/Descent3/multi_ui.cpp b/Descent3/multi_ui.cpp index facde1fbe..7ff191151 100644 --- a/Descent3/multi_ui.cpp +++ b/Descent3/multi_ui.cpp @@ -442,11 +442,12 @@ int MainMultiplayerMenu() { std::vector dllnames; - ddio_DoForeachFile(std::filesystem::path(Base_directory) / "online", std::regex(".*\\.d3c"), - [&dllnames](const std::filesystem::path &path) { - std::string filename = path.stem().string(); + for (const auto &online_directory : cf_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) { @@ -455,6 +456,7 @@ int MainMultiplayerMenu() { dllnames.push_back(filename); } }); + } for (auto const &name : dllnames) { lists->AddItem(name.c_str()); @@ -989,7 +991,7 @@ void DoMultiAllowed(void) { } void MultiDoConfigSave() { - std::filesystem::path file = std::filesystem::path(Base_directory) / "custom" / "settings"; + std::filesystem::path file = cf_GetWritableBaseDirectory() / "custom" / "settings"; if (DoPathFileDialog(true, file, TXT_MULTISAVESET, {"*.mps"}, 0)) { file.replace_extension(".mps"); MultiSaveSettings(file); @@ -997,7 +999,7 @@ void MultiDoConfigSave() { } void MultiDoConfigLoad() { - std::filesystem::path file = std::filesystem::path(Base_directory) / "custom" / "settings"; + std::filesystem::path file = cf_GetWritableBaseDirectory() / "custom" / "settings"; if (DoPathFileDialog(false, file, TXT_MULTILOADSET, {"*.mps"}, PFDF_FILEMUSTEXIST)) MultiLoadSettings(file); } diff --git a/Descent3/pilot.cpp b/Descent3/pilot.cpp index e2191e8f5..244aa5871 100644 --- a/Descent3/pilot.cpp +++ b/Descent3/pilot.cpp @@ -1581,7 +1581,7 @@ bool PltDelete(pilot *Pilot) { std::string pfilename = Pilot->get_filename(); std::error_code ec; if (!pfilename.empty()) { - return std::filesystem::remove(std::filesystem::path(Base_directory) / pfilename, ec); + return std::filesystem::remove(cf_GetWritableBaseDirectory() / pfilename, ec); } else { Int3(); // this is odd @@ -1595,7 +1595,7 @@ bool PltDelete(pilot *Pilot) { PltMakeFNValid(pname); pfilename = std::string(pname) + PLTEXTENSION; - return std::filesystem::remove(std::filesystem::path(Base_directory) / pfilename, ec); + return std::filesystem::remove(cf_GetWritableBaseDirectory() / pfilename, ec); } } @@ -1634,7 +1634,7 @@ void PltReadFile(pilot *Pilot, bool keyconfig, bool missiondata) { return; // open and process file - std::filesystem::path filename = std::filesystem::path(Base_directory) / pfilename; + std::filesystem::path filename = cf_GetWritableBaseDirectory() / pfilename; try { file = cfopen(filename, "rb"); if (!file) @@ -1677,7 +1677,6 @@ std::vector PltGetPilots(std::string ignore_filename, int display_d // clear list PltClearList(); - std::filesystem::path search = std::filesystem::path(Base_directory); std::regex wildcard; std::vector result; @@ -1699,7 +1698,7 @@ std::vector PltGetPilots(std::string ignore_filename, int display_d break; } - ddio_DoForeachFile(search, wildcard, [&ignore_filename, &result](const std::filesystem::path &path) { + ddio_DoForeachFile(cf_GetWritableBaseDirectory(), wildcard, [&ignore_filename, &result](const std::filesystem::path &path) { std::string pilot = path.filename().u8string(); if (!ignore_filename.empty() && stricmp(ignore_filename.c_str(), pilot.c_str()) == 0) { LOG_INFO.printf("Getting Pilots... found %s, but ignoring", pilot.c_str()); @@ -3275,7 +3274,7 @@ void _ReadOldPilotFile(pilot *Pilot, bool keyconfig, bool missiondata) { std::string pfilename = Pilot->get_filename(); // open and process file - std::filesystem::path filename = std::filesystem::path(Base_directory) / pfilename; + std::filesystem::path filename = cf_GetWritableBaseDirectory() / pfilename; CFILE *file = cfopen(filename, "rb"); if (!file) return; diff --git a/Descent3/pilot_class.cpp b/Descent3/pilot_class.cpp index ad33cdfba..d459fff1a 100644 --- a/Descent3/pilot_class.cpp +++ b/Descent3/pilot_class.cpp @@ -415,7 +415,7 @@ int pilot::flush(bool new_file) { } CFILE *file; - std::filesystem::path real_filename = std::filesystem::path(Base_directory) / filename; + std::filesystem::path real_filename = cf_GetWritableBaseDirectory() / filename; if (new_file && cfexist(real_filename)) { // the file already exists, we can't write out @@ -497,7 +497,7 @@ int pilot::read(bool skip_config, bool skip_mission_data) { } CFILE *file; - std::filesystem::path real_filename = std::filesystem::path(Base_directory) / filename; + std::filesystem::path real_filename = cf_GetWritableBaseDirectory() / filename; if (!cfexist(real_filename)) { // the file already exists, we can't write out diff --git a/Descent3/sdlmain.cpp b/Descent3/sdlmain.cpp index dca8161e1..3b04e6b8f 100644 --- a/Descent3/sdlmain.cpp +++ b/Descent3/sdlmain.cpp @@ -160,7 +160,7 @@ class oeD3LnxDatabase : public oeLnxAppDatabase { char *netdir = getenv("D3_DIR"); if (!dir) - strcpy(path, Base_directory); + strcpy(path, ""); else strcpy(path, dir); diff --git a/cfile/cfile.cpp b/cfile/cfile.cpp index 275d3366c..27475f2fd 100644 --- a/cfile/cfile.cpp +++ b/cfile/cfile.cpp @@ -54,6 +54,16 @@ struct library { FILE *file = nullptr; // pointer to file for this lib, if no one using it }; +/* The "root" directories of the D3 file tree + * + * Directories that come later in the list override directories that come + * earlier in the list. For example, if Base_directories[0] / "d3.hog" exists + * and Base_directories[1] / "d3.hog" also exists, then the one in + * Base_directories[1] will get used. The one in Base_directories[0] will be + * ignored. + */ +std::vector Base_directories = {}; + // Map of paths. If value of entry is true, path is only for specific extensions std::map paths; @@ -68,6 +78,133 @@ cfile_error cfe; // The message for unexpected end of file const char *eof_error = "Unexpected end of file"; +/* This function should be called at least once before you use anything else + * from this module. + */ +void cf_AddBaseDirectory(const std::filesystem::path &base_directory) { + if (std::filesystem::exists(base_directory)) { + Base_directories.push_back(base_directory); + } else { + LOG_WARNING << "Ignoring nonexistent base directory: " << base_directory; + } +} + +/* After you call this function, you must call cf_AddBaseDirectory() at least + * once before you use anything else from this module. + */ +void cf_ClearBaseDirectories() { + Base_directories.clear(); +} + + +std::filesystem::path cf_LocatePathCaseInsensitiveHelper(const std::filesystem::path &relative_path, + const std::filesystem::path &starting_dir) { +#ifdef WIN32 + std::filesystem::path result = starting_dir / relative_path; + if (std::filesystem::exists(result)) { + return result; + } else { + return {}; + } +#else + // Dumb check, maybe there already all ok? + if (exists((starting_dir / relative_path))) { + return starting_dir / relative_path; + } + + std::filesystem::path result, search_path, search_file; + + search_path = starting_dir / relative_path.parent_path(); + search_file = relative_path.filename(); + + // If directory does not exist, nothing to search. + if (!std::filesystem::is_directory(search_path) || search_file.empty()) { + return {}; + } + + + // Search component in search_path + auto const &it = std::filesystem::directory_iterator(search_path); + + auto found = std::find_if(it, end(it), [&search_file, &search_path, &result](const auto& dir_entry) { + return stricmp(dir_entry.path().filename().u8string().c_str(), search_file.u8string().c_str()) == 0; + }); + + if (found != end(it)) { + // Match, append to result + result = found->path(); + search_path = result; + } else { + // Component not found, mission failed + return {}; + } + + return result; +#endif +} + +std::vector cf_LocatePathMultiplePathsHelper(const std::filesystem::path &relative_path, + bool stop_after_first_result) { + ASSERT(("realative_path should be a relative path.", relative_path.is_relative())); + std::vector return_value = { }; + for (auto base_directories_iterator = Base_directories.rbegin(); + base_directories_iterator != Base_directories.rend(); + ++base_directories_iterator) { + ASSERT(("base_directory should be an absolute path.", base_directories_iterator->is_absolute())); + auto to_append = cf_LocatePathCaseInsensitiveHelper(relative_path, *base_directories_iterator); + ASSERT(("to_append should be either empty or an absolute path.", to_append.empty() || to_append.is_absolute())); + if (std::filesystem::exists(to_append)) { + return_value.insert(return_value.begin(), to_append); + if (stop_after_first_result) { + break; + } + } + } + return return_value; +} + +/** + * Tries to find a relative path inside of one of the Base_directories. + * + * @param relative_path A relative path that we’ll hopefully find in + * one of the Base_directories. You don’t have to get the + * capitalization of relative_path correct, even on macOS + * and Linux. + * + * @return Either an absolute path that’s inside a base directory or an empty + * path if nothing is found. + */ +std::filesystem::path cf_LocatePath(const std::filesystem::path &relative_path) { + auto return_value_list = cf_LocatePathMultiplePathsHelper(relative_path, true); + if (return_value_list.empty()) { + return ""; + } else { + return return_value_list.front(); + } +} + +/** + * Tries to find multiple relative paths inside of the Base_directories. + * + * @param relative_path A relative path that we’ll hopefully find in + * one or more of the Base_directories. You don’t have to + * get the capitalization of relative_path correct, even on + * macOS and Linux. + * + * @return A list of absolute paths. Each path will be inside one of the + * Base_directories. + */ +std::vector cf_LocateMultiplePaths(const std::filesystem::path &relative_path) { + return cf_LocatePathMultiplePathsHelper(relative_path, false); +} + +/* Not all Base_directories are necessarily writable, but this function will + * return one that should be writable. + */ +std::filesystem::path cf_GetWritableBaseDirectory() { + return Base_directories.front(); +} + // Generates a cfile error void ThrowCFileError(int type, CFILE *file, const char *msg) { cfe.read_write = type; @@ -82,9 +219,9 @@ static void cf_Close(); static CFILE *open_file_in_lib(const char *filename); // Opens a HOG file. Future calls to cfopen(), etc. will look in this HOG. -// Parameters: libname - the path & filename of the HOG file -// NOTE: libname must be valid for the entire execution of the program. Therefore, it should either -// be a fully-specified path name, or the current directory must not change. +// Parameters: libname - path to the HOG file, relative to one of the Base_directories. +// NOTE: libname must be valid for the entire execution of the program. Therefore, Base_directories +// must not change. // Returns: 0 if error, else library handle that can be used to close the library int cf_OpenLibrary(const std::filesystem::path &libname) { FILE *fp; @@ -96,25 +233,7 @@ int cf_OpenLibrary(const std::filesystem::path &libname) { // allocation library structure std::shared_ptr lib = std::make_shared(); - - // resolve library name - std::filesystem::path resolve_dir = libname.parent_path(); - std::filesystem::path resolve_name = libname; - - if (!resolve_dir.empty()) { - resolve_name = libname.filename(); - } - - std::filesystem::path t_out = cf_FindRealFileNameCaseInsensitive(resolve_name, resolve_dir); - if (t_out.empty()) { - return 0; // CF_NO_FILE - } - // re-assemble - if (!resolve_dir.empty()) - lib->name = resolve_dir / t_out; - else - lib->name = t_out; - + lib->name = cf_LocatePath(libname); fp = fopen(lib->name.u8string().c_str(), "rb"); if (fp == nullptr) { return 0; // CF_NO_FILE; @@ -353,42 +472,6 @@ CFILE *open_file_in_lib(const char *filename) { return nullptr; } -std::filesystem::path cf_FindRealFileNameCaseInsensitive(const std::filesystem::path &fname, - const std::filesystem::path &directory) { - // Dumb check, maybe there already all ok? - if (exists((directory / fname))) { - return fname.filename(); - } - - std::filesystem::path result, search_path, search_file; - - search_path = directory / fname.parent_path(); - search_file = fname.filename(); - - // If directory does not exist, nothing to search. - if (!std::filesystem::is_directory(search_path) || search_file.empty()) { - return {}; - } - - // Search component in search_path - auto const &it = std::filesystem::directory_iterator(search_path); - - auto found = std::find_if(it, end(it), [&search_file, &search_path, &result](const auto &dir_entry) { - return stricmp(dir_entry.path().filename().u8string().c_str(), search_file.u8string().c_str()) == 0; - }); - - if (found != end(it)) { - // Match, append to result - result = found->path(); - search_path = result; - } else { - // Component not found, mission failed - return {}; - } - - return result.filename(); -} - // look for the file in the specified directory static CFILE *open_file_in_directory(const std::filesystem::path &filename, const char *mode, const std::filesystem::path &directory); @@ -403,9 +486,12 @@ CFILE *open_file_in_directory(const std::filesystem::path &filename, const char if (std::filesystem::is_directory(directory)) { // Make a full path using_filename = directory / filename; - } else { - // no directory specified, so just use filename passed + } else if (filename.is_absolute()) { + // no directory specified, and filename is an absolute path using_filename = filename; + } else { + // no directory specified, and filename is a relative path + using_filename = cf_LocatePath(filename); } // set read or write mode @@ -416,34 +502,8 @@ CFILE *open_file_in_directory(const std::filesystem::path &filename, const char fp = fopen(using_filename.u8string().c_str(), tmode); if (!fp) { -#if defined(POSIX) - // If we tried to open file for reading, assume there maybe case-sensitive files - if (tmode[0] == 'r') { - // Try different cases of the filename - using_filename = cf_FindRealFileNameCaseInsensitive(filename, directory); - if (using_filename.empty()) { - // just give up - return nullptr; - } - - if (std::filesystem::is_directory(directory)) { - // Make a full path - using_filename = directory / using_filename; - } - - fp = fopen(using_filename.u8string().c_str(), tmode); - if (!fp) { - // no dice - return nullptr; - } - } else { - // Error on writing file - return nullptr; - } -#else - // We on incase-sensitive filesystem, no file means no file. + // File not found return nullptr; -#endif } else { using_filename = filename; } diff --git a/cfile/cfile.h b/cfile/cfile.h index 7c51d189b..427964281 100644 --- a/cfile/cfile.h +++ b/cfile/cfile.h @@ -140,13 +140,64 @@ enum CFileExitStatus { CFES_IN_LIBRARY, }; +/* The "root" directories of the D3 file tree + * + * Directories that come later in the list override directories that come + * earlier in the list. For example, if Base_directories[0] / "d3.hog" exists + * and Base_directories[1] / "d3.hog" also exists, then the one in + * Base_directories[1] will get used. The one in Base_directories[0] will be + * ignored. + */ +extern std::vector Base_directories; + +/* This function should be called at least once before you use anything else + * from this module. + */ +void cf_AddBaseDirectory(const std::filesystem::path &base_directory); + +/* After you call this function, you must call cf_AddBaseDirectory() at least + * once before you use anything else from this module. + */ +void cf_ClearBaseDirectories(); + +/** + * Tries to find a relative path inside of one of the Base_directories. + * + * @param relative_path A relative path that we’ll hopefully find in + * one of the Base_directories. You don’t have to get the + * capitalization of relative_path correct, even on macOS + * and Linux. + * + * @return Either an absolute path that’s inside Base_directory or an empty path + * if nothing is found. + */ +std::filesystem::path cf_LocatePath(const std::filesystem::path &relative_path); + +/** + * Tries to find multiple relative paths inside of the Base_directories. + * + * @param relative_path A relative path that we’ll hopefully find in + * one or more of the Base_directories. You don’t have to + * get the capitalization of relative_path correct, even on + * macOS and Linux. + * + * @return A list of absolute paths. Each path will be inside one of the + * Base_directories. + */ +std::vector cf_LocateMultiplePaths(const std::filesystem::path &relative_path); + +/* Not all Base_directories are necessarily writable, but this function will + * return one that should be writable. + */ +std::filesystem::path cf_GetWritableBaseDirectory(); + // See if a file is in a hog bool cf_IsFileInHog(const std::filesystem::path &filename, const std::filesystem::path &hogname); // Opens a HOG file. Future calls to cfopen(), etc. will look in this HOG. -// Parameters: libname - the path & filename of the HOG file -// NOTE: libname must be valid for the entire execution of the program. Therefore, it should either -// be a fully-specified path name, or the current directory must not change. +// Parameters: libname - path to the HOG file, relative to one of the Base_directories. +// NOTE: libname must be valid for the entire execution of the program. Therefore, Base_directories +// must not change. // Returns: 0 if error, else library handle that can be used to close the library int cf_OpenLibrary(const std::filesystem::path &libname); @@ -154,17 +205,6 @@ int cf_OpenLibrary(const std::filesystem::path &libname); // Parameters: handle: the handle returned by cf_OpenLibrary() void cf_CloseLibrary(int handle); -/** - * Returns fixed case file name to actual case on disk for case-sensitive filesystems (Linux). - * @param fname the fixed case name to map to reality - * @param directory optional directory to search within (default - current path) - * @return filename with actual case name or empty path if there no mapping in filesystem - * @note This function returns only filename without directory part, i.e. - * cf_FindRealFileNameCaseInsensitive("test/test.txt") will return only "test.txt" on success. - */ -std::filesystem::path cf_FindRealFileNameCaseInsensitive(const std::filesystem::path &fname, - const std::filesystem::path &directory = "."); - /** * Add directory path into paths to look in for files. If ext_list is empty, * look in this directory for all files. Otherwise, the directory will only diff --git a/cfile/tests/cfile_tests.cpp b/cfile/tests/cfile_tests.cpp index 316c65812..c2b046878 100644 --- a/cfile/tests/cfile_tests.cpp +++ b/cfile/tests/cfile_tests.cpp @@ -22,7 +22,12 @@ #include #include "cfile.h" +void add_cwd_to_base_directories() { + cf_AddBaseDirectory(std::filesystem::current_path()); +} + TEST(D3, CFileIO) { + add_cwd_to_base_directories(); int lib_handle = cf_OpenLibrary("TestDir/test.hog"); CFILE *file_handle = cfopen("lowercase.txt", "rb"); char buf[5]; @@ -43,6 +48,7 @@ TEST(D3, CFileIO) { } TEST(D3, CFileLibrary) { + add_cwd_to_base_directories(); // First pass - without search path in "TestDir" (i.e. not search actual files in directory) // Second pass - with search path (files in directory goes first) for (int i = 0; i < 2; i++) { @@ -70,55 +76,42 @@ TEST(D3, CFileLibrary) { EXPECT_EQ(file_handle, nullptr); } -TEST(D3, CFileCaseSensitiveSearchNew) { +TEST(D3, CFileLocatePath) { const std::vector test_paths = { std::filesystem::path("TestDir") / "CamelCase.txt", std::filesystem::path("TestDir") / "lowercase.txt", std::filesystem::path("TestDir") / "UPPERCASE.TXT", }; - std::filesystem::path filename_new = cf_FindRealFileNameCaseInsensitive("no-exist-file.txt", "no-exist-dir"); + std::filesystem::path filename_new = cf_LocatePath(std::filesystem::path("no-exist-dir") / "no-exist-file.txt"); EXPECT_TRUE(filename_new.empty()); - filename_new = cf_FindRealFileNameCaseInsensitive("no-exist-file.txt"); + filename_new = cf_LocatePath("no-exist-file.txt"); EXPECT_TRUE(filename_new.empty()); auto cwd = std::filesystem::current_path(); for (auto const &item : test_paths) { auto directory = cwd / item.parent_path(); + cf_ClearBaseDirectories(); + cf_AddBaseDirectory(directory); std::filesystem::path file = item.filename(); std::string file_lc = item.filename().u8string(); std::transform(file_lc.begin(), file_lc.end(), file_lc.begin(), ::tolower); std::string file_uc = item.filename().u8string(); std::transform(file_uc.begin(), file_uc.end(), file_uc.begin(), ::toupper); - EXPECT_FALSE(cf_FindRealFileNameCaseInsensitive(file_lc, directory).empty()); - EXPECT_FALSE(cf_FindRealFileNameCaseInsensitive(file_uc, directory).empty()); - - EXPECT_FALSE(cf_FindRealFileNameCaseInsensitive(directory / file_lc).empty()); - EXPECT_FALSE(cf_FindRealFileNameCaseInsensitive(directory / file_uc).empty()); + EXPECT_FALSE(cf_LocatePath(file_lc).empty()); + EXPECT_FALSE(cf_LocatePath(file_uc).empty()); - // Now try case-insensitive path with non-existing directory in search. + // Now try case-insensitive path with non-existing path. // Expected not found on case-sensitive fs. file_lc = item.u8string(); std::transform(file_lc.begin(), file_lc.end(), file_lc.begin(), ::tolower); file_uc = item.u8string(); std::transform(file_uc.begin(), file_uc.end(), file_uc.begin(), ::toupper); - if (std::filesystem::is_regular_file(file_lc)) { - EXPECT_FALSE(cf_FindRealFileNameCaseInsensitive(file_lc, cwd).empty()); - EXPECT_FALSE(cf_FindRealFileNameCaseInsensitive(file_lc).empty()); - } else { - EXPECT_TRUE(cf_FindRealFileNameCaseInsensitive(file_lc, cwd).empty()); - EXPECT_TRUE(cf_FindRealFileNameCaseInsensitive(file_lc).empty()); - } - if (std::filesystem::is_regular_file(file_uc)) { - EXPECT_FALSE(cf_FindRealFileNameCaseInsensitive(file_uc, cwd).empty()); - EXPECT_FALSE(cf_FindRealFileNameCaseInsensitive(file_uc).empty()); - } else { - EXPECT_TRUE(cf_FindRealFileNameCaseInsensitive(file_uc, cwd).empty()); - EXPECT_TRUE(cf_FindRealFileNameCaseInsensitive(file_uc).empty()); - } + EXPECT_TRUE(cf_LocatePath(file_lc).empty()); + EXPECT_TRUE(cf_LocatePath(file_uc).empty()); } } diff --git a/ddio/sdlcontroller.cpp b/ddio/sdlcontroller.cpp index f8101d120..50b70b182 100644 --- a/ddio/sdlcontroller.cpp +++ b/ddio/sdlcontroller.cpp @@ -1397,93 +1397,97 @@ int CTLLex(const char *command) { return INFFILE_ERROR; } -// okay, now search for a '****.ctl' file in the current directory. +// okay, now search for a '****.ctl' file in the Base_directories void sdlgameController::parse_ctl_file(int devnum, const char *ctlname) { - // parse each file until we find a name match, no name match, just return - ddio_DoForeachFile( - Base_directory, std::regex(".*\\.ctl"), [this, &devnum, &ctlname](const std::filesystem::path &path) { - InfFile file; - bool found_name = false; - - if (file.Open(path.filename(), "[controller settings]", CTLLex)) { - // parse each line, setting the appropriate values, etc. - while (file.ReadLine()) { - int cmd; - char operand[128]; - - while ((cmd = file.ParseLine(operand, INFFILE_LINELEN)) > INFFILE_ERROR) { - // we want to assert that the name command comes before any other to verify - // this is the file we really want to change. - switch (cmd) { - case CTLCMD_NAME: - if (strcmp(ctlname, operand) != 0) - goto cancel_file_parse; - found_name = true; - break; - - case CTLCMD_DEAD: // deadzone - if (!found_name) - goto cancel_file_parse; - else { - m_ControlList[devnum].deadzone = atof(operand); - } - break; - - case CTLCMD_AXIS: // allowable axis. - // format of command is "+Z-R" - // this would add a Z axis to the controller. -R would remove the Rudder. - // you can do this for X,Y,Z,R,U,V. - if (!found_name) - goto cancel_file_parse; - else { - int slen = strlen(operand); - for (int i = 0; i <= slen; i += 2) { - int axis_flag; - if ((i + 1) <= slen) { - char axis_cmd = tolower(operand[i + 1]); - if (axis_cmd == 'x') - axis_flag = CTF_X_AXIS; - else if (axis_cmd == 'y') - axis_flag = CTF_Y_AXIS; - else if (axis_cmd == 'z') - axis_flag = CTF_Z_AXIS; - else if (axis_cmd == 'r') - axis_flag = CTF_R_AXIS; - else if (axis_cmd == 'u') - axis_flag = CTF_U_AXIS; - else if (axis_cmd == 'v') - axis_flag = CTF_V_AXIS; - else - axis_flag = 0; - if (operand[i] == '+') { - m_ControlList[devnum].flags |= axis_flag; - } else if (operand[i] == '-') { - m_ControlList[devnum].flags &= (~axis_flag); + for (auto base_directories_iterator = Base_directories.rbegin(); + base_directories_iterator != Base_directories.rend(); + ++base_directories_iterator) { + // parse each file until we find a name match, no name match, just return + ddio_DoForeachFile( + *base_directories_iterator, std::regex(".*\\.ctl"), [this, &devnum, &ctlname](const std::filesystem::path &path) { + InfFile file; + bool found_name = false; + + if (file.Open(path.filename(), "[controller settings]", CTLLex)) { + // parse each line, setting the appropriate values, etc. + while (file.ReadLine()) { + int cmd; + char operand[128]; + + while ((cmd = file.ParseLine(operand, INFFILE_LINELEN)) > INFFILE_ERROR) { + // we want to assert that the name command comes before any other to verify + // this is the file we really want to change. + switch (cmd) { + case CTLCMD_NAME: + if (strcmp(ctlname, operand) != 0) + goto cancel_file_parse; + found_name = true; + break; + + case CTLCMD_DEAD: // deadzone + if (!found_name) + goto cancel_file_parse; + else { + m_ControlList[devnum].deadzone = atof(operand); + } + break; + + case CTLCMD_AXIS: // allowable axis. + // format of command is "+Z-R" + // this would add a Z axis to the controller. -R would remove the Rudder. + // you can do this for X,Y,Z,R,U,V. + if (!found_name) + goto cancel_file_parse; + else { + int slen = strlen(operand); + for (int i = 0; i <= slen; i += 2) { + int axis_flag; + if ((i + 1) <= slen) { + char axis_cmd = tolower(operand[i + 1]); + if (axis_cmd == 'x') + axis_flag = CTF_X_AXIS; + else if (axis_cmd == 'y') + axis_flag = CTF_Y_AXIS; + else if (axis_cmd == 'z') + axis_flag = CTF_Z_AXIS; + else if (axis_cmd == 'r') + axis_flag = CTF_R_AXIS; + else if (axis_cmd == 'u') + axis_flag = CTF_U_AXIS; + else if (axis_cmd == 'v') + axis_flag = CTF_V_AXIS; + else + axis_flag = 0; + if (operand[i] == '+') { + m_ControlList[devnum].flags |= axis_flag; + } else if (operand[i] == '-') { + m_ControlList[devnum].flags &= (~axis_flag); + } else { + goto cancel_file_parse; + } } else { - goto cancel_file_parse; + break; // this should break out of the axis search but continue with the file } - } else { - break; // this should break out of the axis search but continue with the file } } + break; + + case CTLCMD_SX: // allow modification of global sensitivity modifiers + case CTLCMD_SY: + case CTLCMD_SZ: + case CTLCMD_SR: + case CTLCMD_SU: + case CTLCMD_SV: { + int idx = (cmd - CTLCMD_SX); + m_ControlList[devnum].sensmod[idx] = atof(operand); + break; + } } - break; - - case CTLCMD_SX: // allow modification of global sensitivity modifiers - case CTLCMD_SY: - case CTLCMD_SZ: - case CTLCMD_SR: - case CTLCMD_SU: - case CTLCMD_SV: { - int idx = (cmd - CTLCMD_SX); - m_ControlList[devnum].sensmod[idx] = atof(operand); - break; - } } } + cancel_file_parse: + file.Close(); } - cancel_file_parse: - file.Close(); - } - }); + }); + } } diff --git a/editor/MainFrm.cpp b/editor/MainFrm.cpp index c12cdf38f..009fce25a 100644 --- a/editor/MainFrm.cpp +++ b/editor/MainFrm.cpp @@ -1460,7 +1460,7 @@ void InitCScripts() { CreateNewMine(); // Setup include directories for OSIRIS - ddio_MakePath(path, Base_directory, "data", "levels", NULL); + ddio_MakePath(path, cf_GetWritableBaseDirectory().u8string().c_str(), "data", "levels", NULL); } // Copied from winmain.cpp diff --git a/editor/editor_lighting.cpp b/editor/editor_lighting.cpp index 205cab82f..5af799ec2 100644 --- a/editor/editor_lighting.cpp +++ b/editor/editor_lighting.cpp @@ -870,7 +870,7 @@ void DoRadiosityForRooms() { if (save_after_bsp) { char filename[_MAX_PATH]; - ddio_MakePath(filename, Base_directory, "BSPSave.D3L", NULL); + ddio_MakePath(filename, cf_GetWritableBaseDirectory().u8string().c_str(), "BSPSave.D3L", NULL); // Save the level to SaveLevel(filename); @@ -1140,7 +1140,7 @@ void DoRadiosityForRooms() { SqueezeLightmaps(0, -1); char filename[_MAX_PATH + 1]; - ddio_MakePath(filename, Base_directory, "LightSave.D3L", NULL); + ddio_MakePath(filename, cf_GetWritableBaseDirectory().u8string().c_str(), "LightSave.D3L", NULL); // Save the level to disk SaveLevel(filename); diff --git a/editor/gameeditor.cpp b/editor/gameeditor.cpp index 20cc0334a..efbb05cad 100644 --- a/editor/gameeditor.cpp +++ b/editor/gameeditor.cpp @@ -611,7 +611,7 @@ void GameToEditor(bool set_viewer_from_player) { if (Temp_level_saved) { char filename[_MAX_PATH]; - ddio_MakePath(filename, Base_directory, "GameSave.D3L", NULL); // make explicit path + ddio_MakePath(filename, cf_GetWritableBaseDirectory().u8string().c_str(), "GameSave.D3L", NULL); // make explicit path LoadLevel(filename); Temp_level_saved = 0; } @@ -744,7 +744,7 @@ void EditorToGame() { // set game working directory bool set_size = false; ddio_GetWorkingDir(Editor_dir, sizeof(Editor_dir)); - ddio_SetWorkingDir(Base_directory); + ddio_SetWorkingDir(cf_GetWritableBaseDirectory().u8string().c_str()); Osiris_ResetAllTimers(); diff --git a/manage/manage.cpp b/manage/manage.cpp index ff2f60501..b5429d334 100644 --- a/manage/manage.cpp +++ b/manage/manage.cpp @@ -672,8 +672,13 @@ int mng_LoadTableFiles(int show_progress) { // This is for initting tables on STAND_ALONE, if the network is down, or if // the user doesn't want network support int mng_InitLocalTables() { - // Set the local table directory from the base directory - strcpy(LocalD3Dir, Base_directory); + // Set the local table directory from the base directory. + auto writable_base_directory_string = cf_GetWritableBaseDirectory().u8string(); + strncpy(LocalD3Dir, writable_base_directory_string.c_str(), sizeof LocalD3Dir); + LocalD3Dir[sizeof LocalD3Dir - 1] = '\0'; + if (strlen(LocalD3Dir) != strlen(writable_base_directory_string.c_str())) { + LOG_WARNING << "cf_GetWritableBaseDirectory() is too long to fit in LocalD3Dir, so LocalD3Dir was truncated."; + } LOG_INFO << "Local dir: " << LocalD3Dir; // Make the CFILE system first look at our local directories. If the goods aren't diff --git a/module/module.cpp b/module/module.cpp index 6442b854f..121b69077 100644 --- a/module/module.cpp +++ b/module/module.cpp @@ -183,8 +183,8 @@ bool mod_LoadModule(module *handle, const std::filesystem::path &imodfilename, i handle->handle = dlopen(modfilename.u8string().c_str(), f); if (!handle->handle) { // ok we couldn't find the given name...try other ways - std::filesystem::path parent_path = modfilename.parent_path(); - std::filesystem::path new_filename = cf_FindRealFileNameCaseInsensitive(modfilename.filename(), parent_path); + std::filesystem::path parent_path = modfilename.parent_path().filename(); + std::filesystem::path new_filename = cf_LocatePath(parent_path / modfilename.filename()); if (new_filename.empty()) { LOG_ERROR.printf("Module Load Err: %s", dlerror()); diff --git a/netcon/includes/con_dll.h b/netcon/includes/con_dll.h index 94c81df85..9cc7cf963 100644 --- a/netcon/includes/con_dll.h +++ b/netcon/includes/con_dll.h @@ -701,6 +701,15 @@ typedef void (*ddio_DoForeachFile_fp)(const std::filesystem::path &search_path, const std::function &func); ddio_DoForeachFile_fp DLLddio_DoForeachFile; +typedef decltype(&cf_LocatePath) cf_LocatePath_fp; +cf_LocatePath_fp DLLcf_LocatePath; + +typedef decltype(&cf_LocateMultiplePaths) cf_LocateMultiplePaths_fp; +cf_LocateMultiplePaths_fp DLLcf_LocateMultiplePaths; + +typedef decltype(&cf_GetWritableBaseDirectory) cf_GetWritableBaseDirectory_fp; +cf_GetWritableBaseDirectory_fp DLLcf_GetWritableBaseDirectory; + int DLLUIClass_CurrID = 0xD0; #define MAX_NET_GAMES 100 @@ -768,7 +777,6 @@ int DLLGame_mode; char *DLLTracker_id; int *DLLNum_directplay_games; netgame_info *DLLNetgame; -char *DLLLocalD3Dir; int *DLLMultiGameStarting; netplayer *DLLMNetPlayers; int MTWritingPilot, MTReadingPilot; @@ -998,13 +1006,15 @@ int StartMultiplayerGameMenu() { DLLListRemoveAll(script_list); #if (!(defined(OEM) || defined(DEMO))) - DLLddio_DoForeachFile(std::filesystem::path(DLLLocalD3Dir) / "netgames", std::regex(".+\\.d3m"), - [&dll_ui_items](const std::filesystem::path& path){ - dll_ui_items.insert_or_assign( - path.stem().u8string(), - DLLCreateNewUITextItem(path.stem().u8string().c_str(), UICOL_LISTBOX_LO, -1) - ); - } ); + for (const auto &netgames_directory : DLLcf_LocateMultiplePaths("netgames")) { + DLLddio_DoForeachFile(netgames_directory, std::regex(".+\\.d3m"), + [&dll_ui_items](const std::filesystem::path& path){ + dll_ui_items.insert_or_assign( + path.stem().u8string(), + DLLCreateNewUITextItem(path.stem().u8string().c_str(), UICOL_LISTBOX_LO, -1) + ); + } ); + } #else dll_ui_items.insert_or_assign("Anarchy", DLLCreateNewUITextItem("Anarchy", UICOL_LISTBOX_LO, -1)); dll_ui_items.insert_or_assign("Capture The Flag", DLLCreateNewUITextItem("Capture The Flag", UICOL_LISTBOX_LO, -1)); @@ -1017,10 +1027,13 @@ int StartMultiplayerGameMenu() { #if (!(defined(OEM) || defined(DEMO))) msn_list *mi; - const std::vector> search_paths = { - {std::filesystem::path(DLLLocalD3Dir) / "data" / "levels", std::regex(".+\\.msn")}, - {std::filesystem::path(DLLLocalD3Dir) / "missions", std::regex(".+\\.mn3")} - }; + std::vector> search_paths = { }; + for (const auto &levels_directory : DLLcf_LocateMultiplePaths(std::filesystem::path("data") / "levels")) { + search_paths.push_back({levels_directory, std::regex(".+\\.msn")}); + } + for (const auto &missions_directory : DLLcf_LocateMultiplePaths("missions")) { + search_paths.push_back({missions_directory, std::regex(".+\\.mn3")}); + } for (auto const &i : search_paths) { DLLddio_DoForeachFile(i.first, i.second, [&mi, &list_1](const std::filesystem::path &path) { @@ -1074,7 +1087,7 @@ int StartMultiplayerGameMenu() { DLLNetgame->flags = NF_RANDOMIZE_RESPAWN; DLLNewUIWindowLoadBackgroundImage(main_wnd, "multimain.ogf"); DLLNewUIWindowOpen(main_wnd); - if (DLLMultiLoadSettings(std::filesystem::path(DLLLocalD3Dir) / "custom" / "settings" / "default.mps")) { + if (DLLMultiLoadSettings(DLLcf_GetWritableBaseDirectory() / "custom" / "settings" / "default.mps")) { DLLEditSetText(mission_name_edit, DLLNetgame->name); #if (!(defined(OEM) || defined(DEMO))) p = DLLGetMissionName(DLLNetgame->mission); diff --git a/netcon/includes/mdllinit.h b/netcon/includes/mdllinit.h index 2c3895a52..676b250be 100644 --- a/netcon/includes/mdllinit.h +++ b/netcon/includes/mdllinit.h @@ -275,6 +275,9 @@ DLLShowNetgameInfo = (ShowNetgameInfo_fp)API.fp[108]; // API.fp[109]; // Not used DLLCheckGetD3M = (CheckGetD3M_fp)API.fp[110]; DLLddio_DoForeachFile = (ddio_DoForeachFile_fp)API.fp[111]; +DLLcf_LocatePath = (cf_LocatePath_fp)API.fp[112]; +DLLcf_LocateMultiplePaths = (cf_LocateMultiplePaths_fp)API.fp[113]; +DLLcf_GetWritableBaseDirectory = (cf_GetWritableBaseDirectory_fp)API.fp[114]; DLLMPlayers = (player *)API.players; DLLNetgame = (netgame_info *)API.netgame; @@ -286,7 +289,7 @@ DLLTracker_id = (char *)API.vp[1]; DLLGame_is_master_tracker_game = API.vp[2]; DLLGame_mode = *API.vp[3]; // DLLCurrent_pilot = (pilot *)API.vp[4]; -DLLLocalD3Dir = (char *)(pilot *)API.vp[5]; +// DLLLocalD3Dir = (std::filesystem::path *)API.vp[5]; DLLMultiGameStarting = (int *)API.vp[6]; DLLMTPilotinfo = (vmt_descent3_struct *)API.vp[7]; DLLNum_network_games_known = API.vp[8]; diff --git a/netcon/mtclient/mtclient.cpp b/netcon/mtclient/mtclient.cpp index 39212f85a..bb30d5d7a 100644 --- a/netcon/mtclient/mtclient.cpp +++ b/netcon/mtclient/mtclient.cpp @@ -2132,7 +2132,7 @@ void CheckPXOForAnomalies() { if (stricmp(DLLMPlayers[i].tracker_id, DLLMPlayers[j].tracker_id) == 0) { // Ok, what we have here is multiple users with the same tracker ID. // This is bad. It could be user error, but it could be something worse. - std::filesystem::path errfilepath = std::filesystem::path(DLLLocalD3Dir) / "pxo.err"; + std::filesystem::path errfilepath = DLLcf_GetWritableBaseDirectory() / "pxo.err"; FILE *errfile = fopen(errfilepath.u8string().c_str(), "at"); if (errfile) { fprintf(errfile, "Dup TID: %s & %s / %s\n", DLLMPlayers[j].callsign, DLLMPlayers[i].callsign, diff --git a/netgames/dmfc/dmfcbase.cpp b/netgames/dmfc/dmfcbase.cpp index 4e88d8572..dbd9773e0 100644 --- a/netgames/dmfc/dmfcbase.cpp +++ b/netgames/dmfc/dmfcbase.cpp @@ -1207,7 +1207,7 @@ void DMFCBase::GameInit(int teams) { Remote_Initialize(); // see if we should display Outrage logo at all - if (DLLFindArg("-nooutragelogo")) + if (DLLFindArg("-nooutragelogo", 1)) m_bDisplayOutrageLogo = false; else m_bDisplayOutrageLogo = true; @@ -4916,7 +4916,7 @@ void DMFCBase::ParseStartupScript(void) { int autoexec_arg = -1; - if ((autoexec_arg = DLLFindArg("-autoexec")) != 0) { + if ((autoexec_arg = DLLFindArg("-autoexec", 1)) != 0) { // a specific autoexec.dmfc file was specified, use that strcpy(path, GetGameArg(autoexec_arg + 1)); mprintf(0, "Override AUTOEXEC.DMFC to %s\n", path); diff --git a/netgames/dmfc/dmfcfunctions.cpp b/netgames/dmfc/dmfcfunctions.cpp index e9698ca7c..b9abbb0f1 100644 --- a/netgames/dmfc/dmfcfunctions.cpp +++ b/netgames/dmfc/dmfcfunctions.cpp @@ -483,7 +483,7 @@ void (*DLLg3_DrawSpecialLine)(g3Point *p0, g3Point *p1); void (*DLLg3_DrawPlanarRotatedBitmap)(vector *pos, vector *norm, angle rot_angle, float width, float height, int bm); void (*DLLPlayerStopSounds)(int slot); -int (*DLLFindArg)(const char *which); +int (*DLLFindArg)(const char *which, int start); int (*DLLFireWeaponFromObject)(object *obj, int weapon_num, int gun_num, bool f_force_forward, bool f_force_target); int (*DLLCreateAndFireWeapon)(vector *pos, vector *dir, object *parent, int weapon_num); diff --git a/netgames/includes/gamedll_header.h b/netgames/includes/gamedll_header.h index b2758bab1..29c032f2c 100644 --- a/netgames/includes/gamedll_header.h +++ b/netgames/includes/gamedll_header.h @@ -1591,7 +1591,7 @@ typedef void (*PlayerStopSounds_fp)(int slot); DMFCDLLOUT(PlayerStopSounds_fp DLLPlayerStopSounds;) // Returns index of argument sought, or 0 if not found -typedef int (*FindArg_fp)(const char *which); +typedef decltype(&FindArg) FindArg_fp; DMFCDLLOUT(FindArg_fp DLLFindArg;) // Given an object and a weapon, fires a shot from that object