mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-25 20:28:48 +08:00
8dd3172c0f
Settings probes Set/DisableNoticeScreenSkipFlagAutoSet; unresolved NOT_FOUND can stall the SaveModTime/Load path. Co-authored-by: Cursor <cursoragent@cursor.com>
235 lines
8.9 KiB
C#
235 lines
8.9 KiB
C#
// Copyright (C) 2026 SharpEmu Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
using SharpEmu.HLE;
|
|
using SharpEmu.Libs.Gpu;
|
|
using SharpEmu.Libs.VideoOut;
|
|
using System.Buffers.Binary;
|
|
|
|
namespace SharpEmu.Libs.SystemService;
|
|
|
|
public static class SystemServiceExports
|
|
{
|
|
private const int OrbisSystemServiceErrorParameter = unchecked((int)0x80A10003);
|
|
private const int SystemServiceStatusSize = 0x0C;
|
|
private const int DisplaySafeAreaInfoSize = sizeof(float) + 128;
|
|
private const int HdrToneMapLuminanceSize = sizeof(float) * 3;
|
|
|
|
private const int TitleIdFieldSize = 0x10;
|
|
|
|
private static string? _mainAppTitleId;
|
|
|
|
public static void ConfigureApplicationInfo(string? titleId)
|
|
{
|
|
_mainAppTitleId = string.IsNullOrWhiteSpace(titleId) ? null : titleId.Trim();
|
|
}
|
|
|
|
[SysAbiExport(
|
|
Nid = "3RQ5aQfnstU",
|
|
ExportName = "sceSystemServiceGetNoticeScreenSkipFlag",
|
|
Target = Generation.Gen5,
|
|
LibraryName = "libSceSystemService")]
|
|
public static int SystemServiceGetNoticeScreenSkipFlag(CpuContext ctx)
|
|
{
|
|
var flagAddress = ctx[CpuRegister.Rdi];
|
|
if (flagAddress == 0)
|
|
{
|
|
return ctx.SetReturn(OrbisSystemServiceErrorParameter);
|
|
}
|
|
|
|
// No system notice screen to skip in the emulator; report "do not skip".
|
|
Span<byte> flagBytes = stackalloc byte[1];
|
|
flagBytes[0] = 0;
|
|
return ctx.Memory.TryWrite(flagAddress, flagBytes)
|
|
? ctx.SetReturn(0)
|
|
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
|
}
|
|
|
|
[SysAbiExport(
|
|
Nid = "8Lo6Zv94aho",
|
|
ExportName = "sceSystemServiceDisableNoticeScreenSkipFlagAutoSet",
|
|
Target = Generation.Gen5,
|
|
LibraryName = "libSceSystemService")]
|
|
public static int SystemServiceDisableNoticeScreenSkipFlagAutoSet(CpuContext ctx) =>
|
|
ctx.SetReturn(0);
|
|
|
|
// Settings entry calls this immediately before spawning SaveModTime/Load
|
|
// threads. An unresolved stub returns NOT_FOUND and the title can stall in
|
|
// that path; accept the write and report success.
|
|
[SysAbiExport(
|
|
Nid = "Q3utJvma4Mo",
|
|
ExportName = "sceSystemServiceSetNoticeScreenSkipFlag",
|
|
Target = Generation.Gen5,
|
|
LibraryName = "libSceSystemService")]
|
|
public static int SystemServiceSetNoticeScreenSkipFlag(CpuContext ctx) =>
|
|
ctx.SetReturn(0);
|
|
|
|
[SysAbiExport(
|
|
Nid = "4veE0XiIugA",
|
|
ExportName = "sceSystemServiceGetMainAppTitleId",
|
|
Target = Generation.Gen5,
|
|
LibraryName = "libSceSystemService")]
|
|
public static int SystemServiceGetMainAppTitleId(CpuContext ctx)
|
|
{
|
|
var titleIdAddress = ctx[CpuRegister.Rdi];
|
|
if (titleIdAddress == 0)
|
|
{
|
|
return ctx.SetReturn(OrbisSystemServiceErrorParameter);
|
|
}
|
|
|
|
// Title IDs are a fixed 9-char format written into a 0x10-byte field;
|
|
// bound the length so a malformed param.json cannot drive an unbounded
|
|
// stack allocation or overrun the guest buffer.
|
|
var titleId = _mainAppTitleId ?? "PPSA00000";
|
|
var length = Math.Min(titleId.Length, TitleIdFieldSize - 1);
|
|
Span<byte> titleIdBytes = stackalloc byte[TitleIdFieldSize];
|
|
titleIdBytes.Clear();
|
|
System.Text.Encoding.ASCII.GetBytes(titleId.AsSpan(0, length), titleIdBytes);
|
|
return ctx.Memory.TryWrite(titleIdAddress, titleIdBytes[..(length + 1)])
|
|
? ctx.SetReturn(0)
|
|
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
|
}
|
|
|
|
[SysAbiExport(
|
|
Nid = "fZo48un7LK4",
|
|
ExportName = "sceSystemServiceParamGetInt",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libSceSystemService")]
|
|
public static int SystemServiceParamGetInt(CpuContext ctx)
|
|
{
|
|
var parameterId = unchecked((int)ctx[CpuRegister.Rdi]);
|
|
var valueAddress = ctx[CpuRegister.Rsi];
|
|
if (valueAddress == 0)
|
|
{
|
|
return ctx.SetReturn(OrbisSystemServiceErrorParameter);
|
|
}
|
|
|
|
var value = parameterId switch
|
|
{
|
|
1 or 2 or 3 or 1000 => 1,
|
|
4 => 180,
|
|
_ => 0,
|
|
};
|
|
|
|
Span<byte> valueBytes = stackalloc byte[sizeof(int)];
|
|
BinaryPrimitives.WriteInt32LittleEndian(valueBytes, value);
|
|
return ctx.Memory.TryWrite(valueAddress, valueBytes)
|
|
? ctx.SetReturn(0)
|
|
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
|
}
|
|
|
|
[SysAbiExport(
|
|
Nid = "SsC-m-S9JTA",
|
|
ExportName = "sceSystemServiceParamGetString",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libSceSystemService")]
|
|
public static int SystemServiceParamGetString(CpuContext ctx)
|
|
{
|
|
_ = unchecked((int)ctx[CpuRegister.Rdi]); // parameter id (nickname, etc.)
|
|
var bufferAddress = ctx[CpuRegister.Rsi];
|
|
var bufferSize = unchecked((int)ctx[CpuRegister.Rdx]);
|
|
if (bufferAddress == 0 || bufferSize <= 0)
|
|
{
|
|
return ctx.SetReturn(OrbisSystemServiceErrorParameter);
|
|
}
|
|
|
|
// String params are typically the user nickname. Callers that gate UI
|
|
// or text setup on a successful read (and skip it on failure) stall on
|
|
// a black screen when this returns NOT_FOUND, so return a neutral
|
|
// non-empty default and success.
|
|
var value = System.Text.Encoding.UTF8.GetBytes("SharpEmu");
|
|
var writeLength = Math.Min(value.Length, bufferSize - 1);
|
|
Span<byte> output = stackalloc byte[writeLength + 1];
|
|
value.AsSpan(0, writeLength).CopyTo(output);
|
|
output[writeLength] = 0;
|
|
return ctx.Memory.TryWrite(bufferAddress, output)
|
|
? ctx.SetReturn(0)
|
|
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
|
}
|
|
|
|
[SysAbiExport(
|
|
Nid = "rPo6tV8D9bM",
|
|
ExportName = "sceSystemServiceGetStatus",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libSceSystemService")]
|
|
public static int SystemServiceGetStatus(CpuContext ctx)
|
|
{
|
|
var statusAddress = ctx[CpuRegister.Rdi];
|
|
if (statusAddress == 0)
|
|
{
|
|
return ctx.SetReturn(OrbisSystemServiceErrorParameter);
|
|
}
|
|
|
|
Span<byte> status = stackalloc byte[SystemServiceStatusSize];
|
|
status.Clear();
|
|
BinaryPrimitives.WriteInt32LittleEndian(status, 0);
|
|
status[0x06] = 1;
|
|
|
|
return ctx.Memory.TryWrite(statusAddress, status)
|
|
? ctx.SetReturn(0)
|
|
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
|
}
|
|
|
|
[SysAbiExport(
|
|
Nid = "1n37q1Bvc5Y",
|
|
ExportName = "sceSystemServiceGetDisplaySafeAreaInfo",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libSceSystemService")]
|
|
public static int SystemServiceGetDisplaySafeAreaInfo(CpuContext ctx)
|
|
{
|
|
var infoAddress = ctx[CpuRegister.Rdi];
|
|
if (infoAddress == 0)
|
|
{
|
|
return ctx.SetReturn(OrbisSystemServiceErrorParameter);
|
|
}
|
|
|
|
Span<byte> info = stackalloc byte[DisplaySafeAreaInfoSize];
|
|
info.Clear();
|
|
BinaryPrimitives.WriteSingleLittleEndian(info, 1.0f);
|
|
|
|
return ctx.Memory.TryWrite(infoAddress, info)
|
|
? ctx.SetReturn(0)
|
|
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
|
}
|
|
|
|
[SysAbiExport(
|
|
Nid = "mPpPxv5CZt4",
|
|
ExportName = "sceSystemServiceGetHdrToneMapLuminance",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libSceSystemService")]
|
|
public static int SystemServiceGetHdrToneMapLuminance(CpuContext ctx)
|
|
{
|
|
var luminanceAddress = ctx[CpuRegister.Rdi];
|
|
if (luminanceAddress == 0)
|
|
{
|
|
return ctx.SetReturn(OrbisSystemServiceErrorParameter);
|
|
}
|
|
|
|
Span<byte> luminance = stackalloc byte[HdrToneMapLuminanceSize];
|
|
BinaryPrimitives.WriteSingleLittleEndian(luminance, 1000.0f);
|
|
BinaryPrimitives.WriteSingleLittleEndian(luminance[sizeof(float)..], 1000.0f);
|
|
BinaryPrimitives.WriteSingleLittleEndian(luminance[(sizeof(float) * 2)..], 0.01f);
|
|
return ctx.Memory.TryWrite(luminanceAddress, luminance)
|
|
? ctx.SetReturn(0)
|
|
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
|
}
|
|
|
|
[SysAbiExport(
|
|
Nid = "Vo5V8KAwCmk",
|
|
ExportName = "sceSystemServiceHideSplashScreen",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libSceSystemService")]
|
|
public static int SystemServiceHideSplashScreen(CpuContext ctx)
|
|
{
|
|
GuestGpu.Current.HideSplashScreen();
|
|
return ctx.SetReturn(0);
|
|
}
|
|
|
|
[SysAbiExport(
|
|
Nid = "3s8cHiCBKBE",
|
|
ExportName = "sceSystemServiceReportAbnormalTermination",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libSceSystemService")]
|
|
public static int SystemServiceReportAbnormalTermination(CpuContext ctx) => ctx.SetReturn(0);
|
|
}
|