diff --git a/.gitignore b/.gitignore index c07d279994..4f40fe0c45 100644 --- a/.gitignore +++ b/.gitignore @@ -92,6 +92,7 @@ oprofile_data/ /bin/sstates /bin/textures /bin/translations +/bin/inputprofiles /deps /ipch diff --git a/pcsx2/Config.h b/pcsx2/Config.h index ffbc7e5bc4..8ef2908393 100644 --- a/pcsx2/Config.h +++ b/pcsx2/Config.h @@ -1041,6 +1041,7 @@ namespace EmuFolders extern std::string Covers; extern std::string GameSettings; extern std::string Textures; + extern std::string InputProfiles; // Assumes that AppRoot and DataRoot have been initialized. void SetDefaults(); diff --git a/pcsx2/Frontend/LayeredSettingsInterface.h b/pcsx2/Frontend/LayeredSettingsInterface.h index 7af0b12694..f23ebc59d3 100644 --- a/pcsx2/Frontend/LayeredSettingsInterface.h +++ b/pcsx2/Frontend/LayeredSettingsInterface.h @@ -24,6 +24,7 @@ public: { LAYER_CMDLINE, LAYER_GAME, + LAYER_INPUT, LAYER_BASE, NUM_LAYERS }; diff --git a/pcsx2/HostSettings.cpp b/pcsx2/HostSettings.cpp index 6e1b12d356..77ec09ecf0 100644 --- a/pcsx2/HostSettings.cpp +++ b/pcsx2/HostSettings.cpp @@ -180,3 +180,9 @@ void Host::Internal::SetGameSettingsLayer(SettingsInterface* sif) std::unique_lock lock(s_settings_mutex); s_layered_settings_interface.SetLayer(LayeredSettingsInterface::LAYER_GAME, sif); } + +void Host::Internal::SetInputSettingsLayer(SettingsInterface* sif) +{ + std::unique_lock lock(s_settings_mutex); + s_layered_settings_interface.SetLayer(LayeredSettingsInterface::LAYER_INPUT, sif); +} diff --git a/pcsx2/HostSettings.h b/pcsx2/HostSettings.h index fa5e078701..26967a2e65 100644 --- a/pcsx2/HostSettings.h +++ b/pcsx2/HostSettings.h @@ -66,5 +66,8 @@ namespace Host /// Sets the game settings layer. Called by VMManager when the game changes. void SetGameSettingsLayer(SettingsInterface* sif); + + /// Sets the input profile settings layer. Called by VMManager when the game changes. + void SetInputSettingsLayer(SettingsInterface* sif); } // namespace Internal } // namespace Host \ No newline at end of file diff --git a/pcsx2/Pcsx2Config.cpp b/pcsx2/Pcsx2Config.cpp index 58f3c73427..bfc4e6957c 100644 --- a/pcsx2/Pcsx2Config.cpp +++ b/pcsx2/Pcsx2Config.cpp @@ -50,6 +50,7 @@ namespace EmuFolders std::string Covers; std::string GameSettings; std::string Textures; + std::string InputProfiles; } // namespace EmuFolders void TraceLogFilters::LoadSave(SettingsWrapper& wrap) @@ -1227,6 +1228,7 @@ void EmuFolders::SetDefaults() GameSettings = Path::Combine(DataRoot, "gamesettings"); Cache = Path::Combine(DataRoot, "cache"); Textures = Path::Combine(DataRoot, "textures"); + InputProfiles = Path::Combine(DataRoot, "inputprofiles"); } static std::string LoadPathFromSettings(SettingsInterface& si, const std::string& root, const char* name, const char* def) @@ -1251,6 +1253,7 @@ void EmuFolders::LoadConfig(SettingsInterface& si) GameSettings = LoadPathFromSettings(si, DataRoot, "GameSettings", "gamesettings"); Cache = LoadPathFromSettings(si, DataRoot, "Cache", "cache"); Textures = LoadPathFromSettings(si, DataRoot, "Textures", "textures"); + InputProfiles = LoadPathFromSettings(si, DataRoot, "InputProfiles", "inputprofiles"); Console.WriteLn("BIOS Directory: %s", Bios.c_str()); Console.WriteLn("Snapshots Directory: %s", Snapshots.c_str()); @@ -1264,6 +1267,7 @@ void EmuFolders::LoadConfig(SettingsInterface& si) Console.WriteLn("Game Settings Directory: %s", GameSettings.c_str()); Console.WriteLn("Cache Directory: %s", Cache.c_str()); Console.WriteLn("Textures Directory: %s", Textures.c_str()); + Console.WriteLn("Input Profile Directory: %s", InputProfiles.c_str()); } void EmuFolders::Save(SettingsInterface& si) @@ -1279,6 +1283,7 @@ void EmuFolders::Save(SettingsInterface& si) si.SetStringValue("Folders", "CheatsNI", Path::MakeRelative(CheatsNI, DataRoot).c_str()); si.SetStringValue("Folders", "Cache", Path::MakeRelative(Cache, DataRoot).c_str()); si.SetStringValue("Folders", "Textures", Path::MakeRelative(Textures, DataRoot).c_str()); + si.SetStringValue("Folders", "InputProfiles", Path::MakeRelative(InputProfiles, DataRoot).c_str()); } bool EmuFolders::EnsureFoldersExist() @@ -1296,5 +1301,6 @@ bool EmuFolders::EnsureFoldersExist() result = FileSystem::CreateDirectoryPath(GameSettings.c_str(), false) && result; result = FileSystem::CreateDirectoryPath(Cache.c_str(), false) && result; result = FileSystem::CreateDirectoryPath(Textures.c_str(), false) && result; + result = FileSystem::CreateDirectoryPath(InputProfiles.c_str(), false) && result; return result; } diff --git a/pcsx2/VMManager.cpp b/pcsx2/VMManager.cpp index e30d5f19c6..42d62e7401 100644 --- a/pcsx2/VMManager.cpp +++ b/pcsx2/VMManager.cpp @@ -104,6 +104,7 @@ namespace VMManager static std::unique_ptr s_vm_memory; static std::unique_ptr s_cpu_provider_pack; static std::unique_ptr s_game_settings_interface; +static std::unique_ptr s_input_settings_interface; static std::atomic s_state{VMState::Shutdown}; static bool s_cpu_implementation_changed = false; @@ -119,6 +120,7 @@ static u32 s_patches_crc; static std::string s_game_serial; static std::string s_game_name; static std::string s_elf_override; +static std::string s_input_profile_name; static u32 s_active_game_fixes = 0; static std::vector s_widescreen_cheats_data; static bool s_widescreen_cheats_loaded = false; @@ -339,6 +341,11 @@ std::string VMManager::GetGameSettingsPath(const std::string_view& game_serial, Path::Combine(EmuFolders::GameSettings, fmt::format("{}_{:08X}.ini", sanitized_serial, game_crc)); } +std::string VMManager::GetInputProfilePath(const std::string_view& name) +{ + return Path::Combine(EmuFolders::InputProfiles, fmt::format("{}.ini", name)); +} + void VMManager::RequestDisplaySize(float scale /*= 0.0f*/) { int iwidth, iheight; @@ -413,11 +420,43 @@ bool VMManager::UpdateGameSettingsLayer() } } - if (!s_game_settings_interface && !new_interface) + std::string input_profile_name; + if (new_interface) + new_interface->GetStringValue("Pad", "InputProfileName", &input_profile_name); + + if (!s_game_settings_interface && !new_interface && s_input_profile_name == input_profile_name) return false; Host::Internal::SetGameSettingsLayer(new_interface.get()); s_game_settings_interface = std::move(new_interface); + + std::unique_ptr input_interface; + if (!input_profile_name.empty()) + { + const std::string filename(GetInputProfilePath(input_profile_name)); + if (FileSystem::FileExists(filename.c_str())) + { + Console.WriteLn("Loading input profile from '%s'...", filename.c_str()); + input_interface = std::make_unique(std::move(filename)); + if (!input_interface->Load()) + { + Console.Error("Failed to parse input profile ini '%s'", input_interface->GetFileName().c_str()); + input_interface.reset(); + input_profile_name = {}; + } + } + else + { + DevCon.WriteLn("No game settings found (tried '%s')", filename.c_str()); + input_profile_name = {}; + } + } + + Host::Internal::SetInputSettingsLayer(input_interface.get()); + s_input_settings_interface = std::move(input_interface); + s_input_profile_name = std::move(input_profile_name); + + return true; } diff --git a/pcsx2/VMManager.h b/pcsx2/VMManager.h index 4910b7791d..a7814213b2 100644 --- a/pcsx2/VMManager.h +++ b/pcsx2/VMManager.h @@ -152,6 +152,9 @@ namespace VMManager /// Returns the path for the game settings ini file for the specified CRC. std::string GetGameSettingsPath(const std::string_view& game_serial, u32 game_crc); + /// Returns the path for the input profile ini file with the specified name (may not exist). + std::string GetInputProfilePath(const std::string_view& name); + /// Resizes the render window to the display size, with an optional scale. /// If the scale is set to 0, the internal resolution will be used, otherwise it is treated as a multiplier to 1x. void RequestDisplaySize(float scale = 0.0f);