[Font] Implement sceFontTextSourceInit

Fills the 0x60-byte text source with the text range, the cursor, the parser
callback and its object, and the text size, and leaves the memory after it
untouched.
This commit is contained in:
ParantezTech 2026-10-02 19:07:01 +03:00
parent 67f11e78ea
commit 20e8a18009
2 changed files with 59 additions and 0 deletions

View File

@ -21,6 +21,35 @@ public static class FontExports
private static readonly ConcurrentDictionary<ulong, float> FontHeights = new();
private static readonly Lazy<byte[]?> BitmapFont = new(LoadBitmapFont);
[SysAbiExport(
Nid = "oaJ1BpN2FQk",
ExportName = "sceFontTextSourceInit",
Target = Generation.Gen5,
LibraryName = "libSceFont")]
public static int TextSourceInit(CpuContext ctx)
{
var sourceAddress = ctx[CpuRegister.Rdi];
var textAddress = ctx[CpuRegister.Rsi];
var textSize = (uint)ctx[CpuRegister.Rdx];
if (sourceAddress == 0 || textAddress > ulong.MaxValue - textSize)
{
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
}
Span<byte> source = stackalloc byte[0x60];
source.Clear();
BinaryPrimitives.WriteUInt64LittleEndian(source[0x08..], textAddress);
BinaryPrimitives.WriteUInt64LittleEndian(source[0x10..], textAddress == 0 ? 0 : textAddress + textSize);
BinaryPrimitives.WriteUInt64LittleEndian(source[0x18..], textAddress);
BinaryPrimitives.WriteUInt64LittleEndian(source[0x20..], ctx[CpuRegister.Rcx]);
BinaryPrimitives.WriteUInt64LittleEndian(source[0x28..], ctx[CpuRegister.R8]);
BinaryPrimitives.WriteUInt64LittleEndian(source[0x38..], 0x10);
BinaryPrimitives.WriteUInt64LittleEndian(source[0x40..], textSize);
return ctx.Memory.TryWrite(sourceAddress, source)
? SetSuccess(ctx)
: SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
[SysAbiExport(
Nid = "whrS4oksXc4",
ExportName = "sceFontMemoryInit",

View File

@ -21,6 +21,36 @@ public sealed class FontExportsTests
_ctx = new CpuContext(_memory, Generation.Gen5);
}
[Fact]
public void TextSourceInit_WritesParserAndTextRangeWithoutOverwritingNextObject()
{
const ulong text = Base + 0x300;
const ulong parser = Base + 0x400;
const ulong parserObject = Base + 0x500;
var bytes = new byte[0x68];
Array.Fill(bytes, (byte)0xcc);
Assert.True(_memory.TryWrite(LayoutAddress, bytes));
_ctx[CpuRegister.Rdi] = LayoutAddress;
_ctx[CpuRegister.Rsi] = text;
_ctx[CpuRegister.Rdx] = 9;
_ctx[CpuRegister.Rcx] = parser;
_ctx[CpuRegister.R8] = parserObject;
Assert.Equal(0, FontExports.TextSourceInit(_ctx));
Assert.True(_memory.TryRead(LayoutAddress, bytes));
Assert.Equal(0UL, BinaryPrimitives.ReadUInt64LittleEndian(bytes));
Assert.Equal(text, BinaryPrimitives.ReadUInt64LittleEndian(bytes.AsSpan(0x08)));
Assert.Equal(text + 9, BinaryPrimitives.ReadUInt64LittleEndian(bytes.AsSpan(0x10)));
Assert.Equal(text, BinaryPrimitives.ReadUInt64LittleEndian(bytes.AsSpan(0x18)));
Assert.Equal(parser, BinaryPrimitives.ReadUInt64LittleEndian(bytes.AsSpan(0x20)));
Assert.Equal(parserObject, BinaryPrimitives.ReadUInt64LittleEndian(bytes.AsSpan(0x28)));
Assert.Equal(0UL, BinaryPrimitives.ReadUInt64LittleEndian(bytes.AsSpan(0x30)));
Assert.Equal(0x10UL, BinaryPrimitives.ReadUInt64LittleEndian(bytes.AsSpan(0x38)));
Assert.Equal(9UL, BinaryPrimitives.ReadUInt64LittleEndian(bytes.AsSpan(0x40)));
Assert.All(bytes[0x48..0x60], value => Assert.Equal((byte)0, value));
Assert.All(bytes[0x60..], value => Assert.Equal((byte)0xcc, value));
}
// SceFontHorizontalLayout is three floats; the sentinel directly after
// them must survive the call.
[Fact]