From a0d81372d122d5cabc369ed58d8ed8ebde1aedb5 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Sun, 3 Jul 2022 18:23:19 -0400 Subject: [PATCH] improved path detection (and overriding) routines (cherry picked from commit 32f33fb4b344047c5a988e2a2192771d8325d597) --- common/bspfile.cc | 119 ++++++++++++++++++++++++++----------- common/fs.cc | 6 -- include/common/fs.hh | 4 -- include/common/settings.hh | 36 ++++++++++- light/light.cc | 4 -- qbsp/map.cc | 4 -- 6 files changed, 118 insertions(+), 55 deletions(-) diff --git a/common/bspfile.cc b/common/bspfile.cc index c435c916..c724f6c4 100644 --- a/common/bspfile.cc +++ b/common/bspfile.cc @@ -506,12 +506,17 @@ public: return create_solid_contents(); } - void init_filesystem(const fs::path &, const settings::common_settings &) const override + void init_filesystem(const fs::path &, const settings::common_settings &options) const override { // Q1-like games don't care about the local // filesystem. // they do care about the palette though. fs::clear(); + + for (auto &path : options.paths.values()) { + fs::addArchive(path, true); + } + img::init_palette(this); } @@ -1144,52 +1149,96 @@ private: } public: - void init_filesystem(const fs::path &source, const settings::common_settings &settings) const override + inline void addArchive(const fs::path &path) const + { + fs::addArchive(path, true); + discoverArchives(path); + } + + void init_filesystem(const fs::path &source, const settings::common_settings &options) const override { fs::clear(); + + if (options.defaultpaths.value()) { + constexpr const char *MAPS_FOLDER = "maps"; - constexpr const char *MAPS_FOLDER = "maps"; + // detect gamedir (mod directory path) + fs::path gamedir, basedir; + + // pull in from settings + if (options.gamedir.isChanged()) { + gamedir = options.gamedir.value(); + } + if (options.basedir.isChanged()) { + basedir = options.basedir.value(); + } + + // figure out the gamedir first + if (!gamedir.is_absolute()) { + if (!gamedir.empty() && basedir.is_absolute()) { + // we passed in a relative gamedir. probably meant to + // be derived from basedir. + gamedir = basedir.parent_path() / gamedir; + } - // detect gamedir (mod directory path) - fs::path gamedir; + // no gamedir, so calculate it from the input + if (gamedir.empty()) { + // expand canonicals, and fetch parent of source file + if (auto paths = fs::splitArchivePath(source)) { + // if the source is an archive, use the parent + // of that folder as the mod directory + // pak0.pak/maps/source.map -> C:/Quake/ID1 + gamedir = fs::canonical(paths.archive).parent_path(); + } else { + // maps/source.map -> C:/Quake/ID1/maps + // this is weak because the source may not exist yet + gamedir = fs::weakly_canonical(source).parent_path(); - // expand canonicals, and fetch parent of source file - if (auto paths = fs::splitArchivePath(source)) { - // if the source is an archive, use the parent - // of that folder as the mod directory - // pak0.pak/maps/source.map -> C:/Quake/ID1 - gamedir = fs::canonical(paths.archive).parent_path(); - } else { - // maps/source.map -> C:/Quake/ID1/maps - // this is weak because the source may not exist yet - gamedir = fs::weakly_canonical(source).parent_path(); + if (!string_iequals(gamedir.filename().generic_string(), MAPS_FOLDER)) { + logging::print("WARNING: '{}' is not directly inside '{}'; gamedir can't be automatically determined.\n", + source, MAPS_FOLDER); + } - if (!string_iequals(gamedir.filename().generic_string(), "maps")) { - logging::print("WARNING: '{}' is not directly inside '{}'. This may confuse automated path detection.\n", - source, MAPS_FOLDER); - return; + // C:/Quake/ID1/maps -> C:/Quake/ID1 + gamedir = gamedir.parent_path(); + } + } } - // C:/Quake/ID1/maps -> C:/Quake/ID1 - gamedir = gamedir.parent_path(); + if (!exists(gamedir)) { + logging::print("WARNING: failed to find gamedir '{}'\n", gamedir); + } + + // now find base dir, if we haven't set it yet + if (!basedir.is_absolute()) { + if (!gamedir.empty() && gamedir.is_absolute()) { + // we passed in a relative basedir. probably meant to + // be derived from gamedir. + basedir = gamedir.parent_path() / basedir; + } + + // no basedir, so calculate it from gamedir + if (basedir.empty()) { + basedir = gamedir.parent_path() / default_base_dir; + } + } + + if (!exists(basedir)) { + logging::print("WARNING: failed to find basedir '{}'\n", basedir); + } else if (!equivalent(gamedir, basedir)) { + addArchive(basedir); + } + + if (exists(gamedir)) { + addArchive(gamedir); + } } - // C:/Quake/ID1 -> C:/Quake - fs::path qdir = gamedir.parent_path(); - - // Set base dir and make sure it exists - fs::path basedir = qdir / (settings.basedir.isChanged() ? settings.basedir.value() : default_base_dir); - - if (!exists(basedir)) { - logging::print("WARNING: failed to find '{}' in '{}'\n", default_base_dir, qdir); - } else if (!equivalent(gamedir, basedir)) { - fs::addArchive(basedir); - discoverArchives(basedir); + // add secondary paths + for (auto &path : options.paths.values()) { + addArchive(path); } - fs::addArchive(gamedir); - discoverArchives(gamedir); - // load palette img::init_palette(this); } diff --git a/common/fs.cc b/common/fs.cc index 722865ea..be5fab2b 100644 --- a/common/fs.cc +++ b/common/fs.cc @@ -31,8 +31,6 @@ namespace fs { -path qdir, gamedir, basedir; - struct directory_archive : archive_like { using archive_like::archive_like; @@ -213,10 +211,6 @@ std::list> archives, directories; /** It's possible to compile quake 1/hexen 2 maps without a qdir */ void clear() { - qdir.clear(); - gamedir.clear(); - basedir.clear(); - archives.clear(); directories.clear(); } diff --git a/include/common/fs.hh b/include/common/fs.hh index e6377410..dc2bd61a 100644 --- a/include/common/fs.hh +++ b/include/common/fs.hh @@ -44,10 +44,6 @@ struct archive_like virtual data load(const path &filename) = 0; }; -extern path qdir, // c:/Quake/, c:/Hexen II/ etc. - gamedir, // c:/Quake/mymod/ - basedir; // c:/Quake/ID1/, c:/Quake 2/BASEQ2/ etc. - // clear all initialized/loaded data from fs void clear(); diff --git a/include/common/settings.hh b/include/common/settings.hh index 8e8ec1d6..eee55715 100644 --- a/include/common/settings.hh +++ b/include/common/settings.hh @@ -136,6 +136,11 @@ protected: } } + // skip end of named arguments + if (parser.token == "-" || parser.token == "--") { + parser.parse_token(); + } + while (std::isspace(value.back())) { value.pop_back(); } @@ -563,6 +568,31 @@ public: std::string format() const override { return _format; } }; +class setting_path : public setting_value +{ +public: + inline setting_path(setting_container *dictionary, const nameset &names, fs::path v, + const setting_group *group = nullptr, const char *description = "") + : setting_value(dictionary, names, v, group, description) + { + } + + bool parse(const std::string &settingName, parser_base_t &parser, bool locked = false) override + { + // make sure we can parse token out + if (!parser.parse_token()) { + return false; + } + + setValueFromParse(parser.token, locked); + return true; + } + + std::string stringValue() const override { return _value.string(); } + + std::string format() const override { return "\"relative/path\" or \"C:/absolute/path\""; } +}; + class setting_set : public setting_base { private: @@ -593,7 +623,7 @@ public: { } - const std::unordered_set &values() { return _values; } + const std::unordered_set &values() const { return _values; } inline void setValueLocked(const std::string &f) { addValueInternal(f, source::COMMANDLINE); } @@ -846,10 +876,12 @@ public: setting_bool nostat{this, "nostat", false, &logging_group, "don't output statistic messages"}; setting_bool noprogress{this, "noprogress", false, &logging_group, "don't output progress messages"}; setting_redirect quiet{this, {"quiet", "noverbose"}, {&nopercent, &nostat, &noprogress}, &logging_group, "suppress non-important messages (equivalent to -nopercent -nostat -noprogress)"}; - setting_string basedir{this, "basedir", "", "dir_name", &game_group, "override the default game base directory"}; + setting_path gamedir{this, "gamedir", "", &game_group, "override the default mod base directory. if this is not set, or if it is relative, it will be derived from the input file or the basedir if specified."}; + setting_path basedir{this, "basedir", "", &game_group, "override the default game base directory. if this is not set, or if it is relative, it will be derived from the input file or the gamedir if specified."}; setting_enum filepriority{this, "filepriority", search_priority_t::LOOSE, { { "loose", search_priority_t::LOOSE }, { "archive", search_priority_t::ARCHIVE } }, &game_group, "which types of archives (folders/loose files or packed archives) are higher priority and chosen first for path searching" }; setting_set paths{this, "path", "\"/path/to/folder\" ", &game_group, "additional paths or archives to add to the search path, mostly for loose files"}; setting_bool q2rtx{this, "q2rtx", false, &game_group, "adjust settings to best support Q2RTX"}; + setting_invertible_bool defaultpaths{this, "defaultpaths", true, &game_group, "whether the compiler should attempt to automatically derive game/base paths for games that support it"}; virtual void setParameters(int argc, const char **argv); diff --git a/light/light.cc b/light/light.cc index b100d8a6..60c8487c 100644 --- a/light/light.cc +++ b/light/light.cc @@ -1140,10 +1140,6 @@ void load_textures(const mbsp_t *bsp) { logging::print("--- {} ---\n", __func__); - for (auto &path : options.paths.values()) { - fs::addArchive(path, true); - } - if (bsp->loadversion->game->id == GAME_QUAKE_II) { LoadTextures(bsp); } else if (bsp->dtex.textures.size() > 0) { diff --git a/qbsp/map.cc b/qbsp/map.cc index fed2cb13..473758fb 100644 --- a/qbsp/map.cc +++ b/qbsp/map.cc @@ -102,10 +102,6 @@ static void EnsureTexturesLoaded(const mapentity_t *entity) return; map.textures_loaded = true; - - for (auto &path : options.paths.values()) { - fs::addArchive(path, true); - } // Q2 doesn't need this if (options.target_game->id == GAME_QUAKE_II) {