// Copyright 2016 Dolphin Emulator Project // Licensed under GPLv2+ // Refer to the license.txt file included. #include "Core/ConfigLoaders/BaseConfigLoader.h" #include #include #include #include #include #include #include #include #include #include "Common/CommonTypes.h" #include "Common/Config/Config.h" #include "Common/FileUtil.h" #include "Common/IniFile.h" #include "Common/Logging/Log.h" #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" namespace ConfigLoaders { void SaveToSYSCONF(Config::LayerType layer, std::function predicate) { if (Core::IsRunning()) return; IOS::HLE::Kernel ios; SysConf sysconf{ios.GetFS()}; for (const Config::SYSCONFSetting& setting : Config::SYSCONF_SETTINGS) { std::visit( [&](auto* info) { if (predicate && !predicate(info->GetLocation())) return; const std::string key = info->GetLocation().section + "." + info->GetLocation().key; if (setting.type == SysConf::Entry::Type::Long) { sysconf.SetData(key, setting.type, Config::Get(layer, *info)); } else if (setting.type == SysConf::Entry::Type::Byte) { sysconf.SetData(key, setting.type, static_cast(Config::Get(layer, *info))); } else if (setting.type == SysConf::Entry::Type::BigArray) { // Somewhat hacky support for IPL.SADR. The setting only stores the // first 4 bytes even thought the SYSCONF entry is much bigger. SysConf::Entry* entry = sysconf.GetOrAddEntry(key, setting.type); if (entry->bytes.size() < 0x1007 + 1) entry->bytes.resize(0x1007 + 1); *reinterpret_cast(entry->bytes.data()) = Config::Get(layer, *info); } }, setting.config_info); } sysconf.SetData("IPL.CB", SysConf::Entry::Type::Long, 0); // Disable WiiConnect24's standby mode. If it is enabled, it prevents us from receiving // shutdown commands in the State Transition Manager (STM). // TODO: remove this if and once Dolphin supports WC24 standby mode. SysConf::Entry* idle_entry = sysconf.GetOrAddEntry("IPL.IDL", SysConf::Entry::Type::SmallArray); if (idle_entry->bytes.empty()) idle_entry->bytes = std::vector(2); else idle_entry->bytes[0] = 0; NOTICE_LOG_FMT(CORE, "Disabling WC24 'standby' (shutdown to idle) to avoid hanging on shutdown"); IOS::HLE::RestoreBTInfoSection(&sysconf); sysconf.Save(); } const std::map system_to_ini = { {Config::System::Main, F_DOLPHINCONFIG_IDX}, {Config::System::GCPad, F_GCPADCONFIG_IDX}, {Config::System::WiiPad, F_WIIPADCONFIG_IDX}, {Config::System::GCKeyboard, F_GCKEYBOARDCONFIG_IDX}, {Config::System::GFX, F_GFXCONFIG_IDX}, {Config::System::Logger, F_LOGGERCONFIG_IDX}, {Config::System::Debugger, F_DEBUGGERCONFIG_IDX}, {Config::System::DualShockUDPClient, F_DUALSHOCKUDPCLIENTCONFIG_IDX}, {Config::System::FreeLook, F_FREELOOKCONFIG_IDX}, // Config::System::Session should not be added to this list }; // INI layer configuration loader class BaseConfigLayerLoader final : public Config::ConfigLayerLoader { public: BaseConfigLayerLoader() : ConfigLayerLoader(Config::LayerType::Base) {} void Load(Config::Layer* layer) override { LoadFromSYSCONF(layer); for (const auto& system : system_to_ini) { IniFile ini; ini.Load(File::GetUserPath(system.second)); const std::list& system_sections = ini.GetSections(); for (const auto& section : system_sections) { const std::string section_name = section.GetName(); const IniFile::Section::SectionMap& section_map = section.GetValues(); for (const auto& value : section_map) { const Config::Location location{system.first, section_name, value.first}; layer->Set(location, value.second); } } } } void Save(Config::Layer* layer) override { SaveToSYSCONF(layer->GetLayer()); std::map inis; for (const auto& system : system_to_ini) { inis[system.first].Load(File::GetUserPath(system.second)); } for (const auto& config : layer->GetLayerMap()) { const Config::Location& location = config.first; const std::optional& value = config.second; // Done by SaveToSYSCONF if (location.system == Config::System::SYSCONF) continue; if (location.system == Config::System::Session) continue; auto ini = inis.find(location.system); if (ini == inis.end()) { ERROR_LOG_FMT(COMMON, "Config can't map system '{}' to an INI file!", Config::GetSystemName(location.system)); continue; } if (!IsSettingSaveable(location)) continue; if (value) { IniFile::Section* ini_section = ini->second.GetOrCreateSection(location.section); ini_section->Set(location.key, *value); } else { ini->second.DeleteKey(location.section, location.key); } } for (const auto& system : system_to_ini) { inis[system.first].Save(File::GetUserPath(system.second)); } } private: void LoadFromSYSCONF(Config::Layer* layer) { if (Core::IsRunning()) return; IOS::HLE::Kernel ios; SysConf sysconf{ios.GetFS()}; for (const Config::SYSCONFSetting& setting : Config::SYSCONF_SETTINGS) { std::visit( [&](auto* info) { const Config::Location location = info->GetLocation(); const std::string key = location.section + "." + location.key; if (setting.type == SysConf::Entry::Type::Long) { layer->Set(location, sysconf.GetData(key, info->GetDefaultValue())); } else if (setting.type == SysConf::Entry::Type::Byte) { layer->Set(location, sysconf.GetData(key, info->GetDefaultValue())); } else if (setting.type == SysConf::Entry::Type::BigArray) { // Somewhat hacky support for IPL.SADR. The setting only stores the // first 4 bytes even thought the SYSCONF entry is much bigger. u32 value = info->GetDefaultValue(); SysConf::Entry* entry = sysconf.GetEntry(key); if (entry) { std::memcpy(&value, entry->bytes.data(), std::min(entry->bytes.size(), sizeof(u32))); } layer->Set(location, value); } }, setting.config_info); } } }; // Loader generation std::unique_ptr GenerateBaseConfigLoader() { return std::make_unique(); } } // namespace ConfigLoaders