diff --git a/core/libs/prx/libSceConvertKeycode/Export.cpp b/core/libs/prx/libSceConvertKeycode/Export.cpp index 4464089494..585890186a 100644 --- a/core/libs/prx/libSceConvertKeycode/Export.cpp +++ b/core/libs/prx/libSceConvertKeycode/Export.cpp @@ -226,6 +226,7 @@ constexpr auto VirtualKeys = [] { }(); constexpr int kErrInvalidKeyboardType = static_cast(0x80BC001Du); +constexpr int kErrInvalidParameter = static_cast(0x80BC0030u); constexpr auto CharacterTables = [] { std::array, 111> tables{}; tables[1][4] = 327777; @@ -2453,14 +2454,10 @@ int APS5_VABI sceConvertKeycodeGetVirtualKeycode(std::uint16_t keycode, std::uin int APS5_VABI sceConvertKeycodeGetCharacterFromKeyboardData(const KeyboardData* keyData, std::int32_t index, std::uint32_t state, std::uint32_t keyboardType, char16_t* character) { if (keyData == nullptr || character == nullptr) return kErrInvalidAddress; - if (keyboardType == 0 || keyboardType > 39) return kErrInvalidKeyboardType; - if (!KeyboardTypeAvailable(keyboardType, CompiledSdkVersion(), Ps4ClientOnce())) return kErrInvalidKeyboardType; - if (index < 0 || index >= keyData->length || index >= static_cast(KEYBOARD_MAX_KEYCODES)) { - throw std::runtime_error("sceConvertKeycodeGetCharacterFromKeyboardData: result for an index outside the key data is unknown"); - } - const std::uint16_t keycode = keyData->key_code[index]; - *character = ConvertCharacter(keycode, state, keyboardType); - return 0; + const auto length = static_cast(keyData->length); + if (length > KEYBOARD_MAX_KEYCODES || keyData->led >= 8 || keyData->modifier_key >= 0x100 || (state & 0xE3FFFF7Fu) != 0) return kErrInvalidParameter; + if (index < 0 || static_cast(index) >= length) return kErrInvalidParameter; + return sceConvertKeycodeGetCharacter(keyData->key_code[index], state | keyData->modifier_key << 8 | keyData->led << 16, keyboardType, character); } int APS5_VABI sceConvertKeycodeGetCharacter(std::uint16_t keycode, std::uint32_t state, std::uint32_t type, char16_t* character) { diff --git a/core/libs/tests/GuestConvertKeycode.cpp b/core/libs/tests/GuestConvertKeycode.cpp index 6e29956c43..a68c5deedc 100644 --- a/core/libs/tests/GuestConvertKeycode.cpp +++ b/core/libs/tests/GuestConvertKeycode.cpp @@ -260,16 +260,26 @@ int main() { char16_t kbdChar = u'?'; Require(sceConvertKeycodeGetCharacterFromKeyboardData(nullptr, 0, 0, 4, &kbdChar) == static_cast(0x80BC0031u)); Require(sceConvertKeycodeGetCharacterFromKeyboardData(&keyData, 0, 0, 4, nullptr) == static_cast(0x80BC0031u)); + constexpr int invalidParameter = static_cast(0x80BC0030u); for (std::int32_t badIndex : {-1, 1, 16}) { kbdChar = u'?'; - bool rejected = false; - try { - sceConvertKeycodeGetCharacterFromKeyboardData(&keyData, badIndex, 0, 4, &kbdChar); - } catch (const std::runtime_error&) { - rejected = true; - } - Require(rejected && kbdChar == u'?'); + Require(sceConvertKeycodeGetCharacterFromKeyboardData(&keyData, badIndex, 0, 4, &kbdChar) == invalidParameter && kbdChar == u'?'); } + for (std::uint32_t badState : {0x1u, 0x200u, 0x20000u}) { + kbdChar = u'?'; + Require(sceConvertKeycodeGetCharacterFromKeyboardData(&keyData, 0, badState, 4, &kbdChar) == invalidParameter && kbdChar == u'?'); + } + for (std::int32_t badLength : {0, 17, -1}) { + KeyboardData badData = keyData; + badData.length = badLength; + Require(sceConvertKeycodeGetCharacterFromKeyboardData(&badData, 0, 0, 4, &kbdChar) == invalidParameter); + } + KeyboardData badLed = keyData; + badLed.led = 8; + Require(sceConvertKeycodeGetCharacterFromKeyboardData(&badLed, 0, 0, 4, &kbdChar) == invalidParameter); + KeyboardData badModifier = keyData; + badModifier.modifier_key = 0x100; + Require(sceConvertKeycodeGetCharacterFromKeyboardData(&badModifier, 0, 0, 40, &kbdChar) == invalidParameter); for (std::uint32_t layout : {31u, 39u}) { kbdChar = u'?'; Require(sceConvertKeycodeGetCharacterFromKeyboardData(&keyData, 0, 0, layout, &kbdChar) == 0 && kbdChar == u'a'); @@ -277,6 +287,42 @@ int main() { Require(sceConvertKeycodeGetCharacterFromKeyboardData(&keyData, 0, 0, 0, &kbdChar) == static_cast(0x80BC001Du)); Require(sceConvertKeycodeGetCharacterFromKeyboardData(&keyData, 0, 0, 40, &kbdChar) == static_cast(0x80BC001Du)); Require(sceConvertKeycodeGetCharacterFromKeyboardData(&keyData, 0, 0, 4, &kbdChar) == 0 && kbdChar == u'a'); - Require(sceConvertKeycodeGetCharacterFromKeyboardData(&keyData, 0, 0x200, 4, &kbdChar) == 0 && kbdChar == u'A'); + Require(sceConvertKeycodeGetCharacterFromKeyboardData(&keyData, 0, 0x80, 4, &kbdChar) == 0 && kbdChar == u'a'); + KeyboardData shifted = keyData; + shifted.modifier_key = 0x02; + Require(sceConvertKeycodeGetCharacterFromKeyboardData(&shifted, 0, 0, 4, &kbdChar) == 0 && kbdChar == u'A'); + KeyboardData capsLock = keyData; + capsLock.led = 2; + Require(sceConvertKeycodeGetCharacterFromKeyboardData(&capsLock, 0, 0, 4, &kbdChar) == 0 && kbdChar == u'A'); + KeyboardData numLock = keyData; + numLock.led = 1; + numLock.key_code[0] = 89; + Require(sceConvertKeycodeGetCharacterFromKeyboardData(&numLock, 0, 0, 4, &kbdChar) == 0 && kbdChar == u'1'); + KeyboardData full = keyData; + full.length = 16; + full.key_code[15] = 5; + full.led = 7; + full.modifier_key = 0xFF; + char16_t expected = u'?'; + Require(sceConvertKeycodeGetCharacter(5, 0x7FF00, 4, &expected) == 0); + kbdChar = u'?'; + Require(sceConvertKeycodeGetCharacterFromKeyboardData(&full, 15, 0, 4, &kbdChar) == 0 && kbdChar == expected); + for (std::uint32_t goodState : {0x4000000u, 0x8000000u, 0x10000000u, 0x1C000080u}) { + for (std::uint32_t layout : {4u, 24u, 30u}) { + Require(sceConvertKeycodeGetCharacter(4, goodState, layout, &expected) == 0); + kbdChar = u'?'; + Require(sceConvertKeycodeGetCharacterFromKeyboardData(&keyData, 0, goodState, layout, &kbdChar) == 0 && kbdChar == expected); + } + } + Require(sceConvertKeycodeGetCharacterFromKeyboardData(&keyData, 0, 0x4000000, 24, &kbdChar) == 0 && kbdChar == 0x3141); + for (std::uint32_t badState : {0x40u, 0x100u, 0x20000000u, 0x80000000u}) { + kbdChar = u'?'; + Require(sceConvertKeycodeGetCharacterFromKeyboardData(&keyData, 0, badState, 4, &kbdChar) == invalidParameter && kbdChar == u'?'); + } + const std::uint32_t oldSdkVersion = 0x03000000u; + std::memcpy(process.data() + 16, &oldSdkVersion, sizeof(oldSdkVersion)); + Require(sceConvertKeycodeGetCharacterFromKeyboardData(&keyData, 0, 0, 38, &kbdChar) == static_cast(0x80BC001Du)); + Require(sceConvertKeycodeGetCharacterFromKeyboardData(&badLed, 0, 0, 38, &kbdChar) == invalidParameter); + std::memcpy(process.data() + 16, &sdkVersion, sizeof(sdkVersion)); return 0; } diff --git a/docs/dev/debt/libSceConvertKeycode.md b/docs/dev/debt/libSceConvertKeycode.md index ad1c428747..6b2929fc28 100644 --- a/docs/dev/debt/libSceConvertKeycode.md +++ b/docs/dev/debt/libSceConvertKeycode.md @@ -3,5 +3,5 @@ ### Functional - [sceConvertKeycodeGetVirtualKeycode](../../../core/libs/prx/libSceConvertKeycode/Export.cpp), `sceConvertKeycodeGetCharacter` and `sceConvertKeycodeGetCharacterFromKeyboardData`: 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. -- [sceConvertKeycodeGetCharacterFromKeyboardData](../../../core/libs/prx/libSceConvertKeycode/Export.cpp): an index outside `keyData->length` or `KEYBOARD_MAX_KEYCODES` throws; no reference gives the real result or validation order. +- [sceConvertKeycodeGetCharacterFromKeyboardData](../../../core/libs/prx/libSceConvertKeycode/Export.cpp): returns `0x80BC0030` for a `length` above 16 (or negative), an `led` of 8 or more, a `modifier_key` of 0x100 or more, a `state` bit outside 0x1C000080, or an index outside the key data, before the layout checks, and otherwise converts `key_code[index]` with `state | modifier_key << 8 | led << 16`. From firmware 13.42's module, run natively, not from a console. - [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).