diff --git a/Common/Input/InputState.cpp b/Common/Input/InputState.cpp index a788ed10a8..3db6dc137c 100644 --- a/Common/Input/InputState.cpp +++ b/Common/Input/InputState.cpp @@ -3,6 +3,7 @@ #include "Common/Input/InputState.h" #include "Common/Input/KeyCodes.h" +#include "Common/StringUtils.h" const char *GetDeviceName(int deviceId) { switch (deviceId) { @@ -79,6 +80,23 @@ int GetAnalogYDirection(int deviceId) { return 0; } +// NOTE: Changing the format of FromConfigString/ToConfigString breaks controls.ini backwards compatibility. +InputMapping InputMapping::FromConfigString(const std::string &str) { + std::vector parts; + SplitString(str, '-', parts); + int deviceId = atoi(parts[0].c_str()); + int keyCode = atoi(parts[1].c_str()); + + InputMapping mapping; + mapping.deviceId = deviceId; + mapping.keyCode = keyCode; + return mapping; +} + +std::string InputMapping::ToConfigString() const { + return StringFromFormat("%d-%d", deviceId, keyCode); +} + void InputMapping::FormatDebug(char *buffer, size_t bufSize) const { if (IsAxis()) { int direction; diff --git a/Common/Input/InputState.h b/Common/Input/InputState.h index 305939faae..73abefca31 100644 --- a/Common/Input/InputState.h +++ b/Common/Input/InputState.h @@ -5,8 +5,8 @@ #include #include +#include -#include "Common/Math/lin/vec3.h" #include "Common/Input/KeyCodes.h" #include "Common/Log.h" @@ -105,6 +105,9 @@ public: _dbg_assert_(direction != 0); } + static InputMapping FromConfigString(const std::string &str); + std::string ToConfigString() const; + int deviceId; int keyCode; // Can also represent an axis with direction, if encoded properly. diff --git a/Core/KeyMap.cpp b/Core/KeyMap.cpp index 994ddb9e60..01ecb9cffe 100644 --- a/Core/KeyMap.cpp +++ b/Core/KeyMap.cpp @@ -687,15 +687,12 @@ void LoadFromIni(IniFile &file) { SplitString(value, ',', mappings); for (size_t j = 0; j < mappings.size(); j++) { - // TODO: Properly parse multiple mappings. + MultiInputMapping input = MultiInputMapping::FromConfigString(mappings[j]); + SetInputMapping(psp_button_names[i].key, input, false); - std::vector parts; - SplitString(mappings[j], '-', parts); - int deviceId = atoi(parts[0].c_str()); - int keyCode = atoi(parts[1].c_str()); - - SetInputMapping(psp_button_names[i].key, MultiInputMapping(InputMapping(deviceId, keyCode)), false); - g_seenDeviceIds.insert(deviceId); + for (auto mapping : input.mappings) { + g_seenDeviceIds.insert(mapping.deviceId); + } } } @@ -711,11 +708,7 @@ void SaveToIni(IniFile &file) { std::string value; for (size_t j = 0; j < keys.size(); j++) { - auto mapping = keys[j].mappings[0]; - - char temp[128]; - sprintf(temp, "%i-%i", mapping.deviceId, mapping.keyCode); - value += temp; + value += keys[j].ToConfigString(); if (j != keys.size() - 1) value += ","; } @@ -858,4 +851,23 @@ const char *GetVirtKeyName(int vkey) { return g_vKeyNames[index]; } +MultiInputMapping MultiInputMapping::FromConfigString(const std::string &str) { + MultiInputMapping out; + std::vector parts; + SplitString(str, ':', parts); + for (auto iter : parts) { + out.mappings.push_back(InputMapping::FromConfigString(iter)); + } + return out; +} + +std::string MultiInputMapping::ToConfigString() const { + std::string out; + for (auto iter : mappings) { + out += iter.ToConfigString() + ":"; + } + out.pop_back(); // remove the last ':' + return out; +} + } // KeyMap diff --git a/Core/KeyMap.h b/Core/KeyMap.h index 3153cfe255..f638e53806 100644 --- a/Core/KeyMap.h +++ b/Core/KeyMap.h @@ -109,6 +109,10 @@ namespace KeyMap { explicit MultiInputMapping(const InputMapping &mapping) { mappings.push_back(mapping); } + + static MultiInputMapping FromConfigString(const std::string &str); + std::string ToConfigString() const; + bool operator <(const MultiInputMapping &other) { for (size_t i = 0; i < mappings.capacity(); i++) { // If one ran out of entries, the other wins. diff --git a/unittest/UnitTest.cpp b/unittest/UnitTest.cpp index 10f09e872e..bcbb7ad4e0 100644 --- a/unittest/UnitTest.cpp +++ b/unittest/UnitTest.cpp @@ -62,6 +62,7 @@ #include "Common/File/VFS/DirectoryReader.h" #include "Core/FileSystems/ISOFileSystem.h" #include "Core/MemMap.h" +#include "Core/KeyMap.h" #include "Core/MIPS/MIPSVFPUUtils.h" #include "GPU/Common/TextureDecoder.h" #include "GPU/Common/GPUStateUtils.h" @@ -849,6 +850,47 @@ static bool TestDepthMath() { return true; } +bool TestInputMapping() { + InputMapping mapping; + mapping.deviceId = 10; + mapping.keyCode = 20; + InputMapping mapping2; + mapping2.deviceId = 18; + mapping2.keyCode = 38; + std::string cfg = mapping.ToConfigString(); + + InputMapping parsedMapping = InputMapping::FromConfigString(cfg); + EXPECT_EQ_INT(parsedMapping.deviceId, mapping.deviceId); + EXPECT_EQ_INT(parsedMapping.keyCode, mapping.keyCode); + + using KeyMap::MultiInputMapping; + MultiInputMapping multi(mapping); + + EXPECT_EQ_STR(multi.ToConfigString(), mapping.ToConfigString()); + + multi.mappings.push_back(mapping2); + EXPECT_FALSE(multi.EqualsSingleMapping(mapping)); + EXPECT_TRUE(multi.mappings.contains(mapping2)); + EXPECT_TRUE(multi.mappings.contains(mapping)); + + std::string cfgMulti = multi.ToConfigString(); + + EXPECT_EQ_STR(cfgMulti, std::string("10-20:18-38")); + + MultiInputMapping parsedMulti = MultiInputMapping::FromConfigString(cfgMulti); + + EXPECT_EQ_INT((int)parsedMulti.mappings.size(), 2); + + // OK, both single and multiple mappings parse. Let's now see if the old parsing can handle a multimapping. + // This is a requirement for the new format. + + InputMapping parsedMultiSingle = InputMapping::FromConfigString(cfgMulti); // yes this is an intentional mismatch + // We should get the first mapping. + EXPECT_TRUE(parsedMultiSingle == mapping); + return true; +} + + typedef bool (*TestFunc)(); struct TestItem { const char *name; @@ -901,6 +943,7 @@ TestItem availableTests[] = { TEST_ITEM(TinySet), TEST_ITEM(SmallDataConvert), TEST_ITEM(DepthMath), + TEST_ITEM(InputMapping), }; int main(int argc, const char *argv[]) {