mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-22 19:06:15 +08:00
Add SaveData transaction and NP UDS layout HLE stubs (#168)
* Add SaveData transaction and NP UDS layout HLE stubs Wire Prepare, Commit, and Umount2 for implicit save transactions, unregister guest mounts on Umount2, and add NP UDS CreateEvent, DestroyEvent, and EventPropertyObjectSetString for layout-load imports. * Add NP UDS SetArray and PostEvent layout HLE stubs Add sceNpUniversalDataSystemEventPropertyObjectSetArray and sceNpUniversalDataSystemPostEvent for layout-load imports on PPSA02929.
This commit is contained in:
@@ -200,6 +200,40 @@ public static class KernelMemoryCompatExports
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool TryUnregisterGuestPathMount(string guestMountPoint)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(guestMountPoint))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var normalizedMountPoint = NormalizeGuestStatCachePath(guestMountPoint);
|
||||||
|
if (normalizedMountPoint is null || normalizedMountPoint == "/")
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var removed = false;
|
||||||
|
lock (_guestMountGate)
|
||||||
|
{
|
||||||
|
removed = _guestMounts.Remove(normalizedMountPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!removed)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_statCacheGate)
|
||||||
|
{
|
||||||
|
_negativeStatCache.RemoveWhere(path =>
|
||||||
|
string.Equals(path, normalizedMountPoint, StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
path.StartsWith(normalizedMountPoint + "/", StringComparison.OrdinalIgnoreCase));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
internal static bool TryAllocateHleData(
|
internal static bool TryAllocateHleData(
|
||||||
CpuContext ctx,
|
CpuContext ctx,
|
||||||
ulong length,
|
ulong length,
|
||||||
|
|||||||
@@ -9,7 +9,10 @@ namespace SharpEmu.Libs.Np;
|
|||||||
public static class NpUniversalDataSystemExports
|
public static class NpUniversalDataSystemExports
|
||||||
{
|
{
|
||||||
private const int NpUniversalDataSystemErrorInvalidArgument = unchecked((int)0x80553102);
|
private const int NpUniversalDataSystemErrorInvalidArgument = unchecked((int)0x80553102);
|
||||||
|
private static readonly object _eventGate = new();
|
||||||
|
private static readonly HashSet<int> _createdEvents = [];
|
||||||
private static int _nextHandle = 1;
|
private static int _nextHandle = 1;
|
||||||
|
private static int _nextEvent = 1;
|
||||||
|
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
Nid = "sjaobBgqeB4",
|
Nid = "sjaobBgqeB4",
|
||||||
@@ -67,6 +70,114 @@ public static class NpUniversalDataSystemExports
|
|||||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT, typeof(long));
|
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT, typeof(long));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "p+GcLqwpL9M",
|
||||||
|
ExportName = "sceNpUniversalDataSystemCreateEvent",
|
||||||
|
Target = Generation.Gen4 | Generation.Gen5,
|
||||||
|
LibraryName = "libSceNpUniversalDataSystem")]
|
||||||
|
public static int NpUniversalDataSystemCreateEvent(CpuContext ctx)
|
||||||
|
{
|
||||||
|
var parameterAddress = ctx[CpuRegister.Rdi];
|
||||||
|
if (parameterAddress == 0)
|
||||||
|
{
|
||||||
|
return ctx.SetReturn(NpUniversalDataSystemErrorInvalidArgument, typeof(long));
|
||||||
|
}
|
||||||
|
|
||||||
|
var eventId = Interlocked.Increment(ref _nextEvent);
|
||||||
|
lock (_eventGate)
|
||||||
|
{
|
||||||
|
_createdEvents.Add(eventId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx.TryWriteInt32(ctx[CpuRegister.Rdx], eventId, checkNil: true) ||
|
||||||
|
ctx.TryWriteInt32(ctx[CpuRegister.Rcx], eventId, checkNil: true))
|
||||||
|
{
|
||||||
|
return ctx.SetReturn(0, typeof(long));
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_eventGate)
|
||||||
|
{
|
||||||
|
_createdEvents.Remove(eventId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT, typeof(long));
|
||||||
|
}
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "wG+84pnNIuo",
|
||||||
|
ExportName = "sceNpUniversalDataSystemDestroyEvent",
|
||||||
|
Target = Generation.Gen4 | Generation.Gen5,
|
||||||
|
LibraryName = "libSceNpUniversalDataSystem")]
|
||||||
|
public static int NpUniversalDataSystemDestroyEvent(CpuContext ctx)
|
||||||
|
{
|
||||||
|
var eventId = unchecked((int)ctx[CpuRegister.Rdi]);
|
||||||
|
lock (_eventGate)
|
||||||
|
{
|
||||||
|
_createdEvents.Remove(eventId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.SetReturn(0, typeof(long));
|
||||||
|
}
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "MfDb+4Nln64",
|
||||||
|
ExportName = "sceNpUniversalDataSystemEventPropertyObjectSetString",
|
||||||
|
Target = Generation.Gen4 | Generation.Gen5,
|
||||||
|
LibraryName = "libSceNpUniversalDataSystem")]
|
||||||
|
public static int NpUniversalDataSystemEventPropertyObjectSetString(CpuContext ctx)
|
||||||
|
{
|
||||||
|
var propertyObjectAddress = ctx[CpuRegister.Rsi];
|
||||||
|
var valueAddress = ctx[CpuRegister.Rdx];
|
||||||
|
if (propertyObjectAddress == 0 || valueAddress == 0)
|
||||||
|
{
|
||||||
|
return ctx.SetReturn(NpUniversalDataSystemErrorInvalidArgument, typeof(long));
|
||||||
|
}
|
||||||
|
|
||||||
|
Span<byte> probe = stackalloc byte[1];
|
||||||
|
return ctx.Memory.TryRead(propertyObjectAddress, probe) &&
|
||||||
|
ctx.Memory.TryRead(valueAddress, probe)
|
||||||
|
? ctx.SetReturn(0, typeof(long))
|
||||||
|
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT, typeof(long));
|
||||||
|
}
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "Wxbg5x3pTXA",
|
||||||
|
ExportName = "sceNpUniversalDataSystemEventPropertyObjectSetArray",
|
||||||
|
Target = Generation.Gen4 | Generation.Gen5,
|
||||||
|
LibraryName = "libSceNpUniversalDataSystem")]
|
||||||
|
public static int NpUniversalDataSystemEventPropertyObjectSetArray(CpuContext ctx)
|
||||||
|
{
|
||||||
|
var propertyObjectAddress = ctx[CpuRegister.Rsi];
|
||||||
|
var valueAddress = ctx[CpuRegister.Rdx];
|
||||||
|
if (propertyObjectAddress == 0)
|
||||||
|
{
|
||||||
|
return ctx.SetReturn(NpUniversalDataSystemErrorInvalidArgument, typeof(long));
|
||||||
|
}
|
||||||
|
|
||||||
|
Span<byte> probe = stackalloc byte[1];
|
||||||
|
if (!ctx.Memory.TryRead(propertyObjectAddress, probe))
|
||||||
|
{
|
||||||
|
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT, typeof(long));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valueAddress != 0 && !ctx.Memory.TryRead(valueAddress, probe))
|
||||||
|
{
|
||||||
|
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT, typeof(long));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.SetReturn(0, typeof(long));
|
||||||
|
}
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "CzkKf7ahIyU",
|
||||||
|
ExportName = "sceNpUniversalDataSystemPostEvent",
|
||||||
|
Target = Generation.Gen4 | Generation.Gen5,
|
||||||
|
LibraryName = "libSceNpUniversalDataSystem")]
|
||||||
|
public static int NpUniversalDataSystemPostEvent(CpuContext ctx)
|
||||||
|
{
|
||||||
|
return ctx.SetReturn(0, typeof(long));
|
||||||
|
}
|
||||||
|
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
Nid = "tpFJ8LIKvPw",
|
Nid = "tpFJ8LIKvPw",
|
||||||
ExportName = "sceNpUniversalDataSystemRegisterContext",
|
ExportName = "sceNpUniversalDataSystemRegisterContext",
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ public static class SaveDataExports
|
|||||||
private const int MountResultSize = 0x40;
|
private const int MountResultSize = 0x40;
|
||||||
private static readonly object _stateGate = new();
|
private static readonly object _stateGate = new();
|
||||||
private static readonly HashSet<int> _transactionResources = [];
|
private static readonly HashSet<int> _transactionResources = [];
|
||||||
|
private static readonly HashSet<int> _preparedTransactionResources = [];
|
||||||
private static string? _titleId;
|
private static string? _titleId;
|
||||||
private static int _nextTransactionResource;
|
private static int _nextTransactionResource;
|
||||||
|
|
||||||
@@ -40,6 +41,7 @@ public static class SaveDataExports
|
|||||||
{
|
{
|
||||||
_titleId = string.IsNullOrWhiteSpace(titleId) ? null : SanitizePathSegment(titleId.Trim());
|
_titleId = string.IsNullOrWhiteSpace(titleId) ? null : SanitizePathSegment(titleId.Trim());
|
||||||
_transactionResources.Clear();
|
_transactionResources.Clear();
|
||||||
|
_preparedTransactionResources.Clear();
|
||||||
_nextTransactionResource = 0;
|
_nextTransactionResource = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -278,12 +280,104 @@ public static class SaveDataExports
|
|||||||
lock (_stateGate)
|
lock (_stateGate)
|
||||||
{
|
{
|
||||||
_transactionResources.Remove(resource);
|
_transactionResources.Remove(resource);
|
||||||
|
_preparedTransactionResources.Remove(resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
TraceSaveData($"delete_transaction_resource resource={resource}");
|
TraceSaveData($"delete_transaction_resource resource={resource}");
|
||||||
return ctx.SetReturn(0);
|
return ctx.SetReturn(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "sDCBrmc61XU",
|
||||||
|
ExportName = "sceSaveDataPrepare",
|
||||||
|
Target = Generation.Gen4 | Generation.Gen5,
|
||||||
|
LibraryName = "libSceSaveData")]
|
||||||
|
public static int SaveDataPrepare(CpuContext ctx)
|
||||||
|
{
|
||||||
|
var mountPointAddress = ctx[CpuRegister.Rdi];
|
||||||
|
var resource = unchecked((int)ctx[CpuRegister.Rdx]);
|
||||||
|
if (mountPointAddress == 0)
|
||||||
|
{
|
||||||
|
return ctx.SetReturn(OrbisSaveDataErrorParameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TryReadFixedAscii(ctx, mountPointAddress, 16, out var mountPoint))
|
||||||
|
{
|
||||||
|
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(mountPoint))
|
||||||
|
{
|
||||||
|
return ctx.SetReturn(OrbisSaveDataErrorParameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_stateGate)
|
||||||
|
{
|
||||||
|
if (resource != 0)
|
||||||
|
{
|
||||||
|
_preparedTransactionResources.Add(resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceSaveData($"prepare mount_point={mountPoint} resource={resource}");
|
||||||
|
return ctx.SetReturn(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "ie7qhZ4X0Cc",
|
||||||
|
ExportName = "sceSaveDataCommit",
|
||||||
|
Target = Generation.Gen4 | Generation.Gen5,
|
||||||
|
LibraryName = "libSceSaveData")]
|
||||||
|
public static int SaveDataCommit(CpuContext ctx)
|
||||||
|
{
|
||||||
|
var commitAddress = ctx[CpuRegister.Rdi];
|
||||||
|
if (commitAddress == 0)
|
||||||
|
{
|
||||||
|
return ctx.SetReturn(OrbisSaveDataErrorParameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_stateGate)
|
||||||
|
{
|
||||||
|
_preparedTransactionResources.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceSaveData($"commit commit=0x{commitAddress:X16}");
|
||||||
|
return ctx.SetReturn(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "uW4vfTwMQVo",
|
||||||
|
ExportName = "sceSaveDataUmount2",
|
||||||
|
Target = Generation.Gen4 | Generation.Gen5,
|
||||||
|
LibraryName = "libSceSaveData")]
|
||||||
|
public static int SaveDataUmount2(CpuContext ctx)
|
||||||
|
{
|
||||||
|
var mountPointAddress = ctx[CpuRegister.Rdi];
|
||||||
|
if (mountPointAddress == 0)
|
||||||
|
{
|
||||||
|
mountPointAddress = ctx[CpuRegister.Rsi];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mountPointAddress == 0)
|
||||||
|
{
|
||||||
|
return ctx.SetReturn(OrbisSaveDataErrorParameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TryReadFixedAscii(ctx, mountPointAddress, 16, out var mountPoint))
|
||||||
|
{
|
||||||
|
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(mountPoint))
|
||||||
|
{
|
||||||
|
return ctx.SetReturn(OrbisSaveDataErrorParameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
var unmounted = KernelMemoryCompatExports.TryUnregisterGuestPathMount(mountPoint);
|
||||||
|
TraceSaveData($"umount2 mount_point={mountPoint} unregistered={unmounted}");
|
||||||
|
return ctx.SetReturn(0);
|
||||||
|
}
|
||||||
|
|
||||||
private static bool TryReadSearchCond(CpuContext ctx, ulong address, out SearchCond cond)
|
private static bool TryReadSearchCond(CpuContext ctx, ulong address, out SearchCond cond)
|
||||||
{
|
{
|
||||||
cond = default;
|
cond = default;
|
||||||
|
|||||||
Reference in New Issue
Block a user