mirror of
https://github.com/SysRay/psOff_public.git
synced 2024-11-23 22:39:40 +00:00
235 lines
8.5 KiB
C++
235 lines
8.5 KiB
C++
#define __APICALL_EXTERN
|
|
#include "config_emu.h"
|
|
#undef __APICALL_EXTERN
|
|
|
|
#include <fstream>
|
|
#include <future>
|
|
|
|
namespace {
|
|
class Item {
|
|
public:
|
|
std::string_view const _name;
|
|
|
|
bool _dontfix; // If this flag is set then load function won't try fix the file according to default object
|
|
json _json;
|
|
boost::mutex _mutex;
|
|
std::future<void> _future;
|
|
|
|
Item(std::string_view name, bool df = false): _name(name), _dontfix(df) {}
|
|
|
|
std::pair<boost::unique_lock<boost::mutex>, json*> access() {
|
|
_future.wait();
|
|
return std::make_pair(boost::unique_lock(_mutex), &_json);
|
|
}
|
|
|
|
bool save() {
|
|
auto path = std::string("./config/") + _name.data();
|
|
try {
|
|
std::ofstream json_file(path);
|
|
json_file << std::setw(2) << _json;
|
|
return true;
|
|
} catch (const json::exception& e) {
|
|
printf("Failed to save %s: %s\n", _name.data(), e.what());
|
|
return false;
|
|
}
|
|
};
|
|
};
|
|
} // namespace
|
|
|
|
class Config: public IConfig {
|
|
|
|
Item m_logging = {"logging.json"};
|
|
Item m_graphics = {"graphics.json"};
|
|
Item m_audio = {"audio.json"};
|
|
Item m_controls = {"controls.json"};
|
|
Item m_general = {"general.json"};
|
|
Item m_resolve = {"resolve.json", true /*dontfix flag*/};
|
|
|
|
public:
|
|
Config();
|
|
virtual ~Config() = default;
|
|
|
|
std::pair<boost::unique_lock<boost::mutex>, json*> accessModule(ConfigModFlag flag);
|
|
|
|
virtual bool save(uint32_t flags) final;
|
|
};
|
|
|
|
IConfig* accessConfig() {
|
|
static Config obj;
|
|
return &obj;
|
|
}
|
|
|
|
std::pair<boost::unique_lock<boost::mutex>, json*> Config::accessModule(ConfigModFlag flag) {
|
|
switch (flag) {
|
|
case ConfigModFlag::LOGGING: return m_logging.access();
|
|
case ConfigModFlag::GRAPHICS: return m_graphics.access();
|
|
case ConfigModFlag::AUDIO: return m_audio.access();
|
|
case ConfigModFlag::CONTROLS: return m_controls.access();
|
|
case ConfigModFlag::GENERAL: return m_general.access();
|
|
case ConfigModFlag::RESOLVE: return m_resolve.access();
|
|
|
|
default: printf("Invalid bit flag!\n"); exit(1);
|
|
}
|
|
}
|
|
|
|
bool Config::save(uint32_t flags) {
|
|
|
|
bool result = true;
|
|
|
|
if (!std::filesystem::is_directory("./config/")) std::filesystem::create_directory("./config/");
|
|
if (flags & (uint32_t)ConfigModFlag::LOGGING) result &= m_logging.save();
|
|
if (flags & (uint32_t)ConfigModFlag::GRAPHICS) result &= m_graphics.save();
|
|
if (flags & (uint32_t)ConfigModFlag::AUDIO) result &= m_audio.save();
|
|
if (flags & (uint32_t)ConfigModFlag::CONTROLS) result &= m_controls.save();
|
|
if (flags & (uint32_t)ConfigModFlag::GENERAL) result &= m_general.save();
|
|
if (flags & (uint32_t)ConfigModFlag::RESOLVE) result &= m_resolve.save();
|
|
|
|
return true;
|
|
}
|
|
|
|
Config::Config() {
|
|
auto load = [this](Item* item, json defaults = {}, ConfigModFlag dflag = ConfigModFlag::NONE) {
|
|
auto path = std::string("./config/") + item->_name.data();
|
|
bool should_resave = false, should_backup = false;
|
|
|
|
try {
|
|
std::ifstream json_file(path);
|
|
item->_json = json::parse(json_file, nullptr, true, true);
|
|
|
|
if ((item->_json).type() == defaults.type()) {
|
|
printf("Config %s loaded successfully\n", item->_name.data());
|
|
} else {
|
|
should_backup = true;
|
|
should_resave = true;
|
|
}
|
|
} catch (const json::exception& e) {
|
|
if ((std::filesystem::exists(path))) printf("Failed to parse %s: %s\n", item->_name.data(), e.what());
|
|
|
|
item->_json = defaults;
|
|
should_resave = true;
|
|
should_backup = true;
|
|
}
|
|
|
|
std::function<bool(json&, json&)> fixMissing;
|
|
std::function<bool(json&, json&)> removeUnused;
|
|
|
|
/**
|
|
* @brief This function is necessary since json.items()
|
|
* translates every key to string. Even the array indexes,
|
|
* so we have to translate it back to number for arrays.
|
|
*
|
|
*/
|
|
auto getVal = [](json& _o, std::string_view key) -> json& {
|
|
if (_o.is_array()) {
|
|
int _tempi;
|
|
auto res = std::from_chars(key.data(), key.data() + key.size(), _tempi);
|
|
if (res.ec == std::errc::invalid_argument) {
|
|
printf("Unreachable scenario!\n"); // At least it should be
|
|
exit(1);
|
|
}
|
|
return _o[_tempi];
|
|
}
|
|
|
|
return _o[key];
|
|
};
|
|
|
|
fixMissing = [&should_backup, &getVal, &fixMissing](json& obj, json& def) -> bool {
|
|
bool missing = false;
|
|
|
|
for (auto& [dkey, dval]: def.items()) {
|
|
json& cval = getVal(obj, dkey);
|
|
|
|
if (cval.is_null() && !dval.is_null()) {
|
|
cval = dval;
|
|
missing = true;
|
|
} else if (dval.is_structured()) {
|
|
if (cval.is_structured()) { // Function calls itself if json element is array or object
|
|
missing |= fixMissing(cval, dval);
|
|
continue;
|
|
}
|
|
|
|
should_backup = true;
|
|
cval = dval;
|
|
missing = true;
|
|
}
|
|
}
|
|
|
|
return missing;
|
|
};
|
|
|
|
if (!item->_dontfix && fixMissing(item->_json, defaults)) {
|
|
should_resave = true;
|
|
printf("%s: some missing parameters has been added!\n", item->_name.data());
|
|
}
|
|
|
|
// Just the same thing as above, but for removing unused keys this time
|
|
removeUnused = [&getVal, &removeUnused](json& obj, json& def) -> bool {
|
|
bool unused = false;
|
|
|
|
for (auto& [ckey, cval]: obj.items()) {
|
|
json& dval = getVal(def, ckey);
|
|
|
|
if (dval.is_null()) {
|
|
obj.erase(ckey);
|
|
unused = true;
|
|
} else if (dval.is_structured()) {
|
|
unused |= removeUnused(cval, dval);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return unused;
|
|
};
|
|
|
|
if (!item->_dontfix && removeUnused(item->_json, defaults)) {
|
|
should_backup = true;
|
|
should_resave = true;
|
|
printf("%s: some unused parameters has been removed!\n", item->_name.data());
|
|
}
|
|
|
|
if (should_backup == true && std::filesystem::exists(path)) {
|
|
try {
|
|
std::filesystem::path newp(path);
|
|
newp.replace_extension(".back");
|
|
std::filesystem::rename(path, newp);
|
|
} catch (const std::filesystem::filesystem_error& e) {
|
|
printf("%s: failed to create a backup: %s", path.c_str(), e.what());
|
|
}
|
|
}
|
|
|
|
if (should_resave && dflag != ConfigModFlag::NONE) this->save((uint32_t)dflag);
|
|
};
|
|
|
|
const json defaultpad = {{"type", "gamepad"}, {"deadzones", {{"left_stick", {{"x", 0.0f}, {"y", 0.0f}}}, {"right_stick", {{"x", 0.0f}, {"y", 0.0f}}}}}};
|
|
const json defaultprof = {{"name", "Anon"}, {"color", "blue"}};
|
|
|
|
m_logging._future =
|
|
std::async(std::launch::async | std::launch::deferred, load, &m_logging, json({{"sink", "FileBin"}, {"verbosity", 1}}), ConfigModFlag::LOGGING);
|
|
|
|
m_graphics._future =
|
|
std::async(std::launch::async | std::launch::deferred, load, &m_graphics,
|
|
json({{"fullscreen", false}, {"xpos", -1}, {"ypos", -1}, {"width", 1920}, {"height", 1080}, {"display", 0}}), ConfigModFlag::GRAPHICS);
|
|
|
|
m_audio._future =
|
|
std::async(std::launch::async | std::launch::deferred, load, &m_audio, json({{"volume", 0.5f}, {"device", "[default]"}}), ConfigModFlag::AUDIO);
|
|
|
|
m_controls._future = std::async(std::launch::async | std::launch::deferred, load, &m_controls,
|
|
json({{"pads", json::array({defaultpad, defaultpad, defaultpad, defaultpad})},
|
|
{"keybinds",
|
|
{
|
|
{"triangle", "i"}, {"square", "j"}, {"circle", "l"}, {"cross", "k"},
|
|
{"dpad_up", "up"}, {"dpad_down", "down"}, {"dpad_left", "left"}, {"dpad_right", "right"},
|
|
{"options", "f1"}, {"touchpad", "f4"}, {"l1", "f3"}, {"l2", "f5"},
|
|
{"l3", "space"}, {"r1", "f2"}, {"r2", "f6"}, {"r3", "home"},
|
|
{"lx-", "a"}, {"lx+", "d"}, {"ly-", "w"}, {"ly+", "s"},
|
|
{"rx-", "h"}, {"rx+", "f"}, {"ry-", "t"}, {"ry+", "g"},
|
|
}}}),
|
|
ConfigModFlag::CONTROLS);
|
|
|
|
m_general._future = std::async(std::launch::async, load, &m_general,
|
|
json({{"systemlang", 1}, {"userIndex", 1}, {"profiles", json::array({defaultprof, defaultprof, defaultprof, defaultprof})}}),
|
|
ConfigModFlag::GENERAL);
|
|
|
|
m_resolve._future = std::async(std::launch::async | std::launch::deferred, load, &m_resolve, json({{"localhost", "127.0.0.1"}}), ConfigModFlag::RESOLVE);
|
|
}
|