From 1a4a2902d4d85391c899e6fe4fea30d98e43bf3c Mon Sep 17 00:00:00 2001 From: samto6 <123419830+samto6@users.noreply.github.com> Date: Thu, 16 Jul 2026 21:34:48 -0400 Subject: [PATCH] [HLE] Stub ContentExport, Font, and Pad calls Astro Bot needs to boot (#298) Astro Bot asserts and null-writes when a subsystem init call fails, so each missing import surfaces as a named crash. This stubs the blockers observed during bring-up: content export init, eight font calls, and pad tilt correction. sceFontGetHorizontalLayout writes the same invented geometry as sceFontGetRenderCharGlyphMetrics and the rest report success. Together with save data memory2 these take the title to its splash image and font glyph rendering path. --- .../ContentExport/ContentExportExports.cs | 18 +++++ src/SharpEmu.Libs/Font/FontExports.cs | 79 +++++++++++++++++++ src/SharpEmu.Libs/Pad/PadExports.cs | 13 +++ .../Font/FontExportsTests.cs | 44 +++++++++++ .../Pad/PadExportsTests.cs | 33 ++++++++ 5 files changed, 187 insertions(+) create mode 100644 src/SharpEmu.Libs/ContentExport/ContentExportExports.cs create mode 100644 tests/SharpEmu.Libs.Tests/Font/FontExportsTests.cs create mode 100644 tests/SharpEmu.Libs.Tests/Pad/PadExportsTests.cs diff --git a/src/SharpEmu.Libs/ContentExport/ContentExportExports.cs b/src/SharpEmu.Libs/ContentExport/ContentExportExports.cs new file mode 100644 index 00000000..194cab77 --- /dev/null +++ b/src/SharpEmu.Libs/ContentExport/ContentExportExports.cs @@ -0,0 +1,18 @@ +// Copyright (C) 2026 SharpEmu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +using SharpEmu.HLE; + +namespace SharpEmu.Libs.ContentExport; + +// No host media library exists to export captures into, so initialization +// reports success. +public static class ContentExportExports +{ + [SysAbiExport( + Nid = "0GnN4QCgIfs", + ExportName = "sceContentExportInit2", + Target = Generation.Gen5, + LibraryName = "libSceContentExport")] + public static int ContentExportInit2(CpuContext ctx) => ctx.SetReturn(0); +} diff --git a/src/SharpEmu.Libs/Font/FontExports.cs b/src/SharpEmu.Libs/Font/FontExports.cs index b02d14d3..f5f00fdc 100644 --- a/src/SharpEmu.Libs/Font/FontExports.cs +++ b/src/SharpEmu.Libs/Font/FontExports.cs @@ -74,6 +74,85 @@ public static class FontExports public static int CreateRendererWithEdition(CpuContext ctx) => CreateOpaqueHandle(ctx, ctx[CpuRegister.Rcx], 0x100, magic: 0x0F07); + [SysAbiExport( + Nid = "3OdRkSjOcog", + ExportName = "sceFontBindRenderer", + Target = Generation.Gen5, + LibraryName = "libSceFont")] + public static int BindRenderer(CpuContext ctx) => SetSuccess(ctx); + + [SysAbiExport( + Nid = "N1EBMeGhf7E", + ExportName = "sceFontSetScalePixel", + Target = Generation.Gen5, + LibraryName = "libSceFont")] + public static int SetScalePixel(CpuContext ctx) => SetSuccess(ctx); + + [SysAbiExport( + Nid = "TMtqoFQjjbA", + ExportName = "sceFontSetEffectSlant", + Target = Generation.Gen5, + LibraryName = "libSceFont")] + public static int SetEffectSlant(CpuContext ctx) => SetSuccess(ctx); + + [SysAbiExport( + Nid = "v0phZwa4R5o", + ExportName = "sceFontSetEffectWeight", + Target = Generation.Gen5, + LibraryName = "libSceFont")] + public static int SetEffectWeight(CpuContext ctx) => SetSuccess(ctx); + + [SysAbiExport( + Nid = "6vGCkkQJOcI", + ExportName = "sceFontSetupRenderScalePixel", + Target = Generation.Gen5, + LibraryName = "libSceFont")] + public static int SetupRenderScalePixel(CpuContext ctx) => SetSuccess(ctx); + + [SysAbiExport( + Nid = "lz9y9UFO2UU", + ExportName = "sceFontSetupRenderEffectSlant", + Target = Generation.Gen5, + LibraryName = "libSceFont")] + public static int SetupRenderEffectSlant(CpuContext ctx) => SetSuccess(ctx); + + [SysAbiExport( + Nid = "XIGorvLusDQ", + ExportName = "sceFontSetupRenderEffectWeight", + Target = Generation.Gen5, + LibraryName = "libSceFont")] + public static int SetupRenderEffectWeight(CpuContext ctx) => SetSuccess(ctx); + + [SysAbiExport( + Nid = "imxVx8lm+KM", + ExportName = "sceFontGetHorizontalLayout", + Target = Generation.Gen5, + LibraryName = "libSceFont")] + public static int GetHorizontalLayout(CpuContext ctx) + { + var layoutAddress = ctx[CpuRegister.Rsi]; + if (layoutAddress == 0) + { + return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT); + } + + // Baseline, line advance, decoration extent: the same invented geometry + // as GetRenderCharGlyphMetrics. + var values = new[] { 12.0f, 16.0f, 0.0f }; + for (var index = 0; index < values.Length; index++) + { + if (!TryWriteUInt32( + ctx, + layoutAddress + (ulong)(index * sizeof(float)), + BitConverter.SingleToUInt32Bits(values[index]))) + { + return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT); + } + } + + return SetSuccess(ctx); + } + [SysAbiExport( Nid = "cKYtVmeSTcw", ExportName = "sceFontOpenFontSet", diff --git a/src/SharpEmu.Libs/Pad/PadExports.cs b/src/SharpEmu.Libs/Pad/PadExports.cs index c1a62a68..91884ea0 100644 --- a/src/SharpEmu.Libs/Pad/PadExports.cs +++ b/src/SharpEmu.Libs/Pad/PadExports.cs @@ -127,6 +127,19 @@ public static class PadExports : ctx.SetReturn(OrbisPadErrorInvalidHandle); } + [SysAbiExport( + Nid = "vDLMoJLde8I", + ExportName = "scePadSetTiltCorrectionState", + Target = Generation.Gen4 | Generation.Gen5, + LibraryName = "libScePad")] + public static int PadSetTiltCorrectionState(CpuContext ctx) + { + var handle = unchecked((int)ctx[CpuRegister.Rdi]); + return IsPrimaryPadHandle(handle) + ? ctx.SetReturn(0) + : ctx.SetReturn(OrbisPadErrorInvalidHandle); + } + [SysAbiExport( Nid = "gjP9-KQzoUk", ExportName = "scePadGetControllerInformation", diff --git a/tests/SharpEmu.Libs.Tests/Font/FontExportsTests.cs b/tests/SharpEmu.Libs.Tests/Font/FontExportsTests.cs new file mode 100644 index 00000000..8720a0e6 --- /dev/null +++ b/tests/SharpEmu.Libs.Tests/Font/FontExportsTests.cs @@ -0,0 +1,44 @@ +// Copyright (C) 2026 SharpEmu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +using System.Buffers.Binary; +using SharpEmu.HLE; +using SharpEmu.Libs.Font; +using Xunit; + +namespace SharpEmu.Libs.Tests.Font; + +public sealed class FontExportsTests +{ + private const ulong Base = 0x1_0000_0000; + private const ulong LayoutAddress = Base + 0x100; + + private readonly FakeCpuMemory _memory = new(Base, 0x1000); + private readonly CpuContext _ctx; + + public FontExportsTests() + { + _ctx = new CpuContext(_memory, Generation.Gen5); + } + + // SceFontHorizontalLayout is three floats; the sentinel directly after + // them must survive the call. + [Fact] + public void GetHorizontalLayout_WritesExactlyThreeFloats() + { + const uint Sentinel = 0xDEADBEEF; + Span sentinelBytes = stackalloc byte[sizeof(uint)]; + BinaryPrimitives.WriteUInt32LittleEndian(sentinelBytes, Sentinel); + Assert.True(_ctx.Memory.TryWrite(LayoutAddress + 12, sentinelBytes)); + + _ctx[CpuRegister.Rsi] = LayoutAddress; + Assert.Equal(0, FontExports.GetHorizontalLayout(_ctx)); + + Span layout = stackalloc byte[16]; + Assert.True(_ctx.Memory.TryRead(LayoutAddress, layout)); + Assert.Equal(12.0f, BinaryPrimitives.ReadSingleLittleEndian(layout)); + Assert.Equal(16.0f, BinaryPrimitives.ReadSingleLittleEndian(layout[4..])); + Assert.Equal(0.0f, BinaryPrimitives.ReadSingleLittleEndian(layout[8..])); + Assert.Equal(Sentinel, BinaryPrimitives.ReadUInt32LittleEndian(layout[12..])); + } +} diff --git a/tests/SharpEmu.Libs.Tests/Pad/PadExportsTests.cs b/tests/SharpEmu.Libs.Tests/Pad/PadExportsTests.cs new file mode 100644 index 00000000..ba01238b --- /dev/null +++ b/tests/SharpEmu.Libs.Tests/Pad/PadExportsTests.cs @@ -0,0 +1,33 @@ +// Copyright (C) 2026 SharpEmu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +using SharpEmu.HLE; +using SharpEmu.Libs.Pad; +using Xunit; + +namespace SharpEmu.Libs.Tests.Pad; + +public sealed class PadExportsTests +{ + private const ulong Base = 0x1_0000_0000; + private const int InvalidHandle = unchecked((int)0x80920003); + + private readonly FakeCpuMemory _memory = new(Base, 0x1000); + private readonly CpuContext _ctx; + + public PadExportsTests() + { + _ctx = new CpuContext(_memory, Generation.Gen5); + } + + [Theory] + [InlineData(0, 0)] + [InlineData(1, 0)] + [InlineData(2, InvalidHandle)] + [InlineData(-1, InvalidHandle)] + public void SetTiltCorrectionState_ValidatesHandle(int handle, int expected) + { + _ctx[CpuRegister.Rdi] = unchecked((ulong)handle); + Assert.Equal(expected, PadExports.PadSetTiltCorrectionState(_ctx)); + } +}