diff --git a/Source/Core/Common/IniFile.cpp b/Source/Core/Common/IniFile.cpp index 4285af64bc..50752aabc1 100644 --- a/Source/Core/Common/IniFile.cpp +++ b/Source/Core/Common/IniFile.cpp @@ -256,6 +256,37 @@ bool IniFile::Section::Delete(const std::string& key) return true; } +void IniFile::Section::SetLines(const std::vector& lines) +{ + m_lines = lines; +} + +bool IniFile::Section::GetLines(std::vector* lines, const bool remove_comments) const +{ + for (std::string line : m_lines) + { + line = StripSpaces(line); + + if (remove_comments) + { + size_t commentPos = line.find('#'); + if (commentPos == 0) + { + continue; + } + + if (commentPos != std::string::npos) + { + line = StripSpaces(line.substr(0, commentPos)); + } + } + + lines->push_back(line); + } + + return true; +} + // IniFile const IniFile::Section* IniFile::GetSection(const std::string& sectionName) const @@ -312,7 +343,7 @@ bool IniFile::Exists(const std::string& sectionName, const std::string& key) con void IniFile::SetLines(const std::string& sectionName, const std::vector& lines) { Section* section = GetOrCreateSection(sectionName); - section->lines = lines; + section->SetLines(lines); } bool IniFile::DeleteKey(const std::string& sectionName, const std::string& key) @@ -345,28 +376,7 @@ bool IniFile::GetLines(const std::string& sectionName, std::vector* if (!section) return false; - for (std::string line : section->lines) - { - line = StripSpaces(line); - - if (remove_comments) - { - size_t commentPos = line.find('#'); - if (commentPos == 0) - { - continue; - } - - if (commentPos != std::string::npos) - { - line = StripSpaces(line.substr(0, commentPos)); - } - } - - lines->push_back(line); - } - - return true; + return section->GetLines(lines, remove_comments); } void IniFile::SortSections() @@ -439,7 +449,7 @@ bool IniFile::Load(const std::string& filename, bool keep_current_data) // INI is a hack anyway. if ((key == "" && value == "") || (line.size() >= 1 && (line[0] == '$' || line[0] == '+' || line[0] == '*'))) - current_section->lines.push_back(line); + current_section->m_lines.push_back(line); else current_section->Set(key, value); } @@ -464,12 +474,12 @@ bool IniFile::Save(const std::string& filename) for (const Section& section : sections) { - if (section.keys_order.size() != 0 || section.lines.size() != 0) + if (section.keys_order.size() != 0 || section.m_lines.size() != 0) out << "[" << section.name << "]" << std::endl; if (section.keys_order.size() == 0) { - for (const std::string& s : section.lines) + for (const std::string& s : section.m_lines) out << s << std::endl; } else diff --git a/Source/Core/Common/IniFile.h b/Source/Core/Common/IniFile.h index 67b64a103b..e2021261b1 100644 --- a/Source/Core/Common/IniFile.h +++ b/Source/Core/Common/IniFile.h @@ -66,18 +66,22 @@ public: bool Get(const std::string& key, double* value, double defaultValue = 0.0) const; bool Get(const std::string& key, std::vector* values) const; + void SetLines(const std::vector& lines); + bool GetLines(std::vector* lines, const bool remove_comments = true) const; + bool operator<(const Section& other) const { return name < other.name; } using SectionMap = std::map; const std::string& GetName() const { return name; } const SectionMap& GetValues() const { return values; } + bool HasLines() const { return !m_lines.empty(); } protected: std::string name; std::vector keys_order; SectionMap values; - std::vector lines; + std::vector m_lines; }; /** diff --git a/Source/Core/Common/Logging/Log.h b/Source/Core/Common/Logging/Log.h index 3ef07acbf1..43451767df 100644 --- a/Source/Core/Common/Logging/Log.h +++ b/Source/Core/Common/Logging/Log.h @@ -15,6 +15,7 @@ enum LOG_TYPE COMMANDPROCESSOR, COMMON, CONSOLE, + CORE, DISCIO, DSPHLE, DSPLLE, diff --git a/Source/Core/Common/Logging/LogManager.cpp b/Source/Core/Common/Logging/LogManager.cpp index 964a8df973..18fd57fd43 100644 --- a/Source/Core/Common/Logging/LogManager.cpp +++ b/Source/Core/Common/Logging/LogManager.cpp @@ -49,6 +49,7 @@ LogManager::LogManager() m_Log[LogTypes::COMMANDPROCESSOR] = new LogContainer("CP", "CommandProc"); m_Log[LogTypes::COMMON] = new LogContainer("COMMON", "Common"); m_Log[LogTypes::CONSOLE] = new LogContainer("CONSOLE", "Dolphin Console"); + m_Log[LogTypes::CORE] = new LogContainer("CORE", "Core"); m_Log[LogTypes::DISCIO] = new LogContainer("DIO", "Disc IO"); m_Log[LogTypes::DSPHLE] = new LogContainer("DSPHLE", "DSP HLE"); m_Log[LogTypes::DSPLLE] = new LogContainer("DSPLLE", "DSP LLE"); diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index fad4464f90..83f36fbc6a 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -24,6 +24,10 @@ set(SRCS Boot/Boot_ELF.cpp Boot/Boot_WiiWAD.cpp Boot/ElfReader.cpp + ConfigLoaders/BaseConfigLoader.cpp + ConfigLoaders/GameConfigLoader.cpp + ConfigLoaders/MovieConfigLoader.cpp + ConfigLoaders/NetPlayConfigLoader.cpp Debugger/Debugger_SymbolMap.cpp Debugger/Dump.cpp Debugger/PPCDebugInterface.cpp diff --git a/Source/Core/Core/ConfigLoaders/BaseConfigLoader.cpp b/Source/Core/Core/ConfigLoaders/BaseConfigLoader.cpp new file mode 100644 index 0000000000..c073cfcf2e --- /dev/null +++ b/Source/Core/Core/ConfigLoaders/BaseConfigLoader.cpp @@ -0,0 +1,95 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include +#include +#include + +#include "Common/CommonPaths.h" +#include "Common/CommonTypes.h" +#include "Common/Config/Config.h" +#include "Common/FileUtil.h" +#include "Common/IniFile.h" +#include "Common/Logging/Log.h" + +#include "Core/ConfigLoaders/BaseConfigLoader.h" + +namespace ConfigLoaders +{ +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::UI, F_UICONFIG_IDX}, +}; + +// INI layer configuration loader +class INIBaseConfigLayerLoader final : public Config::ConfigLayerLoader +{ +public: + INIBaseConfigLayerLoader() : ConfigLayerLoader(Config::LayerType::Base) {} + void Load(Config::Layer* config_layer) override + { + 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(); + Config::Section* config_section = + config_layer->GetOrCreateSection(system.first, section_name); + const IniFile::Section::SectionMap& section_map = section.GetValues(); + + for (const auto& value : section_map) + config_section->Set(value.first, value.second); + } + } + } + + void Save(Config::Layer* config_layer) override + { + const Config::LayerMap& sections = config_layer->GetLayerMap(); + for (const auto& system : sections) + { + auto mapping = system_to_ini.find(system.first); + if (mapping == system_to_ini.end()) + { + ERROR_LOG(COMMON, "Config can't map system '%s' to an INI file!", + Config::GetSystemName(system.first).c_str()); + continue; + } + + IniFile ini; + ini.Load(File::GetUserPath(mapping->second)); + + for (const auto& section : system.second) + { + const std::string section_name = section.GetName(); + const Config::SectionValueMap& section_values = section.GetValues(); + + IniFile::Section* ini_section = ini.GetOrCreateSection(section_name); + + for (const auto& value : section_values) + ini_section->Set(value.first, value.second); + } + + ini.Save(File::GetUserPath(mapping->second)); + } + } +}; + +// Loader generation +std::unique_ptr GenerateBaseConfigLoader() +{ + return std::make_unique(); +} +} diff --git a/Source/Core/Core/ConfigLoaders/BaseConfigLoader.h b/Source/Core/Core/ConfigLoaders/BaseConfigLoader.h new file mode 100644 index 0000000000..4356c5ad47 --- /dev/null +++ b/Source/Core/Core/ConfigLoaders/BaseConfigLoader.h @@ -0,0 +1,17 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include + +namespace Config +{ +class ConfigLayerLoader; +} + +namespace ConfigLoaders +{ +std::unique_ptr GenerateBaseConfigLoader(); +} diff --git a/Source/Core/Core/ConfigLoaders/GameConfigLoader.cpp b/Source/Core/Core/ConfigLoaders/GameConfigLoader.cpp new file mode 100644 index 0000000000..92a423e9b7 --- /dev/null +++ b/Source/Core/Core/ConfigLoaders/GameConfigLoader.cpp @@ -0,0 +1,434 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include +#include +#include +#include +#include +#include + +#include "Common/CommonPaths.h" +#include "Common/Config/Config.h" +#include "Common/FileUtil.h" +#include "Common/IniFile.h" +#include "Common/Logging/Log.h" +#include "Common/MsgHandler.h" +#include "Common/StringUtil.h" + +#include "Core/ConfigLoaders/GameConfigLoader.h" + +namespace ConfigLoaders +{ +// Returns all possible filenames in ascending order of priority +static std::vector GetGameIniFilenames(const std::string& id, u16 revision) +{ + std::vector filenames; + + // INIs that match all regions + if (id.size() >= 4) + filenames.push_back(id.substr(0, 3) + ".ini"); + + // Regular INIs + filenames.push_back(id + ".ini"); + + // INIs with specific revisions + filenames.push_back(id + StringFromFormat("r%d", revision) + ".ini"); + + return filenames; +} + +struct ConfigLocation +{ + Config::System system; + std::string section; + std::string key; + + bool operator==(const ConfigLocation& other) const + { + return std::tie(system, section, key) == std::tie(other.system, other.section, other.key); + } + + bool operator!=(const ConfigLocation& other) const { return !operator==(other); } +}; + +static std::map, ConfigLocation> ini_to_location = { + // Core + {{"Core", "CPUThread"}, {Config::System::Main, "Core", "CPUThread"}}, + {{"Core", "SkipIdle"}, {Config::System::Main, "Core", "SkipIdle"}}, + {{"Core", "SyncOnSkipIdle"}, {Config::System::Main, "Core", "SyncOnSkipIdle"}}, + {{"Core", "FPRF"}, {Config::System::Main, "Core", "FPRF"}}, + {{"Core", "AccurateNaNs"}, {Config::System::Main, "Core", "AccurateNaNs"}}, + {{"Core", "MMU"}, {Config::System::Main, "Core", "MMU"}}, + {{"Core", "DCBZ"}, {Config::System::Main, "Core", "DCBZ"}}, + {{"Core", "SyncGPU"}, {Config::System::Main, "Core", "SyncGPU"}}, + {{"Core", "FastDiscSpeed"}, {Config::System::Main, "Core", "FastDiscSpeed"}}, + {{"Core", "DSPHLE"}, {Config::System::Main, "Core", "DSPHLE"}}, + {{"Core", "GFXBackend"}, {Config::System::Main, "Core", "GFXBackend"}}, + {{"Core", "CPUCore"}, {Config::System::Main, "Core", "CPUCore"}}, + {{"Core", "HLE_BS2"}, {Config::System::Main, "Core", "HLE_BS2"}}, + {{"Core", "EmulationSpeed"}, {Config::System::Main, "Core", "EmulationSpeed"}}, + {{"Core", "GPUDeterminismMode"}, {Config::System::Main, "Core", "GPUDeterminismMode"}}, + {{"Core", "ProgressiveScan"}, {Config::System::Main, "Display", "ProgressiveScan"}}, + {{"Core", "PAL60"}, {Config::System::Main, "Display", "PAL60"}}, + + // Action Replay cheats + {{"ActionReplay_Enabled", ""}, {Config::System::Main, "ActionReplay_Enabled", ""}}, + {{"ActionReplay", ""}, {Config::System::Main, "ActionReplay", ""}}, + + // Gecko cheats + {{"Gecko_Enabled", ""}, {Config::System::Main, "Gecko_Enabled", ""}}, + {{"Gecko", ""}, {Config::System::Main, "Gecko", ""}}, + + // OnLoad cheats + {{"OnLoad", ""}, {Config::System::Main, "OnLoad", ""}}, + + // OnFrame cheats + {{"OnFrame_Enabled", ""}, {Config::System::Main, "OnFrame_Enabled", ""}}, + {{"OnFrame", ""}, {Config::System::Main, "OnFrame", ""}}, + + // Speedhacks + {{"Speedhacks", ""}, {Config::System::Main, "Speedhacks", ""}}, + + // Debugger values + {{"Watches", ""}, {Config::System::Debugger, "Watches", ""}}, + {{"BreakPoints", ""}, {Config::System::Debugger, "BreakPoints", ""}}, + {{"MemoryChecks", ""}, {Config::System::Debugger, "MemoryChecks", ""}}, + + // DSP + {{"DSP", "Volume"}, {Config::System::Main, "DSP", "Volume"}}, + {{"DSP", "EnableJIT"}, {Config::System::Main, "DSP", "EnableJIT"}}, + {{"DSP", "Backend"}, {Config::System::Main, "DSP", "Backend"}}, + + // Controls + {{"Controls", "PadType0"}, {Config::System::GCPad, "Core", "SIDevice0"}}, + {{"Controls", "PadType1"}, {Config::System::GCPad, "Core", "SIDevice1"}}, + {{"Controls", "PadType2"}, {Config::System::GCPad, "Core", "SIDevice2"}}, + {{"Controls", "PadType3"}, {Config::System::GCPad, "Core", "SIDevice3"}}, + {{"Controls", "WiimoteSource0"}, {Config::System::WiiPad, "Wiimote1", "Source"}}, + {{"Controls", "WiimoteSource1"}, {Config::System::WiiPad, "Wiimote2", "Source"}}, + {{"Controls", "WiimoteSource2"}, {Config::System::WiiPad, "Wiimote3", "Source"}}, + {{"Controls", "WiimoteSource3"}, {Config::System::WiiPad, "Wiimote4", "Source"}}, + {{"Controls", "WiimoteSourceBB"}, {Config::System::WiiPad, "BalanceBoard", "Source"}}, + + // Controller profiles + {{"Controls", "PadProfile1"}, {Config::System::GCPad, "Controls", "PadProfile1"}}, + {{"Controls", "PadProfile2"}, {Config::System::GCPad, "Controls", "PadProfile2"}}, + {{"Controls", "PadProfile3"}, {Config::System::GCPad, "Controls", "PadProfile3"}}, + {{"Controls", "PadProfile4"}, {Config::System::GCPad, "Controls", "PadProfile4"}}, + {{"Controls", "WiimoteProfile1"}, {Config::System::WiiPad, "Controls", "WiimoteProfile1"}}, + {{"Controls", "WiimoteProfile2"}, {Config::System::WiiPad, "Controls", "WiimoteProfile2"}}, + {{"Controls", "WiimoteProfile3"}, {Config::System::WiiPad, "Controls", "WiimoteProfile3"}}, + {{"Controls", "WiimoteProfile4"}, {Config::System::WiiPad, "Controls", "WiimoteProfile4"}}, + + // Video + {{"Video_Hardware", "VSync"}, {Config::System::GFX, "Hardware", "VSync"}}, + {{"Video_Settings", "wideScreenHack"}, {Config::System::GFX, "Settings", "wideScreenHack"}}, + {{"Video_Settings", "AspectRatio"}, {Config::System::GFX, "Settings", "AspectRatio"}}, + {{"Video_Settings", "Crop"}, {Config::System::GFX, "Settings", "Crop"}}, + {{"Video_Settings", "UseXFB"}, {Config::System::GFX, "Settings", "UseXFB"}}, + {{"Video_Settings", "UseRealXFB"}, {Config::System::GFX, "Settings", "UseRealXFB"}}, + {{"Video_Settings", "SafeTextureCacheColorSamples"}, + {Config::System::GFX, "Settings", "SafeTextureCacheColorSamples"}}, + {{"Video_Settings", "HiresTextures"}, {Config::System::GFX, "Settings", "HiresTextures"}}, + {{"Video_Settings", "ConvertHiresTextures"}, + {Config::System::GFX, "Settings", "ConvertHiresTextures"}}, + {{"Video_Settings", "CacheHiresTextures"}, + {Config::System::GFX, "Settings", "CacheHiresTextures"}}, + {{"Video_Settings", "EnablePixelLighting"}, + {Config::System::GFX, "Settings", "EnablePixelLighting"}}, + {{"Video_Settings", "ForcedSlowDepth"}, {Config::System::GFX, "Settings", "ForcedSlowDepth"}}, + {{"Video_Settings", "MSAA"}, {Config::System::GFX, "Settings", "MSAA"}}, + {{"Video_Settings", "SSAA"}, {Config::System::GFX, "Settings", "SSAA"}}, + {{"Video_Settings", "EFBScale"}, {Config::System::GFX, "Settings", "EFBScale"}}, + {{"Video_Settings", "DisableFog"}, {Config::System::GFX, "Settings", "DisableFog"}}, + + {{"Video_Enhancements", "ForceFiltering"}, + {Config::System::GFX, "Enhancements", "ForceFiltering"}}, + {{"Video_Enhancements", "MaxAnisotropy"}, + {Config::System::GFX, "Enhancements", "MaxAnisotropy"}}, + {{"Video_Enhancements", "PostProcessingShader"}, + {Config::System::GFX, "Enhancements", "PostProcessingShader"}}, + + {{"Video_Stereoscopy", "StereoMode"}, {Config::System::GFX, "Stereoscopy", "StereoMode"}}, + {{"Video_Stereoscopy", "StereoDepth"}, {Config::System::GFX, "Stereoscopy", "StereoDepth"}}, + {{"Video_Stereoscopy", "StereoSwapEyes"}, + {Config::System::GFX, "Stereoscopy", "StereoSwapEyes"}}, + + {{"Video_Hacks", "EFBAccessEnable"}, {Config::System::GFX, "Hacks", "EFBAccessEnable"}}, + {{"Video_Hacks", "BBoxEnable"}, {Config::System::GFX, "Hacks", "BBoxEnable"}}, + {{"Video_Hacks", "ForceProgressive"}, {Config::System::GFX, "Hacks", "ForceProgressive"}}, + {{"Video_Hacks", "EFBToTextureEnable"}, {Config::System::GFX, "Hacks", "EFBToTextureEnable"}}, + {{"Video_Hacks", "EFBScaledCopy"}, {Config::System::GFX, "Hacks", "EFBScaledCopy"}}, + {{"Video_Hacks", "EFBEmulateFormatChanges"}, + {Config::System::GFX, "Hacks", "EFBEmulateFormatChanges"}}, + + // GameINI specific video settings + {{"Video", "ProjectionHack"}, {Config::System::GFX, "Video", "ProjectionHack"}}, + {{"Video", "PH_SZNear"}, {Config::System::GFX, "Video", "PH_SZNear"}}, + {{"Video", "PH_SZFar"}, {Config::System::GFX, "Video", "PH_SZFar"}}, + {{"Video", "PH_ZNear"}, {Config::System::GFX, "Video", "PH_ZNear"}}, + {{"Video", "PH_ZFar"}, {Config::System::GFX, "Video", "PH_ZFar"}}, + {{"Video", "PH_ExtraParam"}, {Config::System::GFX, "Video", "PH_ExtraParam"}}, + {{"Video", "PerfQueriesEnable"}, {Config::System::GFX, "Video", "PerfQueriesEnable"}}, + + {{"Video_Stereoscopy", "StereoConvergence"}, + {Config::System::GFX, "Stereoscopy", "StereoConvergence"}}, + {{"Video_Stereoscopy", "StereoEFBMonoDepth"}, + {Config::System::GFX, "Stereoscopy", "StereoEFBMonoDepth"}}, + {{"Video_Stereoscopy", "StereoDepthPercentage"}, + {Config::System::GFX, "Stereoscopy", "StereoDepthPercentage"}}, + + // UI + {{"EmuState", "EmulationStateId"}, {Config::System::UI, "EmuState", "EmulationStateId"}}, + {{"EmuState", "EmulationIssues"}, {Config::System::UI, "EmuState", "EmulationIssues"}}, + {{"EmuState", "Title"}, {Config::System::UI, "EmuState", "Title"}}, +}; + +static ConfigLocation MapINIToRealLocation(const std::string& section, const std::string& key) +{ + auto it = ini_to_location.find({section, key}); + if (it == ini_to_location.end()) + { + // Try again, but this time with an empty key + // Certain sections like 'Speedhacks' has keys that are variable + it = ini_to_location.find({section, ""}); + if (it != ini_to_location.end()) + return it->second; + + // Attempt to load it as a configuration option + // It will be in the format of '.
' + std::istringstream buffer(section); + std::string system_str, config_section; + + bool fail = false; + std::getline(buffer, system_str, '.'); + fail |= buffer.fail(); + std::getline(buffer, config_section, '.'); + fail |= buffer.fail(); + + if (!fail) + return {Config::GetSystemFromName(system_str), config_section, key}; + + WARN_LOG(CORE, "Unknown game INI option in section %s: %s", section.c_str(), key.c_str()); + return {Config::System::Main, "", ""}; + } + + return ini_to_location[{section, key}]; +} + +static std::pair GetINILocationFromConfig(const ConfigLocation& location) +{ + auto it = std::find_if(ini_to_location.begin(), ini_to_location.end(), + [&location](const auto& entry) { return entry.second == location; }); + + if (it != ini_to_location.end()) + return it->first; + + // Try again, but this time with an empty key + // Certain sections like 'Speedhacks' have keys that are variable + it = std::find_if(ini_to_location.begin(), ini_to_location.end(), [&location](const auto& entry) { + return std::tie(entry.second.system, entry.second.key) == + std::tie(location.system, location.key); + }); + if (it != ini_to_location.end()) + return it->first; + + WARN_LOG(CORE, "Unknown option: %s.%s", location.section.c_str(), location.key.c_str()); + return {"", ""}; +} + +// INI Game layer configuration loader +class INIGameConfigLayerLoader final : public Config::ConfigLayerLoader +{ +public: + INIGameConfigLayerLoader(const std::string& id, u16 revision, bool global) + : ConfigLayerLoader(global ? Config::LayerType::GlobalGame : Config::LayerType::LocalGame), + m_id(id), m_revision(revision) + { + } + + void Load(Config::Layer* config_layer) override + { + IniFile ini; + if (config_layer->GetLayer() == Config::LayerType::GlobalGame) + { + for (const std::string& filename : GetGameIniFilenames(m_id, m_revision)) + ini.Load(File::GetSysDirectory() + GAMESETTINGS_DIR DIR_SEP + filename, true); + } + else + { + for (const std::string& filename : GetGameIniFilenames(m_id, m_revision)) + ini.Load(File::GetUserPath(D_GAMESETTINGS_IDX) + filename, true); + } + + const std::list& system_sections = ini.GetSections(); + + for (const auto& section : system_sections) + { + LoadFromSystemSection(config_layer, section); + } + + LoadControllerConfig(config_layer); + } + + void Save(Config::Layer* config_layer) override; + +private: + void LoadControllerConfig(Config::Layer* config_layer) const + { + // Game INIs can have controller profiles embedded in to them + static const std::array nums = {{'1', '2', '3', '4'}}; + + if (m_id == "00000000") + return; + + const std::array, 2> profile_info = {{ + std::make_tuple("Pad", "GCPad", Config::System::GCPad), + std::make_tuple("Wiimote", "Wiimote", Config::System::WiiPad), + }}; + + for (const auto& use_data : profile_info) + { + std::string type = std::get<0>(use_data); + std::string path = "Profiles/" + std::get<1>(use_data) + "/"; + + Config::Section* control_section = + config_layer->GetOrCreateSection(std::get<2>(use_data), "Controls"); + + for (const char num : nums) + { + bool use_profile = false; + std::string profile; + if (control_section->Exists(type + "Profile" + num)) + { + if (control_section->Get(type + "Profile" + num, &profile)) + { + if (File::Exists(File::GetUserPath(D_CONFIG_IDX) + path + profile + ".ini")) + { + use_profile = true; + } + else + { + // TODO: PanicAlert shouldn't be used for this. + PanicAlertT("Selected controller profile does not exist"); + } + } + } + + if (use_profile) + { + IniFile profile_ini; + profile_ini.Load(File::GetUserPath(D_CONFIG_IDX) + path + profile + ".ini"); + + const IniFile::Section* ini_section = profile_ini.GetOrCreateSection("Profile"); + const IniFile::Section::SectionMap& section_map = ini_section->GetValues(); + for (const auto& value : section_map) + { + Config::Section* section = config_layer->GetOrCreateSection( + std::get<2>(use_data), std::get<1>(use_data) + num); + section->Set(value.first, value.second); + } + } + } + } + } + + void LoadFromSystemSection(Config::Layer* config_layer, const IniFile::Section& section) const + { + const std::string section_name = section.GetName(); + if (section.HasLines()) + { + // Trash INI File chunks + std::vector chunk; + section.GetLines(&chunk, true); + + if (chunk.size()) + { + const auto mapped_config = MapINIToRealLocation(section_name, ""); + + if (mapped_config.section.empty() && mapped_config.key.empty()) + return; + + auto* config_section = + config_layer->GetOrCreateSection(mapped_config.system, mapped_config.section); + config_section->SetLines(chunk); + } + } + + // Regular key,value pairs + const IniFile::Section::SectionMap& section_map = section.GetValues(); + + for (const auto& value : section_map) + { + const auto mapped_config = MapINIToRealLocation(section_name, value.first); + + if (mapped_config.section.empty() && mapped_config.key.empty()) + continue; + + auto* config_section = + config_layer->GetOrCreateSection(mapped_config.system, mapped_config.section); + config_section->Set(mapped_config.key, value.second); + } + } + + const std::string m_id; + const u16 m_revision; +}; + +void INIGameConfigLayerLoader::Save(Config::Layer* config_layer) +{ + if (config_layer->GetLayer() != Config::LayerType::LocalGame) + return; + + IniFile ini; + for (const std::string& file_name : GetGameIniFilenames(m_id, m_revision)) + ini.Load(File::GetUserPath(D_GAMESETTINGS_IDX) + file_name, true); + + for (const auto& system : config_layer->GetLayerMap()) + { + for (const auto& section : system.second) + { + for (const auto& value : section.GetValues()) + { + const auto ini_location = + GetINILocationFromConfig({system.first, section.GetName(), value.first}); + if (ini_location.first.empty() && ini_location.second.empty()) + continue; + + IniFile::Section* ini_section = ini.GetOrCreateSection(ini_location.first); + ini_section->Set(ini_location.second, value.second); + } + } + } + + // Try to save to the revision specific INI first, if it exists. + const std::string gameini_with_rev = + File::GetUserPath(D_GAMESETTINGS_IDX) + m_id + StringFromFormat("r%d", m_revision) + ".ini"; + if (File::Exists(gameini_with_rev)) + { + ini.Save(gameini_with_rev); + return; + } + + // Otherwise, save to the game INI. We don't try any INI broader than that because it will + // likely cause issues with cheat codes and game patches. + const std::string gameini = File::GetUserPath(D_GAMESETTINGS_IDX) + m_id + ".ini"; + ini.Save(gameini); +} + +// Loader generation +std::unique_ptr GenerateGlobalGameConfigLoader(const std::string& id, + u16 revision) +{ + return std::make_unique(id, revision, true); +} + +std::unique_ptr GenerateLocalGameConfigLoader(const std::string& id, + u16 revision) +{ + return std::make_unique(id, revision, false); +} +} diff --git a/Source/Core/Core/ConfigLoaders/GameConfigLoader.h b/Source/Core/Core/ConfigLoaders/GameConfigLoader.h new file mode 100644 index 0000000000..174e4060a0 --- /dev/null +++ b/Source/Core/Core/ConfigLoaders/GameConfigLoader.h @@ -0,0 +1,23 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include "Common/CommonTypes.h" + +namespace Config +{ +class ConfigLayerLoader; +} + +namespace ConfigLoaders +{ +std::unique_ptr GenerateGlobalGameConfigLoader(const std::string& id, + u16 revision); +std::unique_ptr GenerateLocalGameConfigLoader(const std::string& id, + u16 revision); +} diff --git a/Source/Core/Core/ConfigLoaders/MovieConfigLoader.cpp b/Source/Core/Core/ConfigLoaders/MovieConfigLoader.cpp new file mode 100644 index 0000000000..dc115e1bb4 --- /dev/null +++ b/Source/Core/Core/ConfigLoaders/MovieConfigLoader.cpp @@ -0,0 +1,86 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include + +#include "Common/CommonFuncs.h" +#include "Common/CommonPaths.h" +#include "Common/CommonTypes.h" +#include "Common/Config/Config.h" +#include "Common/FileUtil.h" +#include "Common/IniFile.h" + +#include "Core/ConfigLoaders/MovieConfigLoader.h" +#include "Core/Movie.h" + +namespace ConfigLoaders +{ +// TODO: Future project, let this support all the configuration options. +// This will require a large break to the DTM format +void MovieConfigLayerLoader::Load(Config::Layer* config_layer) +{ + Config::Section* core = config_layer->GetOrCreateSection(Config::System::Main, "Core"); + Config::Section* display = config_layer->GetOrCreateSection(Config::System::Main, "Display"); + Config::Section* video_settings = Config::GetOrCreateSection(Config::System::GFX, "Settings"); + Config::Section* video_hacks = Config::GetOrCreateSection(Config::System::GFX, "Hacks"); + + core->Set("SkipIdle", m_header->bSkipIdle); + core->Set("CPUThread", m_header->bDualCore); + core->Set("DSPHLE", m_header->bDSPHLE); + core->Set("FastDiscSpeed", m_header->bFastDiscSpeed); + core->Set("CPUCore", m_header->CPUCore); + core->Set("SyncGPU", m_header->bSyncGPU); + core->Set("GFXBackend", std::string(reinterpret_cast(m_header->videoBackend))); + display->Set("ProgressiveScan", m_header->bProgressive); + display->Set("PAL60", m_header->bPAL60); + + video_settings->Set("UseXFB", m_header->bUseXFB); + video_settings->Set("UseRealXFB", m_header->bUseRealXFB); + video_hacks->Set("EFBAccessEnable", m_header->bEFBAccessEnable); + video_hacks->Set("EFBToTextureEnable", m_header->bSkipEFBCopyToRam); + video_hacks->Set("EFBEmulateFormatChanges", m_header->bEFBEmulateFormatChanges); +} + +void MovieConfigLayerLoader::Save(Config::Layer* config_layer) +{ + Config::Section* core = config_layer->GetOrCreateSection(Config::System::Main, "Core"); + Config::Section* display = config_layer->GetOrCreateSection(Config::System::Main, "Display"); + Config::Section* video_settings = Config::GetOrCreateSection(Config::System::GFX, "Settings"); + Config::Section* video_hacks = Config::GetOrCreateSection(Config::System::GFX, "Hacks"); + + std::string video_backend; + u32 cpu_core; + + core->Get("SkipIdle", &m_header->bSkipIdle); + core->Get("CPUThread", &m_header->bDualCore); + core->Get("DSPHLE", &m_header->bDSPHLE); + core->Get("FastDiscSpeed", &m_header->bFastDiscSpeed); + core->Get("CPUCore", &cpu_core); + core->Get("SyncGPU", &m_header->bSyncGPU); + core->Get("GFXBackend", &video_backend); + display->Get("ProgressiveScan", &m_header->bProgressive); + display->Get("PAL60", &m_header->bPAL60); + + video_settings->Get("UseXFB", &m_header->bUseXFB); + video_settings->Get("UseRealXFB", &m_header->bUseRealXFB); + video_hacks->Get("EFBAccessEnable", &m_header->bEFBAccessEnable); + video_hacks->Get("EFBToTextureEnable", &m_header->bSkipEFBCopyToRam); + video_hacks->Get("EFBEmulateFormatChanges", &m_header->bEFBEmulateFormatChanges); + + // This never used the regular config + m_header->bEFBCopyEnable = true; + m_header->bEFBCopyCacheEnable = false; + + m_header->CPUCore = cpu_core; + strncpy(reinterpret_cast(m_header->videoBackend), video_backend.c_str(), + ArraySize(m_header->videoBackend)); +} + +// Loader generation +std::unique_ptr GenerateMovieConfigLoader(Movie::DTMHeader* header) +{ + return std::make_unique(header); +} +} diff --git a/Source/Core/Core/ConfigLoaders/MovieConfigLoader.h b/Source/Core/Core/ConfigLoaders/MovieConfigLoader.h new file mode 100644 index 0000000000..c14b0fb51c --- /dev/null +++ b/Source/Core/Core/ConfigLoaders/MovieConfigLoader.h @@ -0,0 +1,35 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include + +#include "Common/Config/Config.h" + +namespace Movie +{ +struct DTMHeader; +} + +namespace ConfigLoaders +{ +class MovieConfigLayerLoader final : public Config::ConfigLayerLoader +{ +public: + explicit MovieConfigLayerLoader(Movie::DTMHeader* header) + : ConfigLayerLoader(Config::LayerType::Movie), m_header(header) + { + } + + void Load(Config::Layer* config_layer) override; + void Save(Config::Layer* config_layer) override; + + void ChangeDTMHeader(Movie::DTMHeader* header) { m_header = header; } +private: + Movie::DTMHeader* m_header; +}; + +std::unique_ptr GenerateMovieConfigLoader(Movie::DTMHeader* header); +} diff --git a/Source/Core/Core/ConfigLoaders/NetPlayConfigLoader.cpp b/Source/Core/Core/ConfigLoaders/NetPlayConfigLoader.cpp new file mode 100644 index 0000000000..ffd84edfac --- /dev/null +++ b/Source/Core/Core/ConfigLoaders/NetPlayConfigLoader.cpp @@ -0,0 +1,61 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "Common/CommonPaths.h" +#include "Common/Config/Config.h" +#include "Common/FileUtil.h" +#include "Common/IniFile.h" +#include "Common/Logging/Log.h" + +#include "Core/ConfigLoaders/NetPlayConfigLoader.h" +#include "Core/NetPlayProto.h" + +namespace ConfigLoaders +{ +class NetPlayConfigLayerLoader final : public Config::ConfigLayerLoader +{ +public: + explicit NetPlayConfigLayerLoader(const NetSettings& settings) + : ConfigLayerLoader(Config::LayerType::Netplay), m_settings(settings) + { + } + + void Load(Config::Layer* config_layer) override + { + Config::Section* core = config_layer->GetOrCreateSection(Config::System::Main, "Core"); + Config::Section* dsp = config_layer->GetOrCreateSection(Config::System::Main, "DSP"); + Config::Section* display = config_layer->GetOrCreateSection(Config::System::Main, "Display"); + + core->Set("CPUThread", m_settings.m_CPUthread); + core->Set("CPUCore", m_settings.m_CPUcore); + core->Set("SelectedLanguage", m_settings.m_SelectedLanguage); + core->Set("OverrideGCLang", m_settings.m_OverrideGCLanguage); + core->Set("DSPHLE", m_settings.m_DSPHLE); + core->Set("OverclockEnable", m_settings.m_OCEnable); + core->Set("Overclock", m_settings.m_OCFactor); + core->Set("SlotA", m_settings.m_EXIDevice[0]); + core->Set("SlotB", m_settings.m_EXIDevice[1]); + core->Set("EnableSaving", m_settings.m_WriteToMemcard); + + dsp->Set("EnableJIT", m_settings.m_DSPEnableJIT); + + display->Set("ProgressiveScan", m_settings.m_ProgressiveScan); + display->Set("PAL60", m_settings.m_PAL60); + } + + void Save(Config::Layer* config_layer) override + { + // Do Nothing + } + +private: + const NetSettings m_settings; +}; + +// Loader generation +std::unique_ptr GenerateNetPlayConfigLoader(const NetSettings& settings) +{ + return std::make_unique(settings); +} +} diff --git a/Source/Core/Core/ConfigLoaders/NetPlayConfigLoader.h b/Source/Core/Core/ConfigLoaders/NetPlayConfigLoader.h new file mode 100644 index 0000000000..615d904a06 --- /dev/null +++ b/Source/Core/Core/ConfigLoaders/NetPlayConfigLoader.h @@ -0,0 +1,19 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include + +struct NetSettings; + +namespace Config +{ +class ConfigLayerLoader; +} + +namespace ConfigLoaders +{ +std::unique_ptr GenerateNetPlayConfigLoader(const NetSettings& settings); +} diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index b450ea3f39..9188e95c17 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -220,6 +220,10 @@ + + + + @@ -461,6 +465,10 @@ + + + + diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index d4d3cc9e8f..89ff3f5277 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -170,6 +170,10 @@ + + + + @@ -860,6 +864,10 @@ + + + + diff --git a/Source/Core/UICommon/CommandLineParse.cpp b/Source/Core/UICommon/CommandLineParse.cpp index e591a2accf..0fe4a0c0d4 100644 --- a/Source/Core/UICommon/CommandLineParse.cpp +++ b/Source/Core/UICommon/CommandLineParse.cpp @@ -2,13 +2,65 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include +#include +#include +#include + #include #include "Common/Common.h" +#include "Common/Config/Config.h" #include "UICommon/CommandLineParse.h" namespace CommandLineParse { +class CommandLineConfigLayerLoader final : public Config::ConfigLayerLoader +{ +public: + CommandLineConfigLayerLoader(const std::list& args, const std::string& video_backend, + const std::string& audio_backend) + : ConfigLayerLoader(Config::LayerType::CommandLine) + { + if (video_backend.size()) + m_values.emplace_back(std::make_tuple("Dolphin", "Core", "GFXBackend", video_backend)); + + if (audio_backend.size()) + m_values.emplace_back( + std::make_tuple("Dolphin", "Core", "DSPHLE", audio_backend == "HLE" ? "True" : "False")); + + // Arguments are in the format of .
.=Value + for (const auto& arg : args) + { + std::istringstream buffer(arg); + std::string system, section, key, value; + std::getline(buffer, system, '.'); + std::getline(buffer, section, '.'); + std::getline(buffer, key, '='); + std::getline(buffer, value, '='); + m_values.emplace_back(std::make_tuple(system, section, key, value)); + } + } + + void Load(Config::Layer* config_layer) override + { + for (auto& value : m_values) + { + Config::Section* section = config_layer->GetOrCreateSection( + Config::GetSystemFromName(std::get<0>(value)), std::get<1>(value)); + section->Set(std::get<2>(value), std::get<3>(value)); + } + } + + void Save(Config::Layer* config_layer) override + { + // Save Nothing + } + +private: + std::list> m_values; +}; + std::unique_ptr CreateParser(ParserOptions options) { auto parser = std::make_unique(); @@ -23,6 +75,11 @@ std::unique_ptr CreateParser(ParserOptions options) .metavar("") .type("string") .help("Load the specified file"); + parser->add_option("-C", "--config") + .action("append") + .metavar(".
.=") + .type("string") + .help("Set a configuration option"); if (options == ParserOptions::IncludeGUIOptions) { @@ -32,7 +89,8 @@ std::unique_ptr CreateParser(ParserOptions options) parser->add_option("-c", "--confirm").action("store_true").help("Set Confirm on Stop"); } - // XXX: These two are setting configuration options + parser->set_defaults("video_backend", ""); + parser->set_defaults("audio_emulation", ""); parser->add_option("-v", "--video_backend").action("store").help("Specify a video backend"); parser->add_option("-a", "--audio_emulation") .choices({"HLE", "LLE"}) @@ -43,6 +101,15 @@ std::unique_ptr CreateParser(ParserOptions options) optparse::Values& ParseArguments(optparse::OptionParser* parser, int argc, char** argv) { - return parser->parse_args(argc, argv); + optparse::Values& options = parser->parse_args(argc, argv); + + const std::list& config_args = options.all("config"); + if (config_args.size()) + { + Config::AddLayer(std::make_unique( + config_args, static_cast(options.get("video_backend")), + static_cast(options.get("audio_emulation")))); + } + return options; } }