0
0
Fork 0
mirror of https://github.com/GreemDev/Ryujinx.git synced 2024-12-22 22:45:46 +00:00

Merge branch 'GreemDev:master' into master

This commit is contained in:
Daniel Nylander 2024-12-20 19:30:27 +01:00 committed by GitHub
commit 63d8f907aa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
164 changed files with 2133 additions and 1186 deletions

View file

@ -5,7 +5,7 @@ If you wish to build the emulator yourself, follow these steps:
### Step 1 ### Step 1
Install the [.NET 8.0 (or higher) SDK](https://dotnet.microsoft.com/download/dotnet/8.0). Install the [.NET 9.0 (or higher) SDK](https://dotnet.microsoft.com/download/dotnet/9.0).
Make sure your SDK version is higher or equal to the required version specified in [global.json](global.json). Make sure your SDK version is higher or equal to the required version specified in [global.json](global.json).
### Step 2 ### Step 2

6
Directory.Build.props Normal file
View file

@ -0,0 +1,6 @@
<Project>
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<LangVersion>latest</LangVersion>
</PropertyGroup>
</Project>

View file

@ -48,8 +48,8 @@
<PackageVersion Include="SkiaSharp" Version="2.88.7" /> <PackageVersion Include="SkiaSharp" Version="2.88.7" />
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.7" /> <PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.7" />
<PackageVersion Include="SPB" Version="0.0.4-build32" /> <PackageVersion Include="SPB" Version="0.0.4-build32" />
<PackageVersion Include="System.IO.Hashing" Version="8.0.0" /> <PackageVersion Include="System.IO.Hashing" Version="9.0.0" />
<PackageVersion Include="System.Management" Version="8.0.0" /> <PackageVersion Include="System.Management" Version="9.0.0" />
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" /> <PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -1,6 +1,6 @@
{ {
"sdk": { "sdk": {
"version": "8.0.100", "version": "9.0.100",
"rollForward": "latestFeature" "rollForward": "latestFeature"
} }
} }

View file

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes> <DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
</PropertyGroup> </PropertyGroup>

View file

@ -1,4 +1,5 @@
using ARMeilleure.Common; using ARMeilleure.Common;
using System;
namespace ARMeilleure.Decoders namespace ARMeilleure.Decoders
{ {
@ -149,7 +150,7 @@ namespace ARMeilleure.Decoders
return (((long)opCode << 45) >> 48) & ~3; return (((long)opCode << 45) >> 48) & ~3;
} }
public static bool VectorArgumentsInvalid(bool q, params int[] args) public static bool VectorArgumentsInvalid(bool q, params ReadOnlySpan<int> args)
{ {
if (q) if (q)
{ {

View file

@ -264,7 +264,7 @@ namespace ARMeilleure.Instructions
return TblOrTbx(dest, vector, bytes, tb0, tb1, tb2, tb3); return TblOrTbx(dest, vector, bytes, tb0, tb1, tb2, tb3);
} }
private static V128 TblOrTbx(V128 dest, V128 vector, int bytes, params V128[] tb) private static V128 TblOrTbx(V128 dest, V128 vector, int bytes, params ReadOnlySpan<V128> tb)
{ {
byte[] res = new byte[16]; byte[] res = new byte[16];

View file

@ -337,7 +337,7 @@ namespace ARMeilleure.IntermediateRepresentation
return result; return result;
} }
public static Operation Operation(Intrinsic intrin, Operand dest, params Operand[] srcs) public static Operation Operation(Intrinsic intrin, Operand dest, params ReadOnlySpan<Operand> srcs)
{ {
Operation result = Make(Instruction.Extended, 0, srcs.Length); Operation result = Make(Instruction.Extended, 0, srcs.Length);

View file

@ -8,6 +8,7 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Versioning; using System.Runtime.Versioning;
using System.Threading;
namespace ARMeilleure.Translation.Cache namespace ARMeilleure.Translation.Cache
{ {
@ -26,7 +27,7 @@ namespace ARMeilleure.Translation.Cache
private static readonly List<CacheEntry> _cacheEntries = new(); private static readonly List<CacheEntry> _cacheEntries = new();
private static readonly object _lock = new(); private static readonly Lock _lock = new();
private static bool _initialized; private static bool _initialized;
[SupportedOSPlatform("windows")] [SupportedOSPlatform("windows")]

View file

@ -559,27 +559,27 @@ namespace ARMeilleure.Translation
return dest; return dest;
} }
public Operand AddIntrinsic(Intrinsic intrin, params Operand[] args) public Operand AddIntrinsic(Intrinsic intrin, params ReadOnlySpan<Operand> args)
{ {
return Add(intrin, Local(OperandType.V128), args); return Add(intrin, Local(OperandType.V128), args);
} }
public Operand AddIntrinsicInt(Intrinsic intrin, params Operand[] args) public Operand AddIntrinsicInt(Intrinsic intrin, params ReadOnlySpan<Operand> args)
{ {
return Add(intrin, Local(OperandType.I32), args); return Add(intrin, Local(OperandType.I32), args);
} }
public Operand AddIntrinsicLong(Intrinsic intrin, params Operand[] args) public Operand AddIntrinsicLong(Intrinsic intrin, params ReadOnlySpan<Operand> args)
{ {
return Add(intrin, Local(OperandType.I64), args); return Add(intrin, Local(OperandType.I64), args);
} }
public void AddIntrinsicNoRet(Intrinsic intrin, params Operand[] args) public void AddIntrinsicNoRet(Intrinsic intrin, params ReadOnlySpan<Operand> args)
{ {
Add(intrin, default, args); Add(intrin, default, args);
} }
private Operand Add(Intrinsic intrin, Operand dest, params Operand[] sources) private Operand Add(Intrinsic intrin, Operand dest, params ReadOnlySpan<Operand> sources)
{ {
NewNextBlockIfNeeded(); NewNextBlockIfNeeded();

View file

@ -59,7 +59,7 @@ namespace ARMeilleure.Translation.PTC
private readonly ManualResetEvent _waitEvent; private readonly ManualResetEvent _waitEvent;
private readonly object _lock; private readonly Lock _lock = new();
private bool _disposed; private bool _disposed;
@ -89,8 +89,6 @@ namespace ARMeilleure.Translation.PTC
_waitEvent = new ManualResetEvent(true); _waitEvent = new ManualResetEvent(true);
_lock = new object();
_disposed = false; _disposed = false;
TitleIdText = TitleIdTextDefault; TitleIdText = TitleIdTextDefault;

View file

@ -41,7 +41,7 @@ namespace ARMeilleure.Translation.PTC
private readonly ManualResetEvent _waitEvent; private readonly ManualResetEvent _waitEvent;
private readonly object _lock; private readonly Lock _lock = new();
private bool _disposed; private bool _disposed;
@ -65,8 +65,6 @@ namespace ARMeilleure.Translation.PTC
_waitEvent = new ManualResetEvent(true); _waitEvent = new ManualResetEvent(true);
_lock = new object();
_disposed = false; _disposed = false;
ProfiledFuncs = new Dictionary<ulong, FuncProfile>(); ProfiledFuncs = new Dictionary<ulong, FuncProfile>();

View file

@ -5,6 +5,7 @@ using Ryujinx.Memory;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Threading;
namespace Ryujinx.Audio.Backends.OpenAL namespace Ryujinx.Audio.Backends.OpenAL
{ {
@ -18,7 +19,7 @@ namespace Ryujinx.Audio.Backends.OpenAL
private ulong _playedSampleCount; private ulong _playedSampleCount;
private float _volume; private float _volume;
private readonly object _lock = new(); private readonly Lock _lock = new();
public OpenALHardwareDeviceSession(OpenALHardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount) public OpenALHardwareDeviceSession(OpenALHardwareDeviceDriver driver, IVirtualMemoryManager memoryManager, SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount) : base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
{ {

View file

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes> <DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes> <DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers> <RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes> <DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>

View file

@ -11,7 +11,7 @@ namespace Ryujinx.Audio
/// <summary> /// <summary>
/// Lock used to control the waiters registration. /// Lock used to control the waiters registration.
/// </summary> /// </summary>
private readonly object _lock = new(); private readonly Lock _lock = new();
/// <summary> /// <summary>
/// Events signaled when the driver played audio buffers. /// Events signaled when the driver played audio buffers.

View file

@ -2,6 +2,7 @@ using Ryujinx.Common;
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using System; using System;
using System.Buffers; using System.Buffers;
using System.Threading;
namespace Ryujinx.Audio.Backends.Common namespace Ryujinx.Audio.Backends.Common
{ {
@ -12,7 +13,7 @@ namespace Ryujinx.Audio.Backends.Common
{ {
private const int RingBufferAlignment = 2048; private const int RingBufferAlignment = 2048;
private readonly object _lock = new(); private readonly Lock _lock = new();
private MemoryOwner<byte> _bufferOwner; private MemoryOwner<byte> _bufferOwner;
private Memory<byte> _buffer; private Memory<byte> _buffer;

View file

@ -14,12 +14,12 @@ namespace Ryujinx.Audio.Input
/// </summary> /// </summary>
public class AudioInputManager : IDisposable public class AudioInputManager : IDisposable
{ {
private readonly object _lock = new(); private readonly Lock _lock = new();
/// <summary> /// <summary>
/// Lock used for session allocation. /// Lock used for session allocation.
/// </summary> /// </summary>
private readonly object _sessionLock = new(); private readonly Lock _sessionLock = new();
/// <summary> /// <summary>
/// The session ids allocation table. /// The session ids allocation table.

View file

@ -48,7 +48,7 @@ namespace Ryujinx.Audio.Input
/// <summary> /// <summary>
/// The lock of the parent. /// The lock of the parent.
/// </summary> /// </summary>
private readonly object _parentLock; private readonly Lock _parentLock;
/// <summary> /// <summary>
/// The dispose state. /// The dispose state.
@ -62,7 +62,7 @@ namespace Ryujinx.Audio.Input
/// <param name="parentLock">The lock of the manager</param> /// <param name="parentLock">The lock of the manager</param>
/// <param name="deviceSession">The hardware device session</param> /// <param name="deviceSession">The hardware device session</param>
/// <param name="bufferEvent">The buffer release event of the audio input</param> /// <param name="bufferEvent">The buffer release event of the audio input</param>
public AudioInputSystem(AudioInputManager manager, object parentLock, IHardwareDeviceSession deviceSession, IWritableEvent bufferEvent) public AudioInputSystem(AudioInputManager manager, Lock parentLock, IHardwareDeviceSession deviceSession, IWritableEvent bufferEvent)
{ {
_manager = manager; _manager = manager;
_parentLock = parentLock; _parentLock = parentLock;

View file

@ -14,12 +14,12 @@ namespace Ryujinx.Audio.Output
/// </summary> /// </summary>
public class AudioOutputManager : IDisposable public class AudioOutputManager : IDisposable
{ {
private readonly object _lock = new(); private readonly Lock _lock = new();
/// <summary> /// <summary>
/// Lock used for session allocation. /// Lock used for session allocation.
/// </summary> /// </summary>
private readonly object _sessionLock = new(); private readonly Lock _sessionLock = new();
/// <summary> /// <summary>
/// The session ids allocation table. /// The session ids allocation table.

View file

@ -48,7 +48,7 @@ namespace Ryujinx.Audio.Output
/// <summary> /// <summary>
/// THe lock of the parent. /// THe lock of the parent.
/// </summary> /// </summary>
private readonly object _parentLock; private readonly Lock _parentLock;
/// <summary> /// <summary>
/// The dispose state. /// The dispose state.
@ -62,7 +62,7 @@ namespace Ryujinx.Audio.Output
/// <param name="parentLock">The lock of the manager</param> /// <param name="parentLock">The lock of the manager</param>
/// <param name="deviceSession">The hardware device session</param> /// <param name="deviceSession">The hardware device session</param>
/// <param name="bufferEvent">The buffer release event of the audio output</param> /// <param name="bufferEvent">The buffer release event of the audio output</param>
public AudioOutputSystem(AudioOutputManager manager, object parentLock, IHardwareDeviceSession deviceSession, IWritableEvent bufferEvent) public AudioOutputSystem(AudioOutputManager manager, Lock parentLock, IHardwareDeviceSession deviceSession, IWritableEvent bufferEvent)
{ {
_manager = manager; _manager = manager;
_parentLock = parentLock; _parentLock = parentLock;

View file

@ -26,7 +26,7 @@ namespace Ryujinx.Audio.Renderer.Server
{ {
public class AudioRenderSystem : IDisposable public class AudioRenderSystem : IDisposable
{ {
private readonly object _lock = new(); private readonly Lock _lock = new();
private AudioRendererRenderingDevice _renderingDevice; private AudioRendererRenderingDevice _renderingDevice;
private AudioRendererExecutionMode _executionMode; private AudioRendererExecutionMode _executionMode;

View file

@ -19,12 +19,12 @@ namespace Ryujinx.Audio.Renderer.Server
/// <summary> /// <summary>
/// Lock used for session allocation. /// Lock used for session allocation.
/// </summary> /// </summary>
private readonly object _sessionLock = new(); private readonly Lock _sessionLock = new();
/// <summary> /// <summary>
/// Lock used to control the <see cref="AudioProcessor"/> running state. /// Lock used to control the <see cref="AudioProcessor"/> running state.
/// </summary> /// </summary>
private readonly object _audioProcessorLock = new(); private readonly Lock _audioProcessorLock = new();
/// <summary> /// <summary>
/// The session ids allocation table. /// The session ids allocation table.

View file

@ -1,5 +1,6 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Threading;
namespace Ryujinx.Audio.Renderer.Server.Upsampler namespace Ryujinx.Audio.Renderer.Server.Upsampler
{ {
@ -16,7 +17,7 @@ namespace Ryujinx.Audio.Renderer.Server.Upsampler
/// <summary> /// <summary>
/// Global lock of the object. /// Global lock of the object.
/// </summary> /// </summary>
private readonly object _lock = new(); private readonly Lock _lock = new();
/// <summary> /// <summary>
/// The upsamplers instances. /// The upsamplers instances.

View file

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes> <DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
</PropertyGroup> </PropertyGroup>

View file

@ -124,7 +124,7 @@ namespace Ryujinx.Common.PreciseSleep
} }
} }
private readonly object _lock = new(); private readonly Lock _lock = new();
private readonly List<NanosleepThread> _threads = new(); private readonly List<NanosleepThread> _threads = new();
private readonly List<NanosleepThread> _active = new(); private readonly List<NanosleepThread> _active = new();
private readonly Stack<NanosleepThread> _free = new(); private readonly Stack<NanosleepThread> _free = new();

View file

@ -50,7 +50,7 @@ namespace Ryujinx.Common.SystemInterop
private long _lastTicks = PerformanceCounter.ElapsedTicks; private long _lastTicks = PerformanceCounter.ElapsedTicks;
private long _lastId; private long _lastId;
private readonly object _lock = new(); private readonly Lock _lock = new();
private readonly List<WaitingObject> _waitingObjects = new(); private readonly List<WaitingObject> _waitingObjects = new();
private WindowsGranularTimer() private WindowsGranularTimer()

View file

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefineConstants Condition=" '$(ExtraDefineConstants)' != '' ">$(DefineConstants);$(ExtraDefineConstants)</DefineConstants> <DefineConstants Condition=" '$(ExtraDefineConstants)' != '' ">$(DefineConstants);$(ExtraDefineConstants)</DefineConstants>
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes> <DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>

View file

@ -1,6 +1,7 @@
using Ryujinx.Memory; using Ryujinx.Memory;
using System; using System;
using System.Runtime.Versioning; using System.Runtime.Versioning;
using System.Threading;
namespace Ryujinx.Cpu.AppleHv namespace Ryujinx.Cpu.AppleHv
{ {
@ -12,7 +13,7 @@ namespace Ryujinx.Cpu.AppleHv
private static int _addressSpaces; private static int _addressSpaces;
private static HvIpaAllocator _ipaAllocator; private static HvIpaAllocator _ipaAllocator;
private static readonly object _lock = new(); private static readonly Lock _lock = new();
public static (ulong, HvIpaAllocator) CreateAddressSpace(MemoryBlock block) public static (ulong, HvIpaAllocator) CreateAddressSpace(MemoryBlock block)
{ {

View file

@ -478,7 +478,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
bool skipContext, bool skipContext,
int spillBaseOffset, int spillBaseOffset,
int? resultRegister, int? resultRegister,
params ulong[] callArgs) params ReadOnlySpan<ulong> callArgs)
{ {
uint resultMask = 0u; uint resultMask = 0u;

View file

@ -417,7 +417,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
nint funcPtr, nint funcPtr,
int spillBaseOffset, int spillBaseOffset,
int? resultRegister, int? resultRegister,
params ulong[] callArgs) params ReadOnlySpan<ulong> callArgs)
{ {
uint resultMask = 0u; uint resultMask = 0u;

View file

@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Versioning; using System.Runtime.Versioning;
using System.Threading;
namespace Ryujinx.Cpu.LightningJit.Cache namespace Ryujinx.Cpu.LightningJit.Cache
{ {
@ -23,7 +24,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache
private static readonly List<CacheEntry> _cacheEntries = new(); private static readonly List<CacheEntry> _cacheEntries = new();
private static readonly object _lock = new(); private static readonly Lock _lock = new();
private static bool _initialized; private static bool _initialized;
[SupportedOSPlatform("windows")] [SupportedOSPlatform("windows")]

View file

@ -4,6 +4,7 @@ using Ryujinx.Memory;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Threading;
namespace Ryujinx.Cpu.LightningJit.Cache namespace Ryujinx.Cpu.LightningJit.Cache
{ {
@ -104,7 +105,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache
private readonly MemoryCache _sharedCache; private readonly MemoryCache _sharedCache;
private readonly MemoryCache _localCache; private readonly MemoryCache _localCache;
private readonly PageAlignedRangeList _pendingMap; private readonly PageAlignedRangeList _pendingMap;
private readonly object _lock; private readonly Lock _lock = new();
class ThreadLocalCacheEntry class ThreadLocalCacheEntry
{ {
@ -137,7 +138,6 @@ namespace Ryujinx.Cpu.LightningJit.Cache
_sharedCache = new(allocator, SharedCacheSize); _sharedCache = new(allocator, SharedCacheSize);
_localCache = new(allocator, LocalCacheSize); _localCache = new(allocator, LocalCacheSize);
_pendingMap = new(_sharedCache.ReprotectAsRx, RegisterFunction); _pendingMap = new(_sharedCache.ReprotectAsRx, RegisterFunction);
_lock = new();
} }
public unsafe nint Map(nint framePointer, ReadOnlySpan<byte> code, ulong guestAddress, ulong guestSize) public unsafe nint Map(nint framePointer, ReadOnlySpan<byte> code, ulong guestAddress, ulong guestSize)

View file

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes> <DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
</PropertyGroup> </PropertyGroup>

View file

@ -5,6 +5,7 @@ using System;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading;
namespace Ryujinx.Cpu.Signal namespace Ryujinx.Cpu.Signal
{ {
@ -59,7 +60,7 @@ namespace Ryujinx.Cpu.Signal
private static MemoryBlock _codeBlock; private static MemoryBlock _codeBlock;
private static readonly object _lock = new(); private static readonly Lock _lock = new();
private static bool _initialized; private static bool _initialized;
static NativeSignalHandler() static NativeSignalHandler()

View file

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes> <DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
</PropertyGroup> </PropertyGroup>

View file

@ -58,7 +58,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
public uint ProgramCount { get; set; } = 0; public uint ProgramCount { get; set; } = 0;
private Action _interruptAction; private Action _interruptAction;
private readonly object _interruptLock = new(); private readonly Lock _interruptLock = new();
public event EventHandler<ScreenCaptureImageInfo> ScreenCaptured; public event EventHandler<ScreenCaptureImageInfo> ScreenCaptured;

View file

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes> <DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
</PropertyGroup> </PropertyGroup>

View file

@ -8,6 +8,7 @@ using Ryujinx.Graphics.Texture;
using Ryujinx.Memory.Range; using Ryujinx.Memory.Range;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading; using System.Threading;
namespace Ryujinx.Graphics.Gpu.Image namespace Ryujinx.Graphics.Gpu.Image
@ -998,7 +999,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{ {
bool dataOverlaps = texture.DataOverlaps(overlap, compatibility); bool dataOverlaps = texture.DataOverlaps(overlap, compatibility);
if (!overlap.IsView && dataOverlaps && !incompatibleOverlaps.Exists(incompatible => incompatible.Group == overlap.Group)) if (!overlap.IsView && dataOverlaps && !incompatibleOverlaps.Any(incompatible => incompatible.Group == overlap.Group))
{ {
incompatibleOverlaps.Add(new TextureIncompatibleOverlap(overlap.Group, compatibility)); incompatibleOverlaps.Add(new TextureIncompatibleOverlap(overlap.Group, compatibility));
} }

View file

@ -7,6 +7,7 @@ using Ryujinx.Memory.Range;
using Ryujinx.Memory.Tracking; using Ryujinx.Memory.Tracking;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace Ryujinx.Graphics.Gpu.Image namespace Ryujinx.Graphics.Gpu.Image
@ -1555,7 +1556,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="copy">True if the overlap should register copy dependencies</param> /// <param name="copy">True if the overlap should register copy dependencies</param>
public void RegisterIncompatibleOverlap(TextureIncompatibleOverlap other, bool copy) public void RegisterIncompatibleOverlap(TextureIncompatibleOverlap other, bool copy)
{ {
if (!_incompatibleOverlaps.Exists(overlap => overlap.Group == other.Group)) if (!_incompatibleOverlaps.Any(overlap => overlap.Group == other.Group))
{ {
if (copy && other.Compatibility == TextureViewCompatibility.LayoutIncompatible) if (copy && other.Compatibility == TextureViewCompatibility.LayoutIncompatible)
{ {
@ -1701,3 +1702,4 @@ namespace Ryujinx.Graphics.Gpu.Image
} }
} }
} }

View file

@ -721,7 +721,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="format">The format of the texture</param> /// <param name="format">The format of the texture</param>
/// <param name="components">The texture swizzle components</param> /// <param name="components">The texture swizzle components</param>
/// <returns>The depth-stencil mode</returns> /// <returns>The depth-stencil mode</returns>
private static DepthStencilMode GetDepthStencilMode(Format format, params SwizzleComponent[] components) private static DepthStencilMode GetDepthStencilMode(Format format, params ReadOnlySpan<SwizzleComponent> components)
{ {
// R = Depth, G = Stencil. // R = Depth, G = Stencil.
// On 24-bits depth formats, this is inverted (Stencil is R etc). // On 24-bits depth formats, this is inverted (Stencil is R etc).

View file

@ -2,6 +2,7 @@ using Ryujinx.Common.Pools;
using Ryujinx.Memory.Range; using Ryujinx.Memory.Range;
using System; using System;
using System.Linq; using System.Linq;
using System.Threading;
namespace Ryujinx.Graphics.Gpu.Memory namespace Ryujinx.Graphics.Gpu.Memory
{ {
@ -76,7 +77,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
private BufferMigration _source; private BufferMigration _source;
private BufferModifiedRangeList _migrationTarget; private BufferModifiedRangeList _migrationTarget;
private readonly object _lock = new(); private readonly Lock _lock = new();
/// <summary> /// <summary>
/// Whether the modified range list has any entries or not. /// Whether the modified range list has any entries or not.

View file

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes> <DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes> <DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes> <DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes> <DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes> <DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
</PropertyGroup> </PropertyGroup>

View file

@ -19,7 +19,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries
private ulong _accumulatedCounter; private ulong _accumulatedCounter;
private int _waiterCount; private int _waiterCount;
private readonly object _lock = new(); private readonly Lock _lock = new();
private readonly Queue<BufferedQuery> _queryPool; private readonly Queue<BufferedQuery> _queryPool;
private readonly AutoResetEvent _queuedEvent = new(false); private readonly AutoResetEvent _queuedEvent = new(false);

View file

@ -24,7 +24,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries
private bool _hostAccessReserved = false; private bool _hostAccessReserved = false;
private int _refCount = 1; // Starts with a reference from the counter queue. private int _refCount = 1; // Starts with a reference from the counter queue.
private readonly object _lock = new(); private readonly Lock _lock = new();
private ulong _result = ulong.MaxValue; private ulong _result = ulong.MaxValue;
private double _divisor = 1f; private double _divisor = 1f;

View file

@ -2,6 +2,7 @@ using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.OpenGL.Image; using Ryujinx.Graphics.OpenGL.Image;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading;
namespace Ryujinx.Graphics.OpenGL namespace Ryujinx.Graphics.OpenGL
{ {
@ -19,7 +20,7 @@ namespace Ryujinx.Graphics.OpenGL
{ {
private const int DisposedLiveFrames = 2; private const int DisposedLiveFrames = 2;
private readonly object _lock = new(); private readonly Lock _lock = new();
private readonly Dictionary<TextureCreateInfo, List<DisposedTexture>> _textures = new(); private readonly Dictionary<TextureCreateInfo, List<DisposedTexture>> _textures = new();
/// <summary> /// <summary>

View file

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes> <DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
</PropertyGroup> </PropertyGroup>

View file

@ -4,6 +4,7 @@ using Ryujinx.Graphics.Shader.StructuredIr;
using Ryujinx.Graphics.Shader.Translation; using Ryujinx.Graphics.Shader.Translation;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading;
using static Spv.Specification; using static Spv.Specification;
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
@ -19,13 +20,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
private const int GeneratorPoolCount = 1; private const int GeneratorPoolCount = 1;
private static readonly ObjectPool<SpvInstructionPool> _instructionPool; private static readonly ObjectPool<SpvInstructionPool> _instructionPool;
private static readonly ObjectPool<SpvLiteralIntegerPool> _integerPool; private static readonly ObjectPool<SpvLiteralIntegerPool> _integerPool;
private static readonly object _poolLock; private static readonly Lock _poolLock = new();
static SpirvGenerator() static SpirvGenerator()
{ {
_instructionPool = new(() => new SpvInstructionPool(), GeneratorPoolCount); _instructionPool = new(() => new SpvInstructionPool(), GeneratorPoolCount);
_integerPool = new(() => new SpvLiteralIntegerPool(), GeneratorPoolCount); _integerPool = new(() => new SpvLiteralIntegerPool(), GeneratorPoolCount);
_poolLock = new object();
} }
private const HelperFunctionsMask NeedsInvocationIdMask = HelperFunctionsMask.SwizzleAdd; private const HelperFunctionsMask NeedsInvocationIdMask = HelperFunctionsMask.SwizzleAdd;

View file

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes> <DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
</PropertyGroup> </PropertyGroup>

View file

@ -1,6 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes> <DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes> <DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
</PropertyGroup> </PropertyGroup>

View file

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes> <DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
</PropertyGroup> </PropertyGroup>

View file

@ -25,7 +25,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
bool useBackground = _gd.BackgroundQueue.Handle != 0 && _gd.Vendor != Vendor.Amd; bool useBackground = _gd.BackgroundQueue.Handle != 0 && _gd.Vendor != Vendor.Amd;
Queue queue = useBackground ? _gd.BackgroundQueue : _gd.Queue; Queue queue = useBackground ? _gd.BackgroundQueue : _gd.Queue;
object queueLock = useBackground ? _gd.BackgroundQueueLock : _gd.QueueLock; Lock queueLock = useBackground ? _gd.BackgroundQueueLock : _gd.QueueLock;
lock (queueLock) lock (queueLock)
{ {

View file

@ -17,7 +17,7 @@ namespace Ryujinx.Graphics.Vulkan
private readonly Vk _api; private readonly Vk _api;
private readonly Device _device; private readonly Device _device;
private readonly Queue _queue; private readonly Queue _queue;
private readonly object _queueLock; private readonly Lock _queueLock;
private readonly bool _concurrentFenceWaitUnsupported; private readonly bool _concurrentFenceWaitUnsupported;
private readonly CommandPool _pool; private readonly CommandPool _pool;
private readonly Thread _owner; private readonly Thread _owner;
@ -63,7 +63,7 @@ namespace Ryujinx.Graphics.Vulkan
Vk api, Vk api,
Device device, Device device,
Queue queue, Queue queue,
object queueLock, Lock queueLock,
uint queueFamilyIndex, uint queueFamilyIndex,
bool concurrentFenceWaitUnsupported, bool concurrentFenceWaitUnsupported,
bool isLight = false) bool isLight = false)

View file

@ -68,7 +68,7 @@ namespace Ryujinx.Graphics.Vulkan
_optimalTable = new FormatFeatureFlags[totalFormats]; _optimalTable = new FormatFeatureFlags[totalFormats];
} }
public bool BufferFormatsSupport(FormatFeatureFlags flags, params Format[] formats) public bool BufferFormatsSupport(FormatFeatureFlags flags, params ReadOnlySpan<Format> formats)
{ {
foreach (Format format in formats) foreach (Format format in formats)
{ {
@ -81,7 +81,7 @@ namespace Ryujinx.Graphics.Vulkan
return true; return true;
} }
public bool OptimalFormatsSupport(FormatFeatureFlags flags, params Format[] formats) public bool OptimalFormatsSupport(FormatFeatureFlags flags, params ReadOnlySpan<Format> formats)
{ {
foreach (Format format in formats) foreach (Format format in formats)
{ {

View file

@ -5,6 +5,7 @@ using Silk.NET.Vulkan;
using Silk.NET.Vulkan.Extensions.EXT; using Silk.NET.Vulkan.Extensions.EXT;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading;
namespace Ryujinx.Graphics.Vulkan namespace Ryujinx.Graphics.Vulkan
{ {
@ -31,7 +32,7 @@ namespace Ryujinx.Graphics.Vulkan
private readonly Vk _api; private readonly Vk _api;
private readonly ExtExternalMemoryHost _hostMemoryApi; private readonly ExtExternalMemoryHost _hostMemoryApi;
private readonly Device _device; private readonly Device _device;
private readonly object _lock = new(); private readonly Lock _lock = new();
private readonly List<HostMemoryAllocation> _allocations; private readonly List<HostMemoryAllocation> _allocations;
private readonly IntervalTree<ulong, HostMemoryAllocation> _allocationTree; private readonly IntervalTree<ulong, HostMemoryAllocation> _allocationTree;

View file

@ -24,7 +24,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries
private ulong _accumulatedCounter; private ulong _accumulatedCounter;
private int _waiterCount; private int _waiterCount;
private readonly object _lock = new(); private readonly Lock _lock = new();
private readonly Queue<BufferedQuery> _queryPool; private readonly Queue<BufferedQuery> _queryPool;
private readonly AutoResetEvent _queuedEvent = new(false); private readonly AutoResetEvent _queuedEvent = new(false);

View file

@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries
private bool _hostAccessReserved; private bool _hostAccessReserved;
private int _refCount = 1; // Starts with a reference from the counter queue. private int _refCount = 1; // Starts with a reference from the counter queue.
private readonly object _lock = new(); private readonly Lock _lock = new();
private ulong _result = ulong.MaxValue; private ulong _result = ulong.MaxValue;
private double _divisor = 1f; private double _divisor = 1f;

View file

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes> <DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
</PropertyGroup> </PropertyGroup>

View file

@ -5,6 +5,7 @@ using shaderc;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Ryujinx.Graphics.Vulkan namespace Ryujinx.Graphics.Vulkan
@ -13,7 +14,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
// The shaderc.net dependency's Options constructor and dispose are not thread safe. // The shaderc.net dependency's Options constructor and dispose are not thread safe.
// Take this lock when using them. // Take this lock when using them.
private static readonly object _shaderOptionsLock = new(); private static readonly Lock _shaderOptionsLock = new();
private static readonly nint _ptrMainEntryPointName = Marshal.StringToHGlobalAnsi("main"); private static readonly nint _ptrMainEntryPointName = Marshal.StringToHGlobalAnsi("main");

View file

@ -11,6 +11,7 @@ using Silk.NET.Vulkan.Extensions.KHR;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading;
using Format = Ryujinx.Graphics.GAL.Format; using Format = Ryujinx.Graphics.GAL.Format;
using PrimitiveTopology = Ryujinx.Graphics.GAL.PrimitiveTopology; using PrimitiveTopology = Ryujinx.Graphics.GAL.PrimitiveTopology;
using SamplerCreateInfo = Ryujinx.Graphics.GAL.SamplerCreateInfo; using SamplerCreateInfo = Ryujinx.Graphics.GAL.SamplerCreateInfo;
@ -45,8 +46,8 @@ namespace Ryujinx.Graphics.Vulkan
internal uint QueueFamilyIndex { get; private set; } internal uint QueueFamilyIndex { get; private set; }
internal Queue Queue { get; private set; } internal Queue Queue { get; private set; }
internal Queue BackgroundQueue { get; private set; } internal Queue BackgroundQueue { get; private set; }
internal object BackgroundQueueLock { get; private set; } internal Lock BackgroundQueueLock { get; private set; }
internal object QueueLock { get; private set; } internal Lock QueueLock { get; private set; }
internal MemoryAllocator MemoryAllocator { get; private set; } internal MemoryAllocator MemoryAllocator { get; private set; }
internal HostMemoryAllocator HostMemoryAllocator { get; private set; } internal HostMemoryAllocator HostMemoryAllocator { get; private set; }
@ -163,7 +164,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
Api.GetDeviceQueue(_device, queueFamilyIndex, 1, out var backgroundQueue); Api.GetDeviceQueue(_device, queueFamilyIndex, 1, out var backgroundQueue);
BackgroundQueue = backgroundQueue; BackgroundQueue = backgroundQueue;
BackgroundQueueLock = new object(); BackgroundQueueLock = new();
} }
PhysicalDeviceProperties2 properties2 = new() PhysicalDeviceProperties2 properties2 = new()
@ -496,7 +497,7 @@ namespace Ryujinx.Graphics.Vulkan
Api.GetDeviceQueue(_device, queueFamilyIndex, 0, out var queue); Api.GetDeviceQueue(_device, queueFamilyIndex, 0, out var queue);
Queue = queue; Queue = queue;
QueueLock = new object(); QueueLock = new();
LoadFeatures(maxQueueCount, queueFamilyIndex); LoadFeatures(maxQueueCount, queueFamilyIndex);

View file

@ -22,6 +22,7 @@ using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading;
using Path = System.IO.Path; using Path = System.IO.Path;
namespace Ryujinx.HLE.FileSystem namespace Ryujinx.HLE.FileSystem
@ -55,7 +56,7 @@ namespace Ryujinx.HLE.FileSystem
private readonly VirtualFileSystem _virtualFileSystem; private readonly VirtualFileSystem _virtualFileSystem;
private readonly object _lock = new(); private readonly Lock _lock = new();
public ContentManager(VirtualFileSystem virtualFileSystem) public ContentManager(VirtualFileSystem virtualFileSystem)
{ {
@ -396,7 +397,7 @@ namespace Ryujinx.HLE.FileSystem
if (locationList != null) if (locationList != null)
{ {
LocationEntry entry = LocationEntry entry =
locationList.ToList().Find(x => x.TitleId == titleId && x.ContentType == contentType); locationList.ToList().FirstOrDefault(x => x.TitleId == titleId && x.ContentType == contentType);
if (entry.ContentPath != null) if (entry.ContentPath != null)
{ {
@ -424,7 +425,7 @@ namespace Ryujinx.HLE.FileSystem
{ {
LinkedList<LocationEntry> locationList = _locationEntries[storageId]; LinkedList<LocationEntry> locationList = _locationEntries[storageId];
return locationList.ToList().Find(x => x.TitleId == titleId && x.ContentType == contentType); return locationList.ToList().FirstOrDefault(x => x.TitleId == titleId && x.ContentType == contentType);
} }
public void InstallFirmware(string firmwareSource) public void InstallFirmware(string firmwareSource)
@ -719,7 +720,7 @@ namespace Ryujinx.HLE.FileSystem
if (updateNcas.TryGetValue(SystemUpdateTitleId, out var ncaEntry)) if (updateNcas.TryGetValue(SystemUpdateTitleId, out var ncaEntry))
{ {
string metaPath = ncaEntry.Find(x => x.type == NcaContentType.Meta).path; string metaPath = ncaEntry.FirstOrDefault(x => x.type == NcaContentType.Meta).path;
CnmtContentMetaEntry[] metaEntries = null; CnmtContentMetaEntry[] metaEntries = null;
@ -755,7 +756,7 @@ namespace Ryujinx.HLE.FileSystem
if (updateNcas.TryGetValue(SystemVersionTitleId, out var updateNcasItem)) if (updateNcas.TryGetValue(SystemVersionTitleId, out var updateNcasItem))
{ {
string versionEntry = updateNcasItem.Find(x => x.type != NcaContentType.Meta).path; string versionEntry = updateNcasItem.FirstOrDefault(x => x.type != NcaContentType.Meta).path;
using Stream ncaStream = GetZipStream(archive.GetEntry(versionEntry)); using Stream ncaStream = GetZipStream(archive.GetEntry(versionEntry));
Nca nca = new(_virtualFileSystem.KeySet, ncaStream.AsStorage()); Nca nca = new(_virtualFileSystem.KeySet, ncaStream.AsStorage());
@ -774,9 +775,9 @@ namespace Ryujinx.HLE.FileSystem
{ {
if (updateNcas.TryGetValue(metaEntry.TitleId, out ncaEntry)) if (updateNcas.TryGetValue(metaEntry.TitleId, out ncaEntry))
{ {
metaPath = ncaEntry.Find(x => x.type == NcaContentType.Meta).path; metaPath = ncaEntry.FirstOrDefault(x => x.type == NcaContentType.Meta).path;
string contentPath = ncaEntry.Find(x => x.type != NcaContentType.Meta).path; string contentPath = ncaEntry.FirstOrDefault(x => x.type != NcaContentType.Meta).path;
// Nintendo in 9.0.0, removed PPC and only kept the meta nca of it. // Nintendo in 9.0.0, removed PPC and only kept the meta nca of it.
// This is a perfect valid case, so we should just ignore the missing content nca and continue. // This is a perfect valid case, so we should just ignore the missing content nca and continue.
@ -915,8 +916,8 @@ namespace Ryujinx.HLE.FileSystem
{ {
if (updateNcas.TryGetValue(metaEntry.TitleId, out var ncaEntry)) if (updateNcas.TryGetValue(metaEntry.TitleId, out var ncaEntry))
{ {
string metaNcaPath = ncaEntry.Find(x => x.type == NcaContentType.Meta).path; string metaNcaPath = ncaEntry.FirstOrDefault(x => x.type == NcaContentType.Meta).path;
string contentPath = ncaEntry.Find(x => x.type != NcaContentType.Meta).path; string contentPath = ncaEntry.FirstOrDefault(x => x.type != NcaContentType.Meta).path;
// Nintendo in 9.0.0, removed PPC and only kept the meta nca of it. // Nintendo in 9.0.0, removed PPC and only kept the meta nca of it.
// This is a perfect valid case, so we should just ignore the missing content nca and continue. // This is a perfect valid case, so we should just ignore the missing content nca and continue.

View file

@ -46,7 +46,7 @@ namespace Ryujinx.HLE.FileSystem
continue; continue;
} }
string ncaId = BitConverter.ToString(entry.NcaId).Replace("-", null).ToLower(); string ncaId = Convert.ToHexStringLower(entry.NcaId).Replace("-", null);
Nca nca = _pfs.GetNca(keySet, $"/{ncaId}.nca"); Nca nca = _pfs.GetNca(keySet, $"/{ncaId}.nca");
if (nca.GetProgramIndex() == programIndex) if (nca.GetProgramIndex() == programIndex)

View file

@ -14,6 +14,7 @@ using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading;
namespace Ryujinx.HLE.HOS.Applets namespace Ryujinx.HLE.HOS.Applets
{ {
@ -62,7 +63,7 @@ namespace Ryujinx.HLE.HOS.Applets
private bool _canAcceptController = false; private bool _canAcceptController = false;
private KeyboardInputMode _inputMode = KeyboardInputMode.ControllerAndKeyboard; private KeyboardInputMode _inputMode = KeyboardInputMode.ControllerAndKeyboard;
private readonly object _lock = new(); private readonly Lock _lock = new();
public event EventHandler AppletStateChanged; public event EventHandler AppletStateChanged;

View file

@ -6,6 +6,7 @@ using System.Diagnostics;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading;
namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
{ {
@ -21,7 +22,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
const string CancelText = "Cancel"; const string CancelText = "Cancel";
const string ControllerToggleText = "Toggle input"; const string ControllerToggleText = "Toggle input";
private readonly object _bufferLock = new(); private readonly Lock _bufferLock = new();
private RenderingSurfaceInfo _surfaceInfo = null; private RenderingSurfaceInfo _surfaceInfo = null;
private SKImageInfo _imageInfo; private SKImageInfo _imageInfo;

View file

@ -27,7 +27,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
private TRef<bool> _cancelled = null; private TRef<bool> _cancelled = null;
private Thread _thread = null; private Thread _thread = null;
private readonly object _lock = new(); private readonly Lock _lock = new();
public bool IsRunning public bool IsRunning
{ {

View file

@ -16,6 +16,8 @@ using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemA
using Ryujinx.HLE.HOS.Services.Apm; using Ryujinx.HLE.HOS.Services.Apm;
using Ryujinx.HLE.HOS.Services.Caps; using Ryujinx.HLE.HOS.Services.Caps;
using Ryujinx.HLE.HOS.Services.Mii; using Ryujinx.HLE.HOS.Services.Mii;
using Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption;
using Ryujinx.HLE.HOS.Services.Nfc.Nfp;
using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager; using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager;
using Ryujinx.HLE.HOS.Services.Nv; using Ryujinx.HLE.HOS.Services.Nv;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl;
@ -337,6 +339,11 @@ namespace Ryujinx.HLE.HOS
public void ScanAmiibo(int nfpDeviceId, string amiiboId, bool useRandomUuid) public void ScanAmiibo(int nfpDeviceId, string amiiboId, bool useRandomUuid)
{ {
if (VirtualAmiibo.ApplicationBytes.Length > 0)
{
VirtualAmiibo.ApplicationBytes = new byte[0];
VirtualAmiibo.InputBin = string.Empty;
}
if (NfpDevices[nfpDeviceId].State == NfpDeviceState.SearchingForTag) if (NfpDevices[nfpDeviceId].State == NfpDeviceState.SearchingForTag)
{ {
NfpDevices[nfpDeviceId].State = NfpDeviceState.TagFound; NfpDevices[nfpDeviceId].State = NfpDeviceState.TagFound;
@ -344,6 +351,22 @@ namespace Ryujinx.HLE.HOS
NfpDevices[nfpDeviceId].UseRandomUuid = useRandomUuid; NfpDevices[nfpDeviceId].UseRandomUuid = useRandomUuid;
} }
} }
public void ScanAmiiboFromBin(string path)
{
VirtualAmiibo.InputBin = path;
if (VirtualAmiibo.ApplicationBytes.Length > 0)
{
VirtualAmiibo.ApplicationBytes = new byte[0];
}
byte[] encryptedData = File.ReadAllBytes(path);
VirtualAmiiboFile newFile = AmiiboBinReader.ReadBinFile(encryptedData);
if (SearchingForAmiibo(out int nfpDeviceId))
{
NfpDevices[nfpDeviceId].State = NfpDeviceState.TagFound;
NfpDevices[nfpDeviceId].AmiiboId = newFile.AmiiboId;
NfpDevices[nfpDeviceId].UseRandomUuid = false;
}
}
public bool SearchingForAmiibo(out int nfpDeviceId) public bool SearchingForAmiibo(out int nfpDeviceId)
{ {

View file

@ -2,6 +2,7 @@ using Ryujinx.Common;
using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.Horizon.Common; using Ryujinx.Horizon.Common;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel.Common namespace Ryujinx.HLE.HOS.Kernel.Common
{ {
@ -14,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
private readonly long[] _current2; private readonly long[] _current2;
private readonly long[] _peak; private readonly long[] _peak;
private readonly object _lock; private readonly object _lock = new();
private readonly LinkedList<KThread> _waitingThreads; private readonly LinkedList<KThread> _waitingThreads;
@ -27,8 +28,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
_current2 = new long[(int)LimitableResource.Count]; _current2 = new long[(int)LimitableResource.Count];
_peak = new long[(int)LimitableResource.Count]; _peak = new long[(int)LimitableResource.Count];
_lock = new object();
_waitingThreads = new LinkedList<KThread>(); _waitingThreads = new LinkedList<KThread>();
} }

View file

@ -4,6 +4,7 @@ using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.Horizon.Common; using Ryujinx.Horizon.Common;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel.Memory namespace Ryujinx.HLE.HOS.Kernel.Memory
{ {
@ -11,7 +12,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
{ {
public KProcess Owner { get; private set; } public KProcess Owner { get; private set; }
private readonly KPageList _pageList; private readonly KPageList _pageList;
private readonly object _lock; private readonly Lock _lock = new();
private ulong _address; private ulong _address;
private bool _isOwnerMapped; private bool _isOwnerMapped;
private bool _isMapped; private bool _isMapped;
@ -19,7 +20,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
public KCodeMemory(KernelContext context) : base(context) public KCodeMemory(KernelContext context) : base(context)
{ {
_pageList = new KPageList(); _pageList = new KPageList();
_lock = new object();
} }
public Result Initialize(ulong address, ulong size) public Result Initialize(ulong address, ulong size)

View file

@ -40,8 +40,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
public ProcessState State { get; private set; } public ProcessState State { get; private set; }
private readonly object _processLock = new(); private readonly Lock _processLock = new();
private readonly object _threadingLock = new(); private readonly Lock _threadingLock = new();
public KAddressArbiter AddressArbiter { get; private set; } public KAddressArbiter AddressArbiter { get; private set; }

View file

@ -200,7 +200,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
WakeThreads(_condVarThreads, count, TryAcquireMutex, x => x.CondVarAddress == address); WakeThreads(_condVarThreads, count, TryAcquireMutex, x => x.CondVarAddress == address);
if (!_condVarThreads.Exists(x => x.CondVarAddress == address)) if (!_condVarThreads.Any(x => x.CondVarAddress == address))
{ {
KernelTransfer.KernelToUser(address, 0); KernelTransfer.KernelToUser(address, 0);
} }

View file

@ -5,7 +5,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
class KCriticalSection class KCriticalSection
{ {
private readonly KernelContext _context; private readonly KernelContext _context;
private readonly object _lock; private readonly object _lock = new();
private int _recursionCount; private int _recursionCount;
public object Lock => _lock; public object Lock => _lock;
@ -13,7 +13,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
public KCriticalSection(KernelContext context) public KCriticalSection(KernelContext context)
{ {
_context = context; _context = context;
_lock = new object();
} }
public void Enter() public void Enter()

View file

@ -112,7 +112,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
public bool WaitingInArbitration { get; set; } public bool WaitingInArbitration { get; set; }
private readonly object _activityOperationLock = new(); private readonly Lock _activityOperationLock = new();
public KThread(KernelContext context) : base(context) public KThread(KernelContext context) : base(context)
{ {

View file

@ -168,7 +168,7 @@ namespace Ryujinx.HLE.HOS
if (StrEquals(RomfsDir, modDir.Name)) if (StrEquals(RomfsDir, modDir.Name))
{ {
var modData = modMetadata.Mods.Find(x => modDir.FullName.Contains(x.Path)); var modData = modMetadata.Mods.FirstOrDefault(x => modDir.FullName.Contains(x.Path));
var enabled = modData?.Enabled ?? true; var enabled = modData?.Enabled ?? true;
mods.RomfsDirs.Add(mod = new Mod<DirectoryInfo>(dir.Name, modDir, enabled)); mods.RomfsDirs.Add(mod = new Mod<DirectoryInfo>(dir.Name, modDir, enabled));
@ -176,7 +176,7 @@ namespace Ryujinx.HLE.HOS
} }
else if (StrEquals(ExefsDir, modDir.Name)) else if (StrEquals(ExefsDir, modDir.Name))
{ {
var modData = modMetadata.Mods.Find(x => modDir.FullName.Contains(x.Path)); var modData = modMetadata.Mods.FirstOrDefault(x => modDir.FullName.Contains(x.Path));
var enabled = modData?.Enabled ?? true; var enabled = modData?.Enabled ?? true;
mods.ExefsDirs.Add(mod = new Mod<DirectoryInfo>(dir.Name, modDir, enabled)); mods.ExefsDirs.Add(mod = new Mod<DirectoryInfo>(dir.Name, modDir, enabled));
@ -275,7 +275,7 @@ namespace Ryujinx.HLE.HOS
var fsFile = new FileInfo(Path.Combine(applicationDir.FullName, RomfsContainer)); var fsFile = new FileInfo(Path.Combine(applicationDir.FullName, RomfsContainer));
if (fsFile.Exists) if (fsFile.Exists)
{ {
var modData = modMetadata.Mods.Find(x => fsFile.FullName.Contains(x.Path)); var modData = modMetadata.Mods.FirstOrDefault(x => fsFile.FullName.Contains(x.Path));
var enabled = modData == null || modData.Enabled; var enabled = modData == null || modData.Enabled;
mods.RomfsContainers.Add(new Mod<FileInfo>($"<{applicationDir.Name} RomFs>", fsFile, enabled)); mods.RomfsContainers.Add(new Mod<FileInfo>($"<{applicationDir.Name} RomFs>", fsFile, enabled));
@ -284,7 +284,7 @@ namespace Ryujinx.HLE.HOS
fsFile = new FileInfo(Path.Combine(applicationDir.FullName, ExefsContainer)); fsFile = new FileInfo(Path.Combine(applicationDir.FullName, ExefsContainer));
if (fsFile.Exists) if (fsFile.Exists)
{ {
var modData = modMetadata.Mods.Find(x => fsFile.FullName.Contains(x.Path)); var modData = modMetadata.Mods.FirstOrDefault(x => fsFile.FullName.Contains(x.Path));
var enabled = modData == null || modData.Enabled; var enabled = modData == null || modData.Enabled;
mods.ExefsContainers.Add(new Mod<FileInfo>($"<{applicationDir.Name} ExeFs>", fsFile, enabled)); mods.ExefsContainers.Add(new Mod<FileInfo>($"<{applicationDir.Name} ExeFs>", fsFile, enabled));
@ -403,7 +403,7 @@ namespace Ryujinx.HLE.HOS
} }
// Assumes searchDirPaths don't overlap // Assumes searchDirPaths don't overlap
private static void CollectMods(Dictionary<ulong, ModCache> modCaches, PatchCache patches, params string[] searchDirPaths) private static void CollectMods(Dictionary<ulong, ModCache> modCaches, PatchCache patches, params ReadOnlySpan<string> searchDirPaths)
{ {
static bool IsPatchesDir(string name) => StrEquals(AmsNsoPatchDir, name) || static bool IsPatchesDir(string name) => StrEquals(AmsNsoPatchDir, name) ||
StrEquals(AmsNroPatchDir, name) || StrEquals(AmsNroPatchDir, name) ||
@ -453,7 +453,7 @@ namespace Ryujinx.HLE.HOS
patches.Initialized = true; patches.Initialized = true;
} }
public void CollectMods(IEnumerable<ulong> applications, params string[] searchDirPaths) public void CollectMods(IEnumerable<ulong> applications, params ReadOnlySpan<string> searchDirPaths)
{ {
Clear(); Clear();
@ -680,7 +680,7 @@ namespace Ryujinx.HLE.HOS
ApplyProgramPatches(nroPatches, 0, nro); ApplyProgramPatches(nroPatches, 0, nro);
} }
internal bool ApplyNsoPatches(ulong applicationId, params IExecutable[] programs) internal bool ApplyNsoPatches(ulong applicationId, params ReadOnlySpan<IExecutable> programs)
{ {
IEnumerable<Mod<DirectoryInfo>> nsoMods = _patches.NsoPatches; IEnumerable<Mod<DirectoryInfo>> nsoMods = _patches.NsoPatches;
@ -744,7 +744,7 @@ namespace Ryujinx.HLE.HOS
} }
} }
private static bool ApplyProgramPatches(IEnumerable<Mod<DirectoryInfo>> mods, int protectedOffset, params IExecutable[] programs) private static bool ApplyProgramPatches(IEnumerable<Mod<DirectoryInfo>> mods, int protectedOffset, params ReadOnlySpan<IExecutable> programs)
{ {
int count = 0; int count = 0;
@ -755,12 +755,18 @@ namespace Ryujinx.HLE.HOS
patches[i] = new MemPatch(); patches[i] = new MemPatch();
} }
var buildIds = programs.Select(p => p switch var buildIds = new List<string>(programs.Length);
foreach (IExecutable p in programs)
{
var buildId = p switch
{ {
NsoExecutable nso => Convert.ToHexString(nso.BuildId.ItemsRo.ToArray()).TrimEnd('0'), NsoExecutable nso => Convert.ToHexString(nso.BuildId.ItemsRo.ToArray()).TrimEnd('0'),
NroExecutable nro => Convert.ToHexString(nro.Header.BuildId).TrimEnd('0'), NroExecutable nro => Convert.ToHexString(nro.Header.BuildId).TrimEnd('0'),
_ => string.Empty, _ => string.Empty,
}).ToList(); };
buildIds.Add(buildId);
}
int GetIndex(string buildId) => buildIds.FindIndex(id => id == buildId); // O(n) but list is small int GetIndex(string buildId) => buildIds.FindIndex(id => id == buildId); // O(n) but list is small

View file

@ -4,6 +4,7 @@ using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy.Types; using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy.Types;
using Ryujinx.Horizon.Common; using Ryujinx.Horizon.Common;
using System; using System;
using System.Threading;
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy
{ {
@ -17,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
private KEvent _accumulatedSuspendedTickChangedEvent; private KEvent _accumulatedSuspendedTickChangedEvent;
private int _accumulatedSuspendedTickChangedEventHandle; private int _accumulatedSuspendedTickChangedEventHandle;
private readonly object _fatalSectionLock = new(); private readonly Lock _fatalSectionLock = new();
private int _fatalSectionCount; private int _fatalSectionCount;
// TODO: Set this when the game goes in suspension (go back to home menu ect), we currently don't support that so we can keep it set to 0. // TODO: Set this when the game goes in suspension (go back to home menu ect), we currently don't support that so we can keep it set to 0.

View file

@ -123,7 +123,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
return true; return true;
} }
public void Configure(params ControllerConfig[] configs) public void Configure(params ReadOnlySpan<ControllerConfig> configs)
{ {
_configuredTypes = new ControllerType[MaxControllers]; _configuredTypes = new ControllerType[MaxControllers];

View file

@ -8,7 +8,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
{ {
public TouchDevice(Switch device, bool active) : base(device, active) { } public TouchDevice(Switch device, bool active) : base(device, active) { }
public void Update(params TouchPoint[] points) public void Update(params ReadOnlySpan<TouchPoint> points)
{ {
ref RingLifo<TouchScreenState> lifo = ref _device.Hid.SharedMemory.TouchScreen; ref RingLifo<TouchScreenState> lifo = ref _device.Hid.SharedMemory.TouchScreen;

View file

@ -1,5 +1,6 @@
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading;
namespace Ryujinx.HLE.HOS.Services.Ldn.Types namespace Ryujinx.HLE.HOS.Services.Ldn.Types
{ {
@ -12,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.Types
static class NodeLatestUpdateHelper static class NodeLatestUpdateHelper
{ {
private static readonly object _lock = new(); private static readonly Lock _lock = new();
public static void CalculateLatestUpdate(this Array8<NodeLatestUpdate> array, Array8<NodeInfo> beforeNodes, Array8<NodeInfo> afterNodes) public static void CalculateLatestUpdate(this Array8<NodeLatestUpdate> array, Array8<NodeInfo> beforeNodes, Array8<NodeInfo> afterNodes)
{ {

View file

@ -29,7 +29,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnMitm
private ILdnTcpSocket _tcp; private ILdnTcpSocket _tcp;
private LdnProxyUdpServer _udp, _udp2; private LdnProxyUdpServer _udp, _udp2;
private readonly List<LdnProxyTcpSession> _stations = new(); private readonly List<LdnProxyTcpSession> _stations = new();
private readonly object _lock = new(); private readonly Lock _lock = new();
private readonly AutoResetEvent _apConnected = new(false); private readonly AutoResetEvent _apConnected = new(false);

View file

@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnMitm.Proxy
private byte[] _buffer; private byte[] _buffer;
private int _bufferEnd; private int _bufferEnd;
private readonly object _scanLock = new(); private readonly Lock _scanLock = new();
private Dictionary<ulong, NetworkInfo> _scanResultsLast = new(); private Dictionary<ulong, NetworkInfo> _scanResultsLast = new();
private Dictionary<ulong, NetworkInfo> _scanResults = new(); private Dictionary<ulong, NetworkInfo> _scanResults = new();

View file

@ -10,7 +10,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu
private readonly Action _timeoutCallback; private readonly Action _timeoutCallback;
private CancellationTokenSource _cancel; private CancellationTokenSource _cancel;
private readonly object _lock = new object(); private readonly Lock _lock = new();
public NetworkTimeout(int idleTimeout, Action timeoutCallback) public NetworkTimeout(int idleTimeout, Action timeoutCallback)
{ {

View file

@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading;
namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
{ {
@ -8,7 +9,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
private readonly List<ushort> _ephemeralPorts = new List<ushort>(); private readonly List<ushort> _ephemeralPorts = new List<ushort>();
private readonly object _lock = new object(); private readonly Lock _lock = new();
public ushort Get() public ushort Get()
{ {

View file

@ -0,0 +1,340 @@
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Nfc.Nfp;
using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager;
using System;
using System.IO;
namespace Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption
{
public class AmiiboBinReader
{
private static byte CalculateBCC0(byte[] uid)
{
return (byte)(uid[0] ^ uid[1] ^ uid[2] ^ 0x88);
}
private static byte CalculateBCC1(byte[] uid)
{
return (byte)(uid[3] ^ uid[4] ^ uid[5] ^ uid[6]);
}
public static VirtualAmiiboFile ReadBinFile(byte[] fileBytes)
{
string keyRetailBinPath = GetKeyRetailBinPath();
if (string.IsNullOrEmpty(keyRetailBinPath))
{
return new VirtualAmiiboFile();
}
byte[] initialCounter = new byte[16];
const int totalPages = 135;
const int pageSize = 4;
const int totalBytes = totalPages * pageSize;
if (fileBytes.Length < totalBytes)
{
return new VirtualAmiiboFile();
}
AmiiboDecrypter amiiboDecryptor = new AmiiboDecrypter(keyRetailBinPath);
AmiiboDump amiiboDump = amiiboDecryptor.DecryptAmiiboDump(fileBytes);
byte[] titleId = new byte[8];
byte[] usedCharacter = new byte[2];
byte[] variation = new byte[2];
byte[] amiiboID = new byte[2];
byte[] setID = new byte[1];
byte[] initDate = new byte[2];
byte[] writeDate = new byte[2];
byte[] writeCounter = new byte[2];
byte[] appId = new byte[8];
byte[] settingsBytes = new byte[2];
byte formData = 0;
byte[] applicationAreas = new byte[216];
byte[] dataFull = amiiboDump.GetData();
Logger.Debug?.Print(LogClass.ServiceNfp, $"Data Full Length: {dataFull.Length}");
byte[] uid = new byte[7];
Array.Copy(dataFull, 0, uid, 0, 7);
byte bcc0 = CalculateBCC0(uid);
byte bcc1 = CalculateBCC1(uid);
LogDebugData(uid, bcc0, bcc1);
for (int page = 0; page < 128; page++) // NTAG215 has 128 pages
{
int pageStartIdx = page * 4; // Each page is 4 bytes
byte[] pageData = new byte[4];
byte[] sourceBytes = dataFull;
Array.Copy(sourceBytes, pageStartIdx, pageData, 0, 4);
// Special handling for specific pages
switch (page)
{
case 0: // Page 0 (UID + BCC0)
Logger.Debug?.Print(LogClass.ServiceNfp, "Page 0: UID and BCC0.");
break;
case 2: // Page 2 (BCC1 + Internal Value)
byte internalValue = pageData[1];
Logger.Debug?.Print(LogClass.ServiceNfp, $"Page 2: BCC1 + Internal Value 0x{internalValue:X2} (Expected 0x48).");
break;
case 6:
// Bytes 0 and 1 are init date, bytes 2 and 3 are write date
Array.Copy(pageData, 0, initDate, 0, 2);
Array.Copy(pageData, 2, writeDate, 0, 2);
break;
case 21:
// Bytes 0 and 1 are used character, bytes 2 and 3 are variation
Array.Copy(pageData, 0, usedCharacter, 0, 2);
Array.Copy(pageData, 2, variation, 0, 2);
break;
case 22:
// Bytes 0 and 1 are amiibo ID, byte 2 is set ID, byte 3 is form data
Array.Copy(pageData, 0, amiiboID, 0, 2);
setID[0] = pageData[2];
formData = pageData[3];
break;
case 64:
case 65:
// Extract title ID
int titleIdOffset = (page - 64) * 4;
Array.Copy(pageData, 0, titleId, titleIdOffset, 4);
break;
case 66:
// Bytes 0 and 1 are write counter
Array.Copy(pageData, 0, writeCounter, 0, 2);
break;
// Pages 76 to 127 are application areas
case >= 76 and <= 127:
int appAreaOffset = (page - 76) * 4;
Array.Copy(pageData, 0, applicationAreas, appAreaOffset, 4);
break;
}
}
string usedCharacterStr = BitConverter.ToString(usedCharacter).Replace("-", "");
string variationStr = BitConverter.ToString(variation).Replace("-", "");
string amiiboIDStr = BitConverter.ToString(amiiboID).Replace("-", "");
string setIDStr = BitConverter.ToString(setID).Replace("-", "");
string head = usedCharacterStr + variationStr;
string tail = amiiboIDStr + setIDStr + "02";
string finalID = head + tail;
ushort settingsValue = BitConverter.ToUInt16(settingsBytes, 0);
ushort initDateValue = BitConverter.ToUInt16(initDate, 0);
ushort writeDateValue = BitConverter.ToUInt16(writeDate, 0);
DateTime initDateTime = DateTimeFromTag(initDateValue);
DateTime writeDateTime = DateTimeFromTag(writeDateValue);
ushort writeCounterValue = BitConverter.ToUInt16(writeCounter, 0);
string nickName = amiiboDump.AmiiboNickname;
LogFinalData(titleId, appId, head, tail, finalID, nickName, initDateTime, writeDateTime, settingsValue, writeCounterValue, applicationAreas);
VirtualAmiiboFile virtualAmiiboFile = new VirtualAmiiboFile
{
FileVersion = 1,
TagUuid = uid,
AmiiboId = finalID,
NickName = nickName,
FirstWriteDate = initDateTime,
LastWriteDate = writeDateTime,
WriteCounter = writeCounterValue,
};
if (writeCounterValue > 0)
{
VirtualAmiibo.ApplicationBytes = applicationAreas;
}
VirtualAmiibo.NickName = nickName;
return virtualAmiiboFile;
}
public static bool SaveBinFile(string inputFile, byte[] appData)
{
Logger.Info?.Print(LogClass.ServiceNfp, "Saving bin file.");
byte[] readBytes;
try
{
readBytes = File.ReadAllBytes(inputFile);
}
catch (Exception ex)
{
Logger.Error?.Print(LogClass.ServiceNfp, $"Error reading file: {ex.Message}");
return false;
}
string keyRetailBinPath = GetKeyRetailBinPath();
if (string.IsNullOrEmpty(keyRetailBinPath))
{
Logger.Error?.Print(LogClass.ServiceNfp, "Key retail path is empty.");
return false;
}
if (appData.Length != 216) // Ensure application area size is valid
{
Logger.Error?.Print(LogClass.ServiceNfp, "Invalid application data length. Expected 216 bytes.");
return false;
}
AmiiboDecrypter amiiboDecryptor = new AmiiboDecrypter(keyRetailBinPath);
AmiiboDump amiiboDump = amiiboDecryptor.DecryptAmiiboDump(readBytes);
byte[] oldData = amiiboDump.GetData();
if (oldData.Length != 540) // Verify the expected length for NTAG215 tags
{
Logger.Error?.Print(LogClass.ServiceNfp, "Invalid tag data length. Expected 540 bytes.");
return false;
}
byte[] newData = new byte[oldData.Length];
Array.Copy(oldData, newData, oldData.Length);
// Replace application area with appData
int appAreaOffset = 76 * 4; // Starting page (76) times 4 bytes per page
Array.Copy(appData, 0, newData, appAreaOffset, appData.Length);
AmiiboDump encryptedDump = amiiboDecryptor.EncryptAmiiboDump(newData);
byte[] encryptedData = encryptedDump.GetData();
if (encryptedData == null || encryptedData.Length != readBytes.Length)
{
Logger.Error?.Print(LogClass.ServiceNfp, "Failed to encrypt data correctly.");
return false;
}
inputFile = inputFile.Replace("_modified", string.Empty);
// Save the encrypted data to file or return it for saving externally
string outputFilePath = Path.Combine(Path.GetDirectoryName(inputFile), Path.GetFileNameWithoutExtension(inputFile) + "_modified.bin");
try
{
File.WriteAllBytes(outputFilePath, encryptedData);
Logger.Info?.Print(LogClass.ServiceNfp, $"Modified Amiibo data saved to {outputFilePath}.");
return true;
}
catch (Exception ex)
{
Logger.Error?.Print(LogClass.ServiceNfp, $"Error saving file: {ex.Message}");
return false;
}
}
public static bool SaveBinFile(string inputFile, string newNickName)
{
Logger.Info?.Print(LogClass.ServiceNfp, "Saving bin file.");
byte[] readBytes;
try
{
readBytes = File.ReadAllBytes(inputFile);
}
catch (Exception ex)
{
Logger.Error?.Print(LogClass.ServiceNfp, $"Error reading file: {ex.Message}");
return false;
}
string keyRetailBinPath = GetKeyRetailBinPath();
if (string.IsNullOrEmpty(keyRetailBinPath))
{
Logger.Error?.Print(LogClass.ServiceNfp, "Key retail path is empty.");
return false;
}
AmiiboDecrypter amiiboDecryptor = new AmiiboDecrypter(keyRetailBinPath);
AmiiboDump amiiboDump = amiiboDecryptor.DecryptAmiiboDump(readBytes);
amiiboDump.AmiiboNickname = newNickName;
byte[] oldData = amiiboDump.GetData();
if (oldData.Length != 540) // Verify the expected length for NTAG215 tags
{
Logger.Error?.Print(LogClass.ServiceNfp, "Invalid tag data length. Expected 540 bytes.");
return false;
}
byte[] encryptedData = amiiboDecryptor.EncryptAmiiboDump(oldData).GetData();
if (encryptedData == null || encryptedData.Length != readBytes.Length)
{
Logger.Error?.Print(LogClass.ServiceNfp, "Failed to encrypt data correctly.");
return false;
}
inputFile = inputFile.Replace("_modified", string.Empty);
// Save the encrypted data to file or return it for saving externally
string outputFilePath = Path.Combine(Path.GetDirectoryName(inputFile), Path.GetFileNameWithoutExtension(inputFile) + "_modified.bin");
try
{
File.WriteAllBytes(outputFilePath, encryptedData);
Logger.Info?.Print(LogClass.ServiceNfp, $"Modified Amiibo data saved to {outputFilePath}.");
return true;
}
catch (Exception ex)
{
Logger.Error?.Print(LogClass.ServiceNfp, $"Error saving file: {ex.Message}");
return false;
}
}
private static void LogDebugData(byte[] uid, byte bcc0, byte bcc1)
{
Logger.Debug?.Print(LogClass.ServiceNfp, $"UID: {BitConverter.ToString(uid)}");
Logger.Debug?.Print(LogClass.ServiceNfp, $"BCC0: 0x{bcc0:X2}, BCC1: 0x{bcc1:X2}");
}
private static void LogFinalData(byte[] titleId, byte[] appId, string head, string tail, string finalID, string nickName, DateTime initDateTime, DateTime writeDateTime, ushort settingsValue, ushort writeCounterValue, byte[] applicationAreas)
{
Logger.Debug?.Print(LogClass.ServiceNfp, $"Title ID: 0x{BitConverter.ToString(titleId).Replace("-", "")}");
Logger.Debug?.Print(LogClass.ServiceNfp, $"Application Program ID: 0x{BitConverter.ToString(appId).Replace("-", "")}");
Logger.Debug?.Print(LogClass.ServiceNfp, $"Head: {head}");
Logger.Debug?.Print(LogClass.ServiceNfp, $"Tail: {tail}");
Logger.Debug?.Print(LogClass.ServiceNfp, $"Final ID: {finalID}");
Logger.Debug?.Print(LogClass.ServiceNfp, $"Nickname: {nickName}");
Logger.Debug?.Print(LogClass.ServiceNfp, $"Init Date: {initDateTime}");
Logger.Debug?.Print(LogClass.ServiceNfp, $"Write Date: {writeDateTime}");
Logger.Debug?.Print(LogClass.ServiceNfp, $"Settings: 0x{settingsValue:X4}");
Logger.Debug?.Print(LogClass.ServiceNfp, $"Write Counter: {writeCounterValue}");
Logger.Debug?.Print(LogClass.ServiceNfp, "Length of Application Areas: " + applicationAreas.Length);
}
private static uint CalculateCRC32(byte[] input)
{
uint[] table = new uint[256];
uint polynomial = 0xEDB88320;
for (uint i = 0; i < table.Length; ++i)
{
uint crc = i;
for (int j = 0; j < 8; ++j)
{
if ((crc & 1) != 0)
crc = (crc >> 1) ^ polynomial;
else
crc >>= 1;
}
table[i] = crc;
}
uint result = 0xFFFFFFFF;
foreach (byte b in input)
{
byte index = (byte)((result & 0xFF) ^ b);
result = (result >> 8) ^ table[index];
}
return ~result;
}
private static string GetKeyRetailBinPath()
{
return Path.Combine(AppDataManager.KeysDirPath, "key_retail.bin");
}
public static bool HasKeyRetailBinPath()
{
return File.Exists(GetKeyRetailBinPath());
}
public static DateTime DateTimeFromTag(ushort value)
{
try
{
int day = value & 0x1F;
int month = (value >> 5) & 0x0F;
int year = (value >> 9) & 0x7F;
if (day == 0 || month == 0 || month > 12 || day > DateTime.DaysInMonth(2000 + year, month))
throw new ArgumentOutOfRangeException();
return new DateTime(2000 + year, month, day);
}
catch
{
return DateTime.Now;
}
}
}
}

View file

@ -0,0 +1,43 @@
using System.IO;
namespace Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption
{
public class AmiiboDecrypter
{
public AmiiboMasterKey DataKey { get; private set; }
public AmiiboMasterKey TagKey { get; private set; }
public AmiiboDecrypter(string keyRetailBinPath)
{
var combinedKeys = File.ReadAllBytes(keyRetailBinPath);
var keys = AmiiboMasterKey.FromCombinedBin(combinedKeys);
DataKey = keys.DataKey;
TagKey = keys.TagKey;
}
public AmiiboDump DecryptAmiiboDump(byte[] encryptedDumpData)
{
// Initialize AmiiboDump with encrypted data
AmiiboDump amiiboDump = new AmiiboDump(encryptedDumpData, DataKey, TagKey, isLocked: true);
// Unlock (decrypt) the dump
amiiboDump.Unlock();
// Optional: Verify HMACs
amiiboDump.VerifyHMACs();
return amiiboDump;
}
public AmiiboDump EncryptAmiiboDump(byte[] decryptedDumpData)
{
// Initialize AmiiboDump with decrypted data
AmiiboDump amiiboDump = new AmiiboDump(decryptedDumpData, DataKey, TagKey, isLocked: false);
// Lock (encrypt) the dump
amiiboDump.Lock();
return amiiboDump;
}
}
}

View file

@ -0,0 +1,387 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
namespace Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption
{
public class AmiiboDump
{
private AmiiboMasterKey dataMasterKey;
private AmiiboMasterKey tagMasterKey;
private bool isLocked;
private byte[] data;
private byte[] hmacTagKey;
private byte[] hmacDataKey;
private byte[] aesKey;
private byte[] aesIv;
public AmiiboDump(byte[] dumpData, AmiiboMasterKey dataKey, AmiiboMasterKey tagKey, bool isLocked = true)
{
if (dumpData.Length < 540)
throw new ArgumentException("Incomplete dump. Amiibo data is at least 540 bytes.");
this.data = new byte[540];
Array.Copy(dumpData, this.data, dumpData.Length);
this.dataMasterKey = dataKey;
this.tagMasterKey = tagKey;
this.isLocked = isLocked;
if (!isLocked)
{
DeriveKeysAndCipher();
}
}
private byte[] DeriveKey(AmiiboMasterKey key, bool deriveAes, out byte[] derivedAesKey, out byte[] derivedAesIv)
{
List<byte> seed = new List<byte>();
// Start with the type string (14 bytes)
seed.AddRange(key.TypeString);
// Append data based on magic size
int append = 16 - key.MagicSize;
byte[] extract = new byte[16];
Array.Copy(this.data, 0x011, extract, 0, 2); // Extract two bytes from user data section
for (int i = 2; i < 16; i++)
{
extract[i] = 0x00;
}
seed.AddRange(extract.Take(append));
// Add the magic bytes
seed.AddRange(key.MagicBytes.Take(key.MagicSize));
// Extract the UID (UID is 8 bytes)
byte[] uid = new byte[8];
Array.Copy(this.data, 0x000, uid, 0, 8);
seed.AddRange(uid);
seed.AddRange(uid);
// Extract some tag data (pages 0x20 - 0x28)
byte[] user = new byte[32];
Array.Copy(this.data, 0x060, user, 0, 32);
// XOR it with the key padding (XorPad)
byte[] paddedUser = new byte[32];
for (int i = 0; i < user.Length; i++)
{
paddedUser[i] = (byte)(user[i] ^ key.XorPad[i]);
}
seed.AddRange(paddedUser);
byte[] seedBytes = seed.ToArray();
if (seedBytes.Length != 78)
{
throw new Exception("Size check for key derived seed failed");
}
byte[] hmacKey;
derivedAesKey = null;
derivedAesIv = null;
if (deriveAes)
{
// Derive AES Key and IV
var dataForAes = new byte[2 + seedBytes.Length];
dataForAes[0] = 0x00;
dataForAes[1] = 0x00; // Counter (0)
Array.Copy(seedBytes, 0, dataForAes, 2, seedBytes.Length);
byte[] derivedBytes;
using (var hmac = new HMACSHA256(key.HmacKey))
{
derivedBytes = hmac.ComputeHash(dataForAes);
}
derivedAesKey = derivedBytes.Take(16).ToArray();
derivedAesIv = derivedBytes.Skip(16).Take(16).ToArray();
// Derive HMAC Key
var dataForHmacKey = new byte[2 + seedBytes.Length];
dataForHmacKey[0] = 0x00;
dataForHmacKey[1] = 0x01; // Counter (1)
Array.Copy(seedBytes, 0, dataForHmacKey, 2, seedBytes.Length);
using (var hmac = new HMACSHA256(key.HmacKey))
{
derivedBytes = hmac.ComputeHash(dataForHmacKey);
}
hmacKey = derivedBytes.Take(16).ToArray();
}
else
{
// Derive HMAC Key only
var dataForHmacKey = new byte[2 + seedBytes.Length];
dataForHmacKey[0] = 0x00;
dataForHmacKey[1] = 0x01; // Counter (1)
Array.Copy(seedBytes, 0, dataForHmacKey, 2, seedBytes.Length);
byte[] derivedBytes;
using (var hmac = new HMACSHA256(key.HmacKey))
{
derivedBytes = hmac.ComputeHash(dataForHmacKey);
}
hmacKey = derivedBytes.Take(16).ToArray();
}
return hmacKey;
}
private void DeriveKeysAndCipher()
{
byte[] discard;
// Derive HMAC Tag Key
this.hmacTagKey = DeriveKey(this.tagMasterKey, false, out discard, out discard);
// Derive HMAC Data Key and AES Key/IV
this.hmacDataKey = DeriveKey(this.dataMasterKey, true, out aesKey, out aesIv);
}
private void DecryptData()
{
byte[] encryptedBlock = new byte[0x020 + 0x168];
Array.Copy(data, 0x014, encryptedBlock, 0, 0x020); // data[0x014:0x034]
Array.Copy(data, 0x0A0, encryptedBlock, 0x020, 0x168); // data[0x0A0:0x208]
byte[] decryptedBlock = AES_CTR_Transform(encryptedBlock, aesKey, aesIv);
// Copy decrypted data back
Array.Copy(decryptedBlock, 0, data, 0x014, 0x020);
Array.Copy(decryptedBlock, 0x020, data, 0x0A0, 0x168);
}
private void EncryptData()
{
byte[] plainBlock = new byte[0x020 + 0x168];
Array.Copy(data, 0x014, plainBlock, 0, 0x020); // data[0x014:0x034]
Array.Copy(data, 0x0A0, plainBlock, 0x020, 0x168); // data[0x0A0:0x208]
byte[] encryptedBlock = AES_CTR_Transform(plainBlock, aesKey, aesIv);
// Copy encrypted data back
Array.Copy(encryptedBlock, 0, data, 0x014, 0x020);
Array.Copy(encryptedBlock, 0x020, data, 0x0A0, 0x168);
}
private byte[] AES_CTR_Transform(byte[] data, byte[] key, byte[] iv)
{
byte[] output = new byte[data.Length];
using (Aes aes = Aes.Create())
{
aes.Key = key;
aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.None;
int blockSize = aes.BlockSize / 8; // in bytes, should be 16
byte[] counter = new byte[blockSize];
Array.Copy(iv, counter, blockSize);
using (ICryptoTransform encryptor = aes.CreateEncryptor())
{
byte[] encryptedCounter = new byte[blockSize];
for (int i = 0; i < data.Length; i += blockSize)
{
// Encrypt the counter
encryptor.TransformBlock(counter, 0, blockSize, encryptedCounter, 0);
// Determine the number of bytes to process in this block
int blockLength = Math.Min(blockSize, data.Length - i);
// XOR the encrypted counter with the plaintext/ciphertext block
for (int j = 0; j < blockLength; j++)
{
output[i + j] = (byte)(data[i + j] ^ encryptedCounter[j]);
}
// Increment the counter
IncrementCounter(counter);
}
}
}
return output;
}
private void IncrementCounter(byte[] counter)
{
for (int i = counter.Length - 1; i >= 0; i--)
{
if (++counter[i] != 0)
break;
}
}
private void DeriveHMACs()
{
if (isLocked)
throw new InvalidOperationException("Cannot derive HMACs when data is locked.");
// Calculate tag HMAC
byte[] tagHmacData = new byte[8 + 44];
Array.Copy(data, 0x000, tagHmacData, 0, 8);
Array.Copy(data, 0x054, tagHmacData, 8, 44);
byte[] tagHmac;
using (var hmac = new HMACSHA256(hmacTagKey))
{
tagHmac = hmac.ComputeHash(tagHmacData);
}
// Overwrite the stored tag HMAC
Array.Copy(tagHmac, 0, data, 0x034, 32);
// Prepare data for data HMAC
int len1 = 0x023; // 0x011 to 0x034 (0x034 - 0x011)
int len2 = 0x168; // 0x0A0 to 0x208 (0x208 - 0x0A0)
int len3 = tagHmac.Length; // 32 bytes
int len4 = 0x008; // 0x000 to 0x008 (0x008 - 0x000)
int len5 = 0x02C; // 0x054 to 0x080 (0x080 - 0x054)
int totalLength = len1 + len2 + len3 + len4 + len5;
byte[] dataHmacData = new byte[totalLength];
int offset = 0;
Array.Copy(data, 0x011, dataHmacData, offset, len1);
offset += len1;
Array.Copy(data, 0x0A0, dataHmacData, offset, len2);
offset += len2;
Array.Copy(tagHmac, 0, dataHmacData, offset, len3);
offset += len3;
Array.Copy(data, 0x000, dataHmacData, offset, len4);
offset += len4;
Array.Copy(data, 0x054, dataHmacData, offset, len5);
byte[] dataHmac;
using (var hmac = new HMACSHA256(hmacDataKey))
{
dataHmac = hmac.ComputeHash(dataHmacData);
}
// Overwrite the stored data HMAC
Array.Copy(dataHmac, 0, data, 0x080, 32);
}
public void VerifyHMACs()
{
if (isLocked)
throw new InvalidOperationException("Cannot verify HMACs when data is locked.");
// Calculate tag HMAC
byte[] tagHmacData = new byte[8 + 44];
Array.Copy(data, 0x000, tagHmacData, 0, 8);
Array.Copy(data, 0x054, tagHmacData, 8, 44);
byte[] calculatedTagHmac;
using (var hmac = new HMACSHA256(hmacTagKey))
{
calculatedTagHmac = hmac.ComputeHash(tagHmacData);
}
byte[] storedTagHmac = new byte[32];
Array.Copy(data, 0x034, storedTagHmac, 0, 32);
if (!calculatedTagHmac.SequenceEqual(storedTagHmac))
{
throw new Exception("Tag HMAC verification failed.");
}
// Prepare data for data HMAC
int len1 = 0x023; // 0x011 to 0x034
int len2 = 0x168; // 0x0A0 to 0x208
int len3 = calculatedTagHmac.Length; // 32 bytes
int len4 = 0x008; // 0x000 to 0x008
int len5 = 0x02C; // 0x054 to 0x080
int totalLength = len1 + len2 + len3 + len4 + len5;
byte[] dataHmacData = new byte[totalLength];
int offset = 0;
Array.Copy(data, 0x011, dataHmacData, offset, len1);
offset += len1;
Array.Copy(data, 0x0A0, dataHmacData, offset, len2);
offset += len2;
Array.Copy(calculatedTagHmac, 0, dataHmacData, offset, len3);
offset += len3;
Array.Copy(data, 0x000, dataHmacData, offset, len4);
offset += len4;
Array.Copy(data, 0x054, dataHmacData, offset, len5);
byte[] calculatedDataHmac;
using (var hmac = new HMACSHA256(hmacDataKey))
{
calculatedDataHmac = hmac.ComputeHash(dataHmacData);
}
byte[] storedDataHmac = new byte[32];
Array.Copy(data, 0x080, storedDataHmac, 0, 32);
if (!calculatedDataHmac.SequenceEqual(storedDataHmac))
{
throw new Exception("Data HMAC verification failed.");
}
}
public void Unlock()
{
if (!isLocked)
throw new InvalidOperationException("Data is already unlocked.");
// Derive keys and cipher
DeriveKeysAndCipher();
// Decrypt the encrypted data
DecryptData();
isLocked = false;
}
public void Lock()
{
if (isLocked)
throw new InvalidOperationException("Data is already locked.");
// Recalculate HMACs
DeriveHMACs();
// Encrypt the data
EncryptData();
isLocked = true;
}
public byte[] GetData()
{
return data;
}
// Property to get or set Amiibo nickname
public string AmiiboNickname
{
get
{
// data[0x020:0x034], big endian UTF-16
byte[] nicknameBytes = new byte[0x014];
Array.Copy(data, 0x020, nicknameBytes, 0, 0x014);
string nickname = System.Text.Encoding.BigEndianUnicode.GetString(nicknameBytes).TrimEnd('\0');
return nickname;
}
set
{
byte[] nicknameBytes = System.Text.Encoding.BigEndianUnicode.GetBytes(value.PadRight(10, '\0'));
if (nicknameBytes.Length > 20)
throw new ArgumentException("Nickname too long.");
Array.Copy(nicknameBytes, 0, data, 0x020, nicknameBytes.Length);
// Pad remaining bytes with zeros
for (int i = 0x020 + nicknameBytes.Length; i < 0x034; i++)
{
data[i] = 0x00;
}
}
}
}
}

View file

@ -0,0 +1,42 @@
using System;
using System.Linq;
namespace Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption
{
public class AmiiboMasterKey
{
public byte[] HmacKey { get; private set; } // 16 bytes
public byte[] TypeString { get; private set; } // 14 bytes
public byte Rfu { get; private set; } // 1 byte
public byte MagicSize { get; private set; } // 1 byte
public byte[] MagicBytes { get; private set; } // 16 bytes
public byte[] XorPad { get; private set; } // 32 bytes
public AmiiboMasterKey(byte[] data)
{
if (data.Length != 80)
throw new ArgumentException("Master key data must be 80 bytes.");
HmacKey = data.Take(16).ToArray();
TypeString = data.Skip(16).Take(14).ToArray();
Rfu = data[30];
MagicSize = data[31];
MagicBytes = data.Skip(32).Take(16).ToArray();
XorPad = data.Skip(48).Take(32).ToArray();
}
public static (AmiiboMasterKey DataKey, AmiiboMasterKey TagKey) FromCombinedBin(byte[] combinedBin)
{
if (combinedBin.Length != 160)
throw new ArgumentException($"Data is {combinedBin.Length} bytes (should be 160).");
byte[] dataBin = combinedBin.Take(80).ToArray();
byte[] tagBin = combinedBin.Skip(80).Take(80).ToArray();
AmiiboMasterKey dataKey = new AmiiboMasterKey(dataBin);
AmiiboMasterKey tagKey = new AmiiboMasterKey(tagBin);
return (dataKey, tagKey);
}
}
}

View file

@ -78,7 +78,6 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
if (_state == State.Initialized) if (_state == State.Initialized)
{ {
_cancelTokenSource?.Cancel(); _cancelTokenSource?.Cancel();
// NOTE: All events are destroyed here. // NOTE: All events are destroyed here.
context.Device.System.NfpDevices.Clear(); context.Device.System.NfpDevices.Clear();
@ -146,9 +145,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
break; break;
} }
} }
_cancelTokenSource = new CancellationTokenSource(); _cancelTokenSource = new CancellationTokenSource();
Task.Run(() => Task.Run(() =>
{ {
while (true) while (true)
@ -199,7 +196,6 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
break; break;
} }
} }
return ResultCode.Success; return ResultCode.Success;
} }
@ -229,7 +225,6 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
} }
// TODO: Found how the MountTarget is handled. // TODO: Found how the MountTarget is handled.
for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) for (int i = 0; i < context.Device.System.NfpDevices.Count; i++)
{ {
if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle) if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle)
@ -488,14 +483,12 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
#pragma warning disable IDE0059 // Remove unnecessary value assignment #pragma warning disable IDE0059 // Remove unnecessary value assignment
uint deviceHandle = (uint)context.RequestData.ReadUInt64(); uint deviceHandle = (uint)context.RequestData.ReadUInt64();
#pragma warning restore IDE0059 #pragma warning restore IDE0059
if (context.Device.System.NfpDevices.Count == 0) if (context.Device.System.NfpDevices.Count == 0)
{ {
return ResultCode.DeviceNotFound; return ResultCode.DeviceNotFound;
} }
// NOTE: Since we handle amiibo through VirtualAmiibo, we don't have to flush anything in our case. // NOTE: Since we handle amiibo through VirtualAmiibo, we don't have to flush anything in our case.
return ResultCode.Success; return ResultCode.Success;
} }
@ -884,7 +877,6 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
return ResultCode.Success; return ResultCode.Success;
} }
} }
return ResultCode.DeviceNotFound; return ResultCode.DeviceNotFound;
} }

View file

@ -3,7 +3,7 @@ using System.Collections.Generic;
namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager
{ {
struct VirtualAmiiboFile public struct VirtualAmiiboFile
{ {
public uint FileVersion { get; set; } public uint FileVersion { get; set; }
public byte[] TagUuid { get; set; } public byte[] TagUuid { get; set; }
@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager
public List<VirtualAmiiboApplicationArea> ApplicationAreas { get; set; } public List<VirtualAmiiboApplicationArea> ApplicationAreas { get; set; }
} }
struct VirtualAmiiboApplicationArea public struct VirtualAmiiboApplicationArea
{ {
public uint ApplicationAreaId { get; set; } public uint ApplicationAreaId { get; set; }
public byte[] ApplicationArea { get; set; } public byte[] ApplicationArea { get; set; }

View file

@ -4,19 +4,22 @@ using Ryujinx.Common.Utilities;
using Ryujinx.Cpu; using Ryujinx.Cpu;
using Ryujinx.HLE.HOS.Services.Mii; using Ryujinx.HLE.HOS.Services.Mii;
using Ryujinx.HLE.HOS.Services.Mii.Types; using Ryujinx.HLE.HOS.Services.Mii.Types;
using Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption;
using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager; using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
{ {
static class VirtualAmiibo static class VirtualAmiibo
{ {
private static uint _openedApplicationAreaId; public static uint OpenedApplicationAreaId;
public static byte[] ApplicationBytes = new byte[0];
public static string InputBin = string.Empty;
public static string NickName = string.Empty;
private static readonly AmiiboJsonSerializerContext _serializerContext = AmiiboJsonSerializerContext.Default; private static readonly AmiiboJsonSerializerContext _serializerContext = AmiiboJsonSerializerContext.Default;
public static byte[] GenerateUuid(string amiiboId, bool useRandomUuid) public static byte[] GenerateUuid(string amiiboId, bool useRandomUuid)
{ {
if (useRandomUuid) if (useRandomUuid)
@ -68,6 +71,11 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
{ {
VirtualAmiiboFile amiiboFile = LoadAmiiboFile(amiiboId); VirtualAmiiboFile amiiboFile = LoadAmiiboFile(amiiboId);
string nickname = amiiboFile.NickName ?? "Ryujinx"; string nickname = amiiboFile.NickName ?? "Ryujinx";
if (NickName != string.Empty)
{
nickname = NickName;
NickName = string.Empty;
}
UtilityImpl utilityImpl = new(tickSource); UtilityImpl utilityImpl = new(tickSource);
CharInfo charInfo = new(); CharInfo charInfo = new();
@ -97,16 +105,26 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
{ {
VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId); VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId);
virtualAmiiboFile.NickName = newNickName; virtualAmiiboFile.NickName = newNickName;
if (InputBin != string.Empty)
{
AmiiboBinReader.SaveBinFile(InputBin, virtualAmiiboFile.NickName);
return;
}
SaveAmiiboFile(virtualAmiiboFile); SaveAmiiboFile(virtualAmiiboFile);
} }
public static bool OpenApplicationArea(string amiiboId, uint applicationAreaId) public static bool OpenApplicationArea(string amiiboId, uint applicationAreaId)
{ {
VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId); VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId);
if (ApplicationBytes.Length > 0)
if (virtualAmiiboFile.ApplicationAreas.Exists(item => item.ApplicationAreaId == applicationAreaId))
{ {
_openedApplicationAreaId = applicationAreaId; OpenedApplicationAreaId = applicationAreaId;
return true;
}
if (virtualAmiiboFile.ApplicationAreas.Any(item => item.ApplicationAreaId == applicationAreaId))
{
OpenedApplicationAreaId = applicationAreaId;
return true; return true;
} }
@ -116,11 +134,17 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
public static byte[] GetApplicationArea(string amiiboId) public static byte[] GetApplicationArea(string amiiboId)
{ {
if (ApplicationBytes.Length > 0)
{
byte[] bytes = ApplicationBytes;
ApplicationBytes = new byte[0];
return bytes;
}
VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId); VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId);
foreach (VirtualAmiiboApplicationArea applicationArea in virtualAmiiboFile.ApplicationAreas) foreach (VirtualAmiiboApplicationArea applicationArea in virtualAmiiboFile.ApplicationAreas)
{ {
if (applicationArea.ApplicationAreaId == _openedApplicationAreaId) if (applicationArea.ApplicationAreaId == OpenedApplicationAreaId)
{ {
return applicationArea.ApplicationArea; return applicationArea.ApplicationArea;
} }
@ -133,7 +157,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
{ {
VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId); VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId);
if (virtualAmiiboFile.ApplicationAreas.Exists(item => item.ApplicationAreaId == applicationAreaId)) if (virtualAmiiboFile.ApplicationAreas.Any(item => item.ApplicationAreaId == applicationAreaId))
{ {
return false; return false;
} }
@ -151,17 +175,22 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
public static void SetApplicationArea(string amiiboId, byte[] applicationAreaData) public static void SetApplicationArea(string amiiboId, byte[] applicationAreaData)
{ {
if (InputBin != string.Empty)
{
AmiiboBinReader.SaveBinFile(InputBin, applicationAreaData);
return;
}
VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId); VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId);
if (virtualAmiiboFile.ApplicationAreas.Exists(item => item.ApplicationAreaId == _openedApplicationAreaId)) if (virtualAmiiboFile.ApplicationAreas.Any(item => item.ApplicationAreaId == OpenedApplicationAreaId))
{ {
for (int i = 0; i < virtualAmiiboFile.ApplicationAreas.Count; i++) for (int i = 0; i < virtualAmiiboFile.ApplicationAreas.Count; i++)
{ {
if (virtualAmiiboFile.ApplicationAreas[i].ApplicationAreaId == _openedApplicationAreaId) if (virtualAmiiboFile.ApplicationAreas[i].ApplicationAreaId == OpenedApplicationAreaId)
{ {
virtualAmiiboFile.ApplicationAreas[i] = new VirtualAmiiboApplicationArea() virtualAmiiboFile.ApplicationAreas[i] = new VirtualAmiiboApplicationArea()
{ {
ApplicationAreaId = _openedApplicationAreaId, ApplicationAreaId = OpenedApplicationAreaId,
ApplicationArea = applicationAreaData, ApplicationArea = applicationAreaData,
}; };
@ -204,10 +233,21 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
return virtualAmiiboFile; return virtualAmiiboFile;
} }
private static void SaveAmiiboFile(VirtualAmiiboFile virtualAmiiboFile) public static void SaveAmiiboFile(VirtualAmiiboFile virtualAmiiboFile)
{ {
string filePath = Path.Join(AppDataManager.BaseDirPath, "system", "amiibo", $"{virtualAmiiboFile.AmiiboId}.json"); string filePath = Path.Join(AppDataManager.BaseDirPath, "system", "amiibo", $"{virtualAmiiboFile.AmiiboId}.json");
JsonHelper.SerializeToFile(filePath, virtualAmiiboFile, _serializerContext.VirtualAmiiboFile); JsonHelper.SerializeToFile(filePath, virtualAmiiboFile, _serializerContext.VirtualAmiiboFile);
} }
public static bool SaveFileExists(VirtualAmiiboFile virtualAmiiboFile)
{
if (InputBin != string.Empty)
{
SaveAmiiboFile(virtualAmiiboFile);
return true;
}
return File.Exists(Path.Join(AppDataManager.BaseDirPath, "system", "amiibo", $"{virtualAmiiboFile.AmiiboId}.json"));
}
} }
} }

View file

@ -26,7 +26,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
private NvFence _previousFailingFence; private NvFence _previousFailingFence;
private uint _failingCount; private uint _failingCount;
public readonly object Lock = new(); public readonly Lock Lock = new();
/// <summary> /// <summary>
/// Max failing count until waiting on CPU. /// Max failing count until waiting on CPU.

View file

@ -17,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
private readonly Switch _device; private readonly Switch _device;
private readonly object _syncpointAllocatorLock = new(); private readonly Lock _syncpointAllocatorLock = new();
public NvHostSyncpt(Switch device) public NvHostSyncpt(Switch device)
{ {

View file

@ -3,6 +3,7 @@ using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Numerics; using System.Numerics;
using System.Threading;
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{ {
@ -10,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{ {
private static readonly ConcurrentDictionary<ulong, BsdContext> _registry = new(); private static readonly ConcurrentDictionary<ulong, BsdContext> _registry = new();
private readonly object _lock = new(); private readonly Lock _lock = new();
private readonly List<IFileDescriptor> _fds; private readonly List<IFileDescriptor> _fds;

View file

@ -16,6 +16,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading;
namespace Ryujinx.HLE.HOS.Services.Ssl namespace Ryujinx.HLE.HOS.Services.Ssl
{ {
@ -43,7 +44,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl
private bool _initialized; private bool _initialized;
private Dictionary<CaCertificateId, CertStoreEntry> _certificates; private Dictionary<CaCertificateId, CertStoreEntry> _certificates;
private readonly object _lock = new(); private readonly Lock _lock = new();
private struct CertStoreFileHeader private struct CertStoreFileHeader
{ {

View file

@ -1,5 +1,6 @@
using Ryujinx.HLE.HOS.Services.SurfaceFlinger.Types; using Ryujinx.HLE.HOS.Services.SurfaceFlinger.Types;
using System; using System;
using System.Threading;
namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
{ {
@ -23,7 +24,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
protected BufferQueueConsumer Consumer; protected BufferQueueConsumer Consumer;
protected readonly object Lock = new(); protected readonly Lock Lock = new();
private readonly IConsumerListener _listener; private readonly IConsumerListener _listener;

View file

@ -2,6 +2,7 @@ using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Kernel.Threading;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading;
namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
{ {
@ -11,7 +12,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
private static int _lastBinderId = 0; private static int _lastBinderId = 0;
private static readonly object _lock = new(); private static readonly Lock _lock = new();
public static int RegisterBinderObject(IBinder binder) public static int RegisterBinderObject(IBinder binder)
{ {

Some files were not shown because too many files have changed in this diff Show more