mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-25 20:28:48 +08:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fc69bbd8fb | |||
| 4c84958834 | |||
| fbf3377f65 | |||
| a63a7de35a | |||
| fe1a592496 | |||
| 9d4dad2c24 | |||
| 38d94d8e54 | |||
| 61a1abdb13 | |||
| 88452218cb | |||
| 427a511e48 | |||
| e103795768 | |||
| 24c6e44800 | |||
| 3a30d6419f | |||
| 2d8a795f23 | |||
| adb2acdb39 | |||
| 5e5bead02c | |||
| e98f02966a | |||
| 9ab1e2f21a | |||
| 340eb9e0ab | |||
| 03e3d25e85 | |||
| 2bc466b10e | |||
| 45d0be474b | |||
| 5bb91eff9b | |||
| f4df0ed4bd | |||
| 23691a2bdc | |||
| 5c5ed8c064 | |||
| a4748ce266 | |||
| 5617646c8a | |||
| a4f3d3cd7f | |||
| 92526ecacf |
@@ -37,5 +37,3 @@ packages/
|
||||
Thumbs.db
|
||||
Desktop.ini
|
||||
ehthumbs.db
|
||||
|
||||
.vs/
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "10.0.103",
|
||||
"rollForward": "latestFeature"
|
||||
"rollForward": "disable"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,10 +38,6 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||
<Win32Icon>..\..\assets\images\SharpEmu.ico</Win32Icon>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<NoWarn>$(NoWarn);1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="..\..\LICENSE.txt">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
"net10.0": {
|
||||
"Microsoft.NET.ILLink.Tasks": {
|
||||
"type": "Direct",
|
||||
"requested": "[10.0.9, )",
|
||||
"resolved": "10.0.9",
|
||||
"contentHash": "4Iw41e2h7I4t70SJcX2GCmbyKJIlA273Cfm9RJMM050/3VBejGAG1KcthP5Z2L6SQcbfbf6BhNWO26+ZG+GzMg=="
|
||||
"requested": "[10.0.3, )",
|
||||
"resolved": "10.0.3",
|
||||
"contentHash": "0B6nZyCHWXnvmlB559oduOspVdNOnpNXPjhpWVMovLPAsDVG7A4jJR9rzECf67JUzxP8/ee/wA8clwIzJcWNFA=="
|
||||
},
|
||||
"Avalonia.Angle.Windows.Natives": {
|
||||
"type": "Transitive",
|
||||
|
||||
@@ -415,8 +415,8 @@ public sealed class CpuDispatcher : ICpuDispatcher, IDisposable
|
||||
|
||||
const ulong entryParamsSize = 0x20;
|
||||
var entryParamsAddress = AlignDown(argv0Address - entryParamsSize, 16);
|
||||
if (!context.TryWriteUInt32(entryParamsAddress + 0x00, 1) ||
|
||||
!context.TryWriteUInt32(entryParamsAddress + 0x04, 0) ||
|
||||
if (!TryWriteUInt32(context, entryParamsAddress + 0x00, 1) ||
|
||||
!TryWriteUInt32(context, entryParamsAddress + 0x04, 0) ||
|
||||
!context.TryWriteUInt64(entryParamsAddress + 0x08, argv0Address) ||
|
||||
!context.TryWriteUInt64(entryParamsAddress + 0x10, 0) ||
|
||||
!context.TryWriteUInt64(entryParamsAddress + 0x18, 0))
|
||||
@@ -456,6 +456,13 @@ public sealed class CpuDispatcher : ICpuDispatcher, IDisposable
|
||||
return value & ~(alignment - 1);
|
||||
}
|
||||
|
||||
private static bool TryWriteUInt32(CpuContext context, ulong address, uint value)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[sizeof(uint)];
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer, value);
|
||||
return context.Memory.TryWrite(address, buffer);
|
||||
}
|
||||
|
||||
private static string BuildEntryFrameDiagnostic(
|
||||
ulong entryPoint,
|
||||
CpuContext context,
|
||||
|
||||
@@ -358,21 +358,12 @@ public sealed partial class DirectExecutionBackend
|
||||
}
|
||||
orbisGen2Result = (OrbisGen2Result)returnValue;
|
||||
}
|
||||
else if (importStubEntry.Export is { } mismatchedExport)
|
||||
{
|
||||
dispatchResolved = true;
|
||||
orbisGen2Result = OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_IMPLEMENTED;
|
||||
cpuContext[CpuRegister.Rax] = unchecked((ulong)(int)orbisGen2Result);
|
||||
Console.Error.WriteLine(
|
||||
$"[LOADER][WARN] Import#{num} not implemented for generation {cpuContext.TargetGeneration}: " +
|
||||
$"nid={importStubEntry.Nid} targets={mismatchedExport.Target} ret=0x{num7:X16}");
|
||||
}
|
||||
else
|
||||
{
|
||||
dispatchResolved = false;
|
||||
orbisGen2Result = OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
||||
cpuContext[CpuRegister.Rax] = unchecked((ulong)(int)orbisGen2Result);
|
||||
}
|
||||
else
|
||||
{
|
||||
dispatchResolved = false;
|
||||
orbisGen2Result = OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
||||
cpuContext[CpuRegister.Rax] = unchecked((ulong)(int)orbisGen2Result);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -1679,14 +1679,10 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
ptr2[num++] = 65;
|
||||
ptr2[num++] = 95;
|
||||
ptr2[num++] = 195;
|
||||
uint num2 = default(uint);
|
||||
if (!VirtualProtect(ptr, 192u, 32u, &num2))
|
||||
{
|
||||
Console.Error.WriteLine($"[LOADER][ERROR] VirtualProtect failed for import dispatch stub at 0x{(nint)ptr:X16}");
|
||||
return 0;
|
||||
}
|
||||
FlushInstructionCache(GetCurrentProcess(), ptr, 192u);
|
||||
return (nint)ptr;
|
||||
uint num2 = default(uint);
|
||||
VirtualProtect(ptr, 192u, 32u, &num2);
|
||||
FlushInstructionCache(GetCurrentProcess(), ptr, 192u);
|
||||
return (nint)ptr;
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -1767,11 +1763,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
tlsHandlerAddress[num++] = 195;
|
||||
_tlsPatchStubOffset = (num + 15) & ~15;
|
||||
uint num2 = default(uint);
|
||||
if (!VirtualProtect((void*)_tlsHandlerAddress, TlsHandlerRegionSize, 32u, &num2))
|
||||
{
|
||||
Console.Error.WriteLine($"[LOADER][ERROR] VirtualProtect failed for TLS handler at 0x{_tlsHandlerAddress:X16}");
|
||||
return;
|
||||
}
|
||||
VirtualProtect((void*)_tlsHandlerAddress, TlsHandlerRegionSize, 32u, &num2);
|
||||
FlushInstructionCache(GetCurrentProcess(), (void*)_tlsHandlerAddress, TlsHandlerRegionSize);
|
||||
Console.Error.WriteLine($"[LOADER][INFO] TLS handler at 0x{_tlsHandlerAddress:X16}");
|
||||
}
|
||||
@@ -1792,11 +1784,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
ptr2[i] = 144;
|
||||
}
|
||||
uint num = default(uint);
|
||||
if (!VirtualProtect(ptr, 4096u, 32u, &num))
|
||||
{
|
||||
Console.Error.WriteLine($"[LOADER][ERROR] VirtualProtect failed for unresolved return stub at 0x{(nint)ptr:X16}");
|
||||
return 0;
|
||||
}
|
||||
VirtualProtect(ptr, 4096u, 32u, &num);
|
||||
FlushInstructionCache(GetCurrentProcess(), ptr, 16u);
|
||||
return (nint)ptr;
|
||||
}
|
||||
@@ -1843,11 +1831,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
EmitByte(code, ref offset, 0xC3);
|
||||
|
||||
uint oldProtect = default;
|
||||
if (!VirtualProtect(ptr, stubSize, 32u, &oldProtect))
|
||||
{
|
||||
Console.Error.WriteLine($"[LOADER][ERROR] VirtualProtect failed for guest return stub at 0x{(nint)ptr:X16}");
|
||||
return 0;
|
||||
}
|
||||
VirtualProtect(ptr, stubSize, 32u, &oldProtect);
|
||||
FlushInstructionCache(GetCurrentProcess(), ptr, (nuint)offset);
|
||||
return (nint)ptr;
|
||||
}
|
||||
@@ -1939,11 +1923,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
*(int*)(code + guestRestoreJump) = restoreOffset - (guestRestoreJump + sizeof(int));
|
||||
|
||||
uint oldProtect = default;
|
||||
if (!VirtualProtect(ptr, stubSize, 32u, &oldProtect))
|
||||
{
|
||||
Console.Error.WriteLine($"[LOADER][ERROR] VirtualProtect failed for exception handler trampoline at 0x{(nint)ptr:X16}");
|
||||
return 0;
|
||||
}
|
||||
VirtualProtect(ptr, stubSize, 32u, &oldProtect);
|
||||
FlushInstructionCache(GetCurrentProcess(), ptr, (nuint)offset);
|
||||
return (nint)ptr;
|
||||
}
|
||||
@@ -2274,11 +2254,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
ptr[num2++] = 144;
|
||||
}
|
||||
uint flNewProtect = default(uint);
|
||||
if (!VirtualProtect((void*)num, 32u, 32u, &flNewProtect))
|
||||
{
|
||||
Console.Error.WriteLine($"[LOADER][ERROR] VirtualProtect failed for TLS store helper at 0x{num:X16}");
|
||||
return 0;
|
||||
}
|
||||
VirtualProtect((void*)num, 32u, 32u, &flNewProtect);
|
||||
FlushInstructionCache(GetCurrentProcess(), (void*)num, 32u);
|
||||
return num;
|
||||
}
|
||||
@@ -3615,11 +3591,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
return GuestNativeCallExitReason.Exception;
|
||||
}
|
||||
uint oldProtect = default(uint);
|
||||
if (!VirtualProtect(ptr, stubSize, 64u, &oldProtect))
|
||||
{
|
||||
reason = $"VirtualProtect failed for guest thread entry stub at 0x{(nint)ptr:X16}";
|
||||
return GuestNativeCallExitReason.Exception;
|
||||
}
|
||||
VirtualProtect(ptr, stubSize, 64u, &oldProtect);
|
||||
FlushInstructionCache(GetCurrentProcess(), ptr, stubSize);
|
||||
TlsSetValue(_hostRspSlotTlsIndex, (nint)hostRspSlot);
|
||||
ActiveGuestThreadYieldRequested = false;
|
||||
@@ -3756,11 +3728,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
return GuestNativeCallExitReason.Exception;
|
||||
}
|
||||
uint oldProtect = default(uint);
|
||||
if (!VirtualProtect(ptr, stubSize, 64u, &oldProtect))
|
||||
{
|
||||
reason = $"VirtualProtect failed for guest continuation stub at 0x{(nint)ptr:X16}";
|
||||
return GuestNativeCallExitReason.Exception;
|
||||
}
|
||||
VirtualProtect(ptr, stubSize, 64u, &oldProtect);
|
||||
FlushInstructionCache(GetCurrentProcess(), ptr, stubSize);
|
||||
TlsSetValue(_hostRspSlotTlsIndex, (nint)hostRspSlot);
|
||||
ActiveGuestThreadYieldRequested = false;
|
||||
@@ -4020,12 +3988,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
return false;
|
||||
}
|
||||
uint num5 = default(uint);
|
||||
if (!VirtualProtect(ptr, stubSize, 64u, &num5))
|
||||
{
|
||||
LastError = $"VirtualProtect failed for guest entry stub at 0x{(nint)ptr:X16}";
|
||||
result = OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
return false;
|
||||
}
|
||||
VirtualProtect(ptr, stubSize, 64u, &num5);
|
||||
FlushInstructionCache(GetCurrentProcess(), ptr, stubSize);
|
||||
if (_hostRspSlotStorage != 0)
|
||||
{
|
||||
|
||||
@@ -55,11 +55,7 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
||||
private static extern nuint VirtualQuery(void* lpAddress, out MemoryBasicInformation64 lpBuffer, nuint dwLength);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern void* GetCurrentProcess();
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool FlushInstructionCache(void* hProcess, void* lpBaseAddress, nuint dwSize);
|
||||
private static extern void FlushInstructionCache(void* hProcess, void* lpBaseAddress, nuint dwSize);
|
||||
|
||||
public bool TryAllocateAtExact(ulong desiredAddress, ulong size, bool executable, out ulong actualAddress)
|
||||
{
|
||||
@@ -455,7 +451,7 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
||||
|
||||
if ((flags & ProgramHeaderFlags.Execute) != 0)
|
||||
{
|
||||
FlushInstructionCache(GetCurrentProcess(), (void*)address, (nuint)size);
|
||||
FlushInstructionCache(null, (void*)address, (nuint)size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -703,7 +699,7 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
||||
VirtualProtect(destPtr, (nuint)source.Length, oldProtect, out _);
|
||||
if (IsExecutableProtection(oldProtect))
|
||||
{
|
||||
FlushInstructionCache(GetCurrentProcess(), destPtr, (nuint)source.Length);
|
||||
FlushInstructionCache(null, destPtr, (nuint)source.Length);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<!--
|
||||
<!--
|
||||
Copyright (C) 2026 SharpEmu Emulator Project
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
-->
|
||||
@@ -14,10 +14,6 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||
<PackageReference Include="Iced" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<NoWarn>$(NoWarn);1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -2,20 +2,25 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
using System.Buffers.Binary;
|
||||
using System.Text;
|
||||
|
||||
namespace SharpEmu.HLE;
|
||||
|
||||
public sealed class CpuContext(ICpuMemory memory, Generation generation)
|
||||
public sealed class CpuContext
|
||||
{
|
||||
private readonly ulong[] _registers = new ulong[16];
|
||||
private readonly ulong[] _xmmRegisters = new ulong[32];
|
||||
private readonly ulong[] _ymmUpperRegisters = new ulong[32];
|
||||
private bool _raxWritten;
|
||||
|
||||
public ICpuMemory Memory { get; } = memory ?? throw new ArgumentNullException(nameof(memory));
|
||||
public CpuContext(ICpuMemory memory, Generation generation)
|
||||
{
|
||||
Memory = memory ?? throw new ArgumentNullException(nameof(memory));
|
||||
TargetGeneration = generation;
|
||||
}
|
||||
|
||||
public Generation TargetGeneration { get; } = generation;
|
||||
public ICpuMemory Memory { get; }
|
||||
|
||||
public Generation TargetGeneration { get; }
|
||||
|
||||
public ulong Rip { get; set; }
|
||||
|
||||
@@ -125,91 +130,6 @@ public sealed class CpuContext(ICpuMemory memory, Generation generation)
|
||||
SetYmmUpper(registerIndex, highLow, highHigh);
|
||||
}
|
||||
|
||||
public bool TryReadByte(ulong address, out byte value)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[1];
|
||||
if (!Memory.TryRead(address, buffer))
|
||||
{
|
||||
value = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = buffer[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryReadUInt16(ulong address, out ushort value)
|
||||
{
|
||||
Span<byte> bytes = stackalloc byte[sizeof(ushort)];
|
||||
if (!Memory.TryRead(address, bytes))
|
||||
{
|
||||
value = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = BinaryPrimitives.ReadUInt16LittleEndian(bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryWriteUInt16(ulong address, ushort value)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[sizeof(ushort)];
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(buffer, value);
|
||||
return Memory.TryWrite(address, buffer);
|
||||
}
|
||||
|
||||
public bool TryReadInt32(ulong address, out int value)
|
||||
{
|
||||
Span<byte> bytes = stackalloc byte[sizeof(int)];
|
||||
if (!Memory.TryRead(address, bytes))
|
||||
{
|
||||
value = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = BinaryPrimitives.ReadInt32LittleEndian(bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryWriteInt32(ulong address, int value, bool checkNil = false)
|
||||
{
|
||||
if (checkNil && address == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Span<byte> bytes = stackalloc byte[sizeof(int)];
|
||||
BinaryPrimitives.WriteInt32LittleEndian(bytes, value);
|
||||
return Memory.TryWrite(address, bytes);
|
||||
}
|
||||
|
||||
public bool TryReadUInt32(ulong address, out uint value)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[sizeof(uint)];
|
||||
if (!Memory.TryRead(address, buffer))
|
||||
{
|
||||
value = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = BinaryPrimitives.ReadUInt32LittleEndian(buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryWriteUInt32(ulong address, uint value)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[sizeof(uint)];
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer, value);
|
||||
return Memory.TryWrite(address, buffer);
|
||||
}
|
||||
|
||||
public bool TryWriteInt64(ulong address, long value)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[sizeof(long)];
|
||||
BinaryPrimitives.WriteInt64LittleEndian(buffer, value);
|
||||
return Memory.TryWrite(address, buffer);
|
||||
}
|
||||
|
||||
public bool TryReadUInt64(ulong address, out ulong value)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[sizeof(ulong)];
|
||||
@@ -230,33 +150,6 @@ public sealed class CpuContext(ICpuMemory memory, Generation generation)
|
||||
return Memory.TryWrite(address, buffer);
|
||||
}
|
||||
|
||||
public bool TryReadNullTerminatedUtf8(ulong address, int capacity, out string value)
|
||||
{
|
||||
value = string.Empty;
|
||||
if (address == 0 || capacity <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var bytes = new byte[capacity];
|
||||
for (var index = 0; index < bytes.Length; index++)
|
||||
{
|
||||
if (!Memory.TryRead(address + (ulong)index, bytes.AsSpan(index, 1)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bytes[index] == 0)
|
||||
{
|
||||
value = Encoding.UTF8.GetString(bytes, 0, index);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
value = Encoding.UTF8.GetString(bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool PushUInt64(ulong value)
|
||||
{
|
||||
var rsp = this[CpuRegister.Rsp];
|
||||
@@ -276,23 +169,4 @@ public sealed class CpuContext(ICpuMemory memory, Generation generation)
|
||||
this[CpuRegister.Rsp] = rsp + sizeof(ulong);
|
||||
return true;
|
||||
}
|
||||
|
||||
public int SetReturn(int result, Type? cast = null)
|
||||
{
|
||||
var value = cast switch
|
||||
{
|
||||
null => (ulong)result,
|
||||
_ when cast == typeof(long) => (ulong)(long)result,
|
||||
_ => throw new NotSupportedException(),
|
||||
};
|
||||
|
||||
this[CpuRegister.Rax] = unchecked(value);
|
||||
return result;
|
||||
}
|
||||
|
||||
public int SetReturn(OrbisGen2Result result)
|
||||
{
|
||||
this[CpuRegister.Rax] = unchecked((ulong)(int)result);
|
||||
return (int)result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,6 @@ public sealed class ModuleManager : IModuleManager
|
||||
var handler = CreateHandler(type, method, instances);
|
||||
if (!_dispatchTable.TryAdd(exportInfo.Value.Nid, handler))
|
||||
{
|
||||
Console.Error.WriteLine($"[HLE] Duplicate NID '{exportInfo.Value.Nid}' ({exportInfo.Value.ExportName}) — already registered, skipping.");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -106,7 +105,6 @@ public sealed class ModuleManager : IModuleManager
|
||||
|
||||
if (!_dispatchTable.TryGetValue(nid, out var function) || !_exportTable.TryGetValue(nid, out var export))
|
||||
{
|
||||
Console.Error.WriteLine($"[HLE] NID '{nid}' not found in dispatch table.");
|
||||
context[CpuRegister.Rax] = unchecked((ulong)(int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
|
||||
result = OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
||||
return false;
|
||||
@@ -114,13 +112,11 @@ public sealed class ModuleManager : IModuleManager
|
||||
|
||||
if ((export.Target & context.TargetGeneration) == 0)
|
||||
{
|
||||
Console.Error.WriteLine($"[HLE] NID '{nid}' ({export.Name}) found but not implemented for generation {context.TargetGeneration} (targets: {export.Target}).");
|
||||
context[CpuRegister.Rax] = unchecked((ulong)(int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_IMPLEMENTED);
|
||||
result = OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_IMPLEMENTED;
|
||||
context[CpuRegister.Rax] = unchecked((ulong)(int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
|
||||
result = OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
context.ClearRaxWriteFlag();
|
||||
int ret = ((SysAbiFunction)function).Invoke(context);
|
||||
|
||||
|
||||
@@ -8,10 +8,6 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<NoWarn>$(NoWarn);1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Aerolib\aerolib.bin" />
|
||||
</ItemGroup>
|
||||
|
||||
+415
-361
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
using SharpEmu.HLE;
|
||||
using System.Buffers.Binary;
|
||||
|
||||
namespace SharpEmu.Libs.Agc;
|
||||
|
||||
@@ -17,9 +18,9 @@ internal static class Gen5ShaderMetadataReader
|
||||
out Gen5ShaderMetadata metadata)
|
||||
{
|
||||
metadata = default!;
|
||||
if (!ctx.TryReadUInt64(shaderHeaderAddress + ShaderUserDataOffset, out var userDataAddress) ||
|
||||
if (!TryReadUInt64(ctx, shaderHeaderAddress + ShaderUserDataOffset, out var userDataAddress) ||
|
||||
userDataAddress == 0 ||
|
||||
!ctx.TryReadUInt64(userDataAddress, out var directResourceOffsetsAddress))
|
||||
!TryReadUInt64(ctx, userDataAddress, out var directResourceOffsetsAddress))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -27,7 +28,8 @@ internal static class Gen5ShaderMetadataReader
|
||||
var resourceOffsets = new ulong[ResourceClassCount];
|
||||
for (var resourceClass = 0; resourceClass < ResourceClassCount; resourceClass++)
|
||||
{
|
||||
if (!ctx.TryReadUInt64(
|
||||
if (!TryReadUInt64(
|
||||
ctx,
|
||||
userDataAddress + 0x08 + (ulong)(resourceClass * sizeof(ulong)),
|
||||
out resourceOffsets[resourceClass]))
|
||||
{
|
||||
@@ -35,9 +37,9 @@ internal static class Gen5ShaderMetadataReader
|
||||
}
|
||||
}
|
||||
|
||||
if (!ctx.TryReadUInt16(userDataAddress + 0x28, out var extendedUserDataSize) ||
|
||||
!ctx.TryReadUInt16(userDataAddress + 0x2A, out var shaderResourceTableSize) ||
|
||||
!ctx.TryReadUInt16(userDataAddress + 0x2C, out var directResourceCount) ||
|
||||
if (!TryReadUInt16(ctx, userDataAddress + 0x28, out var extendedUserDataSize) ||
|
||||
!TryReadUInt16(ctx, userDataAddress + 0x2A, out var shaderResourceTableSize) ||
|
||||
!TryReadUInt16(ctx, userDataAddress + 0x2C, out var directResourceCount) ||
|
||||
directResourceCount > MaxMetadataEntries)
|
||||
{
|
||||
return false;
|
||||
@@ -46,7 +48,8 @@ internal static class Gen5ShaderMetadataReader
|
||||
var resourceCounts = new ushort[ResourceClassCount];
|
||||
for (var resourceClass = 0; resourceClass < ResourceClassCount; resourceClass++)
|
||||
{
|
||||
if (!ctx.TryReadUInt16(
|
||||
if (!TryReadUInt16(
|
||||
ctx,
|
||||
userDataAddress + 0x2E + (ulong)(resourceClass * sizeof(ushort)),
|
||||
out resourceCounts[resourceClass]) ||
|
||||
resourceCounts[resourceClass] > MaxMetadataEntries)
|
||||
@@ -65,7 +68,7 @@ internal static class Gen5ShaderMetadataReader
|
||||
|
||||
for (uint type = 0; type < directResourceCount; type++)
|
||||
{
|
||||
if (!ctx.TryReadUInt16(directResourceOffsetsAddress + type * sizeof(ushort), out var offset))
|
||||
if (!TryReadUInt16(ctx, directResourceOffsetsAddress + type * sizeof(ushort), out var offset))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -93,7 +96,8 @@ internal static class Gen5ShaderMetadataReader
|
||||
|
||||
for (uint slot = 0; slot < count; slot++)
|
||||
{
|
||||
if (!ctx.TryReadUInt16(
|
||||
if (!TryReadUInt16(
|
||||
ctx,
|
||||
resourceOffsets[resourceClass] + slot * sizeof(ushort),
|
||||
out var sharp))
|
||||
{
|
||||
@@ -121,4 +125,30 @@ internal static class Gen5ShaderMetadataReader
|
||||
resources);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryReadUInt16(CpuContext ctx, ulong address, out ushort value)
|
||||
{
|
||||
Span<byte> bytes = stackalloc byte[sizeof(ushort)];
|
||||
if (!ctx.Memory.TryRead(address, bytes))
|
||||
{
|
||||
value = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = BinaryPrimitives.ReadUInt16LittleEndian(bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryReadUInt64(CpuContext ctx, ulong address, out ulong value)
|
||||
{
|
||||
Span<byte> bytes = stackalloc byte[sizeof(ulong)];
|
||||
if (!ctx.Memory.TryRead(address, bytes))
|
||||
{
|
||||
value = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = BinaryPrimitives.ReadUInt64LittleEndian(bytes);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using SharpEmu.HLE;
|
||||
using SharpEmu.Libs.Kernel;
|
||||
using System.Buffers.Binary;
|
||||
using System.Numerics;
|
||||
|
||||
namespace SharpEmu.Libs.Agc;
|
||||
@@ -1404,7 +1405,8 @@ internal static class Gen5ShaderScalarEvaluator
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ctx.TryReadUInt32(
|
||||
if (!TryReadUInt32(
|
||||
ctx,
|
||||
address + (ulong)(index * sizeof(uint)),
|
||||
out var value) &&
|
||||
!TryReadUserDataScalarLoad(
|
||||
@@ -1725,4 +1727,16 @@ internal static class Gen5ShaderScalarEvaluator
|
||||
opcode.StartsWith("ImageSample", StringComparison.Ordinal) ||
|
||||
opcode.StartsWith("ImageGather", StringComparison.Ordinal);
|
||||
|
||||
private static bool TryReadUInt32(CpuContext ctx, ulong address, out uint value)
|
||||
{
|
||||
Span<byte> bytes = stackalloc byte[sizeof(uint)];
|
||||
if (!ctx.Memory.TryRead(address, bytes))
|
||||
{
|
||||
value = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = BinaryPrimitives.ReadUInt32LittleEndian(bytes);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,7 +294,7 @@ internal static class Gen5ShaderTranslator
|
||||
var instructionCount = 0;
|
||||
for (uint pc = 0; instructionCount < MaxInstructions;)
|
||||
{
|
||||
if (!ctx.TryReadUInt32(address + pc, out var word))
|
||||
if (!TryReadUInt32(ctx, address + pc, out var word))
|
||||
{
|
||||
error = $"read-failed pc=0x{pc:X}";
|
||||
return false;
|
||||
@@ -317,7 +317,7 @@ internal static class Gen5ShaderTranslator
|
||||
words[0] = word;
|
||||
for (uint wordIndex = 1; wordIndex < sizeDwords; wordIndex++)
|
||||
{
|
||||
if (!ctx.TryReadUInt32(address + pc + wordIndex * sizeof(uint), out words[wordIndex]))
|
||||
if (!TryReadUInt32(ctx, address + pc + wordIndex * sizeof(uint), out words[wordIndex]))
|
||||
{
|
||||
error = $"read-failed pc=0x{pc + wordIndex * sizeof(uint):X}";
|
||||
return false;
|
||||
@@ -397,7 +397,7 @@ internal static class Gen5ShaderTranslator
|
||||
case 0x34:
|
||||
case 0x35:
|
||||
encoding = Gen5ShaderEncoding.Vop3;
|
||||
if (!ctx.TryReadUInt32(baseAddress + pc + sizeof(uint), out var vop3Extra))
|
||||
if (!TryReadUInt32(ctx, baseAddress + pc + sizeof(uint), out var vop3Extra))
|
||||
{
|
||||
error = $"vop3-extra-read-failed pc=0x{pc:X}";
|
||||
return false;
|
||||
@@ -418,7 +418,7 @@ internal static class Gen5ShaderTranslator
|
||||
return DecodeFlat(word, out name, out sizeDwords, out error);
|
||||
case 0x38:
|
||||
encoding = Gen5ShaderEncoding.Mubuf;
|
||||
if (!ctx.TryReadUInt32(baseAddress + pc + sizeof(uint), out var mubufExtra))
|
||||
if (!TryReadUInt32(ctx, baseAddress + pc + sizeof(uint), out var mubufExtra))
|
||||
{
|
||||
error = $"mubuf-extra-read-failed pc=0x{pc:X}";
|
||||
return false;
|
||||
@@ -427,7 +427,7 @@ internal static class Gen5ShaderTranslator
|
||||
return DecodeMubuf(word, mubufExtra, out name, out sizeDwords, out error);
|
||||
case 0x3A:
|
||||
encoding = Gen5ShaderEncoding.Mtbuf;
|
||||
if (!ctx.TryReadUInt32(baseAddress + pc + sizeof(uint), out var mtbufExtra))
|
||||
if (!TryReadUInt32(ctx, baseAddress + pc + sizeof(uint), out var mtbufExtra))
|
||||
{
|
||||
error = $"mtbuf-extra-read-failed pc=0x{pc:X}";
|
||||
return false;
|
||||
@@ -1741,6 +1741,19 @@ internal static class Gen5ShaderTranslator
|
||||
counts[key] = count + 1;
|
||||
}
|
||||
|
||||
private static bool TryReadUInt32(CpuContext ctx, ulong address, out uint value)
|
||||
{
|
||||
Span<byte> bytes = stackalloc byte[sizeof(uint)];
|
||||
if (!ctx.Memory.TryRead(address, bytes))
|
||||
{
|
||||
value = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = BinaryPrimitives.ReadUInt32LittleEndian(bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
private readonly record struct ShaderDecodeInfo(
|
||||
int InstructionCount,
|
||||
Dictionary<string, int> Counts,
|
||||
|
||||
@@ -453,7 +453,7 @@ public static class AmprExports
|
||||
var offset = 0UL;
|
||||
while (offset < writeOffset)
|
||||
{
|
||||
if (!ctx.TryReadUInt32(buffer + offset, out var recordType))
|
||||
if (!TryReadUInt32(ctx, buffer + offset, out var recordType))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
@@ -883,6 +883,19 @@ public static class AmprExports
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryReadUInt32(CpuContext ctx, ulong address, out uint value)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[sizeof(uint)];
|
||||
if (!ctx.Memory.TryRead(address, buffer))
|
||||
{
|
||||
value = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = BinaryPrimitives.ReadUInt32LittleEndian(buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void TraceAmpr(CpuContext ctx, string operation, ulong commandBuffer, ulong arg0, ulong arg1)
|
||||
{
|
||||
if (!_traceAmpr)
|
||||
|
||||
@@ -73,7 +73,7 @@ public static class AppContentExports
|
||||
var valueAddress = ctx[CpuRegister.Rsi];
|
||||
if (valueAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
int value;
|
||||
@@ -83,18 +83,18 @@ public static class AppContentExports
|
||||
}
|
||||
else if (!TryReadUserDefinedParam(paramId, out value))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
Span<byte> valueBytes = stackalloc byte[sizeof(int)];
|
||||
BinaryPrimitives.WriteInt32LittleEndian(valueBytes, value);
|
||||
if (!ctx.Memory.TryWrite(valueAddress, valueBytes))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
TraceAppContent($"app_param_get_int id={paramId} value={value}");
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -168,6 +168,12 @@ public static class AppContentExports
|
||||
}
|
||||
}
|
||||
|
||||
private static int SetReturn(CpuContext ctx, OrbisGen2Result result)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)(int)result);
|
||||
return (int)result;
|
||||
}
|
||||
|
||||
private static void TraceAppContent(string message)
|
||||
{
|
||||
if (!string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_APP_CONTENT"), "1", StringComparison.Ordinal))
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using SharpEmu.HLE;
|
||||
using System.Buffers.Binary;
|
||||
using System.Threading;
|
||||
|
||||
namespace SharpEmu.Libs.Audio;
|
||||
|
||||
@@ -36,7 +37,7 @@ public static class AudioOut2Exports
|
||||
var paramAddress = ctx[CpuRegister.Rdi];
|
||||
if (paramAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
Span<byte> param = stackalloc byte[AudioOut2ContextParamSize];
|
||||
@@ -47,8 +48,8 @@ public static class AudioOut2Exports
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(param[0x0C..], 0x400);
|
||||
|
||||
return ctx.Memory.TryWrite(paramAddress, param)
|
||||
? ctx.SetReturn(0)
|
||||
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -62,7 +63,7 @@ public static class AudioOut2Exports
|
||||
var memoryInfoAddress = ctx[CpuRegister.Rsi];
|
||||
if (paramAddress == 0 || memoryInfoAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
Span<byte> memoryInfo = stackalloc byte[0x20];
|
||||
@@ -73,8 +74,8 @@ public static class AudioOut2Exports
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(memoryInfo[0x18..], AudioOut2ContextMemoryAlignment);
|
||||
|
||||
return ctx.Memory.TryWrite(memoryInfoAddress, memoryInfo)
|
||||
? ctx.SetReturn(0)
|
||||
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -90,13 +91,13 @@ public static class AudioOut2Exports
|
||||
var outContextAddress = ctx[CpuRegister.Rcx];
|
||||
if (paramAddress == 0 || memoryAddress == 0 || memorySize == 0 || outContextAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
var handle = (ulong)Interlocked.Increment(ref _nextContextHandle);
|
||||
return ctx.TryWriteUInt64(outContextAddress, handle)
|
||||
? ctx.SetReturn(0)
|
||||
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return TryWriteUInt64(ctx, outContextAddress, handle)
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -104,7 +105,7 @@ public static class AudioOut2Exports
|
||||
ExportName = "sceAudioOut2ContextDestroy",
|
||||
Target = Generation.Gen5,
|
||||
LibraryName = "libSceAudioOut2")]
|
||||
public static int AudioOut2ContextDestroy(CpuContext ctx) => ctx.SetReturn(0);
|
||||
public static int AudioOut2ContextDestroy(CpuContext ctx) => SetReturn(ctx, 0);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "JK2wamZPzwM",
|
||||
@@ -119,14 +120,14 @@ public static class AudioOut2Exports
|
||||
var contextAddress = ctx[CpuRegister.Rcx];
|
||||
if (type < 0 || type > 255 || paramAddress == 0 || outPortAddress == 0 || contextAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
var portId = unchecked((uint)Interlocked.Increment(ref _nextPortId)) & 0xFF;
|
||||
var handle = 0x2000_0000UL | ((ulong)(uint)type << 16) | portId;
|
||||
return ctx.TryWriteUInt64(outPortAddress, handle)
|
||||
? ctx.SetReturn(0)
|
||||
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return TryWriteUInt64(ctx, outPortAddress, handle)
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -140,7 +141,7 @@ public static class AudioOut2Exports
|
||||
var stateAddress = ctx[CpuRegister.Rsi];
|
||||
if (handle == 0 || stateAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
var type = (int)((handle >> 16) & 0xFF);
|
||||
@@ -153,8 +154,8 @@ public static class AudioOut2Exports
|
||||
BinaryPrimitives.WriteInt16LittleEndian(state[0x04..], -1);
|
||||
|
||||
return ctx.Memory.TryWrite(stateAddress, state)
|
||||
? ctx.SetReturn(0)
|
||||
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -167,7 +168,7 @@ public static class AudioOut2Exports
|
||||
var infoAddress = ctx[CpuRegister.Rdi];
|
||||
if (infoAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
Span<byte> info = stackalloc byte[0x40];
|
||||
@@ -177,8 +178,8 @@ public static class AudioOut2Exports
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(info[0x08..], 48000);
|
||||
|
||||
return ctx.Memory.TryWrite(infoAddress, info)
|
||||
? ctx.SetReturn(0)
|
||||
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -186,14 +187,14 @@ public static class AudioOut2Exports
|
||||
ExportName = "sceAudioOut2PortDestroy",
|
||||
Target = Generation.Gen5,
|
||||
LibraryName = "libSceAudioOut2")]
|
||||
public static int AudioOut2PortDestroy(CpuContext ctx) => ctx.SetReturn(0);
|
||||
public static int AudioOut2PortDestroy(CpuContext ctx) => SetReturn(ctx, 0);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "IaZXJ9M79uo",
|
||||
ExportName = "sceAudioOut2UserDestroy",
|
||||
Target = Generation.Gen5,
|
||||
LibraryName = "libSceAudioOut2")]
|
||||
public static int AudioOut2UserDestroy(CpuContext ctx) => ctx.SetReturn(0);
|
||||
public static int AudioOut2UserDestroy(CpuContext ctx) => SetReturn(ctx, 0);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "xywYcRB7nbQ",
|
||||
@@ -206,12 +207,25 @@ public static class AudioOut2Exports
|
||||
var outUserAddress = ctx[CpuRegister.Rsi];
|
||||
if ((userId != 0 && userId != 1 && userId != 255) || outUserAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
var handle = (ulong)Interlocked.Increment(ref _nextUserHandle);
|
||||
return ctx.TryWriteUInt64(outUserAddress, handle)
|
||||
? ctx.SetReturn(0)
|
||||
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return TryWriteUInt64(ctx, outUserAddress, handle)
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
private static bool TryWriteUInt64(CpuContext ctx, ulong address, ulong value)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[sizeof(ulong)];
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(buffer, value);
|
||||
return ctx.Memory.TryWrite(address, buffer);
|
||||
}
|
||||
|
||||
private static int SetReturn(CpuContext ctx, int result)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using SharpEmu.HLE;
|
||||
using System.Buffers;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
namespace SharpEmu.Libs.Audio;
|
||||
|
||||
@@ -83,7 +84,7 @@ public static class AudioOutExports
|
||||
ExportName = "sceAudioOutInit",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceAudioOut")]
|
||||
public static int AudioOutInit(CpuContext ctx) => ctx.SetReturn(0);
|
||||
public static int AudioOutInit(CpuContext ctx) => SetReturn(ctx, 0);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "ekNvsT22rsY",
|
||||
@@ -100,7 +101,7 @@ public static class AudioOutExports
|
||||
if (bufferLength == 0 || frequency == 0 ||
|
||||
!TryGetFormat(format, out var channels, out var bytesPerSample, out var isFloat))
|
||||
{
|
||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
WinMmAudioPort? backend = null;
|
||||
@@ -132,7 +133,7 @@ public static class AudioOutExports
|
||||
$"[LOADER][INFO] AudioOut port {handle}: {frequency} Hz, " +
|
||||
$"{channels} ch, {(isFloat ? "float32" : "s16")}, " +
|
||||
$"{bufferLength} frames, backend={backendName}");
|
||||
return ctx.SetReturn(handle);
|
||||
return SetReturn(ctx, handle);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -145,11 +146,11 @@ public static class AudioOutExports
|
||||
var handle = unchecked((int)ctx[CpuRegister.Rdi]);
|
||||
if (!Ports.TryRemove(handle, out var port))
|
||||
{
|
||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
port.Dispose();
|
||||
return ctx.SetReturn(0);
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -163,12 +164,12 @@ public static class AudioOutExports
|
||||
var sourceAddress = ctx[CpuRegister.Rsi];
|
||||
if (!Ports.TryGetValue(handle, out var port))
|
||||
{
|
||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
if (sourceAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn(0);
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
|
||||
var buffer = ArrayPool<byte>.Shared.Rent(port.BufferByteLength);
|
||||
@@ -177,7 +178,7 @@ public static class AudioOutExports
|
||||
var source = buffer.AsSpan(0, port.BufferByteLength);
|
||||
if (!ctx.Memory.TryRead(sourceAddress, source))
|
||||
{
|
||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
if (port.Backend is null ||
|
||||
@@ -191,7 +192,7 @@ public static class AudioOutExports
|
||||
port.PaceSilence();
|
||||
}
|
||||
|
||||
return ctx.SetReturn(0);
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -207,12 +208,19 @@ public static class AudioOutExports
|
||||
public static int AudioOutSetVolume(CpuContext ctx)
|
||||
{
|
||||
var handle = unchecked((int)ctx[CpuRegister.Rdi]);
|
||||
return ctx.SetReturn(
|
||||
return SetReturn(
|
||||
ctx,
|
||||
Ports.ContainsKey(handle)
|
||||
? 0
|
||||
: (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
private static int SetReturn(CpuContext ctx, int result)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static bool TryGetFormat(
|
||||
int rawFormat,
|
||||
out int channels,
|
||||
|
||||
@@ -46,7 +46,8 @@ public static class AvPlayerExports
|
||||
var dataAddress = ctx[CpuRegister.Rsi];
|
||||
lock (StateGate)
|
||||
{
|
||||
return ctx.SetReturn(
|
||||
return SetReturn(
|
||||
ctx,
|
||||
handle != 0 && dataAddress != 0 && Players.Contains(handle)
|
||||
? 0
|
||||
: InvalidParameters);
|
||||
@@ -62,7 +63,15 @@ public static class AvPlayerExports
|
||||
{
|
||||
lock (StateGate)
|
||||
{
|
||||
return ctx.SetReturn(Players.Remove(ctx[CpuRegister.Rdi]) ? 0 : InvalidParameters);
|
||||
return SetReturn(
|
||||
ctx,
|
||||
Players.Remove(ctx[CpuRegister.Rdi]) ? 0 : InvalidParameters);
|
||||
}
|
||||
}
|
||||
|
||||
private static int SetReturn(CpuContext ctx, int result)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,17 +104,17 @@ public static class FiberExports
|
||||
var optParam = ctx[CpuRegister.Rdi];
|
||||
if (optParam == 0)
|
||||
{
|
||||
return ctx.SetReturn(FiberErrorNull);
|
||||
return SetReturn(ctx, FiberErrorNull);
|
||||
}
|
||||
|
||||
if ((optParam & 7) != 0)
|
||||
{
|
||||
return ctx.SetReturn(FiberErrorAlignment);
|
||||
return SetReturn(ctx, FiberErrorAlignment);
|
||||
}
|
||||
|
||||
return ctx.TryWriteUInt32(optParam, FiberOptSignature)
|
||||
? ctx.SetReturn(0)
|
||||
: ctx.SetReturn(FiberErrorInvalid);
|
||||
return TryWriteUInt32(ctx, optParam, FiberOptSignature)
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, FiberErrorInvalid);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -127,24 +127,24 @@ public static class FiberExports
|
||||
var fiber = ctx[CpuRegister.Rdi];
|
||||
if (!TryValidateFiber(ctx, fiber, out var error))
|
||||
{
|
||||
return ctx.SetReturn(error);
|
||||
return SetReturn(ctx, error);
|
||||
}
|
||||
|
||||
if (!ctx.TryReadUInt32(fiber + FiberStateOffset, out var state))
|
||||
if (!TryReadUInt32(ctx, fiber + FiberStateOffset, out var state))
|
||||
{
|
||||
return ctx.SetReturn(FiberErrorInvalid);
|
||||
return SetReturn(ctx, FiberErrorInvalid);
|
||||
}
|
||||
|
||||
if (state != FiberStateIdle)
|
||||
{
|
||||
return ctx.SetReturn(FiberErrorState);
|
||||
return SetReturn(ctx, FiberErrorState);
|
||||
}
|
||||
|
||||
_continuations.TryRemove(fiber, out _);
|
||||
_returnTargets.TryRemove(fiber, out _);
|
||||
_stackRanges.TryRemove(fiber, out _);
|
||||
_ = ctx.TryWriteUInt32(fiber + FiberStateOffset, FiberStateTerminated);
|
||||
return ctx.SetReturn(0);
|
||||
_ = TryWriteUInt32(ctx, fiber + FiberStateOffset, FiberStateTerminated);
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -229,20 +229,20 @@ public static class FiberExports
|
||||
var fiberAddress = ResolveCurrentFiberAddress(ctx);
|
||||
if (fiberAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn(FiberErrorPermission);
|
||||
return SetReturn(ctx, FiberErrorPermission);
|
||||
}
|
||||
|
||||
if (GuestThreadExecution.Scheduler is not { SupportsGuestContextTransfer: true } ||
|
||||
!GuestThreadExecution.TryGetCurrentImportCallFrame(out var frame))
|
||||
{
|
||||
return ctx.SetReturn(FiberErrorPermission);
|
||||
return SetReturn(ctx, FiberErrorPermission);
|
||||
}
|
||||
|
||||
var returnArgument = ctx[CpuRegister.Rdi];
|
||||
var argOnRunAddress = ctx[CpuRegister.Rsi];
|
||||
if (argOnRunAddress != 0 && !ctx.TryWriteUInt64(argOnRunAddress, 0))
|
||||
if (argOnRunAddress != 0 && !TryWriteUInt64(ctx, argOnRunAddress, 0))
|
||||
{
|
||||
return ctx.SetReturn(FiberErrorInvalid);
|
||||
return SetReturn(ctx, FiberErrorInvalid);
|
||||
}
|
||||
|
||||
GuestCpuContinuation transferTarget;
|
||||
@@ -256,7 +256,7 @@ public static class FiberExports
|
||||
if (!_returnTargets.TryRemove(fiberAddress, out var returnTarget))
|
||||
{
|
||||
_continuations.TryRemove(fiberAddress, out _);
|
||||
return ctx.SetReturn(FiberErrorPermission);
|
||||
return SetReturn(ctx, FiberErrorPermission);
|
||||
}
|
||||
|
||||
previousFiber = returnTarget.PreviousFiber;
|
||||
@@ -264,10 +264,10 @@ public static class FiberExports
|
||||
{
|
||||
if (!_continuations.TryRemove(previousFiber, out var previousContinuation) ||
|
||||
!TryWriteResumeArgument(ctx, previousContinuation, returnArgument) ||
|
||||
!ctx.TryWriteUInt32(previousFiber + FiberStateOffset, FiberStateRun))
|
||||
!TryWriteUInt32(ctx, previousFiber + FiberStateOffset, FiberStateRun))
|
||||
{
|
||||
_continuations.TryRemove(fiberAddress, out _);
|
||||
return ctx.SetReturn(FiberErrorState);
|
||||
return SetReturn(ctx, FiberErrorState);
|
||||
}
|
||||
|
||||
transferTarget = previousContinuation.Context with { Rax = 0 };
|
||||
@@ -278,15 +278,15 @@ public static class FiberExports
|
||||
!TryWriteResumeArgument(ctx, returnTarget.ThreadContinuation.Value, returnArgument))
|
||||
{
|
||||
_continuations.TryRemove(fiberAddress, out _);
|
||||
return ctx.SetReturn(FiberErrorState);
|
||||
return SetReturn(ctx, FiberErrorState);
|
||||
}
|
||||
|
||||
transferTarget = returnTarget.ThreadContinuation.Value.Context with { Rax = 0 };
|
||||
}
|
||||
|
||||
if (!ctx.TryWriteUInt32(fiberAddress + FiberStateOffset, FiberStateIdle))
|
||||
if (!TryWriteUInt32(ctx, fiberAddress + FiberStateOffset, FiberStateIdle))
|
||||
{
|
||||
return ctx.SetReturn(FiberErrorInvalid);
|
||||
return SetReturn(ctx, FiberErrorInvalid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,7 +296,7 @@ public static class FiberExports
|
||||
TraceFiber(
|
||||
$"return fiber=0x{fiberAddress:X16} to=0x{previousFiber:X16} " +
|
||||
$"resume=0x{transferTarget.Rip:X16} rsp=0x{transferTarget.Rsp:X16} arg=0x{returnArgument:X16}");
|
||||
return ctx.SetReturn(0);
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -309,18 +309,18 @@ public static class FiberExports
|
||||
var outAddress = ctx[CpuRegister.Rdi];
|
||||
if (outAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn(FiberErrorNull);
|
||||
return SetReturn(ctx, FiberErrorNull);
|
||||
}
|
||||
|
||||
var fiberAddress = ResolveCurrentFiberAddress(ctx);
|
||||
if (fiberAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn(FiberErrorPermission);
|
||||
return SetReturn(ctx, FiberErrorPermission);
|
||||
}
|
||||
|
||||
return ctx.TryWriteUInt64(outAddress, fiberAddress)
|
||||
? ctx.SetReturn(0)
|
||||
: ctx.SetReturn(FiberErrorInvalid);
|
||||
return TryWriteUInt64(ctx, outAddress, fiberAddress)
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, FiberErrorInvalid);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -334,35 +334,35 @@ public static class FiberExports
|
||||
var info = ctx[CpuRegister.Rsi];
|
||||
if (info == 0)
|
||||
{
|
||||
return ctx.SetReturn(FiberErrorNull);
|
||||
return SetReturn(ctx, FiberErrorNull);
|
||||
}
|
||||
|
||||
if (!TryValidateFiber(ctx, fiber, out var error))
|
||||
{
|
||||
return ctx.SetReturn(error);
|
||||
return SetReturn(ctx, error);
|
||||
}
|
||||
|
||||
if (!ctx.TryReadUInt64(info, out var size) || size != FiberInfoSize)
|
||||
if (!TryReadUInt64(ctx, info, out var size) || size != FiberInfoSize)
|
||||
{
|
||||
return ctx.SetReturn(FiberErrorInvalid);
|
||||
return SetReturn(ctx, FiberErrorInvalid);
|
||||
}
|
||||
|
||||
if (!TryReadFiberFields(ctx, fiber, out var fields))
|
||||
{
|
||||
return ctx.SetReturn(FiberErrorInvalid);
|
||||
return SetReturn(ctx, FiberErrorInvalid);
|
||||
}
|
||||
|
||||
if (!ctx.TryWriteUInt64(info + 8, fields.Entry) ||
|
||||
!ctx.TryWriteUInt64(info + 16, fields.ArgOnInitialize) ||
|
||||
!ctx.TryWriteUInt64(info + 24, fields.ContextAddress) ||
|
||||
!ctx.TryWriteUInt64(info + 32, fields.ContextSize) ||
|
||||
if (!TryWriteUInt64(ctx, info + 8, fields.Entry) ||
|
||||
!TryWriteUInt64(ctx, info + 16, fields.ArgOnInitialize) ||
|
||||
!TryWriteUInt64(ctx, info + 24, fields.ContextAddress) ||
|
||||
!TryWriteUInt64(ctx, info + 32, fields.ContextSize) ||
|
||||
!TryWriteName(ctx, info + 40, fields.Name) ||
|
||||
!ctx.TryWriteUInt64(info + 72, ulong.MaxValue))
|
||||
!TryWriteUInt64(ctx, info + 72, ulong.MaxValue))
|
||||
{
|
||||
return ctx.SetReturn(FiberErrorInvalid);
|
||||
return SetReturn(ctx, FiberErrorInvalid);
|
||||
}
|
||||
|
||||
return ctx.SetReturn(0);
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -376,22 +376,22 @@ public static class FiberExports
|
||||
var nameAddress = ctx[CpuRegister.Rsi];
|
||||
if (!TryValidateFiber(ctx, fiber, out var error))
|
||||
{
|
||||
return ctx.SetReturn(error);
|
||||
return SetReturn(ctx, error);
|
||||
}
|
||||
|
||||
if (nameAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn(FiberErrorNull);
|
||||
return SetReturn(ctx, FiberErrorNull);
|
||||
}
|
||||
|
||||
if (!ctx.TryReadNullTerminatedUtf8(nameAddress, MaxNameLength + 1, out var name))
|
||||
if (!TryReadNullTerminatedUtf8(ctx, nameAddress, MaxNameLength + 1, out var name))
|
||||
{
|
||||
return ctx.SetReturn(FiberErrorInvalid);
|
||||
return SetReturn(ctx, FiberErrorInvalid);
|
||||
}
|
||||
|
||||
return TryWriteName(ctx, fiber + FiberNameOffset, name)
|
||||
? ctx.SetReturn(0)
|
||||
: ctx.SetReturn(FiberErrorInvalid);
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, FiberErrorInvalid);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -403,12 +403,12 @@ public static class FiberExports
|
||||
{
|
||||
if (ctx[CpuRegister.Rdi] != 0)
|
||||
{
|
||||
return ctx.SetReturn(FiberErrorInvalid);
|
||||
return SetReturn(ctx, FiberErrorInvalid);
|
||||
}
|
||||
|
||||
return Interlocked.Exchange(ref _contextSizeCheck, 1) == 0
|
||||
? ctx.SetReturn(0)
|
||||
: ctx.SetReturn(FiberErrorState);
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, FiberErrorState);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -419,8 +419,8 @@ public static class FiberExports
|
||||
public static int FiberStopContextSizeCheck(CpuContext ctx)
|
||||
{
|
||||
return Interlocked.Exchange(ref _contextSizeCheck, 0) == 1
|
||||
? ctx.SetReturn(0)
|
||||
: ctx.SetReturn(FiberErrorState);
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, FiberErrorState);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -433,17 +433,17 @@ public static class FiberExports
|
||||
var outAddress = ctx[CpuRegister.Rdi];
|
||||
if (outAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn(FiberErrorNull);
|
||||
return SetReturn(ctx, FiberErrorNull);
|
||||
}
|
||||
|
||||
if (ResolveCurrentFiberAddress(ctx) == 0)
|
||||
{
|
||||
return ctx.SetReturn(FiberErrorPermission);
|
||||
return SetReturn(ctx, FiberErrorPermission);
|
||||
}
|
||||
|
||||
return ctx.TryWriteUInt64(outAddress, ctx[CpuRegister.Rbp])
|
||||
? ctx.SetReturn(0)
|
||||
: ctx.SetReturn(FiberErrorInvalid);
|
||||
return TryWriteUInt64(ctx, outAddress, ctx[CpuRegister.Rbp])
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, FiberErrorInvalid);
|
||||
}
|
||||
|
||||
private static int FiberInitializeCore(
|
||||
@@ -459,37 +459,37 @@ public static class FiberExports
|
||||
{
|
||||
if (fiber == 0 || nameAddress == 0 || entry == 0)
|
||||
{
|
||||
return ctx.SetReturn(FiberErrorNull);
|
||||
return SetReturn(ctx, FiberErrorNull);
|
||||
}
|
||||
|
||||
if ((fiber & 7) != 0 ||
|
||||
(contextAddress & 15) != 0 ||
|
||||
(optParam & 7) != 0)
|
||||
{
|
||||
return ctx.SetReturn(FiberErrorAlignment);
|
||||
return SetReturn(ctx, FiberErrorAlignment);
|
||||
}
|
||||
|
||||
if (contextSize != 0 && contextSize < FiberContextMinimumSize)
|
||||
{
|
||||
return ctx.SetReturn(FiberErrorRange);
|
||||
return SetReturn(ctx, FiberErrorRange);
|
||||
}
|
||||
|
||||
if ((contextSize & 15) != 0 ||
|
||||
(contextAddress == 0 && contextSize != 0) ||
|
||||
(contextAddress != 0 && contextSize == 0))
|
||||
{
|
||||
return ctx.SetReturn(FiberErrorInvalid);
|
||||
return SetReturn(ctx, FiberErrorInvalid);
|
||||
}
|
||||
|
||||
if (optParam != 0 &&
|
||||
(!ctx.TryReadUInt32(optParam, out var optMagic) || optMagic != FiberOptSignature))
|
||||
(!TryReadUInt32(ctx, optParam, out var optMagic) || optMagic != FiberOptSignature))
|
||||
{
|
||||
return ctx.SetReturn(FiberErrorInvalid);
|
||||
return SetReturn(ctx, FiberErrorInvalid);
|
||||
}
|
||||
|
||||
if (!ctx.TryReadNullTerminatedUtf8(nameAddress, MaxNameLength + 1, out var name))
|
||||
if (!TryReadNullTerminatedUtf8(ctx, nameAddress, MaxNameLength + 1, out var name))
|
||||
{
|
||||
return ctx.SetReturn(FiberErrorInvalid);
|
||||
return SetReturn(ctx, FiberErrorInvalid);
|
||||
}
|
||||
|
||||
if (Volatile.Read(ref _contextSizeCheck) != 0)
|
||||
@@ -497,27 +497,27 @@ public static class FiberExports
|
||||
flags |= FiberFlagContextSizeCheck;
|
||||
}
|
||||
|
||||
if (!ctx.TryWriteUInt32(fiber + FiberMagicStartOffset, FiberSignature0) ||
|
||||
!ctx.TryWriteUInt32(fiber + FiberStateOffset, FiberStateIdle) ||
|
||||
!ctx.TryWriteUInt64(fiber + FiberEntryOffset, entry) ||
|
||||
!ctx.TryWriteUInt64(fiber + FiberArgOnInitializeOffset, argOnInitialize) ||
|
||||
!ctx.TryWriteUInt64(fiber + FiberContextAddressOffset, contextAddress) ||
|
||||
!ctx.TryWriteUInt64(fiber + FiberContextSizeOffset, contextSize) ||
|
||||
if (!TryWriteUInt32(ctx, fiber + FiberMagicStartOffset, FiberSignature0) ||
|
||||
!TryWriteUInt32(ctx, fiber + FiberStateOffset, FiberStateIdle) ||
|
||||
!TryWriteUInt64(ctx, fiber + FiberEntryOffset, entry) ||
|
||||
!TryWriteUInt64(ctx, fiber + FiberArgOnInitializeOffset, argOnInitialize) ||
|
||||
!TryWriteUInt64(ctx, fiber + FiberContextAddressOffset, contextAddress) ||
|
||||
!TryWriteUInt64(ctx, fiber + FiberContextSizeOffset, contextSize) ||
|
||||
!TryWriteName(ctx, fiber + FiberNameOffset, name) ||
|
||||
!ctx.TryWriteUInt64(fiber + FiberContextPointerOffset, 0) ||
|
||||
!ctx.TryWriteUInt32(fiber + FiberFlagsOffset, flags) ||
|
||||
!ctx.TryWriteUInt64(fiber + FiberContextStartOffset, contextAddress) ||
|
||||
!ctx.TryWriteUInt64(fiber + FiberContextEndOffset, contextAddress == 0 ? 0 : contextAddress + contextSize) ||
|
||||
!ctx.TryWriteUInt32(fiber + FiberMagicEndOffset, FiberSignature1))
|
||||
!TryWriteUInt64(ctx, fiber + FiberContextPointerOffset, 0) ||
|
||||
!TryWriteUInt32(ctx, fiber + FiberFlagsOffset, flags) ||
|
||||
!TryWriteUInt64(ctx, fiber + FiberContextStartOffset, contextAddress) ||
|
||||
!TryWriteUInt64(ctx, fiber + FiberContextEndOffset, contextAddress == 0 ? 0 : contextAddress + contextSize) ||
|
||||
!TryWriteUInt32(ctx, fiber + FiberMagicEndOffset, FiberSignature1))
|
||||
{
|
||||
return ctx.SetReturn(FiberErrorInvalid);
|
||||
return SetReturn(ctx, FiberErrorInvalid);
|
||||
}
|
||||
|
||||
if (contextAddress != 0)
|
||||
{
|
||||
if (!ctx.TryWriteUInt64(contextAddress, FiberStackSignature))
|
||||
if (!TryWriteUInt64(ctx, contextAddress, FiberStackSignature))
|
||||
{
|
||||
return ctx.SetReturn(FiberErrorInvalid);
|
||||
return SetReturn(ctx, FiberErrorInvalid);
|
||||
}
|
||||
|
||||
if ((flags & FiberFlagContextSizeCheck) != 0)
|
||||
@@ -532,7 +532,7 @@ public static class FiberExports
|
||||
}
|
||||
|
||||
TraceFiber($"init fiber=0x{fiber:X16} entry=0x{entry:X16} ctx=0x{contextAddress:X16} size=0x{contextSize:X} name='{name}'");
|
||||
return ctx.SetReturn(0);
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
|
||||
private static int FiberRunCore(
|
||||
@@ -547,12 +547,12 @@ public static class FiberExports
|
||||
{
|
||||
if (!TryValidateFiber(ctx, fiber, out var error))
|
||||
{
|
||||
return ctx.SetReturn(error);
|
||||
return SetReturn(ctx, error);
|
||||
}
|
||||
|
||||
if (!TryReadFiberFields(ctx, fiber, out var fields))
|
||||
{
|
||||
return ctx.SetReturn(FiberErrorInvalid);
|
||||
return SetReturn(ctx, FiberErrorInvalid);
|
||||
}
|
||||
|
||||
if (attachContextAddress != 0 || attachContextSize != 0)
|
||||
@@ -560,7 +560,7 @@ public static class FiberExports
|
||||
var attachResult = AttachContext(ctx, fiber, attachContextAddress, attachContextSize, ref fields);
|
||||
if (attachResult != 0)
|
||||
{
|
||||
return ctx.SetReturn(attachResult);
|
||||
return SetReturn(ctx, attachResult);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -568,16 +568,16 @@ public static class FiberExports
|
||||
if ((isSwitch && previousFiber == 0) ||
|
||||
(!isSwitch && previousFiber != 0))
|
||||
{
|
||||
return ctx.SetReturn(FiberErrorPermission);
|
||||
return SetReturn(ctx, FiberErrorPermission);
|
||||
}
|
||||
if (previousFiber == fiber)
|
||||
{
|
||||
return ctx.SetReturn(FiberErrorState);
|
||||
return SetReturn(ctx, FiberErrorState);
|
||||
}
|
||||
if (GuestThreadExecution.Scheduler is not { SupportsGuestContextTransfer: true } ||
|
||||
!GuestThreadExecution.TryGetCurrentImportCallFrame(out var frame))
|
||||
{
|
||||
return ctx.SetReturn(FiberErrorPermission);
|
||||
return SetReturn(ctx, FiberErrorPermission);
|
||||
}
|
||||
|
||||
GuestCpuContinuation transferTarget;
|
||||
@@ -586,12 +586,12 @@ public static class FiberExports
|
||||
{
|
||||
if (!TryReadFiberFields(ctx, fiber, out fields))
|
||||
{
|
||||
return ctx.SetReturn(FiberErrorInvalid);
|
||||
return SetReturn(ctx, FiberErrorInvalid);
|
||||
}
|
||||
if (fields.State != FiberStateIdle)
|
||||
{
|
||||
TraceFiber($"run-state-error reason={reason} fiber=0x{fiber:X16} state=0x{fields.State:X8}");
|
||||
return ctx.SetReturn(FiberErrorState);
|
||||
return SetReturn(ctx, FiberErrorState);
|
||||
}
|
||||
|
||||
FiberContinuation targetContinuation;
|
||||
@@ -602,12 +602,12 @@ public static class FiberExports
|
||||
}
|
||||
else if (!TryCreateInitialContinuation(ctx, fields, argOnRun, out targetContinuation))
|
||||
{
|
||||
return ctx.SetReturn(FiberErrorInvalid);
|
||||
return SetReturn(ctx, FiberErrorInvalid);
|
||||
}
|
||||
|
||||
if (resumed && !TryWriteResumeArgument(ctx, targetContinuation, argOnRun))
|
||||
{
|
||||
return ctx.SetReturn(FiberErrorInvalid);
|
||||
return SetReturn(ctx, FiberErrorInvalid);
|
||||
}
|
||||
|
||||
var callerContinuation = new FiberContinuation(
|
||||
@@ -616,11 +616,11 @@ public static class FiberExports
|
||||
|
||||
if (previousFiber != 0)
|
||||
{
|
||||
if (!ctx.TryReadUInt32(previousFiber + FiberStateOffset, out var previousState) ||
|
||||
if (!TryReadUInt32(ctx, previousFiber + FiberStateOffset, out var previousState) ||
|
||||
previousState != FiberStateRun ||
|
||||
!ctx.TryWriteUInt32(previousFiber + FiberStateOffset, FiberStateIdle))
|
||||
!TryWriteUInt32(ctx, previousFiber + FiberStateOffset, FiberStateIdle))
|
||||
{
|
||||
return ctx.SetReturn(FiberErrorState);
|
||||
return SetReturn(ctx, FiberErrorState);
|
||||
}
|
||||
|
||||
_continuations[previousFiber] = callerContinuation;
|
||||
@@ -631,15 +631,15 @@ public static class FiberExports
|
||||
_returnTargets[fiber] = new FiberReturnTarget(0, callerContinuation);
|
||||
}
|
||||
|
||||
if (!ctx.TryWriteUInt32(fiber + FiberStateOffset, FiberStateRun))
|
||||
if (!TryWriteUInt32(ctx, fiber + FiberStateOffset, FiberStateRun))
|
||||
{
|
||||
if (previousFiber != 0)
|
||||
{
|
||||
_continuations.TryRemove(previousFiber, out _);
|
||||
_ = ctx.TryWriteUInt32(previousFiber + FiberStateOffset, FiberStateRun);
|
||||
_ = TryWriteUInt32(ctx, previousFiber + FiberStateOffset, FiberStateRun);
|
||||
}
|
||||
_returnTargets.TryRemove(fiber, out _);
|
||||
return ctx.SetReturn(FiberErrorInvalid);
|
||||
return SetReturn(ctx, FiberErrorInvalid);
|
||||
}
|
||||
|
||||
if (resumed)
|
||||
@@ -656,7 +656,7 @@ public static class FiberExports
|
||||
TraceFiber(
|
||||
$"transfer reason={reason} from=0x{previousFiber:X16} to=0x{fiber:X16} resume={resumed} " +
|
||||
$"rip=0x{transferTarget.Rip:X16} rsp=0x{transferTarget.Rsp:X16} arg=0x{argOnRun:X16}");
|
||||
return ctx.SetReturn(0);
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
|
||||
private static bool TryCreateInitialContinuation(
|
||||
@@ -673,7 +673,7 @@ public static class FiberExports
|
||||
|
||||
var stackEnd = fields.ContextAddress + fields.ContextSize;
|
||||
var entryRsp = (stackEnd & ~15UL) - sizeof(ulong);
|
||||
if (!ctx.TryWriteUInt64(entryRsp, 0))
|
||||
if (!TryWriteUInt64(ctx, entryRsp, 0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -708,7 +708,7 @@ public static class FiberExports
|
||||
FiberContinuation continuation,
|
||||
ulong argument) =>
|
||||
continuation.ArgOnRunAddress == 0 ||
|
||||
ctx.TryWriteUInt64(continuation.ArgOnRunAddress, argument);
|
||||
TryWriteUInt64(ctx, continuation.ArgOnRunAddress, argument);
|
||||
|
||||
private static GuestCpuContinuation CaptureContinuation(
|
||||
CpuContext ctx,
|
||||
@@ -779,11 +779,11 @@ public static class FiberExports
|
||||
return FiberErrorInvalid;
|
||||
}
|
||||
|
||||
if (!ctx.TryWriteUInt64(fiber + FiberContextAddressOffset, contextAddress) ||
|
||||
!ctx.TryWriteUInt64(fiber + FiberContextSizeOffset, contextSize) ||
|
||||
!ctx.TryWriteUInt64(fiber + FiberContextStartOffset, contextAddress) ||
|
||||
!ctx.TryWriteUInt64(fiber + FiberContextEndOffset, contextAddress + contextSize) ||
|
||||
!ctx.TryWriteUInt64(contextAddress, FiberStackSignature))
|
||||
if (!TryWriteUInt64(ctx, fiber + FiberContextAddressOffset, contextAddress) ||
|
||||
!TryWriteUInt64(ctx, fiber + FiberContextSizeOffset, contextSize) ||
|
||||
!TryWriteUInt64(ctx, fiber + FiberContextStartOffset, contextAddress) ||
|
||||
!TryWriteUInt64(ctx, fiber + FiberContextEndOffset, contextAddress + contextSize) ||
|
||||
!TryWriteUInt64(ctx, contextAddress, FiberStackSignature))
|
||||
{
|
||||
return FiberErrorInvalid;
|
||||
}
|
||||
@@ -839,8 +839,8 @@ public static class FiberExports
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ctx.TryReadUInt32(fiber + FiberMagicStartOffset, out var magicStart) ||
|
||||
!ctx.TryReadUInt32(fiber + FiberMagicEndOffset, out var magicEnd) ||
|
||||
if (!TryReadUInt32(ctx, fiber + FiberMagicStartOffset, out var magicStart) ||
|
||||
!TryReadUInt32(ctx, fiber + FiberMagicEndOffset, out var magicEnd) ||
|
||||
magicStart != FiberSignature0 ||
|
||||
magicEnd != FiberSignature1)
|
||||
{
|
||||
@@ -855,12 +855,12 @@ public static class FiberExports
|
||||
private static bool TryReadFiberFields(CpuContext ctx, ulong fiber, out FiberFields fields)
|
||||
{
|
||||
fields = default;
|
||||
if (!ctx.TryReadUInt32(fiber + FiberStateOffset, out var state) ||
|
||||
!ctx.TryReadUInt64(fiber + FiberEntryOffset, out var entry) ||
|
||||
!ctx.TryReadUInt64(fiber + FiberArgOnInitializeOffset, out var argOnInitialize) ||
|
||||
!ctx.TryReadUInt64(fiber + FiberContextAddressOffset, out var contextAddress) ||
|
||||
!ctx.TryReadUInt64(fiber + FiberContextSizeOffset, out var contextSize) ||
|
||||
!ctx.TryReadUInt32(fiber + FiberFlagsOffset, out var flags) ||
|
||||
if (!TryReadUInt32(ctx, fiber + FiberStateOffset, out var state) ||
|
||||
!TryReadUInt64(ctx, fiber + FiberEntryOffset, out var entry) ||
|
||||
!TryReadUInt64(ctx, fiber + FiberArgOnInitializeOffset, out var argOnInitialize) ||
|
||||
!TryReadUInt64(ctx, fiber + FiberContextAddressOffset, out var contextAddress) ||
|
||||
!TryReadUInt64(ctx, fiber + FiberContextSizeOffset, out var contextSize) ||
|
||||
!TryReadUInt32(ctx, fiber + FiberFlagsOffset, out var flags) ||
|
||||
!TryReadInlineName(ctx, fiber + FiberNameOffset, out var name))
|
||||
{
|
||||
return false;
|
||||
@@ -898,6 +898,46 @@ public static class FiberExports
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static bool TryReadUInt32(CpuContext ctx, ulong address, out uint value)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[sizeof(uint)];
|
||||
if (!ctx.Memory.TryRead(address, buffer))
|
||||
{
|
||||
value = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = BinaryPrimitives.ReadUInt32LittleEndian(buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryWriteUInt32(CpuContext ctx, ulong address, uint value)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[sizeof(uint)];
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer, value);
|
||||
return ctx.Memory.TryWrite(address, buffer);
|
||||
}
|
||||
|
||||
private static bool TryReadUInt64(CpuContext ctx, ulong address, out ulong value)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[sizeof(ulong)];
|
||||
if (!ctx.Memory.TryRead(address, buffer))
|
||||
{
|
||||
value = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = BinaryPrimitives.ReadUInt64LittleEndian(buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryWriteUInt64(CpuContext ctx, ulong address, ulong value)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[sizeof(ulong)];
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(buffer, value);
|
||||
return ctx.Memory.TryWrite(address, buffer);
|
||||
}
|
||||
|
||||
private static bool TryWriteName(CpuContext ctx, ulong address, string name)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[MaxNameLength + 1];
|
||||
@@ -925,6 +965,37 @@ public static class FiberExports
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryReadNullTerminatedUtf8(CpuContext ctx, ulong address, int capacity, out string value)
|
||||
{
|
||||
var bytes = new byte[capacity];
|
||||
for (var index = 0; index < bytes.Length; index++)
|
||||
{
|
||||
Span<byte> current = stackalloc byte[1];
|
||||
if (!ctx.Memory.TryRead(address + (ulong)index, current))
|
||||
{
|
||||
value = string.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (current[0] == 0)
|
||||
{
|
||||
value = Encoding.UTF8.GetString(bytes, 0, index);
|
||||
return true;
|
||||
}
|
||||
|
||||
bytes[index] = current[0];
|
||||
}
|
||||
|
||||
value = Encoding.UTF8.GetString(bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int SetReturn(CpuContext ctx, int result)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void TraceFiber(string message)
|
||||
{
|
||||
if (string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_FIBER"), "1", StringComparison.Ordinal))
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using SharpEmu.HLE;
|
||||
using SharpEmu.Libs.Ampr;
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
|
||||
@@ -49,7 +50,7 @@ public static class KernelAprCompatExports
|
||||
return completionResult;
|
||||
}
|
||||
|
||||
if (outSubmissionId != 0 && !ctx.TryWriteUInt32(outSubmissionId, submissionId))
|
||||
if (outSubmissionId != 0 && !TryWriteUInt32(ctx, outSubmissionId, submissionId))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
@@ -140,7 +141,7 @@ public static class KernelAprCompatExports
|
||||
return completionResult;
|
||||
}
|
||||
|
||||
if (!ctx.TryWriteUInt32(outSubmissionId, submissionId))
|
||||
if (!TryWriteUInt32(ctx, outSubmissionId, submissionId))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
@@ -177,6 +178,13 @@ public static class KernelAprCompatExports
|
||||
return tag is 0x0C or 0x10;
|
||||
}
|
||||
|
||||
private static bool TryWriteUInt32(CpuContext ctx, ulong address, uint value)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[sizeof(uint)];
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer, value);
|
||||
return ctx.Memory.TryWrite(address, buffer);
|
||||
}
|
||||
|
||||
private static void TraceApr(
|
||||
CpuContext ctx,
|
||||
string operation,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Text;
|
||||
using SharpEmu.HLE;
|
||||
@@ -51,17 +52,17 @@ public static class KernelEventFlagCompatExports
|
||||
optionAddress != 0 ||
|
||||
!IsValidAttributes(attributes))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
if (!ctx.TryReadNullTerminatedUtf8(nameAddress, MaxEventFlagNameLength + 1, out var name))
|
||||
if (!TryReadNullTerminatedUtf8(ctx, nameAddress, MaxEventFlagNameLength + 1, out var name))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
if (Encoding.UTF8.GetByteCount(name) > MaxEventFlagNameLength)
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
var handle = unchecked((ulong)Interlocked.Increment(ref _nextEventFlagHandle));
|
||||
@@ -75,11 +76,11 @@ public static class KernelEventFlagCompatExports
|
||||
if (!ctx.TryWriteUInt64(outAddress, handle))
|
||||
{
|
||||
_eventFlags.TryRemove(handle, out _);
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
TraceEventFlag($"create handle=0x{handle:X16} name='{name}' attr=0x{attributes:X2} bits=0x{initialPattern:X16}");
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -92,7 +93,7 @@ public static class KernelEventFlagCompatExports
|
||||
var handle = ctx[CpuRegister.Rdi];
|
||||
if (!_eventFlags.TryRemove(handle, out var state))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
|
||||
}
|
||||
|
||||
lock (state.Gate)
|
||||
@@ -101,7 +102,7 @@ public static class KernelEventFlagCompatExports
|
||||
}
|
||||
|
||||
TraceEventFlag($"delete handle=0x{handle:X16} name='{state.Name}'");
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -116,7 +117,7 @@ public static class KernelEventFlagCompatExports
|
||||
var returnRip = GetCurrentReturnRip();
|
||||
if (!_eventFlags.TryGetValue(handle, out var state))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
|
||||
}
|
||||
|
||||
lock (state.Gate)
|
||||
@@ -127,7 +128,7 @@ public static class KernelEventFlagCompatExports
|
||||
}
|
||||
|
||||
_ = GuestThreadExecution.Scheduler?.WakeBlockedThreads(GetEventFlagWakeKey(handle));
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -141,7 +142,7 @@ public static class KernelEventFlagCompatExports
|
||||
var pattern = ctx[CpuRegister.Rsi];
|
||||
if (!_eventFlags.TryGetValue(handle, out var state))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
|
||||
}
|
||||
|
||||
lock (state.Gate)
|
||||
@@ -150,7 +151,7 @@ public static class KernelEventFlagCompatExports
|
||||
TraceEventFlag($"clear handle=0x{handle:X16} mask=0x{pattern:X16} bits=0x{state.Bits:X16}");
|
||||
}
|
||||
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -167,29 +168,29 @@ public static class KernelEventFlagCompatExports
|
||||
|
||||
if (!_eventFlags.TryGetValue(handle, out var state))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (pattern == 0 || !IsValidWaitMode(waitMode))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
lock (state.Gate)
|
||||
{
|
||||
if (!TryWriteResultPattern(ctx, resultAddress, state.Bits))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
if (!IsSatisfied(state.Bits, pattern, waitMode))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_BUSY);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_BUSY);
|
||||
}
|
||||
|
||||
ApplyClearMode(state, pattern, waitMode);
|
||||
TraceEventFlag($"poll handle=0x{handle:X16} pattern=0x{pattern:X16} mode=0x{waitMode:X2} bits=0x{state.Bits:X16}");
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,18 +210,18 @@ public static class KernelEventFlagCompatExports
|
||||
|
||||
if (!_eventFlags.TryGetValue(handle, out var state))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (pattern == 0 || !IsValidWaitMode(waitMode))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
uint timeoutUsec = 0;
|
||||
if (timeoutAddress != 0 && !ctx.TryReadUInt32(timeoutAddress, out timeoutUsec))
|
||||
if (timeoutAddress != 0 && !TryReadUInt32(ctx, timeoutAddress, out timeoutUsec))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
Monitor.Enter(state.Gate);
|
||||
@@ -228,15 +229,15 @@ public static class KernelEventFlagCompatExports
|
||||
{
|
||||
if (TryCompleteSatisfiedWait(ctx, state, pattern, waitMode, resultAddress, out var immediateWaitResult))
|
||||
{
|
||||
return ctx.SetReturn(immediateWaitResult);
|
||||
return SetReturn(ctx, immediateWaitResult);
|
||||
}
|
||||
|
||||
if (timeoutAddress != 0)
|
||||
{
|
||||
_ = ctx.TryWriteUInt32(timeoutAddress, 0);
|
||||
_ = TryWriteUInt32(ctx, timeoutAddress, 0);
|
||||
_ = TryWriteResultPattern(ctx, resultAddress, state.Bits);
|
||||
TraceEventFlag($"wait-timeout handle=0x{handle:X16} pattern=0x{pattern:X16} timeout={timeoutUsec} ret=0x{returnRip:X16}");
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_TIMED_OUT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_TIMED_OUT);
|
||||
}
|
||||
|
||||
var currentGuestThread = GuestThreadExecution.CurrentGuestThreadHandle;
|
||||
@@ -271,7 +272,7 @@ public static class KernelEventFlagCompatExports
|
||||
var scheduler = GuestThreadExecution.Scheduler;
|
||||
if (scheduler is null)
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_TRY_AGAIN);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_TRY_AGAIN);
|
||||
}
|
||||
|
||||
state.WaitingThreads++;
|
||||
@@ -296,7 +297,7 @@ public static class KernelEventFlagCompatExports
|
||||
state.WaitingThreads = Math.Max(0, state.WaitingThreads - 1);
|
||||
releaseWaiter = false;
|
||||
TraceEventFlag($"wait-wake handle=0x{handle:X16} pattern=0x{pattern:X16} bits=0x{state.Bits:X16} waiters={state.WaitingThreads} ret=0x{returnRip:X16}");
|
||||
return ctx.SetReturn(pumpedWaitResult);
|
||||
return SetReturn(ctx, pumpedWaitResult);
|
||||
}
|
||||
|
||||
Monitor.Wait(state.Gate, HostWaitPumpMilliseconds);
|
||||
@@ -313,7 +314,7 @@ public static class KernelEventFlagCompatExports
|
||||
|
||||
state.WaitingThreads++;
|
||||
TraceEventFlag($"wait-block handle=0x{handle:X16} pattern=0x{pattern:X16} waiters={state.WaitingThreads} guest_thread=0x{currentGuestThread:X16} fiber=0x{currentFiber:X16} managed={managedThread} ret=0x{returnRip:X16}");
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -333,15 +334,15 @@ public static class KernelEventFlagCompatExports
|
||||
var waiterCountAddress = ctx[CpuRegister.Rdx];
|
||||
if (!_eventFlags.TryGetValue(handle, out var state))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
|
||||
}
|
||||
|
||||
lock (state.Gate)
|
||||
{
|
||||
if (waiterCountAddress != 0 &&
|
||||
!ctx.TryWriteUInt32(waiterCountAddress, unchecked((uint)state.WaitingThreads)))
|
||||
!TryWriteUInt32(ctx, waiterCountAddress, unchecked((uint)state.WaitingThreads)))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
state.Bits = setPattern;
|
||||
@@ -350,7 +351,7 @@ public static class KernelEventFlagCompatExports
|
||||
TraceEventFlag($"cancel handle=0x{handle:X16} bits=0x{setPattern:X16}");
|
||||
}
|
||||
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
}
|
||||
|
||||
private static bool IsValidAttributes(uint attributes)
|
||||
@@ -452,6 +453,84 @@ public static class KernelEventFlagCompatExports
|
||||
private static bool TryWriteResultPattern(CpuContext ctx, ulong address, ulong bits) =>
|
||||
address == 0 || ctx.TryWriteUInt64(address, bits);
|
||||
|
||||
private static bool TryReadUInt32(CpuContext ctx, ulong address, out uint value)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[sizeof(uint)];
|
||||
if (!ctx.Memory.TryRead(address, buffer))
|
||||
{
|
||||
value = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = BinaryPrimitives.ReadUInt32LittleEndian(buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryReadUInt64(CpuContext ctx, ulong address, out ulong value)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[sizeof(ulong)];
|
||||
if (!ctx.Memory.TryRead(address, buffer))
|
||||
{
|
||||
value = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = BinaryPrimitives.ReadUInt64LittleEndian(buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryReadByte(CpuContext ctx, ulong address, out byte value)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[1];
|
||||
if (!ctx.Memory.TryRead(address, buffer))
|
||||
{
|
||||
value = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = buffer[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryWriteUInt32(CpuContext ctx, ulong address, uint value)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[sizeof(uint)];
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer, value);
|
||||
return ctx.Memory.TryWrite(address, buffer);
|
||||
}
|
||||
|
||||
private static bool TryReadNullTerminatedUtf8(CpuContext ctx, ulong address, int capacity, out string value)
|
||||
{
|
||||
var bytes = new byte[capacity];
|
||||
for (var index = 0; index < bytes.Length; index++)
|
||||
{
|
||||
Span<byte> current = stackalloc byte[1];
|
||||
if (!ctx.Memory.TryRead(address + (ulong)index, current))
|
||||
{
|
||||
value = string.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (current[0] == 0)
|
||||
{
|
||||
value = Encoding.UTF8.GetString(bytes, 0, index);
|
||||
return true;
|
||||
}
|
||||
|
||||
bytes[index] = current[0];
|
||||
}
|
||||
|
||||
value = Encoding.UTF8.GetString(bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int SetReturn(CpuContext ctx, OrbisGen2Result result)
|
||||
{
|
||||
var value = (int)result;
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)value);
|
||||
return value;
|
||||
}
|
||||
|
||||
private static void TraceEventFlag(string message)
|
||||
{
|
||||
if (string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_EVENT_FLAG"), "1", StringComparison.Ordinal))
|
||||
@@ -535,7 +614,7 @@ public static class KernelEventFlagCompatExports
|
||||
|
||||
private static void AppendByte(StringBuilder builder, CpuContext ctx, ulong address, string name)
|
||||
{
|
||||
if (ctx.TryReadByte(address, out var value))
|
||||
if (TryReadByte(ctx, address, out var value))
|
||||
{
|
||||
builder.Append($" {name}=0x{value:X2}");
|
||||
}
|
||||
@@ -543,7 +622,7 @@ public static class KernelEventFlagCompatExports
|
||||
|
||||
private static void AppendUInt32(StringBuilder builder, CpuContext ctx, ulong address, string name)
|
||||
{
|
||||
if (ctx.TryReadUInt32(address, out var value))
|
||||
if (TryReadUInt32(ctx, address, out var value))
|
||||
{
|
||||
builder.Append($" {name}=0x{value:X8}");
|
||||
}
|
||||
@@ -551,7 +630,7 @@ public static class KernelEventFlagCompatExports
|
||||
|
||||
private static void AppendUInt64(StringBuilder builder, CpuContext ctx, ulong address, string name)
|
||||
{
|
||||
if (ctx.TryReadUInt64(address, out var value))
|
||||
if (TryReadUInt64(ctx, address, out var value))
|
||||
{
|
||||
builder.Append($" {name}=0x{value:X16}");
|
||||
}
|
||||
|
||||
@@ -287,13 +287,13 @@ public static class KernelEventQueueCompatExports
|
||||
}
|
||||
|
||||
uint timeoutUsec = 0;
|
||||
if (timeoutAddress != 0 && !ctx.TryReadUInt32(timeoutAddress, out timeoutUsec))
|
||||
if (timeoutAddress != 0 && !TryReadUInt32(ctx, timeoutAddress, out timeoutUsec))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
var deliveredCount = DequeueEvents(ctx, handle, eventsAddress, eventCapacity);
|
||||
if (outCountAddress != 0 && !ctx.TryWriteUInt32(outCountAddress, (uint)deliveredCount))
|
||||
if (outCountAddress != 0 && !TryWriteUInt32(ctx, outCountAddress, (uint)deliveredCount))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
@@ -336,7 +336,7 @@ public static class KernelEventQueueCompatExports
|
||||
}
|
||||
|
||||
deliveredCount = DequeueEvents(ctx, handle, eventsAddress, eventCapacity);
|
||||
if (outCountAddress != 0 && !ctx.TryWriteUInt32(outCountAddress, (uint)deliveredCount))
|
||||
if (outCountAddress != 0 && !TryWriteUInt32(ctx, outCountAddress, (uint)deliveredCount))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
@@ -541,7 +541,7 @@ public static class KernelEventQueueCompatExports
|
||||
ulong outCountAddress)
|
||||
{
|
||||
var deliveredCount = DequeueEvents(ctx, handle, eventsAddress, eventCapacity);
|
||||
if (outCountAddress != 0 && !ctx.TryWriteUInt32(outCountAddress, (uint)deliveredCount))
|
||||
if (outCountAddress != 0 && !TryWriteUInt32(ctx, outCountAddress, (uint)deliveredCount))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
@@ -654,8 +654,29 @@ public static class KernelEventQueueCompatExports
|
||||
return;
|
||||
}
|
||||
|
||||
_ = ctx.TryReadUInt64(ctx[CpuRegister.Rsp], out ulong returnRip);
|
||||
var returnRip = 0UL;
|
||||
_ = ctx.TryReadUInt64(ctx[CpuRegister.Rsp], out returnRip);
|
||||
Console.Error.WriteLine(
|
||||
$"[LOADER][TRACE] equeue.{operation}: handle=0x{handle:X16} rsi=0x{ctx[CpuRegister.Rsi]:X16} rdx=0x{ctx[CpuRegister.Rdx]:X16} ret=0x{returnRip:X16}");
|
||||
}
|
||||
|
||||
private static bool TryWriteUInt32(CpuContext ctx, ulong address, uint value)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[sizeof(uint)];
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer, value);
|
||||
return ctx.Memory.TryWrite(address, buffer);
|
||||
}
|
||||
|
||||
private static bool TryReadUInt32(CpuContext ctx, ulong address, out uint value)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[sizeof(uint)];
|
||||
if (!ctx.Memory.TryRead(address, buffer))
|
||||
{
|
||||
value = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = BinaryPrimitives.ReadUInt32LittleEndian(buffer);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace SharpEmu.Libs.Kernel;
|
||||
|
||||
public static class KernelExports
|
||||
{
|
||||
private static int _nextFileDescriptor = 2;
|
||||
private static readonly object _cxaGate = new();
|
||||
private static readonly List<CxaDestructorEntry> _cxaDestructors = new();
|
||||
private static readonly object _coredumpGate = new();
|
||||
|
||||
@@ -7,7 +7,9 @@ using System.Buffers;
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Linq;
|
||||
using System.Globalization;
|
||||
|
||||
namespace SharpEmu.Libs.Kernel;
|
||||
@@ -2903,7 +2905,7 @@ public static class KernelMemoryCompatExports
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
if (protectionOut != 0 && !ctx.TryWriteInt32(protectionOut, region.Protection))
|
||||
if (protectionOut != 0 && !TryWriteInt32(ctx, protectionOut, region.Protection))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
@@ -2942,7 +2944,7 @@ public static class KernelMemoryCompatExports
|
||||
|
||||
if (!ctx.TryWriteUInt64(infoAddress, block.Start) ||
|
||||
!ctx.TryWriteUInt64(infoAddress + sizeof(ulong), block.Start + block.Length) ||
|
||||
!ctx.TryWriteInt32(infoAddress + (sizeof(ulong) * 2), block.MemoryType))
|
||||
!TryWriteInt32(ctx, infoAddress + (sizeof(ulong) * 2), block.MemoryType))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
@@ -3800,7 +3802,7 @@ public static class KernelMemoryCompatExports
|
||||
var addr = argumentSource.NextGpArg();
|
||||
if (addr != 0)
|
||||
{
|
||||
_ = ctx.TryWriteInt32(addr, sb.Length);
|
||||
_ = TryWriteInt32(ctx, addr, sb.Length);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -4525,7 +4527,7 @@ public static class KernelMemoryCompatExports
|
||||
continue;
|
||||
}
|
||||
|
||||
var one = chunk.AsSpan(0, 1);
|
||||
Span<byte> one = stackalloc byte[1];
|
||||
if (!TryReadCompat(ctx, current, one))
|
||||
{
|
||||
return false;
|
||||
@@ -4964,7 +4966,7 @@ public static class KernelMemoryCompatExports
|
||||
processedCount++;
|
||||
}
|
||||
|
||||
if (processedOutAddress != 0 && !ctx.TryWriteInt32(processedOutAddress, processedCount))
|
||||
if (processedOutAddress != 0 && !TryWriteInt32(ctx, processedOutAddress, processedCount))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
@@ -5801,6 +5803,13 @@ public static class KernelMemoryCompatExports
|
||||
return (protect & expected) != 0;
|
||||
}
|
||||
|
||||
private static bool TryWriteInt32(CpuContext ctx, ulong address, int value)
|
||||
{
|
||||
Span<byte> bytes = stackalloc byte[sizeof(int)];
|
||||
BitConverter.TryWriteBytes(bytes, value);
|
||||
return ctx.Memory.TryWrite(address, bytes);
|
||||
}
|
||||
|
||||
private static bool TryWriteOpenDescriptorStat(CpuContext ctx, int fd, ulong statAddress)
|
||||
{
|
||||
if (fd is 0 or 1 or 2)
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using SharpEmu.HLE;
|
||||
using System.Buffers.Binary;
|
||||
using System.Threading;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace SharpEmu.Libs.Kernel;
|
||||
@@ -327,12 +329,6 @@ public static class KernelPthreadCompatExports
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int PosixPthreadCondSignal(CpuContext ctx) => PthreadCondSignalCore(ctx, ctx[CpuRegister.Rdi], broadcast: false);
|
||||
[SysAbiExport(
|
||||
Nid = "27bAgiJmOh0",
|
||||
ExportName = "pthread_cond_timedwait",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int PosixPthreadCondTimedwait(CpuContext ctx) => PthreadCondWaitCore(ctx, ctx[CpuRegister.Rdi], ctx[CpuRegister.Rsi], timed: true, timeoutUsec: unchecked((uint)ctx[CpuRegister.Rdx]));
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "m5-2bsNfv7s",
|
||||
@@ -387,42 +383,42 @@ public static class KernelPthreadCompatExports
|
||||
var initRoutine = ctx[CpuRegister.Rsi];
|
||||
if (onceAddress == 0 || initRoutine == 0)
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
if (!ctx.TryReadInt32(onceAddress, out var onceValue))
|
||||
if (!TryReadInt32(ctx, onceAddress, out var onceValue))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
if (onceValue == PthreadOnceDone)
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
}
|
||||
|
||||
var gate = GetPthreadOnceGate(onceAddress);
|
||||
var shouldCall = false;
|
||||
lock (gate)
|
||||
{
|
||||
if (!ctx.TryReadInt32(onceAddress, out onceValue))
|
||||
if (!TryReadInt32(ctx, onceAddress, out onceValue))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
while (onceValue == PthreadOnceInProgress)
|
||||
{
|
||||
Monitor.Wait(gate, TimeSpan.FromMilliseconds(1));
|
||||
if (!ctx.TryReadInt32(onceAddress, out onceValue))
|
||||
if (!TryReadInt32(ctx, onceAddress, out onceValue))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
}
|
||||
|
||||
if (onceValue != PthreadOnceDone)
|
||||
{
|
||||
if (!ctx.TryWriteInt32(onceAddress, PthreadOnceInProgress))
|
||||
if (!TryWriteInt32(ctx, onceAddress, PthreadOnceInProgress))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
shouldCall = true;
|
||||
@@ -438,21 +434,21 @@ public static class KernelPthreadCompatExports
|
||||
{
|
||||
lock (gate)
|
||||
{
|
||||
_ = ctx.TryWriteInt32(onceAddress, PthreadOnceUninitialized);
|
||||
_ = TryWriteInt32(ctx, onceAddress, PthreadOnceUninitialized);
|
||||
Monitor.PulseAll(gate);
|
||||
}
|
||||
|
||||
TracePthreadOnce(onceAddress, initRoutine, "failed", error);
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_TRY_AGAIN);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_TRY_AGAIN);
|
||||
}
|
||||
|
||||
lock (gate)
|
||||
{
|
||||
if (!ctx.TryWriteInt32(onceAddress, PthreadOnceDone))
|
||||
if (!TryWriteInt32(ctx, onceAddress, PthreadOnceDone))
|
||||
{
|
||||
_ = ctx.TryWriteInt32(onceAddress, PthreadOnceUninitialized);
|
||||
_ = TryWriteInt32(ctx, onceAddress, PthreadOnceUninitialized);
|
||||
Monitor.PulseAll(gate);
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
Monitor.PulseAll(gate);
|
||||
@@ -460,7 +456,7 @@ public static class KernelPthreadCompatExports
|
||||
}
|
||||
|
||||
TracePthreadOnce(onceAddress, initRoutine, shouldCall ? "call" : "done", null);
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
}
|
||||
|
||||
private static int PthreadMutexInitCore(CpuContext ctx, ulong mutexAddress, ulong attrAddress)
|
||||
@@ -1022,12 +1018,19 @@ public static class KernelPthreadCompatExports
|
||||
}
|
||||
|
||||
private static bool InitializeMutexObject(CpuContext ctx, ulong address, PthreadMutexState state) =>
|
||||
ctx.TryWriteUInt32(address + 0x20, unchecked((uint)state.Type)) &&
|
||||
ctx.TryWriteUInt32(address + 0x3C, unchecked((uint)state.Protocol));
|
||||
TryWriteUInt32(ctx, address + 0x20, unchecked((uint)state.Type)) &&
|
||||
TryWriteUInt32(ctx, address + 0x3C, unchecked((uint)state.Protocol));
|
||||
|
||||
private static bool WriteMutexAttrObject(CpuContext ctx, ulong address, PthreadMutexAttrState state) =>
|
||||
ctx.TryWriteUInt32(address, unchecked((uint)state.Type)) &&
|
||||
ctx.TryWriteUInt32(address + 4, unchecked((uint)state.Protocol));
|
||||
TryWriteUInt32(ctx, address, unchecked((uint)state.Type)) &&
|
||||
TryWriteUInt32(ctx, address + 4, unchecked((uint)state.Protocol));
|
||||
|
||||
private static bool TryWriteUInt32(CpuContext ctx, ulong address, uint value)
|
||||
{
|
||||
Span<byte> bytes = stackalloc byte[sizeof(uint)];
|
||||
BitConverter.TryWriteBytes(bytes, value);
|
||||
return ctx.Memory.TryWrite(address, bytes);
|
||||
}
|
||||
|
||||
private static int PthreadCondInitCore(CpuContext ctx, ulong condAddress)
|
||||
{
|
||||
@@ -1319,6 +1322,32 @@ public static class KernelPthreadCompatExports
|
||||
}
|
||||
}
|
||||
|
||||
private static int SetReturn(CpuContext ctx, OrbisGen2Result result)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)(int)result);
|
||||
return (int)result;
|
||||
}
|
||||
|
||||
private static bool TryReadInt32(CpuContext ctx, ulong address, out int value)
|
||||
{
|
||||
Span<byte> bytes = stackalloc byte[sizeof(int)];
|
||||
if (!ctx.Memory.TryRead(address, bytes))
|
||||
{
|
||||
value = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = BinaryPrimitives.ReadInt32LittleEndian(bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryWriteInt32(CpuContext ctx, ulong address, int value)
|
||||
{
|
||||
Span<byte> bytes = stackalloc byte[sizeof(int)];
|
||||
BinaryPrimitives.WriteInt32LittleEndian(bytes, value);
|
||||
return ctx.Memory.TryWrite(address, bytes);
|
||||
}
|
||||
|
||||
private static bool CreateImplicitMutexState(CpuContext ctx, ulong mutexAddress, int type, out ulong resolvedAddress, [NotNullWhen(true)] out PthreadMutexState? state)
|
||||
{
|
||||
var createdState = new PthreadMutexState
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
using SharpEmu.HLE;
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace SharpEmu.Libs.Kernel;
|
||||
@@ -277,7 +279,7 @@ public static class KernelPthreadExtendedCompatExports
|
||||
priority = GetOrCreateThreadStateLocked(thread).Priority;
|
||||
}
|
||||
|
||||
if (!ctx.TryWriteInt32(outPriorityAddress, priority))
|
||||
if (!TryWriteInt32(ctx, outPriorityAddress, priority))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
@@ -324,7 +326,7 @@ public static class KernelPthreadExtendedCompatExports
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (!ctx.TryReadInt32(schedParamAddress, out var schedPriority))
|
||||
if (!TryReadInt32(ctx, schedParamAddress, out var schedPriority))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
@@ -494,7 +496,7 @@ public static class KernelPthreadExtendedCompatExports
|
||||
state = GetOrCreateAttrStateLocked(attrAddress);
|
||||
}
|
||||
|
||||
if (!ctx.TryWriteInt32(outStateAddress, state.DetachState))
|
||||
if (!TryWriteInt32(ctx, outStateAddress, state.DetachState))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
@@ -737,7 +739,7 @@ public static class KernelPthreadExtendedCompatExports
|
||||
state = GetOrCreateAttrStateLocked(attrAddress);
|
||||
}
|
||||
|
||||
if (!ctx.TryWriteInt32(schedParamAddress, state.SchedPriority))
|
||||
if (!TryWriteInt32(ctx, schedParamAddress, state.SchedPriority))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
@@ -760,7 +762,7 @@ public static class KernelPthreadExtendedCompatExports
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (!ctx.TryReadInt32(schedParamAddress, out var schedPriority))
|
||||
if (!TryReadInt32(ctx, schedParamAddress, out var schedPriority))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
@@ -1112,7 +1114,7 @@ public static class KernelPthreadExtendedCompatExports
|
||||
}
|
||||
}
|
||||
|
||||
if (!ctx.TryWriteInt32(outKeyAddress, key))
|
||||
if (!TryWriteInt32(ctx, outKeyAddress, key))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
@@ -1449,4 +1451,24 @@ public static class KernelPthreadExtendedCompatExports
|
||||
payload[^1] = 0;
|
||||
return ctx.Memory.TryWrite(address, payload);
|
||||
}
|
||||
|
||||
private static bool TryReadInt32(CpuContext ctx, ulong address, out int value)
|
||||
{
|
||||
Span<byte> bytes = stackalloc byte[sizeof(int)];
|
||||
if (!ctx.Memory.TryRead(address, bytes))
|
||||
{
|
||||
value = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = BinaryPrimitives.ReadInt32LittleEndian(bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryWriteInt32(CpuContext ctx, ulong address, int value)
|
||||
{
|
||||
Span<byte> bytes = stackalloc byte[sizeof(int)];
|
||||
BinaryPrimitives.WriteInt32LittleEndian(bytes, value);
|
||||
return ctx.Memory.TryWrite(address, bytes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,14 @@
|
||||
using SharpEmu.HLE;
|
||||
using SharpEmu.Libs.Fiber;
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace SharpEmu.Libs.Kernel;
|
||||
|
||||
@@ -256,8 +259,8 @@ public static class KernelRuntimeCompatExports
|
||||
}
|
||||
|
||||
if (timezoneAddress != 0 &&
|
||||
(!ctx.TryWriteInt32(timezoneAddress, 0) ||
|
||||
!ctx.TryWriteInt32(timezoneAddress + sizeof(int), 0)))
|
||||
(!TryWriteInt32(ctx, timezoneAddress, 0) ||
|
||||
!TryWriteInt32(ctx, timezoneAddress + sizeof(int), 0)))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
@@ -614,7 +617,7 @@ public static class KernelRuntimeCompatExports
|
||||
internal static bool TrySetErrno(CpuContext ctx, int value)
|
||||
{
|
||||
var address = GetTlsScratchAddress(ctx, TlsErrnoOffset);
|
||||
return address != 0 && ctx.TryWriteInt32(address, value);
|
||||
return address != 0 && TryWriteInt32(ctx, address, value);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -824,7 +827,7 @@ public static class KernelRuntimeCompatExports
|
||||
}
|
||||
|
||||
var moduleHandle = ResolveModuleHandleByAddress(queriedAddress);
|
||||
if (!ctx.TryWriteInt32(outInfoAddress + ModuleInfoHandleOffset, moduleHandle))
|
||||
if (!TryWriteInt32(ctx, outInfoAddress + ModuleInfoHandleOffset, moduleHandle))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
@@ -854,7 +857,7 @@ public static class KernelRuntimeCompatExports
|
||||
}
|
||||
|
||||
var moduleHandle = ResolveModuleHandleByAddress(queriedAddress);
|
||||
if (!ctx.TryWriteInt32(outInfoAddress + ModuleInfoHandleOffset, moduleHandle))
|
||||
if (!TryWriteInt32(ctx, outInfoAddress + ModuleInfoHandleOffset, moduleHandle))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
@@ -1055,7 +1058,7 @@ public static class KernelRuntimeCompatExports
|
||||
public static int KernelStopUnloadModule(CpuContext ctx)
|
||||
{
|
||||
var resultAddress = ctx[CpuRegister.R9];
|
||||
if (resultAddress != 0 && !ctx.TryWriteInt32(resultAddress, 0))
|
||||
if (resultAddress != 0 && !TryWriteInt32(ctx, resultAddress, 0))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
@@ -1073,7 +1076,7 @@ public static class KernelRuntimeCompatExports
|
||||
{
|
||||
var modulePathAddress = ctx[CpuRegister.Rdi];
|
||||
var resultAddress = ctx[CpuRegister.R9];
|
||||
if (resultAddress != 0 && !ctx.TryWriteInt32(resultAddress, 0))
|
||||
if (resultAddress != 0 && !TryWriteInt32(ctx, resultAddress, 0))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
@@ -1230,8 +1233,8 @@ public static class KernelRuntimeCompatExports
|
||||
: 0;
|
||||
var minutesWest = unchecked((int)-offset.TotalMinutes);
|
||||
|
||||
if (!ctx.TryWriteInt32(timezoneAddress, minutesWest) ||
|
||||
!ctx.TryWriteInt32(timezoneAddress + sizeof(int), dstSeconds / 60))
|
||||
if (!TryWriteInt32(ctx, timezoneAddress, minutesWest) ||
|
||||
!TryWriteInt32(ctx, timezoneAddress + sizeof(int), dstSeconds / 60))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
@@ -1241,7 +1244,7 @@ public static class KernelRuntimeCompatExports
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
if (dstSecondsAddress != 0 && !ctx.TryWriteInt32(dstSecondsAddress, dstSeconds))
|
||||
if (dstSecondsAddress != 0 && !TryWriteInt32(ctx, dstSecondsAddress, dstSeconds))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
@@ -1301,7 +1304,7 @@ public static class KernelRuntimeCompatExports
|
||||
_loadedSysmodules.Add(moduleId);
|
||||
}
|
||||
|
||||
if (resultAddress != 0 && !ctx.TryWriteInt32(resultAddress, 0))
|
||||
if (resultAddress != 0 && !TryWriteInt32(ctx, resultAddress, 0))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
@@ -1391,7 +1394,7 @@ public static class KernelRuntimeCompatExports
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (!ctx.TryWriteInt32(outInfoAddress + ModuleInfoHandleOffset, module.Handle))
|
||||
if (!TryWriteInt32(ctx, outInfoAddress + ModuleInfoHandleOffset, module.Handle))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
@@ -1428,7 +1431,7 @@ public static class KernelRuntimeCompatExports
|
||||
var writableCount = (int)Math.Min(Math.Min(capacity, (ulong)int.MaxValue), (ulong)handles.Length);
|
||||
for (var i = 0; i < writableCount; i++)
|
||||
{
|
||||
if (!ctx.TryWriteInt32(handlesAddress + (ulong)(i * sizeof(int)), handles[i]))
|
||||
if (!TryWriteInt32(ctx, handlesAddress + (ulong)(i * sizeof(int)), handles[i]))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
@@ -1501,6 +1504,13 @@ public static class KernelRuntimeCompatExports
|
||||
return unchecked(ctx.FsBase + offset);
|
||||
}
|
||||
|
||||
private static bool TryWriteInt32(CpuContext ctx, ulong address, int value)
|
||||
{
|
||||
Span<byte> bytes = stackalloc byte[sizeof(int)];
|
||||
BinaryPrimitives.WriteInt32LittleEndian(bytes, value);
|
||||
return ctx.Memory.TryWrite(address, bytes);
|
||||
}
|
||||
|
||||
private static nint AllocateStackChkGuardObject()
|
||||
{
|
||||
try
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Text;
|
||||
using SharpEmu.HLE;
|
||||
|
||||
namespace SharpEmu.Libs.Kernel;
|
||||
@@ -44,12 +46,12 @@ public static class KernelSemaphoreCompatExports
|
||||
initialCount > maxCount ||
|
||||
optionAddress != 0)
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
if (!ctx.TryReadNullTerminatedUtf8(nameAddress, MaxSemaphoreNameLength, out var name))
|
||||
if (!TryReadNullTerminatedUtf8(ctx, nameAddress, MaxSemaphoreNameLength, out var name))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
var handle = unchecked((uint)Interlocked.Increment(ref _nextSemaphoreHandle));
|
||||
@@ -66,14 +68,14 @@ public static class KernelSemaphoreCompatExports
|
||||
Count = initialCount,
|
||||
};
|
||||
|
||||
if (!ctx.TryWriteUInt32(semaphoreAddress, handle))
|
||||
if (!TryWriteUInt32(ctx, semaphoreAddress, handle))
|
||||
{
|
||||
_semaphores.TryRemove(handle, out _);
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
TraceSemaphore($"create handle=0x{handle:X8} name='{name}' attr=0x{attr:X} init={initialCount} max={maxCount}");
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -89,12 +91,12 @@ public static class KernelSemaphoreCompatExports
|
||||
|
||||
if (!_semaphores.TryGetValue(handle, out var semaphore))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (needCount < 1 || needCount > semaphore.MaxCount)
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
lock (semaphore.Gate)
|
||||
@@ -103,30 +105,30 @@ public static class KernelSemaphoreCompatExports
|
||||
{
|
||||
semaphore.Count -= needCount;
|
||||
TraceSemaphore($"wait handle=0x{handle:X8} name='{semaphore.Name}' need={needCount} count={semaphore.Count}");
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
}
|
||||
|
||||
if (timeoutAddress != 0)
|
||||
{
|
||||
if (!ctx.TryReadUInt32(timeoutAddress, out _))
|
||||
if (!TryReadUInt32(ctx, timeoutAddress, out _))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
_ = ctx.TryWriteUInt32(timeoutAddress, 0);
|
||||
_ = TryWriteUInt32(ctx, timeoutAddress, 0);
|
||||
TraceSemaphore($"wait-timeout handle=0x{handle:X8} name='{semaphore.Name}' need={needCount} count={semaphore.Count}");
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_TIMED_OUT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_TIMED_OUT);
|
||||
}
|
||||
|
||||
if (!GuestThreadExecution.RequestCurrentThreadBlock(ctx, "sceKernelWaitSema"))
|
||||
{
|
||||
TraceSemaphore($"wait-would-block handle=0x{handle:X8} name='{semaphore.Name}' need={needCount} count={semaphore.Count}");
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_TRY_AGAIN);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_TRY_AGAIN);
|
||||
}
|
||||
|
||||
semaphore.WaitingThreads++;
|
||||
TraceSemaphore($"wait-block handle=0x{handle:X8} name='{semaphore.Name}' need={needCount} count={semaphore.Count} waiters={semaphore.WaitingThreads}");
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,12 +144,12 @@ public static class KernelSemaphoreCompatExports
|
||||
|
||||
if (!_semaphores.TryGetValue(handle, out var semaphore))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (needCount < 1 || needCount > semaphore.MaxCount)
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
lock (semaphore.Gate)
|
||||
@@ -155,12 +157,12 @@ public static class KernelSemaphoreCompatExports
|
||||
if (semaphore.Count < needCount)
|
||||
{
|
||||
TraceSemaphore($"poll-busy handle=0x{handle:X8} name='{semaphore.Name}' need={needCount} count={semaphore.Count}");
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_BUSY);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_BUSY);
|
||||
}
|
||||
|
||||
semaphore.Count -= needCount;
|
||||
TraceSemaphore($"poll handle=0x{handle:X8} name='{semaphore.Name}' need={needCount} count={semaphore.Count}");
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,24 +178,24 @@ public static class KernelSemaphoreCompatExports
|
||||
|
||||
if (!_semaphores.TryGetValue(handle, out var semaphore))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (signalCount <= 0)
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
lock (semaphore.Gate)
|
||||
{
|
||||
if (semaphore.Count > semaphore.MaxCount - signalCount)
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
semaphore.Count += signalCount;
|
||||
TraceSemaphore($"signal handle=0x{handle:X8} name='{semaphore.Name}' signal={signalCount} count={semaphore.Count} waiters={semaphore.WaitingThreads}");
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,25 +212,25 @@ public static class KernelSemaphoreCompatExports
|
||||
|
||||
if (!_semaphores.TryGetValue(handle, out var semaphore))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (setCount > semaphore.MaxCount)
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
lock (semaphore.Gate)
|
||||
{
|
||||
if (waitingThreadsAddress != 0 && !ctx.TryWriteUInt32(waitingThreadsAddress, unchecked((uint)semaphore.WaitingThreads)))
|
||||
if (waitingThreadsAddress != 0 && !TryWriteUInt32(ctx, waitingThreadsAddress, unchecked((uint)semaphore.WaitingThreads)))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
semaphore.Count = setCount < 0 ? semaphore.InitialCount : setCount;
|
||||
semaphore.WaitingThreads = 0;
|
||||
TraceSemaphore($"cancel handle=0x{handle:X8} name='{semaphore.Name}' set={setCount} count={semaphore.Count}");
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,11 +244,68 @@ public static class KernelSemaphoreCompatExports
|
||||
var handle = unchecked((uint)ctx[CpuRegister.Rdi]);
|
||||
if (!_semaphores.TryRemove(handle, out var semaphore))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
|
||||
}
|
||||
|
||||
TraceSemaphore($"delete handle=0x{handle:X8} name='{semaphore.Name}'");
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
}
|
||||
|
||||
private static int SetReturn(CpuContext ctx, OrbisGen2Result result)
|
||||
{
|
||||
var value = (int)result;
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)value);
|
||||
return value;
|
||||
}
|
||||
|
||||
private static bool TryReadUInt32(CpuContext ctx, ulong address, out uint value)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[sizeof(uint)];
|
||||
if (!ctx.Memory.TryRead(address, buffer))
|
||||
{
|
||||
value = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = BinaryPrimitives.ReadUInt32LittleEndian(buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryWriteUInt32(CpuContext ctx, ulong address, uint value)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[sizeof(uint)];
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer, value);
|
||||
return ctx.Memory.TryWrite(address, buffer);
|
||||
}
|
||||
|
||||
private static bool TryReadNullTerminatedUtf8(CpuContext ctx, ulong address, int maxLength, out string value)
|
||||
{
|
||||
value = string.Empty;
|
||||
if (address == 0 || maxLength <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var bytes = new byte[Math.Min(maxLength, 4096)];
|
||||
for (var i = 0; i < bytes.Length; i++)
|
||||
{
|
||||
Span<byte> current = stackalloc byte[1];
|
||||
if (!ctx.Memory.TryRead(address + (ulong)i, current))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (current[0] == 0)
|
||||
{
|
||||
value = Encoding.UTF8.GetString(bytes, 0, i);
|
||||
return true;
|
||||
}
|
||||
|
||||
bytes[i] = current[0];
|
||||
}
|
||||
|
||||
value = Encoding.UTF8.GetString(bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void TraceSemaphore(string message)
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
using SharpEmu.HLE;
|
||||
|
||||
namespace SharpEmu.Libs.Network;
|
||||
@@ -30,7 +32,7 @@ public static class Http2Exports
|
||||
|
||||
if (poolSize == 0 || maxRequests <= 0)
|
||||
{
|
||||
return ctx.SetReturn(Http2ErrorInvalidArgument);
|
||||
return SetReturn(ctx, Http2ErrorInvalidArgument);
|
||||
}
|
||||
|
||||
var id = Interlocked.Increment(ref _nextContextId);
|
||||
@@ -51,11 +53,17 @@ public static class Http2Exports
|
||||
var id = unchecked((int)ctx[CpuRegister.Rdi]);
|
||||
if (!_contexts.TryRemove(id, out _))
|
||||
{
|
||||
return ctx.SetReturn(Http2ErrorInvalidId);
|
||||
return SetReturn(ctx, Http2ErrorInvalidId);
|
||||
}
|
||||
|
||||
TraceHttp2("term", id, 0, 0, 0, 0);
|
||||
return ctx.SetReturn(0);
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
|
||||
private static int SetReturn(CpuContext ctx, int result)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void TraceHttp2(string operation, int id, ulong arg0, ulong arg1, ulong arg2, ulong arg3)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using SharpEmu.HLE;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
|
||||
namespace SharpEmu.Libs.Network;
|
||||
|
||||
@@ -32,7 +33,7 @@ public static class HttpExports
|
||||
var poolSize = ctx[CpuRegister.Rdx];
|
||||
if (poolSize == 0)
|
||||
{
|
||||
return ctx.SetReturn(HttpErrorInvalidValue);
|
||||
return SetReturn(ctx, HttpErrorInvalidValue);
|
||||
}
|
||||
|
||||
var id = Interlocked.Increment(ref _nextContextId);
|
||||
@@ -52,7 +53,7 @@ public static class HttpExports
|
||||
var contextId = unchecked((int)ctx[CpuRegister.Rdi]);
|
||||
if (!Contexts.ContainsKey(contextId))
|
||||
{
|
||||
return ctx.SetReturn(HttpErrorInvalidId);
|
||||
return SetReturn(ctx, HttpErrorInvalidId);
|
||||
}
|
||||
|
||||
var userAgentAddress = ctx[CpuRegister.Rsi];
|
||||
@@ -74,8 +75,8 @@ public static class HttpExports
|
||||
{
|
||||
var templateId = unchecked((int)ctx[CpuRegister.Rdi]);
|
||||
return Templates.TryRemove(templateId, out _)
|
||||
? ctx.SetReturn(0)
|
||||
: ctx.SetReturn(HttpErrorInvalidId);
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, HttpErrorInvalidId);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -88,7 +89,7 @@ public static class HttpExports
|
||||
var contextId = unchecked((int)ctx[CpuRegister.Rdi]);
|
||||
if (!Contexts.TryRemove(contextId, out _))
|
||||
{
|
||||
return ctx.SetReturn(HttpErrorInvalidId);
|
||||
return SetReturn(ctx, HttpErrorInvalidId);
|
||||
}
|
||||
|
||||
foreach (var pair in Templates)
|
||||
@@ -99,7 +100,13 @@ public static class HttpExports
|
||||
}
|
||||
}
|
||||
|
||||
return ctx.SetReturn(0);
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
|
||||
private static int SetReturn(CpuContext ctx, int result)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void TraceHttp(string operation, int id, ulong arg0, ulong arg1, ulong arg2, ulong arg3)
|
||||
|
||||
@@ -57,27 +57,27 @@ public static class NetCtlExports
|
||||
var natInfoAddress = ctx[CpuRegister.Rdi];
|
||||
if (natInfoAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn(NetCtlErrorInvalidAddress, typeof(long));
|
||||
return SetReturn(ctx, NetCtlErrorInvalidAddress);
|
||||
}
|
||||
|
||||
Span<byte> natInfo = stackalloc byte[NatInfoSize];
|
||||
if (!ctx.Memory.TryRead(natInfoAddress, natInfo))
|
||||
{
|
||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT, typeof(long));
|
||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
var size = BinaryPrimitives.ReadUInt32LittleEndian(natInfo[..sizeof(uint)]);
|
||||
if (size != NatInfoSize)
|
||||
{
|
||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT, typeof(long));
|
||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
BinaryPrimitives.WriteInt32LittleEndian(natInfo[4..], 1);
|
||||
BinaryPrimitives.WriteInt32LittleEndian(natInfo[8..], 3);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(natInfo[12..], 0x7F000001);
|
||||
return ctx.Memory.TryWrite(natInfoAddress, natInfo)
|
||||
? ctx.SetReturn(0, typeof(long))
|
||||
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT, typeof(long));
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -87,7 +87,7 @@ public static class NetCtlExports
|
||||
LibraryName = "libSceNetCtl")]
|
||||
public static int NetCtlCheckCallback(CpuContext ctx)
|
||||
{
|
||||
return ctx.SetReturn(0, typeof(long));
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -100,14 +100,14 @@ public static class NetCtlExports
|
||||
var stateAddress = ctx[CpuRegister.Rdi];
|
||||
if (stateAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn(NetCtlErrorInvalidAddress, typeof(long));
|
||||
return SetReturn(ctx, NetCtlErrorInvalidAddress);
|
||||
}
|
||||
|
||||
Span<byte> stateBytes = stackalloc byte[sizeof(int)];
|
||||
BinaryPrimitives.WriteInt32LittleEndian(stateBytes, 0);
|
||||
return ctx.Memory.TryWrite(stateAddress, stateBytes)
|
||||
? ctx.SetReturn(0, typeof(long))
|
||||
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT, typeof(long));
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -122,7 +122,7 @@ public static class NetCtlExports
|
||||
var callbackIdAddress = ctx[CpuRegister.Rdx];
|
||||
if (function == 0 || callbackIdAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn(NetCtlErrorInvalidAddress, typeof(long));
|
||||
return SetReturn(ctx, NetCtlErrorInvalidAddress);
|
||||
}
|
||||
|
||||
lock (CallbackGate)
|
||||
@@ -130,20 +130,20 @@ public static class NetCtlExports
|
||||
var callbackId = Array.FindIndex(Callbacks, static callback => callback.Function == 0);
|
||||
if (callbackId < 0)
|
||||
{
|
||||
return ctx.SetReturn(NetCtlErrorNoSpace, typeof(long));
|
||||
return SetReturn(ctx, NetCtlErrorNoSpace);
|
||||
}
|
||||
|
||||
Span<byte> callbackIdBytes = stackalloc byte[sizeof(uint)];
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(callbackIdBytes, unchecked((uint)callbackId));
|
||||
if (!ctx.Memory.TryWrite(callbackIdAddress, callbackIdBytes))
|
||||
{
|
||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT, typeof(long));
|
||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
Callbacks[callbackId] = new CallbackRegistration(function, argument);
|
||||
}
|
||||
|
||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_OK, typeof(long));
|
||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -157,7 +157,7 @@ public static class NetCtlExports
|
||||
var infoAddress = ctx[CpuRegister.Rsi];
|
||||
if (infoAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn(NetCtlErrorInvalidAddress, typeof(long));
|
||||
return SetReturn(ctx, NetCtlErrorInvalidAddress);
|
||||
}
|
||||
|
||||
return code switch
|
||||
@@ -177,7 +177,7 @@ public static class NetCtlExports
|
||||
NetCtlInfoHttpProxyConfig => WriteUInt32(ctx, infoAddress, 0),
|
||||
NetCtlInfoHttpProxyServer => WriteAsciiZ(ctx, infoAddress, string.Empty, 256),
|
||||
NetCtlInfoHttpProxyPort => WriteUInt16(ctx, infoAddress, 0),
|
||||
_ => ctx.SetReturn(NetCtlErrorNotConnected, typeof(long)),
|
||||
_ => SetReturn(ctx, NetCtlErrorNotConnected),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -185,8 +185,8 @@ public static class NetCtlExports
|
||||
{
|
||||
Span<byte> bytes = stackalloc byte[count];
|
||||
return ctx.Memory.TryWrite(address, bytes)
|
||||
? ctx.SetReturn(0, typeof(long))
|
||||
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT, typeof(long));
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
private static int WriteUInt32(CpuContext ctx, ulong address, uint value)
|
||||
@@ -194,8 +194,8 @@ public static class NetCtlExports
|
||||
Span<byte> bytes = stackalloc byte[sizeof(uint)];
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(bytes, value);
|
||||
return ctx.Memory.TryWrite(address, bytes)
|
||||
? ctx.SetReturn(0, typeof(long))
|
||||
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT, typeof(long));
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
private static int WriteUInt16(CpuContext ctx, ulong address, ushort value)
|
||||
@@ -203,8 +203,8 @@ public static class NetCtlExports
|
||||
Span<byte> bytes = stackalloc byte[sizeof(ushort)];
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(bytes, value);
|
||||
return ctx.Memory.TryWrite(address, bytes)
|
||||
? ctx.SetReturn(0, typeof(long))
|
||||
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT, typeof(long));
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
private static int WriteAsciiZ(CpuContext ctx, ulong address, string value, int byteCount)
|
||||
@@ -217,7 +217,13 @@ public static class NetCtlExports
|
||||
}
|
||||
|
||||
return ctx.Memory.TryWrite(address, bytes)
|
||||
? ctx.SetReturn(0, typeof(long))
|
||||
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT, typeof(long));
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
private static int SetReturn(CpuContext ctx, int result)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)(long)result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using SharpEmu.HLE;
|
||||
|
||||
namespace SharpEmu.Libs.Network;
|
||||
@@ -33,7 +35,7 @@ public static class NetExports
|
||||
{
|
||||
_initialized = true;
|
||||
TraceNet("init", 0, 0, 0, 0);
|
||||
return ctx.SetReturn(0);
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -47,7 +49,7 @@ public static class NetExports
|
||||
_pools.Clear();
|
||||
_resolvers.Clear();
|
||||
TraceNet("term", 0, 0, 0, 0);
|
||||
return ctx.SetReturn(0);
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -63,7 +65,7 @@ public static class NetExports
|
||||
|
||||
if (size <= 0)
|
||||
{
|
||||
return ctx.SetReturn(NetErrorInvalidArgument);
|
||||
return SetReturn(ctx, NetErrorInvalidArgument);
|
||||
}
|
||||
|
||||
var name = TryReadUtf8Z(ctx, nameAddress, MaxNameLength, out var value)
|
||||
@@ -88,11 +90,11 @@ public static class NetExports
|
||||
var id = unchecked((int)ctx[CpuRegister.Rdi]);
|
||||
if (!_pools.TryRemove(id, out _))
|
||||
{
|
||||
return ctx.SetReturn(NetErrorBadFileDescriptor);
|
||||
return SetReturn(ctx, NetErrorBadFileDescriptor);
|
||||
}
|
||||
|
||||
TraceNet("pool.destroy", id, 0, 0, _initialized ? 1UL : 0UL);
|
||||
return ctx.SetReturn(0);
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -107,7 +109,7 @@ public static class NetExports
|
||||
var flags = unchecked((int)ctx[CpuRegister.Rdx]);
|
||||
if (flags != 0)
|
||||
{
|
||||
return ctx.SetReturn(NetErrorInvalidArgument);
|
||||
return SetReturn(ctx, NetErrorInvalidArgument);
|
||||
}
|
||||
|
||||
var name = TryReadUtf8Z(ctx, nameAddress, MaxNameLength, out var value)
|
||||
@@ -129,8 +131,8 @@ public static class NetExports
|
||||
{
|
||||
var id = unchecked((int)ctx[CpuRegister.Rdi]);
|
||||
return _resolvers.TryRemove(id, out _)
|
||||
? ctx.SetReturn(0)
|
||||
: ctx.SetReturn(NetErrorBadFileDescriptor);
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, NetErrorBadFileDescriptor);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -144,19 +146,25 @@ public static class NetExports
|
||||
var statusAddress = ctx[CpuRegister.Rsi];
|
||||
if (statusAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn(NetErrorInvalidArgument);
|
||||
return SetReturn(ctx, NetErrorInvalidArgument);
|
||||
}
|
||||
|
||||
if (!_resolvers.TryGetValue(id, out var resolver))
|
||||
{
|
||||
return ctx.SetReturn(NetErrorBadFileDescriptor);
|
||||
return SetReturn(ctx, NetErrorBadFileDescriptor);
|
||||
}
|
||||
|
||||
Span<byte> status = stackalloc byte[sizeof(int)];
|
||||
BinaryPrimitives.WriteInt32LittleEndian(status, resolver.LastError);
|
||||
return ctx.Memory.TryWrite(statusAddress, status)
|
||||
? ctx.SetReturn(0)
|
||||
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
private static int SetReturn(CpuContext ctx, int result)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static bool TryReadUtf8Z(CpuContext ctx, ulong address, int maxLength, out string value)
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
using SharpEmu.HLE;
|
||||
|
||||
namespace SharpEmu.Libs.Network;
|
||||
@@ -26,7 +28,7 @@ public static class SslExports
|
||||
var poolSize = ctx[CpuRegister.Rdi];
|
||||
if (poolSize == 0)
|
||||
{
|
||||
return ctx.SetReturn(SslErrorOutOfSize);
|
||||
return SetReturn(ctx, SslErrorOutOfSize);
|
||||
}
|
||||
|
||||
var id = Interlocked.Increment(ref _nextContextId);
|
||||
@@ -47,11 +49,11 @@ public static class SslExports
|
||||
var id = unchecked((int)ctx[CpuRegister.Rdi]);
|
||||
if (!_contexts.TryRemove(id, out _))
|
||||
{
|
||||
return ctx.SetReturn(SslErrorInvalidId);
|
||||
return SetReturn(ctx, SslErrorInvalidId);
|
||||
}
|
||||
|
||||
TraceSsl("term", id, 0);
|
||||
return ctx.SetReturn(0);
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -63,7 +65,13 @@ public static class SslExports
|
||||
{
|
||||
var id = unchecked((int)ctx[CpuRegister.Rdi]);
|
||||
TraceSsl("close", id, 0);
|
||||
return ctx.SetReturn(0);
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
|
||||
private static int SetReturn(CpuContext ctx, int result)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void TraceSsl(string operation, int id, ulong arg0)
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using SharpEmu.HLE;
|
||||
using SharpEmu.Libs.Kernel;
|
||||
using System.Buffers.Binary;
|
||||
using System.Threading;
|
||||
|
||||
namespace SharpEmu.Libs.Ngs2;
|
||||
|
||||
@@ -38,13 +39,13 @@ public static class Ngs2Exports
|
||||
var outHandleAddress = ctx[CpuRegister.Rdx];
|
||||
if (outHandleAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn(OrbisNgs2ErrorInvalidOutAddress);
|
||||
return SetReturn(ctx, OrbisNgs2ErrorInvalidOutAddress);
|
||||
}
|
||||
|
||||
if (!TryCreateHandle(ctx, type: 1, ownerHandle: 0, out var handle) ||
|
||||
!ctx.TryWriteUInt64(outHandleAddress, handle))
|
||||
{
|
||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
lock (StateGate)
|
||||
@@ -52,7 +53,7 @@ public static class Ngs2Exports
|
||||
Systems[handle] = new SystemState(unchecked((uint)Interlocked.Increment(ref _nextUid)));
|
||||
}
|
||||
|
||||
return ctx.SetReturn(0);
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -67,7 +68,7 @@ public static class Ngs2Exports
|
||||
{
|
||||
if (!Systems.Remove(handle))
|
||||
{
|
||||
return ctx.SetReturn(OrbisNgs2ErrorInvalidSystemHandle);
|
||||
return SetReturn(ctx, OrbisNgs2ErrorInvalidSystemHandle);
|
||||
}
|
||||
|
||||
var rackHandles = Racks
|
||||
@@ -80,7 +81,7 @@ public static class Ngs2Exports
|
||||
}
|
||||
}
|
||||
|
||||
return ctx.SetReturn(0);
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -97,19 +98,19 @@ public static class Ngs2Exports
|
||||
{
|
||||
if (!Systems.ContainsKey(systemHandle))
|
||||
{
|
||||
return ctx.SetReturn(OrbisNgs2ErrorInvalidSystemHandle);
|
||||
return SetReturn(ctx, OrbisNgs2ErrorInvalidSystemHandle);
|
||||
}
|
||||
}
|
||||
|
||||
if (outHandleAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn(OrbisNgs2ErrorInvalidOutAddress);
|
||||
return SetReturn(ctx, OrbisNgs2ErrorInvalidOutAddress);
|
||||
}
|
||||
|
||||
if (!TryCreateHandle(ctx, type: 2, systemHandle, out var handle) ||
|
||||
!ctx.TryWriteUInt64(outHandleAddress, handle))
|
||||
{
|
||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
lock (StateGate)
|
||||
@@ -117,7 +118,7 @@ public static class Ngs2Exports
|
||||
Racks[handle] = new RackState(systemHandle, rackId);
|
||||
}
|
||||
|
||||
return ctx.SetReturn(0);
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -132,13 +133,13 @@ public static class Ngs2Exports
|
||||
{
|
||||
if (!Racks.ContainsKey(handle))
|
||||
{
|
||||
return ctx.SetReturn(OrbisNgs2ErrorInvalidRackHandle);
|
||||
return SetReturn(ctx, OrbisNgs2ErrorInvalidRackHandle);
|
||||
}
|
||||
|
||||
RemoveRackLocked(handle);
|
||||
}
|
||||
|
||||
return ctx.SetReturn(0);
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -155,7 +156,7 @@ public static class Ngs2Exports
|
||||
{
|
||||
if (!Racks.ContainsKey(rackHandle))
|
||||
{
|
||||
return ctx.SetReturn(OrbisNgs2ErrorInvalidRackHandle);
|
||||
return SetReturn(ctx, OrbisNgs2ErrorInvalidRackHandle);
|
||||
}
|
||||
|
||||
var existing = Voices.FirstOrDefault(
|
||||
@@ -163,20 +164,20 @@ public static class Ngs2Exports
|
||||
if (existing.Key != 0)
|
||||
{
|
||||
return ctx.TryWriteUInt64(outHandleAddress, existing.Key)
|
||||
? ctx.SetReturn(0)
|
||||
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
}
|
||||
|
||||
if (outHandleAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn(OrbisNgs2ErrorInvalidOutAddress);
|
||||
return SetReturn(ctx, OrbisNgs2ErrorInvalidOutAddress);
|
||||
}
|
||||
|
||||
if (!TryCreateHandle(ctx, type: 4, rackHandle, out var handle) ||
|
||||
!ctx.TryWriteUInt64(outHandleAddress, handle))
|
||||
{
|
||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
lock (StateGate)
|
||||
@@ -184,7 +185,7 @@ public static class Ngs2Exports
|
||||
Voices[handle] = new VoiceState(rackHandle, voiceIndex);
|
||||
}
|
||||
|
||||
return ctx.SetReturn(0);
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -196,10 +197,9 @@ public static class Ngs2Exports
|
||||
{
|
||||
lock (StateGate)
|
||||
{
|
||||
return ctx.SetReturn(
|
||||
Voices.ContainsKey(ctx[CpuRegister.Rdi])
|
||||
? 0
|
||||
: OrbisNgs2ErrorInvalidVoiceHandle);
|
||||
return SetReturn(
|
||||
ctx,
|
||||
Voices.ContainsKey(ctx[CpuRegister.Rdi]) ? 0 : OrbisNgs2ErrorInvalidVoiceHandle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,13 +224,13 @@ public static class Ngs2Exports
|
||||
{
|
||||
if (!Systems.ContainsKey(systemHandle))
|
||||
{
|
||||
return ctx.SetReturn(OrbisNgs2ErrorInvalidSystemHandle);
|
||||
return SetReturn(ctx, OrbisNgs2ErrorInvalidSystemHandle);
|
||||
}
|
||||
}
|
||||
|
||||
if (bufferInfoCount != 0 && bufferInfoAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
for (uint i = 0; i < bufferInfoCount; i++)
|
||||
@@ -239,14 +239,14 @@ public static class Ngs2Exports
|
||||
if (!ctx.TryReadUInt64(entryAddress, out var bufferAddress) ||
|
||||
!ctx.TryReadUInt64(entryAddress + 8, out var bufferSize))
|
||||
{
|
||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
if (bufferAddress != 0 && bufferSize != 0)
|
||||
{
|
||||
if (bufferSize > MaximumRenderBufferSize || !TryClearGuestBuffer(ctx, bufferAddress, bufferSize))
|
||||
{
|
||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -258,7 +258,7 @@ public static class Ngs2Exports
|
||||
$"[LOADER][TRACE] ngs2.render#{count} system=0x{systemHandle:X16} buffers={bufferInfoCount}");
|
||||
}
|
||||
|
||||
return ctx.SetReturn(0);
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
|
||||
private static bool TryCreateHandle(CpuContext ctx, uint type, ulong ownerHandle, out ulong handle)
|
||||
@@ -313,4 +313,10 @@ public static class Ngs2Exports
|
||||
Environment.GetEnvironmentVariable("SHARPEMU_LOG_NGS2"),
|
||||
"1",
|
||||
StringComparison.Ordinal);
|
||||
|
||||
private static int SetReturn(CpuContext ctx, int result)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,12 +26,12 @@ public static class NpEntitlementAccessExports
|
||||
clear.Clear();
|
||||
if (!ctx.Memory.TryWrite(bootParam, clear))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
}
|
||||
|
||||
TraceNpEntitlementAccess($"initialize init=0x{initParam:X16} boot=0x{bootParam:X16}");
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -48,14 +48,20 @@ public static class NpEntitlementAccessExports
|
||||
emptyList.Clear();
|
||||
if (!ctx.Memory.TryWrite(listAddress, emptyList))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
}
|
||||
|
||||
TraceNpEntitlementAccess(
|
||||
$"get_addcont_info_list service=0x{ctx[CpuRegister.Rdi]:X16} list=0x{listAddress:X16} " +
|
||||
$"max={ctx[CpuRegister.Rdx]} flags=0x{ctx[CpuRegister.Rcx]:X16} -> empty");
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
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 void TraceNpEntitlementAccess(string message)
|
||||
|
||||
@@ -76,14 +76,14 @@ public static class NpManagerExports
|
||||
var stateAddress = ctx[CpuRegister.Rsi];
|
||||
if (stateAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
Span<byte> stateBytes = stackalloc byte[sizeof(uint)];
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(stateBytes, 1);
|
||||
return ctx.Memory.TryWrite(stateAddress, stateBytes)
|
||||
? ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK)
|
||||
: ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
? SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK)
|
||||
: SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -97,7 +97,7 @@ public static class NpManagerExports
|
||||
var titleSecretAddress = ctx[CpuRegister.Rsi];
|
||||
if (titleIdAddress == 0 || titleSecretAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
Span<byte> titleId = stackalloc byte[NpTitleIdSize];
|
||||
@@ -105,11 +105,17 @@ public static class NpManagerExports
|
||||
if (!ctx.Memory.TryRead(titleIdAddress, titleId) ||
|
||||
!ctx.Memory.TryRead(titleSecretAddress, titleSecret))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
TraceNp($"set_np_title_id title='{ReadTitleId(titleId)}'");
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
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 string ReadTitleId(ReadOnlySpan<byte> bytes)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using SharpEmu.HLE;
|
||||
using System.Buffers.Binary;
|
||||
using System.Threading;
|
||||
|
||||
namespace SharpEmu.Libs.Np;
|
||||
|
||||
@@ -21,13 +22,13 @@ public static class NpUniversalDataSystemExports
|
||||
var parameterAddress = ctx[CpuRegister.Rdi];
|
||||
if (parameterAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn(NpUniversalDataSystemErrorInvalidArgument, typeof(long));
|
||||
return SetReturn(ctx, NpUniversalDataSystemErrorInvalidArgument);
|
||||
}
|
||||
|
||||
Span<byte> parameters = stackalloc byte[16];
|
||||
return ctx.Memory.TryRead(parameterAddress, parameters)
|
||||
? ctx.SetReturn(0, typeof(long))
|
||||
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT, typeof(long));
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -40,14 +41,14 @@ public static class NpUniversalDataSystemExports
|
||||
var contextAddress = ctx[CpuRegister.Rdi];
|
||||
if (contextAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn(0, typeof(long));
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
|
||||
Span<byte> context = stackalloc byte[sizeof(int)];
|
||||
BinaryPrimitives.WriteInt32LittleEndian(context, 1);
|
||||
return ctx.Memory.TryWrite(contextAddress, context)
|
||||
? ctx.SetReturn(0, typeof(long))
|
||||
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT, typeof(long));
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -58,13 +59,13 @@ public static class NpUniversalDataSystemExports
|
||||
public static int NpUniversalDataSystemCreateHandle(CpuContext ctx)
|
||||
{
|
||||
var handle = Interlocked.Increment(ref _nextHandle);
|
||||
if (ctx.TryWriteInt32(ctx[CpuRegister.Rdi], handle, checkNil: true) ||
|
||||
ctx.TryWriteInt32(ctx[CpuRegister.Rsi], handle, checkNil: true))
|
||||
if (TryWriteInt32(ctx, ctx[CpuRegister.Rdi], handle) ||
|
||||
TryWriteInt32(ctx, ctx[CpuRegister.Rsi], handle))
|
||||
{
|
||||
return ctx.SetReturn(0, typeof(long));
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
|
||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT, typeof(long));
|
||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -74,6 +75,24 @@ public static class NpUniversalDataSystemExports
|
||||
LibraryName = "libSceNpUniversalDataSystem")]
|
||||
public static int NpUniversalDataSystemRegisterContext(CpuContext ctx)
|
||||
{
|
||||
return ctx.SetReturn(0, typeof(long));
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
|
||||
private static bool TryWriteInt32(CpuContext ctx, ulong address, int value)
|
||||
{
|
||||
if (address == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Span<byte> bytes = stackalloc byte[sizeof(int)];
|
||||
BinaryPrimitives.WriteInt32LittleEndian(bytes, value);
|
||||
return ctx.Memory.TryWrite(address, bytes);
|
||||
}
|
||||
|
||||
private static int SetReturn(CpuContext ctx, int result)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)(long)result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using SharpEmu.HLE;
|
||||
|
||||
namespace SharpEmu.Libs.Np;
|
||||
@@ -23,12 +25,12 @@ public static class NpWebApi2Exports
|
||||
|
||||
if (httpContextId <= 0 || poolSize == 0)
|
||||
{
|
||||
return ctx.SetReturn(NpWebApi2ErrorInvalidArgument);
|
||||
return SetReturn(ctx, NpWebApi2ErrorInvalidArgument);
|
||||
}
|
||||
|
||||
Interlocked.Exchange(ref _initialized, 1);
|
||||
TraceNpWebApi2("init", httpContextId, poolSize);
|
||||
return ctx.SetReturn(0);
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -41,7 +43,13 @@ public static class NpWebApi2Exports
|
||||
var libraryContextId = unchecked((int)ctx[CpuRegister.Rdi]);
|
||||
Interlocked.Exchange(ref _initialized, 0);
|
||||
TraceNpWebApi2("term", libraryContextId, 0);
|
||||
return ctx.SetReturn(0);
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
|
||||
private static int SetReturn(CpuContext ctx, int result)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void TraceNpWebApi2(string operation, int id, ulong arg0)
|
||||
|
||||
@@ -30,7 +30,7 @@ public static class PadExports
|
||||
public static int PadInit(CpuContext ctx)
|
||||
{
|
||||
_initialized = true;
|
||||
return ctx.SetReturn(0);
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -46,21 +46,21 @@ public static class PadExports
|
||||
var parameterAddress = ctx[CpuRegister.Rcx];
|
||||
if (!_initialized)
|
||||
{
|
||||
return ctx.SetReturn(OrbisPadErrorNotInitialized);
|
||||
return SetReturn(ctx, OrbisPadErrorNotInitialized);
|
||||
}
|
||||
|
||||
if (userId == -1)
|
||||
{
|
||||
return ctx.SetReturn(OrbisPadErrorDeviceNoHandle);
|
||||
return SetReturn(ctx, OrbisPadErrorDeviceNoHandle);
|
||||
}
|
||||
|
||||
if (userId != PrimaryUserId || type != StandardPortType || index != 0 || parameterAddress != 0)
|
||||
{
|
||||
return ctx.SetReturn(OrbisPadErrorDeviceNotConnected);
|
||||
return SetReturn(ctx, OrbisPadErrorDeviceNotConnected);
|
||||
}
|
||||
|
||||
Console.Error.WriteLine("[LOADER][INFO] Keyboard controls: Arrow keys = D-pad, WASD = left stick, IJKL = right stick, Z/Enter = Cross, X/Esc = Circle, C = Square, V = Triangle, Q = L1, E = R1, R = L2, F = R2, Tab/Backspace = Options");
|
||||
return ctx.SetReturn(PrimaryPadHandle);
|
||||
return SetReturn(ctx, PrimaryPadHandle);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -72,8 +72,8 @@ public static class PadExports
|
||||
{
|
||||
var handle = unchecked((int)ctx[CpuRegister.Rdi]);
|
||||
return handle == PrimaryPadHandle
|
||||
? ctx.SetReturn(0)
|
||||
: ctx.SetReturn(OrbisPadErrorInvalidHandle);
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, OrbisPadErrorInvalidHandle);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -87,12 +87,12 @@ public static class PadExports
|
||||
var informationAddress = ctx[CpuRegister.Rsi];
|
||||
if (handle != PrimaryPadHandle)
|
||||
{
|
||||
return ctx.SetReturn(OrbisPadErrorInvalidHandle);
|
||||
return SetReturn(ctx, OrbisPadErrorInvalidHandle);
|
||||
}
|
||||
|
||||
if (informationAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
Span<byte> information = stackalloc byte[ControllerInformationSize];
|
||||
@@ -107,8 +107,8 @@ public static class PadExports
|
||||
BinaryPrimitives.WriteInt32LittleEndian(information[0x10..], 0);
|
||||
|
||||
return ctx.Memory.TryWrite(informationAddress, information)
|
||||
? ctx.SetReturn(0)
|
||||
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -122,17 +122,17 @@ public static class PadExports
|
||||
var dataAddress = ctx[CpuRegister.Rsi];
|
||||
if (handle != PrimaryPadHandle)
|
||||
{
|
||||
return ctx.SetReturn(OrbisPadErrorInvalidHandle);
|
||||
return SetReturn(ctx, OrbisPadErrorInvalidHandle);
|
||||
}
|
||||
|
||||
if (dataAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
return WriteNeutralPadData(ctx, dataAddress)
|
||||
? ctx.SetReturn(0)
|
||||
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -147,17 +147,17 @@ public static class PadExports
|
||||
var count = unchecked((int)ctx[CpuRegister.Rdx]);
|
||||
if (handle != PrimaryPadHandle)
|
||||
{
|
||||
return ctx.SetReturn(OrbisPadErrorInvalidHandle);
|
||||
return SetReturn(ctx, OrbisPadErrorInvalidHandle);
|
||||
}
|
||||
|
||||
if (dataAddress == 0 || count < 1 || count > 64)
|
||||
{
|
||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
return WriteNeutralPadData(ctx, dataAddress)
|
||||
? ctx.SetReturn(1)
|
||||
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
? SetReturn(ctx, 1)
|
||||
: SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -167,7 +167,7 @@ public static class PadExports
|
||||
LibraryName = "libScePad")]
|
||||
public static int PadSetVibrationMode(CpuContext ctx)
|
||||
{
|
||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
}
|
||||
|
||||
private static bool WriteNeutralPadData(CpuContext ctx, ulong dataAddress)
|
||||
@@ -201,6 +201,12 @@ public static class PadExports
|
||||
return ctx.Memory.TryWrite(dataAddress, data);
|
||||
}
|
||||
|
||||
private static int SetReturn(CpuContext ctx, int result)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)result);
|
||||
return result;
|
||||
}
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern short GetAsyncKeyState(int vKey);
|
||||
|
||||
|
||||
@@ -234,7 +234,7 @@ public static class PlayGoExports
|
||||
if (outChunkIdList == 0)
|
||||
{
|
||||
TracePlayGo($"get_chunk_id count_only entries={availableEntries} out_entries=0x{outEntries:X16}");
|
||||
return ctx.TryWriteUInt32(outEntries, availableEntries)
|
||||
return TryWriteUInt32(ctx, outEntries, availableEntries)
|
||||
? (int)OrbisGen2Result.ORBIS_GEN2_OK
|
||||
: (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
@@ -249,14 +249,14 @@ public static class PlayGoExports
|
||||
for (uint i = 0; i < entriesToWrite; i++)
|
||||
{
|
||||
var chunkId = chunkIds.Length == 0 ? (ushort)0 : chunkIds[i];
|
||||
if (!ctx.TryWriteUInt16(outChunkIdList + (i * sizeof(ushort)), chunkId))
|
||||
if (!TryWriteUInt16(ctx, outChunkIdList + (i * sizeof(ushort)), chunkId))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
}
|
||||
|
||||
TracePlayGo($"get_chunk_id write requested={numberOfEntries} wrote={entriesToWrite} available={availableEntries}");
|
||||
return ctx.TryWriteUInt32(outEntries, entriesToWrite)
|
||||
return TryWriteUInt32(ctx, outEntries, entriesToWrite)
|
||||
? (int)OrbisGen2Result.ORBIS_GEN2_OK
|
||||
: (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
@@ -292,7 +292,7 @@ public static class PlayGoExports
|
||||
|
||||
return ValidateChunkIds(ctx, chunkIds, numberOfEntries) is { } chunkError && chunkError != 0
|
||||
? chunkError
|
||||
: ctx.TryWriteInt64(outEta, 0)
|
||||
: TryWriteInt64(ctx, outEta, 0)
|
||||
? (int)OrbisGen2Result.ORBIS_GEN2_OK
|
||||
: (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
@@ -324,7 +324,7 @@ public static class PlayGoExports
|
||||
speed = _installSpeed;
|
||||
}
|
||||
|
||||
return ctx.TryWriteInt32(outSpeed, speed)
|
||||
return TryWriteInt32(ctx, outSpeed, speed)
|
||||
? (int)OrbisGen2Result.ORBIS_GEN2_OK
|
||||
: (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
@@ -399,7 +399,7 @@ public static class PlayGoExports
|
||||
var loci = new byte[numberOfEntries];
|
||||
for (uint i = 0; i < numberOfEntries; i++)
|
||||
{
|
||||
if (!ctx.TryReadUInt16(chunkIds + (i * sizeof(ushort)), out var chunkId))
|
||||
if (!TryReadUInt16(ctx, chunkIds + (i * sizeof(ushort)), out var chunkId))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
@@ -503,7 +503,7 @@ public static class PlayGoExports
|
||||
}
|
||||
|
||||
TracePlayGo($"get_todo requested={numberOfEntries} wrote=0");
|
||||
return ctx.TryWriteUInt32(outEntries, 0)
|
||||
return TryWriteUInt32(ctx, outEntries, 0)
|
||||
? (int)OrbisGen2Result.ORBIS_GEN2_OK
|
||||
: (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
@@ -648,7 +648,7 @@ public static class PlayGoExports
|
||||
{
|
||||
for (uint i = 0; i < numberOfEntries; i++)
|
||||
{
|
||||
if (!ctx.TryReadUInt16(chunkIds + (i * sizeof(ushort)), out var chunkId))
|
||||
if (!TryReadUInt16(ctx, chunkIds + (i * sizeof(ushort)), out var chunkId))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
@@ -728,6 +728,47 @@ public static class PlayGoExports
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryReadUInt16(CpuContext ctx, ulong address, out ushort value)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[sizeof(ushort)];
|
||||
if (!ctx.Memory.TryRead(address, buffer))
|
||||
{
|
||||
value = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = BinaryPrimitives.ReadUInt16LittleEndian(buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryWriteUInt16(CpuContext ctx, ulong address, ushort value)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[sizeof(ushort)];
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(buffer, value);
|
||||
return ctx.Memory.TryWrite(address, buffer);
|
||||
}
|
||||
|
||||
private static bool TryWriteUInt32(CpuContext ctx, ulong address, uint value)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[sizeof(uint)];
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer, value);
|
||||
return ctx.Memory.TryWrite(address, buffer);
|
||||
}
|
||||
|
||||
private static bool TryWriteInt32(CpuContext ctx, ulong address, int value)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[sizeof(int)];
|
||||
BinaryPrimitives.WriteInt32LittleEndian(buffer, value);
|
||||
return ctx.Memory.TryWrite(address, buffer);
|
||||
}
|
||||
|
||||
private static bool TryWriteInt64(CpuContext ctx, ulong address, long value)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[sizeof(long)];
|
||||
BinaryPrimitives.WriteInt64LittleEndian(buffer, value);
|
||||
return ctx.Memory.TryWrite(address, buffer);
|
||||
}
|
||||
|
||||
private static void TracePlayGo(string message)
|
||||
{
|
||||
if (string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_PLAYGO"), "1", StringComparison.Ordinal))
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using SharpEmu.HLE;
|
||||
using System.Buffers.Binary;
|
||||
using System.Threading;
|
||||
|
||||
namespace SharpEmu.Libs.SaveData;
|
||||
|
||||
@@ -34,10 +35,10 @@ public static class SaveDataDialogExports
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref _status, StatusInitialized, StatusNone) != StatusNone)
|
||||
{
|
||||
return ctx.SetReturn(ErrorAlreadyInitialized);
|
||||
return SetReturn(ctx, ErrorAlreadyInitialized);
|
||||
}
|
||||
|
||||
return ctx.SetReturn(ErrorOk);
|
||||
return SetReturn(ctx, ErrorOk);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -50,22 +51,22 @@ public static class SaveDataDialogExports
|
||||
var paramAddress = ctx[CpuRegister.Rdi];
|
||||
if (paramAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn(ErrorArgNull);
|
||||
return SetReturn(ctx, ErrorArgNull);
|
||||
}
|
||||
|
||||
if (_status is not (StatusInitialized or StatusFinished))
|
||||
{
|
||||
return ctx.SetReturn(ErrorNotInitialized);
|
||||
return SetReturn(ctx, ErrorNotInitialized);
|
||||
}
|
||||
|
||||
_lastMode = TryReadInt32(ctx, paramAddress, out var mode) ? mode : 0;
|
||||
_lastUserData = ctx.TryReadUInt64(paramAddress + 0xC8, out var userData) ? userData : 0;
|
||||
_lastUserData = TryReadUInt64(ctx, paramAddress + 0xC8, out var userData) ? userData : 0;
|
||||
|
||||
// There is no host save dialog yet. Complete immediately with OK so
|
||||
// guest polling sees a finished dialog instead of spinning forever.
|
||||
Interlocked.Exchange(ref _status, StatusFinished);
|
||||
TraceSaveDataDialog($"open mode={_lastMode} userData=0x{_lastUserData:X16} -> finished");
|
||||
return ctx.SetReturn(ErrorOk);
|
||||
return SetReturn(ctx, ErrorOk);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -73,21 +74,21 @@ public static class SaveDataDialogExports
|
||||
ExportName = "sceSaveDataDialogGetStatus",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceSaveDataDialog")]
|
||||
public static int SaveDataDialogGetStatus(CpuContext ctx) => ctx.SetReturn(Volatile.Read(ref _status));
|
||||
public static int SaveDataDialogGetStatus(CpuContext ctx) => SetReturn(ctx, Volatile.Read(ref _status));
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "KK3Bdg1RWK0",
|
||||
ExportName = "sceSaveDataDialogUpdateStatus",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceSaveDataDialog")]
|
||||
public static int SaveDataDialogUpdateStatus(CpuContext ctx) => ctx.SetReturn(Volatile.Read(ref _status));
|
||||
public static int SaveDataDialogUpdateStatus(CpuContext ctx) => SetReturn(ctx, Volatile.Read(ref _status));
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "en7gNVnh878",
|
||||
ExportName = "sceSaveDataDialogIsReadyToDisplay",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceSaveDataDialog")]
|
||||
public static int SaveDataDialogIsReadyToDisplay(CpuContext ctx) => ctx.SetReturn(1);
|
||||
public static int SaveDataDialogIsReadyToDisplay(CpuContext ctx) => SetReturn(ctx, 1);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "yEiJ-qqr6Cg",
|
||||
@@ -99,12 +100,12 @@ public static class SaveDataDialogExports
|
||||
var resultAddress = ctx[CpuRegister.Rdi];
|
||||
if (resultAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn(ErrorArgNull);
|
||||
return SetReturn(ctx, ErrorArgNull);
|
||||
}
|
||||
|
||||
if (Volatile.Read(ref _status) != StatusFinished)
|
||||
{
|
||||
return ctx.SetReturn(ErrorNotFinished);
|
||||
return SetReturn(ctx, ErrorNotFinished);
|
||||
}
|
||||
|
||||
Span<byte> result = stackalloc byte[ResultSize];
|
||||
@@ -116,10 +117,10 @@ public static class SaveDataDialogExports
|
||||
|
||||
if (!ctx.Memory.TryWrite(resultAddress, result))
|
||||
{
|
||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
return ctx.SetReturn(ErrorOk);
|
||||
return SetReturn(ctx, ErrorOk);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -131,10 +132,10 @@ public static class SaveDataDialogExports
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref _status, StatusFinished, StatusRunning) != StatusRunning)
|
||||
{
|
||||
return ctx.SetReturn(ErrorNotRunning);
|
||||
return SetReturn(ctx, ErrorNotRunning);
|
||||
}
|
||||
|
||||
return ctx.SetReturn(ErrorOk);
|
||||
return SetReturn(ctx, ErrorOk);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -146,12 +147,12 @@ public static class SaveDataDialogExports
|
||||
{
|
||||
if (Interlocked.Exchange(ref _status, StatusNone) == StatusNone)
|
||||
{
|
||||
return ctx.SetReturn(ErrorNotInitialized);
|
||||
return SetReturn(ctx, ErrorNotInitialized);
|
||||
}
|
||||
|
||||
_lastMode = 0;
|
||||
_lastUserData = 0;
|
||||
return ctx.SetReturn(ErrorOk);
|
||||
return SetReturn(ctx, ErrorOk);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -159,14 +160,14 @@ public static class SaveDataDialogExports
|
||||
ExportName = "sceSaveDataDialogProgressBarInc",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceSaveDataDialog")]
|
||||
public static int SaveDataDialogProgressBarInc(CpuContext ctx) => ctx.SetReturn(ErrorOk);
|
||||
public static int SaveDataDialogProgressBarInc(CpuContext ctx) => SetReturn(ctx, ErrorOk);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "hay1CfTmLyA",
|
||||
ExportName = "sceSaveDataDialogProgressBarSetValue",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceSaveDataDialog")]
|
||||
public static int SaveDataDialogProgressBarSetValue(CpuContext ctx) => ctx.SetReturn(ErrorOk);
|
||||
public static int SaveDataDialogProgressBarSetValue(CpuContext ctx) => SetReturn(ctx, ErrorOk);
|
||||
|
||||
private static bool TryReadInt32(CpuContext ctx, ulong address, out int value)
|
||||
{
|
||||
@@ -181,6 +182,25 @@ public static class SaveDataDialogExports
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryReadUInt64(CpuContext ctx, ulong address, out ulong value)
|
||||
{
|
||||
value = 0;
|
||||
Span<byte> bytes = stackalloc byte[sizeof(ulong)];
|
||||
if (!ctx.Memory.TryRead(address, bytes))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
value = BinaryPrimitives.ReadUInt64LittleEndian(bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int SetReturn(CpuContext ctx, int result)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void TraceSaveDataDialog(string message)
|
||||
{
|
||||
if (!string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_SAVEDATA"), "1", StringComparison.Ordinal))
|
||||
|
||||
@@ -50,15 +50,15 @@ public static class SaveDataExports
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(ResolveSaveDataRoot());
|
||||
return ctx.SetReturn(0);
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
return ctx.SetReturn(OrbisSaveDataErrorInternal);
|
||||
return SetReturn(ctx, OrbisSaveDataErrorInternal);
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
return ctx.SetReturn(OrbisSaveDataErrorInternal);
|
||||
return SetReturn(ctx, OrbisSaveDataErrorInternal);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,18 +73,18 @@ public static class SaveDataExports
|
||||
var resultAddress = ctx[CpuRegister.Rsi];
|
||||
if (condAddress == 0 || resultAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn(OrbisSaveDataErrorParameter);
|
||||
return SetReturn(ctx, OrbisSaveDataErrorParameter);
|
||||
}
|
||||
|
||||
if (!TryReadSearchCond(ctx, condAddress, out var cond) ||
|
||||
!TryReadSearchResult(ctx, resultAddress, out var result))
|
||||
{
|
||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
if (cond.UserId < 0 || cond.SortKey > SortKeyFreeBlocks || cond.SortOrder > SortOrderDescent)
|
||||
{
|
||||
return ctx.SetReturn(OrbisSaveDataErrorParameter);
|
||||
return SetReturn(ctx, OrbisSaveDataErrorParameter);
|
||||
}
|
||||
|
||||
try
|
||||
@@ -96,7 +96,7 @@ public static class SaveDataExports
|
||||
}
|
||||
else if (!TryReadFixedAscii(ctx, cond.TitleIdAddress, SaveDataTitleIdSize, out titleId))
|
||||
{
|
||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
var root = ResolveTitleSaveRoot(cond.UserId, titleId);
|
||||
@@ -108,21 +108,21 @@ public static class SaveDataExports
|
||||
var setNum = result.DirNamesNum == 0
|
||||
? 0
|
||||
: Math.Min(result.DirNamesNum, entries.Count);
|
||||
if (!ctx.TryWriteUInt32(resultAddress + ResultHitNumOffset, checked((uint)entries.Count)) ||
|
||||
!ctx.TryWriteUInt32(resultAddress + ResultSetNumOffset, checked((uint)setNum)))
|
||||
if (!TryWriteUInt32(ctx, resultAddress + ResultHitNumOffset, checked((uint)entries.Count)) ||
|
||||
!TryWriteUInt32(ctx, resultAddress + ResultSetNumOffset, checked((uint)setNum)))
|
||||
{
|
||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
if (setNum == 0)
|
||||
{
|
||||
TraceSaveData($"dir_name_search user={cond.UserId} title={titleId} hits={entries.Count} set=0 root='{root}'");
|
||||
return ctx.SetReturn(0);
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
|
||||
if (result.DirNamesAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn(OrbisSaveDataErrorParameter);
|
||||
return SetReturn(ctx, OrbisSaveDataErrorParameter);
|
||||
}
|
||||
|
||||
for (var i = 0; i < setNum; i++)
|
||||
@@ -138,20 +138,20 @@ public static class SaveDataExports
|
||||
(result.InfosAddress != 0 &&
|
||||
!TryWriteSearchInfo(ctx, result.InfosAddress + ((ulong)i * SaveDataSearchInfoSize), entry)))
|
||||
{
|
||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
}
|
||||
|
||||
TraceSaveData($"dir_name_search user={cond.UserId} title={titleId} hits={entries.Count} set={setNum} root='{root}'");
|
||||
return ctx.SetReturn(0);
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
return ctx.SetReturn(OrbisSaveDataErrorInternal);
|
||||
return SetReturn(ctx, OrbisSaveDataErrorInternal);
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
return ctx.SetReturn(OrbisSaveDataErrorInternal);
|
||||
return SetReturn(ctx, OrbisSaveDataErrorInternal);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,25 +166,25 @@ public static class SaveDataExports
|
||||
var resultAddress = ctx[CpuRegister.Rsi];
|
||||
if (mountAddress == 0 || resultAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn(OrbisSaveDataErrorParameter);
|
||||
return SetReturn(ctx, OrbisSaveDataErrorParameter);
|
||||
}
|
||||
|
||||
if (!ctx.TryReadInt32(mountAddress, out var userId) ||
|
||||
if (!TryReadInt32(ctx, mountAddress, out var userId) ||
|
||||
!ctx.TryReadUInt64(mountAddress + 0x08, out var dirNameAddress) ||
|
||||
!ctx.TryReadUInt64(mountAddress + 0x10, out var blocks) ||
|
||||
!ctx.TryReadUInt64(mountAddress + 0x18, out var systemBlocks) ||
|
||||
!ctx.TryReadUInt32(mountAddress + 0x20, out var mountMode) ||
|
||||
!ctx.TryReadUInt32(mountAddress + 0x24, out var resource) ||
|
||||
!ctx.TryReadUInt32(mountAddress + 0x28, out var mode) ||
|
||||
!TryReadUInt32(ctx, mountAddress + 0x20, out var mountMode) ||
|
||||
!TryReadUInt32(ctx, mountAddress + 0x24, out var resource) ||
|
||||
!TryReadUInt32(ctx, mountAddress + 0x28, out var mode) ||
|
||||
dirNameAddress == 0 ||
|
||||
!TryReadFixedAscii(ctx, dirNameAddress, SaveDataDirNameSize, out var dirName))
|
||||
{
|
||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
if (userId < 0 || string.IsNullOrWhiteSpace(dirName))
|
||||
{
|
||||
return ctx.SetReturn(OrbisSaveDataErrorParameter);
|
||||
return SetReturn(ctx, OrbisSaveDataErrorParameter);
|
||||
}
|
||||
|
||||
try
|
||||
@@ -199,12 +199,12 @@ public static class SaveDataExports
|
||||
|
||||
if (!existed && !create && !createIfMissing)
|
||||
{
|
||||
return ctx.SetReturn(OrbisSaveDataErrorNotFound);
|
||||
return SetReturn(ctx, OrbisSaveDataErrorNotFound);
|
||||
}
|
||||
|
||||
if (existed && create)
|
||||
{
|
||||
return ctx.SetReturn(OrbisSaveDataErrorExists);
|
||||
return SetReturn(ctx, OrbisSaveDataErrorExists);
|
||||
}
|
||||
|
||||
if (!existed)
|
||||
@@ -221,26 +221,26 @@ public static class SaveDataExports
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(result[0x1C..], createIfMissing && !existed ? 1u : 0u);
|
||||
if (!ctx.Memory.TryWrite(resultAddress, result))
|
||||
{
|
||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
TraceSaveData(
|
||||
$"mount3 user={userId} title={titleId} dir={dirName} blocks={blocks} " +
|
||||
$"system_blocks={systemBlocks} mount_mode=0x{mountMode:X} resource={resource} mode={mode} " +
|
||||
$"mount_point={mountPoint} created={!existed} root='{savePath}'");
|
||||
return ctx.SetReturn(0);
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
return ctx.SetReturn(OrbisSaveDataErrorInternal);
|
||||
return SetReturn(ctx, OrbisSaveDataErrorInternal);
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
return ctx.SetReturn(OrbisSaveDataErrorInternal);
|
||||
return SetReturn(ctx, OrbisSaveDataErrorInternal);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
return ctx.SetReturn(OrbisSaveDataErrorParameter);
|
||||
return SetReturn(ctx, OrbisSaveDataErrorParameter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,30 +258,30 @@ public static class SaveDataExports
|
||||
|
||||
if (resourceAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn(OrbisSaveDataErrorParameter);
|
||||
return SetReturn(ctx, OrbisSaveDataErrorParameter);
|
||||
}
|
||||
|
||||
var id = (uint)Interlocked.Increment(ref _nextTransactionResource);
|
||||
|
||||
if (!ctx.TryWriteUInt32(resourceAddress, id))
|
||||
if (!TryWriteUInt32(ctx, resourceAddress, id))
|
||||
{
|
||||
return ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
TraceSaveData(
|
||||
$"create_transaction_resource user={userId} reserved=0x{reserved:X} resource_addr=0x{resourceAddress:X} id={id}");
|
||||
|
||||
return ctx.SetReturn(0);
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
|
||||
private static bool TryReadSearchCond(CpuContext ctx, ulong address, out SearchCond cond)
|
||||
{
|
||||
cond = default;
|
||||
if (!ctx.TryReadInt32(address, out var userId) ||
|
||||
if (!TryReadInt32(ctx, address, out var userId) ||
|
||||
!ctx.TryReadUInt64(address + 0x08, out var titleIdAddress) ||
|
||||
!ctx.TryReadUInt64(address + 0x10, out var dirNameAddress) ||
|
||||
!ctx.TryReadUInt32(address + 0x18, out var sortKey) ||
|
||||
!ctx.TryReadUInt32(address + 0x1C, out var sortOrder))
|
||||
!TryReadUInt32(ctx, address + 0x18, out var sortKey) ||
|
||||
!TryReadUInt32(ctx, address + 0x1C, out var sortOrder))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -304,7 +304,7 @@ public static class SaveDataExports
|
||||
{
|
||||
result = default;
|
||||
if (!ctx.TryReadUInt64(address + ResultDirNamesOffset, out var dirNamesAddress) ||
|
||||
!ctx.TryReadUInt32(address + ResultDirNamesNumOffset, out var dirNamesNum) ||
|
||||
!TryReadUInt32(ctx, address + ResultDirNamesNumOffset, out var dirNamesNum) ||
|
||||
!ctx.TryReadUInt64(address + ResultParamsOffset, out var paramsAddress) ||
|
||||
!ctx.TryReadUInt64(address + ResultInfosOffset, out var infosAddress))
|
||||
{
|
||||
@@ -505,6 +505,45 @@ public static class SaveDataExports
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryReadInt32(CpuContext ctx, ulong address, out int value)
|
||||
{
|
||||
Span<byte> bytes = stackalloc byte[sizeof(int)];
|
||||
if (!ctx.Memory.TryRead(address, bytes))
|
||||
{
|
||||
value = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = BinaryPrimitives.ReadInt32LittleEndian(bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryReadUInt32(CpuContext ctx, ulong address, out uint value)
|
||||
{
|
||||
Span<byte> bytes = stackalloc byte[sizeof(uint)];
|
||||
if (!ctx.Memory.TryRead(address, bytes))
|
||||
{
|
||||
value = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = BinaryPrimitives.ReadUInt32LittleEndian(bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryWriteUInt32(CpuContext ctx, ulong address, uint value)
|
||||
{
|
||||
Span<byte> bytes = stackalloc byte[sizeof(uint)];
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(bytes, value);
|
||||
return ctx.Memory.TryWrite(address, bytes);
|
||||
}
|
||||
|
||||
private static int SetReturn(CpuContext ctx, int result)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void TraceSaveData(string message)
|
||||
{
|
||||
if (string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_SAVEDATA"), "1", StringComparison.Ordinal))
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using SharpEmu.HLE;
|
||||
|
||||
namespace SharpEmu.Libs.Share;
|
||||
@@ -25,13 +26,13 @@ public static class ShareExports
|
||||
var affinityMask = ctx[CpuRegister.Rdx];
|
||||
if (memorySize == 0)
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
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 ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -44,12 +45,12 @@ public static class ShareExports
|
||||
var contentParamAddress = ctx[CpuRegister.Rdi];
|
||||
if (contentParamAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
if (!TryReadNullTerminatedUtf8(ctx, contentParamAddress, MaxContentParamBytes, out var contentParam))
|
||||
{
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
_contentParam = contentParam;
|
||||
@@ -59,7 +60,13 @@ public static class ShareExports
|
||||
}
|
||||
|
||||
TraceShare($"set_content_param len={contentParam.Length} preview='{FormatTraceString(contentParam)}'");
|
||||
return ctx.SetReturn(OrbisGen2Result.ORBIS_GEN2_OK);
|
||||
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)
|
||||
|
||||
@@ -15,10 +15,6 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||
<PackageReference Include="Silk.NET.Windowing" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<NoWarn>$(NoWarn);1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -24,7 +24,7 @@ public static class SystemServiceExports
|
||||
var valueAddress = ctx[CpuRegister.Rsi];
|
||||
if (valueAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn(OrbisSystemServiceErrorParameter);
|
||||
return SetReturn(ctx, OrbisSystemServiceErrorParameter);
|
||||
}
|
||||
|
||||
var value = parameterId switch
|
||||
@@ -37,8 +37,8 @@ public static class SystemServiceExports
|
||||
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);
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -51,7 +51,7 @@ public static class SystemServiceExports
|
||||
var statusAddress = ctx[CpuRegister.Rdi];
|
||||
if (statusAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn(OrbisSystemServiceErrorParameter);
|
||||
return SetReturn(ctx, OrbisSystemServiceErrorParameter);
|
||||
}
|
||||
|
||||
Span<byte> status = stackalloc byte[SystemServiceStatusSize];
|
||||
@@ -60,8 +60,8 @@ public static class SystemServiceExports
|
||||
status[0x06] = 1;
|
||||
|
||||
return ctx.Memory.TryWrite(statusAddress, status)
|
||||
? ctx.SetReturn(0)
|
||||
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -74,7 +74,7 @@ public static class SystemServiceExports
|
||||
var infoAddress = ctx[CpuRegister.Rdi];
|
||||
if (infoAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn(OrbisSystemServiceErrorParameter);
|
||||
return SetReturn(ctx, OrbisSystemServiceErrorParameter);
|
||||
}
|
||||
|
||||
Span<byte> info = stackalloc byte[DisplaySafeAreaInfoSize];
|
||||
@@ -82,8 +82,8 @@ public static class SystemServiceExports
|
||||
BinaryPrimitives.WriteSingleLittleEndian(info, 1.0f);
|
||||
|
||||
return ctx.Memory.TryWrite(infoAddress, info)
|
||||
? ctx.SetReturn(0)
|
||||
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -94,6 +94,12 @@ public static class SystemServiceExports
|
||||
public static int SystemServiceHideSplashScreen(CpuContext ctx)
|
||||
{
|
||||
VulkanVideoPresenter.HideSplashScreen();
|
||||
return ctx.SetReturn(0);
|
||||
return SetReturn(ctx, 0);
|
||||
}
|
||||
|
||||
private static int SetReturn(CpuContext ctx, int result)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using SharpEmu.HLE;
|
||||
using System.Buffers.Binary;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace SharpEmu.Libs.UserService;
|
||||
|
||||
@@ -39,12 +40,12 @@ public static class UserServiceExports
|
||||
var userIdAddress = ctx[CpuRegister.Rdi];
|
||||
if (userIdAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn(OrbisUserServiceErrorInvalidArgument);
|
||||
return SetReturn(ctx, OrbisUserServiceErrorInvalidArgument);
|
||||
}
|
||||
|
||||
return ctx.TryWriteInt32(userIdAddress, PrimaryUserId)
|
||||
? ctx.SetReturn(0)
|
||||
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return TryWriteInt32(ctx, userIdAddress, PrimaryUserId)
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -57,7 +58,7 @@ public static class UserServiceExports
|
||||
var userIdListAddress = ctx[CpuRegister.Rdi];
|
||||
if (userIdListAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn(OrbisUserServiceErrorInvalidArgument);
|
||||
return SetReturn(ctx, OrbisUserServiceErrorInvalidArgument);
|
||||
}
|
||||
|
||||
Span<byte> userIds = stackalloc byte[sizeof(int) * 4];
|
||||
@@ -66,8 +67,8 @@ public static class UserServiceExports
|
||||
BinaryPrimitives.WriteInt32LittleEndian(userIds[0x08..], InvalidUserId);
|
||||
BinaryPrimitives.WriteInt32LittleEndian(userIds[0x0C..], InvalidUserId);
|
||||
return ctx.Memory.TryWrite(userIdListAddress, userIds)
|
||||
? ctx.SetReturn(0)
|
||||
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -80,20 +81,20 @@ public static class UserServiceExports
|
||||
var eventAddress = ctx[CpuRegister.Rdi];
|
||||
if (eventAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn(OrbisUserServiceErrorInvalidArgument);
|
||||
return SetReturn(ctx, OrbisUserServiceErrorInvalidArgument);
|
||||
}
|
||||
|
||||
if (Interlocked.Exchange(ref _loginEventDelivered, 1) != 0)
|
||||
{
|
||||
return ctx.SetReturn(OrbisUserServiceErrorNoEvent);
|
||||
return SetReturn(ctx, OrbisUserServiceErrorNoEvent);
|
||||
}
|
||||
|
||||
Span<byte> payload = stackalloc byte[sizeof(int) * 2];
|
||||
BinaryPrimitives.WriteInt32LittleEndian(payload[0..], 0);
|
||||
BinaryPrimitives.WriteInt32LittleEndian(payload[sizeof(int)..], PrimaryUserId);
|
||||
return ctx.Memory.TryWrite(eventAddress, payload)
|
||||
? ctx.SetReturn(0)
|
||||
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -108,25 +109,25 @@ public static class UserServiceExports
|
||||
var capacity = ctx[CpuRegister.Rdx];
|
||||
if (userId != PrimaryUserId)
|
||||
{
|
||||
return ctx.SetReturn(OrbisUserServiceErrorInvalidParameter);
|
||||
return SetReturn(ctx, OrbisUserServiceErrorInvalidParameter);
|
||||
}
|
||||
|
||||
if (nameAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn(OrbisUserServiceErrorInvalidArgument);
|
||||
return SetReturn(ctx, OrbisUserServiceErrorInvalidArgument);
|
||||
}
|
||||
|
||||
var nameBytes = Encoding.UTF8.GetBytes(PrimaryUserName);
|
||||
if (capacity <= (ulong)nameBytes.Length)
|
||||
{
|
||||
return ctx.SetReturn(OrbisUserServiceErrorBufferTooShort);
|
||||
return SetReturn(ctx, OrbisUserServiceErrorBufferTooShort);
|
||||
}
|
||||
|
||||
Span<byte> output = stackalloc byte[nameBytes.Length + 1];
|
||||
nameBytes.CopyTo(output);
|
||||
return ctx.Memory.TryWrite(nameAddress, output)
|
||||
? ctx.SetReturn(0)
|
||||
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
@@ -140,16 +141,29 @@ public static class UserServiceExports
|
||||
var valueAddress = ctx[CpuRegister.Rsi];
|
||||
if (parameterId != 1000)
|
||||
{
|
||||
return ctx.SetReturn(OrbisUserServiceErrorInvalidParameter);
|
||||
return SetReturn(ctx, OrbisUserServiceErrorInvalidParameter);
|
||||
}
|
||||
|
||||
if (valueAddress == 0)
|
||||
{
|
||||
return ctx.SetReturn(OrbisUserServiceErrorInvalidArgument);
|
||||
return SetReturn(ctx, OrbisUserServiceErrorInvalidArgument);
|
||||
}
|
||||
|
||||
return ctx.TryWriteInt32(valueAddress, 0)
|
||||
? ctx.SetReturn(0)
|
||||
: ctx.SetReturn((int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
return TryWriteInt32(ctx, valueAddress, 0)
|
||||
? SetReturn(ctx, 0)
|
||||
: SetReturn(ctx, (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT);
|
||||
}
|
||||
|
||||
private static bool TryWriteInt32(CpuContext ctx, ulong address, int value)
|
||||
{
|
||||
Span<byte> bytes = stackalloc byte[sizeof(int)];
|
||||
BinaryPrimitives.WriteInt32LittleEndian(bytes, value);
|
||||
return ctx.Memory.TryWrite(address, bytes);
|
||||
}
|
||||
|
||||
private static int SetReturn(CpuContext ctx, int result)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -922,9 +922,9 @@ public static class VideoOutExports
|
||||
private static bool TryReadBufferAttribute(CpuContext ctx, ulong attributeAddress, bool attribute2, out BufferAttribute attribute)
|
||||
{
|
||||
attribute = default;
|
||||
if (!ctx.TryReadUInt32(attributeAddress + 0x04, out var tilingMode) ||
|
||||
!ctx.TryReadUInt32(attributeAddress + 0x0C, out var width) ||
|
||||
!ctx.TryReadUInt32(attributeAddress + 0x10, out var height))
|
||||
if (!TryReadUInt32(ctx, attributeAddress + 0x04, out var tilingMode) ||
|
||||
!TryReadUInt32(ctx, attributeAddress + 0x0C, out var width) ||
|
||||
!TryReadUInt32(ctx, attributeAddress + 0x10, out var height))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -941,10 +941,10 @@ public static class VideoOutExports
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!ctx.TryReadUInt32(attributeAddress + 0x00, out var pixelFormat32) ||
|
||||
!ctx.TryReadUInt32(attributeAddress + 0x08, out var aspectRatio) ||
|
||||
!ctx.TryReadUInt32(attributeAddress + 0x14, out var pitchInPixel) ||
|
||||
!ctx.TryReadUInt32(attributeAddress + 0x18, out var option32))
|
||||
if (!TryReadUInt32(ctx, attributeAddress + 0x00, out var pixelFormat32) ||
|
||||
!TryReadUInt32(ctx, attributeAddress + 0x08, out var aspectRatio) ||
|
||||
!TryReadUInt32(ctx, attributeAddress + 0x14, out var pitchInPixel) ||
|
||||
!TryReadUInt32(ctx, attributeAddress + 0x18, out var option32))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -1291,6 +1291,19 @@ public static class VideoOutExports
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryReadUInt32(CpuContext ctx, ulong address, out uint value)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[sizeof(uint)];
|
||||
if (!ctx.Memory.TryRead(address, buffer))
|
||||
{
|
||||
value = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = BinaryPrimitives.ReadUInt32LittleEndian(buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryReadInt16(CpuContext ctx, ulong address, out short value)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[sizeof(short)];
|
||||
|
||||
@@ -4,7 +4,4 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||
-->
|
||||
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<NoWarn>$(NoWarn);1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
||||
Reference in New Issue
Block a user