/* +------------------------------------------------------+ ____/ \____ /| - Open source game framework licensed to freely use, | \ / / | copy, modify and sell without restriction | +--\ ^__^ /--+ | | | ~/ \~ | | - created for | | ~~~~~~~~~~~~ | +------------------------------------------------------+ | SPACE ~~~~~ | / | ~~~~~~~ BOX |/ +-------------*/ #include "Configuration.hpp" Configuration::Configuration(Node* parent) : Node(parent) { 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"}, {"pause", "enter"}, {"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} }; config["audio"] = { {"default-sfx-root", "resource/sfx"}, {"default-bgm-root", "resource/bgm"} }; 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", 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} }; } 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) { #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)) { merge(nlohmann::json::parse(contents)); } else { std::ostringstream message; message << "Invalid JSON at " << path; sb::Log::log(message, sb::Log::WARN); } #ifndef __ANDROID__ config_file_modification_time = fs::last_write_time(path); } else { std::ostringstream message; message << "File not found: " << path; sb::Log::log(message, sb::Log::WARN); } #endif } void Configuration::merge(const std::string& path) { merge(fs::path(path)); } void Configuration::merge(const char* path) { merge(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["configuration"]["auto-refresh-interval"].get()); 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_file_modification_time) { std::ostringstream message; message << "config file modified, reloading " << path; sb::Log::log(message, sb::Log::DEBUG); merge(path); 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; }