The trigger mask selects which of the two commands apply, but every mode
was checked against the range, so an unselected command with any value
made the call fail. Quake (PPSA01880) and Quake II (PPSA09477) set only
the right trigger (mask 0x2) and leave the left command uninitialised
(mode 0xb65951b8 in one call), and got SCE_PAD_ERROR_INVALID_ARG; the
right trigger was never switched. Only selected commands are checked now.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The newer-press case in CheckLatchedPressLifetime held the circle press
while it was read, so the check passed for any held button and no longer
showed that a released tap stays latched at the default 100 ms latch.
Release the circle tap before the read again. Each attempt makes two
reads within 50 ms, so the latch is 100 ms, taps cross, waits 150 ms,
taps circle and reads it. Repeat up to 50 times until the reads were
close and the read finished within 50 ms of the circle press, and abort
with a message if no attempt did.
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]>
Covers scePadGetControllerInformation on a withdrawn port 0 (not connected,
connectedCount 0) and scePadGetInfo, whose connected count must match the
ports Pad::IsConnected reports, with no entry when none is connected.
Covers scePadReadState and scePadRead on port 0 while it is withdrawn, input
published during the withdrawal staying out of the reads, and the restored
state after reconnection.
Seven of the titles here import scePadSetTriggerEffect and no test reaches
it. Pin the requests it leaves in the port's output state: a weapon and a
feedback command as their DualSense effect bytes and fallback strengths,
the trigger mask leaving the other trigger as it was, an off command, and
a special handle leaving the output alone.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The latch lasts one and a half times the interval between the previous
two scePadReadState calls, kept between 100 ms and 500 ms. The latch
checks assumed two consecutive reads land within a few microseconds and
that a read follows a tap within the latch, so a stall of about 100 ms
on a loaded runner made them fail: guest_pad aborted in
CheckLatchedPressLifetime in two Windows CI runs on main.
Repeat the two short reads until both finished within 50 ms, hold the
newer press while it is read, and repeat the slow-read and per-port
cases until the tap was read within 250 ms of the second slow read.
A press published before the keyboard captured the pad stayed in the
read latch and in the sample queue. The first ReadState after the
handoff returned it beside the Intercepted bit, and Read drained the
queued samples from before the capture unmasked. Clear both when a
sample starts the capture, so every read while it lasts holds only the
Intercepted bit.
Found by MarcZX10's review.
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.
Port types 3, 4 and 16 are valid ones the console answers.
scePadResetLightBarAllByPortType still refuses them, but a test that
requires the refusal would fail once they are supported. The positive
and invalid-argument checks stay.
Asked for in oneandonlydean's review.
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.