From 7b950166d72dbf8393b4022ed59ee83c4849dde7 Mon Sep 17 00:00:00 2001 From: MarcelMediaDev Date: Thu, 23 Jul 2026 13:35:28 +0100 Subject: [PATCH] fix(remoteplay): stub Initialize and GetConnectionStatus as disconnected (#536) * fix(remoteplay): stub Initialize and GetConnectionStatus as disconnected Titles probe Remote Play during pad/network bring-up; unresolved imports returned NOT_FOUND. Report initialized + disconnected so callers take the normal offline path. Co-authored-by: Cursor * chore: retrigger gameplay CI for PR #536 Co-authored-by: Cursor --------- Co-authored-by: Cursor --- .../Remoteplay/RemoteplayExports.cs | 51 ++++++++++++++++ .../Remoteplay/RemoteplayExportsTests.cs | 58 +++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 src/SharpEmu.Libs/Remoteplay/RemoteplayExports.cs create mode 100644 tests/SharpEmu.Libs.Tests/Remoteplay/RemoteplayExportsTests.cs diff --git a/src/SharpEmu.Libs/Remoteplay/RemoteplayExports.cs b/src/SharpEmu.Libs/Remoteplay/RemoteplayExports.cs new file mode 100644 index 00000000..e6b9aecf --- /dev/null +++ b/src/SharpEmu.Libs/Remoteplay/RemoteplayExports.cs @@ -0,0 +1,51 @@ +// Copyright (C) 2026 SharpEmu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +using SharpEmu.HLE; + +namespace SharpEmu.Libs.Remoteplay; + +// SharpEmu does not implement PS5 Remote Play. Titles still probe this API +// during startup (initialize + connection-status checks while bringing up +// pad/network subsystems). Without a handler they get ORBIS_GEN2_ERROR_NOT_FOUND +// instead of a real status code. Reporting a clean "initialized, not connected" +// state lets callers take their normal no-remote-play path. +public static class RemoteplayExports +{ + private const int StatusDisconnected = 0; + + [SysAbiExport( + Nid = "k1SwgkMSOM8", + ExportName = "sceRemoteplayInitialize", + Target = Generation.Gen5, + LibraryName = "libSceRemoteplay")] + public static int RemoteplayInitialize(CpuContext ctx) => SetReturn(ctx, 0); + + [SysAbiExport( + Nid = "g3PNjYKWqnQ", + ExportName = "sceRemoteplayGetConnectionStatus", + Target = Generation.Gen5, + LibraryName = "libSceRemoteplay")] + public static int RemoteplayGetConnectionStatus(CpuContext ctx) + { + var statusAddress = ctx[CpuRegister.Rsi]; + if (statusAddress != 0) + { + Span status = stackalloc byte[0x10]; + status.Clear(); + status[0] = StatusDisconnected; + if (!ctx.Memory.TryWrite(statusAddress, status)) + { + return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT); + } + } + + return SetReturn(ctx, 0); + } + + private static int SetReturn(CpuContext ctx, int result) + { + ctx[CpuRegister.Rax] = unchecked((ulong)result); + return result; + } +} diff --git a/tests/SharpEmu.Libs.Tests/Remoteplay/RemoteplayExportsTests.cs b/tests/SharpEmu.Libs.Tests/Remoteplay/RemoteplayExportsTests.cs new file mode 100644 index 00000000..9c0b6dbf --- /dev/null +++ b/tests/SharpEmu.Libs.Tests/Remoteplay/RemoteplayExportsTests.cs @@ -0,0 +1,58 @@ +// Copyright (C) 2026 SharpEmu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +using SharpEmu.HLE; +using SharpEmu.Libs.Remoteplay; +using Xunit; + +namespace SharpEmu.Libs.Tests.Remoteplay; + +public sealed class RemoteplayExportsTests +{ + private const ulong MemoryBase = 0x1_0000_0000; + private const ulong StatusAddress = MemoryBase + 0x100; + + private static CpuContext CreateContext(out FakeCpuMemory memory) + { + memory = new FakeCpuMemory(MemoryBase, 0x1000); + return new CpuContext(memory, Generation.Gen5); + } + + [Fact] + public void Initialize_Succeeds() + { + var ctx = CreateContext(out _); + + var result = RemoteplayExports.RemoteplayInitialize(ctx); + + Assert.Equal(0, result); + } + + [Fact] + public void GetConnectionStatus_WritesDisconnectedStatus() + { + var ctx = CreateContext(out var memory); + ctx[CpuRegister.Rdi] = 0x1000_0000; + ctx[CpuRegister.Rsi] = StatusAddress; + memory.TryWrite(StatusAddress, stackalloc byte[] { 0xFF, 0xFF, 0xFF, 0xFF }); + + var result = RemoteplayExports.RemoteplayGetConnectionStatus(ctx); + + Assert.Equal(0, result); + var status = new byte[4]; + Assert.True(ctx.Memory.TryRead(StatusAddress, status)); + Assert.Equal(0, status[0]); + } + + [Fact] + public void GetConnectionStatus_NullOutPointerStillSucceeds() + { + var ctx = CreateContext(out _); + ctx[CpuRegister.Rdi] = 0x1000_0000; + ctx[CpuRegister.Rsi] = 0; + + var result = RemoteplayExports.RemoteplayGetConnectionStatus(ctx); + + Assert.Equal(0, result); + } +}