Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
df99257d7f | ||
|
f3835dc78b | ||
|
51bb8707ef | ||
|
5ff5fe47ba | ||
|
38275f9056 | ||
|
67cbdc3a6a | ||
|
131b43170e | ||
|
730d2f4b9b | ||
|
f6a7309b14 | ||
|
472a621589 |
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: Something doesn't work correctly in Ryujinx.
|
||||
about: Something doesn't work correctly in Ryujinx. Note that game-specific issues should be instead posted on the Game Compatibility List at https://github.com/Ryujinx/Ryujinx-Games-List, unless it is a provable regression.
|
||||
#assignees:
|
||||
---
|
||||
|
||||
|
@@ -57,5 +57,19 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService
|
||||
{
|
||||
return _managerServer.StoreOpenContext(context);
|
||||
}
|
||||
|
||||
[CommandHipc(170)] // 6.0.0+
|
||||
// LoadNetworkServiceLicenseKindAsync() -> object<nn::account::detail::IAsyncNetworkServiceLicenseKindContext>
|
||||
public ResultCode LoadNetworkServiceLicenseKindAsync(ServiceCtx context)
|
||||
{
|
||||
ResultCode resultCode = _managerServer.LoadNetworkServiceLicenseKindAsync(context, out IAsyncNetworkServiceLicenseKindContext asyncContext);
|
||||
|
||||
if (resultCode == ResultCode.Success)
|
||||
{
|
||||
MakeObject(context, asyncContext);
|
||||
}
|
||||
|
||||
return resultCode;
|
||||
}
|
||||
}
|
||||
}
|
@@ -166,5 +166,22 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
public ResultCode LoadNetworkServiceLicenseKindAsync(ServiceCtx context, out IAsyncNetworkServiceLicenseKindContext asyncContext)
|
||||
{
|
||||
KEvent asyncEvent = new KEvent(context.Device.System.KernelContext);
|
||||
AsyncExecution asyncExecution = new AsyncExecution(asyncEvent);
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAcc);
|
||||
|
||||
// NOTE: This is an extension of the data retrieved from the id token cache.
|
||||
asyncExecution.Initialize(1000, EnsureIdTokenCacheAsyncImpl);
|
||||
|
||||
asyncContext = new IAsyncNetworkServiceLicenseKindContext(asyncExecution, NetworkServiceLicenseKind.Subscribed);
|
||||
|
||||
// return ResultCode.NullObject if the IAsyncNetworkServiceLicenseKindContext pointer is null. Doesn't occur in our case.
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
@@ -7,18 +7,18 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
||||
{
|
||||
class IAsyncContext : IpcService
|
||||
{
|
||||
AsyncExecution _asyncExecution;
|
||||
protected AsyncExecution AsyncExecution;
|
||||
|
||||
public IAsyncContext(AsyncExecution asyncExecution)
|
||||
{
|
||||
_asyncExecution = asyncExecution;
|
||||
AsyncExecution = asyncExecution;
|
||||
}
|
||||
|
||||
[CommandHipc(0)]
|
||||
// GetSystemEvent() -> handle<copy>
|
||||
public ResultCode GetSystemEvent(ServiceCtx context)
|
||||
{
|
||||
if (context.Process.HandleTable.GenerateHandle(_asyncExecution.SystemEvent.ReadableEvent, out int _systemEventHandle) != KernelResult.Success)
|
||||
if (context.Process.HandleTable.GenerateHandle(AsyncExecution.SystemEvent.ReadableEvent, out int _systemEventHandle) != KernelResult.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Out of handles!");
|
||||
}
|
||||
@@ -32,14 +32,14 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
||||
// Cancel()
|
||||
public ResultCode Cancel(ServiceCtx context)
|
||||
{
|
||||
if (!_asyncExecution.IsInitialized)
|
||||
if (!AsyncExecution.IsInitialized)
|
||||
{
|
||||
return ResultCode.AsyncExecutionNotInitialized;
|
||||
}
|
||||
|
||||
if (_asyncExecution.IsRunning)
|
||||
if (AsyncExecution.IsRunning)
|
||||
{
|
||||
_asyncExecution.Cancel();
|
||||
AsyncExecution.Cancel();
|
||||
}
|
||||
|
||||
return ResultCode.Success;
|
||||
@@ -49,12 +49,12 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
||||
// HasDone() -> b8
|
||||
public ResultCode HasDone(ServiceCtx context)
|
||||
{
|
||||
if (!_asyncExecution.IsInitialized)
|
||||
if (!AsyncExecution.IsInitialized)
|
||||
{
|
||||
return ResultCode.AsyncExecutionNotInitialized;
|
||||
}
|
||||
|
||||
context.ResponseData.Write(_asyncExecution.SystemEvent.ReadableEvent.IsSignaled());
|
||||
context.ResponseData.Write(AsyncExecution.SystemEvent.ReadableEvent.IsSignaled());
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
@@ -63,12 +63,12 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
||||
// GetResult()
|
||||
public ResultCode GetResult(ServiceCtx context)
|
||||
{
|
||||
if (!_asyncExecution.IsInitialized)
|
||||
if (!AsyncExecution.IsInitialized)
|
||||
{
|
||||
return ResultCode.AsyncExecutionNotInitialized;
|
||||
}
|
||||
|
||||
if (!_asyncExecution.SystemEvent.ReadableEvent.IsSignaled())
|
||||
if (!AsyncExecution.SystemEvent.ReadableEvent.IsSignaled())
|
||||
{
|
||||
return ResultCode.Unknown41;
|
||||
}
|
||||
|
@@ -0,0 +1,38 @@
|
||||
using Ryujinx.HLE.HOS.Services.Account.Acc.AsyncContext;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
||||
{
|
||||
class IAsyncNetworkServiceLicenseKindContext : IAsyncContext
|
||||
{
|
||||
private NetworkServiceLicenseKind? _serviceLicenseKind;
|
||||
|
||||
public IAsyncNetworkServiceLicenseKindContext(AsyncExecution asyncExecution, NetworkServiceLicenseKind? serviceLicenseKind) : base(asyncExecution)
|
||||
{
|
||||
_serviceLicenseKind = serviceLicenseKind;
|
||||
}
|
||||
|
||||
[CommandHipc(100)]
|
||||
// GetNetworkServiceLicenseKind() -> nn::account::NetworkServiceLicenseKind
|
||||
public ResultCode GetNetworkServiceLicenseKind(ServiceCtx context)
|
||||
{
|
||||
if (!AsyncExecution.IsInitialized)
|
||||
{
|
||||
return ResultCode.AsyncExecutionNotInitialized;
|
||||
}
|
||||
|
||||
if (!AsyncExecution.SystemEvent.ReadableEvent.IsSignaled())
|
||||
{
|
||||
return ResultCode.Unknown41;
|
||||
}
|
||||
|
||||
if (!_serviceLicenseKind.HasValue)
|
||||
{
|
||||
return ResultCode.MissingNetworkServiceLicenseKind;
|
||||
}
|
||||
|
||||
context.ResponseData.Write((uint)_serviceLicenseKind.Value);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
||||
{
|
||||
enum NetworkServiceLicenseKind : uint
|
||||
{
|
||||
NoSubscription,
|
||||
Subscribed
|
||||
}
|
||||
}
|
@@ -7,17 +7,18 @@ namespace Ryujinx.HLE.HOS.Services.Account
|
||||
|
||||
Success = 0,
|
||||
|
||||
NullArgument = (20 << ErrorCodeShift) | ModuleId,
|
||||
InvalidArgument = (22 << ErrorCodeShift) | ModuleId,
|
||||
NullInputBuffer = (30 << ErrorCodeShift) | ModuleId,
|
||||
InvalidBufferSize = (31 << ErrorCodeShift) | ModuleId,
|
||||
InvalidBuffer = (32 << ErrorCodeShift) | ModuleId,
|
||||
AsyncExecutionNotInitialized = (40 << ErrorCodeShift) | ModuleId,
|
||||
Unknown41 = (41 << ErrorCodeShift) | ModuleId,
|
||||
InternetRequestDenied = (59 << ErrorCodeShift) | ModuleId,
|
||||
UserNotFound = (100 << ErrorCodeShift) | ModuleId,
|
||||
NullObject = (302 << ErrorCodeShift) | ModuleId,
|
||||
Unknown341 = (341 << ErrorCodeShift) | ModuleId,
|
||||
InvalidIdTokenCacheBufferSize = (451 << ErrorCodeShift) | ModuleId
|
||||
NullArgument = (20 << ErrorCodeShift) | ModuleId,
|
||||
InvalidArgument = (22 << ErrorCodeShift) | ModuleId,
|
||||
NullInputBuffer = (30 << ErrorCodeShift) | ModuleId,
|
||||
InvalidBufferSize = (31 << ErrorCodeShift) | ModuleId,
|
||||
InvalidBuffer = (32 << ErrorCodeShift) | ModuleId,
|
||||
AsyncExecutionNotInitialized = (40 << ErrorCodeShift) | ModuleId,
|
||||
Unknown41 = (41 << ErrorCodeShift) | ModuleId,
|
||||
InternetRequestDenied = (59 << ErrorCodeShift) | ModuleId,
|
||||
UserNotFound = (100 << ErrorCodeShift) | ModuleId,
|
||||
NullObject = (302 << ErrorCodeShift) | ModuleId,
|
||||
Unknown341 = (341 << ErrorCodeShift) | ModuleId,
|
||||
MissingNetworkServiceLicenseKind = (400 << ErrorCodeShift) | ModuleId,
|
||||
InvalidIdTokenCacheBufferSize = (451 << ErrorCodeShift) | ModuleId
|
||||
}
|
||||
}
|
||||
|
@@ -336,6 +336,12 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||
context.Memory.Write(outputBufferPosition + (ulong)(i * Unsafe.SizeOf<PollEventData>()), events[i].Data);
|
||||
}
|
||||
|
||||
// In case of non blocking call timeout should not be returned.
|
||||
if (timeout == 0 && errno == LinuxError.ETIMEDOUT)
|
||||
{
|
||||
errno = LinuxError.SUCCESS;
|
||||
}
|
||||
|
||||
return WriteBsdResult(context, updateCount, errno);
|
||||
}
|
||||
|
||||
@@ -567,14 +573,18 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||
|
||||
LinuxError errno = LinuxError.EBADF;
|
||||
ISocket socket = _context.RetrieveSocket(socketFd);
|
||||
|
||||
if (socket != null)
|
||||
{
|
||||
errno = LinuxError.SUCCESS;
|
||||
errno = LinuxError.ENOTCONN;
|
||||
|
||||
WriteSockAddr(context, bufferPosition, socket, true);
|
||||
WriteBsdResult(context, 0, errno);
|
||||
context.ResponseData.Write(Unsafe.SizeOf<BsdSockAddr>());
|
||||
if (socket.RemoteEndPoint != null)
|
||||
{
|
||||
errno = LinuxError.SUCCESS;
|
||||
|
||||
WriteSockAddr(context, bufferPosition, socket, true);
|
||||
WriteBsdResult(context, 0, errno);
|
||||
context.ResponseData.Write(Unsafe.SizeOf<BsdSockAddr>());
|
||||
}
|
||||
}
|
||||
|
||||
return WriteBsdResult(context, 0, errno);
|
||||
@@ -876,6 +886,91 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||
return WriteBsdResult(context, newSockFd, errno);
|
||||
}
|
||||
|
||||
|
||||
[CommandHipc(29)] // 7.0.0+
|
||||
// RecvMMsg(u32 fd, u32 vlen, u32 flags, u32 reserved, nn::socket::TimeVal timeout) -> (i32 ret, u32 bsd_errno, buffer<bytes, 6> message);
|
||||
public ResultCode RecvMMsg(ServiceCtx context)
|
||||
{
|
||||
int socketFd = context.RequestData.ReadInt32();
|
||||
int vlen = context.RequestData.ReadInt32();
|
||||
BsdSocketFlags socketFlags = (BsdSocketFlags)context.RequestData.ReadInt32();
|
||||
uint reserved = context.RequestData.ReadUInt32();
|
||||
TimeVal timeout = context.RequestData.ReadStruct<TimeVal>();
|
||||
|
||||
ulong receivePosition = context.Request.ReceiveBuff[0].Position;
|
||||
ulong receiveLength = context.Request.ReceiveBuff[0].Size;
|
||||
|
||||
WritableRegion receiveRegion = context.Memory.GetWritableRegion(receivePosition, (int)receiveLength);
|
||||
|
||||
LinuxError errno = LinuxError.EBADF;
|
||||
ISocket socket = _context.RetrieveSocket(socketFd);
|
||||
int result = -1;
|
||||
|
||||
if (socket != null)
|
||||
{
|
||||
errno = BsdMMsgHdr.Deserialize(out BsdMMsgHdr message, receiveRegion.Memory.Span, vlen);
|
||||
|
||||
if (errno == LinuxError.SUCCESS)
|
||||
{
|
||||
errno = socket.RecvMMsg(out result, message, socketFlags, timeout);
|
||||
|
||||
if (errno == LinuxError.SUCCESS)
|
||||
{
|
||||
errno = BsdMMsgHdr.Serialize(receiveRegion.Memory.Span, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (errno == LinuxError.SUCCESS)
|
||||
{
|
||||
SetResultErrno(socket, result);
|
||||
receiveRegion.Dispose();
|
||||
}
|
||||
|
||||
return WriteBsdResult(context, result, errno);
|
||||
}
|
||||
|
||||
[CommandHipc(30)] // 7.0.0+
|
||||
// SendMMsg(u32 fd, u32 vlen, u32 flags) -> (i32 ret, u32 bsd_errno, buffer<bytes, 6> message);
|
||||
public ResultCode SendMMsg(ServiceCtx context)
|
||||
{
|
||||
int socketFd = context.RequestData.ReadInt32();
|
||||
int vlen = context.RequestData.ReadInt32();
|
||||
BsdSocketFlags socketFlags = (BsdSocketFlags)context.RequestData.ReadInt32();
|
||||
|
||||
ulong receivePosition = context.Request.ReceiveBuff[0].Position;
|
||||
ulong receiveLength = context.Request.ReceiveBuff[0].Size;
|
||||
|
||||
WritableRegion receiveRegion = context.Memory.GetWritableRegion(receivePosition, (int)receiveLength);
|
||||
|
||||
LinuxError errno = LinuxError.EBADF;
|
||||
ISocket socket = _context.RetrieveSocket(socketFd);
|
||||
int result = -1;
|
||||
|
||||
if (socket != null)
|
||||
{
|
||||
errno = BsdMMsgHdr.Deserialize(out BsdMMsgHdr message, receiveRegion.Memory.Span, vlen);
|
||||
|
||||
if (errno == LinuxError.SUCCESS)
|
||||
{
|
||||
errno = socket.SendMMsg(out result, message, socketFlags);
|
||||
|
||||
if (errno == LinuxError.SUCCESS)
|
||||
{
|
||||
errno = BsdMMsgHdr.Serialize(receiveRegion.Memory.Span, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (errno == LinuxError.SUCCESS)
|
||||
{
|
||||
SetResultErrno(socket, result);
|
||||
receiveRegion.Dispose();
|
||||
}
|
||||
|
||||
return WriteBsdResult(context, result, errno);
|
||||
}
|
||||
|
||||
[CommandHipc(31)] // 7.0.0+
|
||||
// EventFd(u64 initval, nn::socket::EventFdFlags flags) -> (i32 ret, u32 bsd_errno)
|
||||
public ResultCode EventFd(ServiceCtx context)
|
||||
@@ -897,4 +992,4 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||
return WriteBsdResult(context, newSockFd, errno);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -25,7 +25,12 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||
|
||||
LinuxError SendTo(out int sendSize, ReadOnlySpan<byte> buffer, int size, BsdSocketFlags flags, IPEndPoint remoteEndPoint);
|
||||
|
||||
LinuxError RecvMMsg(out int vlen, BsdMMsgHdr message, BsdSocketFlags flags, TimeVal timeout);
|
||||
|
||||
LinuxError SendMMsg(out int vlen, BsdMMsgHdr message, BsdSocketFlags flags);
|
||||
|
||||
LinuxError GetSocketOption(BsdSocketOption option, SocketOptionLevel level, Span<byte> optionValue);
|
||||
|
||||
LinuxError SetSocketOption(BsdSocketOption option, SocketOptionLevel level, ReadOnlySpan<byte> optionValue);
|
||||
|
||||
bool Poll(int microSeconds, SelectMode mode);
|
||||
|
@@ -1,5 +1,7 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.InteropServices;
|
||||
@@ -323,9 +325,14 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||
|
||||
int value = optionValue.Length >= 4 ? MemoryMarshal.Read<int>(optionValue) : MemoryMarshal.Read<byte>(optionValue);
|
||||
|
||||
if (option == BsdSocketOption.SoLinger)
|
||||
if (level == SocketOptionLevel.Socket && option == BsdSocketOption.SoLinger)
|
||||
{
|
||||
int value2 = MemoryMarshal.Read<int>(optionValue[4..]);
|
||||
int value2 = 0;
|
||||
|
||||
if (optionValue.Length >= 8)
|
||||
{
|
||||
value2 = MemoryMarshal.Read<int>(optionValue[4..]);
|
||||
}
|
||||
|
||||
Socket.SetSocketOption(level, SocketOptionName.Linger, new LingerOption(value != 0, value2));
|
||||
}
|
||||
@@ -351,5 +358,165 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||
{
|
||||
return Send(out writeSize, buffer, BsdSocketFlags.None);
|
||||
}
|
||||
|
||||
private bool CanSupportMMsgHdr(BsdMMsgHdr message)
|
||||
{
|
||||
for (int i = 0; i < message.Messages.Length; i++)
|
||||
{
|
||||
if (message.Messages[i].Name != null ||
|
||||
message.Messages[i].Control != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static IList<ArraySegment<byte>> ConvertMessagesToBuffer(BsdMMsgHdr message)
|
||||
{
|
||||
int segmentCount = 0;
|
||||
int index = 0;
|
||||
|
||||
foreach (BsdMsgHdr msgHeader in message.Messages)
|
||||
{
|
||||
segmentCount += msgHeader.Iov.Length;
|
||||
}
|
||||
|
||||
ArraySegment<byte>[] buffers = new ArraySegment<byte>[segmentCount];
|
||||
|
||||
foreach (BsdMsgHdr msgHeader in message.Messages)
|
||||
{
|
||||
foreach (byte[] iov in msgHeader.Iov)
|
||||
{
|
||||
buffers[index++] = new ArraySegment<byte>(iov);
|
||||
}
|
||||
|
||||
// Clear the length
|
||||
msgHeader.Length = 0;
|
||||
}
|
||||
|
||||
return buffers;
|
||||
}
|
||||
|
||||
private static void UpdateMessages(out int vlen, BsdMMsgHdr message, int transferedSize)
|
||||
{
|
||||
int bytesLeft = transferedSize;
|
||||
int index = 0;
|
||||
|
||||
while (bytesLeft > 0)
|
||||
{
|
||||
// First ensure we haven't finished all buffers
|
||||
if (index >= message.Messages.Length)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
BsdMsgHdr msgHeader = message.Messages[index];
|
||||
|
||||
int possiblyTransferedBytes = 0;
|
||||
|
||||
foreach (byte[] iov in msgHeader.Iov)
|
||||
{
|
||||
possiblyTransferedBytes += iov.Length;
|
||||
}
|
||||
|
||||
int storedBytes;
|
||||
|
||||
if (bytesLeft > possiblyTransferedBytes)
|
||||
{
|
||||
storedBytes = possiblyTransferedBytes;
|
||||
index++;
|
||||
}
|
||||
else
|
||||
{
|
||||
storedBytes = bytesLeft;
|
||||
}
|
||||
|
||||
msgHeader.Length = (uint)storedBytes;
|
||||
bytesLeft -= storedBytes;
|
||||
}
|
||||
|
||||
Debug.Assert(bytesLeft == 0);
|
||||
|
||||
vlen = index + 1;
|
||||
}
|
||||
|
||||
// TODO: Find a way to support passing the timeout somehow without changing the socket ReceiveTimeout.
|
||||
public LinuxError RecvMMsg(out int vlen, BsdMMsgHdr message, BsdSocketFlags flags, TimeVal timeout)
|
||||
{
|
||||
vlen = 0;
|
||||
|
||||
if (message.Messages.Length == 0)
|
||||
{
|
||||
return LinuxError.SUCCESS;
|
||||
}
|
||||
|
||||
if (!CanSupportMMsgHdr(message))
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.ServiceBsd, $"Unsupported BsdMMsgHdr");
|
||||
|
||||
return LinuxError.EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (message.Messages.Length == 0)
|
||||
{
|
||||
return LinuxError.SUCCESS;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
int receiveSize = Socket.Receive(ConvertMessagesToBuffer(message), ConvertBsdSocketFlags(flags), out SocketError socketError);
|
||||
|
||||
if (receiveSize > 0)
|
||||
{
|
||||
UpdateMessages(out vlen, message, receiveSize);
|
||||
}
|
||||
|
||||
return WinSockHelper.ConvertError((WsaError)socketError);
|
||||
}
|
||||
catch (SocketException exception)
|
||||
{
|
||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||
}
|
||||
}
|
||||
|
||||
public LinuxError SendMMsg(out int vlen, BsdMMsgHdr message, BsdSocketFlags flags)
|
||||
{
|
||||
vlen = 0;
|
||||
|
||||
if (message.Messages.Length == 0)
|
||||
{
|
||||
return LinuxError.SUCCESS;
|
||||
}
|
||||
|
||||
if (!CanSupportMMsgHdr(message))
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.ServiceBsd, $"Unsupported BsdMMsgHdr");
|
||||
|
||||
return LinuxError.EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (message.Messages.Length == 0)
|
||||
{
|
||||
return LinuxError.SUCCESS;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
int sendSize = Socket.Send(ConvertMessagesToBuffer(message), ConvertBsdSocketFlags(flags), out SocketError socketError);
|
||||
|
||||
if (sendSize > 0)
|
||||
{
|
||||
UpdateMessages(out vlen, message, sendSize);
|
||||
}
|
||||
|
||||
return WinSockHelper.ConvertError((WsaError)socketError);
|
||||
}
|
||||
catch (SocketException exception)
|
||||
{
|
||||
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -38,12 +38,13 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||
{
|
||||
ManagedSocket socket = (ManagedSocket)evnt.FileDescriptor;
|
||||
|
||||
bool isValidEvent = false;
|
||||
bool isValidEvent = evnt.Data.InputEvents == 0;
|
||||
|
||||
errorEvents.Add(socket.Socket);
|
||||
|
||||
if ((evnt.Data.InputEvents & PollEventTypeMask.Input) != 0)
|
||||
{
|
||||
readEvents.Add(socket.Socket);
|
||||
errorEvents.Add(socket.Socket);
|
||||
|
||||
isValidEvent = true;
|
||||
}
|
||||
@@ -51,7 +52,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||
if ((evnt.Data.InputEvents & PollEventTypeMask.UrgentInput) != 0)
|
||||
{
|
||||
readEvents.Add(socket.Socket);
|
||||
errorEvents.Add(socket.Socket);
|
||||
|
||||
isValidEvent = true;
|
||||
}
|
||||
@@ -59,14 +59,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||
if ((evnt.Data.InputEvents & PollEventTypeMask.Output) != 0)
|
||||
{
|
||||
writeEvents.Add(socket.Socket);
|
||||
errorEvents.Add(socket.Socket);
|
||||
|
||||
isValidEvent = true;
|
||||
}
|
||||
|
||||
if ((evnt.Data.InputEvents & PollEventTypeMask.Error) != 0)
|
||||
{
|
||||
errorEvents.Add(socket.Socket);
|
||||
|
||||
isValidEvent = true;
|
||||
}
|
||||
@@ -93,7 +85,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||
{
|
||||
Socket socket = ((ManagedSocket)evnt.FileDescriptor).Socket;
|
||||
|
||||
PollEventTypeMask outputEvents = 0;
|
||||
PollEventTypeMask outputEvents = evnt.Data.OutputEvents & ~evnt.Data.InputEvents;
|
||||
|
||||
if (errorEvents.Contains(socket))
|
||||
{
|
||||
|
56
Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdMMsgHdr.cs
Normal file
56
Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdMMsgHdr.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||
{
|
||||
class BsdMMsgHdr
|
||||
{
|
||||
public BsdMsgHdr[] Messages { get; }
|
||||
|
||||
private BsdMMsgHdr(BsdMsgHdr[] messages)
|
||||
{
|
||||
Messages = messages;
|
||||
}
|
||||
|
||||
public static LinuxError Serialize(Span<byte> rawData, BsdMMsgHdr message)
|
||||
{
|
||||
rawData[0] = 0x8;
|
||||
rawData = rawData[1..];
|
||||
|
||||
for (int index = 0; index < message.Messages.Length; index++)
|
||||
{
|
||||
LinuxError res = BsdMsgHdr.Serialize(ref rawData, message.Messages[index]);
|
||||
|
||||
if (res != LinuxError.SUCCESS)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
return LinuxError.SUCCESS;
|
||||
}
|
||||
|
||||
public static LinuxError Deserialize(out BsdMMsgHdr message, ReadOnlySpan<byte> rawData, int vlen)
|
||||
{
|
||||
message = null;
|
||||
|
||||
BsdMsgHdr[] messages = new BsdMsgHdr[vlen];
|
||||
|
||||
// Skip "header" byte (Nintendo also ignore it)
|
||||
rawData = rawData[1..];
|
||||
|
||||
for (int index = 0; index < messages.Length; index++)
|
||||
{
|
||||
LinuxError res = BsdMsgHdr.Deserialize(out messages[index], ref rawData);
|
||||
|
||||
if (res != LinuxError.SUCCESS)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
message = new BsdMMsgHdr(messages);
|
||||
|
||||
return LinuxError.SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
212
Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdMsgHdr.cs
Normal file
212
Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdMsgHdr.cs
Normal file
@@ -0,0 +1,212 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||
{
|
||||
class BsdMsgHdr
|
||||
{
|
||||
public byte[] Name { get; }
|
||||
public byte[][] Iov { get; }
|
||||
public byte[] Control { get; }
|
||||
public BsdSocketFlags Flags { get; }
|
||||
public uint Length;
|
||||
|
||||
private BsdMsgHdr(byte[] name, byte[][] iov, byte[] control, BsdSocketFlags flags, uint length)
|
||||
{
|
||||
Name = name;
|
||||
Iov = iov;
|
||||
Control = control;
|
||||
Flags = flags;
|
||||
Length = length;
|
||||
}
|
||||
|
||||
public static LinuxError Serialize(ref Span<byte> rawData, BsdMsgHdr message)
|
||||
{
|
||||
int msgNameLength = message.Name == null ? 0 : message.Name.Length;
|
||||
int iovCount = message.Iov == null ? 0 : message.Iov.Length;
|
||||
int controlLength = message.Control == null ? 0 : message.Control.Length;
|
||||
BsdSocketFlags flags = message.Flags;
|
||||
|
||||
if (!MemoryMarshal.TryWrite(rawData, ref msgNameLength))
|
||||
{
|
||||
return LinuxError.EFAULT;
|
||||
}
|
||||
|
||||
rawData = rawData[sizeof(uint)..];
|
||||
|
||||
if (msgNameLength > 0)
|
||||
{
|
||||
if (rawData.Length < msgNameLength)
|
||||
{
|
||||
return LinuxError.EFAULT;
|
||||
}
|
||||
|
||||
message.Name.CopyTo(rawData);
|
||||
rawData = rawData[msgNameLength..];
|
||||
}
|
||||
|
||||
if (!MemoryMarshal.TryWrite(rawData, ref iovCount))
|
||||
{
|
||||
return LinuxError.EFAULT;
|
||||
}
|
||||
|
||||
rawData = rawData[sizeof(uint)..];
|
||||
|
||||
if (iovCount > 0)
|
||||
{
|
||||
for (int index = 0; index < iovCount; index++)
|
||||
{
|
||||
ulong iovLength = (ulong)message.Iov[index].Length;
|
||||
|
||||
if (!MemoryMarshal.TryWrite(rawData, ref iovLength))
|
||||
{
|
||||
return LinuxError.EFAULT;
|
||||
}
|
||||
|
||||
rawData = rawData[sizeof(ulong)..];
|
||||
|
||||
if (iovLength > 0)
|
||||
{
|
||||
if ((ulong)rawData.Length < iovLength)
|
||||
{
|
||||
return LinuxError.EFAULT;
|
||||
}
|
||||
|
||||
message.Iov[index].CopyTo(rawData);
|
||||
rawData = rawData[(int)iovLength..];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!MemoryMarshal.TryWrite(rawData, ref controlLength))
|
||||
{
|
||||
return LinuxError.EFAULT;
|
||||
}
|
||||
|
||||
rawData = rawData[sizeof(uint)..];
|
||||
|
||||
if (controlLength > 0)
|
||||
{
|
||||
if (rawData.Length < controlLength)
|
||||
{
|
||||
return LinuxError.EFAULT;
|
||||
}
|
||||
|
||||
message.Control.CopyTo(rawData);
|
||||
rawData = rawData[controlLength..];
|
||||
}
|
||||
|
||||
if (!MemoryMarshal.TryWrite(rawData, ref flags))
|
||||
{
|
||||
return LinuxError.EFAULT;
|
||||
}
|
||||
|
||||
rawData = rawData[sizeof(BsdSocketFlags)..];
|
||||
|
||||
if (!MemoryMarshal.TryWrite(rawData, ref message.Length))
|
||||
{
|
||||
return LinuxError.EFAULT;
|
||||
}
|
||||
|
||||
rawData = rawData[sizeof(uint)..];
|
||||
|
||||
return LinuxError.SUCCESS;
|
||||
}
|
||||
|
||||
public static LinuxError Deserialize(out BsdMsgHdr message, ref ReadOnlySpan<byte> rawData)
|
||||
{
|
||||
byte[] name = null;
|
||||
byte[][] iov = null;
|
||||
byte[] control = null;
|
||||
|
||||
message = null;
|
||||
|
||||
if (!MemoryMarshal.TryRead(rawData, out uint msgNameLength))
|
||||
{
|
||||
return LinuxError.EFAULT;
|
||||
}
|
||||
|
||||
rawData = rawData[sizeof(uint)..];
|
||||
|
||||
if (msgNameLength > 0)
|
||||
{
|
||||
if (rawData.Length < msgNameLength)
|
||||
{
|
||||
return LinuxError.EFAULT;
|
||||
}
|
||||
|
||||
name = rawData[..(int)msgNameLength].ToArray();
|
||||
rawData = rawData[(int)msgNameLength..];
|
||||
}
|
||||
|
||||
if (!MemoryMarshal.TryRead(rawData, out uint iovCount))
|
||||
{
|
||||
return LinuxError.EFAULT;
|
||||
}
|
||||
|
||||
rawData = rawData[sizeof(uint)..];
|
||||
|
||||
if (iovCount > 0)
|
||||
{
|
||||
iov = new byte[iovCount][];
|
||||
|
||||
for (int index = 0; index < iov.Length; index++)
|
||||
{
|
||||
if (!MemoryMarshal.TryRead(rawData, out ulong iovLength))
|
||||
{
|
||||
return LinuxError.EFAULT;
|
||||
}
|
||||
|
||||
rawData = rawData[sizeof(ulong)..];
|
||||
|
||||
if (iovLength > 0)
|
||||
{
|
||||
if ((ulong)rawData.Length < iovLength)
|
||||
{
|
||||
return LinuxError.EFAULT;
|
||||
}
|
||||
|
||||
iov[index] = rawData[..(int)iovLength].ToArray();
|
||||
rawData = rawData[(int)iovLength..];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!MemoryMarshal.TryRead(rawData, out uint controlLength))
|
||||
{
|
||||
return LinuxError.EFAULT;
|
||||
}
|
||||
|
||||
rawData = rawData[sizeof(uint)..];
|
||||
|
||||
if (controlLength > 0)
|
||||
{
|
||||
if (rawData.Length < controlLength)
|
||||
{
|
||||
return LinuxError.EFAULT;
|
||||
}
|
||||
|
||||
control = rawData[..(int)controlLength].ToArray();
|
||||
rawData = rawData[(int)controlLength..];
|
||||
}
|
||||
|
||||
if (!MemoryMarshal.TryRead(rawData, out BsdSocketFlags flags))
|
||||
{
|
||||
return LinuxError.EFAULT;
|
||||
}
|
||||
|
||||
rawData = rawData[sizeof(BsdSocketFlags)..];
|
||||
|
||||
if (!MemoryMarshal.TryRead(rawData, out uint length))
|
||||
{
|
||||
return LinuxError.EFAULT;
|
||||
}
|
||||
|
||||
rawData = rawData[sizeof(uint)..];
|
||||
|
||||
message = new BsdMsgHdr(name, iov, control, flags, length);
|
||||
|
||||
return LinuxError.SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
8
Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/TimeVal.cs
Normal file
8
Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/TimeVal.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||
{
|
||||
public struct TimeVal
|
||||
{
|
||||
public ulong TvSec;
|
||||
public ulong TvUsec;
|
||||
}
|
||||
}
|
@@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres.Types
|
||||
{
|
||||
Length = (byte)Unsafe.SizeOf<Array4<byte>>();
|
||||
Family = (byte)AddressFamily.InterNetwork;
|
||||
Port = port;
|
||||
Port = IPAddress.HostToNetworkOrder(port);
|
||||
Address = new Array4<byte>();
|
||||
|
||||
address.TryWriteBytes(Address.AsSpan(), out _);
|
||||
|
@@ -35,6 +35,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||
private long _1msTicks;
|
||||
|
||||
private int _swapInterval;
|
||||
private int _swapIntervalDelay;
|
||||
|
||||
private readonly object Lock = new object();
|
||||
|
||||
@@ -91,7 +92,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||
}
|
||||
else
|
||||
{
|
||||
_ticksPerFrame = Stopwatch.Frequency / (TargetFps / _swapInterval);
|
||||
_ticksPerFrame = Stopwatch.Frequency / TargetFps;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,7 +323,13 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||
|
||||
if (_ticks >= _ticksPerFrame)
|
||||
{
|
||||
Compose();
|
||||
if (_swapIntervalDelay-- == 0)
|
||||
{
|
||||
Compose();
|
||||
|
||||
// When a frame is presented, delay the next one by its swap interval value.
|
||||
_swapIntervalDelay = Math.Max(0, _swapInterval - 1);
|
||||
}
|
||||
|
||||
_device.System?.SignalVsync();
|
||||
|
||||
|
Reference in New Issue
Block a user