From 4777ec45443126ecd9d6fe085a9acf1e7b5d6748 Mon Sep 17 00:00:00 2001 From: ParantezTech Date: Tue, 23 Jun 2026 19:27:24 +0300 Subject: [PATCH] [sceShare] initial share exports --- src/SharpEmu.Libs/Share/ShareExports.cs | 112 ++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 src/SharpEmu.Libs/Share/ShareExports.cs diff --git a/src/SharpEmu.Libs/Share/ShareExports.cs b/src/SharpEmu.Libs/Share/ShareExports.cs new file mode 100644 index 0000000..fa4e803 --- /dev/null +++ b/src/SharpEmu.Libs/Share/ShareExports.cs @@ -0,0 +1,112 @@ +// Copyright (C) 2026 SharpEmu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +using System.Text; +using System.Threading; +using SharpEmu.HLE; + +namespace SharpEmu.Libs.Share; + +public static class ShareExports +{ + private const int MaxContentParamBytes = 4096; + + private static int _initialized; + private static string _contentParam = string.Empty; + + [SysAbiExport( + Nid = "nBDD66kiFW8", + ExportName = "sceShareInitialize", + Target = Generation.Gen4 | Generation.Gen5, + LibraryName = "libSceShareUtility")] + public static int ShareInitialize(CpuContext ctx) + { + var memorySize = ctx[CpuRegister.Rdi]; + var priority = unchecked((int)ctx[CpuRegister.Rsi]); + var affinityMask = ctx[CpuRegister.Rdx]; + if (memorySize == 0) + { + return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT); + } + + Interlocked.Exchange(ref _initialized, 1); + + TraceShare($"initialize memory=0x{memorySize:X} priority={priority} affinity=0x{affinityMask:X}"); + return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK); + } + + [SysAbiExport( + Nid = "7QZtURYnXG4", + ExportName = "sceShareSetContentParam", + Target = Generation.Gen4 | Generation.Gen5, + LibraryName = "libSceShareUtility")] + public static int ShareSetContentParam(CpuContext ctx) + { + var contentParamAddress = ctx[CpuRegister.Rdi]; + if (contentParamAddress == 0) + { + return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT); + } + + if (!TryReadNullTerminatedUtf8(ctx, contentParamAddress, MaxContentParamBytes, out var contentParam)) + { + return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT); + } + + _contentParam = contentParam; + if (Volatile.Read(ref _initialized) == 0) + { + TraceShare("set_content_param before initialize"); + } + + TraceShare($"set_content_param len={contentParam.Length} preview='{FormatTraceString(contentParam)}'"); + return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK); + } + + private static int SetReturn(CpuContext ctx, OrbisGen2Result result) + { + ctx[CpuRegister.Rax] = unchecked((ulong)(int)result); + return (int)result; + } + + private static bool TryReadNullTerminatedUtf8(CpuContext ctx, ulong address, int maxLength, out string value) + { + Span bytes = stackalloc byte[maxLength]; + Span one = stackalloc byte[1]; + for (var index = 0; index < maxLength; index++) + { + if (!ctx.Memory.TryRead(address + (ulong)index, one)) + { + value = string.Empty; + return false; + } + + if (one[0] == 0) + { + value = Encoding.UTF8.GetString(bytes[..index]); + return true; + } + + bytes[index] = one[0]; + } + + value = string.Empty; + return false; + } + + private static string FormatTraceString(string value) + { + var normalized = value.Replace("\r", "\\r", StringComparison.Ordinal).Replace("\n", "\\n", StringComparison.Ordinal); + return normalized.Length <= 120 ? normalized : string.Concat(normalized.AsSpan(0, 120), "..."); + } + + private static void TraceShare(string message) + { + if (!string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_SHARE"), "1", StringComparison.Ordinal)) + { + return; + } + + Console.Error.WriteLine($"[LOADER][TRACE] share.{message}"); + } +}