diff --git a/core/libs/prx/libSceConvertKeycode/Export.cpp b/core/libs/prx/libSceConvertKeycode/Export.cpp index f96e2deca1..e9a0df7392 100644 --- a/core/libs/prx/libSceConvertKeycode/Export.cpp +++ b/core/libs/prx/libSceConvertKeycode/Export.cpp @@ -2409,23 +2409,44 @@ bool Ps4Client() { return false; } } + +std::uint32_t CompiledSdkVersion() { + const auto* process = static_cast(ApplicationProcessParameters_nid_no_patch()); + std::uint32_t sdkVersion; + std::memcpy(&sdkVersion, process + 16, sizeof(sdkVersion)); + return sdkVersion; +} + +bool Ps4ClientOnce() { + static const bool ps4Client = Ps4Client(); + return ps4Client; +} + +bool KeyboardTypeAvailable(std::uint32_t type, std::uint32_t sdkVersion, bool ps4Client) { + if (type >= 31 && ps4Client && sdkVersion < 0x05000000u) return false; + if (type >= 38 && sdkVersion < (ps4Client ? 0x10000000u : 0x06000000u)) return false; + if (type == 39 && (ps4Client || sdkVersion < 0x08000000u)) return false; + return true; +} } // namespace extern "C" { int APS5_VABI sceConvertKeycodeGetImeKeyboardType(int32_t userId, uint32_t* type) { const bool ps4Client = Ps4Client(); - const auto* process = static_cast(ApplicationProcessParameters_nid_no_patch()); - std::uint32_t sdkVersion; - std::memcpy(&sdkVersion, process + 16, sizeof(sdkVersion)); - return ImeKeyboardType::Resolve(userId, type, sdkVersion, ps4Client, sceUserServiceGetKeyboardType, sceUserServiceGetJapaneseInputType, sceUserServiceGetTraditionalChineseInputType); + return ImeKeyboardType::Resolve(userId, type, CompiledSdkVersion(), ps4Client, sceUserServiceGetKeyboardType, sceUserServiceGetJapaneseInputType, sceUserServiceGetTraditionalChineseInputType); } int APS5_VABI sceConvertKeycodeGetVirtualKeycode(std::uint16_t keycode, std::uint32_t keyboardType, std::uint16_t* virtualKeycode) { if (virtualKeycode == nullptr) return kErrInvalidAddress; if (keyboardType == 0 || keyboardType > VirtualKeys.size()) return kErrInvalidKeyboardType; - if (keyboardType >= 31) throw std::runtime_error("sceConvertKeycodeGetVirtualKeycode: guest sdk version is unavailable"); + const std::uint32_t sdkVersion = CompiledSdkVersion(); + const bool ps4Client = Ps4ClientOnce(); + if (!KeyboardTypeAvailable(keyboardType, sdkVersion, ps4Client)) return kErrInvalidKeyboardType; auto value = keycode < BaseVirtualKeys.size() ? VirtualKeys[keyboardType - 1][keycode] : 255; - if ((keyboardType == 2 || keyboardType == 3 || keyboardType == 12) && (value == 89 || value == 90)) throw std::runtime_error("sceConvertKeycodeGetVirtualKeycode: guest sdk version is unavailable"); + if ((keyboardType == 2 || keyboardType == 3 || keyboardType == 12) && sdkVersion < (ps4Client ? 0x09000000u : 0x04000000u)) { + if (value == 89) value = 90; + else if (value == 90) value = 89; + } *virtualKeycode = static_cast(value); return 0; } @@ -2438,7 +2459,7 @@ int APS5_VABI sceConvertKeycodeGetCharacterFromKeyboardData(const KeyboardData* int APS5_VABI sceConvertKeycodeGetCharacter(std::uint16_t keycode, std::uint32_t state, std::uint32_t type, char16_t* character) { if (character == nullptr) return kErrInvalidAddress; if (type == 0 || type > 39) return kErrInvalidKeyboardType; - if (type >= 31) throw std::runtime_error("sceConvertKeycodeGetCharacter: guest sdk version is unavailable"); + if (!KeyboardTypeAvailable(type, CompiledSdkVersion(), Ps4ClientOnce())) return kErrInvalidKeyboardType; *character = ConvertCharacter(keycode, state, type); return 0; } diff --git a/core/libs/tests/GuestConvertKeycode.cpp b/core/libs/tests/GuestConvertKeycode.cpp index d2d3cb463f..babf943c0c 100644 --- a/core/libs/tests/GuestConvertKeycode.cpp +++ b/core/libs/tests/GuestConvertKeycode.cpp @@ -139,16 +139,28 @@ int main() { Require(sceConvertKeycodeGetVirtualKeycode(100, 4, &virtualKey) == 0 && virtualKey == 255); Require(sceConvertKeycodeGetVirtualKeycode(136, 24, &virtualKey) == 0 && virtualKey == 21); Require(sceConvertKeycodeGetVirtualKeycode(65535, 4, &virtualKey) == 0 && virtualKey == 255); - for (std::uint32_t layout : {2u, 3u, 12u, 31u, 39u}) { - virtualKey = 0xABCD; - bool rejected = false; - try { - sceConvertKeycodeGetVirtualKeycode(28, layout, &virtualKey); - - } catch (const std::runtime_error&) { - rejected = true; + for (std::uint32_t layout : {2u, 3u, 12u}) { + Require(sceConvertKeycodeGetVirtualKeycode(28, layout, &virtualKey) == 0 && virtualKey == 90); + Require(sceConvertKeycodeGetVirtualKeycode(29, layout, &virtualKey) == 0 && virtualKey == 89); + } + for (std::uint32_t layout : {31u, 38u, 39u}) { + Require(sceConvertKeycodeGetVirtualKeycode(28, layout, &virtualKey) == 0 && virtualKey == 89); + } + { + constexpr std::uint32_t oldSdkVersion = 0x03000000u; + std::memcpy(process.data() + 16, &oldSdkVersion, sizeof(oldSdkVersion)); + for (std::uint32_t layout : {2u, 3u, 12u}) { + Require(sceConvertKeycodeGetVirtualKeycode(28, layout, &virtualKey) == 0 && virtualKey == 89); + Require(sceConvertKeycodeGetVirtualKeycode(29, layout, &virtualKey) == 0 && virtualKey == 90); } - Require(rejected && virtualKey == 0xABCD); + Require(sceConvertKeycodeGetVirtualKeycode(28, 31, &virtualKey) == 0 && virtualKey == 89); + char16_t rejectedCharacter = u'?'; + for (std::uint32_t layout : {38u, 39u}) { + virtualKey = 0xABCD; + Require(sceConvertKeycodeGetVirtualKeycode(28, layout, &virtualKey) == static_cast(0x80BC001Du) && virtualKey == 0xABCD); + Require(sceConvertKeycodeGetCharacter(28, 0, layout, &rejectedCharacter) == static_cast(0x80BC001Du) && rejectedCharacter == u'?'); + } + std::memcpy(process.data() + 16, &sdkVersion, sizeof(sdkVersion)); } // The exported function with the real UserService: unknown users fail, the initial user reports the US-English default (keyboard type 20 -> IME 4). @@ -208,6 +220,8 @@ int main() { Require(sceConvertKeycodeGetCharacter(31, 0x4000300, 30, &character) == 0 && character == 0x200C); Require(sceConvertKeycodeGetCharacter(32, 0x4000300, 30, &character) == 0 && character == 0x200E); Require(sceConvertKeycodeGetCharacter(33, 0x4000300, 30, &character) == 0 && character == 0x200F); + Require(sceConvertKeycodeGetCharacter(28, 0, 31, &character) == 0 && character == u'y'); + Require(sceConvertKeycodeGetCharacter(29, 0, 39, &character) == 0 && character == u'z'); for (std::uint32_t layout = 1; layout <= 30; ++layout) { for (std::uint16_t key : {83, 224, 225, 226, 227, 228, 229, 230, 231}) { Require(sceConvertKeycodeGetCharacter(key, 0xFFFFFFFFu, layout, &character) == 0 && character == 0); diff --git a/docs/dev/debt/libSceConvertKeycode.md b/docs/dev/debt/libSceConvertKeycode.md index 2c6bae6882..a1bb06d6a3 100644 --- a/docs/dev/debt/libSceConvertKeycode.md +++ b/docs/dev/debt/libSceConvertKeycode.md @@ -2,5 +2,5 @@ ### Functional -- [sceConvertKeycodeGetVirtualKeycode](../../../core/libs/prx/libSceConvertKeycode/Export.cpp) and `sceConvertKeycodeGetCharacter`: layouts 31–39 throw until the guest platform and compiled SDK version are exposed. `GetVirtualKeycode` also throws for version-dependent Y/Z mappings in layouts 2, 3 and 12. Mapping tables and modifier behavior are recovered; no SDK version is guessed. Three titles disassembled for this entry call `sceConvertKeycodeGetVirtualKeycode` at four sites (PPSA24779 0xa9199a, PPSA29866 0x2d8bdd, PPSA30601 0x15e4624 and 0x15e47da): the keyboard type is the constant 4 at 0xa9199a and 0x15e47da and a run-time value at the other two, and no value read is 2, 3, 12 or in 31 to 39. +- [sceConvertKeycodeGetVirtualKeycode](../../../core/libs/prx/libSceConvertKeycode/Export.cpp) and `sceConvertKeycodeGetCharacter`: layouts 31 and up need a PS5 title or a PS4 SDK of 0x05000000, 38 and 39 a PS5 SDK of 0x06000000 or a PS4 SDK of 0x10000000, and 39 a PS5 title with SDK 0x08000000; other titles get `0x80BC001D`. `GetVirtualKeycode` swaps the Y and Z keys of layouts 2, 3 and 12 below PS5 SDK 0x04000000 or PS4 SDK 0x09000000. These rules come from firmware 13.42's module, run natively, not from a console; the SDK version is the one in the process parameters. Three titles disassembled for this entry call `sceConvertKeycodeGetVirtualKeycode` at four sites (PPSA24779 0xa9199a, PPSA29866 0x2d8bdd, PPSA30601 0x15e4624 and 0x15e47da): the keyboard type is the constant 4 at 0xa9199a and 0x15e47da and a run-time value at the other two, and no value read is 2, 3, 12 or in 31 to 39. - [sceConvertKeycodeGetImeKeyboardType](../../../core/libs/prx/libSceConvertKeycode/Export.cpp): implemented exactly as the repository owner (boykopovar) specified the UserService keyboard type (plus the Japanese variant for type 37 and the Traditional Chinese variant for type 39) to IME keyboard type table, the SDK compatibility rules (38 becomes 4 for PS5 SDK below 0x06000000 or PS4 SDK below 0x10000000; 39 becomes 29 for PS5 SDK below 0x08000000 and for every PS4 client), the error codes and the unmatched-type result (success, IME type 0). The PS4/PS5 client rule reads the title ID prefix from `param.sfo` or `param.json` (`CUSA` is PS4); a missing, malformed or oversized ID or title, as in homebrew, is treated as PS5. Reliable detection inspects the original `eboot.bin`, which only the relinker reads, so it is not done. UserService reports the keyboard type of the configured system language, so the function returns the owner's IME type for that language (see libSceUserService.md).