mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-15 15:36:11 +08:00
initial commit
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
using System.Buffers.Binary;
|
||||
using System.Linq;
|
||||
|
||||
namespace SharpEmu.HLE;
|
||||
|
||||
public sealed class Aerolib : ISymbolCatalog
|
||||
{
|
||||
private static readonly Lazy<Aerolib> _instance = new(() => new Aerolib());
|
||||
private static readonly Aerolib EmptyCatalog = new Aerolib(empty: true);
|
||||
|
||||
private Dictionary<string, SysAbiSymbol> _byNid;
|
||||
private Dictionary<string, SysAbiSymbol> _byExportName;
|
||||
|
||||
public static Aerolib Instance => _instance.Value;
|
||||
|
||||
private Aerolib(
|
||||
Dictionary<string, SysAbiSymbol> byNid,
|
||||
Dictionary<string, SysAbiSymbol> byExportName)
|
||||
{
|
||||
_byNid = byNid;
|
||||
_byExportName = byExportName;
|
||||
}
|
||||
|
||||
private Aerolib()
|
||||
{
|
||||
_byNid = new Dictionary<string, SysAbiSymbol>(StringComparer.Ordinal);
|
||||
_byExportName = new Dictionary<string, SysAbiSymbol>(StringComparer.Ordinal);
|
||||
LoadFromEmbeddedBinary();
|
||||
}
|
||||
|
||||
private Aerolib(bool empty)
|
||||
{
|
||||
_byNid = new Dictionary<string, SysAbiSymbol>(StringComparer.Ordinal);
|
||||
_byExportName = new Dictionary<string, SysAbiSymbol>(StringComparer.Ordinal);
|
||||
}
|
||||
|
||||
public static ISymbolCatalog Empty => EmptyCatalog;
|
||||
|
||||
public string GetName(string nid)
|
||||
{
|
||||
if (string.IsNullOrEmpty(nid))
|
||||
return nid ?? string.Empty;
|
||||
|
||||
if (_byNid.TryGetValue(nid, out var symbol))
|
||||
return symbol.ExportName;
|
||||
return nid;
|
||||
}
|
||||
|
||||
public bool TryGetName(string nid, out string name)
|
||||
{
|
||||
if (string.IsNullOrEmpty(nid))
|
||||
{
|
||||
name = string.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_byNid.TryGetValue(nid, out var symbol))
|
||||
{
|
||||
name = symbol.ExportName;
|
||||
return true;
|
||||
}
|
||||
name = string.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool ContainsNid(string nid)
|
||||
{
|
||||
if (string.IsNullOrEmpty(nid))
|
||||
return false;
|
||||
|
||||
return _byNid.ContainsKey(nid);
|
||||
}
|
||||
|
||||
public Dictionary<string, string> GetAllNidNames()
|
||||
{
|
||||
var result = new Dictionary<string, string>(_byNid.Count, StringComparer.Ordinal);
|
||||
foreach (var kvp in _byNid)
|
||||
{
|
||||
result[kvp.Key] = kvp.Value.ExportName;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public int Count => _byNid.Count;
|
||||
|
||||
public bool TryGetByNid(string nid, out SysAbiSymbol symbol)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(nid);
|
||||
return _byNid.TryGetValue(nid, out symbol);
|
||||
}
|
||||
|
||||
public bool TryGetByExportName(string exportName, out SysAbiSymbol symbol)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(exportName);
|
||||
return _byExportName.TryGetValue(exportName, out symbol);
|
||||
}
|
||||
|
||||
private void LoadFromEmbeddedBinary()
|
||||
{
|
||||
try
|
||||
{
|
||||
var assembly = typeof(Aerolib).Assembly;
|
||||
var resourceName = assembly.GetManifestResourceNames()
|
||||
.FirstOrDefault(n => n.EndsWith("aerolib.bin", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (resourceName == null)
|
||||
{
|
||||
Console.Error.WriteLine("[AEROLIB] Embedded resource 'aerolib.bin' not found");
|
||||
return;
|
||||
}
|
||||
|
||||
using var stream = assembly.GetManifestResourceStream(resourceName);
|
||||
if (stream == null)
|
||||
{
|
||||
Console.Error.WriteLine("[AEROLIB] Failed to open embedded resource stream");
|
||||
return;
|
||||
}
|
||||
|
||||
var data = new byte[stream.Length];
|
||||
stream.ReadExactly(data);
|
||||
|
||||
int offset = 0;
|
||||
uint count = BinaryPrimitives.ReadUInt32LittleEndian(data.AsSpan(offset, 4));
|
||||
offset += 4;
|
||||
|
||||
_byNid = new Dictionary<string, SysAbiSymbol>((int)count, StringComparer.Ordinal);
|
||||
_byExportName = new Dictionary<string, SysAbiSymbol>((int)count, StringComparer.Ordinal);
|
||||
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
byte nidLen = data[offset++];
|
||||
string nid = System.Text.Encoding.UTF8.GetString(data, offset, nidLen);
|
||||
offset += nidLen;
|
||||
|
||||
ushort nameLen = BinaryPrimitives.ReadUInt16LittleEndian(data.AsSpan(offset, 2));
|
||||
offset += 2;
|
||||
string name = System.Text.Encoding.UTF8.GetString(data, offset, nameLen);
|
||||
offset += nameLen;
|
||||
|
||||
var symbol = new SysAbiSymbol(nid, name, name, Generation.Gen5);
|
||||
_byNid[nid] = symbol;
|
||||
_byExportName[name] = symbol;
|
||||
}
|
||||
|
||||
Console.Error.WriteLine($"[AEROLIB] Loaded {_byNid.Count} NID entries from binary resource");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.Error.WriteLine($"[AEROLIB] Failed to load embedded aerolib.bin: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,172 @@
|
||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
using System.Buffers.Binary;
|
||||
|
||||
namespace SharpEmu.HLE;
|
||||
|
||||
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 CpuContext(ICpuMemory memory, Generation generation)
|
||||
{
|
||||
Memory = memory ?? throw new ArgumentNullException(nameof(memory));
|
||||
TargetGeneration = generation;
|
||||
}
|
||||
|
||||
public ICpuMemory Memory { get; }
|
||||
|
||||
public Generation TargetGeneration { get; }
|
||||
|
||||
public ulong Rip { get; set; }
|
||||
|
||||
public ulong Rflags { get; set; }
|
||||
|
||||
public ulong FsBase { get; set; }
|
||||
|
||||
public ulong GsBase { get; set; }
|
||||
|
||||
public ulong this[CpuRegister register]
|
||||
{
|
||||
get => _registers[(int)register];
|
||||
set
|
||||
{
|
||||
_registers[(int)register] = value;
|
||||
if (register == CpuRegister.Rax)
|
||||
{
|
||||
_raxWritten = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearRaxWriteFlag()
|
||||
{
|
||||
_raxWritten = false;
|
||||
}
|
||||
|
||||
public bool WasRaxWritten => _raxWritten;
|
||||
|
||||
public void GetXmmRegister(int registerIndex, out ulong low, out ulong high)
|
||||
{
|
||||
if ((uint)registerIndex >= 16)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(registerIndex));
|
||||
}
|
||||
|
||||
var offset = registerIndex * 2;
|
||||
low = _xmmRegisters[offset];
|
||||
high = _xmmRegisters[offset + 1];
|
||||
}
|
||||
|
||||
public void SetXmmRegister(int registerIndex, ulong low, ulong high)
|
||||
{
|
||||
if ((uint)registerIndex >= 16)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(registerIndex));
|
||||
}
|
||||
|
||||
var offset = registerIndex * 2;
|
||||
_xmmRegisters[offset] = low;
|
||||
_xmmRegisters[offset + 1] = high;
|
||||
}
|
||||
|
||||
public void GetYmmUpper(int registerIndex, out ulong low, out ulong high)
|
||||
{
|
||||
if ((uint)registerIndex >= 16)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(registerIndex));
|
||||
}
|
||||
|
||||
var offset = registerIndex * 2;
|
||||
low = _ymmUpperRegisters[offset];
|
||||
high = _ymmUpperRegisters[offset + 1];
|
||||
}
|
||||
|
||||
public void SetYmmUpper(int registerIndex, ulong low, ulong high)
|
||||
{
|
||||
if ((uint)registerIndex >= 16)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(registerIndex));
|
||||
}
|
||||
|
||||
var offset = registerIndex * 2;
|
||||
_ymmUpperRegisters[offset] = low;
|
||||
_ymmUpperRegisters[offset + 1] = high;
|
||||
}
|
||||
|
||||
public void ClearYmmUpper(int registerIndex)
|
||||
{
|
||||
SetYmmUpper(registerIndex, 0, 0);
|
||||
}
|
||||
|
||||
public void ClearAllYmmUpper()
|
||||
{
|
||||
Array.Clear(_ymmUpperRegisters);
|
||||
}
|
||||
|
||||
public void GetYmmRegister(
|
||||
int registerIndex,
|
||||
out ulong lowLow,
|
||||
out ulong lowHigh,
|
||||
out ulong highLow,
|
||||
out ulong highHigh)
|
||||
{
|
||||
GetXmmRegister(registerIndex, out lowLow, out lowHigh);
|
||||
GetYmmUpper(registerIndex, out highLow, out highHigh);
|
||||
}
|
||||
|
||||
public void SetYmmRegister(
|
||||
int registerIndex,
|
||||
ulong lowLow,
|
||||
ulong lowHigh,
|
||||
ulong highLow,
|
||||
ulong highHigh)
|
||||
{
|
||||
SetXmmRegister(registerIndex, lowLow, lowHigh);
|
||||
SetYmmUpper(registerIndex, highLow, highHigh);
|
||||
}
|
||||
|
||||
public bool TryReadUInt64(ulong address, out ulong value)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[sizeof(ulong)];
|
||||
if (!Memory.TryRead(address, buffer))
|
||||
{
|
||||
value = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = BinaryPrimitives.ReadUInt64LittleEndian(buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryWriteUInt64(ulong address, ulong value)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[sizeof(ulong)];
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(buffer, value);
|
||||
return Memory.TryWrite(address, buffer);
|
||||
}
|
||||
|
||||
public bool PushUInt64(ulong value)
|
||||
{
|
||||
var rsp = this[CpuRegister.Rsp];
|
||||
rsp -= sizeof(ulong);
|
||||
this[CpuRegister.Rsp] = rsp;
|
||||
return TryWriteUInt64(rsp, value);
|
||||
}
|
||||
|
||||
public bool PopUInt64(out ulong value)
|
||||
{
|
||||
var rsp = this[CpuRegister.Rsp];
|
||||
if (!TryReadUInt64(rsp, out value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this[CpuRegister.Rsp] = rsp + sizeof(ulong);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
namespace SharpEmu.HLE;
|
||||
|
||||
public enum CpuRegister : int
|
||||
{
|
||||
Rax = 0,
|
||||
|
||||
Rcx = 1,
|
||||
|
||||
Rdx = 2,
|
||||
|
||||
Rbx = 3,
|
||||
|
||||
Rsp = 4,
|
||||
|
||||
Rbp = 5,
|
||||
|
||||
Rsi = 6,
|
||||
|
||||
Rdi = 7,
|
||||
|
||||
R8 = 8,
|
||||
|
||||
R9 = 9,
|
||||
|
||||
R10 = 10,
|
||||
|
||||
R11 = 11,
|
||||
|
||||
R12 = 12,
|
||||
|
||||
R13 = 13,
|
||||
|
||||
R14 = 14,
|
||||
|
||||
R15 = 15,
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
namespace SharpEmu.HLE;
|
||||
|
||||
public sealed class ExportedFunction
|
||||
{
|
||||
public ExportedFunction(string libraryName, string nid, string name, Generation target, SysAbiFunction function)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(libraryName);
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(nid);
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(name);
|
||||
ArgumentNullException.ThrowIfNull(function);
|
||||
|
||||
LibraryName = libraryName;
|
||||
Nid = nid;
|
||||
Name = name;
|
||||
Target = target;
|
||||
Function = function;
|
||||
}
|
||||
|
||||
public string LibraryName { get; }
|
||||
|
||||
public string Nid { get; }
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public Generation Target { get; }
|
||||
|
||||
public SysAbiFunction Function { get; }
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
namespace SharpEmu.HLE;
|
||||
|
||||
[Flags]
|
||||
public enum Generation
|
||||
{
|
||||
None = 0,
|
||||
|
||||
Gen4 = 1,
|
||||
|
||||
Gen5 = 2,
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
namespace SharpEmu.HLE;
|
||||
|
||||
public interface ICpuMemory
|
||||
{
|
||||
bool TryRead(ulong virtualAddress, Span<byte> destination);
|
||||
|
||||
bool TryWrite(ulong virtualAddress, ReadOnlySpan<byte> source);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
using System.Reflection;
|
||||
|
||||
namespace SharpEmu.HLE;
|
||||
|
||||
public interface IModuleManager
|
||||
{
|
||||
int RegisterFromAssembly(Assembly assembly, Generation generation, ISymbolCatalog? symbolCatalog = null);
|
||||
|
||||
void Freeze();
|
||||
|
||||
bool TryGetFunction(string nid, out Delegate function);
|
||||
|
||||
bool TryGetExport(string nid, out ExportedFunction export);
|
||||
|
||||
bool TryGetExportByName(string exportName, out ExportedFunction export);
|
||||
|
||||
OrbisGen2Result Dispatch(string nid, CpuContext context);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
namespace SharpEmu.HLE;
|
||||
|
||||
public interface ISymbolCatalog
|
||||
{
|
||||
bool TryGetByNid(string nid, out SysAbiSymbol symbol);
|
||||
|
||||
bool TryGetByExportName(string exportName, out SysAbiSymbol symbol);
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.Reflection;
|
||||
|
||||
namespace SharpEmu.HLE;
|
||||
|
||||
public sealed class ModuleManager : IModuleManager
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, Delegate> _dispatchTable = new(StringComparer.Ordinal);
|
||||
private readonly ConcurrentDictionary<string, ExportedFunction> _exportTable = new(StringComparer.Ordinal);
|
||||
private readonly ConcurrentDictionary<string, ExportedFunction> _exportNameTable = new(StringComparer.Ordinal);
|
||||
private readonly object _registrationGate = new();
|
||||
private bool _isFrozen;
|
||||
|
||||
public int RegisterFromAssembly(Assembly assembly, Generation generation, ISymbolCatalog? symbolCatalog = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(assembly);
|
||||
|
||||
lock (_registrationGate)
|
||||
{
|
||||
if (_isFrozen)
|
||||
{
|
||||
throw new InvalidOperationException("Module registration is frozen.");
|
||||
}
|
||||
|
||||
var registeredCount = 0;
|
||||
var instances = new Dictionary<Type, object>();
|
||||
|
||||
foreach (var type in assembly.GetTypes())
|
||||
{
|
||||
foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static))
|
||||
{
|
||||
var exportAttribute = method.GetCustomAttribute<SysAbiExportAttribute>(inherit: false);
|
||||
if (exportAttribute is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var exportInfo = ResolveExportInfo(exportAttribute, method, generation, symbolCatalog);
|
||||
if (exportInfo is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var handler = CreateHandler(type, method, instances);
|
||||
if (!_dispatchTable.TryAdd(exportInfo.Value.Nid, handler))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_exportTable[exportInfo.Value.Nid] = new ExportedFunction(
|
||||
exportInfo.Value.LibraryName,
|
||||
exportInfo.Value.Nid,
|
||||
exportInfo.Value.ExportName,
|
||||
exportInfo.Value.Target,
|
||||
(SysAbiFunction)handler);
|
||||
_exportNameTable.TryAdd(exportInfo.Value.ExportName, _exportTable[exportInfo.Value.Nid]);
|
||||
|
||||
registeredCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return registeredCount;
|
||||
}
|
||||
}
|
||||
|
||||
public void Freeze()
|
||||
{
|
||||
lock (_registrationGate)
|
||||
{
|
||||
_isFrozen = true;
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetFunction(string nid, out Delegate function)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(nid);
|
||||
return _dispatchTable.TryGetValue(nid, out function!);
|
||||
}
|
||||
|
||||
public bool TryGetExport(string nid, out ExportedFunction export)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(nid);
|
||||
return _exportTable.TryGetValue(nid, out export!);
|
||||
}
|
||||
|
||||
public bool TryGetExportByName(string exportName, out ExportedFunction export)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(exportName);
|
||||
return _exportNameTable.TryGetValue(exportName, out export!);
|
||||
}
|
||||
|
||||
public OrbisGen2Result Dispatch(string nid, CpuContext context)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(nid);
|
||||
ArgumentNullException.ThrowIfNull(context);
|
||||
|
||||
if (!_dispatchTable.TryGetValue(nid, out var function) || !_exportTable.TryGetValue(nid, out var export))
|
||||
{
|
||||
context[CpuRegister.Rax] = unchecked((ulong)(int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
|
||||
return OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
if ((export.Target & context.TargetGeneration) == 0)
|
||||
{
|
||||
context[CpuRegister.Rax] = unchecked((ulong)(int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
|
||||
return OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
context.ClearRaxWriteFlag();
|
||||
int ret = ((SysAbiFunction)function).Invoke(context);
|
||||
|
||||
if (!context.WasRaxWritten)
|
||||
{
|
||||
context[CpuRegister.Rax] = unchecked((ulong)ret);
|
||||
}
|
||||
|
||||
return (OrbisGen2Result)ret;
|
||||
}
|
||||
|
||||
private static Delegate CreateHandler(Type ownerType, MethodInfo method, IDictionary<Type, object> instances)
|
||||
{
|
||||
ValidateSignature(method);
|
||||
|
||||
object? target = null;
|
||||
if (!method.IsStatic)
|
||||
{
|
||||
if (!instances.TryGetValue(ownerType, out target))
|
||||
{
|
||||
target = Activator.CreateInstance(ownerType)
|
||||
?? throw new InvalidOperationException($"Cannot instantiate module type: {ownerType.FullName}");
|
||||
instances.Add(ownerType, target);
|
||||
}
|
||||
}
|
||||
|
||||
var parameterCount = method.GetParameters().Length;
|
||||
if (parameterCount == 0)
|
||||
{
|
||||
var noArg = method.IsStatic
|
||||
? (Func<int>)method.CreateDelegate(typeof(Func<int>))
|
||||
: (Func<int>)method.CreateDelegate(typeof(Func<int>), target!);
|
||||
|
||||
SysAbiFunction adapter = _ => noArg();
|
||||
return adapter;
|
||||
}
|
||||
|
||||
return method.IsStatic
|
||||
? method.CreateDelegate(typeof(SysAbiFunction))
|
||||
: method.CreateDelegate(typeof(SysAbiFunction), target!);
|
||||
}
|
||||
|
||||
private static void ValidateSignature(MethodInfo method)
|
||||
{
|
||||
if (method.ReturnType != typeof(int))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Method {method.DeclaringType?.FullName}.{method.Name} must return int.");
|
||||
}
|
||||
|
||||
var parameters = method.GetParameters();
|
||||
if (parameters.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (parameters.Length == 1 && parameters[0].ParameterType == typeof(CpuContext))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException(
|
||||
$"Method {method.DeclaringType?.FullName}.{method.Name} must accept no arguments or one {nameof(CpuContext)} argument.");
|
||||
}
|
||||
|
||||
private static ExportInfo? ResolveExportInfo(
|
||||
SysAbiExportAttribute exportAttribute,
|
||||
MethodInfo method,
|
||||
Generation generation,
|
||||
ISymbolCatalog? symbolCatalog)
|
||||
{
|
||||
var target = exportAttribute.Target == Generation.None
|
||||
? generation
|
||||
: exportAttribute.Target;
|
||||
if ((target & generation) == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var nid = exportAttribute.Nid;
|
||||
var exportName = exportAttribute.ExportName;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(nid) && !string.IsNullOrWhiteSpace(exportName) && symbolCatalog?.TryGetByExportName(exportName, out var byName) == true)
|
||||
{
|
||||
nid = byName.Nid;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(nid) && symbolCatalog?.TryGetByNid(nid, out var byNid) == true)
|
||||
{
|
||||
exportName = string.IsNullOrWhiteSpace(exportName) ? byNid.ExportName : exportName;
|
||||
target = exportAttribute.Target == Generation.None ? byNid.Target : target;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(nid))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Method {method.DeclaringType?.FullName}.{method.Name} must define a NID or match one in symbols catalog.");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(exportName))
|
||||
{
|
||||
exportName = method.Name;
|
||||
}
|
||||
|
||||
if ((target & generation) == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var libraryName = string.IsNullOrWhiteSpace(exportAttribute.LibraryName) ? "libKernel" : exportAttribute.LibraryName;
|
||||
return new ExportInfo(nid, exportName, libraryName, target);
|
||||
}
|
||||
|
||||
private readonly record struct ExportInfo(string Nid, string ExportName, string LibraryName, Generation Target);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
namespace SharpEmu.HLE;
|
||||
|
||||
/// <summary>
|
||||
/// Represents synthetic kernel-style result codes used by the Gen5 runtime.
|
||||
/// Prefixed with ORBIS_GEN2 to distinguish from PS4-oriented ORBIS_* codes
|
||||
/// used by other emulators such as shadPS4.
|
||||
/// </summary>
|
||||
public enum OrbisGen2Result : int
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates successful completion.
|
||||
/// </summary>
|
||||
ORBIS_GEN2_OK = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the requested export was not found.
|
||||
/// </summary>
|
||||
ORBIS_GEN2_ERROR_NOT_FOUND = unchecked((int)0x80020002),
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that one or more arguments were invalid.
|
||||
/// </summary>
|
||||
ORBIS_GEN2_ERROR_INVALID_ARGUMENT = unchecked((int)0x80020003),
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that an item already exists.
|
||||
/// </summary>
|
||||
ORBIS_GEN2_ERROR_ALREADY_EXISTS = unchecked((int)0x80020004),
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that behavior is recognized but not implemented yet.
|
||||
/// </summary>
|
||||
ORBIS_GEN2_ERROR_NOT_IMPLEMENTED = unchecked((int)0x8002FFFF),
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that memory access failed.
|
||||
/// </summary>
|
||||
ORBIS_GEN2_ERROR_MEMORY_FAULT = unchecked((int)0x80020101),
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that CPU execution trapped on an unsupported instruction.
|
||||
/// </summary>
|
||||
ORBIS_GEN2_ERROR_CPU_TRAP = unchecked((int)0x80020102),
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<!--
|
||||
Copyright (C) 2026 SharpEmu Emulator Project
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
-->
|
||||
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Aerolib\aerolib.bin" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
namespace SharpEmu.HLE;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class SysAbiExportAttribute : Attribute
|
||||
{
|
||||
public string LibraryName { get; set; } = "libKernel";
|
||||
|
||||
public string Nid { get; set; } = string.Empty;
|
||||
|
||||
public string ExportName { get; set; } = string.Empty;
|
||||
|
||||
public Generation Target { get; set; } = Generation.None;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
namespace SharpEmu.HLE;
|
||||
|
||||
public delegate int SysAbiFunction(CpuContext context);
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
namespace SharpEmu.HLE;
|
||||
|
||||
public readonly struct SysAbiSymbol
|
||||
{
|
||||
public SysAbiSymbol(string nid, string aliasName, string exportName, Generation target)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(nid);
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(exportName);
|
||||
|
||||
Nid = nid;
|
||||
AliasName = aliasName;
|
||||
ExportName = exportName;
|
||||
Target = target;
|
||||
}
|
||||
|
||||
public string Nid { get; }
|
||||
|
||||
public string AliasName { get; }
|
||||
|
||||
public string ExportName { get; }
|
||||
|
||||
public Generation Target { get; }
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"version": 2,
|
||||
"dependencies": {
|
||||
"net10.0": {}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user