From 8d7ff94689a870a9f925b1b5b644a4468369e64d Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 6 Oct 2026 14:02:11 +0000 Subject: [PATCH] feat(libSceKeyboard): translate the 106-key arrangement in sceKeyboardGetKey2Char The 106-key (Japanese) arrangement threw. Its alphanumeric layer now follows the JIS layout of the HID usages: the symbol keys right of the digits and letters, the Ro key (0x87) and the Yen key (0x89). With the Kana LED set it still throws, as the kana the console returns are unknown. Co-Authored-By: Claude Opus 5.5 --- .../libSceKeyboard/include/keyboard_structs.h | 1 + .../prx/libSceKeyboard/src/keyboard_impl.cpp | 15 ++++++++---- .../prx/libSceKeyboard/tests/Keyboard.cpp | 23 +++++++++++++++++-- docs/dev/TechnicalDebt.md | 2 +- 4 files changed, 33 insertions(+), 8 deletions(-) diff --git a/core/libs/prx/libSceKeyboard/include/keyboard_structs.h b/core/libs/prx/libSceKeyboard/include/keyboard_structs.h index 326fa4b84..c219f233d 100644 --- a/core/libs/prx/libSceKeyboard/include/keyboard_structs.h +++ b/core/libs/prx/libSceKeyboard/include/keyboard_structs.h @@ -18,6 +18,7 @@ constexpr std::int32_t KEYBOARD_ARRANGEMENT_106 = 1; constexpr std::uint32_t KEYBOARD_LED_NUM_LOCK = 1; constexpr std::uint32_t KEYBOARD_LED_CAPS_LOCK = 2; constexpr std::uint32_t KEYBOARD_LED_SCROLL_LOCK = 4; +constexpr std::uint32_t KEYBOARD_LED_KANA = 16; constexpr std::uint32_t KEYBOARD_MOD_LEFT_SHIFT = 2; constexpr std::uint32_t KEYBOARD_MOD_RIGHT_SHIFT = 32; diff --git a/core/libs/prx/libSceKeyboard/src/keyboard_impl.cpp b/core/libs/prx/libSceKeyboard/src/keyboard_impl.cpp index 7bc369767..2bb6273e7 100644 --- a/core/libs/prx/libSceKeyboard/src/keyboard_impl.cpp +++ b/core/libs/prx/libSceKeyboard/src/keyboard_impl.cpp @@ -21,6 +21,8 @@ int keyCount = 0; constexpr char US_NORMAL[] = "1234567890\n\0\b\t -=[]\\\0;'`,./"; constexpr char US_SHIFTED[] = "!@#$%^&*()\n\0\b\t _+{}|\0:\"~<>?"; +constexpr char JIS_NORMAL[] = "1234567890\n\0\b\t -^@[]];:\0,./"; +constexpr char JIS_SHIFTED[] = "!\"#$%&'()\0\n\0\b\t =~`{}}+*\0<>?"; constexpr char KEYPAD_DIGITS[] = "1234567890."; KeyboardData snapshot() { @@ -69,15 +71,18 @@ bool updateKey(std::uint16_t keyCode, bool pressed) { return true; } -std::uint16_t usCharacter(std::uint32_t ledState, std::uint32_t modifierKey, std::uint16_t keyCode) { +std::uint16_t character(bool jis, std::uint32_t ledState, std::uint32_t modifierKey, std::uint16_t keyCode) { const bool shift = (modifierKey & (KEYBOARD_MOD_LEFT_SHIFT | KEYBOARD_MOD_RIGHT_SHIFT)) != 0; if (keyCode >= 0x04 && keyCode <= 0x1d) { const bool upper = shift != ((ledState & KEYBOARD_LED_CAPS_LOCK) != 0); return static_cast((upper ? 'A' : 'a') + keyCode - 0x04); } if (keyCode >= 0x1e && keyCode <= 0x38) { - return static_cast((shift ? US_SHIFTED : US_NORMAL)[keyCode - 0x1e]); + const char* table = jis ? (shift ? JIS_SHIFTED : JIS_NORMAL) : (shift ? US_SHIFTED : US_NORMAL); + return static_cast(table[keyCode - 0x1e]); } + if (jis && keyCode == 0x87) return shift ? '_' : '\\'; + if (jis && keyCode == 0x89) return shift ? '|' : '\\'; switch (keyCode) { case 0x54: return '/'; case 0x55: return '*'; @@ -155,13 +160,13 @@ int GetKey2Char(std::int32_t handle, std::int32_t arrange, std::uint32_t ledStat if (handle != KEYBOARD_HANDLE || !opened) return KEYBOARD_ERROR_INVALID_HANDLE; } if (charData == nullptr) return KEYBOARD_ERROR_INVALID_ARG; - if (arrange == KEYBOARD_ARRANGEMENT_106) { + 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; } - if (arrange != KEYBOARD_ARRANGEMENT_101) return KEYBOARD_ERROR_INVALID_ARG; *charData = {}; - charData->char_code = usCharacter(ledState, modifierKey, keyCode); + charData->char_code = character(arrange == KEYBOARD_ARRANGEMENT_106, ledState, modifierKey, keyCode); charData->processed = charData->char_code != 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 3b633176d..802e1da75 100644 --- a/core/libs/prx/libSceKeyboard/tests/Keyboard.cpp +++ b/core/libs/prx/libSceKeyboard/tests/Keyboard.cpp @@ -27,13 +27,17 @@ static SDL_Event KeyEvent(Uint32 type, SDL_Scancode scancode, Uint16 mod = 0, Ui return event; } -static std::uint16_t Char(std::uint32_t led, std::uint32_t modifierKey, std::uint16_t keyCode) { +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, KEYBOARD_ARRANGEMENT_101, led, modifierKey, keyCode, &data) == KEYBOARD_OK); + 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; } +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); +} + int main() { KeyboardData data[16]{}; KeyboardCharData charData{}; @@ -115,7 +119,22 @@ int main() { Require(Char(0, 0, 0x59) == 0 && Char(KEYBOARD_LED_NUM_LOCK, 0, 0x59) == '1'); Require(Char(KEYBOARD_LED_NUM_LOCK, 0, 0x62) == '0' && Char(KEYBOARD_LED_NUM_LOCK, 0, 0x63) == '.'); Require(Char(0, 0, 0x54) == '/' && Char(0, 0, 0x57) == '+' && Char(0, 0, 0x58) == '\n'); + Require(Char(0, 0, 0x87) == 0 && Char(0, 0, 0x89) == 0); + + constexpr std::uint32_t shift = KEYBOARD_MOD_LEFT_SHIFT; + Require(Jis(0, 0x04) == 'a' && Jis(shift, 0x04) == 'A' && Jis(KEYBOARD_MOD_RIGHT_SHIFT, 0x04, KEYBOARD_LED_CAPS_LOCK) == 'a'); + Require(Jis(0, 0x1e) == '1' && Jis(shift, 0x1e) == '!' && Jis(shift, 0x1f) == '"' && Jis(shift, 0x23) == '&'); + Require(Jis(shift, 0x24) == '\'' && Jis(shift, 0x25) == '(' && Jis(shift, 0x26) == ')' && Jis(0, 0x27) == '0' && Jis(shift, 0x27) == 0); + Require(Jis(0, 0x28) == '\n' && Jis(0, 0x29) == 0 && Jis(0, 0x2a) == '\b' && Jis(0, 0x2b) == '\t' && Jis(shift, 0x2c) == ' '); + Require(Jis(0, 0x2d) == '-' && Jis(shift, 0x2d) == '=' && Jis(0, 0x2e) == '^' && Jis(shift, 0x2e) == '~'); + Require(Jis(0, 0x2f) == '@' && Jis(shift, 0x2f) == '`' && Jis(0, 0x30) == '[' && Jis(shift, 0x30) == '{'); + Require(Jis(0, 0x31) == ']' && Jis(shift, 0x31) == '}' && Jis(0, 0x32) == ']' && Jis(shift, 0x32) == '}'); + Require(Jis(0, 0x33) == ';' && Jis(shift, 0x33) == '+' && Jis(0, 0x34) == ':' && Jis(shift, 0x34) == '*'); + Require(Jis(0, 0x35) == 0 && Jis(shift, 0x35) == 0 && Jis(0, 0x36) == ',' && Jis(shift, 0x37) == '>' && Jis(shift, 0x38) == '?'); + Require(Jis(0, 0x87) == '\\' && Jis(shift, 0x87) == '_' && Jis(0, 0x89) == '\\' && Jis(shift, 0x89) == '|'); + Require(Jis(0, 0x59, KEYBOARD_LED_NUM_LOCK) == '1' && Jis(0, 0x59) == 0 && Jis(0, 0x55) == '*' && Jis(0, 0xe1) == 0); Require(sceKeyboardGetKey2Char(KEYBOARD_HANDLE, KEYBOARD_ARRANGEMENT_101, 0, 0, 0x04, nullptr) == KEYBOARD_ERROR_INVALID_ARG); + Require(sceKeyboardGetKey2Char(KEYBOARD_HANDLE, KEYBOARD_ARRANGEMENT_106, 0, 0, 0x04, nullptr) == KEYBOARD_ERROR_INVALID_ARG); Require(sceKeyboardGetKey2Char(KEYBOARD_HANDLE, 2, 0, 0, 0x04, &charData) == KEYBOARD_ERROR_INVALID_ARG); Require(sceKeyboardClose(KEYBOARD_HANDLE) == KEYBOARD_OK); diff --git a/docs/dev/TechnicalDebt.md b/docs/dev/TechnicalDebt.md index 645d976a4..96ffc1403 100644 --- a/docs/dev/TechnicalDebt.md +++ b/docs/dev/TechnicalDebt.md @@ -159,7 +159,7 @@ Throughout the project, every function at every stage either **does exactly what - [DCC display buffers](../../core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp) are presented only from uniform uncompressed or fast-clear keys; other keys, and register-clear keys over a pending image, throw. Keys are read as one byte per 256 bytes (the driver's model), not in the console's layout. - `--to-intel` guest module trampolines are covered only by a synthetic relinker test; no game title has been verified with them on Linux or Windows. - `--to-intel` replaces the approximate VEX.128 register forms of VRSQRTPS and VRCPPS with [correctly rounded](../../core/relinker/codegen/src/x86/ReciprocalLowering.cpp) 1/sqrt(x) and 1/x, not with the AMD approximation: its tables are not public. Intel's approximation of 1/sqrt(1.0) is 0x3F7FF000, and two Newton-Raphson steps on it settle on 0.99999994 instead of 1.0; a renormalised identity quaternion then turns into a NaN axis in PPSA21564, which the console does not show. Refined results now reach the exact value; unrefined ones can still differ from the console in the low bits. Each site costs an out-of-line stub (two jumps, a spill below the red zone, SQRTPS/DIVPS). The 256-bit, scalar, legacy and memory forms keep the native instruction, as does a 4-byte site with no movable instruction after it for the jump (5 sites in PPSA21564). -- [sceKeyboardGetKey2Char](../../core/libs/prx/libSceKeyboard/src/keyboard_impl.cpp) (libSceKeyboard) translates only the 101-key (US) arrangement and throws for the 106-key (Japanese) one; Ctrl and Alt do not change the character. +- [sceKeyboardGetKey2Char](../../core/libs/prx/libSceKeyboard/src/keyboard_impl.cpp) (libSceKeyboard) translates the 101-key (US) arrangement and the alphanumeric layer of the 106-key (Japanese) one, which follows the JIS layout of the HID usages: the Yen (0x89) and Ro (0x87) keys return `\` (0x5C) unshifted, as JIS X 0201 places the yen sign there; whether the console returns U+00A5 instead is unverified. With the Kana LED (bit 4, the HID position) set the 106-key arrangement throws, as the kana the console returns are unknown. Ctrl and Alt do not change the character. - [sceAudioOut2Set3DLatency](../../core/libs/prx/libSceAudioOut/src/AudioOut2System.cpp) and [sceAudioOut2MasteringInit](../../core/libs/prx/libSceAudioOut/src/AudioOut2Mastering.cpp) (libSceAudioOut) have no effect: there is no 3D rendering or mastering stage. - [sceImeKeyboardGetInfo](../../core/libs/prx/libSceIme/Export.cpp) (libSceIme) returns `0x80bc0023` (no resource id) for every id while a keyboard is open, since the IME keyboard never reports a connected device. [sceImeKeyboardSetMode](../../core/libs/prx/libSceIme/Export.cpp) validates the mode bits but the mode has no effect, as no IME keyboard events are delivered. - [libSceAudiodec](../../core/libs/prx/libSceAudiodec/Export.cpp) throws for the 24-bit PCM word size (`iBwPcm` 0): its sample layout is unknown. ATRAC9 decoding is covered only by configuration and error tests, as no ATRAC9 encoder is available for a fixture.