diff options
Diffstat (limited to 'src/client/settings.cc')
| -rw-r--r-- | src/client/settings.cc | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/src/client/settings.cc b/src/client/settings.cc new file mode 100644 index 0000000..8e2d0c9 --- /dev/null +++ b/src/client/settings.cc @@ -0,0 +1,139 @@ +#include "client/settings.hh" + +namespace { +const std::string home_dir = getenv("HOME"); +const char* const config_dir = "/.config/blockgame_linux/"; +const char* const config_name = "blockgame_linux.conf"; +} // namespace + +namespace client { +namespace settings { + +static std::string get_settings_file() noexcept { + std::filesystem::create_directories(home_dir + ::config_dir); + std::ifstream in{home_dir + ::config_dir + config_name, + std::fstream::in | std::fstream::app}; + + // this must end with a newline, IT IS NOT UP FOR NEGOTIATION + const std::string file{std::istreambuf_iterator<char>(in), + std::istreambuf_iterator<char>()}; + return file.ends_with('\n') ? file : file + '\n'; +} + +static void set_settings_file(const std::string_view contents) noexcept { + std::filesystem::create_directories(home_dir + ::config_dir); + std::ofstream out{home_dir + ::config_dir + ::config_name, + std::fstream::out | std::fstream::trunc}; + out << contents; +} + +// Settings maps subkeys to std::string values (ie, "fov" -> "100.0"f). +// Heading maps setting to headings (ie, "camera -> {map with fov, 100.0f}). +using value_map = std::unordered_map<std::string, std::string>; +using setting_map = std::unordered_map<std::string, value_map>; +static setting_map& get_settings() noexcept { + static setting_map ret = []() -> setting_map { + setting_map settings{}; + + std::stringstream ss{get_settings_file()}; + std::string heading = "default"; + for (std::string line; std::getline(ss, line);) { + if (line.empty()) { + continue; + } + + if (line.starts_with('[') && line.ends_with(']')) { + heading = std::string{std::begin(line) + 1, std::end(line) - 1}; + continue; + } + + const size_t split_pos = line.find_first_of(':'); + + if (split_pos == std::string::npos) { + shared::print::warn("client: failed to parse line in settings " + "file, consider manual intervention\n"); + continue; + } + + const std::string key{std::begin(line), + std::begin(line) + + static_cast<long>(split_pos)}; + const std::string value{std::begin(line) + + static_cast<long>(split_pos) + 1, + std::end(line)}; + + value_map& values = settings[heading]; + + values.emplace(key, value); + } + return settings; + }(); + + return ret; +} + +// TODO fix this bit of code duplication +std::string +get_setting_str(const std::pair<const std::string&, const std::string&> loc, + const std::string default_value) noexcept { + const auto& [heading, name] = loc; + + setting_map& settings = get_settings(); + value_map& values = settings[heading]; + + const auto find_it = values.find(name); + if (find_it == std::end(values)) { + values.emplace(name, default_value); + return default_value; + } + return find_it->second; +} + +void set_setting_str( + const std::pair<const std::string&, const std::string&> loc, + const std::string& value) noexcept { + const auto& [heading, name] = loc; + + setting_map& settings = get_settings(); + value_map& values = settings[heading]; + + const auto find_it = values.find(name); + if (find_it == std::end(values)) { + values.emplace(name, value); + return; + } + find_it->second = value; +} + +void save() noexcept { + std::string contents; + + const setting_map& settings = get_settings(); + + // We alphabetically order the headings, and the key:values between them. + std::vector<std::string> headings; + std::ranges::transform(std::begin(settings), std::end(settings), + std::back_inserter(headings), + [](const auto& map) { return map.first; }); + std::ranges::sort(headings); + + for (const auto& heading : headings) { + contents.append('[' + heading + "]\n"); + + const value_map& values = settings.find(heading)->second; + std::vector<std::string> keys; + std::ranges::transform( + std::begin(values), std::end(values), std::back_inserter(keys), + [](const auto& key_val) { return key_val.first; }); + std::ranges::sort(keys); + + for (const auto& key : keys) { + contents.append(key + ':' + values.find(key)->second + '\n'); + } + } + + set_settings_file(contents); +} + +} // namespace settings +} // namespace client |
