[Pad] Reject pad handle 0 after the title opens a pad (#912)
Unreal Engine polls scePadReadState for all four user slots and passes handle 0 for slots it never opened. Accepting handle 0 as the primary pad reported three phantom controllers that repeated the primary pad's buttons, and Poppy Playtime ignored all menu input. Handle 0 stays valid until the title opens a pad, for Monster Truck Championship's early polling.
This commit is contained in:
parent
1759e18b77
commit
ac8ffc3f3a
|
|
@ -24,10 +24,17 @@ public static class PadExports
|
|||
private const int ControllerInformationSize = 0x1C;
|
||||
private const int PadDataSize = 0x78;
|
||||
|
||||
// Real firmware hands out small non-negative handles; 0 is valid. Some titles
|
||||
// (Monster Truck Championship) read pad state with handle 0, and rejecting it
|
||||
// leaves their controller/FFB init path polling a never-valid state forever.
|
||||
private static bool IsPrimaryPadHandle(int handle) => handle is 0 or PrimaryPadHandle;
|
||||
// Monster Truck Championship reads pad state with handle 0 before it opens a pad,
|
||||
// and rejecting that leaves its controller/FFB init path polling forever. After a
|
||||
// title opens a pad, handle 0 is a slot it never opened: Unreal Engine polls all four
|
||||
// user slots and treats every valid handle as a connected controller, so accepting
|
||||
// 0 there reports phantom pads that repeat the primary pad's buttons.
|
||||
private static int _padOpened;
|
||||
|
||||
private static bool IsPrimaryPadHandle(int handle) =>
|
||||
handle == PrimaryPadHandle || (handle == 0 && Volatile.Read(ref _padOpened) == 0);
|
||||
|
||||
internal static void ResetOpenedPadForTests() => Volatile.Write(ref _padOpened, 0);
|
||||
private static readonly long InputSampleIntervalTicks = Math.Max(1, Stopwatch.Frequency / 1000);
|
||||
|
||||
[ThreadStatic]
|
||||
|
|
@ -128,6 +135,7 @@ public static class PadExports
|
|||
: "[LOADER][INFO] Keyboard controls: Arrow keys = D-pad, WASD = left stick, IJKL = right stick, Z/Enter = Cross, X/Esc = Circle, C = Square, V = Triangle, Q = L1, E = R1, R = L2, F = R2, Tab/Backspace = Options. A DualSense or Xbox controller will be used automatically when plugged in.");
|
||||
}
|
||||
|
||||
Volatile.Write(ref _padOpened, 1);
|
||||
return ctx.SetReturn(PrimaryPadHandle);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -73,4 +73,31 @@ public sealed class PadExportsTests
|
|||
_ctx[CpuRegister.Rsi] = Base + 0x100;
|
||||
Assert.Equal(InvalidHandle, PadExports.PadGetTriggerEffectState(_ctx));
|
||||
}
|
||||
|
||||
[NativeX64Fact]
|
||||
public void ReadState_RejectsHandleZeroOnceAPadIsOpen()
|
||||
{
|
||||
const ulong dataAddress = Base + 0x200;
|
||||
try
|
||||
{
|
||||
PadExports.PadInit(_ctx);
|
||||
_ctx[CpuRegister.Rdi] = 0x10000000;
|
||||
_ctx[CpuRegister.Rsi] = 0;
|
||||
_ctx[CpuRegister.Rdx] = 0;
|
||||
_ctx[CpuRegister.Rcx] = 0;
|
||||
Assert.Equal(1, PadExports.PadOpen(_ctx));
|
||||
|
||||
_ctx[CpuRegister.Rdi] = 0;
|
||||
_ctx[CpuRegister.Rsi] = dataAddress;
|
||||
Assert.Equal(InvalidHandle, PadExports.PadReadState(_ctx));
|
||||
|
||||
_ctx[CpuRegister.Rdi] = 1;
|
||||
_ctx[CpuRegister.Rsi] = dataAddress;
|
||||
Assert.Equal(0, PadExports.PadReadState(_ctx));
|
||||
}
|
||||
finally
|
||||
{
|
||||
PadExports.ResetOpenedPadForTests();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue