mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-23 06:15:38 +00:00
GameSettings: Add per-game input bindings from profiles
This just affects the **bindings**. You will still have to set the controller type per game if this is different from the global default.
This commit is contained in:
parent
7278f055cb
commit
0b858658ca
|
@ -154,6 +154,9 @@ void GamePropertiesDialog::setupAdditionalUi()
|
||||||
m_ui.userControllerType2->addItem(
|
m_ui.userControllerType2->addItem(
|
||||||
qApp->translate("ControllerType", Settings::GetControllerTypeDisplayName(static_cast<ControllerType>(i))));
|
qApp->translate("ControllerType", Settings::GetControllerTypeDisplayName(static_cast<ControllerType>(i))));
|
||||||
}
|
}
|
||||||
|
m_ui.userInputProfile->addItem(tr("(unchanged)"));
|
||||||
|
for (const auto& it : m_host_interface->getInputProfileList())
|
||||||
|
m_ui.userInputProfile->addItem(QString::fromStdString(it.name));
|
||||||
|
|
||||||
m_ui.userMemoryCard1Type->addItem(tr("(unchanged)"));
|
m_ui.userMemoryCard1Type->addItem(tr("(unchanged)"));
|
||||||
for (u32 i = 0; i < static_cast<u32>(MemoryCardType::Count); i++)
|
for (u32 i = 0; i < static_cast<u32>(MemoryCardType::Count); i++)
|
||||||
|
@ -331,6 +334,18 @@ void GamePropertiesDialog::populateGameSettings()
|
||||||
QSignalBlocker sb(m_ui.userControllerType2);
|
QSignalBlocker sb(m_ui.userControllerType2);
|
||||||
m_ui.userControllerType2->setCurrentIndex(static_cast<int>(gs.controller_2_type.value()) + 1);
|
m_ui.userControllerType2->setCurrentIndex(static_cast<int>(gs.controller_2_type.value()) + 1);
|
||||||
}
|
}
|
||||||
|
if (!gs.input_profile_name.empty())
|
||||||
|
{
|
||||||
|
QSignalBlocker sb(m_ui.userInputProfile);
|
||||||
|
int index = m_ui.userInputProfile->findText(QString::fromStdString(gs.input_profile_name));
|
||||||
|
if (index < 0)
|
||||||
|
{
|
||||||
|
index = m_ui.userInputProfile->count();
|
||||||
|
m_ui.userInputProfile->addItem(QString::fromStdString(gs.input_profile_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ui.userInputProfile->setCurrentIndex(index);
|
||||||
|
}
|
||||||
|
|
||||||
if (gs.memory_card_1_type.has_value())
|
if (gs.memory_card_1_type.has_value())
|
||||||
{
|
{
|
||||||
|
@ -478,6 +493,14 @@ void GamePropertiesDialog::connectUi()
|
||||||
saveGameSettings();
|
saveGameSettings();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connect(m_ui.userInputProfile, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index) {
|
||||||
|
if (index <= 0)
|
||||||
|
m_game_settings.input_profile_name = {};
|
||||||
|
else
|
||||||
|
m_game_settings.input_profile_name = m_ui.userInputProfile->itemText(index).toStdString();
|
||||||
|
saveGameSettings();
|
||||||
|
});
|
||||||
|
|
||||||
connect(m_ui.userMemoryCard1Type, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index) {
|
connect(m_ui.userMemoryCard1Type, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index) {
|
||||||
if (index <= 0)
|
if (index <= 0)
|
||||||
m_game_settings.memory_card_1_type.reset();
|
m_game_settings.memory_card_1_type.reset();
|
||||||
|
|
|
@ -447,6 +447,16 @@
|
||||||
<item row="1" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="QComboBox" name="userControllerType2"/>
|
<widget class="QComboBox" name="userControllerType2"/>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_19">
|
||||||
|
<property name="text">
|
||||||
|
<string>Input Profile For Bindings:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QComboBox" name="userInputProfile"/>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|
|
@ -977,7 +977,10 @@ bool CommonHostInterface::HandleHostMouseEvent(HostMouseButton button, bool pres
|
||||||
void CommonHostInterface::UpdateInputMap(SettingsInterface& si)
|
void CommonHostInterface::UpdateInputMap(SettingsInterface& si)
|
||||||
{
|
{
|
||||||
ClearInputMap();
|
ClearInputMap();
|
||||||
|
|
||||||
|
if (!UpdateControllerInputMapFromGameSettings())
|
||||||
UpdateControllerInputMap(si);
|
UpdateControllerInputMap(si);
|
||||||
|
|
||||||
UpdateHotkeyInputMap(si);
|
UpdateHotkeyInputMap(si);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1677,6 +1680,19 @@ void CommonHostInterface::FindInputProfiles(const std::string& base_path, InputP
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string CommonHostInterface::GetInputProfilePath(const char* name) const
|
||||||
|
{
|
||||||
|
std::string path = GetUserDirectoryRelativePath("inputprofiles" FS_OSPATH_SEPARATOR_STR "%s.ini", name);
|
||||||
|
if (FileSystem::FileExists(path.c_str()))
|
||||||
|
return path;
|
||||||
|
|
||||||
|
path = GetProgramDirectoryRelativePath("inputprofiles" FS_OSPATH_SEPARATOR_STR "%s.ini", name);
|
||||||
|
if (FileSystem::FileExists(path.c_str()))
|
||||||
|
return path;
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
void CommonHostInterface::ClearAllControllerBindings(SettingsInterface& si)
|
void CommonHostInterface::ClearAllControllerBindings(SettingsInterface& si)
|
||||||
{
|
{
|
||||||
for (u32 controller_index = 1; controller_index <= NUM_CONTROLLER_AND_CARD_PORTS; controller_index++)
|
for (u32 controller_index = 1; controller_index <= NUM_CONTROLLER_AND_CARD_PORTS; controller_index++)
|
||||||
|
@ -2282,6 +2298,35 @@ void CommonHostInterface::ApplyGameSettings(bool display_osd_messages)
|
||||||
gs->ApplySettings(display_osd_messages);
|
gs->ApplySettings(display_osd_messages);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CommonHostInterface::UpdateControllerInputMapFromGameSettings()
|
||||||
|
{
|
||||||
|
// this gets called while booting, so can't use valid
|
||||||
|
if (System::IsShutdown() || System::GetRunningCode().empty() || !g_settings.apply_game_settings)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const GameSettings::Entry* gs = m_game_list->GetGameSettings(System::GetRunningPath(), System::GetRunningCode());
|
||||||
|
if (!gs || gs->input_profile_name.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::string path = GetInputProfilePath(gs->input_profile_name.c_str());
|
||||||
|
if (path.empty())
|
||||||
|
{
|
||||||
|
AddFormattedOSDMessage(10.0f, TranslateString("OSDMessage", "Input profile '%s' cannot be found."),
|
||||||
|
gs->input_profile_name.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (System::GetState() == System::State::Starting)
|
||||||
|
{
|
||||||
|
AddFormattedOSDMessage(5.0f, TranslateString("OSDMessage", "Using input profile '%s'."),
|
||||||
|
gs->input_profile_name.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
INISettingsInterface si(std::move(path));
|
||||||
|
UpdateControllerInputMap(si);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
std::string CommonHostInterface::GetCheatFileName() const
|
std::string CommonHostInterface::GetCheatFileName() const
|
||||||
{
|
{
|
||||||
const std::string& title = System::GetRunningTitle();
|
const std::string& title = System::GetRunningTitle();
|
||||||
|
|
|
@ -233,6 +233,9 @@ protected:
|
||||||
/// Returns a list of all input profiles. first - name, second - path
|
/// Returns a list of all input profiles. first - name, second - path
|
||||||
InputProfileList GetInputProfileList() const;
|
InputProfileList GetInputProfileList() const;
|
||||||
|
|
||||||
|
/// Returns the path for an input profile.
|
||||||
|
std::string GetInputProfilePath(const char* name) const;
|
||||||
|
|
||||||
/// Applies the specified input profile.
|
/// Applies the specified input profile.
|
||||||
void ApplyInputProfile(const char* profile_path, SettingsInterface& si);
|
void ApplyInputProfile(const char* profile_path, SettingsInterface& si);
|
||||||
|
|
||||||
|
@ -330,6 +333,7 @@ private:
|
||||||
void RegisterAudioHotkeys();
|
void RegisterAudioHotkeys();
|
||||||
void FindInputProfiles(const std::string& base_path, InputProfileList* out_list) const;
|
void FindInputProfiles(const std::string& base_path, InputProfileList* out_list) const;
|
||||||
void UpdateControllerInputMap(SettingsInterface& si);
|
void UpdateControllerInputMap(SettingsInterface& si);
|
||||||
|
bool UpdateControllerInputMapFromGameSettings();
|
||||||
void UpdateHotkeyInputMap(SettingsInterface& si);
|
void UpdateHotkeyInputMap(SettingsInterface& si);
|
||||||
void ClearAllControllerBindings(SettingsInterface& si);
|
void ClearAllControllerBindings(SettingsInterface& si);
|
||||||
|
|
||||||
|
|
|
@ -115,7 +115,7 @@ private:
|
||||||
enum : u32
|
enum : u32
|
||||||
{
|
{
|
||||||
GAME_LIST_CACHE_SIGNATURE = 0x45434C47,
|
GAME_LIST_CACHE_SIGNATURE = 0x45434C47,
|
||||||
GAME_LIST_CACHE_VERSION = 11
|
GAME_LIST_CACHE_VERSION = 12
|
||||||
};
|
};
|
||||||
|
|
||||||
using DatabaseMap = std::unordered_map<std::string, GameListDatabaseEntry>;
|
using DatabaseMap = std::unordered_map<std::string, GameListDatabaseEntry>;
|
||||||
|
|
|
@ -121,7 +121,7 @@ bool Entry::LoadFromStream(ByteStream* stream)
|
||||||
!ReadOptionalFromStream(stream, &controller_2_type) || !ReadOptionalFromStream(stream, &memory_card_1_type) ||
|
!ReadOptionalFromStream(stream, &controller_2_type) || !ReadOptionalFromStream(stream, &memory_card_1_type) ||
|
||||||
!ReadOptionalFromStream(stream, &memory_card_2_type) ||
|
!ReadOptionalFromStream(stream, &memory_card_2_type) ||
|
||||||
!ReadStringFromStream(stream, &memory_card_1_shared_path) ||
|
!ReadStringFromStream(stream, &memory_card_1_shared_path) ||
|
||||||
!ReadStringFromStream(stream, &memory_card_2_shared_path))
|
!ReadStringFromStream(stream, &memory_card_2_shared_path) || !ReadStringFromStream(stream, &input_profile_name))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -162,7 +162,7 @@ bool Entry::SaveToStream(ByteStream* stream) const
|
||||||
WriteOptionalToStream(stream, gpu_pgxp) && WriteOptionalToStream(stream, controller_1_type) &&
|
WriteOptionalToStream(stream, gpu_pgxp) && WriteOptionalToStream(stream, controller_1_type) &&
|
||||||
WriteOptionalToStream(stream, controller_2_type) && WriteOptionalToStream(stream, memory_card_1_type) &&
|
WriteOptionalToStream(stream, controller_2_type) && WriteOptionalToStream(stream, memory_card_1_type) &&
|
||||||
WriteOptionalToStream(stream, memory_card_2_type) && WriteStringToStream(stream, memory_card_1_shared_path) &&
|
WriteOptionalToStream(stream, memory_card_2_type) && WriteStringToStream(stream, memory_card_1_shared_path) &&
|
||||||
WriteStringToStream(stream, memory_card_2_shared_path);
|
WriteStringToStream(stream, memory_card_2_shared_path) && WriteStringToStream(stream, input_profile_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ParseIniSection(Entry* entry, const char* section, const CSimpleIniA& ini)
|
static void ParseIniSection(Entry* entry, const char* section, const CSimpleIniA& ini)
|
||||||
|
@ -247,6 +247,9 @@ static void ParseIniSection(Entry* entry, const char* section, const CSimpleIniA
|
||||||
cvalue = ini.GetValue(section, "MemoryCard2SharedPath");
|
cvalue = ini.GetValue(section, "MemoryCard2SharedPath");
|
||||||
if (cvalue)
|
if (cvalue)
|
||||||
entry->memory_card_2_shared_path = cvalue;
|
entry->memory_card_2_shared_path = cvalue;
|
||||||
|
cvalue = ini.GetValue(section, "InputProfileName");
|
||||||
|
if (cvalue)
|
||||||
|
entry->input_profile_name = cvalue;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void StoreIniSection(const Entry& entry, const char* section, CSimpleIniA& ini)
|
static void StoreIniSection(const Entry& entry, const char* section, CSimpleIniA& ini)
|
||||||
|
@ -316,6 +319,8 @@ static void StoreIniSection(const Entry& entry, const char* section, CSimpleIniA
|
||||||
ini.SetValue(section, "MemoryCard1SharedPath", entry.memory_card_1_shared_path.c_str());
|
ini.SetValue(section, "MemoryCard1SharedPath", entry.memory_card_1_shared_path.c_str());
|
||||||
if (!entry.memory_card_2_shared_path.empty())
|
if (!entry.memory_card_2_shared_path.empty())
|
||||||
ini.SetValue(section, "MemoryCard2SharedPath", entry.memory_card_2_shared_path.c_str());
|
ini.SetValue(section, "MemoryCard2SharedPath", entry.memory_card_2_shared_path.c_str());
|
||||||
|
if (!entry.input_profile_name.empty())
|
||||||
|
ini.SetValue(section, "InputProfileName", entry.input_profile_name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
Database::Database() = default;
|
Database::Database() = default;
|
||||||
|
|
|
@ -61,6 +61,7 @@ struct Entry
|
||||||
std::optional<MemoryCardType> memory_card_2_type;
|
std::optional<MemoryCardType> memory_card_2_type;
|
||||||
std::string memory_card_1_shared_path;
|
std::string memory_card_1_shared_path;
|
||||||
std::string memory_card_2_shared_path;
|
std::string memory_card_2_shared_path;
|
||||||
|
std::string input_profile_name;
|
||||||
|
|
||||||
ALWAYS_INLINE bool HasTrait(Trait trait) const { return traits[static_cast<int>(trait)]; }
|
ALWAYS_INLINE bool HasTrait(Trait trait) const { return traits[static_cast<int>(trait)]; }
|
||||||
ALWAYS_INLINE void AddTrait(Trait trait) { traits[static_cast<int>(trait)] = true; }
|
ALWAYS_INLINE void AddTrait(Trait trait) { traits[static_cast<int>(trait)] = true; }
|
||||||
|
|
Loading…
Reference in a new issue