// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using SharpEmu.HLE;
using SharpEmu.Libs.Pad;
using Xunit;
namespace SharpEmu.Libs.Tests.Pad;
public sealed class PadExportsTests
{
private const ulong Base = 0x1_0000_0000;
private const int InvalidHandle = unchecked((int)0x80920003);
private readonly FakeCpuMemory _memory = new(Base, 0x1000);
private readonly CpuContext _ctx;
public PadExportsTests()
{
_ctx = new CpuContext(_memory, Generation.Gen5);
}
[Theory]
[InlineData(0, 0)]
[InlineData(1, 0)]
[InlineData(2, InvalidHandle)]
[InlineData(-1, InvalidHandle)]
public void SetTiltCorrectionState_ValidatesHandle(int handle, int expected)
{
_ctx[CpuRegister.Rdi] = unchecked((ulong)handle);
Assert.Equal(expected, PadExports.PadSetTiltCorrectionState(_ctx));
}
///
/// Mirrors the calling frame observed in PPSA10112: the out-param points at
/// rbp-0x30 and the caller's stack cookie sits at rbp-0x28, so the state is
/// eight bytes. Writing more would smash the cookie and fail the guest's
/// stack check, which is the failure mode this size guards against.
///
[Fact]
public void GetTriggerEffectState_WritesEightBytesAndLeavesTheCookieIntact()
{
const ulong stateAddress = Base + 0x100;
const ulong cookieAddress = stateAddress + 8;
const ulong cookie = 0xC0DEC0DECAFEBA00UL;
Assert.True(_memory.TryWrite(stateAddress, new byte[] { 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE }));
Assert.True(_memory.TryWrite(cookieAddress, BitConverter.GetBytes(cookie)));
_ctx[CpuRegister.Rdi] = 0;
_ctx[CpuRegister.Rsi] = stateAddress;
Assert.Equal(0, PadExports.PadGetTriggerEffectState(_ctx));
Span state = stackalloc byte[8];
Assert.True(_memory.TryRead(stateAddress, state));
foreach (var value in state)
{
Assert.Equal(0, value);
}
Span guard = stackalloc byte[8];
Assert.True(_memory.TryRead(cookieAddress, guard));
Assert.Equal(cookie, BitConverter.ToUInt64(guard));
}
[Theory]
[InlineData(2)]
[InlineData(-1)]
public void GetTriggerEffectState_RejectsForeignHandles(int handle)
{
_ctx[CpuRegister.Rdi] = unchecked((ulong)handle);
_ctx[CpuRegister.Rsi] = Base + 0x100;
Assert.Equal(InvalidHandle, PadExports.PadGetTriggerEffectState(_ctx));
}
}