diff --git a/src/SharpEmu.Libs/Kernel/KernelAprCompatExports.cs b/src/SharpEmu.Libs/Kernel/KernelAprCompatExports.cs index e363383..1242b9d 100644 --- a/src/SharpEmu.Libs/Kernel/KernelAprCompatExports.cs +++ b/src/SharpEmu.Libs/Kernel/KernelAprCompatExports.cs @@ -166,19 +166,6 @@ public static class KernelAprCompatExports return (int)OrbisGen2Result.ORBIS_GEN2_OK; } - // Success stub: the argument layout is unknown and callers tolerate the - // empty answer (Quake streams fine), so no output payload is written until - // the real signature is reversed. - [SysAbiExport( - Nid = "WvEu7yl3Ivg", - ExportName = "sceKernelAprGetFileSize", - Target = Generation.Gen4 | Generation.Gen5, - LibraryName = "libKernel")] - public static int KernelAprGetFileSize(CpuContext ctx) - { - return ctx.SetReturn(0); - } - private static bool TryWriteAprResult( CpuContext ctx, ulong resultAddress, diff --git a/src/SharpEmu.Libs/Kernel/KernelMemoryCompatExports.cs b/src/SharpEmu.Libs/Kernel/KernelMemoryCompatExports.cs index d2d94fb..5093b89 100644 --- a/src/SharpEmu.Libs/Kernel/KernelMemoryCompatExports.cs +++ b/src/SharpEmu.Libs/Kernel/KernelMemoryCompatExports.cs @@ -1993,6 +1993,48 @@ public static partial class KernelMemoryCompatExports return (int)OrbisGen2Result.ORBIS_GEN2_OK; } + // Signature: (SceKernelAprFileId id, uint64_t* size). Callers size their read buffer from the + // answer and may pass an uninitialized slot, so the size must always be written on success. + [SysAbiExport( + Nid = "WvEu7yl3Ivg", + ExportName = "sceKernelAprGetFileSize", + Target = Generation.Gen4 | Generation.Gen5, + LibraryName = "libKernel")] + public static int KernelAprGetFileSize(CpuContext ctx) + { + var fileId = unchecked((uint)ctx[CpuRegister.Rdi]); + var sizeAddress = ctx[CpuRegister.Rsi]; + if (sizeAddress == 0) + { + KernelRuntimeCompatExports.TrySetErrno(ctx, Einval); + return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT; + } + + if (!AmprFileRegistry.TryGetHostPath(fileId, out var hostPath)) + { + LogIoTrace("apr_get_file_size", $"id=0x{fileId:X8}", "result=id_not_registered"); + KernelRuntimeCompatExports.TrySetErrno(ctx, 2); + return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND; + } + + if (!TryGetAprFileSize(hostPath, out var size)) + { + LogIoTrace("apr_get_file_size", hostPath, $"id=0x{fileId:X8} result=not_found"); + KernelRuntimeCompatExports.TrySetErrno(ctx, 2); + return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND; + } + + if (!TryWriteUInt64Compat(ctx, sizeAddress, size)) + { + KernelRuntimeCompatExports.TrySetErrno(ctx, Efault); + return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT; + } + + LogIoTrace("apr_get_file_size", hostPath, $"id=0x{fileId:X8} size={size}"); + ctx[CpuRegister.Rax] = 0; + return (int)OrbisGen2Result.ORBIS_GEN2_OK; + } + [SysAbiExport( Nid = "kBwCPsYX-m4", ExportName = "sceKernelFstat", diff --git a/tests/SharpEmu.Libs.Tests/Ampr/AprStreamingContractTests.cs b/tests/SharpEmu.Libs.Tests/Ampr/AprStreamingContractTests.cs index cbc87d5..b41491b 100644 --- a/tests/SharpEmu.Libs.Tests/Ampr/AprStreamingContractTests.cs +++ b/tests/SharpEmu.Libs.Tests/Ampr/AprStreamingContractTests.cs @@ -22,6 +22,7 @@ public sealed class AprStreamingContractTests const ulong pathAddress = memoryBase + 0x200; const ulong idsAddress = memoryBase + 0x800; const ulong statAddress = memoryBase + 0x900; + const ulong sizeAddress = memoryBase + 0x9A0; const ulong commandBufferAddress = memoryBase + 0x1000; const ulong recordBufferAddress = memoryBase + 0x1100; const ulong destinationAddress = memoryBase + 0x2000; @@ -68,6 +69,16 @@ public sealed class AprStreamingContractTests Assert.True(memory.TryRead(statAddress, stat)); Assert.Equal(fileContents.Length, BinaryPrimitives.ReadInt64LittleEndian(stat[72..])); + WriteUInt64(memory, sizeAddress, 0xDEAD_BEEF); + context[CpuRegister.Rdi] = fileId; + context[CpuRegister.Rsi] = sizeAddress; + + Assert.Equal(0, KernelMemoryCompatExports.KernelAprGetFileSize(context)); + Assert.Equal((ulong)fileContents.Length, ReadUInt64(memory, sizeAddress)); + + context[CpuRegister.Rdi] = 0xFFFF_FFFE; + Assert.NotEqual(0, KernelMemoryCompatExports.KernelAprGetFileSize(context)); + context[CpuRegister.Rdi] = commandBufferAddress; context[CpuRegister.Rsi] = recordBufferAddress; context[CpuRegister.Rdx] = 0x100;