diff --git a/Source/Android/jni/Config/NativeConfig.cpp b/Source/Android/jni/Config/NativeConfig.cpp index 8478174dae..c524c0ea9e 100644 --- a/Source/Android/jni/Config/NativeConfig.cpp +++ b/Source/Android/jni/Config/NativeConfig.cpp @@ -11,7 +11,6 @@ #include "Core/ConfigLoaders/GameConfigLoader.h" #include "Core/ConfigLoaders/IsSettingSaveable.h" #include "jni/AndroidCommon/AndroidCommon.h" -#include "jni/Host.h" constexpr jint LAYER_BASE_OR_CURRENT = 0; constexpr jint LAYER_BASE = 1; @@ -143,10 +142,6 @@ Java_org_dolphinemu_dolphinemu_features_settings_model_NativeConfig_unloadGameIn JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_features_settings_model_NativeConfig_save( JNIEnv*, jclass, jint layer) { - // HostThreadLock is used to ensure we don't try to save to SYSCONF at the same time as - // emulation shutdown does - HostThreadLock guard; - return GetLayer(layer, {})->Save(); } diff --git a/Source/Core/Core/BootManager.cpp b/Source/Core/Core/BootManager.cpp index ae49a14b92..c5a526d262 100644 --- a/Source/Core/Core/BootManager.cpp +++ b/Source/Core/Core/BootManager.cpp @@ -26,7 +26,6 @@ #include "Core/AchievementManager.h" #include "Core/Boot/Boot.h" #include "Core/Config/MainSettings.h" -#include "Core/Config/SYSCONFSettings.h" #include "Core/ConfigLoaders/BaseConfigLoader.h" #include "Core/ConfigLoaders/NetPlayConfigLoader.h" #include "Core/ConfigManager.h" @@ -138,6 +137,8 @@ bool BootCore(Core::System& system, std::unique_ptr boot, Core::UpdateWantDeterminism(system, /*initial*/ true); + ConfigLoaders::TransferSYSCONFControlToGuest(); + if (system.IsWii()) { Core::InitializeWiiRoot(Core::WantsDeterminism()); @@ -146,13 +147,16 @@ bool BootCore(Core::System& system, std::unique_ptr boot, if (!Core::WantsDeterminism()) { Core::BackupWiiSettings(); - ConfigLoaders::SaveToSYSCONF(Config::LayerType::Meta); + ConfigLoaders::SaveToSYSCONF(Config::LayerType::Meta, + ConfigLoaders::SkipIfControlledByGuest::No); } else { - ConfigLoaders::SaveToSYSCONF(Config::LayerType::Meta, [](const Config::Location& location) { - return Config::GetActiveLayerForConfig(location) >= Config::LayerType::Movie; - }); + ConfigLoaders::SaveToSYSCONF( + Config::LayerType::Meta, ConfigLoaders::SkipIfControlledByGuest::No, + [](const Config::Location& location) { + return Config::GetActiveLayerForConfig(location) >= Config::LayerType::Movie; + }); } } @@ -173,43 +177,18 @@ bool BootCore(Core::System& system, std::unique_ptr boot, return Core::Init(system, std::move(boot), wsi); } -// SYSCONF can be modified during emulation by the user and internally, which makes it -// a bad idea to just always overwrite it with the settings from the base layer. -// -// Conversely, we also shouldn't just accept any changes to SYSCONF, as it may cause -// temporary settings (from Movie, Netplay, game INIs, etc.) to stick around. -// -// To avoid inconveniences in most cases, we accept changes that aren't being overridden by a -// non-base layer, and restore only the overridden settings. -static void RestoreSYSCONF() -{ - // This layer contains the new SYSCONF settings (including any temporary settings). - Config::Layer temp_layer(Config::LayerType::Base); - // Use a separate loader so the temp layer doesn't automatically save - ConfigLoaders::GenerateBaseConfigLoader()->Load(&temp_layer); - - for (const auto& setting : Config::SYSCONF_SETTINGS) - { - std::visit( - [&](auto* info) { - // If this setting was overridden, then we copy the base layer value back to the SYSCONF. - // Otherwise we leave the new value in the SYSCONF. - if (Config::GetActiveLayerForConfig(*info) == Config::LayerType::Base) - Config::SetBase(*info, temp_layer.Get(*info)); - }, - setting.config_info); - } - ConfigLoaders::SaveToSYSCONF(Config::LayerType::Base); -} - void RestoreConfig() { Core::ShutdownWiiRoot(); - if (!Core::WiiRootIsTemporary()) + if (Core::WiiRootIsTemporary()) + { + ConfigLoaders::TransferSYSCONFControlFromGuest(ConfigLoaders::WriteBackChangedValues::No); + } + else { Core::RestoreWiiSettings(Core::RestoreReason::EmulationEnd); - RestoreSYSCONF(); + ConfigLoaders::TransferSYSCONFControlFromGuest(ConfigLoaders::WriteBackChangedValues::Yes); } Config::ClearCurrentRunLayer(); diff --git a/Source/Core/Core/ConfigLoaders/BaseConfigLoader.cpp b/Source/Core/Core/ConfigLoaders/BaseConfigLoader.cpp index 811eb9974e..8b35d4d684 100644 --- a/Source/Core/Core/ConfigLoaders/BaseConfigLoader.cpp +++ b/Source/Core/Core/ConfigLoaders/BaseConfigLoader.cpp @@ -8,11 +8,13 @@ #include #include #include +#include #include #include #include "Common/CommonTypes.h" #include "Common/Config/Config.h" +#include "Common/Config/Layer.h" #include "Common/FileUtil.h" #include "Common/IniFile.h" #include "Common/Logging/Log.h" @@ -21,17 +23,68 @@ #include "Core/Config/SYSCONFSettings.h" #include "Core/ConfigLoaders/IsSettingSaveable.h" #include "Core/ConfigManager.h" -#include "Core/Core.h" #include "Core/IOS/IOS.h" #include "Core/IOS/USB/Bluetooth/BTBase.h" #include "Core/SysConf.h" #include "Core/System.h" +static bool s_sysconf_controlled_by_guest = false; +static std::recursive_mutex s_sysconf_lock; + namespace ConfigLoaders { -void SaveToSYSCONF(Config::LayerType layer, std::function predicate) +void TransferSYSCONFControlToGuest() { - if (Core::IsRunning(Core::System::GetInstance())) + std::lock_guard lock(s_sysconf_lock); + + ASSERT(!s_sysconf_controlled_by_guest); + s_sysconf_controlled_by_guest = true; +} + +void TransferSYSCONFControlFromGuest(WriteBackChangedValues write_back_changed_values) +{ + std::lock_guard lock(s_sysconf_lock); + + ASSERT(s_sysconf_controlled_by_guest); + s_sysconf_controlled_by_guest = false; + + if (write_back_changed_values == WriteBackChangedValues::No) + return; + + // SYSCONF can be modified during emulation by the user and internally, which makes it + // a bad idea to just always overwrite it with the settings from the base layer. + // + // Conversely, we also shouldn't just accept any changes to SYSCONF, as it may cause + // temporary settings (from Movie, Netplay, game INIs, etc.) to stick around. + // + // To avoid inconveniences in most cases, we accept changes that aren't being overridden by a + // non-base layer, and restore only the overridden settings. + + // This layer contains the new SYSCONF settings (including any temporary settings). + Config::Layer temp_layer(Config::LayerType::Base); + // Use a separate loader so the temp layer doesn't automatically save + GenerateBaseConfigLoader()->Load(&temp_layer); + + for (const auto& setting : Config::SYSCONF_SETTINGS) + { + std::visit( + [&](auto* info) { + // If this setting was overridden, then we copy the base layer value back to the + // SYSCONF. Otherwise we leave the new value in the SYSCONF. + if (Config::GetActiveLayerForConfig(*info) == Config::LayerType::Base) + Config::SetBase(*info, temp_layer.Get(*info)); + }, + setting.config_info); + } + + SaveToSYSCONF(Config::LayerType::Base, SkipIfControlledByGuest::No); +} + +void SaveToSYSCONF(Config::LayerType layer, SkipIfControlledByGuest skip, + std::function predicate) +{ + std::lock_guard lock(s_sysconf_lock); + if (skip == SkipIfControlledByGuest::Yes && s_sysconf_controlled_by_guest) return; IOS::HLE::Kernel ios; @@ -125,7 +178,7 @@ public: void Save(Config::Layer* layer) override { - SaveToSYSCONF(layer->GetLayer()); + SaveToSYSCONF(layer->GetLayer(), SkipIfControlledByGuest::Yes); std::map inis; @@ -180,7 +233,8 @@ public: private: void LoadFromSYSCONF(Config::Layer* layer) { - if (Core::IsRunning(Core::System::GetInstance())) + std::lock_guard lock(s_sysconf_lock); + if (s_sysconf_controlled_by_guest) return; IOS::HLE::Kernel ios; diff --git a/Source/Core/Core/ConfigLoaders/BaseConfigLoader.h b/Source/Core/Core/ConfigLoaders/BaseConfigLoader.h index 7c20a5e065..30a6d26e4b 100644 --- a/Source/Core/Core/ConfigLoaders/BaseConfigLoader.h +++ b/Source/Core/Core/ConfigLoaders/BaseConfigLoader.h @@ -15,7 +15,21 @@ struct Location; namespace ConfigLoaders { -void SaveToSYSCONF(Config::LayerType layer, +enum class WriteBackChangedValues +{ + No, + Yes, +}; + +enum class SkipIfControlledByGuest +{ + No, + Yes, +}; + +void TransferSYSCONFControlToGuest(); +void TransferSYSCONFControlFromGuest(WriteBackChangedValues write_back_changed_values); +void SaveToSYSCONF(Config::LayerType layer, SkipIfControlledByGuest skip, std::function predicate = {}); std::unique_ptr GenerateBaseConfigLoader(); } // namespace ConfigLoaders