From f75ea052a65eedb0177b2ac64fca76e9fa3e6d89 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Wed, 15 Apr 2020 01:44:04 +1000 Subject: [PATCH] FrontendCommon: Add input profile support --- src/core/host_interface.cpp | 1 + src/frontend-common/common_host_interface.cpp | 151 ++++++++++++++++++ src/frontend-common/common_host_interface.h | 10 ++ 3 files changed, 162 insertions(+) diff --git a/src/core/host_interface.cpp b/src/core/host_interface.cpp index 9e1943ada..1ea742a1f 100644 --- a/src/core/host_interface.cpp +++ b/src/core/host_interface.cpp @@ -694,6 +694,7 @@ void HostInterface::InitializeUserDirectory() result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("cache").c_str(), false); result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("dump").c_str(), false); result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("dump/audio").c_str(), false); + result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("inputprofiles").c_str(), false); result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("savestates").c_str(), false); result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("screenshots").c_str(), false); result &= FileSystem::CreateDirectory(GetUserDirectoryRelativePath("memcards").c_str(), false); diff --git a/src/frontend-common/common_host_interface.cpp b/src/frontend-common/common_host_interface.cpp index 9b6716860..886400ff1 100644 --- a/src/frontend-common/common_host_interface.cpp +++ b/src/frontend-common/common_host_interface.cpp @@ -13,6 +13,7 @@ #include "sdl_audio_stream.h" #include "sdl_controller_interface.h" #endif +#include "ini_settings_interface.h" #include #include Log_SetChannel(CommonHostInterface); @@ -794,3 +795,153 @@ void CommonHostInterface::RegisterSaveStateHotkeys() } } } + +std::vector> CommonHostInterface::GetInputProfileList() const +{ + FileSystem::FindResultsArray results; + FileSystem::FindFiles(GetUserDirectoryRelativePath("inputprofiles").c_str(), "*.ini", + FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_RELATIVE_PATHS, &results); + + std::vector> profile_names; + profile_names.reserve(results.size()); + for (auto& it : results) + { + if (it.FileName.size() < 4) + continue; + + std::string profile_name = it.FileName.substr(0, it.FileName.length() - 4); + std::string full_filename = GetUserDirectoryRelativePath("inputprofiles/%s", it.FileName.c_str()); + profile_names.emplace_back(std::move(profile_name), std::move(full_filename)); + } + + return profile_names; +} + +void CommonHostInterface::ClearAllControllerBindings(SettingsInterface& si) +{ + for (u32 controller_index = 1; controller_index <= NUM_CONTROLLER_AND_CARD_PORTS; controller_index++) + { + const ControllerType ctype = m_settings.controller_types[controller_index - 1]; + if (ctype == ControllerType::None) + continue; + + const auto section_name = TinyString::FromFormat("Controller%u", controller_index); + + si.DeleteValue(section_name, "Type"); + + for (const auto& button : Controller::GetButtonNames(ctype)) + si.DeleteValue(section_name, button.first.c_str()); + + for (const auto& axis : Controller::GetAxisNames(ctype)) + si.DeleteValue(section_name, axis.first.c_str()); + + if (Controller::GetVibrationMotorCount(ctype) > 0) + si.DeleteValue(section_name, "Rumble"); + } +} + +void CommonHostInterface::ApplyInputProfile(const char* profile_path, SettingsInterface& si) +{ + // clear bindings for all controllers + ClearAllControllerBindings(si); + + INISettingsInterface profile(profile_path); + + for (u32 controller_index = 1; controller_index <= NUM_CONTROLLER_AND_CARD_PORTS; controller_index++) + { + const auto section_name = TinyString::FromFormat("Controller%u", controller_index); + const std::string ctype_str = profile.GetStringValue(section_name, "Type"); + if (ctype_str.empty()) + continue; + + std::optional ctype = Settings::ParseControllerTypeName(ctype_str.c_str()); + if (!ctype) + { + Log_ErrorPrintf("Invalid controller type in profile: '%s'", ctype_str.c_str()); + return; + } + + m_settings.controller_types[controller_index - 1] = *ctype; + HostInterface::OnControllerTypeChanged(controller_index - 1); + + si.SetStringValue(section_name, "Type", Settings::GetControllerTypeName(*ctype)); + + for (const auto& button : Controller::GetButtonNames(*ctype)) + { + const auto key_name = TinyString::FromFormat("Button%s", button.first.c_str()); + si.DeleteValue(section_name, key_name); + const std::vector bindings = profile.GetStringList(section_name, key_name); + for (const std::string& binding : bindings) + si.AddToStringList(section_name, key_name, binding.c_str()); + } + + for (const auto& axis : Controller::GetAxisNames(*ctype)) + { + const auto key_name = TinyString::FromFormat("Axis%s", axis.first.c_str()); + si.DeleteValue(section_name, axis.first.c_str()); + const std::vector bindings = profile.GetStringList(section_name, key_name); + for (const std::string& binding : bindings) + si.AddToStringList(section_name, key_name, binding.c_str()); + } + + si.DeleteValue(section_name, "Rumble"); + const std::string rumble_value = profile.GetStringValue(section_name, "Rumble"); + if (!rumble_value.empty()) + si.SetStringValue(section_name, "Rumble", rumble_value.c_str()); + } + + UpdateInputMap(si); + + if (m_system) + m_system->UpdateControllers(); + + ReportFormattedMessage("Loaded input profile from '%s'", profile_path); +} + +bool CommonHostInterface::SaveInputProfile(const char* profile_path, SettingsInterface& si) +{ + if (FileSystem::FileExists(profile_path)) + { + if (!FileSystem::DeleteFile(profile_path)) + { + Log_ErrorPrintf("Failed to delete existing input profile '%s' when saving", profile_path); + return false; + } + } + + INISettingsInterface profile(profile_path); + + for (u32 controller_index = 1; controller_index <= NUM_CONTROLLER_AND_CARD_PORTS; controller_index++) + { + const ControllerType ctype = m_settings.controller_types[controller_index - 1]; + if (ctype == ControllerType::None) + continue; + + const auto section_name = TinyString::FromFormat("Controller%u", controller_index); + + profile.SetStringValue(section_name, "Type", Settings::GetControllerTypeName(ctype)); + + for (const auto& button : Controller::GetButtonNames(ctype)) + { + const auto key_name = TinyString::FromFormat("Button%s", button.first.c_str()); + const std::vector bindings = si.GetStringList(section_name, key_name); + for (const std::string& binding : bindings) + profile.AddToStringList(section_name, key_name, binding.c_str()); + } + + for (const auto& axis : Controller::GetAxisNames(ctype)) + { + const auto key_name = TinyString::FromFormat("Axis%s", axis.first.c_str()); + const std::vector bindings = si.GetStringList(section_name, key_name); + for (const std::string& binding : bindings) + profile.AddToStringList(section_name, key_name, binding.c_str()); + } + + const std::string rumble_value = si.GetStringValue(section_name, "Rumble"); + if (!rumble_value.empty()) + profile.SetStringValue(section_name, "Rumble", rumble_value.c_str()); + } + + profile.Save(); + return true; +} diff --git a/src/frontend-common/common_host_interface.h b/src/frontend-common/common_host_interface.h index fd931c1db..e9916ba5c 100644 --- a/src/frontend-common/common_host_interface.h +++ b/src/frontend-common/common_host_interface.h @@ -86,6 +86,15 @@ protected: /// Reloads the input map from config. Callable from controller interface. virtual void UpdateInputMap() = 0; + /// Returns a list of all input profiles. first - name, second - path + std::vector> GetInputProfileList() const; + + /// Applies the specified input profile. + void ApplyInputProfile(const char* profile_path, SettingsInterface& si); + + /// Saves the current input configuration to the specified profile name. + bool SaveInputProfile(const char* profile_path, SettingsInterface& si); + void RegisterHotkey(String category, String name, String display_name, InputButtonHandler handler); bool HandleHostKeyEvent(HostKeyCode code, bool pressed); void UpdateInputMap(SettingsInterface& si); @@ -102,6 +111,7 @@ private: void RegisterSaveStateHotkeys(); void UpdateControllerInputMap(SettingsInterface& si); void UpdateHotkeyInputMap(SettingsInterface& si); + void ClearAllControllerBindings(SettingsInterface& si); HotkeyInfoList m_hotkeys;