mirror of
https://git.naxdy.org/Mirror/Ryujinx.git
synced 2025-03-14 22:20:18 +00:00
Merge branch 'Ryujinx:master' into master
This commit is contained in:
commit
e2553e6e40
27 changed files with 836 additions and 265 deletions
|
@ -6,6 +6,7 @@ namespace Ryujinx.Graphics.GAL
|
|||
public bool HasVectorIndexingBug { get; }
|
||||
|
||||
public bool SupportsAstcCompression { get; }
|
||||
public bool SupportsR4G4Format { get; }
|
||||
public bool SupportsFragmentShaderInterlock { get; }
|
||||
public bool SupportsFragmentShaderOrderingIntel { get; }
|
||||
public bool SupportsImageLoadFormatted { get; }
|
||||
|
@ -24,6 +25,7 @@ namespace Ryujinx.Graphics.GAL
|
|||
bool hasFrontFacingBug,
|
||||
bool hasVectorIndexingBug,
|
||||
bool supportsAstcCompression,
|
||||
bool supportsR4G4Format,
|
||||
bool supportsFragmentShaderInterlock,
|
||||
bool supportsFragmentShaderOrderingIntel,
|
||||
bool supportsImageLoadFormatted,
|
||||
|
@ -40,6 +42,7 @@ namespace Ryujinx.Graphics.GAL
|
|||
HasFrontFacingBug = hasFrontFacingBug;
|
||||
HasVectorIndexingBug = hasVectorIndexingBug;
|
||||
SupportsAstcCompression = supportsAstcCompression;
|
||||
SupportsR4G4Format = supportsR4G4Format;
|
||||
SupportsFragmentShaderInterlock = supportsFragmentShaderInterlock;
|
||||
SupportsFragmentShaderOrderingIntel = supportsFragmentShaderOrderingIntel;
|
||||
SupportsImageLoadFormatted = supportsImageLoadFormatted;
|
||||
|
|
|
@ -58,6 +58,7 @@ namespace Ryujinx.Graphics.GAL
|
|||
D32FloatS8Uint,
|
||||
R8G8B8X8Srgb,
|
||||
R8G8B8A8Srgb,
|
||||
R4G4Unorm,
|
||||
R4G4B4A4Unorm,
|
||||
R5G5B5X1Unorm,
|
||||
R5G5B5A1Unorm,
|
||||
|
|
|
@ -58,6 +58,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
{ 0x25385, new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2) },
|
||||
{ 0x253b0, new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2) },
|
||||
{ 0xa4908, new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4, 4) },
|
||||
{ 0x2491e, new FormatInfo(Format.R4G4Unorm, 1, 1, 1, 2) },
|
||||
{ 0x24912, new FormatInfo(Format.R4G4B4A4Unorm, 1, 1, 2, 4) },
|
||||
{ 0x24914, new FormatInfo(Format.R5G5B5A1Unorm, 1, 1, 2, 4) },
|
||||
{ 0x24915, new FormatInfo(Format.R5G6B5Unorm, 1, 1, 2, 3) },
|
||||
|
|
|
@ -785,7 +785,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
// Handle compressed cases not supported by the host:
|
||||
// - ASTC is usually not supported on desktop cards.
|
||||
// - BC4/BC5 is not supported on 3D textures.
|
||||
if (!_context.Capabilities.SupportsAstcCompression && Info.FormatInfo.Format.IsAstc())
|
||||
if (!_context.Capabilities.SupportsAstcCompression && Format.IsAstc())
|
||||
{
|
||||
if (!AstcDecoder.TryDecodeToRgba8P(
|
||||
data.ToArray(),
|
||||
|
@ -805,11 +805,15 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
|
||||
data = decoded;
|
||||
}
|
||||
else if (Target == Target.Texture3D && Info.FormatInfo.Format.IsBc4())
|
||||
else if (!_context.Capabilities.SupportsR4G4Format && Format == Format.R4G4Unorm)
|
||||
{
|
||||
data = PixelConverter.ConvertR4G4ToR4G4B4A4(data);
|
||||
}
|
||||
else if (Target == Target.Texture3D && Format.IsBc4())
|
||||
{
|
||||
data = BCnDecoder.DecodeBC4(data, width, height, depth, levels, layers, Info.FormatInfo.Format == Format.Bc4Snorm);
|
||||
}
|
||||
else if (Target == Target.Texture3D && Info.FormatInfo.Format.IsBc5())
|
||||
else if (Target == Target.Texture3D && Format.IsBc5())
|
||||
{
|
||||
data = BCnDecoder.DecodeBC5(data, width, height, depth, levels, layers, Info.FormatInfo.Format == Format.Bc5Snorm);
|
||||
}
|
||||
|
|
|
@ -66,6 +66,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
}
|
||||
}
|
||||
|
||||
if (!caps.SupportsR4G4Format && info.FormatInfo.Format == Format.R4G4Unorm)
|
||||
{
|
||||
return new FormatInfo(Format.R4G4B4A4Unorm, 1, 1, 2, 4);
|
||||
}
|
||||
|
||||
if (info.Target == Target.Texture3D)
|
||||
{
|
||||
// The host API does not support 3D BC4/BC5 compressed formats.
|
||||
|
|
|
@ -171,7 +171,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||
Add(Format.B5G6R5Unorm, new FormatInfo(3, true, false, All.Rgb565, PixelFormat.Rgb, PixelType.UnsignedShort565Reversed));
|
||||
Add(Format.B5G5R5X1Unorm, new FormatInfo(4, true, false, All.Rgb5, PixelFormat.Rgba, PixelType.UnsignedShort1555Reversed));
|
||||
Add(Format.B5G5R5A1Unorm, new FormatInfo(4, true, false, All.Rgb5A1, PixelFormat.Rgba, PixelType.UnsignedShort1555Reversed));
|
||||
Add(Format.A1B5G5R5Unorm, new FormatInfo(4, true, false, All.Rgb5A1, PixelFormat.Bgra, PixelType.UnsignedShort1555Reversed));
|
||||
Add(Format.A1B5G5R5Unorm, new FormatInfo(4, true, false, All.Rgb5A1, PixelFormat.Rgba, PixelType.UnsignedShort5551));
|
||||
Add(Format.B8G8R8X8Unorm, new FormatInfo(4, true, false, All.Rgba8, PixelFormat.Rgba, PixelType.UnsignedByte));
|
||||
Add(Format.B8G8R8A8Unorm, new FormatInfo(4, true, false, All.Rgba8, PixelFormat.Rgba, PixelType.UnsignedByte));
|
||||
Add(Format.B8G8R8X8Srgb, new FormatInfo(4, false, false, All.Srgb8, PixelFormat.Rgba, PixelType.UnsignedByte));
|
||||
|
|
|
@ -72,7 +72,16 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||
(int)Info.SwizzleA.Convert()
|
||||
};
|
||||
|
||||
if (Info.Format.IsBgr())
|
||||
if (Info.Format == Format.A1B5G5R5Unorm)
|
||||
{
|
||||
int temp = swizzleRgba[0];
|
||||
int temp2 = swizzleRgba[1];
|
||||
swizzleRgba[0] = swizzleRgba[3];
|
||||
swizzleRgba[1] = swizzleRgba[2];
|
||||
swizzleRgba[2] = temp2;
|
||||
swizzleRgba[3] = temp;
|
||||
}
|
||||
else if (Info.Format.IsBgr())
|
||||
{
|
||||
// Swap B <-> R for BGRA formats, as OpenGL has no support for them
|
||||
// and we need to manually swap the components on read/write on the GPU.
|
||||
|
|
|
@ -101,21 +101,22 @@ namespace Ryujinx.Graphics.OpenGL
|
|||
public Capabilities GetCapabilities()
|
||||
{
|
||||
return new Capabilities(
|
||||
HwCapabilities.Vendor == HwCapabilities.GpuVendor.IntelWindows,
|
||||
HwCapabilities.Vendor == HwCapabilities.GpuVendor.AmdWindows,
|
||||
HwCapabilities.SupportsAstcCompression,
|
||||
HwCapabilities.SupportsFragmentShaderInterlock,
|
||||
HwCapabilities.SupportsFragmentShaderOrdering,
|
||||
HwCapabilities.SupportsImageLoadFormatted,
|
||||
HwCapabilities.SupportsMismatchingViewFormat,
|
||||
HwCapabilities.SupportsNonConstantTextureOffset,
|
||||
HwCapabilities.SupportsShaderBallot,
|
||||
HwCapabilities.SupportsTextureShadowLod,
|
||||
HwCapabilities.SupportsViewportSwizzle,
|
||||
HwCapabilities.SupportsIndirectParameters,
|
||||
HwCapabilities.MaximumComputeSharedMemorySize,
|
||||
HwCapabilities.MaximumSupportedAnisotropy,
|
||||
HwCapabilities.StorageBufferOffsetAlignment);
|
||||
hasFrontFacingBug: HwCapabilities.Vendor == HwCapabilities.GpuVendor.IntelWindows,
|
||||
hasVectorIndexingBug: HwCapabilities.Vendor == HwCapabilities.GpuVendor.AmdWindows,
|
||||
supportsAstcCompression: HwCapabilities.SupportsAstcCompression,
|
||||
supportsR4G4Format: false,
|
||||
supportsFragmentShaderInterlock: HwCapabilities.SupportsFragmentShaderInterlock,
|
||||
supportsFragmentShaderOrderingIntel: HwCapabilities.SupportsFragmentShaderOrdering,
|
||||
supportsImageLoadFormatted: HwCapabilities.SupportsImageLoadFormatted,
|
||||
supportsMismatchingViewFormat: HwCapabilities.SupportsMismatchingViewFormat,
|
||||
supportsNonConstantTextureOffset: HwCapabilities.SupportsNonConstantTextureOffset,
|
||||
supportsShaderBallot: HwCapabilities.SupportsShaderBallot,
|
||||
supportsTextureShadowLod: HwCapabilities.SupportsTextureShadowLod,
|
||||
supportsViewportSwizzle: HwCapabilities.SupportsViewportSwizzle,
|
||||
supportsIndirectParameters: HwCapabilities.SupportsIndirectParameters,
|
||||
maximumComputeSharedMemorySize: HwCapabilities.MaximumComputeSharedMemorySize,
|
||||
maximumSupportedAnisotropy: HwCapabilities.MaximumSupportedAnisotropy,
|
||||
storageBufferOffsetAlignment: HwCapabilities.StorageBufferOffsetAlignment);
|
||||
}
|
||||
|
||||
public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan<byte> data)
|
||||
|
|
39
Ryujinx.Graphics.Texture/PixelConverter.cs
Normal file
39
Ryujinx.Graphics.Texture/PixelConverter.cs
Normal file
|
@ -0,0 +1,39 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
|
||||
namespace Ryujinx.Graphics.Texture
|
||||
{
|
||||
public static class PixelConverter
|
||||
{
|
||||
public unsafe static byte[] ConvertR4G4ToR4G4B4A4(ReadOnlySpan<byte> data)
|
||||
{
|
||||
byte[] output = new byte[data.Length * 2];
|
||||
int start = 0;
|
||||
|
||||
if (Sse41.IsSupported)
|
||||
{
|
||||
int sizeTrunc = data.Length & ~7;
|
||||
start = sizeTrunc;
|
||||
|
||||
fixed (byte* inputPtr = data, outputPtr = output)
|
||||
{
|
||||
for (ulong offset = 0; offset < (ulong)sizeTrunc; offset += 8)
|
||||
{
|
||||
Sse2.Store(outputPtr + offset * 2, Sse41.ConvertToVector128Int16(inputPtr + offset).AsByte());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Span<ushort> outputSpan = MemoryMarshal.Cast<byte, ushort>(output);
|
||||
|
||||
for (int i = start; i < data.Length; i++)
|
||||
{
|
||||
outputSpan[i] = (ushort)data[i];
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -46,6 +46,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||
public KAddressArbiter AddressArbiter { get; private set; }
|
||||
|
||||
public long[] RandomEntropy { get; private set; }
|
||||
public KThread[] PinnedThreads { get; private set; }
|
||||
|
||||
private bool _signaled;
|
||||
|
||||
|
@ -102,6 +103,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||
Capabilities = new KProcessCapabilities();
|
||||
|
||||
RandomEntropy = new long[KScheduler.CpuCoresCount];
|
||||
PinnedThreads = new KThread[KScheduler.CpuCoresCount];
|
||||
|
||||
// TODO: Remove once we no longer need to initialize it externally.
|
||||
HandleTable = new KHandleTable(context);
|
||||
|
@ -749,7 +751,24 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||
{
|
||||
KThread currentThread = KernelStatic.GetCurrentThread();
|
||||
|
||||
if (currentThread.IsSchedulable)
|
||||
if (currentThread.Owner != null &&
|
||||
currentThread.GetUserDisableCount() != 0 &&
|
||||
currentThread.Owner.PinnedThreads[currentThread.CurrentCore] == null)
|
||||
{
|
||||
KernelContext.CriticalSection.Enter();
|
||||
|
||||
currentThread.Owner.PinThread(currentThread);
|
||||
|
||||
currentThread.SetUserInterruptFlag();
|
||||
|
||||
if (currentThread.IsSchedulable)
|
||||
{
|
||||
KernelContext.Schedulers[currentThread.CurrentCore].Schedule();
|
||||
}
|
||||
|
||||
KernelContext.CriticalSection.Leave();
|
||||
}
|
||||
else if (currentThread.IsSchedulable)
|
||||
{
|
||||
KernelContext.Schedulers[currentThread.CurrentCore].Schedule();
|
||||
}
|
||||
|
@ -952,6 +971,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||
{
|
||||
KernelContext.CriticalSection.Enter();
|
||||
|
||||
if (currentThread != null && PinnedThreads[currentThread.CurrentCore] == currentThread)
|
||||
{
|
||||
UnpinThread(currentThread);
|
||||
}
|
||||
|
||||
foreach (KThread thread in _threads)
|
||||
{
|
||||
if ((thread.SchedFlags & ThreadSchedState.LowMask) != ThreadSchedState.TerminationPending)
|
||||
|
@ -1139,5 +1163,35 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||
|
||||
return KernelResult.InvalidState;
|
||||
}
|
||||
|
||||
public void PinThread(KThread thread)
|
||||
{
|
||||
if (!thread.TerminationRequested)
|
||||
{
|
||||
PinnedThreads[thread.CurrentCore] = thread;
|
||||
|
||||
thread.Pin();
|
||||
|
||||
KernelContext.ThreadReselectionRequested = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void UnpinThread(KThread thread)
|
||||
{
|
||||
if (!thread.TerminationRequested)
|
||||
{
|
||||
thread.Unpin();
|
||||
|
||||
PinnedThreads[thread.CurrentCore] = null;
|
||||
|
||||
KernelContext.ThreadReselectionRequested = true;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsExceptionUserThread(KThread thread)
|
||||
{
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2655,6 +2655,13 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||
};
|
||||
}
|
||||
|
||||
public KernelResult SynchronizePreemptionState()
|
||||
{
|
||||
KernelStatic.GetCurrentThread().SynchronizePreemptionState();
|
||||
|
||||
return KernelResult.Success;
|
||||
}
|
||||
|
||||
private bool IsPointingInsideKernel(ulong address)
|
||||
{
|
||||
return (address + 0x1000000000) < 0xffffff000;
|
||||
|
|
|
@ -491,5 +491,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||
{
|
||||
return _syscall.SignalToAddress(address, type, value, count);
|
||||
}
|
||||
|
||||
public KernelResult SynchronizePreemptionState32()
|
||||
{
|
||||
return _syscall.SynchronizePreemptionState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -405,5 +405,10 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||
{
|
||||
return _syscall.SignalToAddress(address, type, value, count);
|
||||
}
|
||||
|
||||
public KernelResult SynchronizePreemptionState64()
|
||||
{
|
||||
return _syscall.SynchronizePreemptionState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,22 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||
|
||||
public void SvcCall(object sender, InstExceptionEventArgs e)
|
||||
{
|
||||
ExecutionContext context = (ExecutionContext)sender;
|
||||
KThread currentThread = KernelStatic.GetCurrentThread();
|
||||
|
||||
if (currentThread.Owner != null &&
|
||||
currentThread.GetUserDisableCount() != 0 &&
|
||||
currentThread.Owner.PinnedThreads[currentThread.CurrentCore] == null)
|
||||
{
|
||||
_context.CriticalSection.Enter();
|
||||
|
||||
currentThread.Owner.PinThread(currentThread);
|
||||
|
||||
currentThread.SetUserInterruptFlag();
|
||||
|
||||
_context.CriticalSection.Leave();
|
||||
}
|
||||
|
||||
ExecutionContext context = (ExecutionContext)sender;
|
||||
|
||||
if (context.IsAarch32)
|
||||
{
|
||||
|
@ -44,13 +59,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||
svcFunc(_syscall64, context);
|
||||
}
|
||||
|
||||
PostSvcHandler();
|
||||
}
|
||||
|
||||
private void PostSvcHandler()
|
||||
{
|
||||
KThread currentThread = KernelStatic.GetCurrentThread();
|
||||
|
||||
currentThread.HandlePostSyscall();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,6 +71,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||
{ 0x33, nameof(Syscall64.GetThreadContext364) },
|
||||
{ 0x34, nameof(Syscall64.WaitForAddress64) },
|
||||
{ 0x35, nameof(Syscall64.SignalToAddress64) },
|
||||
{ 0x36, nameof(Syscall64.SynchronizePreemptionState64) },
|
||||
{ 0x37, nameof(Syscall64.GetResourceLimitPeakValue64) },
|
||||
{ 0x40, nameof(Syscall64.CreateSession64) },
|
||||
{ 0x41, nameof(Syscall64.AcceptSession64) },
|
||||
|
@ -145,6 +146,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
|||
{ 0x33, nameof(Syscall32.GetThreadContext332) },
|
||||
{ 0x34, nameof(Syscall32.WaitForAddress32) },
|
||||
{ 0x35, nameof(Syscall32.SignalToAddress32) },
|
||||
{ 0x36, nameof(Syscall32.SynchronizePreemptionState32) },
|
||||
{ 0x37, nameof(Syscall32.GetResourceLimitPeakValue32) },
|
||||
{ 0x40, nameof(Syscall32.CreateSession32) },
|
||||
{ 0x41, nameof(Syscall32.AcceptSession32) },
|
||||
|
|
|
@ -36,6 +36,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
private readonly KThread _idleThread;
|
||||
|
||||
public KThread PreviousThread => _previousThread;
|
||||
public KThread CurrentThread => _currentThread;
|
||||
public long LastContextSwitchTime { get; private set; }
|
||||
public long TotalIdleTimeTicks => _idleThread.TotalTimeRunning;
|
||||
|
||||
|
@ -87,6 +88,26 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
{
|
||||
KThread thread = context.PriorityQueue.ScheduledThreads(core).FirstOrDefault();
|
||||
|
||||
if (thread != null &&
|
||||
thread.Owner != null &&
|
||||
thread.Owner.PinnedThreads[core] != null &&
|
||||
thread.Owner.PinnedThreads[core] != thread)
|
||||
{
|
||||
KThread candidate = thread.Owner.PinnedThreads[core];
|
||||
|
||||
if (candidate.KernelWaitersCount == 0 && !thread.Owner.IsExceptionUserThread(candidate))
|
||||
{
|
||||
if (candidate.SchedFlags == ThreadSchedState.Running)
|
||||
{
|
||||
thread = candidate;
|
||||
}
|
||||
else
|
||||
{
|
||||
thread = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scheduledCoresMask |= context.Schedulers[core].SelectThread(thread);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
{
|
||||
class KThread : KSynchronizationObject, IKFutureSchedulerObject
|
||||
{
|
||||
private const int TlsUserDisableCountOffset = 0x100;
|
||||
private const int TlsUserInterruptFlagOffset = 0x102;
|
||||
|
||||
public const int MaxWaitSyncObjects = 64;
|
||||
|
||||
private ManualResetEvent _schedulerWaitEvent;
|
||||
|
@ -43,6 +46,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
public bool IsSchedulable => _customThreadStart == null && !_forcedUnschedulable;
|
||||
|
||||
public ulong MutexAddress { get; set; }
|
||||
public int KernelWaitersCount { get; private set; }
|
||||
|
||||
public KProcess Owner { get; private set; }
|
||||
|
||||
|
@ -65,11 +69,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
private LinkedList<KThread> _mutexWaiters;
|
||||
private LinkedListNode<KThread> _mutexWaiterNode;
|
||||
|
||||
private LinkedList<KThread> _pinnedWaiters;
|
||||
|
||||
public KThread MutexOwner { get; private set; }
|
||||
|
||||
public int ThreadHandleForUserMutex { get; set; }
|
||||
|
||||
private ThreadSchedState _forcePauseFlags;
|
||||
private ThreadSchedState _forcePausePermissionFlags;
|
||||
|
||||
public KernelResult ObjSyncResult { get; set; }
|
||||
|
||||
|
@ -79,11 +86,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
public int CurrentCore { get; set; }
|
||||
public int ActiveCore { get; set; }
|
||||
|
||||
private long _affinityMaskOverride;
|
||||
private int _preferredCoreOverride;
|
||||
#pragma warning disable CS0649
|
||||
private int _affinityOverrideCount;
|
||||
#pragma warning restore CS0649
|
||||
public bool IsPinned { get; private set; }
|
||||
|
||||
private long _originalAffinityMask;
|
||||
private int _originalPreferredCore;
|
||||
private int _originalBasePriority;
|
||||
private int _coreMigrationDisableCount;
|
||||
|
||||
public ThreadSchedState SchedFlags { get; private set; }
|
||||
|
||||
|
@ -108,6 +116,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
|
||||
public long LastPc { get; set; }
|
||||
|
||||
private object ActivityOperationLock = new object();
|
||||
|
||||
public KThread(KernelContext context) : base(context)
|
||||
{
|
||||
WaitSyncObjects = new KSynchronizationObject[MaxWaitSyncObjects];
|
||||
|
@ -116,6 +126,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
SiblingsPerCore = new LinkedListNode<KThread>[KScheduler.CpuCoresCount];
|
||||
|
||||
_mutexWaiters = new LinkedList<KThread>();
|
||||
_pinnedWaiters = new LinkedList<KThread>();
|
||||
}
|
||||
|
||||
public KernelResult Initialize(
|
||||
|
@ -147,6 +158,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
DynamicPriority = priority;
|
||||
BasePriority = priority;
|
||||
CurrentCore = cpuCore;
|
||||
IsPinned = false;
|
||||
|
||||
_entrypoint = entrypoint;
|
||||
_customThreadStart = customThreadStart;
|
||||
|
@ -204,6 +216,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
|
||||
_hasBeenInitialized = true;
|
||||
|
||||
_forcePausePermissionFlags = ThreadSchedState.ForcePauseMask;
|
||||
|
||||
if (owner != null)
|
||||
{
|
||||
owner.SubscribeThreadEventHandlers(Context);
|
||||
|
@ -301,6 +315,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
{
|
||||
KernelContext.CriticalSection.Enter();
|
||||
|
||||
if (Owner != null && Owner.PinnedThreads[KernelStatic.GetCurrentThread().CurrentCore] == this)
|
||||
{
|
||||
Owner.UnpinThread(this);
|
||||
}
|
||||
|
||||
ThreadSchedState result;
|
||||
|
||||
if (Interlocked.CompareExchange(ref _shallBeTerminated, 1, 0) == 0)
|
||||
|
@ -405,6 +424,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
KernelContext.CriticalSection.Enter();
|
||||
|
||||
_forcePauseFlags &= ~ThreadSchedState.ForcePauseMask;
|
||||
_forcePausePermissionFlags = 0;
|
||||
|
||||
bool decRef = ExitImpl();
|
||||
|
||||
|
@ -433,6 +453,19 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
return decRef;
|
||||
}
|
||||
|
||||
private int GetEffectiveRunningCore()
|
||||
{
|
||||
for (int coreNumber = 0; coreNumber < KScheduler.CpuCoresCount; coreNumber++)
|
||||
{
|
||||
if (KernelContext.Schedulers[coreNumber].CurrentThread == this)
|
||||
{
|
||||
return coreNumber;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public KernelResult Sleep(long timeout)
|
||||
{
|
||||
KernelContext.CriticalSection.Enter();
|
||||
|
@ -465,7 +498,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
{
|
||||
KernelContext.CriticalSection.Enter();
|
||||
|
||||
BasePriority = priority;
|
||||
if (IsPinned)
|
||||
{
|
||||
_originalBasePriority = priority;
|
||||
}
|
||||
else
|
||||
{
|
||||
BasePriority = priority;
|
||||
}
|
||||
|
||||
UpdatePriorityInheritance();
|
||||
|
||||
|
@ -497,53 +537,96 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
|
||||
public KernelResult SetActivity(bool pause)
|
||||
{
|
||||
KernelResult result = KernelResult.Success;
|
||||
|
||||
KernelContext.CriticalSection.Enter();
|
||||
|
||||
ThreadSchedState lowNibble = SchedFlags & ThreadSchedState.LowMask;
|
||||
|
||||
if (lowNibble != ThreadSchedState.Paused && lowNibble != ThreadSchedState.Running)
|
||||
lock (ActivityOperationLock)
|
||||
{
|
||||
KernelResult result = KernelResult.Success;
|
||||
|
||||
KernelContext.CriticalSection.Enter();
|
||||
|
||||
ThreadSchedState lowNibble = SchedFlags & ThreadSchedState.LowMask;
|
||||
|
||||
if (lowNibble != ThreadSchedState.Paused && lowNibble != ThreadSchedState.Running)
|
||||
{
|
||||
KernelContext.CriticalSection.Leave();
|
||||
|
||||
return KernelResult.InvalidState;
|
||||
}
|
||||
|
||||
if (!ShallBeTerminated && SchedFlags != ThreadSchedState.TerminationPending)
|
||||
{
|
||||
if (pause)
|
||||
{
|
||||
// Pause, the force pause flag should be clear (thread is NOT paused).
|
||||
if ((_forcePauseFlags & ThreadSchedState.ThreadPauseFlag) == 0)
|
||||
{
|
||||
Suspend(ThreadSchedState.ThreadPauseFlag);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = KernelResult.InvalidState;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unpause, the force pause flag should be set (thread is paused).
|
||||
if ((_forcePauseFlags & ThreadSchedState.ThreadPauseFlag) != 0)
|
||||
{
|
||||
Resume(ThreadSchedState.ThreadPauseFlag);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = KernelResult.InvalidState;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KernelContext.CriticalSection.Leave();
|
||||
|
||||
return KernelResult.InvalidState;
|
||||
}
|
||||
|
||||
KernelContext.CriticalSection.Enter();
|
||||
|
||||
if (!ShallBeTerminated && SchedFlags != ThreadSchedState.TerminationPending)
|
||||
{
|
||||
if (pause)
|
||||
if (result == KernelResult.Success && pause)
|
||||
{
|
||||
// Pause, the force pause flag should be clear (thread is NOT paused).
|
||||
if ((_forcePauseFlags & ThreadSchedState.ThreadPauseFlag) == 0)
|
||||
bool isThreadRunning = true;
|
||||
|
||||
while (isThreadRunning)
|
||||
{
|
||||
Suspend(ThreadSchedState.ThreadPauseFlag);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = KernelResult.InvalidState;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unpause, the force pause flag should be set (thread is paused).
|
||||
if ((_forcePauseFlags & ThreadSchedState.ThreadPauseFlag) != 0)
|
||||
{
|
||||
Resume(ThreadSchedState.ThreadPauseFlag);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = KernelResult.InvalidState;
|
||||
KernelContext.CriticalSection.Enter();
|
||||
|
||||
if (TerminationRequested)
|
||||
{
|
||||
KernelContext.CriticalSection.Leave();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
isThreadRunning = false;
|
||||
|
||||
if (IsPinned)
|
||||
{
|
||||
KThread currentThread = KernelStatic.GetCurrentThread();
|
||||
|
||||
if (currentThread.TerminationRequested)
|
||||
{
|
||||
KernelContext.CriticalSection.Leave();
|
||||
|
||||
result = KernelResult.ThreadTerminating;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
_pinnedWaiters.AddLast(currentThread);
|
||||
|
||||
currentThread.Reschedule(ThreadSchedState.Paused);
|
||||
}
|
||||
else
|
||||
{
|
||||
isThreadRunning = GetEffectiveRunningCore() >= 0;
|
||||
}
|
||||
|
||||
KernelContext.CriticalSection.Leave();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
KernelContext.CriticalSection.Leave();
|
||||
KernelContext.CriticalSection.Leave();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void CancelSynchronization()
|
||||
|
@ -579,58 +662,105 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
|
||||
public KernelResult SetCoreAndAffinityMask(int newCore, long newAffinityMask)
|
||||
{
|
||||
KernelContext.CriticalSection.Enter();
|
||||
|
||||
bool useOverride = _affinityOverrideCount != 0;
|
||||
|
||||
// The value -3 is "do not change the preferred core".
|
||||
if (newCore == -3)
|
||||
lock (ActivityOperationLock)
|
||||
{
|
||||
newCore = useOverride ? _preferredCoreOverride : PreferredCore;
|
||||
KernelContext.CriticalSection.Enter();
|
||||
|
||||
if ((newAffinityMask & (1 << newCore)) == 0)
|
||||
bool isCoreMigrationDisabled = _coreMigrationDisableCount != 0;
|
||||
|
||||
// The value -3 is "do not change the preferred core".
|
||||
if (newCore == -3)
|
||||
{
|
||||
KernelContext.CriticalSection.Leave();
|
||||
newCore = isCoreMigrationDisabled ? _originalPreferredCore : PreferredCore;
|
||||
|
||||
return KernelResult.InvalidCombination;
|
||||
}
|
||||
}
|
||||
|
||||
if (useOverride)
|
||||
{
|
||||
_preferredCoreOverride = newCore;
|
||||
_affinityMaskOverride = newAffinityMask;
|
||||
}
|
||||
else
|
||||
{
|
||||
long oldAffinityMask = AffinityMask;
|
||||
|
||||
PreferredCore = newCore;
|
||||
AffinityMask = newAffinityMask;
|
||||
|
||||
if (oldAffinityMask != newAffinityMask)
|
||||
{
|
||||
int oldCore = ActiveCore;
|
||||
|
||||
if (oldCore >= 0 && ((AffinityMask >> oldCore) & 1) == 0)
|
||||
if ((newAffinityMask & (1 << newCore)) == 0)
|
||||
{
|
||||
if (PreferredCore < 0)
|
||||
KernelContext.CriticalSection.Leave();
|
||||
|
||||
return KernelResult.InvalidCombination;
|
||||
}
|
||||
}
|
||||
|
||||
if (isCoreMigrationDisabled)
|
||||
{
|
||||
_originalPreferredCore = newCore;
|
||||
_originalAffinityMask = newAffinityMask;
|
||||
}
|
||||
else
|
||||
{
|
||||
long oldAffinityMask = AffinityMask;
|
||||
|
||||
PreferredCore = newCore;
|
||||
AffinityMask = newAffinityMask;
|
||||
|
||||
if (oldAffinityMask != newAffinityMask)
|
||||
{
|
||||
int oldCore = ActiveCore;
|
||||
|
||||
if (oldCore >= 0 && ((AffinityMask >> oldCore) & 1) == 0)
|
||||
{
|
||||
ActiveCore = sizeof(ulong) * 8 - 1 - BitOperations.LeadingZeroCount((ulong)AffinityMask);
|
||||
if (PreferredCore < 0)
|
||||
{
|
||||
ActiveCore = sizeof(ulong) * 8 - 1 - BitOperations.LeadingZeroCount((ulong)AffinityMask);
|
||||
}
|
||||
else
|
||||
{
|
||||
ActiveCore = PreferredCore;
|
||||
}
|
||||
}
|
||||
|
||||
AdjustSchedulingForNewAffinity(oldAffinityMask, oldCore);
|
||||
}
|
||||
}
|
||||
|
||||
KernelContext.CriticalSection.Leave();
|
||||
|
||||
bool targetThreadPinned = true;
|
||||
|
||||
while (targetThreadPinned)
|
||||
{
|
||||
KernelContext.CriticalSection.Enter();
|
||||
|
||||
if (TerminationRequested)
|
||||
{
|
||||
KernelContext.CriticalSection.Leave();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
targetThreadPinned = false;
|
||||
|
||||
int coreNumber = GetEffectiveRunningCore();
|
||||
bool isPinnedThreadCurrentlyRunning = coreNumber >= 0;
|
||||
|
||||
if (isPinnedThreadCurrentlyRunning && ((1 << coreNumber) & AffinityMask) == 0)
|
||||
{
|
||||
if (IsPinned)
|
||||
{
|
||||
KThread currentThread = KernelStatic.GetCurrentThread();
|
||||
|
||||
if (currentThread.TerminationRequested)
|
||||
{
|
||||
KernelContext.CriticalSection.Leave();
|
||||
|
||||
return KernelResult.ThreadTerminating;
|
||||
}
|
||||
|
||||
_pinnedWaiters.AddLast(currentThread);
|
||||
|
||||
currentThread.Reschedule(ThreadSchedState.Paused);
|
||||
}
|
||||
else
|
||||
{
|
||||
ActiveCore = PreferredCore;
|
||||
targetThreadPinned = true;
|
||||
}
|
||||
}
|
||||
|
||||
AdjustSchedulingForNewAffinity(oldAffinityMask, oldCore);
|
||||
KernelContext.CriticalSection.Leave();
|
||||
}
|
||||
|
||||
return KernelResult.Success;
|
||||
}
|
||||
|
||||
KernelContext.CriticalSection.Leave();
|
||||
|
||||
return KernelResult.Success;
|
||||
}
|
||||
|
||||
private void CombineForcePauseFlags()
|
||||
|
@ -638,7 +768,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
ThreadSchedState oldFlags = SchedFlags;
|
||||
ThreadSchedState lowNibble = SchedFlags & ThreadSchedState.LowMask;
|
||||
|
||||
SchedFlags = lowNibble | _forcePauseFlags;
|
||||
SchedFlags = lowNibble | (_forcePauseFlags & _forcePausePermissionFlags);
|
||||
|
||||
AdjustScheduling(oldFlags);
|
||||
}
|
||||
|
@ -1106,7 +1236,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
foreach (KThread thread in _mutexWaiters)
|
||||
{
|
||||
thread.MutexOwner = null;
|
||||
thread._preferredCoreOverride = 0;
|
||||
thread._originalPreferredCore = 0;
|
||||
thread.ObjSyncResult = KernelResult.InvalidState;
|
||||
|
||||
thread.ReleaseAndResume();
|
||||
|
@ -1116,5 +1246,113 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
|
||||
Owner?.DecrementThreadCountAndTerminateIfZero();
|
||||
}
|
||||
|
||||
public void Pin()
|
||||
{
|
||||
IsPinned = true;
|
||||
_coreMigrationDisableCount++;
|
||||
|
||||
int activeCore = ActiveCore;
|
||||
|
||||
_originalPreferredCore = PreferredCore;
|
||||
_originalAffinityMask = AffinityMask;
|
||||
|
||||
ActiveCore = CurrentCore;
|
||||
PreferredCore = CurrentCore;
|
||||
AffinityMask = 1 << CurrentCore;
|
||||
|
||||
if (activeCore != CurrentCore || _originalAffinityMask != AffinityMask)
|
||||
{
|
||||
AdjustSchedulingForNewAffinity(_originalAffinityMask, activeCore);
|
||||
}
|
||||
|
||||
_originalBasePriority = BasePriority;
|
||||
BasePriority = Math.Min(_originalBasePriority, BitOperations.TrailingZeroCount(Owner.Capabilities.AllowedThreadPriosMask) - 1);
|
||||
UpdatePriorityInheritance();
|
||||
|
||||
// Disallows thread pausing
|
||||
_forcePausePermissionFlags &= ~ThreadSchedState.ThreadPauseFlag;
|
||||
CombineForcePauseFlags();
|
||||
|
||||
// TODO: Assign reduced SVC permissions
|
||||
}
|
||||
|
||||
public void Unpin()
|
||||
{
|
||||
IsPinned = false;
|
||||
_coreMigrationDisableCount--;
|
||||
|
||||
long affinityMask = AffinityMask;
|
||||
int activeCore = ActiveCore;
|
||||
|
||||
PreferredCore = _originalPreferredCore;
|
||||
AffinityMask = _originalAffinityMask;
|
||||
|
||||
if (AffinityMask != affinityMask)
|
||||
{
|
||||
if ((AffinityMask & 1 << ActiveCore) != 0)
|
||||
{
|
||||
if (PreferredCore >= 0)
|
||||
{
|
||||
ActiveCore = PreferredCore;
|
||||
}
|
||||
else
|
||||
{
|
||||
ActiveCore = sizeof(ulong) * 8 - 1 - BitOperations.LeadingZeroCount((ulong)AffinityMask);
|
||||
}
|
||||
|
||||
AdjustSchedulingForNewAffinity(affinityMask, activeCore);
|
||||
}
|
||||
}
|
||||
|
||||
BasePriority = _originalBasePriority;
|
||||
UpdatePriorityInheritance();
|
||||
|
||||
if (!TerminationRequested)
|
||||
{
|
||||
// Allows thread pausing
|
||||
_forcePausePermissionFlags |= ThreadSchedState.ThreadPauseFlag;
|
||||
CombineForcePauseFlags();
|
||||
|
||||
// TODO: Restore SVC permissions
|
||||
}
|
||||
|
||||
// Wake up waiters
|
||||
foreach (KThread waiter in _pinnedWaiters)
|
||||
{
|
||||
waiter.ReleaseAndResume();
|
||||
}
|
||||
|
||||
_pinnedWaiters.Clear();
|
||||
}
|
||||
|
||||
public void SynchronizePreemptionState()
|
||||
{
|
||||
KernelContext.CriticalSection.Enter();
|
||||
|
||||
if (Owner != null && Owner.PinnedThreads[CurrentCore] == this)
|
||||
{
|
||||
ClearUserInterruptFlag();
|
||||
|
||||
Owner.UnpinThread(this);
|
||||
}
|
||||
|
||||
KernelContext.CriticalSection.Leave();
|
||||
}
|
||||
|
||||
public ushort GetUserDisableCount()
|
||||
{
|
||||
return Owner.CpuMemory.Read<ushort>(_tlsAddress + TlsUserDisableCountOffset);
|
||||
}
|
||||
|
||||
public void SetUserInterruptFlag()
|
||||
{
|
||||
Owner.CpuMemory.Write<ushort>(_tlsAddress + TlsUserInterruptFlagOffset, 1);
|
||||
}
|
||||
|
||||
public void ClearUserInterruptFlag()
|
||||
{
|
||||
Owner.CpuMemory.Write<ushort>(_tlsAddress + TlsUserInterruptFlagOffset, 0);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,11 +4,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
{
|
||||
LowMask = 0xf,
|
||||
HighMask = 0xfff0,
|
||||
ForcePauseMask = 0x70,
|
||||
ForcePauseMask = 0x1f0,
|
||||
|
||||
ProcessPauseFlag = 1 << 4,
|
||||
ThreadPauseFlag = 1 << 5,
|
||||
ProcessDebugPauseFlag = 1 << 6,
|
||||
BacktracePauseFlag = 1 << 7,
|
||||
KernelInitPauseFlag = 1 << 8,
|
||||
|
||||
None = 0,
|
||||
|
|
|
@ -330,6 +330,19 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
|
|||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandHipc(60)] // 2.0.0+
|
||||
// SetMediaPlaybackStateForApplication(bool enabled)
|
||||
public ResultCode SetMediaPlaybackStateForApplication(ServiceCtx context)
|
||||
{
|
||||
bool enabled = context.RequestData.ReadBoolean();
|
||||
|
||||
// NOTE: Service stores the "enabled" value in a private field, when enabled is false, it stores nn::os::GetSystemTick() too.
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAm, new { enabled });
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandHipc(66)] // 3.0.0+
|
||||
// InitializeGamePlayRecording(u64, handle<copy>)
|
||||
public ResultCode InitializeGamePlayRecording(ServiceCtx context)
|
||||
|
|
|
@ -120,6 +120,45 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
|
|||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandHipc(10120)] // 10.0.0+
|
||||
// nn::friends::IsFriendListCacheAvailable(nn::account::Uid userId) -> bool
|
||||
public ResultCode IsFriendListCacheAvailable(ServiceCtx context)
|
||||
{
|
||||
UserId userId = context.RequestData.ReadStruct<UserId>();
|
||||
|
||||
if (userId.IsNull)
|
||||
{
|
||||
return ResultCode.InvalidArgument;
|
||||
}
|
||||
|
||||
// TODO: Service mount the friends:/ system savedata and try to load friend.cache file, returns true if exists, false otherwise.
|
||||
// NOTE: If no cache is available, guest then calls nn::friends::EnsureFriendListAvailable, we can avoid that by faking the cache check.
|
||||
context.ResponseData.Write(true);
|
||||
|
||||
// TODO: Since we don't support friend features, it's fine to stub it for now.
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceFriend, new { UserId = userId.ToString() });
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandHipc(10121)] // 10.0.0+
|
||||
// nn::friends::EnsureFriendListAvailable(nn::account::Uid userId)
|
||||
public ResultCode EnsureFriendListAvailable(ServiceCtx context)
|
||||
{
|
||||
UserId userId = context.RequestData.ReadStruct<UserId>();
|
||||
|
||||
if (userId.IsNull)
|
||||
{
|
||||
return ResultCode.InvalidArgument;
|
||||
}
|
||||
|
||||
// TODO: Service mount the friends:/ system savedata and create a friend.cache file for the given user id.
|
||||
// Since we don't support friend features, it's fine to stub it for now.
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceFriend, new { UserId = userId.ToString() });
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandHipc(10400)]
|
||||
// nn::friends::GetBlockedUserListIds(int offset, nn::account::Uid userId) -> (u32, buffer<nn::account::NetworkServiceAccountId, 0xa>)
|
||||
public ResultCode GetBlockedUserListIds(ServiceCtx context)
|
||||
|
@ -311,4 +350,4 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator
|
|||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
using Ryujinx.Common;
|
||||
using Ryujinx.HLE.Exceptions;
|
||||
using Ryujinx.Common.Configuration.Hid;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.HLE.Exceptions;
|
||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.TouchScreen;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad;
|
||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.TouchScreen;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Configuration.Hid;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
|
|
|
@ -5,9 +5,7 @@ using Ryujinx.HLE.HOS.Kernel.Common;
|
|||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.HidServer;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
|
@ -642,9 +640,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||
// SetSupportedNpadIdType(nn::applet::AppletResourceUserId, array<NpadIdType, 9>)
|
||||
public ResultCode SetSupportedNpadIdType(ServiceCtx context)
|
||||
{
|
||||
long appletResourceUserId = context.RequestData.ReadInt64();
|
||||
ulong arrayPosition = context.Request.PtrBuff[0].Position;
|
||||
ulong arraySize = context.Request.PtrBuff[0].Size;
|
||||
long appletResourceUserId = context.RequestData.ReadInt64();
|
||||
ulong arrayPosition = context.Request.PtrBuff[0].Position;
|
||||
ulong arraySize = context.Request.PtrBuff[0].Size;
|
||||
|
||||
ReadOnlySpan<NpadIdType> supportedPlayerIds = MemoryMarshal.Cast<byte, NpadIdType>(context.Memory.GetSpan(arrayPosition, (int)arraySize));
|
||||
|
||||
|
@ -667,38 +665,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||
// ActivateNpad(nn::applet::AppletResourceUserId)
|
||||
public ResultCode ActivateNpad(ServiceCtx context)
|
||||
{
|
||||
long appletResourceUserId = context.RequestData.ReadInt64();
|
||||
|
||||
context.Device.Hid.Npads.Active = true;
|
||||
|
||||
// Initialize entries to avoid issues with some games.
|
||||
|
||||
List<GamepadInput> emptyGamepadInputs = new List<GamepadInput>();
|
||||
List<SixAxisInput> emptySixAxisInputs = new List<SixAxisInput>();
|
||||
|
||||
for (int player = 0; player < NpadDevices.MaxControllers; player++)
|
||||
{
|
||||
GamepadInput gamepadInput = new GamepadInput();
|
||||
SixAxisInput sixaxisInput = new SixAxisInput();
|
||||
|
||||
gamepadInput.PlayerId = (PlayerIndex)player;
|
||||
sixaxisInput.PlayerId = (PlayerIndex)player;
|
||||
|
||||
sixaxisInput.Orientation = new float[9];
|
||||
|
||||
emptyGamepadInputs.Add(gamepadInput);
|
||||
emptySixAxisInputs.Add(sixaxisInput);
|
||||
}
|
||||
|
||||
for (int entry = 0; entry < Hid.SharedMemEntryCount; entry++)
|
||||
{
|
||||
context.Device.Hid.Npads.Update(emptyGamepadInputs);
|
||||
context.Device.Hid.Npads.UpdateSixAxis(emptySixAxisInputs);
|
||||
}
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId });
|
||||
|
||||
return ResultCode.Success;
|
||||
return ActiveNpadImpl(context);
|
||||
}
|
||||
|
||||
[CommandHipc(104)]
|
||||
|
@ -773,12 +740,20 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||
}
|
||||
|
||||
[CommandHipc(109)] // 5.0.0+
|
||||
// ActivateNpadWithRevision(nn::applet::AppletResourceUserId, int revision)
|
||||
// ActivateNpadWithRevision(nn::applet::AppletResourceUserId, ulong revision)
|
||||
public ResultCode ActivateNpadWithRevision(ServiceCtx context)
|
||||
{
|
||||
int revision = context.RequestData.ReadInt32();
|
||||
ulong revision = context.RequestData.ReadUInt64();
|
||||
|
||||
return ActiveNpadImpl(context, revision);
|
||||
}
|
||||
|
||||
private ResultCode ActiveNpadImpl(ServiceCtx context, ulong revision = 0)
|
||||
{
|
||||
long appletResourceUserId = context.RequestData.ReadInt64();
|
||||
|
||||
context.Device.Hid.Npads.Active = true;
|
||||
|
||||
// Initialize entries to avoid issues with some games.
|
||||
|
||||
List<GamepadInput> emptyGamepadInputs = new List<GamepadInput>();
|
||||
|
@ -974,7 +949,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||
// IsUnintendedHomeButtonInputProtectionEnabled(uint Unknown0, nn::applet::AppletResourceUserId) -> bool IsEnabled
|
||||
public ResultCode IsUnintendedHomeButtonInputProtectionEnabled(ServiceCtx context)
|
||||
{
|
||||
uint unknown0 = context.RequestData.ReadUInt32();
|
||||
uint unknown0 = context.RequestData.ReadUInt32();
|
||||
long appletResourceUserId = context.RequestData.ReadInt64();
|
||||
|
||||
context.ResponseData.Write(_unintendedHomeButtonInputProtectionEnabled);
|
||||
|
@ -989,7 +964,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||
public ResultCode EnableUnintendedHomeButtonInputProtection(ServiceCtx context)
|
||||
{
|
||||
_unintendedHomeButtonInputProtectionEnabled = context.RequestData.ReadBoolean();
|
||||
uint unknown0 = context.RequestData.ReadUInt32();
|
||||
uint unknown0 = context.RequestData.ReadUInt32();
|
||||
long appletResourceUserId = context.RequestData.ReadInt64();
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, unknown0, _unintendedHomeButtonInputProtectionEnabled });
|
||||
|
@ -1027,8 +1002,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||
public ResultCode GetVibrationDeviceInfo(ServiceCtx context)
|
||||
{
|
||||
HidVibrationDeviceHandle deviceHandle = context.RequestData.ReadStruct<HidVibrationDeviceHandle>();
|
||||
NpadStyleIndex deviceType = (NpadStyleIndex)deviceHandle.DeviceType;
|
||||
NpadIdType npadIdType = (NpadIdType)deviceHandle.PlayerId;
|
||||
NpadStyleIndex deviceType = (NpadStyleIndex)deviceHandle.DeviceType;
|
||||
NpadIdType npadIdType = (NpadIdType)deviceHandle.PlayerId;
|
||||
|
||||
if (deviceType < NpadStyleIndex.System || deviceType >= NpadStyleIndex.FullKey)
|
||||
{
|
||||
|
@ -1092,9 +1067,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||
HidVibrationDeviceHandle deviceHandle = new HidVibrationDeviceHandle
|
||||
{
|
||||
DeviceType = context.RequestData.ReadByte(),
|
||||
PlayerId = context.RequestData.ReadByte(),
|
||||
Position = context.RequestData.ReadByte(),
|
||||
Reserved = context.RequestData.ReadByte()
|
||||
PlayerId = context.RequestData.ReadByte(),
|
||||
Position = context.RequestData.ReadByte(),
|
||||
Reserved = context.RequestData.ReadByte()
|
||||
};
|
||||
|
||||
HidVibrationValue vibrationValue = new HidVibrationValue
|
||||
|
@ -1123,9 +1098,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||
HidVibrationDeviceHandle deviceHandle = new HidVibrationDeviceHandle
|
||||
{
|
||||
DeviceType = context.RequestData.ReadByte(),
|
||||
PlayerId = context.RequestData.ReadByte(),
|
||||
Position = context.RequestData.ReadByte(),
|
||||
Reserved = context.RequestData.ReadByte()
|
||||
PlayerId = context.RequestData.ReadByte(),
|
||||
Position = context.RequestData.ReadByte(),
|
||||
Reserved = context.RequestData.ReadByte()
|
||||
};
|
||||
|
||||
long appletResourceUserId = context.RequestData.ReadInt64();
|
||||
|
@ -1185,8 +1160,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||
|
||||
context.Memory.Read(context.Request.PtrBuff[1].Position, vibrationValueBuffer);
|
||||
|
||||
Span<HidVibrationDeviceHandle> deviceHandles = MemoryMarshal.Cast<byte, HidVibrationDeviceHandle>(vibrationDeviceHandleBuffer);
|
||||
Span<HidVibrationValue> vibrationValues = MemoryMarshal.Cast<byte, HidVibrationValue>(vibrationValueBuffer);
|
||||
Span<HidVibrationDeviceHandle> deviceHandles = MemoryMarshal.Cast<byte, HidVibrationDeviceHandle>(vibrationDeviceHandleBuffer);
|
||||
Span<HidVibrationValue> vibrationValues = MemoryMarshal.Cast<byte, HidVibrationValue>(vibrationValueBuffer);
|
||||
|
||||
if (!deviceHandles.IsEmpty && vibrationValues.Length == deviceHandles.Length)
|
||||
{
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
{
|
||||
public enum NpadStyleIndex : byte
|
||||
{
|
||||
FullKey = 3,
|
||||
Handheld = 4,
|
||||
JoyDual = 5,
|
||||
JoyLeft = 6,
|
||||
JoyRight = 7,
|
||||
FullKey = 3,
|
||||
Handheld = 4,
|
||||
JoyDual = 5,
|
||||
JoyLeft = 6,
|
||||
JoyRight = 7,
|
||||
SystemExt = 32,
|
||||
System = 33
|
||||
System = 33
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
|||
[Service("bsd:u", false)]
|
||||
class IClient : IpcService
|
||||
{
|
||||
private static Dictionary<WsaError, LinuxError> _errorMap = new Dictionary<WsaError, LinuxError>
|
||||
private static readonly Dictionary<WsaError, LinuxError> _errorMap = new()
|
||||
{
|
||||
// WSAEINTR
|
||||
{WsaError.WSAEINTR, LinuxError.EINTR},
|
||||
|
@ -97,6 +97,50 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
|||
{0, 0}
|
||||
};
|
||||
|
||||
private static readonly Dictionary<BsdSocketOption, SocketOptionName> _soSocketOptionMap = new()
|
||||
{
|
||||
{ BsdSocketOption.SoDebug, SocketOptionName.Debug },
|
||||
{ BsdSocketOption.SoReuseAddr, SocketOptionName.ReuseAddress },
|
||||
{ BsdSocketOption.SoKeepAlive, SocketOptionName.KeepAlive },
|
||||
{ BsdSocketOption.SoDontRoute, SocketOptionName.DontRoute },
|
||||
{ BsdSocketOption.SoBroadcast, SocketOptionName.Broadcast },
|
||||
{ BsdSocketOption.SoUseLoopBack, SocketOptionName.UseLoopback },
|
||||
{ BsdSocketOption.SoLinger, SocketOptionName.Linger },
|
||||
{ BsdSocketOption.SoOobInline, SocketOptionName.OutOfBandInline },
|
||||
{ BsdSocketOption.SoReusePort, SocketOptionName.ReuseAddress },
|
||||
{ BsdSocketOption.SoSndBuf, SocketOptionName.SendBuffer },
|
||||
{ BsdSocketOption.SoRcvBuf, SocketOptionName.ReceiveBuffer },
|
||||
{ BsdSocketOption.SoSndLoWat, SocketOptionName.SendLowWater },
|
||||
{ BsdSocketOption.SoRcvLoWat, SocketOptionName.ReceiveLowWater },
|
||||
{ BsdSocketOption.SoSndTimeo, SocketOptionName.SendTimeout },
|
||||
{ BsdSocketOption.SoRcvTimeo, SocketOptionName.ReceiveTimeout },
|
||||
{ BsdSocketOption.SoError, SocketOptionName.Error },
|
||||
{ BsdSocketOption.SoType, SocketOptionName.Type }
|
||||
};
|
||||
|
||||
private static readonly Dictionary<BsdSocketOption, SocketOptionName> _ipSocketOptionMap = new()
|
||||
{
|
||||
{ BsdSocketOption.IpOptions, SocketOptionName.IPOptions },
|
||||
{ BsdSocketOption.IpHdrIncl, SocketOptionName.HeaderIncluded },
|
||||
{ BsdSocketOption.IpTtl, SocketOptionName.IpTimeToLive },
|
||||
{ BsdSocketOption.IpMulticastIf, SocketOptionName.MulticastInterface },
|
||||
{ BsdSocketOption.IpMulticastTtl, SocketOptionName.MulticastTimeToLive },
|
||||
{ BsdSocketOption.IpMulticastLoop, SocketOptionName.MulticastLoopback },
|
||||
{ BsdSocketOption.IpAddMembership, SocketOptionName.AddMembership },
|
||||
{ BsdSocketOption.IpDropMembership, SocketOptionName.DropMembership },
|
||||
{ BsdSocketOption.IpDontFrag, SocketOptionName.DontFragment },
|
||||
{ BsdSocketOption.IpAddSourceMembership, SocketOptionName.AddSourceMembership },
|
||||
{ BsdSocketOption.IpDropSourceMembership, SocketOptionName.DropSourceMembership }
|
||||
};
|
||||
|
||||
private static readonly Dictionary<BsdSocketOption, SocketOptionName> _tcpSocketOptionMap = new()
|
||||
{
|
||||
{ BsdSocketOption.TcpNoDelay, SocketOptionName.NoDelay },
|
||||
{ BsdSocketOption.TcpKeepIdle, SocketOptionName.TcpKeepAliveTime },
|
||||
{ BsdSocketOption.TcpKeepIntvl, SocketOptionName.TcpKeepAliveInterval },
|
||||
{ BsdSocketOption.TcpKeepCnt, SocketOptionName.TcpKeepAliveRetryCount }
|
||||
};
|
||||
|
||||
private bool _isPrivileged;
|
||||
|
||||
private List<BsdSocket> _sockets = new List<BsdSocket>();
|
||||
|
@ -118,13 +162,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
|||
|
||||
private static SocketFlags ConvertBsdSocketFlags(BsdSocketFlags bsdSocketFlags)
|
||||
{
|
||||
BsdSocketFlags SupportedFlags =
|
||||
BsdSocketFlags.Oob |
|
||||
BsdSocketFlags.Peek |
|
||||
BsdSocketFlags.DontRoute |
|
||||
BsdSocketFlags.Trunc |
|
||||
BsdSocketFlags.CTrunc;
|
||||
|
||||
SocketFlags socketFlags = SocketFlags.None;
|
||||
|
||||
if (bsdSocketFlags.HasFlag(BsdSocketFlags.Oob))
|
||||
|
@ -166,6 +203,25 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
|||
return socketFlags;
|
||||
}
|
||||
|
||||
private static bool TryConvertSocketOption(BsdSocketOption option, SocketOptionLevel level, out SocketOptionName name)
|
||||
{
|
||||
var table = level switch
|
||||
{
|
||||
SocketOptionLevel.Socket => _soSocketOptionMap,
|
||||
SocketOptionLevel.IP => _ipSocketOptionMap,
|
||||
SocketOptionLevel.Tcp => _tcpSocketOptionMap,
|
||||
_ => null
|
||||
};
|
||||
|
||||
if (table == null)
|
||||
{
|
||||
name = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
return table.TryGetValue(option, out name);
|
||||
}
|
||||
|
||||
private ResultCode WriteWinSock2Error(ServiceCtx context, WsaError errorCode)
|
||||
{
|
||||
return WriteBsdResult(context, -1, ConvertError(errorCode));
|
||||
|
@ -820,9 +876,9 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
|||
// GetSockOpt(u32 socket, u32 level, u32 option_name) -> (i32 ret, u32 bsd_errno, u32, buffer<unknown, 0x22, 0>)
|
||||
public ResultCode GetSockOpt(ServiceCtx context)
|
||||
{
|
||||
int socketFd = context.RequestData.ReadInt32();
|
||||
SocketOptionLevel level = (SocketOptionLevel)context.RequestData.ReadInt32();
|
||||
SocketOptionName optionName = (SocketOptionName)context.RequestData.ReadInt32();
|
||||
int socketFd = context.RequestData.ReadInt32();
|
||||
SocketOptionLevel level = (SocketOptionLevel)context.RequestData.ReadInt32();
|
||||
BsdSocketOption option = (BsdSocketOption)context.RequestData.ReadInt32();
|
||||
|
||||
(ulong bufferPosition, ulong bufferSize) = context.Request.GetBufferType0x22();
|
||||
|
||||
|
@ -831,7 +887,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
|||
|
||||
if (socket != null)
|
||||
{
|
||||
errno = HandleGetSocketOption(context, socket, optionName, level, bufferPosition, bufferSize);
|
||||
errno = HandleGetSocketOption(context, socket, option, level, bufferPosition, bufferSize);
|
||||
}
|
||||
|
||||
return WriteBsdResult(context, 0, errno);
|
||||
|
@ -936,45 +992,26 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
|||
private static LinuxError HandleGetSocketOption(
|
||||
ServiceCtx context,
|
||||
BsdSocket socket,
|
||||
SocketOptionName optionName,
|
||||
BsdSocketOption option,
|
||||
SocketOptionLevel level,
|
||||
ulong optionValuePosition,
|
||||
ulong optionValueSize)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!TryConvertSocketOption(option, level, out SocketOptionName optionName))
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.ServiceBsd, $"Unsupported GetSockOpt Option: {option} Level: {level}");
|
||||
|
||||
return LinuxError.EOPNOTSUPP;
|
||||
}
|
||||
|
||||
byte[] optionValue = new byte[optionValueSize];
|
||||
|
||||
switch (optionName)
|
||||
{
|
||||
case SocketOptionName.Broadcast:
|
||||
case SocketOptionName.DontLinger:
|
||||
case SocketOptionName.Debug:
|
||||
case SocketOptionName.Error:
|
||||
case SocketOptionName.KeepAlive:
|
||||
case SocketOptionName.OutOfBandInline:
|
||||
case SocketOptionName.ReceiveBuffer:
|
||||
case SocketOptionName.ReceiveTimeout:
|
||||
case SocketOptionName.SendBuffer:
|
||||
case SocketOptionName.SendTimeout:
|
||||
case SocketOptionName.Type:
|
||||
case SocketOptionName.Linger:
|
||||
socket.Handle.GetSocketOption(level, optionName, optionValue);
|
||||
context.Memory.Write(optionValuePosition, optionValue);
|
||||
socket.Handle.GetSocketOption(level, optionName, optionValue);
|
||||
context.Memory.Write(optionValuePosition, optionValue);
|
||||
|
||||
return LinuxError.SUCCESS;
|
||||
|
||||
case (SocketOptionName)0x200:
|
||||
socket.Handle.GetSocketOption(level, SocketOptionName.ReuseAddress, optionValue);
|
||||
context.Memory.Write(optionValuePosition, optionValue);
|
||||
|
||||
return LinuxError.SUCCESS;
|
||||
|
||||
default:
|
||||
Logger.Warning?.Print(LogClass.ServiceBsd, $"Unsupported GetSockOpt OptionName: {optionName}");
|
||||
|
||||
return LinuxError.EOPNOTSUPP;
|
||||
}
|
||||
return LinuxError.SUCCESS;
|
||||
}
|
||||
catch (SocketException exception)
|
||||
{
|
||||
|
@ -985,47 +1022,34 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
|||
private static LinuxError HandleSetSocketOption(
|
||||
ServiceCtx context,
|
||||
BsdSocket socket,
|
||||
SocketOptionName optionName,
|
||||
BsdSocketOption option,
|
||||
SocketOptionLevel level,
|
||||
ulong optionValuePosition,
|
||||
ulong optionValueSize)
|
||||
{
|
||||
try
|
||||
{
|
||||
switch (optionName)
|
||||
if (!TryConvertSocketOption(option, level, out SocketOptionName optionName))
|
||||
{
|
||||
case SocketOptionName.Broadcast:
|
||||
case SocketOptionName.DontLinger:
|
||||
case SocketOptionName.Debug:
|
||||
case SocketOptionName.Error:
|
||||
case SocketOptionName.KeepAlive:
|
||||
case SocketOptionName.OutOfBandInline:
|
||||
case SocketOptionName.ReceiveBuffer:
|
||||
case SocketOptionName.ReceiveTimeout:
|
||||
case SocketOptionName.SendBuffer:
|
||||
case SocketOptionName.SendTimeout:
|
||||
case SocketOptionName.Type:
|
||||
case SocketOptionName.ReuseAddress:
|
||||
socket.Handle.SetSocketOption(level, optionName, context.Memory.Read<int>((ulong)optionValuePosition));
|
||||
Logger.Warning?.Print(LogClass.ServiceBsd, $"Unsupported SetSockOpt Option: {option} Level: {level}");
|
||||
|
||||
return LinuxError.SUCCESS;
|
||||
|
||||
case (SocketOptionName)0x200:
|
||||
socket.Handle.SetSocketOption(level, SocketOptionName.ReuseAddress, context.Memory.Read<int>((ulong)optionValuePosition));
|
||||
|
||||
return LinuxError.SUCCESS;
|
||||
|
||||
case SocketOptionName.Linger:
|
||||
socket.Handle.SetSocketOption(level, SocketOptionName.Linger,
|
||||
new LingerOption(context.Memory.Read<int>((ulong)optionValuePosition) != 0, context.Memory.Read<int>((ulong)optionValuePosition + 4)));
|
||||
|
||||
return LinuxError.SUCCESS;
|
||||
|
||||
default:
|
||||
Logger.Warning?.Print(LogClass.ServiceBsd, $"Unsupported SetSockOpt OptionName: {optionName}");
|
||||
|
||||
return LinuxError.EOPNOTSUPP;
|
||||
return LinuxError.EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int value = context.Memory.Read<int>((ulong)optionValuePosition);
|
||||
|
||||
if (option == BsdSocketOption.SoLinger)
|
||||
{
|
||||
int value2 = context.Memory.Read<int>((ulong)optionValuePosition + 4);
|
||||
|
||||
socket.Handle.SetSocketOption(level, SocketOptionName.Linger, new LingerOption(value != 0, value2));
|
||||
}
|
||||
else
|
||||
{
|
||||
socket.Handle.SetSocketOption(level, optionName, value);
|
||||
}
|
||||
|
||||
return LinuxError.SUCCESS;
|
||||
}
|
||||
catch (SocketException exception)
|
||||
{
|
||||
|
@ -1037,9 +1061,9 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
|||
// SetSockOpt(u32 socket, u32 level, u32 option_name, buffer<unknown, 0x21, 0> option_value) -> (i32 ret, u32 bsd_errno)
|
||||
public ResultCode SetSockOpt(ServiceCtx context)
|
||||
{
|
||||
int socketFd = context.RequestData.ReadInt32();
|
||||
SocketOptionLevel level = (SocketOptionLevel)context.RequestData.ReadInt32();
|
||||
SocketOptionName optionName = (SocketOptionName)context.RequestData.ReadInt32();
|
||||
int socketFd = context.RequestData.ReadInt32();
|
||||
SocketOptionLevel level = (SocketOptionLevel)context.RequestData.ReadInt32();
|
||||
BsdSocketOption option = (BsdSocketOption)context.RequestData.ReadInt32();
|
||||
|
||||
(ulong bufferPos, ulong bufferSize) = context.Request.GetBufferType0x21();
|
||||
|
||||
|
@ -1048,7 +1072,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
|||
|
||||
if (socket != null)
|
||||
{
|
||||
errno = HandleSetSocketOption(context, socket, optionName, level, bufferPos, bufferSize);
|
||||
errno = HandleSetSocketOption(context, socket, option, level, bufferPos, bufferSize);
|
||||
}
|
||||
|
||||
return WriteBsdResult(context, 0, errno);
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
using System.Net.Sockets;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||
{
|
||||
enum BsdSocketFlags
|
||||
|
|
119
Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketOption.cs
Normal file
119
Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSocketOption.cs
Normal file
|
@ -0,0 +1,119 @@
|
|||
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||
{
|
||||
enum BsdSocketOption
|
||||
{
|
||||
SoDebug = 0x1,
|
||||
SoAcceptConn = 0x2,
|
||||
SoReuseAddr = 0x4,
|
||||
SoKeepAlive = 0x8,
|
||||
SoDontRoute = 0x10,
|
||||
SoBroadcast = 0x20,
|
||||
SoUseLoopBack = 0x40,
|
||||
SoLinger = 0x80,
|
||||
SoOobInline = 0x100,
|
||||
SoReusePort = 0x200,
|
||||
SoTimestamp = 0x400,
|
||||
SoNoSigpipe = 0x800,
|
||||
SoAcceptFilter = 0x1000,
|
||||
SoBinTime = 0x2000,
|
||||
SoNoOffload = 0x4000,
|
||||
SoNoDdp = 0x8000,
|
||||
SoReusePortLb = 0x10000,
|
||||
SoRError = 0x20000,
|
||||
|
||||
SoSndBuf = 0x1001,
|
||||
SoRcvBuf = 0x1002,
|
||||
SoSndLoWat = 0x1003,
|
||||
SoRcvLoWat = 0x1004,
|
||||
SoSndTimeo = 0x1005,
|
||||
SoRcvTimeo = 0x1006,
|
||||
SoError = 0x1007,
|
||||
SoType = 0x1008,
|
||||
SoLabel = 0x1009,
|
||||
SoPeerLabel = 0x1010,
|
||||
SoListenQLimit = 0x1011,
|
||||
SoListenQLen = 0x1012,
|
||||
SoListenIncQLen = 0x1013,
|
||||
SoSetFib = 0x1014,
|
||||
SoUserCookie = 0x1015,
|
||||
SoProtocol = 0x1016,
|
||||
SoTsClock = 0x1017,
|
||||
SoMaxPacingRate = 0x1018,
|
||||
SoDomain = 0x1019,
|
||||
|
||||
IpOptions = 1,
|
||||
IpHdrIncl = 2,
|
||||
IpTos = 3,
|
||||
IpTtl = 4,
|
||||
IpRecvOpts = 5,
|
||||
IpRecvRetOpts = 6,
|
||||
IpRecvDstAddr = 7,
|
||||
IpRetOpts = 8,
|
||||
IpMulticastIf = 9,
|
||||
IpMulticastTtl = 10,
|
||||
IpMulticastLoop = 11,
|
||||
IpAddMembership = 12,
|
||||
IpDropMembership = 13,
|
||||
IpMulticastVif = 14,
|
||||
IpRsvpOn = 15,
|
||||
IpRsvpOff = 16,
|
||||
IpRsvpVifOn = 17,
|
||||
IpRsvpVifOff = 18,
|
||||
IpPortRange = 19,
|
||||
IpRecvIf = 20,
|
||||
IpIpsecPolicy = 21,
|
||||
IpOnesBcast = 23,
|
||||
IpBindany = 24,
|
||||
IpBindMulti = 25,
|
||||
IpRssListenBucket = 26,
|
||||
IpOrigDstAddr = 27,
|
||||
|
||||
IpFwTableAdd = 40,
|
||||
IpFwTableDel = 41,
|
||||
IpFwTableFlush = 42,
|
||||
IpFwTableGetSize = 43,
|
||||
IpFwTableList = 44,
|
||||
|
||||
IpFw3 = 48,
|
||||
IpDummyNet3 = 49,
|
||||
|
||||
IpFwAdd = 50,
|
||||
IpFwDel = 51,
|
||||
IpFwFlush = 52,
|
||||
IpFwZero = 53,
|
||||
IpFwGet = 54,
|
||||
IpFwResetLog = 55,
|
||||
|
||||
IpFwNatCfg = 56,
|
||||
IpFwNatDel = 57,
|
||||
IpFwNatGetConfig = 58,
|
||||
IpFwNatGetLog = 59,
|
||||
|
||||
IpDummyNetConfigure = 60,
|
||||
IpDummyNetDel = 61,
|
||||
IpDummyNetFlush = 62,
|
||||
IpDummyNetGet = 64,
|
||||
|
||||
IpRecvTtl = 65,
|
||||
IpMinTtl = 66,
|
||||
IpDontFrag = 67,
|
||||
IpRecvTos = 68,
|
||||
|
||||
IpAddSourceMembership = 70,
|
||||
IpDropSourceMembership = 71,
|
||||
IpBlockSource = 72,
|
||||
IpUnblockSource = 73,
|
||||
|
||||
TcpNoDelay = 1,
|
||||
TcpMaxSeg = 2,
|
||||
TcpNoPush = 4,
|
||||
TcpNoOpt = 8,
|
||||
TcpMd5Sig = 16,
|
||||
TcpInfo = 32,
|
||||
TcpCongestion = 64,
|
||||
TcpKeepInit = 128,
|
||||
TcpKeepIdle = 256,
|
||||
TcpKeepIntvl = 512,
|
||||
TcpKeepCnt = 1024
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue