diff --git a/src/SharpEmu.Libs/ContentExport/ContentExportExports.cs b/src/SharpEmu.Libs/ContentExport/ContentExportExports.cs new file mode 100644 index 0000000..194cab7 --- /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 b02d14d..f5f00fd 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 c1a62a6..91884ea 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 0000000..8720a0e --- /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 0000000..ba01238 --- /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)); + } +}