@BunnyFl's corrected reading of 0xe8a0: a null output is refused first,
then initialisation, then a type other than 0 or 2. The test no longer
pins ReadStateExt's handle-first order before init, which follows this
library's ReadState rather than the module; the debt entry says so.
From @BunnyFl's disassembly of the 13.42 libScePad.sprx (#15087):
GetDeviceInfo, GetCapability, GetVersionInfo, GetIdleCount,
GetControllerIdleCount, GetActiveTime, SetPS4BcVibrationMode,
GetInfoByPortType, GetExtensionUnitInfo, ReadHistory, OpenExt2,
ReadStateExt and GetDeviceId get their checks in order, and keep
throwing where the console asks the pad driver or reads state that isn't
modelled. ReadHistory's third argument is a byte size of 0x960, and
OpenExt2 takes (userId, 0x10, param).
Firmware 13.42 checks the initialized flag first in all five and
returns 0x80920005 without writing the outputs. The debt entry now
says where the behaviour comes from.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
scePadGetMotionTimerUnit, scePadGetMotionSensorPosition,
scePadGetSphereRadius, scePadGetBluetoothAddress and
scePadParseUniqueDataLeonardo follow firmware 13.42's handle and
device checks for the one DualSense this library reports.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Names the zeroed read of a withdrawn port 0 under the controller mode, its
source (shadPS4 at a pinned commit), and what the console returns for a
removed controller, which is not measured.
scePadGetControllerInformation answers connected = true on every port in the
default mode; under ANYPS5_PAD_PORT0_STATE=controller port 0 answers its
connection state. The console's answer is not measured in either case.
Add scePadIsSupportedAudioFunction, scePadSetAudioOutPath and
scePadSetVolumeGain. The path and the speaker, headset and microphone
gain are kept per port in the pad output state, where a host backend
can fetch them, and a repeated identical value does not bump the output
sequence.
A path above 4 is rejected with INVALID_ARG, a null gain with
INVALID_ARG, an unknown handle with INVALID_HANDLE, and everything
returns NOT_INITIALIZED before scePadInit. Special handles accept the
setters without effect. Signatures and path values follow duaLib; no
host backend routes controller audio yet, which the technical debt file
records.
Add scePadGetParticularMode, scePadGetControllerBusType,
scePadGetJackState, scePadIsControllerUpdateRequired and
scePadGetContainerIdInformation, and make scePadSetParticularMode keep
its argument for the getter.
Each call returns NOT_INITIALIZED before scePadInit, INVALID_ARG for a
null output pointer, INVALID_HANDLE for an unknown handle and
DEVICE_NOT_CONNECTED when no device is behind the handle (a special
handle or a disconnected port). The emulated DualSense reports a
Bluetooth bus, no headset or microphone, no required update and an
empty container id. Signatures follow duaLib; the console's answers are
recorded as unmeasured in the technical debt file.
BunnyFl ran firmware 13.42's libScePad natively over a slot table he
wrote, and oneandonlydean asked for the four calls to follow it.
scePadIsValidHandle matches the handle's open port type against the
argument, and answers false before scePadInit.
scePadResetLightBarAllByPortType returns INVALID_ARG for any type but 0,
1 and 2 before the initialisation check, and otherwise resets each open
handle of that type as scePadResetLightBar resets it;
scePadResetLightBarAll is type 0. So a standard port with no open handle
keeps its light bar, and types 3, 4 and 16 no longer throw.
scePadSetVibrationForce returns NOT_INITIALIZED before scePadInit, then
INVALID_ARG for a null parameter, without looking the handle up. There
is no pad driver here: a special handle returns PAD_OK and a standard
handle sets its port's motors, open or not. A handle with no pad port
throws, as the driver's answer for it is not measured.
The tests drop the closed and unknown handle checks that no longer hold
and check the measured answers; the debt entries cite the run and say
which parts are this model's.
On a standard-port handle the console answers NOT_PERMITTED (0x80920101):
Halo CE Universal's pad probe measured it on firmware 13.59 for two
connected DualSenses in three runs. The function returned 0 with
invented controller information instead. The special port keeps its
zeroed answer.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
scePadSetVibrationForce takes the same handle and vibration parameter as
scePadSetVibration and returns its result. No system vibration-off state
exists here for it to override: scePadSetVibrationMode only stores its
mode.
scePadResetLightBarAll returns each standard port's light bar to its
default, as scePadResetLightBar does for one handle.
scePadResetLightBarAllByPortType does the same for the standard port
type and nothing for the special one. It throws for types 3, 4 and 16,
and rejects other types with INVALID_ARG, as scePadOpen does.
A handle is valid for its own port type while the handle table that
scePadGetHandle reads holds it open: a standard port that is open, a
special slot that is in use, or the open remote port. Any other port
type answers false.
Accept valid special handles in scePadGetControllerType by using KnownHandle, reporting PAD_CONTROLLER_TYPE_UNKNOWN (0) for special handles with no physical controller behind them.
Main limits the standard port to logged-in users, so scePadOpen now checks the port type alone first, then the user and index, and leaves the standard port's user lookup to PortForUser.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Track up to 4 pad slots in libScePad with per-slot inputs, outputs, sensor fusion, and deadbands
- Support dynamic user login/logout events and multi-user registration in libSceUserService
- Map SDL controllers 0-3 to Players 1-4 in libSceVideoOut while preserving Player 1 keyboard and mouse composition
- Add test coverage for multi-controller pads and multi-user services
scePadOpen for the system user's remote control port (type 16) returned the
DualSense handle, so scePadReadState on the "remote" reported the controller's
buttons as connected. SDL's PS5 video driver maps that port's buttons to keys,
so every Cross also arrived as Return and every D-pad press as an arrow key.
In Raptor (PPSA94002) the Return overwrote the key typed on the on-screen
keyboard before the name field read it, and Circle closed the dialog instead
of the keyboard.
The port now opens its own handle that reads no buttons with connected false,
and closes independently of the DualSense handle. Unlike the special port
handles, it stays out of KnownHandle, so calls that act on the DualSense reject
it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A payload process on a PS5 (firmware 13.60) gets 0x809b0001 for user -1 on types 3 and 4 and on index 1, so scePadOpen answers those before it throws for the unknown types.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
With two users logged in on a PS4 Pro (firmware 10.71) each user's
special port handle, ALREADY_OPENED on its second open and close are
independent of the other user's, as the branch already models.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
The special port table had four slots and threw for a fifth user, which
the progress report counted as scePadOpen going back to a stub. A PS4
holds at most 16 accounts, so the table now has 16 slots and a 17th
distinct user id gets 0x809b0081, the code the console returned for an
unknown user id in the libScePad survey (PS4 Pro, firmware 10.71).
Co-Authored-By: Claude Fable 5.1 <[email protected]>
The ps4probe libScePad survey (PS4 Pro, firmware 10.71) measured the
special port with one user: a second scePadOpen returns ALREADY_OPENED
(0x80920004); scePadRead and scePadReadState give the rest record
(sticks 0x80, orientation w 1, acceleration y 1, connected 0);
scePadGetControllerInformation is zeroed;
scePadDeviceClassGetExtendedInformation reports device class -1; and
scePadSetVibration, scePadSetLightBar, scePadResetLightBar,
scePadSetMotionSensorState and scePadResetOrientation return 0.
The special handle is now accepted by every pad call: setters succeed
without touching the emulated pad, getters return the measured records.
Calls not covered by the survey follow the same rule and the debt entry
lists them.
Co-Authored-By: Claude Fable 5.1 <[email protected]>