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
|
|
|
|
2017-01-15 03:16:46 +01:00
|
|
|
std::string dummy;
|
|
|
|
m_valid = parseConfig(ParseType::FILE, configFile, ParseType::CONFIG, dummy);
|
2016-05-20 03:09:22 +02:00
|
|
|
}
|
|
|
|
|
2017-01-15 03:16:46 +01:00
|
|
|
std::string GameConfig::getConfigFile() const {
|
2016-09-09 22:13:20 +02:00
|
|
|
return m_configPath + "/" + m_configName;
|
2016-05-20 03:09:22 +02:00
|
|
|
}
|
|
|
|
|
2017-01-15 03:16:46 +01:00
|
|
|
bool GameConfig::isValid() const {
|
2016-09-09 22:13:20 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2017-01-15 03:16:46 +01:00
|
|
|
std::string stripComments(const std::string &str) {
|
2016-12-28 18:35:20 +01:00
|
|
|
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");
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-01-15 03:16:46 +01:00
|
|
|
struct IntTranslator {
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
pt::ptree srcTree;
|
|
|
|
|
|
|
|
if ((srcType == ParseType::STRING) || (srcType == ParseType::FILE)) {
|
2017-02-17 01:16:26 +01:00
|
|
|
try {
|
|
|
|
switch (srcType) {
|
|
|
|
case ParseType::STRING:
|
|
|
|
pt::read_ini(source, srcTree);
|
|
|
|
break;
|
|
|
|
case ParseType::FILE:
|
|
|
|
{
|
|
|
|
std::ifstream ifs(source);
|
|
|
|
pt::read_ini(ifs, srcTree);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
//Cannot reach here
|
|
|
|
return false;
|
2017-01-15 03:16:46 +01:00
|
|
|
}
|
2017-02-17 01:16:26 +01:00
|
|
|
} catch (pt::ini_parser_error &e) {
|
|
|
|
RW_MESSAGE(e.what());
|
|
|
|
return false;
|
2017-01-15 03:16:46 +01:00
|
|
|
}
|
|
|
|
}
|
2016-05-20 03:09:22 +02:00
|
|
|
|
2017-02-17 01:16:26 +01:00
|
|
|
bool success = true;
|
|
|
|
|
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) {
|
2017-01-15 03:16:46 +01:00
|
|
|
typedef typename std::remove_reference<decltype(target)>::type config_t;
|
|
|
|
|
|
|
|
config_t sourceValue;
|
|
|
|
|
|
|
|
switch (srcType) {
|
|
|
|
case ParseType::DEFAULT:
|
|
|
|
sourceValue = defaultValue;
|
|
|
|
break;
|
|
|
|
case ParseType::CONFIG:
|
|
|
|
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;
|
2016-12-28 18:35:20 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
auto deft = StringTranslator();
|
|
|
|
auto boolt = BoolTranslator();
|
|
|
|
|
2017-02-17 01:17:50 +01:00
|
|
|
// Add new configuration parameters here.
|
|
|
|
// Additionally, add them to the unit test.
|
2017-01-15 03:16:46 +01:00
|
|
|
|
|
|
|
// @todo Don't allow path separators and relative directories
|
2016-12-28 18:35:20 +01:00
|
|
|
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);
|
|
|
|
|
2017-02-17 01:16:26 +01:00
|
|
|
if (!success)
|
|
|
|
return success;
|
|
|
|
|
2017-01-15 03:16:46 +01:00
|
|
|
if ((destType == ParseType::STRING) || (destType == ParseType::FILE)) {
|
|
|
|
std::ostringstream ostream;
|
|
|
|
try {
|
|
|
|
pt::write_ini(ostream, srcTree);
|
|
|
|
} catch (pt::ini_parser_error &e) {
|
|
|
|
success = false;
|
|
|
|
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;
|
|
|
|
}
|
2016-12-28 18:35:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return success;
|
2016-05-20 03:09:22 +02:00
|
|
|
}
|
2016-12-28 18:35:20 +01:00
|
|
|
|