From 20e8a1800914e678255e1ca12021d9f0bc9097db Mon Sep 17 00:00:00 2001 From: ParantezTech <12572227+par274@users.noreply.github.com> Date: Fri, 2 Oct 2026 19:07:01 +0300 Subject: [PATCH] [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. --- src/SharpEmu.Libs/Font/FontExports.cs | 29 ++++++++++++++++++ .../Font/FontExportsTests.cs | 30 +++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/src/SharpEmu.Libs/Font/FontExports.cs b/src/SharpEmu.Libs/Font/FontExports.cs index 7ef7f3f..36bf98d 100644 --- a/src/SharpEmu.Libs/Font/FontExports.cs +++ b/src/SharpEmu.Libs/Font/FontExports.cs @@ -21,6 +21,35 @@ public static class FontExports private static readonly ConcurrentDictionary FontHeights = new(); private static readonly Lazy 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 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", diff --git a/tests/SharpEmu.Libs.Tests/Font/FontExportsTests.cs b/tests/SharpEmu.Libs.Tests/Font/FontExportsTests.cs index ab5a2ee..eb21982 100644 --- a/tests/SharpEmu.Libs.Tests/Font/FontExportsTests.cs +++ b/tests/SharpEmu.Libs.Tests/Font/FontExportsTests.cs @@ -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]