mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-14 12:56:14 +08:00
203 lines
6.4 KiB
C#
203 lines
6.4 KiB
C#
// 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.Network;
|
|
|
|
public static class NetExports
|
|
{
|
|
private const int NetErrorBadFileDescriptor = unchecked((int)0x80410109);
|
|
private const int NetErrorInvalidArgument = unchecked((int)0x80410116);
|
|
private const int MaxNameLength = 256;
|
|
|
|
private static readonly ConcurrentDictionary<int, NetPool> _pools = new();
|
|
private static readonly ConcurrentDictionary<int, ResolverContext> _resolvers = new();
|
|
private static int _nextPoolId;
|
|
private static int _nextResolverId = 0x2000;
|
|
private static bool _initialized;
|
|
|
|
private sealed record NetPool(string Name, int Size, int Flags);
|
|
|
|
private sealed record ResolverContext(string Name, int PoolId, int Flags, int LastError);
|
|
|
|
[SysAbiExport(
|
|
Nid = "Nlev7Lg8k3A",
|
|
ExportName = "sceNetInit",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libSceNet")]
|
|
public static int NetInit(CpuContext ctx)
|
|
{
|
|
_initialized = true;
|
|
TraceNet("init", 0, 0, 0, 0);
|
|
return ctx.SetReturn(0);
|
|
}
|
|
|
|
[SysAbiExport(
|
|
Nid = "cTGkc6-TBlI",
|
|
ExportName = "sceNetTerm",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libSceNet")]
|
|
public static int NetTerm(CpuContext ctx)
|
|
{
|
|
_initialized = false;
|
|
_pools.Clear();
|
|
_resolvers.Clear();
|
|
TraceNet("term", 0, 0, 0, 0);
|
|
return ctx.SetReturn(0);
|
|
}
|
|
|
|
[SysAbiExport(
|
|
Nid = "dgJBaeJnGpo",
|
|
ExportName = "sceNetPoolCreate",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libSceNet")]
|
|
public static int NetPoolCreate(CpuContext ctx)
|
|
{
|
|
var nameAddress = ctx[CpuRegister.Rdi];
|
|
var size = unchecked((int)ctx[CpuRegister.Rsi]);
|
|
var flags = unchecked((int)ctx[CpuRegister.Rdx]);
|
|
|
|
if (size <= 0)
|
|
{
|
|
return ctx.SetReturn(NetErrorInvalidArgument);
|
|
}
|
|
|
|
var name = TryReadUtf8Z(ctx, nameAddress, MaxNameLength, out var value)
|
|
? value
|
|
: string.Empty;
|
|
|
|
var id = Interlocked.Increment(ref _nextPoolId);
|
|
_pools[id] = new NetPool(name, size, flags);
|
|
|
|
TraceNet("pool.create", id, unchecked((ulong)size), unchecked((ulong)flags), _initialized ? 1UL : 0UL);
|
|
ctx[CpuRegister.Rax] = unchecked((ulong)id);
|
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
|
}
|
|
|
|
[SysAbiExport(
|
|
Nid = "K7RlrTkI-mw",
|
|
ExportName = "sceNetPoolDestroy",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libSceNet")]
|
|
public static int NetPoolDestroy(CpuContext ctx)
|
|
{
|
|
var id = unchecked((int)ctx[CpuRegister.Rdi]);
|
|
if (!_pools.TryRemove(id, out _))
|
|
{
|
|
return ctx.SetReturn(NetErrorBadFileDescriptor);
|
|
}
|
|
|
|
TraceNet("pool.destroy", id, 0, 0, _initialized ? 1UL : 0UL);
|
|
return ctx.SetReturn(0);
|
|
}
|
|
|
|
[SysAbiExport(
|
|
Nid = "C4UgDHHPvdw",
|
|
ExportName = "sceNetResolverCreate",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libSceNet")]
|
|
public static int NetResolverCreate(CpuContext ctx)
|
|
{
|
|
var nameAddress = ctx[CpuRegister.Rdi];
|
|
var poolId = unchecked((int)ctx[CpuRegister.Rsi]);
|
|
var flags = unchecked((int)ctx[CpuRegister.Rdx]);
|
|
if (flags != 0)
|
|
{
|
|
return ctx.SetReturn(NetErrorInvalidArgument);
|
|
}
|
|
|
|
var name = TryReadUtf8Z(ctx, nameAddress, MaxNameLength, out var value)
|
|
? value
|
|
: string.Empty;
|
|
var id = Interlocked.Increment(ref _nextResolverId);
|
|
_resolvers[id] = new ResolverContext(name, poolId, flags, 0);
|
|
TraceNet("resolver.create", id, unchecked((ulong)poolId), unchecked((ulong)flags), _initialized ? 1UL : 0UL);
|
|
ctx[CpuRegister.Rax] = unchecked((ulong)id);
|
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
|
}
|
|
|
|
[SysAbiExport(
|
|
Nid = "kJlYH5uMAWI",
|
|
ExportName = "sceNetResolverDestroy",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libSceNet")]
|
|
public static int NetResolverDestroy(CpuContext ctx)
|
|
{
|
|
var id = unchecked((int)ctx[CpuRegister.Rdi]);
|
|
return _resolvers.TryRemove(id, out _)
|
|
? ctx.SetReturn(0)
|
|
: ctx.SetReturn(NetErrorBadFileDescriptor);
|
|
}
|
|
|
|
[SysAbiExport(
|
|
Nid = "J5i3hiLJMPk",
|
|
ExportName = "sceNetResolverGetError",
|
|
Target = Generation.Gen4 | Generation.Gen5,
|
|
LibraryName = "libSceNet")]
|
|
public static int NetResolverGetError(CpuContext ctx)
|
|
{
|
|
var id = unchecked((int)ctx[CpuRegister.Rdi]);
|
|
var statusAddress = ctx[CpuRegister.Rsi];
|
|
if (statusAddress == 0)
|
|
{
|
|
return ctx.SetReturn(NetErrorInvalidArgument);
|
|
}
|
|
|
|
if (!_resolvers.TryGetValue(id, out var resolver))
|
|
{
|
|
return ctx.SetReturn(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);
|
|
}
|
|
|
|
private static bool TryReadUtf8Z(CpuContext ctx, ulong address, int maxLength, out string value)
|
|
{
|
|
value = string.Empty;
|
|
if (address == 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
Span<byte> one = stackalloc byte[1];
|
|
var bytes = new byte[maxLength];
|
|
var count = 0;
|
|
for (; count < maxLength; count++)
|
|
{
|
|
if (!ctx.Memory.TryRead(address + (ulong)count, one))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (one[0] == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
bytes[count] = one[0];
|
|
}
|
|
|
|
value = Encoding.UTF8.GetString(bytes, 0, count);
|
|
return true;
|
|
}
|
|
|
|
private static void TraceNet(string operation, int id, ulong arg0, ulong arg1, ulong arg2)
|
|
{
|
|
if (!string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_NET"), "1", StringComparison.Ordinal))
|
|
{
|
|
return;
|
|
}
|
|
|
|
Console.Error.WriteLine(
|
|
$"[LOADER][TRACE] net.{operation} id={id} arg0=0x{arg0:X16} arg1=0x{arg1:X16} arg2=0x{arg2:X16}");
|
|
}
|
|
}
|