2016-05-20 03:09:22 +02:00
|
|
|
#include "GameConfig.hpp"
|
2016-05-21 11:15:42 +02:00
|
|
|
#include <cstdlib>
|
2016-09-09 22:13:20 +02:00
|
|
|
#include <cstring>
|
|
|
|
#include <rw/defines.hpp>
|
2016-05-20 03:09:22 +02:00
|
|
|
|
2016-12-28 18:35:20 +01:00
|
|
|
#include <boost/property_tree/ptree.hpp>
|
|
|
|
#include <boost/property_tree/ini_parser.hpp>
|
|
|
|
namespace pt = boost::property_tree;
|
2016-05-20 03:09:22 +02:00
|
|
|
|
|
|
|
const std::string kConfigDirectoryName("OpenRW");
|
|
|
|
|
2016-09-09 22:13:20 +02:00
|
|
|
GameConfig::GameConfig(const std::string& configName,
|
|
|
|
const std::string& configPath)
|
|
|
|
: m_configName(configName)
|
|
|
|
, m_configPath(configPath)
|
|
|
|
, m_valid(false)
|
|
|
|
, m_inputInvertY(false) {
|
|
|
|
if (m_configPath.empty()) {
|
|
|
|
m_configPath = getDefaultConfigPath();
|
|
|
|
}
|
2016-05-20 03:09:22 +02:00
|
|
|
|
2016-09-09 22:13:20 +02:00
|
|
|
// Look up the path to use
|
|
|
|
auto configFile = getConfigFile();
|
2016-05-20 03:09:22 +02:00
|
|
|
|
2016-12-28 18:35:20 +01:00
|
|
|
m_valid = readConfig(configFile);
|
2016-05-20 03:09:22 +02:00
|
|
|
}
|
|
|
|
|
2016-09-09 22:13:20 +02:00
|
|
|
std::string GameConfig::getConfigFile() {
|
|
|
|
return m_configPath + "/" + m_configName;
|
2016-05-20 03:09:22 +02:00
|
|
|
}
|
|
|
|
|
2016-09-09 22:13:20 +02:00
|
|
|
bool GameConfig::isValid() {
|
|
|
|
return m_valid;
|
2016-05-20 03:09:22 +02:00
|
|
|
}
|
|
|
|
|
2016-09-09 22:13:20 +02:00
|
|
|
std::string GameConfig::getDefaultConfigPath() {
|
|
|
|
#if defined(RW_LINUX) || defined(RW_FREEBSD) || defined(RW_NETBSD) || \
|
|
|
|
defined(RW_OPENBSD)
|
|
|
|
char* config_home = getenv("XDG_CONFIG_HOME");
|
|
|
|
if (config_home != nullptr) {
|
|
|
|
return std::string(config_home) + "/" + kConfigDirectoryName;
|
|
|
|
}
|
|
|
|
char* home = getenv("HOME");
|
|
|
|
if (home != nullptr) {
|
|
|
|
return std::string(home) + "/.config/" + kConfigDirectoryName;
|
|
|
|
}
|
2016-05-21 11:15:42 +02:00
|
|
|
|
2016-05-25 17:32:17 +02:00
|
|
|
#elif defined(RW_OSX)
|
2016-09-09 22:13:20 +02:00
|
|
|
char* home = getenv("HOME");
|
|
|
|
if (home)
|
|
|
|
return std::string(home) + "/Library/Preferences/" +
|
|
|
|
kConfigDirectoryName;
|
2016-05-21 11:15:42 +02:00
|
|
|
|
2016-05-20 03:09:22 +02:00
|
|
|
#else
|
2016-09-09 22:13:20 +02:00
|
|
|
return ".";
|
2016-05-20 03:09:22 +02:00
|
|
|
#endif
|
2016-05-21 11:15:42 +02:00
|
|
|
|
2016-09-09 22:13:20 +02:00
|
|
|
// Well now we're stuck.
|
|
|
|
RW_ERROR("No default config path found.");
|
|
|
|
return ".";
|
2016-05-20 03:09:22 +02:00
|
|
|
}
|
|
|
|
|
2016-12-28 18:35:20 +01:00
|
|
|
std::string stripComments(const std::string &str)
|
|
|
|
{
|
|
|
|
auto s = std::string(str, 0, str.find_first_of(";#"));
|
|
|
|
return s.erase(s.find_last_not_of(" \n\r\t")+1);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct StringTranslator {
|
|
|
|
typedef std::string internal_type;
|
|
|
|
typedef std::string external_type;
|
|
|
|
boost::optional<external_type> get_value(const internal_type &str) {
|
|
|
|
return boost::optional<external_type>(stripComments(str));
|
2016-09-09 22:13:20 +02:00
|
|
|
}
|
2016-12-28 18:35:20 +01:00
|
|
|
boost::optional<internal_type> put_value(const external_type &str) {
|
|
|
|
return boost::optional<internal_type>(str);
|
|
|
|
}
|
|
|
|
};
|
2016-05-20 03:09:22 +02:00
|
|
|
|
2016-12-28 18:35:20 +01:00
|
|
|
struct BoolTranslator {
|
|
|
|
typedef std::string internal_type;
|
|
|
|
typedef bool external_type;
|
|
|
|
boost::optional<external_type> get_value(const internal_type &str) {
|
|
|
|
return boost::optional<external_type>(std::stoi(stripComments(str)) != 0);
|
|
|
|
}
|
|
|
|
boost::optional<internal_type> put_value(const external_type &b) {
|
|
|
|
return boost::optional<internal_type>(b ? "1" : "0");
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
bool GameConfig::readConfig(const std::string &path) {
|
|
|
|
pt::ptree config, defaultConfig;
|
|
|
|
bool success = true;
|
2016-05-20 03:09:22 +02:00
|
|
|
|
2016-12-28 18:35:20 +01:00
|
|
|
auto read_config = [&](const std::string &key, auto &target,
|
|
|
|
const auto &defaultValue, auto &translator,
|
|
|
|
bool optional=true) {
|
|
|
|
typedef typename std::remove_reference<decltype(target)>::type targetType;
|
|
|
|
defaultConfig.put(key, defaultValue, translator);
|
|
|
|
try {
|
|
|
|
target = config.get<targetType>(key, translator);
|
|
|
|
} catch (pt::ptree_bad_path &e) {
|
|
|
|
if (optional) {
|
|
|
|
target = defaultValue;
|
|
|
|
} else {
|
|
|
|
success = false;
|
|
|
|
RW_MESSAGE(e.what());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
try {
|
|
|
|
pt::read_ini(path, config);
|
|
|
|
} catch (pt::ini_parser_error &e) {
|
|
|
|
success = false;
|
|
|
|
RW_MESSAGE(e.what());
|
|
|
|
}
|
|
|
|
|
|
|
|
auto deft = StringTranslator();
|
|
|
|
auto boolt = BoolTranslator();
|
|
|
|
|
|
|
|
// @todo Don't allow path seperators and relative directories
|
|
|
|
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("input.invert_y", this->m_inputInvertY, false, boolt);
|
|
|
|
|
|
|
|
if (!success) {
|
|
|
|
std::stringstream strstream;
|
|
|
|
pt::write_ini(strstream, defaultConfig);
|
|
|
|
|
|
|
|
RW_MESSAGE("Failed to parse configuration file (" + path + ").");
|
|
|
|
RW_MESSAGE("Create one looking like this at \"" + path + "\":");
|
|
|
|
RW_MESSAGE(strstream.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
return success;
|
2016-05-20 03:09:22 +02:00
|
|
|
}
|
2016-12-28 18:35:20 +01:00
|
|
|
|