spacebox/src/Configuration.cpp

261 lines
7.2 KiB
C++

/* +------------------------------------------------------+
____/ \____ /| - Open source game framework licensed to freely use, |
\ / / | copy, modify and sell without restriction |
+--\ ^__^ /--+ | |
| ~/ \~ | | - created for <https://foam.shampoo.ooo> |
| ~~~~~~~~~~~~ | +------------------------------------------------------+
| SPACE ~~~~~ | /
| ~~~~~~~ BOX |/
+-------------*/
#include "Configuration.hpp"
Configuration::Configuration()
{
set_defaults();
}
void Configuration::set_defaults()
{
config["keys"] = {
{"record", {"CTRL", "SHIFT", "i"}},
{"save current stash", {"CTRL", "SHIFT", "v"}},
{"screenshot", {"CTRL", "i"}},
{"action", "space"},
{"up", "up"},
{"right", "right"},
{"down", "down"},
{"left", "left"},
{"fullscreen", {"ALT", "enter"}},
{"reset", {"CTRL", "r"}}
};
config["input"] = {
{"suppress any key on mods", true},
{"system any key ignore commands", {"fullscreen", "screenshot", "record", "quit"}},
{"any key ignore commands", nlohmann::json::array()},
{"default-unsuppress-delay", 0.7},
{"ignore repeat keypress", true}
};
config["display"] = {
{"dimensions", {960, 540}},
{"max framerate", -1},
{"vsync", false},
{"sdl delay", 6},
{"title", "[SPACEBOX]"},
{"debug", false},
{"show cursor", false},
{"render-test-spacing", 2},
{"render driver", "opengl"},
{"fluid resize", false},
{"default font path", "BPmono.ttf"},
{"default font size", 16},
{"use play button", false},
{"fullscreen", false},
{"fullscreen enabled", true}
};
config["audio"] = {
{"default-sfx-root", "resource/sfx"},
{"default-bgm-root", "resource/bgm"},
{"frequency", 48000},
{"chunk size", 2048}
};
config["gl"] = {
{"depth size", 16},
{"red size", 8},
{"green size", 8},
{"blue size", 8},
{"share with current context", true},
{"double buffer", true},
{"major version", 3},
{"minor version", 2}
},
config["recording"] = {
{"enabled", false},
{"video frame length", 1.0f / 60.0f},
{"screenshot prefix", "screenshot-"},
{"screenshot extension", ".png"},
{"screenshot zfill", 5},
{"screenshot directory", "."},
{"gif frame length", 0.1f},
{"video directory", "."},
{"write mp4", false},
{"max stash length", 5.0f},
{"max in game stashes", 3},
{"max video stashes", 40},
{"max video memory", 1000},
{"mp4 pixel format", "yuv444p"}
};
config["animation"] = {
{"all frames frameset name", "all"}
};
config["log"] = {
{"enabled", false},
{"debug-to-stdout", true},
{"verbose to stdout", false},
{"debug-to-file", false},
{"output-directory", "."},
{"info-file-name", "space_box_log.txt"},
{"debug-file-name", "space_box_debug_log.txt"},
{"short-name", "spacebox"}
};
config["configuration"] = {
{"auto refresh", false},
{"auto refresh interval", 5.0},
{"android config path", "config_android.json"},
{"wasm config path", "config_wasm.json"}
};
}
nlohmann::json& Configuration::operator[](const std::string& key)
{
return config[key];
}
const nlohmann::json& Configuration::operator[](const std::string& key) const
{
return config[key];
}
const nlohmann::json& Configuration::operator()() const
{
return config;
}
void Configuration::merge(const nlohmann::json& incoming)
{
if (!incoming.empty())
{
/* loop over first level key/value pairs */
for (auto& item: incoming.items())
{
/* if the value is an object (dict), merge it into the config, overwriting keys already in the config */
if (item.value().is_object())
{
config[item.key()].update(item.value());
}
/* otherwise just assign config key to this value */
else
{
config[item.key()] = item.value();
}
}
}
else
{
sb::Log::log("Attempted to merge empty JSON into configuration", sb::Log::WARN);
}
}
void Configuration::merge(const fs::path& path)
{
merge(json_from_file(path));
}
void Configuration::merge(const std::string& path)
{
merge(fs::path(path));
}
void Configuration::merge(const char* path)
{
merge(fs::path(path));
}
nlohmann::json Configuration::json_from_file(const fs::path& path)
{
nlohmann::json json;
#ifndef __ANDROID__
/* Can't check for file existence in an Android APK */
if (fs::exists(path))
{
#endif
/* Load JSON to a string and check for validity. */
std::string contents = sb::file_to_string(path);
if (nlohmann::json::accept(contents))
{
json = nlohmann::json::parse(contents);
}
else
{
std::ostringstream message;
message << "Invalid JSON at " << path;
sb::Log::log(message, sb::Log::WARN);
}
#ifndef __ANDROID__
}
else
{
std::ostringstream message;
message << "File not found: " << path;
sb::Log::log(message, sb::Log::WARN);
}
#endif
return json;
}
nlohmann::json Configuration::json_from_file(const std::string& path)
{
return json_from_file(fs::path(path));
}
nlohmann::json Configuration::json_from_file(const char* path)
{
return json_from_file(fs::path(path));
}
void Configuration::enable_auto_refresh(const fs::path& file_to_refresh)
{
#ifndef __ANDROID__
/* Warn user if the file does not exist */
if (!fs::exists(file_to_refresh))
{
std::ostringstream message;
message << "File to auto-refresh does not exist: " << file_to_refresh;
sb::Log::log(message, sb::Log::WARN);
}
#endif
files_to_refresh.push_back(file_to_refresh);
auto_refresher.frame_length(config.at("configuration").at("auto refresh interval").get<float>());
auto_refresher.play();
}
void Configuration::disable_auto_refresh()
{
auto_refresher.pause();
}
void Configuration::refresh()
{
#if !defined(__ANDROID__)
for (const fs::path& path : files_to_refresh)
{
if (fs::exists(path) && fs::last_write_time(path) > config_modification_time)
{
std::ostringstream message;
message << "config file modified, reloading " << path;
sb::Log::log(message, sb::Log::DEBUG);
merge(path);
config_modification_time = fs::file_time_type::clock::now();
sb::Delegate::post("reconfig");
}
}
#else
/* Warn user file modification check doesn't work on Android */
sb::Log::log("File modification time can't be checked on Android, so file cannot be reloaded automatically", sb::Log::WARN);
#endif
}
void Configuration::update(float timestamp)
{
auto_refresher.update(timestamp);
}
std::ostream& std::operator<<(std::ostream& out, const Configuration& configuration)
{
out << configuration();
return out;
}