mirror of
https://github.com/OpenMW/openmw.git
synced 2025-03-03 15:09:39 +00:00
Allow relative paths in openmw.cfg; support --replace=config.
This commit is contained in:
parent
2061a0b66e
commit
c7ab67c2c1
9 changed files with 137 additions and 124 deletions
|
@ -141,7 +141,7 @@ namespace
|
||||||
if (!local.empty())
|
if (!local.empty())
|
||||||
dataDirs.push_back(std::move(local));
|
dataDirs.push_back(std::move(local));
|
||||||
|
|
||||||
config.processPaths(dataDirs);
|
config.filterOutNonExistingPaths(dataDirs);
|
||||||
|
|
||||||
const auto fsStrict = variables["fs-strict"].as<bool>();
|
const auto fsStrict = variables["fs-strict"].as<bool>();
|
||||||
const auto resDir = variables["resources"].as<Files::MaybeQuotedPath>();
|
const auto resDir = variables["resources"].as<Files::MaybeQuotedPath>();
|
||||||
|
|
|
@ -133,7 +133,7 @@ namespace NavMeshTool
|
||||||
if (!local.empty())
|
if (!local.empty())
|
||||||
dataDirs.push_back(std::move(local));
|
dataDirs.push_back(std::move(local));
|
||||||
|
|
||||||
config.processPaths(dataDirs);
|
config.filterOutNonExistingPaths(dataDirs);
|
||||||
|
|
||||||
const auto fsStrict = variables["fs-strict"].as<bool>();
|
const auto fsStrict = variables["fs-strict"].as<bool>();
|
||||||
const auto resDir = variables["resources"].as<Files::MaybeQuotedPath>();
|
const auto resDir = variables["resources"].as<Files::MaybeQuotedPath>();
|
||||||
|
|
|
@ -137,10 +137,12 @@ std::pair<Files::PathContainer, std::vector<std::string> > CS::Editor::readConfi
|
||||||
|
|
||||||
Files::PathContainer::value_type local(variables["data-local"].as<Files::MaybeQuotedPathContainer::value_type>());
|
Files::PathContainer::value_type local(variables["data-local"].as<Files::MaybeQuotedPathContainer::value_type>());
|
||||||
if (!local.empty())
|
if (!local.empty())
|
||||||
|
{
|
||||||
|
boost::filesystem::create_directories(local);
|
||||||
dataLocal.push_back(local);
|
dataLocal.push_back(local);
|
||||||
|
}
|
||||||
mCfgMgr.processPaths (dataDirs);
|
mCfgMgr.filterOutNonExistingPaths(dataDirs);
|
||||||
mCfgMgr.processPaths (dataLocal, true);
|
mCfgMgr.filterOutNonExistingPaths(dataLocal);
|
||||||
|
|
||||||
if (!dataLocal.empty())
|
if (!dataLocal.empty())
|
||||||
mLocal = dataLocal[0];
|
mLocal = dataLocal[0];
|
||||||
|
|
|
@ -37,8 +37,6 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
|
||||||
typedef std::vector<std::string> StringsVector;
|
typedef std::vector<std::string> StringsVector;
|
||||||
|
|
||||||
bpo::options_description desc = OpenMW::makeOptionsDescription();
|
bpo::options_description desc = OpenMW::makeOptionsDescription();
|
||||||
Files::ConfigurationManager::addCommonOptions(desc);
|
|
||||||
|
|
||||||
bpo::variables_map variables;
|
bpo::variables_map variables;
|
||||||
|
|
||||||
Files::parseArgs(argc, argv, variables, desc);
|
Files::parseArgs(argc, argv, variables, desc);
|
||||||
|
@ -82,7 +80,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
|
||||||
if (!local.empty())
|
if (!local.empty())
|
||||||
dataDirs.push_back(local);
|
dataDirs.push_back(local);
|
||||||
|
|
||||||
cfgMgr.processPaths(dataDirs);
|
cfgMgr.filterOutNonExistingPaths(dataDirs);
|
||||||
|
|
||||||
engine.setResourceDir(variables["resources"].as<Files::MaybeQuotedPath>());
|
engine.setResourceDir(variables["resources"].as<Files::MaybeQuotedPath>());
|
||||||
engine.setDataDirs(dataDirs);
|
engine.setDataDirs(dataDirs);
|
||||||
|
|
|
@ -16,14 +16,12 @@ namespace OpenMW
|
||||||
bpo::options_description makeOptionsDescription()
|
bpo::options_description makeOptionsDescription()
|
||||||
{
|
{
|
||||||
bpo::options_description desc("Syntax: openmw <options>\nAllowed options");
|
bpo::options_description desc("Syntax: openmw <options>\nAllowed options");
|
||||||
|
Files::ConfigurationManager::addCommonOptions(desc);
|
||||||
|
|
||||||
desc.add_options()
|
desc.add_options()
|
||||||
("help", "print help message")
|
("help", "print help message")
|
||||||
("version", "print version information and quit")
|
("version", "print version information and quit")
|
||||||
|
|
||||||
("replace", bpo::value<StringsVector>()->default_value(StringsVector(), "")
|
|
||||||
->multitoken()->composing(), "settings where the values from the current source should replace those from lower-priority sources instead of being appended")
|
|
||||||
|
|
||||||
("data", bpo::value<Files::MaybeQuotedPathContainer>()->default_value(Files::MaybeQuotedPathContainer(), "data")
|
("data", bpo::value<Files::MaybeQuotedPathContainer>()->default_value(Files::MaybeQuotedPathContainer(), "data")
|
||||||
->multitoken()->composing(), "set data directories (later directories have higher priority)")
|
->multitoken()->composing(), "set data directories (later directories have higher priority)")
|
||||||
|
|
||||||
|
|
|
@ -71,12 +71,14 @@ struct ContentFileTest : public ::testing::Test
|
||||||
}
|
}
|
||||||
|
|
||||||
Files::PathContainer::value_type local(variables["data-local"].as<Files::MaybeQuotedPathContainer::value_type>());
|
Files::PathContainer::value_type local(variables["data-local"].as<Files::MaybeQuotedPathContainer::value_type>());
|
||||||
if (!local.empty()) {
|
if (!local.empty())
|
||||||
|
{
|
||||||
|
boost::filesystem::create_directories(local);
|
||||||
dataLocal.push_back(local);
|
dataLocal.push_back(local);
|
||||||
}
|
}
|
||||||
|
|
||||||
mConfigurationManager.processPaths (dataDirs);
|
mConfigurationManager.filterOutNonExistingPaths(dataDirs);
|
||||||
mConfigurationManager.processPaths (dataLocal, true);
|
mConfigurationManager.filterOutNonExistingPaths(dataLocal);
|
||||||
|
|
||||||
if (!dataLocal.empty())
|
if (!dataLocal.empty())
|
||||||
dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end());
|
dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end());
|
||||||
|
|
|
@ -28,7 +28,7 @@ void Config::GameSettings::validatePaths()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the data dirs to convert the tokenized paths
|
// Parse the data dirs to convert the tokenized paths
|
||||||
mCfgMgr.processPaths(dataDirs);
|
mCfgMgr.processPaths(dataDirs, /*basePath=*/"");
|
||||||
mDataDirs.clear();
|
mDataDirs.clear();
|
||||||
|
|
||||||
for (auto & dataDir : dataDirs) {
|
for (auto & dataDir : dataDirs) {
|
||||||
|
@ -54,7 +54,7 @@ void Config::GameSettings::validatePaths()
|
||||||
QByteArray bytes = local.toUtf8();
|
QByteArray bytes = local.toUtf8();
|
||||||
dataDirs.push_back(Files::PathContainer::value_type(std::string(bytes.constData(), bytes.length())));
|
dataDirs.push_back(Files::PathContainer::value_type(std::string(bytes.constData(), bytes.length())));
|
||||||
|
|
||||||
mCfgMgr.processPaths(dataDirs);
|
mCfgMgr.processPaths(dataDirs, /*basePath=*/"");
|
||||||
|
|
||||||
if (!dataDirs.empty()) {
|
if (!dataDirs.empty()) {
|
||||||
QString path = QString::fromUtf8(dataDirs.front().string().c_str());
|
QString path = QString::fromUtf8(dataDirs.front().string().c_str());
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
namespace Files
|
namespace Files
|
||||||
{
|
{
|
||||||
|
|
||||||
|
namespace bpo = boost::program_options;
|
||||||
|
|
||||||
static const char* const openmwCfgFile = "openmw.cfg";
|
static const char* const openmwCfgFile = "openmw.cfg";
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(__WINDOWS__)
|
#if defined(_WIN32) || defined(__WINDOWS__)
|
||||||
|
@ -31,7 +33,6 @@ ConfigurationManager::ConfigurationManager(bool silent)
|
||||||
setupTokensMapping();
|
setupTokensMapping();
|
||||||
|
|
||||||
// Initialize with fixed paths, will be overridden in `readConfiguration`.
|
// Initialize with fixed paths, will be overridden in `readConfiguration`.
|
||||||
mLogPath = mFixedPath.getUserConfigPath();
|
|
||||||
mUserDataPath = mFixedPath.getUserDataPath();
|
mUserDataPath = mFixedPath.getUserDataPath();
|
||||||
mScreenshotPath = mFixedPath.getUserDataPath() / "screenshots";
|
mScreenshotPath = mFixedPath.getUserDataPath() / "screenshots";
|
||||||
}
|
}
|
||||||
|
@ -48,14 +49,25 @@ void ConfigurationManager::setupTokensMapping()
|
||||||
mTokensMapping.insert(std::make_pair(globalToken, &FixedPath<>::getGlobalDataPath));
|
mTokensMapping.insert(std::make_pair(globalToken, &FixedPath<>::getGlobalDataPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigurationManager::readConfiguration(boost::program_options::variables_map& variables,
|
static bool hasReplaceConfig(const bpo::variables_map& variables)
|
||||||
boost::program_options::options_description& description, bool quiet)
|
{
|
||||||
|
if (variables["replace"].empty())
|
||||||
|
return false;
|
||||||
|
for (const std::string& var : variables["replace"].as<std::vector<std::string>>())
|
||||||
|
{
|
||||||
|
if (var == "config")
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigurationManager::readConfiguration(bpo::variables_map& variables,
|
||||||
|
const bpo::options_description& description, bool quiet)
|
||||||
{
|
{
|
||||||
using ParsedConfigFile = bpo::basic_parsed_options<char>;
|
|
||||||
bool silent = mSilent;
|
bool silent = mSilent;
|
||||||
mSilent = quiet;
|
mSilent = quiet;
|
||||||
|
|
||||||
std::optional<ParsedConfigFile> config = loadConfig(mFixedPath.getLocalPath(), description);
|
std::optional<bpo::variables_map> config = loadConfig(mFixedPath.getLocalPath(), description);
|
||||||
if (config)
|
if (config)
|
||||||
mActiveConfigPaths.push_back(mFixedPath.getLocalPath());
|
mActiveConfigPaths.push_back(mFixedPath.getLocalPath());
|
||||||
else
|
else
|
||||||
|
@ -73,9 +85,10 @@ void ConfigurationManager::readConfiguration(boost::program_options::variables_m
|
||||||
|
|
||||||
std::stack<boost::filesystem::path> extraConfigDirs;
|
std::stack<boost::filesystem::path> extraConfigDirs;
|
||||||
addExtraConfigDirs(extraConfigDirs, variables);
|
addExtraConfigDirs(extraConfigDirs, variables);
|
||||||
addExtraConfigDirs(extraConfigDirs, *config);
|
if (!hasReplaceConfig(variables))
|
||||||
|
addExtraConfigDirs(extraConfigDirs, *config);
|
||||||
|
|
||||||
std::vector<ParsedConfigFile> parsedOptions{*std::move(config)};
|
std::vector<bpo::variables_map> parsedConfigs{*std::move(config)};
|
||||||
std::set<boost::filesystem::path> alreadyParsedPaths; // needed to prevent infinite loop in case of a circular link
|
std::set<boost::filesystem::path> alreadyParsedPaths; // needed to prevent infinite loop in case of a circular link
|
||||||
alreadyParsedPaths.insert(boost::filesystem::path(mActiveConfigPaths.front()));
|
alreadyParsedPaths.insert(boost::filesystem::path(mActiveConfigPaths.front()));
|
||||||
|
|
||||||
|
@ -92,20 +105,35 @@ void ConfigurationManager::readConfiguration(boost::program_options::variables_m
|
||||||
alreadyParsedPaths.insert(path);
|
alreadyParsedPaths.insert(path);
|
||||||
mActiveConfigPaths.push_back(path);
|
mActiveConfigPaths.push_back(path);
|
||||||
config = loadConfig(path, description);
|
config = loadConfig(path, description);
|
||||||
if (!config)
|
if (config && hasReplaceConfig(*config) && parsedConfigs.size() > 1)
|
||||||
continue;
|
{
|
||||||
addExtraConfigDirs(extraConfigDirs, *config);
|
mActiveConfigPaths.resize(1);
|
||||||
parsedOptions.push_back(*std::move(config));
|
parsedConfigs.resize(1);
|
||||||
|
Log(Debug::Info) << "Skipping previous configs except " << (mActiveConfigPaths.front() / "openmw.cfg") <<
|
||||||
|
" due to replace=config in " << (path / "openmw.cfg");
|
||||||
|
}
|
||||||
|
mActiveConfigPaths.push_back(path);
|
||||||
|
if (config)
|
||||||
|
{
|
||||||
|
addExtraConfigDirs(extraConfigDirs, *config);
|
||||||
|
parsedConfigs.push_back(*std::move(config));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto it = parsedOptions.rbegin(); it != parsedOptions.rend(); ++it)
|
for (auto it = parsedConfigs.rbegin(); it != parsedConfigs.rend(); ++it)
|
||||||
{
|
{
|
||||||
auto composingVariables = separateComposingVariables(variables, description);
|
auto composingVariables = separateComposingVariables(variables, description);
|
||||||
boost::program_options::store(std::move(*it), variables);
|
for (auto& [k, v] : *it)
|
||||||
|
{
|
||||||
|
auto it = variables.find(k);
|
||||||
|
if (it == variables.end())
|
||||||
|
variables.insert({k, v});
|
||||||
|
else if (it->second.defaulted())
|
||||||
|
it->second = v;
|
||||||
|
}
|
||||||
mergeComposingVariables(variables, composingVariables, description);
|
mergeComposingVariables(variables, composingVariables, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
mLogPath = mActiveConfigPaths.back();
|
|
||||||
mUserDataPath = variables["user-data"].as<Files::MaybeQuotedPath>();
|
mUserDataPath = variables["user-data"].as<Files::MaybeQuotedPath>();
|
||||||
if (mUserDataPath.empty())
|
if (mUserDataPath.empty())
|
||||||
{
|
{
|
||||||
|
@ -113,7 +141,6 @@ void ConfigurationManager::readConfiguration(boost::program_options::variables_m
|
||||||
Log(Debug::Warning) << "Error: `user-data` is not specified";
|
Log(Debug::Warning) << "Error: `user-data` is not specified";
|
||||||
mUserDataPath = mFixedPath.getUserDataPath();
|
mUserDataPath = mFixedPath.getUserDataPath();
|
||||||
}
|
}
|
||||||
processPath(mUserDataPath, true);
|
|
||||||
mScreenshotPath = mUserDataPath / "screenshots";
|
mScreenshotPath = mUserDataPath / "screenshots";
|
||||||
|
|
||||||
boost::filesystem::create_directories(getUserConfigPath());
|
boost::filesystem::create_directories(getUserConfigPath());
|
||||||
|
@ -123,18 +150,6 @@ void ConfigurationManager::readConfiguration(boost::program_options::variables_m
|
||||||
if (!boost::filesystem::is_directory(mScreenshotPath))
|
if (!boost::filesystem::is_directory(mScreenshotPath))
|
||||||
mScreenshotPath = mUserDataPath;
|
mScreenshotPath = mUserDataPath;
|
||||||
|
|
||||||
if (!quiet && !variables["replace"].empty())
|
|
||||||
{
|
|
||||||
for (const std::string& var : variables["replace"].as<std::vector<std::string>>())
|
|
||||||
{
|
|
||||||
if (var == "config")
|
|
||||||
{
|
|
||||||
Log(Debug::Warning) << "replace=config is not allowed and was ignored";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!quiet)
|
if (!quiet)
|
||||||
{
|
{
|
||||||
Log(Debug::Info) << "Logs dir: " << getUserConfigPath().string();
|
Log(Debug::Info) << "Logs dir: " << getUserConfigPath().string();
|
||||||
|
@ -146,42 +161,30 @@ void ConfigurationManager::readConfiguration(boost::program_options::variables_m
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigurationManager::addExtraConfigDirs(std::stack<boost::filesystem::path>& dirs,
|
void ConfigurationManager::addExtraConfigDirs(std::stack<boost::filesystem::path>& dirs,
|
||||||
const bpo::basic_parsed_options<char>& options) const
|
const bpo::variables_map& variables) const
|
||||||
{
|
|
||||||
boost::program_options::variables_map variables;
|
|
||||||
boost::program_options::store(options, variables);
|
|
||||||
boost::program_options::notify(variables);
|
|
||||||
addExtraConfigDirs(dirs, variables);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConfigurationManager::addExtraConfigDirs(std::stack<boost::filesystem::path>& dirs,
|
|
||||||
const boost::program_options::variables_map& variables) const
|
|
||||||
{
|
{
|
||||||
auto configIt = variables.find("config");
|
auto configIt = variables.find("config");
|
||||||
if (configIt == variables.end())
|
if (configIt == variables.end())
|
||||||
return;
|
return;
|
||||||
Files::PathContainer newDirs = asPathContainer(configIt->second.as<Files::MaybeQuotedPathContainer>());
|
Files::PathContainer newDirs = asPathContainer(configIt->second.as<Files::MaybeQuotedPathContainer>());
|
||||||
processPaths(newDirs);
|
|
||||||
for (auto it = newDirs.rbegin(); it != newDirs.rend(); ++it)
|
for (auto it = newDirs.rbegin(); it != newDirs.rend(); ++it)
|
||||||
dirs.push(*it);
|
dirs.push(*it);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigurationManager::addCommonOptions(boost::program_options::options_description& description)
|
void ConfigurationManager::addCommonOptions(bpo::options_description& description)
|
||||||
{
|
{
|
||||||
Files::MaybeQuotedPath defaultUserData;
|
|
||||||
static_cast<boost::filesystem::path&>(defaultUserData) = boost::filesystem::path("?userdata?");
|
|
||||||
|
|
||||||
description.add_options()
|
description.add_options()
|
||||||
("config", bpo::value<Files::MaybeQuotedPathContainer>()->default_value(Files::MaybeQuotedPathContainer(), "")
|
("config", bpo::value<Files::MaybeQuotedPathContainer>()->default_value(Files::MaybeQuotedPathContainer(), "")
|
||||||
->multitoken()->composing(), "additional config directories")
|
->multitoken()->composing(), "additional config directories")
|
||||||
("user-data", bpo::value<Files::MaybeQuotedPath>()->default_value(defaultUserData, ""),
|
("replace", bpo::value<std::vector<std::string>>()->default_value(std::vector<std::string>(), "")->multitoken()->composing(),
|
||||||
|
"settings where the values from the current source should replace those from lower-priority sources instead of being appended")
|
||||||
|
("user-data", bpo::value<Files::MaybeQuotedPath>()->default_value(Files::MaybeQuotedPath(), ""),
|
||||||
"set user data directory (used for saves, screenshots, etc)");
|
"set user data directory (used for saves, screenshots, etc)");
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::program_options::variables_map separateComposingVariables(boost::program_options::variables_map & variables,
|
bpo::variables_map separateComposingVariables(bpo::variables_map & variables, const bpo::options_description& description)
|
||||||
boost::program_options::options_description& description)
|
|
||||||
{
|
{
|
||||||
boost::program_options::variables_map composingVariables;
|
bpo::variables_map composingVariables;
|
||||||
for (auto itr = variables.begin(); itr != variables.end();)
|
for (auto itr = variables.begin(); itr != variables.end();)
|
||||||
{
|
{
|
||||||
if (description.find(itr->first, false).semantic()->is_composing())
|
if (description.find(itr->first, false).semantic()->is_composing())
|
||||||
|
@ -195,8 +198,8 @@ boost::program_options::variables_map separateComposingVariables(boost::program_
|
||||||
return composingVariables;
|
return composingVariables;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mergeComposingVariables(boost::program_options::variables_map& first, boost::program_options::variables_map& second,
|
void mergeComposingVariables(bpo::variables_map& first, bpo::variables_map& second,
|
||||||
boost::program_options::options_description& description)
|
const bpo::options_description& description)
|
||||||
{
|
{
|
||||||
// There are a few places this assumes all variables are present in second, but it's never crashed in the wild, so it looks like that's guaranteed.
|
// There are a few places this assumes all variables are present in second, but it's never crashed in the wild, so it looks like that's guaranteed.
|
||||||
std::set<std::string> replacedVariables;
|
std::set<std::string> replacedVariables;
|
||||||
|
@ -260,16 +263,18 @@ void mergeComposingVariables(boost::program_options::variables_map& first, boost
|
||||||
Log(Debug::Error) << "Unexpected composing variable type. Curse boost and their blasted arcane templates.";
|
Log(Debug::Error) << "Unexpected composing variable type. Curse boost and their blasted arcane templates.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
boost::program_options::notify(first);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigurationManager::processPath(boost::filesystem::path& path, bool create) const
|
void ConfigurationManager::processPath(boost::filesystem::path& path, const boost::filesystem::path& basePath) const
|
||||||
{
|
{
|
||||||
std::string str = path.string();
|
std::string str = path.string();
|
||||||
|
|
||||||
// Do nothing if the path doesn't start with a token
|
|
||||||
if (str.empty() || str[0] != '?')
|
if (str.empty() || str[0] != '?')
|
||||||
|
{
|
||||||
|
if (!path.is_absolute())
|
||||||
|
path = basePath / path;
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::string::size_type pos = str.find('?', 1);
|
std::string::size_type pos = str.find('?', 1);
|
||||||
if (pos != std::string::npos && pos != 0)
|
if (pos != std::string::npos && pos != 0)
|
||||||
|
@ -294,37 +299,49 @@ void ConfigurationManager::processPath(boost::filesystem::path& path, bool creat
|
||||||
path.clear();
|
path.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!boost::filesystem::is_directory(path) && create)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
boost::filesystem::create_directories(path);
|
|
||||||
}
|
|
||||||
catch (...) {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigurationManager::processPaths(Files::PathContainer& dataDirs, bool create) const
|
void ConfigurationManager::processPaths(Files::PathContainer& dataDirs, const boost::filesystem::path& basePath) const
|
||||||
{
|
{
|
||||||
for (Files::PathContainer::iterator it = dataDirs.begin(); it != dataDirs.end(); ++it)
|
for (auto& path : dataDirs)
|
||||||
{
|
processPath(path, basePath);
|
||||||
processPath(*it, create);
|
|
||||||
if (!boost::filesystem::is_directory(*it))
|
|
||||||
{
|
|
||||||
if (!mSilent)
|
|
||||||
Log(Debug::Warning) << "No such dir: " << *it;
|
|
||||||
(*it).clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dataDirs.erase(std::remove_if(dataDirs.begin(), dataDirs.end(),
|
|
||||||
std::bind(&boost::filesystem::path::empty, std::placeholders::_1)), dataDirs.end());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<bpo::basic_parsed_options<char>> ConfigurationManager::loadConfig(
|
void ConfigurationManager::processPaths(boost::program_options::variables_map& variables, const boost::filesystem::path& basePath) const
|
||||||
const boost::filesystem::path& path,
|
{
|
||||||
boost::program_options::options_description& description)
|
for (auto& [name, var] : variables)
|
||||||
|
{
|
||||||
|
if (var.defaulted())
|
||||||
|
continue;
|
||||||
|
if (var.value().type() == typeid(MaybeQuotedPathContainer))
|
||||||
|
{
|
||||||
|
auto& pathContainer = boost::any_cast<MaybeQuotedPathContainer&>(var.value());
|
||||||
|
for (MaybeQuotedPath& path : pathContainer)
|
||||||
|
processPath(path, basePath);
|
||||||
|
}
|
||||||
|
else if (var.value().type() == typeid(MaybeQuotedPath))
|
||||||
|
{
|
||||||
|
boost::filesystem::path& path = boost::any_cast<Files::MaybeQuotedPath&>(var.value());
|
||||||
|
processPath(path, basePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigurationManager::filterOutNonExistingPaths(Files::PathContainer& dataDirs) const
|
||||||
|
{
|
||||||
|
dataDirs.erase(std::remove_if(dataDirs.begin(), dataDirs.end(),
|
||||||
|
[this](const boost::filesystem::path& p)
|
||||||
|
{
|
||||||
|
bool exists = boost::filesystem::is_directory(p);
|
||||||
|
if (!exists && !mSilent)
|
||||||
|
Log(Debug::Warning) << "No such dir: " << p;
|
||||||
|
return !exists;
|
||||||
|
}),
|
||||||
|
dataDirs.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<bpo::variables_map> ConfigurationManager::loadConfig(
|
||||||
|
const boost::filesystem::path& path, const bpo::options_description& description) const
|
||||||
{
|
{
|
||||||
boost::filesystem::path cfgFile(path);
|
boost::filesystem::path cfgFile(path);
|
||||||
cfgFile /= std::string(openmwCfgFile);
|
cfgFile /= std::string(openmwCfgFile);
|
||||||
|
@ -336,7 +353,12 @@ std::optional<bpo::basic_parsed_options<char>> ConfigurationManager::loadConfig(
|
||||||
boost::filesystem::ifstream configFileStream(cfgFile);
|
boost::filesystem::ifstream configFileStream(cfgFile);
|
||||||
|
|
||||||
if (configFileStream.is_open())
|
if (configFileStream.is_open())
|
||||||
return Files::parse_config_file(configFileStream, description, true);
|
{
|
||||||
|
bpo::variables_map variables;
|
||||||
|
bpo::store(Files::parse_config_file(configFileStream, description, true), variables);
|
||||||
|
processPaths(variables, path);
|
||||||
|
return variables;
|
||||||
|
}
|
||||||
else if (!mSilent)
|
else if (!mSilent)
|
||||||
Log(Debug::Error) << "Loading failed.";
|
Log(Debug::Error) << "Loading failed.";
|
||||||
}
|
}
|
||||||
|
@ -381,32 +403,23 @@ const boost::filesystem::path& ConfigurationManager::getInstallPath() const
|
||||||
return mFixedPath.getInstallPath();
|
return mFixedPath.getInstallPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
const boost::filesystem::path& ConfigurationManager::getLogPath() const
|
|
||||||
{
|
|
||||||
return mLogPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
const boost::filesystem::path& ConfigurationManager::getScreenshotPath() const
|
const boost::filesystem::path& ConfigurationManager::getScreenshotPath() const
|
||||||
{
|
{
|
||||||
return mScreenshotPath;
|
return mScreenshotPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
void parseArgs(int argc, const char* const argv[], boost::program_options::variables_map& variables,
|
void parseArgs(int argc, const char* const argv[], bpo::variables_map& variables,
|
||||||
boost::program_options::options_description& description)
|
const bpo::options_description& description)
|
||||||
{
|
{
|
||||||
boost::program_options::store(
|
bpo::store(
|
||||||
boost::program_options::command_line_parser(argc, argv).options(description).allow_unregistered().run(),
|
bpo::command_line_parser(argc, argv).options(description).allow_unregistered().run(),
|
||||||
variables
|
variables
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void parseConfig(std::istream& stream, boost::program_options::variables_map& variables,
|
void parseConfig(std::istream& stream, bpo::variables_map& variables, const bpo::options_description& description)
|
||||||
boost::program_options::options_description& description)
|
|
||||||
{
|
{
|
||||||
boost::program_options::store(
|
bpo::store(Files::parse_config_file(stream, description, true), variables);
|
||||||
Files::parse_config_file(stream, description, true),
|
|
||||||
variables
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::istream& operator>> (std::istream& istream, MaybeQuotedPath& MaybeQuotedPath)
|
std::istream& operator>> (std::istream& istream, MaybeQuotedPath& MaybeQuotedPath)
|
||||||
|
|
|
@ -25,11 +25,14 @@ struct ConfigurationManager
|
||||||
virtual ~ConfigurationManager();
|
virtual ~ConfigurationManager();
|
||||||
|
|
||||||
void readConfiguration(boost::program_options::variables_map& variables,
|
void readConfiguration(boost::program_options::variables_map& variables,
|
||||||
boost::program_options::options_description& description, bool quiet=false);
|
const boost::program_options::options_description& description, bool quiet=false);
|
||||||
|
|
||||||
void processPath(boost::filesystem::path& path, bool create = false) const;
|
void filterOutNonExistingPaths(Files::PathContainer& dataDirs) const;
|
||||||
void processPaths(Files::PathContainer& dataDirs, bool create = false) const;
|
|
||||||
///< \param create Try creating the directory, if it does not exist.
|
// Replaces tokens (`?local?`, `?global?`, etc.) in paths. Adds `basePath` prefix for relative paths.
|
||||||
|
void processPath(boost::filesystem::path& path, const boost::filesystem::path& basePath) const;
|
||||||
|
void processPaths(Files::PathContainer& dataDirs, const boost::filesystem::path& basePath) const;
|
||||||
|
void processPaths(boost::program_options::variables_map& variables, const boost::filesystem::path& basePath) const;
|
||||||
|
|
||||||
/**< Fixed paths */
|
/**< Fixed paths */
|
||||||
const boost::filesystem::path& getGlobalPath() const;
|
const boost::filesystem::path& getGlobalPath() const;
|
||||||
|
@ -44,7 +47,7 @@ struct ConfigurationManager
|
||||||
|
|
||||||
const boost::filesystem::path& getCachePath() const;
|
const boost::filesystem::path& getCachePath() const;
|
||||||
|
|
||||||
const boost::filesystem::path& getLogPath() const;
|
const boost::filesystem::path& getLogPath() const { return getUserConfigPath(); }
|
||||||
const boost::filesystem::path& getScreenshotPath() const;
|
const boost::filesystem::path& getScreenshotPath() const;
|
||||||
|
|
||||||
static void addCommonOptions(boost::program_options::options_description& description);
|
static void addCommonOptions(boost::program_options::options_description& description);
|
||||||
|
@ -55,14 +58,12 @@ struct ConfigurationManager
|
||||||
typedef const boost::filesystem::path& (FixedPathType::*path_type_f)() const;
|
typedef const boost::filesystem::path& (FixedPathType::*path_type_f)() const;
|
||||||
typedef std::map<std::string, path_type_f> TokensMappingContainer;
|
typedef std::map<std::string, path_type_f> TokensMappingContainer;
|
||||||
|
|
||||||
std::optional<boost::program_options::basic_parsed_options<char>> loadConfig(
|
std::optional<boost::program_options::variables_map> loadConfig(
|
||||||
const boost::filesystem::path& path,
|
const boost::filesystem::path& path,
|
||||||
boost::program_options::options_description& description);
|
const boost::program_options::options_description& description) const;
|
||||||
|
|
||||||
void addExtraConfigDirs(std::stack<boost::filesystem::path>& dirs,
|
void addExtraConfigDirs(std::stack<boost::filesystem::path>& dirs,
|
||||||
const boost::program_options::variables_map& variables) const;
|
const boost::program_options::variables_map& variables) const;
|
||||||
void addExtraConfigDirs(std::stack<boost::filesystem::path>& dirs,
|
|
||||||
const boost::program_options::basic_parsed_options<char>& options) const;
|
|
||||||
|
|
||||||
void setupTokensMapping();
|
void setupTokensMapping();
|
||||||
|
|
||||||
|
@ -70,7 +71,6 @@ struct ConfigurationManager
|
||||||
|
|
||||||
FixedPathType mFixedPath;
|
FixedPathType mFixedPath;
|
||||||
|
|
||||||
boost::filesystem::path mLogPath;
|
|
||||||
boost::filesystem::path mUserDataPath;
|
boost::filesystem::path mUserDataPath;
|
||||||
boost::filesystem::path mScreenshotPath;
|
boost::filesystem::path mScreenshotPath;
|
||||||
|
|
||||||
|
@ -80,16 +80,16 @@ struct ConfigurationManager
|
||||||
};
|
};
|
||||||
|
|
||||||
boost::program_options::variables_map separateComposingVariables(boost::program_options::variables_map& variables,
|
boost::program_options::variables_map separateComposingVariables(boost::program_options::variables_map& variables,
|
||||||
boost::program_options::options_description& description);
|
const boost::program_options::options_description& description);
|
||||||
|
|
||||||
void mergeComposingVariables(boost::program_options::variables_map& first, boost::program_options::variables_map& second,
|
void mergeComposingVariables(boost::program_options::variables_map& first, boost::program_options::variables_map& second,
|
||||||
boost::program_options::options_description& description);
|
const boost::program_options::options_description& description);
|
||||||
|
|
||||||
void parseArgs(int argc, const char* const argv[], boost::program_options::variables_map& variables,
|
void parseArgs(int argc, const char* const argv[], boost::program_options::variables_map& variables,
|
||||||
boost::program_options::options_description& description);
|
const boost::program_options::options_description& description);
|
||||||
|
|
||||||
void parseConfig(std::istream& stream, boost::program_options::variables_map& variables,
|
void parseConfig(std::istream& stream, boost::program_options::variables_map& variables,
|
||||||
boost::program_options::options_description& description);
|
const boost::program_options::options_description& description);
|
||||||
|
|
||||||
class MaybeQuotedPath : public boost::filesystem::path
|
class MaybeQuotedPath : public boost::filesystem::path
|
||||||
{
|
{
|
||||||
|
@ -101,6 +101,6 @@ typedef std::vector<MaybeQuotedPath> MaybeQuotedPathContainer;
|
||||||
|
|
||||||
PathContainer asPathContainer(const MaybeQuotedPathContainer& MaybeQuotedPathContainer);
|
PathContainer asPathContainer(const MaybeQuotedPathContainer& MaybeQuotedPathContainer);
|
||||||
|
|
||||||
} /* namespace Cfg */
|
} /* namespace Files */
|
||||||
|
|
||||||
#endif /* COMPONENTS_FILES_CONFIGURATIONMANAGER_HPP */
|
#endif /* COMPONENTS_FILES_CONFIGURATIONMANAGER_HPP */
|
||||||
|
|
Loading…
Reference in a new issue