Compare commits

...

13 Commits

Author SHA1 Message Date
c77c1acd08 Resolve Image Usage Validation Error (#296)
This was a missed change that would resolve Image Usage validation error
that is created fairly frequently.

``VUID-VkImageViewCreateInfo-pNext-02662(ERROR / SPEC): msgNum:
-55646969 - Validation Error: [ VUID-VkImageViewCreateInfo-pNext-02662 ]
Object 0: handle = 0x260b9d1f6b8, type = VK_OBJECT_TYPE_IMAGE; |
MessageID = 0xfcaee507 | vkCreateImageView():
pCreateInfo->pNext<VkImageViewUsageCreateInfo>.usage
(VK_IMAGE_USAGE_SAMPLED_BIT|VK_IMAGE_USAGE_STORAGE_BIT) must not include
any bits that were not set in VkImageCreateInfo::usage
(VK_IMAGE_USAGE_TRANSFER_SRC_BIT|VK_IMAGE_USAGE_TRANSFER_DST_BIT|VK_IMAGE_USAGE_SAMPLED_BIT|VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)
of the image. The Vulkan spec states: If the pNext chain includes a
VkImageViewUsageCreateInfo structure, and image was not created with a
VkImageStencilUsageCreateInfo structure included in the pNext chain of
VkImageCreateInfo, its usage member must not include any bits that were
not set in the usage member of the VkImageCreateInfo structure used to
create image
(https://vulkan.lunarg.com/doc/view/1.3.290.0/windows/1.3-extensions/vkspec.html#VUID-VkImageViewCreateInfo-pNext-02662)
    Objects: 1
        [0] 0x260b9d1f6b8, type: 10, name: NULL
``
2024-12-20 16:31:05 -06:00
d68295e57d infra: remove duplicate tables, I think this is ignored entirely anyways due to omitBodyDuringUpdate. 2024-12-20 16:03:03 -06:00
bb3d95722e infra: fix casing in release artifact tables 2024-12-20 15:51:24 -06:00
2fa3a7bfa1 HLE: rename AmiiboDecrypter to AmiiboDecryptor 2024-12-20 15:44:01 -06:00
381921390a UI: Only show Amiibo bin scan menu item if the key file exists 2024-12-20 15:41:18 -06:00
221524d879 Added Support for 532-Byte Amiibo BIN Files (#412)
Added functionality to load 532-byte Amiibo BIN files, commonly used in
Tagmo and similar tools. These files were missing the following pages.
*    133 (85h) PWD
*    134 (86h) PACK RFUI
These pages can be added as null bytes if not present. The system seems
to function correctly without them.
2024-12-20 13:54:27 -06:00
9cddf3b66b Unified locales (#391)
Use 1 locales file instead of individual files for each langauge.
This makes it easier to keep track of what is missing.
The PR will automatically fix missing locales and throw an error if
anything is incorrect, by running the emulator. That way the person
adding a new locale or new language can just run the emulator once to
populate all the fields, so they can easily begin translating.
2024-12-20 13:27:11 -06:00
0adaa4cb96 Adds the ability to read and write to amiibo bin files (#348)
This introduces the ability to read and write game data and model
information from an Amiibo dump file (BIN format). Note that this
functionality requires the presence of a key_retail.bin file. For the
option to appear and function in the UI, ensure that the key_retail.bin
file is located in the <RyujinxData>/system folder.
2024-12-19 22:36:46 -06:00
ff6628149d Migrate to .NET 9 (#198) 2024-12-19 18:52:25 -06:00
8db5a7e98b UI: Fix logo aliasing 2024-12-17 21:20:49 -06:00
8e00cb5232 UI: Add faq, setup and multiplayer guides to the Help dropdown (#383) 2024-12-15 10:45:37 -06:00
362f62cd39 Revamp README header 2024-12-14 08:41:44 -06:00
2cd54d0da0 Remove icon padding 2024-12-14 08:03:17 -06:00
179 changed files with 23064 additions and 17110 deletions

View File

@ -61,9 +61,9 @@ jobs:
| Platform | Artifact |
|--|--|
| Windows 64 bit | [Canary Windows Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-win_x64.zip) |
| Linux 64 bit | [Canary Linux Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz) |
| Linux arm 64 bit | [Canary Linux arm Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) |
| Windows 64-bit | [Canary Windows Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-win_x64.zip) |
| Linux 64-bit | [Canary Linux Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz) |
| Linux ARM 64-bit | [Canary Linux ARM Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) |
| macOS | [Canary macOS artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz) |
**Full Changelog**: https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO }}/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }}
@ -200,10 +200,10 @@ jobs:
| Platform | Artifact |
|--|--|
| Windows 64 bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-win_x64.zip |
| Linux 64 bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz |
| Linux arm 64 bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz |
| Macos | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz |
| Windows 64-bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-win_x64.zip |
| Linux 64-bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz |
| Linux ARM 64-bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz |
| macOS | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz |
"**Full Changelog**: https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO }}/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }}"
omitBodyDuringUpdate: true
@ -272,19 +272,7 @@ jobs:
name: "Canary ${{ steps.version_info.outputs.build_version }}"
artifacts: "publish_ava/*.tar.gz, publish_headless/*.tar.gz"
tag: ${{ steps.version_info.outputs.build_version }}
body: |
# Canary builds:
These builds are experimental and may sometimes not work, use [regular builds](https://github.com/GreemDev/Ryujinx/releases/latest) instead if that sounds like something you don't want to deal with.
| Platform | Artifact |
|--|--|
| Windows 64 bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-win_x64.zip |
| Linux 64 bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz |
| Linux arm 64 bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz |
| Macos | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz |
"**Full Changelog**: https://github.com/${{ github.repository }}/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }}"
body: ""
omitBodyDuringUpdate: true
allowUpdates: true
replacesArtifacts: true

View File

@ -57,9 +57,9 @@ jobs:
# Regular builds:
| Platform | Artifact |
|--|--|
| Windows 64 bit | [Release Windows Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip) |
| Linux 64 bit | [Release Linux Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz) |
| Linux arm 64 bit | [Release Linux arm Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) |
| Windows 64-bit | [Release Windows Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip) |
| Linux 64-bit | [Release Linux Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz) |
| Linux ARM 64-bit | [Release Linux ARM Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) |
| macOS | [Release macOS Artifact](https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz) |
**Full Changelog**: https://github.com/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }}
@ -189,10 +189,10 @@ jobs:
# Regular builds:
| Platform | Artifact |
|--|--|
| Windows 64 bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip |
| Linux 64 bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz |
| Linux arm 64 bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz |
| Macos | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz |
| Windows 64-bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip |
| Linux 64-bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz |
| Linux ARM 64-bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz |
| macOS | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz |
"**Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }}"
omitBodyDuringUpdate: true
@ -261,16 +261,7 @@ jobs:
name: ${{ steps.version_info.outputs.build_version }}
artifacts: "publish/*.tar.gz, publish_headless/*.tar.gz"
tag: ${{ steps.version_info.outputs.build_version }}
body: |
# Regular builds:
| Platform | Artifact |
|--|--|
| Windows 64 bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip |
| Linux 64 bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz |
| Linux arm 64 bit | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz |
| Macos | https://github.com/${{ github.repository }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz |
"**Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }}"
body: ""
omitBodyDuringUpdate: true
allowUpdates: true
replacesArtifacts: true

View File

@ -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
View File

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

View File

@ -10,6 +10,9 @@
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.0.10" />
<PackageVersion Include="Avalonia.Svg" Version="11.0.0.18" />
<PackageVersion Include="Avalonia.Svg.Skia" Version="11.0.0.18" />
<PackageVersion Include="Microsoft.Build.Framework" Version="17.11.4" />
<PackageVersion Include="Microsoft.Build.Utilities.Core" Version="17.12.6" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="Projektanker.Icons.Avalonia" Version="9.4.0" />
<PackageVersion Include="Projektanker.Icons.Avalonia.FontAwesome" Version="9.4.0"/>
<PackageVersion Include="Projektanker.Icons.Avalonia.MaterialDesign" Version="9.4.0"/>
@ -48,8 +51,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>

View File

@ -1,29 +1,20 @@
<h1 align="center">
<table align="center">
<tr>
<td align="center" width="25%">
<img src="https://raw.githubusercontent.com/GreemDev/ryuassets/refs/heads/main/RyujinxApp_1024.png" alt="Ryujinx" >
</td>
<td align="center" width="75%">
# Ryujinx
[![Release workflow](https://github.com/GreemDev/Ryujinx/actions/workflows/release.yml/badge.svg)](https://github.com/GreemDev/Ryujinx/actions/workflows/release.yml)
[![Latest release](https://img.shields.io/github/v/release/GreemDev/Ryujinx)](https://github.com/GreemDev/Ryujinx/releases/latest)
<br>
<img src="https://raw.githubusercontent.com/GreemDev/Ryujinx/master/distribution/misc/Logo.svg" alt="Ryujinx" width="150"></a>
<br>
<b>Ryujinx</b>
<br>
<sub><sup><b>(REE-YOU-JINX)</b></sup></sub>
<br>
<a href="https://github.com/GreemDev/Ryujinx/actions/workflows/release.yml">
<img src="https://github.com/GreemDev/Ryujinx/actions/workflows/release.yml/badge.svg"
alt="">
</a>
<a href="https://github.com/GreemDev/Ryujinx/releases/latest">
<img src="https://img.shields.io/github/v/release/GreemDev/Ryujinx"
alt="Latest Release">
</a>
<br>
<a href="https://github.com/GreemDev/Ryujinx/actions/workflows/canary.yml">
<img src="https://github.com/GreemDev/Ryujinx/actions/workflows/canary.yml/badge.svg"
alt="">
</a>
<a href="https://github.com/GreemDev/Ryujinx-Canary/releases/latest">
<img src="https://img.shields.io/github/v/release/GreemDev/Ryujinx-Canary?label=canary"
alt="Latest Canary Release">
</a>
</h1>
[![Canary workflow](https://github.com/GreemDev/Ryujinx/actions/workflows/canary.yml/badge.svg)](https://github.com/GreemDev/Ryujinx/actions/workflows/canary.yml)
[![Latest canary release](https://img.shields.io/github/v/release/GreemDev/Ryujinx-Canary?label=canary)](https://github.com/GreemDev/Ryujinx-Canary/releases/latest)
</td>
</tr>
</table>
<p align="center">
Ryujinx is an open-source Nintendo Switch emulator, originally created by gdkchan, written in C#.
@ -63,7 +54,7 @@ failing to meet this requirement may result in a poor gameplay experience or une
## Latest build
Stable builds are made every so often onto a separate "release" branch that then gets put into the releases you know and love.
Stable builds are made every so often onto a separate "release" branch that then gets put into the releases you know and love.
These stable builds exist so that the end user can get a more **enjoyable and stable experience**.
You can find the latest stable release [here](https://github.com/GreemDev/Ryujinx/releases/latest).
@ -91,7 +82,7 @@ If you are planning to contribute or just want to learn more about this project
It translates the ARM code to a custom IR, performs a few optimizations, and turns that into x86 code.
There are three memory manager options available depending on the user's preference, leveraging both software-based (slower) and host-mapped modes (much faster).
The fastest option (host, unchecked) is set by default.
Ryujinx also features an optional Profiled Persistent Translation Cache, which essentially caches translated functions so that they do not need to be translated every time the game loads.
Ryujinx also features an optional Profiled Persistent Translation Cache, which essentially caches translated functions so that they do not need to be translated every time the game loads.
The net result is a significant reduction in load times (the amount of time between launching a game and arriving at the title screen) for nearly every game.
NOTE: This feature is enabled by default in the Options menu > System tab.
You must launch the game at least twice to the title screen or beyond before performance improvements are unlocked on the third launch!

View File

@ -84,12 +84,14 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{36F870C1-3E5F-485F-B426-F0645AF78751}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
.github\workflows\build.yml = .github\workflows\build.yml
.github\workflows\canary.yml = .github\workflows\canary.yml
Directory.Packages.props = Directory.Packages.props
.github/workflows/release.yml = .github/workflows/release.yml
.github/workflows/canary.yml = .github/workflows/canary.yml
.github/workflows/build.yml = .github/workflows/build.yml
.github\workflows\release.yml = .github\workflows\release.yml
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.BuildValidationTasks", "src\Ryujinx.BuildValidationTasks\Ryujinx.BuildValidationTasks.csproj", "{4A89A234-4F19-497D-A576-DDE8CDFC5B22}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -252,6 +254,9 @@ Global
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.Build.0 = Release|Any CPU
{4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4A89A234-4F19-497D-A576-DDE8CDFC5B22}.Release|Any CPU.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 48 KiB

View File

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

View File

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

View File

@ -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)
{

View File

@ -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];

View File

@ -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);

View File

@ -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")]

View File

@ -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();

View File

@ -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;

View File

@ -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>();

View File

@ -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)
{

View File

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

View File

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

View File

@ -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>

View File

@ -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.

View File

@ -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;

View File

@ -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.

View File

@ -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;

View File

@ -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.

View File

@ -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;

View File

@ -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;

View File

@ -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.

View File

@ -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.

View File

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

View File

@ -0,0 +1,76 @@
using System;
using Microsoft.Build.Utilities;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using Newtonsoft.Json;
using Microsoft.Build.Framework;
namespace Ryujinx.BuildValidationTasks
{
public class LocaleValidationTask : Task
{
public override bool Execute()
{
string path = System.Reflection.Assembly.GetExecutingAssembly().Location;
if (path.Split(new string[] { "src" }, StringSplitOptions.None).Length == 1 )
{
//i assume that we are in a build directory in the solution dir
path = new FileInfo(path).Directory.Parent.GetDirectories("src")[0].GetDirectories("Ryujinx")[0].GetDirectories("Assets")[0].GetFiles("locales.json")[0].FullName;
}
else
{
path = path.Split(new string[] { "src" }, StringSplitOptions.None)[0];
path = new FileInfo(path).Directory.GetDirectories("src")[0].GetDirectories("Ryujinx")[0].GetDirectories("Assets")[0].GetFiles("locales.json")[0].FullName;
}
string data;
using (StreamReader sr = new StreamReader(path))
{
data = sr.ReadToEnd();
}
LocalesJson json = JsonConvert.DeserializeObject<LocalesJson>(data);
for (int i = 0; i < json.Locales.Count; i++)
{
LocalesEntry locale = json.Locales[i];
foreach (string language in json.Languages)
{
if (!locale.Translations.ContainsKey(language))
{
locale.Translations.Add(language, "");
Log.LogMessage(MessageImportance.High, $"Added {{{language}}} to Locale {{{locale.ID}}}");
}
}
locale.Translations = locale.Translations.OrderBy(pair => pair.Key).ToDictionary(pair => pair.Key, pair => pair.Value);
json.Locales[i] = locale;
}
string jsonString = JsonConvert.SerializeObject(json, Formatting.Indented);
using (StreamWriter sw = new StreamWriter(path))
{
sw.Write(jsonString);
}
return true;
}
struct LocalesJson
{
public List<string> Languages { get; set; }
public List<LocalesEntry> Locales { get; set; }
}
struct LocalesEntry
{
public string ID { get; set; }
public Dictionary<string, string> Translations { get; set; }
}
}
}

View File

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build.Utilities.Core" />
<PackageReference Include="Newtonsoft.Json" />
</ItemGroup>
<UsingTask TaskName="Ryujinx.BuildValidationTasks.LocaleValidationTask" TaskFactory="TaskHostFactory" AssemblyFile="$(OutDir)Ryujinx.BuildValidationTasks.dll" />
<Target Name="LocalesJsonValidation" AfterTargets="AfterRebuild">
<LocaleValidationTask />
</Target>
</Project>

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> _active = new();
private readonly Stack<NanosleepThread> _free = new();

View File

@ -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()

View File

@ -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>

View File

@ -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)
{

View File

@ -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;

View File

@ -307,7 +307,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
int tempRegister;
int tempGuestAddress = -1;
bool inlineLookup = guestAddress.Kind != OperandKind.Constant &&
bool inlineLookup = guestAddress.Kind != OperandKind.Constant &&
funcTable is { Sparse: true };
if (guestAddress.Kind == OperandKind.Constant)
@ -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;

View File

@ -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")]

View File

@ -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)

View File

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

View File

@ -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()

View File

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

View File

@ -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;

View File

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

View File

@ -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));
}

View File

@ -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
}
}
}

View File

@ -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).

View File

@ -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.
@ -435,7 +436,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (_source == null)
{
// Create a new migration.
// Create a new migration.
_source = new BufferMigration(new BufferMigrationSpan[] { span }, this, _context.SyncNumber);
_context.RegisterBufferMigration(_source);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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);

View File

@ -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;

View File

@ -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>

View File

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

View File

@ -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;

View File

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

View File

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

View File

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

View File

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

View File

@ -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)
{

View File

@ -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)

View File

@ -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)
{
@ -148,7 +148,7 @@ namespace Ryujinx.Graphics.Vulkan
return (formatFeatureFlags & flags) == flags;
}
public VkFormat ConvertToVkFormat(Format srcFormat)
public VkFormat ConvertToVkFormat(Format srcFormat, bool storageFeatureFlagRequired)
{
var format = FormatTable.GetFormat(srcFormat);
@ -165,7 +165,7 @@ namespace Ryujinx.Graphics.Vulkan
requiredFeatures |= FormatFeatureFlags.ColorAttachmentBit;
}
if (srcFormat.IsImageCompatible())
if (srcFormat.IsImageCompatible() && storageFeatureFlagRequired)
{
requiredFeatures |= FormatFeatureFlags.StorageImageBit;
}

View File

@ -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;

View File

@ -29,11 +29,17 @@ namespace Ryujinx.Graphics.Vulkan
int colorCount = 0;
int maxColorAttachmentIndex = -1;
bool isNotMsOrSupportsStorage = gd.Capabilities.SupportsShaderStorageImageMultisample ||
!state.DepthStencilFormat.IsImageCompatible();
for (int i = 0; i < state.AttachmentEnable.Length; i++)
{
if (state.AttachmentEnable[i])
{
attachmentFormats[attachmentCount] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i]);
bool isNotMsOrSupportsStorageAttachments = gd.Capabilities.SupportsShaderStorageImageMultisample ||
!state.AttachmentFormats[i].IsImageCompatible();
attachmentFormats[attachmentCount] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i], isNotMsOrSupportsStorageAttachments);
attachmentIndices[attachmentCount++] = i;
colorCount++;
@ -43,7 +49,7 @@ namespace Ryujinx.Graphics.Vulkan
if (state.DepthStencilEnable)
{
attachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.DepthStencilFormat);
attachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.DepthStencilFormat, isNotMsOrSupportsStorage);
}
if (attachmentCount != 0)
@ -296,7 +302,10 @@ namespace Ryujinx.Graphics.Vulkan
{
if (state.AttachmentEnable[i])
{
pipeline.Internal.AttachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i]);
bool isNotMsOrSupportsStorage = gd.Capabilities.SupportsShaderStorageImageMultisample ||
!state.AttachmentFormats[i].IsImageCompatible();
pipeline.Internal.AttachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i], isNotMsOrSupportsStorage);
maxColorAttachmentIndex = i;
if (state.AttachmentFormats[i].IsInteger())
@ -310,7 +319,10 @@ namespace Ryujinx.Graphics.Vulkan
if (state.DepthStencilEnable)
{
pipeline.Internal.AttachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.DepthStencilFormat);
bool isNotMsOrSupportsStorage = !state.DepthStencilFormat.IsImageCompatible() ||
gd.Capabilities.SupportsShaderStorageImageMultisample;
pipeline.Internal.AttachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.DepthStencilFormat, isNotMsOrSupportsStorage);
}
pipeline.ColorBlendAttachmentStateCount = (uint)(maxColorAttachmentIndex + 1);

View File

@ -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);

View File

@ -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;

View File

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

View File

@ -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");

View File

@ -77,7 +77,9 @@ namespace Ryujinx.Graphics.Vulkan
_device = device;
_info = info;
var format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format);
bool isMsImageStorageSupported = gd.Capabilities.SupportsShaderStorageImageMultisample || !info.Target.IsMultisample();
var format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format, isMsImageStorageSupported);
var levels = (uint)info.Levels;
var layers = (uint)info.GetLayers();
var depth = (uint)(info.Target == Target.Texture3D ? info.Depth : 1);
@ -91,7 +93,7 @@ namespace Ryujinx.Graphics.Vulkan
var sampleCountFlags = ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)info.Samples);
var usage = GetImageUsage(info.Format, info.Target, gd.Capabilities);
var usage = GetImageUsage(info.Format, gd.Capabilities, isMsImageStorageSupported, true);
var flags = ImageCreateFlags.CreateMutableFormatBit | ImageCreateFlags.CreateExtendedUsageBit;
@ -305,7 +307,7 @@ namespace Ryujinx.Graphics.Vulkan
}
}
public static ImageUsageFlags GetImageUsage(Format format, Target target, in HardwareCapabilities capabilities)
public static ImageUsageFlags GetImageUsage(Format format, in HardwareCapabilities capabilities, bool isMsImageStorageSupported, bool extendedUsage)
{
var usage = DefaultUsageFlags;
@ -318,9 +320,7 @@ namespace Ryujinx.Graphics.Vulkan
usage |= ImageUsageFlags.ColorAttachmentBit;
}
bool supportsMsStorage = capabilities.SupportsShaderStorageImageMultisample;
if (format.IsImageCompatible() && (supportsMsStorage || !target.IsMultisample()))
if ((format.IsImageCompatible() && isMsImageStorageSupported) || extendedUsage)
{
usage |= ImageUsageFlags.StorageBit;
}

View File

@ -61,8 +61,11 @@ namespace Ryujinx.Graphics.Vulkan
gd.Textures.Add(this);
var format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format);
var usage = TextureStorage.GetImageUsage(info.Format, info.Target, gd.Capabilities);
bool isMsImageStorageSupported = gd.Capabilities.SupportsShaderStorageImageMultisample || !info.Target.IsMultisample();
var format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format, isMsImageStorageSupported);
var usage = TextureStorage.GetImageUsage(info.Format, gd.Capabilities, isMsImageStorageSupported, false);
var levels = (uint)info.Levels;
var layers = (uint)info.GetLayers();

View File

@ -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; }
@ -120,7 +121,7 @@ namespace Ryujinx.Graphics.Vulkan
}
public static VulkanRenderer Create(
string preferredGpuId,
string preferredGpuId,
Func<Instance, Vk, SurfaceKHR> getSurface,
Func<string[]> getRequiredExtensions
) => new(Vk.GetApi(), getSurface, getRequiredExtensions, preferredGpuId);
@ -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);

View File

@ -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.
@ -1076,7 +1077,7 @@ namespace Ryujinx.HLE.FileSystem
{
if (File.Exists(Path.Combine(pathToCheck, file)))
{
return true;
return true;
}
}
return false;

View File

@ -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)

View File

@ -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;

View File

@ -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;

View File

@ -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
{

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.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)
{

View File

@ -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>();
}

View File

@ -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)

View File

@ -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; }

View File

@ -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);
}

View File

@ -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()

View File

@ -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)
{

View File

@ -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)
{
NsoExecutable nso => Convert.ToHexString(nso.BuildId.ItemsRo.ToArray()).TrimEnd('0'),
NroExecutable nro => Convert.ToHexString(nro.Header.BuildId).TrimEnd('0'),
_ => string.Empty,
}).ToList();
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,
};
buildIds.Add(buildId);
}
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.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.

View File

@ -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];

View File

@ -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;

View File

@ -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)
{

View File

@ -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);

View File

@ -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();

View File

@ -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)
{

View File

@ -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()
{

View File

@ -0,0 +1,358 @@
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 == 532)
{
// add 8 bytes to the end of the file
byte[] newFileBytes = new byte[totalBytes];
Array.Copy(fileBytes, newFileBytes, fileBytes.Length);
fileBytes = newFileBytes;
}
AmiiboDecryptor amiiboDecryptor = new(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;
}
if (readBytes.Length == 532)
{
// add 8 bytes to the end of the file
byte[] newFileBytes = new byte[540];
Array.Copy(readBytes, newFileBytes, readBytes.Length);
readBytes = newFileBytes;
}
AmiiboDecryptor amiiboDecryptor = new AmiiboDecryptor(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;
}
if (readBytes.Length == 532)
{
// add 8 bytes to the end of the file
byte[] newFileBytes = new byte[540];
Array.Copy(readBytes, newFileBytes, readBytes.Length);
readBytes = newFileBytes;
}
AmiiboDecryptor amiiboDecryptor = new AmiiboDecryptor(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 => 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 AmiiboDecryptor
{
public AmiiboMasterKey DataKey { get; private set; }
public AmiiboMasterKey TagKey { get; private set; }
public AmiiboDecryptor(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(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(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;
}
}
}
}
}

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