From 73866db75584a86eb072bc0e816c274c9a4077cf Mon Sep 17 00:00:00 2001 From: gookie <36005381+gookie-dev@users.noreply.github.com> Date: Sun, 11 Oct 2026 19:26:30 +0200 Subject: [PATCH] fix(pad): report special ports as not connected in scePadOpen scePadOpen returned the standard pad's handle for a special port (type 2) as well, and scePadGetHandle found it there. Both ports then read the one input queue, so a press queued between two frames could be drained by the special port's read and never reach the standard port. PPSA16827 (Harmony's Odyssey) opens both ports for the initial user and reads both every frame, also polling controller information and device class data for the special one. Special ports now return DEVICE_NOT_CONNECTED (0x80920007) from scePadOpen, as scePadOpenExt already does because the host has no special controller, and scePadGetHandle has no handle for them. TechnicalDebt's scePadOpenExt entry now covers both calls. guest_pad opens a special port before and after the standard one and expects DEVICE_NOT_CONNECTED, and DEVICE_NO_HANDLE from scePadGetHandle for the special port while the standard handle stays valid. It fails on main. PPSA16827 over 130 s of its title screen: 6812 scePadRead, 3408 scePadGetControllerInformation and 3406 scePadDeviceClassParseData calls before, two reads of the same queue per frame; 2702 reads, one information call and no parse calls after. --- core/libs/prx/libScePad/Export.cpp | 3 ++- core/libs/tests/GuestPad.cpp | 6 +++++- docs/dev/TechnicalDebt.md | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libScePad/Export.cpp b/core/libs/prx/libScePad/Export.cpp index 38bb59ca5..4338e1288 100644 --- a/core/libs/prx/libScePad/Export.cpp +++ b/core/libs/prx/libScePad/Export.cpp @@ -73,7 +73,7 @@ int APS5_VABI scePadGetControllerInformation(int handle, PadControllerInformatio } int APS5_VABI scePadGetHandle(int user_id, int type, int index) { - if (!g_opened || !ValidPort(user_id, type, index)) return PAD_ERROR_DEVICE_NO_HANDLE; + if (!g_opened || !ValidPort(user_id, type, index) || type == PAD_PORT_TYPE_SPECIAL) return PAD_ERROR_DEVICE_NO_HANDLE; return PAD_HANDLE; } @@ -136,6 +136,7 @@ int APS5_VABI scePadOpen_nid_postfix(int userId, int type, int index, const void if (!ValidPort(userId, type, index)) { return PAD_ERROR_INVALID_ARG; } + if (type == PAD_PORT_TYPE_SPECIAL) return PAD_ERROR_DEVICE_NOT_CONNECTED; g_opened = true; return PAD_HANDLE; } diff --git a/core/libs/tests/GuestPad.cpp b/core/libs/tests/GuestPad.cpp index 992f0f7b9..454d8de60 100644 --- a/core/libs/tests/GuestPad.cpp +++ b/core/libs/tests/GuestPad.cpp @@ -183,10 +183,14 @@ int main() { Require(scePadOpenExt(user, PAD_PORT_TYPE_SPECIAL, 1, wheel) == PAD_ERROR_INVALID_ARG); Require(scePadOpenExt(user, PAD_PORT_TYPE_SPECIAL, 0, nullptr) == PAD_ERROR_INVALID_ARG); Require(scePadGetHandle(user, 0, 0) == noHandle); + Require(scePadOpen_nid_postfix(user, PAD_PORT_TYPE_SPECIAL, 0, nullptr) == static_cast(0x80920007)); + Require(scePadGetHandle(user, 0, 0) == noHandle); const int handle = scePadOpen_nid_postfix(user, 0, 0, nullptr); Require(handle > 0); Require(scePadGetHandle(user, 0, 0) == handle); - Require(scePadGetHandle(user, 2, 0) == handle); + Require(scePadOpen_nid_postfix(user, PAD_PORT_TYPE_SPECIAL, 0, nullptr) == static_cast(0x80920007)); + Require(scePadGetHandle(user, PAD_PORT_TYPE_SPECIAL, 0) == noHandle); + Require(scePadGetHandle(user, 0, 0) == handle); CheckTiltCorrection(handle); CheckTouchContact(); CheckReadQueue(handle); diff --git a/docs/dev/TechnicalDebt.md b/docs/dev/TechnicalDebt.md index 0c72b62a7..f2bfa00ca 100644 --- a/docs/dev/TechnicalDebt.md +++ b/docs/dev/TechnicalDebt.md @@ -83,7 +83,7 @@ Throughout the project, every function at every stage either **does exactly what - [sceAgcWaitRegMemPatchMask](../../core/libs/prx/libSceAgc/Patch/src/WaitRegMem.cpp) (libSceAgc) - follows `sceAgcWaitRegMemPatchReference`: the mask is taken as a 32-bit value and written to the low mask word of a 32- or 64-bit wait, as [OpenAGC](https://github.com/OpenAGC/OpenAGC/blob/main/src/game_compat.c) does; [sharpemu](https://github.com/sharpemu/sharpemu/blob/main/src/SharpEmu.Libs/Agc/AgcExports.PacketPatching.cs) writes both words of a 64-bit wait's mask. A mask above 32 bits throws - [scePsmlMfsr2CreateContext, scePsmlMfsr2CreateSharedResources, scePsmlMfsr2GetContextInitRequirement, scePsmlMfsr2GetDispatchPackets, scePsmlMfsr2GetDispatchPacketsSizeInDwords, scePsmlMfsr2GetSharedResourcesInitRequirement, scePsmlMfsr2Init, scePsmlMfsr2ReleaseContext, scePsmlMfsr2ReleaseSharedResources](../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml_mfsr2) - unknown signatures, imported by PPSA23566; declared without parameters - [sceAgcDcbSetCfRegisterDirect, sceAgcDcbSetCfRegisterRangeDirect](../../core/libs/prx/libSceAgc/DcbState/src/Registers.cpp) (libSceAgc) - signatures follow [OpenAGC](https://github.com/OpenAGC/OpenAGC/blob/main/src/game_compat.c) and [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Agc/SceAgc.cs), which agree; the SET_CONFIG_REG (0x68) packet is OpenAGC's. The driver does not execute SET_CONFIG_REG, so a submitted packet throws -- [scePadOpenExt](../../core/libs/prx/libScePad/Export.cpp) (libScePad) - only special ports (type 2) are handled, and they return `DEVICE_NOT_CONNECTED` (0x80920007) because the host has no special controller; whether the console opens a handle and reports the device as disconnected later is unknown. PPSA01317 passes a 16-byte param that names the device by vendor and product id (`b7 0e 04 0e ...` for 0x0eb7:0x0e04) and skips the user on any negative result. Other port types throw +- [scePadOpen and scePadOpenExt](../../core/libs/prx/libScePad/Export.cpp) (libScePad) - special ports (type 2) return `DEVICE_NOT_CONNECTED` (0x80920007) because the host has no special controller, and `scePadGetHandle` has no handle for them; whether the console opens a handle and reports the device as disconnected later is unknown. scePadOpenExt handles only special ports. PPSA01317 passes a 16-byte param that names the device by vendor and product id (`b7 0e 04 0e ...` for 0x0eb7:0x0e04) and skips the user on any negative result. Other port types throw - [scePadVrControllerReadState](../../core/libs/prx/libScePad/Export.cpp) (libScePad, NID `iA-DdobUen8`) - the name hashes to the imported NID; the `(int handle, void* state)` signature follows [PS5VR](https://github.com/theghostonline/PS5VR/blob/0e408909ce7a39891a2e06eee3438d07a09e11e3/app/src/app_vr.c#L326-L369) (PPSA99177), which passes a PS VR2 Sense controller handle from `scePadOpen` type 3 or 4 and a 0x68-byte state it reads as buttons at 0, stick bytes at 4 and a connected byte at 0x40. The title calls it only after `sceHmd2Initialize` succeeds, which it does not here, so every call throws; the state layout is otherwise unknown - PPSA23566 imports declared without parameters, signatures unknown: [sceAudioOut2UserGetSupportedAttributes](../../core/libs/prx/libSceAudioOut/src/AudioOut2System.cpp), [sceConvertKeycodeGetCharacterFromKeyboardData](../../core/libs/prx/libSceConvertKeycode/Export.cpp), [sce::Json::MemAllocator::notifyError](../../core/libs/prx/libSceJson2/Export.cpp), [sceNetResolverAbort](../../core/libs/prx/libSceNet/Export.cpp), [sceNpCommerceDialogOpen2](../../core/libs/prx/libSceNpCommerce/Export.cpp), [sceNpEntitlementAccessGetPftFlag](../../core/libs/prx/libSceNpEntitlementAccess/Export.cpp), [sceProprietaryVoiceChatHelper{Initialize,Terminate,SetVoiceChatState,GetVoiceChatUsageState}](../../core/libs/prx/libSceProprietaryVoiceChatHelperPrx/Export.cpp), [sceVoice{GetResourceInfo,ResetPort,EnableChat,DisableChat}](../../core/libs/prx/libSceVoice/Export.cpp). Names from the shadPS4 aerolib NID list - PPSA28416 imports declared without parameters, signatures unknown: [sceAudioOut2EnableChat](../../core/libs/prx/libSceAudioOut/src/AudioOut2System.cpp), imported by its bundled `party-ps5-shipping.prx`. Name from the shadPS4 aerolib NID list