mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-15 23:31:07 +00:00
Expand Aerolib from nids.csv and wire socket/net kernel NID handlers (#128)
* Expand Aerolib catalog from nids.csv and wire socket/net NID handlers Load authoritative NID pairs from scripts/nids.csv with ps5_names fallback. Replace mislabeled kernel zero stubs with socket/connect/bind/getsockname HLE and sceNet byte-order exports backed by the CSV symbol names. * Add inet_pton, htons, and bzero kernel compat with CSV NIDs Wire libc network helpers using authoritative NID names from nids.csv instead of synthetic Gst* exports used on the crt-loader branch. * Fix REUSE annotation for scripts/nids.csv * Drop bundled nids.csv; extend ps5_names and regenerate Aerolib Remove scripts/nids.csv from the repository and fold csv-only symbol names into scripts/ps5_names.txt so Aerolib keeps the full catalog via name2nid.
This commit is contained in:
@@ -3,8 +3,8 @@
|
||||
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import struct
|
||||
import hashlib
|
||||
import struct
|
||||
from base64 import b64encode as base64enc
|
||||
from binascii import unhexlify as uhx
|
||||
from pathlib import Path
|
||||
@@ -21,7 +21,7 @@ def name2nid(name):
|
||||
def generate():
|
||||
names_path = Path(NAMES)
|
||||
output_path = Path(OUTPUT)
|
||||
|
||||
|
||||
entries = []
|
||||
with open(names_path, 'r', encoding='utf-8') as f:
|
||||
for line in f:
|
||||
@@ -29,12 +29,12 @@ def generate():
|
||||
if name:
|
||||
nid = name2nid(name)
|
||||
entries.append((nid, name))
|
||||
|
||||
|
||||
print(f"Found {len(entries)} entries")
|
||||
|
||||
data = bytearray()
|
||||
data.extend(struct.pack('<I', len(entries)))
|
||||
|
||||
|
||||
for nid, name in entries:
|
||||
nid_bytes = nid.encode('utf-8')
|
||||
name_bytes = name.encode('utf-8')
|
||||
@@ -42,10 +42,11 @@ def generate():
|
||||
data.extend(nid_bytes)
|
||||
data.extend(struct.pack('<H', len(name_bytes)))
|
||||
data.extend(name_bytes)
|
||||
|
||||
|
||||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(output_path, 'wb') as f:
|
||||
f.write(data)
|
||||
|
||||
|
||||
print(f"Generated: {output_path} ({len(data):,} bytes)")
|
||||
print(f"Total entries: {len(entries)}")
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -461,66 +461,10 @@ public static class KernelExports
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
// Unidentified kernel NIDs observed at runtime; stubbed to return 0 until they are
|
||||
// resolved against scripts/ps5_names.txt.
|
||||
[SysAbiExport(
|
||||
Nid = "9T2pDF2Ryqg",
|
||||
ExportName = "sceKernelUnknown9T2p",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int KernelUnknown9T2p(CpuContext ctx) => SetZeroReturn(ctx);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "TU-d9PfIHPM",
|
||||
ExportName = "sceKernelUnknownTU",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int KernelUnknownTU(CpuContext ctx) => SetZeroReturn(ctx);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "iWQWrwiSt8A",
|
||||
ExportName = "sceKernelUnknowniWQW",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int KernelUnknowniWQW(CpuContext ctx) => SetZeroReturn(ctx);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "KuOmgKoqCdY",
|
||||
ExportName = "sceKernelUnknownKuOmg",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int KernelUnknownKuOmg(CpuContext ctx) => SetZeroReturn(ctx);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "RenI1lL1WFk",
|
||||
ExportName = "sceKernelUnknownRenI",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int KernelUnknownRenI(CpuContext ctx) => SetZeroReturn(ctx);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "pQGpHYopAIY",
|
||||
ExportName = "sceKernelUnknownpQGp",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int KernelUnknownpQGp(CpuContext ctx) => SetZeroReturn(ctx);
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "Rbvt+5Y2iEw",
|
||||
ExportName = "sceKernelUnknownRbvt",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int KernelUnknownRbvt(CpuContext ctx) => SetZeroReturn(ctx);
|
||||
|
||||
// NOTE: "SHtAad20YYM"/"DIxvoy7Ngvk" are sce::Json::Value::getType/getInteger, and
|
||||
// "3GPpjQdAMTw"/"9rAeANT2tyE"/"tsvEmnenz48" are __cxa_guard_acquire/__cxa_guard_release/
|
||||
// __cxa_atexit (verified by hashing against scripts/ps5_names.txt). Do not register them
|
||||
// here as kernel mutex functions: the cxa guards are implemented in CxxAbiExports.cs and
|
||||
// shadowing them breaks every C++ static-init guard in the game.
|
||||
|
||||
private static int SetZeroReturn(CpuContext ctx)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,6 +110,15 @@ public static class KernelMemoryCompatExports
|
||||
private static readonly HashSet<string> _negativeStatCache = new(StringComparer.OrdinalIgnoreCase);
|
||||
private static readonly ConcurrentDictionary<string, ulong> _aprFileSizeCache = new(StringComparer.OrdinalIgnoreCase);
|
||||
private static long _nextFileDescriptor = 2;
|
||||
|
||||
internal static int AllocateGuestFileDescriptor()
|
||||
{
|
||||
lock (_fdGate)
|
||||
{
|
||||
return (int)Interlocked.Increment(ref _nextFileDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
private static ulong _nextPhysicalAddress;
|
||||
private static ulong _nextVirtualAddress;
|
||||
private static ulong _mainDirectMemoryPoolBase = UnsetMainDirectMemoryPoolBase;
|
||||
@@ -1555,9 +1564,10 @@ public static class KernelMemoryCompatExports
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
var directoryFd = (int)Interlocked.Increment(ref _nextFileDescriptor);
|
||||
int directoryFd;
|
||||
lock (_fdGate)
|
||||
{
|
||||
directoryFd = AllocateGuestFileDescriptor();
|
||||
_openDirectories[directoryFd] = new OpenDirectory
|
||||
{
|
||||
Path = hostPath,
|
||||
@@ -1578,9 +1588,10 @@ public static class KernelMemoryCompatExports
|
||||
stream.Seek(0, SeekOrigin.End);
|
||||
}
|
||||
|
||||
var fd = (int)Interlocked.Increment(ref _nextFileDescriptor);
|
||||
int fd;
|
||||
lock (_fdGate)
|
||||
{
|
||||
fd = AllocateGuestFileDescriptor();
|
||||
_openFiles[fd] = stream;
|
||||
}
|
||||
|
||||
@@ -1953,6 +1964,12 @@ public static class KernelMemoryCompatExports
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
if (KernelSocketCompatExports.TryCloseSocketFd(fd))
|
||||
{
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
FileStream? stream;
|
||||
lock (_fdGate)
|
||||
{
|
||||
@@ -1996,6 +2013,17 @@ public static class KernelMemoryCompatExports
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
if (KernelSocketCompatExports.TryReadSocketFd(ctx, fd, bufferAddress, requested, out var socketBytesRead, out var socketError))
|
||||
{
|
||||
if (socketError != OrbisGen2Result.ORBIS_GEN2_OK)
|
||||
{
|
||||
return (int)socketError;
|
||||
}
|
||||
|
||||
ctx[CpuRegister.Rax] = socketBytesRead;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
FileStream? stream;
|
||||
lock (_fdGate)
|
||||
{
|
||||
@@ -2225,6 +2253,17 @@ public static class KernelMemoryCompatExports
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
if (KernelSocketCompatExports.TryWriteSocketFd(ctx, fd, payload, out var socketError))
|
||||
{
|
||||
if (socketError != OrbisGen2Result.ORBIS_GEN2_OK)
|
||||
{
|
||||
return (int)socketError;
|
||||
}
|
||||
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)requested);
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
FileStream? stream;
|
||||
lock (_fdGate)
|
||||
{
|
||||
|
||||
558
src/SharpEmu.Libs/Kernel/KernelSocketCompatExports.cs
Normal file
558
src/SharpEmu.Libs/Kernel/KernelSocketCompatExports.cs
Normal file
@@ -0,0 +1,558 @@
|
||||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
using System.Buffers.Binary;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using SharpEmu.HLE;
|
||||
|
||||
namespace SharpEmu.Libs.Kernel;
|
||||
|
||||
internal static class KernelSocketCompatExports
|
||||
{
|
||||
private sealed class EmulatedSocketState
|
||||
{
|
||||
public TcpClient? Client;
|
||||
public NetworkStream? Stream;
|
||||
public IPAddress BoundAddress = IPAddress.Any;
|
||||
public int BoundPort;
|
||||
public bool Bound;
|
||||
public bool Connected;
|
||||
}
|
||||
|
||||
private static readonly object Gate = new();
|
||||
private static readonly Dictionary<int, EmulatedSocketState> Sockets = new();
|
||||
|
||||
internal static bool IsEmulatedSocketFd(int fd)
|
||||
{
|
||||
lock (Gate)
|
||||
{
|
||||
return Sockets.ContainsKey(fd);
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool TryCloseSocketFd(int fd)
|
||||
{
|
||||
lock (Gate)
|
||||
{
|
||||
if (!Sockets.Remove(fd, out var state))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DisposeEmulatedSocket(state);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool TryReadSocketFd(
|
||||
CpuContext ctx,
|
||||
int fd,
|
||||
ulong bufferAddress,
|
||||
int requested,
|
||||
out ulong bytesRead,
|
||||
out OrbisGen2Result error)
|
||||
{
|
||||
bytesRead = 0;
|
||||
error = OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
||||
|
||||
if (!TryGetEmulatedSocketState(fd, out var state) ||
|
||||
state is null ||
|
||||
!state.Connected ||
|
||||
state.Stream is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var socketBuffer = GC.AllocateUninitializedArray<byte>(requested);
|
||||
int socketRead;
|
||||
try
|
||||
{
|
||||
socketRead = state.Stream.Read(socketBuffer, 0, requested);
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (socketRead > 0 && !ctx.Memory.TryWrite(bufferAddress, socketBuffer.AsSpan(0, socketRead)))
|
||||
{
|
||||
error = OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
return true;
|
||||
}
|
||||
|
||||
bytesRead = unchecked((ulong)socketRead);
|
||||
error = OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static bool TryWriteSocketFd(
|
||||
CpuContext ctx,
|
||||
int fd,
|
||||
byte[] payload,
|
||||
out OrbisGen2Result error)
|
||||
{
|
||||
error = OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
||||
|
||||
if (!TryGetEmulatedSocketState(fd, out var state) ||
|
||||
state is null ||
|
||||
!state.Connected ||
|
||||
state.Stream is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
state.Stream.Write(payload, 0, payload.Length);
|
||||
state.Stream.Flush();
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_NET"), "1", StringComparison.Ordinal))
|
||||
{
|
||||
Console.Out.Write(Encoding.UTF8.GetString(payload));
|
||||
Console.Out.Flush();
|
||||
}
|
||||
|
||||
error = OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
return true;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "TU-d9PfIHPM",
|
||||
ExportName = "socket",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int Socket(CpuContext ctx)
|
||||
{
|
||||
var fd = KernelMemoryCompatExports.AllocateGuestFileDescriptor();
|
||||
lock (Gate)
|
||||
{
|
||||
Sockets[fd] = new EmulatedSocketState();
|
||||
}
|
||||
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)fd);
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "XVL8So3QJUk",
|
||||
ExportName = "connect",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int Connect(CpuContext ctx)
|
||||
{
|
||||
var fd = unchecked((int)ctx[CpuRegister.Rdi]);
|
||||
var sockaddrAddress = ctx[CpuRegister.Rsi];
|
||||
var addrlen = unchecked((int)ctx[CpuRegister.Rdx]);
|
||||
|
||||
if (!TryGetEmulatedSocketState(fd, out _))
|
||||
{
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)0xFFFFFFFFFFFFFFFF);
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
if (!TryParseGuestSockaddrIn(sockaddrAddress, addrlen, ctx, out var ipAddress, out var port))
|
||||
{
|
||||
LogNet($"connect sockaddr parse failed: fd={fd} addr=0x{sockaddrAddress:X} len={addrlen}");
|
||||
RemoveEmulatedSocketFd(fd);
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)0xFFFFFFFFFFFFFFFF);
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
var redirectApplied = TryApplyNetRedirect(ref ipAddress);
|
||||
if (redirectApplied)
|
||||
{
|
||||
LogNet($"connect redirect: fd={fd} ip={ipAddress} port={port}");
|
||||
}
|
||||
|
||||
if (!IsGuestTcpOutboundAllowed(ipAddress, redirectApplied))
|
||||
{
|
||||
LogNet($"connect denied by outbound policy: fd={fd} ip={ipAddress} port={port}");
|
||||
RemoveEmulatedSocketFd(fd);
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)0xFFFFFFFFFFFFFFFF);
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
if (!TryEstablishHostTcpConnection(ipAddress, port, out var client, out var stream))
|
||||
{
|
||||
LogNet($"connect failed: fd={fd} ip={ipAddress} port={port}");
|
||||
RemoveEmulatedSocketFd(fd);
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)0xFFFFFFFFFFFFFFFF);
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
LogNet($"connect ok: fd={fd} ip={ipAddress} port={port}");
|
||||
|
||||
lock (Gate)
|
||||
{
|
||||
if (!Sockets.TryGetValue(fd, out var state) || state is null)
|
||||
{
|
||||
try { stream.Dispose(); } catch (IOException) { }
|
||||
try { client.Dispose(); } catch (IOException) { }
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)0xFFFFFFFFFFFFFFFF);
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
DisposeEmulatedSocket(state);
|
||||
state.Client = client;
|
||||
state.Stream = stream;
|
||||
state.Connected = true;
|
||||
state.BoundAddress = ipAddress;
|
||||
state.BoundPort = port;
|
||||
state.Bound = true;
|
||||
}
|
||||
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "KuOmgKoqCdY",
|
||||
ExportName = "bind",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int Bind(CpuContext ctx)
|
||||
{
|
||||
var fd = unchecked((int)ctx[CpuRegister.Rdi]);
|
||||
var sockaddrAddress = ctx[CpuRegister.Rsi];
|
||||
var addrlen = unchecked((int)ctx[CpuRegister.Rdx]);
|
||||
|
||||
if (!TryGetEmulatedSocketState(fd, out var state) || state is null)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)0xFFFFFFFFFFFFFFFF);
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
if (!TryParseGuestSockaddrIn(sockaddrAddress, addrlen, ctx, out var ipAddress, out var port))
|
||||
{
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)0xFFFFFFFFFFFFFFFF);
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
state.BoundAddress = ipAddress;
|
||||
state.BoundPort = port;
|
||||
state.Bound = true;
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "RenI1lL1WFk",
|
||||
ExportName = "getsockname",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int Getsockname(CpuContext ctx)
|
||||
{
|
||||
var fd = unchecked((int)ctx[CpuRegister.Rdi]);
|
||||
var sockaddrAddress = ctx[CpuRegister.Rsi];
|
||||
var addrlenAddress = ctx[CpuRegister.Rdx];
|
||||
|
||||
if (!TryGetEmulatedSocketState(fd, out var state) || state is null || !state.Bound)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)0xFFFFFFFFFFFFFFFF);
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
Span<byte> addrlenBuffer = stackalloc byte[4];
|
||||
if (!ctx.Memory.TryRead(addrlenAddress, addrlenBuffer))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
var addrlen = BinaryPrimitives.ReadInt32LittleEndian(addrlenBuffer);
|
||||
if (addrlen < 8)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)0xFFFFFFFFFFFFFFFF);
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
Span<byte> sockaddr = stackalloc byte[16];
|
||||
sockaddr[0] = 16;
|
||||
sockaddr[1] = 2;
|
||||
BinaryPrimitives.WriteUInt16BigEndian(sockaddr.Slice(2, 2), (ushort)state.BoundPort);
|
||||
var addressBytes = state.BoundAddress.GetAddressBytes();
|
||||
if (addressBytes.Length == 4)
|
||||
{
|
||||
addressBytes.CopyTo(sockaddr.Slice(4, 4));
|
||||
}
|
||||
|
||||
var writeLength = Math.Min(addrlen, 16);
|
||||
if (!ctx.Memory.TryWrite(sockaddrAddress, sockaddr.Slice(0, writeLength)))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
BinaryPrimitives.WriteInt32LittleEndian(addrlenBuffer, writeLength);
|
||||
if (!ctx.Memory.TryWrite(addrlenAddress, addrlenBuffer))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "9oiX1kyeedA",
|
||||
ExportName = "bzero",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int Bzero(CpuContext ctx)
|
||||
{
|
||||
var address = ctx[CpuRegister.Rdi];
|
||||
var length = unchecked((int)ctx[CpuRegister.Rsi]);
|
||||
if (length > 0 && address != 0)
|
||||
{
|
||||
var zeros = new byte[length];
|
||||
if (!ctx.Memory.TryWrite(address, zeros))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
}
|
||||
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "4n51s0zEf0c",
|
||||
ExportName = "inet_pton",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int InetPton(CpuContext ctx)
|
||||
{
|
||||
var af = unchecked((int)ctx[CpuRegister.Rdi]);
|
||||
var srcAddress = ctx[CpuRegister.Rsi];
|
||||
var dstAddress = ctx[CpuRegister.Rdx];
|
||||
if (af != 2 || srcAddress == 0 || dstAddress == 0)
|
||||
{
|
||||
ctx[CpuRegister.Rax] = unchecked((ulong)0xFFFFFFFFFFFFFFFF);
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (!TryReadCString(srcAddress, ctx, out var text) ||
|
||||
!TryParseIpv4Address(text, out var octets))
|
||||
{
|
||||
ctx[CpuRegister.Rax] = 0;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
Span<byte> packed = stackalloc byte[4];
|
||||
packed[0] = octets[0];
|
||||
packed[1] = octets[1];
|
||||
packed[2] = octets[2];
|
||||
packed[3] = octets[3];
|
||||
if (!ctx.Memory.TryWrite(dstAddress, packed))
|
||||
{
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||
}
|
||||
|
||||
ctx[CpuRegister.Rax] = 1;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "jogUIsOV3-U",
|
||||
ExportName = "htons",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libKernel")]
|
||||
public static int Htons(CpuContext ctx)
|
||||
{
|
||||
var value = unchecked((ushort)ctx[CpuRegister.Rdi]);
|
||||
var swapped = (ushort)(((value & 0x00FF) << 8) | ((value >> 8) & 0x00FF));
|
||||
ctx[CpuRegister.Rax] = swapped;
|
||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||
}
|
||||
|
||||
private static bool TryGetEmulatedSocketState(int fd, out EmulatedSocketState? state)
|
||||
{
|
||||
lock (Gate)
|
||||
{
|
||||
return Sockets.TryGetValue(fd, out state);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryParseGuestSockaddrIn(
|
||||
ulong address,
|
||||
int addrlen,
|
||||
CpuContext ctx,
|
||||
out IPAddress ipAddress,
|
||||
out int port)
|
||||
{
|
||||
ipAddress = IPAddress.None;
|
||||
port = 0;
|
||||
if (address == 0 || addrlen < 8)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Span<byte> buffer = stackalloc byte[16];
|
||||
var readLength = Math.Min(addrlen, buffer.Length);
|
||||
if (!ctx.Memory.TryRead(address, buffer.Slice(0, readLength)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (buffer[1] != 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
port = BinaryPrimitives.ReadUInt16BigEndian(buffer.Slice(2, 2));
|
||||
ipAddress = new IPAddress(buffer.Slice(4, 4).ToArray());
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void DisposeEmulatedSocket(EmulatedSocketState state)
|
||||
{
|
||||
try { state.Stream?.Dispose(); } catch (IOException) { }
|
||||
try { state.Client?.Dispose(); } catch (IOException) { }
|
||||
state.Stream = null;
|
||||
state.Client = null;
|
||||
state.Connected = false;
|
||||
}
|
||||
|
||||
private static void RemoveEmulatedSocketFd(int fd)
|
||||
{
|
||||
lock (Gate)
|
||||
{
|
||||
if (Sockets.Remove(fd, out var socketState))
|
||||
{
|
||||
DisposeEmulatedSocket(socketState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void LogNet(string message)
|
||||
{
|
||||
if (string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_NET"), "1", StringComparison.Ordinal))
|
||||
{
|
||||
Console.Error.WriteLine($"[LOADER][DEBUG] {message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryApplyNetRedirect(ref IPAddress ipAddress)
|
||||
{
|
||||
var redirect = Environment.GetEnvironmentVariable("SHARPEMU_NET_REDIRECT");
|
||||
if (string.IsNullOrWhiteSpace(redirect))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IPAddress.TryParse(redirect.Trim(), out var redirectAddress))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ipAddress = redirectAddress;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool IsNetRedirectConfigured()
|
||||
{
|
||||
var redirect = Environment.GetEnvironmentVariable("SHARPEMU_NET_REDIRECT");
|
||||
return !string.IsNullOrWhiteSpace(redirect);
|
||||
}
|
||||
|
||||
private static bool IsGuestTcpOutboundAllowed(IPAddress ipAddress, bool redirectApplied)
|
||||
{
|
||||
return redirectApplied || IsNetRedirectConfigured() || IPAddress.IsLoopback(ipAddress);
|
||||
}
|
||||
|
||||
private static bool TryEstablishHostTcpConnection(
|
||||
IPAddress ipAddress,
|
||||
int port,
|
||||
out TcpClient client,
|
||||
out NetworkStream stream)
|
||||
{
|
||||
client = null!;
|
||||
stream = null!;
|
||||
if (!TryConnectTcpClient(ipAddress, port, out client))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
stream = client.GetStream();
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryConnectTcpClient(IPAddress ipAddress, int port, out TcpClient client)
|
||||
{
|
||||
client = new TcpClient();
|
||||
try
|
||||
{
|
||||
var connectTask = client.ConnectAsync(ipAddress, port);
|
||||
if (!connectTask.Wait(TimeSpan.FromMilliseconds(500)))
|
||||
{
|
||||
client.Dispose();
|
||||
client = null!;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (SocketException)
|
||||
{
|
||||
client.Dispose();
|
||||
client = null!;
|
||||
return false;
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
client.Dispose();
|
||||
client = null!;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryReadCString(ulong address, CpuContext ctx, out string text)
|
||||
{
|
||||
const int maxLength = 64;
|
||||
var buffer = new byte[maxLength];
|
||||
var length = 0;
|
||||
for (; length < maxLength; length++)
|
||||
{
|
||||
if (!ctx.Memory.TryRead(address + (ulong)length, buffer.AsSpan(length, 1)))
|
||||
{
|
||||
text = string.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (buffer[length] == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
text = Encoding.ASCII.GetString(buffer, 0, length);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryParseIpv4Address(string text, out byte[] octets)
|
||||
{
|
||||
octets = Array.Empty<byte>();
|
||||
var parts = text.Split('.', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||
if (parts.Length != 4)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var parsed = new byte[4];
|
||||
for (var i = 0; i < 4; i++)
|
||||
{
|
||||
if (!byte.TryParse(parts[i], out parsed[i]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
octets = parsed;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -95,6 +95,54 @@ public static class NetExports
|
||||
return ctx.SetReturn(0);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "9T2pDF2Ryqg",
|
||||
ExportName = "sceNetHtonl",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceNet")]
|
||||
public static int NetHtonl(CpuContext ctx)
|
||||
{
|
||||
var value = unchecked((uint)ctx[CpuRegister.Rdi]);
|
||||
ctx[CpuRegister.Rax] = BinaryPrimitives.ReverseEndianness(value);
|
||||
return ctx.SetReturn(0);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "iWQWrwiSt8A",
|
||||
ExportName = "sceNetHtons",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceNet")]
|
||||
public static int NetHtons(CpuContext ctx)
|
||||
{
|
||||
var value = unchecked((ushort)ctx[CpuRegister.Rdi]);
|
||||
ctx[CpuRegister.Rax] = BinaryPrimitives.ReverseEndianness(value);
|
||||
return ctx.SetReturn(0);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "pQGpHYopAIY",
|
||||
ExportName = "sceNetNtohl",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceNet")]
|
||||
public static int NetNtohl(CpuContext ctx)
|
||||
{
|
||||
var value = unchecked((uint)ctx[CpuRegister.Rdi]);
|
||||
ctx[CpuRegister.Rax] = BinaryPrimitives.ReverseEndianness(value);
|
||||
return ctx.SetReturn(0);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "Rbvt+5Y2iEw",
|
||||
ExportName = "sceNetNtohs",
|
||||
Target = Generation.Gen4 | Generation.Gen5,
|
||||
LibraryName = "libSceNet")]
|
||||
public static int NetNtohs(CpuContext ctx)
|
||||
{
|
||||
var value = unchecked((ushort)ctx[CpuRegister.Rdi]);
|
||||
ctx[CpuRegister.Rax] = BinaryPrimitives.ReverseEndianness(value);
|
||||
return ctx.SetReturn(0);
|
||||
}
|
||||
|
||||
[SysAbiExport(
|
||||
Nid = "C4UgDHHPvdw",
|
||||
ExportName = "sceNetResolverCreate",
|
||||
|
||||
Reference in New Issue
Block a user