Rewritten config to use JSON instead of INI

dev
Tomasz Kapuściński 2023-08-15 12:02:51 +02:00
parent fde2b901bd
commit 1034c2e45c
9 changed files with 105 additions and 140 deletions

View File

@ -597,17 +597,16 @@ bool CApplication::Create()
// for list of resolutions in options menu, not calling it results in empty list
auto modes = GetVideoResolutionList();
if ( GetConfigFile().GetStringProperty("Setup", "Resolution", sValue) && !m_resolutionOverride )
std::vector<int> values;
if ( GetConfigFile().GetArrayProperty("Setup", "Resolution", values) && !m_resolutionOverride )
{
std::istringstream resolution(sValue);
std::string ws, hs;
std::getline(resolution, ws, 'x');
std::getline(resolution, hs, 'x');
int w = 800, h = 600;
if (!ws.empty() && !hs.empty())
if (values.size() >= 2)
{
w = atoi(ws.c_str());
h = atoi(hs.c_str());
w = values[0];
h = values[1];
}
// Why not just set m_deviceConfig.size to w,h? Because this way if the resolution is no longer supported (e.g. changing monitor) defaults will be used instead

View File

@ -338,17 +338,19 @@ InputSlot CInput::FindBinding(unsigned int key)
void CInput::SaveKeyBindings()
{
std::stringstream key;
std::vector<int> key;
GetConfigFile().SetStringProperty("Keybindings", "_Version", "SDL2");
for (int i = 0; i < INPUT_SLOT_MAX; i++)
{
InputBinding b = GetInputBinding(static_cast<InputSlot>(i));
key.clear();
key.str("");
key << b.primary << " " << b.secondary;
GetConfigFile().SetStringProperty("Keybindings", m_keyTable[static_cast<InputSlot>(i)], key.str());
if (b.primary != KEY_INVALID) key.push_back(b.primary);
if (b.secondary != KEY_INVALID) key.push_back(b.secondary);
GetConfigFile().SetArrayProperty("Keybindings", m_keyTable[static_cast<InputSlot>(i)], key);
}
for (int i = 0; i < JOY_AXIS_SLOT_MAX; i++)
@ -363,21 +365,22 @@ void CInput::SaveKeyBindings()
void CInput::LoadKeyBindings()
{
std::stringstream skey;
std::string keys;
if (GetConfigFile().GetStringProperty("Keybindings", "_Version", keys) && keys == "SDL2") // Keybindings from SDL1.2 are incompatible with SDL2 !!
std::string version;
// Keybindings from SDL1.2 are incompatible with SDL2 !!
if (GetConfigFile().GetStringProperty("Keybindings", "_Version", version) && version == "SDL2")
{
std::vector<int> keys;
for (int i = 0; i < INPUT_SLOT_MAX; i++)
{
InputBinding b;
if (!GetConfigFile().GetStringProperty("Keybindings", m_keyTable[static_cast<InputSlot>(i)], keys))
if (!GetConfigFile().GetArrayProperty("Keybindings", m_keyTable[static_cast<InputSlot>(i)], keys))
continue;
skey.clear();
skey.str(keys);
skey >> b.primary;
skey >> b.secondary;
if (keys.size() >= 1) b.primary = keys[0];
if (keys.size() >= 2) b.secondary = keys[1];
SetInputBinding(static_cast<InputSlot>(i), b);
}

View File

@ -27,13 +27,10 @@
#include "common/system/system.h"
#include <boost/property_tree/ini_parser.hpp>
#include <memory>
#include <utility>
#include <cstring>
namespace bp = boost::property_tree;
#include <fstream>
CConfigFile::CConfigFile()
: m_needsSave(false)
@ -65,20 +62,20 @@ bool CConfigFile::Init()
bool good;
if (m_useCurrentDirectory)
{
auto inputStream = std::make_unique<std::ifstream>("./colobot.ini");
auto inputStream = std::make_unique<std::ifstream>("./colobot.json");
good = inputStream->good();
stream = std::move(inputStream);
}
else
{
auto inputStream = std::make_unique<CInputStream>("colobot.ini");
auto inputStream = std::make_unique<CInputStream>("colobot.json");
good = inputStream->is_open();
stream = std::move(inputStream);
}
if (good)
{
bp::ini_parser::read_ini(*stream, m_propertyTree);
m_properties = nlohmann::json::parse(*stream);
m_loaded = true;
}
else
@ -105,20 +102,20 @@ bool CConfigFile::Save()
bool good;
if (m_useCurrentDirectory)
{
auto outputStream = std::make_unique<std::ofstream>("./colobot.ini");
auto outputStream = std::make_unique<std::ofstream>("./colobot.json");
good = outputStream->good();
stream = std::move(outputStream);
}
else
{
auto outputStream = std::make_unique<COutputStream>("colobot.ini");
auto outputStream = std::make_unique<COutputStream>("colobot.json");
good = outputStream->is_open();
stream = std::move(outputStream);
}
if (good)
{
bp::ini_parser::write_ini(*stream, m_propertyTree);
*stream << m_properties.dump(4);
m_needsSave = false;
}
else
@ -138,119 +135,68 @@ bool CConfigFile::Save()
bool CConfigFile::SetStringProperty(std::string section, std::string key, std::string value)
{
try
{
m_propertyTree.put(section + "." + key, value);
m_needsSave = true;
}
catch (std::exception & e)
{
GetLogger()->Error("Error on editing config file: %s\n", e.what());
return false;
}
m_properties[section][key] = value;
m_needsSave = true;
return true;
}
bool CConfigFile::GetStringProperty(std::string section, std::string key, std::string &value)
{
try
{
std::string readValue = m_propertyTree.get<std::string>(section + "." + key);
value = std::move(readValue);
}
catch (std::exception & e)
{
GetLogger()->Log(m_loaded ? LOG_INFO : LOG_TRACE, "Error on parsing config file: %s\n", e.what());
return false;
}
auto element = m_properties[section][key];
if (!element.is_string()) return false;
value = element.get<std::string>();
return true;
}
bool CConfigFile::SetIntProperty(std::string section, std::string key, int value)
{
try
{
m_propertyTree.put(section + "." + key, value);
m_needsSave = true;
}
catch (std::exception & e)
{
GetLogger()->Error("Error on editing config file: %s\n", e.what());
return false;
}
m_properties[section][key] = value;
m_needsSave = true;
return true;
}
bool CConfigFile::GetIntProperty(std::string section, std::string key, int &value)
{
try
{
int readValue = m_propertyTree.get<int>(section + "." + key);
value = readValue;
}
catch (std::exception & e)
{
GetLogger()->Log(m_loaded ? LOG_INFO : LOG_TRACE, "Error on parsing config file: %s\n", e.what());
return false;
}
auto element = m_properties[section][key];
if (!element.is_number()) return false;
value = element.get<int>();
return true;
}
bool CConfigFile::SetBoolProperty(std::string section, std::string key, bool value)
{
return SetIntProperty(section, key, value ? 1 : 0);
m_properties[section][key] = value;
m_needsSave = true;
return true;
}
bool CConfigFile::GetBoolProperty(std::string section, std::string key, bool& value)
{
int intValue = 0;
bool result = GetIntProperty(section, key, intValue);
if (result)
{
if (intValue == 0)
{
value = false;
}
else if (intValue == 1)
{
value = true;
}
else
{
GetLogger()->Log(m_loaded ? LOG_INFO : LOG_TRACE, "Error on parsing bool property %s.%s (expected 0 or 1, not %d)\n",
section.c_str(), key.c_str(), intValue);
return false;
}
}
return result;
auto element = m_properties[section][key];
if (!element.is_boolean()) return false;
value = element.get<bool>();
return true;
}
bool CConfigFile::SetFloatProperty(std::string section, std::string key, float value)
{
try
{
m_propertyTree.put(section + "." + key, value);
m_needsSave = true;
}
catch (std::exception & e)
{
GetLogger()->Error("Error on editing config file: %s\n", e.what());
return false;
}
m_properties[section][key] = value;
m_needsSave = true;
return true;
}
bool CConfigFile::GetFloatProperty(std::string section, std::string key, float &value)
{
try
{
float readValue = m_propertyTree.get<float>(section + "." + key);
value = readValue;
}
catch (std::exception & e)
{
GetLogger()->Log(m_loaded ? LOG_INFO : LOG_TRACE, "Error on parsing config file: %s\n", e.what());
return false;
}
auto element = m_properties[section][key];
if (!element.is_number()) return false;
value = element.get<float>();
return true;
}

View File

@ -28,7 +28,7 @@
#include "common/logger.h"
#include <boost/property_tree/ptree.hpp>
#include <nlohmann/json.hpp>
#include <string>
#include <sstream>
@ -115,8 +115,14 @@ public:
{
try
{
std::string convertedValue = ArrayToString(array);
m_propertyTree.put(section + "." + key, convertedValue);
auto property = nlohmann::json::array();
for (const auto& value : array)
{
property.push_back(value);
}
m_properties[section][key] = property;
m_needsSave = true;
}
catch (std::exception & e)
@ -137,9 +143,12 @@ public:
{
try
{
std::string readValue = m_propertyTree.get<std::string>(section + "." + key);
std::vector<T> convertedValue = StringToArray<T>(readValue);
array = std::move(convertedValue);
array.clear();
for (auto& value : m_properties[section][key])
{
array.push_back(value.get<T>());
}
}
catch (std::exception & e)
{
@ -189,7 +198,7 @@ private:
}
private:
boost::property_tree::ptree m_propertyTree;
nlohmann::json m_properties;
bool m_needsSave;
bool m_useCurrentDirectory;
bool m_loaded;

View File

@ -58,11 +58,8 @@ CSettings::CSettings()
void CSettings::SaveResolutionSettings(const Gfx::DeviceConfig& config)
{
// NOTE: These settings are loaded in CApplication
std::ostringstream ss;
ss << config.size.x << "x" << config.size.y;
GetConfigFile().SetStringProperty("Setup", "Resolution", ss.str());
std::vector<int> values = { config.size.x, config.size.y };
GetConfigFile().SetArrayProperty("Setup", "Resolution", values);
GetConfigFile().SetBoolProperty("Setup", "Fullscreen", config.fullScreen);
GetConfigFile().Save();
}

View File

@ -1,5 +1,5 @@
set(TEST_FILES
common/colobot.ini
common/colobot.json
)
file(COPY ${TEST_FILES} DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})

View File

@ -1,13 +0,0 @@
[test_float]
float_value=1.5
[test_string]
string_value=Hello world
[test_int]
int_value=42
[test_array]
string_array=AAA,Hello world,Gold Edition
int_array=2,3,1
bool_array=1,0,1

View File

@ -0,0 +1,24 @@
{
"test_float": {
"float_value": 1.5
},
"test_string": {
"string_value": "Hello world"
},
"test_int": {
"int_value": 42
},
"test_array": {
"string_array": [
"AAA",
"Hello world",
"Gold Edition"
],
"int_array": [
2, 3, 1
],
"bool_array": [
true, false, true
]
}
}

View File

@ -38,7 +38,7 @@ TEST_F(CConfigFileTest, ReadTest)
{
m_configFile.SetUseCurrentDirectory(true);
ASSERT_TRUE(m_configFile.Init()); // load colobot.ini file
ASSERT_TRUE(m_configFile.Init()); // load colobot.json file
std::string result;
ASSERT_TRUE(m_configFile.GetStringProperty("test_string", "string_value", result));
@ -57,7 +57,7 @@ TEST_F(CConfigFileTest, ReadArrayTest)
{
m_configFile.SetUseCurrentDirectory(true);
ASSERT_TRUE(m_configFile.Init()); // load colobot.ini file
ASSERT_TRUE(m_configFile.Init()); // load colobot.json file
std::vector<std::string> expected_string_values = { "AAA", "Hello world", "Gold Edition" };
std::vector<std::string> string_values;