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.
This commit is contained in:
gookie 2026-10-11 19:26:30 +02:00
parent a11c8f7f70
commit 73866db755
3 changed files with 8 additions and 3 deletions

View File

@ -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;
}

View File

@ -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<int>(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<int>(0x80920007));
Require(scePadGetHandle(user, PAD_PORT_TYPE_SPECIAL, 0) == noHandle);
Require(scePadGetHandle(user, 0, 0) == handle);
CheckTiltCorrection(handle);
CheckTouchContact();
CheckReadQueue(handle);

View File

@ -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