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:
Mike Saito
2026-07-14 12:59:59 +03:00
committed by GitHub
parent 4db98bd8fe
commit 4f028d0483
7 changed files with 6427 additions and 64 deletions

View File

@@ -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.

View File

@@ -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;
}
}

View File

@@ -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)
{

View 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;
}
}

View File

@@ -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",