[Tests] Cover the AudioOut2, save data, Http2 and dialog exports
Co-authored-by: Sekai <140012992+igettingit@users.noreply.github.com>
This commit is contained in:
parent
edc2d17cf6
commit
810ff6a606
|
|
@ -0,0 +1,92 @@
|
|||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
using System.Buffers.Binary;
|
||||
using SharpEmu.HLE;
|
||||
using SharpEmu.Libs.Audio;
|
||||
using Xunit;
|
||||
|
||||
namespace SharpEmu.Libs.Tests.Audio;
|
||||
|
||||
public sealed class AudioOut2ContextGetQueueLevelExportsTests
|
||||
{
|
||||
private const ulong MemoryBase = 0x1_0000_0000;
|
||||
private const ulong ParamAddress = MemoryBase + 0x100;
|
||||
private const ulong ContextMemoryAddress = MemoryBase + 0x200;
|
||||
private const ulong OutContextAddress = MemoryBase + 0x300;
|
||||
private const ulong LevelAddress = MemoryBase + 0x400;
|
||||
private const ulong AvailableAddress = MemoryBase + 0x404;
|
||||
private const uint QueueDepth = 7;
|
||||
|
||||
private static (CpuContext Context, FakeCpuMemory Memory, ulong Handle) CreateContext()
|
||||
{
|
||||
var memory = new FakeCpuMemory(MemoryBase, 0x2000);
|
||||
var ctx = new CpuContext(memory, Generation.Gen5);
|
||||
|
||||
Span<byte> param = stackalloc byte[0x40];
|
||||
param.Clear();
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(param[0x0C..], QueueDepth);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(param[0x10..], 512);
|
||||
Assert.True(memory.TryWrite(ParamAddress, param));
|
||||
|
||||
ctx[CpuRegister.Rdi] = ParamAddress;
|
||||
ctx[CpuRegister.Rsi] = ContextMemoryAddress;
|
||||
ctx[CpuRegister.Rdx] = 0x1000;
|
||||
ctx[CpuRegister.Rcx] = OutContextAddress;
|
||||
Assert.Equal(0, AudioOut2Exports.AudioOut2ContextCreate(ctx));
|
||||
|
||||
Span<byte> handleBytes = stackalloc byte[8];
|
||||
Assert.True(memory.TryRead(OutContextAddress, handleBytes));
|
||||
return (ctx, memory, BinaryPrimitives.ReadUInt64LittleEndian(handleBytes));
|
||||
}
|
||||
|
||||
private static uint ReadU32(FakeCpuMemory memory, ulong address)
|
||||
{
|
||||
Span<byte> value = stackalloc byte[4];
|
||||
Assert.True(memory.TryRead(address, value));
|
||||
return BinaryPrimitives.ReadUInt32LittleEndian(value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ContextGetQueueLevel_AvailableOnlyWritesQueueCapacity()
|
||||
{
|
||||
var (ctx, memory, handle) = CreateContext();
|
||||
ctx[CpuRegister.Rdi] = handle;
|
||||
ctx[CpuRegister.Rsi] = 0;
|
||||
ctx[CpuRegister.Rdx] = AvailableAddress;
|
||||
|
||||
var result = AudioOut2Exports.AudioOut2ContextGetQueueLevel(ctx);
|
||||
|
||||
Assert.Equal(0, result);
|
||||
Assert.Equal(QueueDepth, ReadU32(memory, AvailableAddress));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ContextGetQueueLevel_LevelOnlyWritesZero()
|
||||
{
|
||||
var (ctx, memory, handle) = CreateContext();
|
||||
ctx[CpuRegister.Rdi] = handle;
|
||||
ctx[CpuRegister.Rsi] = LevelAddress;
|
||||
ctx[CpuRegister.Rdx] = 0;
|
||||
|
||||
var result = AudioOut2Exports.AudioOut2ContextGetQueueLevel(ctx);
|
||||
|
||||
Assert.Equal(0, result);
|
||||
Assert.Equal(0u, ReadU32(memory, LevelAddress));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ContextGetQueueLevel_BothOutputsRemainDistinct()
|
||||
{
|
||||
var (ctx, memory, handle) = CreateContext();
|
||||
ctx[CpuRegister.Rdi] = handle;
|
||||
ctx[CpuRegister.Rsi] = LevelAddress;
|
||||
ctx[CpuRegister.Rdx] = AvailableAddress;
|
||||
|
||||
var result = AudioOut2Exports.AudioOut2ContextGetQueueLevel(ctx);
|
||||
|
||||
Assert.Equal(0, result);
|
||||
Assert.Equal(0u, ReadU32(memory, LevelAddress));
|
||||
Assert.Equal(QueueDepth, ReadU32(memory, AvailableAddress));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
using System.Buffers.Binary;
|
||||
using SharpEmu.HLE;
|
||||
using SharpEmu.Libs.Audio;
|
||||
using Xunit;
|
||||
|
||||
namespace SharpEmu.Libs.Tests.Audio;
|
||||
|
||||
public sealed class AudioOut2ContextQueryMemoryExportsTests
|
||||
{
|
||||
private const int InvalidPointer = unchecked((int)0x8026_800C);
|
||||
private const ulong MemoryBase = 0x10_0000_0000;
|
||||
private const ulong ParamAddress = MemoryBase + 0x100;
|
||||
private const ulong StackPointer = MemoryBase + 0x400;
|
||||
private const ulong MemorySizeAddress = StackPointer + 0x10;
|
||||
private const ulong StaleThirdArgument = MemoryBase + 0x800;
|
||||
|
||||
[Fact]
|
||||
public void ContextQueryMemory_WritesOnlyOneSizeTAndIgnoresRdx()
|
||||
{
|
||||
var memory = new FakeCpuMemory(MemoryBase, 0x1000);
|
||||
var ctx = new CpuContext(memory, Generation.Gen5);
|
||||
|
||||
Span<byte> param = stackalloc byte[0x40];
|
||||
param.Clear();
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(param[0x0C..], 4);
|
||||
Assert.True(memory.TryWrite(ParamAddress, param));
|
||||
|
||||
Span<byte> guard = stackalloc byte[0x10];
|
||||
guard.Fill(0xA5);
|
||||
Assert.True(memory.TryWrite(MemorySizeAddress, guard));
|
||||
Assert.True(memory.TryWrite(StaleThirdArgument, guard));
|
||||
|
||||
ctx[CpuRegister.Rdi] = ParamAddress;
|
||||
ctx[CpuRegister.Rsi] = MemorySizeAddress;
|
||||
ctx[CpuRegister.Rdx] = StaleThirdArgument;
|
||||
ctx[CpuRegister.Rsp] = StackPointer;
|
||||
ctx[CpuRegister.Rbp] = StackPointer + 0x48;
|
||||
|
||||
Assert.Equal(0, AudioOut2Exports.AudioOut2ContextQueryMemory(ctx));
|
||||
|
||||
Span<byte> output = stackalloc byte[0x10];
|
||||
Assert.True(memory.TryRead(MemorySizeAddress, output));
|
||||
Assert.Equal(0x11640UL, BinaryPrimitives.ReadUInt64LittleEndian(output));
|
||||
Assert.Equal(new byte[] { 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5 }, output[8..].ToArray());
|
||||
|
||||
Span<byte> stale = stackalloc byte[0x10];
|
||||
Assert.True(memory.TryRead(StaleThirdArgument, stale));
|
||||
Assert.Equal(guard.ToArray(), stale.ToArray());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ContextQueryMemory_RejectsMissingSecondArgument()
|
||||
{
|
||||
var memory = new FakeCpuMemory(MemoryBase, 0x1000);
|
||||
var ctx = new CpuContext(memory, Generation.Gen5);
|
||||
|
||||
ctx[CpuRegister.Rdi] = ParamAddress;
|
||||
ctx[CpuRegister.Rsi] = 0;
|
||||
ctx[CpuRegister.Rdx] = StaleThirdArgument;
|
||||
|
||||
Assert.Equal(
|
||||
InvalidPointer,
|
||||
AudioOut2Exports.AudioOut2ContextQueryMemory(ctx));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
using SharpEmu.HLE;
|
||||
using SharpEmu.Libs.Audio;
|
||||
using Xunit;
|
||||
|
||||
namespace SharpEmu.Libs.Tests.Audio;
|
||||
|
||||
public sealed class AudioOut2MasteringExportsTests
|
||||
{
|
||||
[Fact]
|
||||
public void MasteringSetParam_AcceptsOpaqueParameters()
|
||||
{
|
||||
var memory = new FakeCpuMemory(0x1_0000_0000, 0x1000);
|
||||
var ctx = new CpuContext(memory, Generation.Gen5);
|
||||
ctx[CpuRegister.Rdi] = 0x1_0000_0100;
|
||||
ctx[CpuRegister.Rsi] = 2;
|
||||
ctx[CpuRegister.Rdx] = 1;
|
||||
ctx[CpuRegister.Rax] = ulong.MaxValue;
|
||||
|
||||
var result = AudioOut2Exports.AudioOut2MasteringSetParam(ctx);
|
||||
|
||||
Assert.Equal(0, result);
|
||||
Assert.Equal(0UL, ctx[CpuRegister.Rax]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MasteringSetParam_RegistersForGen5()
|
||||
{
|
||||
var manager = new ModuleManager();
|
||||
manager.RegisterExports(
|
||||
SharpEmu.Generated.SysAbiExportRegistry.CreateExports(Generation.Gen5));
|
||||
|
||||
Assert.True(manager.TryGetExport("v8iOE+j8a5o", out var export));
|
||||
Assert.Equal("sceAudioOut2MasteringSetParam", export.Name);
|
||||
Assert.Equal("libSceAudioOut2", export.LibraryName);
|
||||
Assert.Equal(Generation.Gen5, export.Target);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,255 @@
|
|||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
using System.Buffers.Binary;
|
||||
using SharpEmu.HLE;
|
||||
using SharpEmu.HLE.Host;
|
||||
using SharpEmu.Libs.Audio;
|
||||
using Xunit;
|
||||
|
||||
namespace SharpEmu.Libs.Tests.Audio;
|
||||
|
||||
[Collection(AudioOutStateCollection.Name)]
|
||||
public sealed class AudioOut2PcmSnapshotTests : IDisposable
|
||||
{
|
||||
private const ulong MemoryBase = 0x1_0000_0000;
|
||||
private const ulong ContextParamAddress = MemoryBase + 0x100;
|
||||
private const ulong ContextOutAddress = MemoryBase + 0x200;
|
||||
private const ulong PortParamAddress = MemoryBase + 0x300;
|
||||
private const ulong FirstPortOutAddress = MemoryBase + 0x400;
|
||||
private const ulong SecondPortOutAddress = MemoryBase + 0x408;
|
||||
private const ulong AttributeAddress = MemoryBase + 0x500;
|
||||
private const ulong PcmAttributeValueAddress = MemoryBase + 0x600;
|
||||
private const ulong ContextMemoryAddress = MemoryBase + 0x1000;
|
||||
private const ulong SharedPcmAddress = MemoryBase + 0x4000;
|
||||
|
||||
private readonly FakeCpuMemory _memory = new(MemoryBase, 0x10000);
|
||||
private readonly CpuContext _ctx;
|
||||
|
||||
public AudioOut2PcmSnapshotTests()
|
||||
{
|
||||
AudioOut2Exports.ResetForTests();
|
||||
_ctx = new CpuContext(_memory, Generation.Gen5);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PortSetAttributes_SnapshotsPcmWhenPortsReuseGuestBuffer()
|
||||
{
|
||||
var context = CreateContext(grainSamples: 64);
|
||||
var firstPort = CreateSigned16StereoPort(context, FirstPortOutAddress);
|
||||
var secondPort = CreateSigned16StereoPort(context, SecondPortOutAddress);
|
||||
WritePcmAttribute();
|
||||
|
||||
var firstPcm = Enumerable.Range(0, 64 * 2 * sizeof(short))
|
||||
.Select(static value => unchecked((byte)(value + 1)))
|
||||
.ToArray();
|
||||
var secondPcm = Enumerable.Range(0, firstPcm.Length)
|
||||
.Select(static value => unchecked((byte)(255 - value)))
|
||||
.ToArray();
|
||||
|
||||
Assert.True(_memory.TryWrite(SharedPcmAddress, firstPcm));
|
||||
SetPcmAttribute(firstPort);
|
||||
|
||||
Assert.True(_memory.TryWrite(SharedPcmAddress, secondPcm));
|
||||
SetPcmAttribute(secondPort);
|
||||
|
||||
Assert.True(_memory.TryWrite(SharedPcmAddress, new byte[firstPcm.Length]));
|
||||
|
||||
Assert.Equal(firstPcm, AudioOut2Exports.GetPortPcmSnapshotForTests(firstPort));
|
||||
Assert.Equal(secondPcm, AudioOut2Exports.GetPortPcmSnapshotForTests(secondPort));
|
||||
|
||||
Assert.Equal(firstPcm, AudioOut2Exports.TakePortPcmSnapshotForTests(firstPort));
|
||||
Assert.Null(AudioOut2Exports.GetPortPcmSnapshotForTests(firstPort));
|
||||
Assert.Null(AudioOut2Exports.TakePortPcmSnapshotForTests(firstPort));
|
||||
|
||||
Assert.Equal(secondPcm, AudioOut2Exports.TakePortPcmSnapshotForTests(secondPort));
|
||||
Assert.Null(AudioOut2Exports.TakePortPcmSnapshotForTests(secondPort));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData((ushort)0x0100)]
|
||||
[InlineData((ushort)6)]
|
||||
public void ContextPush_DoesNotSubmitObjectOrVibrationPorts(ushort portType)
|
||||
{
|
||||
var stream = new RecordingAudioStream();
|
||||
var openCalls = 0;
|
||||
AudioOut2Exports.SetStreamFactoryForTests(_ =>
|
||||
{
|
||||
openCalls++;
|
||||
return stream;
|
||||
});
|
||||
|
||||
var context = CreateContext(grainSamples: 64);
|
||||
var port = CreateSigned16StereoPort(context, FirstPortOutAddress, portType);
|
||||
WritePcmAttribute();
|
||||
var pcm = Enumerable.Range(0, 64 * 2 * sizeof(short))
|
||||
.Select(static value => unchecked((byte)(value + 1)))
|
||||
.ToArray();
|
||||
Assert.True(_memory.TryWrite(SharedPcmAddress, pcm));
|
||||
SetPcmAttribute(port);
|
||||
|
||||
_ctx[CpuRegister.Rdi] = context;
|
||||
_ctx[CpuRegister.Rsi] = 1;
|
||||
Assert.Equal(0, AudioOut2Exports.AudioOut2ContextPush(_ctx));
|
||||
|
||||
Assert.Equal(0, openCalls);
|
||||
Assert.Empty(stream.Submissions);
|
||||
Assert.Null(AudioOut2Exports.GetPortPcmSnapshotForTests(port));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ContextPush_SubmitsDevicePortSnapshotOnlyOnce()
|
||||
{
|
||||
var stream = new RecordingAudioStream();
|
||||
AudioOut2Exports.SetStreamFactoryForTests(_ => stream);
|
||||
|
||||
var context = CreateContext(grainSamples: 64);
|
||||
var port = CreateSigned16StereoPort(context, FirstPortOutAddress);
|
||||
WritePcmAttribute();
|
||||
Assert.True(_memory.TryWrite(
|
||||
SharedPcmAddress,
|
||||
Enumerable.Range(0, 64 * 2 * sizeof(short))
|
||||
.Select(static value => unchecked((byte)(value + 1)))
|
||||
.ToArray()));
|
||||
SetPcmAttribute(port);
|
||||
|
||||
_ctx[CpuRegister.Rdi] = context;
|
||||
_ctx[CpuRegister.Rsi] = 1;
|
||||
Assert.Equal(0, AudioOut2Exports.AudioOut2ContextPush(_ctx));
|
||||
Assert.Single(stream.Submissions);
|
||||
Assert.Equal(64 * 2 * sizeof(short), stream.Submissions[0].Length);
|
||||
|
||||
Assert.Equal(0, AudioOut2Exports.AudioOut2ContextPush(_ctx));
|
||||
Assert.Single(stream.Submissions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ContextAdvance_SubmitsDevicePortSnapshotOnlyOnce()
|
||||
{
|
||||
var stream = new RecordingAudioStream();
|
||||
AudioOut2Exports.SetStreamFactoryForTests(_ => stream);
|
||||
|
||||
var context = CreateContext(grainSamples: 64);
|
||||
var port = CreateSigned16StereoPort(context, FirstPortOutAddress);
|
||||
WritePcmAttribute();
|
||||
Assert.True(_memory.TryWrite(
|
||||
SharedPcmAddress,
|
||||
Enumerable.Range(0, 64 * 2 * sizeof(short))
|
||||
.Select(static value => unchecked((byte)(value + 1)))
|
||||
.ToArray()));
|
||||
SetPcmAttribute(port);
|
||||
|
||||
_ctx[CpuRegister.Rdi] = context;
|
||||
Assert.Equal(0, AudioOut2Exports.AudioOut2ContextAdvance(_ctx));
|
||||
Assert.Single(stream.Submissions);
|
||||
Assert.Equal(64 * 2 * sizeof(short), stream.Submissions[0].Length);
|
||||
|
||||
Assert.Equal(0, AudioOut2Exports.AudioOut2ContextAdvance(_ctx));
|
||||
Assert.Single(stream.Submissions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ContextDestroy_RemovesOwnedPortsAndPendingSnapshots()
|
||||
{
|
||||
var firstContext = CreateContext(grainSamples: 64);
|
||||
var secondContext = CreateContext(grainSamples: 64);
|
||||
var firstPort = CreateSigned16StereoPort(firstContext, FirstPortOutAddress);
|
||||
var secondPort = CreateSigned16StereoPort(secondContext, SecondPortOutAddress);
|
||||
WritePcmAttribute();
|
||||
|
||||
byte[] firstPcm = new byte[64 * 2 * sizeof(short)];
|
||||
byte[] secondPcm = Enumerable.Repeat((byte)0x5A, firstPcm.Length).ToArray();
|
||||
Assert.True(_memory.TryWrite(SharedPcmAddress, firstPcm));
|
||||
SetPcmAttribute(firstPort);
|
||||
Assert.True(_memory.TryWrite(SharedPcmAddress, secondPcm));
|
||||
SetPcmAttribute(secondPort);
|
||||
|
||||
_ctx[CpuRegister.Rdi] = firstContext;
|
||||
Assert.Equal(0, AudioOut2Exports.AudioOut2ContextDestroy(_ctx));
|
||||
|
||||
Assert.False(AudioOut2Exports.HasPortForTests(firstPort));
|
||||
Assert.Null(AudioOut2Exports.GetPortPcmSnapshotForTests(firstPort));
|
||||
Assert.Null(AudioOut2Exports.TakePortPcmSnapshotForTests(firstPort));
|
||||
Assert.True(AudioOut2Exports.HasPortForTests(secondPort));
|
||||
Assert.Equal(secondPcm, AudioOut2Exports.GetPortPcmSnapshotForTests(secondPort));
|
||||
}
|
||||
|
||||
public void Dispose() => AudioOut2Exports.ResetForTests();
|
||||
|
||||
private ulong CreateContext(uint grainSamples)
|
||||
{
|
||||
Span<byte> param = stackalloc byte[0x40];
|
||||
param.Clear();
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(param[0x0C..], 2);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(param[0x10..], grainSamples);
|
||||
Assert.True(_memory.TryWrite(ContextParamAddress, param));
|
||||
|
||||
_ctx[CpuRegister.Rdi] = ContextParamAddress;
|
||||
_ctx[CpuRegister.Rsi] = ContextMemoryAddress;
|
||||
_ctx[CpuRegister.Rdx] = 0x2000;
|
||||
_ctx[CpuRegister.Rcx] = ContextOutAddress;
|
||||
Assert.Equal(0, AudioOut2Exports.AudioOut2ContextCreate(_ctx));
|
||||
return ReadUInt64(ContextOutAddress);
|
||||
}
|
||||
|
||||
private ulong CreateSigned16StereoPort(ulong context, ulong outAddress, ushort portType = 0)
|
||||
{
|
||||
Span<byte> param = stackalloc byte[0x40];
|
||||
param.Clear();
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(param, portType);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(param[0x04..], 0x201);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(param[0x08..], 48000);
|
||||
Assert.True(_memory.TryWrite(PortParamAddress, param));
|
||||
|
||||
_ctx[CpuRegister.Rdi] = context;
|
||||
_ctx[CpuRegister.Rsi] = PortParamAddress;
|
||||
_ctx[CpuRegister.Rdx] = outAddress;
|
||||
_ctx[CpuRegister.Rcx] = 0;
|
||||
Assert.Equal(0, AudioOut2Exports.AudioOut2PortCreate(_ctx));
|
||||
return ReadUInt64(outAddress);
|
||||
}
|
||||
|
||||
private void WritePcmAttribute()
|
||||
{
|
||||
Span<byte> pcm = stackalloc byte[8];
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(pcm, SharedPcmAddress);
|
||||
Assert.True(_memory.TryWrite(PcmAttributeValueAddress, pcm));
|
||||
|
||||
Span<byte> attribute = stackalloc byte[0x18];
|
||||
attribute.Clear();
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(attribute, 0);
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(attribute[0x08..], PcmAttributeValueAddress);
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(attribute[0x10..], 8);
|
||||
Assert.True(_memory.TryWrite(AttributeAddress, attribute));
|
||||
}
|
||||
|
||||
private void SetPcmAttribute(ulong port)
|
||||
{
|
||||
_ctx[CpuRegister.Rdi] = port;
|
||||
_ctx[CpuRegister.Rsi] = AttributeAddress;
|
||||
_ctx[CpuRegister.Rdx] = 1;
|
||||
Assert.Equal(0, AudioOut2Exports.AudioOut2PortSetAttributes(_ctx));
|
||||
}
|
||||
|
||||
private ulong ReadUInt64(ulong address)
|
||||
{
|
||||
Span<byte> value = stackalloc byte[8];
|
||||
Assert.True(_memory.TryRead(address, value));
|
||||
return BinaryPrimitives.ReadUInt64LittleEndian(value);
|
||||
}
|
||||
|
||||
private sealed class RecordingAudioStream : IHostAudioStream
|
||||
{
|
||||
public List<byte[]> Submissions { get; } = [];
|
||||
|
||||
public bool Submit(ReadOnlySpan<byte> stereoPcm16)
|
||||
{
|
||||
Submissions.Add(stereoPcm16.ToArray());
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -42,9 +42,12 @@ public sealed class AudioOut2PortGetStateExportsTests
|
|||
// SceAudioOut2PortState: output@0, numChannels@2, volume@4.
|
||||
Assert.Equal(1, BinaryPrimitives.ReadUInt16LittleEndian(state));
|
||||
Assert.Equal(2, state[2]);
|
||||
Assert.Equal(-1, BinaryPrimitives.ReadInt16LittleEndian(state[4..]));
|
||||
Assert.Equal(0, state[3]);
|
||||
Assert.Equal(127, BinaryPrimitives.ReadInt16LittleEndian(state[4..]));
|
||||
Assert.Equal(0, BinaryPrimitives.ReadUInt16LittleEndian(state[6..]));
|
||||
Assert.Equal(0u, BinaryPrimitives.ReadUInt32LittleEndian(state[8..]));
|
||||
// sizeof(SceAudioOut2PortState) is 0x40; nothing past it may be touched.
|
||||
Assert.Equal(0u, BinaryPrimitives.ReadUInt32LittleEndian(state[0x0C..]));
|
||||
Assert.All(state[0x10..0x40].ToArray(), value => Assert.Equal(0, value));
|
||||
Assert.Equal(0xAB, state[0x40]);
|
||||
Assert.Equal(0xAB, state[0x7F]);
|
||||
}
|
||||
|
|
@ -102,15 +105,19 @@ public sealed class AudioOut2PortGetStateExportsTests
|
|||
Assert.Equal(0, result);
|
||||
Span<byte> info = stackalloc byte[0x80];
|
||||
Assert.True(memory.TryRead(StateAddress, info));
|
||||
// type@0 = SPEAKER_TYPE_TV, availableBits@4 = FRONT_LEFT | FRONT_RIGHT.
|
||||
Assert.Equal(0, info[0]);
|
||||
Assert.Equal(3u, BinaryPrimitives.ReadUInt32LittleEndian(info[4..]));
|
||||
Assert.Equal(0, info[1]);
|
||||
Assert.Equal(0, BinaryPrimitives.ReadInt16LittleEndian(info[2..]));
|
||||
Assert.Equal(0x3u, BinaryPrimitives.ReadUInt32LittleEndian(info[4..]));
|
||||
Assert.Equal(0u, BinaryPrimitives.ReadUInt32LittleEndian(info[8..]));
|
||||
// aSpeakerAngle[0..1] = { -30, 0 }, { +30, 0 } degrees.
|
||||
Assert.Equal(0u, BinaryPrimitives.ReadUInt32LittleEndian(info[0x0C..]));
|
||||
Assert.Equal(-30, BinaryPrimitives.ReadInt16LittleEndian(info[0x10..]));
|
||||
Assert.Equal(0, BinaryPrimitives.ReadInt16LittleEndian(info[0x12..]));
|
||||
Assert.Equal(30, BinaryPrimitives.ReadInt16LittleEndian(info[0x14..]));
|
||||
// sizeof(SceAudioOut2SpeakerInfo) is 0x50.
|
||||
Assert.Equal(0, BinaryPrimitives.ReadInt16LittleEndian(info[0x16..]));
|
||||
Assert.All(info[0x18..0x50].ToArray(), value => Assert.Equal(0, value));
|
||||
Assert.Equal(0xCD, info[0x50]);
|
||||
Assert.Equal(0xCD, info[0x7F]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
using SharpEmu.HLE;
|
||||
using SharpEmu.Libs.AvPlayer;
|
||||
using SharpEmu.Libs.Tests.Kernel;
|
||||
using Xunit;
|
||||
|
||||
namespace SharpEmu.Libs.Tests.AvPlayer;
|
||||
|
||||
[Collection(KernelMemoryCompatStateCollection.Name)]
|
||||
public sealed class AvPlayerControlTests : IDisposable
|
||||
{
|
||||
private const int InvalidParameters = unchecked((int)0x806A0001);
|
||||
private const ulong Handle = 0xA0_0000_2400;
|
||||
private readonly CpuContext _context = new(
|
||||
new FakeCpuMemory(0x1_0000_0000, 0x1000),
|
||||
Generation.Gen5);
|
||||
|
||||
public AvPlayerControlTests()
|
||||
{
|
||||
AvPlayerExports.RegisterPlayerForTest(
|
||||
Handle,
|
||||
width: 16,
|
||||
height: 16,
|
||||
durationMilliseconds: 1000);
|
||||
_context[CpuRegister.Rdi] = Handle;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AvailableBandwidthAcceptsAValidPlayerWithoutAStreamingSource()
|
||||
{
|
||||
_context[CpuRegister.Rsi] = 0;
|
||||
|
||||
Assert.Equal(0, AvPlayerExports.AvPlayerSetAvailableBandwidth(_context));
|
||||
Assert.Equal(0UL, _context[CpuRegister.Rax]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AvailableBandwidthRejectsAnUnknownPlayer()
|
||||
{
|
||||
_context[CpuRegister.Rdi] = Handle + 1;
|
||||
_context[CpuRegister.Rsi] = 25_000_000;
|
||||
|
||||
Assert.Equal(
|
||||
InvalidParameters,
|
||||
AvPlayerExports.AvPlayerSetAvailableBandwidth(_context));
|
||||
Assert.Equal(
|
||||
unchecked((ulong)InvalidParameters),
|
||||
_context[CpuRegister.Rax]);
|
||||
}
|
||||
|
||||
public void Dispose() => AvPlayerExports.RemovePlayerForTest(Handle);
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
using System.Buffers.Binary;
|
||||
using SharpEmu.HLE;
|
||||
using SharpEmu.Libs.CommonDialog;
|
||||
using Xunit;
|
||||
|
||||
namespace SharpEmu.Libs.Tests.CommonDialog;
|
||||
|
||||
[CollectionDefinition("WebBrowserDialog", DisableParallelization = true)]
|
||||
public sealed class WebBrowserDialogCollection;
|
||||
|
||||
[Collection("WebBrowserDialog")]
|
||||
public sealed class WebBrowserDialogExportsTests : IDisposable
|
||||
{
|
||||
private const ulong MemoryBase = 0x1_0000_0000;
|
||||
private readonly FakeCpuMemory _memory = new(MemoryBase, 0x1000);
|
||||
private readonly CpuContext _context;
|
||||
|
||||
public WebBrowserDialogExportsTests()
|
||||
{
|
||||
WebBrowserDialogExports.ResetForTests();
|
||||
_context = new CpuContext(_memory, Generation.Gen5);
|
||||
}
|
||||
|
||||
public void Dispose() => WebBrowserDialogExports.ResetForTests();
|
||||
|
||||
[Fact]
|
||||
public void HeadlessLifecycle_CompletesOnFirstStatusPoll()
|
||||
{
|
||||
Assert.Equal(WebBrowserDialogExports.StatusNone,
|
||||
WebBrowserDialogExports.WebBrowserDialogGetStatus(_context));
|
||||
Assert.Equal(0, WebBrowserDialogExports.WebBrowserDialogInitialize(_context));
|
||||
Assert.Equal(WebBrowserDialogExports.StatusInitialized,
|
||||
WebBrowserDialogExports.WebBrowserDialogGetStatus(_context));
|
||||
|
||||
_context[CpuRegister.Rdi] = MemoryBase + 0x100;
|
||||
Assert.Equal(0, WebBrowserDialogExports.WebBrowserDialogOpen(_context));
|
||||
Assert.Equal(WebBrowserDialogExports.StatusRunning, WebBrowserDialogExports.StatusForTests);
|
||||
Assert.Equal(WebBrowserDialogExports.StatusFinished,
|
||||
WebBrowserDialogExports.WebBrowserDialogGetStatus(_context));
|
||||
|
||||
var resultAddress = MemoryBase + 0x200;
|
||||
_context[CpuRegister.Rdi] = resultAddress;
|
||||
Assert.Equal(0, WebBrowserDialogExports.WebBrowserDialogGetResult(_context));
|
||||
Span<byte> result = stackalloc byte[sizeof(int)];
|
||||
Assert.True(_memory.TryRead(resultAddress, result));
|
||||
Assert.Equal(0, BinaryPrimitives.ReadInt32LittleEndian(result));
|
||||
|
||||
Assert.Equal(0, WebBrowserDialogExports.WebBrowserDialogTerminate(_context));
|
||||
Assert.Equal(WebBrowserDialogExports.StatusNone,
|
||||
WebBrowserDialogExports.WebBrowserDialogGetStatus(_context));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Close_FinishesRunningDialogWithoutPolling()
|
||||
{
|
||||
Assert.Equal(0, WebBrowserDialogExports.WebBrowserDialogInitialize(_context));
|
||||
_context[CpuRegister.Rdi] = MemoryBase + 0x100;
|
||||
Assert.Equal(0, WebBrowserDialogExports.WebBrowserDialogOpenForPredeterminedContent(_context));
|
||||
Assert.Equal(0, WebBrowserDialogExports.WebBrowserDialogClose(_context));
|
||||
Assert.Equal(WebBrowserDialogExports.StatusFinished, WebBrowserDialogExports.StatusForTests);
|
||||
}
|
||||
}
|
||||
|
|
@ -26,6 +26,8 @@ public sealed class JsonExportRegistrationTests
|
|||
("9KUZFjI1IxA", "_ZN3sce4Json6StringC1EPKc"),
|
||||
("cG1VE2HMl6c", "_ZN3sce4Json6StringD1Ev"),
|
||||
("+drDFyAS6u4", "_ZN3sce4Json11Initializer27setGlobalNullAccessCallbackEPFRKNS0_5ValueENS0_9ValueTypeEPS3_PvES7_"),
|
||||
("GvGvswb0v34", "_ZN3sce4Json14InitParameter2C2Ev"),
|
||||
("W72B9ylU2JA", "_ZN3sce4Json18InitParameterRtti216setAllocatorRttiEPNS0_14AllocParamRttiEPv"),
|
||||
};
|
||||
|
||||
private static ModuleManager CreateRegisteredManager()
|
||||
|
|
@ -131,4 +133,36 @@ public sealed class JsonExportRegistrationTests
|
|||
Assert.Equal(0x1_0000_0000UL, ctx[CpuRegister.Rax]);
|
||||
Assert.Equal(JsonValueKind.Null, JsonObjectHeap.Values[0x1_0000_0000].Kind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InitParameter2Aliases_InitializeAndWriteTheirDocumentedFields()
|
||||
{
|
||||
const ulong memoryBase = 0x1_0000_0000;
|
||||
const ulong parameterAddress = memoryBase + 0x100;
|
||||
const ulong allocatorAddress = 0x8_0012_3000;
|
||||
const ulong userDataAddress = memoryBase + 0x500;
|
||||
var memory = new FakeCpuMemory(memoryBase, 0x1000);
|
||||
var manager = CreateRegisteredManager();
|
||||
var ctx = new CpuContext(memory, Generation.Gen5);
|
||||
Assert.True(memory.TryWrite(parameterAddress, Enumerable.Repeat((byte)0xA5, 0x28).ToArray()));
|
||||
|
||||
ctx[CpuRegister.Rdi] = parameterAddress;
|
||||
Assert.True(manager.TryDispatch("GvGvswb0v34", ctx, out var constructorResult));
|
||||
Assert.Equal(OrbisGen2Result.ORBIS_GEN2_OK, constructorResult);
|
||||
|
||||
var parameter = new byte[0x28];
|
||||
Assert.True(memory.TryRead(parameterAddress, parameter));
|
||||
Assert.All(parameter, value => Assert.Equal(0, value));
|
||||
|
||||
ctx[CpuRegister.Rdi] = parameterAddress;
|
||||
ctx[CpuRegister.Rsi] = allocatorAddress;
|
||||
ctx[CpuRegister.Rdx] = userDataAddress;
|
||||
Assert.True(manager.TryDispatch("W72B9ylU2JA", ctx, out var allocatorResult));
|
||||
Assert.Equal(OrbisGen2Result.ORBIS_GEN2_OK, allocatorResult);
|
||||
|
||||
Assert.True(memory.TryRead(parameterAddress, parameter));
|
||||
Assert.Equal(allocatorAddress, BitConverter.ToUInt64(parameter, 0));
|
||||
Assert.Equal(userDataAddress, BitConverter.ToUInt64(parameter, 8));
|
||||
Assert.All(parameter[0x10..], value => Assert.Equal(0, value));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,4 +72,276 @@ public sealed class Http2ExportsTests
|
|||
|
||||
Assert.Equal(unchecked((int)0x817B106A), Http2Exports.Http2CreateTemplate(ctx));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetMinSslVersion_AcceptsLiveIdsAndRejectsDeletedIds()
|
||||
{
|
||||
Http2Exports.ResetRuntimeState();
|
||||
var memory = new FakeCpuMemory(MemoryBase, 0x2000);
|
||||
var ctx = new CpuContext(memory, Generation.Gen5);
|
||||
|
||||
ctx[CpuRegister.Rdi] = 1;
|
||||
ctx[CpuRegister.Rsi] = 2;
|
||||
ctx[CpuRegister.Rdx] = 0x1000;
|
||||
ctx[CpuRegister.Rcx] = 2;
|
||||
Assert.Equal(0, Http2Exports.Http2Init(ctx));
|
||||
var contextId = ctx[CpuRegister.Rax];
|
||||
|
||||
ctx[CpuRegister.Rdi] = contextId;
|
||||
ctx[CpuRegister.Rsi] = memory.WriteCString(MemoryBase + 0x100, "test");
|
||||
ctx[CpuRegister.Rdx] = 3;
|
||||
ctx[CpuRegister.Rcx] = 0;
|
||||
Assert.True(Http2Exports.Http2CreateTemplate(ctx) > 0);
|
||||
var templateId = ctx[CpuRegister.Rax];
|
||||
|
||||
ctx[CpuRegister.Rdi] = templateId;
|
||||
ctx[CpuRegister.Rsi] = 4;
|
||||
Assert.Equal(0, Http2Exports.Http2SetMinSslVersion(ctx));
|
||||
|
||||
ctx[CpuRegister.Rdi] = templateId;
|
||||
ctx[CpuRegister.Rsi] = memory.WriteCString(MemoryBase + 0x200, "GET");
|
||||
ctx[CpuRegister.Rdx] = memory.WriteCString(MemoryBase + 0x300, "https://example.invalid/");
|
||||
ctx[CpuRegister.Rcx] = 0;
|
||||
Assert.True(Http2Exports.Http2CreateRequestWithUrl(ctx) > 0);
|
||||
var requestId = ctx[CpuRegister.Rax];
|
||||
|
||||
ctx[CpuRegister.Rdi] = requestId;
|
||||
ctx[CpuRegister.Rsi] = 4;
|
||||
Assert.Equal(0, Http2Exports.Http2SetMinSslVersion(ctx));
|
||||
|
||||
ctx[CpuRegister.Rdi] = templateId;
|
||||
Assert.Equal(0, Http2Exports.Http2DeleteTemplate(ctx));
|
||||
Assert.Equal(unchecked((int)0x817B1100), Http2Exports.Http2SetMinSslVersion(ctx));
|
||||
|
||||
ctx[CpuRegister.Rdi] = requestId;
|
||||
Assert.Equal(unchecked((int)0x817B1100), Http2Exports.Http2SetMinSslVersion(ctx));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetMinSslVersion_RegistersPs5Nid()
|
||||
{
|
||||
var manager = new ModuleManager();
|
||||
manager.RegisterExports(SharpEmu.Generated.SysAbiExportRegistry.CreateExports(Generation.Gen5));
|
||||
|
||||
Assert.True(manager.TryGetExport("09tk+kIA1Ns", out var export));
|
||||
Assert.Equal("sceHttp2SetMinSslVersion", export.Name);
|
||||
Assert.Equal("libSceHttp2", export.LibraryName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Options_AreStoredInheritedAndIndependentlyUpdated()
|
||||
{
|
||||
var (memory, ctx, templateId) = CreateTemplate();
|
||||
|
||||
ctx[CpuRegister.Rdi] = unchecked((uint)templateId);
|
||||
ctx[CpuRegister.Rsi] = 0x0F;
|
||||
Assert.Equal(0, Http2Exports.Http2SslEnableOption(ctx));
|
||||
|
||||
ctx[CpuRegister.Rsi] = 0x05;
|
||||
Assert.Equal(0, Http2Exports.Http2SslDisableOption(ctx));
|
||||
|
||||
ctx[CpuRegister.Rsi] = 0x1234_5678;
|
||||
ctx[CpuRegister.Rdx] = 0x8765_4321;
|
||||
Assert.Equal(0, Http2Exports.Http2SetRedirectCallback(ctx));
|
||||
|
||||
ctx[CpuRegister.Rsi] = 0;
|
||||
Assert.Equal(0, Http2Exports.Http2SetAutoRedirect(ctx));
|
||||
|
||||
ctx[CpuRegister.Rsi] = 101;
|
||||
Assert.Equal(0, Http2Exports.Http2SetConnectTimeOut(ctx));
|
||||
ctx[CpuRegister.Rsi] = 202;
|
||||
Assert.Equal(0, Http2Exports.Http2SetRecvTimeOut(ctx));
|
||||
ctx[CpuRegister.Rsi] = 303;
|
||||
Assert.Equal(0, Http2Exports.Http2SetSendTimeOut(ctx));
|
||||
|
||||
ctx[CpuRegister.Rsi] = 7;
|
||||
Assert.Equal(0, Http2Exports.Http2SetAuthEnabled(ctx));
|
||||
|
||||
ctx[CpuRegister.Rsi] = 0x1111_2222;
|
||||
ctx[CpuRegister.Rdx] = 0x3333_4444;
|
||||
Assert.Equal(0, Http2Exports.Http2SetSslCallback(ctx));
|
||||
|
||||
Assert.True(Http2Exports.TryGetOptionsForTests(templateId, out var templateOptions));
|
||||
Assert.Equal(0x0Au, templateOptions.SslOptions);
|
||||
Assert.Equal(0x1234_5678UL, templateOptions.RedirectCallback);
|
||||
Assert.Equal(0x8765_4321UL, templateOptions.RedirectUserArgument);
|
||||
Assert.False(templateOptions.AutoRedirect);
|
||||
Assert.Equal(101u, templateOptions.ConnectTimeoutMicroseconds);
|
||||
Assert.Equal(202u, templateOptions.ReceiveTimeoutMicroseconds);
|
||||
Assert.Equal(303u, templateOptions.SendTimeoutMicroseconds);
|
||||
Assert.True(templateOptions.AuthEnabled);
|
||||
Assert.Equal(0x1111_2222UL, templateOptions.SslCallback);
|
||||
Assert.Equal(0x3333_4444UL, templateOptions.SslUserArgument);
|
||||
|
||||
var requestId = CreateRequest(memory, ctx, templateId);
|
||||
Assert.True(Http2Exports.TryGetOptionsForTests(requestId, out var inheritedOptions));
|
||||
Assert.Equal(templateOptions, inheritedOptions);
|
||||
|
||||
ctx[CpuRegister.Rdi] = unchecked((uint)requestId);
|
||||
ctx[CpuRegister.Rsi] = 404;
|
||||
Assert.Equal(0, Http2Exports.Http2SetRecvTimeOut(ctx));
|
||||
Assert.True(Http2Exports.TryGetOptionsForTests(requestId, out var requestOptions));
|
||||
Assert.Equal(404u, requestOptions.ReceiveTimeoutMicroseconds);
|
||||
Assert.True(Http2Exports.TryGetOptionsForTests(templateId, out var unchangedTemplateOptions));
|
||||
Assert.Equal(202u, unchangedTemplateOptions.ReceiveTimeoutMicroseconds);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OptionSetters_RejectUnknownHandles()
|
||||
{
|
||||
Http2Exports.ResetRuntimeState();
|
||||
var ctx = new CpuContext(new FakeCpuMemory(MemoryBase, 0x1000), Generation.Gen5);
|
||||
ctx[CpuRegister.Rdi] = 0x7FFF_FFFF;
|
||||
ctx[CpuRegister.Rsi] = 1;
|
||||
ctx[CpuRegister.Rdx] = 2;
|
||||
|
||||
Func<CpuContext, int>[] setters =
|
||||
[
|
||||
Http2Exports.Http2SslDisableOption,
|
||||
Http2Exports.Http2SslEnableOption,
|
||||
Http2Exports.Http2SetRedirectCallback,
|
||||
Http2Exports.Http2SetAutoRedirect,
|
||||
Http2Exports.Http2SetCookieBox,
|
||||
Http2Exports.Http2SetConnectTimeOut,
|
||||
Http2Exports.Http2SetRecvTimeOut,
|
||||
Http2Exports.Http2SetSendTimeOut,
|
||||
Http2Exports.Http2SetAuthEnabled,
|
||||
Http2Exports.Http2SetSslCallback,
|
||||
];
|
||||
|
||||
foreach (var setter in setters)
|
||||
{
|
||||
Assert.Equal(unchecked((int)0x817B1100), setter(ctx));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddRequestHeader_ValidatesPointersAndTracksGuestStrings()
|
||||
{
|
||||
var (memory, ctx, templateId) = CreateTemplate();
|
||||
var requestId = CreateRequest(memory, ctx, templateId);
|
||||
var nameAddress = memory.WriteCString(MemoryBase + 0x500, "X-SharpEmu-Test");
|
||||
var valueAddress = memory.WriteCString(MemoryBase + 0x600, "enabled");
|
||||
|
||||
ctx[CpuRegister.Rdi] = unchecked((uint)requestId);
|
||||
ctx[CpuRegister.Rsi] = nameAddress;
|
||||
ctx[CpuRegister.Rdx] = valueAddress;
|
||||
ctx[CpuRegister.Rcx] = 1;
|
||||
Assert.Equal(0, Http2Exports.Http2AddRequestHeader(ctx));
|
||||
|
||||
var header = Assert.Single(Http2Exports.GetRequestHeadersForTests(requestId));
|
||||
Assert.Equal("X-SharpEmu-Test", header.Name);
|
||||
Assert.Equal("enabled", header.Value);
|
||||
Assert.Equal(1u, header.Mode);
|
||||
|
||||
ctx[CpuRegister.Rsi] = 0;
|
||||
Assert.Equal(unchecked((int)0x817B1225), Http2Exports.Http2AddRequestHeader(ctx));
|
||||
|
||||
ctx[CpuRegister.Rsi] = MemoryBase + 0x3000;
|
||||
Assert.Equal(unchecked((int)0x817B11FE), Http2Exports.Http2AddRequestHeader(ctx));
|
||||
|
||||
ctx[CpuRegister.Rdi] = 0x7FFF_FFFF;
|
||||
ctx[CpuRegister.Rsi] = nameAddress;
|
||||
Assert.Equal(unchecked((int)0x817B1100), Http2Exports.Http2AddRequestHeader(ctx));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CookieBox_IsCreatedBoundInheritedAndCanBeDetached()
|
||||
{
|
||||
var (memory, ctx, templateId) = CreateTemplate();
|
||||
|
||||
ctx[CpuRegister.Rdi] = 0x80000; // Deliberately stale sceHttp2Init register state.
|
||||
Assert.Equal(0x3001, Http2Exports.Http2CreateCookieBox(ctx));
|
||||
var cookieBoxId = unchecked((int)ctx[CpuRegister.Rax]);
|
||||
|
||||
ctx[CpuRegister.Rdi] = unchecked((uint)templateId);
|
||||
ctx[CpuRegister.Rsi] = unchecked((uint)cookieBoxId);
|
||||
Assert.Equal(0, Http2Exports.Http2SetCookieBox(ctx));
|
||||
Assert.True(Http2Exports.TryGetOptionsForTests(templateId, out var templateOptions));
|
||||
Assert.Equal(cookieBoxId, templateOptions.CookieBoxId);
|
||||
|
||||
var requestId = CreateRequest(memory, ctx, templateId);
|
||||
Assert.True(Http2Exports.TryGetOptionsForTests(requestId, out var inheritedOptions));
|
||||
Assert.Equal(cookieBoxId, inheritedOptions.CookieBoxId);
|
||||
|
||||
ctx[CpuRegister.Rdi] = unchecked((uint)requestId);
|
||||
ctx[CpuRegister.Rsi] = 0;
|
||||
Assert.Equal(0, Http2Exports.Http2SetCookieBox(ctx));
|
||||
Assert.True(Http2Exports.TryGetOptionsForTests(requestId, out var detachedOptions));
|
||||
Assert.Equal(0, detachedOptions.CookieBoxId);
|
||||
|
||||
ctx[CpuRegister.Rsi] = 0x7FFF_FFFF;
|
||||
Assert.Equal(unchecked((int)0x817B1100), Http2Exports.Http2SetCookieBox(ctx));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetRequestContentLength_UpdatesOnlyLiveRequests()
|
||||
{
|
||||
var (memory, ctx, templateId) = CreateTemplate();
|
||||
var requestId = CreateRequest(memory, ctx, templateId);
|
||||
|
||||
ctx[CpuRegister.Rdi] = unchecked((uint)requestId);
|
||||
ctx[CpuRegister.Rsi] = 0x1_0000_0042;
|
||||
Assert.Equal(0, Http2Exports.Http2SetRequestContentLength(ctx));
|
||||
Assert.True(Http2Exports.TryGetRequestContentLengthForTests(requestId, out var contentLength));
|
||||
Assert.Equal(0x1_0000_0042UL, contentLength);
|
||||
|
||||
ctx[CpuRegister.Rdi] = 0x7FFF_FFFF;
|
||||
Assert.Equal(unchecked((int)0x817B1100), Http2Exports.Http2SetRequestContentLength(ctx));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("B37SruheQ5Y", "sceHttp2SslDisableOption")]
|
||||
[InlineData("EWcwMpbr5F8", "sceHttp2SslEnableOption")]
|
||||
[InlineData("BJgi0CH7al4", "sceHttp2SetRedirectCallback")]
|
||||
[InlineData("b9AvoIaOuHI", "sceHttp2SetAutoRedirect")]
|
||||
[InlineData("N4UfjvWJsMw", "sceHttp2CreateCookieBox")]
|
||||
[InlineData("jrVHsKCXA0g", "sceHttp2SetCookieBox")]
|
||||
[InlineData("FSAFOzi0FpM", "sceHttp2SetRequestContentLength")]
|
||||
[InlineData("-HIO4VT87v8", "sceHttp2SetConnectTimeOut")]
|
||||
[InlineData("izvHhqgDt44", "sceHttp2SetRecvTimeOut")]
|
||||
[InlineData("XPtW45xiLHk", "sceHttp2SetSendTimeOut")]
|
||||
[InlineData("jjFahkBPCYs", "sceHttp2SetAuthEnabled")]
|
||||
[InlineData("nrPfOE8TQu0", "sceHttp2AddRequestHeader")]
|
||||
[InlineData("YrWX+DhPHQY", "sceHttp2SetSslCallback")]
|
||||
public void CompatibilityExports_RegisterPs5Nids(string nid, string name)
|
||||
{
|
||||
var manager = new ModuleManager();
|
||||
manager.RegisterExports(SharpEmu.Generated.SysAbiExportRegistry.CreateExports(Generation.Gen5));
|
||||
|
||||
Assert.True(manager.TryGetExport(nid, out var export));
|
||||
Assert.Equal(name, export.Name);
|
||||
Assert.Equal("libSceHttp2", export.LibraryName);
|
||||
}
|
||||
|
||||
private static (FakeCpuMemory Memory, CpuContext Context, int TemplateId) CreateTemplate()
|
||||
{
|
||||
Http2Exports.ResetRuntimeState();
|
||||
var memory = new FakeCpuMemory(MemoryBase, 0x2000);
|
||||
var ctx = new CpuContext(memory, Generation.Gen5);
|
||||
|
||||
ctx[CpuRegister.Rdi] = 1;
|
||||
ctx[CpuRegister.Rsi] = 2;
|
||||
ctx[CpuRegister.Rdx] = 0x1000;
|
||||
ctx[CpuRegister.Rcx] = 2;
|
||||
Assert.Equal(0, Http2Exports.Http2Init(ctx));
|
||||
var contextId = unchecked((int)ctx[CpuRegister.Rax]);
|
||||
|
||||
ctx[CpuRegister.Rdi] = unchecked((uint)contextId);
|
||||
ctx[CpuRegister.Rsi] = memory.WriteCString(MemoryBase + 0x100, "test");
|
||||
ctx[CpuRegister.Rdx] = 3;
|
||||
ctx[CpuRegister.Rcx] = 0;
|
||||
Assert.True(Http2Exports.Http2CreateTemplate(ctx) > 0);
|
||||
return (memory, ctx, unchecked((int)ctx[CpuRegister.Rax]));
|
||||
}
|
||||
|
||||
private static int CreateRequest(FakeCpuMemory memory, CpuContext ctx, int templateId)
|
||||
{
|
||||
ctx[CpuRegister.Rdi] = unchecked((uint)templateId);
|
||||
ctx[CpuRegister.Rsi] = memory.WriteCString(MemoryBase + 0x200, "GET");
|
||||
ctx[CpuRegister.Rdx] = memory.WriteCString(MemoryBase + 0x300, "https://example.invalid/");
|
||||
ctx[CpuRegister.Rcx] = 0;
|
||||
Assert.True(Http2Exports.Http2CreateRequestWithUrl(ctx) > 0);
|
||||
return unchecked((int)ctx[CpuRegister.Rax]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,11 +44,18 @@ public sealed class SaveDataExportsTests : IDisposable
|
|||
private const ulong SearchResult = Base + 0x1140;
|
||||
private const ulong SearchDirNames = Base + 0x1180;
|
||||
private const ulong SearchInfos = Base + 0x1200;
|
||||
private const ulong BackupParam = Base + 0x1300;
|
||||
private const ulong BackupTitleId = Base + 0x1380;
|
||||
private const ulong BackupDirName = Base + 0x13C0;
|
||||
private const ulong Unmapped = Base + 0x2_0000;
|
||||
|
||||
private const int NoEvent = unchecked((int)0x809F0008);
|
||||
private const int ParameterError = unchecked((int)0x809F0000);
|
||||
private const int MemoryFault = (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
private const int ResourceBusy = unchecked((int)0x809F001B);
|
||||
private const uint MountModeCreate = 1u << 2;
|
||||
private const string BackupTitle = "PPSA10067";
|
||||
private const string BackupDirectory = "SLOT_WITH_32_CHARACTERS_12345678";
|
||||
|
||||
private readonly FakeCpuMemory _memory = new(Base, 0x10000);
|
||||
private readonly CpuContext _ctx;
|
||||
|
|
@ -121,6 +128,65 @@ public sealed class SaveDataExportsTests : IDisposable
|
|||
Assert.Equal(ParameterError, SaveDataExports.SaveDataGetEventResult(Reg(rsi: 0)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Backup_QueuesCompleteBackupEndEvent_ThenDrains()
|
||||
{
|
||||
WriteAscii(BackupTitleId, BackupTitle);
|
||||
WriteAscii(BackupDirName, BackupDirectory);
|
||||
WriteBackupParam(BackupTitleId, BackupDirName);
|
||||
|
||||
Assert.Equal(0, SaveDataExports.SaveDataBackup(Reg(rdi: BackupParam)));
|
||||
|
||||
var initialized = new byte[0x70];
|
||||
Array.Fill(initialized, (byte)0xCC);
|
||||
Assert.True(_memory.TryWrite(EventOut, initialized));
|
||||
Assert.Equal(0, SaveDataExports.SaveDataGetEventResult(Reg(rsi: EventOut)));
|
||||
|
||||
var ev = new byte[0x70];
|
||||
Assert.True(_memory.TryRead(EventOut, ev));
|
||||
Assert.Equal(2u, BinaryPrimitives.ReadUInt32LittleEndian(ev));
|
||||
Assert.Equal(0, BinaryPrimitives.ReadInt32LittleEndian(ev.AsSpan(0x04)));
|
||||
Assert.Equal(UserId, BinaryPrimitives.ReadInt32LittleEndian(ev.AsSpan(0x08)));
|
||||
Assert.Equal(BackupTitle, Encoding.ASCII.GetString(ev, 0x10, 0x10).TrimEnd('\0'));
|
||||
Assert.Equal(BackupDirectory, Encoding.ASCII.GetString(ev, 0x20, 0x20).TrimEnd('\0'));
|
||||
Assert.All(ev[0x1A..0x20], value => Assert.Equal((byte)0, value));
|
||||
Assert.All(ev[0x40..0x68], value => Assert.Equal((byte)0, value));
|
||||
Assert.All(ev[0x68..0x70], value => Assert.Equal((byte)0xCC, value));
|
||||
Assert.Equal(NoEvent, SaveDataExports.SaveDataGetEventResult(Reg(rsi: EventOut)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Backup_NullParameterOrDirectory_ReturnsParameterWithoutEvent()
|
||||
{
|
||||
Assert.Equal(ParameterError, SaveDataExports.SaveDataBackup(Reg(rdi: 0)));
|
||||
|
||||
WriteBackupParam(titleIdAddress: 0, dirNameAddress: 0);
|
||||
Assert.Equal(ParameterError, SaveDataExports.SaveDataBackup(Reg(rdi: BackupParam)));
|
||||
Assert.Equal(NoEvent, SaveDataExports.SaveDataGetEventResult(Reg(rsi: EventOut)));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public void Backup_InvalidNestedPointer_ReturnsMemoryFaultWithoutEvent(bool invalidTitle)
|
||||
{
|
||||
WriteAscii(BackupTitleId, BackupTitle);
|
||||
WriteAscii(BackupDirName, BackupDirectory);
|
||||
WriteBackupParam(
|
||||
invalidTitle ? Unmapped : BackupTitleId,
|
||||
invalidTitle ? BackupDirName : Unmapped);
|
||||
|
||||
Assert.Equal(MemoryFault, SaveDataExports.SaveDataBackup(Reg(rdi: BackupParam)));
|
||||
Assert.Equal(NoEvent, SaveDataExports.SaveDataGetEventResult(Reg(rsi: EventOut)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Backup_InvalidStructurePointer_ReturnsMemoryFaultWithoutEvent()
|
||||
{
|
||||
Assert.Equal(MemoryFault, SaveDataExports.SaveDataBackup(Reg(rdi: Unmapped)));
|
||||
Assert.Equal(NoEvent, SaveDataExports.SaveDataGetEventResult(Reg(rsi: EventOut)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SyncSaveDataMemory_PostsSyncEndEvent_DrainedOnce()
|
||||
{
|
||||
|
|
@ -145,6 +211,11 @@ public sealed class SaveDataExportsTests : IDisposable
|
|||
Assert.True(_ctx.TryReadInt32(EventOut + 0x04, out var errorCode));
|
||||
Assert.Equal(0, errorCode);
|
||||
|
||||
var ev = new byte[0x68];
|
||||
Assert.True(_memory.TryRead(EventOut, ev));
|
||||
Assert.Equal(TitleId[..10], Encoding.ASCII.GetString(ev, 0x10, 0x10).TrimEnd('\0'));
|
||||
Assert.Equal("sce_sdmemory", Encoding.ASCII.GetString(ev, 0x20, 0x20).TrimEnd('\0'));
|
||||
|
||||
Assert.Equal(NoEvent, SaveDataExports.SaveDataGetEventResult(Reg(rsi: EventOut)));
|
||||
}
|
||||
|
||||
|
|
@ -397,4 +468,14 @@ public sealed class SaveDataExportsTests : IDisposable
|
|||
0,
|
||||
SaveDataExports.SaveDataPrepare(Reg(rdi: MountPointStr, rsi: PrepareParam)));
|
||||
}
|
||||
|
||||
private void WriteBackupParam(ulong titleIdAddress, ulong dirNameAddress)
|
||||
{
|
||||
Span<byte> backup = stackalloc byte[0x40];
|
||||
backup.Clear();
|
||||
BinaryPrimitives.WriteInt32LittleEndian(backup, UserId);
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(backup[0x08..], titleIdAddress);
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(backup[0x10..], dirNameAddress);
|
||||
Assert.True(_memory.TryWrite(BackupParam, backup));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue