improved path detection (and overriding) routines

This commit is contained in:
Jonathan 2022-07-03 18:23:19 -04:00
parent b8bb4cdd2a
commit 32f33fb4b3
6 changed files with 118 additions and 55 deletions

View File

@ -261,12 +261,17 @@ struct gamedef_q1_like_t : public gamedef_t
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);
}
@ -687,52 +692,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);
}

View File

@ -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<std::shared_ptr<archive_like>> 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();
}

View File

@ -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();

View File

@ -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<fs::path>
{
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<std::string> &values() { return _values; }
const std::unordered_set<std::string> &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<search_priority_t> 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\" <multiple allowed>", &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);

View File

@ -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) {

View File

@ -101,10 +101,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) {