| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042 |
- // SPDX-FileCopyrightText: 2023 yuzu Emulator Project
- // SPDX-License-Identifier: GPL-2.0-or-later
- #include <algorithm>
- #include <array>
- #include "common/fs/fs.h"
- #include "common/fs/path_util.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 "core/hle/service/hid/controllers/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 {
- 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("_");
- }
- auto& player = Settings::values.players.GetValue()[player_index];
- if (IsCustomConfig()) {
- const auto profile_name =
- ReadStringSetting(std::string(player_prefix).append("profile_name"));
- if (profile_name.empty()) {
- // Use the global input config
- player = Settings::values.players.GetValue(true)[player_index];
- 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 {
- 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"), 15));
- Settings::values.touchscreen.diameter_y =
- static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_diameter_y"), 15));
- }
- 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);
- }
- // Disable docked mode if handheld is selected
- 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(); // entries
- 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(); // touch_from_button_maps
- 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));
- // Intentionally not using the QT default setting as this is intended to be changed in the ini
- Settings::values.record_frame_times =
- ReadBooleanSetting(std::string("record_frame_times"), std::make_optional(false));
- ReadCategory(Settings::Category::Debugging);
- ReadCategory(Settings::Category::DebuggingGraphics);
- EndGroup();
- }
- void Config::ReadServiceValues() {
- BeginGroup(Settings::TranslateCategory(Settings::Category::Services));
- ReadCategory(Settings::Category::Services);
- EndGroup();
- }
- void Config::ReadDisabledAddOnValues() {
- // Custom config section
- 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(); // d
- Settings::values.disabled_addons.insert_or_assign(title_id, out);
- }
- EndArray(); // Base disabled addons array - Has no base key
- 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::ReadValues() {
- if (global) {
- ReadDataStorageValues();
- ReadDebuggingValues();
- ReadDisabledAddOnValues();
- ReadNetworkValues();
- ReadServiceValues();
- ReadWebServiceValues();
- ReadMiscellaneousValues();
- }
- ReadControlValues();
- ReadCoreValues();
- ReadCpuValues();
- 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()) {
- // No custom profile selected
- 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()) {
- 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>(15)));
- WriteIntegerSetting(std::string("touchscreen_diameter_y"), touchscreen.diameter_y,
- std::make_optional(static_cast<u32>(15)));
- }
- 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(); // entries
- }
- EndArray(); // touch_from_button_maps
- }
- void Config::SaveValues() {
- if (global) {
- SaveDataStorageValues();
- SaveDebuggingValues();
- SaveDisabledAddOnValues();
- SaveNetworkValues();
- SaveWebServiceValues();
- SaveMiscellaneousValues();
- }
- SaveControlValues();
- SaveCoreValues();
- SaveCpuValues();
- 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));
- // Intentionally not using the QT default setting as this is intended to be changed in the ini
- WriteBooleanSetting(std::string("record_frame_times"), Settings::values.record_frame_times);
- WriteCategory(Settings::Category::Debugging);
- WriteCategory(Settings::Category::DebuggingGraphics);
- EndGroup();
- }
- void Config::SaveNetworkValues() {
- BeginGroup(Settings::TranslateCategory(Settings::Category::Services));
- WriteCategory(Settings::Category::Network);
- EndGroup();
- }
- void Config::SaveDisabledAddOnValues() {
- // Custom config section
- 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(); // disabled
- ++i;
- }
- EndArray(); // Base disabled addons array - Has no base key
- 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();
- }
- 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);
- }
- template <typename T>
- std::enable_if_t<std::is_integral_v<T>> Config::WriteIntegerSetting(
- const std::string& key, const T& value, const std::optional<T>& 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();
- // To apply default value changes
- SaveValues();
- }
- void Config::Save() {
- SaveValues();
- }
- void Config::ClearControlPlayerValues() const {
- // If key is an empty string, all keys in the current group() are removed.
- const char* section = Settings::TranslateCategory(Settings::Category::Controls);
- CSimpleIniA::TNamesDepend keys;
- config->GetAllKeys(section, keys);
- for (const auto& key : keys) {
- if (std::string(config->GetValue(section, key.pItem)).empty()) {
- config->Delete(section, key.pItem);
- }
- }
- }
- 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 {
- // Empty string resets the Setting to default
- 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) {
- // You can't begin a group while reading/writing from a config array
- ASSERT(array_stack.empty());
- key_stack.push_back(AdjustKey(group));
- }
- void Config::EndGroup() {
- // You can't end a group if you haven't started one yet
- ASSERT(!key_stack.empty());
- // You can't end a group when reading/writing from a config array
- 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, "\\", "/");
- // Windows requires that two forward slashes are used at the start of a path for unmapped
- // network drives so we have to watch for that here
- #ifndef ANDROID
- if (string.substr(0, 2) == "//") {
- boost::replace_all(adjusted_string, "//", "/");
- adjusted_string.insert(0, "/");
- } else {
- boost::replace_all(adjusted_string, "//", "/");
- }
- #endif
- // Needed for backwards compatibility with QSettings deserialization
- 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() {
- // You can't end a config array before starting one
- ASSERT(!array_stack.empty());
- // Set the array size to 0 if the array is ended without changing the index
- int size = 0;
- if (array_stack.back().index != 0) {
- size = array_stack.back().size;
- }
- // Write out the size to config
- if (key_stack.size() == 1 && array_stack.back().name.empty()) {
- // Edge-case where the first array created doesn't have a name
- 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) {
- // You can't set the array index if you haven't started one yet
- ASSERT(!array_stack.empty());
- const int array_index = index + 1;
- // You can't exceed the known max size of the array by more than 1
- ASSERT(array_stack.front().size + 1 >= array_index);
- // Change the config array size to the current index since you may want
- // to reduce the number of elements that you read back from the config
- // in the future.
- array_stack.back().size = array_index;
- array_stack.back().index = array_index;
- }
|