From a9c4ef118043276343bfe7c91175b179a6986082 Mon Sep 17 00:00:00 2001 From: "Azamat H. Hackimov" Date: Sun, 6 Oct 2024 17:39:40 +0300 Subject: [PATCH 1/9] Update cf_LocatePathCaseInsensitiveHelper() Make case-insensitive path work on all path components instead of only filename. --- cfile/cfile.cpp | 61 +++++++++++++++---------------------- cfile/tests/cfile_tests.cpp | 41 ++++++++----------------- 2 files changed, 37 insertions(+), 65 deletions(-) diff --git a/cfile/cfile.cpp b/cfile/cfile.cpp index 27475f2fd..550849529 100644 --- a/cfile/cfile.cpp +++ b/cfile/cfile.cpp @@ -99,44 +99,35 @@ void cf_ClearBaseDirectories() { 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; + // Dumb check, maybe there already all ok? 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()) { +#ifdef WIN32 + else { + // On Windows there no need to case insensitive search, failing is failing return {}; } +#else + result = starting_dir; + // Iterate path components + for (auto path_it = relative_path.begin(); path_it != relative_path.end(); ++path_it) { + // Search component in search_path + auto const &it = std::filesystem::directory_iterator(result); - // 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; - }); + auto found = std::find_if(it, end(it), [&path_it](const auto& dir_entry) { + return stricmp(dir_entry.path().filename().u8string().c_str(), path_it->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 {}; + if (found != end(it)) { + // Match, append to result + result /= found->path(); + } else { + // Component not found, mission failed + return {}; + } } return result; @@ -145,13 +136,11 @@ std::filesystem::path cf_LocatePathCaseInsensitiveHelper(const std::filesystem:: 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(("relative_path should be a relative path.", relative_path.is_relative())); + std::vector return_value; + for (auto const &directory : Base_directories) { + ASSERT(("directory should be an absolute path.", directory.is_absolute())); + auto to_append = cf_LocatePathCaseInsensitiveHelper(relative_path, directory); 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); diff --git a/cfile/tests/cfile_tests.cpp b/cfile/tests/cfile_tests.cpp index c2b046878..3569dffd5 100644 --- a/cfile/tests/cfile_tests.cpp +++ b/cfile/tests/cfile_tests.cpp @@ -22,12 +22,8 @@ #include #include "cfile.h" -void add_cwd_to_base_directories() { - cf_AddBaseDirectory(std::filesystem::current_path()); -} - TEST(D3, CFileIO) { - add_cwd_to_base_directories(); + cf_AddBaseDirectory(std::filesystem::current_path()); int lib_handle = cf_OpenLibrary("TestDir/test.hog"); CFILE *file_handle = cfopen("lowercase.txt", "rb"); char buf[5]; @@ -48,12 +44,12 @@ TEST(D3, CFileIO) { } TEST(D3, CFileLibrary) { - add_cwd_to_base_directories(); + cf_AddBaseDirectory(std::filesystem::current_path()); // 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++) { if (i != 0) { - EXPECT_EQ(cf_SetSearchPath("TestDir"), true); + EXPECT_TRUE(cf_SetSearchPath("TestDir")); } int lib_handle = cf_OpenLibrary("TestDir/test.hog"); @@ -78,40 +74,27 @@ TEST(D3, CFileLibrary) { 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", + "TestDir/CamelCase.txt", + "TestDir/lowercase.txt", + "TestDir/UPPERCASE.TXT", }; + cf_ClearBaseDirectories(); + auto cwd = std::filesystem::current_path(); + cf_AddBaseDirectory(cwd); - std::filesystem::path filename_new = cf_LocatePath(std::filesystem::path("no-exist-dir") / "no-exist-file.txt"); + std::filesystem::path filename_new = cf_LocatePath("no-exist-dir/no-exist-file.txt"); EXPECT_TRUE(filename_new.empty()); 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::string file_lc = item.u8string(); std::transform(file_lc.begin(), file_lc.end(), file_lc.begin(), ::tolower); - std::string file_uc = item.filename().u8string(); + std::string file_uc = item.u8string(); std::transform(file_uc.begin(), file_uc.end(), file_uc.begin(), ::toupper); EXPECT_FALSE(cf_LocatePath(file_lc).empty()); EXPECT_FALSE(cf_LocatePath(file_uc).empty()); - - // 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); - - EXPECT_TRUE(cf_LocatePath(file_lc).empty()); - EXPECT_TRUE(cf_LocatePath(file_uc).empty()); } } From a237a531342b21e8e097d78980e5ddc9803320b1 Mon Sep 17 00:00:00 2001 From: "Azamat H. Hackimov" Date: Tue, 8 Oct 2024 11:29:37 +0300 Subject: [PATCH 2/9] Fix error message in mve_PlayMovie() Fix error message when real_name is empty. --- Descent3/d3movie.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Descent3/d3movie.cpp b/Descent3/d3movie.cpp index 3a123c2ad..bd64935c6 100644 --- a/Descent3/d3movie.cpp +++ b/Descent3/d3movie.cpp @@ -74,12 +74,16 @@ void mve_SetRenderProperties(int16_t x, int16_t y, int16_t w, int16_t h, rendere // plays a movie using the current screen. int mve_PlayMovie(const std::filesystem::path &pMovieName, oeApplication *pApp) { #ifndef NO_MOVIES - // first, find that movie.. + // first, find that movie. std::filesystem::path real_name = cf_LocatePath("movies" / pMovieName); + if (real_name.empty()) { + LOG_ERROR << "MOVIE: File " << pMovieName << " not found, skipping playback"; + return MVELIB_FILE_ERROR; + } // open movie file. FILE *hFile = fopen(real_name.u8string().c_str(), "rb"); if (hFile == nullptr) { - LOG_ERROR.printf("MOVIE: Unable to open %s", real_name.u8string().c_str()); + LOG_ERROR << "MOVIE: Unable to open " << pMovieName; return MVELIB_FILE_ERROR; } From 8e4dede147870f466b9d59f8ee0b7233be084913 Mon Sep 17 00:00:00 2001 From: "Azamat H. Hackimov" Date: Tue, 8 Oct 2024 11:31:43 +0300 Subject: [PATCH 3/9] Fix ordering of base directories First added entries to base directories has higher priority on search path. --- cfile/cfile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cfile/cfile.cpp b/cfile/cfile.cpp index 550849529..c56e9d468 100644 --- a/cfile/cfile.cpp +++ b/cfile/cfile.cpp @@ -143,7 +143,7 @@ std::vector cf_LocatePathMultiplePathsHelper(const std::f auto to_append = cf_LocatePathCaseInsensitiveHelper(relative_path, directory); 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); + return_value.push_back(to_append); if (stop_after_first_result) { break; } From f2118ae37faeebb2801564c48bd433a83d827179 Mon Sep 17 00:00:00 2001 From: "Azamat H. Hackimov" Date: Tue, 8 Oct 2024 11:34:33 +0300 Subject: [PATCH 4/9] Implementing ddio_GetPrefPath() and ddio_GetBasePath() ddio_GetPrefPath() implements retrieving writable location path. ddio_GetBasePath() implements retrieving parent path of executable. --- ddio/ddio.h | 14 ++++++++++++++ ddio/file.cpp | 23 +++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/ddio/ddio.h b/ddio/ddio.h index d747f81b7..ff2038741 100644 --- a/ddio/ddio.h +++ b/ddio/ddio.h @@ -383,6 +383,20 @@ void ddio_DoForeachFile(const std::filesystem::path &search_path, const std::reg */ std::filesystem::path ddio_GetTmpFileName(const std::filesystem::path &basedir, const char *prefix); +/** + * Gets path where files can be written. + * @param org name of organization + * @param app name of application + * @return path that contains org and app components where user can write files, empty path if there some errors + */ +std::filesystem::path ddio_GetPrefPath(const char *org, const char *app); + +/** + * Gets path directory of executable + * @return parent path of executable or empty path on error + */ +std::filesystem::path ddio_GetBasePath(); + /** * Check process existence by PID * @param pid PID of requested process diff --git a/ddio/file.cpp b/ddio/file.cpp index 367736883..f64d3327b 100644 --- a/ddio/file.cpp +++ b/ddio/file.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include "IOOps.h" #include "chrono_timer.h" @@ -192,3 +193,25 @@ std::filesystem::path ddio_GetTmpFileName(const std::filesystem::path &basedir, mem_free(random_name); return result; } + +std::filesystem::path ddio_GetPrefPath(const char *org, const char *app) { + char *pref_path = SDL_GetPrefPath(org, app); + if (!pref_path) { + LOG_ERROR << "Failed to get writable preference path!"; + return {}; + } + std::filesystem::path result = std::filesystem::canonical(pref_path); + SDL_free(pref_path); + return result; +} + +std::filesystem::path ddio_GetBasePath() { + char *exe_path = SDL_GetBasePath(); + if (!exe_path) { + LOG_ERROR << "Failed to get parent path of executable!"; + return {}; + } + std::filesystem::path result = std::filesystem::canonical(exe_path); + SDL_free(exe_path); + return result; +} From b1b174d874cabd29cf3ba8778b464982dc08b151 Mon Sep 17 00:00:00 2001 From: "Azamat H. Hackimov" Date: Tue, 8 Oct 2024 11:54:14 +0300 Subject: [PATCH 5/9] Implementing base directory populating on init After initialization, we get following base directories list (in priority order): * Writable preference path * User defined paths (cmd-line and configuration) * Platform defined paths (such as /usr/share/Descent3 on Linux or Steam installation paths on all platforms (TBD)) * Directory of executable Removing `-setdir` and `-useexedir` as redundant (both can be replaced with `-additionaldir` option). --- CMakeLists.txt | 6 +++ Descent3/CMakeLists.txt | 6 +++ Descent3/init.cpp | 87 +++++++++++++++++++++++++-------------- cfile/cfile.cpp | 13 +++--- lib/d3_platform_path.h.in | 28 +++++++++++++ linux/CMakeLists.txt | 1 + linux/lnxdata.cpp | 11 +++-- 7 files changed, 106 insertions(+), 46 deletions(-) create mode 100644 lib/d3_platform_path.h.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c4eb7316..4eea9adc1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,6 +83,12 @@ if(FORCE_PORTABLE_INSTALL) set(CMAKE_INSTALL_LIBDIR ".") set(CMAKE_INSTALL_DATADIR ".") set(CMAKE_INSTALL_DOCDIR ".") + + set(D3_DATADIR "${CMAKE_INSTALL_DATADIR}") +else() + set(CMAKE_INSTALL_DATADIR "${CMAKE_INSTALL_DATADIR}/Descent3") + # On system install files will go into /usr/share/Descent3 + set(D3_DATADIR "${CMAKE_INSTALL_FULL_DATADIR}/Descent3") endif() if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) diff --git a/Descent3/CMakeLists.txt b/Descent3/CMakeLists.txt index 8cdd27559..d585f8f59 100644 --- a/Descent3/CMakeLists.txt +++ b/Descent3/CMakeLists.txt @@ -294,6 +294,12 @@ if(WIN32) ) endif() +configure_file( + ${CMAKE_SOURCE_DIR}/lib/d3_platform_path.h.in + ${CMAKE_BINARY_DIR}/lib/d3_platform_path.h + @ONLY +) + if(UNIX AND NOT APPLE) set(PLATFORM_LIBS m ${CMAKE_DL_LIBS}) endif() diff --git a/Descent3/init.cpp b/Descent3/init.cpp index fcec5fce7..faa62da1d 100644 --- a/Descent3/init.cpp +++ b/Descent3/init.cpp @@ -921,6 +921,7 @@ #include "init.h" #include "config.h" #include "3d.h" +#include "d3_platform_path.h" #include "hlsoundlib.h" #include "manage.h" #include "bitmap.h" @@ -1002,6 +1003,9 @@ bool Running_editor = false; // didn't we have a variable like this somewhere static bool Init_in_editor = false; +// Base directory from configuration path +std::filesystem::path config_base_directory; + // used to update load bar. static void SetInitMessageLength(const char *c, float amount); // portion of total bar to fill (0 to 1) extern void UpdateInitMessage(float amount); // amount is 0 to 1 @@ -1192,6 +1196,9 @@ void SaveGameSettings() { Database->write("Default_pilot", Default_pilot.c_str(), strlen(Default_pilot.c_str()) + 1); else Database->write("Default_pilot", " ", 2); + + Database->write("GAME_base_directory", config_base_directory.u8string().c_str(), + strlen(config_base_directory.u8string().c_str()) + 1); } /* @@ -1254,6 +1261,11 @@ void LoadGameSettings() { } } + templen = TEMPBUFFERSIZE; + if (Database->read("GAME_base_directory", tempbuffer, &templen)) { + config_base_directory = tempbuffer; + } + templen = TEMPBUFFERSIZE; if (Database->read("RS_gamma", tempbuffer, &templen)) { Render_preferred_state.gamma = strtod(tempbuffer, &stoptemp); @@ -1388,44 +1400,58 @@ void LoadGameSettings() { I/O systems initialization */ void InitIOSystems(bool editor) { - ddio_init_info io_info; + // Read in stuff from the registry + INIT_MESSAGE(("Reading settings.")); + LoadGameSettings(); - // Set the writable base directory - int dirarg = FindArg("-setdir"); - int exedirarg = FindArg("-useexedir"); - std::filesystem::path writable_base_directory; - if (dirarg) { - writable_base_directory = GameArgs[dirarg + 1]; - } else if (exedirarg) { - char exec_path[_MAX_PATH]; - memset(exec_path, 0, sizeof(exec_path)); - // Populate exec_path with the executable path - if (!ddio_GetBinaryPath(exec_path, sizeof(exec_path))) { - Error("Failed to get executable path\n"); - } else { - std::filesystem::path executablePath(exec_path); - writable_base_directory = executablePath.parent_path(); - LOG_INFO << "Using working directory of " << writable_base_directory; - } - } else { - writable_base_directory = std::filesystem::current_path(); + /* + * Populate base directories. In result, we have the following list of directories (in priority order): + * - Writable preference path + * - User defined paths (cmd-line and configuration) + * - Platform defined paths (such as /usr/share/Descent3 on Linux or Steam installation paths) + * - Directory of executable + */ + + // Writable preference path + std::filesystem::path pref_path = ddio_GetPrefPath(D3_PREF_ORG, D3_PREF_APP); + if (pref_path.empty()) { + Error("Failed to get preference path!"); } + LOG_INFO << "Setting writable preference path " << pref_path; + cf_AddBaseDirectory(pref_path); - ddio_SetWorkingDir(writable_base_directory.u8string().c_str()); - cf_AddBaseDirectory(writable_base_directory); - - // Set any additional base directories - auto additionaldirarg = 0; + // Additional paths + int 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."; + if (dir_to_add == nullptr) { + LOG_WARNING << "-additionaldir requires directory path as value."; break; } else { - cf_AddBaseDirectory(std::filesystem::path(dir_to_add)); + cf_AddBaseDirectory(dir_to_add); additionaldirarg += 2; } } + // Path from configuration + if (!config_base_directory.empty()) { + cf_AddBaseDirectory(config_base_directory); + } + + // Platform dependent paths + std::filesystem::path platform_dir = std::filesystem::canonical(D3_DATADIR); + cf_AddBaseDirectory(platform_dir); + // TODO: add Steam/registry locations + + // Add path of executable + std::filesystem::path exec_path = ddio_GetBasePath(); + // Populate exec_path with the executable path + if (exec_path.empty() || exec_path == platform_dir) { + LOG_DEBUG << "Skipping adding executable path (empty or redundant path)."; + } else { + cf_AddBaseDirectory(exec_path); + } + + LOG_INFO << "Base directories: " << cf_LocateMultiplePaths(""); Descent->set_defer_handler(D3DeferHandler); @@ -1442,6 +1468,7 @@ void InitIOSystems(bool editor) { #endif // do io init stuff + ddio_init_info io_info{}; io_info.obj = Descent; INIT_MESSAGE(("Initializing DDIO systems.")); @@ -1456,10 +1483,6 @@ void InitIOSystems(bool editor) { RTI_MATCENFRAMETIME); RTP_ENABLEFLAGS(RTI_OBJFRAMETIME | RTI_AIFRAMETIME | RTI_PROCESSKEYTIME); - // Read in stuff from the registry - INIT_MESSAGE(("Reading settings.")); - LoadGameSettings(); - // Setup temp directory INIT_MESSAGE(("Setting up temp directory.")); SetupTempDirectory(); diff --git a/cfile/cfile.cpp b/cfile/cfile.cpp index c56e9d468..38cc4cbcc 100644 --- a/cfile/cfile.cpp +++ b/cfile/cfile.cpp @@ -54,13 +54,10 @@ 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. +/* + * List of base directories of the D3 file tree. + * Directories at the top of the list have higher priority. + * First entry should be a writable directory. */ std::vector Base_directories = {}; @@ -82,7 +79,7 @@ const char *eof_error = "Unexpected end of file"; * from this module. */ void cf_AddBaseDirectory(const std::filesystem::path &base_directory) { - if (std::filesystem::exists(base_directory)) { + if (std::filesystem::exists(base_directory) && std::filesystem::is_directory(base_directory)) { Base_directories.push_back(base_directory); } else { LOG_WARNING << "Ignoring nonexistent base directory: " << base_directory; diff --git a/lib/d3_platform_path.h.in b/lib/d3_platform_path.h.in new file mode 100644 index 000000000..3c360e6ab --- /dev/null +++ b/lib/d3_platform_path.h.in @@ -0,0 +1,28 @@ +/* + * Descent 3 + * Copyright (C) 2024 Descent Developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +// Platform-specific compile-time defined location of data files +#define D3_DATADIR "@D3_DATADIR@" + +// Part of preference path (organization) +#define D3_PREF_ORG "Outrage Entertainment" + +// Part of preference path (application) +#define D3_PREF_APP "Descent 3" diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index f4db42537..7c2bda675 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -19,6 +19,7 @@ target_include_directories(linux PUBLIC $ + ${PROJECT_BINARY_DIR}/lib ) target_link_libraries(linux PRIVATE ddebug diff --git a/linux/lnxdata.cpp b/linux/lnxdata.cpp index ea79dd869..e97310325 100644 --- a/linux/lnxdata.cpp +++ b/linux/lnxdata.cpp @@ -44,8 +44,6 @@ #include #include -#include - #if defined(POSIX) #include #include @@ -55,6 +53,8 @@ #endif #include "appdatabase.h" +#include "d3_platform_path.h" +#include "ddio.h" #include "linux/lnxdatabase.h" #include "log.h" #include "pserror.h" @@ -68,13 +68,12 @@ oeLnxAppDatabase::oeLnxAppDatabase() { // Open up the database file, for reading, read in all data and keep it in memory // then close the database - const char* prefPath = SDL_GetPrefPath("Outrage Entertainment", "Descent 3"); - if (prefPath == nullptr) { + std::filesystem::path prefPath = ddio_GetPrefPath(D3_PREF_ORG, D3_PREF_APP); + if (prefPath.empty()) { LOG_FATAL << "Couldn't find preference directory!"; exit(43); } - std::filesystem::path fileName = std::filesystem::path(prefPath) / REGISTRY_FILENAME; - SDL_free((void *)prefPath); + std::filesystem::path fileName = prefPath / REGISTRY_FILENAME; database = new CRegistry(fileName.u8string().c_str()); database->Import(); From c567a84c9d378da2b8436f0d17a0ca6f7996de42 Mon Sep 17 00:00:00 2001 From: "Azamat H. Hackimov" Date: Tue, 8 Oct 2024 15:45:00 +0300 Subject: [PATCH 6/9] Cleanup loadgame / savegame functions Convert functions to use std::fs::path, minor cleanups. --- Descent3/demofile.cpp | 1 + Descent3/gamesave.cpp | 88 +++++++++++++++++++----------------------- Descent3/gamesave.h | 66 ++++++++++++++++++++++--------- Descent3/loadstate.cpp | 36 +++++++---------- Descent3/menu.cpp | 1 + 5 files changed, 103 insertions(+), 89 deletions(-) diff --git a/Descent3/demofile.cpp b/Descent3/demofile.cpp index 2cef503d5..bae3fa54f 100644 --- a/Descent3/demofile.cpp +++ b/Descent3/demofile.cpp @@ -269,6 +269,7 @@ #include #include "cfile.h" +#include "ddio.h" #include "objinfo.h" #include "ship.h" #include "ui.h" diff --git a/Descent3/gamesave.cpp b/Descent3/gamesave.cpp index 774fda848..4893ef73f 100644 --- a/Descent3/gamesave.cpp +++ b/Descent3/gamesave.cpp @@ -265,6 +265,7 @@ #include #include +#include #include "gamesave.h" #include "descent.h" @@ -317,7 +318,7 @@ void SGSSnapshot(CFILE *fp); #define GAMESAVE_SLOT_X 12 // we need this directory to load the savegame from -static char LGS_Path[_MAX_PATH]; +static std::filesystem::path LGS_Path; extern int Times_game_restored; // available for all. @@ -340,21 +341,15 @@ void QuickSaveGame() { SaveGameDialog(); } else { // verify savegame still exists in the appropriate slot, if not just run dialog, if so then save - char filename[PSFILENAME_LEN + 1]; - char pathname[_MAX_PATH]; - char desc[GAMESAVE_DESCLEN + 1]; - FILE *fp; - int i; - - i = Quicksave_game_slot; + std::stringstream filename; + filename << "saveg" << std::setw(3) << std::setfill('0') << Quicksave_game_slot; + std::filesystem::path pathname = cf_GetWritableBaseDirectory() / "savegame" / filename.str(); - snprintf(filename, sizeof(filename), "saveg00%d", i); - ddio_MakePath(pathname, cf_GetWritableBaseDirectory().u8string().c_str(), "savegame", filename, NULL); - - fp = fopen(pathname, "rb"); + FILE *fp = fopen(pathname.u8string().c_str(), "rb"); if (fp) { // slot valid, save here. fclose(fp); + char desc[GAMESAVE_DESCLEN + 1]; if (GetGameStateInfo(pathname, desc)) { if (SaveGameState(pathname, desc)) { AddHUDMessage(TXT_QUICKSAVE); @@ -373,9 +368,8 @@ void SaveGameDialog() { newuiSheet *sheet; int i, res; - char savegame_dir[_MAX_PATH]; - char pathname[_MAX_PATH]; - char filename[PSFILENAME_LEN + 1]; + std::filesystem::path savegame_dir; + std::filesystem::path pathname; char desc[GAMESAVE_DESCLEN + 1]; bool occupied_slot[N_SAVE_SLOTS]; @@ -392,8 +386,7 @@ void SaveGameDialog() { #endif // setup paths. - ddio_MakePath(savegame_dir, cf_GetWritableBaseDirectory().u8string().c_str(), "savegame", NULL); - // ddio_MakePath(pathname, savegame_dir, "*.sav", NULL); -unused + savegame_dir = cf_GetWritableBaseDirectory() / "savegame"; // create savegame directory if it didn't exist before. std::error_code ec; @@ -409,21 +402,23 @@ void SaveGameDialog() { wnd.Open(); sheet = wnd.GetSheet(); - sheet->NewGroup(NULL, GAMESAVE_HELP_X, GAMESAVE_HELP_Y); + sheet->NewGroup(nullptr, GAMESAVE_HELP_X, GAMESAVE_HELP_Y); sheet->AddText(TXT_SAVEGAMEHELP); - sheet->NewGroup(NULL, GAMESAVE_SLOT_X, GAMESAVE_SLOT_Y2); + sheet->NewGroup(nullptr, GAMESAVE_SLOT_X, GAMESAVE_SLOT_Y2); // generate save slots. for (i = 0; i < N_SAVE_SLOTS; i++) { FILE *fp; bool ingroup = (i == 0 || i == (N_SAVE_SLOTS - 1)) ? true : false; - snprintf(filename, sizeof(filename), "saveg00%d", i); - ddio_MakePath(pathname, savegame_dir, filename, NULL); + std::stringstream filename; + filename << "saveg" << std::setw(3) << std::setfill('0') << i; + + pathname = savegame_dir / filename.str(); occupied_slot[i] = false; - fp = fopen(pathname, "rb"); + fp = fopen(pathname.u8string().c_str(), "rb"); if (fp) { fclose(fp); @@ -438,7 +433,7 @@ void SaveGameDialog() { } } - sheet->NewGroup(NULL, GAMESAVE_WND_W - 148, GAMESAVE_WND_H - 100); + sheet->NewGroup(nullptr, GAMESAVE_WND_W - 148, GAMESAVE_WND_H - 100); sheet->AddButton(TXT_CANCEL, UID_CANCEL); // Mouse clicks from gameplay will be read by the dialog without this flush @@ -488,8 +483,9 @@ void SaveGameDialog() { occupied_slot[slot] ? (DoMessageBox(TXT_WARNING, TXT_OVERWRITESAVE, MSGBOX_YESNO) ? true : false) : true; if (do_save) { - snprintf(filename, sizeof(filename), "saveg00%d", slot); - ddio_MakePath(pathname, savegame_dir, filename, NULL); + std::stringstream filename; + filename << "saveg" << std::setw(3) << std::setfill('0') << slot; + pathname = savegame_dir / filename.str(); if (!SaveGameState(pathname, desc)) { DoMessageBox("", TXT_SAVEGAMEFAILED, MSGBOX_OK); } else { @@ -569,15 +565,13 @@ void __cdecl LoadGameDialogCB(newuiTiledWindow *wnd, void *data) } bool LoadGameDialog() { - tLoadGameDialogData lgd_data; + tLoadGameDialogData lgd_data{}; newuiTiledWindow wnd; newuiSheet *sheet; int i, res; bool retval = true; - char savegame_dir[_MAX_PATH]; - char pathname[_MAX_PATH]; - char filename[PSFILENAME_LEN + 1]; + std::filesystem::path pathname; char desc[GAMESAVE_DESCLEN + 1]; bool occupied_slot[N_SAVE_SLOTS], loadgames_avail = false; @@ -587,8 +581,7 @@ bool LoadGameDialog() { } // setup paths. - ddio_MakePath(savegame_dir, cf_GetWritableBaseDirectory().u8string().c_str(), "savegame", NULL); - ddio_MakePath(pathname, savegame_dir, "*.sav", NULL); + std::filesystem::path savegame_dir = cf_GetWritableBaseDirectory() / "savegame"; // create savegame directory if it didn't exist before. if (!std::filesystem::is_directory(savegame_dir)) { @@ -601,25 +594,26 @@ bool LoadGameDialog() { wnd.Open(); sheet = wnd.GetSheet(); - sheet->NewGroup(NULL, GAMESAVE_HELP_X, GAMESAVE_HELP_Y); + sheet->NewGroup(nullptr, GAMESAVE_HELP_X, GAMESAVE_HELP_Y); sheet->AddText(TXT_LOADGAMEHELP); - sheet->NewGroup(NULL, GAMESAVE_SLOT_X, GAMESAVE_SLOT_Y); + sheet->NewGroup(nullptr, GAMESAVE_SLOT_X, GAMESAVE_SLOT_Y); // generate save slots. lgd_data.cur_slot = SAVE_HOTSPOT_ID; - lgd_data.chunk.bm_array = NULL; + lgd_data.chunk.bm_array = nullptr; for (i = 0; i < N_SAVE_SLOTS; i++) { FILE *fp; bool ingroup = (i == 0 || i == (N_SAVE_SLOTS - 1)) ? true : false; - snprintf(filename, sizeof(filename), "saveg00%d", i); - ddio_MakePath(pathname, savegame_dir, filename, NULL); + std::stringstream filename; + filename << "saveg" << std::setw(3) << std::setfill('0') << i; + pathname = savegame_dir / filename.str(); occupied_slot[i] = false; - fp = fopen(pathname, "rb"); + fp = fopen(pathname.u8string().c_str(), "rb"); if (fp) { int bm_handle = -1; int *pbm_handle; @@ -628,7 +622,7 @@ bool LoadGameDialog() { if (lgd_data.cur_slot == (SAVE_HOTSPOT_ID + i)) { pbm_handle = &bm_handle; } else { - pbm_handle = NULL; + pbm_handle = nullptr; } if (GetGameStateInfo(pathname, desc, pbm_handle)) { @@ -660,7 +654,7 @@ bool LoadGameDialog() { goto loadgame_fail; } - sheet->NewGroup(NULL, GAMESAVE_WND_W - 148, GAMESAVE_WND_H - 100); + sheet->NewGroup(nullptr, GAMESAVE_WND_W - 148, GAMESAVE_WND_H - 100); sheet->AddButton(TXT_CANCEL, UID_CANCEL); wnd.SetData(&lgd_data); @@ -679,9 +673,10 @@ bool LoadGameDialog() { int slot = res - SAVE_HOTSPOT_ID; if (occupied_slot[slot]) { - snprintf(filename, sizeof(filename), "saveg00%d", slot); - ddio_MakePath(pathname, savegame_dir, filename, NULL); - strcpy(LGS_Path, pathname); + std::stringstream filename; + filename << "saveg" << std::setw(3) << std::setfill('0') << slot; + pathname = savegame_dir / filename.str(); + LGS_Path = pathname; SetGameState(GAMESTATE_LOADGAME); res = UID_CANCEL; } @@ -703,7 +698,6 @@ bool LoadGameDialog() { ////////////////////////////////////////////////////////////////////////////// -// loads savegame as specified from LoadGameDialog. bool LoadCurrentSaveGame() { int retval = LoadGameState(LGS_Path); if (retval != LGS_OK) { @@ -717,8 +711,7 @@ bool LoadCurrentSaveGame() { ////////////////////////////////////////////////////////////////////////////// -// give a description and slot number (0 to GAMESAVE_SLOTS-1) -bool SaveGameState(const char *pathname, const char *description) { +bool SaveGameState(const std::filesystem::path &pathname, const char *description) { CFILE *fp; char buf[GAMESAVE_DESCLEN + 1]; int16_t pending_music_region; @@ -728,9 +721,8 @@ bool SaveGameState(const char *pathname, const char *description) { return false; // Delete the old games restored count. - char countpath[_MAX_PATH * 2]; - strcpy(countpath, pathname); - strcat(countpath, ".cnt"); + std::filesystem::path countpath = pathname; + countpath.replace_extension(".cnt"); CFILE *countfp; countfp = cfopen(countpath, "wb"); if (countfp) { diff --git a/Descent3/gamesave.h b/Descent3/gamesave.h index 5e164c120..476318af5 100644 --- a/Descent3/gamesave.h +++ b/Descent3/gamesave.h @@ -84,20 +84,19 @@ #ifndef GAMESAVE_H #define GAMESAVE_H -#include "pstypes.h" -#include "cfile.h" -#include "log.h" -#include "object.h" -#include "objinfo.h" +#include -#include "gametexture.h" #include "bitmap.h" -#include "ddio.h" +#include "cfile.h" #include "door.h" #include "doorway.h" +#include "gametexture.h" +#include "log.h" +#include "object.h" +#include "objinfo.h" +#include "polymodel.h" #include "ship.h" #include "weapon.h" -#include "polymodel.h" #define GAMESAVE_SLOTS 8 // maximum number of savegames #define GAMESAVE_DESCLEN 31 // gamesave description maximum length. @@ -123,11 +122,27 @@ struct gs_tables { #define GAMESAVE_VERSION 2 #define GAMESAVE_OLDVER 0 // any version before this value is obsolete. +/** + * Creates Save Game dialog in UI. + */ void SaveGameDialog(); -bool LoadGameDialog(); // returns true if ok, false if canceled. + +/** + * Creates Load Game dialog in UI. + * @return true if user pressed OK, false if user pressed Cancel or there some errors occurred. + */ +bool LoadGameDialog(); + +/** + * Quick saves game into predefined save game slot + */ void QuickSaveGame(); -bool LoadCurrentSaveGame(); // loads savegame as specified from LoadGameDialog (false fails) +/** + * Loads savegame as specified from LoadGameDialog + * @return false on failure + */ +bool LoadCurrentSaveGame(); extern int Quicksave_game_slot; // externed so gamesequencing can reset this value starting new game. @@ -142,7 +157,12 @@ extern int Quicksave_game_slot; // externed so gamesequencing can reset this val #define LGS_OBJECTSCORRUPT 5 // object list is corrupt (or out of date with level) #define LGS_CORRUPTLEVEL 6 // either level is out of date, or list is corrupted. -int LoadGameState(const char *pathname); +/** + * Loads a game from a given slot. + * @param pathname path to savegame. + * @return 0 on success, positive error code on failure. + */ +int LoadGameState(const std::filesystem::path &pathname); // Easy IO routines for repetitive tasks. @@ -232,13 +252,23 @@ void SGSSpew(CFILE *fp); // load matcens void SGSMatcens(CFILE *fp); -// give a description and slot number (0 to GAMESAVE_SLOTS-1) -bool SaveGameState(const char *pathname, const char *description); - -// retreive gamesave file header info. description must be a buffer of length GAMESAVE_DESCLEN+1 -// returns true if it's a valid savegame file. false if corrupted somehow -// pointer to bm_handle will return a bitmap handle to the snapshot for game. (*bm_handle) can be invalid. -bool GetGameStateInfo(const char *pathname, char *description, int *bm_handle = NULL); +/** + * Give a description and slot number (0 to GAMESAVE_SLOTS-1) + * @param pathname path to savegame + * @param description description + * @return true on success + */ +bool SaveGameState(const std::filesystem::path &pathname, const char *description); + +/** + * Retrieves gamesave file header info. Description must be a buffer of length GAMESAVE_DESCLEN+1. + * Pointer to bm_handle will return a bitmap handle to the snapshot for game. (*bm_handle) can be invalid. + * @param pathname path to savegame + * @param description buffer for description of savegame + * @param bm_handle bitmap handle for screen snapshot + * @return true if savegame is valid, false if it is corrupted somehow + */ +bool GetGameStateInfo(const std::filesystem::path &pathname, char *description, int *bm_handle = nullptr); /////////////////////////////////////////////////////////////////////////////// // reads in translation tables diff --git a/Descent3/loadstate.cpp b/Descent3/loadstate.cpp index c64e4014e..e2c8cf7ad 100644 --- a/Descent3/loadstate.cpp +++ b/Descent3/loadstate.cpp @@ -181,6 +181,7 @@ #include #include +#include #include "gamesave.h" #include "cfile.h" @@ -224,7 +225,7 @@ extern void PageInAllData(); // dynamically allocated to be efficient (only needed during save/load) static int LGSSnapshot(CFILE *fp); -static void IncreaseRestoreCount(const char *file); +static void IncreaseRestoreCount(const std::filesystem::path &file); int Times_game_restored = 0; // static gs_tables *gs_Xlates = NULL; @@ -232,17 +233,14 @@ int Times_game_restored = 0; gs_tables *gs_Xlates = NULL; // int Gamesave_read_version=0; -void IncreaseRestoreCount(const char *file) { +void IncreaseRestoreCount(const std::filesystem::path &file) { // Open the file up in read more, read the current count, then incease it // and write the increased value back out. - char countpath[_MAX_PATH * 2]; - CFILE *cfp; + std::filesystem::path countpath = file; + countpath.replace_extension(".cnt"); - strcpy(countpath, file); - strcat(countpath, ".cnt"); - - cfp = cfopen(countpath, "rb"); + CFILE *cfp = cfopen(countpath, "rb"); if (cfp) { Times_game_restored = cf_ReadInt(cfp); cfclose(cfp); @@ -259,10 +257,8 @@ void IncreaseRestoreCount(const char *file) { } extern bool IsRestoredGame; -/////////////////////////////////////////////////////////////////////////////// -// loads a game from a given slot. -int LoadGameState(const char *pathname) { - CFILE *fp; + +int LoadGameState(const std::filesystem::path &pathname) { int retval = LGS_OK; char desc[GAMESAVE_DESCLEN + 1]; char path[_MAX_PATH]; @@ -271,7 +267,7 @@ int LoadGameState(const char *pathname) { int16_t pending_music_region; IsRestoredGame = true; // load in stuff - fp = cfopen(pathname, "rb"); + CFILE *fp = cfopen(pathname, "rb"); if (!fp) { Int3(); return LGS_FILENOTFOUND; @@ -414,20 +410,18 @@ int LoadGameState(const char *pathname) { return retval; } -// retreive gamesave file header info. description must be a buffer of length GAMESAVE_DESCLEN+1 -// returns true if it's a valid savegame file. false if corrupted somehow -bool GetGameStateInfo(const char *pathname, char *description, int *bm_handle) { - CFILE *fp; +bool GetGameStateInfo(const std::filesystem::path &pathname, char *description, int *bm_handle) { int bitmap; char desc[GAMESAVE_DESCLEN + 1]; - fp = cfopen(pathname, "rb"); + CFILE *fp = cfopen(pathname, "rb"); if (!fp) return false; if (!cf_ReadBytes((uint8_t *)desc, GAMESAVE_DESCLEN + 1, fp)) { strcpy(description, TXT_ILLEGALSAVEGAME); - goto savesg_error; + cfclose(fp); + return false; } strcpy(description, desc); @@ -440,10 +434,6 @@ bool GetGameStateInfo(const char *pathname, char *description, int *bm_handle) { cfclose(fp); return true; - -savesg_error: - cfclose(fp); - return false; } ////////////////////////////////////////////////////////////////////////////// diff --git a/Descent3/menu.cpp b/Descent3/menu.cpp index 417cd3800..9d1f2be64 100644 --- a/Descent3/menu.cpp +++ b/Descent3/menu.cpp @@ -665,6 +665,7 @@ #include "config.h" #include "gamesave.h" #include "gamesequence.h" +#include "ddio.h" #include "demofile.h" #include "pilot.h" #include "LoadLevel.h" From 36d89eac2361781aa47c5a430e2e1674cfb55ab1 Mon Sep 17 00:00:00 2001 From: "Azamat H. Hackimov" Date: Tue, 8 Oct 2024 16:13:08 +0300 Subject: [PATCH 7/9] Ensure demo directory exists before writing a demo file --- Descent3/demofile.cpp | 43 +++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/Descent3/demofile.cpp b/Descent3/demofile.cpp index bae3fa54f..23d7000f1 100644 --- a/Descent3/demofile.cpp +++ b/Descent3/demofile.cpp @@ -358,30 +358,41 @@ void DemoToggleRecording() { // hand coded 128 because long filenames were failing in cfopen(which called fopen, which failed on very // long filenames. instead of printing a message out to the user, just don't allow filenames that long) if (DoEditDialog(TXT_DEMOFILENAME, szfile, 128)) { - if (stricmp(szfile + (strlen(szfile) - 4), ".dem") != 0) { - strcat(szfile, ".dem"); + std::filesystem::path demo_directory = cf_GetWritableBaseDirectory() / "demo"; + + std::error_code ec; + std::filesystem::create_directories(demo_directory, ec); + if (ec) { + LOG_ERROR << "Failed to create " << demo_directory << " directory. Unable to create demo file!"; + AddBlinkingHUDMessage(TXT_DEMOCANTCREATE); + Demo_fname.clear(); + return; } - Demo_fname = cf_GetWritableBaseDirectory() / "demo" / szfile; - LOG_INFO.printf("Saving demo to file: %s", Demo_fname.u8string().c_str()); + + Demo_fname = demo_directory / szfile; + Demo_fname.replace_extension(".dem"); + + LOG_INFO << "Saving demo to file " << Demo_fname; // Try to create the file Demo_cfp = cfopen(Demo_fname, "wb"); - if (Demo_cfp) { - // Setup the demo variables - if (!(Game_mode & GM_MULTI)) { - MultiBuildMatchTables(); - } - // Male sure we write the player info the first frame - Demo_last_pinfo = timer_GetTime() - (DEMO_PINFO_UPDATE * 2); - Demo_flags = DF_RECORDING; - // Write the header - DemoWriteHeader(); - DemoStartNewFrame(); - } else { + if (!Demo_cfp) { // cfopen failed + LOG_ERROR << "Unable to create demo file!"; AddBlinkingHUDMessage(TXT_DEMOCANTCREATE); Demo_fname.clear(); return; } + + // Set up the demo variables + if (!(Game_mode & GM_MULTI)) { + MultiBuildMatchTables(); + } + // Male sure we write the player info the first frame + Demo_last_pinfo = timer_GetTime() - (DEMO_PINFO_UPDATE * 2); + Demo_flags = DF_RECORDING; + // Write the header + DemoWriteHeader(); + DemoStartNewFrame(); } } From 1fd8789504b6f9f5f96709af0e3410d01830994c Mon Sep 17 00:00:00 2001 From: "Azamat H. Hackimov" Date: Tue, 8 Oct 2024 17:42:30 +0300 Subject: [PATCH 8/9] Convert LocalCustomGraphicsDir and LocalCustomSoundsDir to use std::fs::path Write custom graphics and sounds to writable base directory. --- Descent3/audiotaunts.cpp | 10 +++++----- Descent3/audiotaunts.h | 4 +++- Descent3/multi.cpp | 10 +++++----- Descent3/pilot.cpp | 4 ++-- lib/manage.h | 4 ++-- manage/manage.cpp | 28 +++++++++++++--------------- stream_audio/osfarchive.cpp | 8 ++++---- stream_audio/streamaudio.cpp | 6 +++--- stream_audio/streamaudio.h | 10 ++++++---- 9 files changed, 43 insertions(+), 41 deletions(-) diff --git a/Descent3/audiotaunts.cpp b/Descent3/audiotaunts.cpp index 303beaa89..93d673198 100644 --- a/Descent3/audiotaunts.cpp +++ b/Descent3/audiotaunts.cpp @@ -18,8 +18,8 @@ #include #include +#include -#include "debug.h" #include "pserror.h" #include "audiotaunts.h" #include "cfile.h" @@ -92,7 +92,7 @@ void taunt_SetDelayTime(float t) { Audio_taunt_delay_time = t; } // taunt_PlayTauntFile // // Given a path to an .osf file, it will play it -bool taunt_PlayTauntFile(const char *filename) { +bool taunt_PlayTauntFile(const std::filesystem::path &filename) { if (!Audio_taunts_enabled) return false; @@ -113,7 +113,7 @@ bool taunt_PlayPlayerTaunt(int pnum, int index) { } if ((NetPlayers[pnum].flags & NPF_CONNECTED) && (NetPlayers[pnum].sequence == NETSEQ_PLAYING)) { - char fullpath[_MAX_PATH]; + std::filesystem::path fullpath; char *file; switch (index) { case 0: @@ -130,10 +130,10 @@ bool taunt_PlayPlayerTaunt(int pnum, int index) { break; } - ddio_MakePath(fullpath, LocalCustomSoundsDir, file, NULL); + fullpath = LocalCustomSoundsDir / file; if (!cfexist(fullpath)) { - LOG_WARNING.printf("TAUNT: file %s doesn't exist (pnum=%d)", fullpath, pnum); + LOG_WARNING.printf("TAUNT: file %s doesn't exist (pnum=%d)", fullpath.u8string().c_str(), pnum); return false; } diff --git a/Descent3/audiotaunts.h b/Descent3/audiotaunts.h index 7e931fbca..d7ef75c2f 100644 --- a/Descent3/audiotaunts.h +++ b/Descent3/audiotaunts.h @@ -45,6 +45,8 @@ #ifndef __AUDIO_TAUNT_H_ #define __AUDIO_TAUNT_H_ +#include + extern bool Audio_taunts_enabled; // Error codes: @@ -82,7 +84,7 @@ bool taunt_ImportWave(const char *wave_filename, const char *outputfilename); // taunt_PlayTauntFile // // Given a path to an .osf file, it will play it -bool taunt_PlayTauntFile(const char *filename); +bool taunt_PlayTauntFile(const std::filesystem::path &filename); // taunt_PlayPlayerTaunt // diff --git a/Descent3/multi.cpp b/Descent3/multi.cpp index 543d298ed..33fba77c5 100644 --- a/Descent3/multi.cpp +++ b/Descent3/multi.cpp @@ -8213,20 +8213,20 @@ void MultiSendRequestPlayTaunt(int index) { return; // make sure an audio file exists there - char audio_file[_MAX_PATH]; + std::filesystem::path audio_file; switch (index) { case 0: - ddio_MakePath(audio_file, LocalCustomSoundsDir, NetPlayers[Player_num].voice_taunt1, NULL); + audio_file = LocalCustomSoundsDir / NetPlayers[Player_num].voice_taunt1; break; case 1: - ddio_MakePath(audio_file, LocalCustomSoundsDir, NetPlayers[Player_num].voice_taunt2, NULL); + audio_file = LocalCustomSoundsDir / NetPlayers[Player_num].voice_taunt2; break; case 2: - ddio_MakePath(audio_file, LocalCustomSoundsDir, NetPlayers[Player_num].voice_taunt3, NULL); + audio_file = LocalCustomSoundsDir / NetPlayers[Player_num].voice_taunt3; break; case 3: - ddio_MakePath(audio_file, LocalCustomSoundsDir, NetPlayers[Player_num].voice_taunt4, NULL); + audio_file = LocalCustomSoundsDir / NetPlayers[Player_num].voice_taunt4; break; } diff --git a/Descent3/pilot.cpp b/Descent3/pilot.cpp index 7f5fceac1..5351fa12c 100644 --- a/Descent3/pilot.cpp +++ b/Descent3/pilot.cpp @@ -2526,7 +2526,7 @@ bool PltSelectShip(pilot *Pilot) { FreeVClip(handle); break; } - std::filesystem::path newf = std::filesystem::path(LocalCustomGraphicsDir) / tempf.filename().replace_extension(".oaf"); + std::filesystem::path newf = LocalCustomGraphicsDir / tempf.filename().replace_extension(".oaf"); if (SaveVClip(newf, handle) == 0) { // error saving @@ -2581,7 +2581,7 @@ bool PltSelectShip(pilot *Pilot) { if (DoPathFileDialog(false, path, TXT_CHOOSE, {"*.wav"}, PFDF_FILEMUSTEXIST)) { std::filesystem::path dpath; std::filesystem::path filename = path.filename().replace_extension(".osf"); - std::filesystem::path tempfile = std::filesystem::path(LocalCustomSoundsDir) / filename; + std::filesystem::path tempfile = LocalCustomSoundsDir / filename; // import the sound LOG_INFO.printf("Importing: '%s'->'%s'", path.u8string().c_str(), tempfile.u8string().c_str()); diff --git a/lib/manage.h b/lib/manage.h index 6bb4beda0..893051b05 100644 --- a/lib/manage.h +++ b/lib/manage.h @@ -128,8 +128,8 @@ extern char LocalScriptDir[]; extern std::filesystem::path NetArtDir; extern std::filesystem::path LocalArtDir; -extern char LocalCustomGraphicsDir[]; -extern char LocalCustomSoundsDir[]; +extern std::filesystem::path LocalCustomGraphicsDir; +extern std::filesystem::path LocalCustomSoundsDir; extern char TempTableFilename[]; extern char TempTableLockFilename[]; diff --git a/manage/manage.cpp b/manage/manage.cpp index b5429d334..5ca671859 100644 --- a/manage/manage.cpp +++ b/manage/manage.cpp @@ -489,8 +489,8 @@ std::filesystem::path LocalMusicDir, NetMusicDir; std::filesystem::path LocalVoiceDir, NetVoiceDir; std::filesystem::path NetTableDir, LocalTableDir; char LocalD3Dir[TABLE_NAME_LEN], NetD3Dir[TABLE_NAME_LEN]; -char LocalCustomGraphicsDir[TABLE_NAME_LEN]; -char LocalCustomSoundsDir[TABLE_NAME_LEN]; +std::filesystem::path LocalCustomGraphicsDir; +std::filesystem::path LocalCustomSoundsDir; std::filesystem::path LockerFile; std::filesystem::path VersionFile; char TableUser[TABLE_NAME_LEN]; @@ -601,17 +601,14 @@ int mng_InitTableFiles() { Network_up = 0; } + // Do locals + mng_InitLocalTables(); + mng_InitLocalDirectories(); + mng_CheckToCreateLocalTables(); + if (Network_up == 0) { - mng_InitLocalTables(); - mng_InitLocalDirectories(); - mng_CheckToCreateLocalTables(); mng_InitTrackLocks(); } else { - // Do locals - mng_InitLocalTables(); - mng_InitLocalDirectories(); - mng_CheckToCreateLocalTables(); - // Do network mng_InitNetTables(); mng_InitNetDirectories(); @@ -689,8 +686,8 @@ int mng_InitLocalTables() { LocalManageGraphicsDir = localdir / "data" / "graphics"; LocalModelsDir = localdir / "data" / "models"; LocalSoundsDir = localdir / "data" / "sounds"; - ddio_MakePath(LocalCustomSoundsDir, LocalD3Dir, "custom", "sounds", NULL); - ddio_MakePath(LocalCustomGraphicsDir, LocalD3Dir, "custom", "graphics", NULL); + LocalCustomSoundsDir = cf_GetWritableBaseDirectory() / "custom" / "sounds"; + LocalCustomGraphicsDir = cf_GetWritableBaseDirectory() / "custom" / "graphics"; LocalRoomsDir = localdir / "data" / "rooms"; LocalBriefingDir = localdir / "data" / "briefings"; ddio_MakePath(LocalScriptDir, LocalD3Dir, "data", "scripts", NULL); @@ -815,18 +812,19 @@ void mng_CheckToCreateLocalTables() { } // Creates directories if needed void mng_InitLocalDirectories() { - std::filesystem::path dir = LocalD3Dir; + std::filesystem::path dir = cf_GetWritableBaseDirectory(); std::error_code ec; - std::filesystem::create_directories(dir / "custom", ec); + std::filesystem::create_directories(dir / "demo", ec); std::filesystem::create_directories(dir / "custom" / "graphics", ec); std::filesystem::create_directories(dir / "custom" / "sounds", ec); std::filesystem::create_directories(dir / "custom" / "settings", ec); + std::filesystem::create_directories(dir / "savegame", ec); + cf_SetSearchPath(LocalCustomGraphicsDir); cf_SetSearchPath(LocalCustomSoundsDir); if (Network_up) { - std::filesystem::create_directories(dir / "data", ec); std::filesystem::create_directories(dir / "data" / "tables", ec); std::filesystem::create_directories(dir / "data" / "graphics", ec); std::filesystem::create_directories(dir / "data" / "sounds", ec); diff --git a/stream_audio/osfarchive.cpp b/stream_audio/osfarchive.cpp index 60225bf30..d902eb5b7 100644 --- a/stream_audio/osfarchive.cpp +++ b/stream_audio/osfarchive.cpp @@ -75,7 +75,7 @@ OSFArchive::~OSFArchive() { } } -bool OSFArchive::Open(const char *filename, bool write) { +bool OSFArchive::Open(const std::filesystem::path &filename, bool write) { char tag[8]; ASSERT(!m_fp); @@ -99,7 +99,7 @@ bool OSFArchive::Open(const char *filename, bool write) { cfseek(m_fp, -OSF_HDR_SIZE, SEEK_END); cf_ReadBytes((uint8_t *)tag, strlen(OSF_TAG), m_fp); if (strcmp(OSF_TAG, tag) != 0) { - LOG_WARNING.printf("Illegal OSF file format for %s.", filename); + LOG_WARNING.printf("Illegal OSF file format for %s.", filename.u8string().c_str()); cfclose(m_fp); m_fp = NULL; return false; @@ -122,7 +122,7 @@ bool OSFArchive::Open(const char *filename, bool write) { // read in aux header based off of type. m_hdr.digi.measure = (uint32_t)cf_ReadInt(m_fp); } else { - LOG_WARNING.printf("Unsupported OSF file type in %s!", filename); + LOG_WARNING.printf("Unsupported OSF file type in %s!", filename.u8string().c_str()); cfclose(m_fp); m_fp = NULL; return false; @@ -133,7 +133,7 @@ bool OSFArchive::Open(const char *filename, bool write) { cfseek(m_fp, -OSF_HDR_TITLE_OFS, SEEK_END); if (!cf_ReadBytes((uint8_t *)m_name, OSF_HDR_TITLE_LEN, m_fp)) { - LOG_WARNING.printf("Stream title not found for %s.", filename); + LOG_WARNING.printf("Stream title not found for %s.", filename.u8string().c_str()); cfclose(m_fp); m_fp = NULL; return false; diff --git a/stream_audio/streamaudio.cpp b/stream_audio/streamaudio.cpp index 8de53231e..90a058d83 100644 --- a/stream_audio/streamaudio.cpp +++ b/stream_audio/streamaudio.cpp @@ -335,14 +335,14 @@ void AudioStream::SetVolume(float vol) { } float AudioStream::GetVolume() { return m_volume; } // flags specify what type of stream it is. -bool AudioStream::Open(const char *filename, int open_flags) { +bool AudioStream::Open(const std::filesystem::path &filename, int open_flags) { // don't open a stream that's already open, or bogus filename if (m_state != STRM_INVALID) { AudioStream::Close(); // Int3(); // return false; } - if (m_archive.Opened() || !filename) + if (m_archive.Opened() || filename.empty()) return false; if (!m_ll_sndsys) return false; @@ -920,7 +920,7 @@ void StreamVolume(float master_volume) { User_audio_stream.SetVolume(Stream_volume * master_volume); } // these functions are the 'simplified' stream interface from Jeff (most of this code is from Jeff) -int StreamPlay(const char *filename, float volume, int flags) { +int StreamPlay(const std::filesystem::path &filename, float volume, int flags) { int retval = -1; try { flags = 0; diff --git a/stream_audio/streamaudio.h b/stream_audio/streamaudio.h index 5f18ffb22..1679555aa 100644 --- a/stream_audio/streamaudio.h +++ b/stream_audio/streamaudio.h @@ -138,12 +138,14 @@ #ifndef __STREAMAUDIO_H_ #define __STREAMAUDIO_H_ +#include + #include "adecode.h" #include "ssl_lib.h" void *AudioStreamCB(void *user_data, int handle, int *size); int ADecodeFileRead(void *data, void *buf, uint32_t qty); -int StreamPlay(const char *filename, float volume, int flags); +int StreamPlay(const std::filesystem::path &filename, float volume, int flags); void StreamStop(int handle); int StreamGetSoundHandle(int handle); @@ -191,7 +193,7 @@ class OSFArchive { public: OSFArchive(); ~OSFArchive(); - bool Open(const char *filename, bool write = false); + bool Open(const std::filesystem::path &filename, bool write = false); void Close(); bool Opened() const { return m_fp ? true : false; }; void Rewind(); @@ -300,7 +302,7 @@ class AudioStream { ~AudioStream(); // simple operations // flags specify what type of stream you want. - bool Open(const char *filename, int open_flags = 0); + bool Open(const std::filesystem::path &filename, int open_flags = 0); // specifies next stream to be opened when current one ends. void Close(); // simple requests @@ -334,7 +336,7 @@ class AudioStream { return m_llshandle; }; // these functions are the 'simplified' stream interface from Jeff (most of this code is from Jeff) - friend int StreamPlay(const char *filename, float volume, int flags); + friend int StreamPlay(const std::filesystem::path &filename, float volume, int flags); friend void StreamStop(int handle); friend int StreamGetSoundHandle(int handle); }; From 9f6fb0561c828f9eda4bf399aa47ef8335012c75 Mon Sep 17 00:00:00 2001 From: "Azamat H. Hackimov" Date: Tue, 8 Oct 2024 21:06:05 +0300 Subject: [PATCH 9/9] Ensure that screenshot are saved in writable path Now screenshots are saved in "screenshots" directory of writable base path. Fix minor issues. --- Descent3/descent.cpp | 2 - Descent3/game.cpp | 141 ++++++++++++++++++------------------------- Descent3/render.cpp | 5 +- Descent3/render.h | 5 ++ 4 files changed, 66 insertions(+), 87 deletions(-) diff --git a/Descent3/descent.cpp b/Descent3/descent.cpp index 46cabed32..f8460116f 100644 --- a/Descent3/descent.cpp +++ b/Descent3/descent.cpp @@ -707,5 +707,3 @@ void D3DebugResumeHandler() { } #endif - -void RenderBlankScreen(); diff --git a/Descent3/game.cpp b/Descent3/game.cpp index 9a74c0a3d..fb8299c8e 100644 --- a/Descent3/game.cpp +++ b/Descent3/game.cpp @@ -655,38 +655,40 @@ * $NoKeywords: $ */ +#include #include +#include +#include -#include "game.h" -#include "ddio.h" -#include "pserror.h" -#include "descent.h" -#include "trigger.h" -#include "player.h" -#include "slew.h" +#include "args.h" +#include "cfile.h" +#include "cinematics.h" +#include "cockpit.h" +#include "config.h" #include "controls.h" -#include "renderer.h" +#include "dedicated_server.h" +#include "demofile.h" +#include "descent.h" #include "doorway.h" +#include "game.h" +#include "game2dll.h" +#include "gamefont.h" +#include "gameloop.h" +#include "gamesequence.h" #include "hud.h" #include "log.h" +#include "Mission.h" #include "multi.h" -#include "gamefont.h" +#include "NewBitmap.h" #include "newui.h" -#include "gamesequence.h" -#include "cinematics.h" +#include "pilot.h" +#include "player.h" +#include "pserror.h" +#include "render.h" +#include "renderer.h" +#include "slew.h" #include "SmallViews.h" -#include "Mission.h" -#include "cfile.h" -#include "gameloop.h" -#include "cockpit.h" -#include "game2dll.h" -#include "config.h" #include "stringtable.h" -#include "pilot.h" -#include "args.h" -#include "dedicated_server.h" -#include "demofile.h" -#include /////////////////////////////////////////////////////////////////////////////// // Variables @@ -728,17 +730,10 @@ int Game_do_ai_movement = 1; int Game_do_ai_vis = 1; #endif -// How much of the mine has been explored? -int Num_rooms_explored = 0; - -// Save and restores per level -int Num_player_saves = 0; -int Num_player_restores = 0; - // Missile camera int Missile_camera_window = SVW_LEFT; // will default to -1 when interface is in -// contains all relevent information for gamemode pertaining to d3x system. +// Contains all relevant information for gamemode pertaining to d3x system. gamemode Gamemode_info; /////////////////////////////////////////////////////////////////////////////// @@ -752,7 +747,7 @@ float GetFPS() { if (Frametime == 0.0f) { Frametime = 0.1f; } - return 1.0 / Frametime; + return 1.0f / Frametime; } /////////////////////////////////////////////////////////////////////////////// @@ -822,7 +817,7 @@ void QuickPlayGame() { void PlayGame() { // Initialize misc game if (InitGame()) { - // Run the game (note, if this call returns false, we couldn't play a level. Display an error maybe? + // Run the game (note, if this call returns false, we couldn't play a level. Display an error maybe?) GameSequencer(); } else { SetFunctionMode(MENU_MODE); @@ -830,8 +825,8 @@ void PlayGame() { // if they were going into a multiplayer game than we need to handle cleaning all that up if (Game_mode & GM_MULTI) { SetGameMode(GM_NORMAL); - for (int i = 0; i < MAX_PLAYERS; i++) { - NetPlayers[i].flags &= ~NPF_CONNECTED; + for (auto & NetPlayer : NetPlayers) { + NetPlayer.flags &= ~NPF_CONNECTED; } } } @@ -855,14 +850,13 @@ void PlayGame() { /////////////////////////////////////////////////////////////////////////////// void SetGamemodeScript(const char *scrfilename, int num_teams) { - if (scrfilename != NULL) + if (scrfilename != nullptr) strcpy(Gamemode_info.scriptname, scrfilename); else Gamemode_info.scriptname[0] = 0; Gamemode_info.requested_num_teams = num_teams; } -void RenderBlankScreen(void); bool InitGameScript() { // initialize gamemode script here. if (Gamemode_info.scriptname[0]) { @@ -913,7 +907,7 @@ int Low_vidmem = 0; void SetScreenMode(int sm, bool force_res_change) { static int old_sm = SM_NULL; static int rend_width = 0, rend_height = 0; - rendering_state rs; + rendering_state rs{}; if (sm == SM_CINEMATIC) { // force cinematic to menu @@ -939,8 +933,7 @@ void SetScreenMode(int sm, bool force_res_change) { rend_Close(); rend_initted = 0; } - } - else { + } else { int scr_width, scr_height, scr_bitdepth; if (sm == SM_GAME) { @@ -1047,16 +1040,16 @@ void SetScreenMode(int sm, bool force_res_change) { switch (sm) { case SM_GAME: { ui_HideCursor(); - SetUICallback(NULL); + SetUICallback(nullptr); int gw, gh; - Current_pilot.get_hud_data(NULL, NULL, NULL, &gw, &gh); + Current_pilot.get_hud_data(nullptr, nullptr, nullptr, &gw, &gh); if (force_res_change) { gw = Max_window_w; gh = Max_window_h; } InitGameScreen(gw, gh); // need to do this since the pilot w,h could change. - Current_pilot.set_hud_data(NULL, NULL, NULL, &Game_window_w, &Game_window_h); + Current_pilot.set_hud_data(nullptr, nullptr, nullptr, &Game_window_w, &Game_window_h); break; } @@ -1099,8 +1092,8 @@ struct tFrameStackFrame { tFrameStackFrame *next; tFrameStackFrame *prev; }; -tFrameStackFrame *FrameStackRoot = NULL; -tFrameStackFrame *FrameStackPtr = NULL; +tFrameStackFrame *FrameStackRoot = nullptr; +tFrameStackFrame *FrameStackPtr = nullptr; tFrameStackFrame FrameStack[8]; int FrameStackDepth = 0; @@ -1117,8 +1110,8 @@ void FramePush(int x1, int y1, int x2, int y2, bool clear) { Error("Out of memory\n"); } - curr->prev = NULL; - curr->next = NULL; + curr->prev = nullptr; + curr->next = nullptr; } else { // add on to the end of the list curr->next = FrameStackPtr = &FrameStack[FrameStackDepth]; @@ -1128,7 +1121,7 @@ void FramePush(int x1, int y1, int x2, int y2, bool clear) { } curr->next->prev = curr; // setup previous frame curr = curr->next; - curr->next = NULL; + curr->next = nullptr; } // at this point curr should be a valid frame, with prev and next set @@ -1168,12 +1161,12 @@ void FramePop(int *x1, int *y1, int *x2, int *y2, bool *clear) { if (frame == FrameStackRoot) { // we're popping off the root // DAJ mem_free(FrameStackRoot); - FrameStackRoot = NULL; - FrameStackPtr = NULL; + FrameStackRoot = nullptr; + FrameStackPtr = nullptr; } else { // we're just going back a frame, but still have a stack FrameStackPtr = FrameStackPtr->prev; // pop back a frame - FrameStackPtr->next = NULL; + FrameStackPtr->next = nullptr; // DAJ mem_free(frame); } FrameStackDepth--; @@ -1203,7 +1196,7 @@ void StartFrame(int x, int y, int x2, int y2, bool clear, bool push_on_stack) { if (last_fov != Render_FOV) { // Figure out new zoom factor float num = (Render_FOV / 2); - num = (3.14 * (float)num / 180.0); + num = (3.14f * (float)num / 180.0f); Render_zoom = tan(num); last_fov = Render_FOV; @@ -1252,22 +1245,9 @@ void EndFrame() { // Does a screenshot and tells the bitmap lib to save out the picture as a tga void DoScreenshot() { - int count; - char str[255], filename[255]; - CFILE *infile; - int done = 0; - int width = 640, height = 480; - - if (UseHardware) { - rendering_state rs; - rend_GetRenderState(&rs); - width = rs.screen_width; - height = rs.screen_height; - } - StopTime(); - // Tell our renderer lib to take a screen shot + // Tell our renderer lib to take a screenshot auto screenshot = rend_Screenshot(); if (!screenshot || screenshot->getData() == nullptr) { @@ -1275,28 +1255,25 @@ void DoScreenshot() { return; } - // Find a valid filename - count = 1; - while (!done) { - snprintf(str, sizeof(str), "Screenshot%.3d.png", count); - ddio_MakePath(filename, cf_GetWritableBaseDirectory().u8string().c_str(), str, NULL); - infile = (CFILE *)cfopen(filename, "rb"); - if (infile == NULL) { - done = 1; - continue; - } else - cfclose(infile); - - count++; - if (count > 999) - break; + std::filesystem::path screenshots_path = cf_GetWritableBaseDirectory() / "screenshots"; + + std::error_code ec; + std::filesystem::create_directories(screenshots_path, ec); + if (ec) { + LOG_ERROR << "Cannot create " << screenshots_path; + return; } + // Generate screenshot filename based on millisecond since epoch + const auto now = std::chrono::system_clock::now().time_since_epoch(); + std::stringstream screen_name; + screen_name << "screenshot-" << std::chrono::duration_cast(now).count() << ".png"; + std::filesystem::path filename = screenshots_path / screen_name.str(); // Now save it - screenshot->saveAsPNG(filename); + screenshot->saveAsPNG(filename.u8string().c_str()); if (Demo_flags != DF_PLAYBACK) { - AddHUDMessage(TXT_SCRNSHT, filename); + AddHUDMessage(TXT_SCRNSHT, filename.filename().u8string().c_str()); } StartTime(); diff --git a/Descent3/render.cpp b/Descent3/render.cpp index 005abd6b9..65c133576 100644 --- a/Descent3/render.cpp +++ b/Descent3/render.cpp @@ -3765,9 +3765,8 @@ int FogBlendFace (g3Point **src,int nv,int *num_solid,int *num_alpha) } */ -// RenderBlankScreen -// Renders a blank screen, to be used for UI callbacks to prevent Hall of mirrors with mouse cursor -void RenderBlankScreen(void) { rend_ClearScreen(GR_BLACK); } +void RenderBlankScreen() { rend_ClearScreen(GR_BLACK); } + #ifdef EDITOR // Finds what room & face is visible at a given screen x & y // Everything must be set up just like for RenderMineRoom(), and presumably is the same as diff --git a/Descent3/render.h b/Descent3/render.h index 2e5a4b7e0..da6defab1 100644 --- a/Descent3/render.h +++ b/Descent3/render.h @@ -311,6 +311,11 @@ void SetupRoomFog(room *rp, vector *eye, matrix *orient, int viewer_room); // facenum - which face in the specified room void RenderFace(room *rp, int facenum); +/** + * Renders a black screen, to be used for UI callbacks to prevent Hall of mirrors with mouse cursor + */ +void RenderBlankScreen(); + // Renders a specular face void RenderSpecularFacesFlat(room *rp);