update clientstructs

- fix broken gamepad sig by delegating to CS
This commit is contained in:
Kaz Wolfe 2025-03-24 21:58:23 -07:00
parent 445fe09181
commit 919ca87bbe
No known key found for this signature in database
GPG key ID: 258813F53A16EBB4
5 changed files with 48 additions and 134 deletions

View file

@ -24,12 +24,6 @@ internal sealed class ClientStateAddressResolver : BaseAddressResolver
/// </summary>
public IntPtr ProcessPacketPlayerSetup { get; private set; }
/// <summary>
/// Gets the address of the method which polls the gamepads for data.
/// Called every frame, even when `Enable Gamepad` is off in the settings.
/// </summary>
public IntPtr GamepadPoll { get; private set; }
/// <summary>
/// Scan for and setup any configured address pointers.
/// </summary>
@ -43,7 +37,5 @@ internal sealed class ClientStateAddressResolver : BaseAddressResolver
// movzx edx, byte ptr [rbx+rsi+1D5E0E0h] KeyboardStateIndexArray
this.KeyboardState = sig.ScanText("48 8D 0C 85 ?? ?? ?? ?? 8B 04 31 85 C2 0F 85") + 0x4;
this.KeyboardStateIndexArray = sig.ScanText("0F B6 94 33 ?? ?? ?? ?? 84 D2") + 0x4;
this.GamepadPoll = sig.ScanText("40 55 53 57 41 54 41 57 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 44 0F 29 B4 24"); // unnamed in cs
}
}

View file

@ -1,75 +0,0 @@
using System.Runtime.InteropServices;
namespace Dalamud.Game.ClientState.GamePad;
/// <summary>
/// Struct which gets populated by polling the gamepads.
///
/// Has an array of gamepads, among many other things (here not mapped).
/// All we really care about is the final data which the game uses to determine input.
///
/// The size is definitely bigger than only the following fields but I do not know how big.
/// </summary>
[StructLayout(LayoutKind.Explicit)]
public struct GamepadInput
{
/// <summary>
/// Left analogue stick's horizontal value, -99 for left, 99 for right.
/// </summary>
[FieldOffset(0x78)]
public int LeftStickX;
/// <summary>
/// Left analogue stick's vertical value, -99 for down, 99 for up.
/// </summary>
[FieldOffset(0x7C)]
public int LeftStickY;
/// <summary>
/// Right analogue stick's horizontal value, -99 for left, 99 for right.
/// </summary>
[FieldOffset(0x80)]
public int RightStickX;
/// <summary>
/// Right analogue stick's vertical value, -99 for down, 99 for up.
/// </summary>
[FieldOffset(0x84)]
public int RightStickY;
/// <summary>
/// Raw input, set the whole time while a button is held. See <see cref="GamepadButtons"/> for the mapping.
/// </summary>
/// <remarks>
/// This is a bitfield.
/// </remarks>
[FieldOffset(0x88)]
public ushort ButtonsRaw;
/// <summary>
/// Button pressed, set once when the button is pressed. See <see cref="GamepadButtons"/> for the mapping.
/// </summary>
/// <remarks>
/// This is a bitfield.
/// </remarks>
[FieldOffset(0x8C)]
public ushort ButtonsPressed;
/// <summary>
/// Button released input, set once right after the button is not hold anymore. See <see cref="GamepadButtons"/> for the mapping.
/// </summary>
/// <remarks>
/// This is a bitfield.
/// </remarks>
[FieldOffset(0x90)]
public ushort ButtonsReleased;
/// <summary>
/// Repeatedly emits the held button input in fixed intervals. See <see cref="GamepadButtons"/> for the mapping.
/// </summary>
/// <remarks>
/// This is a bitfield.
/// </remarks>
[FieldOffset(0x94)]
public ushort ButtonsRepeat;
}

View file

@ -4,7 +4,8 @@ using Dalamud.Hooking;
using Dalamud.IoC;
using Dalamud.IoC.Internal;
using Dalamud.Plugin.Services;
using Dalamud.Utility;
using FFXIVClientStructs.FFXIV.Client.System.Input;
using ImGuiNET;
using Serilog;
@ -23,7 +24,7 @@ namespace Dalamud.Game.ClientState.GamePad;
#pragma warning restore SA1015
internal unsafe class GamepadState : IInternalDisposableService, IGamepadState
{
private readonly Hook<ControllerPoll>? gamepadPoll;
private readonly Hook<PadDevice.Delegates.Poll>? gamepadPoll;
private bool isDisposed;
@ -35,14 +36,10 @@ internal unsafe class GamepadState : IInternalDisposableService, IGamepadState
[ServiceManager.ServiceConstructor]
private GamepadState(ClientState clientState)
{
var resolver = clientState.AddressResolver;
Log.Verbose($"GamepadPoll address {Util.DescribeAddress(resolver.GamepadPoll)}");
this.gamepadPoll = Hook<ControllerPoll>.FromAddress(resolver.GamepadPoll, this.GamepadPollDetour);
this.gamepadPoll = Hook<PadDevice.Delegates.Poll>.FromAddress((nint)PadDevice.StaticVirtualTablePointer->Poll, this.GamepadPollDetour);
this.gamepadPoll?.Enable();
}
private delegate int ControllerPoll(IntPtr controllerInput);
/// <summary>
/// Gets the pointer to the current instance of the GamepadInput struct.
/// </summary>
@ -61,28 +58,28 @@ internal unsafe class GamepadState : IInternalDisposableService, IGamepadState
///
/// Exposed internally for Debug Data window.
/// </summary>
internal ushort ButtonsPressed { get; private set; }
internal GamepadButtons ButtonsPressed { get; private set; }
/// <summary>
/// Gets raw button bitmask, set the whole time while a button is held. See <see cref="GamepadButtons"/> for the mapping.
///
/// Exposed internally for Debug Data window.
/// </summary>
internal ushort ButtonsRaw { get; private set; }
internal GamepadButtons ButtonsRaw { get; private set; }
/// <summary>
/// Gets button released bitmask, set once right after the button is not hold anymore. See <see cref="GamepadButtons"/> for the mapping.
///
/// Exposed internally for Debug Data window.
/// </summary>
internal ushort ButtonsReleased { get; private set; }
internal GamepadButtons ButtonsReleased { get; private set; }
/// <summary>
/// Gets button repeat bitmask, emits the held button input in fixed intervals. See <see cref="GamepadButtons"/> for the mapping.
///
/// Exposed internally for Debug Data window.
/// </summary>
internal ushort ButtonsRepeat { get; private set; }
internal GamepadButtons ButtonsRepeat { get; private set; }
/// <summary>
/// Gets or sets a value indicating whether detour should block gamepad input for game.
@ -95,16 +92,16 @@ internal unsafe class GamepadState : IInternalDisposableService, IGamepadState
internal bool NavEnableGamepad { get; set; }
/// <inheritdoc/>
public float Pressed(GamepadButtons button) => (this.ButtonsPressed & (ushort)button) > 0 ? 1 : 0;
public float Pressed(GamepadButtons button) => (this.ButtonsPressed & button) > 0 ? 1 : 0;
/// <inheritdoc/>
public float Repeat(GamepadButtons button) => (this.ButtonsRepeat & (ushort)button) > 0 ? 1 : 0;
public float Repeat(GamepadButtons button) => (this.ButtonsRepeat & button) > 0 ? 1 : 0;
/// <inheritdoc/>
public float Released(GamepadButtons button) => (this.ButtonsReleased & (ushort)button) > 0 ? 1 : 0;
public float Released(GamepadButtons button) => (this.ButtonsReleased & button) > 0 ? 1 : 0;
/// <inheritdoc/>
public float Raw(GamepadButtons button) => (this.ButtonsRaw & (ushort)button) > 0 ? 1 : 0;
public float Raw(GamepadButtons button) => (this.ButtonsRaw & button) > 0 ? 1 : 0;
/// <summary>
/// Disposes this instance, alongside its hooks.
@ -115,28 +112,28 @@ internal unsafe class GamepadState : IInternalDisposableService, IGamepadState
GC.SuppressFinalize(this);
}
private int GamepadPollDetour(IntPtr gamepadInput)
private nint GamepadPollDetour(PadDevice* gamepadInput)
{
var original = this.gamepadPoll!.Original(gamepadInput);
try
{
this.GamepadInputAddress = gamepadInput;
var input = (GamepadInput*)gamepadInput;
this.leftStickX = input->LeftStickX;
this.leftStickY = input->LeftStickY;
this.rightStickX = input->RightStickX;
this.rightStickY = input->RightStickY;
this.ButtonsRaw = input->ButtonsRaw;
this.ButtonsPressed = input->ButtonsPressed;
this.ButtonsReleased = input->ButtonsReleased;
this.ButtonsRepeat = input->ButtonsRepeat;
this.GamepadInputAddress = (nint)gamepadInput;
this.leftStickX = gamepadInput->GamepadInputData.LeftStickX;
this.leftStickY = gamepadInput->GamepadInputData.LeftStickY;
this.rightStickX = gamepadInput->GamepadInputData.RightStickX;
this.rightStickY = gamepadInput->GamepadInputData.RightStickY;
this.ButtonsRaw = (GamepadButtons)gamepadInput->GamepadInputData.Buttons;
this.ButtonsPressed = (GamepadButtons)gamepadInput->GamepadInputData.ButtonsPressed;
this.ButtonsReleased = (GamepadButtons)gamepadInput->GamepadInputData.ButtonsReleased;
this.ButtonsRepeat = (GamepadButtons)gamepadInput->GamepadInputData.ButtonsRepeat;
if (this.NavEnableGamepad)
{
input->LeftStickX = 0;
input->LeftStickY = 0;
input->RightStickX = 0;
input->RightStickY = 0;
gamepadInput->GamepadInputData.LeftStickX = 0;
gamepadInput->GamepadInputData.LeftStickY = 0;
gamepadInput->GamepadInputData.RightStickX = 0;
gamepadInput->GamepadInputData.RightStickY = 0;
// NOTE (Chiv) Zeroing `ButtonsRaw` destroys `ButtonPressed`, `ButtonReleased`
// and `ButtonRepeat` as the game uses the RAW input to determine those (apparently).
@ -153,16 +150,16 @@ internal unsafe class GamepadState : IInternalDisposableService, IGamepadState
// `ButtonPressed` while ImGuiConfigFlags.NavEnableGamepad is set.
// This is debatable.
// ImGui itself does not care either way as it uses the Raw values and does its own state handling.
const ushort deletionMask = (ushort)(~GamepadButtons.L2
& ~GamepadButtons.R2
& ~GamepadButtons.DpadDown
& ~GamepadButtons.DpadLeft
& ~GamepadButtons.DpadUp
& ~GamepadButtons.DpadRight);
input->ButtonsRaw &= deletionMask;
input->ButtonsPressed = 0;
input->ButtonsReleased = 0;
input->ButtonsRepeat = 0;
const GamepadButtonsFlags deletionMask = ~GamepadButtonsFlags.L2
& ~GamepadButtonsFlags.R2
& ~GamepadButtonsFlags.DPadDown
& ~GamepadButtonsFlags.DPadLeft
& ~GamepadButtonsFlags.DPadUp
& ~GamepadButtonsFlags.DPadRight;
gamepadInput->GamepadInputData.Buttons &= deletionMask;
gamepadInput->GamepadInputData.ButtonsPressed = 0;
gamepadInput->GamepadInputData.ButtonsReleased = 0;
gamepadInput->GamepadInputData.ButtonsRepeat = 0;
return 0;
}

View file

@ -42,19 +42,19 @@ internal class GamepadWidget : IDataWindowWidget
this.DrawHelper(
"Buttons Raw",
gamepadState.ButtonsRaw,
(uint)gamepadState.ButtonsRaw,
gamepadState.Raw);
this.DrawHelper(
"Buttons Pressed",
gamepadState.ButtonsPressed,
(uint)gamepadState.ButtonsPressed,
gamepadState.Pressed);
this.DrawHelper(
"Buttons Repeat",
gamepadState.ButtonsRepeat,
(uint)gamepadState.ButtonsRepeat,
gamepadState.Repeat);
this.DrawHelper(
"Buttons Released",
gamepadState.ButtonsReleased,
(uint)gamepadState.ButtonsReleased,
gamepadState.Released);
ImGui.Text($"LeftStick {gamepadState.LeftStick}");
ImGui.Text($"RightStick {gamepadState.RightStick}");

@ -1 +1 @@
Subproject commit 3c99b4f8f7f56ee4defd3ee75809c73312359f9e
Subproject commit 48076a4ce750c8f008f3bedd04d0bced03147f56