diff --git a/Core/Config.cpp b/Core/Config.cpp index dce5afbc93..8eb170f641 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -637,22 +637,34 @@ bool DisplayLayoutConfig::InternalRotationIsPortrait() const { return (iInternalScreenRotation == ROTATION_LOCKED_VERTICAL || iInternalScreenRotation == ROTATION_LOCKED_VERTICAL180) && !g_Config.bSkipBufferEffects; } +bool DisplayLayoutConfig::ResetToDefault(std::string_view blockName) { + static const DisplayLayoutConfig defaultLayout = DisplayLayoutConfig(); + *this = defaultLayout; + if (endsWith(blockName, ".Portrait")) { + // TODO: On mobile, where the aspect is fixed, we should use the screen size to compute this properly, + // so the screen almost touches the top edge. + fDisplayOffsetY = 0.25f; + } + return true; +} + // These were previously part of Graphics. // It's instantiated into g_Config.displayLayoutLandscape and g_Config.displayLayoutPortrait. +// Defaults are set directly on the struct declaration (and adjusted per instance in ResetToDefault above). static const ConfigSetting displayLayoutSettings[] = { - ConfigSetting("BufferFiltering", SETTING(g_Config.displayLayoutLandscape, iDisplayFilter), SCALE_LINEAR, CfgFlag::PER_GAME), - ConfigSetting("DisplayStretch", SETTING(g_Config.displayLayoutLandscape, bDisplayStretch), false, CfgFlag::PER_GAME), - ConfigSetting("DisplayOffsetX", SETTING(g_Config.displayLayoutLandscape, fDisplayOffsetX), 0.5f, CfgFlag::PER_GAME), - ConfigSetting("DisplayOffsetY", SETTING(g_Config.displayLayoutLandscape, fDisplayOffsetY), 0.5f, CfgFlag::PER_GAME), - ConfigSetting("DisplayScale", SETTING(g_Config.displayLayoutLandscape, fDisplayScale), 1.0f, CfgFlag::PER_GAME), - ConfigSetting("DisplayIntegerScale", SETTING(g_Config.displayLayoutLandscape, bDisplayIntegerScale), false, CfgFlag::PER_GAME), - ConfigSetting("DisplayAspectRatio", SETTING(g_Config.displayLayoutLandscape, fDisplayAspectRatio), 1.0f, CfgFlag::PER_GAME), - ConfigSetting("IgnoreScreenInsets", SETTING(g_Config.displayLayoutLandscape, bIgnoreScreenInsets), true, CfgFlag::PER_GAME), - ConfigSetting("InternalScreenRotation", SETTING(g_Config.displayLayoutLandscape, iInternalScreenRotation), ROTATION_LOCKED_HORIZONTAL, CfgFlag::PER_GAME), - ConfigSetting("EnableCardboardVR", SETTING(g_Config.displayLayoutLandscape, bEnableCardboardVR), false, CfgFlag::PER_GAME), - ConfigSetting("CardboardScreenSize", SETTING(g_Config.displayLayoutLandscape, iCardboardScreenSize), 50, CfgFlag::PER_GAME), - ConfigSetting("CardboardXShift", SETTING(g_Config.displayLayoutLandscape, iCardboardXShift), 0, CfgFlag::PER_GAME), - ConfigSetting("CardboardYShift", SETTING(g_Config.displayLayoutLandscape, iCardboardYShift), 0, CfgFlag::PER_GAME), + ConfigSetting("BufferFiltering", SETTING(g_Config.displayLayoutLandscape, iDisplayFilter), CfgFlag::PER_GAME), + ConfigSetting("DisplayStretch", SETTING(g_Config.displayLayoutLandscape, bDisplayStretch), CfgFlag::PER_GAME), + ConfigSetting("DisplayOffsetX", SETTING(g_Config.displayLayoutLandscape, fDisplayOffsetX), CfgFlag::PER_GAME), + ConfigSetting("DisplayOffsetY", SETTING(g_Config.displayLayoutLandscape, fDisplayOffsetY), CfgFlag::PER_GAME), + ConfigSetting("DisplayScale", SETTING(g_Config.displayLayoutLandscape, fDisplayScale), CfgFlag::PER_GAME), + ConfigSetting("DisplayIntegerScale", SETTING(g_Config.displayLayoutLandscape, bDisplayIntegerScale), CfgFlag::PER_GAME), + ConfigSetting("DisplayAspectRatio", SETTING(g_Config.displayLayoutLandscape, fDisplayAspectRatio), CfgFlag::PER_GAME), + ConfigSetting("IgnoreScreenInsets", SETTING(g_Config.displayLayoutLandscape, bIgnoreScreenInsets), CfgFlag::PER_GAME), + ConfigSetting("InternalScreenRotation", SETTING(g_Config.displayLayoutLandscape, iInternalScreenRotation), CfgFlag::PER_GAME), + ConfigSetting("EnableCardboardVR", SETTING(g_Config.displayLayoutLandscape, bEnableCardboardVR), CfgFlag::PER_GAME), + ConfigSetting("CardboardScreenSize", SETTING(g_Config.displayLayoutLandscape, iCardboardScreenSize), CfgFlag::PER_GAME), + ConfigSetting("CardboardXShift", SETTING(g_Config.displayLayoutLandscape, iCardboardXShift), CfgFlag::PER_GAME), + ConfigSetting("CardboardYShift", SETTING(g_Config.displayLayoutLandscape, iCardboardYShift), CfgFlag::PER_GAME), }; static const ConfigSetting graphicsSettings[] = { @@ -1064,7 +1076,7 @@ static const ConfigSetting vrSettings[] = { }; // The first column says what structure the parameters are relative to. -static const ConfigSectionMeta sectionMeta[] = { +static const ConfigSectionMeta g_sectionMeta[] = { { &g_Config, generalSettings, ARRAY_SIZE(generalSettings), "General" }, { &g_Config, cpuSettings, ARRAY_SIZE(cpuSettings), "CPU" }, { &g_Config, graphicsSettings, ARRAY_SIZE(graphicsSettings), "Graphics" }, @@ -1081,54 +1093,32 @@ static const ConfigSectionMeta sectionMeta[] = { { &g_Config.displayLayoutPortrait, displayLayoutSettings, ARRAY_SIZE(displayLayoutSettings), "DisplayLayout.Portrait"}, // These we don't want to read from the old settings, since for most people, those settings will be bad. }; -const size_t numSections = ARRAY_SIZE(sectionMeta); - -static inline void IterateSettingsIni(IniFile &iniFile, std::function func, bool tryFallback) { - for (size_t i = 0; i < numSections; ++i) { - Section *section = iniFile.GetSection(sectionMeta[i].section); - // Not found? Try the fallback (to upgrade settings that have been moved from old sections). - if (!section && tryFallback) { - section = iniFile.GetSection(sectionMeta[i].fallbackSection); - } - // Still not found? Create the original section. - if (!section) { - section = iniFile.GetOrCreateSection(sectionMeta[i].section); - } - // Now section is guaranteed to be valid. - ConfigBlock *configBlock = sectionMeta[i].configBlock; - for (size_t j = 0; j < sectionMeta[i].settingsCount; j++) { - func(configBlock, section, sectionMeta[i].settings[j]); +ConfigBlock *GetConfigBlockForSection(std::string_view sectionName) { + for (const ConfigSectionMeta &meta : g_sectionMeta) { + if (equals(meta.section, sectionName)) { + return meta.configBlock; } } + return nullptr; } -static inline void IterateSettings(std::function func) { - for (size_t i = 0; i < numSections; ++i) { - ConfigBlock *configBlock = sectionMeta[i].configBlock; - for (size_t j = 0; j < sectionMeta[i].settingsCount; j++) { - func(configBlock, sectionMeta[i].settings[j]); - } - } -} +const size_t numSections = ARRAY_SIZE(g_sectionMeta); -static inline void IterateConfigBlock(ConfigSectionMeta *meta, std::function func) { - for (size_t j = 0; j < meta->settingsCount; j++) { - func(*meta, meta->settings[j]); - } -} - -std::map &Config::getPtrLUT() { - static std::map lut; +std::map> &Config::getPtrLUT() { + static std::map> lut; return lut; } Config::Config() { // Initialize the pointer->setting lookup map. auto ref = getPtrLUT(); - IterateSettings([&ref](ConfigBlock *configBlock, const ConfigSetting &setting) { - const void *ptr = setting.GetVoidPtr(configBlock); - ref[ptr] = &setting; - }); + for (size_t i = 0; i < numSections; ++i) { + ConfigBlock *configBlock = g_sectionMeta[i].configBlock; + for (size_t j = 0; j < g_sectionMeta[i].settingsCount; j++) { + const void *ptr = g_sectionMeta[i].settings[j].GetVoidPtr(configBlock); + ref[ptr] = std::make_pair(configBlock, &g_sectionMeta[i].settings[j]); + } + } } Config::~Config() { @@ -1162,11 +1152,15 @@ bool Config::LoadAppendedConfig() { return false; } - IterateSettingsIni(iniFile, [&iniFile](ConfigBlock *configBlock, Section *section, const ConfigSetting &setting) { - if (section->HasKey(setting.iniKey_)) { - setting.ReadFromIniSection(configBlock, section, true); + for (const ConfigSectionMeta &meta : g_sectionMeta) { + Section *section = iniFile.GetSection(meta.section); + if (!section) { + continue; } - }, true); + for (size_t j = 0; j < meta.settingsCount; j++) { + meta.settings[j].ReadFromIniSection(meta.configBlock, section, false); + } + } INFO_LOG(Log::Loader, "Loaded appended config '%s'.", appendedConfigFileName_.c_str()); @@ -1184,6 +1178,28 @@ void Config::UpdateAfterSettingAutoFrameSkip() { } } +void Config::ReadAllSettings(const IniFile &iniFile) { + // Read settings. Note, configblocks can now support their own defaulting mechanism. + for (const ConfigSectionMeta &meta : g_sectionMeta) { + const Section *section = iniFile.GetSection(meta.section); + ConfigBlock *configBlock = meta.configBlock; + // Not found? Try the fallback (to upgrade settings that have been moved from old sections). + if (!section && !meta.fallbackSectionName.empty()) { + section = iniFile.GetSection(meta.fallbackSectionName); + configBlock = GetConfigBlockForSection(meta.fallbackSectionName); + } + // If section is still null, we'll handle that gracefully by resetting to defaults. + _dbg_assert_(configBlock); + bool applyDefaultPerSetting = true; + if (configBlock->ResetToDefault(meta.section)) { + applyDefaultPerSetting = false; + } + for (size_t j = 0; j < meta.settingsCount; j++) { + meta.settings[j].ReadFromIniSection(configBlock, section, applyDefaultPerSetting); + } + } +} + void Config::Load(const char *iniFileName, const char *controllerIniFilename) { double startTime = time_now_d(); @@ -1205,9 +1221,7 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) { // Continue anyway to initialize the config. } - IterateSettingsIni(iniFile, [](ConfigBlock *configBlock, const Section *section, const ConfigSetting &setting) { - setting.ReadFromIniSection(configBlock, section, true); - }, true); + ReadAllSettings(iniFile); iRunCount++; @@ -1331,11 +1345,14 @@ bool Config::Save(const char *saveReason) { // Need to do this somewhere... bFirstRun = false; - IterateSettingsIni(iniFile, [&](ConfigBlock *configBlock, Section *section, const ConfigSetting &setting) { - if (!gameSpecific_ || !setting.PerGame()) { - setting.WriteToIniSection(configBlock, section); + // Do the writing. + for (const ConfigSectionMeta &meta : g_sectionMeta) { + Section *section = iniFile.GetOrCreateSection(meta.section); + ConfigBlock *configBlock = meta.configBlock; + for (size_t j = 0; j < meta.settingsCount; j++) { + meta.settings[j].WriteToIniSection(configBlock, section); } - }, false); + } Section *recent = iniFile.GetOrCreateSection("Recent"); recent->Set("MaxRecent", iMaxRecent); @@ -1544,9 +1561,17 @@ void Config::RestoreDefaults(RestoreSettingsBits whatToRestore, bool log) { Load(); } else { if (whatToRestore & RestoreSettingsBits::SETTINGS) { - IterateSettings([log](ConfigBlock *configBlock, const ConfigSetting &setting) { - setting.RestoreToDefault(configBlock, log); - }); + // Read settings. Note, ConfigBlocks can now support their own defaulting mechanism. + for (const auto &meta : g_sectionMeta) { + ConfigBlock *configBlock = meta.configBlock; + bool applyDefaultPerSetting = true; + if (!configBlock->ResetToDefault(meta.section)) { + // Reset the settings one by one. + for (size_t j = 0; j < meta.settingsCount; j++) { + meta.settings[j].RestoreToDefault(configBlock, log); + } + } + } } if (whatToRestore & RestoreSettingsBits::CONTROLS) { @@ -1629,11 +1654,15 @@ bool Config::SaveGameConfig(const std::string &gameId, std::string_view titleFor PreSaveCleanup(); - IterateSettingsIni(iniFile, [](ConfigBlock *configBlock, Section *section, const ConfigSetting &setting) { - if (setting.PerGame()) { - setting.WriteToIniSection(configBlock, section); + for (const ConfigSectionMeta &meta : g_sectionMeta) { + Section *section = iniFile.GetOrCreateSection(meta.section); + ConfigBlock *configBlock = meta.configBlock; + for (size_t j = 0; j < meta.settingsCount; j++) { + if (meta.settings[j].PerGame()) { + meta.settings[j].WriteToIniSection(configBlock, section); + } } - }, false); + } Section *postShaderSetting = iniFile.GetOrCreateSection("PostShaderSetting"); postShaderSetting->Clear(); @@ -1691,11 +1720,18 @@ bool Config::LoadGameConfig(const std::string &gameId) { vPostShaderNames.push_back(v); } - IterateSettingsIni(iniFile, [](ConfigBlock *configBlock, const Section *section, const ConfigSetting &setting) { - if (setting.PerGame()) { - setting.ReadFromIniSection(configBlock, section, true); + for (const ConfigSectionMeta &meta : g_sectionMeta) { + Section *section = iniFile.GetSection(meta.section); + ConfigBlock *configBlock = meta.configBlock; + // Not found? Try the fallback (to upgrade settings that have been moved from old sections). + if (!section && !meta.fallbackSectionName.empty()) { + section = iniFile.GetSection(meta.fallbackSectionName); + configBlock = GetConfigBlockForSection(meta.fallbackSectionName); } - }, true); + for (size_t j = 0; j < meta.settingsCount; j++) { + meta.settings[j].ReadFromIniSection(configBlock, section, false); + } + } KeyMap::LoadFromIni(iniFile); @@ -1721,9 +1757,8 @@ void Config::UnloadGameConfig() { // Reload all settings from the main ini file. IniFile iniFile; iniFile.Load(iniFilename_); - IterateSettingsIni(iniFile, [](ConfigBlock *configBlock, const Section *section, const ConfigSetting &setting) { - setting.ReadFromIniSection(configBlock, section, true); - }, true); + + ReadAllSettings(iniFile); auto postShaderSetting = iniFile.GetOrCreateSection("PostShaderSetting")->ToMap(); mPostShaderSetting.clear(); @@ -1778,11 +1813,11 @@ void Config::ResetControlLayout() { } void Config::GetReportingInfo(UrlEncoder &data) const { - for (size_t i = 0; i < numSections; ++i) { - const std::string prefix = join("config.", sectionMeta[i].section); - ConfigBlock *configBlock = sectionMeta[i].configBlock; - for (size_t j = 0; j < sectionMeta[i].settingsCount; j++) { - sectionMeta[i].settings[j].ReportSetting(configBlock, data, prefix); + for (const ConfigSectionMeta &meta : g_sectionMeta) { + const std::string prefix = join("config.", meta.section); + ConfigBlock *configBlock = meta.configBlock; + for (size_t j = 0; j < meta.settingsCount; j++) { + meta.settings[j].ReportSetting(configBlock, data, prefix); } } } diff --git a/Core/Config.h b/Core/Config.h index 196aebabe5..09acf0b059 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -39,6 +39,7 @@ namespace http { struct UrlEncoder; class Section; +class IniFile; class PlayTimeTracker { public: @@ -65,6 +66,9 @@ struct ConfigSetting; struct ConfigBlock { virtual ~ConfigBlock() = default; + virtual bool CanResetToDefault() const { return false; } + // If a block returns false here (like Config itself does), resetting to default will happen by the old per-setting mechanism. + virtual bool ResetToDefault(std::string_view blockName) { return false; } }; struct ConfigSectionMeta { @@ -72,26 +76,29 @@ struct ConfigSectionMeta { const ConfigSetting *settings; size_t settingsCount; std::string_view section; - std::string_view fallbackSection; // used if section is not found (useful when moving settings into a struct from Config). + std::string_view fallbackSectionName; // used if section is not found (useful when moving settings into a struct from Config). }; struct DisplayLayoutConfig : public ConfigBlock { - int iDisplayFilter; // 1 = linear, 2 = nearest - bool bDisplayStretch; // Automatically matches the aspect ratio of the window. - float fDisplayOffsetX; - float fDisplayOffsetY; - float fDisplayScale; // Relative to the most constraining axis (x or y). - bool bDisplayIntegerScale; // Snaps scaling to integer scale factors in raw pixels. - float fDisplayAspectRatio; // Stored relative to the PSP's native ratio, so 1.0 is the normal pixel aspect ratio. - int iInternalScreenRotation; // The internal screen rotation angle. Useful for vertical SHMUPs and similar. - bool bIgnoreScreenInsets; // Android: Center screen disregarding insets if this is enabled. + int iDisplayFilter = SCALE_LINEAR; // 1 = linear, 2 = nearest + bool bDisplayStretch = false; // Automatically matches the aspect ratio of the window. + float fDisplayOffsetX = 0.5f; + float fDisplayOffsetY = 0.5f; + float fDisplayScale = 1.0f; // Relative to the most constraining axis (x or y). + bool bDisplayIntegerScale = false; // Snaps scaling to integer scale factors in raw pixels. + float fDisplayAspectRatio = 1.0f; // Stored relative to the PSP's native ratio, so 1.0 is the normal pixel aspect ratio. + int iInternalScreenRotation = ROTATION_LOCKED_HORIZONTAL; // The internal screen rotation angle. Useful for vertical SHMUPs and similar. + bool bIgnoreScreenInsets = true; // Android: Center screen disregarding insets if this is enabled. - bool bEnableCardboardVR; // Cardboard Master Switch - int iCardboardScreenSize; // Screen Size (in %) - int iCardboardXShift; // X-Shift of Screen (in %) - int iCardboardYShift; // Y-Shift of Screen (in %) + // Deprecated + bool bEnableCardboardVR = false; // Cardboard Master Switch + int iCardboardScreenSize = 50; // Screen Size (in %) + int iCardboardXShift = 0; // X-Shift of Screen (in %) + int iCardboardYShift = 0; // Y-Shift of Screen (in %) bool InternalRotationIsPortrait() const; + bool CanResetToDefault() const override { return true; } + bool ResetToDefault(std::string_view blockName) override; }; struct Config : public ConfigBlock { @@ -689,16 +696,20 @@ public: return orientation == DeviceOrientation::Portrait ? displayLayoutPortrait : displayLayoutLandscape; } -protected: +private: void LoadStandardControllerIni(); void PostLoadCleanup(); void PreSaveCleanup(); void PostSaveCleanup(); - static std::map &getPtrLUT(); + friend struct ConfigSetting; + + static std::map> &getPtrLUT(); + + // Applies defaults for missing settings. + void ReadAllSettings(const IniFile &iniFile); -private: bool reload_ = false; bool gameSpecific_ = false; @@ -711,9 +722,6 @@ private: Path appendedConfigFileName_; // A set make more sense, but won't have many entry, and I dont want to include the whole std::set header here std::vector appendedConfigUpdatedGames_; - - // TODO: Remove hack. - friend struct ConfigSetting; }; std::string CreateRandMAC(); diff --git a/Core/ConfigSettings.cpp b/Core/ConfigSettings.cpp index 2f51e7a66c..ab4358a59d 100644 --- a/Core/ConfigSettings.cpp +++ b/Core/ConfigSettings.cpp @@ -9,7 +9,7 @@ #include "Core/Config.h" bool ConfigSetting::perGame(void *ptr) { - return g_Config.gameSpecific_ && g_Config.getPtrLUT().count(ptr) > 0 && g_Config.getPtrLUT()[ptr]->PerGame(); + return g_Config.IsGameSpecific() && g_Config.getPtrLUT().count(ptr) > 0 && g_Config.getPtrLUT()[ptr].second->PerGame(); } bool ConfigSetting::ReadFromIniSection(ConfigBlock *configBlock, const Section *section, bool applyDefaultIfMissing) const { @@ -18,7 +18,7 @@ bool ConfigSetting::ReadFromIniSection(ConfigBlock *configBlock, const Section * case Type::TYPE_BOOL: { bool *target = (bool *)(owner + offset_); - if (!section->Get(iniKey_, target)) { + if (!section || !section->Get(iniKey_, target)) { if (applyDefaultIfMissing) { *target = defaultCallback_.b ? defaultCallback_.b() : default_.b; } @@ -29,14 +29,14 @@ bool ConfigSetting::ReadFromIniSection(ConfigBlock *configBlock, const Section * case Type::TYPE_INT: { int *target = (int *)(owner + offset_); - if (translateFrom_) { + if (translateFrom_ && section) { std::string value; if (section->Get(iniKey_, &value)) { *((int *)(owner + offset_)) = translateFrom_(value); return true; } } - if (!section->Get(iniKey_, target)) { + if (!section || !section->Get(iniKey_, target)) { if (applyDefaultIfMissing) { *target = defaultCallback_.i ? defaultCallback_.i() : default_.i; } @@ -47,7 +47,7 @@ bool ConfigSetting::ReadFromIniSection(ConfigBlock *configBlock, const Section * case Type::TYPE_UINT32: { uint32_t *target = (uint32_t *)(owner + offset_); - if (!section->Get(iniKey_, target)) { + if (!section || !section->Get(iniKey_, target)) { if (applyDefaultIfMissing) { *target = defaultCallback_.u ? defaultCallback_.u() : default_.u; } @@ -58,7 +58,7 @@ bool ConfigSetting::ReadFromIniSection(ConfigBlock *configBlock, const Section * case Type::TYPE_UINT64: { uint64_t *target = (uint64_t *)(owner + offset_); - if (!section->Get(iniKey_, target)) { + if (!section || !section->Get(iniKey_, target)) { if (applyDefaultIfMissing) { *target = defaultCallback_.lu ? defaultCallback_.lu() : default_.lu; } @@ -69,7 +69,7 @@ bool ConfigSetting::ReadFromIniSection(ConfigBlock *configBlock, const Section * case Type::TYPE_FLOAT: { float *target = (float *)(owner + offset_); - if (!section->Get(iniKey_, target)) { + if (!section || !section->Get(iniKey_, target)) { if (applyDefaultIfMissing) { *target = defaultCallback_.f ? defaultCallback_.f() : default_.f; } @@ -80,7 +80,7 @@ bool ConfigSetting::ReadFromIniSection(ConfigBlock *configBlock, const Section * case Type::TYPE_STRING: { std::string *target = (std::string *)(owner + offset_); - if (!section->Get(iniKey_, target)) { + if (!section || !section->Get(iniKey_, target)) { if (applyDefaultIfMissing) { if (defaultCallback_.s) { *target = defaultCallback_.s(); @@ -98,7 +98,7 @@ bool ConfigSetting::ReadFromIniSection(ConfigBlock *configBlock, const Section * { // No support for callbacks for these yet. that's not an issue. std::vector *ptr = (std::vector *)(owner + offset_); - if (!section->Get(iniKey_, ptr)) { + if (!section || !section->Get(iniKey_, ptr)) { if (applyDefaultIfMissing && default_.v) { CopyStrings(ptr, *default_.v); } @@ -112,16 +112,16 @@ bool ConfigSetting::ReadFromIniSection(ConfigBlock *configBlock, const Section * ConfigTouchPos defaultTouchPos = defaultCallback_.touchPos ? defaultCallback_.touchPos() : default_.touchPos; ConfigTouchPos *touchPos = ((ConfigTouchPos *)(owner + offset_)); - if (!section->Get(iniKey_, &touchPos->x) && applyDefaultIfMissing) { + if (!section || (!section->Get(iniKey_, &touchPos->x) && applyDefaultIfMissing)) { touchPos->x = defaultTouchPos.x; } - if (!section->Get(ini2_, &touchPos->y) && applyDefaultIfMissing) { + if (!section || (!section->Get(ini2_, &touchPos->y) && applyDefaultIfMissing)) { touchPos->y = defaultTouchPos.y; } - if (!section->Get(ini3_, &touchPos->scale) && applyDefaultIfMissing) { + if (!section || (!section->Get(ini3_, &touchPos->scale) && applyDefaultIfMissing)) { touchPos->scale = defaultTouchPos.scale; } - if (ini4_ && section->Get(ini4_, &touchPos->show)) { + if (ini4_ && section && section->Get(ini4_, &touchPos->show)) { // do nothing, succeeded. } else if (applyDefaultIfMissing) { touchPos->show = defaultTouchPos.show; @@ -132,7 +132,7 @@ bool ConfigSetting::ReadFromIniSection(ConfigBlock *configBlock, const Section * { Path *target = (Path *)(owner + offset_); std::string tmp; - if (!section->Get(iniKey_, &tmp)) { + if (!section || !section->Get(iniKey_, &tmp)) { if (applyDefaultIfMissing) { if (defaultCallback_.p) { *target = defaultCallback_.p(); @@ -150,19 +150,19 @@ bool ConfigSetting::ReadFromIniSection(ConfigBlock *configBlock, const Section * ConfigCustomButton defaultCustomButton = defaultCallback_.customButton ? defaultCallback_.customButton() : default_.customButton; ConfigCustomButton *customButton = ((ConfigCustomButton *)(owner + offset_)); - if (!section->Get(iniKey_, &customButton->key) && applyDefaultIfMissing) { + if (!section || (!section->Get(iniKey_, &customButton->key) && applyDefaultIfMissing)) { customButton->key = defaultCustomButton.key; } - if (!section->Get(ini2_, &customButton->image) && applyDefaultIfMissing) { + if (!section || (!section->Get(ini2_, &customButton->image) && applyDefaultIfMissing)) { customButton->image = defaultCustomButton.image; } - if (!section->Get(ini3_, &customButton->shape) && applyDefaultIfMissing) { + if (!section || (!section->Get(ini3_, &customButton->shape) && applyDefaultIfMissing)) { customButton->shape = defaultCustomButton.shape; } - if (!section->Get(ini4_, &customButton->toggle) && applyDefaultIfMissing) { + if (!section || (!section->Get(ini4_, &customButton->toggle) && applyDefaultIfMissing)) { customButton->toggle = defaultCustomButton.toggle; } - if (!section->Get(ini5_, &customButton->repeat) && applyDefaultIfMissing) { + if (!section || (!section->Get(ini5_, &customButton->repeat) && applyDefaultIfMissing)) { customButton->repeat = defaultCustomButton.repeat; } return true; @@ -177,6 +177,7 @@ void ConfigSetting::WriteToIniSection(const ConfigBlock *configBlock, Section *s if (!SaveSetting()) { return; } + _dbg_assert_(section); const char *owner = (const char *)configBlock; switch (type_) { @@ -232,6 +233,9 @@ void ConfigSetting::WriteToIniSection(const ConfigBlock *configBlock, Section *s } bool ConfigSetting::RestoreToDefault(ConfigBlock *configBlock, bool log) const { + // If the block supports resetting itself, don't allow per-setting resets. Shake them out with this assert. + _dbg_assert_(!configBlock->CanResetToDefault()); + const char *owner = (const char *)configBlock; switch (type_) { case Type::TYPE_BOOL: @@ -363,6 +367,27 @@ bool ConfigSetting::RestoreToDefault(ConfigBlock *configBlock, bool log) const { return false; } +// Might be used to copy individual settings from defaulted blocks. Didn't end up using this for now. +void ConfigSetting::CopyFromBlock(const ConfigBlock *other) { + const char *otherOwner = (const char *)other; + const char *thisOwner = (const char *)this; + switch (type_) { + case Type::TYPE_BOOL: *(bool *)(thisOwner + offset_) = *(const bool *)(otherOwner + offset_); break; + case Type::TYPE_INT: *(int *)(thisOwner + offset_) = *(const int *)(otherOwner + offset_); break; + case Type::TYPE_UINT32: *(uint32_t *)(thisOwner + offset_) = *(const uint32_t *)(otherOwner + offset_); break; + case Type::TYPE_UINT64: *(uint64_t *)(thisOwner + offset_) = *(const uint64_t *)(otherOwner + offset_); break; + case Type::TYPE_FLOAT: *(float *)(thisOwner + offset_) = *(const float *)(otherOwner + offset_); break; + case Type::TYPE_STRING: *(std::string *)(thisOwner + offset_) = *(const std::string *)(otherOwner + offset_); break; + case Type::TYPE_STRING_VECTOR: *(std::vector *)(thisOwner + offset_) = *(const std::vector *)(otherOwner + offset_); break; + case Type::TYPE_PATH: *(Path *)(thisOwner + offset_) = *(const Path *)(otherOwner + offset_); break; + case Type::TYPE_TOUCH_POS: *(ConfigTouchPos *)(thisOwner + offset_) = *(const ConfigTouchPos *)(otherOwner + offset_); break; + case Type::TYPE_CUSTOM_BUTTON: *(ConfigCustomButton *)(thisOwner + offset_) = *(const ConfigCustomButton *)(otherOwner + offset_); break; + default: + _dbg_assert_msg_(false, "CopyFromBlock(%.*s): Unexpected setting type: %d", STR_VIEW(iniKey_), (int)type_); + return; + } +} + void ConfigSetting::ReportSetting(const ConfigBlock *configBlock, UrlEncoder &data, const std::string &prefix) const { if (!Report()) return; @@ -373,8 +398,8 @@ void ConfigSetting::ReportSetting(const ConfigBlock *configBlock, UrlEncoder &da switch (type_) { case Type::TYPE_BOOL: return data.Add(key, *(const bool *)(owner + offset_)); case Type::TYPE_INT: return data.Add(key, *(const int *)(owner + offset_)); - case Type::TYPE_UINT32: return data.Add(key, *(const u32 *)(owner + offset_)); - case Type::TYPE_UINT64: return data.Add(key, *(const u64 *)(owner + offset_)); + case Type::TYPE_UINT32: return data.Add(key, *(const uint32_t *)(owner + offset_)); + case Type::TYPE_UINT64: return data.Add(key, *(const uint64_t *)(owner + offset_)); case Type::TYPE_FLOAT: return data.Add(key, *(const float *)(owner + offset_)); case Type::TYPE_STRING: return data.Add(key, *(const std::string *)(owner + offset_)); case Type::TYPE_STRING_VECTOR: return data.Add(key, *(const std::vector *)(owner + offset_)); diff --git a/Core/ConfigSettings.h b/Core/ConfigSettings.h index 3830d673ed..deb7f89671 100644 --- a/Core/ConfigSettings.h +++ b/Core/ConfigSettings.h @@ -73,12 +73,24 @@ struct ConfigSetting { default_.b = def; } + ConfigSetting(std::string_view ini, const char *owner, bool *v, CfgFlag flags) noexcept + : iniKey_(ini), type_(Type::TYPE_BOOL), flags_(flags), offset_((const char *)v - owner) { + defaultCallback_.b = nullptr; + default_.b = false; // unused with this constructor + } + ConfigSetting(std::string_view ini, const char *owner, int *v, int def, CfgFlag flags) noexcept : iniKey_(ini), type_(Type::TYPE_INT), flags_(flags), offset_((const char *)v - owner) { defaultCallback_.i = nullptr; default_.i = def; } + ConfigSetting(std::string_view ini, const char *owner, int *v, CfgFlag flags) noexcept + : iniKey_(ini), type_(Type::TYPE_INT), flags_(flags), offset_((const char *)v - owner) { + defaultCallback_.i = nullptr; + default_.i = 0; // unused with this constructor + } + ConfigSetting(std::string_view ini, const char *owner, int *v, int def, std::string (*transTo)(int), int (*transFrom)(const std::string &), CfgFlag flags) noexcept : iniKey_(ini), type_(Type::TYPE_INT), flags_(flags), translateTo_(transTo), translateFrom_(transFrom), offset_((const char *)v - owner) { defaultCallback_.i = nullptr; @@ -103,6 +115,12 @@ struct ConfigSetting { default_.f = def; } + ConfigSetting(std::string_view ini, const char *owner, float *v, CfgFlag flags) noexcept + : iniKey_(ini), type_(Type::TYPE_FLOAT), flags_(flags), offset_((const char *)v - owner) { + defaultCallback_.f = nullptr; + default_.f = 0.0f; + } + ConfigSetting(std::string_view ini, const char *owner, std::string *v, const char *def, CfgFlag flags) noexcept : iniKey_(ini), type_(Type::TYPE_STRING), flags_(flags), offset_((const char *)v - owner) { defaultCallback_.s = nullptr; @@ -169,6 +187,8 @@ struct ConfigSetting { bool ReadFromIniSection(ConfigBlock *configBlock, const Section *section, bool applyDefaultIfMissing) const; + void CopyFromBlock(const ConfigBlock *other); + // Yes, this can be const because what's modified is not the ConfigSetting struct, but the value which is stored elsewhere. // Should actually be called WriteToIni or something. void WriteToIniSection(const ConfigBlock *configBlock, Section *section) const; diff --git a/Core/ConfigValues.h b/Core/ConfigValues.h index e02d06e714..e5e98aae87 100644 --- a/Core/ConfigValues.h +++ b/Core/ConfigValues.h @@ -122,6 +122,7 @@ enum class AudioSyncMode { CLASSIC_PITCH = 1, }; +// TODO: We can make this more fine-grained. enum class RestoreSettingsBits : int { SETTINGS = 1, CONTROLS = 2,