From 1cf22f2484bbd75a0a2d0271b3642f7c2684d6b8 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Sun, 15 Feb 2026 11:53:47 +0200 Subject: [PATCH] update emulator_settings to latest --- src/common/logging/filter.cpp | 1 + src/common/logging/types.h | 1 + src/core/emulator_settings.cpp | 202 +++++++++++++++++++++++---------- src/core/emulator_settings.h | 80 ++++++++++--- 4 files changed, 205 insertions(+), 79 deletions(-) diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index 9a3fe0aa1..f2597603e 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -164,6 +164,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { CLS(Input) \ CLS(Tty) \ CLS(KeyManager) \ + CLS(EmuSettings) \ CLS(Loader) // GetClassName is a macro defined by Windows.h, grrr... diff --git a/src/common/logging/types.h b/src/common/logging/types.h index 9e176c698..4c6e53453 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -132,6 +132,7 @@ enum class Class : u8 { Input, ///< Input emulation Tty, ///< Debug output from emu KeyManager, ///< Key management system + EmuSettings, /// Emulator settings system Count ///< Total number of logging classes }; diff --git a/src/core/emulator_settings.cpp b/src/core/emulator_settings.cpp index a454c692e..f4717e4be 100644 --- a/src/core/emulator_settings.cpp +++ b/src/core/emulator_settings.cpp @@ -3,8 +3,8 @@ #include #include -#include #include +#include "common/logging/log.h" #include "emulator_settings.h" using json = nlohmann::json; @@ -29,12 +29,13 @@ struct adl_serializer { // -------------------- void EmulatorSettings::PrintChangedSummary(const std::vector& changed) { if (changed.empty()) { - std::cout << "[Settings] No game-specific overrides applied\n"; + LOG_DEBUG(EmuSettings, "[Settings] No game-specific overrides applied"); return; } - std::cout << "[Settings] Game-specific overrides applied:\n"; - for (const auto& k : changed) - std::cout << " * " << k << "\n"; + LOG_DEBUG(EmuSettings, "[Settings] Game-specific overrides applied:"); + for (const auto& k : changed) { + LOG_DEBUG(EmuSettings, " * {}", k); + } } // -------------------- @@ -141,6 +142,17 @@ void EmulatorSettings::SetSysModulesDir(const std::filesystem::path& dir) { m_general.sys_modules_dir.value = dir; } +std::filesystem::path EmulatorSettings::GetFontsDir() { + if (m_general.font_dir.value.empty()) { + return Common::FS::GetUserPath(Common::FS::PathType::FontsDir); + } + return m_general.font_dir.value; +} + +void EmulatorSettings::SetFontsDir(const std::filesystem::path& dir) { + m_general.font_dir.value = dir; +} + // -------------------- // Save // -------------------- @@ -210,13 +222,13 @@ bool EmulatorSettings::Save(const std::string& serial) const { std::ofstream out(path); if (!out.is_open()) { - std::cerr << "Failed to open file for writing: " << path << std::endl; + LOG_ERROR(EmuSettings, "Failed to open file for writing: {}", path.string()); return false; } out << std::setw(4) << j; out.flush(); if (out.fail()) { - std::cerr << "Failed to write settings to: " << path << std::endl; + LOG_ERROR(EmuSettings, "Failed to write settings to: {}", path.string()); return false; } return true; @@ -234,19 +246,19 @@ bool EmulatorSettings::Save(const std::string& serial) const { std::ofstream out(path); if (!out.is_open()) { - std::cerr << "Failed to open file for writing: " << path << std::endl; + LOG_ERROR(EmuSettings, "Failed to open file for writing: {}", path.string()); return false; } out << std::setw(4) << j; out.flush(); if (out.fail()) { - std::cerr << "Failed to write settings to: " << path << std::endl; + LOG_ERROR(EmuSettings, "Failed to write settings to: {}", path.string()); return false; } return true; } } catch (const std::exception& e) { - std::cerr << "Error saving settings: " << e.what() << std::endl; + LOG_ERROR(EmuSettings, "Error saving settings: {}", e.what()); return false; } } @@ -256,96 +268,145 @@ bool EmulatorSettings::Save(const std::string& serial) const { // -------------------- bool EmulatorSettings::Load(const std::string& serial) { try { - const std::filesystem::path userDir = - Common::FS::GetUserPath(Common::FS::PathType::UserDir); - const std::filesystem::path configPath = userDir / "config.json"; + // If serial is empty, load ONLY global settings + if (serial.empty()) { + const std::filesystem::path userDir = + Common::FS::GetUserPath(Common::FS::PathType::UserDir); + const std::filesystem::path configPath = userDir / "config.json"; - // Load global config if exists - if (std::ifstream globalIn{configPath}; globalIn.good()) { - json gj; - globalIn >> gj; - if (gj.contains("General")) { - json current = m_general; // JSON from existing struct with all defaults - current.update(gj.at("General")); // merge only fields present in file - m_general = current.get(); // convert back + LOG_DEBUG(EmuSettings, "[EmulatorSettings] Loading global settings from: {}", + configPath.string()); + + // Load global config if exists + if (std::ifstream globalIn{configPath}; globalIn.good()) { + json gj; + globalIn >> gj; + + LOG_DEBUG(EmuSettings, "[EmulatorSettings] Global config JSON size: {}", gj.size()); + + if (gj.contains("General")) { + json current = m_general; + current.update(gj.at("General")); + m_general = current.get(); + LOG_DEBUG(EmuSettings, "[EmulatorSettings] Loaded General settings"); + } + if (gj.contains("Debug")) { + json current = m_debug; + current.update(gj.at("Debug")); + m_debug = current.get(); + LOG_DEBUG(EmuSettings, "[EmulatorSettings] Loaded Debug settings"); + } + if (gj.contains("Input")) { + json current = m_input; + current.update(gj.at("Input")); + m_input = current.get(); + LOG_DEBUG(EmuSettings, "[EmulatorSettings] Loaded Input settings"); + } + if (gj.contains("Audio")) { + json current = m_audio; + current.update(gj.at("Audio")); + m_audio = current.get(); + LOG_DEBUG(EmuSettings, "[EmulatorSettings] Loaded Audio settings"); + } + if (gj.contains("GPU")) { + json current = m_gpu; + current.update(gj.at("GPU")); + m_gpu = current.get(); + LOG_DEBUG(EmuSettings, "[EmulatorSettings] Loaded GPU settings"); + } + if (gj.contains("Vulkan")) { + json current = m_vulkan; + current.update(gj.at("Vulkan")); + m_vulkan = current.get(); + LOG_DEBUG(EmuSettings, "[EmulatorSettings] Loaded Vulkan settings"); + } + if (gj.contains("Users")) { + m_userManager.GetUsers() = gj.at("Users").get(); + LOG_DEBUG(EmuSettings, "[EmulatorSettings] Loaded Users"); + } + } else { + LOG_DEBUG(EmuSettings, + "[EmulatorSettings] Global config not found, setting defaults"); + SetDefaultValues(); + // ensure a default user exists + if (m_userManager.GetUsers().user.empty()) + m_userManager.GetUsers().user = m_userManager.CreateDefaultUser(); + Save(); } - if (gj.contains("Debug")) { - json current = m_debug; - current.update(gj.at("Debug")); - m_debug = current.get(); - } - if (gj.contains("Input")) { - json current = m_input; - current.update(gj.at("Input")); - m_input = current.get(); - } - if (gj.contains("Audio")) { - json current = m_audio; - current.update(gj.at("Audio")); - m_audio = current.get(); - } - if (gj.contains("GPU")) { - json current = m_gpu; - current.update(gj.at("GPU")); - m_gpu = current.get(); - } - if (gj.contains("Vulkan")) { - json current = m_vulkan; - current.update(gj.at("Vulkan")); - m_vulkan = current.get(); - } - if (gj.contains("Users")) - m_userManager.GetUsers() = gj.at("Users").get(); - } else { - SetDefaultValues(); - // ensure a default user exists - if (m_userManager.GetUsers().user.empty()) - m_userManager.GetUsers().user = m_userManager.CreateDefaultUser(); - Save(); + + return true; } + // If serial is provided, ONLY apply game-specific overrides + // WITHOUT reloading global settings! + else { + LOG_DEBUG(EmuSettings, "[EmulatorSettings] Applying game-specific overrides for: {}", + serial); - // Load per-game overrides and apply - if (!serial.empty()) { const std::filesystem::path gamePath = Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs) / (serial + ".json"); - if (!std::filesystem::exists(gamePath)) + + LOG_DEBUG(EmuSettings, "[EmulatorSettings] Game config path: {}", gamePath.string()); + + if (!std::filesystem::exists(gamePath)) { + LOG_DEBUG(EmuSettings, "[EmulatorSettings] No game-specific config found"); return false; + } std::ifstream in(gamePath); - if (!in.is_open()) + if (!in.is_open()) { + LOG_ERROR(EmuSettings, "[EmulatorSettings] Failed to open game config file"); return false; + } json gj; in >> gj; + LOG_DEBUG(EmuSettings, "[EmulatorSettings] Game config JSON: {}", gj.dump(2)); + std::vector changed; if (gj.contains("General")) { + LOG_DEBUG(EmuSettings, "[EmulatorSettings] Applying General overrides"); ApplyGroupOverrides(m_general, gj.at("General"), changed); } if (gj.contains("Debug")) { + LOG_DEBUG(EmuSettings, "[EmulatorSettings] Applying Debug overrides"); ApplyGroupOverrides(m_debug, gj.at("Debug"), changed); } if (gj.contains("Input")) { + LOG_DEBUG(EmuSettings, "[EmulatorSettings] Applying Input overrides"); ApplyGroupOverrides(m_input, gj.at("Input"), changed); } if (gj.contains("Audio")) { + LOG_DEBUG(EmuSettings, "[EmulatorSettings] Applying Audio overrides"); ApplyGroupOverrides(m_audio, gj.at("Audio"), changed); } if (gj.contains("GPU")) { + LOG_DEBUG(EmuSettings, "[EmulatorSettings] Applying GPU overrides"); ApplyGroupOverrides(m_gpu, gj.at("GPU"), changed); + + // Debug: Print specific GPU values + auto gpuJson = gj["GPU"]; + if (gpuJson.contains("fsr_enabled")) { + LOG_DEBUG(EmuSettings, "[EmulatorSettings] GPU/fsr_enabled JSON: {}", + gpuJson["fsr_enabled"].dump()); + } + if (gpuJson.contains("rcas_enabled")) { + LOG_DEBUG(EmuSettings, "[EmulatorSettings] GPU/rcas_enabled JSON: {}", + gpuJson["rcas_enabled"].dump()); + } } if (gj.contains("Vulkan")) { + LOG_DEBUG(EmuSettings, "[EmulatorSettings] Applying Vulkan overrides"); ApplyGroupOverrides(m_vulkan, gj.at("Vulkan"), changed); } PrintChangedSummary(changed); + return true; } - - return true; } catch (const std::exception& e) { - std::cerr << "Error loading settings: " << e.what() << std::endl; + LOG_ERROR(EmuSettings, "Error loading settings: {}", e.what()); return false; } } @@ -358,3 +419,22 @@ void EmulatorSettings::SetDefaultValues() { m_gpu = GPUSettings{}; m_vulkan = VulkanSettings{}; } + +std::vector EmulatorSettings::GetAllOverrideableKeys() const { + std::vector keys; + + auto addKeys = [&keys](const std::vector& items) { + for (const auto& item : items) { + keys.push_back(item.key); + } + }; + + addKeys(m_general.GetOverrideableFields()); + addKeys(m_debug.GetOverrideableFields()); + addKeys(m_input.GetOverrideableFields()); + addKeys(m_audio.GetOverrideableFields()); + addKeys(m_gpu.GetOverrideableFields()); + addKeys(m_vulkan.GetOverrideableFields()); + + return keys; +} \ No newline at end of file diff --git a/src/core/emulator_settings.h b/src/core/emulator_settings.h index d778dc28d..f69bcdd2e 100644 --- a/src/core/emulator_settings.h +++ b/src/core/emulator_settings.h @@ -10,6 +10,7 @@ #include #include #include +#include "common/logging/log.h" #include "common/types.h" #include "core/user_manager.h" @@ -43,24 +44,37 @@ struct OverrideItem { // Helper factory: create an OverrideItem binding a pointer-to-member template inline OverrideItem make_override(const char* key, Setting Struct::* member) { - return OverrideItem{key, [member, key](void* base, const nlohmann::json& entry, - std::vector& changed) { - if (!entry.is_object()) - return; + return OverrideItem{ + key, + [member, key](void* base, const nlohmann::json& entry, std::vector& changed) { + LOG_DEBUG(EmuSettings, "[make_override] Processing key: {}", key); + LOG_DEBUG(EmuSettings, "[make_override] Entry JSON: {}", entry.dump()); - Struct* obj = reinterpret_cast(base); - Setting& dst = obj->*member; + Struct* obj = reinterpret_cast(base); + Setting& dst = obj->*member; - Setting tmp = entry.get>(); + try { + // Parse the value from JSON + T newValue = entry.get(); - if (dst.value != tmp.value) { - changed.push_back(std::string(key) + " ( " + - nlohmann::json(dst.value).dump() + " → " + - nlohmann::json(tmp.value).dump() + " )"); - } + LOG_DEBUG(EmuSettings, "[make_override] Parsed value: {}", newValue); + LOG_DEBUG(EmuSettings, "[make_override] Current value: {}", dst.value); - dst.value = tmp.value; - }}; + if (dst.value != newValue) { + std::ostringstream oss; + oss << key << " ( " << dst.value << " → " << newValue << " )"; + changed.push_back(oss.str()); + LOG_DEBUG(EmuSettings, "[make_override] Recorded change: {}", oss.str()); + } + + dst.value = newValue; + LOG_DEBUG(EmuSettings, "[make_override] Successfully updated {}", key); + } catch (const std::exception& e) { + LOG_ERROR(EmuSettings, "[make_override] ERROR parsing {}: {}", key, e.what()); + LOG_ERROR(EmuSettings, "[make_override] Entry was: {}", entry.dump()); + LOG_ERROR(EmuSettings, "[make_override] Type name: {}", entry.type_name()); + } + }}; } // ------------------------------- @@ -80,6 +94,7 @@ struct GeneralSettings { Setting addon_install_dir; Setting home_dir; Setting sys_modules_dir; + Setting font_dir; Setting volume_slider{100}; Setting neo_mode{false}; @@ -92,6 +107,7 @@ struct GeneralSettings { Setting log_filter{""}; Setting log_type{"sync"}; Setting show_splash{false}; + Setting identical_log_grouped{true}; Setting connected_to_network{false}; Setting discord_rpc_enabled{false}; Setting show_fps_counter{false}; @@ -112,6 +128,8 @@ struct GeneralSettings { &GeneralSettings::trophy_notification_duration), make_override("log_filter", &GeneralSettings::log_filter), make_override("log_type", &GeneralSettings::log_type), + make_override("identical_log_grouped", + &GeneralSettings::identical_log_grouped), make_override("show_splash", &GeneralSettings::show_splash), make_override("trophy_notification_side", &GeneralSettings::trophy_notification_side), @@ -120,11 +138,12 @@ struct GeneralSettings { } }; NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(GeneralSettings, install_dirs, addon_install_dir, home_dir, - sys_modules_dir, volume_slider, neo_mode, dev_kit_mode, + sys_modules_dir, font_dir, volume_slider, neo_mode, dev_kit_mode, extra_dmem_in_mbytes, psn_signed_in, trophy_popup_disabled, trophy_notification_duration, log_filter, log_type, show_splash, - trophy_notification_side, connected_to_network, - discord_rpc_enabled, show_fps_counter, console_language) + identical_log_grouped, trophy_notification_side, + connected_to_network, discord_rpc_enabled, show_fps_counter, + console_language) // ------------------------------- // Debug settings @@ -245,6 +264,7 @@ struct GPUSettings { &GPUSettings::readback_linear_images_enabled), make_override("direct_memory_access_enabled", &GPUSettings::direct_memory_access_enabled), + make_override("vblank_frequency", &GPUSettings::vblank_frequency), }; } }; @@ -332,6 +352,8 @@ public: void SetHomeDir(const std::filesystem::path& dir); std::filesystem::path GetSysModulesDir(); void SetSysModulesDir(const std::filesystem::path& dir); + std::filesystem::path GetFontsDir(); + void SetFontsDir(const std::filesystem::path& dir); // user helpers UserManager& GetUserManager() { @@ -364,6 +386,26 @@ private: static void PrintChangedSummary(const std::vector& changed); public: + // Add these getters to access overrideable fields + std::vector GetGeneralOverrideableFields() const { + return m_general.GetOverrideableFields(); + } + std::vector GetDebugOverrideableFields() const { + return m_debug.GetOverrideableFields(); + } + std::vector GetInputOverrideableFields() const { + return m_input.GetOverrideableFields(); + } + std::vector GetAudioOverrideableFields() const { + return m_audio.GetOverrideableFields(); + } + std::vector GetGPUOverrideableFields() const { + return m_gpu.GetOverrideableFields(); + } + std::vector GetVulkanOverrideableFields() const { + return m_vulkan.GetOverrideableFields(); + } + std::vector GetAllOverrideableKeys() const; #define SETTING_FORWARD(group, Name, field) \ auto Get##Name() const { \ return group.field.value; \ @@ -393,6 +435,7 @@ public: SETTING_FORWARD(m_general, TrophyNotificationDuration, trophy_notification_duration) SETTING_FORWARD(m_general, TrophyNotificationSide, trophy_notification_side) SETTING_FORWARD_BOOL(m_general, ShowSplash, show_splash) + SETTING_FORWARD_BOOL(m_general, IdenticalLogGrouped, identical_log_grouped) SETTING_FORWARD(m_general, AddonInstallDir, addon_install_dir) SETTING_FORWARD(m_general, LogFilter, log_filter) SETTING_FORWARD(m_general, LogType, log_type) @@ -455,6 +498,7 @@ public: SETTING_FORWARD(m_input, DefaultControllerId, default_controller_id) SETTING_FORWARD_BOOL(m_input, UsingSpecialPad, use_special_pad) SETTING_FORWARD(m_input, SpecialPadClass, special_pad_class) + SETTING_FORWARD_BOOL(m_input, UseUnifiedInputConfig, use_unified_Input_Config) // Vulkan settings SETTING_FORWARD(m_vulkan, GpuId, gpu_id) @@ -471,4 +515,4 @@ public: #undef SETTING_FORWARD #undef SETTING_FORWARD_BOOL -}; +}; \ No newline at end of file