diff --git a/core/libs/prx/libSceKeyboard/src/keyboard_impl.cpp b/core/libs/prx/libSceKeyboard/src/keyboard_impl.cpp index 954040b147..ba9d9bfff8 100644 --- a/core/libs/prx/libSceKeyboard/src/keyboard_impl.cpp +++ b/core/libs/prx/libSceKeyboard/src/keyboard_impl.cpp @@ -175,17 +175,15 @@ int GetKey2Char(std::int32_t handle, std::int32_t arrange, std::uint32_t ledStat { std::lock_guard lock(keyboardMutex); if (!initialized) return KEYBOARD_ERROR_NOT_INITIALIZED; + if (keyCode < 4 || keyCode > 231) return KEYBOARD_ERROR_INVALID_ARG; if (handle != KEYBOARD_HANDLE || !opened) return KEYBOARD_ERROR_INVALID_HANDLE; } if (charData == nullptr) return KEYBOARD_ERROR_INVALID_ARG; if (arrange != KEYBOARD_ARRANGEMENT_101 && arrange != KEYBOARD_ARRANGEMENT_106) return KEYBOARD_ERROR_INVALID_ARG; - if (arrange == KEYBOARD_ARRANGEMENT_106 && (ledState & KEYBOARD_LED_KANA) != 0) { - NotImplemented_nid_no_patch(__func__); - return KEYBOARD_ERROR_INVALID_ARG; - } *charData = {}; - charData->char_code = character(arrange == KEYBOARD_ARRANGEMENT_106, ledState, modifierKey, keyCode); - charData->processed = charData->char_code != 0; + const std::uint16_t converted = character(arrange == KEYBOARD_ARRANGEMENT_106, ledState, modifierKey, keyCode); + charData->char_code = converted != 0 ? converted : keyCode; + charData->processed = converted != 0; charData->length = charData->processed ? 1 : 0; return KEYBOARD_OK; } diff --git a/core/libs/prx/libSceKeyboard/tests/Keyboard.cpp b/core/libs/prx/libSceKeyboard/tests/Keyboard.cpp index 0307b994df..1b063419a0 100644 --- a/core/libs/prx/libSceKeyboard/tests/Keyboard.cpp +++ b/core/libs/prx/libSceKeyboard/tests/Keyboard.cpp @@ -4,7 +4,11 @@ #include "SDL_events.h" #include "SDL_keyboard.h" +#include #include +#include +#include +#include #include extern "C" { @@ -19,7 +23,11 @@ int APS5_VABI sceKeyboardConnectPort(void); int APS5_VABI sceKeyboardDisconnectAll(void); } -static void Require(bool value) { if (!value) std::abort(); } +static void Require(bool value, std::source_location location = std::source_location::current()) { + if (value) return; + std::fprintf(stderr, "%s:%u: keyboard assertion failed\n", location.file_name(), location.line()); + std::abort(); +} static SDL_Event KeyEvent(Uint32 type, SDL_Scancode scancode, Uint16 mod = 0, Uint8 repeat = 0) { SDL_Event event{}; @@ -34,14 +42,63 @@ static SDL_Event KeyEvent(Uint32 type, SDL_Scancode scancode, Uint16 mod = 0, Ui static std::uint16_t Char(std::uint32_t led, std::uint32_t modifierKey, std::uint16_t keyCode, std::int32_t arrange = KEYBOARD_ARRANGEMENT_101) { KeyboardCharData data{}; Require(sceKeyboardGetKey2Char(KEYBOARD_HANDLE, arrange, led, modifierKey, keyCode, &data) == KEYBOARD_OK); - Require(data.processed == (data.char_code != 0) && data.length == (data.processed ? 1 : 0)); - return data.char_code; + Require(data.processed ? data.char_code != 0 : data.char_code == keyCode); + Require(data.length == (data.processed ? 1 : 0)); + return data.processed ? data.char_code : 0; } static std::uint16_t Jis(std::uint32_t modifierKey, std::uint16_t keyCode, std::uint32_t led = 0) { return Char(led, modifierKey, keyCode, KEYBOARD_ARRANGEMENT_106); } +static void CheckInvalidKeys(bool opened) { + constexpr std::uint16_t invalidKeys[] = {0, 1, 2, 3, 232, 255, 256, 65535}; + for (const auto key : invalidKeys) { + for (const auto handle : {KEYBOARD_HANDLE, 42}) { + KeyboardCharData data; + std::memset(&data, 0xa5, sizeof(data)); + unsigned char before[sizeof(data)]; + std::memcpy(before, &data, sizeof(data)); + Require(sceKeyboardGetKey2Char(handle, KEYBOARD_ARRANGEMENT_101, 0, 0, key, &data) == KEYBOARD_ERROR_INVALID_ARG); + Require(std::memcmp(&data, before, sizeof(data)) == 0); + Require(sceKeyboardGetKey2Char(handle, KEYBOARD_ARRANGEMENT_106, KEYBOARD_LED_KANA, 0, key, nullptr) == KEYBOARD_ERROR_INVALID_ARG); + } + } + for (const auto key : {std::uint16_t{4}, std::uint16_t{231}}) { + KeyboardCharData data{}; + const auto expected = opened ? KEYBOARD_OK : KEYBOARD_ERROR_INVALID_HANDLE; + Require(sceKeyboardGetKey2Char(KEYBOARD_HANDLE, KEYBOARD_ARRANGEMENT_101, 0, 0, key, &data) == expected); + } +} + +static void CheckUnconvertedKeys() { + constexpr std::uint16_t unconvertedKeys[] = {0x29, 0x39, 0x3a, 0x4f, 0x59, 0x88, 0xe0, 0xe7}; + for (const auto arrange : {KEYBOARD_ARRANGEMENT_101, KEYBOARD_ARRANGEMENT_106}) { + for (const auto key : unconvertedKeys) { + KeyboardCharData data{}; + Require(sceKeyboardGetKey2Char(KEYBOARD_HANDLE, arrange, 0, 0, key, &data) == KEYBOARD_OK); + Require(!data.processed && data.char_code == key); + } + } +} + +static void CheckKanaLed() { + for (const auto arrange : {KEYBOARD_ARRANGEMENT_101, KEYBOARD_ARRANGEMENT_106}) { + for (const auto modifier : {std::uint8_t{0}, std::uint8_t{KEYBOARD_MOD_LEFT_SHIFT}, std::uint8_t{KEYBOARD_MOD_RIGHT_SHIFT}}) { + for (const auto led : {std::uint8_t{0}, std::uint8_t{KEYBOARD_LED_NUM_LOCK | KEYBOARD_LED_CAPS_LOCK}}) { + for (std::uint16_t key = 4; key <= 231; ++key) { + KeyboardCharData withoutKana{}; + KeyboardCharData withKana{}; + Require(sceKeyboardGetKey2Char(KEYBOARD_HANDLE, arrange, led, modifier, key, &withoutKana) == KEYBOARD_OK); + Require(sceKeyboardGetKey2Char(KEYBOARD_HANDLE, arrange, led | KEYBOARD_LED_KANA, modifier, key, &withKana) == KEYBOARD_OK); + Require(withKana.processed == withoutKana.processed); + Require(withKana.length == withoutKana.length && withKana.char_code == withoutKana.char_code); + } + } + } + } +} + int main() { KeyboardData data[16]{}; KeyboardCharData charData{}; @@ -50,11 +107,15 @@ int main() { Require(sceKeyboardGetConnection(KEYBOARD_HANDLE, &connected) == KEYBOARD_ERROR_NOT_INITIALIZED); Require(sceKeyboardInit() == KEYBOARD_OK); Require(sceKeyboardInit() == KEYBOARD_OK); + CheckInvalidKeys(false); Require(sceKeyboardOpen(1, 0, 1, nullptr) == KEYBOARD_ERROR_INVALID_ARG); Require(sceKeyboardOpen(1, 1, 0, nullptr) == KEYBOARD_ERROR_INVALID_ARG); Require(sceKeyboardGetKey2Char(KEYBOARD_HANDLE, KEYBOARD_ARRANGEMENT_101, 0, 0, 0x04, &charData) == KEYBOARD_ERROR_INVALID_HANDLE); Require(sceKeyboardGetConnection(KEYBOARD_HANDLE, &connected) == KEYBOARD_ERROR_INVALID_HANDLE); Require(sceKeyboardOpen(1, 0, 0, nullptr) == KEYBOARD_HANDLE); + CheckInvalidKeys(true); + CheckUnconvertedKeys(); + CheckKanaLed(); Require(sceKeyboardOpen(1, 0, 0, nullptr) == KEYBOARD_ERROR_ALREADY_OPENED); Require(sceKeyboardGetConnection(42, &connected) == KEYBOARD_ERROR_INVALID_HANDLE); Require(sceKeyboardGetConnection(KEYBOARD_HANDLE, nullptr) == KEYBOARD_ERROR_INVALID_ARG); diff --git a/docs/dev/debt/libSceKeyboard.md b/docs/dev/debt/libSceKeyboard.md index ba3dcb499f..df0713a7ed 100644 --- a/docs/dev/debt/libSceKeyboard.md +++ b/docs/dev/debt/libSceKeyboard.md @@ -2,7 +2,7 @@ ### Functional -- [sceKeyboardGetKey2Char](../../../core/libs/prx/libSceKeyboard/src/keyboard_impl.cpp): the original validates HID usages 4 through 231 before the handle and preserves unconverted HID usages in the output with `processed == 0`. The host instead accepts out-of-range usages and writes zero for unconverted keys. The original ignores the Kana LED, while the host throws for it. This partially implemented function contains `NotImplemented_nid_no_patch(__func__)`; its body is preserved under the requested restriction. One title (PPSA15246) disassembled for this entry calls it once with an output buffer cleared to 24 bytes, uses a character only when the result is 0 and the first output byte is nonzero, and then reads the 16-bit value at offset 8, which agrees with `processed` and `char_code` as laid out here. Which other output fields it reads and what its other arguments mean are not shown. +- [sceKeyboardGetKey2Char](../../../core/libs/prx/libSceKeyboard/src/keyboard_impl.cpp): initialization, output length and reserved bytes remain unverified on a console; unconverted keys retain the host's length of 0. One title (PPSA15246) disassembled for this entry checks the first output byte and reads the 16-bit value at offset 8. Which other output fields it reads and what its other arguments mean are not shown. ### Unknown function info