mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-22 10:56:20 +08:00
[HLE] Add strchr/strrchr/memchr/strcat/strncat/strstr libc exports (#132)
Implement six missing libc string/memory search and concatenation routines in the kernel compat layer. Titles frequently call these during startup string handling (path parsing, config lookups, format string assembly), and without them the loader currently falls through to unresolved-import handling. The implementations follow the existing byte-at-a-time compat helpers (TryReadCompat/TryWriteCompat) already used by strcpy/strncpy/memcmp, matching native semantics: strchr/strrchr scan through and including the terminator, memchr is bounded strictly by count, strcat/strncat overwrite the destination terminator and re-terminate, and strstr returns the haystack pointer for an empty needle. NIDs are the libSceLibcInternal/libc symbol hashes for each name.
This commit is contained in:
@@ -1488,6 +1488,214 @@ public static class KernelMemoryCompatExports
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "ob5xAW4ln-0",
|
||||
ExportName = "strchr",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libc")]
|
||||
public static int Strchr(CpuContext ctx)
|
||||
{
|
||||
var address = ctx[CpuRegister.Rdi];
|
||||
var needle = unchecked((byte)ctx[CpuRegister.Rsi]);
|
||||
if (address == 0)
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
// The terminator counts as part of the scanned range, so strchr(s, '\0')
|
||||
// returns a pointer to the string's null byte just like a native libc.
|
||||
Span<byte> current = stackalloc byte[1];
|
||||
for (ulong index = 0; index < 1_048_576; index++)
|
||||
{
|
||||
if (!TryReadCompat(ctx, address + index, current))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
if (current[0] == needle)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = address + index;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
if (current[0] == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "9yDWMxEFdJU",
|
||||
ExportName = "strrchr",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libc")]
|
||||
public static int Strrchr(CpuContext ctx)
|
||||
{
|
||||
var address = ctx[CpuRegister.Rdi];
|
||||
var needle = unchecked((byte)ctx[CpuRegister.Rsi]);
|
||||
if (address == 0)
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
ulong match = 0;
|
||||
var found = false;
|
||||
Span<byte> current = stackalloc byte[1];
|
||||
for (ulong index = 0; index < 1_048_576; index++)
|
||||
{
|
||||
if (!TryReadCompat(ctx, address + index, current))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
if (current[0] == needle)
|
||||
{
|
||||
match = address + index;
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (current[0] == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ctx[CpuRegister.Rax] = found ? match : 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "8u8lPzUEq+U",
|
||||
ExportName = "memchr",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libc")]
|
||||
public static int Memchr(CpuContext ctx)
|
||||
{
|
||||
var address = ctx[CpuRegister.Rdi];
|
||||
var needle = unchecked((byte)ctx[CpuRegister.Rsi]);
|
||||
var count = ctx[CpuRegister.Rdx];
|
||||
|
||||
Span<byte> current = stackalloc byte[1];
|
||||
for (ulong index = 0; index < count; index++)
|
||||
{
|
||||
if (!TryReadCompat(ctx, address + index, current))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
if (current[0] == needle)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = address + index;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
}
|
||||
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "Ls4tzzhimqQ",
|
||||
ExportName = "strcat",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libc")]
|
||||
public static int Strcat(CpuContext ctx)
|
||||
{
|
||||
var destination = ctx[CpuRegister.Rdi];
|
||||
var source = ctx[CpuRegister.Rsi];
|
||||
if (!TryReadCString(ctx, source, 1_048_576, out var sourceBytes))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
if (!TryReadCString(ctx, destination, 1_048_576, out var destinationBytes))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
// Overwrite the destination terminator and re-terminate after the copied bytes.
|
||||
var appendAddress = destination + (ulong)destinationBytes.Length;
|
||||
var payload = new byte[sourceBytes.Length + 1];
|
||||
sourceBytes.CopyTo(payload.AsSpan());
|
||||
if (!TryWriteCompat(ctx, appendAddress, payload))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
ctx[CpuRegister.Rax] = destination;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "kHg45qPC6f0",
|
||||
ExportName = "strncat",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libc")]
|
||||
public static int Strncat(CpuContext ctx)
|
||||
{
|
||||
var destination = ctx[CpuRegister.Rdi];
|
||||
var source = ctx[CpuRegister.Rsi];
|
||||
var limit = ctx[CpuRegister.Rdx];
|
||||
|
||||
// Bounding the source read by the count yields strncat's "at most n bytes"
|
||||
// semantics while still stopping early at the source terminator.
|
||||
if (!TryReadCString(ctx, source, limit, out var sourceBytes))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
if (!TryReadCString(ctx, destination, 1_048_576, out var destinationBytes))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
var appendAddress = destination + (ulong)destinationBytes.Length;
|
||||
var payload = new byte[sourceBytes.Length + 1];
|
||||
sourceBytes.CopyTo(payload.AsSpan());
|
||||
if (!TryWriteCompat(ctx, appendAddress, payload))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
ctx[CpuRegister.Rax] = destination;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "viiwFMaNamA",
|
||||
ExportName = "strstr",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libc")]
|
||||
public static int Strstr(CpuContext ctx)
|
||||
{
|
||||
var haystack = ctx[CpuRegister.Rdi];
|
||||
var needle = ctx[CpuRegister.Rsi];
|
||||
if (!TryReadCString(ctx, haystack, 1_048_576, out var haystackBytes))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
if (!TryReadCString(ctx, needle, 1_048_576, out var needleBytes))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
// An empty needle matches at the start of the haystack.
|
||||
if (needleBytes.Length == 0)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = haystack;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
var matchIndex = haystackBytes.AsSpan().IndexOf(needleBytes.AsSpan());
|
||||
ctx[CpuRegister.Rax] = matchIndex >= 0 ? haystack + (ulong)matchIndex : 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "QrZZdJ8XsX0",
|
||||
ExportName = "fputs",
|
||||
|
||||
Reference in New Issue
Block a user