From 22a7ca97ede3d1ca04afa955ee9496c43e22c172 Mon Sep 17 00:00:00 2001 From: Gutemberg Ribeiro Date: Sun, 19 Jul 2026 04:11:05 +0100 Subject: [PATCH] [HLE] Resolve Lunar Lander's remaining unresolved imports With the threading rework letting Lunar Lander Beyond run ~8x further (800K -> 6.7M imports), five previously-unreached imports surfaced. All resolved -> zero unresolved NIDs: - unlink (VAzswvTOCzI): POSIX alias of sceKernelUnlink. - sceAgcDriverGetEqContextId (Zw7uUVPulbw): returns a single stable driver eq-context id (1); events are keyed on (equeue, ident, filter) so one id satisfies the contract. - sceImeKeyboardGetInfo (VkqLPArfFdc): zeroes the info struct (no keyboard connected) and returns OK. - sceSysmoduleGetModuleInfoForUnwind (4fU5yvOkVG4): delegates to the existing sceKernelGetModuleInfoForUnwind (same contract, Gen5 module surface). - _is_signal_return (crb5j7mkk1c): libc unwinder predicate; guest signal returns use no guest-visible trampoline, so always false. Lunar now reaches its next wall (a C++ exception-unwind loop, 0 DCBs) with no unresolved imports; the other games are unaffected (additive exports). 349 Libs tests pass. --- src/SharpEmu.Libs/Agc/AgcExports.cs | 26 +++++++++++++++++++ src/SharpEmu.Libs/Ime/ImeExports.cs | 21 +++++++++++++++ .../Kernel/KernelMemoryCompatExports.cs | 9 +++++++ .../Kernel/KernelRuntimeCompatExports.cs | 24 +++++++++++++++++ 4 files changed, 80 insertions(+) diff --git a/src/SharpEmu.Libs/Agc/AgcExports.cs b/src/SharpEmu.Libs/Agc/AgcExports.cs index 39419e44..2a55726c 100644 --- a/src/SharpEmu.Libs/Agc/AgcExports.cs +++ b/src/SharpEmu.Libs/Agc/AgcExports.cs @@ -2622,6 +2622,32 @@ public static partial class AgcExports return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK); } + // Hands the game the driver-side context id its GPU event-queue packets + // reference. We key events purely on (equeue, ident, filter), so a single + // stable id satisfies the contract; the game only checks the call + // succeeded and threads the id back through later driver calls. + [SysAbiExport( + Nid = "Zw7uUVPulbw", + ExportName = "sceAgcDriverGetEqContextId", + Target = Generation.Gen5, + LibraryName = "libSceAgcDriver")] + public static int DriverGetEqContextId(CpuContext ctx) + { + var contextIdAddress = ctx[CpuRegister.Rdi]; + if (contextIdAddress == 0) + { + return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT); + } + + if (!TryWriteUInt32(ctx, contextIdAddress, 1)) + { + return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT); + } + + TraceAgc($"agc.driver_get_eq_context_id out=0x{contextIdAddress:X16} -> 1"); + return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK); + } + [SysAbiExport( Nid = "DL2RXaXOy88", ExportName = "sceAgcDriverDeleteEqEvent", diff --git a/src/SharpEmu.Libs/Ime/ImeExports.cs b/src/SharpEmu.Libs/Ime/ImeExports.cs index 7235731b..f3cbab8b 100644 --- a/src/SharpEmu.Libs/Ime/ImeExports.cs +++ b/src/SharpEmu.Libs/Ime/ImeExports.cs @@ -43,4 +43,25 @@ public static class ImeExports ctx[CpuRegister.Rax] = 0; return (int)OrbisGen2Result.ORBIS_GEN2_OK; } + + // No hardware keyboard is ever connected; zero the caller's info struct so + // it reads as "not connected" rather than uninitialized stack. + [SysAbiExport( + Nid = "VkqLPArfFdc", + ExportName = "sceImeKeyboardGetInfo", + Target = Generation.Gen4 | Generation.Gen5, + LibraryName = "libSceIme")] + public static int ImeKeyboardGetInfo(CpuContext ctx) + { + var infoAddress = ctx[CpuRegister.Rsi] != 0 ? ctx[CpuRegister.Rsi] : ctx[CpuRegister.Rdi]; + if (infoAddress != 0) + { + Span info = stackalloc byte[0x40]; + info.Clear(); + _ = ctx.Memory.TryWrite(infoAddress, info); + } + + ctx[CpuRegister.Rax] = 0; + return (int)OrbisGen2Result.ORBIS_GEN2_OK; + } } diff --git a/src/SharpEmu.Libs/Kernel/KernelMemoryCompatExports.cs b/src/SharpEmu.Libs/Kernel/KernelMemoryCompatExports.cs index dac33c6e..a7b6969b 100644 --- a/src/SharpEmu.Libs/Kernel/KernelMemoryCompatExports.cs +++ b/src/SharpEmu.Libs/Kernel/KernelMemoryCompatExports.cs @@ -1819,6 +1819,15 @@ public static partial class KernelMemoryCompatExports return (int)OrbisGen2Result.ORBIS_GEN2_OK; } + // POSIX alias; same Orbis result convention as the other posix-named + // file exports in this module (mkdir/rmdir/open). + [SysAbiExport( + Nid = "VAzswvTOCzI", + ExportName = "unlink", + Target = Generation.Gen4 | Generation.Gen5, + LibraryName = "libKernel")] + public static int PosixUnlink(CpuContext ctx) => KernelUnlink(ctx); + [SysAbiExport( Nid = "AUXVxWeJU-A", ExportName = "sceKernelUnlink", diff --git a/src/SharpEmu.Libs/Kernel/KernelRuntimeCompatExports.cs b/src/SharpEmu.Libs/Kernel/KernelRuntimeCompatExports.cs index 5eb422c0..c716dee5 100644 --- a/src/SharpEmu.Libs/Kernel/KernelRuntimeCompatExports.cs +++ b/src/SharpEmu.Libs/Kernel/KernelRuntimeCompatExports.cs @@ -1302,6 +1302,30 @@ public static class KernelRuntimeCompatExports return (int)OrbisGen2Result.ORBIS_GEN2_OK; } + // Same (pc, flags, out-info) contract as sceKernelGetModuleInfoForUnwind, + // surfaced through libSceSysmodule on Gen5; the unwinder threads whichever + // one the module's libc was linked against. + [SysAbiExport( + Nid = "4fU5yvOkVG4", + ExportName = "sceSysmoduleGetModuleInfoForUnwind", + Target = Generation.Gen4 | Generation.Gen5, + LibraryName = "libSceSysmodule")] + public static int SysmoduleGetModuleInfoForUnwind(CpuContext ctx) => KernelGetModuleInfoForUnwind(ctx); + + // libc unwinder predicate: is this PC the kernel signal-return trampoline? + // Guest signal returns do not run through a guest-visible trampoline here, + // so no PC is ever one — report false and let the frame unwind normally. + [SysAbiExport( + Nid = "crb5j7mkk1c", + ExportName = "_is_signal_return", + Target = Generation.Gen4 | Generation.Gen5, + LibraryName = "libc")] + public static int IsSignalReturn(CpuContext ctx) + { + ctx[CpuRegister.Rax] = 0; + return (int)OrbisGen2Result.ORBIS_GEN2_OK; + } + [SysAbiExport( Nid = "nu4a0-arQis", ExportName = "sceKernelAioInitializeParam",