[Ampr] Separate APR guest file IDs from local handles

This commit is contained in:
ParantezTech 2026-09-22 04:17:44 +03:00
parent f1be35aa35
commit 5c347d2cb2
2 changed files with 43 additions and 5 deletions

View File

@ -50,6 +50,23 @@ internal static class AmprFileRegistry
}
}
public static uint RegisterAprResolvedPath(string guestPath, string hostPath)
{
if (TryGetApp0Relative(guestPath, out var relative) && relative.Length != 0)
{
RegisterApp0Relative(relative, hostPath);
}
// APR file ids are part of the guest ABI: ResolveFilepathsToIds returns
// the 31-bit FNV-1a hash of the guest path. Keep the collision-safe
// process-local handles used by Register() separate from this path so a
// title can compare resolved ids with ids baked into its asset tables.
var fileId = ComputeFileId(guestPath);
_hostPathsById[fileId] = hostPath;
_ambiguousCompatibilityIds.TryRemove(fileId, out _);
return fileId;
}
public static bool TryGetHostPath(uint id, out string hostPath)
{
if ((id & 0x80000000) != 0)

View File

@ -1740,7 +1740,7 @@ public static partial class KernelMemoryCompatExports
return -1;
}
var fileId = AmprFileRegistry.Register(guestPath, hostPath);
var fileId = AmprFileRegistry.RegisterAprResolvedPath(guestPath, hostPath);
LogIoTrace("apr_resolve", guestPath, $"host='{hostPath}' index={i} count={count} id=0x{fileId:X8} size={fileSize}");
if (idsAddress != 0 &&
@ -1832,7 +1832,7 @@ public static partial class KernelMemoryCompatExports
return -1;
}
var fileId = AmprFileRegistry.Register(guestPath, hostPath);
var fileId = AmprFileRegistry.RegisterAprResolvedPath(guestPath, hostPath);
LogIoTrace("apr_resolve_with_prefix", guestPath, $"host='{hostPath}' index={i} count={count} id=0x{fileId:X8} size={fileSize}");
if (idsAddress != 0 &&
@ -1873,7 +1873,8 @@ public static partial class KernelMemoryCompatExports
// IDs, then hand those IDs to sceAmprAprCommandBufferReadFile. Without it
// the paths never register in AmprFileRegistry, so every subsequent
// ReadFile fails with NOT_FOUND and the streaming pipeline stalls forever.
// Signature: (const char* const* paths, size_t count, uint32_t* ids).
// Signature: (const char* const* paths, uint32_t count, uint32_t* ids,
// uint32_t* errorIndex).
[SysAbiExport(
Nid = "WT-5NKy42fw",
ExportName = "sceKernelAprResolveFilepathsToIds",
@ -1884,6 +1885,7 @@ public static partial class KernelMemoryCompatExports
var pathListAddress = ctx[CpuRegister.Rdi];
var count = ctx[CpuRegister.Rsi];
var idsAddress = ctx[CpuRegister.Rdx];
var errorIndexAddress = ctx[CpuRegister.Rcx];
if (pathListAddress == 0 || count == 0 || idsAddress == 0 || count > 1024)
{
KernelRuntimeCompatExports.TrySetErrno(ctx, Einval);
@ -1908,11 +1910,19 @@ public static partial class KernelMemoryCompatExports
if (!TryGetAprFileSize(hostPath, out _))
{
LogIoTrace("apr_resolve_ids", guestPath, $"host='{hostPath}' index={i} count={count} result=not_found");
if (errorIndexAddress != 0 &&
!TryWriteUInt32Compat(ctx, errorIndexAddress, (uint)i))
{
KernelRuntimeCompatExports.TrySetErrno(ctx, Efault);
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
}
KernelRuntimeCompatExports.TrySetErrno(ctx, 2);
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
ctx[CpuRegister.Rax] = ulong.MaxValue;
return -1;
}
var fileId = AmprFileRegistry.Register(guestPath, hostPath);
var fileId = AmprFileRegistry.RegisterAprResolvedPath(guestPath, hostPath);
LogIoTrace("apr_resolve_ids", guestPath, $"host='{hostPath}' index={i} count={count} id=0x{fileId:X8}");
if (!TryWriteUInt32Compat(ctx, idsAddress + (i * sizeof(uint)), fileId))
@ -4811,6 +4821,17 @@ public static partial class KernelMemoryCompatExports
return CombineWithinMount(app0Root, relative);
}
if (string.Equals(guestPath, "/contents", StringComparison.OrdinalIgnoreCase))
{
return CombineWithinMount(app0Root, "contents");
}
if (guestPath.StartsWith("/contents/", StringComparison.OrdinalIgnoreCase))
{
var relative = NormalizeMountRelativePath(guestPath[1..]);
return CombineWithinMount(app0Root, relative);
}
if (!Path.IsPathFullyQualified(guestPath) &&
!guestPath.StartsWith("/", StringComparison.Ordinal) &&
!guestPath.StartsWith("\\", StringComparison.Ordinal))