sharpemu/src/SharpEmu.Libs/Stubs/GameServiceStubs.cs

210 lines
10 KiB
C#

// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using SharpEmu.HLE;
namespace SharpEmu.Libs.Stubs;
/// <summary>
/// Success stubs for trophy, character-encoding and telemetry ABI calls that
/// Void Terrarium (and other titles) invoke during startup. They were
/// previously unresolved and returned NOT_FOUND, and a title that gates its
/// UI/text initialization on these succeeding then skips ahead and never draws
/// its content (a black screen with only a clear pass). These return success
/// (and a non-zero handle where an out pointer is expected) so init proceeds.
/// </summary>
public static class GameServiceStubs
{
private static int Ok(CpuContext ctx)
{
ctx[CpuRegister.Rax] = 0;
return 0;
}
// Writes a small non-zero handle to the pointer in the given register so
// the caller treats the object as created; returns success.
private static int OkWithHandle(CpuContext ctx, CpuRegister outPointerRegister)
{
var outAddress = ctx[outPointerRegister];
if (outAddress != 0)
{
Span<byte> handle = stackalloc byte[sizeof(int)];
System.Buffers.Binary.BinaryPrimitives.WriteInt32LittleEndian(handle, 1);
_ = ctx.Memory.TryWrite(outAddress, handle);
}
return Ok(ctx);
}
// ---- NpTrophy2: trophy context/handle registration at boot ----
public static int NpTrophy2CreateContext(CpuContext ctx) => OkWithHandle(ctx, CpuRegister.Rdi);
public static int NpTrophy2CreateHandle(CpuContext ctx) => OkWithHandle(ctx, CpuRegister.Rdi);
public static int NpTrophy2RegisterContext(CpuContext ctx) => Ok(ctx);
[SysAbiExport(Nid = "4IzqhhUQ3nk", ExportName = "sceNpTrophy2GetGameInfo",
Target = Generation.Gen4 | Generation.Gen5, LibraryName = "libSceNpTrophy2")]
public static int NpTrophy2GetGameInfo(CpuContext ctx) => Ok(ctx);
// ---- CES: Shift-JIS <-> Unicode conversion setup (Japanese text) ----
[SysAbiExport(Nid = "ZiDCxUUGbec", ExportName = "sceCesUcsProfileInitSJis1997Cp932",
Target = Generation.Gen4 | Generation.Gen5, LibraryName = "libSceLibcInternal")]
public static int CesUcsProfileInitSJis1997Cp932(CpuContext ctx) => Ok(ctx);
[SysAbiExport(Nid = "538bRGc6Zo8", ExportName = "sceCesMbcsUcsContextInit",
Target = Generation.Gen4 | Generation.Gen5, LibraryName = "libSceLibcInternal")]
public static int CesMbcsUcsContextInit(CpuContext ctx) => Ok(ctx);
// ---- NpUniversalDataSystem: gameplay telemetry events ----
public static int NpUniversalDataSystemCreateEvent(CpuContext ctx) => OkWithHandle(ctx, CpuRegister.Rdi);
public static int NpUniversalDataSystemPostEvent(CpuContext ctx) => Ok(ctx);
public static int NpUniversalDataSystemDestroyEvent(CpuContext ctx) => Ok(ctx);
[SysAbiExport(Nid = "0HBYxYAjmf0", ExportName = "sceNpGameIntentTerminate",
Target = Generation.Gen5, LibraryName = "libSceNpGameIntent")]
public static int NpGameIntentTerminate(CpuContext ctx) => Ok(ctx);
[SysAbiExport(Nid = "mlYGfmqE3fQ", ExportName = "sceSigninDialogInitialize",
Target = Generation.Gen4 | Generation.Gen5, LibraryName = "libSceSigninDialog")]
public static int SigninDialogInitialize(CpuContext ctx) => Ok(ctx);
[SysAbiExport(Nid = "JlpJVoRWv7U", ExportName = "sceSigninDialogOpen",
Target = Generation.Gen4 | Generation.Gen5, LibraryName = "libSceSigninDialog")]
public static int SigninDialogOpen(CpuContext ctx) => Ok(ctx);
[SysAbiExport(Nid = "Bw31liTFT3A", ExportName = "sceSigninDialogUpdateStatus",
Target = Generation.Gen4 | Generation.Gen5, LibraryName = "libSceSigninDialog")]
public static int SigninDialogUpdateStatus(CpuContext ctx) => ctx.SetReturn(3);
[SysAbiExport(Nid = "nqG7rqnYw1U", ExportName = "sceSigninDialogGetResult",
Target = Generation.Gen4 | Generation.Gen5, LibraryName = "libSceSigninDialog")]
public static int SigninDialogGetResult(CpuContext ctx)
{
var address = ctx[CpuRegister.Rdi];
if (address == 0)
{
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
}
Span<byte> result = stackalloc byte[16];
System.Buffers.Binary.BinaryPrimitives.WriteInt32LittleEndian(result, 1); // User canceled.
return ctx.Memory.TryWrite(address, result)
? Ok(ctx)
: ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
}
[SysAbiExport(Nid = "LXlmS6PvJdU", ExportName = "sceSigninDialogTerminate",
Target = Generation.Gen4 | Generation.Gen5, LibraryName = "libSceSigninDialog")]
public static int SigninDialogTerminate(CpuContext ctx) => Ok(ctx);
[SysAbiExport(Nid = "UOjiprYwVNw", ExportName = "sceTextToSpeech2Initialize",
Target = Generation.Gen5, LibraryName = "libSceTextToSpeech2")]
public static int TextToSpeech2Initialize(CpuContext ctx) => Ok(ctx);
[SysAbiExport(Nid = "X0HZNbSiqyg", ExportName = "sceTextToSpeech2Open",
Target = Generation.Gen5, LibraryName = "libSceTextToSpeech2")]
public static int TextToSpeech2Open(CpuContext ctx) =>
ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_IMPLEMENTED);
[SysAbiExport(Nid = "kvYEw2lBndk", ExportName = "sceGameLiveStreamingInitialize",
Target = Generation.Gen5, LibraryName = "libSceGameLiveStreaming")]
public static int GameLiveStreamingInitialize(CpuContext ctx) => Ok(ctx);
[SysAbiExport(Nid = "isruqthpYcw", ExportName = "sceSharePlayInitialize",
Target = Generation.Gen5, LibraryName = "libSceSharePlay")]
public static int SharePlayInitialize(CpuContext ctx) => Ok(ctx);
[SysAbiExport(Nid = "0IL1keINExQ", ExportName = "sceShareTerminate",
Target = Generation.Gen5, LibraryName = "libSceShareUtility")]
public static int ShareTerminate(CpuContext ctx) => Ok(ctx);
[SysAbiExport(Nid = "YBiIdcDPrxs", ExportName = "sceShareFeaturePermit",
Target = Generation.Gen5, LibraryName = "libSceShareUtility")]
public static int ShareFeaturePermit(CpuContext ctx) => Ok(ctx);
[SysAbiExport(Nid = "9TrhuGzberQ", ExportName = "sceVoiceInit",
Target = Generation.Gen5, LibraryName = "libSceVoice")]
public static int VoiceInit(CpuContext ctx) => Ok(ctx);
[SysAbiExport(Nid = "clyKUyi3RYU", ExportName = "sceVoiceSetThreadsParams",
Target = Generation.Gen5, LibraryName = "libSceVoice")]
public static int VoiceSetThreadsParams(CpuContext ctx) => Ok(ctx);
[SysAbiExport(Nid = "nXpje5yNpaE", ExportName = "sceVoiceCreatePort",
Target = Generation.Gen5, LibraryName = "libSceVoice")]
public static int VoiceCreatePort(CpuContext ctx) => OkWithHandle(ctx, CpuRegister.Rdi);
[SysAbiExport(Nid = "b7kJI+nx2hg", ExportName = "sceVoiceDeletePort",
Target = Generation.Gen5, LibraryName = "libSceVoice")]
public static int VoiceDeletePort(CpuContext ctx) => Ok(ctx);
[SysAbiExport(Nid = "oV9GAdJ23Gw", ExportName = "sceVoiceConnectIPortToOPort",
Target = Generation.Gen5, LibraryName = "libSceVoice")]
public static int VoiceConnectIPortToOPort(CpuContext ctx) => Ok(ctx);
[SysAbiExport(Nid = "ajVj3QG2um4", ExportName = "sceVoiceDisconnectIPortFromOPort",
Target = Generation.Gen5, LibraryName = "libSceVoice")]
public static int VoiceDisconnectIPortFromOPort(CpuContext ctx) => Ok(ctx);
[SysAbiExport(Nid = "Oo0S5PH7FIQ", ExportName = "sceVoiceEnd",
Target = Generation.Gen5, LibraryName = "libSceVoice")]
public static int VoiceEnd(CpuContext ctx) => Ok(ctx);
[SysAbiExport(Nid = "amuBfI-AQc4", ExportName = "sceRudpInit",
Target = Generation.Gen4 | Generation.Gen5, LibraryName = "libSceRudp")]
public static int RudpInit(CpuContext ctx) => Ok(ctx);
[SysAbiExport(Nid = "6PBNpsgyaxw", ExportName = "sceRudpEnableInternalIOThread",
Target = Generation.Gen4 | Generation.Gen5, LibraryName = "libSceRudp")]
public static int RudpEnableInternalIOThread(CpuContext ctx) => Ok(ctx);
[SysAbiExport(Nid = "SUEVes8gvmw", ExportName = "sceRudpSetEventHandler",
Target = Generation.Gen4 | Generation.Gen5, LibraryName = "libSceRudp")]
public static int RudpSetEventHandler(CpuContext ctx) => Ok(ctx);
[SysAbiExport(Nid = "84fDxStrG44", ExportName = "sceDeviceServiceInitialize",
Target = Generation.Gen4 | Generation.Gen5, LibraryName = "libSceDeviceService")]
public static int DeviceServiceInitialize(CpuContext ctx) => Ok(ctx);
[SysAbiExport(Nid = "9ddRUOV8Q5A", ExportName = "sceDeviceServiceGetEventState",
Target = Generation.Gen4 | Generation.Gen5, LibraryName = "libSceDeviceService")]
public static int DeviceServiceGetEventState(CpuContext ctx) => Ok(ctx);
[SysAbiExport(Nid = "UNMEa+5lrUA", ExportName = "sceDeviceServiceQueryDeviceInfo_",
Target = Generation.Gen4 | Generation.Gen5, LibraryName = "libSceDeviceService")]
public static int DeviceServiceQueryDeviceInfo(CpuContext ctx)
{
// No host device-service backend yet. Report the device as unavailable
// so callers can take their normal gamepad/non-special-device fallback.
const int notFound = (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
ctx[CpuRegister.Rax] = unchecked((ulong)notFound);
return notFound;
}
[SysAbiExport(Nid = "c812oYs7Vsc", ExportName = "sceHmd2Initialize",
Target = Generation.Gen4 | Generation.Gen5, LibraryName = "libSceHmd2")]
public static int Hmd2Initialize(CpuContext ctx)
{
// HMD2 is optional for this title; keep VR unavailable rather than
// advertising an initialized headset that SharpEmu cannot service.
const int notFound = (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
ctx[CpuRegister.Rax] = unchecked((ulong)notFound);
return notFound;
}
[SysAbiExport(Nid = "dPj4ZtRcIWk", ExportName = "sceContentSearchInit",
Target = Generation.Gen5, LibraryName = "libSceContentSearch")]
public static int ContentSearchInit(CpuContext ctx) => Ok(ctx);
[SysAbiExport(Nid = "zoxb0wEChEM", ExportName = "sceContentDeleteInitialize",
Target = Generation.Gen5, LibraryName = "libSceContentDelete")]
public static int ContentDeleteInitialize(CpuContext ctx) => Ok(ctx);
[SysAbiExport(Nid = "Fc8qxlKINYQ", ExportName = "sceVideoRecordingSetInfo",
Target = Generation.Gen5, LibraryName = "libSceVideoRecording")]
public static int VideoRecordingSetInfo(CpuContext ctx) => Ok(ctx);
}