[Kernel] sceKernelAprGetFileSize writes the resolved file size

The export was a success stub that never wrote its out parameter, so callers
sized buffers from uninitialized memory. The signature is (id, uint64_t* size);
it now answers from the APR file registry like sceKernelAprGetFileStat.
This commit is contained in:
kuba 2026-09-30 16:09:40 +02:00
parent 9e1e15cc5c
commit bedf4c44c7
3 changed files with 53 additions and 13 deletions

View File

@ -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,

View File

@ -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",

View File

@ -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;