// Copyright 2016 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "Common/Config/Config.h" #include #include #include #include #include #include #include namespace Config { using Layers = std::map>; static Layers s_layers; static std::vector> s_callbacks; static size_t s_next_callback_id = 0; static u32 s_callback_guards = 0; static std::atomic s_config_version = 0; static std::shared_mutex s_layers_rw_lock; using ReadLock = std::shared_lock; using WriteLock = std::unique_lock; static void AddLayerInternal(std::shared_ptr layer) { { WriteLock lock(s_layers_rw_lock); const Config::LayerType layer_type = layer->GetLayer(); s_layers.insert_or_assign(layer_type, std::move(layer)); } OnConfigChanged(); } void AddLayer(std::unique_ptr loader) { AddLayerInternal(std::make_shared(std::move(loader))); } std::shared_ptr GetLayer(LayerType layer) { ReadLock lock(s_layers_rw_lock); std::shared_ptr result; const auto it = s_layers.find(layer); if (it != s_layers.end()) { result = it->second; } return result; } void RemoveLayer(LayerType layer) { { WriteLock lock(s_layers_rw_lock); s_layers.erase(layer); } OnConfigChanged(); } ConfigChangedCallbackID AddConfigChangedCallback(ConfigChangedCallback func) { const ConfigChangedCallbackID callback_id{s_next_callback_id}; ++s_next_callback_id; s_callbacks.emplace_back(std::make_pair(callback_id, std::move(func))); return callback_id; } void RemoveConfigChangedCallback(ConfigChangedCallbackID callback_id) { for (auto it = s_callbacks.begin(); it != s_callbacks.end(); ++it) { if (it->first == callback_id) { s_callbacks.erase(it); return; } } } void OnConfigChanged() { // Increment the config version to invalidate caches. // To ensure that getters do not return stale data, this should always be done // even when callbacks are suppressed. s_config_version.fetch_add(1, std::memory_order_relaxed); if (s_callback_guards) return; for (const auto& callback : s_callbacks) callback.second(); } u64 GetConfigVersion() { return s_config_version.load(std::memory_order_relaxed); } // Explicit load and save of layers void Load() { { ReadLock lock(s_layers_rw_lock); for (auto& layer : s_layers) layer.second->Load(); } OnConfigChanged(); } void Save() { { ReadLock lock(s_layers_rw_lock); for (auto& layer : s_layers) layer.second->Save(); } OnConfigChanged(); } void Init() { // These layers contain temporary values ClearCurrentRunLayer(); } void Shutdown() { WriteLock lock(s_layers_rw_lock); s_layers.clear(); } void ClearCurrentRunLayer() { WriteLock lock(s_layers_rw_lock); s_layers.insert_or_assign(LayerType::CurrentRun, std::make_shared(LayerType::CurrentRun)); } static const std::map system_to_name = { {System::Main, "Dolphin"}, {System::GCPad, "GCPad"}, {System::WiiPad, "Wiimote"}, {System::GCKeyboard, "GCKeyboard"}, {System::GFX, "Graphics"}, {System::Logger, "Logger"}, {System::Debugger, "Debugger"}, {System::SYSCONF, "SYSCONF"}, {System::DualShockUDPClient, "DualShockUDPClient"}, {System::FreeLook, "FreeLook"}, {System::Session, "Session"}, {System::GameSettingsOnly, "GameSettingsOnly"}, {System::Achievements, "Achievements"}}; const std::string& GetSystemName(System system) { return system_to_name.at(system); } std::optional GetSystemFromName(const std::string& name) { const auto system = std::find_if(system_to_name.begin(), system_to_name.end(), [&name](const auto& entry) { return entry.second == name; }); if (system != system_to_name.end()) return system->first; return {}; } const std::string& GetLayerName(LayerType layer) { static const std::map layer_to_name = { {LayerType::Base, "Base"}, {LayerType::GlobalGame, "Global GameINI"}, {LayerType::LocalGame, "Local GameINI"}, {LayerType::Netplay, "Netplay"}, {LayerType::Movie, "Movie"}, {LayerType::CommandLine, "Command Line"}, {LayerType::CurrentRun, "Current Run"}, }; return layer_to_name.at(layer); } LayerType GetActiveLayerForConfig(const Location& config) { ReadLock lock(s_layers_rw_lock); for (auto layer : SEARCH_ORDER) { const auto it = s_layers.find(layer); if (it != s_layers.end()) { if (it->second->Exists(config)) return layer; } } // If config is not present in any layer, base layer is considered active. return LayerType::Base; } std::optional GetAsString(const Location& config) { std::optional result; ReadLock lock(s_layers_rw_lock); for (auto layer : SEARCH_ORDER) { const auto it = s_layers.find(layer); if (it != s_layers.end()) { result = it->second->Get(config); if (result.has_value()) break; } } return result; } ConfigChangeCallbackGuard::ConfigChangeCallbackGuard() { ++s_callback_guards; } ConfigChangeCallbackGuard::~ConfigChangeCallbackGuard() { if (--s_callback_guards) return; OnConfigChanged(); } } // namespace Config