mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2026-04-02 19:08:03 -06:00
reset my latest try
This commit is contained in:
parent
e121b228ea
commit
cecd98aa28
@ -170,6 +170,7 @@ void EmulatorSettingsImpl::ClearGameSpecificOverrides() {
|
||||
}
|
||||
|
||||
void EmulatorSettingsImpl::ResetGameSpecificValue(const std::string& key) {
|
||||
// Walk every overrideable group until we find the matching key.
|
||||
auto tryGroup = [&key](auto& group) {
|
||||
for (auto& item : group.GetOverrideableFields()) {
|
||||
if (key == item.key) {
|
||||
@ -197,89 +198,34 @@ void EmulatorSettingsImpl::ResetGameSpecificValue(const std::string& key) {
|
||||
bool EmulatorSettingsImpl::Save(const std::string& serial) {
|
||||
try {
|
||||
if (!serial.empty()) {
|
||||
// ── Per-game config ─────────────────────────────────────
|
||||
const auto cfgDir = Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs);
|
||||
std::filesystem::create_directories(cfgDir);
|
||||
const auto path = cfgDir / (serial + ".json");
|
||||
|
||||
// Read existing config to preserve unknown sections
|
||||
json existing_json;
|
||||
if (std::filesystem::exists(path)) {
|
||||
std::ifstream existing_in(path);
|
||||
if (existing_in.good()) {
|
||||
existing_in >> existing_json;
|
||||
}
|
||||
}
|
||||
json j = json::object();
|
||||
|
||||
json j = existing_json.is_null() ? json::object() : existing_json;
|
||||
|
||||
// Save game-specific overrides (only overrideable fields)
|
||||
json generalObj = json::object();
|
||||
SaveGroupGameSpecific(m_general, generalObj);
|
||||
|
||||
// Merge with existing General section to preserve unknown fields within it
|
||||
if (j.contains("General") && j["General"].is_object()) {
|
||||
for (auto& [key, value] : j["General"].items()) {
|
||||
if (!generalObj.contains(key)) {
|
||||
generalObj[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
j["General"] = generalObj;
|
||||
|
||||
json debugObj = json::object();
|
||||
SaveGroupGameSpecific(m_debug, debugObj);
|
||||
if (j.contains("Debug") && j["Debug"].is_object()) {
|
||||
for (auto& [key, value] : j["Debug"].items()) {
|
||||
if (!debugObj.contains(key)) {
|
||||
debugObj[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
j["Debug"] = debugObj;
|
||||
|
||||
json inputObj = json::object();
|
||||
SaveGroupGameSpecific(m_input, inputObj);
|
||||
if (j.contains("Input") && j["Input"].is_object()) {
|
||||
for (auto& [key, value] : j["Input"].items()) {
|
||||
if (!inputObj.contains(key)) {
|
||||
inputObj[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
j["Input"] = inputObj;
|
||||
|
||||
json audioObj = json::object();
|
||||
SaveGroupGameSpecific(m_audio, audioObj);
|
||||
if (j.contains("Audio") && j["Audio"].is_object()) {
|
||||
for (auto& [key, value] : j["Audio"].items()) {
|
||||
if (!audioObj.contains(key)) {
|
||||
audioObj[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
j["Audio"] = audioObj;
|
||||
|
||||
json gpuObj = json::object();
|
||||
SaveGroupGameSpecific(m_gpu, gpuObj);
|
||||
if (j.contains("GPU") && j["GPU"].is_object()) {
|
||||
for (auto& [key, value] : j["GPU"].items()) {
|
||||
if (!gpuObj.contains(key)) {
|
||||
gpuObj[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
j["GPU"] = gpuObj;
|
||||
|
||||
json vulkanObj = json::object();
|
||||
SaveGroupGameSpecific(m_vulkan, vulkanObj);
|
||||
if (j.contains("Vulkan") && j["Vulkan"].is_object()) {
|
||||
for (auto& [key, value] : j["Vulkan"].items()) {
|
||||
if (!vulkanObj.contains(key)) {
|
||||
vulkanObj[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
j["Vulkan"] = vulkanObj;
|
||||
|
||||
std::ofstream out(path);
|
||||
@ -295,45 +241,14 @@ bool EmulatorSettingsImpl::Save(const std::string& serial) {
|
||||
const auto path =
|
||||
Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "config.json";
|
||||
|
||||
// Read existing config to preserve unknown sections
|
||||
json existing_json;
|
||||
if (std::filesystem::exists(path)) {
|
||||
std::ifstream existing_in(path);
|
||||
if (existing_in.good()) {
|
||||
existing_in >> existing_json;
|
||||
}
|
||||
}
|
||||
|
||||
// Start with unknown sections we've stored from previous loads
|
||||
json j = json::object();
|
||||
|
||||
// Add all unknown sections first
|
||||
for (const auto& [section_name, section_data] : m_unknown_sections) {
|
||||
j[section_name] = section_data;
|
||||
}
|
||||
|
||||
// Update schema version
|
||||
SetConfigVersion(Common::g_scm_rev);
|
||||
m_debug.config_schema_version.value = CURRENT_CONFIG_SCHEMA_VERSION;
|
||||
|
||||
// Save known sections (this will overwrite any unknown sections with the same name)
|
||||
json j;
|
||||
j["General"] = m_general;
|
||||
j["Debug"] = m_debug;
|
||||
j["Input"] = m_input;
|
||||
j["Audio"] = m_audio;
|
||||
j["GPU"] = m_gpu;
|
||||
j["Vulkan"] = m_vulkan;
|
||||
|
||||
// Merge with existing JSON to preserve any sections that weren't loaded
|
||||
// (this is a safety net in case we missed some sections)
|
||||
if (!existing_json.is_null()) {
|
||||
for (auto& [key, value] : existing_json.items()) {
|
||||
if (!j.contains(key)) {
|
||||
j[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::ofstream out(path);
|
||||
if (!out) {
|
||||
LOG_ERROR(EmuSettings, "Failed to open config for writing: {}", path.string());
|
||||
@ -358,43 +273,24 @@ bool EmulatorSettingsImpl::Load(const std::string& serial) {
|
||||
const auto configPath = userDir / "config.json";
|
||||
LOG_DEBUG(EmuSettings, "Loading global config from: {}", configPath.string());
|
||||
|
||||
// Clear unknown sections from previous load
|
||||
m_unknown_sections.clear();
|
||||
|
||||
if (std::ifstream in{configPath}; in.good()) {
|
||||
json j;
|
||||
in >> j;
|
||||
json gj;
|
||||
in >> gj;
|
||||
|
||||
// Check schema version
|
||||
int file_schema_version = 1;
|
||||
if (j.contains("Debug") && j["Debug"].contains("config_schema_version")) {
|
||||
file_schema_version = j["Debug"]["config_schema_version"].get<int>();
|
||||
}
|
||||
auto mergeGroup = [&gj](auto& group, const char* section) {
|
||||
if (!gj.contains(section))
|
||||
return;
|
||||
json current = group;
|
||||
current.update(gj.at(section));
|
||||
group = current.get<std::remove_reference_t<decltype(group)>>();
|
||||
};
|
||||
|
||||
LOG_DEBUG(EmuSettings, "Config schema version: {} (current: {})",
|
||||
file_schema_version, CURRENT_CONFIG_SCHEMA_VERSION);
|
||||
|
||||
// Load known sections
|
||||
if (j.contains("General"))
|
||||
j["General"].get_to(m_general);
|
||||
if (j.contains("Debug"))
|
||||
j["Debug"].get_to(m_debug);
|
||||
if (j.contains("Input"))
|
||||
j["Input"].get_to(m_input);
|
||||
if (j.contains("Audio"))
|
||||
j["Audio"].get_to(m_audio);
|
||||
if (j.contains("GPU"))
|
||||
j["GPU"].get_to(m_gpu);
|
||||
if (j.contains("Vulkan"))
|
||||
j["Vulkan"].get_to(m_vulkan);
|
||||
|
||||
// Store any unknown top-level sections
|
||||
for (auto it = j.begin(); it != j.end(); ++it) {
|
||||
if (!IsKnownSection(it.key())) {
|
||||
LOG_DEBUG(EmuSettings, "Preserving unknown section: {}", it.key());
|
||||
m_unknown_sections[it.key()] = it.value();
|
||||
}
|
||||
}
|
||||
mergeGroup(m_general, "General");
|
||||
mergeGroup(m_debug, "Debug");
|
||||
mergeGroup(m_input, "Input");
|
||||
mergeGroup(m_audio, "Audio");
|
||||
mergeGroup(m_gpu, "GPU");
|
||||
mergeGroup(m_vulkan, "Vulkan");
|
||||
|
||||
LOG_DEBUG(EmuSettings, "Global config loaded successfully");
|
||||
} else {
|
||||
@ -418,8 +314,8 @@ bool EmulatorSettingsImpl::Load(const std::string& serial) {
|
||||
int result;
|
||||
SDL_ShowMessageBox(&msg_box, &result);
|
||||
if (result == 1) {
|
||||
SDL_ShowSimpleMessageBox(0, "Error", "Migration not implemented yet",
|
||||
nullptr);
|
||||
SDL_ShowSimpleMessageBox(
|
||||
0, "Error", "sike this actually isn't implemented yet lol", nullptr);
|
||||
std::quick_exit(1);
|
||||
}
|
||||
}
|
||||
@ -427,17 +323,15 @@ bool EmulatorSettingsImpl::Load(const std::string& serial) {
|
||||
SetDefaultValues();
|
||||
Save();
|
||||
}
|
||||
|
||||
// Update schema version if needed
|
||||
if (m_debug.config_schema_version.value < CURRENT_CONFIG_SCHEMA_VERSION) {
|
||||
m_debug.config_schema_version.value = CURRENT_CONFIG_SCHEMA_VERSION;
|
||||
if (GetConfigVersion() != Common::g_scm_rev) {
|
||||
Save();
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
// ── Per-game override file ─────────────────────────────────
|
||||
// Never reloads global settings. Only applies
|
||||
// game_specific_value overrides on top of the already-loaded
|
||||
// base configuration.
|
||||
const auto gamePath =
|
||||
Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs) / (serial + ".json");
|
||||
LOG_DEBUG(EmuSettings, "Applying game config: {}", gamePath.string());
|
||||
@ -458,7 +352,10 @@ bool EmulatorSettingsImpl::Load(const std::string& serial) {
|
||||
|
||||
std::vector<std::string> changed;
|
||||
|
||||
// Apply overrides - these will set game_specific_value
|
||||
// ApplyGroupOverrides now correctly stores values as
|
||||
// game_specific_value (see make_override in the header).
|
||||
// ConfigMode::Default will then resolve them at getter call
|
||||
// time without ever touching the base values.
|
||||
if (gj.contains("General"))
|
||||
ApplyGroupOverrides(m_general, gj.at("General"), changed);
|
||||
if (gj.contains("Debug"))
|
||||
@ -489,7 +386,6 @@ void EmulatorSettingsImpl::SetDefaultValues() {
|
||||
m_audio = AudioSettings{};
|
||||
m_gpu = GPUSettings{};
|
||||
m_vulkan = VulkanSettings{};
|
||||
m_unknown_sections.clear();
|
||||
}
|
||||
|
||||
std::vector<std::string> EmulatorSettingsImpl::GetAllOverrideableKeys() const {
|
||||
|
||||
@ -9,15 +9,12 @@
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "common/logging/log.h"
|
||||
#include "common/types.h"
|
||||
|
||||
#define EmulatorSettings (*EmulatorSettingsImpl::GetInstance())
|
||||
#define CURRENT_CONFIG_SCHEMA_VERSION 1 // Increment when adding new settings
|
||||
|
||||
enum HideCursorState : int {
|
||||
Never,
|
||||
@ -51,6 +48,8 @@ struct Setting {
|
||||
std::optional<T> game_specific_value{};
|
||||
|
||||
Setting() = default;
|
||||
// Single-argument ctor: initialises both default_value and value so
|
||||
// that CleanMode can always recover the intended factory default.
|
||||
/*implicit*/ Setting(T init) : default_value(std::move(init)), value(default_value) {}
|
||||
|
||||
/// Return the active value under the given mode.
|
||||
@ -67,11 +66,13 @@ struct Setting {
|
||||
}
|
||||
|
||||
/// Write v to the base layer.
|
||||
/// Game-specific overrides are applied exclusively via Load(serial)
|
||||
void set(const T& v) {
|
||||
value = v;
|
||||
}
|
||||
|
||||
/// Discard the game-specific override.
|
||||
/// Discard the game-specific override; subsequent get(Default) will
|
||||
/// fall back to the base value.
|
||||
void reset_game_specific() {
|
||||
game_specific_value = std::nullopt;
|
||||
}
|
||||
@ -92,7 +93,11 @@ struct OverrideItem {
|
||||
std::function<void(void* group_ptr, const nlohmann::json& entry,
|
||||
std::vector<std::string>& changed)>
|
||||
apply;
|
||||
/// Return the value that should be written to the per-game config file.
|
||||
/// Falls back to base value if no game-specific override is set.
|
||||
std::function<nlohmann::json(const void* group_ptr)> get_for_save;
|
||||
|
||||
/// Clear game_specific_value for this field.
|
||||
std::function<void(void* group_ptr)> reset_game_specific;
|
||||
};
|
||||
|
||||
@ -101,25 +106,40 @@ inline OverrideItem make_override(const char* key, Setting<T> Struct::* member)
|
||||
return OverrideItem{
|
||||
key,
|
||||
[member, key](void* base, const nlohmann::json& entry, std::vector<std::string>& changed) {
|
||||
LOG_DEBUG(EmuSettings, "[make_override] Processing key: {}", key);
|
||||
LOG_DEBUG(EmuSettings, "[make_override] Entry JSON: {}", entry.dump());
|
||||
Struct* obj = reinterpret_cast<Struct*>(base);
|
||||
Setting<T>& dst = obj->*member;
|
||||
try {
|
||||
T newValue = entry.get<T>();
|
||||
LOG_DEBUG(EmuSettings, "[make_override] Parsed value: {}", newValue);
|
||||
LOG_DEBUG(EmuSettings, "[make_override] Current value: {}", dst.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.game_specific_value = newValue;
|
||||
LOG_DEBUG(EmuSettings, "[make_override] Successfully updated {}", key);
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(EmuSettings, "ERROR parsing {}: {}", key, e.what());
|
||||
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());
|
||||
}
|
||||
},
|
||||
|
||||
// --- get_for_save -------------------------------------------
|
||||
// Returns game_specific_value when present, otherwise base value.
|
||||
// This means a freshly-opened game-specific dialog still shows
|
||||
// useful (current-global) values rather than empty entries.
|
||||
[member](const void* base) -> nlohmann::json {
|
||||
const Struct* obj = reinterpret_cast<const Struct*>(base);
|
||||
const Setting<T>& src = obj->*member;
|
||||
return nlohmann::json(src.game_specific_value.value_or(src.value));
|
||||
},
|
||||
|
||||
// --- reset_game_specific ------------------------------------
|
||||
[member](void* base) {
|
||||
Struct* obj = reinterpret_cast<Struct*>(base);
|
||||
(obj->*member).reset_game_specific();
|
||||
@ -162,6 +182,7 @@ struct GeneralSettings {
|
||||
Setting<bool> show_fps_counter{false};
|
||||
Setting<int> console_language{1};
|
||||
|
||||
// return a vector of override descriptors (runtime, but tiny)
|
||||
std::vector<OverrideItem> GetOverrideableFields() const {
|
||||
return std::vector<OverrideItem>{
|
||||
make_override<GeneralSettings>("volume_slider", &GeneralSettings::volume_slider),
|
||||
@ -197,12 +218,11 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(GeneralSettings, install_dirs, addon_install_
|
||||
// Debug settings
|
||||
// -------------------------------
|
||||
struct DebugSettings {
|
||||
Setting<bool> separate_logging_enabled{false};
|
||||
Setting<bool> debug_dump{false};
|
||||
Setting<bool> shader_collect{false};
|
||||
Setting<bool> log_enabled{true};
|
||||
Setting<std::string> config_version{""};
|
||||
Setting<int> config_schema_version{CURRENT_CONFIG_SCHEMA_VERSION};
|
||||
Setting<bool> separate_logging_enabled{false}; // specific
|
||||
Setting<bool> debug_dump{false}; // specific
|
||||
Setting<bool> shader_collect{false}; // specific
|
||||
Setting<bool> log_enabled{true}; // specific
|
||||
Setting<std::string> config_version{""}; // specific
|
||||
|
||||
std::vector<OverrideItem> GetOverrideableFields() const {
|
||||
return std::vector<OverrideItem>{
|
||||
@ -214,22 +234,22 @@ struct DebugSettings {
|
||||
}
|
||||
};
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(DebugSettings, separate_logging_enabled, debug_dump,
|
||||
shader_collect, log_enabled, config_version,
|
||||
config_schema_version)
|
||||
shader_collect, log_enabled, config_version)
|
||||
|
||||
// -------------------------------
|
||||
// Input settings
|
||||
// -------------------------------
|
||||
|
||||
struct InputSettings {
|
||||
Setting<int> cursor_state{HideCursorState::Idle};
|
||||
Setting<int> cursor_hide_timeout{5};
|
||||
Setting<int> usb_device_backend{UsbBackendType::Real};
|
||||
Setting<int> cursor_state{HideCursorState::Idle}; // specific
|
||||
Setting<int> cursor_hide_timeout{5}; // specific
|
||||
Setting<int> usb_device_backend{UsbBackendType::Real}; // specific
|
||||
Setting<bool> use_special_pad{false};
|
||||
Setting<int> special_pad_class{1};
|
||||
Setting<bool> motion_controls_enabled{true};
|
||||
Setting<bool> motion_controls_enabled{true}; // specific
|
||||
Setting<bool> use_unified_input_config{true};
|
||||
Setting<std::string> default_controller_id{""};
|
||||
Setting<bool> background_controller_input{false};
|
||||
Setting<bool> background_controller_input{false}; // specific
|
||||
Setting<s32> camera_id{-1};
|
||||
|
||||
std::vector<OverrideItem> GetOverrideableFields() const {
|
||||
@ -249,7 +269,6 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(InputSettings, cursor_state, cursor_hide_time
|
||||
usb_device_backend, use_special_pad, special_pad_class,
|
||||
motion_controls_enabled, use_unified_input_config,
|
||||
default_controller_id, background_controller_input, camera_id)
|
||||
|
||||
// -------------------------------
|
||||
// Audio settings
|
||||
// -------------------------------
|
||||
@ -258,6 +277,7 @@ struct AudioSettings {
|
||||
Setting<std::string> main_output_device{"Default Device"};
|
||||
Setting<std::string> padSpk_output_device{"Default Device"};
|
||||
|
||||
// TODO add overrides
|
||||
std::vector<OverrideItem> GetOverrideableFields() const {
|
||||
return std::vector<OverrideItem>{
|
||||
make_override<AudioSettings>("mic_device", &AudioSettings::mic_device),
|
||||
@ -266,6 +286,7 @@ struct AudioSettings {
|
||||
&AudioSettings::padSpk_output_device)};
|
||||
}
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(AudioSettings, mic_device, main_output_device,
|
||||
padSpk_output_device)
|
||||
|
||||
@ -292,7 +313,7 @@ struct GPUSettings {
|
||||
Setting<bool> fsr_enabled{false};
|
||||
Setting<bool> rcas_enabled{true};
|
||||
Setting<int> rcas_attenuation{250};
|
||||
|
||||
// TODO add overrides
|
||||
std::vector<OverrideItem> GetOverrideableFields() const {
|
||||
return std::vector<OverrideItem>{
|
||||
make_override<GPUSettings>("null_gpu", &GPUSettings::null_gpu),
|
||||
@ -323,7 +344,6 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(GPUSettings, window_width, window_height, int
|
||||
direct_memory_access_enabled, dump_shaders, patch_shaders,
|
||||
vblank_frequency, full_screen, full_screen_mode, present_mode,
|
||||
hdr_allowed, fsr_enabled, rcas_enabled, rcas_attenuation)
|
||||
|
||||
// -------------------------------
|
||||
// Vulkan settings
|
||||
// -------------------------------
|
||||
@ -339,7 +359,6 @@ struct VulkanSettings {
|
||||
Setting<bool> vkguest_markers{false};
|
||||
Setting<bool> pipeline_cache_enabled{false};
|
||||
Setting<bool> pipeline_cache_archived{false};
|
||||
|
||||
std::vector<OverrideItem> GetOverrideableFields() const {
|
||||
return std::vector<OverrideItem>{
|
||||
make_override<VulkanSettings>("gpu_id", &VulkanSettings::gpu_id),
|
||||
@ -384,6 +403,7 @@ public:
|
||||
bool Load(const std::string& serial = "");
|
||||
void SetDefaultValues();
|
||||
|
||||
// Config mode
|
||||
ConfigMode GetConfigMode() const {
|
||||
return m_configMode;
|
||||
}
|
||||
@ -391,11 +411,16 @@ public:
|
||||
m_configMode = mode;
|
||||
}
|
||||
|
||||
//
|
||||
// Game-specific override management
|
||||
/// Clears all per-game overrides. Call this when a game exits so
|
||||
/// the emulator reverts to global settings.
|
||||
void ClearGameSpecificOverrides();
|
||||
|
||||
/// Reset a single field's game-specific override by its JSON ke
|
||||
void ResetGameSpecificValue(const std::string& key);
|
||||
|
||||
// General accessors
|
||||
// general accessors
|
||||
bool AddGameInstallDir(const std::filesystem::path& dir, bool enabled = true);
|
||||
std::vector<std::filesystem::path> GetGameInstallDirs() const;
|
||||
void SetAllGameInstallDirs(const std::vector<GameInstallDir>& dirs);
|
||||
@ -412,7 +437,47 @@ public:
|
||||
std::filesystem::path GetFontsDir();
|
||||
void SetFontsDir(const std::filesystem::path& dir);
|
||||
|
||||
// Overrideable fields accessors
|
||||
private:
|
||||
GeneralSettings m_general{};
|
||||
DebugSettings m_debug{};
|
||||
InputSettings m_input{};
|
||||
AudioSettings m_audio{};
|
||||
GPUSettings m_gpu{};
|
||||
VulkanSettings m_vulkan{};
|
||||
ConfigMode m_configMode{ConfigMode::Default};
|
||||
|
||||
static std::shared_ptr<EmulatorSettingsImpl> s_instance;
|
||||
static std::mutex s_mutex;
|
||||
|
||||
/// Apply overrideable fields from groupJson into group.game_specific_value.
|
||||
template <typename Group>
|
||||
void ApplyGroupOverrides(Group& group, const nlohmann::json& groupJson,
|
||||
std::vector<std::string>& changed) {
|
||||
for (auto& item : group.GetOverrideableFields()) {
|
||||
if (!groupJson.contains(item.key))
|
||||
continue;
|
||||
item.apply(&group, groupJson.at(item.key), changed);
|
||||
}
|
||||
}
|
||||
|
||||
// Write all overrideable fields from group into out (for game-specific save).
|
||||
template <typename Group>
|
||||
static void SaveGroupGameSpecific(const Group& group, nlohmann::json& out) {
|
||||
for (auto& item : group.GetOverrideableFields())
|
||||
out[item.key] = item.get_for_save(&group);
|
||||
}
|
||||
|
||||
// Discard every game-specific override in group.
|
||||
template <typename Group>
|
||||
static void ClearGroupOverrides(Group& group) {
|
||||
for (auto& item : group.GetOverrideableFields())
|
||||
item.reset_game_specific(&group);
|
||||
}
|
||||
|
||||
static void PrintChangedSummary(const std::vector<std::string>& changed);
|
||||
|
||||
public:
|
||||
// Add these getters to access overrideable fields
|
||||
std::vector<OverrideItem> GetGeneralOverrideableFields() const {
|
||||
return m_general.GetOverrideableFields();
|
||||
}
|
||||
@ -433,54 +498,6 @@ public:
|
||||
}
|
||||
std::vector<std::string> GetAllOverrideableKeys() const;
|
||||
|
||||
private:
|
||||
GeneralSettings m_general{};
|
||||
DebugSettings m_debug{};
|
||||
InputSettings m_input{};
|
||||
AudioSettings m_audio{};
|
||||
GPUSettings m_gpu{};
|
||||
VulkanSettings m_vulkan{};
|
||||
|
||||
// Store unknown top-level sections to preserve them across saves
|
||||
std::unordered_map<std::string, nlohmann::json> m_unknown_sections;
|
||||
|
||||
ConfigMode m_configMode{ConfigMode::Default};
|
||||
|
||||
static std::shared_ptr<EmulatorSettingsImpl> s_instance;
|
||||
static std::mutex s_mutex;
|
||||
|
||||
template <typename Group>
|
||||
void ApplyGroupOverrides(Group& group, const nlohmann::json& groupJson,
|
||||
std::vector<std::string>& changed) {
|
||||
for (auto& item : group.GetOverrideableFields()) {
|
||||
if (!groupJson.contains(item.key))
|
||||
continue;
|
||||
item.apply(&group, groupJson.at(item.key), changed);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Group>
|
||||
static void SaveGroupGameSpecific(const Group& group, nlohmann::json& out) {
|
||||
for (auto& item : group.GetOverrideableFields())
|
||||
out[item.key] = item.get_for_save(&group);
|
||||
}
|
||||
|
||||
template <typename Group>
|
||||
static void ClearGroupOverrides(Group& group) {
|
||||
for (auto& item : group.GetOverrideableFields())
|
||||
item.reset_game_specific(&group);
|
||||
}
|
||||
|
||||
static void PrintChangedSummary(const std::vector<std::string>& changed);
|
||||
|
||||
// Set of known section names
|
||||
bool IsKnownSection(const std::string& name) const {
|
||||
static const std::unordered_set<std::string> known_sections = {
|
||||
"General", "Debug", "Input", "Audio", "GPU", "Vulkan"};
|
||||
return known_sections.find(name) != known_sections.end();
|
||||
}
|
||||
|
||||
public:
|
||||
#define SETTING_FORWARD(group, Name, field) \
|
||||
auto Get##Name() const { \
|
||||
return (group).field.get(m_configMode); \
|
||||
@ -488,7 +505,6 @@ public:
|
||||
void Set##Name(const decltype((group).field.value)& v) { \
|
||||
(group).field.value = v; \
|
||||
}
|
||||
|
||||
#define SETTING_FORWARD_BOOL(group, Name, field) \
|
||||
bool Is##Name() const { \
|
||||
return (group).field.get(m_configMode); \
|
||||
@ -496,7 +512,6 @@ public:
|
||||
void Set##Name(bool v) { \
|
||||
(group).field.value = v; \
|
||||
}
|
||||
|
||||
#define SETTING_FORWARD_BOOL_READONLY(group, Name, field) \
|
||||
bool Is##Name() const { \
|
||||
return (group).field.get(m_configMode); \
|
||||
@ -532,9 +547,6 @@ public:
|
||||
SETTING_FORWARD_BOOL(m_debug, ShaderCollect, shader_collect)
|
||||
SETTING_FORWARD_BOOL(m_debug, LogEnabled, log_enabled)
|
||||
SETTING_FORWARD(m_debug, ConfigVersion, config_version)
|
||||
int GetConfigSchemaVersion() const {
|
||||
return m_debug.config_schema_version.get(m_configMode);
|
||||
}
|
||||
|
||||
// GPU Settings
|
||||
SETTING_FORWARD_BOOL(m_gpu, NullGPU, null_gpu)
|
||||
@ -598,4 +610,4 @@ public:
|
||||
#undef SETTING_FORWARD
|
||||
#undef SETTING_FORWARD_BOOL
|
||||
#undef SETTING_FORWARD_BOOL_READONLY
|
||||
};
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user