mirror of
https://github.com/rwengine/openrw.git
synced 2024-11-25 20:02:40 +01:00
config: allow reading INI from string, file, default and current config
This (slightly more complicated implementation) allows us, once a configuration menu has been implemented, to store the current/new configuration to a INI file. The definitions of the parameters in rwgame/GameConfig.cpp is limited to one location.
This commit is contained in:
parent
a725a51baa
commit
05db65dbbc
@ -13,6 +13,10 @@ GameBase::GameBase(Logger &inlog, int argc, char *argv[]) : log(inlog) {
|
|||||||
log.info("Game", "Build: " + kBuildStr);
|
log.info("Game", "Build: " + kBuildStr);
|
||||||
|
|
||||||
if (!config.isValid()) {
|
if (!config.isValid()) {
|
||||||
|
log.error("Config", "Invalid INI file at \""
|
||||||
|
+ config.getConfigFile() + "\".\n"
|
||||||
|
+ "Adapt the following default INI to your configuration.\n"
|
||||||
|
+ config.getDefaultINIString());
|
||||||
throw std::runtime_error("Invalid configuration file at: " +
|
throw std::runtime_error("Invalid configuration file at: " +
|
||||||
config.getConfigFile());
|
config.getConfigFile());
|
||||||
}
|
}
|
||||||
|
@ -22,14 +22,15 @@ GameConfig::GameConfig(const std::string& configName,
|
|||||||
// Look up the path to use
|
// Look up the path to use
|
||||||
auto configFile = getConfigFile();
|
auto configFile = getConfigFile();
|
||||||
|
|
||||||
m_valid = readConfig(configFile);
|
std::string dummy;
|
||||||
|
m_valid = parseConfig(ParseType::FILE, configFile, ParseType::CONFIG, dummy);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GameConfig::getConfigFile() {
|
std::string GameConfig::getConfigFile() const {
|
||||||
return m_configPath + "/" + m_configName;
|
return m_configPath + "/" + m_configName;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GameConfig::isValid() {
|
bool GameConfig::isValid() const {
|
||||||
return m_valid;
|
return m_valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,8 +61,7 @@ std::string GameConfig::getDefaultConfigPath() {
|
|||||||
return ".";
|
return ".";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string stripComments(const std::string &str)
|
std::string stripComments(const std::string &str) {
|
||||||
{
|
|
||||||
auto s = std::string(str, 0, str.find_first_of(";#"));
|
auto s = std::string(str, 0, str.find_first_of(";#"));
|
||||||
return s.erase(s.find_last_not_of(" \n\r\t")+1);
|
return s.erase(s.find_last_not_of(" \n\r\t")+1);
|
||||||
}
|
}
|
||||||
@ -88,50 +88,139 @@ struct BoolTranslator {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
bool GameConfig::readConfig(const std::string &path) {
|
struct IntTranslator {
|
||||||
pt::ptree config, defaultConfig;
|
typedef std::string internal_type;
|
||||||
|
typedef int external_type;
|
||||||
|
boost::optional<external_type> get_value(const internal_type &str) {
|
||||||
|
return boost::optional<external_type>(std::stoi(stripComments(str)));
|
||||||
|
}
|
||||||
|
boost::optional<internal_type> put_value(const external_type &i) {
|
||||||
|
return boost::optional<internal_type>(std::to_string(i));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string GameConfig::getDefaultINIString() {
|
||||||
|
std::string result;
|
||||||
|
parseConfig(ParseType::DEFAULT, "", ParseType::STRING, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GameConfig::parseConfig(
|
||||||
|
GameConfig::ParseType srcType, const std::string &source,
|
||||||
|
ParseType destType, std::string &destination)
|
||||||
|
{
|
||||||
bool success = true;
|
bool success = true;
|
||||||
|
pt::ptree srcTree;
|
||||||
|
|
||||||
|
if ((srcType == ParseType::STRING) || (srcType == ParseType::FILE)) {
|
||||||
|
std::istream *istream = nullptr;
|
||||||
|
switch (srcType) {
|
||||||
|
case ParseType::STRING:
|
||||||
|
istream = new std::istringstream(source);
|
||||||
|
break;
|
||||||
|
case ParseType::FILE:
|
||||||
|
istream = new std::ifstream(source);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
//Cannot reach here
|
||||||
|
success = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (istream != nullptr) {
|
||||||
|
try {
|
||||||
|
pt::read_ini(*istream, srcTree);
|
||||||
|
} catch (pt::ini_parser_error &e) {
|
||||||
|
success = false;
|
||||||
|
RW_MESSAGE(e.what());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
success = false;
|
||||||
|
RW_MESSAGE("Unable to create stream from source.");
|
||||||
|
}
|
||||||
|
delete istream;
|
||||||
|
}
|
||||||
|
|
||||||
auto read_config = [&](const std::string &key, auto &target,
|
auto read_config = [&](const std::string &key, auto &target,
|
||||||
const auto &defaultValue, auto &translator,
|
const auto &defaultValue, auto &translator,
|
||||||
bool optional=true) {
|
bool optional=true) {
|
||||||
typedef typename std::remove_reference<decltype(target)>::type targetType;
|
typedef typename std::remove_reference<decltype(target)>::type config_t;
|
||||||
defaultConfig.put(key, defaultValue, translator);
|
|
||||||
try {
|
config_t sourceValue;
|
||||||
target = config.get<targetType>(key, translator);
|
|
||||||
} catch (pt::ptree_bad_path &e) {
|
switch (srcType) {
|
||||||
if (optional) {
|
case ParseType::DEFAULT:
|
||||||
target = defaultValue;
|
sourceValue = defaultValue;
|
||||||
} else {
|
break;
|
||||||
success = false;
|
case ParseType::CONFIG:
|
||||||
RW_MESSAGE(e.what());
|
sourceValue = target;
|
||||||
}
|
break;
|
||||||
|
case ParseType::FILE:
|
||||||
|
case ParseType::STRING:
|
||||||
|
try {
|
||||||
|
sourceValue = srcTree.get<config_t>(key, translator);
|
||||||
|
} catch (pt::ptree_bad_path &e) {
|
||||||
|
if (optional) {
|
||||||
|
sourceValue = defaultValue;
|
||||||
|
} else {
|
||||||
|
success = false;
|
||||||
|
RW_MESSAGE(e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
srcTree.put(key, sourceValue, translator);
|
||||||
|
|
||||||
|
switch (destType) {
|
||||||
|
case ParseType::DEFAULT:
|
||||||
|
RW_ERROR("Target cannot be DEFAULT.");
|
||||||
|
success = false;
|
||||||
|
break;
|
||||||
|
case ParseType::CONFIG:
|
||||||
|
//Don't care if success == false
|
||||||
|
target = sourceValue;
|
||||||
|
break;
|
||||||
|
case ParseType::FILE:
|
||||||
|
case ParseType::STRING:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
|
||||||
pt::read_ini(path, config);
|
|
||||||
} catch (pt::ini_parser_error &e) {
|
|
||||||
success = false;
|
|
||||||
RW_MESSAGE(e.what());
|
|
||||||
}
|
|
||||||
|
|
||||||
auto deft = StringTranslator();
|
auto deft = StringTranslator();
|
||||||
auto boolt = BoolTranslator();
|
auto boolt = BoolTranslator();
|
||||||
|
|
||||||
// @todo Don't allow path seperators and relative directories
|
//Add new configuration parameters here.
|
||||||
|
|
||||||
|
// @todo Don't allow path separators and relative directories
|
||||||
read_config("game.path", this->m_gamePath, "/opt/games/Grand Theft Auto 3", deft, false);
|
read_config("game.path", this->m_gamePath, "/opt/games/Grand Theft Auto 3", deft, false);
|
||||||
read_config("game.language", this->m_gameLanguage, "american", deft);
|
read_config("game.language", this->m_gameLanguage, "american", deft);
|
||||||
|
|
||||||
read_config("input.invert_y", this->m_inputInvertY, false, boolt);
|
read_config("input.invert_y", this->m_inputInvertY, false, boolt);
|
||||||
|
|
||||||
if (!success) {
|
if ((destType == ParseType::STRING) || (destType == ParseType::FILE)) {
|
||||||
std::stringstream strstream;
|
std::ostringstream ostream;
|
||||||
pt::write_ini(strstream, defaultConfig);
|
try {
|
||||||
|
pt::write_ini(ostream, srcTree);
|
||||||
RW_MESSAGE("Failed to parse configuration file (" + path + ").");
|
} catch (pt::ini_parser_error &e) {
|
||||||
RW_MESSAGE("Create one looking like this at \"" + path + "\":");
|
success = false;
|
||||||
RW_MESSAGE(strstream.str());
|
RW_MESSAGE(e.what());
|
||||||
|
}
|
||||||
|
switch (destType) {
|
||||||
|
case ParseType::STRING:
|
||||||
|
destination = ostream.str();
|
||||||
|
break;
|
||||||
|
case ParseType::FILE:
|
||||||
|
{
|
||||||
|
std::ofstream ofs(destination);
|
||||||
|
ofs << ostream.str();
|
||||||
|
ofs.close();
|
||||||
|
success &= !ofs.fail();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
//Cannot reach here
|
||||||
|
success = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
|
@ -15,13 +15,19 @@ public:
|
|||||||
/**
|
/**
|
||||||
* @brief getFilePath Returns the system file path for the configuration
|
* @brief getFilePath Returns the system file path for the configuration
|
||||||
*/
|
*/
|
||||||
std::string getConfigFile();
|
std::string getConfigFile() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief isValid
|
* @brief isValid
|
||||||
* @return True if the loaded configuration is valid
|
* @return True if the loaded configuration is valid
|
||||||
*/
|
*/
|
||||||
bool isValid();
|
bool isValid() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief getConfigString Returns the content of the default INI configuration.
|
||||||
|
* @return INI string
|
||||||
|
*/
|
||||||
|
std::string getDefaultINIString();
|
||||||
|
|
||||||
const std::string& getGameDataPath() const {
|
const std::string& getGameDataPath() const {
|
||||||
return m_gamePath;
|
return m_gamePath;
|
||||||
@ -36,7 +42,27 @@ public:
|
|||||||
private:
|
private:
|
||||||
static std::string getDefaultConfigPath();
|
static std::string getDefaultConfigPath();
|
||||||
|
|
||||||
bool readConfig(const std::string &path);
|
enum ParseType {
|
||||||
|
DEFAULT,
|
||||||
|
CONFIG,
|
||||||
|
FILE,
|
||||||
|
STRING
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief parseConfig Load data from source and write it to destination.
|
||||||
|
* @param srcType Can be DEFAULT | CONFIG | FILE | STRING
|
||||||
|
* @param source don't care if srcType == (DEFAULT | CONFIG),
|
||||||
|
* path of INI file if srcType == FILE
|
||||||
|
* INI string if srcType == STRING
|
||||||
|
* @param destType Can be CONFIG | FILE | STRING (DEFAULT is invalid)
|
||||||
|
* @param destination don't care if srcType == CONFIG
|
||||||
|
* path of INI file if destType == FILE
|
||||||
|
* INI string if srcType == STRING
|
||||||
|
* @return True if the parsing succeeded
|
||||||
|
*/
|
||||||
|
bool parseConfig(ParseType srcType, const std::string &source,
|
||||||
|
ParseType destType, std::string &destination);
|
||||||
|
|
||||||
/* Config State */
|
/* Config State */
|
||||||
std::string m_configName;
|
std::string m_configName;
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE(ConfigTests)
|
BOOST_AUTO_TEST_SUITE(ConfigTests)
|
||||||
|
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(test_loading) {
|
BOOST_AUTO_TEST_CASE(test_loading) {
|
||||||
// Write out a temporary file
|
// Write out a temporary file
|
||||||
std::ofstream test_config("/tmp/openrw_test.ini");
|
std::ofstream test_config("/tmp/openrw_test.ini");
|
||||||
@ -20,6 +21,7 @@ BOOST_AUTO_TEST_CASE(test_loading) {
|
|||||||
|
|
||||||
BOOST_CHECK_EQUAL(config.getGameDataPath(), "/dev/test");
|
BOOST_CHECK_EQUAL(config.getGameDataPath(), "/dev/test");
|
||||||
BOOST_CHECK_EQUAL(config.getGameLanguage(), "american");
|
BOOST_CHECK_EQUAL(config.getGameLanguage(), "american");
|
||||||
|
BOOST_CHECK_EQUAL(config.getInputInvertY(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user