| | |
| | |
| |
|
| | #include <algorithm> |
| | #include <array> |
| | #include "common/fs/fs.h" |
| | #include "common/fs/path_util.h" |
| | #include "common/logging/log.h" |
| | #include "common/settings.h" |
| | #include "common/settings_common.h" |
| | #include "common/settings_enums.h" |
| | #include "config.h" |
| | #include "core/core.h" |
| | #include "core/hle/service/acc/profile_manager.h" |
| | #include "hid_core/resources/npad/npad.h" |
| | #include "network/network.h" |
| |
|
| | #include <boost/algorithm/string/replace.hpp> |
| |
|
| | #include "common/string_util.h" |
| |
|
| | namespace FS = Common::FS; |
| |
|
| | Config::Config(const ConfigType config_type) |
| | : type(config_type), global{config_type == ConfigType::GlobalConfig} {} |
| |
|
| | void Config::Initialize(const std::string& config_name) { |
| | const std::filesystem::path fs_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir); |
| | const auto config_file = fmt::format("{}.ini", config_name); |
| |
|
| | switch (type) { |
| | case ConfigType::GlobalConfig: |
| | config_loc = FS::PathToUTF8String(fs_config_loc / config_file); |
| | void(FS::CreateParentDir(config_loc)); |
| | SetUpIni(); |
| | Reload(); |
| | break; |
| | case ConfigType::PerGameConfig: |
| | config_loc = FS::PathToUTF8String(fs_config_loc / "custom" / FS::ToU8String(config_file)); |
| | void(FS::CreateParentDir(config_loc)); |
| | SetUpIni(); |
| | Reload(); |
| | break; |
| | case ConfigType::InputProfile: |
| | config_loc = FS::PathToUTF8String(fs_config_loc / "input" / config_file); |
| | void(FS::CreateParentDir(config_loc)); |
| | SetUpIni(); |
| | break; |
| | } |
| | } |
| |
|
| | void Config::Initialize(const std::optional<std::string> config_path) { |
| | const std::filesystem::path default_sdl_config_path = |
| | FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "sdl2-config.ini"; |
| | config_loc = config_path.value_or(FS::PathToUTF8String(default_sdl_config_path)); |
| | void(FS::CreateParentDir(config_loc)); |
| | SetUpIni(); |
| | Reload(); |
| | } |
| |
|
| | void Config::WriteToIni() const { |
| | std::string config_type; |
| | switch (type) { |
| | case ConfigType::GlobalConfig: |
| | config_type = "Global"; |
| | break; |
| | case ConfigType::PerGameConfig: |
| | config_type = "Game Specific"; |
| | break; |
| | case ConfigType::InputProfile: |
| | config_type = "Input Profile"; |
| | break; |
| | } |
| | LOG_INFO(Config, "Writing {} configuration to: {}", config_type, config_loc); |
| | FILE* fp = nullptr; |
| | #ifdef _WIN32 |
| | fp = _wfopen(Common::UTF8ToUTF16W(config_loc).data(), L"wb"); |
| | #else |
| | fp = fopen(config_loc.c_str(), "wb"); |
| | #endif |
| |
|
| | if (fp == nullptr) { |
| | LOG_ERROR(Frontend, "Config file could not be saved!"); |
| | return; |
| | } |
| |
|
| | CSimpleIniA::FileWriter writer(fp); |
| | const SI_Error rc = config->Save(writer, false); |
| | if (rc < 0) { |
| | LOG_ERROR(Frontend, "Config file could not be saved!"); |
| | } |
| | fclose(fp); |
| | } |
| |
|
| | void Config::SetUpIni() { |
| | config = std::make_unique<CSimpleIniA>(); |
| | config->SetUnicode(true); |
| | config->SetSpaces(false); |
| |
|
| | FILE* fp = nullptr; |
| | #ifdef _WIN32 |
| | _wfopen_s(&fp, Common::UTF8ToUTF16W(config_loc).data(), L"rb, ccs=UTF-8"); |
| | if (fp == nullptr) { |
| | fp = _wfopen(Common::UTF8ToUTF16W(config_loc).data(), L"wb, ccs=UTF-8"); |
| | } |
| | #else |
| | fp = fopen(config_loc.c_str(), "rb"); |
| | if (fp == nullptr) { |
| | fp = fopen(config_loc.c_str(), "wb"); |
| | } |
| | #endif |
| |
|
| | if (fp == nullptr) { |
| | LOG_ERROR(Frontend, "Config file could not be loaded!"); |
| | return; |
| | } |
| |
|
| | if (SI_Error rc = config->LoadFile(fp); rc < 0) { |
| | LOG_ERROR(Frontend, "Config file could not be loaded!"); |
| | } |
| | fclose(fp); |
| | } |
| |
|
| | bool Config::IsCustomConfig() const { |
| | return type == ConfigType::PerGameConfig; |
| | } |
| |
|
| | void Config::ReadPlayerValues(const std::size_t player_index) { |
| | std::string player_prefix; |
| | if (type != ConfigType::InputProfile) { |
| | player_prefix.append("player_").append(ToString(player_index)).append("_"); |
| | } |
| |
|
| | const auto profile_name = ReadStringSetting(std::string(player_prefix).append("profile_name")); |
| |
|
| | auto& player = Settings::values.players.GetValue()[player_index]; |
| | if (IsCustomConfig()) { |
| | if (profile_name.empty()) { |
| | |
| | player = Settings::values.players.GetValue(true)[player_index]; |
| | player.profile_name = ""; |
| | return; |
| | } |
| | player.profile_name = profile_name; |
| | } |
| |
|
| | if (player_prefix.empty() && Settings::IsConfiguringGlobal()) { |
| | const auto controller = static_cast<Settings::ControllerType>( |
| | ReadIntegerSetting(std::string(player_prefix).append("type"), |
| | static_cast<u8>(Settings::ControllerType::ProController))); |
| |
|
| | if (controller == Settings::ControllerType::LeftJoycon || |
| | controller == Settings::ControllerType::RightJoycon) { |
| | player.controller_type = controller; |
| | } |
| | } else { |
| | if (global) { |
| | auto& player_global = Settings::values.players.GetValue(true)[player_index]; |
| | player_global.profile_name = profile_name; |
| | } |
| | std::string connected_key = player_prefix; |
| | player.connected = ReadBooleanSetting(connected_key.append("connected"), |
| | std::make_optional(player_index == 0)); |
| |
|
| | player.controller_type = static_cast<Settings::ControllerType>( |
| | ReadIntegerSetting(std::string(player_prefix).append("type"), |
| | static_cast<u8>(Settings::ControllerType::ProController))); |
| |
|
| | player.vibration_enabled = ReadBooleanSetting( |
| | std::string(player_prefix).append("vibration_enabled"), std::make_optional(true)); |
| |
|
| | player.vibration_strength = static_cast<int>( |
| | ReadIntegerSetting(std::string(player_prefix).append("vibration_strength"), 100)); |
| |
|
| | player.body_color_left = static_cast<u32>(ReadIntegerSetting( |
| | std::string(player_prefix).append("body_color_left"), Settings::JOYCON_BODY_NEON_BLUE)); |
| | player.body_color_right = static_cast<u32>(ReadIntegerSetting( |
| | std::string(player_prefix).append("body_color_right"), Settings::JOYCON_BODY_NEON_RED)); |
| | player.button_color_left = static_cast<u32>( |
| | ReadIntegerSetting(std::string(player_prefix).append("button_color_left"), |
| | Settings::JOYCON_BUTTONS_NEON_BLUE)); |
| | player.button_color_right = static_cast<u32>( |
| | ReadIntegerSetting(std::string(player_prefix).append("button_color_right"), |
| | Settings::JOYCON_BUTTONS_NEON_RED)); |
| | } |
| | } |
| |
|
| | void Config::ReadTouchscreenValues() { |
| | Settings::values.touchscreen.enabled = |
| | ReadBooleanSetting(std::string("touchscreen_enabled"), std::make_optional(true)); |
| | Settings::values.touchscreen.rotation_angle = |
| | static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_angle"), 0)); |
| | Settings::values.touchscreen.diameter_x = |
| | static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_diameter_x"), 90)); |
| | Settings::values.touchscreen.diameter_y = |
| | static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_diameter_y"), 90)); |
| | } |
| |
|
| | void Config::ReadAudioValues() { |
| | BeginGroup(Settings::TranslateCategory(Settings::Category::Audio)); |
| |
|
| | ReadCategory(Settings::Category::Audio); |
| | ReadCategory(Settings::Category::UiAudio); |
| |
|
| | EndGroup(); |
| | } |
| |
|
| | void Config::ReadControlValues() { |
| | BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); |
| |
|
| | ReadCategory(Settings::Category::Controls); |
| |
|
| | Settings::values.players.SetGlobal(!IsCustomConfig()); |
| | for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { |
| | ReadPlayerValues(p); |
| | } |
| |
|
| | |
| | const auto controller_type = Settings::values.players.GetValue()[0].controller_type; |
| | if (controller_type == Settings::ControllerType::Handheld) { |
| | Settings::values.use_docked_mode.SetGlobal(!IsCustomConfig()); |
| | Settings::values.use_docked_mode.SetValue(Settings::ConsoleMode::Handheld); |
| | } |
| |
|
| | if (IsCustomConfig()) { |
| | EndGroup(); |
| | return; |
| | } |
| | ReadTouchscreenValues(); |
| | ReadMotionTouchValues(); |
| |
|
| | EndGroup(); |
| | } |
| |
|
| | void Config::ReadMotionTouchValues() { |
| | Settings::values.touch_from_button_maps.clear(); |
| | int num_touch_from_button_maps = BeginArray(std::string("touch_from_button_maps")); |
| |
|
| | if (num_touch_from_button_maps > 0) { |
| | for (int i = 0; i < num_touch_from_button_maps; ++i) { |
| | SetArrayIndex(i); |
| |
|
| | Settings::TouchFromButtonMap map; |
| | map.name = ReadStringSetting(std::string("name"), std::string("default")); |
| |
|
| | const int num_touch_maps = BeginArray(std::string("entries")); |
| | map.buttons.reserve(num_touch_maps); |
| | for (int j = 0; j < num_touch_maps; j++) { |
| | SetArrayIndex(j); |
| | std::string touch_mapping = ReadStringSetting(std::string("bind")); |
| | map.buttons.emplace_back(std::move(touch_mapping)); |
| | } |
| | EndArray(); |
| | Settings::values.touch_from_button_maps.emplace_back(std::move(map)); |
| | } |
| | } else { |
| | Settings::values.touch_from_button_maps.emplace_back( |
| | Settings::TouchFromButtonMap{"default", {}}); |
| | num_touch_from_button_maps = 1; |
| | } |
| | EndArray(); |
| |
|
| | Settings::values.touch_from_button_map_index = std::clamp( |
| | Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1); |
| | } |
| |
|
| | void Config::ReadCoreValues() { |
| | BeginGroup(Settings::TranslateCategory(Settings::Category::Core)); |
| |
|
| | ReadCategory(Settings::Category::Core); |
| |
|
| | EndGroup(); |
| | } |
| |
|
| | void Config::ReadDataStorageValues() { |
| | BeginGroup(Settings::TranslateCategory(Settings::Category::DataStorage)); |
| |
|
| | FS::SetYuzuPath(FS::YuzuPath::NANDDir, ReadStringSetting(std::string("nand_directory"))); |
| | FS::SetYuzuPath(FS::YuzuPath::SDMCDir, ReadStringSetting(std::string("sdmc_directory"))); |
| | FS::SetYuzuPath(FS::YuzuPath::LoadDir, ReadStringSetting(std::string("load_directory"))); |
| | FS::SetYuzuPath(FS::YuzuPath::DumpDir, ReadStringSetting(std::string("dump_directory"))); |
| | FS::SetYuzuPath(FS::YuzuPath::TASDir, ReadStringSetting(std::string("tas_directory"))); |
| |
|
| | ReadCategory(Settings::Category::DataStorage); |
| |
|
| | EndGroup(); |
| | } |
| |
|
| | void Config::ReadDebuggingValues() { |
| | BeginGroup(Settings::TranslateCategory(Settings::Category::Debugging)); |
| |
|
| | |
| | Settings::values.record_frame_times = |
| | ReadBooleanSetting(std::string("record_frame_times"), std::make_optional(false)); |
| |
|
| | ReadCategory(Settings::Category::Debugging); |
| | ReadCategory(Settings::Category::DebuggingGraphics); |
| |
|
| | EndGroup(); |
| | } |
| |
|
| | #ifdef __unix__ |
| | void Config::ReadLinuxValues() { |
| | BeginGroup(Settings::TranslateCategory(Settings::Category::Linux)); |
| |
|
| | ReadCategory(Settings::Category::Linux); |
| |
|
| | EndGroup(); |
| | } |
| | #endif |
| |
|
| | void Config::ReadServiceValues() { |
| | BeginGroup(Settings::TranslateCategory(Settings::Category::Services)); |
| |
|
| | ReadCategory(Settings::Category::Services); |
| |
|
| | EndGroup(); |
| | } |
| |
|
| | void Config::ReadDisabledAddOnValues() { |
| | |
| | BeginGroup(std::string("DisabledAddOns")); |
| |
|
| | const int size = BeginArray(std::string("")); |
| | for (int i = 0; i < size; ++i) { |
| | SetArrayIndex(i); |
| | const auto title_id = ReadUnsignedIntegerSetting(std::string("title_id"), 0); |
| | std::vector<std::string> out; |
| | const int d_size = BeginArray("disabled"); |
| | for (int j = 0; j < d_size; ++j) { |
| | SetArrayIndex(j); |
| | out.push_back(ReadStringSetting(std::string("d"), std::string(""))); |
| | } |
| | EndArray(); |
| | Settings::values.disabled_addons.insert_or_assign(title_id, out); |
| | } |
| | EndArray(); |
| |
|
| | EndGroup(); |
| | } |
| |
|
| | void Config::ReadMiscellaneousValues() { |
| | BeginGroup(Settings::TranslateCategory(Settings::Category::Miscellaneous)); |
| |
|
| | ReadCategory(Settings::Category::Miscellaneous); |
| |
|
| | EndGroup(); |
| | } |
| |
|
| | void Config::ReadCpuValues() { |
| | BeginGroup(Settings::TranslateCategory(Settings::Category::Cpu)); |
| |
|
| | ReadCategory(Settings::Category::Cpu); |
| | ReadCategory(Settings::Category::CpuDebug); |
| | ReadCategory(Settings::Category::CpuUnsafe); |
| |
|
| | EndGroup(); |
| | } |
| |
|
| | void Config::ReadRendererValues() { |
| | BeginGroup(Settings::TranslateCategory(Settings::Category::Renderer)); |
| |
|
| | ReadCategory(Settings::Category::Renderer); |
| | ReadCategory(Settings::Category::RendererAdvanced); |
| | ReadCategory(Settings::Category::RendererDebug); |
| |
|
| | EndGroup(); |
| | } |
| |
|
| | void Config::ReadScreenshotValues() { |
| | BeginGroup(Settings::TranslateCategory(Settings::Category::Screenshots)); |
| |
|
| | ReadCategory(Settings::Category::Screenshots); |
| | FS::SetYuzuPath(FS::YuzuPath::ScreenshotsDir, |
| | ReadStringSetting(std::string("screenshot_path"))); |
| |
|
| | EndGroup(); |
| | } |
| |
|
| | void Config::ReadSystemValues() { |
| | BeginGroup(Settings::TranslateCategory(Settings::Category::System)); |
| |
|
| | ReadCategory(Settings::Category::System); |
| | ReadCategory(Settings::Category::SystemAudio); |
| |
|
| | EndGroup(); |
| | } |
| |
|
| | void Config::ReadWebServiceValues() { |
| | BeginGroup(Settings::TranslateCategory(Settings::Category::WebService)); |
| |
|
| | ReadCategory(Settings::Category::WebService); |
| |
|
| | EndGroup(); |
| | } |
| |
|
| | void Config::ReadNetworkValues() { |
| | BeginGroup(Settings::TranslateCategory(Settings::Category::Services)); |
| |
|
| | ReadCategory(Settings::Category::Network); |
| |
|
| | EndGroup(); |
| | } |
| |
|
| | void Config::ReadLibraryAppletValues() { |
| | BeginGroup(Settings::TranslateCategory(Settings::Category::LibraryApplet)); |
| |
|
| | ReadCategory(Settings::Category::LibraryApplet); |
| |
|
| | EndGroup(); |
| | } |
| |
|
| | void Config::ReadValues() { |
| | if (global) { |
| | ReadDataStorageValues(); |
| | ReadDebuggingValues(); |
| | ReadDisabledAddOnValues(); |
| | ReadNetworkValues(); |
| | ReadServiceValues(); |
| | ReadWebServiceValues(); |
| | ReadMiscellaneousValues(); |
| | ReadLibraryAppletValues(); |
| | } |
| | ReadControlValues(); |
| | ReadCoreValues(); |
| | ReadCpuValues(); |
| | #ifdef __unix__ |
| | ReadLinuxValues(); |
| | #endif |
| | ReadRendererValues(); |
| | ReadAudioValues(); |
| | ReadSystemValues(); |
| | } |
| |
|
| | void Config::SavePlayerValues(const std::size_t player_index) { |
| | std::string player_prefix; |
| | if (type != ConfigType::InputProfile) { |
| | player_prefix = std::string("player_").append(ToString(player_index)).append("_"); |
| | } |
| |
|
| | const auto& player = Settings::values.players.GetValue()[player_index]; |
| | if (IsCustomConfig()) { |
| | if (player.profile_name.empty()) { |
| | |
| | return; |
| | } |
| | WriteStringSetting(std::string(player_prefix).append("profile_name"), player.profile_name, |
| | std::make_optional(std::string(""))); |
| | } |
| |
|
| | WriteIntegerSetting( |
| | std::string(player_prefix).append("type"), static_cast<u8>(player.controller_type), |
| | std::make_optional(static_cast<u8>(Settings::ControllerType::ProController))); |
| |
|
| | if (!player_prefix.empty() || !Settings::IsConfiguringGlobal()) { |
| | if (global) { |
| | const auto& player_global = Settings::values.players.GetValue(true)[player_index]; |
| | WriteStringSetting(std::string(player_prefix).append("profile_name"), |
| | player_global.profile_name, std::make_optional(std::string(""))); |
| | } |
| | WriteBooleanSetting(std::string(player_prefix).append("connected"), player.connected, |
| | std::make_optional(player_index == 0)); |
| | WriteIntegerSetting(std::string(player_prefix).append("vibration_enabled"), |
| | player.vibration_enabled, std::make_optional(true)); |
| | WriteIntegerSetting(std::string(player_prefix).append("vibration_strength"), |
| | player.vibration_strength, std::make_optional(100)); |
| | WriteIntegerSetting(std::string(player_prefix).append("body_color_left"), |
| | player.body_color_left, |
| | std::make_optional(Settings::JOYCON_BODY_NEON_BLUE)); |
| | WriteIntegerSetting(std::string(player_prefix).append("body_color_right"), |
| | player.body_color_right, |
| | std::make_optional(Settings::JOYCON_BODY_NEON_RED)); |
| | WriteIntegerSetting(std::string(player_prefix).append("button_color_left"), |
| | player.button_color_left, |
| | std::make_optional(Settings::JOYCON_BUTTONS_NEON_BLUE)); |
| | WriteIntegerSetting(std::string(player_prefix).append("button_color_right"), |
| | player.button_color_right, |
| | std::make_optional(Settings::JOYCON_BUTTONS_NEON_RED)); |
| | } |
| | } |
| |
|
| | void Config::SaveTouchscreenValues() { |
| | const auto& touchscreen = Settings::values.touchscreen; |
| |
|
| | WriteBooleanSetting(std::string("touchscreen_enabled"), touchscreen.enabled, |
| | std::make_optional(true)); |
| |
|
| | WriteIntegerSetting(std::string("touchscreen_angle"), touchscreen.rotation_angle, |
| | std::make_optional(static_cast<u32>(0))); |
| | WriteIntegerSetting(std::string("touchscreen_diameter_x"), touchscreen.diameter_x, |
| | std::make_optional(static_cast<u32>(90))); |
| | WriteIntegerSetting(std::string("touchscreen_diameter_y"), touchscreen.diameter_y, |
| | std::make_optional(static_cast<u32>(90))); |
| | } |
| |
|
| | void Config::SaveMotionTouchValues() { |
| | BeginArray(std::string("touch_from_button_maps")); |
| | for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) { |
| | SetArrayIndex(static_cast<int>(p)); |
| | WriteStringSetting(std::string("name"), Settings::values.touch_from_button_maps[p].name, |
| | std::make_optional(std::string("default"))); |
| |
|
| | BeginArray(std::string("entries")); |
| | for (std::size_t q = 0; q < Settings::values.touch_from_button_maps[p].buttons.size(); |
| | ++q) { |
| | SetArrayIndex(static_cast<int>(q)); |
| | WriteStringSetting(std::string("bind"), |
| | Settings::values.touch_from_button_maps[p].buttons[q]); |
| | } |
| | EndArray(); |
| | } |
| | EndArray(); |
| | } |
| |
|
| | void Config::SaveValues() { |
| | if (global) { |
| | LOG_DEBUG(Config, "Saving global generic configuration values"); |
| | SaveDataStorageValues(); |
| | SaveDebuggingValues(); |
| | SaveDisabledAddOnValues(); |
| | SaveNetworkValues(); |
| | SaveWebServiceValues(); |
| | SaveMiscellaneousValues(); |
| | SaveLibraryAppletValues(); |
| | } else { |
| | LOG_DEBUG(Config, "Saving only generic configuration values"); |
| | } |
| | SaveControlValues(); |
| | SaveCoreValues(); |
| | SaveCpuValues(); |
| | #ifdef __unix__ |
| | SaveLinuxValues(); |
| | #endif |
| | SaveRendererValues(); |
| | SaveAudioValues(); |
| | SaveSystemValues(); |
| |
|
| | WriteToIni(); |
| | } |
| |
|
| | void Config::SaveAudioValues() { |
| | BeginGroup(Settings::TranslateCategory(Settings::Category::Audio)); |
| |
|
| | WriteCategory(Settings::Category::Audio); |
| | WriteCategory(Settings::Category::UiAudio); |
| |
|
| | EndGroup(); |
| | } |
| |
|
| | void Config::SaveControlValues() { |
| | BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); |
| |
|
| | WriteCategory(Settings::Category::Controls); |
| |
|
| | Settings::values.players.SetGlobal(!IsCustomConfig()); |
| | for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { |
| | SavePlayerValues(p); |
| | } |
| | if (IsCustomConfig()) { |
| | EndGroup(); |
| | return; |
| | } |
| | SaveTouchscreenValues(); |
| | SaveMotionTouchValues(); |
| |
|
| | EndGroup(); |
| | } |
| |
|
| | void Config::SaveCoreValues() { |
| | BeginGroup(Settings::TranslateCategory(Settings::Category::Core)); |
| |
|
| | WriteCategory(Settings::Category::Core); |
| |
|
| | EndGroup(); |
| | } |
| |
|
| | void Config::SaveDataStorageValues() { |
| | BeginGroup(Settings::TranslateCategory(Settings::Category::DataStorage)); |
| |
|
| | WriteStringSetting(std::string("nand_directory"), FS::GetYuzuPathString(FS::YuzuPath::NANDDir), |
| | std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::NANDDir))); |
| | WriteStringSetting(std::string("sdmc_directory"), FS::GetYuzuPathString(FS::YuzuPath::SDMCDir), |
| | std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir))); |
| | WriteStringSetting(std::string("load_directory"), FS::GetYuzuPathString(FS::YuzuPath::LoadDir), |
| | std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::LoadDir))); |
| | WriteStringSetting(std::string("dump_directory"), FS::GetYuzuPathString(FS::YuzuPath::DumpDir), |
| | std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::DumpDir))); |
| | WriteStringSetting(std::string("tas_directory"), FS::GetYuzuPathString(FS::YuzuPath::TASDir), |
| | std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::TASDir))); |
| |
|
| | WriteCategory(Settings::Category::DataStorage); |
| |
|
| | EndGroup(); |
| | } |
| |
|
| | void Config::SaveDebuggingValues() { |
| | BeginGroup(Settings::TranslateCategory(Settings::Category::Debugging)); |
| |
|
| | |
| | WriteBooleanSetting(std::string("record_frame_times"), Settings::values.record_frame_times); |
| |
|
| | WriteCategory(Settings::Category::Debugging); |
| | WriteCategory(Settings::Category::DebuggingGraphics); |
| |
|
| | EndGroup(); |
| | } |
| |
|
| | #ifdef __unix__ |
| | void Config::SaveLinuxValues() { |
| | BeginGroup(Settings::TranslateCategory(Settings::Category::Linux)); |
| |
|
| | WriteCategory(Settings::Category::Linux); |
| |
|
| | EndGroup(); |
| | } |
| | #endif |
| |
|
| | void Config::SaveNetworkValues() { |
| | BeginGroup(Settings::TranslateCategory(Settings::Category::Services)); |
| |
|
| | WriteCategory(Settings::Category::Network); |
| |
|
| | EndGroup(); |
| | } |
| |
|
| | void Config::SaveDisabledAddOnValues() { |
| | |
| | BeginGroup(std::string("DisabledAddOns")); |
| |
|
| | int i = 0; |
| | BeginArray(std::string("")); |
| | for (const auto& elem : Settings::values.disabled_addons) { |
| | SetArrayIndex(i); |
| | WriteIntegerSetting(std::string("title_id"), elem.first, |
| | std::make_optional(static_cast<u64>(0))); |
| | BeginArray(std::string("disabled")); |
| | for (std::size_t j = 0; j < elem.second.size(); ++j) { |
| | SetArrayIndex(static_cast<int>(j)); |
| | WriteStringSetting(std::string("d"), elem.second[j], |
| | std::make_optional(std::string(""))); |
| | } |
| | EndArray(); |
| | ++i; |
| | } |
| | EndArray(); |
| |
|
| | EndGroup(); |
| | } |
| |
|
| | void Config::SaveMiscellaneousValues() { |
| | BeginGroup(Settings::TranslateCategory(Settings::Category::Miscellaneous)); |
| |
|
| | WriteCategory(Settings::Category::Miscellaneous); |
| |
|
| | EndGroup(); |
| | } |
| |
|
| | void Config::SaveCpuValues() { |
| | BeginGroup(Settings::TranslateCategory(Settings::Category::Cpu)); |
| |
|
| | WriteCategory(Settings::Category::Cpu); |
| | WriteCategory(Settings::Category::CpuDebug); |
| | WriteCategory(Settings::Category::CpuUnsafe); |
| |
|
| | EndGroup(); |
| | } |
| |
|
| | void Config::SaveRendererValues() { |
| | BeginGroup(Settings::TranslateCategory(Settings::Category::Renderer)); |
| |
|
| | WriteCategory(Settings::Category::Renderer); |
| | WriteCategory(Settings::Category::RendererAdvanced); |
| | WriteCategory(Settings::Category::RendererDebug); |
| |
|
| | EndGroup(); |
| | } |
| |
|
| | void Config::SaveScreenshotValues() { |
| | BeginGroup(Settings::TranslateCategory(Settings::Category::Screenshots)); |
| |
|
| | WriteStringSetting(std::string("screenshot_path"), |
| | FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir)); |
| | WriteCategory(Settings::Category::Screenshots); |
| |
|
| | EndGroup(); |
| | } |
| |
|
| | void Config::SaveSystemValues() { |
| | BeginGroup(Settings::TranslateCategory(Settings::Category::System)); |
| |
|
| | WriteCategory(Settings::Category::System); |
| | WriteCategory(Settings::Category::SystemAudio); |
| |
|
| | EndGroup(); |
| | } |
| |
|
| | void Config::SaveWebServiceValues() { |
| | BeginGroup(Settings::TranslateCategory(Settings::Category::WebService)); |
| |
|
| | WriteCategory(Settings::Category::WebService); |
| |
|
| | EndGroup(); |
| | } |
| |
|
| | void Config::SaveLibraryAppletValues() { |
| | BeginGroup(Settings::TranslateCategory(Settings::Category::LibraryApplet)); |
| |
|
| | WriteCategory(Settings::Category::LibraryApplet); |
| |
|
| | EndGroup(); |
| | } |
| |
|
| | bool Config::ReadBooleanSetting(const std::string& key, const std::optional<bool> default_value) { |
| | std::string full_key = GetFullKey(key, false); |
| | if (!default_value.has_value()) { |
| | return config->GetBoolValue(GetSection().c_str(), full_key.c_str(), false); |
| | } |
| |
|
| | if (config->GetBoolValue(GetSection().c_str(), |
| | std::string(full_key).append("\\default").c_str(), false)) { |
| | return static_cast<bool>(default_value.value()); |
| | } else { |
| | return config->GetBoolValue(GetSection().c_str(), full_key.c_str(), |
| | static_cast<bool>(default_value.value())); |
| | } |
| | } |
| |
|
| | s64 Config::ReadIntegerSetting(const std::string& key, const std::optional<s64> default_value) { |
| | std::string full_key = GetFullKey(key, false); |
| | if (!default_value.has_value()) { |
| | try { |
| | return std::stoll( |
| | std::string(config->GetValue(GetSection().c_str(), full_key.c_str(), "0"))); |
| | } catch (...) { |
| | return 0; |
| | } |
| | } |
| |
|
| | s64 result = 0; |
| | if (config->GetBoolValue(GetSection().c_str(), |
| | std::string(full_key).append("\\default").c_str(), true)) { |
| | result = default_value.value(); |
| | } else { |
| | try { |
| | result = std::stoll(std::string(config->GetValue( |
| | GetSection().c_str(), full_key.c_str(), ToString(default_value.value()).c_str()))); |
| | } catch (...) { |
| | result = default_value.value(); |
| | } |
| | } |
| | return result; |
| | } |
| |
|
| | u64 Config::ReadUnsignedIntegerSetting(const std::string& key, |
| | const std::optional<u64> default_value) { |
| | std::string full_key = GetFullKey(key, false); |
| | if (!default_value.has_value()) { |
| | try { |
| | return std::stoull( |
| | std::string(config->GetValue(GetSection().c_str(), full_key.c_str(), "0"))); |
| | } catch (...) { |
| | return 0; |
| | } |
| | } |
| |
|
| | u64 result = 0; |
| | if (config->GetBoolValue(GetSection().c_str(), |
| | std::string(full_key).append("\\default").c_str(), true)) { |
| | result = default_value.value(); |
| | } else { |
| | try { |
| | result = std::stoull(std::string(config->GetValue( |
| | GetSection().c_str(), full_key.c_str(), ToString(default_value.value()).c_str()))); |
| | } catch (...) { |
| | result = default_value.value(); |
| | } |
| | } |
| | return result; |
| | } |
| |
|
| | double Config::ReadDoubleSetting(const std::string& key, |
| | const std::optional<double> default_value) { |
| | std::string full_key = GetFullKey(key, false); |
| | if (!default_value.has_value()) { |
| | return config->GetDoubleValue(GetSection().c_str(), full_key.c_str(), 0); |
| | } |
| |
|
| | double result; |
| | if (config->GetBoolValue(GetSection().c_str(), |
| | std::string(full_key).append("\\default").c_str(), true)) { |
| | result = default_value.value(); |
| | } else { |
| | result = |
| | config->GetDoubleValue(GetSection().c_str(), full_key.c_str(), default_value.value()); |
| | } |
| | return result; |
| | } |
| |
|
| | std::string Config::ReadStringSetting(const std::string& key, |
| | const std::optional<std::string> default_value) { |
| | std::string result; |
| | std::string full_key = GetFullKey(key, false); |
| | if (!default_value.has_value()) { |
| | result = config->GetValue(GetSection().c_str(), full_key.c_str(), ""); |
| | boost::replace_all(result, "\"", ""); |
| | return result; |
| | } |
| |
|
| | if (config->GetBoolValue(GetSection().c_str(), |
| | std::string(full_key).append("\\default").c_str(), true)) { |
| | result = default_value.value(); |
| | } else { |
| | result = |
| | config->GetValue(GetSection().c_str(), full_key.c_str(), default_value.value().c_str()); |
| | } |
| | boost::replace_all(result, "\"", ""); |
| | boost::replace_all(result, "//", "/"); |
| | return result; |
| | } |
| |
|
| | bool Config::Exists(const std::string& section, const std::string& key) const { |
| | const std::string value = config->GetValue(section.c_str(), key.c_str(), ""); |
| | return !value.empty(); |
| | } |
| |
|
| | void Config::WriteBooleanSetting(const std::string& key, const bool& value, |
| | const std::optional<bool>& default_value, |
| | const std::optional<bool>& use_global) { |
| | std::optional<std::string> string_default = std::nullopt; |
| | if (default_value.has_value()) { |
| | string_default = std::make_optional(ToString(default_value.value())); |
| | } |
| | WritePreparedSetting(key, AdjustOutputString(ToString(value)), string_default, use_global); |
| | } |
| |
|
| | void Config::WriteDoubleSetting(const std::string& key, const double& value, |
| | const std::optional<double>& default_value, |
| | const std::optional<bool>& use_global) { |
| | std::optional<std::string> string_default = std::nullopt; |
| | if (default_value.has_value()) { |
| | string_default = std::make_optional(ToString(default_value.value())); |
| | } |
| | WritePreparedSetting(key, AdjustOutputString(ToString(value)), string_default, use_global); |
| | } |
| |
|
| | void Config::WriteStringSetting(const std::string& key, const std::string& value, |
| | const std::optional<std::string>& default_value, |
| | const std::optional<bool>& use_global) { |
| | std::optional string_default = default_value; |
| | if (default_value.has_value()) { |
| | string_default.value().append(AdjustOutputString(default_value.value())); |
| | } |
| | WritePreparedSetting(key, AdjustOutputString(value), string_default, use_global); |
| | } |
| |
|
| | void Config::WritePreparedSetting(const std::string& key, const std::string& adjusted_value, |
| | const std::optional<std::string>& adjusted_default_value, |
| | const std::optional<bool>& use_global) { |
| | std::string full_key = GetFullKey(key, false); |
| | if (adjusted_default_value.has_value() && use_global.has_value()) { |
| | if (!global) { |
| | WriteString(std::string(full_key).append("\\global"), ToString(use_global.value())); |
| | } |
| | if (global || use_global.value() == false) { |
| | WriteString(std::string(full_key).append("\\default"), |
| | ToString(adjusted_default_value == adjusted_value)); |
| | WriteString(full_key, adjusted_value); |
| | } |
| | } else if (adjusted_default_value.has_value() && !use_global.has_value()) { |
| | WriteString(std::string(full_key).append("\\default"), |
| | ToString(adjusted_default_value == adjusted_value)); |
| | WriteString(full_key, adjusted_value); |
| | } else { |
| | WriteString(full_key, adjusted_value); |
| | } |
| | } |
| |
|
| | void Config::WriteString(const std::string& key, const std::string& value) { |
| | config->SetValue(GetSection().c_str(), key.c_str(), value.c_str()); |
| | } |
| |
|
| | void Config::Reload() { |
| | ReadValues(); |
| | |
| | SaveValues(); |
| | } |
| |
|
| | void Config::ClearControlPlayerValues() const { |
| | |
| | const char* section = Settings::TranslateCategory(Settings::Category::Controls); |
| | config->Delete(section, nullptr, true); |
| | } |
| |
|
| | const std::string& Config::GetConfigFilePath() const { |
| | return config_loc; |
| | } |
| |
|
| | void Config::ReadCategory(const Settings::Category category) { |
| | const auto& settings = FindRelevantList(category); |
| | std::ranges::for_each(settings, [&](const auto& setting) { ReadSettingGeneric(setting); }); |
| | } |
| |
|
| | void Config::WriteCategory(const Settings::Category category) { |
| | const auto& settings = FindRelevantList(category); |
| | std::ranges::for_each(settings, [&](const auto& setting) { WriteSettingGeneric(setting); }); |
| | } |
| |
|
| | void Config::ReadSettingGeneric(Settings::BasicSetting* const setting) { |
| | if (!setting->Save() || (!setting->Switchable() && !global)) { |
| | return; |
| | } |
| |
|
| | const std::string key = AdjustKey(setting->GetLabel()); |
| | const std::string default_value(setting->DefaultToString()); |
| |
|
| | bool use_global = true; |
| | if (setting->Switchable() && !global) { |
| | use_global = |
| | ReadBooleanSetting(std::string(key).append("\\use_global"), std::make_optional(true)); |
| | setting->SetGlobal(use_global); |
| | } |
| |
|
| | if (global || !use_global) { |
| | const bool is_default = |
| | ReadBooleanSetting(std::string(key).append("\\default"), std::make_optional(true)); |
| | if (!is_default) { |
| | const std::string setting_string = ReadStringSetting(key, default_value); |
| | setting->LoadString(setting_string); |
| | } else { |
| | |
| | setting->LoadString(""); |
| | } |
| | } |
| | } |
| |
|
| | void Config::WriteSettingGeneric(const Settings::BasicSetting* const setting) { |
| | if (!setting->Save()) { |
| | return; |
| | } |
| |
|
| | std::string key = AdjustKey(setting->GetLabel()); |
| | if (setting->Switchable()) { |
| | if (!global) { |
| | WriteBooleanSetting(std::string(key).append("\\use_global"), setting->UsingGlobal()); |
| | } |
| | if (global || !setting->UsingGlobal()) { |
| | auto value = global ? setting->ToStringGlobal() : setting->ToString(); |
| | WriteBooleanSetting(std::string(key).append("\\default"), |
| | value == setting->DefaultToString()); |
| | WriteStringSetting(key, value); |
| | } |
| | } else if (global) { |
| | WriteBooleanSetting(std::string(key).append("\\default"), |
| | setting->ToString() == setting->DefaultToString()); |
| | WriteStringSetting(key, setting->ToString()); |
| | } |
| | } |
| |
|
| | void Config::BeginGroup(const std::string& group) { |
| | |
| | ASSERT(array_stack.empty()); |
| |
|
| | key_stack.push_back(AdjustKey(group)); |
| | } |
| |
|
| | void Config::EndGroup() { |
| | |
| | ASSERT(!key_stack.empty()); |
| |
|
| | |
| | ASSERT(array_stack.empty()); |
| |
|
| | key_stack.pop_back(); |
| | } |
| |
|
| | std::string Config::GetSection() { |
| | if (key_stack.empty()) { |
| | return std::string{""}; |
| | } |
| |
|
| | return key_stack.front(); |
| | } |
| |
|
| | std::string Config::GetGroup() const { |
| | if (key_stack.size() <= 1) { |
| | return std::string{""}; |
| | } |
| |
|
| | std::string key; |
| | for (size_t i = 1; i < key_stack.size(); ++i) { |
| | key.append(key_stack[i]).append("\\"); |
| | } |
| | return key; |
| | } |
| |
|
| | std::string Config::AdjustKey(const std::string& key) { |
| | std::string adjusted_key(key); |
| | boost::replace_all(adjusted_key, "/", "\\"); |
| | boost::replace_all(adjusted_key, " ", "%20"); |
| | return adjusted_key; |
| | } |
| |
|
| | std::string Config::AdjustOutputString(const std::string& string) { |
| | std::string adjusted_string(string); |
| | boost::replace_all(adjusted_string, "\\", "/"); |
| |
|
| | |
| | |
| | #ifndef ANDROID |
| | if (string.substr(0, 2) == "//") { |
| | boost::replace_all(adjusted_string, "//", "/"); |
| | adjusted_string.insert(0, "/"); |
| | } else { |
| | boost::replace_all(adjusted_string, "//", "/"); |
| | } |
| | #endif |
| |
|
| | |
| | for (const auto& special_character : special_characters) { |
| | if (adjusted_string.find(special_character) != std::string::npos) { |
| | adjusted_string.insert(0, "\""); |
| | adjusted_string.append("\""); |
| | break; |
| | } |
| | } |
| | return adjusted_string; |
| | } |
| |
|
| | std::string Config::GetFullKey(const std::string& key, bool skipArrayIndex) { |
| | if (array_stack.empty()) { |
| | return std::string(GetGroup()).append(AdjustKey(key)); |
| | } |
| |
|
| | std::string array_key; |
| | for (size_t i = 0; i < array_stack.size(); ++i) { |
| | if (!array_stack[i].name.empty()) { |
| | array_key.append(array_stack[i].name).append("\\"); |
| | } |
| |
|
| | if (!skipArrayIndex || (array_stack.size() - 1 != i && array_stack.size() > 1)) { |
| | array_key.append(ToString(array_stack[i].index)).append("\\"); |
| | } |
| | } |
| | std::string final_key = std::string(GetGroup()).append(array_key).append(AdjustKey(key)); |
| | return final_key; |
| | } |
| |
|
| | int Config::BeginArray(const std::string& array) { |
| | array_stack.push_back(ConfigArray{AdjustKey(array), 0, 0}); |
| | const int size = config->GetLongValue(GetSection().c_str(), |
| | GetFullKey(std::string("size"), true).c_str(), 0); |
| | array_stack.back().size = size; |
| | return size; |
| | } |
| |
|
| | void Config::EndArray() { |
| | |
| | ASSERT(!array_stack.empty()); |
| |
|
| | |
| | int size = 0; |
| | if (array_stack.back().index != 0) { |
| | size = array_stack.back().size; |
| | } |
| |
|
| | |
| | if (key_stack.size() == 1 && array_stack.back().name.empty()) { |
| | |
| | config->SetValue(GetSection().c_str(), std::string("size").c_str(), ToString(size).c_str()); |
| | } else { |
| | const auto key = GetFullKey(std::string("size"), true); |
| | config->SetValue(GetSection().c_str(), key.c_str(), ToString(size).c_str()); |
| | } |
| |
|
| | array_stack.pop_back(); |
| | } |
| |
|
| | void Config::SetArrayIndex(const int index) { |
| | |
| | ASSERT(!array_stack.empty()); |
| |
|
| | const int array_index = index + 1; |
| |
|
| | |
| | ASSERT(array_stack.front().size + 1 >= array_index); |
| |
|
| | |
| | |
| | |
| | array_stack.back().size = array_index; |
| | array_stack.back().index = array_index; |
| | } |
| |
|