mirror of
https://github.com/GreemDev/Ryujinx.git
synced 2024-12-22 18:05:46 +00:00
Merge branch 'GreemDev:master' into master
This commit is contained in:
commit
63d8f907aa
164 changed files with 2133 additions and 1186 deletions
|
@ -5,7 +5,7 @@ If you wish to build the emulator yourself, follow these steps:
|
|||
|
||||
### 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).
|
||||
|
||||
### Step 2
|
||||
|
|
6
Directory.Build.props
Normal file
6
Directory.Build.props
Normal file
|
@ -0,0 +1,6 @@
|
|||
<Project>
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
</Project>
|
|
@ -48,8 +48,8 @@
|
|||
<PackageVersion Include="SkiaSharp" Version="2.88.7" />
|
||||
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.7" />
|
||||
<PackageVersion Include="SPB" Version="0.0.4-build32" />
|
||||
<PackageVersion Include="System.IO.Hashing" Version="8.0.0" />
|
||||
<PackageVersion Include="System.Management" Version="8.0.0" />
|
||||
<PackageVersion Include="System.IO.Hashing" Version="9.0.0" />
|
||||
<PackageVersion Include="System.Management" Version="9.0.0" />
|
||||
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"sdk": {
|
||||
"version": "8.0.100",
|
||||
"version": "9.0.100",
|
||||
"rollForward": "latestFeature"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using ARMeilleure.Common;
|
||||
using System;
|
||||
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
|
@ -149,7 +150,7 @@ namespace ARMeilleure.Decoders
|
|||
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)
|
||||
{
|
||||
|
|
|
@ -264,7 +264,7 @@ namespace ARMeilleure.Instructions
|
|||
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];
|
||||
|
||||
|
|
|
@ -337,7 +337,7 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||
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);
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ using System.Collections.Generic;
|
|||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Threading;
|
||||
|
||||
namespace ARMeilleure.Translation.Cache
|
||||
{
|
||||
|
@ -26,7 +27,7 @@ namespace ARMeilleure.Translation.Cache
|
|||
|
||||
private static readonly List<CacheEntry> _cacheEntries = new();
|
||||
|
||||
private static readonly object _lock = new();
|
||||
private static readonly Lock _lock = new();
|
||||
private static bool _initialized;
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
|
|
|
@ -559,27 +559,27 @@ namespace ARMeilleure.Translation
|
|||
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);
|
||||
}
|
||||
|
||||
public Operand AddIntrinsicInt(Intrinsic intrin, params Operand[] args)
|
||||
public Operand AddIntrinsicInt(Intrinsic intrin, params ReadOnlySpan<Operand> 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);
|
||||
}
|
||||
|
||||
public void AddIntrinsicNoRet(Intrinsic intrin, params Operand[] args)
|
||||
public void AddIntrinsicNoRet(Intrinsic intrin, params ReadOnlySpan<Operand> 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();
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ namespace ARMeilleure.Translation.PTC
|
|||
|
||||
private readonly ManualResetEvent _waitEvent;
|
||||
|
||||
private readonly object _lock;
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
|
@ -89,8 +89,6 @@ namespace ARMeilleure.Translation.PTC
|
|||
|
||||
_waitEvent = new ManualResetEvent(true);
|
||||
|
||||
_lock = new object();
|
||||
|
||||
_disposed = false;
|
||||
|
||||
TitleIdText = TitleIdTextDefault;
|
||||
|
|
|
@ -41,7 +41,7 @@ namespace ARMeilleure.Translation.PTC
|
|||
|
||||
private readonly ManualResetEvent _waitEvent;
|
||||
|
||||
private readonly object _lock;
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
|
@ -65,8 +65,6 @@ namespace ARMeilleure.Translation.PTC
|
|||
|
||||
_waitEvent = new ManualResetEvent(true);
|
||||
|
||||
_lock = new object();
|
||||
|
||||
_disposed = false;
|
||||
|
||||
ProfiledFuncs = new Dictionary<ulong, FuncProfile>();
|
||||
|
|
|
@ -5,6 +5,7 @@ using Ryujinx.Memory;
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Audio.Backends.OpenAL
|
||||
{
|
||||
|
@ -18,7 +19,7 @@ namespace Ryujinx.Audio.Backends.OpenAL
|
|||
private ulong _playedSampleCount;
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace Ryujinx.Audio
|
|||
/// <summary>
|
||||
/// Lock used to control the waiters registration.
|
||||
/// </summary>
|
||||
private readonly object _lock = new();
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
/// <summary>
|
||||
/// Events signaled when the driver played audio buffers.
|
||||
|
|
|
@ -2,6 +2,7 @@ using Ryujinx.Common;
|
|||
using Ryujinx.Common.Memory;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Audio.Backends.Common
|
||||
{
|
||||
|
@ -12,7 +13,7 @@ namespace Ryujinx.Audio.Backends.Common
|
|||
{
|
||||
private const int RingBufferAlignment = 2048;
|
||||
|
||||
private readonly object _lock = new();
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
private MemoryOwner<byte> _bufferOwner;
|
||||
private Memory<byte> _buffer;
|
||||
|
|
|
@ -14,12 +14,12 @@ namespace Ryujinx.Audio.Input
|
|||
/// </summary>
|
||||
public class AudioInputManager : IDisposable
|
||||
{
|
||||
private readonly object _lock = new();
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
/// <summary>
|
||||
/// Lock used for session allocation.
|
||||
/// </summary>
|
||||
private readonly object _sessionLock = new();
|
||||
private readonly Lock _sessionLock = new();
|
||||
|
||||
/// <summary>
|
||||
/// The session ids allocation table.
|
||||
|
|
|
@ -48,7 +48,7 @@ namespace Ryujinx.Audio.Input
|
|||
/// <summary>
|
||||
/// The lock of the parent.
|
||||
/// </summary>
|
||||
private readonly object _parentLock;
|
||||
private readonly Lock _parentLock;
|
||||
|
||||
/// <summary>
|
||||
/// The dispose state.
|
||||
|
@ -62,7 +62,7 @@ namespace Ryujinx.Audio.Input
|
|||
/// <param name="parentLock">The lock of the manager</param>
|
||||
/// <param name="deviceSession">The hardware device session</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;
|
||||
_parentLock = parentLock;
|
||||
|
|
|
@ -14,12 +14,12 @@ namespace Ryujinx.Audio.Output
|
|||
/// </summary>
|
||||
public class AudioOutputManager : IDisposable
|
||||
{
|
||||
private readonly object _lock = new();
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
/// <summary>
|
||||
/// Lock used for session allocation.
|
||||
/// </summary>
|
||||
private readonly object _sessionLock = new();
|
||||
private readonly Lock _sessionLock = new();
|
||||
|
||||
/// <summary>
|
||||
/// The session ids allocation table.
|
||||
|
|
|
@ -48,7 +48,7 @@ namespace Ryujinx.Audio.Output
|
|||
/// <summary>
|
||||
/// THe lock of the parent.
|
||||
/// </summary>
|
||||
private readonly object _parentLock;
|
||||
private readonly Lock _parentLock;
|
||||
|
||||
/// <summary>
|
||||
/// The dispose state.
|
||||
|
@ -62,7 +62,7 @@ namespace Ryujinx.Audio.Output
|
|||
/// <param name="parentLock">The lock of the manager</param>
|
||||
/// <param name="deviceSession">The hardware device session</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;
|
||||
_parentLock = parentLock;
|
||||
|
|
|
@ -26,7 +26,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||
{
|
||||
public class AudioRenderSystem : IDisposable
|
||||
{
|
||||
private readonly object _lock = new();
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
private AudioRendererRenderingDevice _renderingDevice;
|
||||
private AudioRendererExecutionMode _executionMode;
|
||||
|
|
|
@ -19,12 +19,12 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||
/// <summary>
|
||||
/// Lock used for session allocation.
|
||||
/// </summary>
|
||||
private readonly object _sessionLock = new();
|
||||
private readonly Lock _sessionLock = new();
|
||||
|
||||
/// <summary>
|
||||
/// Lock used to control the <see cref="AudioProcessor"/> running state.
|
||||
/// </summary>
|
||||
private readonly object _audioProcessorLock = new();
|
||||
private readonly Lock _audioProcessorLock = new();
|
||||
|
||||
/// <summary>
|
||||
/// The session ids allocation table.
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Audio.Renderer.Server.Upsampler
|
||||
{
|
||||
|
@ -16,7 +17,7 @@ namespace Ryujinx.Audio.Renderer.Server.Upsampler
|
|||
/// <summary>
|
||||
/// Global lock of the object.
|
||||
/// </summary>
|
||||
private readonly object _lock = new();
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
/// <summary>
|
||||
/// The upsamplers instances.
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -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> _active = new();
|
||||
private readonly Stack<NanosleepThread> _free = new();
|
||||
|
|
|
@ -50,7 +50,7 @@ namespace Ryujinx.Common.SystemInterop
|
|||
private long _lastTicks = PerformanceCounter.ElapsedTicks;
|
||||
private long _lastId;
|
||||
|
||||
private readonly object _lock = new();
|
||||
private readonly Lock _lock = new();
|
||||
private readonly List<WaitingObject> _waitingObjects = new();
|
||||
|
||||
private WindowsGranularTimer()
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefineConstants Condition=" '$(ExtraDefineConstants)' != '' ">$(DefineConstants);$(ExtraDefineConstants)</DefineConstants>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Cpu.AppleHv
|
||||
{
|
||||
|
@ -12,7 +13,7 @@ namespace Ryujinx.Cpu.AppleHv
|
|||
|
||||
private static int _addressSpaces;
|
||||
private static HvIpaAllocator _ipaAllocator;
|
||||
private static readonly object _lock = new();
|
||||
private static readonly Lock _lock = new();
|
||||
|
||||
public static (ulong, HvIpaAllocator) CreateAddressSpace(MemoryBlock block)
|
||||
{
|
||||
|
|
|
@ -478,7 +478,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
|||
bool skipContext,
|
||||
int spillBaseOffset,
|
||||
int? resultRegister,
|
||||
params ulong[] callArgs)
|
||||
params ReadOnlySpan<ulong> callArgs)
|
||||
{
|
||||
uint resultMask = 0u;
|
||||
|
||||
|
|
|
@ -417,7 +417,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
|
|||
nint funcPtr,
|
||||
int spillBaseOffset,
|
||||
int? resultRegister,
|
||||
params ulong[] callArgs)
|
||||
params ReadOnlySpan<ulong> callArgs)
|
||||
{
|
||||
uint resultMask = 0u;
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ using System.Collections.Generic;
|
|||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Cpu.LightningJit.Cache
|
||||
{
|
||||
|
@ -23,7 +24,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||
|
||||
private static readonly List<CacheEntry> _cacheEntries = new();
|
||||
|
||||
private static readonly object _lock = new();
|
||||
private static readonly Lock _lock = new();
|
||||
private static bool _initialized;
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
|
|
|
@ -4,6 +4,7 @@ using Ryujinx.Memory;
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Cpu.LightningJit.Cache
|
||||
{
|
||||
|
@ -104,7 +105,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||
private readonly MemoryCache _sharedCache;
|
||||
private readonly MemoryCache _localCache;
|
||||
private readonly PageAlignedRangeList _pendingMap;
|
||||
private readonly object _lock;
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
class ThreadLocalCacheEntry
|
||||
{
|
||||
|
@ -137,7 +138,6 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||
_sharedCache = new(allocator, SharedCacheSize);
|
||||
_localCache = new(allocator, LocalCacheSize);
|
||||
_pendingMap = new(_sharedCache.ReprotectAsRx, RegisterFunction);
|
||||
_lock = new();
|
||||
}
|
||||
|
||||
public unsafe nint Map(nint framePointer, ReadOnlySpan<byte> code, ulong guestAddress, ulong guestSize)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Cpu.Signal
|
||||
{
|
||||
|
@ -59,7 +60,7 @@ namespace Ryujinx.Cpu.Signal
|
|||
|
||||
private static MemoryBlock _codeBlock;
|
||||
|
||||
private static readonly object _lock = new();
|
||||
private static readonly Lock _lock = new();
|
||||
private static bool _initialized;
|
||||
|
||||
static NativeSignalHandler()
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||
public uint ProgramCount { get; set; } = 0;
|
||||
|
||||
private Action _interruptAction;
|
||||
private readonly object _interruptLock = new();
|
||||
private readonly Lock _interruptLock = new();
|
||||
|
||||
public event EventHandler<ScreenCaptureImageInfo> ScreenCaptured;
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ using Ryujinx.Graphics.Texture;
|
|||
using Ryujinx.Memory.Range;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
|
@ -998,7 +999,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
{
|
||||
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));
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ using Ryujinx.Memory.Range;
|
|||
using Ryujinx.Memory.Tracking;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
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>
|
||||
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)
|
||||
{
|
||||
|
@ -1701,3 +1702,4 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -721,7 +721,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||
/// <param name="format">The format of the texture</param>
|
||||
/// <param name="components">The texture swizzle components</param>
|
||||
/// <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.
|
||||
// On 24-bits depth formats, this is inverted (Stencil is R etc).
|
||||
|
|
|
@ -2,6 +2,7 @@ using Ryujinx.Common.Pools;
|
|||
using Ryujinx.Memory.Range;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Memory
|
||||
{
|
||||
|
@ -76,7 +77,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||
private BufferMigration _source;
|
||||
private BufferModifiedRangeList _migrationTarget;
|
||||
|
||||
private readonly object _lock = new();
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
/// <summary>
|
||||
/// Whether the modified range list has any entries or not.
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries
|
|||
private ulong _accumulatedCounter;
|
||||
private int _waiterCount;
|
||||
|
||||
private readonly object _lock = new();
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
private readonly Queue<BufferedQuery> _queryPool;
|
||||
private readonly AutoResetEvent _queuedEvent = new(false);
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries
|
|||
private bool _hostAccessReserved = false;
|
||||
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 double _divisor = 1f;
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ using Ryujinx.Graphics.GAL;
|
|||
using Ryujinx.Graphics.OpenGL.Image;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Graphics.OpenGL
|
||||
{
|
||||
|
@ -19,7 +20,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||
{
|
||||
private const int DisposedLiveFrames = 2;
|
||||
|
||||
private readonly object _lock = new();
|
||||
private readonly Lock _lock = new();
|
||||
private readonly Dictionary<TextureCreateInfo, List<DisposedTexture>> _textures = new();
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -4,6 +4,7 @@ using Ryujinx.Graphics.Shader.StructuredIr;
|
|||
using Ryujinx.Graphics.Shader.Translation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using static Spv.Specification;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
|
@ -19,13 +20,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||
private const int GeneratorPoolCount = 1;
|
||||
private static readonly ObjectPool<SpvInstructionPool> _instructionPool;
|
||||
private static readonly ObjectPool<SpvLiteralIntegerPool> _integerPool;
|
||||
private static readonly object _poolLock;
|
||||
private static readonly Lock _poolLock = new();
|
||||
|
||||
static SpirvGenerator()
|
||||
{
|
||||
_instructionPool = new(() => new SpvInstructionPool(), GeneratorPoolCount);
|
||||
_integerPool = new(() => new SpvLiteralIntegerPool(), GeneratorPoolCount);
|
||||
_poolLock = new object();
|
||||
}
|
||||
|
||||
private const HelperFunctionsMask NeedsInvocationIdMask = HelperFunctionsMask.SwizzleAdd;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
{
|
||||
bool useBackground = _gd.BackgroundQueue.Handle != 0 && _gd.Vendor != Vendor.Amd;
|
||||
Queue queue = useBackground ? _gd.BackgroundQueue : _gd.Queue;
|
||||
object queueLock = useBackground ? _gd.BackgroundQueueLock : _gd.QueueLock;
|
||||
Lock queueLock = useBackground ? _gd.BackgroundQueueLock : _gd.QueueLock;
|
||||
|
||||
lock (queueLock)
|
||||
{
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
private readonly Vk _api;
|
||||
private readonly Device _device;
|
||||
private readonly Queue _queue;
|
||||
private readonly object _queueLock;
|
||||
private readonly Lock _queueLock;
|
||||
private readonly bool _concurrentFenceWaitUnsupported;
|
||||
private readonly CommandPool _pool;
|
||||
private readonly Thread _owner;
|
||||
|
@ -63,7 +63,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
Vk api,
|
||||
Device device,
|
||||
Queue queue,
|
||||
object queueLock,
|
||||
Lock queueLock,
|
||||
uint queueFamilyIndex,
|
||||
bool concurrentFenceWaitUnsupported,
|
||||
bool isLight = false)
|
||||
|
|
|
@ -68,7 +68,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
_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)
|
||||
{
|
||||
|
@ -81,7 +81,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
return true;
|
||||
}
|
||||
|
||||
public bool OptimalFormatsSupport(FormatFeatureFlags flags, params Format[] formats)
|
||||
public bool OptimalFormatsSupport(FormatFeatureFlags flags, params ReadOnlySpan<Format> formats)
|
||||
{
|
||||
foreach (Format format in formats)
|
||||
{
|
||||
|
|
|
@ -5,6 +5,7 @@ using Silk.NET.Vulkan;
|
|||
using Silk.NET.Vulkan.Extensions.EXT;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
|
@ -31,7 +32,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
private readonly Vk _api;
|
||||
private readonly ExtExternalMemoryHost _hostMemoryApi;
|
||||
private readonly Device _device;
|
||||
private readonly object _lock = new();
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
private readonly List<HostMemoryAllocation> _allocations;
|
||||
private readonly IntervalTree<ulong, HostMemoryAllocation> _allocationTree;
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries
|
|||
private ulong _accumulatedCounter;
|
||||
private int _waiterCount;
|
||||
|
||||
private readonly object _lock = new();
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
private readonly Queue<BufferedQuery> _queryPool;
|
||||
private readonly AutoResetEvent _queuedEvent = new(false);
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries
|
|||
private bool _hostAccessReserved;
|
||||
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 double _divisor = 1f;
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);._*</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ using shaderc;
|
|||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
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.
|
||||
// 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");
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ using Silk.NET.Vulkan.Extensions.KHR;
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using Format = Ryujinx.Graphics.GAL.Format;
|
||||
using PrimitiveTopology = Ryujinx.Graphics.GAL.PrimitiveTopology;
|
||||
using SamplerCreateInfo = Ryujinx.Graphics.GAL.SamplerCreateInfo;
|
||||
|
@ -45,8 +46,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
internal uint QueueFamilyIndex { get; private set; }
|
||||
internal Queue Queue { get; private set; }
|
||||
internal Queue BackgroundQueue { get; private set; }
|
||||
internal object BackgroundQueueLock { get; private set; }
|
||||
internal object QueueLock { get; private set; }
|
||||
internal Lock BackgroundQueueLock { get; private set; }
|
||||
internal Lock QueueLock { get; private set; }
|
||||
|
||||
internal MemoryAllocator MemoryAllocator { 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);
|
||||
BackgroundQueue = backgroundQueue;
|
||||
BackgroundQueueLock = new object();
|
||||
BackgroundQueueLock = new();
|
||||
}
|
||||
|
||||
PhysicalDeviceProperties2 properties2 = new()
|
||||
|
@ -496,7 +497,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||
|
||||
Api.GetDeviceQueue(_device, queueFamilyIndex, 0, out var queue);
|
||||
Queue = queue;
|
||||
QueueLock = new object();
|
||||
QueueLock = new();
|
||||
|
||||
LoadFeatures(maxQueueCount, queueFamilyIndex);
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ using System.IO.Compression;
|
|||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using Path = System.IO.Path;
|
||||
|
||||
namespace Ryujinx.HLE.FileSystem
|
||||
|
@ -55,7 +56,7 @@ namespace Ryujinx.HLE.FileSystem
|
|||
|
||||
private readonly VirtualFileSystem _virtualFileSystem;
|
||||
|
||||
private readonly object _lock = new();
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
public ContentManager(VirtualFileSystem virtualFileSystem)
|
||||
{
|
||||
|
@ -396,7 +397,7 @@ namespace Ryujinx.HLE.FileSystem
|
|||
if (locationList != null)
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
@ -424,7 +425,7 @@ namespace Ryujinx.HLE.FileSystem
|
|||
{
|
||||
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)
|
||||
|
@ -719,7 +720,7 @@ namespace Ryujinx.HLE.FileSystem
|
|||
|
||||
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;
|
||||
|
||||
|
@ -755,7 +756,7 @@ namespace Ryujinx.HLE.FileSystem
|
|||
|
||||
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));
|
||||
Nca nca = new(_virtualFileSystem.KeySet, ncaStream.AsStorage());
|
||||
|
@ -774,9 +775,9 @@ namespace Ryujinx.HLE.FileSystem
|
|||
{
|
||||
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.
|
||||
// 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))
|
||||
{
|
||||
string metaNcaPath = ncaEntry.Find(x => x.type == NcaContentType.Meta).path;
|
||||
string contentPath = ncaEntry.Find(x => x.type != NcaContentType.Meta).path;
|
||||
string metaNcaPath = ncaEntry.FirstOrDefault(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.
|
||||
// This is a perfect valid case, so we should just ignore the missing content nca and continue.
|
||||
|
|
|
@ -46,7 +46,7 @@ namespace Ryujinx.HLE.FileSystem
|
|||
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");
|
||||
|
||||
if (nca.GetProgramIndex() == programIndex)
|
||||
|
|
|
@ -14,6 +14,7 @@ using System.IO;
|
|||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Applets
|
||||
{
|
||||
|
@ -62,7 +63,7 @@ namespace Ryujinx.HLE.HOS.Applets
|
|||
private bool _canAcceptController = false;
|
||||
private KeyboardInputMode _inputMode = KeyboardInputMode.ControllerAndKeyboard;
|
||||
|
||||
private readonly object _lock = new();
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
public event EventHandler AppletStateChanged;
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ using System.Diagnostics;
|
|||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
||||
{
|
||||
|
@ -21,7 +22,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
|||
const string CancelText = "Cancel";
|
||||
const string ControllerToggleText = "Toggle input";
|
||||
|
||||
private readonly object _bufferLock = new();
|
||||
private readonly Lock _bufferLock = new();
|
||||
|
||||
private RenderingSurfaceInfo _surfaceInfo = null;
|
||||
private SKImageInfo _imageInfo;
|
||||
|
|
|
@ -27,7 +27,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
|||
|
||||
private TRef<bool> _cancelled = null;
|
||||
private Thread _thread = null;
|
||||
private readonly object _lock = new();
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
public bool IsRunning
|
||||
{
|
||||
|
|
|
@ -16,6 +16,8 @@ using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemA
|
|||
using Ryujinx.HLE.HOS.Services.Apm;
|
||||
using Ryujinx.HLE.HOS.Services.Caps;
|
||||
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.Nv;
|
||||
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)
|
||||
{
|
||||
if (VirtualAmiibo.ApplicationBytes.Length > 0)
|
||||
{
|
||||
VirtualAmiibo.ApplicationBytes = new byte[0];
|
||||
VirtualAmiibo.InputBin = string.Empty;
|
||||
}
|
||||
if (NfpDevices[nfpDeviceId].State == NfpDeviceState.SearchingForTag)
|
||||
{
|
||||
NfpDevices[nfpDeviceId].State = NfpDeviceState.TagFound;
|
||||
|
@ -344,6 +351,22 @@ namespace Ryujinx.HLE.HOS
|
|||
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)
|
||||
{
|
||||
|
|
|
@ -2,6 +2,7 @@ using Ryujinx.Common;
|
|||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||
{
|
||||
|
@ -14,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
|||
private readonly long[] _current2;
|
||||
private readonly long[] _peak;
|
||||
|
||||
private readonly object _lock;
|
||||
private readonly object _lock = new();
|
||||
|
||||
private readonly LinkedList<KThread> _waitingThreads;
|
||||
|
||||
|
@ -27,8 +28,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
|||
_current2 = new long[(int)LimitableResource.Count];
|
||||
_peak = new long[(int)LimitableResource.Count];
|
||||
|
||||
_lock = new object();
|
||||
|
||||
_waitingThreads = new LinkedList<KThread>();
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ using Ryujinx.HLE.HOS.Kernel.Process;
|
|||
using Ryujinx.Horizon.Common;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
{
|
||||
|
@ -11,7 +12,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||
{
|
||||
public KProcess Owner { get; private set; }
|
||||
private readonly KPageList _pageList;
|
||||
private readonly object _lock;
|
||||
private readonly Lock _lock = new();
|
||||
private ulong _address;
|
||||
private bool _isOwnerMapped;
|
||||
private bool _isMapped;
|
||||
|
@ -19,7 +20,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||
public KCodeMemory(KernelContext context) : base(context)
|
||||
{
|
||||
_pageList = new KPageList();
|
||||
_lock = new object();
|
||||
}
|
||||
|
||||
public Result Initialize(ulong address, ulong size)
|
||||
|
|
|
@ -40,8 +40,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||
|
||||
public ProcessState State { get; private set; }
|
||||
|
||||
private readonly object _processLock = new();
|
||||
private readonly object _threadingLock = new();
|
||||
private readonly Lock _processLock = new();
|
||||
private readonly Lock _threadingLock = new();
|
||||
|
||||
public KAddressArbiter AddressArbiter { get; private set; }
|
||||
|
||||
|
|
|
@ -200,7 +200,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
class KCriticalSection
|
||||
{
|
||||
private readonly KernelContext _context;
|
||||
private readonly object _lock;
|
||||
private readonly object _lock = new();
|
||||
private int _recursionCount;
|
||||
|
||||
public object Lock => _lock;
|
||||
|
@ -13,7 +13,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
public KCriticalSection(KernelContext context)
|
||||
{
|
||||
_context = context;
|
||||
_lock = new object();
|
||||
}
|
||||
|
||||
public void Enter()
|
||||
|
|
|
@ -112,7 +112,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
|||
|
||||
public bool WaitingInArbitration { get; set; }
|
||||
|
||||
private readonly object _activityOperationLock = new();
|
||||
private readonly Lock _activityOperationLock = new();
|
||||
|
||||
public KThread(KernelContext context) : base(context)
|
||||
{
|
||||
|
|
|
@ -168,7 +168,7 @@ namespace Ryujinx.HLE.HOS
|
|||
|
||||
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;
|
||||
|
||||
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))
|
||||
{
|
||||
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;
|
||||
|
||||
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));
|
||||
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;
|
||||
|
||||
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));
|
||||
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;
|
||||
|
||||
mods.ExefsContainers.Add(new Mod<FileInfo>($"<{applicationDir.Name} ExeFs>", fsFile, enabled));
|
||||
|
@ -403,7 +403,7 @@ namespace Ryujinx.HLE.HOS
|
|||
}
|
||||
|
||||
// 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) ||
|
||||
StrEquals(AmsNroPatchDir, name) ||
|
||||
|
@ -453,7 +453,7 @@ namespace Ryujinx.HLE.HOS
|
|||
patches.Initialized = true;
|
||||
}
|
||||
|
||||
public void CollectMods(IEnumerable<ulong> applications, params string[] searchDirPaths)
|
||||
public void CollectMods(IEnumerable<ulong> applications, params ReadOnlySpan<string> searchDirPaths)
|
||||
{
|
||||
Clear();
|
||||
|
||||
|
@ -680,7 +680,7 @@ namespace Ryujinx.HLE.HOS
|
|||
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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -755,12 +755,18 @@ namespace Ryujinx.HLE.HOS
|
|||
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'),
|
||||
NroExecutable nro => Convert.ToHexString(nro.Header.BuildId).TrimEnd('0'),
|
||||
_ => string.Empty,
|
||||
}).ToList();
|
||||
};
|
||||
buildIds.Add(buildId);
|
||||
}
|
||||
|
||||
int GetIndex(string buildId) => buildIds.FindIndex(id => id == buildId); // O(n) but list is small
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ using Ryujinx.HLE.HOS.Kernel.Threading;
|
|||
using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy.Types;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
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 int _accumulatedSuspendedTickChangedEventHandle;
|
||||
|
||||
private readonly object _fatalSectionLock = new();
|
||||
private readonly Lock _fatalSectionLock = new();
|
||||
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.
|
||||
|
|
|
@ -123,7 +123,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||
return true;
|
||||
}
|
||||
|
||||
public void Configure(params ControllerConfig[] configs)
|
||||
public void Configure(params ReadOnlySpan<ControllerConfig> configs)
|
||||
{
|
||||
_configuredTypes = new ControllerType[MaxControllers];
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||
{
|
||||
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;
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using Ryujinx.Common.Memory;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Ldn.Types
|
||||
{
|
||||
|
@ -12,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.Types
|
|||
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -29,7 +29,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnMitm
|
|||
private ILdnTcpSocket _tcp;
|
||||
private LdnProxyUdpServer _udp, _udp2;
|
||||
private readonly List<LdnProxyTcpSession> _stations = new();
|
||||
private readonly object _lock = new();
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
private readonly AutoResetEvent _apConnected = new(false);
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnMitm.Proxy
|
|||
private byte[] _buffer;
|
||||
private int _bufferEnd;
|
||||
|
||||
private readonly object _scanLock = new();
|
||||
private readonly Lock _scanLock = new();
|
||||
|
||||
private Dictionary<ulong, NetworkInfo> _scanResultsLast = new();
|
||||
private Dictionary<ulong, NetworkInfo> _scanResults = new();
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu
|
|||
private readonly Action _timeoutCallback;
|
||||
private CancellationTokenSource _cancel;
|
||||
|
||||
private readonly object _lock = new object();
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
public NetworkTimeout(int idleTimeout, Action timeoutCallback)
|
||||
{
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
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 object _lock = new object();
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
public ushort Get()
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
387
src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboDump.cs
Normal file
387
src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboDump.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -78,7 +78,6 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
|||
if (_state == State.Initialized)
|
||||
{
|
||||
_cancelTokenSource?.Cancel();
|
||||
|
||||
// NOTE: All events are destroyed here.
|
||||
context.Device.System.NfpDevices.Clear();
|
||||
|
||||
|
@ -146,9 +145,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_cancelTokenSource = new CancellationTokenSource();
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
while (true)
|
||||
|
@ -199,7 +196,6 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
|
@ -229,7 +225,6 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
|||
}
|
||||
|
||||
// TODO: Found how the MountTarget is handled.
|
||||
|
||||
for (int i = 0; i < context.Device.System.NfpDevices.Count; i++)
|
||||
{
|
||||
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
|
||||
uint deviceHandle = (uint)context.RequestData.ReadUInt64();
|
||||
#pragma warning restore IDE0059
|
||||
|
||||
if (context.Device.System.NfpDevices.Count == 0)
|
||||
{
|
||||
return ResultCode.DeviceNotFound;
|
||||
}
|
||||
|
||||
// NOTE: Since we handle amiibo through VirtualAmiibo, we don't have to flush anything in our case.
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
|
@ -884,7 +877,6 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
|||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
|
||||
return ResultCode.DeviceNotFound;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ using System.Collections.Generic;
|
|||
|
||||
namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager
|
||||
{
|
||||
struct VirtualAmiiboFile
|
||||
public struct VirtualAmiiboFile
|
||||
{
|
||||
public uint FileVersion { 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; }
|
||||
}
|
||||
|
||||
struct VirtualAmiiboApplicationArea
|
||||
public struct VirtualAmiiboApplicationArea
|
||||
{
|
||||
public uint ApplicationAreaId { get; set; }
|
||||
public byte[] ApplicationArea { get; set; }
|
||||
|
|
|
@ -4,19 +4,22 @@ using Ryujinx.Common.Utilities;
|
|||
using Ryujinx.Cpu;
|
||||
using Ryujinx.HLE.HOS.Services.Mii;
|
||||
using Ryujinx.HLE.HOS.Services.Mii.Types;
|
||||
using Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption;
|
||||
using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
||||
{
|
||||
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;
|
||||
|
||||
public static byte[] GenerateUuid(string amiiboId, bool useRandomUuid)
|
||||
{
|
||||
if (useRandomUuid)
|
||||
|
@ -68,6 +71,11 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
|||
{
|
||||
VirtualAmiiboFile amiiboFile = LoadAmiiboFile(amiiboId);
|
||||
string nickname = amiiboFile.NickName ?? "Ryujinx";
|
||||
if (NickName != string.Empty)
|
||||
{
|
||||
nickname = NickName;
|
||||
NickName = string.Empty;
|
||||
}
|
||||
UtilityImpl utilityImpl = new(tickSource);
|
||||
CharInfo charInfo = new();
|
||||
|
||||
|
@ -97,16 +105,26 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
|||
{
|
||||
VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId);
|
||||
virtualAmiiboFile.NickName = newNickName;
|
||||
if (InputBin != string.Empty)
|
||||
{
|
||||
AmiiboBinReader.SaveBinFile(InputBin, virtualAmiiboFile.NickName);
|
||||
return;
|
||||
}
|
||||
SaveAmiiboFile(virtualAmiiboFile);
|
||||
}
|
||||
|
||||
public static bool OpenApplicationArea(string amiiboId, uint applicationAreaId)
|
||||
{
|
||||
VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId);
|
||||
|
||||
if (virtualAmiiboFile.ApplicationAreas.Exists(item => item.ApplicationAreaId == applicationAreaId))
|
||||
if (ApplicationBytes.Length > 0)
|
||||
{
|
||||
_openedApplicationAreaId = applicationAreaId;
|
||||
OpenedApplicationAreaId = applicationAreaId;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (virtualAmiiboFile.ApplicationAreas.Any(item => item.ApplicationAreaId == applicationAreaId))
|
||||
{
|
||||
OpenedApplicationAreaId = applicationAreaId;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -116,11 +134,17 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
|||
|
||||
public static byte[] GetApplicationArea(string amiiboId)
|
||||
{
|
||||
if (ApplicationBytes.Length > 0)
|
||||
{
|
||||
byte[] bytes = ApplicationBytes;
|
||||
ApplicationBytes = new byte[0];
|
||||
return bytes;
|
||||
}
|
||||
VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId);
|
||||
|
||||
foreach (VirtualAmiiboApplicationArea applicationArea in virtualAmiiboFile.ApplicationAreas)
|
||||
{
|
||||
if (applicationArea.ApplicationAreaId == _openedApplicationAreaId)
|
||||
if (applicationArea.ApplicationAreaId == OpenedApplicationAreaId)
|
||||
{
|
||||
return applicationArea.ApplicationArea;
|
||||
}
|
||||
|
@ -133,7 +157,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
|||
{
|
||||
VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId);
|
||||
|
||||
if (virtualAmiiboFile.ApplicationAreas.Exists(item => item.ApplicationAreaId == applicationAreaId))
|
||||
if (virtualAmiiboFile.ApplicationAreas.Any(item => item.ApplicationAreaId == applicationAreaId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -151,17 +175,22 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
|||
|
||||
public static void SetApplicationArea(string amiiboId, byte[] applicationAreaData)
|
||||
{
|
||||
if (InputBin != string.Empty)
|
||||
{
|
||||
AmiiboBinReader.SaveBinFile(InputBin, applicationAreaData);
|
||||
return;
|
||||
}
|
||||
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++)
|
||||
{
|
||||
if (virtualAmiiboFile.ApplicationAreas[i].ApplicationAreaId == _openedApplicationAreaId)
|
||||
if (virtualAmiiboFile.ApplicationAreas[i].ApplicationAreaId == OpenedApplicationAreaId)
|
||||
{
|
||||
virtualAmiiboFile.ApplicationAreas[i] = new VirtualAmiiboApplicationArea()
|
||||
{
|
||||
ApplicationAreaId = _openedApplicationAreaId,
|
||||
ApplicationAreaId = OpenedApplicationAreaId,
|
||||
ApplicationArea = applicationAreaData,
|
||||
};
|
||||
|
||||
|
@ -204,10 +233,21 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
|
|||
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");
|
||||
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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
|
|||
private NvFence _previousFailingFence;
|
||||
private uint _failingCount;
|
||||
|
||||
public readonly object Lock = new();
|
||||
public readonly Lock Lock = new();
|
||||
|
||||
/// <summary>
|
||||
/// Max failing count until waiting on CPU.
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl
|
|||
|
||||
private readonly Switch _device;
|
||||
|
||||
private readonly object _syncpointAllocatorLock = new();
|
||||
private readonly Lock _syncpointAllocatorLock = new();
|
||||
|
||||
public NvHostSyncpt(Switch device)
|
||||
{
|
||||
|
|
|
@ -3,6 +3,7 @@ using System;
|
|||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Threading;
|
||||
|
||||
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 readonly object _lock = new();
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
private readonly List<IFileDescriptor> _fds;
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Ssl
|
||||
{
|
||||
|
@ -43,7 +44,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl
|
|||
private bool _initialized;
|
||||
private Dictionary<CaCertificateId, CertStoreEntry> _certificates;
|
||||
|
||||
private readonly object _lock = new();
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
private struct CertStoreFileHeader
|
||||
{
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using Ryujinx.HLE.HOS.Services.SurfaceFlinger.Types;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||
{
|
||||
|
@ -23,7 +24,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
|||
|
||||
protected BufferQueueConsumer Consumer;
|
||||
|
||||
protected readonly object Lock = new();
|
||||
protected readonly Lock Lock = new();
|
||||
|
||||
private readonly IConsumerListener _listener;
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ using Ryujinx.Common.Logging;
|
|||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||
{
|
||||
|
@ -11,7 +12,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
|||
|
||||
private static int _lastBinderId = 0;
|
||||
|
||||
private static readonly object _lock = new();
|
||||
private static readonly Lock _lock = new();
|
||||
|
||||
public static int RegisterBinderObject(IBinder binder)
|
||||
{
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue