diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 9c8cbebd7..000000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1 +0,0 @@ -patreon: Ryujinx diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 20bdc19d1..000000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,40 +0,0 @@ -version: 2 -updates: - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: weekly - labels: - - "infra" - reviewers: - - TSRBerry - commit-message: - prefix: "ci" - - - package-ecosystem: nuget - directory: / - open-pull-requests-limit: 10 - schedule: - interval: daily - labels: - - "infra" - reviewers: - - TSRBerry - commit-message: - prefix: nuget - groups: - Avalonia: - patterns: - - "*Avalonia*" - Silk.NET: - patterns: - - "Silk.NET*" - OpenTK: - patterns: - - "OpenTK*" - SixLabors: - patterns: - - "SixLabors*" - NUnit: - patterns: - - "NUnit*" diff --git a/.github/labeler.yml b/.github/labeler.yml index cd7650a9d..ac3c77288 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -20,7 +20,7 @@ gpu: gui: - changed-files: - - any-glob-to-any-file: ['src/Ryujinx/**', 'src/Ryujinx.UI.Common/**', 'src/Ryujinx.UI.LocaleGenerator/**', 'src/Ryujinx.Gtk3/**'] + - any-glob-to-any-file: ['src/Ryujinx/**', 'src/Ryujinx.UI.Common/**', 'src/Ryujinx.UI.LocaleGenerator/**'] horizon: - changed-files: @@ -32,4 +32,12 @@ kernel: infra: - changed-files: - - any-glob-to-any-file: ['.github/**', 'distribution/**', 'Directory.Packages.props'] + - any-glob-to-any-file: ['.github/**', 'distribution/**', 'Directory.Packages.props', 'src/Ryujinx.BuildValidationTasks/**'] + +documentation: +- changed-files: + - any-glob-to-any-file: 'docs/**' + +ldn: +- changed-files: + - any-glob-to-any-file: 'src/Ryujinx.HLE/HOS/Services/Ldn/**' diff --git a/.github/reviewers.yml b/.github/reviewers.yml index 46c0d5c11..97f5e8268 100644 --- a/.github/reviewers.yml +++ b/.github/reviewers.yml @@ -1,25 +1,17 @@ - cpu: - - gdkchan - - riperiperi - - LDj3SNuD + - GreemDev gpu: - - gdkchan - - riperiperi + - GreemDev gui: - - Ack77 - - emmauss - - TSRBerry + - GreemDev horizon: - - gdkchan - - Ack77 - - TSRBerry + - GreemDev infra: - - TSRBerry + - GreemDev default: - '@developers' diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 221c7732e..21dc3eb0b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,7 +6,8 @@ on: env: POWERSHELL_TELEMETRY_OPTOUT: 1 DOTNET_CLI_TELEMETRY_OPTOUT: 1 - RYUJINX_BASE_VERSION: "1.1.0" + RYUJINX_BASE_VERSION: "1.2.0" + RELEASE: 0 jobs: build: @@ -60,24 +61,50 @@ jobs: if: matrix.platform.name != 'linux-arm64' - name: Publish Ryujinx - run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx --self-contained true + run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx --self-contained if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13' - name: Publish Ryujinx.Headless.SDL2 - run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained true - if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13' - - - name: Publish Ryujinx.Gtk3 - run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish_gtk -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Gtk3 --self-contained true + run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13' - name: Set executable bit run: | chmod +x ./publish/Ryujinx ./publish/Ryujinx.sh chmod +x ./publish_sdl2_headless/Ryujinx.Headless.SDL2 ./publish_sdl2_headless/Ryujinx.sh - chmod +x ./publish_gtk/Ryujinx.Gtk3 ./publish_gtk/Ryujinx.sh if: github.event_name == 'pull_request' && matrix.platform.os == 'ubuntu-latest' + - name: Build AppImage + if: github.event_name == 'pull_request' && matrix.platform.os == 'ubuntu-latest' + run: | + PLATFORM_NAME="${{ matrix.platform.name }}" + + sudo apt install -y zsync desktop-file-utils appstream + + mkdir -p tools + export PATH="$PATH:$(readlink -f tools)" + + # Setup appimagetool + wget -q -O tools/appimagetool "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage" + chmod +x tools/appimagetool + chmod +x distribution/linux/appimage/build-appimage.sh + + # Explicitly set $ARCH for appimagetool ($ARCH_NAME is for the file name) + if [ "$PLATFORM_NAME" = "linux-x64" ]; then + ARCH_NAME=x64 + export ARCH=x86_64 + elif [ "$PLATFORM_NAME" = "linux-arm64" ]; then + ARCH_NAME=arm64 + export ARCH=aarch64 + else + echo "Unexpected PLATFORM_NAME "$PLATFORM_NAME"" + exit 1 + fi + + export UFLAG="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|*-$ARCH_NAME.AppImage.zsync" + BUILDDIR=publish OUTDIR=publish_appimage distribution/linux/appimage/build-appimage.sh + shell: bash + - name: Upload Ryujinx artifact uses: actions/upload-artifact@v4 with: @@ -85,20 +112,20 @@ jobs: path: publish if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13' + - name: Upload Ryujinx (AppImage) artifact + uses: actions/upload-artifact@v4 + if: github.event_name == 'pull_request' && matrix.platform.os == 'ubuntu-latest' + with: + name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }}-AppImage + path: publish_appimage + - name: Upload Ryujinx.Headless.SDL2 artifact uses: actions/upload-artifact@v4 with: - name: sdl2-ryujinx-headless-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }} + name: nogui-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }} path: publish_sdl2_headless if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13' - - name: Upload Ryujinx.Gtk3 artifact - uses: actions/upload-artifact@v4 - with: - name: gtk-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }} - path: publish_gtk - if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13' - build_macos: name: macOS Universal (${{ matrix.configuration }}) runs-on: ubuntu-latest @@ -158,6 +185,6 @@ jobs: - name: Upload Ryujinx.Headless.SDL2 artifact uses: actions/upload-artifact@v4 with: - name: sdl2-ryujinx-headless-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal + name: nogui-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal path: "publish_headless/*.tar.gz" if: github.event_name == 'pull_request' diff --git a/.github/workflows/canary.yml b/.github/workflows/canary.yml new file mode 100644 index 000000000..3100671e9 --- /dev/null +++ b/.github/workflows/canary.yml @@ -0,0 +1,281 @@ +name: Canary release job + +on: + workflow_dispatch: + inputs: {} + push: + branches: [ master ] + paths-ignore: + - '.github/**' + - 'docs/**' + - 'assets/**' + - '*.yml' + - '*.json' + - '*.config' + - '*.md' + +concurrency: release + +env: + POWERSHELL_TELEMETRY_OPTOUT: 1 + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + RYUJINX_BASE_VERSION: "1.2" + RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "canary" + RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "GreemDev" + RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO: "Ryujinx" + RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "Ryujinx-Canary" + RELEASE: 1 + +jobs: + tag: + name: Create tag + runs-on: ubuntu-20.04 + steps: + - name: Get version info + id: version_info + run: | + echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT + echo "prev_build_version=${{ env.RYUJINX_BASE_VERSION }}.$((${{ github.run_number }} - 1))" >> $GITHUB_OUTPUT + shell: bash + + - name: Create tag + uses: actions/github-script@v7 + with: + script: | + github.rest.git.createRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: 'refs/tags/Canary-${{ steps.version_info.outputs.build_version }}', + sha: context.sha + }) + + - name: Create release + uses: ncipollo/release-action@v1 + with: + name: "Canary ${{ steps.version_info.outputs.build_version }}" + 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/${{ github.repository }}/releases/latest) instead if that sounds like something you don't want to deal with. + + | 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) | + | 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 }} + omitBodyDuringUpdate: true + owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }} + repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }} + token: ${{ secrets.RELEASE_TOKEN }} + + release: + name: Release for ${{ matrix.platform.name }} + runs-on: ${{ matrix.platform.os }} + strategy: + matrix: + platform: + - { name: win-x64, os: windows-latest, zip_os_name: win_x64 } + - { name: linux-x64, os: ubuntu-latest, zip_os_name: linux_x64 } + - { name: linux-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 } + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-dotnet@v4 + with: + global-json-file: global.json + + - name: Overwrite csc problem matcher + run: echo "::add-matcher::.github/csc.json" + + - name: Get version info + id: version_info + run: | + echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT + echo "prev_build_version=${{ env.RYUJINX_BASE_VERSION }}.$((${{ github.run_number }} - 1))" >> $GITHUB_OUTPUT + echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT + shell: bash + + - name: Configure for release + run: | + sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' src/Ryujinx.Common/ReleaseInformation.cs + sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' src/Ryujinx.Common/ReleaseInformation.cs + sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs + sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs + sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs + sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs + sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/Config\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs + shell: bash + + - name: Create output dir + run: "mkdir release_output" + + - name: Publish + run: | + dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained + dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained + + - name: Packing Windows builds + if: matrix.platform.os == 'windows-latest' + run: | + pushd publish_ava + rm publish/libarmeilleure-jitsupport.dylib + 7z a ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish + popd + + pushd publish_sdl2_headless + rm publish/libarmeilleure-jitsupport.dylib + 7z a ../release_output/nogui-ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish + popd + shell: bash + + - name: Packing Linux builds + if: matrix.platform.os == 'ubuntu-latest' + run: | + pushd publish_ava + rm publish/libarmeilleure-jitsupport.dylib + chmod +x publish/Ryujinx.sh publish/Ryujinx + tar -czvf ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz publish + popd + + pushd publish_sdl2_headless + rm publish/libarmeilleure-jitsupport.dylib + chmod +x publish/Ryujinx.sh publish/Ryujinx.Headless.SDL2 + tar -czvf ../release_output/nogui-ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz publish + popd + shell: bash + + #- name: Build AppImage (Linux) + # if: matrix.platform.os == 'ubuntu-latest' + # run: | + # BUILD_VERSION="${{ steps.version_info.outputs.build_version }}" + # PLATFORM_NAME="${{ matrix.platform.name }}" + + # sudo apt install -y zsync desktop-file-utils appstream + + # mkdir -p tools + # export PATH="$PATH:$(readlink -f tools)" + + # Setup appimagetool + # wget -q -O tools/appimagetool "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage" + # chmod +x tools/appimagetool + # chmod +x distribution/linux/appimage/build-appimage.sh + + # Explicitly set $ARCH for appimagetool ($ARCH_NAME is for the file name) + # if [ "$PLATFORM_NAME" = "linux-x64" ]; then + # ARCH_NAME=x64 + # export ARCH=x86_64 + # elif [ "$PLATFORM_NAME" = "linux-arm64" ]; then + # ARCH_NAME=arm64 + # export ARCH=aarch64 + # else + # echo "Unexpected PLATFORM_NAME "$PLATFORM_NAME"" + # exit 1 + # fi + + # export UFLAG="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|*-$ARCH_NAME.AppImage.zsync" + # BUILDDIR=publish_ava OUTDIR=publish_ava_appimage distribution/linux/appimage/build-appimage.sh + + # Add to release output + # pushd publish_ava_appimage + # mv Ryujinx.AppImage ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage + # mv Ryujinx.AppImage.zsync ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage.zsync + # popd + # shell: bash + + - name: Pushing new release + uses: ncipollo/release-action@v1 + with: + name: ${{ steps.version_info.outputs.build_version }} + artifacts: "release_output/*.tar.gz,release_output/*.zip" + #artifacts: "release_output/*.tar.gz,release_output/*.zip/*AppImage*" + 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/${{ 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 + allowUpdates: true + replacesArtifacts: true + owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }} + repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }} + token: ${{ secrets.RELEASE_TOKEN }} + + macos_release: + name: Release MacOS universal + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-dotnet@v4 + with: + global-json-file: global.json + + - name: Setup LLVM 15 + run: | + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + sudo ./llvm.sh 15 + + - name: Install rcodesign + run: | + mkdir -p $HOME/.bin + gh release download -R indygreg/apple-platform-rs -O apple-codesign.tar.gz -p 'apple-codesign-*-x86_64-unknown-linux-musl.tar.gz' + tar -xzvf apple-codesign.tar.gz --wildcards '*/rcodesign' --strip-components=1 + rm apple-codesign.tar.gz + mv rcodesign $HOME/.bin/ + echo "$HOME/.bin" >> $GITHUB_PATH + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Get version info + id: version_info + run: | + echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT + echo "prev_build_version=${{ env.RYUJINX_BASE_VERSION }}.$((${{ github.run_number }} - 1))" >> $GITHUB_OUTPUT + echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT + + - name: Configure for release + run: | + sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' src/Ryujinx.Common/ReleaseInformation.cs + sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' src/Ryujinx.Common/ReleaseInformation.cs + sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs + sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs + sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs + sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs + sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/Config\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs + shell: bash + + - name: Publish macOS Ryujinx + run: | + ./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish_ava ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 1 + + - name: Publish macOS Ryujinx.Headless.SDL2 + run: | + ./distribution/macos/create_macos_build_headless.sh . publish_tmp_headless publish_headless ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 1 + + - name: Pushing new release + uses: ncipollo/release-action@v1 + with: + 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: "" + omitBodyDuringUpdate: true + allowUpdates: true + replacesArtifacts: true + owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }} + repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }} + token: ${{ secrets.RELEASE_TOKEN }} diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 2bef2d8e0..43c5f227a 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -1,4 +1,4 @@ -name: Perform checks +name: Build PR on: pull_request: @@ -20,55 +20,6 @@ concurrency: cancel-in-progress: true jobs: - format: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - uses: actions/setup-dotnet@v4 - with: - global-json-file: global.json - - - name: Overwrite csc problem matcher - run: echo "::add-matcher::.github/csc.json" - - - run: dotnet restore - - - name: Print dotnet format version - run: dotnet format --version - - - name: Run dotnet format whitespace - run: | - dotnet format whitespace --verify-no-changes --report ./whitespace-report.json -v d - - # For some unknown reason this step sometimes fails with exit code 139 (segfault?), - # so in that case we'll try again (3 tries max). - - name: Run dotnet format style - uses: TSRBerry/unstable-commands@v1 - with: - commands: dotnet format style --severity info --verify-no-changes --report ./style-report.json -v d - timeout-minutes: 5 - retry-codes: 139 - - # For some unknown reason this step sometimes fails with exit code 139 (segfault?), - # so in that case we'll try again (3 tries max). - - name: Run dotnet format analyzers - uses: TSRBerry/unstable-commands@v1 - with: - commands: dotnet format analyzers --severity info --verify-no-changes --report ./analyzers-report.json -v d - timeout-minutes: 5 - retry-codes: 139 - - - name: Upload report - if: failure() - uses: actions/upload-artifact@v4 - with: - name: dotnet-format - path: ./*-report.json - pr_build: uses: ./.github/workflows/build.yml - needs: format secrets: inherit diff --git a/.github/workflows/flatpak.yml b/.github/workflows/flatpak.yml deleted file mode 100644 index bfed328c9..000000000 --- a/.github/workflows/flatpak.yml +++ /dev/null @@ -1,212 +0,0 @@ -name: Flatpak release job - -on: - workflow_call: - inputs: - ryujinx_version: - required: true - type: string - - -concurrency: flatpak-release - -jobs: - release: - timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }} - runs-on: ubuntu-latest - - env: - NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages - GIT_COMMITTER_NAME: "RyujinxBot" - GIT_COMMITTER_EMAIL: "61127645+RyujinxBot@users.noreply.github.com" - RYUJINX_PROJECT_FILE: "src/Ryujinx/Ryujinx.csproj" - NUGET_SOURCES_DESTDIR: "nuget-sources" - RYUJINX_VERSION: "${{ inputs.ryujinx_version }}" - - steps: - - uses: actions/checkout@v4 - with: - path: Ryujinx - - - uses: actions/setup-dotnet@v4 - with: - global-json-file: Ryujinx/global.json - - - name: Get version info - id: version_info - working-directory: Ryujinx - run: | - echo "git_hash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT - - - uses: actions/checkout@v4 - with: - repository: flathub/org.ryujinx.Ryujinx - token: ${{ secrets.RYUJINX_BOT_PAT }} - submodules: recursive - path: flathub - - - name: Install dependencies - run: python -m pip install PyYAML lxml - - - name: Restore Nuget packages - # With .NET 8.0.100, Microsoft.NET.ILLink.Tasks isn't restored by default and only seems to appears when publishing. - # So we just publish to grab the dependencies - run: | - dotnet publish -c Release -r linux-x64 Ryujinx/${{ env.RYUJINX_PROJECT_FILE }} --self-contained - dotnet publish -c Release -r linux-arm64 Ryujinx/${{ env.RYUJINX_PROJECT_FILE }} --self-contained - - - name: Generate nuget_sources.json - shell: python - run: | - import hashlib - from pathlib import Path - import base64 - import binascii - import json - import os - import urllib.request - - sources = [] - - - def create_source_from_external(name, version): - full_dir_path = Path(os.environ["NUGET_PACKAGES"]).joinpath(name).joinpath(version) - os.makedirs(full_dir_path, exist_ok=True) - - filename = "{}.{}.nupkg".format(name, version) - url = "https://api.nuget.org/v3-flatcontainer/{}/{}/{}".format( - name, version, filename - ) - - print(f"Processing {url}...") - response = urllib.request.urlopen(url) - sha512 = hashlib.sha512(response.read()).hexdigest() - - return { - "type": "file", - "url": url, - "sha512": sha512, - "dest": os.environ["NUGET_SOURCES_DESTDIR"], - "dest-filename": filename, - } - - - has_added_x64_apphost = False - - for path in Path(os.environ["NUGET_PACKAGES"]).glob("**/*.nupkg.sha512"): - name = path.parent.parent.name - version = path.parent.name - filename = "{}.{}.nupkg".format(name, version) - url = "https://api.nuget.org/v3-flatcontainer/{}/{}/{}".format( - name, version, filename - ) - - with path.open() as fp: - sha512 = binascii.hexlify(base64.b64decode(fp.read())).decode("ascii") - - sources.append( - { - "type": "file", - "url": url, - "sha512": sha512, - "dest": os.environ["NUGET_SOURCES_DESTDIR"], - "dest-filename": filename, - } - ) - - # .NET will not add current installed application host to the list, force inject it here. - if not has_added_x64_apphost and name.startswith('microsoft.netcore.app.host'): - sources.append(create_source_from_external("microsoft.netcore.app.host.linux-x64", version)) - has_added_x64_apphost = True - - with open("flathub/nuget_sources.json", "w") as fp: - json.dump(sources, fp, indent=4) - - - name: Update flatpak metadata - id: metadata - env: - RYUJINX_GIT_HASH: ${{ steps.version_info.outputs.git_hash }} - shell: python - run: | - import hashlib - import hmac - import json - import os - import yaml - from datetime import datetime - from lxml import etree - - - # Ensure we don't destroy multiline strings - def str_presenter(dumper, data): - if len(data.splitlines()) > 1: - return dumper.represent_scalar("tag:yaml.org,2002:str", data, style="|") - return dumper.represent_scalar("tag:yaml.org,2002:str", data) - - - yaml.representer.SafeRepresenter.add_representer(str, str_presenter) - - yaml_file = "flathub/org.ryujinx.Ryujinx.yml" - xml_file = "flathub/org.ryujinx.Ryujinx.appdata.xml" - - with open(yaml_file, "r") as f: - data = yaml.safe_load(f) - - for source in data["modules"][0]["sources"]: - if type(source) is str: - continue - if ( - source["type"] == "git" - and source["url"] == "https://github.com/Ryujinx/Ryujinx.git" - ): - source["commit"] = os.environ['RYUJINX_GIT_HASH'] - - is_same_version = data["modules"][0]["build-options"]["env"]["RYUJINX_VERSION"] == os.environ['RYUJINX_VERSION'] - - with open(os.environ['GITHUB_OUTPUT'], "a") as gh_out: - if is_same_version: - gh_out.write(f"commit_message=Retry update to {os.environ['RYUJINX_VERSION']}") - else: - gh_out.write(f"commit_message=Update to {os.environ['RYUJINX_VERSION']}") - - if not is_same_version: - data["modules"][0]["build-options"]["env"]["RYUJINX_VERSION"] = os.environ['RYUJINX_VERSION'] - - with open(yaml_file, "w") as f: - yaml.safe_dump(data, f, sort_keys=False) - - parser = etree.XMLParser(remove_blank_text=True) - tree = etree.parse(xml_file, parser) - - root = tree.getroot() - - releases = root.find("releases") - - element = etree.Element("release") - element.set("version", os.environ['RYUJINX_VERSION']) - element.set("date", datetime.now().date().isoformat()) - releases.insert(0, element) - - # Ensure 4 spaces - etree.indent(root, space=" ") - - with open(xml_file, "wb") as f: - f.write( - etree.tostring( - tree, - pretty_print=True, - encoding="UTF-8", - doctype='', - ) - ) - - - name: Push flatpak update - working-directory: flathub - env: - COMMIT_MESSAGE: ${{ steps.metadata.outputs.commit_message }} - run: | - git config user.name "${{ env.GIT_COMMITTER_NAME }}" - git config user.email "${{ env.GIT_COMMITTER_EMAIL }}" - git add . - git commit -m "$COMMIT_MESSAGE" - git push origin master \ No newline at end of file diff --git a/.github/workflows/mako.yml b/.github/workflows/mako.yml deleted file mode 100644 index 19165fb04..000000000 --- a/.github/workflows/mako.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: Mako -on: - discussion: - types: [created, edited, answered, unanswered, category_changed] - discussion_comment: - types: [created, edited] - gollum: - issue_comment: - types: [created, edited] - issues: - types: [opened, edited, reopened, pinned, milestoned, demilestoned, assigned, unassigned, labeled, unlabeled] - pull_request_target: - types: [opened, edited, reopened, synchronize, ready_for_review, assigned, unassigned] - -jobs: - tasks: - name: Run Ryujinx tasks - permissions: - actions: read - contents: read - discussions: write - issues: write - pull-requests: write - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - if: github.event_name == 'pull_request_target' - with: - # Ensure we pin the source origin as pull_request_target run under forks. - fetch-depth: 0 - repository: Ryujinx/Ryujinx - ref: master - - - name: Run Mako command - uses: Ryujinx/Ryujinx-Mako@master - with: - command: exec-ryujinx-tasks - args: --event-name "${{ github.event_name }}" --event-path "${{ github.event_path }}" -w "${{ github.workspace }}" "${{ github.repository }}" "${{ github.run_id }}" - app_id: ${{ secrets.MAKO_APP_ID }} - private_key: ${{ secrets.MAKO_PRIVATE_KEY }} - installation_id: ${{ secrets.MAKO_INSTALLATION_ID }} diff --git a/.github/workflows/nightly_pr_comment.yml b/.github/workflows/nightly_pr_comment.yml index 38850df06..85a6e2de4 100644 --- a/.github/workflows/nightly_pr_comment.yml +++ b/.github/workflows/nightly_pr_comment.yml @@ -2,14 +2,13 @@ name: Comment PR artifacts links on: workflow_run: - workflows: ['Perform checks'] + workflows: ['Build PR'] types: [completed] jobs: pr_comment: if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' runs-on: ubuntu-latest - timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }} steps: - uses: actions/github-script@v6 with: @@ -39,24 +38,19 @@ jobs: return core.error(`No artifacts found`); } let body = `Download the artifacts for this pull request:\n`; - let hidden_gtk_artifacts = `\n\n
Old GUI (GTK3)\n`; - let hidden_headless_artifacts = `\n\n
GUI-less (SDL2)\n`; + let hidden_headless_artifacts = `\n\n
GUI-less\n`; let hidden_debug_artifacts = `\n\n
Only for Developers\n`; for (const art of artifacts) { if(art.name.includes('Debug')) { hidden_debug_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`; - } else if(art.name.includes('gtk-ryujinx')) { - hidden_gtk_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`; - } else if(art.name.includes('sdl2-ryujinx-headless')) { + } else if(art.name.includes('nogui-ryujinx')) { hidden_headless_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`; } else { body += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`; } } - hidden_gtk_artifacts += `\n
`; hidden_headless_artifacts += `\n
`; hidden_debug_artifacts += `\n
`; - body += hidden_gtk_artifacts; body += hidden_headless_artifacts; body += hidden_debug_artifacts; @@ -68,4 +62,4 @@ jobs: } else { core.info(`Creating a comment`); await github.rest.issues.createComment({repo, owner, issue_number, body}); - } \ No newline at end of file + } diff --git a/.github/workflows/pr_triage.yml b/.github/workflows/pr_triage.yml index d8d66b70f..2c4936159 100644 --- a/.github/workflows/pr_triage.yml +++ b/.github/workflows/pr_triage.yml @@ -18,7 +18,7 @@ jobs: with: # Ensure we pin the source origin as pull_request_target run under forks. fetch-depth: 0 - repository: Ryujinx/Ryujinx + repository: GreemDev/Ryujinx ref: master - name: Update labels based on changes diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f2bebc77f..59c31c71b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,9 +4,11 @@ on: workflow_dispatch: inputs: {} push: - branches: [ master ] + branches: [ release ] paths-ignore: - '.github/**' + - 'docs/**' + - 'assets/**' - '*.yml' - '*.json' - '*.config' @@ -17,10 +19,11 @@ concurrency: release env: POWERSHELL_TELEMETRY_OPTOUT: 1 DOTNET_CLI_TELEMETRY_OPTOUT: 1 - RYUJINX_BASE_VERSION: "1.1" - RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "master" - RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "Ryujinx" - RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "release-channel-master" + RYUJINX_BASE_VERSION: "1.2" + RYUJINX_TARGET_RELEASE_CHANNEL_NAME: "release" + RYUJINX_TARGET_RELEASE_CHANNEL_OWNER: "GreemDev" + RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "Ryujinx" + RELEASE: 1 jobs: tag: @@ -31,10 +34,11 @@ jobs: id: version_info run: | echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT + echo "prev_build_version=${{ env.RYUJINX_BASE_VERSION }}.$((${{ github.run_number }} - 1))" >> $GITHUB_OUTPUT shell: bash - name: Create tag - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: script: | github.rest.git.createRef({ @@ -49,7 +53,16 @@ jobs: with: name: ${{ steps.version_info.outputs.build_version }} tag: ${{ steps.version_info.outputs.build_version }} - body: "For more information about this release please check out the official [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog)." + body: | + # 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) | + | 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 }} omitBodyDuringUpdate: true owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }} repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }} @@ -58,7 +71,6 @@ jobs: release: name: Release for ${{ matrix.platform.name }} runs-on: ${{ matrix.platform.os }} - timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }} strategy: matrix: platform: @@ -79,6 +91,7 @@ jobs: id: version_info run: | echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT + echo "prev_build_version=${{ env.RYUJINX_BASE_VERSION }}.$((${{ github.run_number }} - 1))" >> $GITHUB_OUTPUT echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT shell: bash @@ -89,6 +102,7 @@ jobs: sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs + sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/Config\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs shell: bash @@ -97,36 +111,71 @@ jobs: - name: Publish run: | - dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained true - dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained true + dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained + dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained - name: Packing Windows builds if: matrix.platform.os == 'windows-latest' run: | - pushd publish_ava - cp publish/Ryujinx.exe publish/Ryujinx.Ava.exe - 7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish - 7z a ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish + pushd publish + rm libarmeilleure-jitsupport.dylib + 7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip ../publish popd pushd publish_sdl2_headless - 7z a ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip publish + rm libarmeilleure-jitsupport.dylib + 7z a ../release_output/nogui-ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip ../publish popd shell: bash + + - name: Build AppImage (Linux) + if: matrix.platform.os == 'ubuntu-latest' + run: | + BUILD_VERSION="${{ steps.version_info.outputs.build_version }}" + PLATFORM_NAME="${{ matrix.platform.name }}" + + sudo apt install -y zsync desktop-file-utils appstream + + mkdir -p tools + export PATH="$PATH:$(readlink -f tools)" + + # Setup appimagetool + wget -q -O tools/appimagetool "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage" + chmod +x tools/appimagetool + chmod +x distribution/linux/appimage/build-appimage.sh + + # Explicitly set $ARCH for appimagetool ($ARCH_NAME is for the file name) + if [ "$PLATFORM_NAME" = "linux-x64" ]; then + ARCH_NAME=x64 + export ARCH=x86_64 + elif [ "$PLATFORM_NAME" = "linux-arm64" ]; then + ARCH_NAME=arm64 + export ARCH=aarch64 + else + echo "Unexpected PLATFORM_NAME "$PLATFORM_NAME"" + exit 1 + fi + + export UFLAG="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|*-$ARCH_NAME.AppImage.zsync" + BUILDDIR=publish OUTDIR=publish_appimage distribution/linux/appimage/build-appimage.sh + + pushd publish_appimage + mv Ryujinx.AppImage ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage + mv Ryujinx.AppImage.zsync ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage.zsync + popd + shell: bash - name: Packing Linux builds if: matrix.platform.os == 'ubuntu-latest' run: | - pushd publish_ava - cp publish/Ryujinx publish/Ryujinx.Ava - chmod +x publish/Ryujinx.sh publish/Ryujinx.Ava publish/Ryujinx - tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz publish - tar -czvf ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz publish + pushd publish + chmod +x Ryujinx.sh Ryujinx + tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz ../publish popd pushd publish_sdl2_headless - chmod +x publish/Ryujinx.sh publish/Ryujinx.Headless.SDL2 - tar -czvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz publish + chmod +x Ryujinx.sh Ryujinx.Headless.SDL2 + tar -czvf ../release_output/nogui-ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz ../publish popd shell: bash @@ -134,9 +183,18 @@ jobs: uses: ncipollo/release-action@v1 with: name: ${{ steps.version_info.outputs.build_version }} - artifacts: "release_output/*.tar.gz,release_output/*.zip" + artifacts: "release_output/*.tar.gz,release_output/*.zip,release_output/*AppImage*" tag: ${{ steps.version_info.outputs.build_version }} - body: "For more information about this release please check out the official [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog)." + 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 }}" omitBodyDuringUpdate: true allowUpdates: true replacesArtifacts: true @@ -147,7 +205,6 @@ jobs: macos_release: name: Release MacOS universal runs-on: ubuntu-latest - timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }} steps: - uses: actions/checkout@v4 @@ -176,6 +233,7 @@ jobs: id: version_info run: | echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT + echo "prev_build_version=${{ env.RYUJINX_BASE_VERSION }}.$((${{ github.run_number }} - 1))" >> $GITHUB_OUTPUT echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT - name: Configure for release @@ -185,34 +243,28 @@ jobs: sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' src/Ryujinx.Common/ReleaseInformation.cs sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' src/Ryujinx.Common/ReleaseInformation.cs sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs + sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' src/Ryujinx.Common/ReleaseInformation.cs sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/Config\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs shell: bash - name: Publish macOS Ryujinx run: | - ./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish_ava ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release + ./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 0 - name: Publish macOS Ryujinx.Headless.SDL2 run: | - ./distribution/macos/create_macos_build_headless.sh . publish_tmp_headless publish_headless ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release + ./distribution/macos/create_macos_build_headless.sh . publish_tmp_headless publish_headless ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 0 - name: Pushing new release uses: ncipollo/release-action@v1 with: name: ${{ steps.version_info.outputs.build_version }} - artifacts: "publish_ava/*.tar.gz, publish_headless/*.tar.gz" + artifacts: "publish/*.tar.gz, publish_headless/*.tar.gz" tag: ${{ steps.version_info.outputs.build_version }} - body: "For more information about this release please check out the official [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog)." + body: "" omitBodyDuringUpdate: true allowUpdates: true replacesArtifacts: true owner: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }} repo: ${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }} token: ${{ secrets.RELEASE_TOKEN }} - - flatpak_release: - uses: ./.github/workflows/flatpak.yml - needs: release - with: - ryujinx_version: "1.1.${{ github.run_number }}" - secrets: inherit diff --git a/.gitignore b/.gitignore index 37b419d07..9a192926f 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,8 @@ x64/ build/ [Bb]in/ [Oo]bj/ +AppDir/ +publish_appimage/ # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets !packages/*/build/ @@ -173,3 +175,6 @@ PublishProfiles/ # Glade backup files *.glade~ + +# Ignore MacOS Attribute Files +._* diff --git a/COMPILING.md b/COMPILING.md new file mode 100644 index 000000000..64f7e4e1b --- /dev/null +++ b/COMPILING.md @@ -0,0 +1,23 @@ +## Compilation + +Building the project is for users that want to contribute code only. +If you wish to build the emulator yourself, follow these steps: + +### Step 1 + +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 + +Either use `git clone https://github.com/GreemDev/Ryujinx` on the command line to clone the repository or use Code --> Download zip button to get the files. + +### Step 3 + +To build Ryujinx, open a command prompt inside the project directory. +You can quickly access it on Windows by holding shift in File Explorer, then right clicking and selecting `Open command window here`. +Then type the following command: `dotnet build -c Release -o build` +the built files will be found in the newly created build directory. + +Ryujinx system files are stored in the `Ryujinx` folder. +This folder is located in the user folder, which can be accessed by clicking `Open Ryujinx Folder` under the File menu in the GUI. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 366eb8435..686ea3994 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,24 +12,15 @@ Please read the entire document before continuing as it can potentially save eve We always welcome bug reports, feature proposals and overall feedback. Here are a few tips on how you can make reporting your issue as effective as possible. -### Identify Where to Report - -The Ryujinx codebase is distributed across multiple repositories in the [Ryujinx organization](https://github.com/Ryujinx). Depending on the feedback you might want to file the issue on a different repo. Here are a few common repos: - -* [Ryujinx/Ryujinx](https://github.com/Ryujinx/Ryujinx) Ryujinx core project files. -* [Ryujinx/Ryujinx-Games-List](https://github.com/Ryujinx/Ryujinx-Games-List) Ryujinx game compatibility list. -* [Ryujinx/Ryujinx-Website](https://github.com/Ryujinx/Ryujinx-Website) Ryujinx website source code. -* [Ryujinx/Ryujinx-Ldn-Website](https://github.com/Ryujinx/Ryujinx-Ldn-Website) Ryujinx LDN website source code. - ### Finding Existing Issues -Before filing a new issue, please search our [open issues](https://github.com/Ryujinx/Ryujinx/issues) to check if it already exists. +Before filing a new issue, please search our [open issues](https://github.com/GreemDev/Ryujinx/issues) to check if it already exists. If you do find an existing issue, please include your own feedback in the discussion. Do consider upvoting (👍 reaction) the original post, as this helps us prioritize popular issues in our backlog. ### Writing a Good Feature Request -Please review any feature requests already opened to both check it has not already been suggested, and to familiarize yourself with the format. When ready to submit a proposal, please use the [Feature Request issue template](https://github.com/Ryujinx/Ryujinx/issues/new?assignees=&labels=&projects=&template=feature_request.yml&title=%5BFeature+Request%5D). +Please review any feature requests already opened to both check it has not already been suggested, and to familiarize yourself with the format. When ready to submit a proposal, please use the [Feature Request issue template](https://github.com/GreemDev/Ryujinx/issues/new?assignees=&labels=&projects=&template=feature_request.yml&title=%5BFeature+Request%5D). ### Writing a Good Bug Report @@ -43,13 +34,13 @@ Ideally, a bug report should contain the following information: * A Ryujinx log file of the run instance where the issue occurred. Log files can be found in `[Executable Folder]/Logs` and are named chronologically. * Additional information, e.g. is it a regression from previous versions? Are there any known workarounds? -When ready to submit a bug report, please use the [Bug Report issue template](https://github.com/Ryujinx/Ryujinx/issues/new?assignees=&labels=bug&projects=&template=bug_report.yml&title=%5BBug%5D). +When ready to submit a bug report, please use the [Bug Report issue template](https://github.com/GreemDev/Ryujinx/issues/new?assignees=&labels=bug&projects=&template=bug_report.yml&title=%5BBug%5D). ## Contributing Changes Project maintainers will merge changes that both improve the project and meet our standards for code quality. -The [Pull Request Guide](docs/workflow/pr-guide.md) and [License](https://github.com/Ryujinx/Ryujinx/blob/master/LICENSE.txt) docs define additional guidance. +The [Pull Request Guide](docs/workflow/pr-guide.md) and [License](https://github.com/GreemDev/Ryujinx/blob/master/LICENSE.txt) docs define additional guidance. ### DOs and DON'Ts @@ -83,23 +74,23 @@ We use and recommend the following workflow: 3. In your fork, create a branch off of main (`git checkout -b mybranch`). - Branches are useful since they isolate your changes from incoming changes from upstream. They also enable you to create multiple PRs from the same fork. 4. Make and commit your changes to your branch. - - [Build Instructions](https://github.com/Ryujinx/Ryujinx#building) explains how to build and test. + - [Build Instructions](https://github.com/GreemDev/Ryujinx/blob/master/COMPILING.md) explains how to build and test. - Commit messages should be clear statements of action and intent. 6. Build the repository with your changes. - Make sure that the builds are clean. - Make sure that `dotnet format` has been run and any corrections tested and committed. 7. Create a pull request (PR) against the Ryujinx/Ryujinx repository's **main** branch. - State in the description what issue or improvement your change is addressing. - - Check if all the Continuous Integration checks are passing. Refer to [Actions](https://github.com/Ryujinx/Ryujinx/actions) to check for outstanding errors. -8. Wait for feedback or approval of your changes from the [core development team](https://github.com/orgs/Ryujinx/teams/developers) - - Details about the pull request [review procedure](docs/workflow/ci/pr-guide.md). + - Check if all the Continuous Integration checks are passing. Refer to [Actions](https://github.com/GreemDev/Ryujinx/actions) to check for outstanding errors. +8. Wait for feedback or approval of your changes from the core development team + - Details about the pull request [review procedure](docs/workflow/pr-guide.md). 9. When the team members have signed off, and all checks are green, your PR will be merged. - The next official build will automatically include your change. - You can delete the branch you used for making the change. ### Good First Issues -The team marks the most straightforward issues as [good first issues](https://github.com/Ryujinx/Ryujinx/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22). This set of issues is the place to start if you are interested in contributing but new to the codebase. +The team marks the most straightforward issues as [good first issues](https://github.com/GreemDev/Ryujinx/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22). This set of issues is the place to start if you are interested in contributing but new to the codebase. ### Commit Messages @@ -122,7 +113,7 @@ Also do your best to factor commits appropriately, not too large with unrelated ### PR - CI Process -The [Ryujinx continuous integration](https://github.com/Ryujinx/Ryujinx/actions) (CI) system will automatically perform the required builds and run tests (including the ones you are expected to run) for PRs. Builds and test runs must be clean or have bugs properly filed against flaky/unexpected failures that are unrelated to your change. +The [Ryujinx continuous integration](https://github.com/GreemDev/Ryujinx/actions) (CI) system will automatically perform the required builds and run tests (including the ones you are expected to run) for PRs. Builds and test runs must be clean or have bugs properly filed against flaky/unexpected failures that are unrelated to your change. If the CI build fails for any reason, the PR actions tab should be consulted for further information on the failure. There are a few usual suspects for such a failure: * `dotnet format` has not been run on the PR and has outstanding stylistic issues. @@ -143,5 +134,5 @@ Ryujinx uses some implementations and frameworks from other projects. The follow - The license of the file is [permissive](https://en.wikipedia.org/wiki/Permissive_free_software_licence). - The license of the file is left in-tact. -- The contribution is correctly attributed in the [3rd party notices](https://github.com/Ryujinx/Ryujinx/blob/master/distribution/legal/THIRDPARTY.md) file in the repository, as needed. +- The contribution is correctly attributed in the [3rd party notices](https://github.com/GreemDev/Ryujinx/blob/master/distribution/legal/THIRDPARTY.md) file in the repository, as needed. diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 000000000..d7a2ac1f2 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,6 @@ + + + net9.0 + latest + + diff --git a/Directory.Packages.props b/Directory.Packages.props index e59d2d4fb..07fc8cc28 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -10,17 +10,22 @@ + + + + + + - - + - + @@ -31,11 +36,12 @@ + - + @@ -46,8 +52,8 @@ - - + + - \ No newline at end of file + diff --git a/README.md b/README.md index 7f2294d31..1532245f2 100644 --- a/README.md +++ b/README.md @@ -1,98 +1,74 @@ -

+ + + + + +
+ Ryujinx + + +# 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)
- Ryujinx -
- Ryujinx -
- (REE-YOU-JINX) -
- +[![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) +

- Ryujinx is an open-source Nintendo Switch emulator, created by gdkchan, written in C#. + Ryujinx is an open-source Nintendo Switch emulator, originally created by gdkchan, written in C#. This emulator aims at providing excellent accuracy and performance, a user-friendly interface and consistent builds. It was written from scratch and development on the project began in September 2017. - Ryujinx is available on Github under the MIT license. + Ryujinx is available on GitHub under the MIT license.

+

+ On October 1st 2024, Ryujinx was discontinued as the creator was forced to abandon the project. +
+ This fork is intended to be a QoL uplift for existing Ryujinx users. +
+ This is not a Ryujinx revival project. This is not a Phoenix project. +
+ Guides and documentation can be found on the Wiki tab. +

+

+ If you would like a more preservative fork of Ryujinx, check out ryujinx-mirror. +

- - - - - - - - Discord + Click below to join the Discord: +
+
+ Discord

- +

-## Compatibility - -As of May 2024, Ryujinx has been tested on approximately 4,300 titles; -over 4,100 boot past menus and into gameplay, with roughly 3,550 of those being considered playable. - -You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues). - -Anyone is free to submit a new game test or update an existing game test entry; -simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue. -Use the search function to see if a game has been tested already! - ## Usage To run this emulator, your PC must be equipped with at least 8GiB of RAM; failing to meet this requirement may result in a poor gameplay experience or unexpected crashes. -See our [Setup & Configuration Guide](https://github.com/Ryujinx/Ryujinx/wiki/Ryujinx-Setup-&-Configuration-Guide) on how to set up the emulator. - -For our Local Wireless (LDN) builds, see our [Multiplayer: Local Play/Local Wireless Guide -](https://github.com/Ryujinx/Ryujinx/wiki/Multiplayer-(LDN-Local-Wireless)-Guide). - -Avalonia UI comes with translations for various languages. See [Crowdin](https://crwd.in/ryujinx) for more information. - ## Latest build -These builds are compiled automatically for each commit on the master branch. -While we strive to ensure optimal stability and performance prior to pushing an update, our automated builds **may be unstable or completely broken**. +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**. -If you want to see details on updates to the emulator, you can visit our [Changelog](https://github.com/Ryujinx/Ryujinx/wiki/Changelog). +You can find the latest stable release [here](https://github.com/GreemDev/Ryujinx/releases/latest). -The latest automatic build for Windows, macOS, and Linux can be found on the [Official Website](https://ryujinx.org/download). +Canary builds are compiled automatically for each commit on the master branch. +While we strive to ensure optimal stability and performance prior to pushing an update, these builds **may be unstable or completely broken**. +These canary builds are only recommended for experienced users. + +You can find the latest canary release [here](https://github.com/GreemDev/Ryujinx-Canary/releases/latest). ## Documentation If you are planning to contribute or just want to learn more about this project please read through our [documentation](docs/README.md). -## Building - -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). -Make sure your SDK version is higher or equal to the required version specified in [global.json](global.json). - -### Step 2 - -Either use `git clone https://github.com/Ryujinx/Ryujinx` on the command line to clone the repository or use Code --> Download zip button to get the files. - -### Step 3 - -To build Ryujinx, open a command prompt inside the project directory. -You can quickly access it on Windows by holding shift in File Explorer, then right clicking and selecting `Open command window here`. -Then type the following command: `dotnet build -c Release -o build` -the built files will be found in the newly created build directory. - -Ryujinx system files are stored in the `Ryujinx` folder. -This folder is located in the user folder, which can be accessed by clicking `Open Ryujinx Folder` under the File menu in the GUI. - ## Features - **Audio** @@ -106,7 +82,7 @@ This folder is located in the user folder, which can be accessed by clicking `Op 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! @@ -135,27 +111,6 @@ This folder is located in the user folder, which can be accessed by clicking `Op The emulator has settings for enabling or disabling some logging, remapping controllers, and more. You can configure all of them through the graphical interface or manually through the config file, `Config.json`, found in the user folder which can be accessed by clicking `Open Ryujinx Folder` under the File menu in the GUI. -## Contact - -If you have contributions, suggestions, need emulator support or just want to get in touch with the team, join our [Discord server](https://discord.com/invite/Ryujinx). -You may also review our [FAQ](https://github.com/Ryujinx/Ryujinx/wiki/Frequently-Asked-Questions). - -## Donations - -If you'd like to support the project financially, Ryujinx has an active Patreon campaign. - - - - - -All developers working on the project do so in their free time, but the project has several expenses: -* Hackable Nintendo Switch consoles to reverse-engineer the hardware -* Additional computer hardware for testing purposes (e.g. GPUs to diagnose graphical bugs, etc.) -* Licenses for various software development tools (e.g. Jetbrains, IDA) -* Web hosting and infrastructure maintenance (e.g. LDN servers) - -All funds received through Patreon are considered a donation to support the project. Patrons receive early access to progress reports and exclusive access to developer interviews. - ## License This software is licensed under the terms of the [MIT license](LICENSE.txt). diff --git a/Ryujinx.sln b/Ryujinx.sln index 9b6d3ce58..dd54faf2a 100644 --- a/Ryujinx.sln +++ b/Ryujinx.sln @@ -3,8 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.1.32228.430 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Gtk3", "src\Ryujinx.Gtk3\Ryujinx.Gtk3.csproj", "{074045D4-3ED2-4711-9169-E385F2BFB5A0}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests", "src\Ryujinx.Tests\Ryujinx.Tests.csproj", "{EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests.Unicorn", "src\Ryujinx.Tests.Unicorn\Ryujinx.Tests.Unicorn.csproj", "{D8F72938-78EF-4E8C-BAFE-531C9C3C8F15}" @@ -31,12 +29,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec", "s EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio", "src\Ryujinx.Audio\Ryujinx.Audio.csproj", "{806ACF6D-90B0-45D0-A1AC-5F220F3B3985}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{36F870C1-3E5F-485F-B426-F0645AF78751}" - ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig - Directory.Packages.props = Directory.Packages.props - EndProjectSection -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Memory", "src\Ryujinx.Memory\Ryujinx.Memory.csproj", "{A5E6C691-9E22-4263-8F40-42F002CE66BE}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Tests.Memory", "src\Ryujinx.Tests.Memory\Ryujinx.Tests.Memory.csproj", "{D1CC5322-7325-4F6B-9625-194B30BE1296}" @@ -93,16 +85,23 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Graphics.Metal", "s {A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E} = {A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E} EndProjectSection 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 + 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 Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {074045D4-3ED2-4711-9169-E385F2BFB5A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {074045D4-3ED2-4711-9169-E385F2BFB5A0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {074045D4-3ED2-4711-9169-E385F2BFB5A0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {074045D4-3ED2-4711-9169-E385F2BFB5A0}.Release|Any CPU.Build.0 = Release|Any CPU {EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Debug|Any CPU.Build.0 = Debug|Any CPU {EBB55AEA-C7D7-4DEB-BF96-FA1789E225E9}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -263,6 +262,9 @@ Global {C08931FA-1191-417A-864F-3882D93E683B}.Debug|Any CPU.Build.0 = Debug|Any CPU {C08931FA-1191-417A-864F-3882D93E683B}.Release|Any CPU.ActiveCfg = Release|Any CPU {C08931FA-1191-417A-864F-3882D93E683B}.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 diff --git a/Ryujinx.sln.DotSettings b/Ryujinx.sln.DotSettings index ed7f3e911..018aa1331 100644 --- a/Ryujinx.sln.DotSettings +++ b/Ryujinx.sln.DotSettings @@ -3,9 +3,13 @@ WARNING UseExplicitType UseExplicitType - <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="I" Suffix="" Style="AaBb" /></Policy> + GL + SDL + OS + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="I" Suffix="" Style="AaBb" /></Policy> <Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Types and namespaces"><ElementKinds><Kind Name="NAMESPACE" /><Kind Name="CLASS" /><Kind Name="STRUCT" /><Kind Name="ENUM" /><Kind Name="DELEGATE" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="I" Suffix="" Style="AaBb" /></Policy></Policy> True + True True True True @@ -20,4 +24,4 @@ True True True - \ No newline at end of file + diff --git a/assets/amiibo/Amiibo.json b/assets/amiibo/Amiibo.json new file mode 100644 index 000000000..03c2c020e --- /dev/null +++ b/assets/amiibo/Amiibo.json @@ -0,0 +1,48348 @@ +{ + "amiibo": [ + { + "amiiboSeries": "Animal Crossing", + "character": "Sandy", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04380001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04380001-03000502.png", + "name": "Sandy", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "03000502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Isabelle", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "01810101", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01810101-00b40502.png", + "name": "Isabelle - Winter", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00b40502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Sonic", + "gameSeries": "Sonic", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "32000000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_32000000-00300002.png", + "name": "Sonic", + "release": { + "au": "2015-01-29", + "eu": "2015-02-20", + "jp": "2015-01-22", + "na": "2015-02-01" + }, + "tail": "00300002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Ava", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "029e0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_029e0001-013d0502.png", + "name": "Ava", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "013d0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Blanca", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01b30001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01b30001-00b50502.png", + "name": "Blanca", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00b50502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Mac", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02f80001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02f80001-01380502.png", + "name": "Mac", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01380502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Lucha", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "023c0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_023c0001-00bd0502.png", + "name": "Lucha", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00bd0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Punchy", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02630001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02630001-00750502.png", + "name": "Punchy", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00750502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Violet", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03700001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03700001-015d0502.png", + "name": "Violet", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "015d0502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Mii", + "gameSeries": "Mii", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "07c00000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_07c00000-00210002.png", + "name": "Mii Brawler", + "release": { + "au": "2015-09-26", + "eu": "2015-09-25", + "jp": "2015-09-10", + "na": "2015-11-01" + }, + "tail": "00210002", + "type": "Figure" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Wario", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c50201", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c50201-02830e02.png", + "name": "Wario - Baseball", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02830e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Tom", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "026c0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_026c0001-00c30502.png", + "name": "Tom", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00c30502", + "type": "Card" + }, + { + "amiiboSeries": "Legend Of Zelda", + "character": "Zelda", + "gameSeries": "The Legend of Zelda", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive specific crafting materials or a weapon for the character", + "write": false + } + ], + "gameID": [ + "01002B00111A2000" + ], + "gameName": "Hyrule Warriors: Age of Calamity" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a weapon rated 3 stars or higher", + "write": false + } + ], + "gameID": [ + "0100AE00096EA000" + ], + "gameName": "Hyrule Warriors: Definitive Edition" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Legend of Zelda-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a chest of loot, potentially containing gear inspired by the Legend of Zelda series", + "write": false + } + ], + "gameID": [ + "01000A10041EA000" + ], + "gameName": "The Elder Scrolls V: Skyrim" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock one of five amiibo-exclusive Chamber Dungeon chambers", + "write": false + }, + { + "Usage": "Save your Chamber Dungeon to the amiibo to share with friends", + "write": true + } + ], + "gameID": [ + "01006BB00C6F0000" + ], + "gameName": "The Legend of Zelda: Link's Awakening" + }, + { + "amiiboUsage": [ + { + "Usage": "Quickly travel between the surface and the sky", + "write": false + } + ], + "gameID": [ + "01002DA013484000" + ], + "gameName": "The Legend of Zelda: Skyward Sword HD" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive character-specific paraglider fabric", + "write": false + }, + { + "Usage": "Receive materials and a weapon or rare item", + "write": false + } + ], + "gameID": [ + "0100F2C0115B6000" + ], + "gameName": "The Legend of Zelda: Tears of the Kingdom" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive the Blue Attire", + "write": false + }, + { + "Usage": "Receive random materials", + "write": false + } + ], + "gameID": [ + "01008CF01BAAC000" + ], + "gameName": "The Legend of Zelda: Echoes of Wisdom" + } + ], + "head": "01010300", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01010300-04140902.png", + "name": "Zelda & Loftwing", + "release": { + "au": "2021-07-16", + "eu": "2021-07-16", + "jp": "2021-07-16", + "na": "2021-07-16" + }, + "tail": "04140902", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Mint", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04e60001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04e60001-00820502.png", + "name": "Mint", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00820502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Caroline", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04e30001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04e30001-01650502.png", + "name": "Caroline", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01650502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Mabel", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01880001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01880001-01120502.png", + "name": "Mabel", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01120502", + "type": "Card" + }, + { + "amiiboSeries": "Splatoon", + "character": "Inkling", + "gameSeries": "Splatoon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Splatoon-themed racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "01003BC0000A0000" + ], + "gameName": "Splatoon 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "0100C2500FC20000" + ], + "gameName": "Splatoon 3" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "08000100", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_08000100-04150402.png", + "name": "Inkling - Yellow", + "release": { + "au": "2022-11-11", + "eu": "2022-11-11", + "jp": "2022-11-11", + "na": "2022-11-11" + }, + "tail": "04150402", + "type": "Figure" + }, + { + "amiiboSeries": "Splatoon", + "character": "Shiver", + "gameSeries": "Splatoon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Splatoon-themed racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "0100C2500FC20000" + ], + "gameName": "Splatoon 3" + } + ], + "head": "08070000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_08070000-04330402.png", + "name": "Shiver", + "release": { + "au": "2023-11-17", + "eu": "2023-11-17", + "jp": "2023-11-17", + "na": "2023-11-17" + }, + "tail": "04330402", + "type": "Figure" + }, + { + "amiiboSeries": "Splatoon", + "character": "Frye", + "gameSeries": "Splatoon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Splatoon-themed racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "0100C2500FC20000" + ], + "gameName": "Splatoon 3" + } + ], + "head": "08080000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_08080000-04340402.png", + "name": "Frye", + "release": { + "au": "2023-11-17", + "eu": "2023-11-17", + "jp": "2023-11-17", + "na": "2023-11-17" + }, + "tail": "04340402", + "type": "Figure" + }, + { + "amiiboSeries": "Splatoon", + "character": "Big Man", + "gameSeries": "Splatoon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Splatoon-themed racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "0100C2500FC20000" + ], + "gameName": "Splatoon 3" + } + ], + "head": "08090000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_08090000-04350402.png", + "name": "Big Man", + "release": { + "au": "2023-11-17", + "eu": "2023-11-17", + "jp": "2023-11-17", + "na": "2023-11-17" + }, + "tail": "04350402", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Frett", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "0a1d0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0a1d0001-03d40502.png", + "name": "Frett", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03d40502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Kidd", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "035d0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_035d0001-00c90502.png", + "name": "Kidd", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00c90502", + "type": "Card" + }, + { + "amiiboSeries": "BoxBoy!", + "character": "Qbby", + "gameSeries": "BoxBoy!", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive star coins and a boost item", + "write": false + } + ], + "gameID": [ + "01004D300C5AE000" + ], + "gameName": "Kirby and the Forgotten Land" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive two Picture Pieces, a Maxim Tomato, and two Point Stars", + "write": false + } + ], + "gameID": [ + "01007E3006DDA000" + ], + "gameName": "Kirby Star Allies" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive more useful items", + "write": false + } + ], + "gameID": [ + "01006B601380E000" + ], + "gameName": "Kirby's Return to Dream Land Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive 20 Fragments", + "write": false + } + ], + "gameID": [ + "01003FB00C5A8000" + ], + "gameName": "Super Kirby Clash" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "1f400000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_1f400000-035e1002.png", + "name": "Qbby", + "release": { + "au": null, + "eu": null, + "jp": "2017-02-02", + "na": null + }, + "tail": "035e1002", + "type": "Figure" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Waluigi", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c60101", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c60101-02870e02.png", + "name": "Waluigi - Soccer", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02870e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Purrl", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02640001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02640001-01ac0502.png", + "name": "Purrl", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01ac0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Mitzi", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "025e0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_025e0001-01250502.png", + "name": "Mitzi", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01250502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Reneigh", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "0a100001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0a100001-03c70502.png", + "name": "Reneigh", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03c70502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Rasher", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "047a0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_047a0001-00600502.png", + "name": "Rasher", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00600502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Chrissy", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04a10001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04a10001-016f0502.png", + "name": "Chrissy", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "016f0502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Metal Mario", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09d00301", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09d00301-02bb0e02.png", + "name": "Metal Mario - Tennis", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02bb0e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Harriet", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01910001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01910001-004e0502.png", + "name": "Harriet", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "004e0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Daisy", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02f10001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02f10001-01450502.png", + "name": "Daisy", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01450502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Bam", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02d70001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02d70001-01300502.png", + "name": "Bam", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01300502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Anabelle", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02030001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02030001-019a0502.png", + "name": "Anabelle", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "019a0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Label", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01890001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01890001-00ab0502.png", + "name": "Labelle", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00ab0502", + "type": "Card" + }, + { + "amiiboSeries": "Yu-Gi-Oh!", + "character": "Tatsuhisa \u201cLuke\u201d Kamij\u014d", + "gameSeries": "Yu-Gi-Oh!", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive items/bonuses", + "write": false + } + ], + "gameID": [ + "01003C101454A000" + ], + "gameName": "Yu-Gi-Oh! Rush Duel Saikyo Battle Royale" + } + ], + "head": "38410001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_38410001-04251902.png", + "name": "Tatsuhisa \u201cLuke\u201d Kamij\u014d", + "release": { + "au": null, + "eu": null, + "jp": null, + "na": "2021-08-12" + }, + "tail": "04251902", + "type": "Card" + }, + { + "amiiboSeries": "Splatoon", + "character": "Smallfry", + "gameSeries": "Splatoon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Splatoon-themed racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "0100C2500FC20000" + ], + "gameName": "Splatoon 3" + } + ], + "head": "08060100", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_08060100-041c0402.png", + "name": "Smallfry", + "release": { + "au": "2022-11-11", + "eu": "2022-11-11", + "jp": "2022-11-11", + "na": "2022-11-11" + }, + "tail": "041c0402", + "type": "Figure" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Inkling", + "gameSeries": "Splatoon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a Splatoon-themed racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "01003BC0000A0000" + ], + "gameName": "Splatoon 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "0100C2500FC20000" + ], + "gameName": "Splatoon 3" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "08000100", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_08000100-03820002.png", + "name": "Inkling", + "release": { + "au": "2018-12-07", + "eu": "2018-12-07", + "jp": "2018-12-07", + "na": "2018-12-07" + }, + "tail": "03820002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Rover", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "018d0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_018d0001-010c0502.png", + "name": "Rover", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "010c0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Wendell", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01a70001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01a70001-01140502.png", + "name": "Wendell", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01140502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Ren\u00e9e", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04ba0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04ba0001-005d0502.png", + "name": "Ren\u00e9e", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "005d0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Agnes", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04890001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04890001-00ef0502.png", + "name": "Agnes", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00ef0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Resetti", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "018e0101", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_018e0101-01780502.png", + "name": "Resetti - Without Hat", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01780502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Daisy Mae", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "0a040001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0a040001-03b50502.png", + "name": "Daisy Mae", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03b50502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Merry", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "026d0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_026d0001-013f0502.png", + "name": "Merry", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "013f0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Big Top", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03250001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03250001-010a0502.png", + "name": "Big Top", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "010a0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Leif", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01b40001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01b40001-01130502.png", + "name": "Leif", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01130502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Rocco", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03900001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03900001-01850502.png", + "name": "Rocco", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01850502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Donkey Kong", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c70501", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c70501-02900e02.png", + "name": "Donkey Kong - Horse Racing", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02900e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Gladys", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04370001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04370001-01050502.png", + "name": "Gladys", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "01050502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Twiggy", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02300001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02300001-01d20502.png", + "name": "Twiggy", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01d20502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Camofrog", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "033b0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_033b0001-00fa0502.png", + "name": "Camofrog", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00fa0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Lottie", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "01c10000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01c10000-02440502.png", + "name": "Lottie", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-11-21", + "na": "2015-11-22" + }, + "tail": "02440502", + "type": "Figure" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "King K. Rool", + "gameSeries": "Donkey Kong", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "00c00000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00c00000-037b0002.png", + "name": "King K. Rool", + "release": { + "au": "2019-02-15", + "eu": "2019-02-15", + "jp": "2019-02-15", + "na": "2019-02-15" + }, + "tail": "037b0002", + "type": "Figure" + }, + { + "amiiboSeries": "Yu-Gi-Oh!", + "character": "Nail Saionji", + "gameSeries": "Yu-Gi-Oh!", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive items/bonuses", + "write": false + } + ], + "gameID": [ + "01003C101454A000" + ], + "gameName": "Yu-Gi-Oh! Rush Duel Saikyo Battle Royale" + } + ], + "head": "38450001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_38450001-04291902.png", + "name": "Nail Saionji", + "release": { + "au": null, + "eu": null, + "jp": null, + "na": "2021-08-12" + }, + "tail": "04291902", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Sephiroth", + "gameSeries": "Final Fantasy", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "36010000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_36010000-04210002.png", + "name": "Sephiroth", + "release": { + "au": "2023-01-13", + "eu": "2023-01-13", + "jp": "2023-01-13", + "na": "2023-01-13" + }, + "tail": "04210002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Deirdre", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02da0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02da0001-01330502.png", + "name": "Deirdre", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01330502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Flick", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "0a030001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0a030001-03b40502.png", + "name": "Flick", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03b40502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Lucina", + "gameSeries": "Fire Emblem", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive a Fashion Ticket and a Music Ticket, for unlocking any of the available costumes and music tracks", + "write": false + } + ], + "gameID": [ + "0100A6301214E000" + ], + "gameName": "Fire Emblem Engage" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a weapon", + "write": false + } + ], + "gameID": [ + "0100F15003E64000" + ], + "gameName": "Fire Emblem Warriors" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive better-quality randomized resources, weapons, or equipment", + "write": false + } + ], + "gameID": [ + "010071F0143EA000" + ], + "gameName": "Fire Emblem Warriors: Three Hopes" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special piece of battle music / Receive higher-quality items and materials", + "write": false + } + ], + "gameID": [ + "010055D009F78000" + ], + "gameName": "Fire Emblem: Three Houses" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "21020000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_21020000-00290002.png", + "name": "Lucina", + "release": { + "au": "2015-04-25", + "eu": "2015-04-24", + "jp": "2015-04-29", + "na": "2015-05-29" + }, + "tail": "00290002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Naomi", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02b80001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02b80001-019c0502.png", + "name": "Naomi", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "019c0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Raddle", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03470001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03470001-03020502.png", + "name": "Raddle", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "03020502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Tortimer", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01b00001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01b00001-00520502.png", + "name": "Tortimer", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00520502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Digby", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "018c0000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_018c0000-02430502.png", + "name": "Digby", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-11-21", + "na": "2015-11-13" + }, + "tail": "02430502", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Puddles", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "033e0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_033e0001-01a20502.png", + "name": "Puddles", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01a20502", + "type": "Card" + }, + { + "amiiboSeries": "Splatoon", + "character": "Octoling", + "gameSeries": "Splatoon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Splatoon-themed racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "01003BC0000A0000" + ], + "gameName": "Splatoon 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "0100C2500FC20000" + ], + "gameName": "Splatoon 3" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "08050200", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_08050200-038f0402.png", + "name": "Octoling Boy", + "release": { + "au": "2018-11-11", + "eu": "2018-11-09", + "jp": "2018-11-09", + "na": "2018-11-09" + }, + "tail": "038f0402", + "type": "Figure" + }, + { + "amiiboSeries": "Legend Of Zelda", + "character": "Guardian", + "gameSeries": "The Legend of Zelda", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive specific crafting materials or a weapon for the character", + "write": false + } + ], + "gameID": [ + "01002B00111A2000" + ], + "gameName": "Hyrule Warriors: Age of Calamity" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Legend of Zelda-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a chest of loot, potentially containing gear inspired by the Legend of Zelda series", + "write": false + } + ], + "gameID": [ + "01000A10041EA000" + ], + "gameName": "The Elder Scrolls V: Skyrim" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive materials and a rare or exclusive item", + "write": false + } + ], + "gameID": [ + "01007EF00011E000" + ], + "gameName": "The Legend of Zelda: Breath of the Wild" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock one of five amiibo-exclusive Chamber Dungeon chambers", + "write": false + }, + { + "Usage": "Save your Chamber Dungeon to the amiibo to share with friends", + "write": true + } + ], + "gameID": [ + "01006BB00C6F0000" + ], + "gameName": "The Legend of Zelda: Link's Awakening" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive character-specific paraglider fabric", + "write": false + }, + { + "Usage": "Receive materials and a weapon or rare item", + "write": false + } + ], + "gameID": [ + "0100F2C0115B6000" + ], + "gameName": "The Legend of Zelda: Tears of the Kingdom" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive the Black Cat Clothes", + "write": false + }, + { + "Usage": "Receive random materials", + "write": false + } + ], + "gameID": [ + "01008CF01BAAC000" + ], + "gameName": "The Legend of Zelda: Echoes of Wisdom" + } + ], + "head": "01400000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01400000-03550902.png", + "name": "Guardian", + "release": { + "au": "2017-03-03", + "eu": "2017-03-03", + "jp": "2017-03-03", + "na": "2017-03-03" + }, + "tail": "03550902", + "type": "Figure" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Squirtle", + "gameSeries": "Pokemon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "19070000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_19070000-03840002.png", + "name": "Squirtle", + "release": { + "au": "2019-09-20", + "eu": "2019-09-20", + "jp": "2019-09-20", + "na": "2019-09-20" + }, + "tail": "03840002", + "type": "Figure" + }, + { + "amiiboSeries": "Super Nintendo World", + "character": "Peach", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a costume based on the character (short-hair version)", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive weapon for the character and their Rabbid counterpart", + "write": false + } + ], + "gameID": [ + "010067300059A000" + ], + "gameName": "Mario + Rabbids: Kingdom Battle" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a life-up heart", + "write": false + } + ], + "gameID": [ + "0100000000010000" + ], + "gameName": "Super Mario Odyssey" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01006000040C2000" + ], + "gameName": "Yoshi's Crafted World" + } + ], + "head": "00020003", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00020003-039dff02.png", + "name": "Peach - Power Up Band", + "release": { + "au": null, + "eu": null, + "jp": "2021-02-04", + "na": null + }, + "tail": "039dff02", + "type": "Band" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Jacob", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02380001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02380001-02f80502.png", + "name": "Jacob", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "02f80502", + "type": "Card" + }, + { + "amiiboSeries": "Super Mario Bros.", + "character": "Boo", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock the Chain Chomp weapon", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "00170000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00170000-02680102.png", + "name": "Boo", + "release": { + "au": "2016-10-08", + "eu": "2016-10-07", + "jp": "2016-10-20", + "na": "2016-11-04" + }, + "tail": "02680102", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Pashmina", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "035e0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_035e0001-018e0502.png", + "name": "Pashmina", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "018e0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Paolo", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03280001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03280001-02eb0502.png", + "name": "Paolo", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "02eb0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "June", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "028a0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_028a0001-02e90502.png", + "name": "June", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "02e90502", + "type": "Card" + }, + { + "amiiboSeries": "Others", + "character": "Mario Cereal", + "gameSeries": "Kellogs", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "See the location of a Power Moon (as \"delicious amiibo\")", + "write": false + } + ], + "gameID": [ + "0100000000010000" + ], + "gameName": "Super Mario Odyssey" + } + ], + "head": "37400001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_37400001-03741402.png", + "name": "Super Mario Cereal", + "release": { + "au": null, + "eu": null, + "jp": null, + "na": "2017-12-11" + }, + "tail": "03741402", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Link", + "gameSeries": "The Legend of Zelda", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a costume based on the character (short-hair version)", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive specific crafting materials or a weapon for the character", + "write": false + } + ], + "gameID": [ + "01002B00111A2000" + ], + "gameName": "Hyrule Warriors: Age of Calamity" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a weapon rated 3 stars or higher", + "write": false + } + ], + "gameID": [ + "0100AE00096EA000" + ], + "gameName": "Hyrule Warriors: Definitive Edition" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Legend of Zelda-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a chest of loot, potentially containing gear inspired by the Legend of Zelda series", + "write": false + } + ], + "gameID": [ + "01000A10041EA000" + ], + "gameName": "The Elder Scrolls V: Skyrim" + }, + { + "amiiboUsage": [ + { + "Usage": "Bring Epona into the game as a rideable horse", + "write": false + }, + { + "Usage": "Receive materials and a rare or exclusive item", + "write": false + } + ], + "gameID": [ + "01007EF00011E000" + ], + "gameName": "The Legend of Zelda: Breath of the Wild" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock one of five amiibo-exclusive Chamber Dungeon chambers", + "write": false + }, + { + "Usage": "Save your Chamber Dungeon to the amiibo to share with friends", + "write": true + } + ], + "gameID": [ + "01006BB00C6F0000" + ], + "gameName": "The Legend of Zelda: Link's Awakening" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive character-specific paraglider fabric", + "write": false + }, + { + "Usage": "Bring Epona into the game as a rideable horse", + "write": false + }, + { + "Usage": "Receive materials and a weapon or rare item", + "write": false + } + ], + "gameID": [ + "0100F2C0115B6000" + ], + "gameName": "The Legend of Zelda: Tears of the Kingdom" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive the Red Tunic", + "write": false + }, + { + "Usage": "Receive random materials", + "write": false + } + ], + "gameID": [ + "01008CF01BAAC000" + ], + "gameName": "The Legend of Zelda: Echoes of Wisdom" + } + ], + "head": "01000000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01000000-00040002.png", + "name": "Link", + "release": { + "au": "2014-11-29", + "eu": "2014-11-28", + "jp": "2014-12-06", + "na": "2014-11-21" + }, + "tail": "00040002", + "type": "Figure" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Cloud Strife", + "gameSeries": "Final Fantasy", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "36000000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_36000000-02590002.png", + "name": "Cloud", + "release": { + "au": "2017-07-22", + "eu": "2017-07-21", + "jp": "2017-07-21", + "na": "2017-07-21" + }, + "tail": "02590002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Rudy", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02710001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02710001-019b0502.png", + "name": "Rudy", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "019b0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Flurry", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03840001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03840001-00860502.png", + "name": "Flurry", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00860502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Tammy", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "028e0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_028e0001-019e0502.png", + "name": "Tammy", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "019e0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Teddy", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02140001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02140001-00e40502.png", + "name": "Teddy", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00e40502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Frank", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04510001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04510001-015e0502.png", + "name": "Frank", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "015e0502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Peach", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c20501", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c20501-02770e02.png", + "name": "Peach - Horse Racing", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02770e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Stu", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "024d0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_024d0001-02f60502.png", + "name": "Stu", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "02f60502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Roscoe", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03a80001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03a80001-00910502.png", + "name": "Roscoe", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00910502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Ike", + "gameSeries": "Fire Emblem", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive a Fashion Ticket and a Music Ticket, for unlocking any of the available costumes and music tracks", + "write": false + } + ], + "gameID": [ + "0100A6301214E000" + ], + "gameName": "Fire Emblem Engage" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a weapon", + "write": false + } + ], + "gameID": [ + "0100F15003E64000" + ], + "gameName": "Fire Emblem Warriors" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive better-quality randomized resources, weapons, or equipment", + "write": false + } + ], + "gameID": [ + "010071F0143EA000" + ], + "gameName": "Fire Emblem Warriors: Three Hopes" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special piece of battle music / Receive higher-quality items and materials", + "write": false + } + ], + "gameID": [ + "010055D009F78000" + ], + "gameName": "Fire Emblem: Three Houses" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "21010000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_21010000-00180002.png", + "name": "Ike", + "release": { + "au": "2015-01-29", + "eu": "2015-01-23", + "jp": "2015-01-22", + "na": "2015-02-01" + }, + "tail": "00180002", + "type": "Figure" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Luigi", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c10401", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c10401-02710e02.png", + "name": "Luigi - Golf", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02710e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Rodney", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03810001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03810001-00d50502.png", + "name": "Rodney", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00d50502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Baby Mario", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09cc0101", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09cc0101-02a50e02.png", + "name": "Baby Mario - Soccer", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02a50e02", + "type": "Card" + }, + { + "amiiboSeries": "Power Pros", + "character": "Daijobu", + "gameSeries": "Power Pros", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive in-game items and power-ups / Save items to your card after playing with friends to bring them home", + "write": true + } + ], + "gameID": [ + "0100E9C00BF28000" + ], + "gameName": "Jikkyou Powerful Pro Baseball" + } + ], + "head": "38050001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_38050001-03981702.png", + "name": "Daijobu", + "release": { + "au": null, + "eu": null, + "jp": "2019-06-27", + "na": null + }, + "tail": "03981702", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Bruce", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02d90001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02d90001-01c80502.png", + "name": "Bruce", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01c80502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Bree", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "040f0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_040f0001-01500502.png", + "name": "Bree", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01500502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Bangle", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04fd0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04fd0001-007b0502.png", + "name": "Bangle", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "007b0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Stitches", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02820001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02820001-01810502.png", + "name": "Stitches", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01810502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Aurora", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "045f0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_045f0001-01a80502.png", + "name": "Aurora", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01a80502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Iggly", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "046a0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_046a0001-01d00502.png", + "name": "Iggly", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01d00502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Vic", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02520001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02520001-00fe0502.png", + "name": "Vic", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00fe0502", + "type": "Card" + }, + { + "amiiboSeries": "Monster Hunter Rise", + "character": "Palico", + "gameSeries": "Monster Hunter", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock special layered armor / Enter daily lottery for a variety of useful items", + "write": false + } + ], + "gameID": [ + "0100B04011742000" + ], + "gameName": "Monster Hunter Rise" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the Hunter Sticker Set / Have Tsukino read your fortune and receive a random item", + "write": false + } + ], + "gameID": [ + "0100E21011446000" + ], + "gameName": "Monster Hunter Stories 2: Wings of Ruin" + } + ], + "head": "35090100", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_35090100-042b1802.png", + "name": "Palico", + "release": { + "au": "2022-06-30", + "eu": "2022-06-30", + "jp": "2022-06-30", + "na": "2022-06-30" + }, + "tail": "042b1802", + "type": "Figure" + }, + { + "amiiboSeries": "Super Nintendo World", + "character": "Mario", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive weapon for the character and their Rabbid counterpart", + "write": false + } + ], + "gameID": [ + "010067300059A000" + ], + "gameName": "Mario + Rabbids: Kingdom Battle" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a character-based costume", + "write": false + }, + { + "Usage": "Gain temporary invincibility", + "write": false + } + ], + "gameID": [ + "0100000000010000" + ], + "gameName": "Super Mario Odyssey" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01006000040C2000" + ], + "gameName": "Yoshi's Crafted World" + } + ], + "head": "00000003", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00000003-0430ff02.png", + "name": "Golden - Power Up Band", + "release": { + "au": null, + "eu": null, + "jp": "2023-03-18", + "na": null + }, + "tail": "0430ff02", + "type": "Band" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Amelia", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "044c0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_044c0001-008e0502.png", + "name": "Amelia", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "008e0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Miranda", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03130001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03130001-01210502.png", + "name": "Miranda", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01210502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Katrina", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01a50001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01a50001-01720502.png", + "name": "Katrina", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01720502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Audie", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "0a0c0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0a0c0001-03c30502.png", + "name": "Audie", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03c30502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Diddy Kong", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c80501", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c80501-02950e02.png", + "name": "Diddy Kong - Horse Racing", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02950e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Bill", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03070001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03070001-00640502.png", + "name": "Bill", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00640502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Anchovy", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "022f0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_022f0001-011e0502.png", + "name": "Anchovy", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "011e0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Harvey", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "0a050001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0a050001-03b80502.png", + "name": "Harvey", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03b80502", + "type": "Card" + }, + { + "amiiboSeries": "Legend Of Zelda", + "character": "Link", + "gameSeries": "The Legend of Zelda", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a costume based on the character (short-hair version)", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive specific crafting materials or a weapon for the character", + "write": false + } + ], + "gameID": [ + "01002B00111A2000" + ], + "gameName": "Hyrule Warriors: Age of Calamity" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a weapon rated 3 stars or higher", + "write": false + } + ], + "gameID": [ + "0100AE00096EA000" + ], + "gameName": "Hyrule Warriors: Definitive Edition" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Legend of Zelda-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a chest of loot, potentially containing gear inspired by the Legend of Zelda series", + "write": false + } + ], + "gameID": [ + "01000A10041EA000" + ], + "gameName": "The Elder Scrolls V: Skyrim" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive materials and a rare or exclusive item", + "write": false + } + ], + "gameID": [ + "01007EF00011E000" + ], + "gameName": "The Legend of Zelda: Breath of the Wild" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock one of five amiibo-exclusive Chamber Dungeon chambers", + "write": false + }, + { + "Usage": "Save your Chamber Dungeon to the amiibo to share with friends", + "write": true + } + ], + "gameID": [ + "01006BB00C6F0000" + ], + "gameName": "The Legend of Zelda: Link's Awakening" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive character-specific paraglider fabric", + "write": false + }, + { + "Usage": "Receive materials and a weapon or rare item", + "write": false + } + ], + "gameID": [ + "0100F2C0115B6000" + ], + "gameName": "The Legend of Zelda: Tears of the Kingdom" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive the Red Tunic", + "write": false + }, + { + "Usage": "Receive random materials", + "write": false + } + ], + "gameID": [ + "01008CF01BAAC000" + ], + "gameName": "The Legend of Zelda: Echoes of Wisdom" + } + ], + "head": "01000000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01000000-03530902.png", + "name": "Link - Archer", + "release": { + "au": "2017-03-03", + "eu": "2017-03-03", + "jp": "2017-03-03", + "na": "2017-03-03" + }, + "tail": "03530902", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Tommy", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01860101", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01860101-00af0502.png", + "name": "Tommy - Uniform", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00af0502", + "type": "Card" + }, + { + "amiiboSeries": "Splatoon", + "character": "Octoling", + "gameSeries": "Splatoon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Splatoon-themed racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "01003BC0000A0000" + ], + "gameName": "Splatoon 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "0100C2500FC20000" + ], + "gameName": "Splatoon 3" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "08050100", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_08050100-038e0402.png", + "name": "Octoling Girl", + "release": { + "au": "2018-11-11", + "eu": "2018-11-09", + "jp": "2018-11-09", + "na": "2018-11-09" + }, + "tail": "038e0402", + "type": "Figure" + }, + { + "amiiboSeries": "Legend Of Zelda", + "character": "Link", + "gameSeries": "The Legend of Zelda", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a costume based on the character (short-hair version)", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive specific crafting materials or a weapon for the character", + "write": false + } + ], + "gameID": [ + "01002B00111A2000" + ], + "gameName": "Hyrule Warriors: Age of Calamity" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a weapon rated 3 stars or higher", + "write": false + } + ], + "gameID": [ + "0100AE00096EA000" + ], + "gameName": "Hyrule Warriors: Definitive Edition" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a chest of loot, potentially containing gear inspired by the Legend of Zelda series", + "write": false + } + ], + "gameID": [ + "01000A10041EA000" + ], + "gameName": "The Elder Scrolls V: Skyrim" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive materials and a rare or exclusive item", + "write": false + } + ], + "gameID": [ + "01007EF00011E000" + ], + "gameName": "The Legend of Zelda: Breath of the Wild" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock one of five amiibo-exclusive Chamber Dungeon chambers", + "write": false + }, + { + "Usage": "Save your Chamber Dungeon to the amiibo to share with friends", + "write": true + } + ], + "gameID": [ + "01006BB00C6F0000" + ], + "gameName": "The Legend of Zelda: Link's Awakening" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive character-specific paraglider fabric", + "write": false + }, + { + "Usage": "Receive materials and a weapon or rare item", + "write": false + } + ], + "gameID": [ + "0100F2C0115B6000" + ], + "gameName": "The Legend of Zelda: Tears of the Kingdom" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive the Red Tunic", + "write": false + }, + { + "Usage": "Receive random materials", + "write": false + } + ], + "gameID": [ + "01008CF01BAAC000" + ], + "gameName": "The Legend of Zelda: Echoes of Wisdom" + } + ], + "head": "01000000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01000000-034f0902.png", + "name": "8-Bit Link", + "release": { + "au": "2016-12-03", + "eu": "2016-12-02", + "jp": "2016-12-01", + "na": "2016-12-02" + }, + "tail": "034f0902", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Maggie", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04820001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04820001-02fd0502.png", + "name": "Maggie", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "02fd0502", + "type": "Card" + }, + { + "amiiboSeries": "Mega Man", + "character": "Mega Man", + "gameSeries": "Megaman", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive E Tanks and other useful in-game items", + "write": false + } + ], + "gameID": [ + "0100B0C0086B0000" + ], + "gameName": "Mega Man 11" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock 11 exclusive challenge stages designed by fans", + "write": false + } + ], + "gameID": [ + "01002D4007AE0000" + ], + "gameName": "Mega Man Legacy Collection" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock new platforming challenges", + "write": false + } + ], + "gameID": [ + "0100842008EC4000" + ], + "gameName": "Mega Man Legacy Collection 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a lot of BP / Receive better supplies (compared to other amiibo)", + "write": false + } + ], + "gameID": [ + "0100643002136000" + ], + "gameName": "Resident Evil Revelations" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a lot of BP / Receive better supplies (compared to other amiibo)", + "write": false + } + ], + "gameID": [ + "010095300212A000" + ], + "gameName": "Resident Evil Revelations 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "34800000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_34800000-03791502.png", + "name": "Mega Man", + "release": { + "au": null, + "eu": null, + "jp": "2018-10-04", + "na": "2018-10-02" + }, + "tail": "03791502", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Orville", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "0a000001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0a000001-03ab0502.png", + "name": "Orville", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03ab0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Chai", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + }, + { + "Usage": "Unlock special furniture items and a poster based on the card's Sanrio character", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "032e0101", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_032e0101-031c0502.png", + "name": "Chai", + "release": { + "au": null, + "eu": "2016-11-25", + "jp": "2016-11-03", + "na": null + }, + "tail": "031c0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Dom", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "0a0b0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0a0b0001-03c20502.png", + "name": "Dom", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03c20502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Kapp'n", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "01960000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01960000-024e0502.png", + "name": "Kapp'n", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-03-24", + "na": "2016-03-18" + }, + "tail": "024e0502", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Limberg", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "040d0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_040d0001-00780502.png", + "name": "Limberg", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00780502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Weber", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03120001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03120001-03090502.png", + "name": "Weber", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "03090502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Bunnie", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04940001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04940001-009a0502.png", + "name": "Bunnie", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "009a0502", + "type": "Card" + }, + { + "amiiboSeries": "Yoshi's Woolly World", + "character": "Poochy", + "gameSeries": "Yoshi's Woolly World", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01006000040C2000" + ], + "gameName": "Yoshi's Crafted World" + } + ], + "head": "00800102", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00800102-035d0302.png", + "name": "Poochy", + "release": { + "au": "2017-02-04", + "eu": "2017-02-03", + "jp": "2017-01-19", + "na": "2017-02-03" + }, + "tail": "035d0302", + "type": "Yarn" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Waluigi", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c60301", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c60301-02890e02.png", + "name": "Waluigi - Tennis", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02890e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Pelly", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01a00001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01a00001-010f0502.png", + "name": "Pelly", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "010f0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Frobert", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "033a0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_033a0001-01cc0502.png", + "name": "Frobert", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01cc0502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Birdo", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09ce0501", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09ce0501-02b30e02.png", + "name": "Birdo - Horse Racing", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02b30e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Tasha", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04ea0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04ea0001-03180502.png", + "name": "Tasha", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "03180502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Robin", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "022e0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_022e0001-01d30502.png", + "name": "Robin", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01d30502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Alfonso", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02c30001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02c30001-00dc0502.png", + "name": "Alfonso", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00dc0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Peck", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "023e0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_023e0001-00d10502.png", + "name": "Peck", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00d10502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Baby Luigi", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09cd0201", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09cd0201-02ab0e02.png", + "name": "Baby Luigi - Baseball", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02ab0e02", + "type": "Card" + }, + { + "amiiboSeries": "Super Mario Bros.", + "character": "Rosalina", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "00040000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00040000-02620102.png", + "name": "Rosalina", + "release": { + "au": "2016-10-08", + "eu": "2016-10-07", + "jp": "2016-10-20", + "na": "2016-11-04" + }, + "tail": "02620102", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Cyrus", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "018b0000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_018b0000-02460502.png", + "name": "Cyrus", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-11-21", + "na": "2015-11-13" + }, + "tail": "02460502", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Frita", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04d00001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04d00001-01960502.png", + "name": "Frita", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01960502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Sprinkle", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "046d0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_046d0001-00f30502.png", + "name": "Sprinkle", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00f30502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Bella", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "040e0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_040e0001-00880502.png", + "name": "Bella", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00880502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Drago", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02cb0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02cb0001-01360502.png", + "name": "Drago", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01360502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Grams", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01990001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01990001-01160502.png", + "name": "Grams", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01160502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Corrin", + "gameSeries": "Fire Emblem", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive a Fashion Ticket and a Music Ticket, for unlocking any of the available costumes and music tracks", + "write": false + } + ], + "gameID": [ + "0100A6301214E000" + ], + "gameName": "Fire Emblem Engage" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive better-quality randomized resources, weapons, or equipment", + "write": false + } + ], + "gameID": [ + "010071F0143EA000" + ], + "gameName": "Fire Emblem Warriors: Three Hopes" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special piece of battle music / Receive higher-quality items and materials", + "write": false + } + ], + "gameID": [ + "010055D009F78000" + ], + "gameName": "Fire Emblem: Three Houses" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "21050000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_21050000-025a0002.png", + "name": "Corrin", + "release": { + "au": "2017-07-22", + "eu": "2017-07-21", + "jp": "2017-07-21", + "na": "2017-07-21" + }, + "tail": "025a0002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "OHare", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04a30001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04a30001-01c90502.png", + "name": "OHare", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01c90502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Dark Pit", + "gameSeries": "Kid Icarus", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "07410000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_07410000-00200002.png", + "name": "Dark Pit", + "release": { + "au": "2015-07-04", + "eu": "2015-06-26", + "jp": "2015-06-11", + "na": "2015-07-31" + }, + "tail": "00200002", + "type": "Figure" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Rosalina", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "00040100", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00040100-00130002.png", + "name": "Rosalina & Luma", + "release": { + "au": "2015-01-29", + "eu": "2015-01-23", + "jp": "2015-01-22", + "na": "2015-02-01" + }, + "tail": "00130002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Redd", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01a80001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01a80001-004f0502.png", + "name": "Redd", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "004f0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Mabel", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "01880000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01880000-02410502.png", + "name": "Mabel", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-11-21", + "na": "2015-11-13" + }, + "tail": "02410502", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Hamphrey", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03850001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03850001-01060502.png", + "name": "Hamphrey", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "01060502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Pave", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01ab0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01ab0001-017c0502.png", + "name": "Pave", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "017c0502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Wario", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c50101", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c50101-02820e02.png", + "name": "Wario - Soccer", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02820e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Alli", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02c40001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02c40001-00670502.png", + "name": "Alli", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00670502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Yoshi", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c40401", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c40401-02800e02.png", + "name": "Yoshi - Golf", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02800e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Bones", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02ee0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02ee0001-01990502.png", + "name": "Bones", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01990502", + "type": "Card" + }, + { + "amiiboSeries": "Metroid", + "character": "E.M.M.I.", + "gameSeries": "Metroid", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Metroid-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Permanently increase missile capacity by 10", + "write": false + } + ], + "gameID": [ + "010093801237C000" + ], + "gameName": "Metroid Dread" + }, + { + "amiiboUsage": [ + { + "Usage": "Replenish a random amount of missiles once per day", + "write": false + } + ], + "gameID": [ + "010093801237C000" + ], + "gameName": "Metroid Dread" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "05c40000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_05c40000-04131302.png", + "name": "E.M.M.I.", + "release": { + "au": "2021-10-08", + "eu": "2021-11-05", + "jp": "2021-10-08", + "na": "2021-10-08" + }, + "tail": "04131302", + "type": "Figure" + }, + { + "amiiboSeries": "Legend Of Zelda", + "character": "Link", + "gameSeries": "The Legend of Zelda", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a costume based on the character (short-hair version)", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive specific crafting materials or a weapon for the character", + "write": false + } + ], + "gameID": [ + "01002B00111A2000" + ], + "gameName": "Hyrule Warriors: Age of Calamity" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a weapon rated 3 stars or higher", + "write": false + } + ], + "gameID": [ + "0100AE00096EA000" + ], + "gameName": "Hyrule Warriors: Definitive Edition" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Legend of Zelda-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a chest of loot, potentially containing gear inspired by the Legend of Zelda series", + "write": false + } + ], + "gameID": [ + "01000A10041EA000" + ], + "gameName": "The Elder Scrolls V: Skyrim" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive materials and a rare or exclusive item", + "write": false + } + ], + "gameID": [ + "01007EF00011E000" + ], + "gameName": "The Legend of Zelda: Breath of the Wild" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock one of five amiibo-exclusive Chamber Dungeon chambers", + "write": false + }, + { + "Usage": "Save your Chamber Dungeon to the amiibo to share with friends", + "write": true + } + ], + "gameID": [ + "01006BB00C6F0000" + ], + "gameName": "The Legend of Zelda: Link's Awakening" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive character-specific paraglider fabric", + "write": false + }, + { + "Usage": "Receive materials and a weapon or rare item", + "write": false + } + ], + "gameID": [ + "0100F2C0115B6000" + ], + "gameName": "The Legend of Zelda: Tears of the Kingdom" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive the Red Tunic", + "write": false + }, + { + "Usage": "Receive random materials", + "write": false + } + ], + "gameID": [ + "01008CF01BAAC000" + ], + "gameName": "The Legend of Zelda: Echoes of Wisdom" + } + ], + "head": "01000000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01000000-03540902.png", + "name": "Link - Rider", + "release": { + "au": "2017-03-03", + "eu": "2017-03-03", + "jp": "2017-03-03", + "na": "2017-03-03" + }, + "tail": "03540902", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Gaston", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04980001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04980001-014a0502.png", + "name": "Gaston", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "014a0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Quillson", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03180001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03180001-006c0502.png", + "name": "Quillson", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "006c0502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Pink Gold Peach", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09d10101", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09d10101-02be0e02.png", + "name": "Pink Gold Peach - Soccer", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02be0e02", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Wii Fit Trainer", + "gameSeries": "Wii Fit", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "07000000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_07000000-00070002.png", + "name": "Wii Fit Trainer", + "release": { + "au": "2014-11-29", + "eu": "2014-11-28", + "jp": "2014-12-06", + "na": "2014-11-21" + }, + "tail": "00070002", + "type": "Figure" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Wario", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a character-based costume", + "write": false + } + ], + "gameID": [ + "0100000000010000" + ], + "gameName": "Super Mario Odyssey" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "00070000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00070000-001a0002.png", + "name": "Wario", + "release": { + "au": "2015-04-25", + "eu": "2015-04-24", + "jp": "2015-04-29", + "na": "2015-05-29" + }, + "tail": "001a0002", + "type": "Figure" + }, + { + "amiiboSeries": "Chibi-Robo!", + "character": "Chibi-Robo", + "gameSeries": "Chibi Robo", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "22c00000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_22c00000-003a0202.png", + "name": "Chibi Robo", + "release": { + "au": "2015-11-07", + "eu": "2015-11-06", + "jp": "2015-10-08", + "na": "2015-10-09" + }, + "tail": "003a0202", + "type": "Figure" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Diddy Kong", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a character-based costume", + "write": false + } + ], + "gameID": [ + "0100000000010000" + ], + "gameName": "Super Mario Odyssey" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "00090000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00090000-000d0002.png", + "name": "Diddy Kong", + "release": { + "au": "2014-12-12", + "eu": "2014-12-19", + "jp": "2014-12-06", + "na": "2014-12-14" + }, + "tail": "000d0002", + "type": "Figure" + }, + { + "amiiboSeries": "Monster Hunter", + "character": "Razewing Ratha", + "gameSeries": "Monster Hunter", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock Monster Hunter Stories 2 sticker set", + "write": false + } + ], + "gameID": [ + "0100B04011742000" + ], + "gameName": "Monster Hunter Rise" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a character-based costume for Navirou", + "write": false + } + ], + "gameID": [ + "010069301B1D4000" + ], + "gameName": "Monster Hunter Stories" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-specific special layered armor set / Have Tsukino read your fortune and receive a random item", + "write": false + } + ], + "gameID": [ + "0100E21011446000" + ], + "gameName": "Monster Hunter Stories 2: Wings of Ruin" + } + ], + "head": "35050000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_35050000-040c0f02.png", + "name": "Razewing Ratha", + "release": { + "au": "2021-07-09", + "eu": "2021-07-09", + "jp": "2021-07-09", + "na": "2021-07-09" + }, + "tail": "040c0f02", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Zipper", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01ac0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01ac0001-017f0502.png", + "name": "Zipper", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "017f0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Pierce", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "044d0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_044d0001-01930502.png", + "name": "Pierce", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01930502", + "type": "Card" + }, + { + "amiiboSeries": "Power Pros", + "character": "Ikari", + "gameSeries": "Power Pros", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive in-game items and power-ups / Save items to your card after playing with friends to bring them home", + "write": true + } + ], + "gameID": [ + "0100E9C00BF28000" + ], + "gameName": "Jikkyou Powerful Pro Baseball" + } + ], + "head": "38010001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_38010001-03941702.png", + "name": "Ikari", + "release": { + "au": null, + "eu": null, + "jp": "2019-06-27", + "na": null + }, + "tail": "03941702", + "type": "Card" + }, + { + "amiiboSeries": "Legend Of Zelda", + "character": "Link", + "gameSeries": "The Legend of Zelda", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a costume based on the character (short-hair version)", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive specific crafting materials or a weapon for the character", + "write": false + } + ], + "gameID": [ + "01002B00111A2000" + ], + "gameName": "Hyrule Warriors: Age of Calamity" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a weapon rated 3 stars or higher", + "write": false + } + ], + "gameID": [ + "0100AE00096EA000" + ], + "gameName": "Hyrule Warriors: Definitive Edition" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Legend of Zelda-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a chest of loot, potentially containing gear inspired by the Legend of Zelda series", + "write": false + } + ], + "gameID": [ + "01000A10041EA000" + ], + "gameName": "The Elder Scrolls V: Skyrim" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive materials and a rare or exclusive item", + "write": false + } + ], + "gameID": [ + "01007EF00011E000" + ], + "gameName": "The Legend of Zelda: Breath of the Wild" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock one of five amiibo-exclusive Chamber Dungeon chambers", + "write": false + }, + { + "Usage": "Save your Chamber Dungeon to the amiibo to share with friends", + "write": true + } + ], + "gameID": [ + "01006BB00C6F0000" + ], + "gameName": "The Legend of Zelda: Link's Awakening" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive character-specific paraglider fabric", + "write": false + }, + { + "Usage": "Receive materials and a weapon or rare item", + "write": false + } + ], + "gameID": [ + "0100F2C0115B6000" + ], + "gameName": "The Legend of Zelda: Tears of the Kingdom" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive the Red Tunic", + "write": false + }, + { + "Usage": "Receive random materials", + "write": false + } + ], + "gameID": [ + "01008CF01BAAC000" + ], + "gameName": "The Legend of Zelda: Echoes of Wisdom" + } + ], + "head": "01000100", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01000100-03500902.png", + "name": "Toon Link - The Wind Waker", + "release": { + "au": "2016-12-03", + "eu": "2016-12-02", + "jp": "2016-12-01", + "na": "2016-12-02" + }, + "tail": "03500902", + "type": "Figure" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Hero", + "gameSeries": "Dragon Quest", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "36400000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_36400000-03a20002.png", + "name": "Hero", + "release": { + "au": "2020-09-25", + "eu": "2020-09-25", + "jp": "2020-09-25", + "na": "2020-10-05" + }, + "tail": "03a20002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Kapp'n", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01960001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01960001-00480502.png", + "name": "Kapp'n", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00480502", + "type": "Card" + }, + { + "amiiboSeries": "Super Nintendo World", + "character": "Luigi", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive weapon for the character and their Rabbid counterpart", + "write": false + } + ], + "gameID": [ + "010067300059A000" + ], + "gameName": "Mario + Rabbids: Kingdom Battle" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a character-based costume", + "write": false + } + ], + "gameID": [ + "0100000000010000" + ], + "gameName": "Super Mario Odyssey" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "00010003", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00010003-039cff02.png", + "name": "Luigi - Power Up Band", + "release": { + "au": null, + "eu": null, + "jp": "2021-02-04", + "na": null + }, + "tail": "039cff02", + "type": "Band" + }, + { + "amiiboSeries": "Splatoon", + "character": "Inkling", + "gameSeries": "Splatoon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Splatoon-themed racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "01003BC0000A0000" + ], + "gameName": "Splatoon 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "0100C2500FC20000" + ], + "gameName": "Splatoon 3" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "08000100", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_08000100-003e0402.png", + "name": "Inkling Girl", + "release": { + "au": "2015-05-30", + "eu": "2015-05-29", + "jp": "2015-05-28", + "na": "2015-05-29" + }, + "tail": "003e0402", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Lobo", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "050c0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_050c0001-01c10502.png", + "name": "Lobo", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01c10502", + "type": "Card" + }, + { + "amiiboSeries": "Legend Of Zelda", + "character": "Zelda", + "gameSeries": "The Legend of Zelda", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive specific crafting materials or a weapon for the character", + "write": false + } + ], + "gameID": [ + "01002B00111A2000" + ], + "gameName": "Hyrule Warriors: Age of Calamity" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a weapon rated 3 stars or higher", + "write": false + } + ], + "gameID": [ + "0100AE00096EA000" + ], + "gameName": "Hyrule Warriors: Definitive Edition" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Legend of Zelda-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a chest of loot, potentially containing gear inspired by the Legend of Zelda series", + "write": false + } + ], + "gameID": [ + "01000A10041EA000" + ], + "gameName": "The Elder Scrolls V: Skyrim" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive materials and a rare or exclusive item", + "write": false + } + ], + "gameID": [ + "01007EF00011E000" + ], + "gameName": "The Legend of Zelda: Breath of the Wild" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock one of five amiibo-exclusive Chamber Dungeon chambers", + "write": false + }, + { + "Usage": "Save your Chamber Dungeon to the amiibo to share with friends", + "write": true + } + ], + "gameID": [ + "01006BB00C6F0000" + ], + "gameName": "The Legend of Zelda: Link's Awakening" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive character-specific paraglider fabric", + "write": false + }, + { + "Usage": "Receive materials and a weapon or rare item", + "write": false + } + ], + "gameID": [ + "0100F2C0115B6000" + ], + "gameName": "The Legend of Zelda: Tears of the Kingdom" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive the Blue Attire", + "write": false + }, + { + "Usage": "Receive random materials", + "write": false + } + ], + "gameID": [ + "01008CF01BAAC000" + ], + "gameName": "The Legend of Zelda: Echoes of Wisdom" + } + ], + "head": "01010000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01010000-03560902.png", + "name": "Zelda", + "release": { + "au": "2017-03-03", + "eu": "2017-03-03", + "jp": "2017-03-03", + "na": "2017-03-03" + }, + "tail": "03560902", + "type": "Figure" + }, + { + "amiiboSeries": "Monster Hunter", + "character": "Rathian", + "gameSeries": "Monster Hunter", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock Monster Hunter Stories 2 sticker set", + "write": false + } + ], + "gameID": [ + "0100B04011742000" + ], + "gameName": "Monster Hunter Rise" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a character-based monstie egg", + "write": false + } + ], + "gameID": [ + "010069301B1D4000" + ], + "gameName": "Monster Hunter Stories" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the Hakum Rider Outfit layered armor set / Have Tsukino read your fortune and receive a random item", + "write": false + } + ], + "gameID": [ + "0100E21011446000" + ], + "gameName": "Monster Hunter Stories 2: Wings of Ruin" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a lot of BP / Receive better supplies (compared to other amiibo)", + "write": false + } + ], + "gameID": [ + "0100643002136000" + ], + "gameName": "Resident Evil Revelations" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a lot of BP / Receive better supplies (compared to other amiibo)", + "write": false + } + ], + "gameID": [ + "010095300212A000" + ], + "gameName": "Resident Evil Revelations 2" + } + ], + "head": "35020100", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_35020100-02e40f02.png", + "name": "Rathian and Cheval", + "release": { + "au": null, + "eu": null, + "jp": "2016-12-08", + "na": null + }, + "tail": "02e40f02", + "type": "Figure" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Waluigi", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c60501", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c60501-028b0e02.png", + "name": "Waluigi - Horse Racing", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "028b0e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Quinn", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "0a180001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0a180001-03cf0502.png", + "name": "Quinn", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03cf0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Digby", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "018c0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_018c0001-004c0502.png", + "name": "Digby", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "004c0502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Link", + "gameSeries": "The Legend of Zelda", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a costume based on the character (short-hair version)", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive specific crafting materials or a weapon for the character", + "write": false + } + ], + "gameID": [ + "01002B00111A2000" + ], + "gameName": "Hyrule Warriors: Age of Calamity" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a weapon rated 3 stars or higher", + "write": false + } + ], + "gameID": [ + "0100AE00096EA000" + ], + "gameName": "Hyrule Warriors: Definitive Edition" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Legend of Zelda-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a chest of loot, potentially containing gear inspired by the Legend of Zelda series", + "write": false + } + ], + "gameID": [ + "01000A10041EA000" + ], + "gameName": "The Elder Scrolls V: Skyrim" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive materials and a rare or exclusive item", + "write": false + } + ], + "gameID": [ + "01007EF00011E000" + ], + "gameName": "The Legend of Zelda: Breath of the Wild" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock one of five amiibo-exclusive Chamber Dungeon chambers", + "write": false + }, + { + "Usage": "Save your Chamber Dungeon to the amiibo to share with friends", + "write": true + } + ], + "gameID": [ + "01006BB00C6F0000" + ], + "gameName": "The Legend of Zelda: Link's Awakening" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive character-specific paraglider fabric", + "write": false + }, + { + "Usage": "Receive materials and a weapon or rare item", + "write": false + } + ], + "gameID": [ + "0100F2C0115B6000" + ], + "gameName": "The Legend of Zelda: Tears of the Kingdom" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive the Red Tunic", + "write": false + }, + { + "Usage": "Receive random materials", + "write": false + } + ], + "gameID": [ + "01008CF01BAAC000" + ], + "gameName": "The Legend of Zelda: Echoes of Wisdom" + } + ], + "head": "01000000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01000000-037c0002.png", + "name": "Young Link", + "release": { + "au": "2019-04-12", + "eu": "2019-04-12", + "jp": "2019-04-12", + "na": "2019-04-12" + }, + "tail": "037c0002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Huck", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03430001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03430001-02ef0502.png", + "name": "Huck", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "02ef0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Avery", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04500001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04500001-00cf0502.png", + "name": "Avery", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00cf0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Carrie", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03d30001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03d30001-02f30502.png", + "name": "Carrie", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "02f30502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Kiki", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02610001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02610001-00650502.png", + "name": "Kiki", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00650502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Isabelle", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "01810000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01810000-024b0502.png", + "name": "Isabelle - Summer Outfit", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "024b0502", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Rooney", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03da0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03da0001-01510502.png", + "name": "Rooney", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01510502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Nate", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02190001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02190001-007e0502.png", + "name": "Nate", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "007e0502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Metal Mario", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09d00101", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09d00101-02b90e02.png", + "name": "Metal Mario - Soccer", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02b90e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Pippy", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "049a0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_049a0001-014e0502.png", + "name": "Pippy", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "014e0502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Yoshi", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive weapon for the character and their Rabbid counterpart", + "write": false + } + ], + "gameID": [ + "010067300059A000" + ], + "gameName": "Mario + Rabbids: Kingdom Battle" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01006000040C2000" + ], + "gameName": "Yoshi's Crafted World" + } + ], + "head": "00030000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00030000-00020002.png", + "name": "Yoshi", + "release": { + "au": "2014-11-29", + "eu": "2014-11-28", + "jp": "2014-12-06", + "na": "2014-11-21" + }, + "tail": "00020002", + "type": "Figure" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Birdo", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09ce0301", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09ce0301-02b10e02.png", + "name": "Birdo - Tennis", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02b10e02", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Mythra", + "gameSeries": "Xenoblade Chronicles", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-specific weapon skin for characters using the Swordfighter Class", + "write": false + } + ], + "gameID": [ + "010074F013262000" + ], + "gameName": "Xenoblade Chronicles 3" + } + ], + "head": "22420000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_22420000-041f0002.png", + "name": "Mythra", + "release": { + "au": "2023-07-21", + "eu": "2023-07-21", + "jp": "2023-07-21", + "na": "2023-07-21" + }, + "tail": "041f0002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Rocket", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03720001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03720001-010b0502.png", + "name": "Rocket", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "010b0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Peaches", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03ac0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03ac0001-01880502.png", + "name": "Peaches", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01880502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Ken", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02a60001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02a60001-01240502.png", + "name": "Ken", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01240502", + "type": "Card" + }, + { + "amiiboSeries": "Splatoon", + "character": "Inkling", + "gameSeries": "Splatoon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Splatoon-themed racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "01003BC0000A0000" + ], + "gameName": "Splatoon 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "0100C2500FC20000" + ], + "gameName": "Splatoon 3" + } + ], + "head": "08000200", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_08000200-003f0402.png", + "name": "Inkling Boy", + "release": { + "au": "2015-05-30", + "eu": "2015-05-29", + "jp": "2015-05-28", + "na": "2015-05-29" + }, + "tail": "003f0402", + "type": "Figure" + }, + { + "amiiboSeries": "Super Mario Bros.", + "character": "Luigi", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive weapon for the character and their Rabbid counterpart", + "write": false + } + ], + "gameID": [ + "010067300059A000" + ], + "gameName": "Mario + Rabbids: Kingdom Battle" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a character-based costume", + "write": false + } + ], + "gameID": [ + "0100000000010000" + ], + "gameName": "Super Mario Odyssey" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01006000040C2000" + ], + "gameName": "Yoshi's Crafted World" + } + ], + "head": "00010000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00010000-00350102.png", + "name": "Luigi", + "release": { + "au": "2015-03-21", + "eu": "2015-03-20", + "jp": "2015-03-12", + "na": "2015-03-20" + }, + "tail": "00350102", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Chip", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "019a0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_019a0001-00b70502.png", + "name": "Chip", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00b70502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Claudia", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04ff0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04ff0001-01620502.png", + "name": "Claudia", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01620502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Greta", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "041c0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_041c0001-01410502.png", + "name": "Greta", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01410502", + "type": "Card" + }, + { + "amiiboSeries": "Fire Emblem", + "character": "Celica", + "gameSeries": "Fire Emblem", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive a Fashion Ticket and a Music Ticket, for unlocking any of the available costumes and music tracks", + "write": false + } + ], + "gameID": [ + "0100A6301214E000" + ], + "gameName": "Fire Emblem Engage" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a weapon", + "write": false + } + ], + "gameID": [ + "0100F15003E64000" + ], + "gameName": "Fire Emblem Warriors" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive better-quality randomized resources, weapons, or equipment", + "write": false + } + ], + "gameID": [ + "010071F0143EA000" + ], + "gameName": "Fire Emblem Warriors: Three Hopes" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special piece of battle music / Receive higher-quality items and materials", + "write": false + } + ], + "gameID": [ + "010055D009F78000" + ], + "gameName": "Fire Emblem: Three Houses" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "21070000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_21070000-03611202.png", + "name": "Celica", + "release": { + "au": "2017-05-20", + "eu": "2017-05-19", + "jp": "2017-04-20", + "na": "2017-05-19" + }, + "tail": "03611202", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Deena", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "030b0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_030b0001-00790502.png", + "name": "Deena", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00790502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Ivysaur", + "gameSeries": "Pokemon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "19020000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_19020000-03830002.png", + "name": "Ivysaur", + "release": { + "au": "2019-09-20", + "eu": "2019-09-20", + "jp": "2019-09-20", + "na": "2019-09-20" + }, + "tail": "03830002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Filbert", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04df0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04df0001-00e80502.png", + "name": "Filbert", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00e80502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Velma", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "035c0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_035c0001-01290502.png", + "name": "Velma", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01290502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Beardo", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02210001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02210001-013c0502.png", + "name": "Beardo", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "013c0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Azalea", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "0a1e0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0a1e0001-03d50502.png", + "name": "Azalea", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03d50502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Samus", + "gameSeries": "Metroid", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a costume based on the character (short-hair version)", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Metroid-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Restore a random amount of health once per day", + "write": false + } + ], + "gameID": [ + "010093801237C000" + ], + "gameName": "Metroid Dread" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "05c00000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_05c00000-00060002.png", + "name": "Samus", + "release": { + "au": "2014-11-29", + "eu": "2014-11-28", + "jp": "2014-12-06", + "na": "2014-11-21" + }, + "tail": "00060002", + "type": "Figure" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Simon", + "gameSeries": "Castlevania", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "37c00000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_37c00000-038b0002.png", + "name": "Simon", + "release": { + "au": "2019-11-15", + "eu": "2019-11-15", + "jp": "2019-11-08", + "na": "2019-11-15" + }, + "tail": "038b0002", + "type": "Figure" + }, + { + "amiiboSeries": "Monster Hunter Rise", + "character": "Magnamalo", + "gameSeries": "Monster Hunter", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock special layered armor / Enter daily lottery for a variety of useful items", + "write": false + } + ], + "gameID": [ + "0100B04011742000" + ], + "gameName": "Monster Hunter Rise" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the Hunter Sticker Set / Have Tsukino read your fortune and receive a random item", + "write": false + } + ], + "gameID": [ + "0100E21011446000" + ], + "gameName": "Monster Hunter Stories 2: Wings of Ruin" + } + ], + "head": "35080000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_35080000-040f1802.png", + "name": "Magnamalo", + "release": { + "au": "2021-03-26", + "eu": "2021-03-26", + "jp": "2021-03-26", + "na": "2021-03-26" + }, + "tail": "040f1802", + "type": "Figure" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Rosalina", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09cf0301", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09cf0301-02b60e02.png", + "name": "Rosalina - Tennis", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02b60e02", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Boo", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09cb0401", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09cb0401-02a30e02.png", + "name": "Boo - Golf", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02a30e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Flip", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03ff0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03ff0001-00f40502.png", + "name": "Flip", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00f40502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Hazel", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04ef0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04ef0001-013b0502.png", + "name": "Hazel", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "013b0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Bubbles", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03920001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03920001-01270502.png", + "name": "Bubbles", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01270502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Elmer", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03a70001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03a70001-01a10502.png", + "name": "Elmer", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01a10502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Boots", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02c50001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02c50001-03080502.png", + "name": "Boots", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "03080502", + "type": "Card" + }, + { + "amiiboSeries": "Skylanders", + "character": "Bowser", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock the Chain Chomp weapon", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + }, + { + "Usage": "Make Fury Bowser appear (in Bowser's Fury mode)", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01006000040C2000" + ], + "gameName": "Yoshi's Crafted World" + } + ], + "head": "0005ff00", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0005ff00-023a0702.png", + "name": "Hammer Slam Bowser", + "release": { + "au": "2015-09-24", + "eu": "2015-09-25", + "jp": null, + "na": "2015-09-20" + }, + "tail": "023a0702", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Bea", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02f40001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02f40001-03050502.png", + "name": "Bea", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "03050502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Boone", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "036b0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_036b0001-018b0502.png", + "name": "Boone", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "018b0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Wardell", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "0a080001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0a080001-03bd0502.png", + "name": "Wardell", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03bd0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Petri", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "0a160001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0a160001-03cd0502.png", + "name": "Petri", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03cd0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Ace", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "0a1b0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0a1b0001-03d20502.png", + "name": "Ace", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03d20502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Mii", + "gameSeries": "Mii", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "07c00100", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_07c00100-00220002.png", + "name": "Mii Swordfighter", + "release": { + "au": "2015-09-26", + "eu": "2015-09-25", + "jp": "2015-09-10", + "na": "2015-11-01" + }, + "tail": "00220002", + "type": "Figure" + }, + { + "amiiboSeries": "Yu-Gi-Oh!", + "character": "Gakuto S\u014dgetsu", + "gameSeries": "Yu-Gi-Oh!", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive items/bonuses", + "write": false + } + ], + "gameID": [ + "01003C101454A000" + ], + "gameName": "Yu-Gi-Oh! Rush Duel Saikyo Battle Royale" + } + ], + "head": "38420001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_38420001-04261902.png", + "name": "Gakuto S\u014dgetsu", + "release": { + "au": null, + "eu": null, + "jp": null, + "na": "2021-08-12" + }, + "tail": "04261902", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Terry", + "gameSeries": "Fatal Fury", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "3c800000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_3c800000-03a40002.png", + "name": "Terry", + "release": { + "au": "2021-03-26", + "eu": "2021-03-26", + "jp": "2021-03-26", + "na": "2021-03-26" + }, + "tail": "03a40002", + "type": "Figure" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Daisy", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c30501", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c30501-027c0e02.png", + "name": "Daisy - Horse Racing", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "027c0e02", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Snake", + "gameSeries": "Metal Gear Solid", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "37800000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_37800000-038a0002.png", + "name": "Snake", + "release": { + "au": "2019-09-20", + "eu": "2019-09-20", + "jp": "2019-09-20", + "na": "2019-09-20" + }, + "tail": "038a0002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Franklin", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01ae0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01ae0001-011b0502.png", + "name": "Franklin", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "011b0502", + "type": "Card" + }, + { + "amiiboSeries": "Pokemon", + "character": "Detective Pikachu", + "gameSeries": "Pokemon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "1d010000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_1d010000-03750d02.png", + "name": "Detective Pikachu", + "release": { + "au": "2018-03-24", + "eu": "2018-03-23", + "jp": "2018-03-23", + "na": "2018-03-23" + }, + "tail": "03750d02", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Hopper", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04620001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04620001-00f60502.png", + "name": "Hopper", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00f60502", + "type": "Card" + }, + { + "amiiboSeries": "Monster Hunter Rise", + "character": "Palamute", + "gameSeries": "Monster Hunter", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock special layered armor / Enter daily lottery for a variety of useful items", + "write": false + } + ], + "gameID": [ + "0100B04011742000" + ], + "gameName": "Monster Hunter Rise" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the Hunter Sticker Set / Have Tsukino read your fortune and receive a random item", + "write": false + } + ], + "gameID": [ + "0100E21011446000" + ], + "gameName": "Monster Hunter Stories 2: Wings of Ruin" + } + ], + "head": "350a0100", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_350a0100-042c1802.png", + "name": "Palamute", + "release": { + "au": "2022-06-30", + "eu": "2022-06-30", + "jp": "2022-06-30", + "na": "2022-06-30" + }, + "tail": "042c1802", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Colton", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03af0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03af0001-012c0502.png", + "name": "Colton", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "012c0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Phoebe", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04400001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04400001-00ca0502.png", + "name": "Phoebe", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00ca0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Ursala", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "021c0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_021c0001-02f70502.png", + "name": "Ursala", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "02f70502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Gayle", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02ca0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02ca0001-01ca0502.png", + "name": "Gayle", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01ca0502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Daisy", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a costume based on the character (short-hair version)", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "00130000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00130000-037a0002.png", + "name": "Daisy", + "release": { + "au": "2019-04-12", + "eu": "2019-04-12", + "jp": "2019-04-12", + "na": "2019-04-12" + }, + "tail": "037a0002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Lucy", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "047c0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_047c0001-01a00502.png", + "name": "Lucy", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01a00502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Sylvana", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04eb0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04eb0001-02f00502.png", + "name": "Sylvana", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "02f00502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Billy", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03580001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03580001-02fa0502.png", + "name": "Billy", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "02fa0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Tutu", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "021b0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_021b0001-00800502.png", + "name": "Tutu", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00800502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Niko", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "0a070001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0a070001-03bc0502.png", + "name": "Niko", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03bc0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Grizzly", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "021d0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_021d0001-01cd0502.png", + "name": "Grizzly", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01cd0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Isabelle", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "01810001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01810001-00440502.png", + "name": "Isabelle", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00440502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Hopkins", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04a20001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04a20001-02e80502.png", + "name": "Hopkins", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "02e80502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Scoot", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03110001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03110001-00d60502.png", + "name": "Scoot", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00d60502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Sydney", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03bf0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03bf0001-01bc0502.png", + "name": "Sydney", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01bc0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Candi", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04140001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04140001-030a0502.png", + "name": "Candi", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "030a0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "\u00c9toile", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + }, + { + "Usage": "Unlock special furniture items and a poster based on the card's Sanrio character", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04d30101", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04d30101-031b0502.png", + "name": "\u00c9toile", + "release": { + "au": null, + "eu": "2016-11-25", + "jp": "2016-11-03", + "na": null + }, + "tail": "031b0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Axel", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03290001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03290001-009d0502.png", + "name": "Axel", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "009d0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Rowan", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04fb0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04fb0001-01c60502.png", + "name": "Rowan", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01c60502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Rosie", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "025f0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_025f0001-01d70502.png", + "name": "Rosie - Amiibo Festival", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-11-21", + "na": "2015-11-13" + }, + "tail": "01d70502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Kicks", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "01940000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01940000-024a0502.png", + "name": "Kicks", + "release": { + "au": "2016-01-30", + "eu": "2016-01-29", + "jp": "2015-12-17", + "na": "2016-01-22" + }, + "tail": "024a0502", + "type": "Figure" + }, + { + "amiiboSeries": "Monster Hunter", + "character": "One-Eyed Rathalos", + "gameSeries": "Monster Hunter", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock Monster Hunter Stories 2 sticker set", + "write": false + } + ], + "gameID": [ + "0100B04011742000" + ], + "gameName": "Monster Hunter Rise" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a character-based monstie egg", + "write": false + } + ], + "gameID": [ + "010069301B1D4000" + ], + "gameName": "Monster Hunter Stories" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the Hakum Rider Outfit layered armor set / Have Tsukino read your fortune and receive a random item", + "write": false + } + ], + "gameID": [ + "0100E21011446000" + ], + "gameName": "Monster Hunter Stories 2: Wings of Ruin" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a lot of BP / Receive better supplies (compared to other amiibo)", + "write": false + } + ], + "gameID": [ + "0100643002136000" + ], + "gameName": "Resident Evil Revelations" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a lot of BP / Receive better supplies (compared to other amiibo)", + "write": false + } + ], + "gameID": [ + "010095300212A000" + ], + "gameName": "Resident Evil Revelations 2" + } + ], + "head": "35000200", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_35000200-02e20f02.png", + "name": "One-Eyed Rathalos and Rider - Female", + "release": { + "au": null, + "eu": null, + "jp": "2016-10-08", + "na": null + }, + "tail": "02e20f02", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Copper", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "019d0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_019d0001-00ac0502.png", + "name": "Copper", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00ac0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Admiral", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + } + ], + "head": "02330001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02330001-03060502.png", + "name": "Admiral", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "03060502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Cranston", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "043c0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_043c0001-01cb0502.png", + "name": "Cranston", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01cb0502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Wolf", + "gameSeries": "Star Fox", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "05840000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_05840000-037e0002.png", + "name": "Wolf", + "release": { + "au": "2018-12-07", + "eu": "2018-12-07", + "jp": "2018-12-07", + "na": "2018-12-07" + }, + "tail": "037e0002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Apple", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "037f0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_037f0001-01aa0502.png", + "name": "Apple", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01aa0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Resetti", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "018e0000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_018e0000-02490502.png", + "name": "Resetti", + "release": { + "au": "2016-01-30", + "eu": "2016-01-29", + "jp": "2015-12-17", + "na": "2016-01-22" + }, + "tail": "02490502", + "type": "Figure" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Daisy", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c30101", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c30101-02780e02.png", + "name": "Daisy - Soccer", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02780e02", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Pikachu", + "gameSeries": "Pokemon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "19190000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_19190000-00090002.png", + "name": "Pikachu", + "release": { + "au": "2014-11-29", + "eu": "2014-11-28", + "jp": "2014-12-06", + "na": "2014-11-21" + }, + "tail": "00090002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Monty", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03fd0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03fd0001-01580502.png", + "name": "Monty", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01580502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Snooty", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02060001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02060001-03120502.png", + "name": "Snooty", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "03120502", + "type": "Card" + }, + { + "amiiboSeries": "Yoshi's Woolly World", + "character": "Yoshi", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive weapon for the character and their Rabbid counterpart", + "write": false + } + ], + "gameID": [ + "010067300059A000" + ], + "gameName": "Mario + Rabbids: Kingdom Battle" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01006000040C2000" + ], + "gameName": "Yoshi's Crafted World" + } + ], + "head": "00030102", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00030102-023e0302.png", + "name": "Mega Yarn Yoshi", + "release": { + "au": "2015-11-28", + "eu": "2015-11-27", + "jp": "2015-12-10", + "na": "2015-11-15" + }, + "tail": "023e0302", + "type": "Yarn" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Plucky", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02a30001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02a30001-02ff0502.png", + "name": "Plucky", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "02ff0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Genji", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "049c0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_049c0001-01400502.png", + "name": "Genji", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01400502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Timmy", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01850001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01850001-004b0502.png", + "name": "Timmy", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "004b0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Ketchup", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03140001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03140001-02f40502.png", + "name": "Ketchup", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "02f40502", + "type": "Card" + }, + { + "amiiboSeries": "Legend Of Zelda", + "character": "Midna", + "gameSeries": "The Legend of Zelda", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive specific crafting materials or a weapon for the character", + "write": false + } + ], + "gameID": [ + "01002B00111A2000" + ], + "gameName": "Hyrule Warriors: Age of Calamity" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a weapon rated 3 stars or higher", + "write": false + } + ], + "gameID": [ + "0100AE00096EA000" + ], + "gameName": "Hyrule Warriors: Definitive Edition" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Legend of Zelda-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a chest of loot, potentially containing gear inspired by the Legend of Zelda series", + "write": false + } + ], + "gameID": [ + "01000A10041EA000" + ], + "gameName": "The Elder Scrolls V: Skyrim" + }, + { + "amiiboUsage": [ + { + "Usage": "Bring Wolf Link into the game as a partner character", + "write": false + } + ], + "gameID": [ + "01007EF00011E000" + ], + "gameName": "The Legend of Zelda: Breath of the Wild" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock one of five amiibo-exclusive Chamber Dungeon chambers", + "write": false + }, + { + "Usage": "Save your Chamber Dungeon to the amiibo to share with friends", + "write": true + } + ], + "gameID": [ + "01006BB00C6F0000" + ], + "gameName": "The Legend of Zelda: Link's Awakening" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive character-specific paraglider fabric", + "write": false + }, + { + "Usage": "Receive materials and a weapon or rare item", + "write": false + } + ], + "gameID": [ + "0100F2C0115B6000" + ], + "gameName": "The Legend of Zelda: Tears of the Kingdom" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive the Red Tunic", + "write": false + }, + { + "Usage": "Receive random materials", + "write": false + } + ], + "gameID": [ + "01008CF01BAAC000" + ], + "gameName": "The Legend of Zelda: Echoes of Wisdom" + } + ], + "head": "01030000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01030000-024f0902.png", + "name": "Midna & Wolf Link", + "release": { + "au": "2016-03-05", + "eu": "2016-03-04", + "jp": "2016-03-10", + "na": "2016-03-04" + }, + "tail": "024f0902", + "type": "Figure" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Luigi", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive weapon for the character and their Rabbid counterpart", + "write": false + } + ], + "gameID": [ + "010067300059A000" + ], + "gameName": "Mario + Rabbids: Kingdom Battle" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a character-based costume", + "write": false + } + ], + "gameID": [ + "0100000000010000" + ], + "gameName": "Super Mario Odyssey" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01006000040C2000" + ], + "gameName": "Yoshi's Crafted World" + } + ], + "head": "00010000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00010000-000c0002.png", + "name": "Luigi", + "release": { + "au": "2014-12-12", + "eu": "2014-12-19", + "jp": "2014-12-06", + "na": "2014-12-14" + }, + "tail": "000c0002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Label", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01890101", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01890101-03b10502.png", + "name": "Label", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03b10502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Steve", + "gameSeries": "Minecraft", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "3dc00000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_3dc00000-04220002.png", + "name": "Steve", + "release": { + "au": "2022-09-09", + "eu": "2022-09-09", + "jp": "2022-09-09", + "na": "2022-09-09" + }, + "tail": "04220002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Timmy & Tommy", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + }, + { + "Usage": "Unlock Timmy & Tommy's shop early", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "01840000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01840000-024d0502.png", + "name": "Timmy & Tommy", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-03-24", + "na": "2016-03-18" + }, + "tail": "024d0502", + "type": "Figure" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Marth", + "gameSeries": "Fire Emblem", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive a Fashion Ticket and a Music Ticket, for unlocking any of the available costumes and music tracks", + "write": false + } + ], + "gameID": [ + "0100A6301214E000" + ], + "gameName": "Fire Emblem Engage" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a weapon", + "write": false + } + ], + "gameID": [ + "0100F15003E64000" + ], + "gameName": "Fire Emblem Warriors" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive better-quality randomized resources, weapons, or equipment", + "write": false + } + ], + "gameID": [ + "010071F0143EA000" + ], + "gameName": "Fire Emblem Warriors: Three Hopes" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special piece of battle music / Receive higher-quality items and materials", + "write": false + } + ], + "gameID": [ + "010055D009F78000" + ], + "gameName": "Fire Emblem: Three Houses" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "21000000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_21000000-000b0002.png", + "name": "Marth", + "release": { + "au": "2014-11-29", + "eu": "2014-11-28", + "jp": "2014-12-06", + "na": "2014-11-21" + }, + "tail": "000b0002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Porter", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01950001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01950001-00b00502.png", + "name": "Porter", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00b00502", + "type": "Card" + }, + { + "amiiboSeries": "Super Mario Bros.", + "character": "Yoshi", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive weapon for the character and their Rabbid counterpart", + "write": false + } + ], + "gameID": [ + "010067300059A000" + ], + "gameName": "Mario + Rabbids: Kingdom Battle" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01006000040C2000" + ], + "gameName": "Yoshi's Crafted World" + } + ], + "head": "00030000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00030000-00370102.png", + "name": "Yoshi", + "release": { + "au": "2015-03-21", + "eu": "2015-03-20", + "jp": "2015-03-12", + "na": "2015-03-20" + }, + "tail": "00370102", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Derwin", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "030f0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_030f0001-016d0502.png", + "name": "Derwin", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "016d0502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "R.O.B.", + "gameSeries": "Classic Nintendo", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "07810000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_07810000-002e0002.png", + "name": "R.O.B. - Famicom", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2015-10-29", + "na": "2016-03-18" + }, + "tail": "002e0002", + "type": "Figure" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Cloud Strife", + "gameSeries": "Final Fantasy", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "36000100", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_36000100-03620002.png", + "name": "Cloud - Player 2", + "release": { + "au": "2017-07-22", + "eu": "2017-07-21", + "jp": "2017-07-21", + "na": "2017-07-21" + }, + "tail": "03620002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Olaf", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02090001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02090001-019f0502.png", + "name": "Olaf", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "019f0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Rhonda", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04b30001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04b30001-00dd0502.png", + "name": "Rhonda", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00dd0502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Waluigi", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c60401", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c60401-028a0e02.png", + "name": "Waluigi - Golf", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "028a0e02", + "type": "Card" + }, + { + "amiiboSeries": "Fire Emblem", + "character": "Chrom", + "gameSeries": "Fire Emblem", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive a Fashion Ticket and a Music Ticket, for unlocking any of the available costumes and music tracks", + "write": false + } + ], + "gameID": [ + "0100A6301214E000" + ], + "gameName": "Fire Emblem Engage" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive weapon", + "write": false + }, + { + "Usage": "Receive a weapon", + "write": false + } + ], + "gameID": [ + "0100F15003E64000" + ], + "gameName": "Fire Emblem Warriors" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive better-quality randomized resources, weapons, or equipment", + "write": false + } + ], + "gameID": [ + "010071F0143EA000" + ], + "gameName": "Fire Emblem Warriors: Three Hopes" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special piece of battle music / Receive higher-quality items and materials", + "write": false + } + ], + "gameID": [ + "010055D009F78000" + ], + "gameName": "Fire Emblem: Three Houses" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "21080000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_21080000-036f1202.png", + "name": "Chrom", + "release": { + "au": "2017-10-20", + "eu": "2017-10-20", + "jp": "2017-09-28", + "na": "2017-10-20" + }, + "tail": "036f1202", + "type": "Figure" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Birdo", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09ce0401", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09ce0401-02b20e02.png", + "name": "Birdo - Golf", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02b20e02", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Wario", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c50401", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c50401-02850e02.png", + "name": "Wario - Golf", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02850e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Cesar", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03690001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03690001-00d30502.png", + "name": "Cesar", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00d30502", + "type": "Card" + }, + { + "amiiboSeries": "Others", + "character": "Solaire of Astora", + "gameSeries": "Dark Souls", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock and perform the \u201cPraise the Sun\u201d gesture", + "write": false + } + ], + "gameID": [ + "01004AB00A260000" + ], + "gameName": "Dark Souls: Remastered" + } + ], + "head": "33800000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_33800000-03781402.png", + "name": "Solaire of Astora", + "release": { + "au": "2018-10-19", + "eu": "2018-10-19", + "jp": "2018-10-18", + "na": "2018-10-19" + }, + "tail": "03781402", + "type": "Figure" + }, + { + "amiiboSeries": "Yu-Gi-Oh!", + "character": "Roa Kirishima", + "gameSeries": "Yu-Gi-Oh!", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive items/bonuses", + "write": false + } + ], + "gameID": [ + "01003C101454A000" + ], + "gameName": "Yu-Gi-Oh! Rush Duel Saikyo Battle Royale" + } + ], + "head": "38440001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_38440001-04281902.png", + "name": "Roa Kirishima", + "release": { + "au": null, + "eu": null, + "jp": null, + "na": "2021-08-12" + }, + "tail": "04281902", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Pinky", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02150001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02150001-01820502.png", + "name": "Pinky", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01820502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Olive", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02860001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02860001-03130502.png", + "name": "Olive", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "03130502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Ice Climbers", + "gameSeries": "Classic Nintendo", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "078f0000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_078f0000-03810002.png", + "name": "Ice Climbers", + "release": { + "au": "2019-02-15", + "eu": "2019-02-15", + "jp": "2019-02-15", + "na": "2019-02-15" + }, + "tail": "03810002", + "type": "Figure" + }, + { + "amiiboSeries": "Yu-Gi-Oh!", + "character": "Asana Mutsuba", + "gameSeries": "Yu-Gi-Oh!", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive items/bonuses", + "write": false + } + ], + "gameID": [ + "01003C101454A000" + ], + "gameName": "Yu-Gi-Oh! Rush Duel Saikyo Battle Royale" + } + ], + "head": "38460001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_38460001-042a1902.png", + "name": "Asana Mutsuba", + "release": { + "au": null, + "eu": null, + "jp": null, + "na": "2021-08-12" + }, + "tail": "042a1902", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Tom Nook", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01830101", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01830101-010e0502.png", + "name": "Tom Nook - Jacket", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "010e0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Anicotti", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04160001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04160001-00fb0502.png", + "name": "Anicotti", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00fb0502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Mario", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c00101", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c00101-02690e02.png", + "name": "Mario - Soccer", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02690e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Croque", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03490001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03490001-018d0502.png", + "name": "Croque", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "018d0502", + "type": "Card" + }, + { + "amiiboSeries": "Legend Of Zelda", + "character": "Daruk", + "gameSeries": "The Legend of Zelda", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive specific crafting materials or a weapon for the character", + "write": false + } + ], + "gameID": [ + "01002B00111A2000" + ], + "gameName": "Hyrule Warriors: Age of Calamity" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Legend of Zelda-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a chest of loot, potentially containing gear inspired by the Legend of Zelda series", + "write": false + } + ], + "gameID": [ + "01000A10041EA000" + ], + "gameName": "The Elder Scrolls V: Skyrim" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive materials and a rare or exclusive item", + "write": false + } + ], + "gameID": [ + "01007EF00011E000" + ], + "gameName": "The Legend of Zelda: Breath of the Wild" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock one of five amiibo-exclusive Chamber Dungeon chambers", + "write": false + }, + { + "Usage": "Save your Chamber Dungeon to the amiibo to share with friends", + "write": true + } + ], + "gameID": [ + "01006BB00C6F0000" + ], + "gameName": "The Legend of Zelda: Link's Awakening" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive character-specific paraglider fabric", + "write": false + }, + { + "Usage": "Receive materials and a weapon or rare item", + "write": false + } + ], + "gameID": [ + "0100F2C0115B6000" + ], + "gameName": "The Legend of Zelda: Tears of the Kingdom" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive the Black Cat Clothes", + "write": false + }, + { + "Usage": "Receive random materials", + "write": false + } + ], + "gameID": [ + "01008CF01BAAC000" + ], + "gameName": "The Legend of Zelda: Echoes of Wisdom" + } + ], + "head": "01050000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01050000-03580902.png", + "name": "Daruk", + "release": { + "au": "2017-11-10", + "eu": "2017-11-10", + "jp": "2017-11-10", + "na": "2017-11-10" + }, + "tail": "03580902", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Chops", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04860001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04860001-00fc0502.png", + "name": "Chops", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00fc0502", + "type": "Card" + }, + { + "amiiboSeries": "Shovel Knight", + "character": "Shovel Knight", + "gameSeries": "Shovel Knight", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock boss fight against Shovel Knight", + "write": false + } + ], + "gameID": [ + "0100192003FA4000" + ], + "gameName": "Azure Striker Gunvolt: Striker Pack" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a fairy companion and player color palette matching the character", + "write": false + } + ], + "gameID": [ + "01008D100DE46000" + ], + "gameName": "Cyber Shadow" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-specific Shovel Knight remix immediately", + "write": false + } + ], + "gameID": [ + "0100830008426000" + ], + "gameName": "Just Shapes & Beats" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock Custom Knight, and save customizations to the amiibo (Shovel of Hope only)", + "write": true + }, + { + "Usage": "Unlock character-specific challenge stages, a character-based fairy companion, and costumes for the character", + "write": false + } + ], + "gameID": [ + "010057D0021E8000" + ], + "gameName": "Shovel Knight" + }, + { + "amiiboUsage": [ + { + "Usage": "Summon a fairy friend", + "write": false + } + ], + "gameID": [ + "0100B62017E68000" + ], + "gameName": "Shovel Knight Dig" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume for the character", + "write": false + } + ], + "gameID": [ + "0100B380022AE000" + ], + "gameName": "Shovel Knight Showdown" + } + ], + "head": "35c00000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_35c00000-03920a02.png", + "name": "Shovel Knight - Gold Edition", + "release": { + "au": null, + "eu": "2019-12-10", + "jp": null, + "na": "2019-12-10" + }, + "tail": "03920a02", + "type": "Figure" + }, + { + "amiiboSeries": "Super Mario Bros.", + "character": "Bowser", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock the Chain Chomp weapon", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock Super Mario Odyssey-themed levels early", + "write": false + } + ], + "gameID": [ + "01009BF0072D4000" + ], + "gameName": "Captain Toad: Treasure Tracker" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + }, + { + "Usage": "Make Fury Bowser appear (in Bowser's Fury mode)", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Reveal regional coin locations", + "write": false + } + ], + "gameID": [ + "0100000000010000" + ], + "gameName": "Super Mario Odyssey" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a character-based costume", + "write": false + } + ], + "gameID": [ + "0100000000010000" + ], + "gameName": "Super Mario Odyssey" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01006000040C2000" + ], + "gameName": "Yoshi's Crafted World" + } + ], + "head": "00050000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00050000-03730102.png", + "name": "Bowser - Wedding", + "release": { + "au": "2017-10-27", + "eu": "2017-10-27", + "jp": "2017-10-27", + "na": "2017-10-27" + }, + "tail": "03730102", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "K.K. Slider", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01820101", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01820101-00460502.png", + "name": "DJ KK", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00460502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Saharah", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01a60001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01a60001-03b70502.png", + "name": "Saharah", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03b70502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Jitters", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02310001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02310001-006a0502.png", + "name": "Jitters", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "006a0502", + "type": "Card" + }, + { + "amiiboSeries": "Legend Of Zelda", + "character": "Mipha", + "gameSeries": "The Legend of Zelda", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive specific crafting materials or a weapon for the character", + "write": false + } + ], + "gameID": [ + "01002B00111A2000" + ], + "gameName": "Hyrule Warriors: Age of Calamity" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Legend of Zelda-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a chest of loot, potentially containing gear inspired by the Legend of Zelda series", + "write": false + } + ], + "gameID": [ + "01000A10041EA000" + ], + "gameName": "The Elder Scrolls V: Skyrim" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive materials and a rare or exclusive item", + "write": false + } + ], + "gameID": [ + "01007EF00011E000" + ], + "gameName": "The Legend of Zelda: Breath of the Wild" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock one of five amiibo-exclusive Chamber Dungeon chambers", + "write": false + }, + { + "Usage": "Save your Chamber Dungeon to the amiibo to share with friends", + "write": true + } + ], + "gameID": [ + "01006BB00C6F0000" + ], + "gameName": "The Legend of Zelda: Link's Awakening" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive character-specific paraglider fabric", + "write": false + }, + { + "Usage": "Receive materials and a weapon or rare item", + "write": false + } + ], + "gameID": [ + "0100F2C0115B6000" + ], + "gameName": "The Legend of Zelda: Tears of the Kingdom" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive the Black Cat Clothes", + "write": false + }, + { + "Usage": "Receive random materials", + "write": false + } + ], + "gameID": [ + "01008CF01BAAC000" + ], + "gameName": "The Legend of Zelda: Echoes of Wisdom" + } + ], + "head": "01070000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01070000-035a0902.png", + "name": "Mipha", + "release": { + "au": "2017-11-10", + "eu": "2017-11-10", + "jp": "2017-11-10", + "na": "2017-11-10" + }, + "tail": "035a0902", + "type": "Figure" + }, + { + "amiiboSeries": "Monster Hunter", + "character": "Tsukino", + "gameSeries": "Monster Hunter", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock Monster Hunter Stories 2 sticker set", + "write": false + } + ], + "gameID": [ + "0100B04011742000" + ], + "gameName": "Monster Hunter Rise" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a character-based costume for Navirou", + "write": false + } + ], + "gameID": [ + "010069301B1D4000" + ], + "gameName": "Monster Hunter Stories" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-specific special layered armor set / Have Tsukino read your fortune and receive a random item", + "write": false + } + ], + "gameID": [ + "0100E21011446000" + ], + "gameName": "Monster Hunter Stories 2: Wings of Ruin" + } + ], + "head": "35070000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_35070000-040e0f02.png", + "name": "Tsukino", + "release": { + "au": "2021-07-09", + "eu": "2021-07-09", + "jp": "2021-07-09", + "na": "2021-07-09" + }, + "tail": "040e0f02", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Coach", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02510001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02510001-00c10502.png", + "name": "Coach", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00c10502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Erik", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02df0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02df0001-01910502.png", + "name": "Erik", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01910502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Tammi", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03fc0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03fc0001-01470502.png", + "name": "Tammi", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01470502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Gigi", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03480001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03480001-006b0502.png", + "name": "Gigi", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "006b0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Biskit", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02ed0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02ed0001-015a0502.png", + "name": "Biskit", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "015a0502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Ken", + "gameSeries": "Street fighter", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "34c10000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_34c10000-03890002.png", + "name": "Ken", + "release": { + "au": "2019-04-12", + "eu": "2019-04-12", + "jp": "2019-04-12", + "na": "2019-04-12" + }, + "tail": "03890002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Sable", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01870001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01870001-00470502.png", + "name": "Sable", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00470502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Brewster", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01900001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01900001-01710502.png", + "name": "Brewster", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01710502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Lyman", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03c50001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03c50001-015c0502.png", + "name": "Lyman", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "015c0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Cyrano", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02000001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02000001-00a10502.png", + "name": "Cyrano", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00a10502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Broccolo", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04180001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04180001-00d80502.png", + "name": "Broccolo", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00d80502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Winnie", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03a90001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03a90001-00710502.png", + "name": "Winnie", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00710502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Midge", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02350001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02350001-00840502.png", + "name": "Midge", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00840502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Cally", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04e80001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04e80001-01ce0502.png", + "name": "Cally", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01ce0502", + "type": "Card" + }, + { + "amiiboSeries": "Monster Hunter", + "character": "Qurupeco", + "gameSeries": "Monster Hunter", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock Monster Hunter Stories 2 sticker set", + "write": false + } + ], + "gameID": [ + "0100B04011742000" + ], + "gameName": "Monster Hunter Rise" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a character-based monstie egg", + "write": false + } + ], + "gameID": [ + "010069301B1D4000" + ], + "gameName": "Monster Hunter Stories" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the Hakum Rider Outfit layered armor set / Have Tsukino read your fortune and receive a random item", + "write": false + } + ], + "gameID": [ + "0100E21011446000" + ], + "gameName": "Monster Hunter Stories 2: Wings of Ruin" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a lot of BP / Receive better supplies (compared to other amiibo)", + "write": false + } + ], + "gameID": [ + "0100643002136000" + ], + "gameName": "Resident Evil Revelations" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a lot of BP / Receive better supplies (compared to other amiibo)", + "write": false + } + ], + "gameID": [ + "010095300212A000" + ], + "gameName": "Resident Evil Revelations 2" + } + ], + "head": "35040100", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_35040100-02e60f02.png", + "name": "Qurupeco and Dan", + "release": { + "au": null, + "eu": null, + "jp": "2016-12-08", + "na": null + }, + "tail": "02e60f02", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Vladimir", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02830001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02830001-00c70502.png", + "name": "Vladimir", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00c70502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Leopold", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03ea0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03ea0001-030b0502.png", + "name": "Leopold", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "030b0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Maelle", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "030a0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_030a0001-01c70502.png", + "name": "Maelle", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01c70502", + "type": "Card" + }, + { + "amiiboSeries": "Splatoon", + "character": "Callie", + "gameSeries": "Splatoon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Splatoon-themed racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "01003BC0000A0000" + ], + "gameName": "Splatoon 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "0100C2500FC20000" + ], + "gameName": "Splatoon 3" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "08010000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_08010000-025d0402.png", + "name": "Callie", + "release": { + "au": "2016-07-09", + "eu": "2016-07-08", + "jp": "2016-07-07", + "na": "2016-07-08" + }, + "tail": "025d0402", + "type": "Figure" + }, + { + "amiiboSeries": "Splatoon", + "character": "Callie", + "gameSeries": "Splatoon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Splatoon-themed racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "01003BC0000A0000" + ], + "gameName": "Splatoon 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "0100C2500FC20000" + ], + "gameName": "Splatoon 3" + } + ], + "head": "08010000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_08010000-04360402.png", + "name": "Callie - Alterna", + "release": { + "au": "2024-09-05", + "eu": "2024-09-05", + "jp": "2024-09-05", + "na": "2024-09-05" + }, + "tail": "04360402", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Toby", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + }, + { + "Usage": "Unlock special furniture items and a poster based on the card's Sanrio character", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04a80101", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04a80101-031e0502.png", + "name": "Toby", + "release": { + "au": null, + "eu": "2016-11-25", + "jp": "2016-11-03", + "na": null + }, + "tail": "031e0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Coco", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04960001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04960001-00d90502.png", + "name": "Coco", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00d90502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Sprocket", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04390001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04390001-03110502.png", + "name": "Sprocket", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "03110502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Jack", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01ad0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01ad0001-00b80502.png", + "name": "Jack", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00b80502", + "type": "Card" + }, + { + "amiiboSeries": "Power Pros", + "character": "Pawapuro", + "gameSeries": "Power Pros", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive in-game items and power-ups / Save items to your card after playing with friends to bring them home", + "write": true + } + ], + "gameID": [ + "0100E9C00BF28000" + ], + "gameName": "Jikkyou Powerful Pro Baseball" + } + ], + "head": "38000001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_38000001-03931702.png", + "name": "Pawapuro", + "release": { + "au": null, + "eu": null, + "jp": "2019-06-27", + "na": null + }, + "tail": "03931702", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Banjo", + "gameSeries": "Banjo Kazooie", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "3b400000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_3b400000-03a30002.png", + "name": "Banjo & Kazooie", + "release": { + "au": "2021-03-26", + "eu": "2021-03-26", + "jp": "2021-03-26", + "na": "2021-03-26" + }, + "tail": "03a30002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Sylvia", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03d70001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03d70001-01b40502.png", + "name": "Sylvia", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01b40502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Whitney", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "050e0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_050e0001-00d70502.png", + "name": "Whitney", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00d70502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Redd", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01a80101", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01a80101-017e0502.png", + "name": "Redd - Shirt", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "017e0502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Min Min", + "gameSeries": "ARMS", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "0a400000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0a400000-041d0002.png", + "name": "Min Min", + "release": { + "au": "2022-04-29", + "eu": "2022-04-29", + "jp": "2022-04-29", + "na": "2022-04-29" + }, + "tail": "041d0002", + "type": "Figure" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Peach", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c20101", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c20101-02730e02.png", + "name": "Peach - Soccer", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02730e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Sherb", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "0a090001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0a090001-03c00502.png", + "name": "Sherb", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03c00502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Stinky", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "026a0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_026a0001-01460502.png", + "name": "Stinky", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01460502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Francine", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04a00001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04a00001-016e0502.png", + "name": "Francine", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "016e0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Roald", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04600001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04600001-00a50502.png", + "name": "Roald", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00a50502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Boo", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09cb0501", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09cb0501-02a40e02.png", + "name": "Boo - Horse Racing", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02a40e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Monique", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02680001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02680001-007d0502.png", + "name": "Monique", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "007d0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Tybalt", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04fc0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04fc0001-02ee0502.png", + "name": "Tybalt", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "02ee0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Melba", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03be0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03be0001-01980502.png", + "name": "Melba", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01980502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Luigi", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c10101", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c10101-026e0e02.png", + "name": "Luigi - Soccer", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "026e0e02", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Chrom", + "gameSeries": "Fire Emblem", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive a Fashion Ticket and a Music Ticket, for unlocking any of the available costumes and music tracks", + "write": false + } + ], + "gameID": [ + "0100A6301214E000" + ], + "gameName": "Fire Emblem Engage" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive weapon", + "write": false + }, + { + "Usage": "Receive a weapon", + "write": false + } + ], + "gameID": [ + "0100F15003E64000" + ], + "gameName": "Fire Emblem Warriors" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive better-quality randomized resources, weapons, or equipment", + "write": false + } + ], + "gameID": [ + "010071F0143EA000" + ], + "gameName": "Fire Emblem Warriors: Three Hopes" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special piece of battle music / Receive higher-quality items and materials", + "write": false + } + ], + "gameID": [ + "010055D009F78000" + ], + "gameName": "Fire Emblem: Three Houses" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "21080000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_21080000-03880002.png", + "name": "Chrom", + "release": { + "au": "2019-11-15", + "eu": "2019-11-15", + "jp": "2019-11-08", + "na": "2019-11-15" + }, + "tail": "03880002", + "type": "Figure" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Meta Knight", + "gameSeries": "Kirby", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive star coins and a boost item", + "write": false + } + ], + "gameID": [ + "01004D300C5AE000" + ], + "gameName": "Kirby and the Forgotten Land" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive two Picture Pieces, a Maxim Tomato, and two Point Stars", + "write": false + } + ], + "gameID": [ + "01007E3006DDA000" + ], + "gameName": "Kirby Star Allies" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive more useful items", + "write": false + } + ], + "gameID": [ + "01006B601380E000" + ], + "gameName": "Kirby's Return to Dream Land Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Kirby-themed racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive 10 Fragments", + "write": false + } + ], + "gameID": [ + "01003FB00C5A8000" + ], + "gameName": "Super Kirby Clash" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "1f010000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_1f010000-00270002.png", + "name": "Meta Knight", + "release": { + "au": "2015-01-29", + "eu": "2015-02-20", + "jp": "2015-01-22", + "na": "2015-02-01" + }, + "tail": "00270002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Nibbles", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04e10001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04e10001-01be0502.png", + "name": "Nibbles", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01be0502", + "type": "Card" + }, + { + "amiiboSeries": "Splatoon", + "character": "Marie", + "gameSeries": "Splatoon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Splatoon-themed racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "01003BC0000A0000" + ], + "gameName": "Splatoon 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "0100C2500FC20000" + ], + "gameName": "Splatoon 3" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "08020000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_08020000-025e0402.png", + "name": "Marie", + "release": { + "au": "2016-07-09", + "eu": "2016-07-08", + "jp": "2016-07-07", + "na": "2016-07-08" + }, + "tail": "025e0402", + "type": "Figure" + }, + { + "amiiboSeries": "Splatoon", + "character": "Marie", + "gameSeries": "Splatoon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Splatoon-themed racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "01003BC0000A0000" + ], + "gameName": "Splatoon 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "0100C2500FC20000" + ], + "gameName": "Splatoon 3" + } + ], + "head": "08020000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_08020000-04370402.png", + "name": "Marie - Alterna", + "release": { + "au": "2024-09-05", + "eu": "2024-09-05", + "jp": "2024-09-05", + "na": "2024-09-05" + }, + "tail": "04370402", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Marty", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + }, + { + "Usage": "Unlock special furniture items and a poster based on the card's Sanrio character", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "028f0101", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_028f0101-031a0502.png", + "name": "Marty", + "release": { + "au": null, + "eu": "2016-11-25", + "jp": "2016-11-03", + "na": null + }, + "tail": "031a0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Spork/Crackle", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "047d0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_047d0001-012e0502.png", + "name": "Spork/Crackle", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "012e0502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Boo", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09cb0101", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09cb0101-02a00e02.png", + "name": "Boo - Soccer", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02a00e02", + "type": "Card" + }, + { + "amiiboSeries": "Shovel Knight", + "character": "King Knight", + "gameSeries": "Shovel Knight", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a fairy companion and player color palette matching the character", + "write": false + } + ], + "gameID": [ + "01008D100DE46000" + ], + "gameName": "Cyber Shadow" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-specific Shovel Knight remix immediately", + "write": false + } + ], + "gameID": [ + "0100830008426000" + ], + "gameName": "Just Shapes & Beats" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock character-specific challenge stages, a character-based fairy companion, and costumes for the character", + "write": false + } + ], + "gameID": [ + "010057D0021E8000" + ], + "gameName": "Shovel Knight" + }, + { + "amiiboUsage": [ + { + "Usage": "Summon a fairy friend", + "write": false + } + ], + "gameID": [ + "0100B62017E68000" + ], + "gameName": "Shovel Knight Dig" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume for the character", + "write": false + } + ], + "gameID": [ + "0100B380022AE000" + ], + "gameName": "Shovel Knight Showdown" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "35c30000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_35c30000-036e0a02.png", + "name": "King Knight", + "release": { + "au": null, + "eu": "2019-12-10", + "jp": null, + "na": "2019-12-10" + }, + "tail": "036e0a02", + "type": "Figure" + }, + { + "amiiboSeries": "Splatoon", + "character": "Inkling", + "gameSeries": "Splatoon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Splatoon-themed racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "01003BC0000A0000" + ], + "gameName": "Splatoon 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "0100C2500FC20000" + ], + "gameName": "Splatoon 3" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "08000300", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_08000300-00400402.png", + "name": "Inkling Squid", + "release": { + "au": "2015-05-30", + "eu": "2015-05-29", + "jp": "2015-05-28", + "na": "2015-05-29" + }, + "tail": "00400402", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Henry", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "034b0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_034b0001-009f0502.png", + "name": "Henry", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "009f0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Blathers", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "01920000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01920000-02470502.png", + "name": "Blathers", + "release": { + "au": "2016-01-30", + "eu": "2016-01-29", + "jp": "2015-12-17", + "na": "2016-01-22" + }, + "tail": "02470502", + "type": "Figure" + }, + { + "amiiboSeries": "Legend Of Zelda", + "character": "Revali", + "gameSeries": "The Legend of Zelda", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive specific crafting materials or a weapon for the character", + "write": false + } + ], + "gameID": [ + "01002B00111A2000" + ], + "gameName": "Hyrule Warriors: Age of Calamity" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Legend of Zelda-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a chest of loot, potentially containing gear inspired by the Legend of Zelda series", + "write": false + } + ], + "gameID": [ + "01000A10041EA000" + ], + "gameName": "The Elder Scrolls V: Skyrim" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive materials and a rare or exclusive item", + "write": false + } + ], + "gameID": [ + "01007EF00011E000" + ], + "gameName": "The Legend of Zelda: Breath of the Wild" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock one of five amiibo-exclusive Chamber Dungeon chambers", + "write": false + }, + { + "Usage": "Save your Chamber Dungeon to the amiibo to share with friends", + "write": true + } + ], + "gameID": [ + "01006BB00C6F0000" + ], + "gameName": "The Legend of Zelda: Link's Awakening" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive character-specific paraglider fabric", + "write": false + }, + { + "Usage": "Receive materials and a weapon or rare item", + "write": false + } + ], + "gameID": [ + "0100F2C0115B6000" + ], + "gameName": "The Legend of Zelda: Tears of the Kingdom" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive the Black Cat Clothes", + "write": false + }, + { + "Usage": "Receive random materials", + "write": false + } + ], + "gameID": [ + "01008CF01BAAC000" + ], + "gameName": "The Legend of Zelda: Echoes of Wisdom" + } + ], + "head": "01080000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01080000-035b0902.png", + "name": "Revali", + "release": { + "au": "2017-11-10", + "eu": "2017-11-10", + "jp": "2017-11-10", + "na": "2017-11-10" + }, + "tail": "035b0902", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Timbra", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04cf0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04cf0001-00e10502.png", + "name": "Timbra", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00e10502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Ed", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03aa0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03aa0001-00e60502.png", + "name": "Ed", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00e60502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Sable", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01870001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01870001-03b00502.png", + "name": "Sable", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03b00502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Keaton", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04530001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04530001-01040502.png", + "name": "Keaton", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "01040502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Hugh", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "047b0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_047b0001-00f50502.png", + "name": "Hugh", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00f50502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Wart Jr.", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "033d0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_033d0001-013a0502.png", + "name": "Wart Jr.", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "013a0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Kicks", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01940001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01940001-00aa0502.png", + "name": "Kicks", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00aa0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Shrunk", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01b10001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01b10001-00b20502.png", + "name": "Shrunk", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00b20502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Eugene", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03c60001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03c60001-00930502.png", + "name": "Eugene", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00930502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Pecan", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04e00001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04e00001-00f70502.png", + "name": "Pecan", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00f70502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Lucario", + "gameSeries": "Pokemon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "1ac00000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_1ac00000-00110002.png", + "name": "Lucario", + "release": { + "au": "2015-01-29", + "eu": "2015-01-23", + "jp": "2015-01-22", + "na": "2015-02-01" + }, + "tail": "00110002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Sparro", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "023f0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_023f0001-01660502.png", + "name": "Sparro", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01660502", + "type": "Card" + }, + { + "amiiboSeries": "Legend Of Zelda", + "character": "Zelda", + "gameSeries": "The Legend of Zelda", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive specific crafting materials or a weapon for the character", + "write": false + } + ], + "gameID": [ + "01002B00111A2000" + ], + "gameName": "Hyrule Warriors: Age of Calamity" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a weapon rated 3 stars or higher", + "write": false + } + ], + "gameID": [ + "0100AE00096EA000" + ], + "gameName": "Hyrule Warriors: Definitive Edition" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Legend of Zelda-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a chest of loot, potentially containing gear inspired by the Legend of Zelda series", + "write": false + } + ], + "gameID": [ + "01000A10041EA000" + ], + "gameName": "The Elder Scrolls V: Skyrim" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive materials and a rare or exclusive item", + "write": false + } + ], + "gameID": [ + "01007EF00011E000" + ], + "gameName": "The Legend of Zelda: Breath of the Wild" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock one of five amiibo-exclusive Chamber Dungeon chambers", + "write": false + }, + { + "Usage": "Save your Chamber Dungeon to the amiibo to share with friends", + "write": true + } + ], + "gameID": [ + "01006BB00C6F0000" + ], + "gameName": "The Legend of Zelda: Link's Awakening" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive character-specific paraglider fabric", + "write": false + }, + { + "Usage": "Receive materials and a weapon or rare item", + "write": false + } + ], + "gameID": [ + "0100F2C0115B6000" + ], + "gameName": "The Legend of Zelda: Tears of the Kingdom" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive the Blue Attire", + "write": false + }, + { + "Usage": "Receive random materials", + "write": false + } + ], + "gameID": [ + "01008CF01BAAC000" + ], + "gameName": "The Legend of Zelda: Echoes of Wisdom" + } + ], + "head": "01010000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01010000-03520902.png", + "name": "Toon Zelda - The Wind Waker", + "release": { + "au": "2016-12-03", + "eu": "2016-12-02", + "jp": "2016-12-01", + "na": "2016-12-02" + }, + "tail": "03520902", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Isabelle", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "01810301", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01810301-01700502.png", + "name": "Isabelle - Dress", + "release": { + "au": "2016-06-18", + "eu": "2016-06-17", + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01700502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Kabuki", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02660001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02660001-00680502.png", + "name": "Kabuki", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00680502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Eloise", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03260001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03260001-01390502.png", + "name": "Eloise", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01390502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Gala", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04850001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04850001-014c0502.png", + "name": "Gala", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "014c0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Tex", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "046b0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_046b0001-01970502.png", + "name": "Tex", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01970502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Reese", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "018a0000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_018a0000-02450502.png", + "name": "Reese", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-11-21", + "na": "2015-11-13" + }, + "tail": "02450502", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Bianca", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "05000001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_05000001-00e70502.png", + "name": "Bianca", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00e70502", + "type": "Card" + }, + { + "amiiboSeries": "Super Mario Bros.", + "character": "Mario", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive weapon for the character and their Rabbid counterpart", + "write": false + } + ], + "gameID": [ + "010067300059A000" + ], + "gameName": "Mario + Rabbids: Kingdom Battle" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a character-based costume", + "write": false + }, + { + "Usage": "Gain temporary invincibility", + "write": false + } + ], + "gameID": [ + "0100000000010000" + ], + "gameName": "Super Mario Odyssey" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01006000040C2000" + ], + "gameName": "Yoshi's Crafted World" + } + ], + "head": "00000000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00000000-00340102.png", + "name": "Mario", + "release": { + "au": "2015-03-21", + "eu": "2015-03-20", + "jp": "2015-03-12", + "na": "2015-03-20" + }, + "tail": "00340102", + "type": "Figure" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Mii", + "gameSeries": "Mii", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "07c00200", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_07c00200-00230002.png", + "name": "Mii Gunner", + "release": { + "au": "2015-09-26", + "eu": "2015-09-25", + "jp": "2015-09-10", + "na": "2015-11-01" + }, + "tail": "00230002", + "type": "Figure" + }, + { + "amiiboSeries": "Super Mario Bros.", + "character": "Peach", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a costume based on the character (short-hair version)", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock Super Mario Odyssey-themed levels early", + "write": false + } + ], + "gameID": [ + "01009BF0072D4000" + ], + "gameName": "Captain Toad: Treasure Tracker" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive weapon for the character and their Rabbid counterpart", + "write": false + } + ], + "gameID": [ + "010067300059A000" + ], + "gameName": "Mario + Rabbids: Kingdom Battle" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a character-based costume", + "write": false + } + ], + "gameID": [ + "0100000000010000" + ], + "gameName": "Super Mario Odyssey" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a life-up heart", + "write": false + } + ], + "gameID": [ + "0100000000010000" + ], + "gameName": "Super Mario Odyssey" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01006000040C2000" + ], + "gameName": "Yoshi's Crafted World" + } + ], + "head": "00020000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00020000-03720102.png", + "name": "Peach - Wedding", + "release": { + "au": "2017-10-27", + "eu": "2017-10-27", + "jp": "2017-10-27", + "na": "2017-10-27" + }, + "tail": "03720102", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Cephalobot", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "0a170001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0a170001-03ce0502.png", + "name": "Cephalobot", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03ce0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Gwen", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04640001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04640001-00c00502.png", + "name": "Gwen", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00c00502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Donkey Kong", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c70101", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c70101-028c0e02.png", + "name": "Donkey Kong - Soccer", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "028c0e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Kevin", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04870001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04870001-01bf0502.png", + "name": "Kevin", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01bf0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Blaire", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04de0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04de0001-00ce0502.png", + "name": "Blaire", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00ce0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Wolfgang", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "050d0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_050d0001-01420502.png", + "name": "Wolfgang", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01420502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Yoshi", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c40301", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c40301-027f0e02.png", + "name": "Yoshi - Tennis", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "027f0e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Resetti", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "018e0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_018e0001-00490502.png", + "name": "Resetti", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00490502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Peggy", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04830001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04830001-01b00502.png", + "name": "Peggy", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01b00502", + "type": "Card" + }, + { + "amiiboSeries": "Legend Of Zelda", + "character": "Link", + "gameSeries": "The Legend of Zelda", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a costume based on the character (short-hair version)", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive specific crafting materials or a weapon for the character", + "write": false + } + ], + "gameID": [ + "01002B00111A2000" + ], + "gameName": "Hyrule Warriors: Age of Calamity" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a weapon rated 3 stars or higher", + "write": false + } + ], + "gameID": [ + "0100AE00096EA000" + ], + "gameName": "Hyrule Warriors: Definitive Edition" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Legend of Zelda-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a chest of loot, potentially containing gear inspired by the Legend of Zelda series", + "write": false + } + ], + "gameID": [ + "01000A10041EA000" + ], + "gameName": "The Elder Scrolls V: Skyrim" + }, + { + "amiiboUsage": [ + { + "Usage": "Bring Epona into the game as a rideable horse", + "write": false + }, + { + "Usage": "Receive materials and a rare or exclusive item", + "write": false + } + ], + "gameID": [ + "01007EF00011E000" + ], + "gameName": "The Legend of Zelda: Breath of the Wild" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock one of five amiibo-exclusive Chamber Dungeon chambers", + "write": false + }, + { + "Usage": "Save your Chamber Dungeon to the amiibo to share with friends", + "write": true + } + ], + "gameID": [ + "01006BB00C6F0000" + ], + "gameName": "The Legend of Zelda: Link's Awakening" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive character-specific paraglider fabric", + "write": false + }, + { + "Usage": "Bring Epona into the game as a rideable horse", + "write": false + }, + { + "Usage": "Receive materials and a weapon or rare item", + "write": false + } + ], + "gameID": [ + "0100F2C0115B6000" + ], + "gameName": "The Legend of Zelda: Tears of the Kingdom" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive the Red Tunic", + "write": false + }, + { + "Usage": "Receive random materials", + "write": false + } + ], + "gameID": [ + "01008CF01BAAC000" + ], + "gameName": "The Legend of Zelda: Echoes of Wisdom" + } + ], + "head": "01000000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01000000-034d0902.png", + "name": "Link - Twilight Princess", + "release": { + "au": "2017-06-24", + "eu": "2017-06-23", + "jp": "2017-06-23", + "na": "2017-06-23" + }, + "tail": "034d0902", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Boyd", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "036e0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_036e0001-02fb0502.png", + "name": "Boyd", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "02fb0502", + "type": "Card" + }, + { + "amiiboSeries": "Monster Hunter", + "character": "One-Eyed Rathalos", + "gameSeries": "Monster Hunter", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock Monster Hunter Stories 2 sticker set", + "write": false + } + ], + "gameID": [ + "0100B04011742000" + ], + "gameName": "Monster Hunter Rise" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a character-based monstie egg", + "write": false + } + ], + "gameID": [ + "010069301B1D4000" + ], + "gameName": "Monster Hunter Stories" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the Hakum Rider Outfit layered armor set / Have Tsukino read your fortune and receive a random item", + "write": false + } + ], + "gameID": [ + "0100E21011446000" + ], + "gameName": "Monster Hunter Stories 2: Wings of Ruin" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a lot of BP / Receive better supplies (compared to other amiibo)", + "write": false + } + ], + "gameID": [ + "0100643002136000" + ], + "gameName": "Resident Evil Revelations" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a lot of BP / Receive better supplies (compared to other amiibo)", + "write": false + } + ], + "gameID": [ + "010095300212A000" + ], + "gameName": "Resident Evil Revelations 2" + } + ], + "head": "35000100", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_35000100-02e10f02.png", + "name": "One-Eyed Rathalos and Rider - Male", + "release": { + "au": null, + "eu": null, + "jp": "2016-10-08", + "na": null + }, + "tail": "02e10f02", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Gabi", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04990001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04990001-00df0502.png", + "name": "Gabi", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00df0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "T-Bone", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "024f0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_024f0001-00810502.png", + "name": "T-Bone", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00810502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Wario", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c50301", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c50301-02840e02.png", + "name": "Wario - Tennis", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02840e02", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Fox", + "gameSeries": "Star Fox", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a Star Fox costume", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "05800000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_05800000-00050002.png", + "name": "Fox", + "release": { + "au": "2014-11-29", + "eu": "2014-11-28", + "jp": "2014-12-06", + "na": "2014-11-21" + }, + "tail": "00050002", + "type": "Figure" + }, + { + "amiiboSeries": "Splatoon", + "character": "Inkling", + "gameSeries": "Splatoon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Splatoon-themed racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "01003BC0000A0000" + ], + "gameName": "Splatoon 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "0100C2500FC20000" + ], + "gameName": "Splatoon 3" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "08000300", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_08000300-02610402.png", + "name": "Inkling Squid - Orange", + "release": { + "au": "2016-07-09", + "eu": "2016-07-08", + "jp": "2016-07-07", + "na": "2016-07-08" + }, + "tail": "02610402", + "type": "Figure" + }, + { + "amiiboSeries": "Legend Of Zelda", + "character": "Ganon", + "gameSeries": "The Legend of Zelda", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive specific crafting materials or a weapon for the character", + "write": false + } + ], + "gameID": [ + "01002B00111A2000" + ], + "gameName": "Hyrule Warriors: Age of Calamity" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a weapon rated 3 stars or higher", + "write": false + } + ], + "gameID": [ + "0100AE00096EA000" + ], + "gameName": "Hyrule Warriors: Definitive Edition" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Legend of Zelda-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a chest of loot, potentially containing gear inspired by the Legend of Zelda series", + "write": false + } + ], + "gameID": [ + "01000A10041EA000" + ], + "gameName": "The Elder Scrolls V: Skyrim" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive materials and a rare or exclusive item", + "write": false + } + ], + "gameID": [ + "01007EF00011E000" + ], + "gameName": "The Legend of Zelda: Breath of the Wild" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock one of five amiibo-exclusive Chamber Dungeon chambers", + "write": false + }, + { + "Usage": "Save your Chamber Dungeon to the amiibo to share with friends", + "write": true + } + ], + "gameID": [ + "01006BB00C6F0000" + ], + "gameName": "The Legend of Zelda: Link's Awakening" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive character-specific paraglider fabric", + "write": false + }, + { + "Usage": "Receive materials and a weapon or rare item", + "write": false + } + ], + "gameID": [ + "0100F2C0115B6000" + ], + "gameName": "The Legend of Zelda: Tears of the Kingdom" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive the Black Cat Clothes", + "write": false + }, + { + "Usage": "Receive random materials", + "write": false + } + ], + "gameID": [ + "01008CF01BAAC000" + ], + "gameName": "The Legend of Zelda: Echoes of Wisdom" + } + ], + "head": "01020100", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01020100-041a0902.png", + "name": "Ganondorf - Tears of the Kingdom", + "release": { + "au": "2023-11-03", + "eu": "2023-11-03", + "jp": "2023-11-03", + "na": "2023-11-03" + }, + "tail": "041a0902", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Boris", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04810001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04810001-02f10502.png", + "name": "Boris", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "02f10502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Antonio", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02010001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02010001-016a0502.png", + "name": "Antonio", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "016a0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Murphy", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02840001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02840001-02fe0502.png", + "name": "Murphy", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "02fe0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Wade", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04680001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04680001-02f20502.png", + "name": "Wade", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "02f20502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Stitches", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02820001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02820001-01d60502.png", + "name": "Stitches - Amiibo Festival", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-11-21", + "na": "2015-11-13" + }, + "tail": "01d60502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Pascal", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01a40001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01a40001-004d0502.png", + "name": "Pascal", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "004d0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Wisp", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "0a060001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0a060001-03ba0502.png", + "name": "Wisp", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03ba0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Fuchsia", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02dc0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02dc0001-00be0502.png", + "name": "Fuchsia", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00be0502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Jigglypuff", + "gameSeries": "Pokemon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "19270000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_19270000-00260002.png", + "name": "Jigglypuff", + "release": { + "au": "2015-05-30", + "eu": "2015-05-29", + "jp": "2015-04-29", + "na": "2015-05-29" + }, + "tail": "00260002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Tucker", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "032c0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_032c0001-01480502.png", + "name": "Tucker", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01480502", + "type": "Card" + }, + { + "amiiboSeries": "Legend Of Zelda", + "character": "Link", + "gameSeries": "The Legend of Zelda", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a costume based on the character (short-hair version)", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive specific crafting materials or a weapon for the character", + "write": false + } + ], + "gameID": [ + "01002B00111A2000" + ], + "gameName": "Hyrule Warriors: Age of Calamity" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a weapon rated 3 stars or higher", + "write": false + } + ], + "gameID": [ + "0100AE00096EA000" + ], + "gameName": "Hyrule Warriors: Definitive Edition" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Legend of Zelda-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a chest of loot, potentially containing gear inspired by the Legend of Zelda series", + "write": false + } + ], + "gameID": [ + "01000A10041EA000" + ], + "gameName": "The Elder Scrolls V: Skyrim" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock one of five amiibo-exclusive Chamber Dungeon chambers", + "write": false + }, + { + "Usage": "Save your Chamber Dungeon to the amiibo to share with friends", + "write": true + } + ], + "gameID": [ + "01006BB00C6F0000" + ], + "gameName": "The Legend of Zelda: Link's Awakening" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive character-specific paraglider fabric", + "write": false + }, + { + "Usage": "Receive materials and a weapon or rare item", + "write": false + } + ], + "gameID": [ + "0100F2C0115B6000" + ], + "gameName": "The Legend of Zelda: Tears of the Kingdom" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive the Red Tunic", + "write": false + }, + { + "Usage": "Receive random materials", + "write": false + } + ], + "gameID": [ + "01008CF01BAAC000" + ], + "gameName": "The Legend of Zelda: Echoes of Wisdom" + } + ], + "head": "01000000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01000000-034b0902.png", + "name": "Link - Ocarina of Time", + "release": { + "au": "2016-12-03", + "eu": "2016-12-02", + "jp": "2016-12-01", + "na": "2016-12-02" + }, + "tail": "034b0902", + "type": "Figure" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Boo", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09cb0201", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09cb0201-02a10e02.png", + "name": "Boo - Baseball", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02a10e02", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Peach", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c20401", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c20401-02760e02.png", + "name": "Peach - Golf", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02760e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Elvis", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03e70001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03e70001-012a0502.png", + "name": "Elvis", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "012a0502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Pichu", + "gameSeries": "Pokemon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "19ac0000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_19ac0000-03850002.png", + "name": "Pichu", + "release": { + "au": "2019-07-19", + "eu": "2019-07-19", + "jp": "2019-07-19", + "na": "2019-07-19" + }, + "tail": "03850002", + "type": "Figure" + }, + { + "amiiboSeries": "Splatoon", + "character": "Pearl", + "gameSeries": "Splatoon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Splatoon-themed racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "01003BC0000A0000" + ], + "gameName": "Splatoon 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "0100C2500FC20000" + ], + "gameName": "Splatoon 3" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "08030000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_08030000-03760402.png", + "name": "Pearl", + "release": { + "au": "2018-07-13", + "eu": "2018-07-13", + "jp": "2018-07-13", + "na": "2018-07-13" + }, + "tail": "03760402", + "type": "Figure" + }, + { + "amiiboSeries": "Splatoon", + "character": "Pearl", + "gameSeries": "Splatoon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Splatoon-themed racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "01003BC0000A0000" + ], + "gameName": "Splatoon 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "0100C2500FC20000" + ], + "gameName": "Splatoon 3" + } + ], + "head": "08030000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_08030000-04380402.png", + "name": "Pearl - Side Order", + "release": { + "au": "2024-09-05", + "eu": "2024-09-05", + "jp": "2024-09-05", + "na": "2024-09-05" + }, + "tail": "04380402", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Shino", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "0a140001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0a140001-03cb0502.png", + "name": "Shino", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03cb0502", + "type": "Card" + }, + { + "amiiboSeries": "Legend Of Zelda", + "character": "Link", + "gameSeries": "The Legend of Zelda", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a costume based on the character (short-hair version)", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a weapon rated 3 stars or higher", + "write": false + } + ], + "gameID": [ + "0100AE00096EA000" + ], + "gameName": "Hyrule Warriors: Definitive Edition" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Legend of Zelda-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a chest of loot, potentially containing gear inspired by the Legend of Zelda series", + "write": false + } + ], + "gameID": [ + "01000A10041EA000" + ], + "gameName": "The Elder Scrolls V: Skyrim" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive materials and a rare or exclusive item", + "write": false + } + ], + "gameID": [ + "01007EF00011E000" + ], + "gameName": "The Legend of Zelda: Breath of the Wild" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock one of five amiibo-exclusive Chamber Dungeon chambers", + "write": false + }, + { + "Usage": "Save your Chamber Dungeon to the amiibo to share with friends", + "write": true + } + ], + "gameID": [ + "01006BB00C6F0000" + ], + "gameName": "The Legend of Zelda: Link's Awakening" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive character-specific paraglider fabric", + "write": false + }, + { + "Usage": "Receive materials and a weapon or rare item", + "write": false + } + ], + "gameID": [ + "0100F2C0115B6000" + ], + "gameName": "The Legend of Zelda: Tears of the Kingdom" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive the Red Tunic", + "write": false + }, + { + "Usage": "Receive random materials", + "write": false + } + ], + "gameID": [ + "01008CF01BAAC000" + ], + "gameName": "The Legend of Zelda: Echoes of Wisdom" + } + ], + "head": "01000000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01000000-034c0902.png", + "name": "Link - Majora's Mask", + "release": { + "au": "2017-06-24", + "eu": "2017-06-23", + "jp": "2017-06-23", + "na": "2017-06-23" + }, + "tail": "034c0902", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Lottie", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01c10101", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01c10101-017a0502.png", + "name": "Lottie - Black Skirt And Bow", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "017a0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Vivian", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "05130001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_05130001-02e70502.png", + "name": "Vivian", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "02e70502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Bettina", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "041b0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_041b0001-00f10502.png", + "name": "Bettina", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00f10502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Alex", + "gameSeries": "Minecraft", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "3dc10000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_3dc10000-04230002.png", + "name": "Alex", + "release": { + "au": "2022-09-09", + "eu": "2022-09-09", + "jp": "2022-09-09", + "na": "2022-09-09" + }, + "tail": "04230002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Lyle", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01aa0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01aa0001-00530502.png", + "name": "Lyle", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00530502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Walker", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02f00001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02f00001-00a70502.png", + "name": "Walker", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00a70502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Ozzie", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03c10001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03c10001-00bb0502.png", + "name": "Ozzie", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00bb0502", + "type": "Card" + }, + { + "amiiboSeries": "Skylanders", + "character": "Donkey Kong", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "0008ff00", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0008ff00-023b0702.png", + "name": "Turbo Charge Donkey Kong", + "release": { + "au": "2015-09-24", + "eu": "2015-09-25", + "jp": null, + "na": "2015-09-20" + }, + "tail": "023b0702", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Sterling", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04520001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04520001-00730502.png", + "name": "Sterling", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00730502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Isabelle", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "01810001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01810001-01d40502.png", + "name": "Isabelle - Character Parfait", + "release": { + "au": null, + "eu": null, + "jp": "2015-08-01", + "na": null + }, + "tail": "01d40502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Claude", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "049f0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_049f0001-03010502.png", + "name": "Claude", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "03010502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Goldie", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02ea0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02ea0001-01d50502.png", + "name": "Goldie - Amiibo Festival", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-11-21", + "na": "2015-11-13" + }, + "tail": "01d50502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Fauna", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02d60001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02d60001-00560502.png", + "name": "Fauna", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00560502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Ankha", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02700001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02700001-00ff0502.png", + "name": "Ankha", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00ff0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Gulliver", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01a20001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01a20001-03b90502.png", + "name": "Gulliver", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03b90502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Kid Cat", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02670001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02670001-01080502.png", + "name": "Kid Cat", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "01080502", + "type": "Card" + }, + { + "amiiboSeries": "Yu-Gi-Oh!", + "character": "Romin Kirishima", + "gameSeries": "Yu-Gi-Oh!", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive items/bonuses", + "write": false + } + ], + "gameID": [ + "01003C101454A000" + ], + "gameName": "Yu-Gi-Oh! Rush Duel Saikyo Battle Royale" + } + ], + "head": "38430001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_38430001-04271902.png", + "name": "Romin Kirishima", + "release": { + "au": null, + "eu": null, + "jp": null, + "na": "2021-08-12" + }, + "tail": "04271902", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Katt", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02720001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02720001-01860502.png", + "name": "Katt", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01860502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Piranha Plant", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "00240000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00240000-038d0002.png", + "name": "Piranha Plant", + "release": { + "au": "2019-02-15", + "eu": "2019-02-15", + "jp": "2019-02-15", + "na": "2019-02-15" + }, + "tail": "038d0002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Rex", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03e80001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03e80001-02f50502.png", + "name": "Rex", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "02f50502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Beau", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02dd0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02dd0001-00ea0502.png", + "name": "Beau", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00ea0502", + "type": "Card" + }, + { + "amiiboSeries": "Legend Of Zelda", + "character": "Bokoblin", + "gameSeries": "The Legend of Zelda", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Legend of Zelda-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a chest of loot, potentially containing gear inspired by the Legend of Zelda series", + "write": false + } + ], + "gameID": [ + "01000A10041EA000" + ], + "gameName": "The Elder Scrolls V: Skyrim" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive materials and a rare or exclusive item", + "write": false + } + ], + "gameID": [ + "01007EF00011E000" + ], + "gameName": "The Legend of Zelda: Breath of the Wild" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock one of five amiibo-exclusive Chamber Dungeon chambers", + "write": false + }, + { + "Usage": "Save your Chamber Dungeon to the amiibo to share with friends", + "write": true + } + ], + "gameID": [ + "01006BB00C6F0000" + ], + "gameName": "The Legend of Zelda: Link's Awakening" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive character-specific paraglider fabric", + "write": false + }, + { + "Usage": "Receive materials and a weapon or rare item", + "write": false + } + ], + "gameID": [ + "0100F2C0115B6000" + ], + "gameName": "The Legend of Zelda: Tears of the Kingdom" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive the Black Cat Clothes", + "write": false + }, + { + "Usage": "Receive random materials", + "write": false + } + ], + "gameID": [ + "01008CF01BAAC000" + ], + "gameName": "The Legend of Zelda: Echoes of Wisdom" + } + ], + "head": "01410000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01410000-035c0902.png", + "name": "Bokoblin", + "release": { + "au": "2017-03-03", + "eu": "2017-03-03", + "jp": "2017-03-03", + "na": "2017-03-03" + }, + "tail": "035c0902", + "type": "Figure" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Rosalina", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09cf0201", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09cf0201-02b50e02.png", + "name": "Rosalina - Baseball", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02b50e02", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Mewtwo", + "gameSeries": "Pokemon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "19960000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_19960000-023d0002.png", + "name": "Mewtwo", + "release": { + "au": "2015-10-24", + "eu": "2015-10-23", + "jp": "2015-10-29", + "na": "2015-11-13" + }, + "tail": "023d0002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Bertha", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03930001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03930001-00a00502.png", + "name": "Bertha", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00a00502", + "type": "Card" + }, + { + "amiiboSeries": "Pokemon", + "character": "Shadow Mewtwo", + "gameSeries": "Pokemon", + "gamesSwitch": [], + "head": "1d000001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_1d000001-025c0d02.png", + "name": "Shadow Mewtwo", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-03-18", + "na": "2016-03-18" + }, + "tail": "025c0d02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Puck", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04650001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04650001-006e0502.png", + "name": "Puck", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "006e0502", + "type": "Card" + }, + { + "amiiboSeries": "Legend Of Zelda", + "character": "Urbosa", + "gameSeries": "The Legend of Zelda", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive specific crafting materials or a weapon for the character", + "write": false + } + ], + "gameID": [ + "01002B00111A2000" + ], + "gameName": "Hyrule Warriors: Age of Calamity" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Legend of Zelda-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a chest of loot, potentially containing gear inspired by the Legend of Zelda series", + "write": false + } + ], + "gameID": [ + "01000A10041EA000" + ], + "gameName": "The Elder Scrolls V: Skyrim" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive materials and a rare or exclusive item", + "write": false + } + ], + "gameID": [ + "01007EF00011E000" + ], + "gameName": "The Legend of Zelda: Breath of the Wild" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock one of five amiibo-exclusive Chamber Dungeon chambers", + "write": false + }, + { + "Usage": "Save your Chamber Dungeon to the amiibo to share with friends", + "write": true + } + ], + "gameID": [ + "01006BB00C6F0000" + ], + "gameName": "The Legend of Zelda: Link's Awakening" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive character-specific paraglider fabric", + "write": false + }, + { + "Usage": "Receive materials and a weapon or rare item", + "write": false + } + ], + "gameID": [ + "0100F2C0115B6000" + ], + "gameName": "The Legend of Zelda: Tears of the Kingdom" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive the Black Cat Clothes", + "write": false + }, + { + "Usage": "Receive random materials", + "write": false + } + ], + "gameID": [ + "01008CF01BAAC000" + ], + "gameName": "The Legend of Zelda: Echoes of Wisdom" + } + ], + "head": "01060000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01060000-03590902.png", + "name": "Urbosa", + "release": { + "au": "2017-11-10", + "eu": "2017-11-10", + "jp": "2017-11-10", + "na": "2017-11-10" + }, + "tail": "03590902", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Rosie", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "025f0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_025f0001-01c50502.png", + "name": "Rosie", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01c50502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Byleth", + "gameSeries": "Fire Emblem", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive a Fashion Ticket and a Music Ticket, for unlocking any of the available costumes and music tracks", + "write": false + } + ], + "gameID": [ + "0100A6301214E000" + ], + "gameName": "Fire Emblem Engage" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive better-quality randomized resources, weapons, or equipment", + "write": false + } + ], + "gameID": [ + "010071F0143EA000" + ], + "gameName": "Fire Emblem Warriors: Three Hopes" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "210b0000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_210b0000-03a50002.png", + "name": "Byleth", + "release": { + "au": "2021-03-26", + "eu": "2021-03-26", + "jp": "2021-03-26", + "na": "2021-03-26" + }, + "tail": "03a50002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Spike", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04b40001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04b40001-030c0502.png", + "name": "Spike", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "030c0502", + "type": "Card" + }, + { + "amiiboSeries": "Legend Of Zelda", + "character": "Link", + "gameSeries": "The Legend of Zelda", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a costume based on the character (short-hair version)", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive specific crafting materials or a weapon for the character", + "write": false + } + ], + "gameID": [ + "01002B00111A2000" + ], + "gameName": "Hyrule Warriors: Age of Calamity" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a weapon rated 3 stars or higher", + "write": false + } + ], + "gameID": [ + "0100AE00096EA000" + ], + "gameName": "Hyrule Warriors: Definitive Edition" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Legend of Zelda-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a chest of loot, potentially containing gear inspired by the Legend of Zelda series", + "write": false + } + ], + "gameID": [ + "01000A10041EA000" + ], + "gameName": "The Elder Scrolls V: Skyrim" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive materials and a rare or exclusive item", + "write": false + } + ], + "gameID": [ + "01007EF00011E000" + ], + "gameName": "The Legend of Zelda: Breath of the Wild" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the Shadow Link Plus Effect for Chamber Dungeons", + "write": false + } + ], + "gameID": [ + "01006BB00C6F0000" + ], + "gameName": "The Legend of Zelda: Link's Awakening" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock one of five amiibo-exclusive Chamber Dungeon chambers", + "write": false + }, + { + "Usage": "Save your Chamber Dungeon to the amiibo to share with friends", + "write": true + } + ], + "gameID": [ + "01006BB00C6F0000" + ], + "gameName": "The Legend of Zelda: Link's Awakening" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive character-specific paraglider fabric", + "write": false + }, + { + "Usage": "Receive materials and a weapon or rare item", + "write": false + } + ], + "gameID": [ + "0100F2C0115B6000" + ], + "gameName": "The Legend of Zelda: Tears of the Kingdom" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive the Red Tunic", + "write": false + }, + { + "Usage": "Receive random materials", + "write": false + } + ], + "gameID": [ + "01008CF01BAAC000" + ], + "gameName": "The Legend of Zelda: Echoes of Wisdom" + } + ], + "head": "01000000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01000000-03990902.png", + "name": "Link - Link's Awakening", + "release": { + "au": "2019-09-20", + "eu": "2019-09-20", + "jp": "2019-09-20", + "na": "2019-09-20" + }, + "tail": "03990902", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Moose", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "041a0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_041a0001-00e00502.png", + "name": "Moose", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00e00502", + "type": "Card" + }, + { + "amiiboSeries": "8-bit Mario", + "character": "Mario", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive weapon for the character and their Rabbid counterpart", + "write": false + } + ], + "gameID": [ + "010067300059A000" + ], + "gameName": "Mario + Rabbids: Kingdom Battle" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a character-based costume", + "write": false + }, + { + "Usage": "Gain temporary invincibility", + "write": false + } + ], + "gameID": [ + "0100000000010000" + ], + "gameName": "Super Mario Odyssey" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01006000040C2000" + ], + "gameName": "Yoshi's Crafted World" + } + ], + "head": "00000000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00000000-02390602.png", + "name": "8-Bit Mario Modern Color", + "release": { + "au": "2015-10-24", + "eu": "2015-10-23", + "jp": "2015-09-10", + "na": "2015-09-11" + }, + "tail": "02390602", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Cobb", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04800001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04800001-008d0502.png", + "name": "Cobb", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "008d0502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Diddy Kong", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c80101", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c80101-02910e02.png", + "name": "Diddy Kong - Soccer", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02910e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Portia", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02ef0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02ef0001-00580502.png", + "name": "Portia", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00580502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Benedict", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "029a0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_029a0001-00ee0502.png", + "name": "Benedict", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00ee0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Pudge", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02800001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02800001-00830502.png", + "name": "Pudge", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00830502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Jay", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "022d0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_022d0001-00f20502.png", + "name": "Jay", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00f20502", + "type": "Card" + }, + { + "amiiboSeries": "Shovel Knight", + "character": "Shovel Knight", + "gameSeries": "Shovel Knight", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock boss fight against Shovel Knight", + "write": false + } + ], + "gameID": [ + "0100192003FA4000" + ], + "gameName": "Azure Striker Gunvolt: Striker Pack" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a fairy companion and player color palette matching the character", + "write": false + } + ], + "gameID": [ + "01008D100DE46000" + ], + "gameName": "Cyber Shadow" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-specific Shovel Knight remix immediately", + "write": false + } + ], + "gameID": [ + "0100830008426000" + ], + "gameName": "Just Shapes & Beats" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock Custom Knight, and save customizations to the amiibo (Shovel of Hope only)", + "write": true + }, + { + "Usage": "Unlock character-specific challenge stages, a character-based fairy companion, and costumes for the character", + "write": false + } + ], + "gameID": [ + "010057D0021E8000" + ], + "gameName": "Shovel Knight" + }, + { + "amiiboUsage": [ + { + "Usage": "Summon a fairy friend", + "write": false + } + ], + "gameID": [ + "0100B62017E68000" + ], + "gameName": "Shovel Knight Dig" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume for the character", + "write": false + } + ], + "gameID": [ + "0100B380022AE000" + ], + "gameName": "Shovel Knight Showdown" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "35c00000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_35c00000-02500a02.png", + "name": "Shovel Knight", + "release": { + "au": "2015-12-11", + "eu": "2016-01-08", + "jp": "2016-06-30", + "na": "2016-01-08" + }, + "tail": "02500a02", + "type": "Figure" + }, + { + "amiiboSeries": "Diablo", + "character": "Loot Goblin", + "gameSeries": "Diablo", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Summon a portal to Golden Greed's Domain", + "write": false + } + ], + "gameID": [ + "01001B300B9BE000" + ], + "gameName": "Diablo III: Eternal Collection" + } + ], + "head": "38c00000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_38c00000-03911602.png", + "name": "Loot Goblin", + "release": { + "au": null, + "eu": null, + "jp": null, + "na": "2018-12-21" + }, + "tail": "03911602", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Tabby", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02690001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02690001-011f0502.png", + "name": "Tabby", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "011f0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Kody", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02810001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02810001-01200502.png", + "name": "Kody", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01200502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Bowser Jr.", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09ca0501", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09ca0501-029f0e02.png", + "name": "Bowser Jr. - Horse Racing", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "029f0e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Apollo", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "044b0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_044b0001-016c0502.png", + "name": "Apollo", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "016c0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Timmy & Tommy", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01840501", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01840501-03a90502.png", + "name": "Timmy & Tommy", + "release": { + "au": "2021-10-05", + "eu": "2021-10-05", + "jp": "2021-10-05", + "na": "2021-10-05" + }, + "tail": "03a90502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Maple", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "027e0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_027e0001-01690502.png", + "name": "Maple", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01690502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Mario", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive weapon for the character and their Rabbid counterpart", + "write": false + } + ], + "gameID": [ + "010067300059A000" + ], + "gameName": "Mario + Rabbids: Kingdom Battle" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a character-based costume", + "write": false + }, + { + "Usage": "Gain temporary invincibility", + "write": false + } + ], + "gameID": [ + "0100000000010000" + ], + "gameName": "Super Mario Odyssey" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01006000040C2000" + ], + "gameName": "Yoshi's Crafted World" + } + ], + "head": "00000100", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00000100-00190002.png", + "name": "Dr. Mario", + "release": { + "au": "2015-07-23", + "eu": "2015-07-17", + "jp": "2015-07-17", + "na": "2015-09-11" + }, + "tail": "00190002", + "type": "Figure" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Baby Mario", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09cc0501", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09cc0501-02a90e02.png", + "name": "Baby Mario - Horse Racing", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02a90e02", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Baby Luigi", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09cd0301", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09cd0301-02ac0e02.png", + "name": "Baby Luigi - Tennis", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02ac0e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Annalisa", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02080001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02080001-00960502.png", + "name": "Annalisa", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00960502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Tia", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "032d0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_032d0001-00bc0502.png", + "name": "Tia", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00bc0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Yuka", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03bc0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03bc0001-008a0502.png", + "name": "Yuka", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "008a0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Tom Nook", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "01830000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01830000-02420502.png", + "name": "Tom Nook", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-11-21", + "na": "2015-11-13" + }, + "tail": "02420502", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Pate", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03090001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03090001-00c60502.png", + "name": "Pate", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00c60502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Kirby", + "gameSeries": "Kirby", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive star coins and a boost item", + "write": false + } + ], + "gameID": [ + "01004D300C5AE000" + ], + "gameName": "Kirby and the Forgotten Land" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive two Picture Pieces, a Maxim Tomato, and two Point Stars", + "write": false + } + ], + "gameID": [ + "01007E3006DDA000" + ], + "gameName": "Kirby Star Allies" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive more useful items", + "write": false + } + ], + "gameID": [ + "01006B601380E000" + ], + "gameName": "Kirby's Return to Dream Land Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Kirby-themed racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive 10 Fragments", + "write": false + } + ], + "gameID": [ + "01003FB00C5A8000" + ], + "gameName": "Super Kirby Clash" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "1f000000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_1f000000-000a0002.png", + "name": "Kirby", + "release": { + "au": "2014-11-29", + "eu": "2014-11-28", + "jp": "2014-12-06", + "na": "2014-11-21" + }, + "tail": "000a0002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Carmen", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04a40001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04a40001-00d40502.png", + "name": "Carmen", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00d40502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Phineas", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "019c0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_019c0001-01730502.png", + "name": "Phineas", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01730502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Lily", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03380001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03380001-011d0502.png", + "name": "Lily", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "011d0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Bob", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "025d0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_025d0001-00550502.png", + "name": "Bob", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00550502", + "type": "Card" + }, + { + "amiiboSeries": "Monster Hunter", + "character": "Nabiru", + "gameSeries": "Monster Hunter", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock Monster Hunter Stories 2 sticker set", + "write": false + } + ], + "gameID": [ + "0100B04011742000" + ], + "gameName": "Monster Hunter Rise" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a character-based monstie egg", + "write": false + } + ], + "gameID": [ + "010069301B1D4000" + ], + "gameName": "Monster Hunter Stories" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the Hakum Rider Outfit layered armor set / Have Tsukino read your fortune and receive a random item", + "write": false + } + ], + "gameID": [ + "0100E21011446000" + ], + "gameName": "Monster Hunter Stories 2: Wings of Ruin" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a lot of BP / Receive better supplies (compared to other amiibo)", + "write": false + } + ], + "gameID": [ + "0100643002136000" + ], + "gameName": "Resident Evil Revelations" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a lot of BP / Receive better supplies (compared to other amiibo)", + "write": false + } + ], + "gameID": [ + "010095300212A000" + ], + "gameName": "Resident Evil Revelations 2" + } + ], + "head": "35010000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_35010000-02e30f02.png", + "name": "Nabiru", + "release": { + "au": null, + "eu": null, + "jp": "2016-10-08", + "na": null + }, + "tail": "02e30f02", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Cyrus", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "018b0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_018b0001-01150502.png", + "name": "Cyrus", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01150502", + "type": "Card" + }, + { + "amiiboSeries": "Monster Hunter Rise", + "character": "Palamute", + "gameSeries": "Monster Hunter", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock special layered armor / Enter daily lottery for a variety of useful items", + "write": false + } + ], + "gameID": [ + "0100B04011742000" + ], + "gameName": "Monster Hunter Rise" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the Hunter Sticker Set / Have Tsukino read your fortune and receive a random item", + "write": false + } + ], + "gameID": [ + "0100E21011446000" + ], + "gameName": "Monster Hunter Stories 2: Wings of Ruin" + } + ], + "head": "350a0000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_350a0000-04111802.png", + "name": "Palamute", + "release": { + "au": "2021-03-26", + "eu": "2021-03-26", + "jp": "2021-03-26", + "na": "2021-03-26" + }, + "tail": "04111802", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Isabelle", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "01810401", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01810401-03aa0502.png", + "name": "Isabelle", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03aa0502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Kazuya", + "gameSeries": "Tekken", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "33c00000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_33c00000-04200002.png", + "name": "Kazuya", + "release": { + "au": "2023-01-13", + "eu": "2023-01-13", + "jp": "2023-01-13", + "na": "2023-01-13" + }, + "tail": "04200002", + "type": "Figure" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Falco", + "gameSeries": "Star Fox", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a Star Fox costume", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "05810000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_05810000-001c0002.png", + "name": "Falco", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-11-05", + "na": "2015-11-20" + }, + "tail": "001c0002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Savannah", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03a60001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03a60001-00c80502.png", + "name": "Savannah", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00c80502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Lucas", + "gameSeries": "Earthbound", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "22810000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_22810000-02510002.png", + "name": "Lucas", + "release": { + "au": "2016-01-30", + "eu": "2016-01-29", + "jp": "2015-12-17", + "na": "2016-01-30" + }, + "tail": "02510002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Phil", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "043d0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_043d0001-007c0502.png", + "name": "Phil", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "007c0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Pete", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "019f0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_019f0001-01110502.png", + "name": "Pete", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01110502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Graham", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03800001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03800001-01870502.png", + "name": "Graham", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01870502", + "type": "Card" + }, + { + "amiiboSeries": "Monster Hunter", + "character": "Barioth", + "gameSeries": "Monster Hunter", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock Monster Hunter Stories 2 sticker set", + "write": false + } + ], + "gameID": [ + "0100B04011742000" + ], + "gameName": "Monster Hunter Rise" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a character-based monstie egg", + "write": false + } + ], + "gameID": [ + "010069301B1D4000" + ], + "gameName": "Monster Hunter Stories" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the Hakum Rider Outfit layered armor set / Have Tsukino read your fortune and receive a random item", + "write": false + } + ], + "gameID": [ + "0100E21011446000" + ], + "gameName": "Monster Hunter Stories 2: Wings of Ruin" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a lot of BP / Receive better supplies (compared to other amiibo)", + "write": false + } + ], + "gameID": [ + "0100643002136000" + ], + "gameName": "Resident Evil Revelations" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a lot of BP / Receive better supplies (compared to other amiibo)", + "write": false + } + ], + "gameID": [ + "010095300212A000" + ], + "gameName": "Resident Evil Revelations 2" + } + ], + "head": "35030100", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_35030100-02e50f02.png", + "name": "Barioth and Ayuria", + "release": { + "au": null, + "eu": null, + "jp": "2016-12-08", + "na": null + }, + "tail": "02e50f02", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Freckles", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "030e0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_030e0001-012f0502.png", + "name": "Freckles", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "012f0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Roswell", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "0a1f0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0a1f0001-03d60502.png", + "name": "Roswell", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03d60502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Bowser", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock the Chain Chomp weapon", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + }, + { + "Usage": "Make Fury Bowser appear (in Bowser's Fury mode)", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Reveal regional coin locations", + "write": false + } + ], + "gameID": [ + "0100000000010000" + ], + "gameName": "Super Mario Odyssey" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01006000040C2000" + ], + "gameName": "Yoshi's Crafted World" + } + ], + "head": "00050000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00050000-00140002.png", + "name": "Bowser", + "release": { + "au": "2015-01-29", + "eu": "2015-01-23", + "jp": "2015-01-22", + "na": "2015-02-01" + }, + "tail": "00140002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Benjamin", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02fa0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02fa0001-00970502.png", + "name": "Benjamin", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00970502", + "type": "Card" + }, + { + "amiiboSeries": "Metroid", + "character": "Samus", + "gameSeries": "Metroid", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a costume based on the character (short-hair version)", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Metroid-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Permanently increase health by 100", + "write": false + } + ], + "gameID": [ + "010093801237C000" + ], + "gameName": "Metroid Dread" + }, + { + "amiiboUsage": [ + { + "Usage": "Restore a random amount of health once per day", + "write": false + } + ], + "gameID": [ + "010093801237C000" + ], + "gameName": "Metroid Dread" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "05c00000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_05c00000-04121302.png", + "name": "Samus - Metroid Dread", + "release": { + "au": "2021-10-08", + "eu": "2021-11-05", + "jp": "2021-10-08", + "na": "2021-10-08" + }, + "tail": "04121302", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Lopez", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02db0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02db0001-005e0502.png", + "name": "Lopez", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "005e0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Willow", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04cc0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04cc0001-00a40502.png", + "name": "Willow", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00a40502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Jambette", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03450001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03450001-005f0502.png", + "name": "Jambette", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "005f0502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Charizard", + "gameSeries": "Pokemon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "19060000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_19060000-00240002.png", + "name": "Charizard", + "release": { + "au": "2015-04-25", + "eu": "2015-04-24", + "jp": "2015-04-29", + "na": "2015-05-29" + }, + "tail": "00240002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Mira", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04a70001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04a70001-01a60502.png", + "name": "Mira", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01a60502", + "type": "Card" + }, + { + "amiiboSeries": "Super Mario Bros.", + "character": "Mario", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive weapon for the character and their Rabbid counterpart", + "write": false + } + ], + "gameID": [ + "010067300059A000" + ], + "gameName": "Mario + Rabbids: Kingdom Battle" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01006000040C2000" + ], + "gameName": "Yoshi's Crafted World" + } + ], + "head": "00000300", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00000300-03a60102.png", + "name": "Mario - Cat", + "release": { + "au": "2021-02-12", + "eu": "2021-02-12", + "jp": "2021-02-12", + "na": "2021-02-12" + }, + "tail": "03a60102", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Shrunk", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01b10101", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01b10101-017b0502.png", + "name": "Shrunk - Loud Jacket", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "017b0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Raymond", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "0a0f0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0a0f0001-03c60502.png", + "name": "Raymond", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03c60502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Ione", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "0a120001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0a120001-03c90502.png", + "name": "Ione", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03c90502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Molly", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03170001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03170001-00a60502.png", + "name": "Molly", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00a60502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Gracie", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01a90001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01a90001-01760502.png", + "name": "Gracie", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01760502", + "type": "Card" + }, + { + "amiiboSeries": "Fire Emblem", + "character": "Tiki", + "gameSeries": "Fire Emblem", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive a Fashion Ticket and a Music Ticket, for unlocking any of the available costumes and music tracks", + "write": false + } + ], + "gameID": [ + "0100A6301214E000" + ], + "gameName": "Fire Emblem Engage" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive weapon", + "write": false + }, + { + "Usage": "Receive a weapon", + "write": false + } + ], + "gameID": [ + "0100F15003E64000" + ], + "gameName": "Fire Emblem Warriors" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive better-quality randomized resources, weapons, or equipment", + "write": false + } + ], + "gameID": [ + "010071F0143EA000" + ], + "gameName": "Fire Emblem Warriors: Three Hopes" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special piece of battle music / Receive higher-quality items and materials", + "write": false + } + ], + "gameID": [ + "010055D009F78000" + ], + "gameName": "Fire Emblem: Three Houses" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "21090000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_21090000-03701202.png", + "name": "Tiki", + "release": { + "au": "2017-10-20", + "eu": "2017-10-20", + "jp": "2017-09-28", + "na": "2017-10-20" + }, + "tail": "03701202", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Hans", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03730001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03730001-01340502.png", + "name": "Hans", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01340502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Nat", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "019b0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_019b0001-00b60502.png", + "name": "Nat", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00b60502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Tommy", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01860301", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01860301-01750502.png", + "name": "Tommy - Suit", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01750502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Mega Man", + "gameSeries": "Megaman", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive E Tanks and other useful in-game items", + "write": false + } + ], + "gameID": [ + "0100B0C0086B0000" + ], + "gameName": "Mega Man 11" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock 11 exclusive challenge stages designed by fans", + "write": false + } + ], + "gameID": [ + "01002D4007AE0000" + ], + "gameName": "Mega Man Legacy Collection" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock new platforming challenges", + "write": false + } + ], + "gameID": [ + "0100842008EC4000" + ], + "gameName": "Mega Man Legacy Collection 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a lot of BP / Receive better supplies (compared to other amiibo)", + "write": false + } + ], + "gameID": [ + "0100643002136000" + ], + "gameName": "Resident Evil Revelations" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a lot of BP / Receive better supplies (compared to other amiibo)", + "write": false + } + ], + "gameID": [ + "010095300212A000" + ], + "gameName": "Resident Evil Revelations 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "34800000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_34800000-02580002.png", + "name": "Mega Man - Gold Edition", + "release": { + "au": null, + "eu": null, + "jp": null, + "na": "2016-02-23" + }, + "tail": "02580002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Timmy", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01850201", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01850201-01170502.png", + "name": "Timmy - Full Apron", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01170502", + "type": "Card" + }, + { + "amiiboSeries": "Legend Of Zelda", + "character": "Zelda", + "gameSeries": "The Legend of Zelda", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive specific crafting materials or a weapon for the character", + "write": false + } + ], + "gameID": [ + "01002B00111A2000" + ], + "gameName": "Hyrule Warriors: Age of Calamity" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a weapon rated 3 stars or higher", + "write": false + } + ], + "gameID": [ + "0100AE00096EA000" + ], + "gameName": "Hyrule Warriors: Definitive Edition" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Legend of Zelda-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a chest of loot, potentially containing gear inspired by the Legend of Zelda series", + "write": false + } + ], + "gameID": [ + "01000A10041EA000" + ], + "gameName": "The Elder Scrolls V: Skyrim" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive materials and a rare or exclusive item", + "write": false + } + ], + "gameID": [ + "01007EF00011E000" + ], + "gameName": "The Legend of Zelda: Breath of the Wild" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock one of five amiibo-exclusive Chamber Dungeon chambers", + "write": false + }, + { + "Usage": "Save your Chamber Dungeon to the amiibo to share with friends", + "write": true + } + ], + "gameID": [ + "01006BB00C6F0000" + ], + "gameName": "The Legend of Zelda: Link's Awakening" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive character-specific paraglider fabric", + "write": false + }, + { + "Usage": "Receive materials and a weapon or rare item", + "write": false + } + ], + "gameID": [ + "0100F2C0115B6000" + ], + "gameName": "The Legend of Zelda: Tears of the Kingdom" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive the Blue Attire", + "write": false + }, + { + "Usage": "Receive random materials", + "write": false + } + ], + "gameID": [ + "01008CF01BAAC000" + ], + "gameName": "The Legend of Zelda: Echoes of Wisdom" + } + ], + "head": "01010000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01010000-04190902.png", + "name": "Zelda - Tears of the Kingdom", + "release": { + "au": "2023-11-03", + "eu": "2023-11-03", + "jp": "2023-11-03", + "na": "2023-11-03" + }, + "tail": "04190902", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Felicity", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "026e0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_026e0001-00ba0502.png", + "name": "Felicity", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00ba0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Gulliver", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01a20001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01a20001-017d0502.png", + "name": "Gulliver", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "017d0502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Waluigi", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c60201", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c60201-02880e02.png", + "name": "Waluigi - Baseball", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02880e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Blathers", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01920001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01920001-010d0502.png", + "name": "Blathers", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "010d0502", + "type": "Card" + }, + { + "amiiboSeries": "Kirby", + "character": "Kirby", + "gameSeries": "Kirby", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive star coins and a boost item", + "write": false + } + ], + "gameID": [ + "01004D300C5AE000" + ], + "gameName": "Kirby and the Forgotten Land" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive two Picture Pieces, a Maxim Tomato, and two Point Stars", + "write": false + } + ], + "gameID": [ + "01007E3006DDA000" + ], + "gameName": "Kirby Star Allies" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive more useful items", + "write": false + } + ], + "gameID": [ + "01006B601380E000" + ], + "gameName": "Kirby's Return to Dream Land Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Kirby-themed racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive 20 Fragments", + "write": false + } + ], + "gameID": [ + "01003FB00C5A8000" + ], + "gameName": "Super Kirby Clash" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "1f000000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_1f000000-02540c02.png", + "name": "Kirby", + "release": { + "au": "2016-06-11", + "eu": "2016-06-10", + "jp": "2016-04-28", + "na": "2016-06-10" + }, + "tail": "02540c02", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Rodeo", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "024b0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_024b0001-01260502.png", + "name": "Rodeo", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01260502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Baby Mario", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09cc0401", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09cc0401-02a80e02.png", + "name": "Baby Mario - Golf", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02a80e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Kitt", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03d10001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03d10001-00c20502.png", + "name": "Kitt", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00c20502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Prince", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03440001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03440001-00c50502.png", + "name": "Prince", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00c50502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "K.K. Slider", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01820001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01820001-01d80502.png", + "name": "K. K. Slider - Pikopuri", + "release": { + "au": null, + "eu": null, + "jp": "2016-03-15", + "na": null + }, + "tail": "01d80502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Olimar", + "gameSeries": "Pikmin", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a Pikmin-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "06400100", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_06400100-001e0002.png", + "name": "Olimar", + "release": { + "au": "2015-07-23", + "eu": "2015-07-17", + "jp": "2015-07-17", + "na": "2015-09-11" + }, + "tail": "001e0002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Clyde", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03ae0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03ae0001-00870502.png", + "name": "Clyde", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00870502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Ruby", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "049d0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_049d0001-00ed0502.png", + "name": "Ruby", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00ed0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Groucho", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "021a0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_021a0001-00da0502.png", + "name": "Groucho", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00da0502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "King Dedede", + "gameSeries": "Kirby", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive star coins and a boost item", + "write": false + } + ], + "gameID": [ + "01004D300C5AE000" + ], + "gameName": "Kirby and the Forgotten Land" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive two Picture Pieces, a Maxim Tomato, and two Point Stars", + "write": false + } + ], + "gameID": [ + "01007E3006DDA000" + ], + "gameName": "Kirby Star Allies" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive more useful items", + "write": false + } + ], + "gameID": [ + "01006B601380E000" + ], + "gameName": "Kirby's Return to Dream Land Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Kirby-themed racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive 10 Fragments", + "write": false + } + ], + "gameID": [ + "01003FB00C5A8000" + ], + "gameName": "Super Kirby Clash" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "1f020000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_1f020000-00280002.png", + "name": "King Dedede", + "release": { + "au": "2015-01-29", + "eu": "2015-02-20", + "jp": "2015-01-22", + "na": "2015-02-01" + }, + "tail": "00280002", + "type": "Figure" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Roy", + "gameSeries": "Fire Emblem", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive a Fashion Ticket and a Music Ticket, for unlocking any of the available costumes and music tracks", + "write": false + } + ], + "gameID": [ + "0100A6301214E000" + ], + "gameName": "Fire Emblem Engage" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a weapon", + "write": false + } + ], + "gameID": [ + "0100F15003E64000" + ], + "gameName": "Fire Emblem Warriors" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive better-quality randomized resources, weapons, or equipment", + "write": false + } + ], + "gameID": [ + "010071F0143EA000" + ], + "gameName": "Fire Emblem Warriors: Three Hopes" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special piece of battle music / Receive higher-quality items and materials", + "write": false + } + ], + "gameID": [ + "010055D009F78000" + ], + "gameName": "Fire Emblem: Three Houses" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "21040000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_21040000-02520002.png", + "name": "Roy", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-04-28", + "na": "2016-03-18" + }, + "tail": "02520002", + "type": "Figure" + }, + { + "amiiboSeries": "Monster Hunter Rise", + "character": "Malzeno", + "gameSeries": "Monster Hunter", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock special layered armor / Enter daily lottery for a variety of useful items", + "write": false + } + ], + "gameID": [ + "0100B04011742000" + ], + "gameName": "Monster Hunter Rise" + } + ], + "head": "350b0000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_350b0000-042d1802.png", + "name": "Malzeno", + "release": { + "au": "2022-06-30", + "eu": "2022-06-30", + "jp": "2022-06-30", + "na": "2022-06-30" + }, + "tail": "042d1802", + "type": "Figure" + }, + { + "amiiboSeries": "Super Mario Bros.", + "character": "Mario", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock Super Mario Odyssey-themed levels early", + "write": false + } + ], + "gameID": [ + "01009BF0072D4000" + ], + "gameName": "Captain Toad: Treasure Tracker" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive weapon for the character and their Rabbid counterpart", + "write": false + } + ], + "gameID": [ + "010067300059A000" + ], + "gameName": "Mario + Rabbids: Kingdom Battle" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a character-based costume", + "write": false + } + ], + "gameID": [ + "0100000000010000" + ], + "gameName": "Super Mario Odyssey" + }, + { + "amiiboUsage": [ + { + "Usage": "Gain temporary invincibility", + "write": false + } + ], + "gameID": [ + "0100000000010000" + ], + "gameName": "Super Mario Odyssey" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01006000040C2000" + ], + "gameName": "Yoshi's Crafted World" + } + ], + "head": "00000000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00000000-03710102.png", + "name": "Mario - Wedding", + "release": { + "au": "2017-10-27", + "eu": "2017-10-27", + "jp": "2017-10-27", + "na": "2017-10-27" + }, + "tail": "03710102", + "type": "Figure" + }, + { + "amiiboSeries": "Yoshi's Woolly World", + "character": "Yoshi", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive weapon for the character and their Rabbid counterpart", + "write": false + } + ], + "gameID": [ + "010067300059A000" + ], + "gameName": "Mario + Rabbids: Kingdom Battle" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01006000040C2000" + ], + "gameName": "Yoshi's Crafted World" + } + ], + "head": "00030102", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00030102-00430302.png", + "name": "Light Blue Yarn Yoshi", + "release": { + "au": "2015-06-25", + "eu": "2015-06-26", + "jp": "2015-07-16", + "na": "2015-10-16" + }, + "tail": "00430302", + "type": "Yarn" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Baabara", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04c60001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04c60001-01670502.png", + "name": "Baabara", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01670502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Mr. G&W", + "gameSeries": "Classic Nintendo", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "07800000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_07800000-002d0002.png", + "name": "Mr. Game & Watch", + "release": { + "au": "2015-09-26", + "eu": "2015-09-25", + "jp": "2015-10-29", + "na": "2015-09-25" + }, + "tail": "002d0002", + "type": "Figure" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Captain Falcon", + "gameSeries": "F-Zero", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "06000000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_06000000-00120002.png", + "name": "Captain Falcon", + "release": { + "au": "2014-12-12", + "eu": "2014-12-19", + "jp": "2014-12-06", + "na": "2014-12-14" + }, + "tail": "00120002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Static", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04e50001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04e50001-01ad0502.png", + "name": "Static", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01ad0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Rod", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04110001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04110001-01ab0502.png", + "name": "Rod", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01ab0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Bitty", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03950001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03950001-02fc0502.png", + "name": "Bitty", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "02fc0502", + "type": "Card" + }, + { + "amiiboSeries": "Super Mario Bros.", + "character": "Peach", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a costume based on the character (short-hair version)", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive weapon for the character and their Rabbid counterpart", + "write": false + } + ], + "gameID": [ + "010067300059A000" + ], + "gameName": "Mario + Rabbids: Kingdom Battle" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01006000040C2000" + ], + "gameName": "Yoshi's Crafted World" + } + ], + "head": "00020100", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00020100-03a70102.png", + "name": "Peach - Cat", + "release": { + "au": "2021-02-12", + "eu": "2021-02-12", + "jp": "2021-02-12", + "na": "2021-02-12" + }, + "tail": "03a70102", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Buzz", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "044e0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_044e0001-03150502.png", + "name": "Buzz", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "03150502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Blathers", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01920001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01920001-03ad0502.png", + "name": "Blathers", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03ad0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Bonbon", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04a50001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04a50001-00740502.png", + "name": "Bonbon", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00740502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Rolf", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04fa0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04fa0001-01680502.png", + "name": "Rolf", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01680502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Queenie", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04360001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04360001-01940502.png", + "name": "Queenie", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01940502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Diana", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02de0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02de0001-009c0502.png", + "name": "Diana", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "009c0502", + "type": "Card" + }, + { + "amiiboSeries": "Power Pros", + "character": "Hayakawa", + "gameSeries": "Power Pros", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive in-game items and power-ups / Save items to your card after playing with friends to bring them home", + "write": true + } + ], + "gameID": [ + "0100E9C00BF28000" + ], + "gameName": "Jikkyou Powerful Pro Baseball" + } + ], + "head": "38030001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_38030001-03961702.png", + "name": "Hayakawa", + "release": { + "au": null, + "eu": null, + "jp": "2019-06-27", + "na": null + }, + "tail": "03961702", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Alice", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03bd0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03bd0001-00f90502.png", + "name": "Alice", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00f90502", + "type": "Card" + }, + { + "amiiboSeries": "Super Mario Bros.", + "character": "Wario", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a character-based costume", + "write": false + } + ], + "gameID": [ + "0100000000010000" + ], + "gameName": "Super Mario Odyssey" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "00070000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00070000-02630102.png", + "name": "Wario", + "release": { + "au": "2016-10-08", + "eu": "2016-10-07", + "jp": "2016-10-20", + "na": "2016-11-04" + }, + "tail": "02630102", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Paula", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "021e0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_021e0001-01230502.png", + "name": "Paula", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01230502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Metal Mario", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09d00401", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09d00401-02bc0e02.png", + "name": "Metal Mario - Golf", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02bc0e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Piper", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02320001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02320001-02ea0502.png", + "name": "Piper", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "02ea0502", + "type": "Card" + }, + { + "amiiboSeries": "Kirby", + "character": "Meta Knight", + "gameSeries": "Kirby", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive star coins and a boost item", + "write": false + } + ], + "gameID": [ + "01004D300C5AE000" + ], + "gameName": "Kirby and the Forgotten Land" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive two Picture Pieces, a Maxim Tomato, and two Point Stars", + "write": false + } + ], + "gameID": [ + "01007E3006DDA000" + ], + "gameName": "Kirby Star Allies" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive more useful items", + "write": false + } + ], + "gameID": [ + "01006B601380E000" + ], + "gameName": "Kirby's Return to Dream Land Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Kirby-themed racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive 20 Fragments", + "write": false + } + ], + "gameID": [ + "01003FB00C5A8000" + ], + "gameName": "Super Kirby Clash" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "1f010000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_1f010000-02550c02.png", + "name": "Meta Knight", + "release": { + "au": "2016-06-11", + "eu": "2016-06-10", + "jp": "2016-04-28", + "na": "2016-06-10" + }, + "tail": "02550c02", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Ribbot", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03390001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03390001-01b10502.png", + "name": "Ribbot", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01b10502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Hornsby", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04b60001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04b60001-02ec0502.png", + "name": "Hornsby", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "02ec0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Timmy", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01850401", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01850401-01790502.png", + "name": "Timmy - Suit", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01790502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Wilbur", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "0a010001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0a010001-03ac0502.png", + "name": "Wilbur", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03ac0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Ellie", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "032a0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_032a0001-03070502.png", + "name": "Ellie", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "03070502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Bluebear", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "027d0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_027d0001-00630502.png", + "name": "Bluebear", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00630502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Bowser Jr.", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock the Chain Chomp weapon", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + }, + { + "Usage": "Unleash a powerful shockwave to knock out nearby enemies and blocks (in Bowser's Fury mode)", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "00060000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00060000-00150002.png", + "name": "Bowser Jr.", + "release": { + "au": "2015-07-23", + "eu": "2015-07-17", + "jp": "2015-07-17", + "na": "2015-09-11" + }, + "tail": "00150002", + "type": "Figure" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Shulk", + "gameSeries": "Xenoblade Chronicles", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-specific weapon skin for characters using the Swordfighter Class", + "write": false + } + ], + "gameID": [ + "010074F013262000" + ], + "gameName": "Xenoblade Chronicles 3" + } + ], + "head": "22400000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_22400000-002b0002.png", + "name": "Shulk", + "release": { + "au": "2015-01-29", + "eu": "2015-02-20", + "jp": "2015-01-22", + "na": "2015-02-01" + }, + "tail": "002b0002", + "type": "Figure" + }, + { + "amiiboSeries": "Super Mario Bros.", + "character": "Daisy", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a costume based on the character (short-hair version)", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + } + ], + "head": "00130000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00130000-02660102.png", + "name": "Daisy", + "release": { + "au": "2016-11-05", + "eu": "2016-11-04", + "jp": "2016-10-20", + "na": "2016-11-04" + }, + "tail": "02660102", + "type": "Figure" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Bowser", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c90101", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c90101-02960e02.png", + "name": "Bowser - Soccer", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02960e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Sally", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04e40001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04e40001-01b60502.png", + "name": "Sally", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01b60502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Sasha", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "0a110001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0a110001-03c80502.png", + "name": "Sasha", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03c80502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Robin", + "gameSeries": "Fire Emblem", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive a Fashion Ticket and a Music Ticket, for unlocking any of the available costumes and music tracks", + "write": false + } + ], + "gameID": [ + "0100A6301214E000" + ], + "gameName": "Fire Emblem Engage" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a weapon", + "write": false + } + ], + "gameID": [ + "0100F15003E64000" + ], + "gameName": "Fire Emblem Warriors" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive better-quality randomized resources, weapons, or equipment", + "write": false + } + ], + "gameID": [ + "010071F0143EA000" + ], + "gameName": "Fire Emblem Warriors: Three Hopes" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special piece of battle music / Receive higher-quality items and materials", + "write": false + } + ], + "gameID": [ + "010055D009F78000" + ], + "gameName": "Fire Emblem: Three Houses" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "21030000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_21030000-002a0002.png", + "name": "Robin", + "release": { + "au": "2015-04-25", + "eu": "2015-04-24", + "jp": "2015-04-29", + "na": "2015-05-29" + }, + "tail": "002a0002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Patty", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02b10001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02b10001-00690502.png", + "name": "Patty", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00690502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Gloria", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03160001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03160001-01c00502.png", + "name": "Gloria", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01c00502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "K.K. Slider", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01820001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01820001-00a80502.png", + "name": "K.K. Slider", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00a80502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Simon", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03fb0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03fb0001-01cf0502.png", + "name": "Simon", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01cf0502", + "type": "Card" + }, + { + "amiiboSeries": "Legend Of Zelda", + "character": "Link", + "gameSeries": "The Legend of Zelda", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a costume based on the character (short-hair version)", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive specific crafting materials or a weapon for the character", + "write": false + } + ], + "gameID": [ + "01002B00111A2000" + ], + "gameName": "Hyrule Warriors: Age of Calamity" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a weapon rated 3 stars or higher", + "write": false + } + ], + "gameID": [ + "0100AE00096EA000" + ], + "gameName": "Hyrule Warriors: Definitive Edition" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Legend of Zelda-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a chest of loot, potentially containing gear inspired by the Legend of Zelda series", + "write": false + } + ], + "gameID": [ + "01000A10041EA000" + ], + "gameName": "The Elder Scrolls V: Skyrim" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive materials and a rare or exclusive item", + "write": false + } + ], + "gameID": [ + "01007EF00011E000" + ], + "gameName": "The Legend of Zelda: Breath of the Wild" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock one of five amiibo-exclusive Chamber Dungeon chambers", + "write": false + }, + { + "Usage": "Save your Chamber Dungeon to the amiibo to share with friends", + "write": true + } + ], + "gameID": [ + "01006BB00C6F0000" + ], + "gameName": "The Legend of Zelda: Link's Awakening" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive character-specific paraglider fabric", + "write": false + }, + { + "Usage": "Receive materials and a weapon or rare item", + "write": false + } + ], + "gameID": [ + "0100F2C0115B6000" + ], + "gameName": "The Legend of Zelda: Tears of the Kingdom" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive the Red Tunic", + "write": false + }, + { + "Usage": "Receive random materials", + "write": false + } + ], + "gameID": [ + "01008CF01BAAC000" + ], + "gameName": "The Legend of Zelda: Echoes of Wisdom" + } + ], + "head": "01000000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01000000-04180902.png", + "name": "Link - Tears of the Kingdom", + "release": { + "au": "2023-05-12", + "eu": "2023-05-12", + "jp": "2023-05-12", + "na": "2023-05-12" + }, + "tail": "04180902", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Papi", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03b00001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03b00001-01a90502.png", + "name": "Papi", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01a90502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Dizzy", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03240001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03240001-01890502.png", + "name": "Dizzy", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01890502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Chow", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02170001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02170001-01b30502.png", + "name": "Chow", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01b30502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Tipper", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02b20001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02b20001-00c40502.png", + "name": "Tipper", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00c40502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Leonardo", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04fe0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04fe0001-00590502.png", + "name": "Leonardo", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00590502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Link", + "gameSeries": "The Legend of Zelda", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a costume based on the character (short-hair version)", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive specific crafting materials or a weapon for the character", + "write": false + } + ], + "gameID": [ + "01002B00111A2000" + ], + "gameName": "Hyrule Warriors: Age of Calamity" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a weapon rated 3 stars or higher", + "write": false + } + ], + "gameID": [ + "0100AE00096EA000" + ], + "gameName": "Hyrule Warriors: Definitive Edition" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Legend of Zelda-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a chest of loot, potentially containing gear inspired by the Legend of Zelda series", + "write": false + } + ], + "gameID": [ + "01000A10041EA000" + ], + "gameName": "The Elder Scrolls V: Skyrim" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive materials and a rare or exclusive item", + "write": false + } + ], + "gameID": [ + "01007EF00011E000" + ], + "gameName": "The Legend of Zelda: Breath of the Wild" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock one of five amiibo-exclusive Chamber Dungeon chambers", + "write": false + }, + { + "Usage": "Save your Chamber Dungeon to the amiibo to share with friends", + "write": true + } + ], + "gameID": [ + "01006BB00C6F0000" + ], + "gameName": "The Legend of Zelda: Link's Awakening" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive character-specific paraglider fabric", + "write": false + }, + { + "Usage": "Receive materials and a weapon or rare item", + "write": false + } + ], + "gameID": [ + "0100F2C0115B6000" + ], + "gameName": "The Legend of Zelda: Tears of the Kingdom" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive the Red Tunic", + "write": false + }, + { + "Usage": "Receive random materials", + "write": false + } + ], + "gameID": [ + "01008CF01BAAC000" + ], + "gameName": "The Legend of Zelda: Echoes of Wisdom" + } + ], + "head": "01000100", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01000100-00160002.png", + "name": "Toon Link", + "release": { + "au": "2015-01-29", + "eu": "2015-01-23", + "jp": "2015-01-22", + "na": "2015-02-01" + }, + "tail": "00160002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Pango", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02020001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02020001-01030502.png", + "name": "Pango", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "01030502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "R.O.B.", + "gameSeries": "Classic Nintendo", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "07810000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_07810000-00330002.png", + "name": "R.O.B. - NES", + "release": { + "au": "2015-09-26", + "eu": "2015-09-25", + "jp": null, + "na": "2015-09-25" + }, + "tail": "00330002", + "type": "Figure" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Pink Gold Peach", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09d10201", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09d10201-02bf0e02.png", + "name": "Pink Gold Peach - Baseball", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02bf0e02", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "PAC-MAN", + "gameSeries": "Pac-man", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock the Pakku Mask", + "write": false + } + ], + "gameID": [ + "01002FC00412C000" + ], + "gameName": "Little Nightmares: Complete Edition" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "33400000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_33400000-00320002.png", + "name": "Pac-Man", + "release": { + "au": "2015-04-25", + "eu": "2015-04-24", + "jp": "2015-04-29", + "na": "2015-05-29" + }, + "tail": "00320002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Marlo", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "0a150001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0a150001-03cc0502.png", + "name": "Marlo", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03cc0502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Luigi", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c10301", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c10301-02700e02.png", + "name": "Luigi - Tennis", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02700e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Lottie", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01c10001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01c10001-00540502.png", + "name": "Lottie", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00540502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Peach", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c20301", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c20301-02750e02.png", + "name": "Peach - Tennis", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02750e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Poncho", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "027f0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_027f0001-00b90502.png", + "name": "Poncho", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00b90502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Mott", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03ec0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03ec0001-01830502.png", + "name": "Mott", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01830502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Zucker", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "042b0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_042b0001-01af0502.png", + "name": "Zucker", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01af0502", + "type": "Card" + }, + { + "amiiboSeries": "Super Nintendo World", + "character": "Daisy", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a costume based on the character (short-hair version)", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + } + ], + "head": "00130003", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00130003-039eff02.png", + "name": "Daisy - Power Up Band", + "release": { + "au": null, + "eu": null, + "jp": "2021-02-04", + "na": null + }, + "tail": "039eff02", + "type": "Band" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Mega Man", + "gameSeries": "Megaman", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive E Tanks and other useful in-game items", + "write": false + } + ], + "gameID": [ + "0100B0C0086B0000" + ], + "gameName": "Mega Man 11" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock 11 exclusive challenge stages designed by fans", + "write": false + } + ], + "gameID": [ + "01002D4007AE0000" + ], + "gameName": "Mega Man Legacy Collection" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock new platforming challenges", + "write": false + } + ], + "gameID": [ + "0100842008EC4000" + ], + "gameName": "Mega Man Legacy Collection 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a lot of BP / Receive better supplies (compared to other amiibo)", + "write": false + } + ], + "gameID": [ + "0100643002136000" + ], + "gameName": "Resident Evil Revelations" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a lot of BP / Receive better supplies (compared to other amiibo)", + "write": false + } + ], + "gameID": [ + "010095300212A000" + ], + "gameName": "Resident Evil Revelations 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "34800000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_34800000-00310002.png", + "name": "Mega Man", + "release": { + "au": "2015-01-29", + "eu": "2015-02-20", + "jp": "2015-01-22", + "na": "2015-02-01" + }, + "tail": "00310002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Gruff", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "035a0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_035a0001-00850502.png", + "name": "Gruff", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00850502", + "type": "Card" + }, + { + "amiiboSeries": "Metroid", + "character": "Metroid", + "gameSeries": "Metroid", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Metroid-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Replenish a random amount of missiles once per day", + "write": false + } + ], + "gameID": [ + "010093801237C000" + ], + "gameName": "Metroid Dread" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "05c10000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_05c10000-03661302.png", + "name": "Metroid", + "release": { + "au": "2017-09-16", + "eu": "2017-09-15", + "jp": "2017-09-15", + "na": "2017-09-15" + }, + "tail": "03661302", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Pompom", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "030c0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_030c0001-01b80502.png", + "name": "Pompom", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01b80502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Celeste", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01930001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01930001-03ae0502.png", + "name": "Celeste", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03ae0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Cashmere", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04c90001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04c90001-030d0502.png", + "name": "Cashmere", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "030d0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Diva", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "034a0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_034a0001-01430502.png", + "name": "Diva", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01430502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Truffles", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04790001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04790001-00920502.png", + "name": "Truffles", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00920502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Marina", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "042a0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_042a0001-012d0502.png", + "name": "Marina", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "012d0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Nana", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03fa0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03fa0001-00d00502.png", + "name": "Nana", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00d00502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Bowser Jr.", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09ca0301", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09ca0301-029d0e02.png", + "name": "Bowser Jr. - Tennis", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "029d0e02", + "type": "Card" + }, + { + "amiiboSeries": "Splatoon", + "character": "Inkling", + "gameSeries": "Splatoon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Splatoon-themed racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "01003BC0000A0000" + ], + "gameName": "Splatoon 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "0100C2500FC20000" + ], + "gameName": "Splatoon 3" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "08000200", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_08000200-02600402.png", + "name": "Inkling Boy - Purple", + "release": { + "au": "2016-07-09", + "eu": "2016-07-08", + "jp": "2016-07-07", + "na": "2016-07-08" + }, + "tail": "02600402", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Eunice", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04c70001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04c70001-00940502.png", + "name": "Eunice", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00940502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Bowser", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c90501", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c90501-029a0e02.png", + "name": "Bowser - Horse Racing", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "029a0e02", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Donkey Kong", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "00080000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00080000-00030002.png", + "name": "Donkey Kong", + "release": { + "au": "2014-11-29", + "eu": "2014-11-28", + "jp": "2014-12-06", + "na": "2014-11-21" + }, + "tail": "00030002", + "type": "Figure" + }, + { + "amiiboSeries": "Super Nintendo World", + "character": "Toad", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive an invincibility mushroom", + "write": false + } + ], + "gameID": [ + "01009BF0072D4000" + ], + "gameName": "Captain Toad: Treasure Tracker" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01006000040C2000" + ], + "gameName": "Yoshi's Crafted World" + } + ], + "head": "000a0003", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_000a0003-03a0ff02.png", + "name": "Toad - Power Up Band", + "release": { + "au": null, + "eu": null, + "jp": "2021-02-04", + "na": null + }, + "tail": "03a0ff02", + "type": "Band" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Rory", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03ed0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03ed0001-01a30502.png", + "name": "Rory", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01a30502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Egbert", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "029b0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_029b0001-00cb0502.png", + "name": "Egbert", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00cb0502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Mario", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive weapon for the character and their Rabbid counterpart", + "write": false + } + ], + "gameID": [ + "010067300059A000" + ], + "gameName": "Mario + Rabbids: Kingdom Battle" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a character-based costume", + "write": false + }, + { + "Usage": "Gain temporary invincibility", + "write": false + } + ], + "gameID": [ + "0100000000010000" + ], + "gameName": "Super Mario Odyssey" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01006000040C2000" + ], + "gameName": "Yoshi's Crafted World" + } + ], + "head": "00000000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00000000-00000002.png", + "name": "Mario", + "release": { + "au": "2014-11-29", + "eu": "2014-11-28", + "jp": "2014-12-06", + "na": "2014-11-21" + }, + "tail": "00000002", + "type": "Figure" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Rosalina", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09cf0101", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09cf0101-02b40e02.png", + "name": "Rosalina - Soccer", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02b40e02", + "type": "Card" + }, + { + "amiiboSeries": "Legend Of Zelda", + "character": "Link", + "gameSeries": "The Legend of Zelda", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a costume based on the character (short-hair version)", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive specific crafting materials or a weapon for the character", + "write": false + } + ], + "gameID": [ + "01002B00111A2000" + ], + "gameName": "Hyrule Warriors: Age of Calamity" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a weapon rated 3 stars or higher", + "write": false + } + ], + "gameID": [ + "0100AE00096EA000" + ], + "gameName": "Hyrule Warriors: Definitive Edition" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Legend of Zelda-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a chest of loot, potentially containing gear inspired by the Legend of Zelda series", + "write": false + } + ], + "gameID": [ + "01000A10041EA000" + ], + "gameName": "The Elder Scrolls V: Skyrim" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive materials and a rare or exclusive item", + "write": false + } + ], + "gameID": [ + "01007EF00011E000" + ], + "gameName": "The Legend of Zelda: Breath of the Wild" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock one of five amiibo-exclusive Chamber Dungeon chambers", + "write": false + }, + { + "Usage": "Save your Chamber Dungeon to the amiibo to share with friends", + "write": true + } + ], + "gameID": [ + "01006BB00C6F0000" + ], + "gameName": "The Legend of Zelda: Link's Awakening" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive character-specific paraglider fabric", + "write": false + }, + { + "Usage": "Receive materials and a weapon or rare item", + "write": false + } + ], + "gameID": [ + "0100F2C0115B6000" + ], + "gameName": "The Legend of Zelda: Tears of the Kingdom" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive the Red Tunic", + "write": false + }, + { + "Usage": "Receive random materials", + "write": false + } + ], + "gameID": [ + "01008CF01BAAC000" + ], + "gameName": "The Legend of Zelda: Echoes of Wisdom" + } + ], + "head": "01000000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01000000-034e0902.png", + "name": "Link - Skyward Sword", + "release": { + "au": "2017-06-24", + "eu": "2017-06-23", + "jp": "2017-06-23", + "na": "2017-06-23" + }, + "tail": "034e0902", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Cheri", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02870001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02870001-005a0502.png", + "name": "Cheri", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "005a0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Tom Nook", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01830201", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01830201-03a80502.png", + "name": "Tom Nook", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03a80502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Luigi", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c10501", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c10501-02720e02.png", + "name": "Luigi - Horse Racing", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02720e02", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Yoshi", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c40101", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c40101-027d0e02.png", + "name": "Yoshi - Soccer", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "027d0e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Mathilda", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03d20001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03d20001-00e50502.png", + "name": "Mathilda", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00e50502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Mario", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c00301", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c00301-026b0e02.png", + "name": "Mario - Tennis", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "026b0e02", + "type": "Card" + }, + { + "amiiboSeries": "Splatoon", + "character": "Inkling", + "gameSeries": "Splatoon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Splatoon-themed racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "01003BC0000A0000" + ], + "gameName": "Splatoon 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "0100C2500FC20000" + ], + "gameName": "Splatoon 3" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "08000300", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_08000300-036b0402.png", + "name": "Inkling Squid - Neon Purple", + "release": { + "au": "2017-07-21", + "eu": "2017-07-21", + "jp": "2017-07-21", + "na": "2017-07-21" + }, + "tail": "036b0402", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Pietro", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04d20001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04d20001-01a70502.png", + "name": "Pietro", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01a70502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Baby Mario", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09cc0301", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09cc0301-02a70e02.png", + "name": "Baby Mario - Tennis", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02a70e02", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Diddy Kong", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c80201", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c80201-02920e02.png", + "name": "Diddy Kong - Baseball", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02920e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Drake", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03100001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03100001-00f80502.png", + "name": "Drake", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00f80502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Dobie", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "050f0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_050f0001-03140502.png", + "name": "Dobie", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "03140502", + "type": "Card" + }, + { + "amiiboSeries": "Super Mario Bros.", + "character": "Peach", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a costume based on the character (short-hair version)", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive weapon for the character and their Rabbid counterpart", + "write": false + } + ], + "gameID": [ + "010067300059A000" + ], + "gameName": "Mario + Rabbids: Kingdom Battle" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a life-up heart", + "write": false + } + ], + "gameID": [ + "0100000000010000" + ], + "gameName": "Super Mario Odyssey" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01006000040C2000" + ], + "gameName": "Yoshi's Crafted World" + } + ], + "head": "00020000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00020000-00360102.png", + "name": "Peach", + "release": { + "au": "2015-03-21", + "eu": "2015-03-20", + "jp": "2015-03-12", + "na": "2015-03-20" + }, + "tail": "00360102", + "type": "Figure" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Donkey Kong", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c70201", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c70201-028d0e02.png", + "name": "Donkey Kong - Baseball", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "028d0e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Zoe", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "0a1a0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0a1a0001-03d10502.png", + "name": "Zoe", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03d10502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Tom Nook", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01830001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01830001-00450502.png", + "name": "Tom Nook", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00450502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Nan", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03570001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03570001-00eb0502.png", + "name": "Nan", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00eb0502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Yoshi", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c40201", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c40201-027e0e02.png", + "name": "Yoshi - Baseball", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "027e0e02", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Peach", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a costume based on the character (short-hair version)", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive weapon for the character and their Rabbid counterpart", + "write": false + } + ], + "gameID": [ + "010067300059A000" + ], + "gameName": "Mario + Rabbids: Kingdom Battle" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a life-up heart", + "write": false + } + ], + "gameID": [ + "0100000000010000" + ], + "gameName": "Super Mario Odyssey" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01006000040C2000" + ], + "gameName": "Yoshi's Crafted World" + } + ], + "head": "00020000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00020000-00010002.png", + "name": "Peach", + "release": { + "au": "2014-11-29", + "eu": "2014-11-28", + "jp": "2014-12-06", + "na": "2014-11-21" + }, + "tail": "00010002", + "type": "Figure" + }, + { + "amiiboSeries": "Super Mario Bros.", + "character": "Waluigi", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a character-based costume", + "write": false + } + ], + "gameID": [ + "0100000000010000" + ], + "gameName": "Super Mario Odyssey" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "00140000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00140000-02670102.png", + "name": "Waluigi", + "release": { + "au": "2016-11-05", + "eu": "2016-11-04", + "jp": "2016-10-20", + "na": "2016-11-04" + }, + "tail": "02670102", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "K.K. Slider", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01820001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01820001-03b20502.png", + "name": "K.K. Slider", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03b20502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Victoria", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03a50001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03a50001-015b0502.png", + "name": "Victoria", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "015b0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Kitty", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "026b0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_026b0001-00e90502.png", + "name": "Kitty", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00e90502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Rizzo", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04150001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04150001-01bb0502.png", + "name": "Rizzo", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01bb0502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Diddy Kong", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c80401", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c80401-02940e02.png", + "name": "Diddy Kong - Golf", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02940e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Agent S", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04e20001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04e20001-01090502.png", + "name": "Agent S", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "01090502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Greninja", + "gameSeries": "Pokemon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "1b920000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_1b920000-00250002.png", + "name": "Greninja", + "release": { + "au": "2015-05-30", + "eu": "2015-05-29", + "jp": "2015-04-29", + "na": "2015-05-29" + }, + "tail": "00250002", + "type": "Figure" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Peach", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c20201", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c20201-02740e02.png", + "name": "Peach - Baseball", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02740e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Flora", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "043f0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_043f0001-01550502.png", + "name": "Flora", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01550502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Cole", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04a60001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04a60001-00a30502.png", + "name": "Cole", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00a30502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Octavian", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04290001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04290001-00700502.png", + "name": "Octavian", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00700502", + "type": "Card" + }, + { + "amiiboSeries": "Super Mario Bros.", + "character": "Koopa Troopa", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock the Chain Chomp weapon", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01006000040C2000" + ], + "gameName": "Yoshi's Crafted World" + } + ], + "head": "00230000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00230000-03680102.png", + "name": "Koopa Troopa", + "release": { + "au": "2017-10-07", + "eu": "2017-10-06", + "jp": "2017-10-05", + "na": "2017-10-06" + }, + "tail": "03680102", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Friga", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04630001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04630001-01310502.png", + "name": "Friga", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01310502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Cousteau", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03420001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03420001-01280502.png", + "name": "Cousteau", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01280502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Don Resetti", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "018f0101", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_018f0101-01190502.png", + "name": "Don Resetti - Without Hat", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01190502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Tiansheng", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "0a130001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0a130001-03ca0502.png", + "name": "Tiansheng", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03ca0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Becky", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02a20001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02a20001-01ba0502.png", + "name": "Becky", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01ba0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Klaus", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02220001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02220001-01440502.png", + "name": "Klaus", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01440502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Chadder", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "041e0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_041e0001-015f0502.png", + "name": "Chadder", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "015f0502", + "type": "Card" + }, + { + "amiiboSeries": "Splatoon", + "character": "Inkling", + "gameSeries": "Splatoon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Splatoon-themed racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "01003BC0000A0000" + ], + "gameName": "Splatoon 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "0100C2500FC20000" + ], + "gameName": "Splatoon 3" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "08000100", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_08000100-03690402.png", + "name": "Inkling Girl - Neon Pink", + "release": { + "au": "2017-07-21", + "eu": "2017-07-21", + "jp": "2017-07-21", + "na": "2017-07-21" + }, + "tail": "03690402", + "type": "Figure" + }, + { + "amiiboSeries": "Super Mario Bros.", + "character": "Goomba", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock the Chain Chomp weapon", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "00150000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00150000-03670102.png", + "name": "Goomba", + "release": { + "au": "2017-10-07", + "eu": "2017-10-06", + "jp": "2017-10-05", + "na": "2017-10-06" + }, + "tail": "03670102", + "type": "Figure" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Bayonetta", + "gameSeries": "Bayonetta", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock the Super Mirror and Super Mirror 64 and all the costumes they contain", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "32400100", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_32400100-03640002.png", + "name": "Bayonetta - Player 2", + "release": { + "au": "2017-07-22", + "eu": "2017-07-21", + "jp": "2017-07-21", + "na": "2017-07-21" + }, + "tail": "03640002", + "type": "Figure" + }, + { + "amiiboSeries": "Super Mario Bros.", + "character": "Mario", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive weapon for the character and their Rabbid counterpart", + "write": false + } + ], + "gameID": [ + "010067300059A000" + ], + "gameName": "Mario + Rabbids: Kingdom Battle" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a character-based costume", + "write": false + } + ], + "gameID": [ + "0100000000010000" + ], + "gameName": "Super Mario Odyssey" + }, + { + "amiiboUsage": [ + { + "Usage": "Gain temporary invincibility", + "write": false + } + ], + "gameID": [ + "0100000000010000" + ], + "gameName": "Super Mario Odyssey" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01006000040C2000" + ], + "gameName": "Yoshi's Crafted World" + } + ], + "head": "00000000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00000000-003c0102.png", + "name": "Mario - Gold Edition", + "release": { + "au": "2015-06-25", + "eu": null, + "jp": "2015-12-17", + "na": "2015-03-20" + }, + "tail": "003c0102", + "type": "Figure" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Samus", + "gameSeries": "Metroid", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a costume based on the character (short-hair version)", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Metroid-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Restore a random amount of health once per day", + "write": false + } + ], + "gameID": [ + "010093801237C000" + ], + "gameName": "Metroid Dread" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "05c00100", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_05c00100-001d0002.png", + "name": "Zero Suit Samus", + "release": { + "au": "2015-07-04", + "eu": "2015-06-26", + "jp": "2015-06-11", + "na": "2015-09-11" + }, + "tail": "001d0002", + "type": "Figure" + }, + { + "amiiboSeries": "Super Nintendo World", + "character": "Mario", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive weapon for the character and their Rabbid counterpart", + "write": false + } + ], + "gameID": [ + "010067300059A000" + ], + "gameName": "Mario + Rabbids: Kingdom Battle" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a character-based costume", + "write": false + }, + { + "Usage": "Gain temporary invincibility", + "write": false + } + ], + "gameID": [ + "0100000000010000" + ], + "gameName": "Super Mario Odyssey" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + } + ], + "head": "00000003", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00000003-039bff02.png", + "name": "Mario - Power Up Band", + "release": { + "au": null, + "eu": null, + "jp": "2021-02-04", + "na": null + }, + "tail": "039bff02", + "type": "Band" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Pit", + "gameSeries": "Kid Icarus", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "07400000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_07400000-00100002.png", + "name": "Pit", + "release": { + "au": "2014-12-12", + "eu": "2014-12-19", + "jp": "2014-12-06", + "na": "2014-12-14" + }, + "tail": "00100002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Dotty", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04950001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04950001-01920502.png", + "name": "Dotty", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01920502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Katie", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01b60001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01b60001-00ae0502.png", + "name": "Katie", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00ae0502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Zelda", + "gameSeries": "The Legend of Zelda", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive specific crafting materials or a weapon for the character", + "write": false + } + ], + "gameID": [ + "01002B00111A2000" + ], + "gameName": "Hyrule Warriors: Age of Calamity" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a weapon rated 3 stars or higher", + "write": false + } + ], + "gameID": [ + "0100AE00096EA000" + ], + "gameName": "Hyrule Warriors: Definitive Edition" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Legend of Zelda-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a chest of loot, potentially containing gear inspired by the Legend of Zelda series", + "write": false + } + ], + "gameID": [ + "01000A10041EA000" + ], + "gameName": "The Elder Scrolls V: Skyrim" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive materials and a rare or exclusive item", + "write": false + } + ], + "gameID": [ + "01007EF00011E000" + ], + "gameName": "The Legend of Zelda: Breath of the Wild" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock one of five amiibo-exclusive Chamber Dungeon chambers", + "write": false + }, + { + "Usage": "Save your Chamber Dungeon to the amiibo to share with friends", + "write": true + } + ], + "gameID": [ + "01006BB00C6F0000" + ], + "gameName": "The Legend of Zelda: Link's Awakening" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive character-specific paraglider fabric", + "write": false + }, + { + "Usage": "Receive materials and a weapon or rare item", + "write": false + } + ], + "gameID": [ + "0100F2C0115B6000" + ], + "gameName": "The Legend of Zelda: Tears of the Kingdom" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive the Blue Attire", + "write": false + }, + { + "Usage": "Receive random materials", + "write": false + } + ], + "gameID": [ + "01008CF01BAAC000" + ], + "gameName": "The Legend of Zelda: Echoes of Wisdom" + } + ], + "head": "01010100", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01010100-00170002.png", + "name": "Sheik", + "release": { + "au": "2015-01-29", + "eu": "2015-01-23", + "jp": "2015-01-22", + "na": "2015-02-01" + }, + "tail": "00170002", + "type": "Figure" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Pink Gold Peach", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09d10501", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09d10501-02c20e02.png", + "name": "Pink Gold Peach - Horse Racing", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02c20e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Jingle", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01af0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01af0001-011c0502.png", + "name": "Jingle", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "011c0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Lolly", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "026f0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_026f0001-01900502.png", + "name": "Lolly", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01900502", + "type": "Card" + }, + { + "amiiboSeries": "Splatoon", + "character": "Inkling", + "gameSeries": "Splatoon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Splatoon-themed racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "01003BC0000A0000" + ], + "gameName": "Splatoon 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "0100C2500FC20000" + ], + "gameName": "Splatoon 3" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "08000100", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_08000100-025f0402.png", + "name": "Inkling Girl - Lime Green", + "release": { + "au": "2016-07-09", + "eu": "2016-07-08", + "jp": "2016-07-07", + "na": "2016-07-08" + }, + "tail": "025f0402", + "type": "Figure" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Metal Mario", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09d00501", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09d00501-02bd0e02.png", + "name": "Metal Mario - Horse Racing", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02bd0e02", + "type": "Card" + }, + { + "amiiboSeries": "Splatoon", + "character": "Octoling", + "gameSeries": "Splatoon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Splatoon-themed racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "01003BC0000A0000" + ], + "gameName": "Splatoon 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "0100C2500FC20000" + ], + "gameName": "Splatoon 3" + } + ], + "head": "08050200", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_08050200-041b0402.png", + "name": "Octoling - Blue", + "release": { + "au": "2022-11-11", + "eu": "2022-11-11", + "jp": "2022-11-11", + "na": "2022-11-11" + }, + "tail": "041b0402", + "type": "Figure" + }, + { + "amiiboSeries": "Fire Emblem", + "character": "Alm", + "gameSeries": "Fire Emblem", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive a Fashion Ticket and a Music Ticket, for unlocking any of the available costumes and music tracks", + "write": false + } + ], + "gameID": [ + "0100A6301214E000" + ], + "gameName": "Fire Emblem Engage" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a weapon", + "write": false + } + ], + "gameID": [ + "0100F15003E64000" + ], + "gameName": "Fire Emblem Warriors" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive better-quality randomized resources, weapons, or equipment", + "write": false + } + ], + "gameID": [ + "010071F0143EA000" + ], + "gameName": "Fire Emblem Warriors: Three Hopes" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "21060000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_21060000-03601202.png", + "name": "Alm", + "release": { + "au": "2017-05-20", + "eu": "2017-05-19", + "jp": "2017-04-20", + "na": "2017-05-19" + }, + "tail": "03601202", + "type": "Figure" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Richter", + "gameSeries": "Castlevania", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "37c10000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_37c10000-038c0002.png", + "name": "Richter", + "release": { + "au": "2020-01-17", + "eu": "2020-01-17", + "jp": "2020-01-17", + "na": "2020-01-17" + }, + "tail": "038c0002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Jeremiah", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "033f0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_033f0001-008f0502.png", + "name": "Jeremiah", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "008f0502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Bowser", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c90401", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c90401-02990e02.png", + "name": "Bowser - Golf", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02990e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Booker", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "019e0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_019e0001-00ad0502.png", + "name": "Booker", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00ad0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Lucky", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02ec0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02ec0001-01c40502.png", + "name": "Lucky", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01c40502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "K.K. Slider", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "01820000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01820000-02400502.png", + "name": "K. K. Slider", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-11-21", + "na": "2015-11-13" + }, + "tail": "02400502", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Luna", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01b50001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01b50001-00510502.png", + "name": "Luna", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00510502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Doc", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "049e0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_049e0001-01b70502.png", + "name": "Doc", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01b70502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Celia", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04540001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04540001-01ae0502.png", + "name": "Celia", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01ae0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Hippeux", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03990001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03990001-01c20502.png", + "name": "Hippeux", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01c20502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Baby Luigi", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09cd0101", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09cd0101-02aa0e02.png", + "name": "Baby Luigi - Soccer", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02aa0e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Poppy", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04ec0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04ec0001-00770502.png", + "name": "Poppy", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00770502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Tank", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04b20001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04b20001-01b90502.png", + "name": "Tank", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01b90502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Lottie", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01c10201", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01c10201-03bb0502.png", + "name": "Lottie - Island", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03bb0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Marcel", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02f90001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02f90001-01020502.png", + "name": "Marcel", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "01020502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Reese", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "018a0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_018a0001-00a90502.png", + "name": "Reese", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00a90502", + "type": "Card" + }, + { + "amiiboSeries": "Pikmin", + "character": "Pikmin", + "gameSeries": "Pikmin", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a Pikmin-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "06420000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_06420000-035f1102.png", + "name": "Pikmin", + "release": { + "au": "2017-07-29", + "eu": "2017-07-28", + "jp": "2017-07-13", + "na": "2017-07-28" + }, + "tail": "035f1102", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Buck", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03a40001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03a40001-014f0502.png", + "name": "Buck", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "014f0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Ricky", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04e70001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04e70001-01320502.png", + "name": "Ricky", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01320502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Cube", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04610001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04610001-01610502.png", + "name": "Cube", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01610502", + "type": "Card" + }, + { + "amiiboSeries": "Yu-Gi-Oh!", + "character": "Yuga Ohdo", + "gameSeries": "Yu-Gi-Oh!", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive items/bonuses", + "write": false + } + ], + "gameID": [ + "01003C101454A000" + ], + "gameName": "Yu-Gi-Oh! Rush Duel Saikyo Battle Royale" + } + ], + "head": "38400001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_38400001-04241902.png", + "name": "Yuga Ohdo", + "release": { + "au": null, + "eu": null, + "jp": null, + "na": "2021-08-12" + }, + "tail": "04241902", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Birdo", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09ce0101", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09ce0101-02af0e02.png", + "name": "Birdo - Soccer", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02af0e02", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Daisy", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c30301", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c30301-027a0e02.png", + "name": "Daisy - Tennis", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "027a0e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Astrid", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03d60001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03d60001-01570502.png", + "name": "Astrid", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01570502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Isabelle", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "01810100", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01810100-023f0502.png", + "name": "Isabelle - Winter Outfit", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-11-21", + "na": "2015-11-13" + }, + "tail": "023f0502", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Celeste", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "01930000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01930000-02480502.png", + "name": "Celeste", + "release": { + "au": "2016-01-30", + "eu": "2016-01-29", + "jp": "2015-12-17", + "na": "2016-01-22" + }, + "tail": "02480502", + "type": "Figure" + }, + { + "amiiboSeries": "Yoshi's Woolly World", + "character": "Yoshi", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive weapon for the character and their Rabbid counterpart", + "write": false + } + ], + "gameID": [ + "010067300059A000" + ], + "gameName": "Mario + Rabbids: Kingdom Battle" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01006000040C2000" + ], + "gameName": "Yoshi's Crafted World" + } + ], + "head": "00030102", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00030102-00420302.png", + "name": "Pink Yarn Yoshi", + "release": { + "au": "2015-06-25", + "eu": "2015-06-26", + "jp": "2015-07-16", + "na": "2015-10-16" + }, + "tail": "00420302", + "type": "Yarn" + }, + { + "amiiboSeries": "Power Pros", + "character": "Ganda", + "gameSeries": "Power Pros", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive in-game items and power-ups / Save items to your card after playing with friends to bring them home", + "write": true + } + ], + "gameID": [ + "0100E9C00BF28000" + ], + "gameName": "Jikkyou Powerful Pro Baseball" + } + ], + "head": "38040001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_38040001-03971702.png", + "name": "Ganda", + "release": { + "au": null, + "eu": null, + "jp": "2019-06-27", + "na": null + }, + "tail": "03971702", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Daisy", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c30201", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c30201-02790e02.png", + "name": "Daisy - Baseball", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02790e02", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Bowser", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c90301", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c90301-02980e02.png", + "name": "Bowser - Tennis", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02980e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Boomer", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04690001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04690001-01640502.png", + "name": "Boomer", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01640502", + "type": "Card" + }, + { + "amiiboSeries": "Super Mario Bros.", + "character": "Diddy Kong", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a character-based costume", + "write": false + } + ], + "gameID": [ + "0100000000010000" + ], + "gameName": "Super Mario Odyssey" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "00090000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00090000-02650102.png", + "name": "Diddy Kong", + "release": { + "au": "2016-11-05", + "eu": "2016-11-04", + "jp": "2016-10-20", + "na": "2016-11-04" + }, + "tail": "02650102", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Canberra", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03c40001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03c40001-012b0502.png", + "name": "Canberra", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "012b0502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Pink Gold Peach", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09d10401", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09d10401-02c10e02.png", + "name": "Pink Gold Peach - Golf", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02c10e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Chief", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "050b0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_050b0001-00990502.png", + "name": "Chief", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00990502", + "type": "Card" + }, + { + "amiiboSeries": "Splatoon", + "character": "Inkling", + "gameSeries": "Splatoon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Splatoon-themed racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "01003BC0000A0000" + ], + "gameName": "Splatoon 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "0100C2500FC20000" + ], + "gameName": "Splatoon 3" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "08000200", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_08000200-036a0402.png", + "name": "Inkling Boy - Neon Green", + "release": { + "au": "2017-07-21", + "eu": "2017-07-21", + "jp": "2017-07-21", + "na": "2017-07-21" + }, + "tail": "036a0402", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Biff", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03940001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03940001-00890502.png", + "name": "Biff", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00890502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Little Mac", + "gameSeries": "Punch Out", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "06c00000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_06c00000-000f0002.png", + "name": "Little Mac", + "release": { + "au": "2014-12-12", + "eu": "2014-12-19", + "jp": "2014-12-06", + "na": "2014-12-14" + }, + "tail": "000f0002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Julian", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03b10001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03b10001-00f00502.png", + "name": "Julian", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00f00502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Moe", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02650001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02650001-01540502.png", + "name": "Moe", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01540502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Baby Mario", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09cc0201", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09cc0201-02a60e02.png", + "name": "Baby Mario - Baseball", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02a60e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Phyllis", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01a10001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01a10001-01100502.png", + "name": "Phyllis", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01100502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Megan", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "0a0a0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0a0a0001-03c10502.png", + "name": "Megan", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03c10502", + "type": "Card" + }, + { + "amiiboSeries": "Yoshi's Woolly World", + "character": "Yoshi", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive weapon for the character and their Rabbid counterpart", + "write": false + } + ], + "gameID": [ + "010067300059A000" + ], + "gameName": "Mario + Rabbids: Kingdom Battle" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01006000040C2000" + ], + "gameName": "Yoshi's Crafted World" + } + ], + "head": "00030102", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00030102-00410302.png", + "name": "Green Yarn Yoshi", + "release": { + "au": "2015-06-25", + "eu": "2015-06-26", + "jp": "2015-07-16", + "na": "2015-10-16" + }, + "tail": "00410302", + "type": "Yarn" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Maddie", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02f30001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02f30001-02f90502.png", + "name": "Maddie", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "02f90502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Wendy", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04ce0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04ce0001-00db0502.png", + "name": "Wendy", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00db0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Peanut", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04dd0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04dd0001-00a20502.png", + "name": "Peanut", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00a20502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Chabwick", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "0a190001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0a190001-03d00502.png", + "name": "Chabwick", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03d00502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Leila", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01980001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01980001-00b10502.png", + "name": "Leila", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00b10502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Pyra", + "gameSeries": "Xenoblade Chronicles", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-specific weapon skin for characters using the Swordfighter Class", + "write": false + } + ], + "gameID": [ + "010074F013262000" + ], + "gameName": "Xenoblade Chronicles 3" + } + ], + "head": "22410000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_22410000-041e0002.png", + "name": "Pyra", + "release": { + "au": "2023-07-21", + "eu": "2023-07-21", + "jp": "2023-07-21", + "na": "2023-07-21" + }, + "tail": "041e0002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Rilla", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + }, + { + "Usage": "Unlock special furniture items and a poster based on the card's Sanrio character", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03740101", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03740101-03190502.png", + "name": "Rilla", + "release": { + "au": null, + "eu": "2016-11-25", + "jp": "2016-11-03", + "na": null + }, + "tail": "03190502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Villager", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special poster of the character", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "01800000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01800000-00080002.png", + "name": "Villager", + "release": { + "au": "2014-11-29", + "eu": "2014-11-28", + "jp": "2014-12-06", + "na": "2014-11-21" + }, + "tail": "00080002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Mabel", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01880001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01880001-03af0502.png", + "name": "Mabel", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03af0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Tiffany", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "049b0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_049b0001-00610502.png", + "name": "Tiffany", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00610502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Bowser Jr.", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09ca0401", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09ca0401-029e0e02.png", + "name": "Bowser Jr. - Golf", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "029e0e02", + "type": "Card" + }, + { + "amiiboSeries": "Kirby", + "character": "Waddle Dee", + "gameSeries": "Kirby", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive star coins and a boost item", + "write": false + } + ], + "gameID": [ + "01004D300C5AE000" + ], + "gameName": "Kirby and the Forgotten Land" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive two Picture Pieces, a Maxim Tomato, and two Point Stars", + "write": false + } + ], + "gameID": [ + "01007E3006DDA000" + ], + "gameName": "Kirby Star Allies" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive more useful items", + "write": false + } + ], + "gameID": [ + "01006B601380E000" + ], + "gameName": "Kirby's Return to Dream Land Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Kirby-themed racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive 20 Fragments", + "write": false + } + ], + "gameID": [ + "01003FB00C5A8000" + ], + "gameName": "Super Kirby Clash" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "1f030000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_1f030000-02570c02.png", + "name": "Waddle Dee", + "release": { + "au": "2016-06-11", + "eu": "2016-06-10", + "jp": "2016-04-28", + "na": "2016-06-10" + }, + "tail": "02570c02", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Del", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02c70001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02c70001-01220502.png", + "name": "Del", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01220502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Cookie", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02f20001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02f20001-00cc0502.png", + "name": "Cookie", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00cc0502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Donkey Kong", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c70301", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c70301-028e0e02.png", + "name": "Donkey Kong - Tennis", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "028e0e02", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Isabelle", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "01810000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01810000-037d0002.png", + "name": "Isabelle", + "release": { + "au": "2019-07-19", + "eu": "2019-07-19", + "jp": "2019-07-19", + "na": "2019-07-26" + }, + "tail": "037d0002", + "type": "Figure" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Ryu", + "gameSeries": "Street fighter", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive a lot of BP / Receive better supplies (compared to other amiibo)", + "write": false + } + ], + "gameID": [ + "0100643002136000" + ], + "gameName": "Resident Evil Revelations" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a lot of BP / Receive better supplies (compared to other amiibo)", + "write": false + } + ], + "gameID": [ + "010095300212A000" + ], + "gameName": "Resident Evil Revelations 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "34c00000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_34c00000-02530002.png", + "name": "Ryu", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-04-28", + "na": "2016-03-18" + }, + "tail": "02530002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Harry", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03980001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03980001-00bf0502.png", + "name": "Harry", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00bf0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Deli", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04010001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04010001-00660502.png", + "name": "Deli", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00660502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Chelsea", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + }, + { + "Usage": "Unlock special furniture items and a poster based on the card's Sanrio character", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02e00101", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02e00101-031d0502.png", + "name": "Chelsea", + "release": { + "au": null, + "eu": "2016-11-25", + "jp": "2016-11-03", + "na": null + }, + "tail": "031d0502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Ganon", + "gameSeries": "The Legend of Zelda", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive specific crafting materials or a weapon for the character", + "write": false + } + ], + "gameID": [ + "01002B00111A2000" + ], + "gameName": "Hyrule Warriors: Age of Calamity" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a weapon rated 3 stars or higher", + "write": false + } + ], + "gameID": [ + "0100AE00096EA000" + ], + "gameName": "Hyrule Warriors: Definitive Edition" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Legend of Zelda-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a chest of loot, potentially containing gear inspired by the Legend of Zelda series", + "write": false + } + ], + "gameID": [ + "01000A10041EA000" + ], + "gameName": "The Elder Scrolls V: Skyrim" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive materials and a rare or exclusive item", + "write": false + } + ], + "gameID": [ + "01007EF00011E000" + ], + "gameName": "The Legend of Zelda: Breath of the Wild" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock one of five amiibo-exclusive Chamber Dungeon chambers", + "write": false + }, + { + "Usage": "Save your Chamber Dungeon to the amiibo to share with friends", + "write": true + } + ], + "gameID": [ + "01006BB00C6F0000" + ], + "gameName": "The Legend of Zelda: Link's Awakening" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive character-specific paraglider fabric", + "write": false + }, + { + "Usage": "Receive materials and a weapon or rare item", + "write": false + } + ], + "gameID": [ + "0100F2C0115B6000" + ], + "gameName": "The Legend of Zelda: Tears of the Kingdom" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive the Black Cat Clothes", + "write": false + }, + { + "Usage": "Receive random materials", + "write": false + } + ], + "gameID": [ + "01008CF01BAAC000" + ], + "gameName": "The Legend of Zelda: Echoes of Wisdom" + } + ], + "head": "01020100", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01020100-001b0002.png", + "name": "Ganondorf", + "release": { + "au": "2015-07-04", + "eu": "2015-06-26", + "jp": "2015-06-11", + "na": "2015-09-11" + }, + "tail": "001b0002", + "type": "Figure" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Daisy", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c30401", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c30401-027b0e02.png", + "name": "Daisy - Golf", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "027b0e02", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Pink Gold Peach", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09d10301", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09d10301-02c00e02.png", + "name": "Pink Gold Peach - Tennis", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02c00e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Bud", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03e60001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03e60001-00ec0502.png", + "name": "Bud", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00ec0502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Joker", + "gameSeries": "Persona", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "3a000000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_3a000000-03a10002.png", + "name": "Joker", + "release": { + "au": "2020-09-25", + "eu": "2020-09-25", + "jp": "2020-09-25", + "na": "2020-10-02" + }, + "tail": "03a10002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Al", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "03710001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03710001-005c0502.png", + "name": "Al", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "005c0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Elise", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03fe0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03fe0001-01a40502.png", + "name": "Elise", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01a40502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Dark Samus", + "gameSeries": "Metroid", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a Metroid-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Replenish a random amount of missiles once per day", + "write": false + } + ], + "gameID": [ + "010093801237C000" + ], + "gameName": "Metroid Dread" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "05c30000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_05c30000-03800002.png", + "name": "Dark Samus", + "release": { + "au": "2020-01-17", + "eu": "2020-01-17", + "jp": "2020-01-17", + "na": "2020-01-17" + }, + "tail": "03800002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Marcie", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03db0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03db0001-006d0502.png", + "name": "Marcie", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "006d0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Curt", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02160001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02160001-00570502.png", + "name": "Curt", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00570502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Kyle", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "05150001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_05150001-005b0502.png", + "name": "Kyle", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "005b0502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Corrin", + "gameSeries": "Fire Emblem", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive a Fashion Ticket and a Music Ticket, for unlocking any of the available costumes and music tracks", + "write": false + } + ], + "gameID": [ + "0100A6301214E000" + ], + "gameName": "Fire Emblem Engage" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a weapon", + "write": false + } + ], + "gameID": [ + "0100F15003E64000" + ], + "gameName": "Fire Emblem Warriors" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive better-quality randomized resources, weapons, or equipment", + "write": false + } + ], + "gameID": [ + "010071F0143EA000" + ], + "gameName": "Fire Emblem Warriors: Three Hopes" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special piece of battle music / Receive higher-quality items and materials", + "write": false + } + ], + "gameID": [ + "010055D009F78000" + ], + "gameName": "Fire Emblem: Three Houses" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "21050100", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_21050100-03630002.png", + "name": "Corrin - Player 2", + "release": { + "au": "2017-07-22", + "eu": "2017-07-21", + "jp": "2017-07-21", + "na": "2017-07-21" + }, + "tail": "03630002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Angus", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "024a0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_024a0001-01d10502.png", + "name": "Angus", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01d10502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Curly", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04780001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04780001-01630502.png", + "name": "Curly", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01630502", + "type": "Card" + }, + { + "amiiboSeries": "Super Nintendo World", + "character": "Yoshi", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive weapon for the character and their Rabbid counterpart", + "write": false + } + ], + "gameID": [ + "010067300059A000" + ], + "gameName": "Mario + Rabbids: Kingdom Battle" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01006000040C2000" + ], + "gameName": "Yoshi's Crafted World" + } + ], + "head": "00030003", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00030003-039fff02.png", + "name": "Yoshi - Power Up Band", + "release": { + "au": null, + "eu": null, + "jp": "2021-02-04", + "na": null + }, + "tail": "039fff02", + "type": "Band" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Mario", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c00401", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c00401-026c0e02.png", + "name": "Mario - Golf", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "026c0e02", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Baby Luigi", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09cd0401", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09cd0401-02ad0e02.png", + "name": "Baby Luigi - Golf", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02ad0e02", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Wario", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c50501", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c50501-02860e02.png", + "name": "Wario - Horse Racing", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02860e02", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Bowser Jr.", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09ca0201", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09ca0201-029c0e02.png", + "name": "Bowser Jr. - Baseball", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "029c0e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Isabelle", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "01810201", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01810201-011a0502.png", + "name": "Isabelle - Kimono", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "011a0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Drift", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "033c0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_033c0001-01000502.png", + "name": "Drift", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "01000502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Margie", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03270001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03270001-01c30502.png", + "name": "Margie", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01c30502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Mallary", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "030d0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_030d0001-01840502.png", + "name": "Mallary", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01840502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Bowser", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c90201", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c90201-02970e02.png", + "name": "Bowser - Baseball", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02970e02", + "type": "Card" + }, + { + "amiiboSeries": "Kirby", + "character": "King Dedede", + "gameSeries": "Kirby", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive star coins and a boost item", + "write": false + } + ], + "gameID": [ + "01004D300C5AE000" + ], + "gameName": "Kirby and the Forgotten Land" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive two Picture Pieces, a Maxim Tomato, and two Point Stars", + "write": false + } + ], + "gameID": [ + "01007E3006DDA000" + ], + "gameName": "Kirby Star Allies" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive more useful items", + "write": false + } + ], + "gameID": [ + "01006B601380E000" + ], + "gameName": "Kirby's Return to Dream Land Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Kirby-themed racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive 20 Fragments", + "write": false + } + ], + "gameID": [ + "01003FB00C5A8000" + ], + "gameName": "Super Kirby Clash" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "1f020000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_1f020000-02560c02.png", + "name": "King Dedede", + "release": { + "au": "2016-06-11", + "eu": "2016-06-10", + "jp": "2016-04-28", + "na": "2016-06-10" + }, + "tail": "02560c02", + "type": "Figure" + }, + { + "amiiboSeries": "Monster Hunter", + "character": "Ena", + "gameSeries": "Monster Hunter", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock Monster Hunter Stories 2 sticker set", + "write": false + } + ], + "gameID": [ + "0100B04011742000" + ], + "gameName": "Monster Hunter Rise" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a character-based costume for Navirou", + "write": false + } + ], + "gameID": [ + "010069301B1D4000" + ], + "gameName": "Monster Hunter Stories" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-specific special layered armor set / Have Tsukino read your fortune and receive a random item", + "write": false + } + ], + "gameID": [ + "0100E21011446000" + ], + "gameName": "Monster Hunter Stories 2: Wings of Ruin" + } + ], + "head": "35060000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_35060000-040d0f02.png", + "name": "Ena", + "release": { + "au": "2021-07-09", + "eu": "2021-07-09", + "jp": "2021-07-09", + "na": "2021-07-09" + }, + "tail": "040d0f02", + "type": "Figure" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Luigi", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c10201", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c10201-026f0e02.png", + "name": "Luigi - Baseball", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "026f0e02", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Duck Hunt", + "gameSeries": "Classic Nintendo", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "07820000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_07820000-002f0002.png", + "name": "Duck Hunt", + "release": { + "au": "2015-09-26", + "eu": "2015-09-25", + "jp": "2015-10-29", + "na": "2015-09-25" + }, + "tail": "002f0002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Cleo", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03ab0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03ab0001-03160502.png", + "name": "Cleo", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "03160502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Digby", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "018c0101", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_018c0101-01180502.png", + "name": "Digby - Raincoat", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01180502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Rosalina", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09cf0501", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09cf0501-02b80e02.png", + "name": "Rosalina - Horse Racing", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02b80e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Cyd", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "0a0d0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0a0d0001-03c40502.png", + "name": "Cyd", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03c40502", + "type": "Card" + }, + { + "amiiboSeries": "Super Mario Bros.", + "character": "Bowser", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock the Chain Chomp weapon", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + }, + { + "Usage": "Make Fury Bowser appear (in Bowser's Fury mode)", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Reveal regional coin locations", + "write": false + } + ], + "gameID": [ + "0100000000010000" + ], + "gameName": "Super Mario Odyssey" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01006000040C2000" + ], + "gameName": "Yoshi's Crafted World" + } + ], + "head": "00050000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00050000-00390102.png", + "name": "Bowser", + "release": { + "au": "2015-03-21", + "eu": "2015-03-20", + "jp": "2015-03-12", + "na": "2015-03-20" + }, + "tail": "00390102", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Faith", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "0a200001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0a200001-03d70502.png", + "name": "Faith", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03d70502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Annalise", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03ad0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03ad0001-01b20502.png", + "name": "Annalise", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01b20502", + "type": "Card" + }, + { + "amiiboSeries": "Super Mario Bros.", + "character": "Mario", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive weapon for the character and their Rabbid counterpart", + "write": false + } + ], + "gameID": [ + "010067300059A000" + ], + "gameName": "Mario + Rabbids: Kingdom Battle" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a character-based costume", + "write": false + } + ], + "gameID": [ + "0100000000010000" + ], + "gameName": "Super Mario Odyssey" + }, + { + "amiiboUsage": [ + { + "Usage": "Gain temporary invincibility", + "write": false + } + ], + "gameID": [ + "0100000000010000" + ], + "gameName": "Super Mario Odyssey" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01006000040C2000" + ], + "gameName": "Yoshi's Crafted World" + } + ], + "head": "00000000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00000000-003d0102.png", + "name": "Mario - Silver Edition", + "release": { + "au": "2015-05-30", + "eu": null, + "jp": null, + "na": "2015-05-29" + }, + "tail": "003d0102", + "type": "Figure" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Incineroar", + "gameSeries": "Pokemon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "1bd70000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_1bd70000-03860002.png", + "name": "Incineroar", + "release": { + "au": "2019-11-15", + "eu": "2019-11-15", + "jp": "2019-11-08", + "na": "2019-11-15" + }, + "tail": "03860002", + "type": "Figure" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Yoshi", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c40501", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c40501-02810e02.png", + "name": "Yoshi - Horse Racing", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02810e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Sly", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02c90001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02c90001-00cd0502.png", + "name": "Sly", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00cd0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Goose", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02990001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02990001-00950502.png", + "name": "Goose", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00950502", + "type": "Card" + }, + { + "amiiboSeries": "Splatoon", + "character": "Marina", + "gameSeries": "Splatoon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Splatoon-themed racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "01003BC0000A0000" + ], + "gameName": "Splatoon 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "0100C2500FC20000" + ], + "gameName": "Splatoon 3" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "08040000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_08040000-03770402.png", + "name": "Marina", + "release": { + "au": "2018-07-13", + "eu": "2018-07-13", + "jp": "2018-07-13", + "na": "2018-07-13" + }, + "tail": "03770402", + "type": "Figure" + }, + { + "amiiboSeries": "Splatoon", + "character": "Marina", + "gameSeries": "Splatoon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Splatoon-themed racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "01003BC0000A0000" + ], + "gameName": "Splatoon 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "0100C2500FC20000" + ], + "gameName": "Splatoon 3" + } + ], + "head": "08040000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_08040000-04390402.png", + "name": "Marina - Side Order", + "release": { + "au": "2024-09-05", + "eu": "2024-09-05", + "jp": "2024-09-05", + "na": "2024-09-05" + }, + "tail": "04390402", + "type": "Figure" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Bayonetta", + "gameSeries": "Bayonetta", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock the Super Mirror 2 and all the costumes it contains", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "32400000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_32400000-025b0002.png", + "name": "Bayonetta", + "release": { + "au": "2017-07-22", + "eu": "2017-07-21", + "jp": "2017-07-21", + "na": "2017-07-21" + }, + "tail": "025b0002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Joey", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03080001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03080001-014d0502.png", + "name": "Joey", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "014d0502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Pokemon Trainer", + "gameSeries": "Pokemon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "1d400000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_1d400000-03870002.png", + "name": "Pokemon Trainer", + "release": { + "au": "2019-07-19", + "eu": "2019-07-19", + "jp": "2019-07-19", + "na": "2019-07-26" + }, + "tail": "03870002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Tangy", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02620001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02620001-01370502.png", + "name": "Tangy", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01370502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Pancetti", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04880001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04880001-00980502.png", + "name": "Pancetti", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00980502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Ridley", + "gameSeries": "Metroid", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a Metroid-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Replenish a random amount of missiles once per day", + "write": false + } + ], + "gameID": [ + "010093801237C000" + ], + "gameName": "Metroid Dread" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "05c20000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_05c20000-037f0002.png", + "name": "Ridley", + "release": { + "au": "2018-12-07", + "eu": "2018-12-07", + "jp": "2018-12-07", + "na": "2018-12-07" + }, + "tail": "037f0002", + "type": "Figure" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Donkey Kong", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c70401", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c70401-028f0e02.png", + "name": "Donkey Kong - Golf", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "028f0e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Louie", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "036d0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_036d0001-03040502.png", + "name": "Louie", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "03040502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Dora", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "040c0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_040c0001-01590502.png", + "name": "Dora", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01590502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Broffina", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02a50001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02a50001-018c0502.png", + "name": "Broffina", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "018c0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Blanche", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "043e0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_043e0001-01490502.png", + "name": "Blanche", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01490502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Mario", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c00501", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c00501-026d0e02.png", + "name": "Mario - Horse Racing", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "026d0e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Lionel", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03ee0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03ee0001-008b0502.png", + "name": "Lionel", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "008b0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Jacques", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "023d0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_023d0001-01b50502.png", + "name": "Jacques", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01b50502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Vesta", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04c50001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04c50001-01010502.png", + "name": "Vesta", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "01010502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Chevre", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03560001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03560001-01350502.png", + "name": "Chevre", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01350502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Shep", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02fc0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02fc0001-018f0502.png", + "name": "Shep", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "018f0502", + "type": "Card" + }, + { + "amiiboSeries": "Shovel Knight", + "character": "Specter Knight", + "gameSeries": "Shovel Knight", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a fairy companion and player color palette matching the character", + "write": false + } + ], + "gameID": [ + "01008D100DE46000" + ], + "gameName": "Cyber Shadow" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-specific Shovel Knight remix immediately", + "write": false + } + ], + "gameID": [ + "0100830008426000" + ], + "gameName": "Just Shapes & Beats" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock character-specific challenge stages, a character-based fairy companion, and costumes for the character", + "write": false + } + ], + "gameID": [ + "010057D0021E8000" + ], + "gameName": "Shovel Knight" + }, + { + "amiiboUsage": [ + { + "Usage": "Summon a fairy friend", + "write": false + } + ], + "gameID": [ + "0100B62017E68000" + ], + "gameName": "Shovel Knight Dig" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume for the character", + "write": false + } + ], + "gameID": [ + "0100B380022AE000" + ], + "gameName": "Shovel Knight Showdown" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "35c20000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_35c20000-036d0a02.png", + "name": "Specter Knight", + "release": { + "au": null, + "eu": "2019-12-10", + "jp": null, + "na": "2019-12-10" + }, + "tail": "036d0a02", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Fang", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "05110001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_05110001-01950502.png", + "name": "Fang", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01950502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Isabelle", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "01810501", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01810501-03bf0502.png", + "name": "Isabelle - Sweater", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03bf0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Snake", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04970001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04970001-007a0502.png", + "name": "Snake", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "007a0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Rio", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + }, + { + "Usage": "Unlock special furniture items and a poster based on the card's Sanrio character", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "0a1c0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0a1c0001-03d30502.png", + "name": "Rio", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03d30502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Pekoe", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "028b0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_028b0001-00e30502.png", + "name": "Pekoe", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00e30502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Celeste", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01930001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01930001-01740502.png", + "name": "Celeste", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01740502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Norma", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02b70001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02b70001-030f0502.png", + "name": "Norma", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "030f0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Butch", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02eb0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02eb0001-00de0502.png", + "name": "Butch", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00de0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Marshal", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04ee0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04ee0001-014b0502.png", + "name": "Marshal", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "014b0502", + "type": "Card" + }, + { + "amiiboSeries": "Splatoon", + "character": "Octoling", + "gameSeries": "Splatoon", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Splatoon-themed racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "01003BC0000A0000" + ], + "gameName": "Splatoon 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock exclusive gear / Save favorite weapons, gear, and control settings / Take photos with the character or saved gear", + "write": true + } + ], + "gameID": [ + "0100C2500FC20000" + ], + "gameName": "Splatoon 3" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "08050300", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_08050300-03900402.png", + "name": "Octoling Octopus", + "release": { + "au": "2018-11-11", + "eu": "2018-11-09", + "jp": "2018-11-09", + "na": "2018-11-09" + }, + "tail": "03900402", + "type": "Figure" + }, + { + "amiiboSeries": "8-bit Mario", + "character": "Mario", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive weapon for the character and their Rabbid counterpart", + "write": false + } + ], + "gameID": [ + "010067300059A000" + ], + "gameName": "Mario + Rabbids: Kingdom Battle" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a character-based costume", + "write": false + }, + { + "Usage": "Gain temporary invincibility", + "write": false + } + ], + "gameID": [ + "0100000000010000" + ], + "gameName": "Super Mario Odyssey" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01006000040C2000" + ], + "gameName": "Yoshi's Crafted World" + } + ], + "head": "00000000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00000000-02380602.png", + "name": "8-Bit Mario Classic Color", + "release": { + "au": "2015-09-12", + "eu": "2015-11-09", + "jp": "2015-09-10", + "na": "2015-09-11" + }, + "tail": "02380602", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Olivia", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02600001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02600001-00d20502.png", + "name": "Olivia", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00d20502", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Zelda", + "gameSeries": "The Legend of Zelda", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive specific crafting materials or a weapon for the character", + "write": false + } + ], + "gameID": [ + "01002B00111A2000" + ], + "gameName": "Hyrule Warriors: Age of Calamity" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a weapon rated 3 stars or higher", + "write": false + } + ], + "gameID": [ + "0100AE00096EA000" + ], + "gameName": "Hyrule Warriors: Definitive Edition" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Legend of Zelda-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a chest of loot, potentially containing gear inspired by the Legend of Zelda series", + "write": false + } + ], + "gameID": [ + "01000A10041EA000" + ], + "gameName": "The Elder Scrolls V: Skyrim" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive materials and a rare or exclusive item", + "write": false + } + ], + "gameID": [ + "01007EF00011E000" + ], + "gameName": "The Legend of Zelda: Breath of the Wild" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock one of five amiibo-exclusive Chamber Dungeon chambers", + "write": false + }, + { + "Usage": "Save your Chamber Dungeon to the amiibo to share with friends", + "write": true + } + ], + "gameID": [ + "01006BB00C6F0000" + ], + "gameName": "The Legend of Zelda: Link's Awakening" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an amiibo-exclusive character-specific paraglider fabric", + "write": false + }, + { + "Usage": "Receive materials and a weapon or rare item", + "write": false + } + ], + "gameID": [ + "0100F2C0115B6000" + ], + "gameName": "The Legend of Zelda: Tears of the Kingdom" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive the Blue Attire", + "write": false + }, + { + "Usage": "Receive random materials", + "write": false + } + ], + "gameID": [ + "01008CF01BAAC000" + ], + "gameName": "The Legend of Zelda: Echoes of Wisdom" + } + ], + "head": "01010000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01010000-000e0002.png", + "name": "Zelda", + "release": { + "au": "2014-12-12", + "eu": "2014-12-19", + "jp": "2014-12-06", + "na": "2014-12-14" + }, + "tail": "000e0002", + "type": "Figure" + }, + { + "amiiboSeries": "Metroid", + "character": "Samus", + "gameSeries": "Metroid", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a costume based on the character (short-hair version)", + "write": false + } + ], + "gameID": [ + "01007960049A0000" + ], + "gameName": "Bayonetta 2" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a Metroid-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Restore a random amount of health once per day", + "write": false + } + ], + "gameID": [ + "010093801237C000" + ], + "gameName": "Metroid Dread" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "05c00000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_05c00000-03651302.png", + "name": "Samus Aran", + "release": { + "au": "2017-09-16", + "eu": "2017-09-15", + "jp": "2017-09-15", + "na": "2017-09-15" + }, + "tail": "03651302", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Zell", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02d80001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02d80001-00e20502.png", + "name": "Zell", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00e20502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Clay", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03830001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03830001-009b0502.png", + "name": "Clay", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "009b0502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Boo", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09cb0301", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09cb0301-02a20e02.png", + "name": "Boo - Tennis", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02a20e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Goldie", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02ea0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02ea0001-01800502.png", + "name": "Goldie", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01800502", + "type": "Card" + }, + { + "amiiboSeries": "Power Pros", + "character": "Yabe", + "gameSeries": "Power Pros", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive in-game items and power-ups / Save items to your card after playing with friends to bring them home", + "write": true + } + ], + "gameID": [ + "0100E9C00BF28000" + ], + "gameName": "Jikkyou Powerful Pro Baseball" + } + ], + "head": "38020001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_38020001-03951702.png", + "name": "Yabe", + "release": { + "au": null, + "eu": null, + "jp": "2019-06-27", + "na": null + }, + "tail": "03951702", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Ness", + "gameSeries": "Earthbound", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "22800000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_22800000-002c0002.png", + "name": "Ness", + "release": { + "au": "2015-04-25", + "eu": "2015-04-24", + "jp": "2015-04-29", + "na": "2015-05-29" + }, + "tail": "002c0002", + "type": "Figure" + }, + { + "amiiboSeries": "Shovel Knight", + "character": "Plague Knight", + "gameSeries": "Shovel Knight", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a fairy companion and player color palette matching the character", + "write": false + } + ], + "gameID": [ + "01008D100DE46000" + ], + "gameName": "Cyber Shadow" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-specific Shovel Knight remix immediately", + "write": false + } + ], + "gameID": [ + "0100830008426000" + ], + "gameName": "Just Shapes & Beats" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock character-specific challenge stages, a character-based fairy companion, and costumes for the character", + "write": false + } + ], + "gameID": [ + "010057D0021E8000" + ], + "gameName": "Shovel Knight" + }, + { + "amiiboUsage": [ + { + "Usage": "Summon a fairy friend", + "write": false + } + ], + "gameID": [ + "0100B62017E68000" + ], + "gameName": "Shovel Knight Dig" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume for the character", + "write": false + } + ], + "gameID": [ + "0100B380022AE000" + ], + "gameName": "Shovel Knight Showdown" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "35c10000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_35c10000-036c0a02.png", + "name": "Plague Knight", + "release": { + "au": null, + "eu": "2019-12-10", + "jp": null, + "na": "2019-12-10" + }, + "tail": "036c0a02", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Tad", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03410001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03410001-030e0502.png", + "name": "Tad", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "030e0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Penelope", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "041d0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_041d0001-018a0502.png", + "name": "Penelope", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "018a0502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Rosalina", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09cf0401", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09cf0401-02b70e02.png", + "name": "Rosalina - Golf", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02b70e02", + "type": "Card" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Palutena", + "gameSeries": "Kid Icarus", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "07420000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_07420000-001f0002.png", + "name": "Palutena", + "release": { + "au": "2015-07-04", + "eu": "2015-06-26", + "jp": "2015-06-11", + "na": "2015-07-24" + }, + "tail": "001f0002", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Curlos", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04cd0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04cd0001-01520502.png", + "name": "Curlos", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01520502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Samson", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04100001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04100001-007f0502.png", + "name": "Samson", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "007f0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Saharah", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01a60001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01a60001-00500502.png", + "name": "Saharah", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00500502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Walt", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03d90001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03d90001-01a50502.png", + "name": "Walt", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01a50502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Julia", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "043b0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_043b0001-03030502.png", + "name": "Julia", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "03030502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Hamlet", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "037e0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_037e0001-01560502.png", + "name": "Hamlet", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01560502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Charlise", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02200001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02200001-00fd0502.png", + "name": "Charlise", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00fd0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Gonzo", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03c00001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03c00001-03100502.png", + "name": "Gonzo", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "03100502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Opal", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03230001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03230001-00760502.png", + "name": "Opal", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00760502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Flo", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "046c0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_046c0001-008c0502.png", + "name": "Flo", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "008c0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Knox", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02a40001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02a40001-00720502.png", + "name": "Knox", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00720502", + "type": "Card" + }, + { + "amiiboSeries": "Super Mario Bros.", + "character": "Donkey Kong", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the character's shiny sticker", + "write": false + } + ], + "gameID": [ + "010036B0034E4000" + ], + "gameName": "Super Mario Party" + }, + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "00080000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_00080000-02640102.png", + "name": "Donkey Kong", + "release": { + "au": "2016-10-08", + "eu": "2016-10-07", + "jp": "2016-10-20", + "na": "2016-11-04" + }, + "tail": "02640102", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Cherry", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "02fb0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_02fb0001-00900502.png", + "name": "Cherry", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00900502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Rover", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "018d0000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_018d0000-024c0502.png", + "name": "Rover", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-03-24", + "na": "2016-03-18" + }, + "tail": "024c0502", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Soleil", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "03820001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_03820001-016b0502.png", + "name": "Soleil", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "016b0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Barold", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "028d0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_028d0001-01bd0502.png", + "name": "Barold", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01bd0502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Birdo", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09ce0201", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09ce0201-02b00e02.png", + "name": "Birdo - Baseball", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02b00e02", + "type": "Card" + }, + { + "amiiboSeries": "Super Mario Bros.", + "character": "Toad", + "gameSeries": "Super Mario", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Receive an invincibility mushroom", + "write": false + } + ], + "gameID": [ + "01009BF0072D4000" + ], + "gameName": "Captain Toad: Treasure Tracker" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01003DA010E8A000" + ], + "gameName": "Miitopia" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a particular power-up depending on the character", + "write": false + } + ], + "gameID": [ + "010028600EBDA000" + ], + "gameName": "Super Mario 3D World + Bowser's Fury" + }, + { + "amiiboUsage": [ + { + "Usage": "Receive a Spirit of the character", + "write": false + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a character-based costume", + "write": false + } + ], + "gameID": [ + "01006000040C2000" + ], + "gameName": "Yoshi's Crafted World" + } + ], + "head": "000a0000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_000a0000-00380102.png", + "name": "Toad", + "release": { + "au": "2015-03-21", + "eu": "2015-03-20", + "jp": "2015-03-12", + "na": "2015-03-20" + }, + "tail": "00380102", + "type": "Figure" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Shari", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04000001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04000001-006f0502.png", + "name": "Shari", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "006f0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Joan", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01a30001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01a30001-004a0502.png", + "name": "Joan", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "004a0502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Bowser Jr.", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09ca0101", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09ca0101-029b0e02.png", + "name": "Bowser Jr. - Soccer", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "029b0e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Sheldon", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04ed0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04ed0001-00620502.png", + "name": "Sheldon", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "00620502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Chester", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "028c0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_028c0001-013e0502.png", + "name": "Chester", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "013e0502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Baby Luigi", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09cd0501", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09cd0501-02ae0e02.png", + "name": "Baby Luigi - Horse Racing", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02ae0e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Ike", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "021f0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_021f0001-03170502.png", + "name": "Ike", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "03170502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Leilani", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01970001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01970001-01770502.png", + "name": "Leilani", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "01770502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Skye", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "05140001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_05140001-01530502.png", + "name": "Skye", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01530502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "C.J.", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "0a020001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0a020001-03b30502.png", + "name": "C.J.", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03b30502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Metal Mario", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09d00201", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09d00201-02ba0e02.png", + "name": "Metal Mario - Baseball", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02ba0e02", + "type": "Card" + }, + { + "amiiboSeries": "Monster Hunter Rise", + "character": "Palico", + "gameSeries": "Monster Hunter", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock special layered armor / Enter daily lottery for a variety of useful items", + "write": false + } + ], + "gameID": [ + "0100B04011742000" + ], + "gameName": "Monster Hunter Rise" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock the Hunter Sticker Set / Have Tsukino read your fortune and receive a random item", + "write": false + } + ], + "gameID": [ + "0100E21011446000" + ], + "gameName": "Monster Hunter Stories 2: Wings of Ruin" + } + ], + "head": "35090000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_35090000-04101802.png", + "name": "Palico", + "release": { + "au": "2021-03-26", + "eu": "2021-03-26", + "jp": "2021-03-26", + "na": "2021-03-26" + }, + "tail": "04101802", + "type": "Figure" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Mario", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c00201", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c00201-026a0e02.png", + "name": "Mario - Baseball", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "026a0e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Kicks", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01940001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01940001-03b60502.png", + "name": "Kicks", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03b60502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Stella", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04c80001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04c80001-02ed0502.png", + "name": "Stella", + "release": { + "au": "2016-11-10", + "eu": "2016-11-11", + "jp": "2016-11-03", + "na": "2016-12-02" + }, + "tail": "02ed0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Freya", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "05100001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_05100001-01070502.png", + "name": "Freya", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "01070502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Judy", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "0a0e0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_0a0e0001-03c50502.png", + "name": "Judy", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03c50502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Peewee", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "036a0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_036a0001-019d0502.png", + "name": "Peewee", + "release": { + "au": "2016-06-18", + "eu": null, + "jp": "2016-03-24", + "na": "2016-06-10" + }, + "tail": "019d0502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Tom Nook", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "01830301", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_01830301-03be0502.png", + "name": "Tom Nook - Coat", + "release": { + "au": "2021-11-05", + "eu": "2021-11-05", + "jp": "2021-11-05", + "na": "2021-11-05" + }, + "tail": "03be0502", + "type": "Card" + }, + { + "amiiboSeries": "Mario Sports Superstars", + "character": "Diddy Kong", + "gameSeries": "Mario Sports Superstars", + "gamesSwitch": [], + "head": "09c80301", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_09c80301-02930e02.png", + "name": "Diddy Kong - Tennis", + "release": { + "au": "2017-03-11", + "eu": "2017-03-10", + "jp": "2017-03-30", + "na": "2017-03-24" + }, + "tail": "02930e02", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Don Resetti", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "018f0001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_018f0001-00b30502.png", + "name": "Don Resetti", + "release": { + "au": "2015-11-21", + "eu": "2015-11-20", + "jp": "2015-10-29", + "na": "2016-01-22" + }, + "tail": "00b30502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Merengue", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04b90001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04b90001-01600502.png", + "name": "Merengue", + "release": { + "au": "2016-03-19", + "eu": "2016-03-18", + "jp": "2016-01-14", + "na": "2016-03-18" + }, + "tail": "01600502", + "type": "Card" + }, + { + "amiiboSeries": "Animal Crossing", + "character": "Muffy", + "gameSeries": "Animal Crossing", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Invite the character to your campsite and, eventually, to move to your island / Invite the character for photo shoots at Photopia / Unlock a special poster of the character / Invite the character to the Roost for a cup of coffee / Invite the character to Happy Home Paradise to design their vacation home", + "write": false + } + ], + "gameID": [ + "01006F8002326000" + ], + "gameName": "Animal Crossing: New Horizons" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock a special costume", + "write": false + } + ], + "gameID": [ + "01007EF00399C000" + ], + "gameName": "Conga Master Party!" + }, + { + "amiiboUsage": [ + { + "Usage": "Unlock an Animal Crossing-themed Mii racing suit", + "write": false + } + ], + "gameID": [ + "0100152000022000" + ], + "gameName": "Mario Kart 8 Deluxe" + } + ], + "head": "04d10001", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_04d10001-009e0502.png", + "name": "Muffy", + "release": { + "au": "2015-10-03", + "eu": "2015-10-02", + "jp": "2015-07-30", + "na": "2015-09-25" + }, + "tail": "009e0502", + "type": "Card" + }, + { + "amiiboSeries": "Xenoblade Chronicles 3", + "character": "Noah", + "gameSeries": "Xenoblade Chronicles", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock the \u201cN\u2019s Consul Suit\u201d outfit for Noah as well as Noah, Lanz, and Eunie costumes without jackets", + "write": false + } + ], + "gameID": [ + "010074F013262000" + ], + "gameName": "Xenoblade Chronicles 3" + } + ], + "head": "22430000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_22430000-043d1b02.png", + "name": "Noah", + "release": { + "au": "2024-01-19", + "eu": "2024-01-19", + "jp": "2024-01-19", + "na": "2024-01-19" + }, + "tail": "043d1b02", + "type": "Figure" + }, + { + "amiiboSeries": "Xenoblade Chronicles 3", + "character": "Mio", + "gameSeries": "Xenoblade Chronicles", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Unlock the \u201cM\u2019s Consul Suit\u201d outfit for Mio as well as Mio, Sena, and Taion costumes without jackets", + "write": false + } + ], + "gameID": [ + "010074F013262000" + ], + "gameName": "Xenoblade Chronicles 3" + } + ], + "head": "22440000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_22440000-043e1b02.png", + "name": "Mio", + "release": { + "au": "2024-01-19", + "eu": "2024-01-19", + "jp": "2024-01-19", + "na": "2024-01-19" + }, + "tail": "043e1b02", + "type": "Figure" + }, + { + "amiiboSeries": "Super Smash Bros.", + "character": "Sora", + "gameSeries": "Kingdom Hearts", + "gamesSwitch": [ + { + "amiiboUsage": [ + { + "Usage": "Battle and train up a computer-controlled Figure Player of the character", + "write": true + } + ], + "gameID": [ + "01006A800016E000" + ], + "gameName": "Super Smash Bros. Ultimate" + } + ], + "head": "3f000000", + "image": "https://raw.githubusercontent.com/GreemDev/Ryujinx/refs/heads/master/assets/amiibo/images/icon_3f000000-042e0002.png", + "name": "Sora", + "release": { + "au": "2024-02-16", + "eu": "2024-02-16", + "jp": "2024-02-16", + "na": "2024-02-16" + }, + "tail": "042e0002", + "type": "Figure" + } + ], + "lastUpdated": "2024-11-17T15:28:47.035619" +} diff --git a/assets/amiibo/images/icon_00000000-00000002.png b/assets/amiibo/images/icon_00000000-00000002.png new file mode 100644 index 000000000..7e238140d Binary files /dev/null and b/assets/amiibo/images/icon_00000000-00000002.png differ diff --git a/assets/amiibo/images/icon_00000000-00340102.png b/assets/amiibo/images/icon_00000000-00340102.png new file mode 100644 index 000000000..0566243de Binary files /dev/null and b/assets/amiibo/images/icon_00000000-00340102.png differ diff --git a/assets/amiibo/images/icon_00000000-003c0102.png b/assets/amiibo/images/icon_00000000-003c0102.png new file mode 100644 index 000000000..0c93ab0dd Binary files /dev/null and b/assets/amiibo/images/icon_00000000-003c0102.png differ diff --git a/assets/amiibo/images/icon_00000000-003d0102.png b/assets/amiibo/images/icon_00000000-003d0102.png new file mode 100644 index 000000000..ceb3232b1 Binary files /dev/null and b/assets/amiibo/images/icon_00000000-003d0102.png differ diff --git a/assets/amiibo/images/icon_00000000-02380602.png b/assets/amiibo/images/icon_00000000-02380602.png new file mode 100644 index 000000000..e17bfa297 Binary files /dev/null and b/assets/amiibo/images/icon_00000000-02380602.png differ diff --git a/assets/amiibo/images/icon_00000000-02390602.png b/assets/amiibo/images/icon_00000000-02390602.png new file mode 100644 index 000000000..71293f2d6 Binary files /dev/null and b/assets/amiibo/images/icon_00000000-02390602.png differ diff --git a/assets/amiibo/images/icon_00000000-03710102.png b/assets/amiibo/images/icon_00000000-03710102.png new file mode 100644 index 000000000..b42ada43b Binary files /dev/null and b/assets/amiibo/images/icon_00000000-03710102.png differ diff --git a/assets/amiibo/images/icon_00000003-039bff02.png b/assets/amiibo/images/icon_00000003-039bff02.png new file mode 100644 index 000000000..c7e12e520 Binary files /dev/null and b/assets/amiibo/images/icon_00000003-039bff02.png differ diff --git a/assets/amiibo/images/icon_00000003-0430ff02.png b/assets/amiibo/images/icon_00000003-0430ff02.png new file mode 100644 index 000000000..188b8b1ea Binary files /dev/null and b/assets/amiibo/images/icon_00000003-0430ff02.png differ diff --git a/assets/amiibo/images/icon_00000100-00190002.png b/assets/amiibo/images/icon_00000100-00190002.png new file mode 100644 index 000000000..0a61be06e Binary files /dev/null and b/assets/amiibo/images/icon_00000100-00190002.png differ diff --git a/assets/amiibo/images/icon_00000300-03a60102.png b/assets/amiibo/images/icon_00000300-03a60102.png new file mode 100644 index 000000000..e76e47ac2 Binary files /dev/null and b/assets/amiibo/images/icon_00000300-03a60102.png differ diff --git a/assets/amiibo/images/icon_00010000-000c0002.png b/assets/amiibo/images/icon_00010000-000c0002.png new file mode 100644 index 000000000..4c34aae16 Binary files /dev/null and b/assets/amiibo/images/icon_00010000-000c0002.png differ diff --git a/assets/amiibo/images/icon_00010000-00350102.png b/assets/amiibo/images/icon_00010000-00350102.png new file mode 100644 index 000000000..d51680212 Binary files /dev/null and b/assets/amiibo/images/icon_00010000-00350102.png differ diff --git a/assets/amiibo/images/icon_00010003-039cff02.png b/assets/amiibo/images/icon_00010003-039cff02.png new file mode 100644 index 000000000..5c91d4319 Binary files /dev/null and b/assets/amiibo/images/icon_00010003-039cff02.png differ diff --git a/assets/amiibo/images/icon_00020000-00010002.png b/assets/amiibo/images/icon_00020000-00010002.png new file mode 100644 index 000000000..b90bbbd21 Binary files /dev/null and b/assets/amiibo/images/icon_00020000-00010002.png differ diff --git a/assets/amiibo/images/icon_00020000-00360102.png b/assets/amiibo/images/icon_00020000-00360102.png new file mode 100644 index 000000000..69e45a3ab Binary files /dev/null and b/assets/amiibo/images/icon_00020000-00360102.png differ diff --git a/assets/amiibo/images/icon_00020000-03720102.png b/assets/amiibo/images/icon_00020000-03720102.png new file mode 100644 index 000000000..0808fc2ab Binary files /dev/null and b/assets/amiibo/images/icon_00020000-03720102.png differ diff --git a/assets/amiibo/images/icon_00020003-039dff02.png b/assets/amiibo/images/icon_00020003-039dff02.png new file mode 100644 index 000000000..2e62af9e4 Binary files /dev/null and b/assets/amiibo/images/icon_00020003-039dff02.png differ diff --git a/assets/amiibo/images/icon_00020100-03a70102.png b/assets/amiibo/images/icon_00020100-03a70102.png new file mode 100644 index 000000000..dac040cac Binary files /dev/null and b/assets/amiibo/images/icon_00020100-03a70102.png differ diff --git a/assets/amiibo/images/icon_00030000-00020002.png b/assets/amiibo/images/icon_00030000-00020002.png new file mode 100644 index 000000000..1b3b1f81a Binary files /dev/null and b/assets/amiibo/images/icon_00030000-00020002.png differ diff --git a/assets/amiibo/images/icon_00030000-00370102.png b/assets/amiibo/images/icon_00030000-00370102.png new file mode 100644 index 000000000..337dc3db2 Binary files /dev/null and b/assets/amiibo/images/icon_00030000-00370102.png differ diff --git a/assets/amiibo/images/icon_00030003-039fff02.png b/assets/amiibo/images/icon_00030003-039fff02.png new file mode 100644 index 000000000..ec3ce7a9c Binary files /dev/null and b/assets/amiibo/images/icon_00030003-039fff02.png differ diff --git a/assets/amiibo/images/icon_00030102-00410302.png b/assets/amiibo/images/icon_00030102-00410302.png new file mode 100644 index 000000000..518e40610 Binary files /dev/null and b/assets/amiibo/images/icon_00030102-00410302.png differ diff --git a/assets/amiibo/images/icon_00030102-00420302.png b/assets/amiibo/images/icon_00030102-00420302.png new file mode 100644 index 000000000..51945ca20 Binary files /dev/null and b/assets/amiibo/images/icon_00030102-00420302.png differ diff --git a/assets/amiibo/images/icon_00030102-00430302.png b/assets/amiibo/images/icon_00030102-00430302.png new file mode 100644 index 000000000..b426fbaf5 Binary files /dev/null and b/assets/amiibo/images/icon_00030102-00430302.png differ diff --git a/assets/amiibo/images/icon_00030102-023e0302.png b/assets/amiibo/images/icon_00030102-023e0302.png new file mode 100644 index 000000000..3f1396ad9 Binary files /dev/null and b/assets/amiibo/images/icon_00030102-023e0302.png differ diff --git a/assets/amiibo/images/icon_00040000-02620102.png b/assets/amiibo/images/icon_00040000-02620102.png new file mode 100644 index 000000000..29a96ae90 Binary files /dev/null and b/assets/amiibo/images/icon_00040000-02620102.png differ diff --git a/assets/amiibo/images/icon_00040100-00130002.png b/assets/amiibo/images/icon_00040100-00130002.png new file mode 100644 index 000000000..3399daf93 Binary files /dev/null and b/assets/amiibo/images/icon_00040100-00130002.png differ diff --git a/assets/amiibo/images/icon_00050000-00140002.png b/assets/amiibo/images/icon_00050000-00140002.png new file mode 100644 index 000000000..76f480d03 Binary files /dev/null and b/assets/amiibo/images/icon_00050000-00140002.png differ diff --git a/assets/amiibo/images/icon_00050000-00390102.png b/assets/amiibo/images/icon_00050000-00390102.png new file mode 100644 index 000000000..23a574d6c Binary files /dev/null and b/assets/amiibo/images/icon_00050000-00390102.png differ diff --git a/assets/amiibo/images/icon_00050000-03730102.png b/assets/amiibo/images/icon_00050000-03730102.png new file mode 100644 index 000000000..59ef48e1e Binary files /dev/null and b/assets/amiibo/images/icon_00050000-03730102.png differ diff --git a/assets/amiibo/images/icon_0005ff00-023a0702.png b/assets/amiibo/images/icon_0005ff00-023a0702.png new file mode 100644 index 000000000..ba99e5a18 Binary files /dev/null and b/assets/amiibo/images/icon_0005ff00-023a0702.png differ diff --git a/assets/amiibo/images/icon_00060000-00150002.png b/assets/amiibo/images/icon_00060000-00150002.png new file mode 100644 index 000000000..307d0981d Binary files /dev/null and b/assets/amiibo/images/icon_00060000-00150002.png differ diff --git a/assets/amiibo/images/icon_00070000-001a0002.png b/assets/amiibo/images/icon_00070000-001a0002.png new file mode 100644 index 000000000..97e3c66fa Binary files /dev/null and b/assets/amiibo/images/icon_00070000-001a0002.png differ diff --git a/assets/amiibo/images/icon_00070000-02630102.png b/assets/amiibo/images/icon_00070000-02630102.png new file mode 100644 index 000000000..aed740985 Binary files /dev/null and b/assets/amiibo/images/icon_00070000-02630102.png differ diff --git a/assets/amiibo/images/icon_00080000-00030002.png b/assets/amiibo/images/icon_00080000-00030002.png new file mode 100644 index 000000000..5b43fc4f4 Binary files /dev/null and b/assets/amiibo/images/icon_00080000-00030002.png differ diff --git a/assets/amiibo/images/icon_00080000-02640102.png b/assets/amiibo/images/icon_00080000-02640102.png new file mode 100644 index 000000000..f4a6d291a Binary files /dev/null and b/assets/amiibo/images/icon_00080000-02640102.png differ diff --git a/assets/amiibo/images/icon_0008ff00-023b0702.png b/assets/amiibo/images/icon_0008ff00-023b0702.png new file mode 100644 index 000000000..f4869fee9 Binary files /dev/null and b/assets/amiibo/images/icon_0008ff00-023b0702.png differ diff --git a/assets/amiibo/images/icon_00090000-000d0002.png b/assets/amiibo/images/icon_00090000-000d0002.png new file mode 100644 index 000000000..43f563daf Binary files /dev/null and b/assets/amiibo/images/icon_00090000-000d0002.png differ diff --git a/assets/amiibo/images/icon_00090000-02650102.png b/assets/amiibo/images/icon_00090000-02650102.png new file mode 100644 index 000000000..6219c458d Binary files /dev/null and b/assets/amiibo/images/icon_00090000-02650102.png differ diff --git a/assets/amiibo/images/icon_000a0000-00380102.png b/assets/amiibo/images/icon_000a0000-00380102.png new file mode 100644 index 000000000..0da5fbdfd Binary files /dev/null and b/assets/amiibo/images/icon_000a0000-00380102.png differ diff --git a/assets/amiibo/images/icon_000a0003-03a0ff02.png b/assets/amiibo/images/icon_000a0003-03a0ff02.png new file mode 100644 index 000000000..c638956e7 Binary files /dev/null and b/assets/amiibo/images/icon_000a0003-03a0ff02.png differ diff --git a/assets/amiibo/images/icon_00130000-02660102.png b/assets/amiibo/images/icon_00130000-02660102.png new file mode 100644 index 000000000..502850e8f Binary files /dev/null and b/assets/amiibo/images/icon_00130000-02660102.png differ diff --git a/assets/amiibo/images/icon_00130000-037a0002.png b/assets/amiibo/images/icon_00130000-037a0002.png new file mode 100644 index 000000000..cc5d09782 Binary files /dev/null and b/assets/amiibo/images/icon_00130000-037a0002.png differ diff --git a/assets/amiibo/images/icon_00130003-039eff02.png b/assets/amiibo/images/icon_00130003-039eff02.png new file mode 100644 index 000000000..215ea5bcc Binary files /dev/null and b/assets/amiibo/images/icon_00130003-039eff02.png differ diff --git a/assets/amiibo/images/icon_00140000-02670102.png b/assets/amiibo/images/icon_00140000-02670102.png new file mode 100644 index 000000000..e04bba004 Binary files /dev/null and b/assets/amiibo/images/icon_00140000-02670102.png differ diff --git a/assets/amiibo/images/icon_00150000-03670102.png b/assets/amiibo/images/icon_00150000-03670102.png new file mode 100644 index 000000000..76524c612 Binary files /dev/null and b/assets/amiibo/images/icon_00150000-03670102.png differ diff --git a/assets/amiibo/images/icon_00170000-02680102.png b/assets/amiibo/images/icon_00170000-02680102.png new file mode 100644 index 000000000..0b93845c4 Binary files /dev/null and b/assets/amiibo/images/icon_00170000-02680102.png differ diff --git a/assets/amiibo/images/icon_00230000-03680102.png b/assets/amiibo/images/icon_00230000-03680102.png new file mode 100644 index 000000000..d3e966cf4 Binary files /dev/null and b/assets/amiibo/images/icon_00230000-03680102.png differ diff --git a/assets/amiibo/images/icon_00240000-038d0002.png b/assets/amiibo/images/icon_00240000-038d0002.png new file mode 100644 index 000000000..1b06313b5 Binary files /dev/null and b/assets/amiibo/images/icon_00240000-038d0002.png differ diff --git a/assets/amiibo/images/icon_00800102-035d0302.png b/assets/amiibo/images/icon_00800102-035d0302.png new file mode 100644 index 000000000..1b0bb1f9f Binary files /dev/null and b/assets/amiibo/images/icon_00800102-035d0302.png differ diff --git a/assets/amiibo/images/icon_00c00000-037b0002.png b/assets/amiibo/images/icon_00c00000-037b0002.png new file mode 100644 index 000000000..2ecb86bb9 Binary files /dev/null and b/assets/amiibo/images/icon_00c00000-037b0002.png differ diff --git a/assets/amiibo/images/icon_01000000-00040002.png b/assets/amiibo/images/icon_01000000-00040002.png new file mode 100644 index 000000000..89db88178 Binary files /dev/null and b/assets/amiibo/images/icon_01000000-00040002.png differ diff --git a/assets/amiibo/images/icon_01000000-034b0902.png b/assets/amiibo/images/icon_01000000-034b0902.png new file mode 100644 index 000000000..d76fc5f51 Binary files /dev/null and b/assets/amiibo/images/icon_01000000-034b0902.png differ diff --git a/assets/amiibo/images/icon_01000000-034c0902.png b/assets/amiibo/images/icon_01000000-034c0902.png new file mode 100644 index 000000000..d0fefccda Binary files /dev/null and b/assets/amiibo/images/icon_01000000-034c0902.png differ diff --git a/assets/amiibo/images/icon_01000000-034d0902.png b/assets/amiibo/images/icon_01000000-034d0902.png new file mode 100644 index 000000000..5e75dcfa4 Binary files /dev/null and b/assets/amiibo/images/icon_01000000-034d0902.png differ diff --git a/assets/amiibo/images/icon_01000000-034e0902.png b/assets/amiibo/images/icon_01000000-034e0902.png new file mode 100644 index 000000000..aef55553f Binary files /dev/null and b/assets/amiibo/images/icon_01000000-034e0902.png differ diff --git a/assets/amiibo/images/icon_01000000-034f0902.png b/assets/amiibo/images/icon_01000000-034f0902.png new file mode 100644 index 000000000..11a59e917 Binary files /dev/null and b/assets/amiibo/images/icon_01000000-034f0902.png differ diff --git a/assets/amiibo/images/icon_01000000-03530902.png b/assets/amiibo/images/icon_01000000-03530902.png new file mode 100644 index 000000000..46d157378 Binary files /dev/null and b/assets/amiibo/images/icon_01000000-03530902.png differ diff --git a/assets/amiibo/images/icon_01000000-03540902.png b/assets/amiibo/images/icon_01000000-03540902.png new file mode 100644 index 000000000..23c61e036 Binary files /dev/null and b/assets/amiibo/images/icon_01000000-03540902.png differ diff --git a/assets/amiibo/images/icon_01000000-037c0002.png b/assets/amiibo/images/icon_01000000-037c0002.png new file mode 100644 index 000000000..95dec8361 Binary files /dev/null and b/assets/amiibo/images/icon_01000000-037c0002.png differ diff --git a/assets/amiibo/images/icon_01000000-03990902.png b/assets/amiibo/images/icon_01000000-03990902.png new file mode 100644 index 000000000..37d7a0db0 Binary files /dev/null and b/assets/amiibo/images/icon_01000000-03990902.png differ diff --git a/assets/amiibo/images/icon_01000000-04180902.png b/assets/amiibo/images/icon_01000000-04180902.png new file mode 100644 index 000000000..939071f54 Binary files /dev/null and b/assets/amiibo/images/icon_01000000-04180902.png differ diff --git a/assets/amiibo/images/icon_01000100-00160002.png b/assets/amiibo/images/icon_01000100-00160002.png new file mode 100644 index 000000000..2d8738b53 Binary files /dev/null and b/assets/amiibo/images/icon_01000100-00160002.png differ diff --git a/assets/amiibo/images/icon_01000100-03500902.png b/assets/amiibo/images/icon_01000100-03500902.png new file mode 100644 index 000000000..bbb04cd21 Binary files /dev/null and b/assets/amiibo/images/icon_01000100-03500902.png differ diff --git a/assets/amiibo/images/icon_01010000-000e0002.png b/assets/amiibo/images/icon_01010000-000e0002.png new file mode 100644 index 000000000..b399f67a1 Binary files /dev/null and b/assets/amiibo/images/icon_01010000-000e0002.png differ diff --git a/assets/amiibo/images/icon_01010000-03520902.png b/assets/amiibo/images/icon_01010000-03520902.png new file mode 100644 index 000000000..c2259a606 Binary files /dev/null and b/assets/amiibo/images/icon_01010000-03520902.png differ diff --git a/assets/amiibo/images/icon_01010000-03560902.png b/assets/amiibo/images/icon_01010000-03560902.png new file mode 100644 index 000000000..8d336a55d Binary files /dev/null and b/assets/amiibo/images/icon_01010000-03560902.png differ diff --git a/assets/amiibo/images/icon_01010000-04190902.png b/assets/amiibo/images/icon_01010000-04190902.png new file mode 100644 index 000000000..155656e7c Binary files /dev/null and b/assets/amiibo/images/icon_01010000-04190902.png differ diff --git a/assets/amiibo/images/icon_01010100-00170002.png b/assets/amiibo/images/icon_01010100-00170002.png new file mode 100644 index 000000000..418b500f7 Binary files /dev/null and b/assets/amiibo/images/icon_01010100-00170002.png differ diff --git a/assets/amiibo/images/icon_01010300-04140902.png b/assets/amiibo/images/icon_01010300-04140902.png new file mode 100644 index 000000000..c1db27252 Binary files /dev/null and b/assets/amiibo/images/icon_01010300-04140902.png differ diff --git a/assets/amiibo/images/icon_01020100-001b0002.png b/assets/amiibo/images/icon_01020100-001b0002.png new file mode 100644 index 000000000..8a5b3a0a2 Binary files /dev/null and b/assets/amiibo/images/icon_01020100-001b0002.png differ diff --git a/assets/amiibo/images/icon_01020100-041a0902.png b/assets/amiibo/images/icon_01020100-041a0902.png new file mode 100644 index 000000000..f1729e327 Binary files /dev/null and b/assets/amiibo/images/icon_01020100-041a0902.png differ diff --git a/assets/amiibo/images/icon_01030000-024f0902.png b/assets/amiibo/images/icon_01030000-024f0902.png new file mode 100644 index 000000000..e044af322 Binary files /dev/null and b/assets/amiibo/images/icon_01030000-024f0902.png differ diff --git a/assets/amiibo/images/icon_01050000-03580902.png b/assets/amiibo/images/icon_01050000-03580902.png new file mode 100644 index 000000000..4680bbbd6 Binary files /dev/null and b/assets/amiibo/images/icon_01050000-03580902.png differ diff --git a/assets/amiibo/images/icon_01060000-03590902.png b/assets/amiibo/images/icon_01060000-03590902.png new file mode 100644 index 000000000..18c1b0f11 Binary files /dev/null and b/assets/amiibo/images/icon_01060000-03590902.png differ diff --git a/assets/amiibo/images/icon_01070000-035a0902.png b/assets/amiibo/images/icon_01070000-035a0902.png new file mode 100644 index 000000000..19971ab00 Binary files /dev/null and b/assets/amiibo/images/icon_01070000-035a0902.png differ diff --git a/assets/amiibo/images/icon_01080000-035b0902.png b/assets/amiibo/images/icon_01080000-035b0902.png new file mode 100644 index 000000000..215829c7b Binary files /dev/null and b/assets/amiibo/images/icon_01080000-035b0902.png differ diff --git a/assets/amiibo/images/icon_01400000-03550902.png b/assets/amiibo/images/icon_01400000-03550902.png new file mode 100644 index 000000000..8d669c7f8 Binary files /dev/null and b/assets/amiibo/images/icon_01400000-03550902.png differ diff --git a/assets/amiibo/images/icon_01410000-035c0902.png b/assets/amiibo/images/icon_01410000-035c0902.png new file mode 100644 index 000000000..d09e84278 Binary files /dev/null and b/assets/amiibo/images/icon_01410000-035c0902.png differ diff --git a/assets/amiibo/images/icon_01800000-00080002.png b/assets/amiibo/images/icon_01800000-00080002.png new file mode 100644 index 000000000..3c229cb35 Binary files /dev/null and b/assets/amiibo/images/icon_01800000-00080002.png differ diff --git a/assets/amiibo/images/icon_01810000-024b0502.png b/assets/amiibo/images/icon_01810000-024b0502.png new file mode 100644 index 000000000..9cdfdf5ac Binary files /dev/null and b/assets/amiibo/images/icon_01810000-024b0502.png differ diff --git a/assets/amiibo/images/icon_01810000-037d0002.png b/assets/amiibo/images/icon_01810000-037d0002.png new file mode 100644 index 000000000..c46642153 Binary files /dev/null and b/assets/amiibo/images/icon_01810000-037d0002.png differ diff --git a/assets/amiibo/images/icon_01810001-00440502.png b/assets/amiibo/images/icon_01810001-00440502.png new file mode 100644 index 000000000..0d4439f02 Binary files /dev/null and b/assets/amiibo/images/icon_01810001-00440502.png differ diff --git a/assets/amiibo/images/icon_01810001-01d40502.png b/assets/amiibo/images/icon_01810001-01d40502.png new file mode 100644 index 000000000..fb6e5c8ad Binary files /dev/null and b/assets/amiibo/images/icon_01810001-01d40502.png differ diff --git a/assets/amiibo/images/icon_01810100-023f0502.png b/assets/amiibo/images/icon_01810100-023f0502.png new file mode 100644 index 000000000..26a5c0c11 Binary files /dev/null and b/assets/amiibo/images/icon_01810100-023f0502.png differ diff --git a/assets/amiibo/images/icon_01810101-00b40502.png b/assets/amiibo/images/icon_01810101-00b40502.png new file mode 100644 index 000000000..d5a901289 Binary files /dev/null and b/assets/amiibo/images/icon_01810101-00b40502.png differ diff --git a/assets/amiibo/images/icon_01810201-011a0502.png b/assets/amiibo/images/icon_01810201-011a0502.png new file mode 100644 index 000000000..22dc77e1f Binary files /dev/null and b/assets/amiibo/images/icon_01810201-011a0502.png differ diff --git a/assets/amiibo/images/icon_01810301-01700502.png b/assets/amiibo/images/icon_01810301-01700502.png new file mode 100644 index 000000000..bdd969b3a Binary files /dev/null and b/assets/amiibo/images/icon_01810301-01700502.png differ diff --git a/assets/amiibo/images/icon_01810401-03aa0502.png b/assets/amiibo/images/icon_01810401-03aa0502.png new file mode 100644 index 000000000..44e646f67 Binary files /dev/null and b/assets/amiibo/images/icon_01810401-03aa0502.png differ diff --git a/assets/amiibo/images/icon_01810501-03bf0502.png b/assets/amiibo/images/icon_01810501-03bf0502.png new file mode 100644 index 000000000..97c89b5fb Binary files /dev/null and b/assets/amiibo/images/icon_01810501-03bf0502.png differ diff --git a/assets/amiibo/images/icon_01820000-02400502.png b/assets/amiibo/images/icon_01820000-02400502.png new file mode 100644 index 000000000..fae58f523 Binary files /dev/null and b/assets/amiibo/images/icon_01820000-02400502.png differ diff --git a/assets/amiibo/images/icon_01820001-00a80502.png b/assets/amiibo/images/icon_01820001-00a80502.png new file mode 100644 index 000000000..aedee2c99 Binary files /dev/null and b/assets/amiibo/images/icon_01820001-00a80502.png differ diff --git a/assets/amiibo/images/icon_01820001-01d80502.png b/assets/amiibo/images/icon_01820001-01d80502.png new file mode 100644 index 000000000..f31ded694 Binary files /dev/null and b/assets/amiibo/images/icon_01820001-01d80502.png differ diff --git a/assets/amiibo/images/icon_01820001-03b20502.png b/assets/amiibo/images/icon_01820001-03b20502.png new file mode 100644 index 000000000..974b4415d Binary files /dev/null and b/assets/amiibo/images/icon_01820001-03b20502.png differ diff --git a/assets/amiibo/images/icon_01820101-00460502.png b/assets/amiibo/images/icon_01820101-00460502.png new file mode 100644 index 000000000..4eee12a4d Binary files /dev/null and b/assets/amiibo/images/icon_01820101-00460502.png differ diff --git a/assets/amiibo/images/icon_01830000-02420502.png b/assets/amiibo/images/icon_01830000-02420502.png new file mode 100644 index 000000000..df6a8df77 Binary files /dev/null and b/assets/amiibo/images/icon_01830000-02420502.png differ diff --git a/assets/amiibo/images/icon_01830001-00450502.png b/assets/amiibo/images/icon_01830001-00450502.png new file mode 100644 index 000000000..e7a8edd06 Binary files /dev/null and b/assets/amiibo/images/icon_01830001-00450502.png differ diff --git a/assets/amiibo/images/icon_01830101-010e0502.png b/assets/amiibo/images/icon_01830101-010e0502.png new file mode 100644 index 000000000..c397bc1c4 Binary files /dev/null and b/assets/amiibo/images/icon_01830101-010e0502.png differ diff --git a/assets/amiibo/images/icon_01830201-03a80502.png b/assets/amiibo/images/icon_01830201-03a80502.png new file mode 100644 index 000000000..7c53003ea Binary files /dev/null and b/assets/amiibo/images/icon_01830201-03a80502.png differ diff --git a/assets/amiibo/images/icon_01830301-03be0502.png b/assets/amiibo/images/icon_01830301-03be0502.png new file mode 100644 index 000000000..a25b00bd9 Binary files /dev/null and b/assets/amiibo/images/icon_01830301-03be0502.png differ diff --git a/assets/amiibo/images/icon_01840000-024d0502.png b/assets/amiibo/images/icon_01840000-024d0502.png new file mode 100644 index 000000000..af0b9c5ae Binary files /dev/null and b/assets/amiibo/images/icon_01840000-024d0502.png differ diff --git a/assets/amiibo/images/icon_01840501-03a90502.png b/assets/amiibo/images/icon_01840501-03a90502.png new file mode 100644 index 000000000..6f3dd2672 Binary files /dev/null and b/assets/amiibo/images/icon_01840501-03a90502.png differ diff --git a/assets/amiibo/images/icon_01850001-004b0502.png b/assets/amiibo/images/icon_01850001-004b0502.png new file mode 100644 index 000000000..70043a090 Binary files /dev/null and b/assets/amiibo/images/icon_01850001-004b0502.png differ diff --git a/assets/amiibo/images/icon_01850201-01170502.png b/assets/amiibo/images/icon_01850201-01170502.png new file mode 100644 index 000000000..d3950e2bd Binary files /dev/null and b/assets/amiibo/images/icon_01850201-01170502.png differ diff --git a/assets/amiibo/images/icon_01850401-01790502.png b/assets/amiibo/images/icon_01850401-01790502.png new file mode 100644 index 000000000..2f49cc6e2 Binary files /dev/null and b/assets/amiibo/images/icon_01850401-01790502.png differ diff --git a/assets/amiibo/images/icon_01860101-00af0502.png b/assets/amiibo/images/icon_01860101-00af0502.png new file mode 100644 index 000000000..a75b5ddc5 Binary files /dev/null and b/assets/amiibo/images/icon_01860101-00af0502.png differ diff --git a/assets/amiibo/images/icon_01860301-01750502.png b/assets/amiibo/images/icon_01860301-01750502.png new file mode 100644 index 000000000..82075f79e Binary files /dev/null and b/assets/amiibo/images/icon_01860301-01750502.png differ diff --git a/assets/amiibo/images/icon_01870001-00470502.png b/assets/amiibo/images/icon_01870001-00470502.png new file mode 100644 index 000000000..e068c1e52 Binary files /dev/null and b/assets/amiibo/images/icon_01870001-00470502.png differ diff --git a/assets/amiibo/images/icon_01870001-03b00502.png b/assets/amiibo/images/icon_01870001-03b00502.png new file mode 100644 index 000000000..736abcaec Binary files /dev/null and b/assets/amiibo/images/icon_01870001-03b00502.png differ diff --git a/assets/amiibo/images/icon_01880000-02410502.png b/assets/amiibo/images/icon_01880000-02410502.png new file mode 100644 index 000000000..cbfef0032 Binary files /dev/null and b/assets/amiibo/images/icon_01880000-02410502.png differ diff --git a/assets/amiibo/images/icon_01880001-01120502.png b/assets/amiibo/images/icon_01880001-01120502.png new file mode 100644 index 000000000..1167638f9 Binary files /dev/null and b/assets/amiibo/images/icon_01880001-01120502.png differ diff --git a/assets/amiibo/images/icon_01880001-03af0502.png b/assets/amiibo/images/icon_01880001-03af0502.png new file mode 100644 index 000000000..6e162e6d3 Binary files /dev/null and b/assets/amiibo/images/icon_01880001-03af0502.png differ diff --git a/assets/amiibo/images/icon_01890001-00ab0502.png b/assets/amiibo/images/icon_01890001-00ab0502.png new file mode 100644 index 000000000..a96ad753e Binary files /dev/null and b/assets/amiibo/images/icon_01890001-00ab0502.png differ diff --git a/assets/amiibo/images/icon_01890101-03b10502.png b/assets/amiibo/images/icon_01890101-03b10502.png new file mode 100644 index 000000000..c973f9e65 Binary files /dev/null and b/assets/amiibo/images/icon_01890101-03b10502.png differ diff --git a/assets/amiibo/images/icon_018a0000-02450502.png b/assets/amiibo/images/icon_018a0000-02450502.png new file mode 100644 index 000000000..29bcf9427 Binary files /dev/null and b/assets/amiibo/images/icon_018a0000-02450502.png differ diff --git a/assets/amiibo/images/icon_018a0001-00a90502.png b/assets/amiibo/images/icon_018a0001-00a90502.png new file mode 100644 index 000000000..a08a53e75 Binary files /dev/null and b/assets/amiibo/images/icon_018a0001-00a90502.png differ diff --git a/assets/amiibo/images/icon_018b0000-02460502.png b/assets/amiibo/images/icon_018b0000-02460502.png new file mode 100644 index 000000000..d3ca3157f Binary files /dev/null and b/assets/amiibo/images/icon_018b0000-02460502.png differ diff --git a/assets/amiibo/images/icon_018b0001-01150502.png b/assets/amiibo/images/icon_018b0001-01150502.png new file mode 100644 index 000000000..0b50ed0d0 Binary files /dev/null and b/assets/amiibo/images/icon_018b0001-01150502.png differ diff --git a/assets/amiibo/images/icon_018c0000-02430502.png b/assets/amiibo/images/icon_018c0000-02430502.png new file mode 100644 index 000000000..5b8af1a5e Binary files /dev/null and b/assets/amiibo/images/icon_018c0000-02430502.png differ diff --git a/assets/amiibo/images/icon_018c0001-004c0502.png b/assets/amiibo/images/icon_018c0001-004c0502.png new file mode 100644 index 000000000..c67f64bbc Binary files /dev/null and b/assets/amiibo/images/icon_018c0001-004c0502.png differ diff --git a/assets/amiibo/images/icon_018c0101-01180502.png b/assets/amiibo/images/icon_018c0101-01180502.png new file mode 100644 index 000000000..93fbe1bce Binary files /dev/null and b/assets/amiibo/images/icon_018c0101-01180502.png differ diff --git a/assets/amiibo/images/icon_018d0000-024c0502.png b/assets/amiibo/images/icon_018d0000-024c0502.png new file mode 100644 index 000000000..38c2dcb37 Binary files /dev/null and b/assets/amiibo/images/icon_018d0000-024c0502.png differ diff --git a/assets/amiibo/images/icon_018d0001-010c0502.png b/assets/amiibo/images/icon_018d0001-010c0502.png new file mode 100644 index 000000000..fb328b1c0 Binary files /dev/null and b/assets/amiibo/images/icon_018d0001-010c0502.png differ diff --git a/assets/amiibo/images/icon_018e0000-02490502.png b/assets/amiibo/images/icon_018e0000-02490502.png new file mode 100644 index 000000000..f27bd19fa Binary files /dev/null and b/assets/amiibo/images/icon_018e0000-02490502.png differ diff --git a/assets/amiibo/images/icon_018e0001-00490502.png b/assets/amiibo/images/icon_018e0001-00490502.png new file mode 100644 index 000000000..d3f5e504b Binary files /dev/null and b/assets/amiibo/images/icon_018e0001-00490502.png differ diff --git a/assets/amiibo/images/icon_018e0101-01780502.png b/assets/amiibo/images/icon_018e0101-01780502.png new file mode 100644 index 000000000..fb579f84d Binary files /dev/null and b/assets/amiibo/images/icon_018e0101-01780502.png differ diff --git a/assets/amiibo/images/icon_018f0001-00b30502.png b/assets/amiibo/images/icon_018f0001-00b30502.png new file mode 100644 index 000000000..5c8bf0d0d Binary files /dev/null and b/assets/amiibo/images/icon_018f0001-00b30502.png differ diff --git a/assets/amiibo/images/icon_018f0101-01190502.png b/assets/amiibo/images/icon_018f0101-01190502.png new file mode 100644 index 000000000..805ae3eb4 Binary files /dev/null and b/assets/amiibo/images/icon_018f0101-01190502.png differ diff --git a/assets/amiibo/images/icon_01900001-01710502.png b/assets/amiibo/images/icon_01900001-01710502.png new file mode 100644 index 000000000..df82724d5 Binary files /dev/null and b/assets/amiibo/images/icon_01900001-01710502.png differ diff --git a/assets/amiibo/images/icon_01910001-004e0502.png b/assets/amiibo/images/icon_01910001-004e0502.png new file mode 100644 index 000000000..62ab4d28a Binary files /dev/null and b/assets/amiibo/images/icon_01910001-004e0502.png differ diff --git a/assets/amiibo/images/icon_01920000-02470502.png b/assets/amiibo/images/icon_01920000-02470502.png new file mode 100644 index 000000000..c605f10dd Binary files /dev/null and b/assets/amiibo/images/icon_01920000-02470502.png differ diff --git a/assets/amiibo/images/icon_01920001-010d0502.png b/assets/amiibo/images/icon_01920001-010d0502.png new file mode 100644 index 000000000..465c7d348 Binary files /dev/null and b/assets/amiibo/images/icon_01920001-010d0502.png differ diff --git a/assets/amiibo/images/icon_01920001-03ad0502.png b/assets/amiibo/images/icon_01920001-03ad0502.png new file mode 100644 index 000000000..371e97846 Binary files /dev/null and b/assets/amiibo/images/icon_01920001-03ad0502.png differ diff --git a/assets/amiibo/images/icon_01930000-02480502.png b/assets/amiibo/images/icon_01930000-02480502.png new file mode 100644 index 000000000..033acf278 Binary files /dev/null and b/assets/amiibo/images/icon_01930000-02480502.png differ diff --git a/assets/amiibo/images/icon_01930001-01740502.png b/assets/amiibo/images/icon_01930001-01740502.png new file mode 100644 index 000000000..3ccafabe9 Binary files /dev/null and b/assets/amiibo/images/icon_01930001-01740502.png differ diff --git a/assets/amiibo/images/icon_01930001-03ae0502.png b/assets/amiibo/images/icon_01930001-03ae0502.png new file mode 100644 index 000000000..7d85c371c Binary files /dev/null and b/assets/amiibo/images/icon_01930001-03ae0502.png differ diff --git a/assets/amiibo/images/icon_01940000-024a0502.png b/assets/amiibo/images/icon_01940000-024a0502.png new file mode 100644 index 000000000..5d0571c61 Binary files /dev/null and b/assets/amiibo/images/icon_01940000-024a0502.png differ diff --git a/assets/amiibo/images/icon_01940001-00aa0502.png b/assets/amiibo/images/icon_01940001-00aa0502.png new file mode 100644 index 000000000..8fbaa48ab Binary files /dev/null and b/assets/amiibo/images/icon_01940001-00aa0502.png differ diff --git a/assets/amiibo/images/icon_01940001-03b60502.png b/assets/amiibo/images/icon_01940001-03b60502.png new file mode 100644 index 000000000..5ce1e16ad Binary files /dev/null and b/assets/amiibo/images/icon_01940001-03b60502.png differ diff --git a/assets/amiibo/images/icon_01950001-00b00502.png b/assets/amiibo/images/icon_01950001-00b00502.png new file mode 100644 index 000000000..31e3f7c4a Binary files /dev/null and b/assets/amiibo/images/icon_01950001-00b00502.png differ diff --git a/assets/amiibo/images/icon_01960000-024e0502.png b/assets/amiibo/images/icon_01960000-024e0502.png new file mode 100644 index 000000000..3eb260263 Binary files /dev/null and b/assets/amiibo/images/icon_01960000-024e0502.png differ diff --git a/assets/amiibo/images/icon_01960001-00480502.png b/assets/amiibo/images/icon_01960001-00480502.png new file mode 100644 index 000000000..2b5f81c6f Binary files /dev/null and b/assets/amiibo/images/icon_01960001-00480502.png differ diff --git a/assets/amiibo/images/icon_01970001-01770502.png b/assets/amiibo/images/icon_01970001-01770502.png new file mode 100644 index 000000000..64d3a0cee Binary files /dev/null and b/assets/amiibo/images/icon_01970001-01770502.png differ diff --git a/assets/amiibo/images/icon_01980001-00b10502.png b/assets/amiibo/images/icon_01980001-00b10502.png new file mode 100644 index 000000000..5b0fed906 Binary files /dev/null and b/assets/amiibo/images/icon_01980001-00b10502.png differ diff --git a/assets/amiibo/images/icon_01990001-01160502.png b/assets/amiibo/images/icon_01990001-01160502.png new file mode 100644 index 000000000..1fba27a52 Binary files /dev/null and b/assets/amiibo/images/icon_01990001-01160502.png differ diff --git a/assets/amiibo/images/icon_019a0001-00b70502.png b/assets/amiibo/images/icon_019a0001-00b70502.png new file mode 100644 index 000000000..03723b3e9 Binary files /dev/null and b/assets/amiibo/images/icon_019a0001-00b70502.png differ diff --git a/assets/amiibo/images/icon_019b0001-00b60502.png b/assets/amiibo/images/icon_019b0001-00b60502.png new file mode 100644 index 000000000..f1a8d4730 Binary files /dev/null and b/assets/amiibo/images/icon_019b0001-00b60502.png differ diff --git a/assets/amiibo/images/icon_019c0001-01730502.png b/assets/amiibo/images/icon_019c0001-01730502.png new file mode 100644 index 000000000..bacbaa11e Binary files /dev/null and b/assets/amiibo/images/icon_019c0001-01730502.png differ diff --git a/assets/amiibo/images/icon_019d0001-00ac0502.png b/assets/amiibo/images/icon_019d0001-00ac0502.png new file mode 100644 index 000000000..fbc0976fd Binary files /dev/null and b/assets/amiibo/images/icon_019d0001-00ac0502.png differ diff --git a/assets/amiibo/images/icon_019e0001-00ad0502.png b/assets/amiibo/images/icon_019e0001-00ad0502.png new file mode 100644 index 000000000..16ed5854f Binary files /dev/null and b/assets/amiibo/images/icon_019e0001-00ad0502.png differ diff --git a/assets/amiibo/images/icon_019f0001-01110502.png b/assets/amiibo/images/icon_019f0001-01110502.png new file mode 100644 index 000000000..42d0ae64e Binary files /dev/null and b/assets/amiibo/images/icon_019f0001-01110502.png differ diff --git a/assets/amiibo/images/icon_01a00001-010f0502.png b/assets/amiibo/images/icon_01a00001-010f0502.png new file mode 100644 index 000000000..76283ef27 Binary files /dev/null and b/assets/amiibo/images/icon_01a00001-010f0502.png differ diff --git a/assets/amiibo/images/icon_01a10001-01100502.png b/assets/amiibo/images/icon_01a10001-01100502.png new file mode 100644 index 000000000..9f09fbced Binary files /dev/null and b/assets/amiibo/images/icon_01a10001-01100502.png differ diff --git a/assets/amiibo/images/icon_01a20001-017d0502.png b/assets/amiibo/images/icon_01a20001-017d0502.png new file mode 100644 index 000000000..821fb22f7 Binary files /dev/null and b/assets/amiibo/images/icon_01a20001-017d0502.png differ diff --git a/assets/amiibo/images/icon_01a20001-03b90502.png b/assets/amiibo/images/icon_01a20001-03b90502.png new file mode 100644 index 000000000..768c08f4f Binary files /dev/null and b/assets/amiibo/images/icon_01a20001-03b90502.png differ diff --git a/assets/amiibo/images/icon_01a30001-004a0502.png b/assets/amiibo/images/icon_01a30001-004a0502.png new file mode 100644 index 000000000..c7c3ac4f3 Binary files /dev/null and b/assets/amiibo/images/icon_01a30001-004a0502.png differ diff --git a/assets/amiibo/images/icon_01a40001-004d0502.png b/assets/amiibo/images/icon_01a40001-004d0502.png new file mode 100644 index 000000000..b7fc5a5ca Binary files /dev/null and b/assets/amiibo/images/icon_01a40001-004d0502.png differ diff --git a/assets/amiibo/images/icon_01a50001-01720502.png b/assets/amiibo/images/icon_01a50001-01720502.png new file mode 100644 index 000000000..c2606e78c Binary files /dev/null and b/assets/amiibo/images/icon_01a50001-01720502.png differ diff --git a/assets/amiibo/images/icon_01a60001-00500502.png b/assets/amiibo/images/icon_01a60001-00500502.png new file mode 100644 index 000000000..e1979133e Binary files /dev/null and b/assets/amiibo/images/icon_01a60001-00500502.png differ diff --git a/assets/amiibo/images/icon_01a60001-03b70502.png b/assets/amiibo/images/icon_01a60001-03b70502.png new file mode 100644 index 000000000..4679e138a Binary files /dev/null and b/assets/amiibo/images/icon_01a60001-03b70502.png differ diff --git a/assets/amiibo/images/icon_01a70001-01140502.png b/assets/amiibo/images/icon_01a70001-01140502.png new file mode 100644 index 000000000..40d9725c6 Binary files /dev/null and b/assets/amiibo/images/icon_01a70001-01140502.png differ diff --git a/assets/amiibo/images/icon_01a80001-004f0502.png b/assets/amiibo/images/icon_01a80001-004f0502.png new file mode 100644 index 000000000..45f21ea13 Binary files /dev/null and b/assets/amiibo/images/icon_01a80001-004f0502.png differ diff --git a/assets/amiibo/images/icon_01a80101-017e0502.png b/assets/amiibo/images/icon_01a80101-017e0502.png new file mode 100644 index 000000000..f5df61dfa Binary files /dev/null and b/assets/amiibo/images/icon_01a80101-017e0502.png differ diff --git a/assets/amiibo/images/icon_01a90001-01760502.png b/assets/amiibo/images/icon_01a90001-01760502.png new file mode 100644 index 000000000..67651098f Binary files /dev/null and b/assets/amiibo/images/icon_01a90001-01760502.png differ diff --git a/assets/amiibo/images/icon_01aa0001-00530502.png b/assets/amiibo/images/icon_01aa0001-00530502.png new file mode 100644 index 000000000..a68c51f4d Binary files /dev/null and b/assets/amiibo/images/icon_01aa0001-00530502.png differ diff --git a/assets/amiibo/images/icon_01ab0001-017c0502.png b/assets/amiibo/images/icon_01ab0001-017c0502.png new file mode 100644 index 000000000..345a0e7f6 Binary files /dev/null and b/assets/amiibo/images/icon_01ab0001-017c0502.png differ diff --git a/assets/amiibo/images/icon_01ac0001-017f0502.png b/assets/amiibo/images/icon_01ac0001-017f0502.png new file mode 100644 index 000000000..9a54b2bfa Binary files /dev/null and b/assets/amiibo/images/icon_01ac0001-017f0502.png differ diff --git a/assets/amiibo/images/icon_01ad0001-00b80502.png b/assets/amiibo/images/icon_01ad0001-00b80502.png new file mode 100644 index 000000000..78ec67d2d Binary files /dev/null and b/assets/amiibo/images/icon_01ad0001-00b80502.png differ diff --git a/assets/amiibo/images/icon_01ae0001-011b0502.png b/assets/amiibo/images/icon_01ae0001-011b0502.png new file mode 100644 index 000000000..b12d3c1e2 Binary files /dev/null and b/assets/amiibo/images/icon_01ae0001-011b0502.png differ diff --git a/assets/amiibo/images/icon_01af0001-011c0502.png b/assets/amiibo/images/icon_01af0001-011c0502.png new file mode 100644 index 000000000..0b4e4ac27 Binary files /dev/null and b/assets/amiibo/images/icon_01af0001-011c0502.png differ diff --git a/assets/amiibo/images/icon_01b00001-00520502.png b/assets/amiibo/images/icon_01b00001-00520502.png new file mode 100644 index 000000000..57f138b37 Binary files /dev/null and b/assets/amiibo/images/icon_01b00001-00520502.png differ diff --git a/assets/amiibo/images/icon_01b10001-00b20502.png b/assets/amiibo/images/icon_01b10001-00b20502.png new file mode 100644 index 000000000..7a1fbef4f Binary files /dev/null and b/assets/amiibo/images/icon_01b10001-00b20502.png differ diff --git a/assets/amiibo/images/icon_01b10101-017b0502.png b/assets/amiibo/images/icon_01b10101-017b0502.png new file mode 100644 index 000000000..eac173ad6 Binary files /dev/null and b/assets/amiibo/images/icon_01b10101-017b0502.png differ diff --git a/assets/amiibo/images/icon_01b30001-00b50502.png b/assets/amiibo/images/icon_01b30001-00b50502.png new file mode 100644 index 000000000..4a9db5473 Binary files /dev/null and b/assets/amiibo/images/icon_01b30001-00b50502.png differ diff --git a/assets/amiibo/images/icon_01b40001-01130502.png b/assets/amiibo/images/icon_01b40001-01130502.png new file mode 100644 index 000000000..426021a34 Binary files /dev/null and b/assets/amiibo/images/icon_01b40001-01130502.png differ diff --git a/assets/amiibo/images/icon_01b50001-00510502.png b/assets/amiibo/images/icon_01b50001-00510502.png new file mode 100644 index 000000000..5f2979eff Binary files /dev/null and b/assets/amiibo/images/icon_01b50001-00510502.png differ diff --git a/assets/amiibo/images/icon_01b60001-00ae0502.png b/assets/amiibo/images/icon_01b60001-00ae0502.png new file mode 100644 index 000000000..ff7b9006d Binary files /dev/null and b/assets/amiibo/images/icon_01b60001-00ae0502.png differ diff --git a/assets/amiibo/images/icon_01c10000-02440502.png b/assets/amiibo/images/icon_01c10000-02440502.png new file mode 100644 index 000000000..9707816af Binary files /dev/null and b/assets/amiibo/images/icon_01c10000-02440502.png differ diff --git a/assets/amiibo/images/icon_01c10001-00540502.png b/assets/amiibo/images/icon_01c10001-00540502.png new file mode 100644 index 000000000..3971b1a63 Binary files /dev/null and b/assets/amiibo/images/icon_01c10001-00540502.png differ diff --git a/assets/amiibo/images/icon_01c10101-017a0502.png b/assets/amiibo/images/icon_01c10101-017a0502.png new file mode 100644 index 000000000..ee4f4a096 Binary files /dev/null and b/assets/amiibo/images/icon_01c10101-017a0502.png differ diff --git a/assets/amiibo/images/icon_01c10201-03bb0502.png b/assets/amiibo/images/icon_01c10201-03bb0502.png new file mode 100644 index 000000000..5421bf70b Binary files /dev/null and b/assets/amiibo/images/icon_01c10201-03bb0502.png differ diff --git a/assets/amiibo/images/icon_02000001-00a10502.png b/assets/amiibo/images/icon_02000001-00a10502.png new file mode 100644 index 000000000..3a85b87db Binary files /dev/null and b/assets/amiibo/images/icon_02000001-00a10502.png differ diff --git a/assets/amiibo/images/icon_02010001-016a0502.png b/assets/amiibo/images/icon_02010001-016a0502.png new file mode 100644 index 000000000..0f0560f48 Binary files /dev/null and b/assets/amiibo/images/icon_02010001-016a0502.png differ diff --git a/assets/amiibo/images/icon_02020001-01030502.png b/assets/amiibo/images/icon_02020001-01030502.png new file mode 100644 index 000000000..d498b6828 Binary files /dev/null and b/assets/amiibo/images/icon_02020001-01030502.png differ diff --git a/assets/amiibo/images/icon_02030001-019a0502.png b/assets/amiibo/images/icon_02030001-019a0502.png new file mode 100644 index 000000000..e47a6c82b Binary files /dev/null and b/assets/amiibo/images/icon_02030001-019a0502.png differ diff --git a/assets/amiibo/images/icon_02060001-03120502.png b/assets/amiibo/images/icon_02060001-03120502.png new file mode 100644 index 000000000..fee76bd28 Binary files /dev/null and b/assets/amiibo/images/icon_02060001-03120502.png differ diff --git a/assets/amiibo/images/icon_02080001-00960502.png b/assets/amiibo/images/icon_02080001-00960502.png new file mode 100644 index 000000000..74b1f0fed Binary files /dev/null and b/assets/amiibo/images/icon_02080001-00960502.png differ diff --git a/assets/amiibo/images/icon_02090001-019f0502.png b/assets/amiibo/images/icon_02090001-019f0502.png new file mode 100644 index 000000000..b964bb673 Binary files /dev/null and b/assets/amiibo/images/icon_02090001-019f0502.png differ diff --git a/assets/amiibo/images/icon_02140001-00e40502.png b/assets/amiibo/images/icon_02140001-00e40502.png new file mode 100644 index 000000000..0bdc41652 Binary files /dev/null and b/assets/amiibo/images/icon_02140001-00e40502.png differ diff --git a/assets/amiibo/images/icon_02150001-01820502.png b/assets/amiibo/images/icon_02150001-01820502.png new file mode 100644 index 000000000..1ed1e3fb3 Binary files /dev/null and b/assets/amiibo/images/icon_02150001-01820502.png differ diff --git a/assets/amiibo/images/icon_02160001-00570502.png b/assets/amiibo/images/icon_02160001-00570502.png new file mode 100644 index 000000000..d1c719550 Binary files /dev/null and b/assets/amiibo/images/icon_02160001-00570502.png differ diff --git a/assets/amiibo/images/icon_02170001-01b30502.png b/assets/amiibo/images/icon_02170001-01b30502.png new file mode 100644 index 000000000..ca2fb9742 Binary files /dev/null and b/assets/amiibo/images/icon_02170001-01b30502.png differ diff --git a/assets/amiibo/images/icon_02190001-007e0502.png b/assets/amiibo/images/icon_02190001-007e0502.png new file mode 100644 index 000000000..7a83357c8 Binary files /dev/null and b/assets/amiibo/images/icon_02190001-007e0502.png differ diff --git a/assets/amiibo/images/icon_021a0001-00da0502.png b/assets/amiibo/images/icon_021a0001-00da0502.png new file mode 100644 index 000000000..84cf671b3 Binary files /dev/null and b/assets/amiibo/images/icon_021a0001-00da0502.png differ diff --git a/assets/amiibo/images/icon_021b0001-00800502.png b/assets/amiibo/images/icon_021b0001-00800502.png new file mode 100644 index 000000000..2062ec0ff Binary files /dev/null and b/assets/amiibo/images/icon_021b0001-00800502.png differ diff --git a/assets/amiibo/images/icon_021c0001-02f70502.png b/assets/amiibo/images/icon_021c0001-02f70502.png new file mode 100644 index 000000000..e90ac27be Binary files /dev/null and b/assets/amiibo/images/icon_021c0001-02f70502.png differ diff --git a/assets/amiibo/images/icon_021d0001-01cd0502.png b/assets/amiibo/images/icon_021d0001-01cd0502.png new file mode 100644 index 000000000..1cc55a53c Binary files /dev/null and b/assets/amiibo/images/icon_021d0001-01cd0502.png differ diff --git a/assets/amiibo/images/icon_021e0001-01230502.png b/assets/amiibo/images/icon_021e0001-01230502.png new file mode 100644 index 000000000..e00d6b0b2 Binary files /dev/null and b/assets/amiibo/images/icon_021e0001-01230502.png differ diff --git a/assets/amiibo/images/icon_021f0001-03170502.png b/assets/amiibo/images/icon_021f0001-03170502.png new file mode 100644 index 000000000..b1ed41933 Binary files /dev/null and b/assets/amiibo/images/icon_021f0001-03170502.png differ diff --git a/assets/amiibo/images/icon_02200001-00fd0502.png b/assets/amiibo/images/icon_02200001-00fd0502.png new file mode 100644 index 000000000..9d39523c1 Binary files /dev/null and b/assets/amiibo/images/icon_02200001-00fd0502.png differ diff --git a/assets/amiibo/images/icon_02210001-013c0502.png b/assets/amiibo/images/icon_02210001-013c0502.png new file mode 100644 index 000000000..ec0d12d1a Binary files /dev/null and b/assets/amiibo/images/icon_02210001-013c0502.png differ diff --git a/assets/amiibo/images/icon_02220001-01440502.png b/assets/amiibo/images/icon_02220001-01440502.png new file mode 100644 index 000000000..1bcd9f160 Binary files /dev/null and b/assets/amiibo/images/icon_02220001-01440502.png differ diff --git a/assets/amiibo/images/icon_022d0001-00f20502.png b/assets/amiibo/images/icon_022d0001-00f20502.png new file mode 100644 index 000000000..ca575920e Binary files /dev/null and b/assets/amiibo/images/icon_022d0001-00f20502.png differ diff --git a/assets/amiibo/images/icon_022e0001-01d30502.png b/assets/amiibo/images/icon_022e0001-01d30502.png new file mode 100644 index 000000000..8b3d0db28 Binary files /dev/null and b/assets/amiibo/images/icon_022e0001-01d30502.png differ diff --git a/assets/amiibo/images/icon_022f0001-011e0502.png b/assets/amiibo/images/icon_022f0001-011e0502.png new file mode 100644 index 000000000..5c9df328a Binary files /dev/null and b/assets/amiibo/images/icon_022f0001-011e0502.png differ diff --git a/assets/amiibo/images/icon_02300001-01d20502.png b/assets/amiibo/images/icon_02300001-01d20502.png new file mode 100644 index 000000000..d35c91e6d Binary files /dev/null and b/assets/amiibo/images/icon_02300001-01d20502.png differ diff --git a/assets/amiibo/images/icon_02310001-006a0502.png b/assets/amiibo/images/icon_02310001-006a0502.png new file mode 100644 index 000000000..67051c521 Binary files /dev/null and b/assets/amiibo/images/icon_02310001-006a0502.png differ diff --git a/assets/amiibo/images/icon_02320001-02ea0502.png b/assets/amiibo/images/icon_02320001-02ea0502.png new file mode 100644 index 000000000..3d8b089b0 Binary files /dev/null and b/assets/amiibo/images/icon_02320001-02ea0502.png differ diff --git a/assets/amiibo/images/icon_02330001-03060502.png b/assets/amiibo/images/icon_02330001-03060502.png new file mode 100644 index 000000000..d918fa161 Binary files /dev/null and b/assets/amiibo/images/icon_02330001-03060502.png differ diff --git a/assets/amiibo/images/icon_02350001-00840502.png b/assets/amiibo/images/icon_02350001-00840502.png new file mode 100644 index 000000000..5b16b02d5 Binary files /dev/null and b/assets/amiibo/images/icon_02350001-00840502.png differ diff --git a/assets/amiibo/images/icon_02380001-02f80502.png b/assets/amiibo/images/icon_02380001-02f80502.png new file mode 100644 index 000000000..7a6f375c5 Binary files /dev/null and b/assets/amiibo/images/icon_02380001-02f80502.png differ diff --git a/assets/amiibo/images/icon_023c0001-00bd0502.png b/assets/amiibo/images/icon_023c0001-00bd0502.png new file mode 100644 index 000000000..4b79159c7 Binary files /dev/null and b/assets/amiibo/images/icon_023c0001-00bd0502.png differ diff --git a/assets/amiibo/images/icon_023d0001-01b50502.png b/assets/amiibo/images/icon_023d0001-01b50502.png new file mode 100644 index 000000000..3f80dff60 Binary files /dev/null and b/assets/amiibo/images/icon_023d0001-01b50502.png differ diff --git a/assets/amiibo/images/icon_023e0001-00d10502.png b/assets/amiibo/images/icon_023e0001-00d10502.png new file mode 100644 index 000000000..8c76abb05 Binary files /dev/null and b/assets/amiibo/images/icon_023e0001-00d10502.png differ diff --git a/assets/amiibo/images/icon_023f0001-01660502.png b/assets/amiibo/images/icon_023f0001-01660502.png new file mode 100644 index 000000000..7036a0c27 Binary files /dev/null and b/assets/amiibo/images/icon_023f0001-01660502.png differ diff --git a/assets/amiibo/images/icon_024a0001-01d10502.png b/assets/amiibo/images/icon_024a0001-01d10502.png new file mode 100644 index 000000000..d7f6430b2 Binary files /dev/null and b/assets/amiibo/images/icon_024a0001-01d10502.png differ diff --git a/assets/amiibo/images/icon_024b0001-01260502.png b/assets/amiibo/images/icon_024b0001-01260502.png new file mode 100644 index 000000000..fed4228a0 Binary files /dev/null and b/assets/amiibo/images/icon_024b0001-01260502.png differ diff --git a/assets/amiibo/images/icon_024d0001-02f60502.png b/assets/amiibo/images/icon_024d0001-02f60502.png new file mode 100644 index 000000000..a2d0832ef Binary files /dev/null and b/assets/amiibo/images/icon_024d0001-02f60502.png differ diff --git a/assets/amiibo/images/icon_024f0001-00810502.png b/assets/amiibo/images/icon_024f0001-00810502.png new file mode 100644 index 000000000..9456ed75c Binary files /dev/null and b/assets/amiibo/images/icon_024f0001-00810502.png differ diff --git a/assets/amiibo/images/icon_02510001-00c10502.png b/assets/amiibo/images/icon_02510001-00c10502.png new file mode 100644 index 000000000..9ce5c04d0 Binary files /dev/null and b/assets/amiibo/images/icon_02510001-00c10502.png differ diff --git a/assets/amiibo/images/icon_02520001-00fe0502.png b/assets/amiibo/images/icon_02520001-00fe0502.png new file mode 100644 index 000000000..915a8942f Binary files /dev/null and b/assets/amiibo/images/icon_02520001-00fe0502.png differ diff --git a/assets/amiibo/images/icon_025d0001-00550502.png b/assets/amiibo/images/icon_025d0001-00550502.png new file mode 100644 index 000000000..96b45ecdd Binary files /dev/null and b/assets/amiibo/images/icon_025d0001-00550502.png differ diff --git a/assets/amiibo/images/icon_025e0001-01250502.png b/assets/amiibo/images/icon_025e0001-01250502.png new file mode 100644 index 000000000..83a06f997 Binary files /dev/null and b/assets/amiibo/images/icon_025e0001-01250502.png differ diff --git a/assets/amiibo/images/icon_025f0001-01c50502.png b/assets/amiibo/images/icon_025f0001-01c50502.png new file mode 100644 index 000000000..372cea8ba Binary files /dev/null and b/assets/amiibo/images/icon_025f0001-01c50502.png differ diff --git a/assets/amiibo/images/icon_025f0001-01d70502.png b/assets/amiibo/images/icon_025f0001-01d70502.png new file mode 100644 index 000000000..d44a31cac Binary files /dev/null and b/assets/amiibo/images/icon_025f0001-01d70502.png differ diff --git a/assets/amiibo/images/icon_02600001-00d20502.png b/assets/amiibo/images/icon_02600001-00d20502.png new file mode 100644 index 000000000..e1941dab6 Binary files /dev/null and b/assets/amiibo/images/icon_02600001-00d20502.png differ diff --git a/assets/amiibo/images/icon_02610001-00650502.png b/assets/amiibo/images/icon_02610001-00650502.png new file mode 100644 index 000000000..fe5bddf16 Binary files /dev/null and b/assets/amiibo/images/icon_02610001-00650502.png differ diff --git a/assets/amiibo/images/icon_02620001-01370502.png b/assets/amiibo/images/icon_02620001-01370502.png new file mode 100644 index 000000000..6af1156a5 Binary files /dev/null and b/assets/amiibo/images/icon_02620001-01370502.png differ diff --git a/assets/amiibo/images/icon_02630001-00750502.png b/assets/amiibo/images/icon_02630001-00750502.png new file mode 100644 index 000000000..b4ae4ecbb Binary files /dev/null and b/assets/amiibo/images/icon_02630001-00750502.png differ diff --git a/assets/amiibo/images/icon_02640001-01ac0502.png b/assets/amiibo/images/icon_02640001-01ac0502.png new file mode 100644 index 000000000..9ddb980a3 Binary files /dev/null and b/assets/amiibo/images/icon_02640001-01ac0502.png differ diff --git a/assets/amiibo/images/icon_02650001-01540502.png b/assets/amiibo/images/icon_02650001-01540502.png new file mode 100644 index 000000000..2912b78f6 Binary files /dev/null and b/assets/amiibo/images/icon_02650001-01540502.png differ diff --git a/assets/amiibo/images/icon_02660001-00680502.png b/assets/amiibo/images/icon_02660001-00680502.png new file mode 100644 index 000000000..8b6937be3 Binary files /dev/null and b/assets/amiibo/images/icon_02660001-00680502.png differ diff --git a/assets/amiibo/images/icon_02670001-01080502.png b/assets/amiibo/images/icon_02670001-01080502.png new file mode 100644 index 000000000..95882b58b Binary files /dev/null and b/assets/amiibo/images/icon_02670001-01080502.png differ diff --git a/assets/amiibo/images/icon_02680001-007d0502.png b/assets/amiibo/images/icon_02680001-007d0502.png new file mode 100644 index 000000000..18882d1be Binary files /dev/null and b/assets/amiibo/images/icon_02680001-007d0502.png differ diff --git a/assets/amiibo/images/icon_02690001-011f0502.png b/assets/amiibo/images/icon_02690001-011f0502.png new file mode 100644 index 000000000..e83b0c984 Binary files /dev/null and b/assets/amiibo/images/icon_02690001-011f0502.png differ diff --git a/assets/amiibo/images/icon_026a0001-01460502.png b/assets/amiibo/images/icon_026a0001-01460502.png new file mode 100644 index 000000000..9f0e870ec Binary files /dev/null and b/assets/amiibo/images/icon_026a0001-01460502.png differ diff --git a/assets/amiibo/images/icon_026b0001-00e90502.png b/assets/amiibo/images/icon_026b0001-00e90502.png new file mode 100644 index 000000000..b2c9407bd Binary files /dev/null and b/assets/amiibo/images/icon_026b0001-00e90502.png differ diff --git a/assets/amiibo/images/icon_026c0001-00c30502.png b/assets/amiibo/images/icon_026c0001-00c30502.png new file mode 100644 index 000000000..d47fde6b8 Binary files /dev/null and b/assets/amiibo/images/icon_026c0001-00c30502.png differ diff --git a/assets/amiibo/images/icon_026d0001-013f0502.png b/assets/amiibo/images/icon_026d0001-013f0502.png new file mode 100644 index 000000000..ce54b0201 Binary files /dev/null and b/assets/amiibo/images/icon_026d0001-013f0502.png differ diff --git a/assets/amiibo/images/icon_026e0001-00ba0502.png b/assets/amiibo/images/icon_026e0001-00ba0502.png new file mode 100644 index 000000000..eb6d3b580 Binary files /dev/null and b/assets/amiibo/images/icon_026e0001-00ba0502.png differ diff --git a/assets/amiibo/images/icon_026f0001-01900502.png b/assets/amiibo/images/icon_026f0001-01900502.png new file mode 100644 index 000000000..a3a008e52 Binary files /dev/null and b/assets/amiibo/images/icon_026f0001-01900502.png differ diff --git a/assets/amiibo/images/icon_02700001-00ff0502.png b/assets/amiibo/images/icon_02700001-00ff0502.png new file mode 100644 index 000000000..d748bb1cb Binary files /dev/null and b/assets/amiibo/images/icon_02700001-00ff0502.png differ diff --git a/assets/amiibo/images/icon_02710001-019b0502.png b/assets/amiibo/images/icon_02710001-019b0502.png new file mode 100644 index 000000000..fd8f7bbe8 Binary files /dev/null and b/assets/amiibo/images/icon_02710001-019b0502.png differ diff --git a/assets/amiibo/images/icon_02720001-01860502.png b/assets/amiibo/images/icon_02720001-01860502.png new file mode 100644 index 000000000..76854ded4 Binary files /dev/null and b/assets/amiibo/images/icon_02720001-01860502.png differ diff --git a/assets/amiibo/images/icon_027d0001-00630502.png b/assets/amiibo/images/icon_027d0001-00630502.png new file mode 100644 index 000000000..d205c6105 Binary files /dev/null and b/assets/amiibo/images/icon_027d0001-00630502.png differ diff --git a/assets/amiibo/images/icon_027e0001-01690502.png b/assets/amiibo/images/icon_027e0001-01690502.png new file mode 100644 index 000000000..ce2146352 Binary files /dev/null and b/assets/amiibo/images/icon_027e0001-01690502.png differ diff --git a/assets/amiibo/images/icon_027f0001-00b90502.png b/assets/amiibo/images/icon_027f0001-00b90502.png new file mode 100644 index 000000000..2dcf62c96 Binary files /dev/null and b/assets/amiibo/images/icon_027f0001-00b90502.png differ diff --git a/assets/amiibo/images/icon_02800001-00830502.png b/assets/amiibo/images/icon_02800001-00830502.png new file mode 100644 index 000000000..59acd3e09 Binary files /dev/null and b/assets/amiibo/images/icon_02800001-00830502.png differ diff --git a/assets/amiibo/images/icon_02810001-01200502.png b/assets/amiibo/images/icon_02810001-01200502.png new file mode 100644 index 000000000..6d1fe3acb Binary files /dev/null and b/assets/amiibo/images/icon_02810001-01200502.png differ diff --git a/assets/amiibo/images/icon_02820001-01810502.png b/assets/amiibo/images/icon_02820001-01810502.png new file mode 100644 index 000000000..88066c59a Binary files /dev/null and b/assets/amiibo/images/icon_02820001-01810502.png differ diff --git a/assets/amiibo/images/icon_02820001-01d60502.png b/assets/amiibo/images/icon_02820001-01d60502.png new file mode 100644 index 000000000..0ea8032cf Binary files /dev/null and b/assets/amiibo/images/icon_02820001-01d60502.png differ diff --git a/assets/amiibo/images/icon_02830001-00c70502.png b/assets/amiibo/images/icon_02830001-00c70502.png new file mode 100644 index 000000000..1d263c7cb Binary files /dev/null and b/assets/amiibo/images/icon_02830001-00c70502.png differ diff --git a/assets/amiibo/images/icon_02840001-02fe0502.png b/assets/amiibo/images/icon_02840001-02fe0502.png new file mode 100644 index 000000000..f5df3dddf Binary files /dev/null and b/assets/amiibo/images/icon_02840001-02fe0502.png differ diff --git a/assets/amiibo/images/icon_02860001-03130502.png b/assets/amiibo/images/icon_02860001-03130502.png new file mode 100644 index 000000000..769a4aceb Binary files /dev/null and b/assets/amiibo/images/icon_02860001-03130502.png differ diff --git a/assets/amiibo/images/icon_02870001-005a0502.png b/assets/amiibo/images/icon_02870001-005a0502.png new file mode 100644 index 000000000..6a81c697c Binary files /dev/null and b/assets/amiibo/images/icon_02870001-005a0502.png differ diff --git a/assets/amiibo/images/icon_028a0001-02e90502.png b/assets/amiibo/images/icon_028a0001-02e90502.png new file mode 100644 index 000000000..d9f28c379 Binary files /dev/null and b/assets/amiibo/images/icon_028a0001-02e90502.png differ diff --git a/assets/amiibo/images/icon_028b0001-00e30502.png b/assets/amiibo/images/icon_028b0001-00e30502.png new file mode 100644 index 000000000..609bc7d27 Binary files /dev/null and b/assets/amiibo/images/icon_028b0001-00e30502.png differ diff --git a/assets/amiibo/images/icon_028c0001-013e0502.png b/assets/amiibo/images/icon_028c0001-013e0502.png new file mode 100644 index 000000000..5ba4e987c Binary files /dev/null and b/assets/amiibo/images/icon_028c0001-013e0502.png differ diff --git a/assets/amiibo/images/icon_028d0001-01bd0502.png b/assets/amiibo/images/icon_028d0001-01bd0502.png new file mode 100644 index 000000000..b5c349f7e Binary files /dev/null and b/assets/amiibo/images/icon_028d0001-01bd0502.png differ diff --git a/assets/amiibo/images/icon_028e0001-019e0502.png b/assets/amiibo/images/icon_028e0001-019e0502.png new file mode 100644 index 000000000..4edb9846b Binary files /dev/null and b/assets/amiibo/images/icon_028e0001-019e0502.png differ diff --git a/assets/amiibo/images/icon_028f0101-031a0502.png b/assets/amiibo/images/icon_028f0101-031a0502.png new file mode 100644 index 000000000..8c6620594 Binary files /dev/null and b/assets/amiibo/images/icon_028f0101-031a0502.png differ diff --git a/assets/amiibo/images/icon_02990001-00950502.png b/assets/amiibo/images/icon_02990001-00950502.png new file mode 100644 index 000000000..43a7fdb0d Binary files /dev/null and b/assets/amiibo/images/icon_02990001-00950502.png differ diff --git a/assets/amiibo/images/icon_029a0001-00ee0502.png b/assets/amiibo/images/icon_029a0001-00ee0502.png new file mode 100644 index 000000000..ce13951b8 Binary files /dev/null and b/assets/amiibo/images/icon_029a0001-00ee0502.png differ diff --git a/assets/amiibo/images/icon_029b0001-00cb0502.png b/assets/amiibo/images/icon_029b0001-00cb0502.png new file mode 100644 index 000000000..a4205538b Binary files /dev/null and b/assets/amiibo/images/icon_029b0001-00cb0502.png differ diff --git a/assets/amiibo/images/icon_029e0001-013d0502.png b/assets/amiibo/images/icon_029e0001-013d0502.png new file mode 100644 index 000000000..1a0b16897 Binary files /dev/null and b/assets/amiibo/images/icon_029e0001-013d0502.png differ diff --git a/assets/amiibo/images/icon_02a20001-01ba0502.png b/assets/amiibo/images/icon_02a20001-01ba0502.png new file mode 100644 index 000000000..c08f5439f Binary files /dev/null and b/assets/amiibo/images/icon_02a20001-01ba0502.png differ diff --git a/assets/amiibo/images/icon_02a30001-02ff0502.png b/assets/amiibo/images/icon_02a30001-02ff0502.png new file mode 100644 index 000000000..f817f4b6f Binary files /dev/null and b/assets/amiibo/images/icon_02a30001-02ff0502.png differ diff --git a/assets/amiibo/images/icon_02a40001-00720502.png b/assets/amiibo/images/icon_02a40001-00720502.png new file mode 100644 index 000000000..bb2a694b7 Binary files /dev/null and b/assets/amiibo/images/icon_02a40001-00720502.png differ diff --git a/assets/amiibo/images/icon_02a50001-018c0502.png b/assets/amiibo/images/icon_02a50001-018c0502.png new file mode 100644 index 000000000..8fa364a7b Binary files /dev/null and b/assets/amiibo/images/icon_02a50001-018c0502.png differ diff --git a/assets/amiibo/images/icon_02a60001-01240502.png b/assets/amiibo/images/icon_02a60001-01240502.png new file mode 100644 index 000000000..be41540e5 Binary files /dev/null and b/assets/amiibo/images/icon_02a60001-01240502.png differ diff --git a/assets/amiibo/images/icon_02b10001-00690502.png b/assets/amiibo/images/icon_02b10001-00690502.png new file mode 100644 index 000000000..738cfe08a Binary files /dev/null and b/assets/amiibo/images/icon_02b10001-00690502.png differ diff --git a/assets/amiibo/images/icon_02b20001-00c40502.png b/assets/amiibo/images/icon_02b20001-00c40502.png new file mode 100644 index 000000000..02484d867 Binary files /dev/null and b/assets/amiibo/images/icon_02b20001-00c40502.png differ diff --git a/assets/amiibo/images/icon_02b70001-030f0502.png b/assets/amiibo/images/icon_02b70001-030f0502.png new file mode 100644 index 000000000..c516fd13f Binary files /dev/null and b/assets/amiibo/images/icon_02b70001-030f0502.png differ diff --git a/assets/amiibo/images/icon_02b80001-019c0502.png b/assets/amiibo/images/icon_02b80001-019c0502.png new file mode 100644 index 000000000..d09751c2a Binary files /dev/null and b/assets/amiibo/images/icon_02b80001-019c0502.png differ diff --git a/assets/amiibo/images/icon_02c30001-00dc0502.png b/assets/amiibo/images/icon_02c30001-00dc0502.png new file mode 100644 index 000000000..dcb38f063 Binary files /dev/null and b/assets/amiibo/images/icon_02c30001-00dc0502.png differ diff --git a/assets/amiibo/images/icon_02c40001-00670502.png b/assets/amiibo/images/icon_02c40001-00670502.png new file mode 100644 index 000000000..0f360252d Binary files /dev/null and b/assets/amiibo/images/icon_02c40001-00670502.png differ diff --git a/assets/amiibo/images/icon_02c50001-03080502.png b/assets/amiibo/images/icon_02c50001-03080502.png new file mode 100644 index 000000000..ed57c3dc8 Binary files /dev/null and b/assets/amiibo/images/icon_02c50001-03080502.png differ diff --git a/assets/amiibo/images/icon_02c70001-01220502.png b/assets/amiibo/images/icon_02c70001-01220502.png new file mode 100644 index 000000000..a9e004a29 Binary files /dev/null and b/assets/amiibo/images/icon_02c70001-01220502.png differ diff --git a/assets/amiibo/images/icon_02c90001-00cd0502.png b/assets/amiibo/images/icon_02c90001-00cd0502.png new file mode 100644 index 000000000..243025e28 Binary files /dev/null and b/assets/amiibo/images/icon_02c90001-00cd0502.png differ diff --git a/assets/amiibo/images/icon_02ca0001-01ca0502.png b/assets/amiibo/images/icon_02ca0001-01ca0502.png new file mode 100644 index 000000000..3e9f542ee Binary files /dev/null and b/assets/amiibo/images/icon_02ca0001-01ca0502.png differ diff --git a/assets/amiibo/images/icon_02cb0001-01360502.png b/assets/amiibo/images/icon_02cb0001-01360502.png new file mode 100644 index 000000000..f2cf11f71 Binary files /dev/null and b/assets/amiibo/images/icon_02cb0001-01360502.png differ diff --git a/assets/amiibo/images/icon_02d60001-00560502.png b/assets/amiibo/images/icon_02d60001-00560502.png new file mode 100644 index 000000000..da83d04bb Binary files /dev/null and b/assets/amiibo/images/icon_02d60001-00560502.png differ diff --git a/assets/amiibo/images/icon_02d70001-01300502.png b/assets/amiibo/images/icon_02d70001-01300502.png new file mode 100644 index 000000000..82573edb8 Binary files /dev/null and b/assets/amiibo/images/icon_02d70001-01300502.png differ diff --git a/assets/amiibo/images/icon_02d80001-00e20502.png b/assets/amiibo/images/icon_02d80001-00e20502.png new file mode 100644 index 000000000..2d0fdfe06 Binary files /dev/null and b/assets/amiibo/images/icon_02d80001-00e20502.png differ diff --git a/assets/amiibo/images/icon_02d90001-01c80502.png b/assets/amiibo/images/icon_02d90001-01c80502.png new file mode 100644 index 000000000..60b0c8bcf Binary files /dev/null and b/assets/amiibo/images/icon_02d90001-01c80502.png differ diff --git a/assets/amiibo/images/icon_02da0001-01330502.png b/assets/amiibo/images/icon_02da0001-01330502.png new file mode 100644 index 000000000..41710be35 Binary files /dev/null and b/assets/amiibo/images/icon_02da0001-01330502.png differ diff --git a/assets/amiibo/images/icon_02db0001-005e0502.png b/assets/amiibo/images/icon_02db0001-005e0502.png new file mode 100644 index 000000000..9c275d06d Binary files /dev/null and b/assets/amiibo/images/icon_02db0001-005e0502.png differ diff --git a/assets/amiibo/images/icon_02dc0001-00be0502.png b/assets/amiibo/images/icon_02dc0001-00be0502.png new file mode 100644 index 000000000..3a6bd74e5 Binary files /dev/null and b/assets/amiibo/images/icon_02dc0001-00be0502.png differ diff --git a/assets/amiibo/images/icon_02dd0001-00ea0502.png b/assets/amiibo/images/icon_02dd0001-00ea0502.png new file mode 100644 index 000000000..14f97b6db Binary files /dev/null and b/assets/amiibo/images/icon_02dd0001-00ea0502.png differ diff --git a/assets/amiibo/images/icon_02de0001-009c0502.png b/assets/amiibo/images/icon_02de0001-009c0502.png new file mode 100644 index 000000000..36be4a260 Binary files /dev/null and b/assets/amiibo/images/icon_02de0001-009c0502.png differ diff --git a/assets/amiibo/images/icon_02df0001-01910502.png b/assets/amiibo/images/icon_02df0001-01910502.png new file mode 100644 index 000000000..e5dec7031 Binary files /dev/null and b/assets/amiibo/images/icon_02df0001-01910502.png differ diff --git a/assets/amiibo/images/icon_02e00101-031d0502.png b/assets/amiibo/images/icon_02e00101-031d0502.png new file mode 100644 index 000000000..5433fe297 Binary files /dev/null and b/assets/amiibo/images/icon_02e00101-031d0502.png differ diff --git a/assets/amiibo/images/icon_02ea0001-01800502.png b/assets/amiibo/images/icon_02ea0001-01800502.png new file mode 100644 index 000000000..5a267856d Binary files /dev/null and b/assets/amiibo/images/icon_02ea0001-01800502.png differ diff --git a/assets/amiibo/images/icon_02ea0001-01d50502.png b/assets/amiibo/images/icon_02ea0001-01d50502.png new file mode 100644 index 000000000..4c0a9820a Binary files /dev/null and b/assets/amiibo/images/icon_02ea0001-01d50502.png differ diff --git a/assets/amiibo/images/icon_02eb0001-00de0502.png b/assets/amiibo/images/icon_02eb0001-00de0502.png new file mode 100644 index 000000000..3ebf34fb1 Binary files /dev/null and b/assets/amiibo/images/icon_02eb0001-00de0502.png differ diff --git a/assets/amiibo/images/icon_02ec0001-01c40502.png b/assets/amiibo/images/icon_02ec0001-01c40502.png new file mode 100644 index 000000000..a999e5720 Binary files /dev/null and b/assets/amiibo/images/icon_02ec0001-01c40502.png differ diff --git a/assets/amiibo/images/icon_02ed0001-015a0502.png b/assets/amiibo/images/icon_02ed0001-015a0502.png new file mode 100644 index 000000000..cb5991f34 Binary files /dev/null and b/assets/amiibo/images/icon_02ed0001-015a0502.png differ diff --git a/assets/amiibo/images/icon_02ee0001-01990502.png b/assets/amiibo/images/icon_02ee0001-01990502.png new file mode 100644 index 000000000..e38d197c8 Binary files /dev/null and b/assets/amiibo/images/icon_02ee0001-01990502.png differ diff --git a/assets/amiibo/images/icon_02ef0001-00580502.png b/assets/amiibo/images/icon_02ef0001-00580502.png new file mode 100644 index 000000000..17eb8cbe0 Binary files /dev/null and b/assets/amiibo/images/icon_02ef0001-00580502.png differ diff --git a/assets/amiibo/images/icon_02f00001-00a70502.png b/assets/amiibo/images/icon_02f00001-00a70502.png new file mode 100644 index 000000000..bcdb3ec80 Binary files /dev/null and b/assets/amiibo/images/icon_02f00001-00a70502.png differ diff --git a/assets/amiibo/images/icon_02f10001-01450502.png b/assets/amiibo/images/icon_02f10001-01450502.png new file mode 100644 index 000000000..9db98f772 Binary files /dev/null and b/assets/amiibo/images/icon_02f10001-01450502.png differ diff --git a/assets/amiibo/images/icon_02f20001-00cc0502.png b/assets/amiibo/images/icon_02f20001-00cc0502.png new file mode 100644 index 000000000..172e7641a Binary files /dev/null and b/assets/amiibo/images/icon_02f20001-00cc0502.png differ diff --git a/assets/amiibo/images/icon_02f30001-02f90502.png b/assets/amiibo/images/icon_02f30001-02f90502.png new file mode 100644 index 000000000..d13d3ec6a Binary files /dev/null and b/assets/amiibo/images/icon_02f30001-02f90502.png differ diff --git a/assets/amiibo/images/icon_02f40001-03050502.png b/assets/amiibo/images/icon_02f40001-03050502.png new file mode 100644 index 000000000..029075d2c Binary files /dev/null and b/assets/amiibo/images/icon_02f40001-03050502.png differ diff --git a/assets/amiibo/images/icon_02f80001-01380502.png b/assets/amiibo/images/icon_02f80001-01380502.png new file mode 100644 index 000000000..971ce7bad Binary files /dev/null and b/assets/amiibo/images/icon_02f80001-01380502.png differ diff --git a/assets/amiibo/images/icon_02f90001-01020502.png b/assets/amiibo/images/icon_02f90001-01020502.png new file mode 100644 index 000000000..d21d32053 Binary files /dev/null and b/assets/amiibo/images/icon_02f90001-01020502.png differ diff --git a/assets/amiibo/images/icon_02fa0001-00970502.png b/assets/amiibo/images/icon_02fa0001-00970502.png new file mode 100644 index 000000000..f0e5d0f1e Binary files /dev/null and b/assets/amiibo/images/icon_02fa0001-00970502.png differ diff --git a/assets/amiibo/images/icon_02fb0001-00900502.png b/assets/amiibo/images/icon_02fb0001-00900502.png new file mode 100644 index 000000000..0834b1e52 Binary files /dev/null and b/assets/amiibo/images/icon_02fb0001-00900502.png differ diff --git a/assets/amiibo/images/icon_02fc0001-018f0502.png b/assets/amiibo/images/icon_02fc0001-018f0502.png new file mode 100644 index 000000000..016a029f3 Binary files /dev/null and b/assets/amiibo/images/icon_02fc0001-018f0502.png differ diff --git a/assets/amiibo/images/icon_03070001-00640502.png b/assets/amiibo/images/icon_03070001-00640502.png new file mode 100644 index 000000000..93c08298b Binary files /dev/null and b/assets/amiibo/images/icon_03070001-00640502.png differ diff --git a/assets/amiibo/images/icon_03080001-014d0502.png b/assets/amiibo/images/icon_03080001-014d0502.png new file mode 100644 index 000000000..1171a607e Binary files /dev/null and b/assets/amiibo/images/icon_03080001-014d0502.png differ diff --git a/assets/amiibo/images/icon_03090001-00c60502.png b/assets/amiibo/images/icon_03090001-00c60502.png new file mode 100644 index 000000000..a47eebcf4 Binary files /dev/null and b/assets/amiibo/images/icon_03090001-00c60502.png differ diff --git a/assets/amiibo/images/icon_030a0001-01c70502.png b/assets/amiibo/images/icon_030a0001-01c70502.png new file mode 100644 index 000000000..547aa7125 Binary files /dev/null and b/assets/amiibo/images/icon_030a0001-01c70502.png differ diff --git a/assets/amiibo/images/icon_030b0001-00790502.png b/assets/amiibo/images/icon_030b0001-00790502.png new file mode 100644 index 000000000..8fec4500d Binary files /dev/null and b/assets/amiibo/images/icon_030b0001-00790502.png differ diff --git a/assets/amiibo/images/icon_030c0001-01b80502.png b/assets/amiibo/images/icon_030c0001-01b80502.png new file mode 100644 index 000000000..6ba27c7c8 Binary files /dev/null and b/assets/amiibo/images/icon_030c0001-01b80502.png differ diff --git a/assets/amiibo/images/icon_030d0001-01840502.png b/assets/amiibo/images/icon_030d0001-01840502.png new file mode 100644 index 000000000..f7c81b472 Binary files /dev/null and b/assets/amiibo/images/icon_030d0001-01840502.png differ diff --git a/assets/amiibo/images/icon_030e0001-012f0502.png b/assets/amiibo/images/icon_030e0001-012f0502.png new file mode 100644 index 000000000..0a03741e5 Binary files /dev/null and b/assets/amiibo/images/icon_030e0001-012f0502.png differ diff --git a/assets/amiibo/images/icon_030f0001-016d0502.png b/assets/amiibo/images/icon_030f0001-016d0502.png new file mode 100644 index 000000000..562aec36f Binary files /dev/null and b/assets/amiibo/images/icon_030f0001-016d0502.png differ diff --git a/assets/amiibo/images/icon_03100001-00f80502.png b/assets/amiibo/images/icon_03100001-00f80502.png new file mode 100644 index 000000000..e6bd987e4 Binary files /dev/null and b/assets/amiibo/images/icon_03100001-00f80502.png differ diff --git a/assets/amiibo/images/icon_03110001-00d60502.png b/assets/amiibo/images/icon_03110001-00d60502.png new file mode 100644 index 000000000..5075a1ff6 Binary files /dev/null and b/assets/amiibo/images/icon_03110001-00d60502.png differ diff --git a/assets/amiibo/images/icon_03120001-03090502.png b/assets/amiibo/images/icon_03120001-03090502.png new file mode 100644 index 000000000..d7075e5e1 Binary files /dev/null and b/assets/amiibo/images/icon_03120001-03090502.png differ diff --git a/assets/amiibo/images/icon_03130001-01210502.png b/assets/amiibo/images/icon_03130001-01210502.png new file mode 100644 index 000000000..272cd46c1 Binary files /dev/null and b/assets/amiibo/images/icon_03130001-01210502.png differ diff --git a/assets/amiibo/images/icon_03140001-02f40502.png b/assets/amiibo/images/icon_03140001-02f40502.png new file mode 100644 index 000000000..6eb1f94f1 Binary files /dev/null and b/assets/amiibo/images/icon_03140001-02f40502.png differ diff --git a/assets/amiibo/images/icon_03160001-01c00502.png b/assets/amiibo/images/icon_03160001-01c00502.png new file mode 100644 index 000000000..f9098de66 Binary files /dev/null and b/assets/amiibo/images/icon_03160001-01c00502.png differ diff --git a/assets/amiibo/images/icon_03170001-00a60502.png b/assets/amiibo/images/icon_03170001-00a60502.png new file mode 100644 index 000000000..bad1e9869 Binary files /dev/null and b/assets/amiibo/images/icon_03170001-00a60502.png differ diff --git a/assets/amiibo/images/icon_03180001-006c0502.png b/assets/amiibo/images/icon_03180001-006c0502.png new file mode 100644 index 000000000..da154406c Binary files /dev/null and b/assets/amiibo/images/icon_03180001-006c0502.png differ diff --git a/assets/amiibo/images/icon_03230001-00760502.png b/assets/amiibo/images/icon_03230001-00760502.png new file mode 100644 index 000000000..48414da5e Binary files /dev/null and b/assets/amiibo/images/icon_03230001-00760502.png differ diff --git a/assets/amiibo/images/icon_03240001-01890502.png b/assets/amiibo/images/icon_03240001-01890502.png new file mode 100644 index 000000000..e1462000f Binary files /dev/null and b/assets/amiibo/images/icon_03240001-01890502.png differ diff --git a/assets/amiibo/images/icon_03250001-010a0502.png b/assets/amiibo/images/icon_03250001-010a0502.png new file mode 100644 index 000000000..6f99a0539 Binary files /dev/null and b/assets/amiibo/images/icon_03250001-010a0502.png differ diff --git a/assets/amiibo/images/icon_03260001-01390502.png b/assets/amiibo/images/icon_03260001-01390502.png new file mode 100644 index 000000000..2ef5b99a8 Binary files /dev/null and b/assets/amiibo/images/icon_03260001-01390502.png differ diff --git a/assets/amiibo/images/icon_03270001-01c30502.png b/assets/amiibo/images/icon_03270001-01c30502.png new file mode 100644 index 000000000..07a18b1ac Binary files /dev/null and b/assets/amiibo/images/icon_03270001-01c30502.png differ diff --git a/assets/amiibo/images/icon_03280001-02eb0502.png b/assets/amiibo/images/icon_03280001-02eb0502.png new file mode 100644 index 000000000..e1041ad62 Binary files /dev/null and b/assets/amiibo/images/icon_03280001-02eb0502.png differ diff --git a/assets/amiibo/images/icon_03290001-009d0502.png b/assets/amiibo/images/icon_03290001-009d0502.png new file mode 100644 index 000000000..9dc2efb62 Binary files /dev/null and b/assets/amiibo/images/icon_03290001-009d0502.png differ diff --git a/assets/amiibo/images/icon_032a0001-03070502.png b/assets/amiibo/images/icon_032a0001-03070502.png new file mode 100644 index 000000000..d66af0365 Binary files /dev/null and b/assets/amiibo/images/icon_032a0001-03070502.png differ diff --git a/assets/amiibo/images/icon_032c0001-01480502.png b/assets/amiibo/images/icon_032c0001-01480502.png new file mode 100644 index 000000000..f104dd4ca Binary files /dev/null and b/assets/amiibo/images/icon_032c0001-01480502.png differ diff --git a/assets/amiibo/images/icon_032d0001-00bc0502.png b/assets/amiibo/images/icon_032d0001-00bc0502.png new file mode 100644 index 000000000..2ecb354fc Binary files /dev/null and b/assets/amiibo/images/icon_032d0001-00bc0502.png differ diff --git a/assets/amiibo/images/icon_032e0101-031c0502.png b/assets/amiibo/images/icon_032e0101-031c0502.png new file mode 100644 index 000000000..322986fa5 Binary files /dev/null and b/assets/amiibo/images/icon_032e0101-031c0502.png differ diff --git a/assets/amiibo/images/icon_03380001-011d0502.png b/assets/amiibo/images/icon_03380001-011d0502.png new file mode 100644 index 000000000..11061b7e5 Binary files /dev/null and b/assets/amiibo/images/icon_03380001-011d0502.png differ diff --git a/assets/amiibo/images/icon_03390001-01b10502.png b/assets/amiibo/images/icon_03390001-01b10502.png new file mode 100644 index 000000000..030259000 Binary files /dev/null and b/assets/amiibo/images/icon_03390001-01b10502.png differ diff --git a/assets/amiibo/images/icon_033a0001-01cc0502.png b/assets/amiibo/images/icon_033a0001-01cc0502.png new file mode 100644 index 000000000..352a20851 Binary files /dev/null and b/assets/amiibo/images/icon_033a0001-01cc0502.png differ diff --git a/assets/amiibo/images/icon_033b0001-00fa0502.png b/assets/amiibo/images/icon_033b0001-00fa0502.png new file mode 100644 index 000000000..ac370dd6f Binary files /dev/null and b/assets/amiibo/images/icon_033b0001-00fa0502.png differ diff --git a/assets/amiibo/images/icon_033c0001-01000502.png b/assets/amiibo/images/icon_033c0001-01000502.png new file mode 100644 index 000000000..cc28dc6b2 Binary files /dev/null and b/assets/amiibo/images/icon_033c0001-01000502.png differ diff --git a/assets/amiibo/images/icon_033d0001-013a0502.png b/assets/amiibo/images/icon_033d0001-013a0502.png new file mode 100644 index 000000000..cf23f8b8c Binary files /dev/null and b/assets/amiibo/images/icon_033d0001-013a0502.png differ diff --git a/assets/amiibo/images/icon_033e0001-01a20502.png b/assets/amiibo/images/icon_033e0001-01a20502.png new file mode 100644 index 000000000..ae87a860a Binary files /dev/null and b/assets/amiibo/images/icon_033e0001-01a20502.png differ diff --git a/assets/amiibo/images/icon_033f0001-008f0502.png b/assets/amiibo/images/icon_033f0001-008f0502.png new file mode 100644 index 000000000..3d9ebcb22 Binary files /dev/null and b/assets/amiibo/images/icon_033f0001-008f0502.png differ diff --git a/assets/amiibo/images/icon_03410001-030e0502.png b/assets/amiibo/images/icon_03410001-030e0502.png new file mode 100644 index 000000000..c1b04a37a Binary files /dev/null and b/assets/amiibo/images/icon_03410001-030e0502.png differ diff --git a/assets/amiibo/images/icon_03420001-01280502.png b/assets/amiibo/images/icon_03420001-01280502.png new file mode 100644 index 000000000..86296d34d Binary files /dev/null and b/assets/amiibo/images/icon_03420001-01280502.png differ diff --git a/assets/amiibo/images/icon_03430001-02ef0502.png b/assets/amiibo/images/icon_03430001-02ef0502.png new file mode 100644 index 000000000..15bc190cc Binary files /dev/null and b/assets/amiibo/images/icon_03430001-02ef0502.png differ diff --git a/assets/amiibo/images/icon_03440001-00c50502.png b/assets/amiibo/images/icon_03440001-00c50502.png new file mode 100644 index 000000000..910935482 Binary files /dev/null and b/assets/amiibo/images/icon_03440001-00c50502.png differ diff --git a/assets/amiibo/images/icon_03450001-005f0502.png b/assets/amiibo/images/icon_03450001-005f0502.png new file mode 100644 index 000000000..47b7d63d3 Binary files /dev/null and b/assets/amiibo/images/icon_03450001-005f0502.png differ diff --git a/assets/amiibo/images/icon_03470001-03020502.png b/assets/amiibo/images/icon_03470001-03020502.png new file mode 100644 index 000000000..177f635e9 Binary files /dev/null and b/assets/amiibo/images/icon_03470001-03020502.png differ diff --git a/assets/amiibo/images/icon_03480001-006b0502.png b/assets/amiibo/images/icon_03480001-006b0502.png new file mode 100644 index 000000000..280be9772 Binary files /dev/null and b/assets/amiibo/images/icon_03480001-006b0502.png differ diff --git a/assets/amiibo/images/icon_03490001-018d0502.png b/assets/amiibo/images/icon_03490001-018d0502.png new file mode 100644 index 000000000..4588dcd51 Binary files /dev/null and b/assets/amiibo/images/icon_03490001-018d0502.png differ diff --git a/assets/amiibo/images/icon_034a0001-01430502.png b/assets/amiibo/images/icon_034a0001-01430502.png new file mode 100644 index 000000000..207e31c7d Binary files /dev/null and b/assets/amiibo/images/icon_034a0001-01430502.png differ diff --git a/assets/amiibo/images/icon_034b0001-009f0502.png b/assets/amiibo/images/icon_034b0001-009f0502.png new file mode 100644 index 000000000..d2372fe58 Binary files /dev/null and b/assets/amiibo/images/icon_034b0001-009f0502.png differ diff --git a/assets/amiibo/images/icon_03560001-01350502.png b/assets/amiibo/images/icon_03560001-01350502.png new file mode 100644 index 000000000..91bab9566 Binary files /dev/null and b/assets/amiibo/images/icon_03560001-01350502.png differ diff --git a/assets/amiibo/images/icon_03570001-00eb0502.png b/assets/amiibo/images/icon_03570001-00eb0502.png new file mode 100644 index 000000000..18231d6a1 Binary files /dev/null and b/assets/amiibo/images/icon_03570001-00eb0502.png differ diff --git a/assets/amiibo/images/icon_03580001-02fa0502.png b/assets/amiibo/images/icon_03580001-02fa0502.png new file mode 100644 index 000000000..51b60e18b Binary files /dev/null and b/assets/amiibo/images/icon_03580001-02fa0502.png differ diff --git a/assets/amiibo/images/icon_035a0001-00850502.png b/assets/amiibo/images/icon_035a0001-00850502.png new file mode 100644 index 000000000..caff771e8 Binary files /dev/null and b/assets/amiibo/images/icon_035a0001-00850502.png differ diff --git a/assets/amiibo/images/icon_035c0001-01290502.png b/assets/amiibo/images/icon_035c0001-01290502.png new file mode 100644 index 000000000..36a08c4fe Binary files /dev/null and b/assets/amiibo/images/icon_035c0001-01290502.png differ diff --git a/assets/amiibo/images/icon_035d0001-00c90502.png b/assets/amiibo/images/icon_035d0001-00c90502.png new file mode 100644 index 000000000..1e3eb5fae Binary files /dev/null and b/assets/amiibo/images/icon_035d0001-00c90502.png differ diff --git a/assets/amiibo/images/icon_035e0001-018e0502.png b/assets/amiibo/images/icon_035e0001-018e0502.png new file mode 100644 index 000000000..458379e12 Binary files /dev/null and b/assets/amiibo/images/icon_035e0001-018e0502.png differ diff --git a/assets/amiibo/images/icon_03690001-00d30502.png b/assets/amiibo/images/icon_03690001-00d30502.png new file mode 100644 index 000000000..dfe73c64b Binary files /dev/null and b/assets/amiibo/images/icon_03690001-00d30502.png differ diff --git a/assets/amiibo/images/icon_036a0001-019d0502.png b/assets/amiibo/images/icon_036a0001-019d0502.png new file mode 100644 index 000000000..28ea32165 Binary files /dev/null and b/assets/amiibo/images/icon_036a0001-019d0502.png differ diff --git a/assets/amiibo/images/icon_036b0001-018b0502.png b/assets/amiibo/images/icon_036b0001-018b0502.png new file mode 100644 index 000000000..8e6d1ae2a Binary files /dev/null and b/assets/amiibo/images/icon_036b0001-018b0502.png differ diff --git a/assets/amiibo/images/icon_036d0001-03040502.png b/assets/amiibo/images/icon_036d0001-03040502.png new file mode 100644 index 000000000..e7f92cd53 Binary files /dev/null and b/assets/amiibo/images/icon_036d0001-03040502.png differ diff --git a/assets/amiibo/images/icon_036e0001-02fb0502.png b/assets/amiibo/images/icon_036e0001-02fb0502.png new file mode 100644 index 000000000..4faaef80d Binary files /dev/null and b/assets/amiibo/images/icon_036e0001-02fb0502.png differ diff --git a/assets/amiibo/images/icon_03700001-015d0502.png b/assets/amiibo/images/icon_03700001-015d0502.png new file mode 100644 index 000000000..6ad05a10b Binary files /dev/null and b/assets/amiibo/images/icon_03700001-015d0502.png differ diff --git a/assets/amiibo/images/icon_03710001-005c0502.png b/assets/amiibo/images/icon_03710001-005c0502.png new file mode 100644 index 000000000..03f3d8c71 Binary files /dev/null and b/assets/amiibo/images/icon_03710001-005c0502.png differ diff --git a/assets/amiibo/images/icon_03720001-010b0502.png b/assets/amiibo/images/icon_03720001-010b0502.png new file mode 100644 index 000000000..e3fb76304 Binary files /dev/null and b/assets/amiibo/images/icon_03720001-010b0502.png differ diff --git a/assets/amiibo/images/icon_03730001-01340502.png b/assets/amiibo/images/icon_03730001-01340502.png new file mode 100644 index 000000000..14909d468 Binary files /dev/null and b/assets/amiibo/images/icon_03730001-01340502.png differ diff --git a/assets/amiibo/images/icon_03740101-03190502.png b/assets/amiibo/images/icon_03740101-03190502.png new file mode 100644 index 000000000..e83cf5925 Binary files /dev/null and b/assets/amiibo/images/icon_03740101-03190502.png differ diff --git a/assets/amiibo/images/icon_037e0001-01560502.png b/assets/amiibo/images/icon_037e0001-01560502.png new file mode 100644 index 000000000..fdbe3f50b Binary files /dev/null and b/assets/amiibo/images/icon_037e0001-01560502.png differ diff --git a/assets/amiibo/images/icon_037f0001-01aa0502.png b/assets/amiibo/images/icon_037f0001-01aa0502.png new file mode 100644 index 000000000..b36e5eb3d Binary files /dev/null and b/assets/amiibo/images/icon_037f0001-01aa0502.png differ diff --git a/assets/amiibo/images/icon_03800001-01870502.png b/assets/amiibo/images/icon_03800001-01870502.png new file mode 100644 index 000000000..0c73368ed Binary files /dev/null and b/assets/amiibo/images/icon_03800001-01870502.png differ diff --git a/assets/amiibo/images/icon_03810001-00d50502.png b/assets/amiibo/images/icon_03810001-00d50502.png new file mode 100644 index 000000000..eeb9db20c Binary files /dev/null and b/assets/amiibo/images/icon_03810001-00d50502.png differ diff --git a/assets/amiibo/images/icon_03820001-016b0502.png b/assets/amiibo/images/icon_03820001-016b0502.png new file mode 100644 index 000000000..b53577381 Binary files /dev/null and b/assets/amiibo/images/icon_03820001-016b0502.png differ diff --git a/assets/amiibo/images/icon_03830001-009b0502.png b/assets/amiibo/images/icon_03830001-009b0502.png new file mode 100644 index 000000000..21f77c24c Binary files /dev/null and b/assets/amiibo/images/icon_03830001-009b0502.png differ diff --git a/assets/amiibo/images/icon_03840001-00860502.png b/assets/amiibo/images/icon_03840001-00860502.png new file mode 100644 index 000000000..fb98b0086 Binary files /dev/null and b/assets/amiibo/images/icon_03840001-00860502.png differ diff --git a/assets/amiibo/images/icon_03850001-01060502.png b/assets/amiibo/images/icon_03850001-01060502.png new file mode 100644 index 000000000..5f306ef46 Binary files /dev/null and b/assets/amiibo/images/icon_03850001-01060502.png differ diff --git a/assets/amiibo/images/icon_03900001-01850502.png b/assets/amiibo/images/icon_03900001-01850502.png new file mode 100644 index 000000000..5118b7d58 Binary files /dev/null and b/assets/amiibo/images/icon_03900001-01850502.png differ diff --git a/assets/amiibo/images/icon_03920001-01270502.png b/assets/amiibo/images/icon_03920001-01270502.png new file mode 100644 index 000000000..a57a9090b Binary files /dev/null and b/assets/amiibo/images/icon_03920001-01270502.png differ diff --git a/assets/amiibo/images/icon_03930001-00a00502.png b/assets/amiibo/images/icon_03930001-00a00502.png new file mode 100644 index 000000000..0d1b2b0d9 Binary files /dev/null and b/assets/amiibo/images/icon_03930001-00a00502.png differ diff --git a/assets/amiibo/images/icon_03940001-00890502.png b/assets/amiibo/images/icon_03940001-00890502.png new file mode 100644 index 000000000..f39909723 Binary files /dev/null and b/assets/amiibo/images/icon_03940001-00890502.png differ diff --git a/assets/amiibo/images/icon_03950001-02fc0502.png b/assets/amiibo/images/icon_03950001-02fc0502.png new file mode 100644 index 000000000..4dbb794f2 Binary files /dev/null and b/assets/amiibo/images/icon_03950001-02fc0502.png differ diff --git a/assets/amiibo/images/icon_03980001-00bf0502.png b/assets/amiibo/images/icon_03980001-00bf0502.png new file mode 100644 index 000000000..ff4abd930 Binary files /dev/null and b/assets/amiibo/images/icon_03980001-00bf0502.png differ diff --git a/assets/amiibo/images/icon_03990001-01c20502.png b/assets/amiibo/images/icon_03990001-01c20502.png new file mode 100644 index 000000000..591d765bf Binary files /dev/null and b/assets/amiibo/images/icon_03990001-01c20502.png differ diff --git a/assets/amiibo/images/icon_03a40001-014f0502.png b/assets/amiibo/images/icon_03a40001-014f0502.png new file mode 100644 index 000000000..8d994aba0 Binary files /dev/null and b/assets/amiibo/images/icon_03a40001-014f0502.png differ diff --git a/assets/amiibo/images/icon_03a50001-015b0502.png b/assets/amiibo/images/icon_03a50001-015b0502.png new file mode 100644 index 000000000..6a7920391 Binary files /dev/null and b/assets/amiibo/images/icon_03a50001-015b0502.png differ diff --git a/assets/amiibo/images/icon_03a60001-00c80502.png b/assets/amiibo/images/icon_03a60001-00c80502.png new file mode 100644 index 000000000..bf5eabed8 Binary files /dev/null and b/assets/amiibo/images/icon_03a60001-00c80502.png differ diff --git a/assets/amiibo/images/icon_03a70001-01a10502.png b/assets/amiibo/images/icon_03a70001-01a10502.png new file mode 100644 index 000000000..08ed537e6 Binary files /dev/null and b/assets/amiibo/images/icon_03a70001-01a10502.png differ diff --git a/assets/amiibo/images/icon_03a80001-00910502.png b/assets/amiibo/images/icon_03a80001-00910502.png new file mode 100644 index 000000000..30f550e72 Binary files /dev/null and b/assets/amiibo/images/icon_03a80001-00910502.png differ diff --git a/assets/amiibo/images/icon_03a90001-00710502.png b/assets/amiibo/images/icon_03a90001-00710502.png new file mode 100644 index 000000000..d944f0375 Binary files /dev/null and b/assets/amiibo/images/icon_03a90001-00710502.png differ diff --git a/assets/amiibo/images/icon_03aa0001-00e60502.png b/assets/amiibo/images/icon_03aa0001-00e60502.png new file mode 100644 index 000000000..50cdff012 Binary files /dev/null and b/assets/amiibo/images/icon_03aa0001-00e60502.png differ diff --git a/assets/amiibo/images/icon_03ab0001-03160502.png b/assets/amiibo/images/icon_03ab0001-03160502.png new file mode 100644 index 000000000..3d63e2ca0 Binary files /dev/null and b/assets/amiibo/images/icon_03ab0001-03160502.png differ diff --git a/assets/amiibo/images/icon_03ac0001-01880502.png b/assets/amiibo/images/icon_03ac0001-01880502.png new file mode 100644 index 000000000..90341b5b9 Binary files /dev/null and b/assets/amiibo/images/icon_03ac0001-01880502.png differ diff --git a/assets/amiibo/images/icon_03ad0001-01b20502.png b/assets/amiibo/images/icon_03ad0001-01b20502.png new file mode 100644 index 000000000..5c144b07f Binary files /dev/null and b/assets/amiibo/images/icon_03ad0001-01b20502.png differ diff --git a/assets/amiibo/images/icon_03ae0001-00870502.png b/assets/amiibo/images/icon_03ae0001-00870502.png new file mode 100644 index 000000000..765d97b33 Binary files /dev/null and b/assets/amiibo/images/icon_03ae0001-00870502.png differ diff --git a/assets/amiibo/images/icon_03af0001-012c0502.png b/assets/amiibo/images/icon_03af0001-012c0502.png new file mode 100644 index 000000000..23de0e475 Binary files /dev/null and b/assets/amiibo/images/icon_03af0001-012c0502.png differ diff --git a/assets/amiibo/images/icon_03b00001-01a90502.png b/assets/amiibo/images/icon_03b00001-01a90502.png new file mode 100644 index 000000000..585d160fa Binary files /dev/null and b/assets/amiibo/images/icon_03b00001-01a90502.png differ diff --git a/assets/amiibo/images/icon_03b10001-00f00502.png b/assets/amiibo/images/icon_03b10001-00f00502.png new file mode 100644 index 000000000..9a9e5753a Binary files /dev/null and b/assets/amiibo/images/icon_03b10001-00f00502.png differ diff --git a/assets/amiibo/images/icon_03bc0001-008a0502.png b/assets/amiibo/images/icon_03bc0001-008a0502.png new file mode 100644 index 000000000..e6961eb13 Binary files /dev/null and b/assets/amiibo/images/icon_03bc0001-008a0502.png differ diff --git a/assets/amiibo/images/icon_03bd0001-00f90502.png b/assets/amiibo/images/icon_03bd0001-00f90502.png new file mode 100644 index 000000000..6a8dd6768 Binary files /dev/null and b/assets/amiibo/images/icon_03bd0001-00f90502.png differ diff --git a/assets/amiibo/images/icon_03be0001-01980502.png b/assets/amiibo/images/icon_03be0001-01980502.png new file mode 100644 index 000000000..8c0007366 Binary files /dev/null and b/assets/amiibo/images/icon_03be0001-01980502.png differ diff --git a/assets/amiibo/images/icon_03bf0001-01bc0502.png b/assets/amiibo/images/icon_03bf0001-01bc0502.png new file mode 100644 index 000000000..270bf1180 Binary files /dev/null and b/assets/amiibo/images/icon_03bf0001-01bc0502.png differ diff --git a/assets/amiibo/images/icon_03c00001-03100502.png b/assets/amiibo/images/icon_03c00001-03100502.png new file mode 100644 index 000000000..67dabbb83 Binary files /dev/null and b/assets/amiibo/images/icon_03c00001-03100502.png differ diff --git a/assets/amiibo/images/icon_03c10001-00bb0502.png b/assets/amiibo/images/icon_03c10001-00bb0502.png new file mode 100644 index 000000000..20f70a119 Binary files /dev/null and b/assets/amiibo/images/icon_03c10001-00bb0502.png differ diff --git a/assets/amiibo/images/icon_03c40001-012b0502.png b/assets/amiibo/images/icon_03c40001-012b0502.png new file mode 100644 index 000000000..9ae17c709 Binary files /dev/null and b/assets/amiibo/images/icon_03c40001-012b0502.png differ diff --git a/assets/amiibo/images/icon_03c50001-015c0502.png b/assets/amiibo/images/icon_03c50001-015c0502.png new file mode 100644 index 000000000..dfe8385cd Binary files /dev/null and b/assets/amiibo/images/icon_03c50001-015c0502.png differ diff --git a/assets/amiibo/images/icon_03c60001-00930502.png b/assets/amiibo/images/icon_03c60001-00930502.png new file mode 100644 index 000000000..10095b607 Binary files /dev/null and b/assets/amiibo/images/icon_03c60001-00930502.png differ diff --git a/assets/amiibo/images/icon_03d10001-00c20502.png b/assets/amiibo/images/icon_03d10001-00c20502.png new file mode 100644 index 000000000..98de5e48f Binary files /dev/null and b/assets/amiibo/images/icon_03d10001-00c20502.png differ diff --git a/assets/amiibo/images/icon_03d20001-00e50502.png b/assets/amiibo/images/icon_03d20001-00e50502.png new file mode 100644 index 000000000..fe4bd1853 Binary files /dev/null and b/assets/amiibo/images/icon_03d20001-00e50502.png differ diff --git a/assets/amiibo/images/icon_03d30001-02f30502.png b/assets/amiibo/images/icon_03d30001-02f30502.png new file mode 100644 index 000000000..66ed073e3 Binary files /dev/null and b/assets/amiibo/images/icon_03d30001-02f30502.png differ diff --git a/assets/amiibo/images/icon_03d60001-01570502.png b/assets/amiibo/images/icon_03d60001-01570502.png new file mode 100644 index 000000000..b0e001da4 Binary files /dev/null and b/assets/amiibo/images/icon_03d60001-01570502.png differ diff --git a/assets/amiibo/images/icon_03d70001-01b40502.png b/assets/amiibo/images/icon_03d70001-01b40502.png new file mode 100644 index 000000000..6bf80eee1 Binary files /dev/null and b/assets/amiibo/images/icon_03d70001-01b40502.png differ diff --git a/assets/amiibo/images/icon_03d90001-01a50502.png b/assets/amiibo/images/icon_03d90001-01a50502.png new file mode 100644 index 000000000..7b6feae20 Binary files /dev/null and b/assets/amiibo/images/icon_03d90001-01a50502.png differ diff --git a/assets/amiibo/images/icon_03da0001-01510502.png b/assets/amiibo/images/icon_03da0001-01510502.png new file mode 100644 index 000000000..2b99f8760 Binary files /dev/null and b/assets/amiibo/images/icon_03da0001-01510502.png differ diff --git a/assets/amiibo/images/icon_03db0001-006d0502.png b/assets/amiibo/images/icon_03db0001-006d0502.png new file mode 100644 index 000000000..2251a5cc0 Binary files /dev/null and b/assets/amiibo/images/icon_03db0001-006d0502.png differ diff --git a/assets/amiibo/images/icon_03e60001-00ec0502.png b/assets/amiibo/images/icon_03e60001-00ec0502.png new file mode 100644 index 000000000..9dfad5926 Binary files /dev/null and b/assets/amiibo/images/icon_03e60001-00ec0502.png differ diff --git a/assets/amiibo/images/icon_03e70001-012a0502.png b/assets/amiibo/images/icon_03e70001-012a0502.png new file mode 100644 index 000000000..b075a3210 Binary files /dev/null and b/assets/amiibo/images/icon_03e70001-012a0502.png differ diff --git a/assets/amiibo/images/icon_03e80001-02f50502.png b/assets/amiibo/images/icon_03e80001-02f50502.png new file mode 100644 index 000000000..3dfeac4db Binary files /dev/null and b/assets/amiibo/images/icon_03e80001-02f50502.png differ diff --git a/assets/amiibo/images/icon_03ea0001-030b0502.png b/assets/amiibo/images/icon_03ea0001-030b0502.png new file mode 100644 index 000000000..2994374cd Binary files /dev/null and b/assets/amiibo/images/icon_03ea0001-030b0502.png differ diff --git a/assets/amiibo/images/icon_03ec0001-01830502.png b/assets/amiibo/images/icon_03ec0001-01830502.png new file mode 100644 index 000000000..f36a4c5d1 Binary files /dev/null and b/assets/amiibo/images/icon_03ec0001-01830502.png differ diff --git a/assets/amiibo/images/icon_03ed0001-01a30502.png b/assets/amiibo/images/icon_03ed0001-01a30502.png new file mode 100644 index 000000000..82e50fe0a Binary files /dev/null and b/assets/amiibo/images/icon_03ed0001-01a30502.png differ diff --git a/assets/amiibo/images/icon_03ee0001-008b0502.png b/assets/amiibo/images/icon_03ee0001-008b0502.png new file mode 100644 index 000000000..b147f180f Binary files /dev/null and b/assets/amiibo/images/icon_03ee0001-008b0502.png differ diff --git a/assets/amiibo/images/icon_03fa0001-00d00502.png b/assets/amiibo/images/icon_03fa0001-00d00502.png new file mode 100644 index 000000000..d36868c07 Binary files /dev/null and b/assets/amiibo/images/icon_03fa0001-00d00502.png differ diff --git a/assets/amiibo/images/icon_03fb0001-01cf0502.png b/assets/amiibo/images/icon_03fb0001-01cf0502.png new file mode 100644 index 000000000..51b844c62 Binary files /dev/null and b/assets/amiibo/images/icon_03fb0001-01cf0502.png differ diff --git a/assets/amiibo/images/icon_03fc0001-01470502.png b/assets/amiibo/images/icon_03fc0001-01470502.png new file mode 100644 index 000000000..a2e0d07cd Binary files /dev/null and b/assets/amiibo/images/icon_03fc0001-01470502.png differ diff --git a/assets/amiibo/images/icon_03fd0001-01580502.png b/assets/amiibo/images/icon_03fd0001-01580502.png new file mode 100644 index 000000000..e7260d1c5 Binary files /dev/null and b/assets/amiibo/images/icon_03fd0001-01580502.png differ diff --git a/assets/amiibo/images/icon_03fe0001-01a40502.png b/assets/amiibo/images/icon_03fe0001-01a40502.png new file mode 100644 index 000000000..2a453accd Binary files /dev/null and b/assets/amiibo/images/icon_03fe0001-01a40502.png differ diff --git a/assets/amiibo/images/icon_03ff0001-00f40502.png b/assets/amiibo/images/icon_03ff0001-00f40502.png new file mode 100644 index 000000000..5e96cc5fb Binary files /dev/null and b/assets/amiibo/images/icon_03ff0001-00f40502.png differ diff --git a/assets/amiibo/images/icon_04000001-006f0502.png b/assets/amiibo/images/icon_04000001-006f0502.png new file mode 100644 index 000000000..5339ed84e Binary files /dev/null and b/assets/amiibo/images/icon_04000001-006f0502.png differ diff --git a/assets/amiibo/images/icon_04010001-00660502.png b/assets/amiibo/images/icon_04010001-00660502.png new file mode 100644 index 000000000..01d2bf99f Binary files /dev/null and b/assets/amiibo/images/icon_04010001-00660502.png differ diff --git a/assets/amiibo/images/icon_040c0001-01590502.png b/assets/amiibo/images/icon_040c0001-01590502.png new file mode 100644 index 000000000..47cc39b9a Binary files /dev/null and b/assets/amiibo/images/icon_040c0001-01590502.png differ diff --git a/assets/amiibo/images/icon_040d0001-00780502.png b/assets/amiibo/images/icon_040d0001-00780502.png new file mode 100644 index 000000000..5bd30b5ad Binary files /dev/null and b/assets/amiibo/images/icon_040d0001-00780502.png differ diff --git a/assets/amiibo/images/icon_040e0001-00880502.png b/assets/amiibo/images/icon_040e0001-00880502.png new file mode 100644 index 000000000..49849c3cb Binary files /dev/null and b/assets/amiibo/images/icon_040e0001-00880502.png differ diff --git a/assets/amiibo/images/icon_040f0001-01500502.png b/assets/amiibo/images/icon_040f0001-01500502.png new file mode 100644 index 000000000..af18b2c22 Binary files /dev/null and b/assets/amiibo/images/icon_040f0001-01500502.png differ diff --git a/assets/amiibo/images/icon_04100001-007f0502.png b/assets/amiibo/images/icon_04100001-007f0502.png new file mode 100644 index 000000000..b7bb883f3 Binary files /dev/null and b/assets/amiibo/images/icon_04100001-007f0502.png differ diff --git a/assets/amiibo/images/icon_04110001-01ab0502.png b/assets/amiibo/images/icon_04110001-01ab0502.png new file mode 100644 index 000000000..9f058ab98 Binary files /dev/null and b/assets/amiibo/images/icon_04110001-01ab0502.png differ diff --git a/assets/amiibo/images/icon_04140001-030a0502.png b/assets/amiibo/images/icon_04140001-030a0502.png new file mode 100644 index 000000000..e3a2d3b67 Binary files /dev/null and b/assets/amiibo/images/icon_04140001-030a0502.png differ diff --git a/assets/amiibo/images/icon_04150001-01bb0502.png b/assets/amiibo/images/icon_04150001-01bb0502.png new file mode 100644 index 000000000..4ea1fcc40 Binary files /dev/null and b/assets/amiibo/images/icon_04150001-01bb0502.png differ diff --git a/assets/amiibo/images/icon_04160001-00fb0502.png b/assets/amiibo/images/icon_04160001-00fb0502.png new file mode 100644 index 000000000..55288c8af Binary files /dev/null and b/assets/amiibo/images/icon_04160001-00fb0502.png differ diff --git a/assets/amiibo/images/icon_04180001-00d80502.png b/assets/amiibo/images/icon_04180001-00d80502.png new file mode 100644 index 000000000..02940a384 Binary files /dev/null and b/assets/amiibo/images/icon_04180001-00d80502.png differ diff --git a/assets/amiibo/images/icon_041a0001-00e00502.png b/assets/amiibo/images/icon_041a0001-00e00502.png new file mode 100644 index 000000000..a100014ba Binary files /dev/null and b/assets/amiibo/images/icon_041a0001-00e00502.png differ diff --git a/assets/amiibo/images/icon_041b0001-00f10502.png b/assets/amiibo/images/icon_041b0001-00f10502.png new file mode 100644 index 000000000..371ed0838 Binary files /dev/null and b/assets/amiibo/images/icon_041b0001-00f10502.png differ diff --git a/assets/amiibo/images/icon_041c0001-01410502.png b/assets/amiibo/images/icon_041c0001-01410502.png new file mode 100644 index 000000000..f0f3241e9 Binary files /dev/null and b/assets/amiibo/images/icon_041c0001-01410502.png differ diff --git a/assets/amiibo/images/icon_041d0001-018a0502.png b/assets/amiibo/images/icon_041d0001-018a0502.png new file mode 100644 index 000000000..1009e18c4 Binary files /dev/null and b/assets/amiibo/images/icon_041d0001-018a0502.png differ diff --git a/assets/amiibo/images/icon_041e0001-015f0502.png b/assets/amiibo/images/icon_041e0001-015f0502.png new file mode 100644 index 000000000..a9d532763 Binary files /dev/null and b/assets/amiibo/images/icon_041e0001-015f0502.png differ diff --git a/assets/amiibo/images/icon_04290001-00700502.png b/assets/amiibo/images/icon_04290001-00700502.png new file mode 100644 index 000000000..b75f15fd4 Binary files /dev/null and b/assets/amiibo/images/icon_04290001-00700502.png differ diff --git a/assets/amiibo/images/icon_042a0001-012d0502.png b/assets/amiibo/images/icon_042a0001-012d0502.png new file mode 100644 index 000000000..d6b0901d9 Binary files /dev/null and b/assets/amiibo/images/icon_042a0001-012d0502.png differ diff --git a/assets/amiibo/images/icon_042b0001-01af0502.png b/assets/amiibo/images/icon_042b0001-01af0502.png new file mode 100644 index 000000000..568784816 Binary files /dev/null and b/assets/amiibo/images/icon_042b0001-01af0502.png differ diff --git a/assets/amiibo/images/icon_04360001-01940502.png b/assets/amiibo/images/icon_04360001-01940502.png new file mode 100644 index 000000000..dfe7ab602 Binary files /dev/null and b/assets/amiibo/images/icon_04360001-01940502.png differ diff --git a/assets/amiibo/images/icon_04370001-01050502.png b/assets/amiibo/images/icon_04370001-01050502.png new file mode 100644 index 000000000..b4105b6a8 Binary files /dev/null and b/assets/amiibo/images/icon_04370001-01050502.png differ diff --git a/assets/amiibo/images/icon_04380001-03000502.png b/assets/amiibo/images/icon_04380001-03000502.png new file mode 100644 index 000000000..1febdfad9 Binary files /dev/null and b/assets/amiibo/images/icon_04380001-03000502.png differ diff --git a/assets/amiibo/images/icon_04390001-03110502.png b/assets/amiibo/images/icon_04390001-03110502.png new file mode 100644 index 000000000..effd0143b Binary files /dev/null and b/assets/amiibo/images/icon_04390001-03110502.png differ diff --git a/assets/amiibo/images/icon_043b0001-03030502.png b/assets/amiibo/images/icon_043b0001-03030502.png new file mode 100644 index 000000000..11d4f5767 Binary files /dev/null and b/assets/amiibo/images/icon_043b0001-03030502.png differ diff --git a/assets/amiibo/images/icon_043c0001-01cb0502.png b/assets/amiibo/images/icon_043c0001-01cb0502.png new file mode 100644 index 000000000..732f4b31f Binary files /dev/null and b/assets/amiibo/images/icon_043c0001-01cb0502.png differ diff --git a/assets/amiibo/images/icon_043d0001-007c0502.png b/assets/amiibo/images/icon_043d0001-007c0502.png new file mode 100644 index 000000000..34e958ff7 Binary files /dev/null and b/assets/amiibo/images/icon_043d0001-007c0502.png differ diff --git a/assets/amiibo/images/icon_043e0001-01490502.png b/assets/amiibo/images/icon_043e0001-01490502.png new file mode 100644 index 000000000..9ba22b901 Binary files /dev/null and b/assets/amiibo/images/icon_043e0001-01490502.png differ diff --git a/assets/amiibo/images/icon_043f0001-01550502.png b/assets/amiibo/images/icon_043f0001-01550502.png new file mode 100644 index 000000000..4a672c317 Binary files /dev/null and b/assets/amiibo/images/icon_043f0001-01550502.png differ diff --git a/assets/amiibo/images/icon_04400001-00ca0502.png b/assets/amiibo/images/icon_04400001-00ca0502.png new file mode 100644 index 000000000..b0a8729c0 Binary files /dev/null and b/assets/amiibo/images/icon_04400001-00ca0502.png differ diff --git a/assets/amiibo/images/icon_044b0001-016c0502.png b/assets/amiibo/images/icon_044b0001-016c0502.png new file mode 100644 index 000000000..a9233d9e9 Binary files /dev/null and b/assets/amiibo/images/icon_044b0001-016c0502.png differ diff --git a/assets/amiibo/images/icon_044c0001-008e0502.png b/assets/amiibo/images/icon_044c0001-008e0502.png new file mode 100644 index 000000000..6c726a616 Binary files /dev/null and b/assets/amiibo/images/icon_044c0001-008e0502.png differ diff --git a/assets/amiibo/images/icon_044d0001-01930502.png b/assets/amiibo/images/icon_044d0001-01930502.png new file mode 100644 index 000000000..a71549608 Binary files /dev/null and b/assets/amiibo/images/icon_044d0001-01930502.png differ diff --git a/assets/amiibo/images/icon_044e0001-03150502.png b/assets/amiibo/images/icon_044e0001-03150502.png new file mode 100644 index 000000000..49141cc04 Binary files /dev/null and b/assets/amiibo/images/icon_044e0001-03150502.png differ diff --git a/assets/amiibo/images/icon_04500001-00cf0502.png b/assets/amiibo/images/icon_04500001-00cf0502.png new file mode 100644 index 000000000..13be22964 Binary files /dev/null and b/assets/amiibo/images/icon_04500001-00cf0502.png differ diff --git a/assets/amiibo/images/icon_04510001-015e0502.png b/assets/amiibo/images/icon_04510001-015e0502.png new file mode 100644 index 000000000..35e6e8e7d Binary files /dev/null and b/assets/amiibo/images/icon_04510001-015e0502.png differ diff --git a/assets/amiibo/images/icon_04520001-00730502.png b/assets/amiibo/images/icon_04520001-00730502.png new file mode 100644 index 000000000..3ed3c81bc Binary files /dev/null and b/assets/amiibo/images/icon_04520001-00730502.png differ diff --git a/assets/amiibo/images/icon_04530001-01040502.png b/assets/amiibo/images/icon_04530001-01040502.png new file mode 100644 index 000000000..eba854c14 Binary files /dev/null and b/assets/amiibo/images/icon_04530001-01040502.png differ diff --git a/assets/amiibo/images/icon_04540001-01ae0502.png b/assets/amiibo/images/icon_04540001-01ae0502.png new file mode 100644 index 000000000..b157f5b61 Binary files /dev/null and b/assets/amiibo/images/icon_04540001-01ae0502.png differ diff --git a/assets/amiibo/images/icon_045f0001-01a80502.png b/assets/amiibo/images/icon_045f0001-01a80502.png new file mode 100644 index 000000000..15f988762 Binary files /dev/null and b/assets/amiibo/images/icon_045f0001-01a80502.png differ diff --git a/assets/amiibo/images/icon_04600001-00a50502.png b/assets/amiibo/images/icon_04600001-00a50502.png new file mode 100644 index 000000000..8fa50c665 Binary files /dev/null and b/assets/amiibo/images/icon_04600001-00a50502.png differ diff --git a/assets/amiibo/images/icon_04610001-01610502.png b/assets/amiibo/images/icon_04610001-01610502.png new file mode 100644 index 000000000..a9a5c84ea Binary files /dev/null and b/assets/amiibo/images/icon_04610001-01610502.png differ diff --git a/assets/amiibo/images/icon_04620001-00f60502.png b/assets/amiibo/images/icon_04620001-00f60502.png new file mode 100644 index 000000000..731e551d4 Binary files /dev/null and b/assets/amiibo/images/icon_04620001-00f60502.png differ diff --git a/assets/amiibo/images/icon_04630001-01310502.png b/assets/amiibo/images/icon_04630001-01310502.png new file mode 100644 index 000000000..f9f65b68f Binary files /dev/null and b/assets/amiibo/images/icon_04630001-01310502.png differ diff --git a/assets/amiibo/images/icon_04640001-00c00502.png b/assets/amiibo/images/icon_04640001-00c00502.png new file mode 100644 index 000000000..4866ccf00 Binary files /dev/null and b/assets/amiibo/images/icon_04640001-00c00502.png differ diff --git a/assets/amiibo/images/icon_04650001-006e0502.png b/assets/amiibo/images/icon_04650001-006e0502.png new file mode 100644 index 000000000..ccf44f8fe Binary files /dev/null and b/assets/amiibo/images/icon_04650001-006e0502.png differ diff --git a/assets/amiibo/images/icon_04680001-02f20502.png b/assets/amiibo/images/icon_04680001-02f20502.png new file mode 100644 index 000000000..2a9400b03 Binary files /dev/null and b/assets/amiibo/images/icon_04680001-02f20502.png differ diff --git a/assets/amiibo/images/icon_04690001-01640502.png b/assets/amiibo/images/icon_04690001-01640502.png new file mode 100644 index 000000000..5d0c55a48 Binary files /dev/null and b/assets/amiibo/images/icon_04690001-01640502.png differ diff --git a/assets/amiibo/images/icon_046a0001-01d00502.png b/assets/amiibo/images/icon_046a0001-01d00502.png new file mode 100644 index 000000000..499ac6b4a Binary files /dev/null and b/assets/amiibo/images/icon_046a0001-01d00502.png differ diff --git a/assets/amiibo/images/icon_046b0001-01970502.png b/assets/amiibo/images/icon_046b0001-01970502.png new file mode 100644 index 000000000..11baad688 Binary files /dev/null and b/assets/amiibo/images/icon_046b0001-01970502.png differ diff --git a/assets/amiibo/images/icon_046c0001-008c0502.png b/assets/amiibo/images/icon_046c0001-008c0502.png new file mode 100644 index 000000000..d0c18d3ca Binary files /dev/null and b/assets/amiibo/images/icon_046c0001-008c0502.png differ diff --git a/assets/amiibo/images/icon_046d0001-00f30502.png b/assets/amiibo/images/icon_046d0001-00f30502.png new file mode 100644 index 000000000..3d52d9285 Binary files /dev/null and b/assets/amiibo/images/icon_046d0001-00f30502.png differ diff --git a/assets/amiibo/images/icon_04780001-01630502.png b/assets/amiibo/images/icon_04780001-01630502.png new file mode 100644 index 000000000..fc72e2691 Binary files /dev/null and b/assets/amiibo/images/icon_04780001-01630502.png differ diff --git a/assets/amiibo/images/icon_04790001-00920502.png b/assets/amiibo/images/icon_04790001-00920502.png new file mode 100644 index 000000000..d7d5bd169 Binary files /dev/null and b/assets/amiibo/images/icon_04790001-00920502.png differ diff --git a/assets/amiibo/images/icon_047a0001-00600502.png b/assets/amiibo/images/icon_047a0001-00600502.png new file mode 100644 index 000000000..b047d7ef3 Binary files /dev/null and b/assets/amiibo/images/icon_047a0001-00600502.png differ diff --git a/assets/amiibo/images/icon_047b0001-00f50502.png b/assets/amiibo/images/icon_047b0001-00f50502.png new file mode 100644 index 000000000..c7bf99573 Binary files /dev/null and b/assets/amiibo/images/icon_047b0001-00f50502.png differ diff --git a/assets/amiibo/images/icon_047c0001-01a00502.png b/assets/amiibo/images/icon_047c0001-01a00502.png new file mode 100644 index 000000000..29b0f6e45 Binary files /dev/null and b/assets/amiibo/images/icon_047c0001-01a00502.png differ diff --git a/assets/amiibo/images/icon_047d0001-012e0502.png b/assets/amiibo/images/icon_047d0001-012e0502.png new file mode 100644 index 000000000..d67d9926e Binary files /dev/null and b/assets/amiibo/images/icon_047d0001-012e0502.png differ diff --git a/assets/amiibo/images/icon_04800001-008d0502.png b/assets/amiibo/images/icon_04800001-008d0502.png new file mode 100644 index 000000000..80a4ea82d Binary files /dev/null and b/assets/amiibo/images/icon_04800001-008d0502.png differ diff --git a/assets/amiibo/images/icon_04810001-02f10502.png b/assets/amiibo/images/icon_04810001-02f10502.png new file mode 100644 index 000000000..51e1b6ed9 Binary files /dev/null and b/assets/amiibo/images/icon_04810001-02f10502.png differ diff --git a/assets/amiibo/images/icon_04820001-02fd0502.png b/assets/amiibo/images/icon_04820001-02fd0502.png new file mode 100644 index 000000000..911c35d00 Binary files /dev/null and b/assets/amiibo/images/icon_04820001-02fd0502.png differ diff --git a/assets/amiibo/images/icon_04830001-01b00502.png b/assets/amiibo/images/icon_04830001-01b00502.png new file mode 100644 index 000000000..cbd81bd4b Binary files /dev/null and b/assets/amiibo/images/icon_04830001-01b00502.png differ diff --git a/assets/amiibo/images/icon_04850001-014c0502.png b/assets/amiibo/images/icon_04850001-014c0502.png new file mode 100644 index 000000000..6bbb3dd03 Binary files /dev/null and b/assets/amiibo/images/icon_04850001-014c0502.png differ diff --git a/assets/amiibo/images/icon_04860001-00fc0502.png b/assets/amiibo/images/icon_04860001-00fc0502.png new file mode 100644 index 000000000..974999f4d Binary files /dev/null and b/assets/amiibo/images/icon_04860001-00fc0502.png differ diff --git a/assets/amiibo/images/icon_04870001-01bf0502.png b/assets/amiibo/images/icon_04870001-01bf0502.png new file mode 100644 index 000000000..afec0720a Binary files /dev/null and b/assets/amiibo/images/icon_04870001-01bf0502.png differ diff --git a/assets/amiibo/images/icon_04880001-00980502.png b/assets/amiibo/images/icon_04880001-00980502.png new file mode 100644 index 000000000..b7a96db41 Binary files /dev/null and b/assets/amiibo/images/icon_04880001-00980502.png differ diff --git a/assets/amiibo/images/icon_04890001-00ef0502.png b/assets/amiibo/images/icon_04890001-00ef0502.png new file mode 100644 index 000000000..5b6376b80 Binary files /dev/null and b/assets/amiibo/images/icon_04890001-00ef0502.png differ diff --git a/assets/amiibo/images/icon_04940001-009a0502.png b/assets/amiibo/images/icon_04940001-009a0502.png new file mode 100644 index 000000000..904e3c38f Binary files /dev/null and b/assets/amiibo/images/icon_04940001-009a0502.png differ diff --git a/assets/amiibo/images/icon_04950001-01920502.png b/assets/amiibo/images/icon_04950001-01920502.png new file mode 100644 index 000000000..b8e0bff4c Binary files /dev/null and b/assets/amiibo/images/icon_04950001-01920502.png differ diff --git a/assets/amiibo/images/icon_04960001-00d90502.png b/assets/amiibo/images/icon_04960001-00d90502.png new file mode 100644 index 000000000..706c7c38a Binary files /dev/null and b/assets/amiibo/images/icon_04960001-00d90502.png differ diff --git a/assets/amiibo/images/icon_04970001-007a0502.png b/assets/amiibo/images/icon_04970001-007a0502.png new file mode 100644 index 000000000..281b38190 Binary files /dev/null and b/assets/amiibo/images/icon_04970001-007a0502.png differ diff --git a/assets/amiibo/images/icon_04980001-014a0502.png b/assets/amiibo/images/icon_04980001-014a0502.png new file mode 100644 index 000000000..d6255e4d9 Binary files /dev/null and b/assets/amiibo/images/icon_04980001-014a0502.png differ diff --git a/assets/amiibo/images/icon_04990001-00df0502.png b/assets/amiibo/images/icon_04990001-00df0502.png new file mode 100644 index 000000000..d4fb987ce Binary files /dev/null and b/assets/amiibo/images/icon_04990001-00df0502.png differ diff --git a/assets/amiibo/images/icon_049a0001-014e0502.png b/assets/amiibo/images/icon_049a0001-014e0502.png new file mode 100644 index 000000000..7fee6f9bf Binary files /dev/null and b/assets/amiibo/images/icon_049a0001-014e0502.png differ diff --git a/assets/amiibo/images/icon_049b0001-00610502.png b/assets/amiibo/images/icon_049b0001-00610502.png new file mode 100644 index 000000000..23b46d064 Binary files /dev/null and b/assets/amiibo/images/icon_049b0001-00610502.png differ diff --git a/assets/amiibo/images/icon_049c0001-01400502.png b/assets/amiibo/images/icon_049c0001-01400502.png new file mode 100644 index 000000000..9074dc403 Binary files /dev/null and b/assets/amiibo/images/icon_049c0001-01400502.png differ diff --git a/assets/amiibo/images/icon_049d0001-00ed0502.png b/assets/amiibo/images/icon_049d0001-00ed0502.png new file mode 100644 index 000000000..cf18eb9aa Binary files /dev/null and b/assets/amiibo/images/icon_049d0001-00ed0502.png differ diff --git a/assets/amiibo/images/icon_049e0001-01b70502.png b/assets/amiibo/images/icon_049e0001-01b70502.png new file mode 100644 index 000000000..5bbb1ae02 Binary files /dev/null and b/assets/amiibo/images/icon_049e0001-01b70502.png differ diff --git a/assets/amiibo/images/icon_049f0001-03010502.png b/assets/amiibo/images/icon_049f0001-03010502.png new file mode 100644 index 000000000..b3b4417c6 Binary files /dev/null and b/assets/amiibo/images/icon_049f0001-03010502.png differ diff --git a/assets/amiibo/images/icon_04a00001-016e0502.png b/assets/amiibo/images/icon_04a00001-016e0502.png new file mode 100644 index 000000000..4cb4f0083 Binary files /dev/null and b/assets/amiibo/images/icon_04a00001-016e0502.png differ diff --git a/assets/amiibo/images/icon_04a10001-016f0502.png b/assets/amiibo/images/icon_04a10001-016f0502.png new file mode 100644 index 000000000..513f2da6c Binary files /dev/null and b/assets/amiibo/images/icon_04a10001-016f0502.png differ diff --git a/assets/amiibo/images/icon_04a20001-02e80502.png b/assets/amiibo/images/icon_04a20001-02e80502.png new file mode 100644 index 000000000..b5947612d Binary files /dev/null and b/assets/amiibo/images/icon_04a20001-02e80502.png differ diff --git a/assets/amiibo/images/icon_04a30001-01c90502.png b/assets/amiibo/images/icon_04a30001-01c90502.png new file mode 100644 index 000000000..cf1195fcf Binary files /dev/null and b/assets/amiibo/images/icon_04a30001-01c90502.png differ diff --git a/assets/amiibo/images/icon_04a40001-00d40502.png b/assets/amiibo/images/icon_04a40001-00d40502.png new file mode 100644 index 000000000..f6e7a9919 Binary files /dev/null and b/assets/amiibo/images/icon_04a40001-00d40502.png differ diff --git a/assets/amiibo/images/icon_04a50001-00740502.png b/assets/amiibo/images/icon_04a50001-00740502.png new file mode 100644 index 000000000..132809dcf Binary files /dev/null and b/assets/amiibo/images/icon_04a50001-00740502.png differ diff --git a/assets/amiibo/images/icon_04a60001-00a30502.png b/assets/amiibo/images/icon_04a60001-00a30502.png new file mode 100644 index 000000000..2c3f56b3b Binary files /dev/null and b/assets/amiibo/images/icon_04a60001-00a30502.png differ diff --git a/assets/amiibo/images/icon_04a70001-01a60502.png b/assets/amiibo/images/icon_04a70001-01a60502.png new file mode 100644 index 000000000..765967df6 Binary files /dev/null and b/assets/amiibo/images/icon_04a70001-01a60502.png differ diff --git a/assets/amiibo/images/icon_04a80101-031e0502.png b/assets/amiibo/images/icon_04a80101-031e0502.png new file mode 100644 index 000000000..7fa0c4759 Binary files /dev/null and b/assets/amiibo/images/icon_04a80101-031e0502.png differ diff --git a/assets/amiibo/images/icon_04b20001-01b90502.png b/assets/amiibo/images/icon_04b20001-01b90502.png new file mode 100644 index 000000000..944f685e1 Binary files /dev/null and b/assets/amiibo/images/icon_04b20001-01b90502.png differ diff --git a/assets/amiibo/images/icon_04b30001-00dd0502.png b/assets/amiibo/images/icon_04b30001-00dd0502.png new file mode 100644 index 000000000..b15f5bbe1 Binary files /dev/null and b/assets/amiibo/images/icon_04b30001-00dd0502.png differ diff --git a/assets/amiibo/images/icon_04b40001-030c0502.png b/assets/amiibo/images/icon_04b40001-030c0502.png new file mode 100644 index 000000000..626afa842 Binary files /dev/null and b/assets/amiibo/images/icon_04b40001-030c0502.png differ diff --git a/assets/amiibo/images/icon_04b60001-02ec0502.png b/assets/amiibo/images/icon_04b60001-02ec0502.png new file mode 100644 index 000000000..f866d240c Binary files /dev/null and b/assets/amiibo/images/icon_04b60001-02ec0502.png differ diff --git a/assets/amiibo/images/icon_04b90001-01600502.png b/assets/amiibo/images/icon_04b90001-01600502.png new file mode 100644 index 000000000..1a7cb5d0a Binary files /dev/null and b/assets/amiibo/images/icon_04b90001-01600502.png differ diff --git a/assets/amiibo/images/icon_04ba0001-005d0502.png b/assets/amiibo/images/icon_04ba0001-005d0502.png new file mode 100644 index 000000000..523b6993f Binary files /dev/null and b/assets/amiibo/images/icon_04ba0001-005d0502.png differ diff --git a/assets/amiibo/images/icon_04c50001-01010502.png b/assets/amiibo/images/icon_04c50001-01010502.png new file mode 100644 index 000000000..91aaa2926 Binary files /dev/null and b/assets/amiibo/images/icon_04c50001-01010502.png differ diff --git a/assets/amiibo/images/icon_04c60001-01670502.png b/assets/amiibo/images/icon_04c60001-01670502.png new file mode 100644 index 000000000..a6c846513 Binary files /dev/null and b/assets/amiibo/images/icon_04c60001-01670502.png differ diff --git a/assets/amiibo/images/icon_04c70001-00940502.png b/assets/amiibo/images/icon_04c70001-00940502.png new file mode 100644 index 000000000..59ab69e99 Binary files /dev/null and b/assets/amiibo/images/icon_04c70001-00940502.png differ diff --git a/assets/amiibo/images/icon_04c80001-02ed0502.png b/assets/amiibo/images/icon_04c80001-02ed0502.png new file mode 100644 index 000000000..c092125e6 Binary files /dev/null and b/assets/amiibo/images/icon_04c80001-02ed0502.png differ diff --git a/assets/amiibo/images/icon_04c90001-030d0502.png b/assets/amiibo/images/icon_04c90001-030d0502.png new file mode 100644 index 000000000..44ae1f18c Binary files /dev/null and b/assets/amiibo/images/icon_04c90001-030d0502.png differ diff --git a/assets/amiibo/images/icon_04cc0001-00a40502.png b/assets/amiibo/images/icon_04cc0001-00a40502.png new file mode 100644 index 000000000..48e0c40a1 Binary files /dev/null and b/assets/amiibo/images/icon_04cc0001-00a40502.png differ diff --git a/assets/amiibo/images/icon_04cd0001-01520502.png b/assets/amiibo/images/icon_04cd0001-01520502.png new file mode 100644 index 000000000..4f68d039b Binary files /dev/null and b/assets/amiibo/images/icon_04cd0001-01520502.png differ diff --git a/assets/amiibo/images/icon_04ce0001-00db0502.png b/assets/amiibo/images/icon_04ce0001-00db0502.png new file mode 100644 index 000000000..229987607 Binary files /dev/null and b/assets/amiibo/images/icon_04ce0001-00db0502.png differ diff --git a/assets/amiibo/images/icon_04cf0001-00e10502.png b/assets/amiibo/images/icon_04cf0001-00e10502.png new file mode 100644 index 000000000..8d7805621 Binary files /dev/null and b/assets/amiibo/images/icon_04cf0001-00e10502.png differ diff --git a/assets/amiibo/images/icon_04d00001-01960502.png b/assets/amiibo/images/icon_04d00001-01960502.png new file mode 100644 index 000000000..9aa3c0d2d Binary files /dev/null and b/assets/amiibo/images/icon_04d00001-01960502.png differ diff --git a/assets/amiibo/images/icon_04d10001-009e0502.png b/assets/amiibo/images/icon_04d10001-009e0502.png new file mode 100644 index 000000000..7db7be011 Binary files /dev/null and b/assets/amiibo/images/icon_04d10001-009e0502.png differ diff --git a/assets/amiibo/images/icon_04d20001-01a70502.png b/assets/amiibo/images/icon_04d20001-01a70502.png new file mode 100644 index 000000000..1b39b02ad Binary files /dev/null and b/assets/amiibo/images/icon_04d20001-01a70502.png differ diff --git a/assets/amiibo/images/icon_04d30101-031b0502.png b/assets/amiibo/images/icon_04d30101-031b0502.png new file mode 100644 index 000000000..bac3e9ef2 Binary files /dev/null and b/assets/amiibo/images/icon_04d30101-031b0502.png differ diff --git a/assets/amiibo/images/icon_04dd0001-00a20502.png b/assets/amiibo/images/icon_04dd0001-00a20502.png new file mode 100644 index 000000000..d0d5f1bf2 Binary files /dev/null and b/assets/amiibo/images/icon_04dd0001-00a20502.png differ diff --git a/assets/amiibo/images/icon_04de0001-00ce0502.png b/assets/amiibo/images/icon_04de0001-00ce0502.png new file mode 100644 index 000000000..ea0bb8759 Binary files /dev/null and b/assets/amiibo/images/icon_04de0001-00ce0502.png differ diff --git a/assets/amiibo/images/icon_04df0001-00e80502.png b/assets/amiibo/images/icon_04df0001-00e80502.png new file mode 100644 index 000000000..8f1335d1a Binary files /dev/null and b/assets/amiibo/images/icon_04df0001-00e80502.png differ diff --git a/assets/amiibo/images/icon_04e00001-00f70502.png b/assets/amiibo/images/icon_04e00001-00f70502.png new file mode 100644 index 000000000..e9c66d594 Binary files /dev/null and b/assets/amiibo/images/icon_04e00001-00f70502.png differ diff --git a/assets/amiibo/images/icon_04e10001-01be0502.png b/assets/amiibo/images/icon_04e10001-01be0502.png new file mode 100644 index 000000000..9bdd6e29a Binary files /dev/null and b/assets/amiibo/images/icon_04e10001-01be0502.png differ diff --git a/assets/amiibo/images/icon_04e20001-01090502.png b/assets/amiibo/images/icon_04e20001-01090502.png new file mode 100644 index 000000000..e75376d4a Binary files /dev/null and b/assets/amiibo/images/icon_04e20001-01090502.png differ diff --git a/assets/amiibo/images/icon_04e30001-01650502.png b/assets/amiibo/images/icon_04e30001-01650502.png new file mode 100644 index 000000000..534c4c7fd Binary files /dev/null and b/assets/amiibo/images/icon_04e30001-01650502.png differ diff --git a/assets/amiibo/images/icon_04e40001-01b60502.png b/assets/amiibo/images/icon_04e40001-01b60502.png new file mode 100644 index 000000000..f0132edf7 Binary files /dev/null and b/assets/amiibo/images/icon_04e40001-01b60502.png differ diff --git a/assets/amiibo/images/icon_04e50001-01ad0502.png b/assets/amiibo/images/icon_04e50001-01ad0502.png new file mode 100644 index 000000000..e4f6011c7 Binary files /dev/null and b/assets/amiibo/images/icon_04e50001-01ad0502.png differ diff --git a/assets/amiibo/images/icon_04e60001-00820502.png b/assets/amiibo/images/icon_04e60001-00820502.png new file mode 100644 index 000000000..7417922d7 Binary files /dev/null and b/assets/amiibo/images/icon_04e60001-00820502.png differ diff --git a/assets/amiibo/images/icon_04e70001-01320502.png b/assets/amiibo/images/icon_04e70001-01320502.png new file mode 100644 index 000000000..1ab787712 Binary files /dev/null and b/assets/amiibo/images/icon_04e70001-01320502.png differ diff --git a/assets/amiibo/images/icon_04e80001-01ce0502.png b/assets/amiibo/images/icon_04e80001-01ce0502.png new file mode 100644 index 000000000..a8e854a18 Binary files /dev/null and b/assets/amiibo/images/icon_04e80001-01ce0502.png differ diff --git a/assets/amiibo/images/icon_04ea0001-03180502.png b/assets/amiibo/images/icon_04ea0001-03180502.png new file mode 100644 index 000000000..d6109cdde Binary files /dev/null and b/assets/amiibo/images/icon_04ea0001-03180502.png differ diff --git a/assets/amiibo/images/icon_04eb0001-02f00502.png b/assets/amiibo/images/icon_04eb0001-02f00502.png new file mode 100644 index 000000000..3a3a2e89a Binary files /dev/null and b/assets/amiibo/images/icon_04eb0001-02f00502.png differ diff --git a/assets/amiibo/images/icon_04ec0001-00770502.png b/assets/amiibo/images/icon_04ec0001-00770502.png new file mode 100644 index 000000000..dc8033191 Binary files /dev/null and b/assets/amiibo/images/icon_04ec0001-00770502.png differ diff --git a/assets/amiibo/images/icon_04ed0001-00620502.png b/assets/amiibo/images/icon_04ed0001-00620502.png new file mode 100644 index 000000000..911e03c9b Binary files /dev/null and b/assets/amiibo/images/icon_04ed0001-00620502.png differ diff --git a/assets/amiibo/images/icon_04ee0001-014b0502.png b/assets/amiibo/images/icon_04ee0001-014b0502.png new file mode 100644 index 000000000..7f1dad03e Binary files /dev/null and b/assets/amiibo/images/icon_04ee0001-014b0502.png differ diff --git a/assets/amiibo/images/icon_04ef0001-013b0502.png b/assets/amiibo/images/icon_04ef0001-013b0502.png new file mode 100644 index 000000000..1fc03fcba Binary files /dev/null and b/assets/amiibo/images/icon_04ef0001-013b0502.png differ diff --git a/assets/amiibo/images/icon_04fa0001-01680502.png b/assets/amiibo/images/icon_04fa0001-01680502.png new file mode 100644 index 000000000..82b44dbec Binary files /dev/null and b/assets/amiibo/images/icon_04fa0001-01680502.png differ diff --git a/assets/amiibo/images/icon_04fb0001-01c60502.png b/assets/amiibo/images/icon_04fb0001-01c60502.png new file mode 100644 index 000000000..29d0dbce5 Binary files /dev/null and b/assets/amiibo/images/icon_04fb0001-01c60502.png differ diff --git a/assets/amiibo/images/icon_04fc0001-02ee0502.png b/assets/amiibo/images/icon_04fc0001-02ee0502.png new file mode 100644 index 000000000..3426938a4 Binary files /dev/null and b/assets/amiibo/images/icon_04fc0001-02ee0502.png differ diff --git a/assets/amiibo/images/icon_04fd0001-007b0502.png b/assets/amiibo/images/icon_04fd0001-007b0502.png new file mode 100644 index 000000000..001c0e76d Binary files /dev/null and b/assets/amiibo/images/icon_04fd0001-007b0502.png differ diff --git a/assets/amiibo/images/icon_04fe0001-00590502.png b/assets/amiibo/images/icon_04fe0001-00590502.png new file mode 100644 index 000000000..fc41a4ac2 Binary files /dev/null and b/assets/amiibo/images/icon_04fe0001-00590502.png differ diff --git a/assets/amiibo/images/icon_04ff0001-01620502.png b/assets/amiibo/images/icon_04ff0001-01620502.png new file mode 100644 index 000000000..42457c5b2 Binary files /dev/null and b/assets/amiibo/images/icon_04ff0001-01620502.png differ diff --git a/assets/amiibo/images/icon_05000001-00e70502.png b/assets/amiibo/images/icon_05000001-00e70502.png new file mode 100644 index 000000000..73163bed2 Binary files /dev/null and b/assets/amiibo/images/icon_05000001-00e70502.png differ diff --git a/assets/amiibo/images/icon_050b0001-00990502.png b/assets/amiibo/images/icon_050b0001-00990502.png new file mode 100644 index 000000000..a3774feec Binary files /dev/null and b/assets/amiibo/images/icon_050b0001-00990502.png differ diff --git a/assets/amiibo/images/icon_050c0001-01c10502.png b/assets/amiibo/images/icon_050c0001-01c10502.png new file mode 100644 index 000000000..460cea4ab Binary files /dev/null and b/assets/amiibo/images/icon_050c0001-01c10502.png differ diff --git a/assets/amiibo/images/icon_050d0001-01420502.png b/assets/amiibo/images/icon_050d0001-01420502.png new file mode 100644 index 000000000..b4e07f997 Binary files /dev/null and b/assets/amiibo/images/icon_050d0001-01420502.png differ diff --git a/assets/amiibo/images/icon_050e0001-00d70502.png b/assets/amiibo/images/icon_050e0001-00d70502.png new file mode 100644 index 000000000..2b69f818b Binary files /dev/null and b/assets/amiibo/images/icon_050e0001-00d70502.png differ diff --git a/assets/amiibo/images/icon_050f0001-03140502.png b/assets/amiibo/images/icon_050f0001-03140502.png new file mode 100644 index 000000000..1a1211383 Binary files /dev/null and b/assets/amiibo/images/icon_050f0001-03140502.png differ diff --git a/assets/amiibo/images/icon_05100001-01070502.png b/assets/amiibo/images/icon_05100001-01070502.png new file mode 100644 index 000000000..1d1f5e4b5 Binary files /dev/null and b/assets/amiibo/images/icon_05100001-01070502.png differ diff --git a/assets/amiibo/images/icon_05110001-01950502.png b/assets/amiibo/images/icon_05110001-01950502.png new file mode 100644 index 000000000..1367350b9 Binary files /dev/null and b/assets/amiibo/images/icon_05110001-01950502.png differ diff --git a/assets/amiibo/images/icon_05130001-02e70502.png b/assets/amiibo/images/icon_05130001-02e70502.png new file mode 100644 index 000000000..ad3925f85 Binary files /dev/null and b/assets/amiibo/images/icon_05130001-02e70502.png differ diff --git a/assets/amiibo/images/icon_05140001-01530502.png b/assets/amiibo/images/icon_05140001-01530502.png new file mode 100644 index 000000000..c594d3330 Binary files /dev/null and b/assets/amiibo/images/icon_05140001-01530502.png differ diff --git a/assets/amiibo/images/icon_05150001-005b0502.png b/assets/amiibo/images/icon_05150001-005b0502.png new file mode 100644 index 000000000..a6246088c Binary files /dev/null and b/assets/amiibo/images/icon_05150001-005b0502.png differ diff --git a/assets/amiibo/images/icon_05800000-00050002.png b/assets/amiibo/images/icon_05800000-00050002.png new file mode 100644 index 000000000..7ef3ef1d3 Binary files /dev/null and b/assets/amiibo/images/icon_05800000-00050002.png differ diff --git a/assets/amiibo/images/icon_05810000-001c0002.png b/assets/amiibo/images/icon_05810000-001c0002.png new file mode 100644 index 000000000..76cf31c0d Binary files /dev/null and b/assets/amiibo/images/icon_05810000-001c0002.png differ diff --git a/assets/amiibo/images/icon_05840000-037e0002.png b/assets/amiibo/images/icon_05840000-037e0002.png new file mode 100644 index 000000000..4f5511d59 Binary files /dev/null and b/assets/amiibo/images/icon_05840000-037e0002.png differ diff --git a/assets/amiibo/images/icon_05c00000-00060002.png b/assets/amiibo/images/icon_05c00000-00060002.png new file mode 100644 index 000000000..f05b04bfb Binary files /dev/null and b/assets/amiibo/images/icon_05c00000-00060002.png differ diff --git a/assets/amiibo/images/icon_05c00000-03651302.png b/assets/amiibo/images/icon_05c00000-03651302.png new file mode 100644 index 000000000..71b80ee00 Binary files /dev/null and b/assets/amiibo/images/icon_05c00000-03651302.png differ diff --git a/assets/amiibo/images/icon_05c00000-04121302.png b/assets/amiibo/images/icon_05c00000-04121302.png new file mode 100644 index 000000000..f59e2e38d Binary files /dev/null and b/assets/amiibo/images/icon_05c00000-04121302.png differ diff --git a/assets/amiibo/images/icon_05c00100-001d0002.png b/assets/amiibo/images/icon_05c00100-001d0002.png new file mode 100644 index 000000000..4a3f91858 Binary files /dev/null and b/assets/amiibo/images/icon_05c00100-001d0002.png differ diff --git a/assets/amiibo/images/icon_05c10000-03661302.png b/assets/amiibo/images/icon_05c10000-03661302.png new file mode 100644 index 000000000..d0fea06de Binary files /dev/null and b/assets/amiibo/images/icon_05c10000-03661302.png differ diff --git a/assets/amiibo/images/icon_05c20000-037f0002.png b/assets/amiibo/images/icon_05c20000-037f0002.png new file mode 100644 index 000000000..395bbd458 Binary files /dev/null and b/assets/amiibo/images/icon_05c20000-037f0002.png differ diff --git a/assets/amiibo/images/icon_05c30000-03800002.png b/assets/amiibo/images/icon_05c30000-03800002.png new file mode 100644 index 000000000..372efafbd Binary files /dev/null and b/assets/amiibo/images/icon_05c30000-03800002.png differ diff --git a/assets/amiibo/images/icon_05c40000-04131302.png b/assets/amiibo/images/icon_05c40000-04131302.png new file mode 100644 index 000000000..61e224c17 Binary files /dev/null and b/assets/amiibo/images/icon_05c40000-04131302.png differ diff --git a/assets/amiibo/images/icon_06000000-00120002.png b/assets/amiibo/images/icon_06000000-00120002.png new file mode 100644 index 000000000..77d355410 Binary files /dev/null and b/assets/amiibo/images/icon_06000000-00120002.png differ diff --git a/assets/amiibo/images/icon_06400100-001e0002.png b/assets/amiibo/images/icon_06400100-001e0002.png new file mode 100644 index 000000000..1f6948f1a Binary files /dev/null and b/assets/amiibo/images/icon_06400100-001e0002.png differ diff --git a/assets/amiibo/images/icon_06420000-035f1102.png b/assets/amiibo/images/icon_06420000-035f1102.png new file mode 100644 index 000000000..6e851d7e9 Binary files /dev/null and b/assets/amiibo/images/icon_06420000-035f1102.png differ diff --git a/assets/amiibo/images/icon_06c00000-000f0002.png b/assets/amiibo/images/icon_06c00000-000f0002.png new file mode 100644 index 000000000..1f07b9ec1 Binary files /dev/null and b/assets/amiibo/images/icon_06c00000-000f0002.png differ diff --git a/assets/amiibo/images/icon_07000000-00070002.png b/assets/amiibo/images/icon_07000000-00070002.png new file mode 100644 index 000000000..3cd5bd046 Binary files /dev/null and b/assets/amiibo/images/icon_07000000-00070002.png differ diff --git a/assets/amiibo/images/icon_07400000-00100002.png b/assets/amiibo/images/icon_07400000-00100002.png new file mode 100644 index 000000000..c5be3753d Binary files /dev/null and b/assets/amiibo/images/icon_07400000-00100002.png differ diff --git a/assets/amiibo/images/icon_07410000-00200002.png b/assets/amiibo/images/icon_07410000-00200002.png new file mode 100644 index 000000000..3ba5b062f Binary files /dev/null and b/assets/amiibo/images/icon_07410000-00200002.png differ diff --git a/assets/amiibo/images/icon_07420000-001f0002.png b/assets/amiibo/images/icon_07420000-001f0002.png new file mode 100644 index 000000000..b60823370 Binary files /dev/null and b/assets/amiibo/images/icon_07420000-001f0002.png differ diff --git a/assets/amiibo/images/icon_07800000-002d0002.png b/assets/amiibo/images/icon_07800000-002d0002.png new file mode 100644 index 000000000..afa8d7d15 Binary files /dev/null and b/assets/amiibo/images/icon_07800000-002d0002.png differ diff --git a/assets/amiibo/images/icon_07810000-002e0002.png b/assets/amiibo/images/icon_07810000-002e0002.png new file mode 100644 index 000000000..5491c00cc Binary files /dev/null and b/assets/amiibo/images/icon_07810000-002e0002.png differ diff --git a/assets/amiibo/images/icon_07810000-00330002.png b/assets/amiibo/images/icon_07810000-00330002.png new file mode 100644 index 000000000..cacdaa0fd Binary files /dev/null and b/assets/amiibo/images/icon_07810000-00330002.png differ diff --git a/assets/amiibo/images/icon_07820000-002f0002.png b/assets/amiibo/images/icon_07820000-002f0002.png new file mode 100644 index 000000000..b6a0fd750 Binary files /dev/null and b/assets/amiibo/images/icon_07820000-002f0002.png differ diff --git a/assets/amiibo/images/icon_078f0000-03810002.png b/assets/amiibo/images/icon_078f0000-03810002.png new file mode 100644 index 000000000..c33e7947a Binary files /dev/null and b/assets/amiibo/images/icon_078f0000-03810002.png differ diff --git a/assets/amiibo/images/icon_07c00000-00210002.png b/assets/amiibo/images/icon_07c00000-00210002.png new file mode 100644 index 000000000..a9c717d52 Binary files /dev/null and b/assets/amiibo/images/icon_07c00000-00210002.png differ diff --git a/assets/amiibo/images/icon_07c00100-00220002.png b/assets/amiibo/images/icon_07c00100-00220002.png new file mode 100644 index 000000000..5b6f292e5 Binary files /dev/null and b/assets/amiibo/images/icon_07c00100-00220002.png differ diff --git a/assets/amiibo/images/icon_07c00200-00230002.png b/assets/amiibo/images/icon_07c00200-00230002.png new file mode 100644 index 000000000..b0a722736 Binary files /dev/null and b/assets/amiibo/images/icon_07c00200-00230002.png differ diff --git a/assets/amiibo/images/icon_08000100-003e0402.png b/assets/amiibo/images/icon_08000100-003e0402.png new file mode 100644 index 000000000..8b36f2ecb Binary files /dev/null and b/assets/amiibo/images/icon_08000100-003e0402.png differ diff --git a/assets/amiibo/images/icon_08000100-025f0402.png b/assets/amiibo/images/icon_08000100-025f0402.png new file mode 100644 index 000000000..124eadf55 Binary files /dev/null and b/assets/amiibo/images/icon_08000100-025f0402.png differ diff --git a/assets/amiibo/images/icon_08000100-03690402.png b/assets/amiibo/images/icon_08000100-03690402.png new file mode 100644 index 000000000..280a4bc23 Binary files /dev/null and b/assets/amiibo/images/icon_08000100-03690402.png differ diff --git a/assets/amiibo/images/icon_08000100-03820002.png b/assets/amiibo/images/icon_08000100-03820002.png new file mode 100644 index 000000000..cf1f34b7d Binary files /dev/null and b/assets/amiibo/images/icon_08000100-03820002.png differ diff --git a/assets/amiibo/images/icon_08000100-04150402.png b/assets/amiibo/images/icon_08000100-04150402.png new file mode 100644 index 000000000..1b30696e5 Binary files /dev/null and b/assets/amiibo/images/icon_08000100-04150402.png differ diff --git a/assets/amiibo/images/icon_08000200-003f0402.png b/assets/amiibo/images/icon_08000200-003f0402.png new file mode 100644 index 000000000..ad67ecfcb Binary files /dev/null and b/assets/amiibo/images/icon_08000200-003f0402.png differ diff --git a/assets/amiibo/images/icon_08000200-02600402.png b/assets/amiibo/images/icon_08000200-02600402.png new file mode 100644 index 000000000..1dbb393db Binary files /dev/null and b/assets/amiibo/images/icon_08000200-02600402.png differ diff --git a/assets/amiibo/images/icon_08000200-036a0402.png b/assets/amiibo/images/icon_08000200-036a0402.png new file mode 100644 index 000000000..de98a445b Binary files /dev/null and b/assets/amiibo/images/icon_08000200-036a0402.png differ diff --git a/assets/amiibo/images/icon_08000300-00400402.png b/assets/amiibo/images/icon_08000300-00400402.png new file mode 100644 index 000000000..42cd5a5b6 Binary files /dev/null and b/assets/amiibo/images/icon_08000300-00400402.png differ diff --git a/assets/amiibo/images/icon_08000300-02610402.png b/assets/amiibo/images/icon_08000300-02610402.png new file mode 100644 index 000000000..e18b73f3f Binary files /dev/null and b/assets/amiibo/images/icon_08000300-02610402.png differ diff --git a/assets/amiibo/images/icon_08000300-036b0402.png b/assets/amiibo/images/icon_08000300-036b0402.png new file mode 100644 index 000000000..6bc022f43 Binary files /dev/null and b/assets/amiibo/images/icon_08000300-036b0402.png differ diff --git a/assets/amiibo/images/icon_08010000-025d0402.png b/assets/amiibo/images/icon_08010000-025d0402.png new file mode 100644 index 000000000..6cbee1a7a Binary files /dev/null and b/assets/amiibo/images/icon_08010000-025d0402.png differ diff --git a/assets/amiibo/images/icon_08010000-04360402.png b/assets/amiibo/images/icon_08010000-04360402.png new file mode 100644 index 000000000..b45684564 Binary files /dev/null and b/assets/amiibo/images/icon_08010000-04360402.png differ diff --git a/assets/amiibo/images/icon_08020000-025e0402.png b/assets/amiibo/images/icon_08020000-025e0402.png new file mode 100644 index 000000000..5fa18cc97 Binary files /dev/null and b/assets/amiibo/images/icon_08020000-025e0402.png differ diff --git a/assets/amiibo/images/icon_08020000-04370402.png b/assets/amiibo/images/icon_08020000-04370402.png new file mode 100644 index 000000000..10cb5987a Binary files /dev/null and b/assets/amiibo/images/icon_08020000-04370402.png differ diff --git a/assets/amiibo/images/icon_08030000-03760402.png b/assets/amiibo/images/icon_08030000-03760402.png new file mode 100644 index 000000000..85a5e9f6a Binary files /dev/null and b/assets/amiibo/images/icon_08030000-03760402.png differ diff --git a/assets/amiibo/images/icon_08030000-04380402.png b/assets/amiibo/images/icon_08030000-04380402.png new file mode 100644 index 000000000..84425939e Binary files /dev/null and b/assets/amiibo/images/icon_08030000-04380402.png differ diff --git a/assets/amiibo/images/icon_08040000-03770402.png b/assets/amiibo/images/icon_08040000-03770402.png new file mode 100644 index 000000000..35880b996 Binary files /dev/null and b/assets/amiibo/images/icon_08040000-03770402.png differ diff --git a/assets/amiibo/images/icon_08040000-04390402.png b/assets/amiibo/images/icon_08040000-04390402.png new file mode 100644 index 000000000..ab3ee111b Binary files /dev/null and b/assets/amiibo/images/icon_08040000-04390402.png differ diff --git a/assets/amiibo/images/icon_08050100-038e0402.png b/assets/amiibo/images/icon_08050100-038e0402.png new file mode 100644 index 000000000..a14e3641a Binary files /dev/null and b/assets/amiibo/images/icon_08050100-038e0402.png differ diff --git a/assets/amiibo/images/icon_08050200-038f0402.png b/assets/amiibo/images/icon_08050200-038f0402.png new file mode 100644 index 000000000..6764e467d Binary files /dev/null and b/assets/amiibo/images/icon_08050200-038f0402.png differ diff --git a/assets/amiibo/images/icon_08050200-041b0402.png b/assets/amiibo/images/icon_08050200-041b0402.png new file mode 100644 index 000000000..03c0cb37a Binary files /dev/null and b/assets/amiibo/images/icon_08050200-041b0402.png differ diff --git a/assets/amiibo/images/icon_08050300-03900402.png b/assets/amiibo/images/icon_08050300-03900402.png new file mode 100644 index 000000000..8359af06b Binary files /dev/null and b/assets/amiibo/images/icon_08050300-03900402.png differ diff --git a/assets/amiibo/images/icon_08060100-041c0402.png b/assets/amiibo/images/icon_08060100-041c0402.png new file mode 100644 index 000000000..695b655c9 Binary files /dev/null and b/assets/amiibo/images/icon_08060100-041c0402.png differ diff --git a/assets/amiibo/images/icon_08070000-04330402.png b/assets/amiibo/images/icon_08070000-04330402.png new file mode 100644 index 000000000..55641008b Binary files /dev/null and b/assets/amiibo/images/icon_08070000-04330402.png differ diff --git a/assets/amiibo/images/icon_08080000-04340402.png b/assets/amiibo/images/icon_08080000-04340402.png new file mode 100644 index 000000000..d3070548d Binary files /dev/null and b/assets/amiibo/images/icon_08080000-04340402.png differ diff --git a/assets/amiibo/images/icon_08090000-04350402.png b/assets/amiibo/images/icon_08090000-04350402.png new file mode 100644 index 000000000..c7d9baee8 Binary files /dev/null and b/assets/amiibo/images/icon_08090000-04350402.png differ diff --git a/assets/amiibo/images/icon_09c00101-02690e02.png b/assets/amiibo/images/icon_09c00101-02690e02.png new file mode 100644 index 000000000..4b06e9379 Binary files /dev/null and b/assets/amiibo/images/icon_09c00101-02690e02.png differ diff --git a/assets/amiibo/images/icon_09c00201-026a0e02.png b/assets/amiibo/images/icon_09c00201-026a0e02.png new file mode 100644 index 000000000..cf281b228 Binary files /dev/null and b/assets/amiibo/images/icon_09c00201-026a0e02.png differ diff --git a/assets/amiibo/images/icon_09c00301-026b0e02.png b/assets/amiibo/images/icon_09c00301-026b0e02.png new file mode 100644 index 000000000..2b34ce4f4 Binary files /dev/null and b/assets/amiibo/images/icon_09c00301-026b0e02.png differ diff --git a/assets/amiibo/images/icon_09c00401-026c0e02.png b/assets/amiibo/images/icon_09c00401-026c0e02.png new file mode 100644 index 000000000..3626bf2f2 Binary files /dev/null and b/assets/amiibo/images/icon_09c00401-026c0e02.png differ diff --git a/assets/amiibo/images/icon_09c00501-026d0e02.png b/assets/amiibo/images/icon_09c00501-026d0e02.png new file mode 100644 index 000000000..eb75ff4a2 Binary files /dev/null and b/assets/amiibo/images/icon_09c00501-026d0e02.png differ diff --git a/assets/amiibo/images/icon_09c10101-026e0e02.png b/assets/amiibo/images/icon_09c10101-026e0e02.png new file mode 100644 index 000000000..5ed8fa848 Binary files /dev/null and b/assets/amiibo/images/icon_09c10101-026e0e02.png differ diff --git a/assets/amiibo/images/icon_09c10201-026f0e02.png b/assets/amiibo/images/icon_09c10201-026f0e02.png new file mode 100644 index 000000000..a41f561b1 Binary files /dev/null and b/assets/amiibo/images/icon_09c10201-026f0e02.png differ diff --git a/assets/amiibo/images/icon_09c10301-02700e02.png b/assets/amiibo/images/icon_09c10301-02700e02.png new file mode 100644 index 000000000..41f0303ab Binary files /dev/null and b/assets/amiibo/images/icon_09c10301-02700e02.png differ diff --git a/assets/amiibo/images/icon_09c10401-02710e02.png b/assets/amiibo/images/icon_09c10401-02710e02.png new file mode 100644 index 000000000..a2e9857b3 Binary files /dev/null and b/assets/amiibo/images/icon_09c10401-02710e02.png differ diff --git a/assets/amiibo/images/icon_09c10501-02720e02.png b/assets/amiibo/images/icon_09c10501-02720e02.png new file mode 100644 index 000000000..1e2435e58 Binary files /dev/null and b/assets/amiibo/images/icon_09c10501-02720e02.png differ diff --git a/assets/amiibo/images/icon_09c20101-02730e02.png b/assets/amiibo/images/icon_09c20101-02730e02.png new file mode 100644 index 000000000..e16422004 Binary files /dev/null and b/assets/amiibo/images/icon_09c20101-02730e02.png differ diff --git a/assets/amiibo/images/icon_09c20201-02740e02.png b/assets/amiibo/images/icon_09c20201-02740e02.png new file mode 100644 index 000000000..67151ed3e Binary files /dev/null and b/assets/amiibo/images/icon_09c20201-02740e02.png differ diff --git a/assets/amiibo/images/icon_09c20301-02750e02.png b/assets/amiibo/images/icon_09c20301-02750e02.png new file mode 100644 index 000000000..850bf778e Binary files /dev/null and b/assets/amiibo/images/icon_09c20301-02750e02.png differ diff --git a/assets/amiibo/images/icon_09c20401-02760e02.png b/assets/amiibo/images/icon_09c20401-02760e02.png new file mode 100644 index 000000000..ee6e9a14c Binary files /dev/null and b/assets/amiibo/images/icon_09c20401-02760e02.png differ diff --git a/assets/amiibo/images/icon_09c20501-02770e02.png b/assets/amiibo/images/icon_09c20501-02770e02.png new file mode 100644 index 000000000..1d78c40f3 Binary files /dev/null and b/assets/amiibo/images/icon_09c20501-02770e02.png differ diff --git a/assets/amiibo/images/icon_09c30101-02780e02.png b/assets/amiibo/images/icon_09c30101-02780e02.png new file mode 100644 index 000000000..f43043198 Binary files /dev/null and b/assets/amiibo/images/icon_09c30101-02780e02.png differ diff --git a/assets/amiibo/images/icon_09c30201-02790e02.png b/assets/amiibo/images/icon_09c30201-02790e02.png new file mode 100644 index 000000000..ef5474478 Binary files /dev/null and b/assets/amiibo/images/icon_09c30201-02790e02.png differ diff --git a/assets/amiibo/images/icon_09c30301-027a0e02.png b/assets/amiibo/images/icon_09c30301-027a0e02.png new file mode 100644 index 000000000..f4499e97c Binary files /dev/null and b/assets/amiibo/images/icon_09c30301-027a0e02.png differ diff --git a/assets/amiibo/images/icon_09c30401-027b0e02.png b/assets/amiibo/images/icon_09c30401-027b0e02.png new file mode 100644 index 000000000..ad4534890 Binary files /dev/null and b/assets/amiibo/images/icon_09c30401-027b0e02.png differ diff --git a/assets/amiibo/images/icon_09c30501-027c0e02.png b/assets/amiibo/images/icon_09c30501-027c0e02.png new file mode 100644 index 000000000..fd7dd66f4 Binary files /dev/null and b/assets/amiibo/images/icon_09c30501-027c0e02.png differ diff --git a/assets/amiibo/images/icon_09c40101-027d0e02.png b/assets/amiibo/images/icon_09c40101-027d0e02.png new file mode 100644 index 000000000..a06217baf Binary files /dev/null and b/assets/amiibo/images/icon_09c40101-027d0e02.png differ diff --git a/assets/amiibo/images/icon_09c40201-027e0e02.png b/assets/amiibo/images/icon_09c40201-027e0e02.png new file mode 100644 index 000000000..fe801a167 Binary files /dev/null and b/assets/amiibo/images/icon_09c40201-027e0e02.png differ diff --git a/assets/amiibo/images/icon_09c40301-027f0e02.png b/assets/amiibo/images/icon_09c40301-027f0e02.png new file mode 100644 index 000000000..fe358924d Binary files /dev/null and b/assets/amiibo/images/icon_09c40301-027f0e02.png differ diff --git a/assets/amiibo/images/icon_09c40401-02800e02.png b/assets/amiibo/images/icon_09c40401-02800e02.png new file mode 100644 index 000000000..1b29d1338 Binary files /dev/null and b/assets/amiibo/images/icon_09c40401-02800e02.png differ diff --git a/assets/amiibo/images/icon_09c40501-02810e02.png b/assets/amiibo/images/icon_09c40501-02810e02.png new file mode 100644 index 000000000..35c48cf56 Binary files /dev/null and b/assets/amiibo/images/icon_09c40501-02810e02.png differ diff --git a/assets/amiibo/images/icon_09c50101-02820e02.png b/assets/amiibo/images/icon_09c50101-02820e02.png new file mode 100644 index 000000000..8f6833c11 Binary files /dev/null and b/assets/amiibo/images/icon_09c50101-02820e02.png differ diff --git a/assets/amiibo/images/icon_09c50201-02830e02.png b/assets/amiibo/images/icon_09c50201-02830e02.png new file mode 100644 index 000000000..4eac5dbdb Binary files /dev/null and b/assets/amiibo/images/icon_09c50201-02830e02.png differ diff --git a/assets/amiibo/images/icon_09c50301-02840e02.png b/assets/amiibo/images/icon_09c50301-02840e02.png new file mode 100644 index 000000000..5bc86c598 Binary files /dev/null and b/assets/amiibo/images/icon_09c50301-02840e02.png differ diff --git a/assets/amiibo/images/icon_09c50401-02850e02.png b/assets/amiibo/images/icon_09c50401-02850e02.png new file mode 100644 index 000000000..daaee032a Binary files /dev/null and b/assets/amiibo/images/icon_09c50401-02850e02.png differ diff --git a/assets/amiibo/images/icon_09c50501-02860e02.png b/assets/amiibo/images/icon_09c50501-02860e02.png new file mode 100644 index 000000000..88c3f6338 Binary files /dev/null and b/assets/amiibo/images/icon_09c50501-02860e02.png differ diff --git a/assets/amiibo/images/icon_09c60101-02870e02.png b/assets/amiibo/images/icon_09c60101-02870e02.png new file mode 100644 index 000000000..d3874a25b Binary files /dev/null and b/assets/amiibo/images/icon_09c60101-02870e02.png differ diff --git a/assets/amiibo/images/icon_09c60201-02880e02.png b/assets/amiibo/images/icon_09c60201-02880e02.png new file mode 100644 index 000000000..9d16e06c5 Binary files /dev/null and b/assets/amiibo/images/icon_09c60201-02880e02.png differ diff --git a/assets/amiibo/images/icon_09c60301-02890e02.png b/assets/amiibo/images/icon_09c60301-02890e02.png new file mode 100644 index 000000000..5e6ae378b Binary files /dev/null and b/assets/amiibo/images/icon_09c60301-02890e02.png differ diff --git a/assets/amiibo/images/icon_09c60401-028a0e02.png b/assets/amiibo/images/icon_09c60401-028a0e02.png new file mode 100644 index 000000000..8bae55726 Binary files /dev/null and b/assets/amiibo/images/icon_09c60401-028a0e02.png differ diff --git a/assets/amiibo/images/icon_09c60501-028b0e02.png b/assets/amiibo/images/icon_09c60501-028b0e02.png new file mode 100644 index 000000000..1f97ae4ed Binary files /dev/null and b/assets/amiibo/images/icon_09c60501-028b0e02.png differ diff --git a/assets/amiibo/images/icon_09c70101-028c0e02.png b/assets/amiibo/images/icon_09c70101-028c0e02.png new file mode 100644 index 000000000..d13ce245d Binary files /dev/null and b/assets/amiibo/images/icon_09c70101-028c0e02.png differ diff --git a/assets/amiibo/images/icon_09c70201-028d0e02.png b/assets/amiibo/images/icon_09c70201-028d0e02.png new file mode 100644 index 000000000..e20cae51d Binary files /dev/null and b/assets/amiibo/images/icon_09c70201-028d0e02.png differ diff --git a/assets/amiibo/images/icon_09c70301-028e0e02.png b/assets/amiibo/images/icon_09c70301-028e0e02.png new file mode 100644 index 000000000..952ecc950 Binary files /dev/null and b/assets/amiibo/images/icon_09c70301-028e0e02.png differ diff --git a/assets/amiibo/images/icon_09c70401-028f0e02.png b/assets/amiibo/images/icon_09c70401-028f0e02.png new file mode 100644 index 000000000..d0dc99b28 Binary files /dev/null and b/assets/amiibo/images/icon_09c70401-028f0e02.png differ diff --git a/assets/amiibo/images/icon_09c70501-02900e02.png b/assets/amiibo/images/icon_09c70501-02900e02.png new file mode 100644 index 000000000..9945ad8fe Binary files /dev/null and b/assets/amiibo/images/icon_09c70501-02900e02.png differ diff --git a/assets/amiibo/images/icon_09c80101-02910e02.png b/assets/amiibo/images/icon_09c80101-02910e02.png new file mode 100644 index 000000000..b3b1677dc Binary files /dev/null and b/assets/amiibo/images/icon_09c80101-02910e02.png differ diff --git a/assets/amiibo/images/icon_09c80201-02920e02.png b/assets/amiibo/images/icon_09c80201-02920e02.png new file mode 100644 index 000000000..2b8d35f4e Binary files /dev/null and b/assets/amiibo/images/icon_09c80201-02920e02.png differ diff --git a/assets/amiibo/images/icon_09c80301-02930e02.png b/assets/amiibo/images/icon_09c80301-02930e02.png new file mode 100644 index 000000000..436ade949 Binary files /dev/null and b/assets/amiibo/images/icon_09c80301-02930e02.png differ diff --git a/assets/amiibo/images/icon_09c80401-02940e02.png b/assets/amiibo/images/icon_09c80401-02940e02.png new file mode 100644 index 000000000..c5e3778e7 Binary files /dev/null and b/assets/amiibo/images/icon_09c80401-02940e02.png differ diff --git a/assets/amiibo/images/icon_09c80501-02950e02.png b/assets/amiibo/images/icon_09c80501-02950e02.png new file mode 100644 index 000000000..fe389a659 Binary files /dev/null and b/assets/amiibo/images/icon_09c80501-02950e02.png differ diff --git a/assets/amiibo/images/icon_09c90101-02960e02.png b/assets/amiibo/images/icon_09c90101-02960e02.png new file mode 100644 index 000000000..d57f5999e Binary files /dev/null and b/assets/amiibo/images/icon_09c90101-02960e02.png differ diff --git a/assets/amiibo/images/icon_09c90201-02970e02.png b/assets/amiibo/images/icon_09c90201-02970e02.png new file mode 100644 index 000000000..7baf1ce76 Binary files /dev/null and b/assets/amiibo/images/icon_09c90201-02970e02.png differ diff --git a/assets/amiibo/images/icon_09c90301-02980e02.png b/assets/amiibo/images/icon_09c90301-02980e02.png new file mode 100644 index 000000000..616b3b598 Binary files /dev/null and b/assets/amiibo/images/icon_09c90301-02980e02.png differ diff --git a/assets/amiibo/images/icon_09c90401-02990e02.png b/assets/amiibo/images/icon_09c90401-02990e02.png new file mode 100644 index 000000000..dcb3a1c8d Binary files /dev/null and b/assets/amiibo/images/icon_09c90401-02990e02.png differ diff --git a/assets/amiibo/images/icon_09c90501-029a0e02.png b/assets/amiibo/images/icon_09c90501-029a0e02.png new file mode 100644 index 000000000..a7568ace1 Binary files /dev/null and b/assets/amiibo/images/icon_09c90501-029a0e02.png differ diff --git a/assets/amiibo/images/icon_09ca0101-029b0e02.png b/assets/amiibo/images/icon_09ca0101-029b0e02.png new file mode 100644 index 000000000..78eb4604d Binary files /dev/null and b/assets/amiibo/images/icon_09ca0101-029b0e02.png differ diff --git a/assets/amiibo/images/icon_09ca0201-029c0e02.png b/assets/amiibo/images/icon_09ca0201-029c0e02.png new file mode 100644 index 000000000..4252e0811 Binary files /dev/null and b/assets/amiibo/images/icon_09ca0201-029c0e02.png differ diff --git a/assets/amiibo/images/icon_09ca0301-029d0e02.png b/assets/amiibo/images/icon_09ca0301-029d0e02.png new file mode 100644 index 000000000..9b2000936 Binary files /dev/null and b/assets/amiibo/images/icon_09ca0301-029d0e02.png differ diff --git a/assets/amiibo/images/icon_09ca0401-029e0e02.png b/assets/amiibo/images/icon_09ca0401-029e0e02.png new file mode 100644 index 000000000..75629fba4 Binary files /dev/null and b/assets/amiibo/images/icon_09ca0401-029e0e02.png differ diff --git a/assets/amiibo/images/icon_09ca0501-029f0e02.png b/assets/amiibo/images/icon_09ca0501-029f0e02.png new file mode 100644 index 000000000..bf7b20627 Binary files /dev/null and b/assets/amiibo/images/icon_09ca0501-029f0e02.png differ diff --git a/assets/amiibo/images/icon_09cb0101-02a00e02.png b/assets/amiibo/images/icon_09cb0101-02a00e02.png new file mode 100644 index 000000000..0be53a6ec Binary files /dev/null and b/assets/amiibo/images/icon_09cb0101-02a00e02.png differ diff --git a/assets/amiibo/images/icon_09cb0201-02a10e02.png b/assets/amiibo/images/icon_09cb0201-02a10e02.png new file mode 100644 index 000000000..cbeab29a0 Binary files /dev/null and b/assets/amiibo/images/icon_09cb0201-02a10e02.png differ diff --git a/assets/amiibo/images/icon_09cb0301-02a20e02.png b/assets/amiibo/images/icon_09cb0301-02a20e02.png new file mode 100644 index 000000000..116c54001 Binary files /dev/null and b/assets/amiibo/images/icon_09cb0301-02a20e02.png differ diff --git a/assets/amiibo/images/icon_09cb0401-02a30e02.png b/assets/amiibo/images/icon_09cb0401-02a30e02.png new file mode 100644 index 000000000..cc6dd95e3 Binary files /dev/null and b/assets/amiibo/images/icon_09cb0401-02a30e02.png differ diff --git a/assets/amiibo/images/icon_09cb0501-02a40e02.png b/assets/amiibo/images/icon_09cb0501-02a40e02.png new file mode 100644 index 000000000..2d83d372a Binary files /dev/null and b/assets/amiibo/images/icon_09cb0501-02a40e02.png differ diff --git a/assets/amiibo/images/icon_09cc0101-02a50e02.png b/assets/amiibo/images/icon_09cc0101-02a50e02.png new file mode 100644 index 000000000..fed772541 Binary files /dev/null and b/assets/amiibo/images/icon_09cc0101-02a50e02.png differ diff --git a/assets/amiibo/images/icon_09cc0201-02a60e02.png b/assets/amiibo/images/icon_09cc0201-02a60e02.png new file mode 100644 index 000000000..b9e0f5d60 Binary files /dev/null and b/assets/amiibo/images/icon_09cc0201-02a60e02.png differ diff --git a/assets/amiibo/images/icon_09cc0301-02a70e02.png b/assets/amiibo/images/icon_09cc0301-02a70e02.png new file mode 100644 index 000000000..6ae820337 Binary files /dev/null and b/assets/amiibo/images/icon_09cc0301-02a70e02.png differ diff --git a/assets/amiibo/images/icon_09cc0401-02a80e02.png b/assets/amiibo/images/icon_09cc0401-02a80e02.png new file mode 100644 index 000000000..5683d4d60 Binary files /dev/null and b/assets/amiibo/images/icon_09cc0401-02a80e02.png differ diff --git a/assets/amiibo/images/icon_09cc0501-02a90e02.png b/assets/amiibo/images/icon_09cc0501-02a90e02.png new file mode 100644 index 000000000..14563a651 Binary files /dev/null and b/assets/amiibo/images/icon_09cc0501-02a90e02.png differ diff --git a/assets/amiibo/images/icon_09cd0101-02aa0e02.png b/assets/amiibo/images/icon_09cd0101-02aa0e02.png new file mode 100644 index 000000000..b078973ea Binary files /dev/null and b/assets/amiibo/images/icon_09cd0101-02aa0e02.png differ diff --git a/assets/amiibo/images/icon_09cd0201-02ab0e02.png b/assets/amiibo/images/icon_09cd0201-02ab0e02.png new file mode 100644 index 000000000..40e8ebe08 Binary files /dev/null and b/assets/amiibo/images/icon_09cd0201-02ab0e02.png differ diff --git a/assets/amiibo/images/icon_09cd0301-02ac0e02.png b/assets/amiibo/images/icon_09cd0301-02ac0e02.png new file mode 100644 index 000000000..728bdd9bc Binary files /dev/null and b/assets/amiibo/images/icon_09cd0301-02ac0e02.png differ diff --git a/assets/amiibo/images/icon_09cd0401-02ad0e02.png b/assets/amiibo/images/icon_09cd0401-02ad0e02.png new file mode 100644 index 000000000..dd4c21a1c Binary files /dev/null and b/assets/amiibo/images/icon_09cd0401-02ad0e02.png differ diff --git a/assets/amiibo/images/icon_09cd0501-02ae0e02.png b/assets/amiibo/images/icon_09cd0501-02ae0e02.png new file mode 100644 index 000000000..589141a07 Binary files /dev/null and b/assets/amiibo/images/icon_09cd0501-02ae0e02.png differ diff --git a/assets/amiibo/images/icon_09ce0101-02af0e02.png b/assets/amiibo/images/icon_09ce0101-02af0e02.png new file mode 100644 index 000000000..f11a9255d Binary files /dev/null and b/assets/amiibo/images/icon_09ce0101-02af0e02.png differ diff --git a/assets/amiibo/images/icon_09ce0201-02b00e02.png b/assets/amiibo/images/icon_09ce0201-02b00e02.png new file mode 100644 index 000000000..a00c613bd Binary files /dev/null and b/assets/amiibo/images/icon_09ce0201-02b00e02.png differ diff --git a/assets/amiibo/images/icon_09ce0301-02b10e02.png b/assets/amiibo/images/icon_09ce0301-02b10e02.png new file mode 100644 index 000000000..cd80a6c9d Binary files /dev/null and b/assets/amiibo/images/icon_09ce0301-02b10e02.png differ diff --git a/assets/amiibo/images/icon_09ce0401-02b20e02.png b/assets/amiibo/images/icon_09ce0401-02b20e02.png new file mode 100644 index 000000000..0a487fa8c Binary files /dev/null and b/assets/amiibo/images/icon_09ce0401-02b20e02.png differ diff --git a/assets/amiibo/images/icon_09ce0501-02b30e02.png b/assets/amiibo/images/icon_09ce0501-02b30e02.png new file mode 100644 index 000000000..f0b64e622 Binary files /dev/null and b/assets/amiibo/images/icon_09ce0501-02b30e02.png differ diff --git a/assets/amiibo/images/icon_09cf0101-02b40e02.png b/assets/amiibo/images/icon_09cf0101-02b40e02.png new file mode 100644 index 000000000..631fc0625 Binary files /dev/null and b/assets/amiibo/images/icon_09cf0101-02b40e02.png differ diff --git a/assets/amiibo/images/icon_09cf0201-02b50e02.png b/assets/amiibo/images/icon_09cf0201-02b50e02.png new file mode 100644 index 000000000..1369f4a0a Binary files /dev/null and b/assets/amiibo/images/icon_09cf0201-02b50e02.png differ diff --git a/assets/amiibo/images/icon_09cf0301-02b60e02.png b/assets/amiibo/images/icon_09cf0301-02b60e02.png new file mode 100644 index 000000000..47583c338 Binary files /dev/null and b/assets/amiibo/images/icon_09cf0301-02b60e02.png differ diff --git a/assets/amiibo/images/icon_09cf0401-02b70e02.png b/assets/amiibo/images/icon_09cf0401-02b70e02.png new file mode 100644 index 000000000..7ccca04fb Binary files /dev/null and b/assets/amiibo/images/icon_09cf0401-02b70e02.png differ diff --git a/assets/amiibo/images/icon_09cf0501-02b80e02.png b/assets/amiibo/images/icon_09cf0501-02b80e02.png new file mode 100644 index 000000000..d89f69c11 Binary files /dev/null and b/assets/amiibo/images/icon_09cf0501-02b80e02.png differ diff --git a/assets/amiibo/images/icon_09d00101-02b90e02.png b/assets/amiibo/images/icon_09d00101-02b90e02.png new file mode 100644 index 000000000..98251a253 Binary files /dev/null and b/assets/amiibo/images/icon_09d00101-02b90e02.png differ diff --git a/assets/amiibo/images/icon_09d00201-02ba0e02.png b/assets/amiibo/images/icon_09d00201-02ba0e02.png new file mode 100644 index 000000000..6fd847af8 Binary files /dev/null and b/assets/amiibo/images/icon_09d00201-02ba0e02.png differ diff --git a/assets/amiibo/images/icon_09d00301-02bb0e02.png b/assets/amiibo/images/icon_09d00301-02bb0e02.png new file mode 100644 index 000000000..d1777e410 Binary files /dev/null and b/assets/amiibo/images/icon_09d00301-02bb0e02.png differ diff --git a/assets/amiibo/images/icon_09d00401-02bc0e02.png b/assets/amiibo/images/icon_09d00401-02bc0e02.png new file mode 100644 index 000000000..d406b5401 Binary files /dev/null and b/assets/amiibo/images/icon_09d00401-02bc0e02.png differ diff --git a/assets/amiibo/images/icon_09d00501-02bd0e02.png b/assets/amiibo/images/icon_09d00501-02bd0e02.png new file mode 100644 index 000000000..3ace8999f Binary files /dev/null and b/assets/amiibo/images/icon_09d00501-02bd0e02.png differ diff --git a/assets/amiibo/images/icon_09d10101-02be0e02.png b/assets/amiibo/images/icon_09d10101-02be0e02.png new file mode 100644 index 000000000..faa60b54e Binary files /dev/null and b/assets/amiibo/images/icon_09d10101-02be0e02.png differ diff --git a/assets/amiibo/images/icon_09d10201-02bf0e02.png b/assets/amiibo/images/icon_09d10201-02bf0e02.png new file mode 100644 index 000000000..14d77a191 Binary files /dev/null and b/assets/amiibo/images/icon_09d10201-02bf0e02.png differ diff --git a/assets/amiibo/images/icon_09d10301-02c00e02.png b/assets/amiibo/images/icon_09d10301-02c00e02.png new file mode 100644 index 000000000..327294a0a Binary files /dev/null and b/assets/amiibo/images/icon_09d10301-02c00e02.png differ diff --git a/assets/amiibo/images/icon_09d10401-02c10e02.png b/assets/amiibo/images/icon_09d10401-02c10e02.png new file mode 100644 index 000000000..71d4cdbbf Binary files /dev/null and b/assets/amiibo/images/icon_09d10401-02c10e02.png differ diff --git a/assets/amiibo/images/icon_09d10501-02c20e02.png b/assets/amiibo/images/icon_09d10501-02c20e02.png new file mode 100644 index 000000000..4ec9c4fcd Binary files /dev/null and b/assets/amiibo/images/icon_09d10501-02c20e02.png differ diff --git a/assets/amiibo/images/icon_0a000001-03ab0502.png b/assets/amiibo/images/icon_0a000001-03ab0502.png new file mode 100644 index 000000000..510277318 Binary files /dev/null and b/assets/amiibo/images/icon_0a000001-03ab0502.png differ diff --git a/assets/amiibo/images/icon_0a010001-03ac0502.png b/assets/amiibo/images/icon_0a010001-03ac0502.png new file mode 100644 index 000000000..c7c554873 Binary files /dev/null and b/assets/amiibo/images/icon_0a010001-03ac0502.png differ diff --git a/assets/amiibo/images/icon_0a020001-03b30502.png b/assets/amiibo/images/icon_0a020001-03b30502.png new file mode 100644 index 000000000..d18384c2c Binary files /dev/null and b/assets/amiibo/images/icon_0a020001-03b30502.png differ diff --git a/assets/amiibo/images/icon_0a030001-03b40502.png b/assets/amiibo/images/icon_0a030001-03b40502.png new file mode 100644 index 000000000..926e0f1aa Binary files /dev/null and b/assets/amiibo/images/icon_0a030001-03b40502.png differ diff --git a/assets/amiibo/images/icon_0a040001-03b50502.png b/assets/amiibo/images/icon_0a040001-03b50502.png new file mode 100644 index 000000000..363f70b2f Binary files /dev/null and b/assets/amiibo/images/icon_0a040001-03b50502.png differ diff --git a/assets/amiibo/images/icon_0a050001-03b80502.png b/assets/amiibo/images/icon_0a050001-03b80502.png new file mode 100644 index 000000000..16d9bb7cd Binary files /dev/null and b/assets/amiibo/images/icon_0a050001-03b80502.png differ diff --git a/assets/amiibo/images/icon_0a060001-03ba0502.png b/assets/amiibo/images/icon_0a060001-03ba0502.png new file mode 100644 index 000000000..07bc3cd25 Binary files /dev/null and b/assets/amiibo/images/icon_0a060001-03ba0502.png differ diff --git a/assets/amiibo/images/icon_0a070001-03bc0502.png b/assets/amiibo/images/icon_0a070001-03bc0502.png new file mode 100644 index 000000000..3aa13de02 Binary files /dev/null and b/assets/amiibo/images/icon_0a070001-03bc0502.png differ diff --git a/assets/amiibo/images/icon_0a080001-03bd0502.png b/assets/amiibo/images/icon_0a080001-03bd0502.png new file mode 100644 index 000000000..caecf627c Binary files /dev/null and b/assets/amiibo/images/icon_0a080001-03bd0502.png differ diff --git a/assets/amiibo/images/icon_0a090001-03c00502.png b/assets/amiibo/images/icon_0a090001-03c00502.png new file mode 100644 index 000000000..d86a8f141 Binary files /dev/null and b/assets/amiibo/images/icon_0a090001-03c00502.png differ diff --git a/assets/amiibo/images/icon_0a0a0001-03c10502.png b/assets/amiibo/images/icon_0a0a0001-03c10502.png new file mode 100644 index 000000000..746aacea7 Binary files /dev/null and b/assets/amiibo/images/icon_0a0a0001-03c10502.png differ diff --git a/assets/amiibo/images/icon_0a0b0001-03c20502.png b/assets/amiibo/images/icon_0a0b0001-03c20502.png new file mode 100644 index 000000000..e29db73ab Binary files /dev/null and b/assets/amiibo/images/icon_0a0b0001-03c20502.png differ diff --git a/assets/amiibo/images/icon_0a0c0001-03c30502.png b/assets/amiibo/images/icon_0a0c0001-03c30502.png new file mode 100644 index 000000000..22f1a3f45 Binary files /dev/null and b/assets/amiibo/images/icon_0a0c0001-03c30502.png differ diff --git a/assets/amiibo/images/icon_0a0d0001-03c40502.png b/assets/amiibo/images/icon_0a0d0001-03c40502.png new file mode 100644 index 000000000..91c74f3c2 Binary files /dev/null and b/assets/amiibo/images/icon_0a0d0001-03c40502.png differ diff --git a/assets/amiibo/images/icon_0a0e0001-03c50502.png b/assets/amiibo/images/icon_0a0e0001-03c50502.png new file mode 100644 index 000000000..40f7bbc90 Binary files /dev/null and b/assets/amiibo/images/icon_0a0e0001-03c50502.png differ diff --git a/assets/amiibo/images/icon_0a0f0001-03c60502.png b/assets/amiibo/images/icon_0a0f0001-03c60502.png new file mode 100644 index 000000000..718cf8cb1 Binary files /dev/null and b/assets/amiibo/images/icon_0a0f0001-03c60502.png differ diff --git a/assets/amiibo/images/icon_0a100001-03c70502.png b/assets/amiibo/images/icon_0a100001-03c70502.png new file mode 100644 index 000000000..a94756028 Binary files /dev/null and b/assets/amiibo/images/icon_0a100001-03c70502.png differ diff --git a/assets/amiibo/images/icon_0a110001-03c80502.png b/assets/amiibo/images/icon_0a110001-03c80502.png new file mode 100644 index 000000000..8d1780827 Binary files /dev/null and b/assets/amiibo/images/icon_0a110001-03c80502.png differ diff --git a/assets/amiibo/images/icon_0a120001-03c90502.png b/assets/amiibo/images/icon_0a120001-03c90502.png new file mode 100644 index 000000000..501457991 Binary files /dev/null and b/assets/amiibo/images/icon_0a120001-03c90502.png differ diff --git a/assets/amiibo/images/icon_0a130001-03ca0502.png b/assets/amiibo/images/icon_0a130001-03ca0502.png new file mode 100644 index 000000000..8f00679e7 Binary files /dev/null and b/assets/amiibo/images/icon_0a130001-03ca0502.png differ diff --git a/assets/amiibo/images/icon_0a140001-03cb0502.png b/assets/amiibo/images/icon_0a140001-03cb0502.png new file mode 100644 index 000000000..2bb0a28c1 Binary files /dev/null and b/assets/amiibo/images/icon_0a140001-03cb0502.png differ diff --git a/assets/amiibo/images/icon_0a150001-03cc0502.png b/assets/amiibo/images/icon_0a150001-03cc0502.png new file mode 100644 index 000000000..5c4613f86 Binary files /dev/null and b/assets/amiibo/images/icon_0a150001-03cc0502.png differ diff --git a/assets/amiibo/images/icon_0a160001-03cd0502.png b/assets/amiibo/images/icon_0a160001-03cd0502.png new file mode 100644 index 000000000..d30a7be4e Binary files /dev/null and b/assets/amiibo/images/icon_0a160001-03cd0502.png differ diff --git a/assets/amiibo/images/icon_0a170001-03ce0502.png b/assets/amiibo/images/icon_0a170001-03ce0502.png new file mode 100644 index 000000000..ce8a63e93 Binary files /dev/null and b/assets/amiibo/images/icon_0a170001-03ce0502.png differ diff --git a/assets/amiibo/images/icon_0a180001-03cf0502.png b/assets/amiibo/images/icon_0a180001-03cf0502.png new file mode 100644 index 000000000..e6d69cef8 Binary files /dev/null and b/assets/amiibo/images/icon_0a180001-03cf0502.png differ diff --git a/assets/amiibo/images/icon_0a190001-03d00502.png b/assets/amiibo/images/icon_0a190001-03d00502.png new file mode 100644 index 000000000..49be62741 Binary files /dev/null and b/assets/amiibo/images/icon_0a190001-03d00502.png differ diff --git a/assets/amiibo/images/icon_0a1a0001-03d10502.png b/assets/amiibo/images/icon_0a1a0001-03d10502.png new file mode 100644 index 000000000..8d66162d9 Binary files /dev/null and b/assets/amiibo/images/icon_0a1a0001-03d10502.png differ diff --git a/assets/amiibo/images/icon_0a1b0001-03d20502.png b/assets/amiibo/images/icon_0a1b0001-03d20502.png new file mode 100644 index 000000000..5e8a03b75 Binary files /dev/null and b/assets/amiibo/images/icon_0a1b0001-03d20502.png differ diff --git a/assets/amiibo/images/icon_0a1c0001-03d30502.png b/assets/amiibo/images/icon_0a1c0001-03d30502.png new file mode 100644 index 000000000..ae4162a06 Binary files /dev/null and b/assets/amiibo/images/icon_0a1c0001-03d30502.png differ diff --git a/assets/amiibo/images/icon_0a1d0001-03d40502.png b/assets/amiibo/images/icon_0a1d0001-03d40502.png new file mode 100644 index 000000000..4f1882c01 Binary files /dev/null and b/assets/amiibo/images/icon_0a1d0001-03d40502.png differ diff --git a/assets/amiibo/images/icon_0a1e0001-03d50502.png b/assets/amiibo/images/icon_0a1e0001-03d50502.png new file mode 100644 index 000000000..28708b09e Binary files /dev/null and b/assets/amiibo/images/icon_0a1e0001-03d50502.png differ diff --git a/assets/amiibo/images/icon_0a1f0001-03d60502.png b/assets/amiibo/images/icon_0a1f0001-03d60502.png new file mode 100644 index 000000000..e7af35a0b Binary files /dev/null and b/assets/amiibo/images/icon_0a1f0001-03d60502.png differ diff --git a/assets/amiibo/images/icon_0a200001-03d70502.png b/assets/amiibo/images/icon_0a200001-03d70502.png new file mode 100644 index 000000000..6d22d1c07 Binary files /dev/null and b/assets/amiibo/images/icon_0a200001-03d70502.png differ diff --git a/assets/amiibo/images/icon_0a400000-041d0002.png b/assets/amiibo/images/icon_0a400000-041d0002.png new file mode 100644 index 000000000..9274e6a6f Binary files /dev/null and b/assets/amiibo/images/icon_0a400000-041d0002.png differ diff --git a/assets/amiibo/images/icon_19020000-03830002.png b/assets/amiibo/images/icon_19020000-03830002.png new file mode 100644 index 000000000..380231ccd Binary files /dev/null and b/assets/amiibo/images/icon_19020000-03830002.png differ diff --git a/assets/amiibo/images/icon_19060000-00240002.png b/assets/amiibo/images/icon_19060000-00240002.png new file mode 100644 index 000000000..cb3ed0a25 Binary files /dev/null and b/assets/amiibo/images/icon_19060000-00240002.png differ diff --git a/assets/amiibo/images/icon_19070000-03840002.png b/assets/amiibo/images/icon_19070000-03840002.png new file mode 100644 index 000000000..973b04912 Binary files /dev/null and b/assets/amiibo/images/icon_19070000-03840002.png differ diff --git a/assets/amiibo/images/icon_19190000-00090002.png b/assets/amiibo/images/icon_19190000-00090002.png new file mode 100644 index 000000000..bb245cc1a Binary files /dev/null and b/assets/amiibo/images/icon_19190000-00090002.png differ diff --git a/assets/amiibo/images/icon_19270000-00260002.png b/assets/amiibo/images/icon_19270000-00260002.png new file mode 100644 index 000000000..95ef6c6b4 Binary files /dev/null and b/assets/amiibo/images/icon_19270000-00260002.png differ diff --git a/assets/amiibo/images/icon_19960000-023d0002.png b/assets/amiibo/images/icon_19960000-023d0002.png new file mode 100644 index 000000000..fbf00009f Binary files /dev/null and b/assets/amiibo/images/icon_19960000-023d0002.png differ diff --git a/assets/amiibo/images/icon_19ac0000-03850002.png b/assets/amiibo/images/icon_19ac0000-03850002.png new file mode 100644 index 000000000..907fa0f29 Binary files /dev/null and b/assets/amiibo/images/icon_19ac0000-03850002.png differ diff --git a/assets/amiibo/images/icon_1ac00000-00110002.png b/assets/amiibo/images/icon_1ac00000-00110002.png new file mode 100644 index 000000000..160d69dd8 Binary files /dev/null and b/assets/amiibo/images/icon_1ac00000-00110002.png differ diff --git a/assets/amiibo/images/icon_1b920000-00250002.png b/assets/amiibo/images/icon_1b920000-00250002.png new file mode 100644 index 000000000..585dfffce Binary files /dev/null and b/assets/amiibo/images/icon_1b920000-00250002.png differ diff --git a/assets/amiibo/images/icon_1bd70000-03860002.png b/assets/amiibo/images/icon_1bd70000-03860002.png new file mode 100644 index 000000000..0a9991ee9 Binary files /dev/null and b/assets/amiibo/images/icon_1bd70000-03860002.png differ diff --git a/assets/amiibo/images/icon_1d000001-025c0d02.png b/assets/amiibo/images/icon_1d000001-025c0d02.png new file mode 100644 index 000000000..8aa9c256a Binary files /dev/null and b/assets/amiibo/images/icon_1d000001-025c0d02.png differ diff --git a/assets/amiibo/images/icon_1d010000-03750d02.png b/assets/amiibo/images/icon_1d010000-03750d02.png new file mode 100644 index 000000000..df0819ccb Binary files /dev/null and b/assets/amiibo/images/icon_1d010000-03750d02.png differ diff --git a/assets/amiibo/images/icon_1d400000-03870002.png b/assets/amiibo/images/icon_1d400000-03870002.png new file mode 100644 index 000000000..709bdb51d Binary files /dev/null and b/assets/amiibo/images/icon_1d400000-03870002.png differ diff --git a/assets/amiibo/images/icon_1f000000-000a0002.png b/assets/amiibo/images/icon_1f000000-000a0002.png new file mode 100644 index 000000000..a5b1eabbe Binary files /dev/null and b/assets/amiibo/images/icon_1f000000-000a0002.png differ diff --git a/assets/amiibo/images/icon_1f000000-02540c02.png b/assets/amiibo/images/icon_1f000000-02540c02.png new file mode 100644 index 000000000..8ddf186b9 Binary files /dev/null and b/assets/amiibo/images/icon_1f000000-02540c02.png differ diff --git a/assets/amiibo/images/icon_1f010000-00270002.png b/assets/amiibo/images/icon_1f010000-00270002.png new file mode 100644 index 000000000..448d7ede5 Binary files /dev/null and b/assets/amiibo/images/icon_1f010000-00270002.png differ diff --git a/assets/amiibo/images/icon_1f010000-02550c02.png b/assets/amiibo/images/icon_1f010000-02550c02.png new file mode 100644 index 000000000..d25052f61 Binary files /dev/null and b/assets/amiibo/images/icon_1f010000-02550c02.png differ diff --git a/assets/amiibo/images/icon_1f020000-00280002.png b/assets/amiibo/images/icon_1f020000-00280002.png new file mode 100644 index 000000000..d0d74dceb Binary files /dev/null and b/assets/amiibo/images/icon_1f020000-00280002.png differ diff --git a/assets/amiibo/images/icon_1f020000-02560c02.png b/assets/amiibo/images/icon_1f020000-02560c02.png new file mode 100644 index 000000000..20b535278 Binary files /dev/null and b/assets/amiibo/images/icon_1f020000-02560c02.png differ diff --git a/assets/amiibo/images/icon_1f030000-02570c02.png b/assets/amiibo/images/icon_1f030000-02570c02.png new file mode 100644 index 000000000..e4632f365 Binary files /dev/null and b/assets/amiibo/images/icon_1f030000-02570c02.png differ diff --git a/assets/amiibo/images/icon_1f400000-035e1002.png b/assets/amiibo/images/icon_1f400000-035e1002.png new file mode 100644 index 000000000..37f22b45a Binary files /dev/null and b/assets/amiibo/images/icon_1f400000-035e1002.png differ diff --git a/assets/amiibo/images/icon_21000000-000b0002.png b/assets/amiibo/images/icon_21000000-000b0002.png new file mode 100644 index 000000000..2760763fe Binary files /dev/null and b/assets/amiibo/images/icon_21000000-000b0002.png differ diff --git a/assets/amiibo/images/icon_21010000-00180002.png b/assets/amiibo/images/icon_21010000-00180002.png new file mode 100644 index 000000000..b17a65b05 Binary files /dev/null and b/assets/amiibo/images/icon_21010000-00180002.png differ diff --git a/assets/amiibo/images/icon_21020000-00290002.png b/assets/amiibo/images/icon_21020000-00290002.png new file mode 100644 index 000000000..7f39e642f Binary files /dev/null and b/assets/amiibo/images/icon_21020000-00290002.png differ diff --git a/assets/amiibo/images/icon_21030000-002a0002.png b/assets/amiibo/images/icon_21030000-002a0002.png new file mode 100644 index 000000000..f90edce7f Binary files /dev/null and b/assets/amiibo/images/icon_21030000-002a0002.png differ diff --git a/assets/amiibo/images/icon_21040000-02520002.png b/assets/amiibo/images/icon_21040000-02520002.png new file mode 100644 index 000000000..60dfd9339 Binary files /dev/null and b/assets/amiibo/images/icon_21040000-02520002.png differ diff --git a/assets/amiibo/images/icon_21050000-025a0002.png b/assets/amiibo/images/icon_21050000-025a0002.png new file mode 100644 index 000000000..8d351018c Binary files /dev/null and b/assets/amiibo/images/icon_21050000-025a0002.png differ diff --git a/assets/amiibo/images/icon_21050100-03630002.png b/assets/amiibo/images/icon_21050100-03630002.png new file mode 100644 index 000000000..9d8eabefb Binary files /dev/null and b/assets/amiibo/images/icon_21050100-03630002.png differ diff --git a/assets/amiibo/images/icon_21060000-03601202.png b/assets/amiibo/images/icon_21060000-03601202.png new file mode 100644 index 000000000..0d0130aef Binary files /dev/null and b/assets/amiibo/images/icon_21060000-03601202.png differ diff --git a/assets/amiibo/images/icon_21070000-03611202.png b/assets/amiibo/images/icon_21070000-03611202.png new file mode 100644 index 000000000..1b7908769 Binary files /dev/null and b/assets/amiibo/images/icon_21070000-03611202.png differ diff --git a/assets/amiibo/images/icon_21080000-036f1202.png b/assets/amiibo/images/icon_21080000-036f1202.png new file mode 100644 index 000000000..383ee8346 Binary files /dev/null and b/assets/amiibo/images/icon_21080000-036f1202.png differ diff --git a/assets/amiibo/images/icon_21080000-03880002.png b/assets/amiibo/images/icon_21080000-03880002.png new file mode 100644 index 000000000..b33e47866 Binary files /dev/null and b/assets/amiibo/images/icon_21080000-03880002.png differ diff --git a/assets/amiibo/images/icon_21090000-03701202.png b/assets/amiibo/images/icon_21090000-03701202.png new file mode 100644 index 000000000..b7650618d Binary files /dev/null and b/assets/amiibo/images/icon_21090000-03701202.png differ diff --git a/assets/amiibo/images/icon_210b0000-03a50002.png b/assets/amiibo/images/icon_210b0000-03a50002.png new file mode 100644 index 000000000..51c6bca8c Binary files /dev/null and b/assets/amiibo/images/icon_210b0000-03a50002.png differ diff --git a/assets/amiibo/images/icon_22400000-002b0002.png b/assets/amiibo/images/icon_22400000-002b0002.png new file mode 100644 index 000000000..c2a350ac3 Binary files /dev/null and b/assets/amiibo/images/icon_22400000-002b0002.png differ diff --git a/assets/amiibo/images/icon_22410000-041e0002.png b/assets/amiibo/images/icon_22410000-041e0002.png new file mode 100644 index 000000000..56ac2d891 Binary files /dev/null and b/assets/amiibo/images/icon_22410000-041e0002.png differ diff --git a/assets/amiibo/images/icon_22420000-041f0002.png b/assets/amiibo/images/icon_22420000-041f0002.png new file mode 100644 index 000000000..98ad4d2c1 Binary files /dev/null and b/assets/amiibo/images/icon_22420000-041f0002.png differ diff --git a/assets/amiibo/images/icon_22430000-043d1b02.png b/assets/amiibo/images/icon_22430000-043d1b02.png new file mode 100644 index 000000000..e26698a04 Binary files /dev/null and b/assets/amiibo/images/icon_22430000-043d1b02.png differ diff --git a/assets/amiibo/images/icon_22440000-043e1b02.png b/assets/amiibo/images/icon_22440000-043e1b02.png new file mode 100644 index 000000000..79b056f02 Binary files /dev/null and b/assets/amiibo/images/icon_22440000-043e1b02.png differ diff --git a/assets/amiibo/images/icon_22800000-002c0002.png b/assets/amiibo/images/icon_22800000-002c0002.png new file mode 100644 index 000000000..dd550c5cb Binary files /dev/null and b/assets/amiibo/images/icon_22800000-002c0002.png differ diff --git a/assets/amiibo/images/icon_22810000-02510002.png b/assets/amiibo/images/icon_22810000-02510002.png new file mode 100644 index 000000000..2956ad517 Binary files /dev/null and b/assets/amiibo/images/icon_22810000-02510002.png differ diff --git a/assets/amiibo/images/icon_22c00000-003a0202.png b/assets/amiibo/images/icon_22c00000-003a0202.png new file mode 100644 index 000000000..c2849b126 Binary files /dev/null and b/assets/amiibo/images/icon_22c00000-003a0202.png differ diff --git a/assets/amiibo/images/icon_32000000-00300002.png b/assets/amiibo/images/icon_32000000-00300002.png new file mode 100644 index 000000000..e1b5f5b6c Binary files /dev/null and b/assets/amiibo/images/icon_32000000-00300002.png differ diff --git a/assets/amiibo/images/icon_32400000-025b0002.png b/assets/amiibo/images/icon_32400000-025b0002.png new file mode 100644 index 000000000..50e456668 Binary files /dev/null and b/assets/amiibo/images/icon_32400000-025b0002.png differ diff --git a/assets/amiibo/images/icon_32400100-03640002.png b/assets/amiibo/images/icon_32400100-03640002.png new file mode 100644 index 000000000..95994cb88 Binary files /dev/null and b/assets/amiibo/images/icon_32400100-03640002.png differ diff --git a/assets/amiibo/images/icon_33400000-00320002.png b/assets/amiibo/images/icon_33400000-00320002.png new file mode 100644 index 000000000..eb0ec7edd Binary files /dev/null and b/assets/amiibo/images/icon_33400000-00320002.png differ diff --git a/assets/amiibo/images/icon_33800000-03781402.png b/assets/amiibo/images/icon_33800000-03781402.png new file mode 100644 index 000000000..fe1caf2c3 Binary files /dev/null and b/assets/amiibo/images/icon_33800000-03781402.png differ diff --git a/assets/amiibo/images/icon_33c00000-04200002.png b/assets/amiibo/images/icon_33c00000-04200002.png new file mode 100644 index 000000000..b234756ef Binary files /dev/null and b/assets/amiibo/images/icon_33c00000-04200002.png differ diff --git a/assets/amiibo/images/icon_34800000-00310002.png b/assets/amiibo/images/icon_34800000-00310002.png new file mode 100644 index 000000000..8a47fc455 Binary files /dev/null and b/assets/amiibo/images/icon_34800000-00310002.png differ diff --git a/assets/amiibo/images/icon_34800000-02580002.png b/assets/amiibo/images/icon_34800000-02580002.png new file mode 100644 index 000000000..9bb39319f Binary files /dev/null and b/assets/amiibo/images/icon_34800000-02580002.png differ diff --git a/assets/amiibo/images/icon_34800000-03791502.png b/assets/amiibo/images/icon_34800000-03791502.png new file mode 100644 index 000000000..67c2d9bd5 Binary files /dev/null and b/assets/amiibo/images/icon_34800000-03791502.png differ diff --git a/assets/amiibo/images/icon_34c00000-02530002.png b/assets/amiibo/images/icon_34c00000-02530002.png new file mode 100644 index 000000000..67b03e459 Binary files /dev/null and b/assets/amiibo/images/icon_34c00000-02530002.png differ diff --git a/assets/amiibo/images/icon_34c10000-03890002.png b/assets/amiibo/images/icon_34c10000-03890002.png new file mode 100644 index 000000000..4f42acca7 Binary files /dev/null and b/assets/amiibo/images/icon_34c10000-03890002.png differ diff --git a/assets/amiibo/images/icon_35000100-02e10f02.png b/assets/amiibo/images/icon_35000100-02e10f02.png new file mode 100644 index 000000000..98ea3e053 Binary files /dev/null and b/assets/amiibo/images/icon_35000100-02e10f02.png differ diff --git a/assets/amiibo/images/icon_35000200-02e20f02.png b/assets/amiibo/images/icon_35000200-02e20f02.png new file mode 100644 index 000000000..46ccb906e Binary files /dev/null and b/assets/amiibo/images/icon_35000200-02e20f02.png differ diff --git a/assets/amiibo/images/icon_35010000-02e30f02.png b/assets/amiibo/images/icon_35010000-02e30f02.png new file mode 100644 index 000000000..747b4c001 Binary files /dev/null and b/assets/amiibo/images/icon_35010000-02e30f02.png differ diff --git a/assets/amiibo/images/icon_35020100-02e40f02.png b/assets/amiibo/images/icon_35020100-02e40f02.png new file mode 100644 index 000000000..bb15618c1 Binary files /dev/null and b/assets/amiibo/images/icon_35020100-02e40f02.png differ diff --git a/assets/amiibo/images/icon_35030100-02e50f02.png b/assets/amiibo/images/icon_35030100-02e50f02.png new file mode 100644 index 000000000..ce4ca715c Binary files /dev/null and b/assets/amiibo/images/icon_35030100-02e50f02.png differ diff --git a/assets/amiibo/images/icon_35040100-02e60f02.png b/assets/amiibo/images/icon_35040100-02e60f02.png new file mode 100644 index 000000000..82ef53cd8 Binary files /dev/null and b/assets/amiibo/images/icon_35040100-02e60f02.png differ diff --git a/assets/amiibo/images/icon_35050000-040c0f02.png b/assets/amiibo/images/icon_35050000-040c0f02.png new file mode 100644 index 000000000..f36c21ae5 Binary files /dev/null and b/assets/amiibo/images/icon_35050000-040c0f02.png differ diff --git a/assets/amiibo/images/icon_35060000-040d0f02.png b/assets/amiibo/images/icon_35060000-040d0f02.png new file mode 100644 index 000000000..c3e362e30 Binary files /dev/null and b/assets/amiibo/images/icon_35060000-040d0f02.png differ diff --git a/assets/amiibo/images/icon_35070000-040e0f02.png b/assets/amiibo/images/icon_35070000-040e0f02.png new file mode 100644 index 000000000..0268c77df Binary files /dev/null and b/assets/amiibo/images/icon_35070000-040e0f02.png differ diff --git a/assets/amiibo/images/icon_35080000-040f1802.png b/assets/amiibo/images/icon_35080000-040f1802.png new file mode 100644 index 000000000..180032a97 Binary files /dev/null and b/assets/amiibo/images/icon_35080000-040f1802.png differ diff --git a/assets/amiibo/images/icon_35090000-04101802.png b/assets/amiibo/images/icon_35090000-04101802.png new file mode 100644 index 000000000..b51e56865 Binary files /dev/null and b/assets/amiibo/images/icon_35090000-04101802.png differ diff --git a/assets/amiibo/images/icon_35090100-042b1802.png b/assets/amiibo/images/icon_35090100-042b1802.png new file mode 100644 index 000000000..5f9be1e18 Binary files /dev/null and b/assets/amiibo/images/icon_35090100-042b1802.png differ diff --git a/assets/amiibo/images/icon_350a0000-04111802.png b/assets/amiibo/images/icon_350a0000-04111802.png new file mode 100644 index 000000000..86a830c38 Binary files /dev/null and b/assets/amiibo/images/icon_350a0000-04111802.png differ diff --git a/assets/amiibo/images/icon_350a0100-042c1802.png b/assets/amiibo/images/icon_350a0100-042c1802.png new file mode 100644 index 000000000..0bf05657b Binary files /dev/null and b/assets/amiibo/images/icon_350a0100-042c1802.png differ diff --git a/assets/amiibo/images/icon_350b0000-042d1802.png b/assets/amiibo/images/icon_350b0000-042d1802.png new file mode 100644 index 000000000..0cbfd3112 Binary files /dev/null and b/assets/amiibo/images/icon_350b0000-042d1802.png differ diff --git a/assets/amiibo/images/icon_35c00000-02500a02.png b/assets/amiibo/images/icon_35c00000-02500a02.png new file mode 100644 index 000000000..3b451e5fd Binary files /dev/null and b/assets/amiibo/images/icon_35c00000-02500a02.png differ diff --git a/assets/amiibo/images/icon_35c00000-03920a02.png b/assets/amiibo/images/icon_35c00000-03920a02.png new file mode 100644 index 000000000..3eff209bf Binary files /dev/null and b/assets/amiibo/images/icon_35c00000-03920a02.png differ diff --git a/assets/amiibo/images/icon_35c10000-036c0a02.png b/assets/amiibo/images/icon_35c10000-036c0a02.png new file mode 100644 index 000000000..6a6cc340b Binary files /dev/null and b/assets/amiibo/images/icon_35c10000-036c0a02.png differ diff --git a/assets/amiibo/images/icon_35c20000-036d0a02.png b/assets/amiibo/images/icon_35c20000-036d0a02.png new file mode 100644 index 000000000..a40f12e06 Binary files /dev/null and b/assets/amiibo/images/icon_35c20000-036d0a02.png differ diff --git a/assets/amiibo/images/icon_35c30000-036e0a02.png b/assets/amiibo/images/icon_35c30000-036e0a02.png new file mode 100644 index 000000000..f9b10e3b4 Binary files /dev/null and b/assets/amiibo/images/icon_35c30000-036e0a02.png differ diff --git a/assets/amiibo/images/icon_36000000-02590002.png b/assets/amiibo/images/icon_36000000-02590002.png new file mode 100644 index 000000000..5f989aa44 Binary files /dev/null and b/assets/amiibo/images/icon_36000000-02590002.png differ diff --git a/assets/amiibo/images/icon_36000100-03620002.png b/assets/amiibo/images/icon_36000100-03620002.png new file mode 100644 index 000000000..9d7fd0bb7 Binary files /dev/null and b/assets/amiibo/images/icon_36000100-03620002.png differ diff --git a/assets/amiibo/images/icon_36010000-04210002.png b/assets/amiibo/images/icon_36010000-04210002.png new file mode 100644 index 000000000..ea0a4faca Binary files /dev/null and b/assets/amiibo/images/icon_36010000-04210002.png differ diff --git a/assets/amiibo/images/icon_36400000-03a20002.png b/assets/amiibo/images/icon_36400000-03a20002.png new file mode 100644 index 000000000..ac5396b0a Binary files /dev/null and b/assets/amiibo/images/icon_36400000-03a20002.png differ diff --git a/assets/amiibo/images/icon_37400001-03741402.png b/assets/amiibo/images/icon_37400001-03741402.png new file mode 100644 index 000000000..379f45227 Binary files /dev/null and b/assets/amiibo/images/icon_37400001-03741402.png differ diff --git a/assets/amiibo/images/icon_37800000-038a0002.png b/assets/amiibo/images/icon_37800000-038a0002.png new file mode 100644 index 000000000..d9ee41829 Binary files /dev/null and b/assets/amiibo/images/icon_37800000-038a0002.png differ diff --git a/assets/amiibo/images/icon_37c00000-038b0002.png b/assets/amiibo/images/icon_37c00000-038b0002.png new file mode 100644 index 000000000..b27281627 Binary files /dev/null and b/assets/amiibo/images/icon_37c00000-038b0002.png differ diff --git a/assets/amiibo/images/icon_37c10000-038c0002.png b/assets/amiibo/images/icon_37c10000-038c0002.png new file mode 100644 index 000000000..80de69f07 Binary files /dev/null and b/assets/amiibo/images/icon_37c10000-038c0002.png differ diff --git a/assets/amiibo/images/icon_38000001-03931702.png b/assets/amiibo/images/icon_38000001-03931702.png new file mode 100644 index 000000000..ef4ac5d96 Binary files /dev/null and b/assets/amiibo/images/icon_38000001-03931702.png differ diff --git a/assets/amiibo/images/icon_38010001-03941702.png b/assets/amiibo/images/icon_38010001-03941702.png new file mode 100644 index 000000000..9eac608a1 Binary files /dev/null and b/assets/amiibo/images/icon_38010001-03941702.png differ diff --git a/assets/amiibo/images/icon_38020001-03951702.png b/assets/amiibo/images/icon_38020001-03951702.png new file mode 100644 index 000000000..c9871b605 Binary files /dev/null and b/assets/amiibo/images/icon_38020001-03951702.png differ diff --git a/assets/amiibo/images/icon_38030001-03961702.png b/assets/amiibo/images/icon_38030001-03961702.png new file mode 100644 index 000000000..5df5e5451 Binary files /dev/null and b/assets/amiibo/images/icon_38030001-03961702.png differ diff --git a/assets/amiibo/images/icon_38040001-03971702.png b/assets/amiibo/images/icon_38040001-03971702.png new file mode 100644 index 000000000..7a4ea23f2 Binary files /dev/null and b/assets/amiibo/images/icon_38040001-03971702.png differ diff --git a/assets/amiibo/images/icon_38050001-03981702.png b/assets/amiibo/images/icon_38050001-03981702.png new file mode 100644 index 000000000..761af21c2 Binary files /dev/null and b/assets/amiibo/images/icon_38050001-03981702.png differ diff --git a/assets/amiibo/images/icon_38400001-04241902.png b/assets/amiibo/images/icon_38400001-04241902.png new file mode 100644 index 000000000..026dba66e Binary files /dev/null and b/assets/amiibo/images/icon_38400001-04241902.png differ diff --git a/assets/amiibo/images/icon_38410001-04251902.png b/assets/amiibo/images/icon_38410001-04251902.png new file mode 100644 index 000000000..ca0e85254 Binary files /dev/null and b/assets/amiibo/images/icon_38410001-04251902.png differ diff --git a/assets/amiibo/images/icon_38420001-04261902.png b/assets/amiibo/images/icon_38420001-04261902.png new file mode 100644 index 000000000..d2124f8f8 Binary files /dev/null and b/assets/amiibo/images/icon_38420001-04261902.png differ diff --git a/assets/amiibo/images/icon_38430001-04271902.png b/assets/amiibo/images/icon_38430001-04271902.png new file mode 100644 index 000000000..d3b43a51c Binary files /dev/null and b/assets/amiibo/images/icon_38430001-04271902.png differ diff --git a/assets/amiibo/images/icon_38440001-04281902.png b/assets/amiibo/images/icon_38440001-04281902.png new file mode 100644 index 000000000..e492f8682 Binary files /dev/null and b/assets/amiibo/images/icon_38440001-04281902.png differ diff --git a/assets/amiibo/images/icon_38450001-04291902.png b/assets/amiibo/images/icon_38450001-04291902.png new file mode 100644 index 000000000..45fcc10f7 Binary files /dev/null and b/assets/amiibo/images/icon_38450001-04291902.png differ diff --git a/assets/amiibo/images/icon_38460001-042a1902.png b/assets/amiibo/images/icon_38460001-042a1902.png new file mode 100644 index 000000000..7d6001adb Binary files /dev/null and b/assets/amiibo/images/icon_38460001-042a1902.png differ diff --git a/assets/amiibo/images/icon_38c00000-03911602.png b/assets/amiibo/images/icon_38c00000-03911602.png new file mode 100644 index 000000000..c0b859397 Binary files /dev/null and b/assets/amiibo/images/icon_38c00000-03911602.png differ diff --git a/assets/amiibo/images/icon_3a000000-03a10002.png b/assets/amiibo/images/icon_3a000000-03a10002.png new file mode 100644 index 000000000..74d62f68d Binary files /dev/null and b/assets/amiibo/images/icon_3a000000-03a10002.png differ diff --git a/assets/amiibo/images/icon_3b400000-03a30002.png b/assets/amiibo/images/icon_3b400000-03a30002.png new file mode 100644 index 000000000..31f46a05e Binary files /dev/null and b/assets/amiibo/images/icon_3b400000-03a30002.png differ diff --git a/assets/amiibo/images/icon_3c800000-03a40002.png b/assets/amiibo/images/icon_3c800000-03a40002.png new file mode 100644 index 000000000..a84d3e7c0 Binary files /dev/null and b/assets/amiibo/images/icon_3c800000-03a40002.png differ diff --git a/assets/amiibo/images/icon_3dc00000-04220002.png b/assets/amiibo/images/icon_3dc00000-04220002.png new file mode 100644 index 000000000..cf567ee88 Binary files /dev/null and b/assets/amiibo/images/icon_3dc00000-04220002.png differ diff --git a/assets/amiibo/images/icon_3dc10000-04230002.png b/assets/amiibo/images/icon_3dc10000-04230002.png new file mode 100644 index 000000000..f218ad8d9 Binary files /dev/null and b/assets/amiibo/images/icon_3dc10000-04230002.png differ diff --git a/assets/amiibo/images/icon_3f000000-042e0002.png b/assets/amiibo/images/icon_3f000000-042e0002.png new file mode 100644 index 000000000..90f97bd2d Binary files /dev/null and b/assets/amiibo/images/icon_3f000000-042e0002.png differ diff --git a/distribution/linux/Ryujinx.sh b/distribution/linux/Ryujinx.sh index 30eb14399..daeea9bfd 100755 --- a/distribution/linux/Ryujinx.sh +++ b/distribution/linux/Ryujinx.sh @@ -14,7 +14,7 @@ if [ -z "$RYUJINX_BIN" ]; then exit 1 fi -COMMAND="env DOTNET_EnableAlternateStackCheck=1" +COMMAND="env LANG=C.UTF-8 DOTNET_EnableAlternateStackCheck=1" if command -v gamemoderun > /dev/null 2>&1; then COMMAND="$COMMAND gamemoderun" diff --git a/distribution/linux/appimage/AppRun b/distribution/linux/appimage/AppRun new file mode 100755 index 000000000..adbb70a0a --- /dev/null +++ b/distribution/linux/appimage/AppRun @@ -0,0 +1,3 @@ +#!/bin/sh +CURRENTDIR="$(readlink -f "$(dirname "$0")")" +exec "$CURRENTDIR"/usr/bin/Ryujinx.sh "$@" diff --git a/distribution/linux/appimage/build-appimage.sh b/distribution/linux/appimage/build-appimage.sh new file mode 100755 index 000000000..5c32d78a8 --- /dev/null +++ b/distribution/linux/appimage/build-appimage.sh @@ -0,0 +1,33 @@ +#!/bin/sh +set -eu + +ROOTDIR="$(readlink -f "$(dirname "$0")")"/../../../ +cd "$ROOTDIR" + +BUILDDIR=${BUILDDIR:-publish} +OUTDIR=${OUTDIR:-publish_appimage} +UFLAG=${UFLAG:-"gh-releases-zsync|GreemDev|ryujinx|latest|*-x64.AppImage.zsync"} + +rm -rf AppDir +mkdir -p AppDir/usr/bin + +cp distribution/linux/Ryujinx.desktop AppDir/Ryujinx.desktop +cp distribution/linux/appimage/AppRun AppDir/AppRun +cp src/Ryujinx.UI.Common/Resources/Logo_Ryujinx.png AppDir/Ryujinx.svg + + +cp -r "$BUILDDIR"/* AppDir/usr/bin/ + +# Ensure necessary bins are set as executable +chmod +x AppDir/AppRun AppDir/usr/bin/Ryujinx* + +mkdir -p "$OUTDIR" + +appimagetool --comp zstd --mksquashfs-opt -Xcompression-level --mksquashfs-opt 21 \ + -u "$UFLAG" \ + AppDir "$OUTDIR"/Ryujinx.AppImage + +# Move zsync file needed for delta updates +if [ "$RELEASE" = "1" ]; then + mv ./*.AppImage.zsync "$OUTDIR" +fi diff --git a/distribution/macos/Info.plist b/distribution/macos/Info.plist index 53929f95e..2602f9905 100644 --- a/distribution/macos/Info.plist +++ b/distribution/macos/Info.plist @@ -40,11 +40,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.1 + 1.2 CFBundleSignature ???? CFBundleVersion - 1.1.0 + 1.2.0 NSHighResolutionCapable CSResourcesFileMapped diff --git a/distribution/macos/Ryujinx.icns b/distribution/macos/Ryujinx.icns index f54a9aeb7..fd5cd7f7e 100644 Binary files a/distribution/macos/Ryujinx.icns and b/distribution/macos/Ryujinx.icns differ diff --git a/distribution/macos/create_app_bundle.sh b/distribution/macos/create_app_bundle.sh index 0fa54eadd..e4397da84 100755 --- a/distribution/macos/create_app_bundle.sh +++ b/distribution/macos/create_app_bundle.sh @@ -46,5 +46,5 @@ then rcodesign sign --entitlements-xml-path "$ENTITLEMENTS_FILE_PATH" "$APP_BUNDLE_DIRECTORY" else echo "Usign codesign for ad-hoc signing" - codesign --entitlements "$ENTITLEMENTS_FILE_PATH" -f --deep -s - "$APP_BUNDLE_DIRECTORY" -fi \ No newline at end of file + codesign --entitlements "$ENTITLEMENTS_FILE_PATH" -f -s - "$APP_BUNDLE_DIRECTORY" +fi diff --git a/distribution/macos/create_macos_build_ava.sh b/distribution/macos/create_macos_build_ava.sh index 23eafc129..b19fa4863 100755 --- a/distribution/macos/create_macos_build_ava.sh +++ b/distribution/macos/create_macos_build_ava.sh @@ -2,8 +2,8 @@ set -e -if [ "$#" -lt 7 ]; then - echo "usage " +if [ "$#" -lt 8 ]; then + echo "usage " exit 1 fi @@ -18,10 +18,11 @@ ENTITLEMENTS_FILE_PATH=$(readlink -f "$4") VERSION=$5 SOURCE_REVISION_ID=$6 CONFIGURATION=$7 -EXTRA_ARGS=$8 +CANARY=$8 -if [ "$VERSION" == "1.1.0" ]; -then +if [ "$CANARY" == "1" ]; then + RELEASE_TAR_FILE_NAME=ryujinx-canary-$VERSION-macos_universal.app.tar +elif [ "$VERSION" == "1.1.0" ]; then RELEASE_TAR_FILE_NAME=ryujinx-$CONFIGURATION-$VERSION+$SOURCE_REVISION_ID-macos_universal.app.tar else RELEASE_TAR_FILE_NAME=ryujinx-$VERSION-macos_universal.app.tar @@ -61,7 +62,7 @@ mkdir -p "$OUTPUT_DIRECTORY" cp -R "$ARM64_APP_BUNDLE" "$UNIVERSAL_APP_BUNDLE" rm "$UNIVERSAL_APP_BUNDLE/$EXECUTABLE_SUB_PATH" -# Make it libraries universal +# Make its libraries universal python3 "$BASE_DIR/distribution/macos/construct_universal_dylib.py" "$ARM64_APP_BUNDLE" "$X64_APP_BUNDLE" "$UNIVERSAL_APP_BUNDLE" "**/*.dylib" if ! [ -x "$(command -v lipo)" ]; @@ -99,7 +100,7 @@ then rcodesign sign --entitlements-xml-path "$ENTITLEMENTS_FILE_PATH" "$UNIVERSAL_APP_BUNDLE" else echo "Using codesign for ad-hoc signing" - codesign --entitlements "$ENTITLEMENTS_FILE_PATH" -f --deep -s - "$UNIVERSAL_APP_BUNDLE" + codesign --entitlements "$ENTITLEMENTS_FILE_PATH" -f -s - "$UNIVERSAL_APP_BUNDLE" fi echo "Creating archive" @@ -109,12 +110,6 @@ python3 "$BASE_DIR/distribution/misc/add_tar_exec.py" "$RELEASE_TAR_FILE_NAME" " gzip -9 < "$RELEASE_TAR_FILE_NAME" > "$RELEASE_TAR_FILE_NAME.gz" rm "$RELEASE_TAR_FILE_NAME" -# Create legacy update package for Avalonia to not left behind old testers. -if [ "$VERSION" != "1.1.0" ]; -then - cp $RELEASE_TAR_FILE_NAME.gz test-ava-ryujinx-$VERSION-macos_universal.app.tar.gz -fi - popd -echo "Done" \ No newline at end of file +echo "Done" diff --git a/distribution/macos/create_macos_build_headless.sh b/distribution/macos/create_macos_build_headless.sh index a439aef45..01951d878 100755 --- a/distribution/macos/create_macos_build_headless.sh +++ b/distribution/macos/create_macos_build_headless.sh @@ -2,8 +2,8 @@ set -e -if [ "$#" -lt 7 ]; then - echo "usage " +if [ "$#" -lt 8 ]; then + echo "usage " exit 1 fi @@ -18,13 +18,14 @@ ENTITLEMENTS_FILE_PATH=$(readlink -f "$4") VERSION=$5 SOURCE_REVISION_ID=$6 CONFIGURATION=$7 -EXTRA_ARGS=$8 +CANARY=$8 -if [ "$VERSION" == "1.1.0" ]; -then - RELEASE_TAR_FILE_NAME=sdl2-ryujinx-headless-$CONFIGURATION-$VERSION+$SOURCE_REVISION_ID-macos_universal.tar +if [ "$CANARY" == "1" ]; then + RELEASE_TAR_FILE_NAME=nogui-ryujinx-canary-$VERSION-macos_universal.tar +elif [ "$VERSION" == "1.1.0" ]; then + RELEASE_TAR_FILE_NAME=nogui-ryujinx-$CONFIGURATION-$VERSION+$SOURCE_REVISION_ID-macos_universal.tar else - RELEASE_TAR_FILE_NAME=sdl2-ryujinx-headless-$VERSION-macos_universal.tar + RELEASE_TAR_FILE_NAME=nogui-ryujinx-$VERSION-macos_universal.tar fi ARM64_OUTPUT="$TEMP_DIRECTORY/publish_arm64" @@ -56,7 +57,7 @@ mkdir -p "$OUTPUT_DIRECTORY" cp -R "$ARM64_OUTPUT/" "$UNIVERSAL_OUTPUT" rm "$UNIVERSAL_OUTPUT/$EXECUTABLE_SUB_PATH" -# Make it libraries universal +# Make its libraries universal python3 "$BASE_DIR/distribution/macos/construct_universal_dylib.py" "$ARM64_OUTPUT" "$X64_OUTPUT" "$UNIVERSAL_OUTPUT" "**/*.dylib" if ! [ -x "$(command -v lipo)" ]; @@ -95,7 +96,7 @@ else echo "Using codesign for ad-hoc signing" for FILE in "$UNIVERSAL_OUTPUT"/*; do if [[ $(file "$FILE") == *"Mach-O"* ]]; then - codesign --entitlements "$ENTITLEMENTS_FILE_PATH" -f --deep -s - "$FILE" + codesign --entitlements "$ENTITLEMENTS_FILE_PATH" -f -s - "$FILE" fi done fi @@ -108,4 +109,4 @@ gzip -9 < "$RELEASE_TAR_FILE_NAME" > "$RELEASE_TAR_FILE_NAME.gz" rm "$RELEASE_TAR_FILE_NAME" popd -echo "Done" \ No newline at end of file +echo "Done" diff --git a/distribution/macos/updater.sh b/distribution/macos/updater.sh index 12e4c3aa1..0465d7c91 100755 --- a/distribution/macos/updater.sh +++ b/distribution/macos/updater.sh @@ -17,7 +17,7 @@ error_handler() { set the button_pressed to the button returned of the result if the button_pressed is \"Open Download Page\" then - open location \"https://ryujinx.org/download\" + open location \"https://ryujinx.app/download\" end if """ @@ -54,4 +54,4 @@ if [ "$#" -le 3 ]; then open -a "$INSTALL_DIRECTORY" else open -a "$INSTALL_DIRECTORY" --args "${APP_ARGUMENTS[@]}" -fi \ No newline at end of file +fi diff --git a/distribution/misc/Logo.svg b/distribution/misc/Logo.svg index d6a76312a..00bba42f0 100644 --- a/distribution/misc/Logo.svg +++ b/distribution/misc/Logo.svg @@ -1 +1,4 @@ - \ No newline at end of file + + + + diff --git a/docs/shell.png b/docs/shell.png new file mode 100644 index 000000000..f1eda13e8 Binary files /dev/null and b/docs/shell.png differ diff --git a/docs/workflow/pr-guide.md b/docs/workflow/pr-guide.md index cc2c5900b..50f44d87f 100644 --- a/docs/workflow/pr-guide.md +++ b/docs/workflow/pr-guide.md @@ -2,14 +2,14 @@ ## Contributing Rules -All contributions to Ryujinx/Ryujinx repository are made via pull requests (PRs) rather than through direct commits. The pull requests are reviewed and merged by the maintainers after a review and at least two approvals from the core development team. +All contributions to GreemDev/Ryujinx repository are made via pull requests (PRs) rather than through direct commits. The pull requests are reviewed and merged by the maintainers after a review and at least two approvals from the core development team. To merge pull requests, you must have write permissions in the repository. ## Quick Code Review Rules * Do not mix unrelated changes in one pull request. For example, a code style change should never be mixed with a bug fix. -* All changes should follow the existing code style. You can read more about our code style at [docs/coding-guidelines](../coding-guidelines/coding-style.md). +* All changes should follow the existing code style. You can read more about our code style at [docs/coding-style](../coding-guidelines/coding-style.md). * Adding external dependencies is to be avoided unless not doing so would introduce _significant_ complexity. Any dependency addition should be justified and discussed before merge. * Use Draft pull requests for changes you are still working on but want early CI loop feedback. When you think your changes are ready for review, [change the status](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/changing-the-stage-of-a-pull-request) of your pull request. * Rebase your changes when required or directly requested. Changes should always be commited on top of the upstream branch, not the other way around. @@ -24,7 +24,7 @@ If during the code review process a merge conflict occurs, the PR author is resp ## Pull Request Builds -When submitting a PR to the `Ryujinx/Ryujinx` repository, various builds will run validating many areas to ensure we keep developer productivity and product quality high. These various workflows can be tracked in the [Actions](https://github.com/Ryujinx/Ryujinx/actions) tab of the repository. If the job continues to completion, the build artifacts will be uploaded and posted as a comment in the PR discussion. +When submitting a PR to the `GreemDev/Ryujinx` repository, various builds will run validating many areas to ensure we keep developer productivity and product quality high. These various workflows can be tracked in the [Actions](https://github.com/GreemDev/Ryujinx/actions) tab of the repository. If the job continues to completion, the build artifacts will be uploaded and posted as a comment in the PR discussion. ## Review Turnaround Times @@ -42,7 +42,7 @@ Anyone with write access can merge a pull request manually when the following co * The PR has been approved by two reviewers and any other objections are addressed. * You can request follow up reviews from the original reviewers if they requested changes. -* The PR successfully builds and passes all tests in the Continuous Integration (CI) system. In case of failures, refer to the [Actions](https://github.com/Ryujinx/Ryujinx/actions) tab of your PR. +* The PR successfully builds and passes all tests in the Continuous Integration (CI) system. In case of failures, refer to the [Actions](https://github.com/GreemDev/Ryujinx/actions) tab of your PR. Typically, PRs are merged as one commit (squash merges). It creates a simpler history than a Merge Commit. "Special circumstances" are rare, and typically mean that there are a series of cleanly separated changes that will be too hard to understand if squashed together, or for some reason we want to preserve the ability to dissect them. diff --git a/global.json b/global.json index 391ba3c2a..cdbb589ed 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "8.0.100", + "version": "9.0.100", "rollForward": "latestFeature" } } diff --git a/src/ARMeilleure/ARMeilleure.csproj b/src/ARMeilleure/ARMeilleure.csproj index 550e50c26..5b6c5a6da 100644 --- a/src/ARMeilleure/ARMeilleure.csproj +++ b/src/ARMeilleure/ARMeilleure.csproj @@ -1,8 +1,8 @@ - net8.0 true + $(DefaultItemExcludes);._* diff --git a/src/ARMeilleure/CodeGen/Arm64/HardwareCapabilities.cs b/src/ARMeilleure/CodeGen/Arm64/HardwareCapabilities.cs index 86afc2b4d..639e4476b 100644 --- a/src/ARMeilleure/CodeGen/Arm64/HardwareCapabilities.cs +++ b/src/ARMeilleure/CodeGen/Arm64/HardwareCapabilities.cs @@ -127,13 +127,13 @@ namespace ARMeilleure.CodeGen.Arm64 #region macOS [LibraryImport("libSystem.dylib", SetLastError = true)] - private static unsafe partial int sysctlbyname([MarshalAs(UnmanagedType.LPStr)] string name, out int oldValue, ref ulong oldSize, IntPtr newValue, ulong newValueSize); + private static unsafe partial int sysctlbyname([MarshalAs(UnmanagedType.LPStr)] string name, out int oldValue, ref ulong oldSize, nint newValue, ulong newValueSize); [SupportedOSPlatform("macos")] private static bool CheckSysctlName(string name) { ulong size = sizeof(int); - if (sysctlbyname(name, out int val, ref size, IntPtr.Zero, 0) == 0 && size == sizeof(int)) + if (sysctlbyname(name, out int val, ref size, nint.Zero, 0) == 0 && size == sizeof(int)) { return val != 0; } diff --git a/src/ARMeilleure/CodeGen/CompiledFunction.cs b/src/ARMeilleure/CodeGen/CompiledFunction.cs index 3844cbfc9..8ea7ff532 100644 --- a/src/ARMeilleure/CodeGen/CompiledFunction.cs +++ b/src/ARMeilleure/CodeGen/CompiledFunction.cs @@ -58,7 +58,7 @@ namespace ARMeilleure.CodeGen /// Type of delegate /// Pointer to the function code in memory /// A delegate of type pointing to the mapped function - public T MapWithPointer(out IntPtr codePointer) + public T MapWithPointer(out nint codePointer) { codePointer = JitCache.Map(this); diff --git a/src/ARMeilleure/CodeGen/RegisterAllocators/LiveInterval.cs b/src/ARMeilleure/CodeGen/RegisterAllocators/LiveInterval.cs index cfe1bc7ca..3a16186d2 100644 --- a/src/ARMeilleure/CodeGen/RegisterAllocators/LiveInterval.cs +++ b/src/ARMeilleure/CodeGen/RegisterAllocators/LiveInterval.cs @@ -387,7 +387,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators public override int GetHashCode() { - return HashCode.Combine((IntPtr)_data); + return HashCode.Combine((nint)_data); } public override string ToString() diff --git a/src/ARMeilleure/CodeGen/RegisterAllocators/LiveRange.cs b/src/ARMeilleure/CodeGen/RegisterAllocators/LiveRange.cs index 412d597e8..dcd573a9d 100644 --- a/src/ARMeilleure/CodeGen/RegisterAllocators/LiveRange.cs +++ b/src/ARMeilleure/CodeGen/RegisterAllocators/LiveRange.cs @@ -63,7 +63,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators public override int GetHashCode() { - return HashCode.Combine((IntPtr)_data); + return HashCode.Combine((nint)_data); } public override string ToString() diff --git a/src/ARMeilleure/Common/AddressTable.cs b/src/ARMeilleure/Common/AddressTable.cs deleted file mode 100644 index fcab3a202..000000000 --- a/src/ARMeilleure/Common/AddressTable.cs +++ /dev/null @@ -1,252 +0,0 @@ -using ARMeilleure.Diagnostics; -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; - -namespace ARMeilleure.Common -{ - /// - /// Represents a table of guest address to a value. - /// - /// Type of the value - public unsafe class AddressTable : IDisposable where TEntry : unmanaged - { - /// - /// Represents a level in an . - /// - public readonly struct Level - { - /// - /// Gets the index of the in the guest address. - /// - public int Index { get; } - - /// - /// Gets the length of the in the guest address. - /// - public int Length { get; } - - /// - /// Gets the mask which masks the bits used by the . - /// - public ulong Mask => ((1ul << Length) - 1) << Index; - - /// - /// Initializes a new instance of the structure with the specified - /// and . - /// - /// Index of the - /// Length of the - public Level(int index, int length) - { - (Index, Length) = (index, length); - } - - /// - /// Gets the value of the from the specified guest . - /// - /// Guest address - /// Value of the from the specified guest - public int GetValue(ulong address) - { - return (int)((address & Mask) >> Index); - } - } - - private bool _disposed; - private TEntry** _table; - private readonly List _pages; - - /// - /// Gets the bits used by the of the instance. - /// - public ulong Mask { get; } - - /// - /// Gets the s used by the instance. - /// - public Level[] Levels { get; } - - /// - /// Gets or sets the default fill value of newly created leaf pages. - /// - public TEntry Fill { get; set; } - - /// - /// Gets the base address of the . - /// - /// instance was disposed - public IntPtr Base - { - get - { - ObjectDisposedException.ThrowIf(_disposed, this); - - lock (_pages) - { - return (IntPtr)GetRootPage(); - } - } - } - - /// - /// Constructs a new instance of the class with the specified list of - /// . - /// - /// is null - /// Length of is less than 2 - public AddressTable(Level[] levels) - { - ArgumentNullException.ThrowIfNull(levels); - - if (levels.Length < 2) - { - throw new ArgumentException("Table must be at least 2 levels deep.", nameof(levels)); - } - - _pages = new List(capacity: 16); - - Levels = levels; - Mask = 0; - - foreach (var level in Levels) - { - Mask |= level.Mask; - } - } - - /// - /// Determines if the specified is in the range of the - /// . - /// - /// Guest address - /// if is valid; otherwise - public bool IsValid(ulong address) - { - return (address & ~Mask) == 0; - } - - /// - /// Gets a reference to the value at the specified guest . - /// - /// Guest address - /// Reference to the value at the specified guest - /// instance was disposed - /// is not mapped - public ref TEntry GetValue(ulong address) - { - ObjectDisposedException.ThrowIf(_disposed, this); - - if (!IsValid(address)) - { - throw new ArgumentException($"Address 0x{address:X} is not mapped onto the table.", nameof(address)); - } - - lock (_pages) - { - return ref GetPage(address)[Levels[^1].GetValue(address)]; - } - } - - /// - /// Gets the leaf page for the specified guest . - /// - /// Guest address - /// Leaf page for the specified guest - private TEntry* GetPage(ulong address) - { - TEntry** page = GetRootPage(); - - for (int i = 0; i < Levels.Length - 1; i++) - { - ref Level level = ref Levels[i]; - ref TEntry* nextPage = ref page[level.GetValue(address)]; - - if (nextPage == null) - { - ref Level nextLevel = ref Levels[i + 1]; - - nextPage = i == Levels.Length - 2 ? - (TEntry*)Allocate(1 << nextLevel.Length, Fill, leaf: true) : - (TEntry*)Allocate(1 << nextLevel.Length, IntPtr.Zero, leaf: false); - } - - page = (TEntry**)nextPage; - } - - return (TEntry*)page; - } - - /// - /// Lazily initialize and get the root page of the . - /// - /// Root page of the - private TEntry** GetRootPage() - { - if (_table == null) - { - _table = (TEntry**)Allocate(1 << Levels[0].Length, fill: IntPtr.Zero, leaf: false); - } - - return _table; - } - - /// - /// Allocates a block of memory of the specified type and length. - /// - /// Type of elements - /// Number of elements - /// Fill value - /// if leaf; otherwise - /// Allocated block - private IntPtr Allocate(int length, T fill, bool leaf) where T : unmanaged - { - var size = sizeof(T) * length; - var page = (IntPtr)NativeAllocator.Instance.Allocate((uint)size); - var span = new Span((void*)page, length); - - span.Fill(fill); - - _pages.Add(page); - - TranslatorEventSource.Log.AddressTableAllocated(size, leaf); - - return page; - } - - /// - /// Releases all resources used by the instance. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Releases all unmanaged and optionally managed resources used by the - /// instance. - /// - /// to dispose managed resources also; otherwise just unmanaged resouces - protected virtual void Dispose(bool disposing) - { - if (!_disposed) - { - foreach (var page in _pages) - { - Marshal.FreeHGlobal(page); - } - - _disposed = true; - } - } - - /// - /// Frees resources used by the instance. - /// - ~AddressTable() - { - Dispose(false); - } - } -} diff --git a/src/ARMeilleure/Common/AddressTableLevel.cs b/src/ARMeilleure/Common/AddressTableLevel.cs new file mode 100644 index 000000000..af3b9b99f --- /dev/null +++ b/src/ARMeilleure/Common/AddressTableLevel.cs @@ -0,0 +1,44 @@ +namespace ARMeilleure.Common +{ + /// + /// Represents a level in an . + /// + public readonly struct AddressTableLevel + { + /// + /// Gets the index of the in the guest address. + /// + public int Index { get; } + + /// + /// Gets the length of the in the guest address. + /// + public int Length { get; } + + /// + /// Gets the mask which masks the bits used by the . + /// + public ulong Mask => ((1ul << Length) - 1) << Index; + + /// + /// Initializes a new instance of the structure with the specified + /// and . + /// + /// Index of the + /// Length of the + public AddressTableLevel(int index, int length) + { + (Index, Length) = (index, length); + } + + /// + /// Gets the value of the from the specified guest . + /// + /// Guest address + /// Value of the from the specified guest + public long GetValue(ulong address) + { + return (long)((address & Mask) >> Index); + } + } +} diff --git a/src/ARMeilleure/Common/AddressTablePresets.cs b/src/ARMeilleure/Common/AddressTablePresets.cs new file mode 100644 index 000000000..977e84a36 --- /dev/null +++ b/src/ARMeilleure/Common/AddressTablePresets.cs @@ -0,0 +1,75 @@ +namespace ARMeilleure.Common +{ + public static class AddressTablePresets + { + private static readonly AddressTableLevel[] _levels64Bit = + new AddressTableLevel[] + { + new(31, 17), + new(23, 8), + new(15, 8), + new( 7, 8), + new( 2, 5), + }; + + private static readonly AddressTableLevel[] _levels32Bit = + new AddressTableLevel[] + { + new(31, 17), + new(23, 8), + new(15, 8), + new( 7, 8), + new( 1, 6), + }; + + private static readonly AddressTableLevel[] _levels64BitSparseTiny = + new AddressTableLevel[] + { + new( 11, 28), + new( 2, 9), + }; + + private static readonly AddressTableLevel[] _levels32BitSparseTiny = + new AddressTableLevel[] + { + new( 10, 22), + new( 1, 9), + }; + + private static readonly AddressTableLevel[] _levels64BitSparseGiant = + new AddressTableLevel[] + { + new( 38, 1), + new( 2, 36), + }; + + private static readonly AddressTableLevel[] _levels32BitSparseGiant = + new AddressTableLevel[] + { + new( 31, 1), + new( 1, 30), + }; + + //high power will run worse on DDR3 systems and some DDR4 systems due to the higher ram utilization + //low power will never run worse than non-sparse, but for most systems it won't be necessary + //high power is always used, but I've left low power in here for future reference + public static AddressTableLevel[] GetArmPreset(bool for64Bits, bool sparse, bool lowPower = false) + { + if (sparse) + { + if (lowPower) + { + return for64Bits ? _levels64BitSparseTiny : _levels32BitSparseTiny; + } + else + { + return for64Bits ? _levels64BitSparseGiant : _levels32BitSparseGiant; + } + } + else + { + return for64Bits ? _levels64Bit : _levels32Bit; + } + } + } +} diff --git a/src/ARMeilleure/Common/Allocator.cs b/src/ARMeilleure/Common/Allocator.cs index 6905a614f..de6a77ebe 100644 --- a/src/ARMeilleure/Common/Allocator.cs +++ b/src/ARMeilleure/Common/Allocator.cs @@ -2,7 +2,7 @@ using System; namespace ARMeilleure.Common { - unsafe abstract class Allocator : IDisposable + public unsafe abstract class Allocator : IDisposable { public T* Allocate(ulong count = 1) where T : unmanaged { diff --git a/src/ARMeilleure/Common/ArenaAllocator.cs b/src/ARMeilleure/Common/ArenaAllocator.cs index ce8e33913..f9dbcbb20 100644 --- a/src/ARMeilleure/Common/ArenaAllocator.cs +++ b/src/ARMeilleure/Common/ArenaAllocator.cs @@ -20,7 +20,7 @@ namespace ARMeilleure.Common private List _pages; private readonly ulong _pageSize; private readonly uint _pageCount; - private readonly List _extras; + private readonly List _extras; public ArenaAllocator(uint pageSize, uint pageCount) { @@ -31,11 +31,11 @@ namespace ARMeilleure.Common _pageIndex = -1; _page = null; - _pages = new List(); + _pages = []; _pageSize = pageSize; _pageCount = pageCount; - _extras = new List(); + _extras = []; } public Span AllocateSpan(ulong count) where T : unmanaged @@ -64,7 +64,7 @@ namespace ARMeilleure.Common { void* extra = NativeAllocator.Instance.Allocate(size); - _extras.Add((IntPtr)extra); + _extras.Add((nint)extra); return extra; } @@ -84,7 +84,7 @@ namespace ARMeilleure.Common { _page = new PageInfo { - Pointer = (byte*)NativeAllocator.Instance.Allocate(_pageSize), + Pointer = (byte*)NativeAllocator.Instance.Allocate(_pageSize) }; _pages.Add(_page); @@ -114,7 +114,7 @@ namespace ARMeilleure.Common } // Free extra blocks that are not page-sized - foreach (IntPtr ptr in _extras) + foreach (nint ptr in _extras) { NativeAllocator.Instance.Free((void*)ptr); } @@ -173,7 +173,7 @@ namespace ARMeilleure.Common NativeAllocator.Instance.Free(info.Pointer); } - foreach (IntPtr ptr in _extras) + foreach (nint ptr in _extras) { NativeAllocator.Instance.Free((void*)ptr); } diff --git a/src/ARMeilleure/Common/EntryTable.cs b/src/ARMeilleure/Common/EntryTable.cs index 625e3f73f..e49a0989e 100644 --- a/src/ARMeilleure/Common/EntryTable.cs +++ b/src/ARMeilleure/Common/EntryTable.cs @@ -15,7 +15,7 @@ namespace ARMeilleure.Common private int _freeHint; private readonly int _pageCapacity; // Number of entries per page. private readonly int _pageLogCapacity; - private readonly Dictionary _pages; + private readonly Dictionary _pages; private readonly BitMap _allocated; /// @@ -41,7 +41,7 @@ namespace ARMeilleure.Common } _allocated = new BitMap(NativeAllocator.Instance); - _pages = new Dictionary(); + _pages = new Dictionary(); _pageLogCapacity = BitOperations.Log2((uint)(pageSize / sizeof(TEntry))); _pageCapacity = 1 << _pageLogCapacity; } @@ -138,9 +138,9 @@ namespace ARMeilleure.Common { var pageIndex = (int)((uint)(index & ~(_pageCapacity - 1)) >> _pageLogCapacity); - if (!_pages.TryGetValue(pageIndex, out IntPtr page)) + if (!_pages.TryGetValue(pageIndex, out nint page)) { - page = (IntPtr)NativeAllocator.Instance.Allocate((uint)sizeof(TEntry) * (uint)_pageCapacity); + page = (nint)NativeAllocator.Instance.Allocate((uint)sizeof(TEntry) * (uint)_pageCapacity); _pages.Add(pageIndex, page); } diff --git a/src/ARMeilleure/Common/IAddressTable.cs b/src/ARMeilleure/Common/IAddressTable.cs new file mode 100644 index 000000000..65077ec43 --- /dev/null +++ b/src/ARMeilleure/Common/IAddressTable.cs @@ -0,0 +1,51 @@ +using System; + +namespace ARMeilleure.Common +{ + public interface IAddressTable : IDisposable where TEntry : unmanaged + { + /// + /// True if the address table's bottom level is sparsely mapped. + /// This also ensures the second bottom level is filled with a dummy page rather than 0. + /// + bool Sparse { get; } + + /// + /// Gets the bits used by the of the instance. + /// + ulong Mask { get; } + + /// + /// Gets the s used by the instance. + /// + AddressTableLevel[] Levels { get; } + + /// + /// Gets or sets the default fill value of newly created leaf pages. + /// + TEntry Fill { get; set; } + + /// + /// Gets the base address of the . + /// + /// instance was disposed + nint Base { get; } + + /// + /// Determines if the specified is in the range of the + /// . + /// + /// Guest address + /// if is valid; otherwise + bool IsValid(ulong address); + + /// + /// Gets a reference to the value at the specified guest . + /// + /// Guest address + /// Reference to the value at the specified guest + /// instance was disposed + /// is not mapped + ref TEntry GetValue(ulong address); + } +} diff --git a/src/ARMeilleure/Common/NativeAllocator.cs b/src/ARMeilleure/Common/NativeAllocator.cs index 93c48adda..ffcffa4bc 100644 --- a/src/ARMeilleure/Common/NativeAllocator.cs +++ b/src/ARMeilleure/Common/NativeAllocator.cs @@ -3,13 +3,13 @@ using System.Runtime.InteropServices; namespace ARMeilleure.Common { - unsafe sealed class NativeAllocator : Allocator + public unsafe sealed class NativeAllocator : Allocator { public static NativeAllocator Instance { get; } = new(); public override void* Allocate(ulong size) { - void* result = (void*)Marshal.AllocHGlobal((IntPtr)size); + void* result = (void*)Marshal.AllocHGlobal((nint)size); if (result == null) { @@ -21,7 +21,7 @@ namespace ARMeilleure.Common public override void Free(void* block) { - Marshal.FreeHGlobal((IntPtr)block); + Marshal.FreeHGlobal((nint)block); } } } diff --git a/src/ARMeilleure/Decoders/DecoderHelper.cs b/src/ARMeilleure/Decoders/DecoderHelper.cs index 35e573955..c39a8a88b 100644 --- a/src/ARMeilleure/Decoders/DecoderHelper.cs +++ b/src/ARMeilleure/Decoders/DecoderHelper.cs @@ -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 args) { if (q) { diff --git a/src/ARMeilleure/Instructions/InstEmitFlowHelper.cs b/src/ARMeilleure/Instructions/InstEmitFlowHelper.cs index 2009bafda..a602ea49e 100644 --- a/src/ARMeilleure/Instructions/InstEmitFlowHelper.cs +++ b/src/ARMeilleure/Instructions/InstEmitFlowHelper.cs @@ -193,6 +193,8 @@ namespace ARMeilleure.Instructions Operand hostAddress; + var table = context.FunctionTable; + // If address is mapped onto the function table, we can skip the table walk. Otherwise we fallback // onto the dispatch stub. if (guestAddress.Kind == OperandKind.Constant && context.FunctionTable.IsValid(guestAddress.Value)) @@ -203,6 +205,30 @@ namespace ARMeilleure.Instructions hostAddress = context.Load(OperandType.I64, hostAddressAddr); } + else if (table.Sparse) + { + // Inline table lookup. Only enabled when the sparse function table is enabled with 2 levels. + // Deliberately attempts to avoid branches. + + Operand tableBase = !context.HasPtc ? + Const(table.Base) : + Const(table.Base, Ptc.FunctionTableSymbol); + + hostAddress = tableBase; + + for (int i = 0; i < table.Levels.Length; i++) + { + var level = table.Levels[i]; + int clearBits = 64 - (level.Index + level.Length); + + Operand index = context.ShiftLeft( + context.ShiftRightUI(context.ShiftLeft(guestAddress, Const(clearBits)), Const(clearBits + level.Index)), + Const(3) + ); + + hostAddress = context.Load(OperandType.I64, context.Add(hostAddress, index)); + } + } else { hostAddress = !context.HasPtc ? diff --git a/src/ARMeilleure/Instructions/InstEmitSystem.cs b/src/ARMeilleure/Instructions/InstEmitSystem.cs index 8c430fc23..11c1d0328 100644 --- a/src/ARMeilleure/Instructions/InstEmitSystem.cs +++ b/src/ARMeilleure/Instructions/InstEmitSystem.cs @@ -49,6 +49,9 @@ namespace ARMeilleure.Instructions case 0b11_011_1101_0000_011: EmitGetTpidrroEl0(context); return; + case 0b11_011_1101_0000_101: + EmitGetTpidr2El0(context); + return; case 0b11_011_1110_0000_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0)); break; @@ -84,6 +87,9 @@ namespace ARMeilleure.Instructions case 0b11_011_1101_0000_010: EmitSetTpidrEl0(context); return; + case 0b11_011_1101_0000_101: + EmitSetTpidr2El0(context); + return; default: throw new NotImplementedException($"Unknown MSR 0x{op.RawOpCode:X8} at 0x{op.Address:X16}."); @@ -213,6 +219,17 @@ namespace ARMeilleure.Instructions SetIntOrZR(context, op.Rt, result); } + private static void EmitGetTpidr2El0(ArmEmitterContext context) + { + OpCodeSystem op = (OpCodeSystem)context.CurrOp; + + Operand nativeContext = context.LoadArgument(OperandType.I64, 0); + + Operand result = context.Load(OperandType.I64, context.Add(nativeContext, Const((ulong)NativeContext.GetTpidr2El0Offset()))); + + SetIntOrZR(context, op.Rt, result); + } + private static void EmitSetNzcv(ArmEmitterContext context) { OpCodeSystem op = (OpCodeSystem)context.CurrOp; @@ -274,5 +291,16 @@ namespace ARMeilleure.Instructions context.Store(context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrEl0Offset())), value); } + + private static void EmitSetTpidr2El0(ArmEmitterContext context) + { + OpCodeSystem op = (OpCodeSystem)context.CurrOp; + + Operand value = GetIntOrZR(context, op.Rt); + + Operand nativeContext = context.LoadArgument(OperandType.I64, 0); + + context.Store(context.Add(nativeContext, Const((ulong)NativeContext.GetTpidr2El0Offset())), value); + } } } diff --git a/src/ARMeilleure/Instructions/SoftFallback.cs b/src/ARMeilleure/Instructions/SoftFallback.cs index c4fe677bf..899326c4b 100644 --- a/src/ARMeilleure/Instructions/SoftFallback.cs +++ b/src/ARMeilleure/Instructions/SoftFallback.cs @@ -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 tb) { byte[] res = new byte[16]; diff --git a/src/ARMeilleure/IntermediateRepresentation/IntrusiveList.cs b/src/ARMeilleure/IntermediateRepresentation/IntrusiveList.cs index 8d300075d..642e5aa90 100644 --- a/src/ARMeilleure/IntermediateRepresentation/IntrusiveList.cs +++ b/src/ARMeilleure/IntermediateRepresentation/IntrusiveList.cs @@ -32,7 +32,7 @@ namespace ARMeilleure.IntermediateRepresentation /// is not pointer sized. public IntrusiveList() { - if (Unsafe.SizeOf() != IntPtr.Size) + if (Unsafe.SizeOf() != nint.Size) { throw new ArgumentException("T must be a reference type or a pointer sized struct."); } diff --git a/src/ARMeilleure/IntermediateRepresentation/MemoryOperand.cs b/src/ARMeilleure/IntermediateRepresentation/MemoryOperand.cs index 9b3df8ca4..45695396f 100644 --- a/src/ARMeilleure/IntermediateRepresentation/MemoryOperand.cs +++ b/src/ARMeilleure/IntermediateRepresentation/MemoryOperand.cs @@ -24,7 +24,7 @@ namespace ARMeilleure.IntermediateRepresentation { Debug.Assert(operand.Kind == OperandKind.Memory); - _data = (Data*)Unsafe.As(ref operand); + _data = (Data*)Unsafe.As(ref operand); } public Operand BaseAddress diff --git a/src/ARMeilleure/IntermediateRepresentation/Operation.cs b/src/ARMeilleure/IntermediateRepresentation/Operation.cs index bc3a71b31..4bc3a2e09 100644 --- a/src/ARMeilleure/IntermediateRepresentation/Operation.cs +++ b/src/ARMeilleure/IntermediateRepresentation/Operation.cs @@ -228,7 +228,7 @@ namespace ARMeilleure.IntermediateRepresentation public readonly override int GetHashCode() { - return HashCode.Combine((IntPtr)_data); + return HashCode.Combine((nint)_data); } public static bool operator ==(Operation a, Operation b) @@ -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 srcs) { Operation result = Make(Instruction.Extended, 0, srcs.Length); diff --git a/src/ARMeilleure/Memory/IJitMemoryBlock.cs b/src/ARMeilleure/Memory/IJitMemoryBlock.cs index c103fe8d1..59710d1ce 100644 --- a/src/ARMeilleure/Memory/IJitMemoryBlock.cs +++ b/src/ARMeilleure/Memory/IJitMemoryBlock.cs @@ -4,7 +4,7 @@ namespace ARMeilleure.Memory { public interface IJitMemoryBlock : IDisposable { - IntPtr Pointer { get; } + nint Pointer { get; } void Commit(ulong offset, ulong size); diff --git a/src/ARMeilleure/Memory/IMemoryManager.cs b/src/ARMeilleure/Memory/IMemoryManager.cs index 46d442655..84d82caf7 100644 --- a/src/ARMeilleure/Memory/IMemoryManager.cs +++ b/src/ARMeilleure/Memory/IMemoryManager.cs @@ -6,7 +6,7 @@ namespace ARMeilleure.Memory { int AddressSpaceBits { get; } - IntPtr PageTablePointer { get; } + nint PageTablePointer { get; } MemoryManagerType Type { get; } diff --git a/src/ARMeilleure/Memory/ReservedRegion.cs b/src/ARMeilleure/Memory/ReservedRegion.cs index 3870d4c84..a3ebd610d 100644 --- a/src/ARMeilleure/Memory/ReservedRegion.cs +++ b/src/ARMeilleure/Memory/ReservedRegion.cs @@ -8,7 +8,7 @@ namespace ARMeilleure.Memory public IJitMemoryBlock Block { get; } - public IntPtr Pointer => Block.Pointer; + public nint Pointer => Block.Pointer; private readonly ulong _maxSize; private readonly ulong _sizeGranularity; diff --git a/src/ARMeilleure/Native/JitSupportDarwin.cs b/src/ARMeilleure/Native/JitSupportDarwin.cs index 339460397..39df3878f 100644 --- a/src/ARMeilleure/Native/JitSupportDarwin.cs +++ b/src/ARMeilleure/Native/JitSupportDarwin.cs @@ -8,6 +8,6 @@ namespace ARMeilleure.Native static partial class JitSupportDarwin { [LibraryImport("libarmeilleure-jitsupport", EntryPoint = "armeilleure_jit_memcpy")] - public static partial void Copy(IntPtr dst, IntPtr src, ulong n); + public static partial void Copy(nint dst, nint src, ulong n); } } diff --git a/src/ARMeilleure/Optimizations.cs b/src/ARMeilleure/Optimizations.cs index 8fe478e47..18390de31 100644 --- a/src/ARMeilleure/Optimizations.cs +++ b/src/ARMeilleure/Optimizations.cs @@ -5,6 +5,9 @@ namespace ARMeilleure public static class Optimizations { + // low-core count PPTC + public static bool LowPower { get; set; } = false; + public static bool FastFP { get; set; } = true; public static bool AllowLcqInFunctionTable { get; set; } = true; @@ -51,8 +54,8 @@ namespace ARMeilleure internal static bool UseSse41 => UseSse41IfAvailable && X86HardwareCapabilities.SupportsSse41; internal static bool UseSse42 => UseSse42IfAvailable && X86HardwareCapabilities.SupportsSse42; internal static bool UsePopCnt => UsePopCntIfAvailable && X86HardwareCapabilities.SupportsPopcnt; - internal static bool UseAvx => UseAvxIfAvailable && X86HardwareCapabilities.SupportsAvx && !ForceLegacySse; - internal static bool UseAvx512F => UseAvx512FIfAvailable && X86HardwareCapabilities.SupportsAvx512F && !ForceLegacySse; + internal static bool UseAvx => UseAvxIfAvailable && X86HardwareCapabilities.SupportsAvx && !ForceLegacySse; + internal static bool UseAvx512F => UseAvx512FIfAvailable && X86HardwareCapabilities.SupportsAvx512F && !ForceLegacySse; internal static bool UseAvx512Vl => UseAvx512VlIfAvailable && X86HardwareCapabilities.SupportsAvx512Vl && !ForceLegacySse; internal static bool UseAvx512Bw => UseAvx512BwIfAvailable && X86HardwareCapabilities.SupportsAvx512Bw && !ForceLegacySse; internal static bool UseAvx512Dq => UseAvx512DqIfAvailable && X86HardwareCapabilities.SupportsAvx512Dq && !ForceLegacySse; diff --git a/src/ARMeilleure/Signal/NativeSignalHandlerGenerator.cs b/src/ARMeilleure/Signal/NativeSignalHandlerGenerator.cs index 2ec5bc1b3..35747d7a4 100644 --- a/src/ARMeilleure/Signal/NativeSignalHandlerGenerator.cs +++ b/src/ARMeilleure/Signal/NativeSignalHandlerGenerator.cs @@ -8,7 +8,7 @@ namespace ARMeilleure.Signal { public static class NativeSignalHandlerGenerator { - public const int MaxTrackedRanges = 8; + public const int MaxTrackedRanges = 16; private const int StructAddressOffset = 0; private const int StructWriteOffset = 4; @@ -21,7 +21,7 @@ namespace ARMeilleure.Signal private const uint EXCEPTION_ACCESS_VIOLATION = 0xc0000005; - private static Operand EmitGenericRegionCheck(EmitterContext context, IntPtr signalStructPtr, Operand faultAddress, Operand isWrite, int rangeStructSize) + private static Operand EmitGenericRegionCheck(EmitterContext context, nint signalStructPtr, Operand faultAddress, Operand isWrite, int rangeStructSize) { Operand inRegionLocal = context.AllocateLocal(OperandType.I32); context.Copy(inRegionLocal, Const(0)); @@ -155,7 +155,7 @@ namespace ARMeilleure.Signal throw new PlatformNotSupportedException(); } - public static byte[] GenerateUnixSignalHandler(IntPtr signalStructPtr, int rangeStructSize) + public static byte[] GenerateUnixSignalHandler(nint signalStructPtr, int rangeStructSize) { EmitterContext context = new(); @@ -203,7 +203,7 @@ namespace ARMeilleure.Signal return Compiler.Compile(cfg, argTypes, OperandType.None, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Code; } - public static byte[] GenerateWindowsSignalHandler(IntPtr signalStructPtr, int rangeStructSize) + public static byte[] GenerateWindowsSignalHandler(nint signalStructPtr, int rangeStructSize) { EmitterContext context = new(); diff --git a/src/ARMeilleure/Signal/TestMethods.cs b/src/ARMeilleure/Signal/TestMethods.cs index 0a8b3f5ff..9d11ab183 100644 --- a/src/ARMeilleure/Signal/TestMethods.cs +++ b/src/ARMeilleure/Signal/TestMethods.cs @@ -16,7 +16,7 @@ namespace ARMeilleure.Signal { public delegate bool DebugPartialUnmap(); public delegate int DebugThreadLocalMapGetOrReserve(int threadId, int initialState); - public delegate void DebugNativeWriteLoop(IntPtr nativeWriteLoopPtr, IntPtr writePtr); + public delegate void DebugNativeWriteLoop(nint nativeWriteLoopPtr, nint writePtr); public static DebugPartialUnmap GenerateDebugPartialUnmap() { @@ -35,7 +35,7 @@ namespace ARMeilleure.Signal return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map(); } - public static DebugThreadLocalMapGetOrReserve GenerateDebugThreadLocalMapGetOrReserve(IntPtr structPtr) + public static DebugThreadLocalMapGetOrReserve GenerateDebugThreadLocalMapGetOrReserve(nint structPtr) { EmitterContext context = new(); diff --git a/src/ARMeilleure/Signal/WindowsPartialUnmapHandler.cs b/src/ARMeilleure/Signal/WindowsPartialUnmapHandler.cs index 3bf6a4498..7aa3e4788 100644 --- a/src/ARMeilleure/Signal/WindowsPartialUnmapHandler.cs +++ b/src/ARMeilleure/Signal/WindowsPartialUnmapHandler.cs @@ -13,18 +13,18 @@ namespace ARMeilleure.Signal internal static partial class WindowsPartialUnmapHandler { [LibraryImport("kernel32.dll", SetLastError = true, EntryPoint = "LoadLibraryA")] - private static partial IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName); + private static partial nint LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName); [LibraryImport("kernel32.dll", SetLastError = true)] - private static partial IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string procName); + private static partial nint GetProcAddress(nint hModule, [MarshalAs(UnmanagedType.LPStr)] string procName); - private static IntPtr _getCurrentThreadIdPtr; + private static nint _getCurrentThreadIdPtr; - public static IntPtr GetCurrentThreadIdFunc() + public static nint GetCurrentThreadIdFunc() { - if (_getCurrentThreadIdPtr == IntPtr.Zero) + if (_getCurrentThreadIdPtr == nint.Zero) { - IntPtr handle = LoadLibrary("kernel32.dll"); + nint handle = LoadLibrary("kernel32.dll"); _getCurrentThreadIdPtr = GetProcAddress(handle, "GetCurrentThreadId"); } @@ -34,13 +34,13 @@ namespace ARMeilleure.Signal public static Operand EmitRetryFromAccessViolation(EmitterContext context) { - IntPtr partialRemapStatePtr = PartialUnmapState.GlobalState; - IntPtr localCountsPtr = IntPtr.Add(partialRemapStatePtr, PartialUnmapState.LocalCountsOffset); + nint partialRemapStatePtr = PartialUnmapState.GlobalState; + nint localCountsPtr = nint.Add(partialRemapStatePtr, PartialUnmapState.LocalCountsOffset); // Get the lock first. - EmitNativeReaderLockAcquire(context, IntPtr.Add(partialRemapStatePtr, PartialUnmapState.PartialUnmapLockOffset)); + EmitNativeReaderLockAcquire(context, nint.Add(partialRemapStatePtr, PartialUnmapState.PartialUnmapLockOffset)); - IntPtr getCurrentThreadId = GetCurrentThreadIdFunc(); + nint getCurrentThreadId = GetCurrentThreadIdFunc(); Operand threadId = context.Call(Const((ulong)getCurrentThreadId), OperandType.I32); Operand threadIndex = EmitThreadLocalMapIntGetOrReserve(context, localCountsPtr, threadId, Const(0)); @@ -58,7 +58,7 @@ namespace ARMeilleure.Signal Operand threadLocalPartialUnmapsPtr = EmitThreadLocalMapIntGetValuePtr(context, localCountsPtr, threadIndex); Operand threadLocalPartialUnmaps = context.Load(OperandType.I32, threadLocalPartialUnmapsPtr); - Operand partialUnmapsCount = context.Load(OperandType.I32, Const((ulong)IntPtr.Add(partialRemapStatePtr, PartialUnmapState.PartialUnmapsCountOffset))); + Operand partialUnmapsCount = context.Load(OperandType.I32, Const((ulong)nint.Add(partialRemapStatePtr, PartialUnmapState.PartialUnmapsCountOffset))); context.Copy(retry, context.ICompareNotEqual(threadLocalPartialUnmaps, partialUnmapsCount)); @@ -79,14 +79,14 @@ namespace ARMeilleure.Signal context.MarkLabel(endLabel); // Finally, release the lock and return the retry value. - EmitNativeReaderLockRelease(context, IntPtr.Add(partialRemapStatePtr, PartialUnmapState.PartialUnmapLockOffset)); + EmitNativeReaderLockRelease(context, nint.Add(partialRemapStatePtr, PartialUnmapState.PartialUnmapLockOffset)); return retry; } - public static Operand EmitThreadLocalMapIntGetOrReserve(EmitterContext context, IntPtr threadLocalMapPtr, Operand threadId, Operand initialState) + public static Operand EmitThreadLocalMapIntGetOrReserve(EmitterContext context, nint threadLocalMapPtr, Operand threadId, Operand initialState) { - Operand idsPtr = Const((ulong)IntPtr.Add(threadLocalMapPtr, ThreadLocalMap.ThreadIdsOffset)); + Operand idsPtr = Const((ulong)nint.Add(threadLocalMapPtr, ThreadLocalMap.ThreadIdsOffset)); Operand i = context.AllocateLocal(OperandType.I32); @@ -130,7 +130,7 @@ namespace ARMeilleure.Signal // If it was 0, then we need to initialize the struct entry and return i. context.BranchIfFalse(idNot0Label, context.ICompareEqual(existingId2, Const(0))); - Operand structsPtr = Const((ulong)IntPtr.Add(threadLocalMapPtr, ThreadLocalMap.StructsOffset)); + Operand structsPtr = Const((ulong)nint.Add(threadLocalMapPtr, ThreadLocalMap.StructsOffset)); Operand structPtr = context.Add(structsPtr, context.SignExtend32(OperandType.I64, offset2)); context.Store(structPtr, initialState); @@ -149,10 +149,10 @@ namespace ARMeilleure.Signal return context.Copy(i); } - private static Operand EmitThreadLocalMapIntGetValuePtr(EmitterContext context, IntPtr threadLocalMapPtr, Operand index) + private static Operand EmitThreadLocalMapIntGetValuePtr(EmitterContext context, nint threadLocalMapPtr, Operand index) { Operand offset = context.Multiply(index, Const(sizeof(int))); - Operand structsPtr = Const((ulong)IntPtr.Add(threadLocalMapPtr, ThreadLocalMap.StructsOffset)); + Operand structsPtr = Const((ulong)nint.Add(threadLocalMapPtr, ThreadLocalMap.StructsOffset)); return context.Add(structsPtr, context.SignExtend32(OperandType.I64, offset)); } @@ -170,9 +170,9 @@ namespace ARMeilleure.Signal context.BranchIfFalse(loop, context.ICompareEqual(initial, replaced)); } - private static void EmitNativeReaderLockAcquire(EmitterContext context, IntPtr nativeReaderLockPtr) + private static void EmitNativeReaderLockAcquire(EmitterContext context, nint nativeReaderLockPtr) { - Operand writeLockPtr = Const((ulong)IntPtr.Add(nativeReaderLockPtr, NativeReaderWriterLock.WriteLockOffset)); + Operand writeLockPtr = Const((ulong)nint.Add(nativeReaderLockPtr, NativeReaderWriterLock.WriteLockOffset)); // Spin until we can acquire the write lock. Operand spinLabel = Label(); @@ -182,16 +182,16 @@ namespace ARMeilleure.Signal context.BranchIfTrue(spinLabel, context.CompareAndSwap(writeLockPtr, Const(0), Const(1))); // Increment reader count. - EmitAtomicAddI32(context, Const((ulong)IntPtr.Add(nativeReaderLockPtr, NativeReaderWriterLock.ReaderCountOffset)), Const(1)); + EmitAtomicAddI32(context, Const((ulong)nint.Add(nativeReaderLockPtr, NativeReaderWriterLock.ReaderCountOffset)), Const(1)); // Release write lock. context.CompareAndSwap(writeLockPtr, Const(1), Const(0)); } - private static void EmitNativeReaderLockRelease(EmitterContext context, IntPtr nativeReaderLockPtr) + private static void EmitNativeReaderLockRelease(EmitterContext context, nint nativeReaderLockPtr) { // Decrement reader count. - EmitAtomicAddI32(context, Const((ulong)IntPtr.Add(nativeReaderLockPtr, NativeReaderWriterLock.ReaderCountOffset)), Const(-1)); + EmitAtomicAddI32(context, Const((ulong)nint.Add(nativeReaderLockPtr, NativeReaderWriterLock.ReaderCountOffset)), Const(-1)); } } } diff --git a/src/ARMeilleure/State/ExecutionContext.cs b/src/ARMeilleure/State/ExecutionContext.cs index ce10a591c..314b06b13 100644 --- a/src/ARMeilleure/State/ExecutionContext.cs +++ b/src/ARMeilleure/State/ExecutionContext.cs @@ -9,7 +9,7 @@ namespace ARMeilleure.State private readonly NativeContext _nativeContext; - internal IntPtr NativeContextPtr => _nativeContext.BasePtr; + internal nint NativeContextPtr => _nativeContext.BasePtr; private bool _interrupted; diff --git a/src/ARMeilleure/State/NativeContext.cs b/src/ARMeilleure/State/NativeContext.cs index 5403042ea..140b6f7a7 100644 --- a/src/ARMeilleure/State/NativeContext.cs +++ b/src/ARMeilleure/State/NativeContext.cs @@ -21,13 +21,14 @@ namespace ARMeilleure.State public ulong ExclusiveValueLow; public ulong ExclusiveValueHigh; public int Running; + public long Tpidr2El0; } private static NativeCtxStorage _dummyStorage = new(); private readonly IJitMemoryBlock _block; - public IntPtr BasePtr => _block.Pointer; + public nint BasePtr => _block.Pointer; public NativeContext(IJitMemoryAllocator allocator) { @@ -176,6 +177,9 @@ namespace ARMeilleure.State public long GetTpidrroEl0() => GetStorage().TpidrroEl0; public void SetTpidrroEl0(long value) => GetStorage().TpidrroEl0 = value; + public long GetTpidr2El0() => GetStorage().Tpidr2El0; + public void SetTpidr2El0(long value) => GetStorage().Tpidr2El0 = value; + public int GetCounter() => GetStorage().Counter; public void SetCounter(int value) => GetStorage().Counter = value; @@ -232,6 +236,11 @@ namespace ARMeilleure.State return StorageOffset(ref _dummyStorage, ref _dummyStorage.TpidrroEl0); } + public static int GetTpidr2El0Offset() + { + return StorageOffset(ref _dummyStorage, ref _dummyStorage.Tpidr2El0); + } + public static int GetCounterOffset() { return StorageOffset(ref _dummyStorage, ref _dummyStorage.Counter); diff --git a/src/ARMeilleure/Translation/ArmEmitterContext.cs b/src/ARMeilleure/Translation/ArmEmitterContext.cs index e24074739..82f12bb02 100644 --- a/src/ARMeilleure/Translation/ArmEmitterContext.cs +++ b/src/ARMeilleure/Translation/ArmEmitterContext.cs @@ -46,7 +46,7 @@ namespace ARMeilleure.Translation public IMemoryManager Memory { get; } public EntryTable CountTable { get; } - public AddressTable FunctionTable { get; } + public IAddressTable FunctionTable { get; } public TranslatorStubs Stubs { get; } public ulong EntryAddress { get; } @@ -62,7 +62,7 @@ namespace ARMeilleure.Translation public ArmEmitterContext( IMemoryManager memory, EntryTable countTable, - AddressTable funcTable, + IAddressTable funcTable, TranslatorStubs stubs, ulong entryAddress, bool highCq, @@ -92,7 +92,7 @@ namespace ARMeilleure.Translation else { int index = Delegates.GetDelegateIndex(info); - IntPtr funcPtr = Delegates.GetDelegateFuncPtrByIndex(index); + nint funcPtr = Delegates.GetDelegateFuncPtrByIndex(index); OperandType returnType = GetOperandType(info.ReturnType); diff --git a/src/ARMeilleure/Translation/Cache/JitCache.cs b/src/ARMeilleure/Translation/Cache/JitCache.cs index e2b5e2d10..3bbec482c 100644 --- a/src/ARMeilleure/Translation/Cache/JitCache.cs +++ b/src/ARMeilleure/Translation/Cache/JitCache.cs @@ -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,12 +27,12 @@ namespace ARMeilleure.Translation.Cache private static readonly List _cacheEntries = new(); - private static readonly object _lock = new(); + private static readonly Lock _lock = new(); private static bool _initialized; [SupportedOSPlatform("windows")] [LibraryImport("kernel32.dll", SetLastError = true)] - public static partial IntPtr FlushInstructionCache(IntPtr hProcess, IntPtr lpAddress, UIntPtr dwSize); + public static partial nint FlushInstructionCache(nint hProcess, nint lpAddress, nuint dwSize); public static void Initialize(IJitMemoryAllocator allocator) { @@ -65,7 +66,7 @@ namespace ARMeilleure.Translation.Cache } } - public static IntPtr Map(CompiledFunction func) + public static nint Map(CompiledFunction func) { byte[] code = func.Code; @@ -75,7 +76,7 @@ namespace ARMeilleure.Translation.Cache int funcOffset = Allocate(code.Length); - IntPtr funcPtr = _jitRegion.Pointer + funcOffset; + nint funcPtr = _jitRegion.Pointer + funcOffset; if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64) { @@ -83,7 +84,7 @@ namespace ARMeilleure.Translation.Cache { fixed (byte* codePtr = code) { - JitSupportDarwin.Copy(funcPtr, (IntPtr)codePtr, (ulong)code.Length); + JitSupportDarwin.Copy(funcPtr, (nint)codePtr, (ulong)code.Length); } } } @@ -95,7 +96,7 @@ namespace ARMeilleure.Translation.Cache if (OperatingSystem.IsWindows() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64) { - FlushInstructionCache(Process.GetCurrentProcess().Handle, funcPtr, (UIntPtr)code.Length); + FlushInstructionCache(Process.GetCurrentProcess().Handle, funcPtr, (nuint)code.Length); } else { @@ -109,7 +110,7 @@ namespace ARMeilleure.Translation.Cache } } - public static void Unmap(IntPtr pointer) + public static void Unmap(nint pointer) { lock (_lock) { diff --git a/src/ARMeilleure/Translation/Cache/JitCacheInvalidation.cs b/src/ARMeilleure/Translation/Cache/JitCacheInvalidation.cs index 3aa2e19f1..6f9c22b4a 100644 --- a/src/ARMeilleure/Translation/Cache/JitCacheInvalidation.cs +++ b/src/ARMeilleure/Translation/Cache/JitCacheInvalidation.cs @@ -68,7 +68,7 @@ namespace ARMeilleure.Translation.Cache } } - public void Invalidate(IntPtr basePointer, ulong size) + public void Invalidate(nint basePointer, ulong size) { if (_needsInvalidation) { diff --git a/src/ARMeilleure/Translation/Cache/JitUnwindWindows.cs b/src/ARMeilleure/Translation/Cache/JitUnwindWindows.cs index 3957a7559..642794188 100644 --- a/src/ARMeilleure/Translation/Cache/JitUnwindWindows.cs +++ b/src/ARMeilleure/Translation/Cache/JitUnwindWindows.cs @@ -40,7 +40,7 @@ namespace ARMeilleure.Translation.Cache PushMachframe = 10, } - private unsafe delegate RuntimeFunction* GetRuntimeFunctionCallback(ulong controlPc, IntPtr context); + private unsafe delegate RuntimeFunction* GetRuntimeFunctionCallback(ulong controlPc, nint context); [LibraryImport("kernel32.dll")] [return: MarshalAs(UnmanagedType.Bool)] @@ -49,7 +49,7 @@ namespace ARMeilleure.Translation.Cache ulong baseAddress, uint length, GetRuntimeFunctionCallback callback, - IntPtr context, + nint context, [MarshalAs(UnmanagedType.LPWStr)] string outOfProcessCallbackDll); private static GetRuntimeFunctionCallback _getRuntimeFunctionCallback; @@ -60,7 +60,7 @@ namespace ARMeilleure.Translation.Cache private unsafe static UnwindInfo* _unwindInfo; - public static void InstallFunctionTableHandler(IntPtr codeCachePointer, uint codeCacheLength, IntPtr workBufferPtr) + public static void InstallFunctionTableHandler(nint codeCachePointer, uint codeCacheLength, nint workBufferPtr) { ulong codeCachePtr = (ulong)codeCachePointer.ToInt64(); @@ -91,7 +91,7 @@ namespace ARMeilleure.Translation.Cache } } - private static unsafe RuntimeFunction* FunctionTableHandler(ulong controlPc, IntPtr context) + private static unsafe RuntimeFunction* FunctionTableHandler(ulong controlPc, nint context) { int offset = (int)((long)controlPc - context.ToInt64()); diff --git a/src/ARMeilleure/Translation/DelegateInfo.cs b/src/ARMeilleure/Translation/DelegateInfo.cs index 706625437..d3b535de1 100644 --- a/src/ARMeilleure/Translation/DelegateInfo.cs +++ b/src/ARMeilleure/Translation/DelegateInfo.cs @@ -8,9 +8,9 @@ namespace ARMeilleure.Translation private readonly Delegate _dlg; // Ensure that this delegate will not be garbage collected. #pragma warning restore IDE0052 - public IntPtr FuncPtr { get; } + public nint FuncPtr { get; } - public DelegateInfo(Delegate dlg, IntPtr funcPtr) + public DelegateInfo(Delegate dlg, nint funcPtr) { _dlg = dlg; FuncPtr = funcPtr; diff --git a/src/ARMeilleure/Translation/Delegates.cs b/src/ARMeilleure/Translation/Delegates.cs index 66412b8e6..d8c1cfd58 100644 --- a/src/ARMeilleure/Translation/Delegates.cs +++ b/src/ARMeilleure/Translation/Delegates.cs @@ -9,7 +9,7 @@ namespace ARMeilleure.Translation { static class Delegates { - public static bool TryGetDelegateFuncPtrByIndex(int index, out IntPtr funcPtr) + public static bool TryGetDelegateFuncPtrByIndex(int index, out nint funcPtr) { if (index >= 0 && index < _delegates.Count) { @@ -25,7 +25,7 @@ namespace ARMeilleure.Translation } } - public static IntPtr GetDelegateFuncPtrByIndex(int index) + public static nint GetDelegateFuncPtrByIndex(int index) { if (index < 0 || index >= _delegates.Count) { @@ -35,7 +35,7 @@ namespace ARMeilleure.Translation return _delegates.Values[index].FuncPtr; // O(1). } - public static IntPtr GetDelegateFuncPtr(MethodInfo info) + public static nint GetDelegateFuncPtr(MethodInfo info) { ArgumentNullException.ThrowIfNull(info); @@ -65,7 +65,7 @@ namespace ARMeilleure.Translation return index; } - private static void SetDelegateInfo(Delegate dlg, IntPtr funcPtr) + private static void SetDelegateInfo(Delegate dlg, nint funcPtr) { string key = GetKey(dlg.Method); diff --git a/src/ARMeilleure/Translation/DispatcherFunction.cs b/src/ARMeilleure/Translation/DispatcherFunction.cs index 649fa0f50..f8b9dc31e 100644 --- a/src/ARMeilleure/Translation/DispatcherFunction.cs +++ b/src/ARMeilleure/Translation/DispatcherFunction.cs @@ -2,6 +2,6 @@ using System; namespace ARMeilleure.Translation { - delegate void DispatcherFunction(IntPtr nativeContext, ulong startAddress); - delegate ulong WrapperFunction(IntPtr nativeContext, ulong startAddress); + delegate void DispatcherFunction(nint nativeContext, ulong startAddress); + delegate ulong WrapperFunction(nint nativeContext, ulong startAddress); } diff --git a/src/ARMeilleure/Translation/Dominance.cs b/src/ARMeilleure/Translation/Dominance.cs index e2185bd85..b62714fdf 100644 --- a/src/ARMeilleure/Translation/Dominance.cs +++ b/src/ARMeilleure/Translation/Dominance.cs @@ -77,7 +77,7 @@ namespace ARMeilleure.Translation { continue; } - + for (int pBlkIndex = 0; pBlkIndex < block.Predecessors.Count; pBlkIndex++) { BasicBlock current = block.Predecessors[pBlkIndex]; diff --git a/src/ARMeilleure/Translation/EmitterContext.cs b/src/ARMeilleure/Translation/EmitterContext.cs index 88bfe1335..22b6b9842 100644 --- a/src/ARMeilleure/Translation/EmitterContext.cs +++ b/src/ARMeilleure/Translation/EmitterContext.cs @@ -97,7 +97,7 @@ namespace ARMeilleure.Translation public virtual Operand Call(MethodInfo info, params Operand[] callArgs) { - IntPtr funcPtr = Delegates.GetDelegateFuncPtr(info); + nint funcPtr = Delegates.GetDelegateFuncPtr(info); OperandType returnType = GetOperandType(info.ReturnType); @@ -559,27 +559,27 @@ namespace ARMeilleure.Translation return dest; } - public Operand AddIntrinsic(Intrinsic intrin, params Operand[] args) + public Operand AddIntrinsic(Intrinsic intrin, params ReadOnlySpan args) { return Add(intrin, Local(OperandType.V128), args); } - public Operand AddIntrinsicInt(Intrinsic intrin, params Operand[] args) + public Operand AddIntrinsicInt(Intrinsic intrin, params ReadOnlySpan args) { return Add(intrin, Local(OperandType.I32), args); } - public Operand AddIntrinsicLong(Intrinsic intrin, params Operand[] args) + public Operand AddIntrinsicLong(Intrinsic intrin, params ReadOnlySpan args) { return Add(intrin, Local(OperandType.I64), args); } - public void AddIntrinsicNoRet(Intrinsic intrin, params Operand[] args) + public void AddIntrinsicNoRet(Intrinsic intrin, params ReadOnlySpan args) { Add(intrin, default, args); } - private Operand Add(Intrinsic intrin, Operand dest, params Operand[] sources) + private Operand Add(Intrinsic intrin, Operand dest, params ReadOnlySpan sources) { NewNextBlockIfNeeded(); diff --git a/src/ARMeilleure/Translation/GuestFunction.cs b/src/ARMeilleure/Translation/GuestFunction.cs index 6414d6bd0..5c7c733f9 100644 --- a/src/ARMeilleure/Translation/GuestFunction.cs +++ b/src/ARMeilleure/Translation/GuestFunction.cs @@ -2,5 +2,5 @@ using System; namespace ARMeilleure.Translation { - delegate ulong GuestFunction(IntPtr nativeContextPtr); + delegate ulong GuestFunction(nint nativeContextPtr); } diff --git a/src/ARMeilleure/Translation/PTC/Ptc.cs b/src/ARMeilleure/Translation/PTC/Ptc.cs index c2eed7a55..4675abc49 100644 --- a/src/ARMeilleure/Translation/PTC/Ptc.cs +++ b/src/ARMeilleure/Translation/PTC/Ptc.cs @@ -13,6 +13,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Compression; +using System.Linq; using System.Runtime; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -29,7 +30,7 @@ namespace ARMeilleure.Translation.PTC private const string OuterHeaderMagicString = "PTCohd\0\0"; private const string InnerHeaderMagicString = "PTCihd\0\0"; - private const uint InternalVersion = 6950; //! To be incremented manually for each change to the ARMeilleure project. + private const uint InternalVersion = 6997; //! To be incremented manually for each change to the ARMeilleure project. private const string ActualDir = "0"; private const string BackupDir = "1"; @@ -40,6 +41,7 @@ namespace ARMeilleure.Translation.PTC public static readonly Symbol PageTableSymbol = new(SymbolType.Special, 1); public static readonly Symbol CountTableSymbol = new(SymbolType.Special, 2); public static readonly Symbol DispatchStubSymbol = new(SymbolType.Special, 3); + public static readonly Symbol FunctionTableSymbol = new(SymbolType.Special, 4); private const byte FillingByte = 0x00; private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest; @@ -57,7 +59,7 @@ namespace ARMeilleure.Translation.PTC private readonly ManualResetEvent _waitEvent; - private readonly object _lock; + private readonly Lock _lock = new(); private bool _disposed; @@ -87,8 +89,6 @@ namespace ARMeilleure.Translation.PTC _waitEvent = new ManualResetEvent(true); - _lock = new object(); - _disposed = false; TitleIdText = TitleIdTextDefault; @@ -100,7 +100,7 @@ namespace ARMeilleure.Translation.PTC Disable(); } - public void Initialize(string titleIdText, string displayVersion, bool enabled, MemoryManagerType memoryMode) + public void Initialize(string titleIdText, string displayVersion, bool enabled, MemoryManagerType memoryMode, string cacheSelector) { Wait(); @@ -126,6 +126,8 @@ namespace ARMeilleure.Translation.PTC DisplayVersion = !string.IsNullOrEmpty(displayVersion) ? displayVersion : DisplayVersionDefault; _memoryMode = memoryMode; + Logger.Info?.Print(LogClass.Ptc, $"PPTC (v{InternalVersion}) Profile: {DisplayVersion}-{cacheSelector}"); + string workPathActual = Path.Combine(AppDataManager.GamesDirPath, TitleIdText, "cache", "cpu", ActualDir); string workPathBackup = Path.Combine(AppDataManager.GamesDirPath, TitleIdText, "cache", "cpu", BackupDir); @@ -139,8 +141,8 @@ namespace ARMeilleure.Translation.PTC Directory.CreateDirectory(workPathBackup); } - CachePathActual = Path.Combine(workPathActual, DisplayVersion); - CachePathBackup = Path.Combine(workPathBackup, DisplayVersion); + CachePathActual = Path.Combine(workPathActual, DisplayVersion) + "-" + cacheSelector; + CachePathBackup = Path.Combine(workPathBackup, DisplayVersion) + "-" + cacheSelector; PreLoad(); Profiler.PreLoad(); @@ -268,11 +270,11 @@ namespace ARMeilleure.Translation.PTC return false; } - IntPtr intPtr = IntPtr.Zero; + nint intPtr = nint.Zero; try { - intPtr = Marshal.AllocHGlobal(new IntPtr(outerHeader.UncompressedStreamSize)); + intPtr = Marshal.AllocHGlobal(new nint(outerHeader.UncompressedStreamSize)); using UnmanagedMemoryStream stream = new((byte*)intPtr.ToPointer(), outerHeader.UncompressedStreamSize, outerHeader.UncompressedStreamSize, FileAccess.ReadWrite); try @@ -309,7 +311,7 @@ namespace ARMeilleure.Translation.PTC ReadOnlySpan infosBytes = new(stream.PositionPointer, innerHeader.InfosLength); stream.Seek(innerHeader.InfosLength, SeekOrigin.Current); - Hash128 infosHash = XXHash128.ComputeHash(infosBytes); + Hash128 infosHash = Hash128.ComputeHash(infosBytes); if (innerHeader.InfosHash != infosHash) { @@ -321,7 +323,7 @@ namespace ARMeilleure.Translation.PTC ReadOnlySpan codesBytes = (int)innerHeader.CodesLength > 0 ? new(stream.PositionPointer, (int)innerHeader.CodesLength) : ReadOnlySpan.Empty; stream.Seek(innerHeader.CodesLength, SeekOrigin.Current); - Hash128 codesHash = XXHash128.ComputeHash(codesBytes); + Hash128 codesHash = Hash128.ComputeHash(codesBytes); if (innerHeader.CodesHash != codesHash) { @@ -333,7 +335,7 @@ namespace ARMeilleure.Translation.PTC ReadOnlySpan relocsBytes = new(stream.PositionPointer, innerHeader.RelocsLength); stream.Seek(innerHeader.RelocsLength, SeekOrigin.Current); - Hash128 relocsHash = XXHash128.ComputeHash(relocsBytes); + Hash128 relocsHash = Hash128.ComputeHash(relocsBytes); if (innerHeader.RelocsHash != relocsHash) { @@ -345,7 +347,7 @@ namespace ARMeilleure.Translation.PTC ReadOnlySpan unwindInfosBytes = new(stream.PositionPointer, innerHeader.UnwindInfosLength); stream.Seek(innerHeader.UnwindInfosLength, SeekOrigin.Current); - Hash128 unwindInfosHash = XXHash128.ComputeHash(unwindInfosBytes); + Hash128 unwindInfosHash = Hash128.ComputeHash(unwindInfosBytes); if (innerHeader.UnwindInfosHash != unwindInfosHash) { @@ -373,7 +375,7 @@ namespace ARMeilleure.Translation.PTC } finally { - if (intPtr != IntPtr.Zero) + if (intPtr != nint.Zero) { Marshal.FreeHGlobal(intPtr); } @@ -455,11 +457,11 @@ namespace ARMeilleure.Translation.PTC outerHeader.SetHeaderHash(); - IntPtr intPtr = IntPtr.Zero; + nint intPtr = nint.Zero; try { - intPtr = Marshal.AllocHGlobal(new IntPtr(outerHeader.UncompressedStreamSize)); + intPtr = Marshal.AllocHGlobal(new nint(outerHeader.UncompressedStreamSize)); using UnmanagedMemoryStream stream = new((byte*)intPtr.ToPointer(), outerHeader.UncompressedStreamSize, outerHeader.UncompressedStreamSize, FileAccess.ReadWrite); stream.Seek((long)Unsafe.SizeOf(), SeekOrigin.Begin); @@ -478,10 +480,10 @@ namespace ARMeilleure.Translation.PTC Debug.Assert(stream.Position == stream.Length); - innerHeader.InfosHash = XXHash128.ComputeHash(infosBytes); - innerHeader.CodesHash = XXHash128.ComputeHash(codesBytes); - innerHeader.RelocsHash = XXHash128.ComputeHash(relocsBytes); - innerHeader.UnwindInfosHash = XXHash128.ComputeHash(unwindInfosBytes); + innerHeader.InfosHash = Hash128.ComputeHash(infosBytes); + innerHeader.CodesHash = Hash128.ComputeHash(codesBytes); + innerHeader.RelocsHash = Hash128.ComputeHash(relocsBytes); + innerHeader.UnwindInfosHash = Hash128.ComputeHash(unwindInfosBytes); innerHeader.SetHeaderHash(); @@ -513,7 +515,7 @@ namespace ARMeilleure.Translation.PTC } finally { - if (intPtr != IntPtr.Zero) + if (intPtr != nint.Zero) { Marshal.FreeHGlobal(intPtr); } @@ -664,7 +666,7 @@ namespace ARMeilleure.Translation.PTC foreach (RelocEntry relocEntry in relocEntries) { - IntPtr? imm = null; + nint? imm = null; Symbol symbol = relocEntry.Symbol; if (symbol.Type == SymbolType.FunctionTable) @@ -675,7 +677,7 @@ namespace ARMeilleure.Translation.PTC { unsafe { - imm = (IntPtr)Unsafe.AsPointer(ref translator.FunctionTable.GetValue(guestAddress)); + imm = (nint)Unsafe.AsPointer(ref translator.FunctionTable.GetValue(guestAddress)); } } } @@ -683,7 +685,7 @@ namespace ARMeilleure.Translation.PTC { int index = (int)symbol.Value; - if (Delegates.TryGetDelegateFuncPtrByIndex(index, out IntPtr funcPtr)) + if (Delegates.TryGetDelegateFuncPtrByIndex(index, out nint funcPtr)) { imm = funcPtr; } @@ -698,13 +700,17 @@ namespace ARMeilleure.Translation.PTC unsafe { - imm = (IntPtr)Unsafe.AsPointer(ref callCounter.Value); + imm = (nint)Unsafe.AsPointer(ref callCounter.Value); } } else if (symbol == DispatchStubSymbol) { imm = translator.Stubs.DispatchStub; } + else if (symbol == FunctionTableSymbol) + { + imm = translator.FunctionTable.Base; + } if (imm == null) { @@ -744,7 +750,7 @@ namespace ARMeilleure.Translation.PTC bool highCq) { var cFunc = new CompiledFunction(code, unwindInfo, RelocInfo.Empty); - var gFunc = cFunc.MapWithPointer(out IntPtr gFuncPointer); + var gFunc = cFunc.MapWithPointer(out nint gFuncPointer); return new TranslatedFunction(gFunc, gFuncPointer, callCounter, guestSize, highCq); } @@ -795,10 +801,15 @@ namespace ARMeilleure.Translation.PTC return; } + + int degreeOfParallelism = Environment.ProcessorCount; + if (Optimizations.LowPower) + degreeOfParallelism /= 3; + // If there are enough cores lying around, we leave one alone for other tasks. - if (degreeOfParallelism > 4) + if (degreeOfParallelism > 4 && !Optimizations.LowPower) { degreeOfParallelism--; } @@ -843,17 +854,15 @@ namespace ARMeilleure.Translation.PTC } } - List threads = new(); - for (int i = 0; i < degreeOfParallelism; i++) - { - Thread thread = new(TranslateFuncs) - { - IsBackground = true, - }; - - threads.Add(thread); - } + List threads = Enumerable.Range(0, degreeOfParallelism) + .Select(idx => + new Thread(TranslateFuncs) + { + IsBackground = true, + Name = "Ptc.TranslateThread." + idx + } + ).ToList(); Stopwatch sw = Stopwatch.StartNew(); @@ -880,6 +889,7 @@ namespace ARMeilleure.Translation.PTC Thread preSaveThread = new(PreSave) { IsBackground = true, + Name = "Ptc.DiskWriter" }; preSaveThread.Start(); } @@ -907,7 +917,7 @@ namespace ARMeilleure.Translation.PTC public static Hash128 ComputeHash(IMemoryManager memory, ulong address, ulong guestSize) { - return XXHash128.ComputeHash(memory.GetSpan(address, checked((int)(guestSize)))); + return Hash128.ComputeHash(memory.GetSpan(address, checked((int)(guestSize)))); } public void WriteCompiledFunction(ulong address, ulong guestSize, Hash128 hash, bool highCq, CompiledFunction compiledFunc) @@ -1036,14 +1046,14 @@ namespace ARMeilleure.Translation.PTC { Span spanHeader = MemoryMarshal.CreateSpan(ref this, 1); - HeaderHash = XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader)[..(Unsafe.SizeOf() - Unsafe.SizeOf())]); + HeaderHash = Hash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader)[..(Unsafe.SizeOf() - Unsafe.SizeOf())]); } public bool IsHeaderValid() { Span spanHeader = MemoryMarshal.CreateSpan(ref this, 1); - return XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader)[..(Unsafe.SizeOf() - Unsafe.SizeOf())]) == HeaderHash; + return Hash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader)[..(Unsafe.SizeOf() - Unsafe.SizeOf())]) == HeaderHash; } } @@ -1071,14 +1081,14 @@ namespace ARMeilleure.Translation.PTC { Span spanHeader = MemoryMarshal.CreateSpan(ref this, 1); - HeaderHash = XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader)[..(Unsafe.SizeOf() - Unsafe.SizeOf())]); + HeaderHash = Hash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader)[..(Unsafe.SizeOf() - Unsafe.SizeOf())]); } public bool IsHeaderValid() { Span spanHeader = MemoryMarshal.CreateSpan(ref this, 1); - return XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader)[..(Unsafe.SizeOf() - Unsafe.SizeOf())]) == HeaderHash; + return Hash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader)[..(Unsafe.SizeOf() - Unsafe.SizeOf())]) == HeaderHash; } } diff --git a/src/ARMeilleure/Translation/PTC/PtcProfiler.cs b/src/ARMeilleure/Translation/PTC/PtcProfiler.cs index 0fe78edab..7e630ae10 100644 --- a/src/ARMeilleure/Translation/PTC/PtcProfiler.cs +++ b/src/ARMeilleure/Translation/PTC/PtcProfiler.cs @@ -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(); @@ -209,7 +207,7 @@ namespace ARMeilleure.Translation.PTC Hash128 expectedHash = DeserializeStructure(stream); - Hash128 actualHash = XXHash128.ComputeHash(GetReadOnlySpan(stream)); + Hash128 actualHash = Hash128.ComputeHash(GetReadOnlySpan(stream)); if (actualHash != expectedHash) { @@ -313,7 +311,7 @@ namespace ARMeilleure.Translation.PTC Debug.Assert(stream.Position == stream.Length); stream.Seek(Unsafe.SizeOf(), SeekOrigin.Begin); - Hash128 hash = XXHash128.ComputeHash(GetReadOnlySpan(stream)); + Hash128 hash = Hash128.ComputeHash(GetReadOnlySpan(stream)); stream.Seek(0L, SeekOrigin.Begin); SerializeStructure(stream, hash); @@ -374,14 +372,14 @@ namespace ARMeilleure.Translation.PTC { Span spanHeader = MemoryMarshal.CreateSpan(ref this, 1); - HeaderHash = XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader)[..(Unsafe.SizeOf() - Unsafe.SizeOf())]); + HeaderHash = Hash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader)[..(Unsafe.SizeOf() - Unsafe.SizeOf())]); } public bool IsHeaderValid() { Span spanHeader = MemoryMarshal.CreateSpan(ref this, 1); - return XXHash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader)[..(Unsafe.SizeOf() - Unsafe.SizeOf())]) == HeaderHash; + return Hash128.ComputeHash(MemoryMarshal.AsBytes(spanHeader)[..(Unsafe.SizeOf() - Unsafe.SizeOf())]) == HeaderHash; } } diff --git a/src/ARMeilleure/Translation/TranslatedFunction.cs b/src/ARMeilleure/Translation/TranslatedFunction.cs index 1446c254a..3d7ae9ffe 100644 --- a/src/ARMeilleure/Translation/TranslatedFunction.cs +++ b/src/ARMeilleure/Translation/TranslatedFunction.cs @@ -7,12 +7,12 @@ namespace ARMeilleure.Translation { private readonly GuestFunction _func; // Ensure that this delegate will not be garbage collected. - public IntPtr FuncPointer { get; } + public nint FuncPointer { get; } public Counter CallCounter { get; } public ulong GuestSize { get; } public bool HighCq { get; } - public TranslatedFunction(GuestFunction func, IntPtr funcPointer, Counter callCounter, ulong guestSize, bool highCq) + public TranslatedFunction(GuestFunction func, nint funcPointer, Counter callCounter, ulong guestSize, bool highCq) { _func = func; FuncPointer = funcPointer; diff --git a/src/ARMeilleure/Translation/Translator.cs b/src/ARMeilleure/Translation/Translator.cs index 014b12035..162368782 100644 --- a/src/ARMeilleure/Translation/Translator.cs +++ b/src/ARMeilleure/Translation/Translator.cs @@ -22,33 +22,13 @@ namespace ARMeilleure.Translation { public class Translator { - private static readonly AddressTable.Level[] _levels64Bit = - new AddressTable.Level[] - { - new(31, 17), - new(23, 8), - new(15, 8), - new( 7, 8), - new( 2, 5), - }; - - private static readonly AddressTable.Level[] _levels32Bit = - new AddressTable.Level[] - { - new(31, 17), - new(23, 8), - new(15, 8), - new( 7, 8), - new( 1, 6), - }; - private readonly IJitMemoryAllocator _allocator; private readonly ConcurrentQueue> _oldFuncs; private readonly Ptc _ptc; internal TranslatorCache Functions { get; } - internal AddressTable FunctionTable { get; } + internal IAddressTable FunctionTable { get; } internal EntryTable CountTable { get; } internal TranslatorStubs Stubs { get; } internal TranslatorQueue Queue { get; } @@ -57,7 +37,7 @@ namespace ARMeilleure.Translation private Thread[] _backgroundTranslationThreads; private volatile int _threadCount; - public Translator(IJitMemoryAllocator allocator, IMemoryManager memory, bool for64Bits) + public Translator(IJitMemoryAllocator allocator, IMemoryManager memory, IAddressTable functionTable) { _allocator = allocator; Memory = memory; @@ -72,15 +52,15 @@ namespace ARMeilleure.Translation CountTable = new EntryTable(); Functions = new TranslatorCache(); - FunctionTable = new AddressTable(for64Bits ? _levels64Bit : _levels32Bit); + FunctionTable = functionTable; Stubs = new TranslatorStubs(FunctionTable); FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub; } - public IPtcLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled) + public IPtcLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector) { - _ptc.Initialize(titleIdText, displayVersion, enabled, Memory.Type); + _ptc.Initialize(titleIdText, displayVersion, enabled, Memory.Type, cacheSelector); return _ptc; } @@ -298,7 +278,7 @@ namespace ARMeilleure.Translation _ptc.WriteCompiledFunction(address, funcSize, hash, highCq, compiledFunc); } - GuestFunction func = compiledFunc.MapWithPointer(out IntPtr funcPointer); + GuestFunction func = compiledFunc.MapWithPointer(out nint funcPointer); Allocators.ResetAll(); diff --git a/src/ARMeilleure/Translation/TranslatorStubs.cs b/src/ARMeilleure/Translation/TranslatorStubs.cs index d80823a8b..bd9aed8d4 100644 --- a/src/ARMeilleure/Translation/TranslatorStubs.cs +++ b/src/ARMeilleure/Translation/TranslatorStubs.cs @@ -15,12 +15,12 @@ namespace ARMeilleure.Translation /// class TranslatorStubs : IDisposable { - private readonly Lazy _slowDispatchStub; + private readonly Lazy _slowDispatchStub; private bool _disposed; - private readonly AddressTable _functionTable; - private readonly Lazy _dispatchStub; + private readonly IAddressTable _functionTable; + private readonly Lazy _dispatchStub; private readonly Lazy _dispatchLoop; private readonly Lazy _contextWrapper; @@ -28,7 +28,7 @@ namespace ARMeilleure.Translation /// Gets the dispatch stub. /// /// instance was disposed - public IntPtr DispatchStub + public nint DispatchStub { get { @@ -42,7 +42,7 @@ namespace ARMeilleure.Translation /// Gets the slow dispatch stub. /// /// instance was disposed - public IntPtr SlowDispatchStub + public nint SlowDispatchStub { get { @@ -86,7 +86,7 @@ namespace ARMeilleure.Translation /// /// Function table used to store pointers to the functions that the guest code will call /// is null - public TranslatorStubs(AddressTable functionTable) + public TranslatorStubs(IAddressTable functionTable) { ArgumentNullException.ThrowIfNull(functionTable); @@ -140,7 +140,7 @@ namespace ARMeilleure.Translation /// Generates a . /// /// Generated - private IntPtr GenerateDispatchStub() + private nint GenerateDispatchStub() { var context = new EmitterContext(); @@ -198,7 +198,7 @@ namespace ARMeilleure.Translation /// Generates a . /// /// Generated - private IntPtr GenerateSlowDispatchStub() + private nint GenerateSlowDispatchStub() { var context = new EmitterContext(); diff --git a/src/ARMeilleure/Translation/TranslatorTestMethods.cs b/src/ARMeilleure/Translation/TranslatorTestMethods.cs index 8cc7a3cf8..186780daa 100644 --- a/src/ARMeilleure/Translation/TranslatorTestMethods.cs +++ b/src/ARMeilleure/Translation/TranslatorTestMethods.cs @@ -9,7 +9,7 @@ namespace ARMeilleure.Translation { public static class TranslatorTestMethods { - public delegate int FpFlagsPInvokeTest(IntPtr managedMethod); + public delegate int FpFlagsPInvokeTest(nint managedMethod); private static bool SetPlatformFtz(EmitterContext context, bool ftz) { diff --git a/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs b/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs index 01286992f..25f91f8e9 100644 --- a/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs +++ b/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceDriver.cs @@ -41,7 +41,7 @@ namespace Ryujinx.Audio.Backends.OpenAL public OpenALHardwareDeviceDriver() { - _device = ALC.OpenDevice(""); + _device = ALC.OpenDevice(string.Empty); _context = ALC.CreateContext(_device, new ALContextAttributes()); _updateRequiredEvent = new ManualResetEvent(false); _pauseEvent = new ManualResetEvent(true); diff --git a/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs b/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs index 3b9129130..7292450a6 100644 --- a/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs +++ b/src/Ryujinx.Audio.Backends.OpenAL/OpenALHardwareDeviceSession.cs @@ -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) { diff --git a/src/Ryujinx.Audio.Backends.OpenAL/Ryujinx.Audio.Backends.OpenAL.csproj b/src/Ryujinx.Audio.Backends.OpenAL/Ryujinx.Audio.Backends.OpenAL.csproj index b5fd8f9e7..4ef3aebec 100644 --- a/src/Ryujinx.Audio.Backends.OpenAL/Ryujinx.Audio.Backends.OpenAL.csproj +++ b/src/Ryujinx.Audio.Backends.OpenAL/Ryujinx.Audio.Backends.OpenAL.csproj @@ -1,7 +1,7 @@ - net8.0 + $(DefaultItemExcludes);._* diff --git a/src/Ryujinx.Audio.Backends.SDL2/Ryujinx.Audio.Backends.SDL2.csproj b/src/Ryujinx.Audio.Backends.SDL2/Ryujinx.Audio.Backends.SDL2.csproj index dd18e70a1..d0d45122e 100644 --- a/src/Ryujinx.Audio.Backends.SDL2/Ryujinx.Audio.Backends.SDL2.csproj +++ b/src/Ryujinx.Audio.Backends.SDL2/Ryujinx.Audio.Backends.SDL2.csproj @@ -1,8 +1,8 @@ - net8.0 true + $(DefaultItemExcludes);._* diff --git a/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs b/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs index e39bfe549..acd1582ec 100644 --- a/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs +++ b/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs @@ -26,7 +26,7 @@ namespace Ryujinx.Audio.Backends.SDL2 // NOTE: We use a DllImport here because of marshaling issue for spec. #pragma warning disable SYSLIB1054 [DllImport("SDL2")] - private static extern int SDL_GetDefaultAudioInfo(IntPtr name, out SDL_AudioSpec spec, int isCapture); + private static extern int SDL_GetDefaultAudioInfo(nint name, out SDL_AudioSpec spec, int isCapture); #pragma warning restore SYSLIB1054 public SDL2HardwareDeviceDriver() @@ -37,7 +37,7 @@ namespace Ryujinx.Audio.Backends.SDL2 SDL2Driver.Instance.Initialize(); - int res = SDL_GetDefaultAudioInfo(IntPtr.Zero, out var spec, 0); + int res = SDL_GetDefaultAudioInfo(nint.Zero, out var spec, 0); if (res != 0) { @@ -136,7 +136,7 @@ namespace Ryujinx.Audio.Backends.SDL2 desired.callback = callback; - uint device = SDL_OpenAudioDevice(IntPtr.Zero, 0, ref desired, out SDL_AudioSpec got, 0); + uint device = SDL_OpenAudioDevice(nint.Zero, 0, ref desired, out SDL_AudioSpec got, 0); if (device == 0) { diff --git a/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs b/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs index 4eb75a578..51cd43c55 100644 --- a/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs +++ b/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceSession.cs @@ -72,7 +72,7 @@ namespace Ryujinx.Audio.Backends.SDL2 } } - private unsafe void Update(IntPtr userdata, IntPtr stream, int streamLength) + private unsafe void Update(nint userdata, nint stream, int streamLength) { Span streamSpan = new((void*)stream, streamLength); @@ -97,7 +97,7 @@ namespace Ryujinx.Audio.Backends.SDL2 fixed (byte* p = samples) { - IntPtr pStreamSrc = (IntPtr)p; + nint pStreamSrc = (nint)p; // Zero the dest buffer streamSpan.Clear(); diff --git a/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIo.cs b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIo.cs index 7fdb1fc04..9decd79fc 100644 --- a/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIo.cs +++ b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIo.cs @@ -10,41 +10,41 @@ namespace Ryujinx.Audio.Backends.SoundIo.Native private const string LibraryName = "libsoundio"; [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void OnDeviceChangeNativeDelegate(IntPtr ctx); + public delegate void OnDeviceChangeNativeDelegate(nint ctx); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void OnBackendDisconnectedDelegate(IntPtr ctx, SoundIoError err); + public delegate void OnBackendDisconnectedDelegate(nint ctx, SoundIoError err); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void OnEventsSignalDelegate(IntPtr ctx); + public delegate void OnEventsSignalDelegate(nint ctx); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void EmitRtPrioWarningDelegate(); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void JackCallbackDelegate(IntPtr msg); + public delegate void JackCallbackDelegate(nint msg); [StructLayout(LayoutKind.Sequential)] public struct SoundIoStruct { - public IntPtr UserData; - public IntPtr OnDeviceChange; - public IntPtr OnBackendDisconnected; - public IntPtr OnEventsSignal; + public nint UserData; + public nint OnDeviceChange; + public nint OnBackendDisconnected; + public nint OnEventsSignal; public SoundIoBackend CurrentBackend; - public IntPtr ApplicationName; - public IntPtr EmitRtPrioWarning; - public IntPtr JackInfoCallback; - public IntPtr JackErrorCallback; + public nint ApplicationName; + public nint EmitRtPrioWarning; + public nint JackInfoCallback; + public nint JackErrorCallback; } public struct SoundIoChannelLayout { - public IntPtr Name; + public nint Name; public int ChannelCount; public Array24 Channels; - public static IntPtr GetDefault(int channelCount) + public static nint GetDefault(int channelCount) { return soundio_channel_layout_get_default(channelCount); } @@ -63,17 +63,17 @@ namespace Ryujinx.Audio.Backends.SoundIo.Native public struct SoundIoDevice { - public IntPtr SoundIo; - public IntPtr Id; - public IntPtr Name; + public nint SoundIo; + public nint Id; + public nint Name; public SoundIoDeviceAim Aim; - public IntPtr Layouts; + public nint Layouts; public int LayoutCount; public SoundIoChannelLayout CurrentLayout; - public IntPtr Formats; + public nint Formats; public int FormatCount; public SoundIoFormat CurrentFormat; - public IntPtr SampleRates; + public nint SampleRates; public int SampleRateCount; public int SampleRateCurrent; public double SoftwareLatencyMin; @@ -86,17 +86,17 @@ namespace Ryujinx.Audio.Backends.SoundIo.Native public struct SoundIoOutStream { - public IntPtr Device; + public nint Device; public SoundIoFormat Format; public int SampleRate; public SoundIoChannelLayout Layout; public double SoftwareLatency; public float Volume; - public IntPtr UserData; - public IntPtr WriteCallback; - public IntPtr UnderflowCallback; - public IntPtr ErrorCallback; - public IntPtr Name; + public nint UserData; + public nint WriteCallback; + public nint UnderflowCallback; + public nint ErrorCallback; + public nint Name; public bool NonTerminalHint; public int BytesPerFrame; public int BytesPerSample; @@ -105,74 +105,74 @@ namespace Ryujinx.Audio.Backends.SoundIo.Native public struct SoundIoChannelArea { - public IntPtr Pointer; + public nint Pointer; public int Step; } [LibraryImport(LibraryName)] - internal static partial IntPtr soundio_create(); + internal static partial nint soundio_create(); [LibraryImport(LibraryName)] - internal static partial SoundIoError soundio_connect(IntPtr ctx); + internal static partial SoundIoError soundio_connect(nint ctx); [LibraryImport(LibraryName)] - internal static partial void soundio_disconnect(IntPtr ctx); + internal static partial void soundio_disconnect(nint ctx); [LibraryImport(LibraryName)] - internal static partial void soundio_flush_events(IntPtr ctx); + internal static partial void soundio_flush_events(nint ctx); [LibraryImport(LibraryName)] - internal static partial int soundio_output_device_count(IntPtr ctx); + internal static partial int soundio_output_device_count(nint ctx); [LibraryImport(LibraryName)] - internal static partial int soundio_default_output_device_index(IntPtr ctx); + internal static partial int soundio_default_output_device_index(nint ctx); [LibraryImport(LibraryName)] - internal static partial IntPtr soundio_get_output_device(IntPtr ctx, int index); + internal static partial nint soundio_get_output_device(nint ctx, int index); [LibraryImport(LibraryName)] [return: MarshalAs(UnmanagedType.Bool)] - internal static partial bool soundio_device_supports_format(IntPtr devCtx, SoundIoFormat format); + internal static partial bool soundio_device_supports_format(nint devCtx, SoundIoFormat format); [LibraryImport(LibraryName)] [return: MarshalAs(UnmanagedType.Bool)] - internal static partial bool soundio_device_supports_layout(IntPtr devCtx, IntPtr layout); + internal static partial bool soundio_device_supports_layout(nint devCtx, nint layout); [LibraryImport(LibraryName)] [return: MarshalAs(UnmanagedType.Bool)] - internal static partial bool soundio_device_supports_sample_rate(IntPtr devCtx, int sampleRate); + internal static partial bool soundio_device_supports_sample_rate(nint devCtx, int sampleRate); [LibraryImport(LibraryName)] - internal static partial IntPtr soundio_outstream_create(IntPtr devCtx); + internal static partial nint soundio_outstream_create(nint devCtx); [LibraryImport(LibraryName)] - internal static partial SoundIoError soundio_outstream_open(IntPtr outStreamCtx); + internal static partial SoundIoError soundio_outstream_open(nint outStreamCtx); [LibraryImport(LibraryName)] - internal static partial SoundIoError soundio_outstream_start(IntPtr outStreamCtx); + internal static partial SoundIoError soundio_outstream_start(nint outStreamCtx); [LibraryImport(LibraryName)] - internal static partial SoundIoError soundio_outstream_begin_write(IntPtr outStreamCtx, IntPtr areas, IntPtr frameCount); + internal static partial SoundIoError soundio_outstream_begin_write(nint outStreamCtx, nint areas, nint frameCount); [LibraryImport(LibraryName)] - internal static partial SoundIoError soundio_outstream_end_write(IntPtr outStreamCtx); + internal static partial SoundIoError soundio_outstream_end_write(nint outStreamCtx); [LibraryImport(LibraryName)] - internal static partial SoundIoError soundio_outstream_pause(IntPtr devCtx, [MarshalAs(UnmanagedType.Bool)] bool pause); + internal static partial SoundIoError soundio_outstream_pause(nint devCtx, [MarshalAs(UnmanagedType.Bool)] bool pause); [LibraryImport(LibraryName)] - internal static partial SoundIoError soundio_outstream_set_volume(IntPtr devCtx, double volume); + internal static partial SoundIoError soundio_outstream_set_volume(nint devCtx, double volume); [LibraryImport(LibraryName)] - internal static partial void soundio_outstream_destroy(IntPtr streamCtx); + internal static partial void soundio_outstream_destroy(nint streamCtx); [LibraryImport(LibraryName)] - internal static partial void soundio_destroy(IntPtr ctx); + internal static partial void soundio_destroy(nint ctx); [LibraryImport(LibraryName)] - internal static partial IntPtr soundio_channel_layout_get_default(int channelCount); + internal static partial nint soundio_channel_layout_get_default(int channelCount); [LibraryImport(LibraryName)] - internal static partial IntPtr soundio_strerror(SoundIoError err); + internal static partial nint soundio_strerror(SoundIoError err); } } diff --git a/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoContext.cs b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoContext.cs index f2e91fcd7..a881e8ffe 100644 --- a/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoContext.cs +++ b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoContext.cs @@ -8,13 +8,13 @@ namespace Ryujinx.Audio.Backends.SoundIo.Native { public class SoundIoContext : IDisposable { - private IntPtr _context; + private nint _context; private Action _onBackendDisconnect; private OnBackendDisconnectedDelegate _onBackendDisconnectNative; - public IntPtr Context => _context; + public nint Context => _context; - internal SoundIoContext(IntPtr context) + internal SoundIoContext(nint context) { _context = context; _onBackendDisconnect = null; @@ -60,9 +60,9 @@ namespace Ryujinx.Audio.Backends.SoundIo.Native public SoundIoDeviceContext GetOutputDevice(int index) { - IntPtr deviceContext = soundio_get_output_device(_context, index); + nint deviceContext = soundio_get_output_device(_context, index); - if (deviceContext == IntPtr.Zero) + if (deviceContext == nint.Zero) { return null; } @@ -72,9 +72,9 @@ namespace Ryujinx.Audio.Backends.SoundIo.Native public static SoundIoContext Create() { - IntPtr context = soundio_create(); + nint context = soundio_create(); - if (context == IntPtr.Zero) + if (context == nint.Zero) { return null; } @@ -84,9 +84,9 @@ namespace Ryujinx.Audio.Backends.SoundIo.Native protected virtual void Dispose(bool disposing) { - IntPtr currentContext = Interlocked.Exchange(ref _context, IntPtr.Zero); + nint currentContext = Interlocked.Exchange(ref _context, nint.Zero); - if (currentContext != IntPtr.Zero) + if (currentContext != nint.Zero) { soundio_destroy(currentContext); } diff --git a/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoDeviceContext.cs b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoDeviceContext.cs index 7923e9b17..efea52b35 100644 --- a/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoDeviceContext.cs +++ b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoDeviceContext.cs @@ -7,11 +7,11 @@ namespace Ryujinx.Audio.Backends.SoundIo.Native { public class SoundIoDeviceContext { - private readonly IntPtr _context; + private readonly nint _context; - public IntPtr Context => _context; + public nint Context => _context; - internal SoundIoDeviceContext(IntPtr context) + internal SoundIoDeviceContext(nint context) { _context = context; } @@ -36,9 +36,9 @@ namespace Ryujinx.Audio.Backends.SoundIo.Native public SoundIoOutStreamContext CreateOutStream() { - IntPtr context = soundio_outstream_create(_context); + nint context = soundio_outstream_create(_context); - if (context == IntPtr.Zero) + if (context == nint.Zero) { return null; } diff --git a/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoOutStreamContext.cs b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoOutStreamContext.cs index 4148ea0dd..b1823a074 100644 --- a/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoOutStreamContext.cs +++ b/src/Ryujinx.Audio.Backends.SoundIo/Native/SoundIoOutStreamContext.cs @@ -8,19 +8,19 @@ namespace Ryujinx.Audio.Backends.SoundIo.Native public class SoundIoOutStreamContext : IDisposable { [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private unsafe delegate void WriteCallbackDelegate(IntPtr ctx, int frameCountMin, int frameCountMax); + private unsafe delegate void WriteCallbackDelegate(nint ctx, int frameCountMin, int frameCountMax); - private IntPtr _context; - private IntPtr _nameStored; + private nint _context; + private nint _nameStored; private Action _writeCallback; private WriteCallbackDelegate _writeCallbackNative; - public IntPtr Context => _context; + public nint Context => _context; - internal SoundIoOutStreamContext(IntPtr context) + internal SoundIoOutStreamContext(nint context) { _context = context; - _nameStored = IntPtr.Zero; + _nameStored = nint.Zero; _writeCallback = null; _writeCallbackNative = null; } @@ -40,7 +40,7 @@ namespace Ryujinx.Audio.Backends.SoundIo.Native { var context = GetOutContext(); - if (_nameStored != IntPtr.Zero && context.Name == _nameStored) + if (_nameStored != nint.Zero && context.Name == _nameStored) { Marshal.FreeHGlobal(_nameStored); } @@ -124,14 +124,14 @@ namespace Ryujinx.Audio.Backends.SoundIo.Native public Span BeginWrite(ref int frameCount) { - IntPtr arenas = default; + nint arenas = default; int nativeFrameCount = frameCount; unsafe { var frameCountPtr = &nativeFrameCount; var arenasPtr = &arenas; - CheckError(soundio_outstream_begin_write(_context, (IntPtr)arenasPtr, (IntPtr)frameCountPtr)); + CheckError(soundio_outstream_begin_write(_context, (nint)arenasPtr, (nint)frameCountPtr)); frameCount = *frameCountPtr; @@ -143,10 +143,10 @@ namespace Ryujinx.Audio.Backends.SoundIo.Native protected virtual void Dispose(bool disposing) { - if (_context != IntPtr.Zero) + if (_context != nint.Zero) { soundio_outstream_destroy(_context); - _context = IntPtr.Zero; + _context = nint.Zero; } } diff --git a/src/Ryujinx.Audio.Backends.SoundIo/Ryujinx.Audio.Backends.SoundIo.csproj b/src/Ryujinx.Audio.Backends.SoundIo/Ryujinx.Audio.Backends.SoundIo.csproj index 5c9423463..d06f66181 100644 --- a/src/Ryujinx.Audio.Backends.SoundIo/Ryujinx.Audio.Backends.SoundIo.csproj +++ b/src/Ryujinx.Audio.Backends.SoundIo/Ryujinx.Audio.Backends.SoundIo.csproj @@ -1,9 +1,9 @@ - net8.0 true win-x64;osx-x64;linux-x64 + $(DefaultItemExcludes);._* diff --git a/src/Ryujinx.Audio/AudioManager.cs b/src/Ryujinx.Audio/AudioManager.cs index 370d3d098..8c2c0ef6b 100644 --- a/src/Ryujinx.Audio/AudioManager.cs +++ b/src/Ryujinx.Audio/AudioManager.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Audio /// /// Lock used to control the waiters registration. /// - private readonly object _lock = new(); + private readonly Lock _lock = new(); /// /// Events signaled when the driver played audio buffers. diff --git a/src/Ryujinx.Audio/Backends/Common/DynamicRingBuffer.cs b/src/Ryujinx.Audio/Backends/Common/DynamicRingBuffer.cs index 7aefe8865..6f31755a3 100644 --- a/src/Ryujinx.Audio/Backends/Common/DynamicRingBuffer.cs +++ b/src/Ryujinx.Audio/Backends/Common/DynamicRingBuffer.cs @@ -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 _bufferOwner; private Memory _buffer; diff --git a/src/Ryujinx.Audio/Input/AudioInputManager.cs b/src/Ryujinx.Audio/Input/AudioInputManager.cs index d56997e9c..ffc3e6da2 100644 --- a/src/Ryujinx.Audio/Input/AudioInputManager.cs +++ b/src/Ryujinx.Audio/Input/AudioInputManager.cs @@ -14,12 +14,12 @@ namespace Ryujinx.Audio.Input /// public class AudioInputManager : IDisposable { - private readonly object _lock = new(); + private readonly Lock _lock = new(); /// /// Lock used for session allocation. /// - private readonly object _sessionLock = new(); + private readonly Lock _sessionLock = new(); /// /// The session ids allocation table. diff --git a/src/Ryujinx.Audio/Input/AudioInputSystem.cs b/src/Ryujinx.Audio/Input/AudioInputSystem.cs index 34623b34f..65b99745d 100644 --- a/src/Ryujinx.Audio/Input/AudioInputSystem.cs +++ b/src/Ryujinx.Audio/Input/AudioInputSystem.cs @@ -48,7 +48,7 @@ namespace Ryujinx.Audio.Input /// /// The lock of the parent. /// - private readonly object _parentLock; + private readonly Lock _parentLock; /// /// The dispose state. @@ -62,7 +62,7 @@ namespace Ryujinx.Audio.Input /// The lock of the manager /// The hardware device session /// The buffer release event of the audio input - public AudioInputSystem(AudioInputManager manager, object parentLock, IHardwareDeviceSession deviceSession, IWritableEvent bufferEvent) + public AudioInputSystem(AudioInputManager manager, Lock parentLock, IHardwareDeviceSession deviceSession, IWritableEvent bufferEvent) { _manager = manager; _parentLock = parentLock; diff --git a/src/Ryujinx.Audio/Output/AudioOutputManager.cs b/src/Ryujinx.Audio/Output/AudioOutputManager.cs index 308cd1564..13e169a24 100644 --- a/src/Ryujinx.Audio/Output/AudioOutputManager.cs +++ b/src/Ryujinx.Audio/Output/AudioOutputManager.cs @@ -14,12 +14,12 @@ namespace Ryujinx.Audio.Output /// public class AudioOutputManager : IDisposable { - private readonly object _lock = new(); + private readonly Lock _lock = new(); /// /// Lock used for session allocation. /// - private readonly object _sessionLock = new(); + private readonly Lock _sessionLock = new(); /// /// The session ids allocation table. diff --git a/src/Ryujinx.Audio/Output/AudioOutputSystem.cs b/src/Ryujinx.Audio/Output/AudioOutputSystem.cs index f9b9bdcf1..dc7d52ced 100644 --- a/src/Ryujinx.Audio/Output/AudioOutputSystem.cs +++ b/src/Ryujinx.Audio/Output/AudioOutputSystem.cs @@ -48,7 +48,7 @@ namespace Ryujinx.Audio.Output /// /// THe lock of the parent. /// - private readonly object _parentLock; + private readonly Lock _parentLock; /// /// The dispose state. @@ -62,7 +62,7 @@ namespace Ryujinx.Audio.Output /// The lock of the manager /// The hardware device session /// The buffer release event of the audio output - public AudioOutputSystem(AudioOutputManager manager, object parentLock, IHardwareDeviceSession deviceSession, IWritableEvent bufferEvent) + public AudioOutputSystem(AudioOutputManager manager, Lock parentLock, IHardwareDeviceSession deviceSession, IWritableEvent bufferEvent) { _manager = manager; _parentLock = parentLock; diff --git a/src/Ryujinx.Audio/Renderer/Dsp/AdpcmHelper.cs b/src/Ryujinx.Audio/Renderer/Dsp/AdpcmHelper.cs index 5cb4509ff..8b497fe2a 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/AdpcmHelper.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/AdpcmHelper.cs @@ -81,7 +81,7 @@ namespace Ryujinx.Audio.Renderer.Dsp [MethodImpl(MethodImplOptions.AggressiveInlining)] private static short GetCoefficientAtIndex(ReadOnlySpan coefficients, int index) { - if ((uint)index > (uint)coefficients.Length) + if ((uint)index >= (uint)coefficients.Length) { Logger.Error?.Print(LogClass.AudioRenderer, $"Out of bound read for coefficient at index {index}"); diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/CommandList.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/CommandList.cs index 3fe106ddf..ba19330b6 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/CommandList.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/CommandList.cs @@ -64,11 +64,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe IntPtr GetBufferPointer(int index) + public unsafe nint GetBufferPointer(int index) { if (index >= 0 && index < _buffersEntryCount) { - return (IntPtr)((float*)_buffersMemoryHandle.Pointer + index * _sampleCount); + return (nint)((float*)_buffersMemoryHandle.Pointer + index * _sampleCount); } throw new ArgumentOutOfRangeException(nameof(index), index, null); diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/CompressorCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/CompressorCommand.cs index 09f415d20..c6c0956a6 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/CompressorCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/CompressorCommand.cs @@ -1,9 +1,11 @@ using Ryujinx.Audio.Renderer.Dsp.Effect; using Ryujinx.Audio.Renderer.Dsp.State; +using Ryujinx.Audio.Renderer.Parameter; using Ryujinx.Audio.Renderer.Parameter.Effect; using Ryujinx.Audio.Renderer.Server.Effect; using System; using System.Diagnostics; +using System.Runtime.InteropServices; namespace Ryujinx.Audio.Renderer.Dsp.Command { @@ -21,18 +23,20 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public CompressorParameter Parameter => _parameter; public Memory State { get; } + public Memory ResultState { get; } public ushort[] OutputBufferIndices { get; } public ushort[] InputBufferIndices { get; } public bool IsEffectEnabled { get; } private CompressorParameter _parameter; - public CompressorCommand(uint bufferOffset, CompressorParameter parameter, Memory state, bool isEnabled, int nodeId) + public CompressorCommand(uint bufferOffset, CompressorParameter parameter, Memory state, Memory resultState, bool isEnabled, int nodeId) { Enabled = true; NodeId = nodeId; _parameter = parameter; State = state; + ResultState = resultState; IsEffectEnabled = isEnabled; @@ -71,9 +75,16 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command if (IsEffectEnabled && _parameter.IsChannelCountValid()) { - Span inputBuffers = stackalloc IntPtr[Parameter.ChannelCount]; - Span outputBuffers = stackalloc IntPtr[Parameter.ChannelCount]; - Span channelInput = stackalloc float[Parameter.ChannelCount]; + if (!ResultState.IsEmpty && _parameter.StatisticsReset) + { + ref CompressorStatistics statistics = ref MemoryMarshal.Cast(ResultState.Span[0].SpecificData)[0]; + + statistics.Reset(_parameter.ChannelCount); + } + + Span inputBuffers = stackalloc nint[_parameter.ChannelCount]; + Span outputBuffers = stackalloc nint[_parameter.ChannelCount]; + Span channelInput = stackalloc float[_parameter.ChannelCount]; ExponentialMovingAverage inputMovingAverage = state.InputMovingAverage; float unknown4 = state.Unknown4; ExponentialMovingAverage compressionGainAverage = state.CompressionGainAverage; @@ -92,7 +103,8 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command channelInput[channelIndex] = *((float*)inputBuffers[channelIndex] + sampleIndex); } - float newMean = inputMovingAverage.Update(FloatingPointHelper.MeanSquare(channelInput), _parameter.InputGain); + float mean = FloatingPointHelper.MeanSquare(channelInput); + float newMean = inputMovingAverage.Update(mean, _parameter.InputGain); float y = FloatingPointHelper.Log10(newMean) * 10.0f; float z = 1.0f; @@ -111,7 +123,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command if (y >= state.Unknown14) { - tmpGain = ((1.0f / Parameter.Ratio) - 1.0f) * (y - Parameter.Threshold); + tmpGain = ((1.0f / _parameter.Ratio) - 1.0f) * (y - _parameter.Threshold); } else { @@ -126,7 +138,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command if ((unknown4 - z) <= 0.08f) { - compressionEmaAlpha = Parameter.ReleaseCoefficient; + compressionEmaAlpha = _parameter.ReleaseCoefficient; if ((unknown4 - z) >= -0.08f) { @@ -140,18 +152,31 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command } else { - compressionEmaAlpha = Parameter.AttackCoefficient; + compressionEmaAlpha = _parameter.AttackCoefficient; } float compressionGain = compressionGainAverage.Update(z, compressionEmaAlpha); - for (int channelIndex = 0; channelIndex < Parameter.ChannelCount; channelIndex++) + for (int channelIndex = 0; channelIndex < _parameter.ChannelCount; channelIndex++) { *((float*)outputBuffers[channelIndex] + sampleIndex) = channelInput[channelIndex] * compressionGain * state.OutputGain; } unknown4 = unknown4New; previousCompressionEmaAlpha = compressionEmaAlpha; + + if (!ResultState.IsEmpty) + { + ref CompressorStatistics statistics = ref MemoryMarshal.Cast(ResultState.Span[0].SpecificData)[0]; + + statistics.MinimumGain = MathF.Min(statistics.MinimumGain, compressionGain * state.OutputGain); + statistics.MaximumMean = MathF.Max(statistics.MaximumMean, mean); + + for (int channelIndex = 0; channelIndex < _parameter.ChannelCount; channelIndex++) + { + statistics.LastSamples[channelIndex] = MathF.Abs(channelInput[channelIndex] * (1f / 32768f)); + } + } } state.InputMovingAverage = inputMovingAverage; @@ -161,7 +186,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command } else { - for (int i = 0; i < Parameter.ChannelCount; i++) + for (int i = 0; i < _parameter.ChannelCount; i++) { if (InputBufferIndices[i] != OutputBufferIndices[i]) { diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs index 6fa3777f4..21cf69504 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs @@ -77,7 +77,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command } [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - private unsafe void ProcessDelayStereo(ref DelayState state, Span outputBuffers, ReadOnlySpan inputBuffers, uint sampleCount) + private unsafe void ProcessDelayStereo(ref DelayState state, Span outputBuffers, ReadOnlySpan inputBuffers, uint sampleCount) { const ushort ChannelCount = 2; @@ -114,7 +114,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command } [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - private unsafe void ProcessDelayQuadraphonic(ref DelayState state, Span outputBuffers, ReadOnlySpan inputBuffers, uint sampleCount) + private unsafe void ProcessDelayQuadraphonic(ref DelayState state, Span outputBuffers, ReadOnlySpan inputBuffers, uint sampleCount) { const ushort ChannelCount = 4; @@ -160,7 +160,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command } [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - private unsafe void ProcessDelaySurround(ref DelayState state, Span outputBuffers, ReadOnlySpan inputBuffers, uint sampleCount) + private unsafe void ProcessDelaySurround(ref DelayState state, Span outputBuffers, ReadOnlySpan inputBuffers, uint sampleCount) { const ushort ChannelCount = 6; @@ -219,8 +219,8 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command if (IsEffectEnabled && Parameter.IsChannelCountValid()) { - Span inputBuffers = stackalloc IntPtr[Parameter.ChannelCount]; - Span outputBuffers = stackalloc IntPtr[Parameter.ChannelCount]; + Span inputBuffers = stackalloc nint[Parameter.ChannelCount]; + Span outputBuffers = stackalloc nint[Parameter.ChannelCount]; for (int i = 0; i < Parameter.ChannelCount; i++) { diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion1.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion1.cs index 3ba0b5884..4e7f67e78 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion1.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion1.cs @@ -38,10 +38,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command InputBufferIndices = new ushort[Constants.VoiceChannelCountMax]; OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax]; - for (int i = 0; i < Parameter.ChannelCount; i++) + for (int i = 0; i < _parameter.ChannelCount; i++) { - InputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Input[i]); - OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]); + InputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Input[i]); + OutputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Output[i]); } } @@ -51,11 +51,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command if (IsEffectEnabled) { - if (Parameter.Status == UsageState.Invalid) + if (_parameter.Status == UsageState.Invalid) { state = new LimiterState(ref _parameter, WorkBuffer); } - else if (Parameter.Status == UsageState.New) + else if (_parameter.Status == UsageState.New) { LimiterState.UpdateParameter(ref _parameter); } @@ -66,56 +66,56 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command private unsafe void ProcessLimiter(CommandList context, ref LimiterState state) { - Debug.Assert(Parameter.IsChannelCountValid()); + Debug.Assert(_parameter.IsChannelCountValid()); - if (IsEffectEnabled && Parameter.IsChannelCountValid()) + if (IsEffectEnabled && _parameter.IsChannelCountValid()) { - Span inputBuffers = stackalloc IntPtr[Parameter.ChannelCount]; - Span outputBuffers = stackalloc IntPtr[Parameter.ChannelCount]; + Span inputBuffers = stackalloc nint[_parameter.ChannelCount]; + Span outputBuffers = stackalloc nint[_parameter.ChannelCount]; - for (int i = 0; i < Parameter.ChannelCount; i++) + for (int i = 0; i < _parameter.ChannelCount; i++) { inputBuffers[i] = context.GetBufferPointer(InputBufferIndices[i]); outputBuffers[i] = context.GetBufferPointer(OutputBufferIndices[i]); } - for (int channelIndex = 0; channelIndex < Parameter.ChannelCount; channelIndex++) + for (int channelIndex = 0; channelIndex < _parameter.ChannelCount; channelIndex++) { for (int sampleIndex = 0; sampleIndex < context.SampleCount; sampleIndex++) { float rawInputSample = *((float*)inputBuffers[channelIndex] + sampleIndex); - float inputSample = (rawInputSample / short.MaxValue) * Parameter.InputGain; + float inputSample = (rawInputSample / short.MaxValue) * _parameter.InputGain; float sampleInputMax = Math.Abs(inputSample); - float inputCoefficient = Parameter.ReleaseCoefficient; + float inputCoefficient = _parameter.ReleaseCoefficient; if (sampleInputMax > state.DetectorAverage[channelIndex].Read()) { - inputCoefficient = Parameter.AttackCoefficient; + inputCoefficient = _parameter.AttackCoefficient; } float detectorValue = state.DetectorAverage[channelIndex].Update(sampleInputMax, inputCoefficient); float attenuation = 1.0f; - if (detectorValue > Parameter.Threshold) + if (detectorValue > _parameter.Threshold) { - attenuation = Parameter.Threshold / detectorValue; + attenuation = _parameter.Threshold / detectorValue; } - float outputCoefficient = Parameter.ReleaseCoefficient; + float outputCoefficient = _parameter.ReleaseCoefficient; if (state.CompressionGainAverage[channelIndex].Read() > attenuation) { - outputCoefficient = Parameter.AttackCoefficient; + outputCoefficient = _parameter.AttackCoefficient; } float compressionGain = state.CompressionGainAverage[channelIndex].Update(attenuation, outputCoefficient); - ref float delayedSample = ref state.DelayedSampleBuffer[channelIndex * Parameter.DelayBufferSampleCountMax + state.DelayedSampleBufferPosition[channelIndex]]; + ref float delayedSample = ref state.DelayedSampleBuffer[channelIndex * _parameter.DelayBufferSampleCountMax + state.DelayedSampleBufferPosition[channelIndex]]; - float outputSample = delayedSample * compressionGain * Parameter.OutputGain; + float outputSample = delayedSample * compressionGain * _parameter.OutputGain; *((float*)outputBuffers[channelIndex] + sampleIndex) = outputSample * short.MaxValue; @@ -123,16 +123,16 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command state.DelayedSampleBufferPosition[channelIndex]++; - while (state.DelayedSampleBufferPosition[channelIndex] >= Parameter.DelayBufferSampleCountMin) + while (state.DelayedSampleBufferPosition[channelIndex] >= _parameter.DelayBufferSampleCountMin) { - state.DelayedSampleBufferPosition[channelIndex] -= Parameter.DelayBufferSampleCountMin; + state.DelayedSampleBufferPosition[channelIndex] -= _parameter.DelayBufferSampleCountMin; } } } } else { - for (int i = 0; i < Parameter.ChannelCount; i++) + for (int i = 0; i < _parameter.ChannelCount; i++) { if (InputBufferIndices[i] != OutputBufferIndices[i]) { diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs index f6e1654dd..b0032c5b7 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs @@ -49,10 +49,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command InputBufferIndices = new ushort[Constants.VoiceChannelCountMax]; OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax]; - for (int i = 0; i < Parameter.ChannelCount; i++) + for (int i = 0; i < _parameter.ChannelCount; i++) { - InputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Input[i]); - OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]); + InputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Input[i]); + OutputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Output[i]); } } @@ -62,11 +62,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command if (IsEffectEnabled) { - if (Parameter.Status == UsageState.Invalid) + if (_parameter.Status == UsageState.Invalid) { state = new LimiterState(ref _parameter, WorkBuffer); } - else if (Parameter.Status == UsageState.New) + else if (_parameter.Status == UsageState.New) { LimiterState.UpdateParameter(ref _parameter); } @@ -77,63 +77,63 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command private unsafe void ProcessLimiter(CommandList context, ref LimiterState state) { - Debug.Assert(Parameter.IsChannelCountValid()); + Debug.Assert(_parameter.IsChannelCountValid()); - if (IsEffectEnabled && Parameter.IsChannelCountValid()) + if (IsEffectEnabled && _parameter.IsChannelCountValid()) { - if (!ResultState.IsEmpty && Parameter.StatisticsReset) + if (!ResultState.IsEmpty && _parameter.StatisticsReset) { ref LimiterStatistics statistics = ref MemoryMarshal.Cast(ResultState.Span[0].SpecificData)[0]; statistics.Reset(); } - Span inputBuffers = stackalloc IntPtr[Parameter.ChannelCount]; - Span outputBuffers = stackalloc IntPtr[Parameter.ChannelCount]; + Span inputBuffers = stackalloc nint[_parameter.ChannelCount]; + Span outputBuffers = stackalloc nint[_parameter.ChannelCount]; - for (int i = 0; i < Parameter.ChannelCount; i++) + for (int i = 0; i < _parameter.ChannelCount; i++) { inputBuffers[i] = context.GetBufferPointer(InputBufferIndices[i]); outputBuffers[i] = context.GetBufferPointer(OutputBufferIndices[i]); } - for (int channelIndex = 0; channelIndex < Parameter.ChannelCount; channelIndex++) + for (int channelIndex = 0; channelIndex < _parameter.ChannelCount; channelIndex++) { for (int sampleIndex = 0; sampleIndex < context.SampleCount; sampleIndex++) { float rawInputSample = *((float*)inputBuffers[channelIndex] + sampleIndex); - float inputSample = (rawInputSample / short.MaxValue) * Parameter.InputGain; + float inputSample = (rawInputSample / short.MaxValue) * _parameter.InputGain; float sampleInputMax = Math.Abs(inputSample); - float inputCoefficient = Parameter.ReleaseCoefficient; + float inputCoefficient = _parameter.ReleaseCoefficient; if (sampleInputMax > state.DetectorAverage[channelIndex].Read()) { - inputCoefficient = Parameter.AttackCoefficient; + inputCoefficient = _parameter.AttackCoefficient; } float detectorValue = state.DetectorAverage[channelIndex].Update(sampleInputMax, inputCoefficient); float attenuation = 1.0f; - if (detectorValue > Parameter.Threshold) + if (detectorValue > _parameter.Threshold) { - attenuation = Parameter.Threshold / detectorValue; + attenuation = _parameter.Threshold / detectorValue; } - float outputCoefficient = Parameter.ReleaseCoefficient; + float outputCoefficient = _parameter.ReleaseCoefficient; if (state.CompressionGainAverage[channelIndex].Read() > attenuation) { - outputCoefficient = Parameter.AttackCoefficient; + outputCoefficient = _parameter.AttackCoefficient; } float compressionGain = state.CompressionGainAverage[channelIndex].Update(attenuation, outputCoefficient); - ref float delayedSample = ref state.DelayedSampleBuffer[channelIndex * Parameter.DelayBufferSampleCountMax + state.DelayedSampleBufferPosition[channelIndex]]; + ref float delayedSample = ref state.DelayedSampleBuffer[channelIndex * _parameter.DelayBufferSampleCountMax + state.DelayedSampleBufferPosition[channelIndex]]; - float outputSample = delayedSample * compressionGain * Parameter.OutputGain; + float outputSample = delayedSample * compressionGain * _parameter.OutputGain; *((float*)outputBuffers[channelIndex] + sampleIndex) = outputSample * short.MaxValue; @@ -141,9 +141,9 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command state.DelayedSampleBufferPosition[channelIndex]++; - while (state.DelayedSampleBufferPosition[channelIndex] >= Parameter.DelayBufferSampleCountMin) + while (state.DelayedSampleBufferPosition[channelIndex] >= _parameter.DelayBufferSampleCountMin) { - state.DelayedSampleBufferPosition[channelIndex] -= Parameter.DelayBufferSampleCountMin; + state.DelayedSampleBufferPosition[channelIndex] -= _parameter.DelayBufferSampleCountMin; } if (!ResultState.IsEmpty) @@ -158,7 +158,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command } else { - for (int i = 0; i < Parameter.ChannelCount; i++) + for (int i = 0; i < _parameter.ChannelCount; i++) { if (InputBufferIndices[i] != OutputBufferIndices[i]) { diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/Reverb3dCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/Reverb3dCommand.cs index 8cdd4843b..58023ac9d 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/Reverb3dCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/Reverb3dCommand.cs @@ -71,30 +71,30 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ProcessReverb3dMono(ref Reverb3dState state, ReadOnlySpan outputBuffers, ReadOnlySpan inputBuffers, uint sampleCount) + private void ProcessReverb3dMono(ref Reverb3dState state, ReadOnlySpan outputBuffers, ReadOnlySpan inputBuffers, uint sampleCount) { ProcessReverb3dGeneric(ref state, outputBuffers, inputBuffers, sampleCount, _outputEarlyIndicesTableMono, _targetEarlyDelayLineIndicesTableMono, _targetOutputFeedbackIndicesTableMono); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ProcessReverb3dStereo(ref Reverb3dState state, ReadOnlySpan outputBuffers, ReadOnlySpan inputBuffers, uint sampleCount) + private void ProcessReverb3dStereo(ref Reverb3dState state, ReadOnlySpan outputBuffers, ReadOnlySpan inputBuffers, uint sampleCount) { ProcessReverb3dGeneric(ref state, outputBuffers, inputBuffers, sampleCount, _outputEarlyIndicesTableStereo, _targetEarlyDelayLineIndicesTableStereo, _targetOutputFeedbackIndicesTableStereo); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ProcessReverb3dQuadraphonic(ref Reverb3dState state, ReadOnlySpan outputBuffers, ReadOnlySpan inputBuffers, uint sampleCount) + private void ProcessReverb3dQuadraphonic(ref Reverb3dState state, ReadOnlySpan outputBuffers, ReadOnlySpan inputBuffers, uint sampleCount) { ProcessReverb3dGeneric(ref state, outputBuffers, inputBuffers, sampleCount, _outputEarlyIndicesTableQuadraphonic, _targetEarlyDelayLineIndicesTableQuadraphonic, _targetOutputFeedbackIndicesTableQuadraphonic); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ProcessReverb3dSurround(ref Reverb3dState state, ReadOnlySpan outputBuffers, ReadOnlySpan inputBuffers, uint sampleCount) + private void ProcessReverb3dSurround(ref Reverb3dState state, ReadOnlySpan outputBuffers, ReadOnlySpan inputBuffers, uint sampleCount) { ProcessReverb3dGeneric(ref state, outputBuffers, inputBuffers, sampleCount, _outputEarlyIndicesTableSurround, _targetEarlyDelayLineIndicesTableSurround, _targetOutputFeedbackIndicesTableSurround); } - private unsafe void ProcessReverb3dGeneric(ref Reverb3dState state, ReadOnlySpan outputBuffers, ReadOnlySpan inputBuffers, uint sampleCount, ReadOnlySpan outputEarlyIndicesTable, ReadOnlySpan targetEarlyDelayLineIndicesTable, ReadOnlySpan targetOutputFeedbackIndicesTable) + private unsafe void ProcessReverb3dGeneric(ref Reverb3dState state, ReadOnlySpan outputBuffers, ReadOnlySpan inputBuffers, uint sampleCount, ReadOnlySpan outputEarlyIndicesTable, ReadOnlySpan targetEarlyDelayLineIndicesTable, ReadOnlySpan targetOutputFeedbackIndicesTable) { const int DelayLineSampleIndexOffset = 1; @@ -193,8 +193,8 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command if (IsEffectEnabled && Parameter.IsChannelCountValid()) { - Span inputBuffers = stackalloc IntPtr[Parameter.ChannelCount]; - Span outputBuffers = stackalloc IntPtr[Parameter.ChannelCount]; + Span inputBuffers = stackalloc nint[Parameter.ChannelCount]; + Span outputBuffers = stackalloc nint[Parameter.ChannelCount]; for (int i = 0; i < Parameter.ChannelCount; i++) { diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/ReverbCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/ReverbCommand.cs index 874eb8e8b..204570cec 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/ReverbCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/ReverbCommand.cs @@ -77,7 +77,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ProcessReverbMono(ref ReverbState state, ReadOnlySpan outputBuffers, ReadOnlySpan inputBuffers, uint sampleCount) + private void ProcessReverbMono(ref ReverbState state, ReadOnlySpan outputBuffers, ReadOnlySpan inputBuffers, uint sampleCount) { ProcessReverbGeneric( ref state, @@ -91,7 +91,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ProcessReverbStereo(ref ReverbState state, ReadOnlySpan outputBuffers, ReadOnlySpan inputBuffers, uint sampleCount) + private void ProcessReverbStereo(ref ReverbState state, ReadOnlySpan outputBuffers, ReadOnlySpan inputBuffers, uint sampleCount) { ProcessReverbGeneric( ref state, @@ -105,7 +105,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ProcessReverbQuadraphonic(ref ReverbState state, ReadOnlySpan outputBuffers, ReadOnlySpan inputBuffers, uint sampleCount) + private void ProcessReverbQuadraphonic(ref ReverbState state, ReadOnlySpan outputBuffers, ReadOnlySpan inputBuffers, uint sampleCount) { ProcessReverbGeneric( ref state, @@ -119,7 +119,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void ProcessReverbSurround(ref ReverbState state, ReadOnlySpan outputBuffers, ReadOnlySpan inputBuffers, uint sampleCount) + private void ProcessReverbSurround(ref ReverbState state, ReadOnlySpan outputBuffers, ReadOnlySpan inputBuffers, uint sampleCount) { ProcessReverbGeneric( ref state, @@ -132,7 +132,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command _outputIndicesTableSurround); } - private unsafe void ProcessReverbGeneric(ref ReverbState state, ReadOnlySpan outputBuffers, ReadOnlySpan inputBuffers, uint sampleCount, ReadOnlySpan outputEarlyIndicesTable, ReadOnlySpan targetEarlyDelayLineIndicesTable, ReadOnlySpan targetOutputFeedbackIndicesTable, ReadOnlySpan outputIndicesTable) + private unsafe void ProcessReverbGeneric(ref ReverbState state, ReadOnlySpan outputBuffers, ReadOnlySpan inputBuffers, uint sampleCount, ReadOnlySpan outputEarlyIndicesTable, ReadOnlySpan targetEarlyDelayLineIndicesTable, ReadOnlySpan targetOutputFeedbackIndicesTable, ReadOnlySpan outputIndicesTable) { bool isSurround = Parameter.ChannelCount == 6; @@ -223,8 +223,8 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command if (IsEffectEnabled && Parameter.IsChannelCountValid()) { - Span inputBuffers = stackalloc IntPtr[Parameter.ChannelCount]; - Span outputBuffers = stackalloc IntPtr[Parameter.ChannelCount]; + Span inputBuffers = stackalloc nint[Parameter.ChannelCount]; + Span outputBuffers = stackalloc nint[Parameter.ChannelCount]; for (int i = 0; i < Parameter.ChannelCount; i++) { diff --git a/src/Ryujinx.Audio/Renderer/Parameter/Effect/CompressorParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/Effect/CompressorParameter.cs index b403f1370..c00118e49 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/Effect/CompressorParameter.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/Effect/CompressorParameter.cs @@ -90,9 +90,16 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect public bool MakeupGainEnabled; /// - /// Reserved/padding. + /// Indicate if the compressor effect should output statistics. /// - private Array2 _reserved; + [MarshalAs(UnmanagedType.I1)] + public bool StatisticsEnabled; + + /// + /// Indicate to the DSP that the user did a statistics reset. + /// + [MarshalAs(UnmanagedType.I1)] + public bool StatisticsReset; /// /// Check if the is valid. diff --git a/src/Ryujinx.Audio/Renderer/Parameter/Effect/CompressorStatistics.cs b/src/Ryujinx.Audio/Renderer/Parameter/Effect/CompressorStatistics.cs new file mode 100644 index 000000000..65335e2d9 --- /dev/null +++ b/src/Ryujinx.Audio/Renderer/Parameter/Effect/CompressorStatistics.cs @@ -0,0 +1,38 @@ +using Ryujinx.Common.Memory; +using System.Runtime.InteropServices; + +namespace Ryujinx.Audio.Renderer.Parameter.Effect +{ + /// + /// Effect result state for . + /// + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct CompressorStatistics + { + /// + /// Maximum input mean value since last reset. + /// + public float MaximumMean; + + /// + /// Minimum output gain since last reset. + /// + public float MinimumGain; + + /// + /// Last processed input sample, per channel. + /// + public Array6 LastSamples; + + /// + /// Reset the statistics. + /// + /// Number of channels to reset. + public void Reset(ushort channelCount) + { + MaximumMean = 0.0f; + MinimumGain = 1.0f; + LastSamples.AsSpan()[..channelCount].Clear(); + } + } +} diff --git a/src/Ryujinx.Audio/Renderer/Parameter/ISplitterDestinationInParameter.cs b/src/Ryujinx.Audio/Renderer/Parameter/ISplitterDestinationInParameter.cs index 807232f20..7ee49f11a 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/ISplitterDestinationInParameter.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/ISplitterDestinationInParameter.cs @@ -28,6 +28,11 @@ namespace Ryujinx.Audio.Renderer.Parameter /// bool IsUsed { get; } + /// + /// Set to true to force resetting the previous mix volumes. + /// + bool ResetPrevVolume { get; } + /// /// Mix buffer volumes. /// diff --git a/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion1.cs b/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion1.cs index 029c001ea..f346efcb0 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion1.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion1.cs @@ -37,10 +37,16 @@ namespace Ryujinx.Audio.Renderer.Parameter [MarshalAs(UnmanagedType.I1)] public bool IsUsed; + /// + /// Set to true to force resetting the previous mix volumes. + /// + [MarshalAs(UnmanagedType.I1)] + public bool ResetPrevVolume; + /// /// Reserved/padding. /// - private unsafe fixed byte _reserved[3]; + private unsafe fixed byte _reserved[2]; [StructLayout(LayoutKind.Sequential, Size = sizeof(float) * Constants.MixBufferCountMax, Pack = 1)] private struct MixArray { } @@ -58,6 +64,7 @@ namespace Ryujinx.Audio.Renderer.Parameter readonly Array2 ISplitterDestinationInParameter.BiquadFilters => default; readonly bool ISplitterDestinationInParameter.IsUsed => IsUsed; + readonly bool ISplitterDestinationInParameter.ResetPrevVolume => ResetPrevVolume; /// /// The expected constant of any input header. diff --git a/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion2.cs b/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion2.cs index 312be8b70..1d867919d 100644 --- a/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion2.cs +++ b/src/Ryujinx.Audio/Renderer/Parameter/SplitterDestinationInParameterVersion2.cs @@ -42,10 +42,16 @@ namespace Ryujinx.Audio.Renderer.Parameter [MarshalAs(UnmanagedType.I1)] public bool IsUsed; + /// + /// Set to true to force resetting the previous mix volumes. + /// + [MarshalAs(UnmanagedType.I1)] + public bool ResetPrevVolume; + /// /// Reserved/padding. /// - private unsafe fixed byte _reserved[11]; + private unsafe fixed byte _reserved[10]; [StructLayout(LayoutKind.Sequential, Size = sizeof(float) * Constants.MixBufferCountMax, Pack = 1)] private struct MixArray { } @@ -63,6 +69,7 @@ namespace Ryujinx.Audio.Renderer.Parameter readonly Array2 ISplitterDestinationInParameter.BiquadFilters => BiquadFilters; readonly bool ISplitterDestinationInParameter.IsUsed => IsUsed; + readonly bool ISplitterDestinationInParameter.ResetPrevVolume => ResetPrevVolume; /// /// The expected constant of any input header. diff --git a/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs b/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs index 246889c48..65a134af1 100644 --- a/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs +++ b/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs @@ -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; diff --git a/src/Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs b/src/Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs index e334a89f6..6d7db059f 100644 --- a/src/Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs +++ b/src/Ryujinx.Audio/Renderer/Server/AudioRendererManager.cs @@ -19,12 +19,12 @@ namespace Ryujinx.Audio.Renderer.Server /// /// Lock used for session allocation. /// - private readonly object _sessionLock = new(); + private readonly Lock _sessionLock = new(); /// /// Lock used to control the running state. /// - private readonly object _audioProcessorLock = new(); + private readonly Lock _audioProcessorLock = new(); /// /// The session ids allocation table. diff --git a/src/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs b/src/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs index 32c7de6cf..f725eb9f3 100644 --- a/src/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs +++ b/src/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs @@ -108,10 +108,18 @@ namespace Ryujinx.Audio.Renderer.Server /// This was added in system update 17.0.0 public const int Revision12 = 12 << 24; + /// + /// REV13: + /// The compressor effect can now output statistics. + /// Splitter destinations now explicitly reset the previous mix volume, instead of doing so on first use. + /// + /// This was added in system update 18.0.0 + public const int Revision13 = 13 << 24; + /// /// Last revision supported by the implementation. /// - public const int LastRevision = Revision12; + public const int LastRevision = Revision13; /// /// Target revision magic supported by the implementation. @@ -384,6 +392,15 @@ namespace Ryujinx.Audio.Renderer.Server return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision12); } + /// + /// Check if the audio renderer should support explicit previous mix volume reset on splitter. + /// + /// True if the audio renderer support explicit previous mix volume reset on splitter + public bool IsSplitterPrevVolumeResetSupported() + { + return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision13); + } + /// /// Get the version of the . /// diff --git a/src/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs b/src/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs index 702f05462..4c353b37e 100644 --- a/src/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs +++ b/src/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs @@ -583,11 +583,20 @@ namespace Ryujinx.Audio.Renderer.Server } } - public void GenerateCompressorEffect(uint bufferOffset, CompressorParameter parameter, Memory state, bool isEnabled, int nodeId) + /// + /// Generate a new . + /// + /// The target buffer offset. + /// The compressor parameter. + /// The compressor state. + /// The DSP effect result state. + /// Set to true if the effect should be active. + /// The node id associated to this command. + public void GenerateCompressorEffect(uint bufferOffset, CompressorParameter parameter, Memory state, Memory effectResultState, bool isEnabled, int nodeId) { if (parameter.IsChannelCountValid()) { - CompressorCommand command = new(bufferOffset, parameter, state, isEnabled, nodeId); + CompressorCommand command = new(bufferOffset, parameter, state, effectResultState, isEnabled, nodeId); command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); diff --git a/src/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs b/src/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs index d798230c1..0b789537a 100644 --- a/src/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs +++ b/src/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs @@ -735,14 +735,26 @@ namespace Ryujinx.Audio.Renderer.Server } } - private void GenerateCompressorEffect(uint bufferOffset, CompressorEffect effect, int nodeId) + private void GenerateCompressorEffect(uint bufferOffset, CompressorEffect effect, int nodeId, int effectId) { Debug.Assert(effect.Type == EffectType.Compressor); + Memory dspResultState; + + if (effect.Parameter.StatisticsEnabled) + { + dspResultState = _effectContext.GetDspStateMemory(effectId); + } + else + { + dspResultState = Memory.Empty; + } + _commandBuffer.GenerateCompressorEffect( bufferOffset, effect.Parameter, effect.State, + dspResultState, effect.IsEnabled, nodeId); } @@ -795,7 +807,7 @@ namespace Ryujinx.Audio.Renderer.Server GenerateCaptureEffect(mix.BufferOffset, (CaptureBufferEffect)effect, nodeId); break; case EffectType.Compressor: - GenerateCompressorEffect(mix.BufferOffset, (CompressorEffect)effect, nodeId); + GenerateCompressorEffect(mix.BufferOffset, (CompressorEffect)effect, nodeId, effectId); break; default: throw new NotImplementedException($"Unsupported effect type {effect.Type}"); diff --git a/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion5.cs b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion5.cs index 06f135a88..bc9ba073d 100644 --- a/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion5.cs +++ b/src/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion5.cs @@ -169,14 +169,28 @@ namespace Ryujinx.Audio.Renderer.Server { if (command.Enabled) { - return command.Parameter.ChannelCount switch + if (command.Parameter.StatisticsEnabled) { - 1 => 34431, - 2 => 44253, - 4 => 63827, - 6 => 83361, - _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), - }; + return command.Parameter.ChannelCount switch + { + 1 => 22100, + 2 => 33211, + 4 => 41587, + 6 => 58819, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; + } + else + { + return command.Parameter.ChannelCount switch + { + 1 => 19052, + 2 => 29852, + 4 => 37904, + 6 => 55020, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; + } } return command.Parameter.ChannelCount switch @@ -191,14 +205,28 @@ namespace Ryujinx.Audio.Renderer.Server if (command.Enabled) { - return command.Parameter.ChannelCount switch + if (command.Parameter.StatisticsEnabled) { - 1 => 51095, - 2 => 65693, - 4 => 95383, - 6 => 124510, - _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), - }; + return command.Parameter.ChannelCount switch + { + 1 => 32518, + 2 => 49102, + 4 => 61685, + 6 => 87250, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; + } + else + { + return command.Parameter.ChannelCount switch + { + 1 => 27963, + 2 => 44016, + 4 => 56183, + 6 => 81862, + _ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"), + }; + } } return command.Parameter.ChannelCount switch diff --git a/src/Ryujinx.Audio/Renderer/Server/Effect/CompressorEffect.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/CompressorEffect.cs index eff60e7da..de0f44e47 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Effect/CompressorEffect.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Effect/CompressorEffect.cs @@ -62,6 +62,19 @@ namespace Ryujinx.Audio.Renderer.Server.Effect UpdateUsageStateForCommandGeneration(); Parameter.Status = UsageState.Enabled; + Parameter.StatisticsReset = false; + } + + public override void InitializeResultState(ref EffectResultState state) + { + ref CompressorStatistics statistics = ref MemoryMarshal.Cast(state.SpecificData)[0]; + + statistics.Reset(Parameter.ChannelCount); + } + + public override void UpdateResultState(ref EffectResultState destState, ref EffectResultState srcState) + { + destState = srcState; } } } diff --git a/src/Ryujinx.Audio/Renderer/Server/MemoryPool/AddressInfo.cs b/src/Ryujinx.Audio/Renderer/Server/MemoryPool/AddressInfo.cs index a7ec4cf51..3337e44b0 100644 --- a/src/Ryujinx.Audio/Renderer/Server/MemoryPool/AddressInfo.cs +++ b/src/Ryujinx.Audio/Renderer/Server/MemoryPool/AddressInfo.cs @@ -29,7 +29,7 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool private readonly unsafe ref MemoryPoolState MemoryPoolState => ref *_memoryPools; - public readonly unsafe bool HasMemoryPoolState => (IntPtr)_memoryPools != IntPtr.Zero; + public readonly unsafe bool HasMemoryPoolState => (nint)_memoryPools != nint.Zero; /// /// Create an new empty . diff --git a/src/Ryujinx.Audio/Renderer/Server/MemoryPool/MemoryPoolState.cs b/src/Ryujinx.Audio/Renderer/Server/MemoryPool/MemoryPoolState.cs index 91bd5dbf5..d0133622a 100644 --- a/src/Ryujinx.Audio/Renderer/Server/MemoryPool/MemoryPoolState.cs +++ b/src/Ryujinx.Audio/Renderer/Server/MemoryPool/MemoryPoolState.cs @@ -55,7 +55,7 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool [MarshalAs(UnmanagedType.I1)] public bool IsUsed; - public static unsafe MemoryPoolState* Null => (MemoryPoolState*)IntPtr.Zero.ToPointer(); + public static unsafe MemoryPoolState* Null => (MemoryPoolState*)nint.Zero.ToPointer(); /// /// Create a new with the given . diff --git a/src/Ryujinx.Audio/Renderer/Server/Mix/MixState.cs b/src/Ryujinx.Audio/Renderer/Server/Mix/MixState.cs index 5ba58ea5b..34b3ed4bd 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Mix/MixState.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Mix/MixState.cs @@ -65,7 +65,7 @@ namespace Ryujinx.Audio.Renderer.Server.Mix /// /// The effect processing order storage. /// - private readonly IntPtr _effectProcessingOrderArrayPointer; + private readonly nint _effectProcessingOrderArrayPointer; /// /// The max element count that can be found in the effect processing order storage. @@ -123,7 +123,7 @@ namespace Ryujinx.Audio.Renderer.Server.Mix { get { - if (_effectProcessingOrderArrayPointer == IntPtr.Zero) + if (_effectProcessingOrderArrayPointer == nint.Zero) { return Span.Empty; } @@ -153,7 +153,7 @@ namespace Ryujinx.Audio.Renderer.Server.Mix unsafe { // SAFETY: safe as effectProcessingOrderArray comes from the work buffer memory that is pinned. - _effectProcessingOrderArrayPointer = (IntPtr)Unsafe.AsPointer(ref MemoryMarshal.GetReference(effectProcessingOrderArray.Span)); + _effectProcessingOrderArrayPointer = (nint)Unsafe.AsPointer(ref MemoryMarshal.GetReference(effectProcessingOrderArray.Span)); } EffectProcessingOrderArrayMaxCount = (uint)effectProcessingOrderArray.Length; diff --git a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs index a7b82a6bd..6dddb4315 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs @@ -51,6 +51,11 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// public bool IsBugFixed { get; private set; } + /// + /// If set to true, the previous mix volume is explicitly resetted using the input parameter, instead of implicitly on first use. + /// + public bool IsSplitterPrevVolumeResetSupported { get; private set; } + /// /// Initialize . /// @@ -139,6 +144,8 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter } } + IsSplitterPrevVolumeResetSupported = behaviourContext.IsSplitterPrevVolumeResetSupported(); + SplitterState.InitializeSplitters(splitters.Span); Setup(splitters, splitterDestinationsV1, splitterDestinationsV2, behaviourContext.IsSplitterBugFixed()); @@ -277,7 +284,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter { SplitterDestination destination = GetDestination(parameter.Id); - destination.Update(parameter); + destination.Update(parameter, IsSplitterPrevVolumeResetSupported); } return true; diff --git a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestination.cs b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestination.cs index 36dfa5e41..1a46d41fd 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestination.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestination.cs @@ -184,15 +184,16 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// Update the splitter destination data from user parameter. /// /// The user parameter. - public void Update(in T parameter) where T : ISplitterDestinationInParameter + /// Indicates that the audio renderer revision in use supports explicitly resetting the volume. + public void Update(in T parameter, bool isPrevVolumeResetSupported) where T : ISplitterDestinationInParameter { if (Unsafe.IsNullRef(ref _v2)) { - _v1.Update(parameter); + _v1.Update(parameter, isPrevVolumeResetSupported); } else { - _v2.Update(parameter); + _v2.Update(parameter, isPrevVolumeResetSupported); } } diff --git a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion1.cs b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion1.cs index 5d2b8fb0f..ce8f33685 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion1.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion1.cs @@ -93,7 +93,8 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// Update the from user parameter. /// /// The user parameter. - public void Update(in T parameter) where T : ISplitterDestinationInParameter + /// Indicates that the audio renderer revision in use supports explicitly resetting the volume. + public void Update(in T parameter, bool isPrevVolumeResetSupported) where T : ISplitterDestinationInParameter { Debug.Assert(Id == parameter.Id); @@ -103,7 +104,8 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter parameter.MixBufferVolume.CopyTo(MixBufferVolume); - if (!IsUsed && parameter.IsUsed) + bool resetPrevVolume = isPrevVolumeResetSupported ? parameter.ResetPrevVolume : !IsUsed && parameter.IsUsed; + if (resetPrevVolume) { MixBufferVolume.CopyTo(PreviousMixBufferVolume); diff --git a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion2.cs b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion2.cs index f9487909d..5f96ef3aa 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion2.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion2.cs @@ -98,7 +98,8 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// Update the from user parameter. /// /// The user parameter. - public void Update(in T parameter) where T : ISplitterDestinationInParameter + /// Indicates that the audio renderer revision in use supports explicitly resetting the volume. + public void Update(in T parameter, bool isPrevVolumeResetSupported) where T : ISplitterDestinationInParameter { Debug.Assert(Id == parameter.Id); @@ -110,7 +111,8 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter _biquadFilters = parameter.BiquadFilters; - if (!IsUsed && parameter.IsUsed) + bool resetPrevVolume = isPrevVolumeResetSupported ? parameter.ResetPrevVolume : !IsUsed && parameter.IsUsed; + if (resetPrevVolume) { MixBufferVolume.CopyTo(PreviousMixBufferVolume); diff --git a/src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerManager.cs b/src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerManager.cs index dbc2c9b3f..8b3f39439 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerManager.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Upsampler/UpsamplerManager.cs @@ -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 /// /// Global lock of the object. /// - private readonly object _lock = new(); + private readonly Lock _lock = new(); /// /// The upsamplers instances. diff --git a/src/Ryujinx.Audio/Ryujinx.Audio.csproj b/src/Ryujinx.Audio/Ryujinx.Audio.csproj index fc20f4ec4..92e0fe93f 100644 --- a/src/Ryujinx.Audio/Ryujinx.Audio.csproj +++ b/src/Ryujinx.Audio/Ryujinx.Audio.csproj @@ -1,8 +1,8 @@  - net8.0 true + $(DefaultItemExcludes);._* diff --git a/src/Ryujinx.BuildValidationTasks/LocaleValidationTask.cs b/src/Ryujinx.BuildValidationTasks/LocaleValidationTask.cs new file mode 100644 index 000000000..6dc3d8aa8 --- /dev/null +++ b/src/Ryujinx.BuildValidationTasks/LocaleValidationTask.cs @@ -0,0 +1,73 @@ +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(["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(["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(path)) + { + data = sr.ReadToEnd(); + } + + LocalesJson json = JsonConvert.DeserializeObject(data); + + for (int i = 0; i < json.Locales.Count; i++) + { + LocalesEntry locale = json.Locales[i]; + + foreach (string langCode in json.Languages.Where(it => !locale.Translations.ContainsKey(it))) + { + locale.Translations.Add(langCode, string.Empty); + Log.LogMessage(MessageImportance.High, $"Added '{langCode}' 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(path)) + { + sw.Write(jsonString); + } + + return true; + } + + struct LocalesJson + { + public List Languages { get; set; } + public List Locales { get; set; } + } + + struct LocalesEntry + { + public string ID { get; set; } + public Dictionary Translations { get; set; } + } + } +} diff --git a/src/Ryujinx.BuildValidationTasks/Ryujinx.BuildValidationTasks.csproj b/src/Ryujinx.BuildValidationTasks/Ryujinx.BuildValidationTasks.csproj new file mode 100644 index 000000000..dbd9492df --- /dev/null +++ b/src/Ryujinx.BuildValidationTasks/Ryujinx.BuildValidationTasks.csproj @@ -0,0 +1,19 @@ + + + + netstandard2.0 + true + + + + + + + + + + + + + + diff --git a/src/Ryujinx.Common/Collections/IntervalTree.cs b/src/Ryujinx.Common/Collections/IntervalTree.cs index d3a5e7fcf..f804bca91 100644 --- a/src/Ryujinx.Common/Collections/IntervalTree.cs +++ b/src/Ryujinx.Common/Collections/IntervalTree.cs @@ -492,7 +492,7 @@ namespace Ryujinx.Common.Collections Start = start; End = end; Max = end; - Values = new List> { new RangeNode(start, end, value) }; + Values = [new RangeNode(start, end, value)]; Parent = parent; } } diff --git a/src/Ryujinx.Common/Configuration/AppDataManager.cs b/src/Ryujinx.Common/Configuration/AppDataManager.cs index deaa03def..ca8e389ba 100644 --- a/src/Ryujinx.Common/Configuration/AppDataManager.cs +++ b/src/Ryujinx.Common/Configuration/AppDataManager.cs @@ -98,7 +98,7 @@ namespace Ryujinx.Common.Configuration if (IsPathSymlink(BaseDirPath)) { - Logger.Warning?.Print(LogClass.Application, $"Application data directory is a symlink. This may be unintended."); + Logger.Warning?.Print(LogClass.Application, "Application data directory is a symlink. This may be unintended."); } SetupBasePaths(); @@ -119,7 +119,7 @@ namespace Ryujinx.Common.Configuration private static string SetUpLogsDir() { - string logDir = ""; + string logDir = string.Empty; if (Mode == LaunchMode.Portable) { @@ -148,7 +148,7 @@ namespace Ryujinx.Common.Configuration catch { Logger.Warning?.Print(LogClass.Application, $"Logging directory could not be created '{logDir}'"); - logDir = ""; + logDir = string.Empty; } if (string.IsNullOrEmpty(logDir)) @@ -179,7 +179,7 @@ namespace Ryujinx.Common.Configuration catch { Logger.Warning?.Print(LogClass.Application, $"Logging directory could not be created '{logDir}'"); - logDir = ""; + logDir = string.Empty; } if (string.IsNullOrEmpty(logDir)) diff --git a/src/Ryujinx.Common/Configuration/Hid/Controller/JoyconConfigControllerStick.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/JoyconConfigControllerStick.cs index 608681551..076530744 100644 --- a/src/Ryujinx.Common/Configuration/Hid/Controller/JoyconConfigControllerStick.cs +++ b/src/Ryujinx.Common/Configuration/Hid/Controller/JoyconConfigControllerStick.cs @@ -1,6 +1,8 @@ namespace Ryujinx.Common.Configuration.Hid.Controller { - public class JoyconConfigControllerStick where TButton : unmanaged where TStick : unmanaged + public class JoyconConfigControllerStick + where TButton : unmanaged + where TStick : unmanaged { public TStick Joystick { get; set; } public bool InvertStickX { get; set; } diff --git a/src/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs b/src/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs index 0cb49ca8c..6b8152b9d 100644 --- a/src/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs +++ b/src/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs @@ -2,7 +2,7 @@ namespace Ryujinx.Common.Configuration.Hid { public class KeyboardHotkeys { - public Key ToggleVsync { get; set; } + public Key ToggleVSyncMode { get; set; } public Key Screenshot { get; set; } public Key ShowUI { get; set; } public Key Pause { get; set; } @@ -11,5 +11,7 @@ namespace Ryujinx.Common.Configuration.Hid public Key ResScaleDown { get; set; } public Key VolumeUp { get; set; } public Key VolumeDown { get; set; } + public Key CustomVSyncIntervalIncrement { get; set; } + public Key CustomVSyncIntervalDecrement { get; set; } } } diff --git a/src/Ryujinx.Common/Configuration/Multiplayer/MultiplayerMode.cs b/src/Ryujinx.Common/Configuration/Multiplayer/MultiplayerMode.cs index 69f7d876d..be0e1518c 100644 --- a/src/Ryujinx.Common/Configuration/Multiplayer/MultiplayerMode.cs +++ b/src/Ryujinx.Common/Configuration/Multiplayer/MultiplayerMode.cs @@ -3,6 +3,7 @@ namespace Ryujinx.Common.Configuration.Multiplayer public enum MultiplayerMode { Disabled, + LdnRyu, LdnMitm, } } diff --git a/src/Ryujinx.Common/Configuration/VSyncMode.cs b/src/Ryujinx.Common/Configuration/VSyncMode.cs new file mode 100644 index 000000000..ca93b5e1c --- /dev/null +++ b/src/Ryujinx.Common/Configuration/VSyncMode.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Common.Configuration +{ + public enum VSyncMode + { + Switch, + Unbounded, + Custom + } +} diff --git a/src/Ryujinx.Common/Extensions/StreamExtensions.cs b/src/Ryujinx.Common/Extensions/StreamExtensions.cs index 431d5534a..4b02781c9 100644 --- a/src/Ryujinx.Common/Extensions/StreamExtensions.cs +++ b/src/Ryujinx.Common/Extensions/StreamExtensions.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Common public static class StreamExtensions { /// - /// Writes a " /> to this stream. + /// Writes a to this stream. /// /// This default implementation converts each buffer value to a stack-allocated /// byte array, then writes it to the Stream using . @@ -66,8 +66,8 @@ namespace Ryujinx.Common } /// - // Writes a four-byte unsigned integer to this stream. The current position - // of the stream is advanced by four. + /// Writes a four-byte unsigned integer to this stream. The current position + /// of the stream is advanced by four. /// /// The stream to be written to /// The value to be written diff --git a/src/Ryujinx.Common/GraphicsDriver/NVThreadedOptimization.cs b/src/Ryujinx.Common/GraphicsDriver/NVThreadedOptimization.cs index f7b11783d..8d969bd6a 100644 --- a/src/Ryujinx.Common/GraphicsDriver/NVThreadedOptimization.cs +++ b/src/Ryujinx.Common/GraphicsDriver/NVThreadedOptimization.cs @@ -2,6 +2,7 @@ using Ryujinx.Common.GraphicsDriver.NVAPI; using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +// ReSharper disable InconsistentNaming namespace Ryujinx.Common.GraphicsDriver { @@ -20,33 +21,33 @@ namespace Ryujinx.Common.GraphicsDriver private const uint NvAPI_DRS_DestroySession_ID = 0x0DAD9CFF8; [LibraryImport("nvapi64")] - private static partial IntPtr nvapi_QueryInterface(uint id); + private static partial nint nvapi_QueryInterface(uint id); private delegate int NvAPI_InitializeDelegate(); private static NvAPI_InitializeDelegate NvAPI_Initialize; - private delegate int NvAPI_DRS_CreateSessionDelegate(out IntPtr handle); + private delegate int NvAPI_DRS_CreateSessionDelegate(out nint handle); private static NvAPI_DRS_CreateSessionDelegate NvAPI_DRS_CreateSession; - private delegate int NvAPI_DRS_LoadSettingsDelegate(IntPtr handle); + private delegate int NvAPI_DRS_LoadSettingsDelegate(nint handle); private static NvAPI_DRS_LoadSettingsDelegate NvAPI_DRS_LoadSettings; - private delegate int NvAPI_DRS_FindProfileByNameDelegate(IntPtr handle, NvapiUnicodeString profileName, out IntPtr profileHandle); + private delegate int NvAPI_DRS_FindProfileByNameDelegate(nint handle, NvapiUnicodeString profileName, out nint profileHandle); private static NvAPI_DRS_FindProfileByNameDelegate NvAPI_DRS_FindProfileByName; - private delegate int NvAPI_DRS_CreateProfileDelegate(IntPtr handle, ref NvdrsProfile profileInfo, out IntPtr profileHandle); + private delegate int NvAPI_DRS_CreateProfileDelegate(nint handle, ref NvdrsProfile profileInfo, out nint profileHandle); private static NvAPI_DRS_CreateProfileDelegate NvAPI_DRS_CreateProfile; - private delegate int NvAPI_DRS_CreateApplicationDelegate(IntPtr handle, IntPtr profileHandle, ref NvdrsApplicationV4 app); + private delegate int NvAPI_DRS_CreateApplicationDelegate(nint handle, nint profileHandle, ref NvdrsApplicationV4 app); private static NvAPI_DRS_CreateApplicationDelegate NvAPI_DRS_CreateApplication; - private delegate int NvAPI_DRS_SetSettingDelegate(IntPtr handle, IntPtr profileHandle, ref NvdrsSetting setting); + private delegate int NvAPI_DRS_SetSettingDelegate(nint handle, nint profileHandle, ref NvdrsSetting setting); private static NvAPI_DRS_SetSettingDelegate NvAPI_DRS_SetSetting; - private delegate int NvAPI_DRS_SaveSettingsDelegate(IntPtr handle); + private delegate int NvAPI_DRS_SaveSettingsDelegate(nint handle); private static NvAPI_DRS_SaveSettingsDelegate NvAPI_DRS_SaveSettings; - private delegate int NvAPI_DRS_DestroySessionDelegate(IntPtr handle); + private delegate int NvAPI_DRS_DestroySessionDelegate(nint handle); private static NvAPI_DRS_DestroySessionDelegate NvAPI_DRS_DestroySession; private static bool _initialized; @@ -93,7 +94,7 @@ namespace Ryujinx.Common.GraphicsDriver Check(NvAPI_Initialize()); - Check(NvAPI_DRS_CreateSession(out IntPtr handle)); + Check(NvAPI_DRS_CreateSession(out nint handle)); Check(NvAPI_DRS_LoadSettings(handle)); @@ -120,8 +121,8 @@ namespace Ryujinx.Common.GraphicsDriver }; application.AppName.Set("Ryujinx.exe"); application.UserFriendlyName.Set("Ryujinx"); - application.Launcher.Set(""); - application.FileInFolder.Set(""); + application.Launcher.Set(string.Empty); + application.FileInFolder.Set(string.Empty); Check(NvAPI_DRS_CreateApplication(handle, profileHandle, ref application)); } @@ -147,9 +148,9 @@ namespace Ryujinx.Common.GraphicsDriver private static T NvAPI_Delegate(uint id) where T : class { - IntPtr ptr = nvapi_QueryInterface(id); + nint ptr = nvapi_QueryInterface(id); - if (ptr != IntPtr.Zero) + if (ptr != nint.Zero) { return Marshal.GetDelegateForFunctionPointer(ptr); } diff --git a/src/Ryujinx.Common/Hash128.cs b/src/Ryujinx.Common/Hash128.cs index e0ffd230e..baee3e7d2 100644 --- a/src/Ryujinx.Common/Hash128.cs +++ b/src/Ryujinx.Common/Hash128.cs @@ -1,48 +1,736 @@ using System; +using System.Buffers.Binary; +using System.Diagnostics; +using System.Numerics; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; +// ReSharper disable InconsistentNaming namespace Ryujinx.Common { [StructLayout(LayoutKind.Sequential)] - public struct Hash128 : IEquatable + public struct Hash128(ulong low, ulong high) : IEquatable { - public ulong Low; - public ulong High; + public ulong Low = low; + public ulong High = high; - public Hash128(ulong low, ulong high) + public readonly override string ToString() => $"{High:x16}{Low:x16}"; + + public static bool operator ==(Hash128 x, Hash128 y) => x.Equals(y); + + public static bool operator !=(Hash128 x, Hash128 y) => !x.Equals(y); + + public readonly override bool Equals(object obj) => obj is Hash128 hash128 && Equals(hash128); + + public readonly bool Equals(Hash128 cmpObj) => Low == cmpObj.Low && High == cmpObj.High; + + public readonly override int GetHashCode() => HashCode.Combine(Low, High); + + public static Hash128 ComputeHash(ReadOnlySpan input) => Xxh3128bitsInternal(input, Xxh3KSecret, 0UL); + + #region Hash computation + + private const int StripeLen = 64; + private const int AccNb = StripeLen / sizeof(ulong); + private const int SecretConsumeRate = 8; + private const int SecretLastAccStart = 7; + private const int SecretMergeAccsStart = 11; + private const int SecretSizeMin = 136; + private const int MidSizeStartOffset = 3; + private const int MidSizeLastOffset = 17; + + private const uint Prime32_1 = 0x9E3779B1U; + private const uint Prime32_2 = 0x85EBCA77U; + private const uint Prime32_3 = 0xC2B2AE3DU; + private const uint Prime32_4 = 0x27D4EB2FU; + private const uint Prime32_5 = 0x165667B1U; + + private const ulong Prime64_1 = 0x9E3779B185EBCA87UL; + private const ulong Prime64_2 = 0xC2B2AE3D27D4EB4FUL; + private const ulong Prime64_3 = 0x165667B19E3779F9UL; + private const ulong Prime64_4 = 0x85EBCA77C2B2AE63UL; + private const ulong Prime64_5 = 0x27D4EB2F165667C5UL; + + private static readonly ulong[] _xxh3InitAcc = + [ + Prime32_3, + Prime64_1, + Prime64_2, + Prime64_3, + Prime64_4, + Prime32_2, + Prime64_5, + Prime32_1 + ]; + + private static ReadOnlySpan Xxh3KSecret => + [ + 0xb8, + 0xfe, + 0x6c, + 0x39, + 0x23, + 0xa4, + 0x4b, + 0xbe, + 0x7c, + 0x01, + 0x81, + 0x2c, + 0xf7, + 0x21, + 0xad, + 0x1c, + 0xde, + 0xd4, + 0x6d, + 0xe9, + 0x83, + 0x90, + 0x97, + 0xdb, + 0x72, + 0x40, + 0xa4, + 0xa4, + 0xb7, + 0xb3, + 0x67, + 0x1f, + 0xcb, + 0x79, + 0xe6, + 0x4e, + 0xcc, + 0xc0, + 0xe5, + 0x78, + 0x82, + 0x5a, + 0xd0, + 0x7d, + 0xcc, + 0xff, + 0x72, + 0x21, + 0xb8, + 0x08, + 0x46, + 0x74, + 0xf7, + 0x43, + 0x24, + 0x8e, + 0xe0, + 0x35, + 0x90, + 0xe6, + 0x81, + 0x3a, + 0x26, + 0x4c, + 0x3c, + 0x28, + 0x52, + 0xbb, + 0x91, + 0xc3, + 0x00, + 0xcb, + 0x88, + 0xd0, + 0x65, + 0x8b, + 0x1b, + 0x53, + 0x2e, + 0xa3, + 0x71, + 0x64, + 0x48, + 0x97, + 0xa2, + 0x0d, + 0xf9, + 0x4e, + 0x38, + 0x19, + 0xef, + 0x46, + 0xa9, + 0xde, + 0xac, + 0xd8, + 0xa8, + 0xfa, + 0x76, + 0x3f, + 0xe3, + 0x9c, + 0x34, + 0x3f, + 0xf9, + 0xdc, + 0xbb, + 0xc7, + 0xc7, + 0x0b, + 0x4f, + 0x1d, + 0x8a, + 0x51, + 0xe0, + 0x4b, + 0xcd, + 0xb4, + 0x59, + 0x31, + 0xc8, + 0x9f, + 0x7e, + 0xc9, + 0xd9, + 0x78, + 0x73, + 0x64, + 0xea, + 0xc5, + 0xac, + 0x83, + 0x34, + 0xd3, + 0xeb, + 0xc3, + 0xc5, + 0x81, + 0xa0, + 0xff, + 0xfa, + 0x13, + 0x63, + 0xeb, + 0x17, + 0x0d, + 0xdd, + 0x51, + 0xb7, + 0xf0, + 0xda, + 0x49, + 0xd3, + 0x16, + 0x55, + 0x26, + 0x29, + 0xd4, + 0x68, + 0x9e, + 0x2b, + 0x16, + 0xbe, + 0x58, + 0x7d, + 0x47, + 0xa1, + 0xfc, + 0x8f, + 0xf8, + 0xb8, + 0xd1, + 0x7a, + 0xd0, + 0x31, + 0xce, + 0x45, + 0xcb, + 0x3a, + 0x8f, + 0x95, + 0x16, + 0x04, + 0x28, + 0xaf, + 0xd7, + 0xfb, + 0xca, + 0xbb, + 0x4b, + 0x40, + 0x7e + ]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong Mult32To64(ulong x, ulong y) => (uint)x * (ulong)(uint)y; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Hash128 Mult64To128(ulong lhs, ulong rhs) { - Low = low; - High = high; + ulong high = Math.BigMul(lhs, rhs, out ulong low); + + return new Hash128 + { + Low = low, + High = high, + }; } - public readonly override string ToString() + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong Mul128Fold64(ulong lhs, ulong rhs) { - return $"{High:x16}{Low:x16}"; + Hash128 product = Mult64To128(lhs, rhs); + + return product.Low ^ product.High; } - public static bool operator ==(Hash128 x, Hash128 y) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong XorShift64(ulong v64, int shift) { - return x.Equals(y); + Debug.Assert(shift is >= 0 and < 64); + + return v64 ^ (v64 >> shift); } - public static bool operator !=(Hash128 x, Hash128 y) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong Xxh3Avalanche(ulong h64) { - return !x.Equals(y); + h64 = XorShift64(h64, 37); + h64 *= 0x165667919E3779F9UL; + h64 = XorShift64(h64, 32); + + return h64; } - public readonly override bool Equals(object obj) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong Xxh64Avalanche(ulong h64) { - return obj is Hash128 hash128 && Equals(hash128); + h64 ^= h64 >> 33; + h64 *= Prime64_2; + h64 ^= h64 >> 29; + h64 *= Prime64_3; + h64 ^= h64 >> 32; + + return h64; } - public readonly bool Equals(Hash128 cmpObj) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe static void Xxh3Accumulate512(Span acc, ReadOnlySpan input, ReadOnlySpan secret) { - return Low == cmpObj.Low && High == cmpObj.High; + if (Avx2.IsSupported) + { + fixed (ulong* pAcc = acc) + { + fixed (byte* pInput = input, pSecret = secret) + { + Vector256* xAcc = (Vector256*)pAcc; + Vector256* xInput = (Vector256*)pInput; + Vector256* xSecret = (Vector256*)pSecret; + + for (ulong i = 0; i < StripeLen / 32; i++) + { + Vector256 dataVec = xInput[i]; + Vector256 keyVec = xSecret[i]; + Vector256 dataKey = Avx2.Xor(dataVec, keyVec); + Vector256 dataKeyLo = Avx2.Shuffle(dataKey.AsUInt32(), 0b00110001); + Vector256 product = Avx2.Multiply(dataKey.AsUInt32(), dataKeyLo); + Vector256 dataSwap = Avx2.Shuffle(dataVec.AsUInt32(), 0b01001110); + Vector256 sum = Avx2.Add(xAcc[i], dataSwap.AsUInt64()); + xAcc[i] = Avx2.Add(product, sum); + } + } + } + } + else if (Sse2.IsSupported) + { + fixed (ulong* pAcc = acc) + { + fixed (byte* pInput = input, pSecret = secret) + { + Vector128* xAcc = (Vector128*)pAcc; + Vector128* xInput = (Vector128*)pInput; + Vector128* xSecret = (Vector128*)pSecret; + + for (ulong i = 0; i < StripeLen / 16; i++) + { + Vector128 dataVec = xInput[i]; + Vector128 keyVec = xSecret[i]; + Vector128 dataKey = Sse2.Xor(dataVec, keyVec); + Vector128 dataKeyLo = Sse2.Shuffle(dataKey.AsUInt32(), 0b00110001); + Vector128 product = Sse2.Multiply(dataKey.AsUInt32(), dataKeyLo); + Vector128 dataSwap = Sse2.Shuffle(dataVec.AsUInt32(), 0b01001110); + Vector128 sum = Sse2.Add(xAcc[i], dataSwap.AsUInt64()); + xAcc[i] = Sse2.Add(product, sum); + } + } + } + } + else + { + for (int i = 0; i < AccNb; i++) + { + ulong dataVal = BinaryPrimitives.ReadUInt64LittleEndian(input[(i * sizeof(ulong))..]); + ulong dataKey = dataVal ^ BinaryPrimitives.ReadUInt64LittleEndian(secret[(i * sizeof(ulong))..]); + acc[i ^ 1] += dataVal; + acc[i] += Mult32To64((uint)dataKey, dataKey >> 32); + } + } } - public readonly override int GetHashCode() + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private unsafe static void Xxh3ScrambleAcc(Span acc, ReadOnlySpan secret) { - return HashCode.Combine(Low, High); + if (Avx2.IsSupported) + { + fixed (ulong* pAcc = acc) + { + fixed (byte* pSecret = secret) + { + Vector256 prime32 = Vector256.Create(Prime32_1); + Vector256* xAcc = (Vector256*)pAcc; + Vector256* xSecret = (Vector256*)pSecret; + + for (ulong i = 0; i < StripeLen / 32; i++) + { + Vector256 accVec = xAcc[i]; + Vector256 shifted = Avx2.ShiftRightLogical(accVec, 47); + Vector256 dataVec = Avx2.Xor(accVec, shifted); + + Vector256 keyVec = xSecret[i]; + Vector256 dataKey = Avx2.Xor(dataVec.AsUInt32(), keyVec.AsUInt32()); + + Vector256 dataKeyHi = Avx2.Shuffle(dataKey.AsUInt32(), 0b00110001); + Vector256 prodLo = Avx2.Multiply(dataKey, prime32); + Vector256 prodHi = Avx2.Multiply(dataKeyHi, prime32); + + xAcc[i] = Avx2.Add(prodLo, Avx2.ShiftLeftLogical(prodHi, 32)); + } + } + } + } + else if (Sse2.IsSupported) + { + fixed (ulong* pAcc = acc) + { + fixed (byte* pSecret = secret) + { + Vector128 prime32 = Vector128.Create(Prime32_1); + Vector128* xAcc = (Vector128*)pAcc; + Vector128* xSecret = (Vector128*)pSecret; + + for (ulong i = 0; i < StripeLen / 16; i++) + { + Vector128 accVec = xAcc[i]; + Vector128 shifted = Sse2.ShiftRightLogical(accVec, 47); + Vector128 dataVec = Sse2.Xor(accVec, shifted); + + Vector128 keyVec = xSecret[i]; + Vector128 dataKey = Sse2.Xor(dataVec.AsUInt32(), keyVec.AsUInt32()); + + Vector128 dataKeyHi = Sse2.Shuffle(dataKey.AsUInt32(), 0b00110001); + Vector128 prodLo = Sse2.Multiply(dataKey, prime32); + Vector128 prodHi = Sse2.Multiply(dataKeyHi, prime32); + + xAcc[i] = Sse2.Add(prodLo, Sse2.ShiftLeftLogical(prodHi, 32)); + } + } + } + } + else + { + for (int i = 0; i < AccNb; i++) + { + ulong key64 = BinaryPrimitives.ReadUInt64LittleEndian(secret[(i * sizeof(ulong))..]); + ulong acc64 = acc[i]; + acc64 = XorShift64(acc64, 47); + acc64 ^= key64; + acc64 *= Prime32_1; + acc[i] = acc64; + } + } } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void Xxh3Accumulate(Span acc, ReadOnlySpan input, ReadOnlySpan secret, int nbStripes) + { + for (int n = 0; n < nbStripes; n++) + { + ReadOnlySpan inData = input[(n * StripeLen)..]; + Xxh3Accumulate512(acc, inData, secret[(n * SecretConsumeRate)..]); + } + } + + private static void Xxh3HashLongInternalLoop(Span acc, ReadOnlySpan input, ReadOnlySpan secret) + { + int nbStripesPerBlock = (secret.Length - StripeLen) / SecretConsumeRate; + int blockLen = StripeLen * nbStripesPerBlock; + int nbBlocks = (input.Length - 1) / blockLen; + + Debug.Assert(secret.Length >= SecretSizeMin); + + for (int n = 0; n < nbBlocks; n++) + { + Xxh3Accumulate(acc, input[(n * blockLen)..], secret, nbStripesPerBlock); + Xxh3ScrambleAcc(acc, secret[^StripeLen..]); + } + + Debug.Assert(input.Length > StripeLen); + + int nbStripes = (input.Length - 1 - (blockLen * nbBlocks)) / StripeLen; + Debug.Assert(nbStripes <= (secret.Length / SecretConsumeRate)); + Xxh3Accumulate(acc, input[(nbBlocks * blockLen)..], secret, nbStripes); + + ReadOnlySpan p = input[^StripeLen..]; + Xxh3Accumulate512(acc, p, secret[(secret.Length - StripeLen - SecretLastAccStart)..]); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong Xxh3Mix2Accs(Span acc, ReadOnlySpan secret) + { + return Mul128Fold64( + acc[0] ^ BinaryPrimitives.ReadUInt64LittleEndian(secret), + acc[1] ^ BinaryPrimitives.ReadUInt64LittleEndian(secret[8..])); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong Xxh3MergeAccs(Span acc, ReadOnlySpan secret, ulong start) + { + ulong result64 = start; + + for (int i = 0; i < 4; i++) + { + result64 += Xxh3Mix2Accs(acc[(2 * i)..], secret[(16 * i)..]); + } + + return Xxh3Avalanche(result64); + } + + [SkipLocalsInit] + private static Hash128 Xxh3HashLong128bInternal(ReadOnlySpan input, ReadOnlySpan secret) + { + Span acc = stackalloc ulong[AccNb]; + _xxh3InitAcc.CopyTo(acc); + + Xxh3HashLongInternalLoop(acc, input, secret); + + Debug.Assert(acc.Length == 8); + Debug.Assert(secret.Length >= acc.Length * sizeof(ulong) + SecretMergeAccsStart); + + return new Hash128 + { + Low = Xxh3MergeAccs(acc, secret[SecretMergeAccsStart..], (ulong)input.Length * Prime64_1), + High = Xxh3MergeAccs( + acc, + secret[(secret.Length - acc.Length * sizeof(ulong) - SecretMergeAccsStart)..], + ~((ulong)input.Length * Prime64_2)), + }; + } + + private static Hash128 Xxh3Len1To3128b(ReadOnlySpan input, ReadOnlySpan secret, ulong seed) + { + Debug.Assert(1 <= input.Length && input.Length <= 3); + + byte c1 = input[0]; + byte c2 = input[input.Length >> 1]; + byte c3 = input[^1]; + + uint combinedL = ((uint)c1 << 16) | ((uint)c2 << 24) | c3 | ((uint)input.Length << 8); + uint combinedH = BitOperations.RotateLeft(BinaryPrimitives.ReverseEndianness(combinedL), 13); + ulong bitFlipL = (BinaryPrimitives.ReadUInt32LittleEndian(secret) ^ BinaryPrimitives.ReadUInt32LittleEndian(secret[4..])) + seed; + ulong bitFlipH = (BinaryPrimitives.ReadUInt32LittleEndian(secret[8..]) ^ BinaryPrimitives.ReadUInt32LittleEndian(secret[12..])) - seed; + ulong keyedLo = combinedL ^ bitFlipL; + ulong keyedHi = combinedH ^ bitFlipH; + + return new Hash128 + { + Low = Xxh64Avalanche(keyedLo), + High = Xxh64Avalanche(keyedHi), + }; + } + + private static Hash128 Xxh3Len4To8128b(ReadOnlySpan input, ReadOnlySpan secret, ulong seed) + { + Debug.Assert(4 <= input.Length && input.Length <= 8); + + seed ^= BinaryPrimitives.ReverseEndianness((uint)seed) << 32; + + uint inputLo = BinaryPrimitives.ReadUInt32LittleEndian(input); + uint inputHi = BinaryPrimitives.ReadUInt32LittleEndian(input[^4..]); + ulong input64 = inputLo + ((ulong)inputHi << 32); + ulong bitFlip = (BinaryPrimitives.ReadUInt64LittleEndian(secret[16..]) ^ BinaryPrimitives.ReadUInt64LittleEndian(secret[24..])) + seed; + ulong keyed = input64 ^ bitFlip; + + Hash128 m128 = Mult64To128(keyed, Prime64_1 + ((ulong)input.Length << 2)); + + m128.High += m128.Low << 1; + m128.Low ^= m128.High >> 3; + + m128.Low = XorShift64(m128.Low, 35); + m128.Low *= 0x9FB21C651E98DF25UL; + m128.Low = XorShift64(m128.Low, 28); + m128.High = Xxh3Avalanche(m128.High); + + return m128; + } + + private static Hash128 Xxh3Len9To16128b(ReadOnlySpan input, ReadOnlySpan secret, ulong seed) + { + Debug.Assert(9 <= input.Length && input.Length <= 16); + + ulong bitFlipL = (BinaryPrimitives.ReadUInt64LittleEndian(secret[32..]) ^ BinaryPrimitives.ReadUInt64LittleEndian(secret[40..])) - seed; + ulong bitFlipH = (BinaryPrimitives.ReadUInt64LittleEndian(secret[48..]) ^ BinaryPrimitives.ReadUInt64LittleEndian(secret[56..])) + seed; + ulong inputLo = BinaryPrimitives.ReadUInt64LittleEndian(input); + ulong inputHi = BinaryPrimitives.ReadUInt64LittleEndian(input[^8..]); + + Hash128 m128 = Mult64To128(inputLo ^ inputHi ^ bitFlipL, Prime64_1); + m128.Low += ((ulong)input.Length - 1) << 54; + inputHi ^= bitFlipH; + m128.High += inputHi + Mult32To64((uint)inputHi, Prime32_2 - 1); + m128.Low ^= BinaryPrimitives.ReverseEndianness(m128.High); + + Hash128 h128 = Mult64To128(m128.Low, Prime64_2); + h128.High += m128.High * Prime64_2; + h128.Low = Xxh3Avalanche(h128.Low); + h128.High = Xxh3Avalanche(h128.High); + + return h128; + } + + private static Hash128 Xxh3Len0To16128b(ReadOnlySpan input, ReadOnlySpan secret, ulong seed) + { + Debug.Assert(input.Length <= 16); + + if (input.Length > 8) + { + return Xxh3Len9To16128b(input, secret, seed); + } + + if (input.Length >= 4) + { + return Xxh3Len4To8128b(input, secret, seed); + } + + if (input.Length != 0) + { + return Xxh3Len1To3128b(input, secret, seed); + } + + Hash128 h128 = new(); + ulong bitFlipL = BinaryPrimitives.ReadUInt64LittleEndian(secret[64..]) ^ BinaryPrimitives.ReadUInt64LittleEndian(secret[72..]); + ulong bitFlipH = BinaryPrimitives.ReadUInt64LittleEndian(secret[80..]) ^ BinaryPrimitives.ReadUInt64LittleEndian(secret[88..]); + h128.Low = Xxh64Avalanche(seed ^ bitFlipL); + h128.High = Xxh64Avalanche(seed ^ bitFlipH); + + return h128; + } + + private static ulong Xxh3Mix16b(ReadOnlySpan input, ReadOnlySpan secret, ulong seed) + { + ulong inputLo = BinaryPrimitives.ReadUInt64LittleEndian(input); + ulong inputHi = BinaryPrimitives.ReadUInt64LittleEndian(input[8..]); + + return Mul128Fold64( + inputLo ^ (BinaryPrimitives.ReadUInt64LittleEndian(secret) + seed), + inputHi ^ (BinaryPrimitives.ReadUInt64LittleEndian(secret[8..]) - seed)); + } + + private static Hash128 Xxh128Mix32b(Hash128 acc, ReadOnlySpan input, ReadOnlySpan input2, ReadOnlySpan secret, ulong seed) + { + acc.Low += Xxh3Mix16b(input, secret, seed); + acc.Low ^= BinaryPrimitives.ReadUInt64LittleEndian(input2) + BinaryPrimitives.ReadUInt64LittleEndian(input2[8..]); + acc.High += Xxh3Mix16b(input2, secret[16..], seed); + acc.High ^= BinaryPrimitives.ReadUInt64LittleEndian(input) + BinaryPrimitives.ReadUInt64LittleEndian(input[8..]); + + return acc; + } + + private static Hash128 Xxh3Len17To128128b(ReadOnlySpan input, ReadOnlySpan secret, ulong seed) + { + Debug.Assert(secret.Length >= SecretSizeMin); + Debug.Assert(16 < input.Length && input.Length <= 128); + + Hash128 acc = new() + { + Low = (ulong)input.Length * Prime64_1, + High = 0, + }; + + if (input.Length > 32) + { + if (input.Length > 64) + { + if (input.Length > 96) + { + acc = Xxh128Mix32b(acc, input[48..], input[^64..], secret[96..], seed); + } + acc = Xxh128Mix32b(acc, input[32..], input[^48..], secret[64..], seed); + } + acc = Xxh128Mix32b(acc, input[16..], input[^32..], secret[32..], seed); + } + acc = Xxh128Mix32b(acc, input, input[^16..], secret, seed); + + Hash128 h128 = new() + { + Low = acc.Low + acc.High, + High = acc.Low * Prime64_1 + acc.High * Prime64_4 + ((ulong)input.Length - seed) * Prime64_2, + }; + h128.Low = Xxh3Avalanche(h128.Low); + h128.High = 0UL - Xxh3Avalanche(h128.High); + + return h128; + } + + private static Hash128 Xxh3Len129To240128b(ReadOnlySpan input, ReadOnlySpan secret, ulong seed) + { + Debug.Assert(secret.Length >= SecretSizeMin); + Debug.Assert(128 < input.Length && input.Length <= 240); + + Hash128 acc = new(); + + int nbRounds = input.Length / 32; + acc.Low = (ulong)input.Length * Prime64_1; + acc.High = 0; + + for (int i = 0; i < 4; i++) + { + acc = Xxh128Mix32b(acc, input[(32 * i)..], input[(32 * i + 16)..], secret[(32 * i)..], seed); + } + + acc.Low = Xxh3Avalanche(acc.Low); + acc.High = Xxh3Avalanche(acc.High); + Debug.Assert(nbRounds >= 4); + + for (int i = 4; i < nbRounds; i++) + { + acc = Xxh128Mix32b(acc, input[(32 * i)..], input[(32 * i + 16)..], secret[(MidSizeStartOffset + 32 * (i - 4))..], seed); + } + + acc = Xxh128Mix32b(acc, input[^16..], input[^32..], secret[(SecretSizeMin - MidSizeLastOffset - 16)..], 0UL - seed); + + Hash128 h128 = new() + { + Low = acc.Low + acc.High, + High = acc.Low * Prime64_1 + acc.High * Prime64_4 + ((ulong)input.Length - seed) * Prime64_2, + }; + h128.Low = Xxh3Avalanche(h128.Low); + h128.High = 0UL - Xxh3Avalanche(h128.High); + + return h128; + } + + private static Hash128 Xxh3128bitsInternal(ReadOnlySpan input, ReadOnlySpan secret, ulong seed) + { + Debug.Assert(secret.Length >= SecretSizeMin); + + return input.Length switch + { + <= 16 => Xxh3Len0To16128b(input, secret, seed), + <= 128 => Xxh3Len17To128128b(input, secret, seed), + <= 240 => Xxh3Len129To240128b(input, secret, seed), + _ => Xxh3HashLong128bInternal(input, secret) + }; + } + + #endregion } } diff --git a/src/Ryujinx.Common/Logging/LogClass.cs b/src/Ryujinx.Common/Logging/LogClass.cs index 1b404a06a..a4117580e 100644 --- a/src/Ryujinx.Common/Logging/LogClass.cs +++ b/src/Ryujinx.Common/Logging/LogClass.cs @@ -72,5 +72,6 @@ namespace Ryujinx.Common.Logging TamperMachine, UI, Vic, + XCIFileTrimmer } } diff --git a/src/Ryujinx.Common/Logging/Logger.cs b/src/Ryujinx.Common/Logging/Logger.cs index db46739ac..26d343969 100644 --- a/src/Ryujinx.Common/Logging/Logger.cs +++ b/src/Ryujinx.Common/Logging/Logger.cs @@ -24,7 +24,7 @@ namespace Ryujinx.Common.Logging public readonly struct Log { private static readonly string _homeDir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); - private static readonly string _homeDirRedacted = Path.Combine(Directory.GetParent(_homeDir).FullName, "[redacted]"); + private static readonly string _homeDirRedacted = Path.Combine(Directory.GetParent(_homeDir)!.FullName, "[redacted]"); internal readonly LogLevel Level; @@ -38,7 +38,7 @@ namespace Ryujinx.Common.Logging { if (_enabledClasses[(int)logClass]) { - Updated?.Invoke(null, new LogEventArgs(Level, _time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, "", message))); + Updated?.Invoke(null, new LogEventArgs(Level, _time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, string.Empty, message))); } } @@ -212,9 +212,7 @@ namespace Ryujinx.Common.Logging foreach (var log in logs) { if (log.HasValue) - { levels.Add(log.Value.Level); - } } return levels; @@ -233,7 +231,8 @@ namespace Ryujinx.Common.Logging case LogLevel.AccessLog : AccessLog = enabled ? new Log(LogLevel.AccessLog) : new Log?(); break; case LogLevel.Stub : Stub = enabled ? new Log(LogLevel.Stub) : new Log?(); break; case LogLevel.Trace : Trace = enabled ? new Log(LogLevel.Trace) : new Log?(); break; - default: throw new ArgumentException("Unknown Log Level"); + case LogLevel.Notice : break; + default: throw new ArgumentException("Unknown Log Level", nameof(logLevel)); #pragma warning restore IDE0055 } } diff --git a/src/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs b/src/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs index 02c6dc97b..a9dbe646a 100644 --- a/src/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs +++ b/src/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs @@ -30,10 +30,10 @@ namespace Ryujinx.Common.Logging.Targets string ILogTarget.Name { get => _target.Name; } public AsyncLogTargetWrapper(ILogTarget target) - : this(target, -1, AsyncLogTargetOverflowAction.Block) + : this(target, -1) { } - public AsyncLogTargetWrapper(ILogTarget target, int queueLimit, AsyncLogTargetOverflowAction overflowAction) + public AsyncLogTargetWrapper(ILogTarget target, int queueLimit = -1, AsyncLogTargetOverflowAction overflowAction = AsyncLogTargetOverflowAction.Block) { _target = target; _messageQueue = new BlockingCollection(queueLimit); diff --git a/src/Ryujinx.Common/Logging/Targets/FileLogTarget.cs b/src/Ryujinx.Common/Logging/Targets/FileLogTarget.cs index 8d4ede96c..94e9359c8 100644 --- a/src/Ryujinx.Common/Logging/Targets/FileLogTarget.cs +++ b/src/Ryujinx.Common/Logging/Targets/FileLogTarget.cs @@ -47,7 +47,7 @@ namespace Ryujinx.Common.Logging.Targets } // Clean up old logs, should only keep 3 - FileInfo[] files = logDir.GetFiles("*.log").OrderBy((info => info.CreationTime)).ToArray(); + FileInfo[] files = logDir.GetFiles("*.log").OrderBy(info => info.CreationTime).ToArray(); for (int i = 0; i < files.Length - 2; i++) { try @@ -69,9 +69,10 @@ namespace Ryujinx.Common.Logging.Targets } string version = ReleaseInformation.Version; + string appName = ReleaseInformation.IsCanaryBuild ? "Ryujinx_Canary" : "Ryujinx"; // Get path for the current time - path = Path.Combine(logDir.FullName, $"Ryujinx_{version}_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.log"); + path = Path.Combine(logDir.FullName, $"{appName}_{version}_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.log"); try { diff --git a/src/Ryujinx.Common/Logging/XCIFileTrimmerLog.cs b/src/Ryujinx.Common/Logging/XCIFileTrimmerLog.cs new file mode 100644 index 000000000..fb11432b0 --- /dev/null +++ b/src/Ryujinx.Common/Logging/XCIFileTrimmerLog.cs @@ -0,0 +1,30 @@ +using Ryujinx.Common.Utilities; + +namespace Ryujinx.Common.Logging +{ + public class XCIFileTrimmerLog : XCIFileTrimmer.ILog + { + public virtual void Progress(long current, long total, string text, bool complete) + { + } + + public void Write(XCIFileTrimmer.LogType logType, string text) + { + switch (logType) + { + case XCIFileTrimmer.LogType.Info: + Logger.Notice.Print(LogClass.XCIFileTrimmer, text); + break; + case XCIFileTrimmer.LogType.Warn: + Logger.Warning?.Print(LogClass.XCIFileTrimmer, text); + break; + case XCIFileTrimmer.LogType.Error: + Logger.Error?.Print(LogClass.XCIFileTrimmer, text); + break; + case XCIFileTrimmer.LogType.Progress: + Logger.Info?.Print(LogClass.XCIFileTrimmer, text); + break; + } + } + } +} diff --git a/src/Ryujinx.Common/Memory/ArrayPtr.cs b/src/Ryujinx.Common/Memory/ArrayPtr.cs index 7487a1ff5..a54bdb3f6 100644 --- a/src/Ryujinx.Common/Memory/ArrayPtr.cs +++ b/src/Ryujinx.Common/Memory/ArrayPtr.cs @@ -11,17 +11,17 @@ namespace Ryujinx.Common.Memory /// Array element type public unsafe struct ArrayPtr : IEquatable>, IArray where T : unmanaged { - private IntPtr _ptr; + private nint _ptr; /// /// Null pointer. /// - public static ArrayPtr Null => new() { _ptr = IntPtr.Zero }; + public static ArrayPtr Null => new() { _ptr = nint.Zero }; /// /// True if the pointer is null, false otherwise. /// - public readonly bool IsNull => _ptr == IntPtr.Zero; + public readonly bool IsNull => _ptr == nint.Zero; /// /// Number of elements on the array. @@ -50,7 +50,7 @@ namespace Ryujinx.Common.Memory /// Number of elements on the array public ArrayPtr(ref T value, int length) { - _ptr = (IntPtr)Unsafe.AsPointer(ref value); + _ptr = (nint)Unsafe.AsPointer(ref value); Length = length; } @@ -61,7 +61,7 @@ namespace Ryujinx.Common.Memory /// Number of elements on the array public ArrayPtr(T* ptr, int length) { - _ptr = (IntPtr)ptr; + _ptr = (nint)ptr; Length = length; } @@ -70,7 +70,7 @@ namespace Ryujinx.Common.Memory /// /// Array base pointer /// Number of elements on the array - public ArrayPtr(IntPtr ptr, int length) + public ArrayPtr(nint ptr, int length) { _ptr = ptr; Length = length; diff --git a/src/Ryujinx.Common/Memory/PartialUnmaps/PartialUnmapState.cs b/src/Ryujinx.Common/Memory/PartialUnmaps/PartialUnmapState.cs index 93fef5c3b..60fdd7af6 100644 --- a/src/Ryujinx.Common/Memory/PartialUnmaps/PartialUnmapState.cs +++ b/src/Ryujinx.Common/Memory/PartialUnmaps/PartialUnmapState.cs @@ -21,7 +21,7 @@ namespace Ryujinx.Common.Memory.PartialUnmaps public readonly static int PartialUnmapsCountOffset; public readonly static int LocalCountsOffset; - public readonly static IntPtr GlobalState; + public readonly static nint GlobalState; [SupportedOSPlatform("windows")] [LibraryImport("kernel32.dll")] @@ -29,17 +29,17 @@ namespace Ryujinx.Common.Memory.PartialUnmaps [SupportedOSPlatform("windows")] [LibraryImport("kernel32.dll", SetLastError = true)] - private static partial IntPtr OpenThread(int dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwThreadId); + private static partial nint OpenThread(int dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwThreadId); [SupportedOSPlatform("windows")] [LibraryImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] - private static partial bool CloseHandle(IntPtr hObject); + private static partial bool CloseHandle(nint hObject); [SupportedOSPlatform("windows")] [LibraryImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] - private static partial bool GetExitCodeThread(IntPtr hThread, out uint lpExitCode); + private static partial bool GetExitCodeThread(nint hThread, out uint lpExitCode); /// /// Creates a global static PartialUnmapState and populates the field offsets. @@ -137,9 +137,9 @@ namespace Ryujinx.Common.Memory.PartialUnmaps if (id != 0) { - IntPtr handle = OpenThread(ThreadQueryInformation, false, (uint)id); + nint handle = OpenThread(ThreadQueryInformation, false, (uint)id); - if (handle == IntPtr.Zero) + if (handle == nint.Zero) { Interlocked.CompareExchange(ref ids[i], 0, id); } diff --git a/src/Ryujinx.Common/Memory/Ptr.cs b/src/Ryujinx.Common/Memory/Ptr.cs index d01748c16..3a8c1e1b6 100644 --- a/src/Ryujinx.Common/Memory/Ptr.cs +++ b/src/Ryujinx.Common/Memory/Ptr.cs @@ -10,17 +10,17 @@ namespace Ryujinx.Common.Memory /// Type of the unmanaged resource public unsafe struct Ptr : IEquatable> where T : unmanaged { - private IntPtr _ptr; + private nint _ptr; /// /// Null pointer. /// - public static Ptr Null => new() { _ptr = IntPtr.Zero }; + public static Ptr Null => new() { _ptr = nint.Zero }; /// /// True if the pointer is null, false otherwise. /// - public readonly bool IsNull => _ptr == IntPtr.Zero; + public readonly bool IsNull => _ptr == nint.Zero; /// /// Gets a reference to the value. @@ -37,7 +37,7 @@ namespace Ryujinx.Common.Memory /// Reference to the unmanaged resource public Ptr(ref T value) { - _ptr = (IntPtr)Unsafe.AsPointer(ref value); + _ptr = (nint)Unsafe.AsPointer(ref value); } public readonly override bool Equals(object obj) diff --git a/src/Ryujinx.Common/Memory/StructArrayHelpers.cs b/src/Ryujinx.Common/Memory/StructArrayHelpers.cs index 762c73889..fcb2229a7 100644 --- a/src/Ryujinx.Common/Memory/StructArrayHelpers.cs +++ b/src/Ryujinx.Common/Memory/StructArrayHelpers.cs @@ -803,18 +803,6 @@ namespace Ryujinx.Common.Memory public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } - public struct Array256 : IArray where T : unmanaged - { - T _e0; - Array128 _other; - Array127 _other2; - public readonly int Length => 256; - public ref T this[int index] => ref AsSpan()[index]; - - [Pure] - public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); - } - public struct Array140 : IArray where T : unmanaged { T _e0; @@ -828,6 +816,18 @@ namespace Ryujinx.Common.Memory public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); } + public struct Array256 : IArray where T : unmanaged + { + T _e0; + Array128 _other; + Array127 _other2; + public readonly int Length => 256; + public ref T this[int index] => ref AsSpan()[index]; + + [Pure] + public Span AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length); + } + public struct Array384 : IArray where T : unmanaged { T _e0; diff --git a/src/Ryujinx.Common/Pools/ObjectPool.cs b/src/Ryujinx.Common/Pools/ObjectPool.cs index 0b6ce3771..c4610a59c 100644 --- a/src/Ryujinx.Common/Pools/ObjectPool.cs +++ b/src/Ryujinx.Common/Pools/ObjectPool.cs @@ -3,19 +3,11 @@ using System.Threading; namespace Ryujinx.Common { - public class ObjectPool + public class ObjectPool(Func factory, int size) where T : class { private T _firstItem; - private readonly T[] _items; - - private readonly Func _factory; - - public ObjectPool(Func factory, int size) - { - _items = new T[size - 1]; - _factory = factory; - } + private readonly T[] _items = new T[size - 1]; public T Allocate() { @@ -43,7 +35,7 @@ namespace Ryujinx.Common } } - return _factory(); + return factory(); } public void Release(T obj) diff --git a/src/Ryujinx.Common/PreciseSleep/NanosleepPool.cs b/src/Ryujinx.Common/PreciseSleep/NanosleepPool.cs index c0973dcb3..45b8e95fa 100644 --- a/src/Ryujinx.Common/PreciseSleep/NanosleepPool.cs +++ b/src/Ryujinx.Common/PreciseSleep/NanosleepPool.cs @@ -124,7 +124,7 @@ namespace Ryujinx.Common.PreciseSleep } } - private readonly object _lock = new(); + private readonly Lock _lock = new(); private readonly List _threads = new(); private readonly List _active = new(); private readonly Stack _free = new(); diff --git a/src/Ryujinx.Common/PreciseSleep/WindowsGranularTimer.cs b/src/Ryujinx.Common/PreciseSleep/WindowsGranularTimer.cs index 3bf092704..cef4dc927 100644 --- a/src/Ryujinx.Common/PreciseSleep/WindowsGranularTimer.cs +++ b/src/Ryujinx.Common/PreciseSleep/WindowsGranularTimer.cs @@ -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 _waitingObjects = new(); private WindowsGranularTimer() diff --git a/src/Ryujinx.Common/ReactiveObject.cs b/src/Ryujinx.Common/ReactiveObject.cs index 4831edb52..8df1e20fe 100644 --- a/src/Ryujinx.Common/ReactiveObject.cs +++ b/src/Ryujinx.Common/ReactiveObject.cs @@ -1,11 +1,13 @@ +using Ryujinx.Common.Logging; using System; +using System.Globalization; using System.Threading; namespace Ryujinx.Common { public class ReactiveObject { - private readonly ReaderWriterLockSlim _readerWriterLock = new(); + private readonly ReaderWriterLockSlim _rwLock = new(); private bool _isInitialized; private T _value; @@ -15,15 +17,15 @@ namespace Ryujinx.Common { get { - _readerWriterLock.EnterReadLock(); + _rwLock.EnterReadLock(); T value = _value; - _readerWriterLock.ExitReadLock(); + _rwLock.ExitReadLock(); return value; } set { - _readerWriterLock.EnterWriteLock(); + _rwLock.EnterWriteLock(); T oldValue = _value; @@ -32,7 +34,7 @@ namespace Ryujinx.Common _isInitialized = true; _value = value; - _readerWriterLock.ExitWriteLock(); + _rwLock.ExitWriteLock(); if (!oldIsInitialized || oldValue == null || !oldValue.Equals(_value)) { @@ -40,22 +42,28 @@ namespace Ryujinx.Common } } } + + public void LogChangesToValue(string valueName, LogClass logClass = LogClass.Configuration) + => Event += (_, e) => ReactiveObjectHelper.LogValueChange(logClass, e, valueName); - public static implicit operator T(ReactiveObject obj) - { - return obj.Value; - } + public static implicit operator T(ReactiveObject obj) => obj.Value; } - public class ReactiveEventArgs + public static class ReactiveObjectHelper { - public T OldValue { get; } - public T NewValue { get; } - - public ReactiveEventArgs(T oldValue, T newValue) + public static void LogValueChange(LogClass logClass, ReactiveEventArgs eventArgs, string valueName) { - OldValue = oldValue; - NewValue = newValue; + string message = string.Create(CultureInfo.InvariantCulture, $"{valueName} set to: {eventArgs.NewValue}"); + + Logger.Info?.Print(logClass, message); } + + public static void Toggle(this ReactiveObject rBoolean) => rBoolean.Value = !rBoolean.Value; + } + + public class ReactiveEventArgs(T oldValue, T newValue) + { + public T OldValue { get; } = oldValue; + public T NewValue { get; } = newValue; } } diff --git a/src/Ryujinx.Common/ReleaseInformation.cs b/src/Ryujinx.Common/ReleaseInformation.cs index 774ae012a..cbf93013f 100644 --- a/src/Ryujinx.Common/ReleaseInformation.cs +++ b/src/Ryujinx.Common/ReleaseInformation.cs @@ -1,3 +1,4 @@ +using System; using System.Reflection; namespace Ryujinx.Common @@ -5,14 +6,16 @@ namespace Ryujinx.Common // DO NOT EDIT, filled by CI public static class ReleaseInformation { - private const string FlatHubChannelOwner = "flathub"; + private const string CanaryChannel = "canary"; + private const string ReleaseChannel = "release"; private const string BuildVersion = "%%RYUJINX_BUILD_VERSION%%"; - private const string BuildGitHash = "%%RYUJINX_BUILD_GIT_HASH%%"; + public const string BuildGitHash = "%%RYUJINX_BUILD_GIT_HASH%%"; private const string ReleaseChannelName = "%%RYUJINX_TARGET_RELEASE_CHANNEL_NAME%%"; private const string ConfigFileName = "%%RYUJINX_CONFIG_FILE_NAME%%"; public const string ReleaseChannelOwner = "%%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER%%"; + public const string ReleaseChannelSourceRepo = "%%RYUJINX_TARGET_RELEASE_CHANNEL_SOURCE_REPO%%"; public const string ReleaseChannelRepo = "%%RYUJINX_TARGET_RELEASE_CHANNEL_REPO%%"; public static string ConfigName => !ConfigFileName.StartsWith("%%") ? ConfigFileName : "Config.json"; @@ -21,11 +24,22 @@ namespace Ryujinx.Common !BuildGitHash.StartsWith("%%") && !ReleaseChannelName.StartsWith("%%") && !ReleaseChannelOwner.StartsWith("%%") && + !ReleaseChannelSourceRepo.StartsWith("%%") && !ReleaseChannelRepo.StartsWith("%%") && !ConfigFileName.StartsWith("%%"); - public static bool IsFlatHubBuild => IsValid && ReleaseChannelOwner.Equals(FlatHubChannelOwner); + public static bool IsCanaryBuild => IsValid && ReleaseChannelName.Equals(CanaryChannel); + + public static bool IsReleaseBuild => IsValid && ReleaseChannelName.Equals(ReleaseChannel); public static string Version => IsValid ? BuildVersion : Assembly.GetEntryAssembly()!.GetCustomAttribute()?.InformationalVersion; + + public static string GetChangelogUrl(Version currentVersion, Version newVersion) => + IsCanaryBuild + ? $"https://github.com/{ReleaseChannelOwner}/{ReleaseChannelSourceRepo}/compare/Canary-{currentVersion}...Canary-{newVersion}" + : $"https://github.com/{ReleaseChannelOwner}/{ReleaseChannelSourceRepo}/releases/tag/{newVersion}"; + + public static string GetChangelogForVersion(Version version) => + $"https://github.com/{ReleaseChannelOwner}/{ReleaseChannelRepo}/releases/tag/{version}"; } } diff --git a/src/Ryujinx.Common/Ryujinx.Common.csproj b/src/Ryujinx.Common/Ryujinx.Common.csproj index da2f13a21..de163aae7 100644 --- a/src/Ryujinx.Common/Ryujinx.Common.csproj +++ b/src/Ryujinx.Common/Ryujinx.Common.csproj @@ -1,15 +1,17 @@ - net8.0 true $(DefineConstants);$(ExtraDefineConstants) + $(DefaultItemExcludes);._* + + diff --git a/src/Ryujinx.Common/SystemInterop/ForceDpiAware.cs b/src/Ryujinx.Common/SystemInterop/ForceDpiAware.cs index b8e1df7d2..330638171 100644 --- a/src/Ryujinx.Common/SystemInterop/ForceDpiAware.cs +++ b/src/Ryujinx.Common/SystemInterop/ForceDpiAware.cs @@ -14,19 +14,19 @@ namespace Ryujinx.Common.SystemInterop private const string X11LibraryName = "libX11.so.6"; [LibraryImport(X11LibraryName)] - private static partial IntPtr XOpenDisplay([MarshalAs(UnmanagedType.LPStr)] string display); + private static partial nint XOpenDisplay([MarshalAs(UnmanagedType.LPStr)] string display); [LibraryImport(X11LibraryName)] - private static partial IntPtr XGetDefault(IntPtr display, [MarshalAs(UnmanagedType.LPStr)] string program, [MarshalAs(UnmanagedType.LPStr)] string option); + private static partial nint XGetDefault(nint display, [MarshalAs(UnmanagedType.LPStr)] string program, [MarshalAs(UnmanagedType.LPStr)] string option); [LibraryImport(X11LibraryName)] - private static partial int XDisplayWidth(IntPtr display, int screenNumber); + private static partial int XDisplayWidth(nint display, int screenNumber); [LibraryImport(X11LibraryName)] - private static partial int XDisplayWidthMM(IntPtr display, int screenNumber); + private static partial int XDisplayWidthMM(nint display, int screenNumber); [LibraryImport(X11LibraryName)] - private static partial int XCloseDisplay(IntPtr display); + private static partial int XCloseDisplay(nint display); private const double StandardDpiScale = 96.0; private const double MaxScaleFactor = 1.25; @@ -51,7 +51,7 @@ namespace Ryujinx.Common.SystemInterop { if (OperatingSystem.IsWindows()) { - userDpiScale = GdiPlusHelper.GetDpiX(IntPtr.Zero); + userDpiScale = GdiPlusHelper.GetDpiX(nint.Zero); } else if (OperatingSystem.IsLinux()) { @@ -59,7 +59,7 @@ namespace Ryujinx.Common.SystemInterop if (xdgSessionType == null || xdgSessionType == "x11") { - IntPtr display = XOpenDisplay(null); + nint display = XOpenDisplay(null); string dpiString = Marshal.PtrToStringAnsi(XGetDefault(display, "Xft", "dpi")); if (dpiString == null || !double.TryParse(dpiString, NumberStyles.Any, CultureInfo.InvariantCulture, out userDpiScale)) { diff --git a/src/Ryujinx.Common/SystemInterop/GdiPlusHelper.cs b/src/Ryujinx.Common/SystemInterop/GdiPlusHelper.cs index 7e8e9f2a5..c00598c98 100644 --- a/src/Ryujinx.Common/SystemInterop/GdiPlusHelper.cs +++ b/src/Ryujinx.Common/SystemInterop/GdiPlusHelper.cs @@ -9,7 +9,7 @@ namespace Ryujinx.Common.SystemInterop { private const string LibraryName = "gdiplus.dll"; - private static readonly IntPtr _initToken; + private static readonly nint _initToken; static GdiPlusHelper() { @@ -29,7 +29,7 @@ namespace Ryujinx.Common.SystemInterop public int GdiplusVersion; #pragma warning disable CS0649 // Field is never assigned to - public IntPtr DebugEventCallback; + public nint DebugEventCallback; public int SuppressBackgroundThread; public int SuppressExternalCodecs; public int StartupParameters; @@ -39,7 +39,7 @@ namespace Ryujinx.Common.SystemInterop { // We assume Windows 8 and upper GdiplusVersion = 2, - DebugEventCallback = IntPtr.Zero, + DebugEventCallback = nint.Zero, SuppressBackgroundThread = 0, SuppressExternalCodecs = 0, StartupParameters = 0, @@ -48,25 +48,25 @@ namespace Ryujinx.Common.SystemInterop private struct StartupOutput { - public IntPtr NotificationHook; - public IntPtr NotificationUnhook; + public nint NotificationHook; + public nint NotificationUnhook; } [LibraryImport(LibraryName)] - private static partial int GdiplusStartup(out IntPtr token, in StartupInputEx input, out StartupOutput output); + private static partial int GdiplusStartup(out nint token, in StartupInputEx input, out StartupOutput output); [LibraryImport(LibraryName)] - private static partial int GdipCreateFromHWND(IntPtr hwnd, out IntPtr graphics); + private static partial int GdipCreateFromHWND(nint hwnd, out nint graphics); [LibraryImport(LibraryName)] - private static partial int GdipDeleteGraphics(IntPtr graphics); + private static partial int GdipDeleteGraphics(nint graphics); [LibraryImport(LibraryName)] - private static partial int GdipGetDpiX(IntPtr graphics, out float dpi); + private static partial int GdipGetDpiX(nint graphics, out float dpi); - public static float GetDpiX(IntPtr hwnd) + public static float GetDpiX(nint hwnd) { - CheckStatus(GdipCreateFromHWND(hwnd, out IntPtr graphicsHandle)); + CheckStatus(GdipCreateFromHWND(hwnd, out nint graphicsHandle)); CheckStatus(GdipGetDpiX(graphicsHandle, out float result)); CheckStatus(GdipDeleteGraphics(graphicsHandle)); diff --git a/src/Ryujinx.Common/Utilities/BitUtils.cs b/src/Ryujinx.Common/Utilities/BitUtils.cs index 0bf9c8acd..b9dae2e53 100644 --- a/src/Ryujinx.Common/Utilities/BitUtils.cs +++ b/src/Ryujinx.Common/Utilities/BitUtils.cs @@ -4,23 +4,18 @@ namespace Ryujinx.Common { public static class BitUtils { - public static T AlignUp(T value, T size) - where T : IBinaryInteger - { - return (value + (size - T.One)) & -size; - } + public static T AlignUp(T value, T size) where T : IBinaryInteger + => (value + (size - T.One)) & -size; - public static T AlignDown(T value, T size) - where T : IBinaryInteger - { - return value & -size; - } + public static T AlignDown(T value, T size) where T : IBinaryInteger + => value & -size; - public static T DivRoundUp(T value, T dividend) - where T : IBinaryInteger - { - return (value + (dividend - T.One)) / dividend; - } + public static T DivRoundUp(T value, T dividend) where T : IBinaryInteger + => (value + (dividend - T.One)) / dividend; + + public static int Pow2RoundDown(int value) => BitOperations.IsPow2(value) ? value : Pow2RoundUp(value) >> 1; + + public static long ReverseBits64(long value) => (long)ReverseBits64((ulong)value); public static int Pow2RoundUp(int value) { @@ -35,16 +30,6 @@ namespace Ryujinx.Common return ++value; } - public static int Pow2RoundDown(int value) - { - return BitOperations.IsPow2(value) ? value : Pow2RoundUp(value) >> 1; - } - - public static long ReverseBits64(long value) - { - return (long)ReverseBits64((ulong)value); - } - private static ulong ReverseBits64(ulong value) { value = ((value & 0xaaaaaaaaaaaaaaaa) >> 1) | ((value & 0x5555555555555555) << 1); diff --git a/src/Ryujinx.Common/Utilities/JsonHelper.cs b/src/Ryujinx.Common/Utilities/JsonHelper.cs index 95daec27a..82eeaddc1 100644 --- a/src/Ryujinx.Common/Utilities/JsonHelper.cs +++ b/src/Ryujinx.Common/Utilities/JsonHelper.cs @@ -1,3 +1,4 @@ +using System; using System.IO; using System.Text; using System.Text.Json; @@ -17,29 +18,24 @@ namespace Ryujinx.Common.Utilities /// It is REQUIRED for you to save returned options statically or as a part of static serializer context /// in order to avoid performance issues. You can safely modify returned options for your case before storing. /// - public static JsonSerializerOptions GetDefaultSerializerOptions(bool indented = true) - { - JsonSerializerOptions options = new() + public static JsonSerializerOptions GetDefaultSerializerOptions(bool indented = true) => + new() { DictionaryKeyPolicy = _snakeCasePolicy, PropertyNamingPolicy = _snakeCasePolicy, WriteIndented = indented, AllowTrailingCommas = true, - ReadCommentHandling = JsonCommentHandling.Skip, + ReadCommentHandling = JsonCommentHandling.Skip }; - return options; - } + public static string Serialize(T value, JsonTypeInfo typeInfo) + => JsonSerializer.Serialize(value, typeInfo); - public static string Serialize(T value, JsonTypeInfo typeInfo) - { - return JsonSerializer.Serialize(value, typeInfo); - } + public static T Deserialize(string value, JsonTypeInfo typeInfo) + => JsonSerializer.Deserialize(value, typeInfo); - public static T Deserialize(string value, JsonTypeInfo typeInfo) - { - return JsonSerializer.Deserialize(value, typeInfo); - } + public static T Deserialize(ReadOnlySpan utf8Value, JsonTypeInfo typeInfo) + => JsonSerializer.Deserialize(utf8Value, typeInfo); public static void SerializeToFile(string filePath, T value, JsonTypeInfo typeInfo) { @@ -53,10 +49,7 @@ namespace Ryujinx.Common.Utilities return JsonSerializer.Deserialize(file, typeInfo); } - public static void SerializeToStream(Stream stream, T value, JsonTypeInfo typeInfo) - { - JsonSerializer.Serialize(stream, value, typeInfo); - } + public static void SerializeToStream(Stream stream, T value, JsonTypeInfo typeInfo) => JsonSerializer.Serialize(stream, value, typeInfo); private class SnakeCaseNamingPolicy : JsonNamingPolicy { @@ -75,15 +68,10 @@ namespace Ryujinx.Common.Utilities if (char.IsUpper(c)) { - if (i == 0 || char.IsUpper(name[i - 1])) - { - builder.Append(char.ToLowerInvariant(c)); - } - else - { + if (!(i == 0 || char.IsUpper(name[i - 1]))) builder.Append('_'); - builder.Append(char.ToLowerInvariant(c)); - } + + builder.Append(char.ToLowerInvariant(c)); } else { diff --git a/src/Ryujinx.Common/Utilities/NetworkHelpers.cs b/src/Ryujinx.Common/Utilities/NetworkHelpers.cs index 71e02184e..53d1e4f33 100644 --- a/src/Ryujinx.Common/Utilities/NetworkHelpers.cs +++ b/src/Ryujinx.Common/Utilities/NetworkHelpers.cs @@ -1,6 +1,7 @@ using System.Buffers.Binary; using System.Net; using System.Net.NetworkInformation; +using System.Runtime.InteropServices; namespace Ryujinx.Common.Utilities { @@ -65,6 +66,11 @@ namespace Ryujinx.Common.Utilities return (targetProperties, targetAddressInfo); } + public static bool SupportsDynamicDns() + { + return RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + } + public static uint ConvertIpv4Address(IPAddress ipAddress) { return BinaryPrimitives.ReadUInt32BigEndian(ipAddress.GetAddressBytes()); diff --git a/src/Ryujinx.Common/Utilities/UInt128Utils.cs b/src/Ryujinx.Common/Utilities/UInt128Utils.cs index 113855355..23afaa3a1 100644 --- a/src/Ryujinx.Common/Utilities/UInt128Utils.cs +++ b/src/Ryujinx.Common/Utilities/UInt128Utils.cs @@ -5,14 +5,16 @@ namespace Ryujinx.Common.Utilities { public static class UInt128Utils { - public static UInt128 FromHex(string hex) - { - return new UInt128(ulong.Parse(hex.AsSpan(0, 16), NumberStyles.HexNumber), ulong.Parse(hex.AsSpan(16), NumberStyles.HexNumber)); - } + public static UInt128 FromHex(string hex) => + new( + ulong.Parse(hex.AsSpan(0, 16), NumberStyles.HexNumber), + ulong.Parse(hex.AsSpan(16), NumberStyles.HexNumber) + ); - public static UInt128 CreateRandom() - { - return new UInt128((ulong)Random.Shared.NextInt64(), (ulong)Random.Shared.NextInt64()); - } + public static Int128 NextInt128(this Random rand) => + new((ulong)rand.NextInt64(), (ulong)rand.NextInt64()); + + public static UInt128 NextUInt128(this Random rand) => + new((ulong)rand.NextInt64(), (ulong)rand.NextInt64()); } } diff --git a/src/Ryujinx.Common/Utilities/XCIFileTrimmer.cs b/src/Ryujinx.Common/Utilities/XCIFileTrimmer.cs new file mode 100644 index 000000000..050e78d1e --- /dev/null +++ b/src/Ryujinx.Common/Utilities/XCIFileTrimmer.cs @@ -0,0 +1,524 @@ +// Uncomment the line below to ensure XCIFileTrimmer does not modify files +//#define XCI_TRIMMER_READ_ONLY_MODE + +using Gommon; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading; + +namespace Ryujinx.Common.Utilities +{ + public sealed class XCIFileTrimmer + { + private const long BytesInAMegabyte = 1024 * 1024; + private const int BufferSize = 8 * (int)BytesInAMegabyte; + + private const long CartSizeMBinFormattedGB = 952; + private const int CartKeyAreaSize = 0x1000; + private const byte PaddingByte = 0xFF; + private const int HeaderFilePos = 0x100; + private const int CartSizeFilePos = 0x10D; + private const int DataSizeFilePos = 0x118; + private const string HeaderMagicValue = "HEAD"; + + /// + /// Cartridge Sizes (ByteIdentifier, SizeInGB) + /// + private static readonly Dictionary _cartSizesGB = new() + { + { 0xFA, 1 }, + { 0xF8, 2 }, + { 0xF0, 4 }, + { 0xE0, 8 }, + { 0xE1, 16 }, + { 0xE2, 32 } + }; + + private static long RecordsToByte(long records) + { + return 512 + (records * 512); + } + + public static bool CanTrim(string filename, ILog log = null) + { + if (Path.GetExtension(filename).Equals(".XCI", StringComparison.InvariantCultureIgnoreCase)) + { + var trimmer = new XCIFileTrimmer(filename, log); + return trimmer.CanBeTrimmed; + } + + return false; + } + + public static bool CanUntrim(string filename, ILog log = null) + { + if (Path.GetExtension(filename).Equals(".XCI", StringComparison.InvariantCultureIgnoreCase)) + { + var trimmer = new XCIFileTrimmer(filename, log); + return trimmer.CanBeUntrimmed; + } + + return false; + } + + private ILog _log; + private string _filename; + private FileStream _fileStream; + private BinaryReader _binaryReader; + private long _offsetB, _dataSizeB, _cartSizeB, _fileSizeB; + private bool _fileOK = true; + private bool _freeSpaceChecked = false; + private bool _freeSpaceValid = false; + + public enum OperationOutcome + { + Undetermined, + InvalidXCIFile, + NoTrimNecessary, + NoUntrimPossible, + FreeSpaceCheckFailed, + FileIOWriteError, + ReadOnlyFileCannotFix, + FileSizeChanged, + Successful, + Cancelled + } + + public enum LogType + { + Info, + Warn, + Error, + Progress + } + + public interface ILog + { + public void Write(LogType logType, string text); + public void Progress(long current, long total, string text, bool complete); + } + + public bool FileOK => _fileOK; + public bool Trimmed => _fileOK && FileSizeB < UntrimmedFileSizeB; + public bool ContainsKeyArea => _offsetB != 0; + public bool CanBeTrimmed => _fileOK && FileSizeB > TrimmedFileSizeB; + public bool CanBeUntrimmed => _fileOK && FileSizeB < UntrimmedFileSizeB; + public bool FreeSpaceChecked => _fileOK && _freeSpaceChecked; + public bool FreeSpaceValid => _fileOK && _freeSpaceValid; + public long DataSizeB => _dataSizeB; + public long CartSizeB => _cartSizeB; + public long FileSizeB => _fileSizeB; + public long DiskSpaceSavedB => CartSizeB - FileSizeB; + public long DiskSpaceSavingsB => CartSizeB - DataSizeB; + public long TrimmedFileSizeB => _offsetB + _dataSizeB; + public long UntrimmedFileSizeB => _offsetB + _cartSizeB; + + public ILog Log + { + get => _log; + set => _log = value; + } + + public String Filename + { + get => _filename; + set + { + _filename = value; + Reset(); + } + } + + public long Pos + { + get => _fileStream.Position; + set => _fileStream.Position = value; + } + + public XCIFileTrimmer(string path, ILog log = null) + { + Log = log; + Filename = path; + ReadHeader(); + } + + public void CheckFreeSpace(CancellationToken? cancelToken = null) + { + if (FreeSpaceChecked) + return; + + try + { + if (CanBeTrimmed) + { + _freeSpaceValid = false; + + OpenReaders(); + + try + { + Pos = TrimmedFileSizeB; + bool freeSpaceValid = true; + long readSizeB = FileSizeB - TrimmedFileSizeB; + + Stopwatch timedSw = Lambda.Timed(() => + { + freeSpaceValid = CheckPadding(readSizeB, cancelToken); + }); + + if (timedSw.Elapsed.TotalSeconds > 0) + { + Log?.Write(LogType.Info, $"Checked at {readSizeB / (double)XCIFileTrimmer.BytesInAMegabyte / timedSw.Elapsed.TotalSeconds:N} Mb/sec"); + } + + if (freeSpaceValid) + Log?.Write(LogType.Info, "Free space is valid"); + + _freeSpaceValid = freeSpaceValid; + } + finally + { + CloseReaders(); + } + + } + else + { + Log?.Write(LogType.Warn, "There is no free space to check."); + _freeSpaceValid = false; + } + } + finally + { + _freeSpaceChecked = true; + } + } + + private bool CheckPadding(long readSizeB, CancellationToken? cancelToken = null) + { + long maxReads = readSizeB / XCIFileTrimmer.BufferSize; + long read = 0; + var buffer = new byte[BufferSize]; + + while (true) + { + if (cancelToken.HasValue && cancelToken.Value.IsCancellationRequested) + { + return false; + } + + int bytes = _fileStream.Read(buffer, 0, XCIFileTrimmer.BufferSize); + if (bytes == 0) + break; + + Log?.Progress(read, maxReads, "Verifying file can be trimmed", false); + if (buffer.Take(bytes).AsParallel().Any(b => b != XCIFileTrimmer.PaddingByte)) + { + Log?.Write(LogType.Warn, "Free space is NOT valid"); + return false; + } + + read++; + } + + return true; + } + + private void Reset() + { + _freeSpaceChecked = false; + _freeSpaceValid = false; + ReadHeader(); + } + + public OperationOutcome Trim(CancellationToken? cancelToken = null) + { + if (!FileOK) + { + return OperationOutcome.InvalidXCIFile; + } + + if (!CanBeTrimmed) + { + return OperationOutcome.NoTrimNecessary; + } + + if (!FreeSpaceChecked) + { + CheckFreeSpace(cancelToken); + } + + if (!FreeSpaceValid) + { + if (cancelToken.HasValue && cancelToken.Value.IsCancellationRequested) + { + return OperationOutcome.Cancelled; + } + else + { + return OperationOutcome.FreeSpaceCheckFailed; + } + } + + Log?.Write(LogType.Info, "Trimming..."); + + try + { + var info = new FileInfo(Filename); + if ((info.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly) + { + try + { + Log?.Write(LogType.Info, "Attempting to remove ReadOnly attribute"); + File.SetAttributes(Filename, info.Attributes & ~FileAttributes.ReadOnly); + } + catch (Exception e) + { + Log?.Write(LogType.Error, e.ToString()); + return OperationOutcome.ReadOnlyFileCannotFix; + } + } + + if (info.Length != FileSizeB) + { + Log?.Write(LogType.Error, "File size has changed, cannot safely trim."); + return OperationOutcome.FileSizeChanged; + } + + var outfileStream = new FileStream(_filename, FileMode.Open, FileAccess.Write, FileShare.Write); + + try + { + +#if !XCI_TRIMMER_READ_ONLY_MODE + outfileStream.SetLength(TrimmedFileSizeB); +#endif + return OperationOutcome.Successful; + } + finally + { + outfileStream.Close(); + Reset(); + } + } + catch (Exception e) + { + Log?.Write(LogType.Error, e.ToString()); + return OperationOutcome.FileIOWriteError; + } + } + + public OperationOutcome Untrim(CancellationToken? cancelToken = null) + { + if (!FileOK) + { + return OperationOutcome.InvalidXCIFile; + } + + if (!CanBeUntrimmed) + { + return OperationOutcome.NoUntrimPossible; + } + + try + { + Log?.Write(LogType.Info, "Untrimming..."); + + var info = new FileInfo(Filename); + if ((info.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly) + { + try + { + Log?.Write(LogType.Info, "Attempting to remove ReadOnly attribute"); + File.SetAttributes(Filename, info.Attributes & ~FileAttributes.ReadOnly); + } + catch (Exception e) + { + Log?.Write(LogType.Error, e.ToString()); + return OperationOutcome.ReadOnlyFileCannotFix; + } + } + + if (info.Length != FileSizeB) + { + Log?.Write(LogType.Error, "File size has changed, cannot safely untrim."); + return OperationOutcome.FileSizeChanged; + } + + var outfileStream = new FileStream(_filename, FileMode.Append, FileAccess.Write, FileShare.Write); + long bytesToWriteB = UntrimmedFileSizeB - FileSizeB; + + try + { + Stopwatch timedSw = Lambda.Timed(() => + { + WritePadding(outfileStream, bytesToWriteB, cancelToken); + }); + + if (timedSw.Elapsed.TotalSeconds > 0) + { + Log?.Write(LogType.Info, $"Wrote at {bytesToWriteB / (double)XCIFileTrimmer.BytesInAMegabyte / timedSw.Elapsed.TotalSeconds:N} Mb/sec"); + } + + if (cancelToken.HasValue && cancelToken.Value.IsCancellationRequested) + { + return OperationOutcome.Cancelled; + } + else + { + return OperationOutcome.Successful; + } + } + finally + { + outfileStream.Close(); + Reset(); + } + } + catch (Exception e) + { + Log?.Write(LogType.Error, e.ToString()); + return OperationOutcome.FileIOWriteError; + } + } + + private void WritePadding(FileStream outfileStream, long bytesToWriteB, CancellationToken? cancelToken = null) + { + long bytesLeftToWriteB = bytesToWriteB; + long writes = bytesLeftToWriteB / XCIFileTrimmer.BufferSize; + int write = 0; + + try + { + var buffer = new byte[BufferSize]; + Array.Fill(buffer, XCIFileTrimmer.PaddingByte); + + while (bytesLeftToWriteB > 0) + { + if (cancelToken.HasValue && cancelToken.Value.IsCancellationRequested) + { + return; + } + + long bytesToWrite = Math.Min(XCIFileTrimmer.BufferSize, bytesLeftToWriteB); + +#if !XCI_TRIMMER_READ_ONLY_MODE + outfileStream.Write(buffer, 0, (int)bytesToWrite); +#endif + + bytesLeftToWriteB -= bytesToWrite; + Log?.Progress(write, writes, "Writing padding data...", false); + write++; + } + } + finally + { + Log?.Progress(write, writes, "Writing padding data...", true); + } + } + + private void OpenReaders() + { + if (_binaryReader == null) + { + _fileStream = new FileStream(_filename, FileMode.Open, FileAccess.Read, FileShare.Read); + _binaryReader = new BinaryReader(_fileStream); + } + } + + private void CloseReaders() + { + if (_binaryReader != null && _binaryReader.BaseStream != null) + _binaryReader.Close(); + _binaryReader = null; + _fileStream = null; + GC.Collect(); + } + + private void ReadHeader() + { + try + { + OpenReaders(); + + try + { + // Attempt without key area + bool success = CheckAndReadHeader(false); + + if (!success) + { + // Attempt with key area + success = CheckAndReadHeader(true); + } + + _fileOK = success; + } + finally + { + CloseReaders(); + } + } + catch (Exception ex) + { + Log?.Write(LogType.Error, ex.Message); + _fileOK = false; + _dataSizeB = 0; + _cartSizeB = 0; + _fileSizeB = 0; + _offsetB = 0; + } + } + + private bool CheckAndReadHeader(bool assumeKeyArea) + { + // Read file size + _fileSizeB = _fileStream.Length; + if (_fileSizeB < 32 * 1024) + { + Log?.Write(LogType.Error, "The source file doesn't look like an XCI file as the data size is too small"); + return false; + } + + // Setup offset + _offsetB = (long)(assumeKeyArea ? XCIFileTrimmer.CartKeyAreaSize : 0); + + // Check header + Pos = _offsetB + XCIFileTrimmer.HeaderFilePos; + string head = System.Text.Encoding.ASCII.GetString(_binaryReader.ReadBytes(4)); + if (head != XCIFileTrimmer.HeaderMagicValue) + { + if (!assumeKeyArea) + { + Log?.Write(LogType.Warn, $"Incorrect header found, file mat contain a key area..."); + } + else + { + Log?.Write(LogType.Error, "The source file doesn't look like an XCI file as the header is corrupted"); + } + + return false; + } + + // Read Cart Size + Pos = _offsetB + XCIFileTrimmer.CartSizeFilePos; + byte cartSizeId = _binaryReader.ReadByte(); + if (!_cartSizesGB.TryGetValue(cartSizeId, out long cartSizeNGB)) + { + Log?.Write(LogType.Error, $"The source file doesn't look like an XCI file as the Cartridge Size is incorrect (0x{cartSizeId:X2})"); + return false; + } + _cartSizeB = cartSizeNGB * XCIFileTrimmer.CartSizeMBinFormattedGB * XCIFileTrimmer.BytesInAMegabyte; + + // Read data size + Pos = _offsetB + XCIFileTrimmer.DataSizeFilePos; + long records = (long)BitConverter.ToUInt32(_binaryReader.ReadBytes(4), 0); + _dataSizeB = RecordsToByte(records); + + return true; + } + } +} diff --git a/src/Ryujinx.Common/XXHash128.cs b/src/Ryujinx.Common/XXHash128.cs deleted file mode 100644 index 686867c9d..000000000 --- a/src/Ryujinx.Common/XXHash128.cs +++ /dev/null @@ -1,548 +0,0 @@ -using System; -using System.Buffers.Binary; -using System.Diagnostics; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; - -namespace Ryujinx.Common -{ - public static class XXHash128 - { - private const int StripeLen = 64; - private const int AccNb = StripeLen / sizeof(ulong); - private const int SecretConsumeRate = 8; - private const int SecretLastAccStart = 7; - private const int SecretMergeAccsStart = 11; - private const int SecretSizeMin = 136; - private const int MidSizeStartOffset = 3; - private const int MidSizeLastOffset = 17; - - private const uint Prime32_1 = 0x9E3779B1U; - private const uint Prime32_2 = 0x85EBCA77U; - private const uint Prime32_3 = 0xC2B2AE3DU; - private const uint Prime32_4 = 0x27D4EB2FU; - private const uint Prime32_5 = 0x165667B1U; - - private const ulong Prime64_1 = 0x9E3779B185EBCA87UL; - private const ulong Prime64_2 = 0xC2B2AE3D27D4EB4FUL; - private const ulong Prime64_3 = 0x165667B19E3779F9UL; - private const ulong Prime64_4 = 0x85EBCA77C2B2AE63UL; - private const ulong Prime64_5 = 0x27D4EB2F165667C5UL; - - private static readonly ulong[] _xxh3InitAcc = { - Prime32_3, - Prime64_1, - Prime64_2, - Prime64_3, - Prime64_4, - Prime32_2, - Prime64_5, - Prime32_1, - }; - - private static ReadOnlySpan Xxh3KSecret => new byte[] - { - 0xb8, 0xfe, 0x6c, 0x39, 0x23, 0xa4, 0x4b, 0xbe, 0x7c, 0x01, 0x81, 0x2c, 0xf7, 0x21, 0xad, 0x1c, - 0xde, 0xd4, 0x6d, 0xe9, 0x83, 0x90, 0x97, 0xdb, 0x72, 0x40, 0xa4, 0xa4, 0xb7, 0xb3, 0x67, 0x1f, - 0xcb, 0x79, 0xe6, 0x4e, 0xcc, 0xc0, 0xe5, 0x78, 0x82, 0x5a, 0xd0, 0x7d, 0xcc, 0xff, 0x72, 0x21, - 0xb8, 0x08, 0x46, 0x74, 0xf7, 0x43, 0x24, 0x8e, 0xe0, 0x35, 0x90, 0xe6, 0x81, 0x3a, 0x26, 0x4c, - 0x3c, 0x28, 0x52, 0xbb, 0x91, 0xc3, 0x00, 0xcb, 0x88, 0xd0, 0x65, 0x8b, 0x1b, 0x53, 0x2e, 0xa3, - 0x71, 0x64, 0x48, 0x97, 0xa2, 0x0d, 0xf9, 0x4e, 0x38, 0x19, 0xef, 0x46, 0xa9, 0xde, 0xac, 0xd8, - 0xa8, 0xfa, 0x76, 0x3f, 0xe3, 0x9c, 0x34, 0x3f, 0xf9, 0xdc, 0xbb, 0xc7, 0xc7, 0x0b, 0x4f, 0x1d, - 0x8a, 0x51, 0xe0, 0x4b, 0xcd, 0xb4, 0x59, 0x31, 0xc8, 0x9f, 0x7e, 0xc9, 0xd9, 0x78, 0x73, 0x64, - 0xea, 0xc5, 0xac, 0x83, 0x34, 0xd3, 0xeb, 0xc3, 0xc5, 0x81, 0xa0, 0xff, 0xfa, 0x13, 0x63, 0xeb, - 0x17, 0x0d, 0xdd, 0x51, 0xb7, 0xf0, 0xda, 0x49, 0xd3, 0x16, 0x55, 0x26, 0x29, 0xd4, 0x68, 0x9e, - 0x2b, 0x16, 0xbe, 0x58, 0x7d, 0x47, 0xa1, 0xfc, 0x8f, 0xf8, 0xb8, 0xd1, 0x7a, 0xd0, 0x31, 0xce, - 0x45, 0xcb, 0x3a, 0x8f, 0x95, 0x16, 0x04, 0x28, 0xaf, 0xd7, 0xfb, 0xca, 0xbb, 0x4b, 0x40, 0x7e, - }; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ulong Mult32To64(ulong x, ulong y) - { - return (uint)x * (ulong)(uint)y; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Hash128 Mult64To128(ulong lhs, ulong rhs) - { - ulong high = Math.BigMul(lhs, rhs, out ulong low); - - return new Hash128 - { - Low = low, - High = high, - }; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ulong Mul128Fold64(ulong lhs, ulong rhs) - { - Hash128 product = Mult64To128(lhs, rhs); - - return product.Low ^ product.High; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ulong XorShift64(ulong v64, int shift) - { - Debug.Assert(0 <= shift && shift < 64); - - return v64 ^ (v64 >> shift); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ulong Xxh3Avalanche(ulong h64) - { - h64 = XorShift64(h64, 37); - h64 *= 0x165667919E3779F9UL; - h64 = XorShift64(h64, 32); - - return h64; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ulong Xxh64Avalanche(ulong h64) - { - h64 ^= h64 >> 33; - h64 *= Prime64_2; - h64 ^= h64 >> 29; - h64 *= Prime64_3; - h64 ^= h64 >> 32; - - return h64; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private unsafe static void Xxh3Accumulate512(Span acc, ReadOnlySpan input, ReadOnlySpan secret) - { - if (Avx2.IsSupported) - { - fixed (ulong* pAcc = acc) - { - fixed (byte* pInput = input, pSecret = secret) - { - Vector256* xAcc = (Vector256*)pAcc; - Vector256* xInput = (Vector256*)pInput; - Vector256* xSecret = (Vector256*)pSecret; - - for (ulong i = 0; i < StripeLen / 32; i++) - { - Vector256 dataVec = xInput[i]; - Vector256 keyVec = xSecret[i]; - Vector256 dataKey = Avx2.Xor(dataVec, keyVec); - Vector256 dataKeyLo = Avx2.Shuffle(dataKey.AsUInt32(), 0b00110001); - Vector256 product = Avx2.Multiply(dataKey.AsUInt32(), dataKeyLo); - Vector256 dataSwap = Avx2.Shuffle(dataVec.AsUInt32(), 0b01001110); - Vector256 sum = Avx2.Add(xAcc[i], dataSwap.AsUInt64()); - xAcc[i] = Avx2.Add(product, sum); - } - } - } - } - else if (Sse2.IsSupported) - { - fixed (ulong* pAcc = acc) - { - fixed (byte* pInput = input, pSecret = secret) - { - Vector128* xAcc = (Vector128*)pAcc; - Vector128* xInput = (Vector128*)pInput; - Vector128* xSecret = (Vector128*)pSecret; - - for (ulong i = 0; i < StripeLen / 16; i++) - { - Vector128 dataVec = xInput[i]; - Vector128 keyVec = xSecret[i]; - Vector128 dataKey = Sse2.Xor(dataVec, keyVec); - Vector128 dataKeyLo = Sse2.Shuffle(dataKey.AsUInt32(), 0b00110001); - Vector128 product = Sse2.Multiply(dataKey.AsUInt32(), dataKeyLo); - Vector128 dataSwap = Sse2.Shuffle(dataVec.AsUInt32(), 0b01001110); - Vector128 sum = Sse2.Add(xAcc[i], dataSwap.AsUInt64()); - xAcc[i] = Sse2.Add(product, sum); - } - } - } - } - else - { - for (int i = 0; i < AccNb; i++) - { - ulong dataVal = BinaryPrimitives.ReadUInt64LittleEndian(input[(i * sizeof(ulong))..]); - ulong dataKey = dataVal ^ BinaryPrimitives.ReadUInt64LittleEndian(secret[(i * sizeof(ulong))..]); - acc[i ^ 1] += dataVal; - acc[i] += Mult32To64((uint)dataKey, dataKey >> 32); - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private unsafe static void Xxh3ScrambleAcc(Span acc, ReadOnlySpan secret) - { - if (Avx2.IsSupported) - { - fixed (ulong* pAcc = acc) - { - fixed (byte* pSecret = secret) - { - Vector256 prime32 = Vector256.Create(Prime32_1); - Vector256* xAcc = (Vector256*)pAcc; - Vector256* xSecret = (Vector256*)pSecret; - - for (ulong i = 0; i < StripeLen / 32; i++) - { - Vector256 accVec = xAcc[i]; - Vector256 shifted = Avx2.ShiftRightLogical(accVec, 47); - Vector256 dataVec = Avx2.Xor(accVec, shifted); - - Vector256 keyVec = xSecret[i]; - Vector256 dataKey = Avx2.Xor(dataVec.AsUInt32(), keyVec.AsUInt32()); - - Vector256 dataKeyHi = Avx2.Shuffle(dataKey.AsUInt32(), 0b00110001); - Vector256 prodLo = Avx2.Multiply(dataKey, prime32); - Vector256 prodHi = Avx2.Multiply(dataKeyHi, prime32); - - xAcc[i] = Avx2.Add(prodLo, Avx2.ShiftLeftLogical(prodHi, 32)); - } - } - } - } - else if (Sse2.IsSupported) - { - fixed (ulong* pAcc = acc) - { - fixed (byte* pSecret = secret) - { - Vector128 prime32 = Vector128.Create(Prime32_1); - Vector128* xAcc = (Vector128*)pAcc; - Vector128* xSecret = (Vector128*)pSecret; - - for (ulong i = 0; i < StripeLen / 16; i++) - { - Vector128 accVec = xAcc[i]; - Vector128 shifted = Sse2.ShiftRightLogical(accVec, 47); - Vector128 dataVec = Sse2.Xor(accVec, shifted); - - Vector128 keyVec = xSecret[i]; - Vector128 dataKey = Sse2.Xor(dataVec.AsUInt32(), keyVec.AsUInt32()); - - Vector128 dataKeyHi = Sse2.Shuffle(dataKey.AsUInt32(), 0b00110001); - Vector128 prodLo = Sse2.Multiply(dataKey, prime32); - Vector128 prodHi = Sse2.Multiply(dataKeyHi, prime32); - - xAcc[i] = Sse2.Add(prodLo, Sse2.ShiftLeftLogical(prodHi, 32)); - } - } - } - } - else - { - for (int i = 0; i < AccNb; i++) - { - ulong key64 = BinaryPrimitives.ReadUInt64LittleEndian(secret[(i * sizeof(ulong))..]); - ulong acc64 = acc[i]; - acc64 = XorShift64(acc64, 47); - acc64 ^= key64; - acc64 *= Prime32_1; - acc[i] = acc64; - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void Xxh3Accumulate(Span acc, ReadOnlySpan input, ReadOnlySpan secret, int nbStripes) - { - for (int n = 0; n < nbStripes; n++) - { - ReadOnlySpan inData = input[(n * StripeLen)..]; - Xxh3Accumulate512(acc, inData, secret[(n * SecretConsumeRate)..]); - } - } - - private static void Xxh3HashLongInternalLoop(Span acc, ReadOnlySpan input, ReadOnlySpan secret) - { - int nbStripesPerBlock = (secret.Length - StripeLen) / SecretConsumeRate; - int blockLen = StripeLen * nbStripesPerBlock; - int nbBlocks = (input.Length - 1) / blockLen; - - Debug.Assert(secret.Length >= SecretSizeMin); - - for (int n = 0; n < nbBlocks; n++) - { - Xxh3Accumulate(acc, input[(n * blockLen)..], secret, nbStripesPerBlock); - Xxh3ScrambleAcc(acc, secret[^StripeLen..]); - } - - Debug.Assert(input.Length > StripeLen); - - int nbStripes = (input.Length - 1 - (blockLen * nbBlocks)) / StripeLen; - Debug.Assert(nbStripes <= (secret.Length / SecretConsumeRate)); - Xxh3Accumulate(acc, input[(nbBlocks * blockLen)..], secret, nbStripes); - - ReadOnlySpan p = input[^StripeLen..]; - Xxh3Accumulate512(acc, p, secret[(secret.Length - StripeLen - SecretLastAccStart)..]); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ulong Xxh3Mix2Accs(Span acc, ReadOnlySpan secret) - { - return Mul128Fold64( - acc[0] ^ BinaryPrimitives.ReadUInt64LittleEndian(secret), - acc[1] ^ BinaryPrimitives.ReadUInt64LittleEndian(secret[8..])); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ulong Xxh3MergeAccs(Span acc, ReadOnlySpan secret, ulong start) - { - ulong result64 = start; - - for (int i = 0; i < 4; i++) - { - result64 += Xxh3Mix2Accs(acc[(2 * i)..], secret[(16 * i)..]); - } - - return Xxh3Avalanche(result64); - } - - [SkipLocalsInit] - private static Hash128 Xxh3HashLong128bInternal(ReadOnlySpan input, ReadOnlySpan secret) - { - Span acc = stackalloc ulong[AccNb]; - _xxh3InitAcc.CopyTo(acc); - - Xxh3HashLongInternalLoop(acc, input, secret); - - Debug.Assert(acc.Length == 8); - Debug.Assert(secret.Length >= acc.Length * sizeof(ulong) + SecretMergeAccsStart); - - return new Hash128 - { - Low = Xxh3MergeAccs(acc, secret[SecretMergeAccsStart..], (ulong)input.Length * Prime64_1), - High = Xxh3MergeAccs( - acc, - secret[(secret.Length - acc.Length * sizeof(ulong) - SecretMergeAccsStart)..], - ~((ulong)input.Length * Prime64_2)), - }; - } - - private static Hash128 Xxh3Len1To3128b(ReadOnlySpan input, ReadOnlySpan secret, ulong seed) - { - Debug.Assert(1 <= input.Length && input.Length <= 3); - - byte c1 = input[0]; - byte c2 = input[input.Length >> 1]; - byte c3 = input[^1]; - - uint combinedL = ((uint)c1 << 16) | ((uint)c2 << 24) | c3 | ((uint)input.Length << 8); - uint combinedH = BitOperations.RotateLeft(BinaryPrimitives.ReverseEndianness(combinedL), 13); - ulong bitFlipL = (BinaryPrimitives.ReadUInt32LittleEndian(secret) ^ BinaryPrimitives.ReadUInt32LittleEndian(secret[4..])) + seed; - ulong bitFlipH = (BinaryPrimitives.ReadUInt32LittleEndian(secret[8..]) ^ BinaryPrimitives.ReadUInt32LittleEndian(secret[12..])) - seed; - ulong keyedLo = combinedL ^ bitFlipL; - ulong keyedHi = combinedH ^ bitFlipH; - - return new Hash128 - { - Low = Xxh64Avalanche(keyedLo), - High = Xxh64Avalanche(keyedHi), - }; - } - - private static Hash128 Xxh3Len4To8128b(ReadOnlySpan input, ReadOnlySpan secret, ulong seed) - { - Debug.Assert(4 <= input.Length && input.Length <= 8); - - seed ^= BinaryPrimitives.ReverseEndianness((uint)seed) << 32; - - uint inputLo = BinaryPrimitives.ReadUInt32LittleEndian(input); - uint inputHi = BinaryPrimitives.ReadUInt32LittleEndian(input[^4..]); - ulong input64 = inputLo + ((ulong)inputHi << 32); - ulong bitFlip = (BinaryPrimitives.ReadUInt64LittleEndian(secret[16..]) ^ BinaryPrimitives.ReadUInt64LittleEndian(secret[24..])) + seed; - ulong keyed = input64 ^ bitFlip; - - Hash128 m128 = Mult64To128(keyed, Prime64_1 + ((ulong)input.Length << 2)); - - m128.High += m128.Low << 1; - m128.Low ^= m128.High >> 3; - - m128.Low = XorShift64(m128.Low, 35); - m128.Low *= 0x9FB21C651E98DF25UL; - m128.Low = XorShift64(m128.Low, 28); - m128.High = Xxh3Avalanche(m128.High); - - return m128; - } - - private static Hash128 Xxh3Len9To16128b(ReadOnlySpan input, ReadOnlySpan secret, ulong seed) - { - Debug.Assert(9 <= input.Length && input.Length <= 16); - - ulong bitFlipL = (BinaryPrimitives.ReadUInt64LittleEndian(secret[32..]) ^ BinaryPrimitives.ReadUInt64LittleEndian(secret[40..])) - seed; - ulong bitFlipH = (BinaryPrimitives.ReadUInt64LittleEndian(secret[48..]) ^ BinaryPrimitives.ReadUInt64LittleEndian(secret[56..])) + seed; - ulong inputLo = BinaryPrimitives.ReadUInt64LittleEndian(input); - ulong inputHi = BinaryPrimitives.ReadUInt64LittleEndian(input[^8..]); - - Hash128 m128 = Mult64To128(inputLo ^ inputHi ^ bitFlipL, Prime64_1); - m128.Low += ((ulong)input.Length - 1) << 54; - inputHi ^= bitFlipH; - m128.High += inputHi + Mult32To64((uint)inputHi, Prime32_2 - 1); - m128.Low ^= BinaryPrimitives.ReverseEndianness(m128.High); - - Hash128 h128 = Mult64To128(m128.Low, Prime64_2); - h128.High += m128.High * Prime64_2; - h128.Low = Xxh3Avalanche(h128.Low); - h128.High = Xxh3Avalanche(h128.High); - - return h128; - } - - private static Hash128 Xxh3Len0To16128b(ReadOnlySpan input, ReadOnlySpan secret, ulong seed) - { - Debug.Assert(input.Length <= 16); - - if (input.Length > 8) - { - return Xxh3Len9To16128b(input, secret, seed); - } - - if (input.Length >= 4) - { - return Xxh3Len4To8128b(input, secret, seed); - } - - if (input.Length != 0) - { - return Xxh3Len1To3128b(input, secret, seed); - } - - Hash128 h128 = new(); - ulong bitFlipL = BinaryPrimitives.ReadUInt64LittleEndian(secret[64..]) ^ BinaryPrimitives.ReadUInt64LittleEndian(secret[72..]); - ulong bitFlipH = BinaryPrimitives.ReadUInt64LittleEndian(secret[80..]) ^ BinaryPrimitives.ReadUInt64LittleEndian(secret[88..]); - h128.Low = Xxh64Avalanche(seed ^ bitFlipL); - h128.High = Xxh64Avalanche(seed ^ bitFlipH); - - return h128; - } - - private static ulong Xxh3Mix16b(ReadOnlySpan input, ReadOnlySpan secret, ulong seed) - { - ulong inputLo = BinaryPrimitives.ReadUInt64LittleEndian(input); - ulong inputHi = BinaryPrimitives.ReadUInt64LittleEndian(input[8..]); - - return Mul128Fold64( - inputLo ^ (BinaryPrimitives.ReadUInt64LittleEndian(secret) + seed), - inputHi ^ (BinaryPrimitives.ReadUInt64LittleEndian(secret[8..]) - seed)); - } - - private static Hash128 Xxh128Mix32b(Hash128 acc, ReadOnlySpan input, ReadOnlySpan input2, ReadOnlySpan secret, ulong seed) - { - acc.Low += Xxh3Mix16b(input, secret, seed); - acc.Low ^= BinaryPrimitives.ReadUInt64LittleEndian(input2) + BinaryPrimitives.ReadUInt64LittleEndian(input2[8..]); - acc.High += Xxh3Mix16b(input2, secret[16..], seed); - acc.High ^= BinaryPrimitives.ReadUInt64LittleEndian(input) + BinaryPrimitives.ReadUInt64LittleEndian(input[8..]); - - return acc; - } - - private static Hash128 Xxh3Len17To128128b(ReadOnlySpan input, ReadOnlySpan secret, ulong seed) - { - Debug.Assert(secret.Length >= SecretSizeMin); - Debug.Assert(16 < input.Length && input.Length <= 128); - - Hash128 acc = new() - { - Low = (ulong)input.Length * Prime64_1, - High = 0, - }; - - if (input.Length > 32) - { - if (input.Length > 64) - { - if (input.Length > 96) - { - acc = Xxh128Mix32b(acc, input[48..], input[^64..], secret[96..], seed); - } - acc = Xxh128Mix32b(acc, input[32..], input[^48..], secret[64..], seed); - } - acc = Xxh128Mix32b(acc, input[16..], input[^32..], secret[32..], seed); - } - acc = Xxh128Mix32b(acc, input, input[^16..], secret, seed); - - Hash128 h128 = new() - { - Low = acc.Low + acc.High, - High = acc.Low * Prime64_1 + acc.High * Prime64_4 + ((ulong)input.Length - seed) * Prime64_2, - }; - h128.Low = Xxh3Avalanche(h128.Low); - h128.High = 0UL - Xxh3Avalanche(h128.High); - - return h128; - } - - private static Hash128 Xxh3Len129To240128b(ReadOnlySpan input, ReadOnlySpan secret, ulong seed) - { - Debug.Assert(secret.Length >= SecretSizeMin); - Debug.Assert(128 < input.Length && input.Length <= 240); - - Hash128 acc = new(); - - int nbRounds = input.Length / 32; - acc.Low = (ulong)input.Length * Prime64_1; - acc.High = 0; - - for (int i = 0; i < 4; i++) - { - acc = Xxh128Mix32b(acc, input[(32 * i)..], input[(32 * i + 16)..], secret[(32 * i)..], seed); - } - - acc.Low = Xxh3Avalanche(acc.Low); - acc.High = Xxh3Avalanche(acc.High); - Debug.Assert(nbRounds >= 4); - - for (int i = 4; i < nbRounds; i++) - { - acc = Xxh128Mix32b(acc, input[(32 * i)..], input[(32 * i + 16)..], secret[(MidSizeStartOffset + 32 * (i - 4))..], seed); - } - - acc = Xxh128Mix32b(acc, input[^16..], input[^32..], secret[(SecretSizeMin - MidSizeLastOffset - 16)..], 0UL - seed); - - Hash128 h128 = new() - { - Low = acc.Low + acc.High, - High = acc.Low * Prime64_1 + acc.High * Prime64_4 + ((ulong)input.Length - seed) * Prime64_2, - }; - h128.Low = Xxh3Avalanche(h128.Low); - h128.High = 0UL - Xxh3Avalanche(h128.High); - - return h128; - } - - private static Hash128 Xxh3128bitsInternal(ReadOnlySpan input, ReadOnlySpan secret, ulong seed) - { - Debug.Assert(secret.Length >= SecretSizeMin); - - if (input.Length <= 16) - { - return Xxh3Len0To16128b(input, secret, seed); - } - - if (input.Length <= 128) - { - return Xxh3Len17To128128b(input, secret, seed); - } - - if (input.Length <= 240) - { - return Xxh3Len129To240128b(input, secret, seed); - } - - return Xxh3HashLong128bInternal(input, secret); - } - - public static Hash128 ComputeHash(ReadOnlySpan input) - { - return Xxh3128bitsInternal(input, Xxh3KSecret, 0UL); - } - } -} diff --git a/src/Ryujinx.Cpu/AddressTable.cs b/src/Ryujinx.Cpu/AddressTable.cs new file mode 100644 index 000000000..038a2009c --- /dev/null +++ b/src/Ryujinx.Cpu/AddressTable.cs @@ -0,0 +1,482 @@ +using ARMeilleure.Memory; +using Ryujinx.Common; +using Ryujinx.Cpu.Signal; +using Ryujinx.Memory; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Threading; +using static Ryujinx.Cpu.MemoryEhMeilleure; + +namespace ARMeilleure.Common +{ + /// + /// Represents a table of guest address to a value. + /// + /// Type of the value + public unsafe class AddressTable : IAddressTable where TEntry : unmanaged + { + /// + /// Represents a page of the address table. + /// + private readonly struct AddressTablePage + { + /// + /// True if the allocation belongs to a sparse block, false otherwise. + /// + public readonly bool IsSparse; + + /// + /// Base address for the page. + /// + public readonly IntPtr Address; + + public AddressTablePage(bool isSparse, IntPtr address) + { + IsSparse = isSparse; + Address = address; + } + } + + /// + /// A sparsely mapped block of memory with a signal handler to map pages as they're accessed. + /// + private readonly struct TableSparseBlock : IDisposable + { + public readonly SparseMemoryBlock Block; + private readonly TrackingEventDelegate _trackingEvent; + + public TableSparseBlock(ulong size, Action ensureMapped, PageInitDelegate pageInit) + { + var block = new SparseMemoryBlock(size, pageInit, null); + + _trackingEvent = (ulong address, ulong size, bool write) => + { + ulong pointer = (ulong)block.Block.Pointer + address; + ensureMapped((IntPtr)pointer); + return pointer; + }; + + bool added = NativeSignalHandler.AddTrackedRegion( + (nuint)block.Block.Pointer, + (nuint)(block.Block.Pointer + (IntPtr)block.Block.Size), + Marshal.GetFunctionPointerForDelegate(_trackingEvent)); + + if (!added) + { + throw new InvalidOperationException("Number of allowed tracked regions exceeded."); + } + + Block = block; + } + + public void Dispose() + { + NativeSignalHandler.RemoveTrackedRegion((nuint)Block.Block.Pointer); + + Block.Dispose(); + } + } + + private bool _disposed; + private TEntry** _table; + private readonly List _pages; + private TEntry _fill; + + private readonly MemoryBlock _sparseFill; + private readonly SparseMemoryBlock _fillBottomLevel; + private readonly TEntry* _fillBottomLevelPtr; + + private readonly List _sparseReserved; + private readonly ReaderWriterLockSlim _sparseLock; + + private ulong _sparseBlockSize; + private ulong _sparseReservedOffset; + + public bool Sparse { get; } + + /// + public ulong Mask { get; } + + /// + public AddressTableLevel[] Levels { get; } + + /// + public TEntry Fill + { + get + { + return _fill; + } + set + { + UpdateFill(value); + } + } + + /// + public IntPtr Base + { + get + { + ObjectDisposedException.ThrowIf(_disposed, this); + + lock (_pages) + { + return (IntPtr)GetRootPage(); + } + } + } + + /// + /// Constructs a new instance of the class with the specified list of + /// . + /// + /// Levels for the address table + /// True if the bottom page should be sparsely mapped + /// is null + /// Length of is less than 2 + public AddressTable(AddressTableLevel[] levels, bool sparse) + { + ArgumentNullException.ThrowIfNull(levels); + + _pages = new List(capacity: 16); + + Levels = levels; + Mask = 0; + + foreach (var level in Levels) + { + Mask |= level.Mask; + } + + Sparse = sparse; + + if (sparse) + { + // If the address table is sparse, allocate a fill block + + _sparseFill = new MemoryBlock(268435456ul, MemoryAllocationFlags.Mirrorable); //low Power TC uses size: 65536ul + + ulong bottomLevelSize = (1ul << levels.Last().Length) * (ulong)sizeof(TEntry); + + _fillBottomLevel = new SparseMemoryBlock(bottomLevelSize, null, _sparseFill); + _fillBottomLevelPtr = (TEntry*)_fillBottomLevel.Block.Pointer; + + _sparseReserved = new List(); + _sparseLock = new ReaderWriterLockSlim(); + + _sparseBlockSize = bottomLevelSize; + } + } + + /// + /// Create an instance for an ARM function table. + /// Selects the best table structure for A32/A64, taking into account the selected memory manager type. + /// + /// True if the guest is A64, false otherwise + /// Memory manager type + /// An for ARM function lookup + public static AddressTable CreateForArm(bool for64Bits, MemoryManagerType type) + { + // Assume software memory means that we don't want to use any signal handlers. + bool sparse = type != MemoryManagerType.SoftwareMmu && type != MemoryManagerType.SoftwarePageTable; + + return new AddressTable(AddressTablePresets.GetArmPreset(for64Bits, sparse), sparse); + } + + /// + /// Update the fill value for the bottom level of the table. + /// + /// New fill value + private void UpdateFill(TEntry fillValue) + { + if (_sparseFill != null) + { + Span span = _sparseFill.GetSpan(0, (int)_sparseFill.Size); + MemoryMarshal.Cast(span).Fill(fillValue); + } + + _fill = fillValue; + } + + /// + /// Signal that the given code range exists. + /// + /// + /// + public void SignalCodeRange(ulong address, ulong size) + { + AddressTableLevel bottom = Levels.Last(); + ulong bottomLevelEntries = 1ul << bottom.Length; + + ulong entryIndex = address >> bottom.Index; + ulong entries = size >> bottom.Index; + entries += entryIndex - BitUtils.AlignDown(entryIndex, bottomLevelEntries); + + _sparseBlockSize = Math.Max(_sparseBlockSize, BitUtils.AlignUp(entries, bottomLevelEntries) * (ulong)sizeof(TEntry)); + } + + /// + public bool IsValid(ulong address) + { + return (address & ~Mask) == 0; + } + + /// + public ref TEntry GetValue(ulong address) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + if (!IsValid(address)) + { + throw new ArgumentException($"Address 0x{address:X} is not mapped onto the table.", nameof(address)); + } + + lock (_pages) + { + TEntry* page = GetPage(address); + + long index = Levels[^1].GetValue(address); + + EnsureMapped((IntPtr)(page + index)); + + return ref page[index]; + } + } + + /// + /// Gets the leaf page for the specified guest . + /// + /// Guest address + /// Leaf page for the specified guest + private TEntry* GetPage(ulong address) + { + TEntry** page = GetRootPage(); + + for (int i = 0; i < Levels.Length - 1; i++) + { + ref AddressTableLevel level = ref Levels[i]; + ref TEntry* nextPage = ref page[level.GetValue(address)]; + + if (nextPage == null || nextPage == _fillBottomLevelPtr) + { + ref AddressTableLevel nextLevel = ref Levels[i + 1]; + + if (i == Levels.Length - 2) + { + nextPage = (TEntry*)Allocate(1 << nextLevel.Length, Fill, leaf: true); + } + else + { + nextPage = (TEntry*)Allocate(1 << nextLevel.Length, GetFillValue(i), leaf: false); + } + } + + page = (TEntry**)nextPage; + } + + return (TEntry*)page; + } + + /// + /// Ensure the given pointer is mapped in any overlapping sparse reservations. + /// + /// Pointer to be mapped + private void EnsureMapped(IntPtr ptr) + { + if (Sparse) + { + // Check sparse allocations to see if the pointer is in any of them. + // Ensure the page is committed if there's a match. + + _sparseLock.EnterReadLock(); + + try + { + foreach (TableSparseBlock reserved in _sparseReserved) + { + SparseMemoryBlock sparse = reserved.Block; + + if (ptr >= sparse.Block.Pointer && ptr < sparse.Block.Pointer + (IntPtr)sparse.Block.Size) + { + sparse.EnsureMapped((ulong)(ptr - sparse.Block.Pointer)); + + break; + } + } + } + finally + { + _sparseLock.ExitReadLock(); + } + } + } + + /// + /// Get the fill value for a non-leaf level of the table. + /// + /// Level to get the fill value for + /// The fill value + private IntPtr GetFillValue(int level) + { + if (_fillBottomLevel != null && level == Levels.Length - 2) + { + return (IntPtr)_fillBottomLevelPtr; + } + else + { + return IntPtr.Zero; + } + } + + /// + /// Lazily initialize and get the root page of the . + /// + /// Root page of the + private TEntry** GetRootPage() + { + if (_table == null) + { + if (Levels.Length == 1) + _table = (TEntry**)Allocate(1 << Levels[0].Length, Fill, leaf: true); + else + _table = (TEntry**)Allocate(1 << Levels[0].Length, GetFillValue(0), leaf: false); + } + + return _table; + } + + /// + /// Initialize a leaf page with the fill value. + /// + /// Page to initialize + private void InitLeafPage(Span page) + { + MemoryMarshal.Cast(page).Fill(_fill); + } + + /// + /// Reserve a new sparse block, and add it to the list. + /// + /// The new sparse block that was added + private TableSparseBlock ReserveNewSparseBlock() + { + var block = new TableSparseBlock(_sparseBlockSize, EnsureMapped, InitLeafPage); + + _sparseReserved.Add(block); + _sparseReservedOffset = 0; + + return block; + } + + /// + /// Allocates a block of memory of the specified type and length. + /// + /// Type of elements + /// Number of elements + /// Fill value + /// if leaf; otherwise + /// Allocated block + private IntPtr Allocate(int length, T fill, bool leaf) where T : unmanaged + { + var size = sizeof(T) * length; + + AddressTablePage page; + + if (Sparse && leaf) + { + _sparseLock.EnterWriteLock(); + + SparseMemoryBlock block; + + if (_sparseReserved.Count == 0) + { + block = ReserveNewSparseBlock().Block; + } + else + { + block = _sparseReserved.Last().Block; + + if (_sparseReservedOffset == block.Block.Size) + { + block = ReserveNewSparseBlock().Block; + } + } + + page = new AddressTablePage(true, block.Block.Pointer + (IntPtr)_sparseReservedOffset); + + _sparseReservedOffset += (ulong)size; + + _sparseLock.ExitWriteLock(); + } + else + { + var address = (IntPtr)NativeAllocator.Instance.Allocate((uint)size); + page = new AddressTablePage(false, address); + + var span = new Span((void*)page.Address, length); + span.Fill(fill); + } + + _pages.Add(page); + + //TranslatorEventSource.Log.AddressTableAllocated(size, leaf); + + return page.Address; + } + + /// + /// Releases all resources used by the instance. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Releases all unmanaged and optionally managed resources used by the + /// instance. + /// + /// to dispose managed resources also; otherwise just unmanaged resouces + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + foreach (var page in _pages) + { + if (!page.IsSparse) + { + Marshal.FreeHGlobal(page.Address); + } + } + + if (Sparse) + { + foreach (TableSparseBlock block in _sparseReserved) + { + block.Dispose(); + } + + _sparseReserved.Clear(); + + _fillBottomLevel.Dispose(); + _sparseFill.Dispose(); + _sparseLock.Dispose(); + } + + _disposed = true; + } + } + + /// + /// Frees resources used by the instance. + /// + ~AddressTable() + { + Dispose(false); + } + } +} diff --git a/src/Ryujinx.Cpu/AppleHv/HvApi.cs b/src/Ryujinx.Cpu/AppleHv/HvApi.cs index e6e08111f..864f6b063 100644 --- a/src/Ryujinx.Cpu/AppleHv/HvApi.cs +++ b/src/Ryujinx.Cpu/AppleHv/HvApi.cs @@ -273,7 +273,7 @@ namespace Ryujinx.Cpu.AppleHv public static partial HvResult hv_vm_get_max_vcpu_count(out uint max_vcpu_count); [LibraryImport(LibraryName, SetLastError = true)] - public static partial HvResult hv_vm_create(IntPtr config); + public static partial HvResult hv_vm_create(nint config); [LibraryImport(LibraryName, SetLastError = true)] public static partial HvResult hv_vm_destroy(); @@ -288,7 +288,7 @@ namespace Ryujinx.Cpu.AppleHv public static partial HvResult hv_vm_protect(ulong ipa, ulong size, HvMemoryFlags flags); [LibraryImport(LibraryName, SetLastError = true)] - public unsafe static partial HvResult hv_vcpu_create(out ulong vcpu, ref HvVcpuExit* exit, IntPtr config); + public unsafe static partial HvResult hv_vcpu_create(out ulong vcpu, ref HvVcpuExit* exit, nint config); [LibraryImport(LibraryName, SetLastError = true)] public unsafe static partial HvResult hv_vcpu_destroy(ulong vcpu); diff --git a/src/Ryujinx.Cpu/AppleHv/HvCpuContext.cs b/src/Ryujinx.Cpu/AppleHv/HvCpuContext.cs index 99e4c0479..784949441 100644 --- a/src/Ryujinx.Cpu/AppleHv/HvCpuContext.cs +++ b/src/Ryujinx.Cpu/AppleHv/HvCpuContext.cs @@ -32,7 +32,7 @@ namespace Ryujinx.Cpu.AppleHv { } - public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled) + public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector) { return new DummyDiskCacheLoadState(); } diff --git a/src/Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs b/src/Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs index bb232940d..9d459d062 100644 --- a/src/Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs +++ b/src/Ryujinx.Cpu/AppleHv/HvExecutionContextVcpu.cs @@ -10,9 +10,9 @@ namespace Ryujinx.Cpu.AppleHv class HvExecutionContextVcpu : IHvExecutionContext { private static readonly MemoryBlock _setSimdFpRegFuncMem; - private delegate HvResult SetSimdFpReg(ulong vcpu, HvSimdFPReg reg, in V128 value, IntPtr funcPtr); + private delegate HvResult SetSimdFpReg(ulong vcpu, HvSimdFPReg reg, in V128 value, nint funcPtr); private static readonly SetSimdFpReg _setSimdFpReg; - private static readonly IntPtr _setSimdFpRegNativePtr; + private static readonly nint _setSimdFpRegNativePtr; static HvExecutionContextVcpu() { @@ -25,7 +25,7 @@ namespace Ryujinx.Cpu.AppleHv _setSimdFpReg = Marshal.GetDelegateForFunctionPointer(_setSimdFpRegFuncMem.Pointer); - if (NativeLibrary.TryLoad(HvApi.LibraryName, out IntPtr hvLibHandle)) + if (NativeLibrary.TryLoad(HvApi.LibraryName, out nint hvLibHandle)) { _setSimdFpRegNativePtr = NativeLibrary.GetExport(hvLibHandle, nameof(HvApi.hv_vcpu_set_simd_fp_reg)); } diff --git a/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs b/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs index abdddb31c..74c39d6a8 100644 --- a/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs +++ b/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs @@ -32,7 +32,7 @@ namespace Ryujinx.Cpu.AppleHv public int AddressSpaceBits { get; } - public IntPtr PageTablePointer => IntPtr.Zero; + public nint PageTablePointer => nint.Zero; public MemoryManagerType Type => MemoryManagerType.SoftwarePageTable; @@ -230,25 +230,20 @@ namespace Ryujinx.Cpu.AppleHv { if (size == 0) { - return Enumerable.Empty(); + yield break; } var guestRegions = GetPhysicalRegionsImpl(va, size); if (guestRegions == null) { - return null; + yield break; } - var regions = new HostMemoryRange[guestRegions.Count]; - - for (int i = 0; i < regions.Length; i++) + foreach (var guestRegion in guestRegions) { - var guestRegion = guestRegions[i]; - IntPtr pointer = _backingMemory.GetPointer(guestRegion.Address, guestRegion.Size); - regions[i] = new HostMemoryRange((nuint)(ulong)pointer, guestRegion.Size); + nint pointer = _backingMemory.GetPointer(guestRegion.Address, guestRegion.Size); + yield return new HostMemoryRange((nuint)(ulong)pointer, guestRegion.Size); } - - return regions; } /// @@ -256,23 +251,24 @@ namespace Ryujinx.Cpu.AppleHv { if (size == 0) { - return Enumerable.Empty(); + yield break; } - return GetPhysicalRegionsImpl(va, size); + foreach (var physicalRegion in GetPhysicalRegionsImpl(va, size)) + { + yield return physicalRegion; + } } - private List GetPhysicalRegionsImpl(ulong va, ulong size) + private IEnumerable GetPhysicalRegionsImpl(ulong va, ulong size) { if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size)) { - return null; + yield break; } int pages = GetPagesCount(va, (uint)size, out va); - var regions = new List(); - ulong regionStart = GetPhysicalAddressInternal(va); ulong regionSize = PageSize; @@ -280,14 +276,14 @@ namespace Ryujinx.Cpu.AppleHv { if (!ValidateAddress(va + PageSize)) { - return null; + yield break; } ulong newPa = GetPhysicalAddressInternal(va + PageSize); if (GetPhysicalAddressInternal(va) + PageSize != newPa) { - regions.Add(new MemoryRange(regionStart, regionSize)); + yield return new MemoryRange(regionStart, regionSize); regionStart = newPa; regionSize = 0; } @@ -296,9 +292,7 @@ namespace Ryujinx.Cpu.AppleHv regionSize += PageSize; } - regions.Add(new MemoryRange(regionStart, regionSize)); - - return regions; + yield return new MemoryRange(regionStart, regionSize); } /// diff --git a/src/Ryujinx.Cpu/AppleHv/HvVcpuPool.cs b/src/Ryujinx.Cpu/AppleHv/HvVcpuPool.cs index 2edcd7e4e..af124fc7a 100644 --- a/src/Ryujinx.Cpu/AppleHv/HvVcpuPool.cs +++ b/src/Ryujinx.Cpu/AppleHv/HvVcpuPool.cs @@ -72,7 +72,7 @@ namespace Ryujinx.Cpu.AppleHv // Create VCPU. HvVcpuExit* exitInfo = null; - HvApi.hv_vcpu_create(out ulong vcpuHandle, ref exitInfo, IntPtr.Zero).ThrowOnError(); + HvApi.hv_vcpu_create(out ulong vcpuHandle, ref exitInfo, nint.Zero).ThrowOnError(); // Enable FP and SIMD instructions. HvApi.hv_vcpu_set_sys_reg(vcpuHandle, HvSysReg.CPACR_EL1, 0b11 << 20).ThrowOnError(); diff --git a/src/Ryujinx.Cpu/AppleHv/HvVm.cs b/src/Ryujinx.Cpu/AppleHv/HvVm.cs index c4f107532..b4d45f36d 100644 --- a/src/Ryujinx.Cpu/AppleHv/HvVm.cs +++ b/src/Ryujinx.Cpu/AppleHv/HvVm.cs @@ -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) { @@ -22,7 +23,7 @@ namespace Ryujinx.Cpu.AppleHv { if (++_addressSpaces == 1) { - HvApi.hv_vm_create(IntPtr.Zero).ThrowOnError(); + HvApi.hv_vm_create(nint.Zero).ThrowOnError(); _ipaAllocator = ipaAllocator = new HvIpaAllocator(); } else diff --git a/src/Ryujinx.Cpu/ICpuContext.cs b/src/Ryujinx.Cpu/ICpuContext.cs index edcebdfc4..1fb3b674d 100644 --- a/src/Ryujinx.Cpu/ICpuContext.cs +++ b/src/Ryujinx.Cpu/ICpuContext.cs @@ -48,7 +48,7 @@ namespace Ryujinx.Cpu /// Version of the application /// True if the cache should be loaded from disk if it exists, false otherwise /// Disk cache load progress reporter and manager - IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled); + IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector); /// /// Indicates that code has been loaded into guest memory, and that it might be executed in the future. diff --git a/src/Ryujinx.Cpu/Jit/HostTracked/AddressSpacePartition.cs b/src/Ryujinx.Cpu/Jit/HostTracked/AddressSpacePartition.cs index 224c5edc3..f9743a0a1 100644 --- a/src/Ryujinx.Cpu/Jit/HostTracked/AddressSpacePartition.cs +++ b/src/Ryujinx.Cpu/Jit/HostTracked/AddressSpacePartition.cs @@ -307,7 +307,7 @@ namespace Ryujinx.Cpu.Jit.HostTracked ulong size, MemoryPermission protection, AddressSpacePartitioned addressSpace, - Action updatePtCallback) + Action updatePtCallback) { if (_baseMemory.LazyInitMirrorForProtection(addressSpace, Address, Size, protection)) { @@ -317,7 +317,7 @@ namespace Ryujinx.Cpu.Jit.HostTracked updatePtCallback(va, _baseMemory.GetPointerForProtection(va - Address, size, protection), size); } - public IntPtr GetPointer(ulong va, ulong size) + public nint GetPointer(ulong va, ulong size) { Debug.Assert(va >= Address); Debug.Assert(va + size <= EndAddress); diff --git a/src/Ryujinx.Cpu/Jit/HostTracked/AddressSpacePartitionAllocator.cs b/src/Ryujinx.Cpu/Jit/HostTracked/AddressSpacePartitionAllocator.cs index 44dedb640..f39b295cd 100644 --- a/src/Ryujinx.Cpu/Jit/HostTracked/AddressSpacePartitionAllocator.cs +++ b/src/Ryujinx.Cpu/Jit/HostTracked/AddressSpacePartitionAllocator.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Cpu.Jit.HostTracked private readonly AddressSpacePartitionAllocator _owner; private readonly PrivateMemoryAllocatorImpl.Allocation _allocation; - public IntPtr Pointer => (IntPtr)((ulong)_allocation.Block.Memory.Pointer + _allocation.Offset); + public nint Pointer => (nint)((ulong)_allocation.Block.Memory.Pointer + _allocation.Offset); public bool IsValid => _owner != null; @@ -43,7 +43,7 @@ namespace Ryujinx.Cpu.Jit.HostTracked _allocation.Block.Memory.Reprotect(_allocation.Offset + offset, size, permission, throwOnFail); } - public IntPtr GetPointer(ulong offset, ulong size) + public nint GetPointer(ulong offset, ulong size) { return _allocation.Block.Memory.GetPointer(_allocation.Offset + offset, size); } @@ -115,6 +115,9 @@ namespace Ryujinx.Cpu.Jit.HostTracked } private readonly AddressIntrusiveRedBlackTree _mappingTree; + + // type is not Lock due to the unique usage of this mechanism, + // an arbitrary object is used as the lock passed in by constructor. private readonly object _lock; public Block(MemoryTracking tracking, Func readPtCallback, MemoryBlock memory, ulong size, object locker) : base(memory, size) @@ -174,6 +177,9 @@ namespace Ryujinx.Cpu.Jit.HostTracked private readonly MemoryTracking _tracking; private readonly Func _readPtCallback; + + // type is not Lock due to the unique usage of this mechanism, + // an arbitrary object is used as the lock passed in by constructor. private readonly object _lock; public AddressSpacePartitionAllocator( diff --git a/src/Ryujinx.Cpu/Jit/HostTracked/AddressSpacePartitionMultiAllocation.cs b/src/Ryujinx.Cpu/Jit/HostTracked/AddressSpacePartitionMultiAllocation.cs index 3b065583f..db1f3ea4b 100644 --- a/src/Ryujinx.Cpu/Jit/HostTracked/AddressSpacePartitionMultiAllocation.cs +++ b/src/Ryujinx.Cpu/Jit/HostTracked/AddressSpacePartitionMultiAllocation.cs @@ -47,7 +47,7 @@ namespace Ryujinx.Cpu.Jit.HostTracked _baseMemory.Reprotect(offset, size, permission, throwOnFail); } - public IntPtr GetPointer(ulong offset, ulong size) + public nint GetPointer(ulong offset, ulong size) { return _baseMemory.GetPointer(offset, size); } @@ -68,7 +68,7 @@ namespace Ryujinx.Cpu.Jit.HostTracked return false; } - public IntPtr GetPointerForProtection(ulong offset, ulong size, MemoryPermission permission) + public nint GetPointerForProtection(ulong offset, ulong size, MemoryPermission permission) { AddressSpacePartitionAllocation allocation = permission switch { diff --git a/src/Ryujinx.Cpu/Jit/HostTracked/AddressSpacePartitioned.cs b/src/Ryujinx.Cpu/Jit/HostTracked/AddressSpacePartitioned.cs index 2cf2c248b..e3cb75f64 100644 --- a/src/Ryujinx.Cpu/Jit/HostTracked/AddressSpacePartitioned.cs +++ b/src/Ryujinx.Cpu/Jit/HostTracked/AddressSpacePartitioned.cs @@ -15,7 +15,7 @@ namespace Ryujinx.Cpu.Jit.HostTracked private readonly MemoryBlock _backingMemory; private readonly List _partitions; private readonly AddressSpacePartitionAllocator _asAllocator; - private readonly Action _updatePtCallback; + private readonly Action _updatePtCallback; private readonly bool _useProtectionMirrors; public AddressSpacePartitioned(MemoryTracking tracking, MemoryBlock backingMemory, NativePageTable nativePageTable, bool useProtectionMirrors) @@ -212,7 +212,7 @@ namespace Ryujinx.Cpu.Jit.HostTracked } } - public IntPtr GetPointer(ulong va, ulong size) + public nint GetPointer(ulong va, ulong size) { AddressSpacePartition partition = FindPartition(va); diff --git a/src/Ryujinx.Cpu/Jit/HostTracked/NativePageTable.cs b/src/Ryujinx.Cpu/Jit/HostTracked/NativePageTable.cs index e3174e3fc..aa663d7d9 100644 --- a/src/Ryujinx.Cpu/Jit/HostTracked/NativePageTable.cs +++ b/src/Ryujinx.Cpu/Jit/HostTracked/NativePageTable.cs @@ -30,7 +30,7 @@ namespace Ryujinx.Cpu.Jit.HostTracked private bool _disposed; - public IntPtr PageTablePointer => _nativePageTable.Pointer; + public nint PageTablePointer => _nativePageTable.Pointer; public NativePageTable(ulong asSize) { @@ -83,7 +83,7 @@ namespace Ryujinx.Cpu.Jit.HostTracked public void Unmap(ulong va, ulong size) { - IntPtr guardPagePtr = GetGuardPagePointer(); + nint guardPagePtr = GetGuardPagePointer(); while (size != 0) { @@ -104,7 +104,7 @@ namespace Ryujinx.Cpu.Jit.HostTracked return pte + (va & PageMask); } - public void Update(ulong va, IntPtr ptr, ulong size) + public void Update(ulong va, nint ptr, ulong size) { ulong remainingSize = size; @@ -148,7 +148,7 @@ namespace Ryujinx.Cpu.Jit.HostTracked Debug.Assert(pageSpan.Length == _entriesPerPtPage); - IntPtr guardPagePtr = GetGuardPagePointer(); + nint guardPagePtr = GetGuardPagePointer(); for (int i = 0; i < pageSpan.Length; i++) { @@ -160,12 +160,12 @@ namespace Ryujinx.Cpu.Jit.HostTracked } } - private IntPtr GetGuardPagePointer() + private nint GetGuardPagePointer() { return _nativePageTable.GetPointer(_nativePageTable.Size - _hostPageSize, _hostPageSize); } - private static ulong GetPte(ulong va, IntPtr ptr) + private static ulong GetPte(ulong va, nint ptr) { Debug.Assert((va & PageMask) == 0); diff --git a/src/Ryujinx.Cpu/Jit/JitCpuContext.cs b/src/Ryujinx.Cpu/Jit/JitCpuContext.cs index 9893c59b2..0793f382d 100644 --- a/src/Ryujinx.Cpu/Jit/JitCpuContext.cs +++ b/src/Ryujinx.Cpu/Jit/JitCpuContext.cs @@ -1,3 +1,4 @@ +using ARMeilleure.Common; using ARMeilleure.Memory; using ARMeilleure.Translation; using Ryujinx.Cpu.Signal; @@ -9,11 +10,13 @@ namespace Ryujinx.Cpu.Jit { private readonly ITickSource _tickSource; private readonly Translator _translator; + private readonly AddressTable _functionTable; public JitCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit) { _tickSource = tickSource; - _translator = new Translator(new JitMemoryAllocator(forJit: true), memory, for64Bit); + _functionTable = AddressTable.CreateForArm(for64Bit, memory.Type); + _translator = new Translator(new JitMemoryAllocator(forJit: true), memory, _functionTable); if (memory.Type.IsHostMappedOrTracked()) { @@ -47,14 +50,15 @@ namespace Ryujinx.Cpu.Jit } /// - public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled) + public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector) { - return new JitDiskCacheLoadState(_translator.LoadDiskCache(titleIdText, displayVersion, enabled)); + return new JitDiskCacheLoadState(_translator.LoadDiskCache(titleIdText, displayVersion, enabled, cacheSelector)); } /// public void PrepareCodeRange(ulong address, ulong size) { + _functionTable.SignalCodeRange(address, size); _translator.PrepareCodeRange(address, size); } diff --git a/src/Ryujinx.Cpu/Jit/JitMemoryBlock.cs b/src/Ryujinx.Cpu/Jit/JitMemoryBlock.cs index bd07d349c..0311db565 100644 --- a/src/Ryujinx.Cpu/Jit/JitMemoryBlock.cs +++ b/src/Ryujinx.Cpu/Jit/JitMemoryBlock.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Cpu.Jit { private readonly MemoryBlock _impl; - public IntPtr Pointer => _impl.Pointer; + public nint Pointer => _impl.Pointer; public JitMemoryBlock(ulong size, MemoryAllocationFlags flags) { diff --git a/src/Ryujinx.Cpu/Jit/MemoryManager.cs b/src/Ryujinx.Cpu/Jit/MemoryManager.cs index 6f594ec2f..076fb6ad8 100644 --- a/src/Ryujinx.Cpu/Jit/MemoryManager.cs +++ b/src/Ryujinx.Cpu/Jit/MemoryManager.cs @@ -39,7 +39,7 @@ namespace Ryujinx.Cpu.Jit /// /// Page table base pointer. /// - public IntPtr PageTablePointer => _pageTable.Pointer; + public nint PageTablePointer => _pageTable.Pointer; public MemoryManagerType Type => MemoryManagerType.SoftwarePageTable; @@ -250,25 +250,20 @@ namespace Ryujinx.Cpu.Jit { if (size == 0) { - return Enumerable.Empty(); + yield break; } var guestRegions = GetPhysicalRegionsImpl(va, size); if (guestRegions == null) { - return null; + yield break; } - var regions = new HostMemoryRange[guestRegions.Count]; - - for (int i = 0; i < regions.Length; i++) + foreach (var guestRegion in guestRegions) { - var guestRegion = guestRegions[i]; - IntPtr pointer = _backingMemory.GetPointer(guestRegion.Address, guestRegion.Size); - regions[i] = new HostMemoryRange((nuint)(ulong)pointer, guestRegion.Size); + nint pointer = _backingMemory.GetPointer(guestRegion.Address, guestRegion.Size); + yield return new HostMemoryRange((nuint)(ulong)pointer, guestRegion.Size); } - - return regions; } /// @@ -276,23 +271,24 @@ namespace Ryujinx.Cpu.Jit { if (size == 0) { - return Enumerable.Empty(); + yield break; } - return GetPhysicalRegionsImpl(va, size); + foreach (var physicalRegion in GetPhysicalRegionsImpl(va, size)) + { + yield return physicalRegion; + } } - private List GetPhysicalRegionsImpl(ulong va, ulong size) + private IEnumerable GetPhysicalRegionsImpl(ulong va, ulong size) { if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size)) { - return null; + yield break; } int pages = GetPagesCount(va, (uint)size, out va); - var regions = new List(); - ulong regionStart = GetPhysicalAddressInternal(va); ulong regionSize = PageSize; @@ -300,14 +296,14 @@ namespace Ryujinx.Cpu.Jit { if (!ValidateAddress(va + PageSize)) { - return null; + yield break; } ulong newPa = GetPhysicalAddressInternal(va + PageSize); if (GetPhysicalAddressInternal(va) + PageSize != newPa) { - regions.Add(new MemoryRange(regionStart, regionSize)); + yield return new MemoryRange(regionStart, regionSize); regionStart = newPa; regionSize = 0; } @@ -316,9 +312,7 @@ namespace Ryujinx.Cpu.Jit regionSize += PageSize; } - regions.Add(new MemoryRange(regionStart, regionSize)); - - return regions; + yield return new MemoryRange(regionStart, regionSize); } /// diff --git a/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs b/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs index 4639ab913..0fe8b344f 100644 --- a/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs +++ b/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs @@ -31,7 +31,7 @@ namespace Ryujinx.Cpu.Jit public int AddressSpaceBits { get; } - public IntPtr PageTablePointer => _addressSpace.Base.Pointer; + public nint PageTablePointer => _addressSpace.Base.Pointer; public MemoryManagerType Type => _unsafeMode ? MemoryManagerType.HostMappedUnsafe : MemoryManagerType.HostMapped; diff --git a/src/Ryujinx.Cpu/Jit/MemoryManagerHostTracked.cs b/src/Ryujinx.Cpu/Jit/MemoryManagerHostTracked.cs index 501109b86..499f991f2 100644 --- a/src/Ryujinx.Cpu/Jit/MemoryManagerHostTracked.cs +++ b/src/Ryujinx.Cpu/Jit/MemoryManagerHostTracked.cs @@ -37,7 +37,7 @@ namespace Ryujinx.Cpu.Jit /// public bool UsesPrivateAllocations => true; - public IntPtr PageTablePointer => _nativePageTable.PageTablePointer; + public nint PageTablePointer => _nativePageTable.PageTablePointer; public MemoryManagerType Type => _unsafeMode ? MemoryManagerType.HostTrackedUnsafe : MemoryManagerType.HostTracked; @@ -452,7 +452,7 @@ namespace Ryujinx.Cpu.Jit { (MemoryBlock memory, ulong rangeOffset, ulong rangeSize) = GetMemoryOffsetAndSize(va, endVa - va); - regions.Add(new((UIntPtr)memory.GetPointer(rangeOffset, rangeSize), rangeSize)); + regions.Add(new((nuint)memory.GetPointer(rangeOffset, rangeSize), rangeSize)); va += rangeSize; } @@ -469,23 +469,21 @@ namespace Ryujinx.Cpu.Jit { if (size == 0) { - return Enumerable.Empty(); + return []; } return GetPhysicalRegionsImpl(va, size); } - private List GetPhysicalRegionsImpl(ulong va, ulong size) + private IEnumerable GetPhysicalRegionsImpl(ulong va, ulong size) { if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size)) { - return null; + yield break; } int pages = GetPagesCount(va, (uint)size, out va); - var regions = new List(); - ulong regionStart = GetPhysicalAddressInternal(va); ulong regionSize = PageSize; @@ -493,14 +491,14 @@ namespace Ryujinx.Cpu.Jit { if (!ValidateAddress(va + PageSize)) { - return null; + yield break; } ulong newPa = GetPhysicalAddressInternal(va + PageSize); if (GetPhysicalAddressInternal(va) + PageSize != newPa) { - regions.Add(new MemoryRange(regionStart, regionSize)); + yield return new MemoryRange(regionStart, regionSize); regionStart = newPa; regionSize = 0; } @@ -509,9 +507,7 @@ namespace Ryujinx.Cpu.Jit regionSize += PageSize; } - regions.Add(new MemoryRange(regionStart, regionSize)); - - return regions; + yield return new MemoryRange(regionStart, regionSize); } /// diff --git a/src/Ryujinx.Cpu/LightningJit/AarchCompiler.cs b/src/Ryujinx.Cpu/LightningJit/AarchCompiler.cs index ee4fc439f..89e1499c0 100644 --- a/src/Ryujinx.Cpu/LightningJit/AarchCompiler.cs +++ b/src/Ryujinx.Cpu/LightningJit/AarchCompiler.cs @@ -15,7 +15,7 @@ namespace Ryujinx.Cpu.LightningJit IMemoryManager memoryManager, ulong address, AddressTable funcTable, - IntPtr dispatchStubPtr, + nint dispatchStubPtr, ExecutionMode executionMode, Architecture targetArch) { diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/A32Compiler.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/A32Compiler.cs index 7f6024d47..0fe42b923 100644 --- a/src/Ryujinx.Cpu/LightningJit/Arm32/A32Compiler.cs +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/A32Compiler.cs @@ -13,7 +13,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32 IMemoryManager memoryManager, ulong address, AddressTable funcTable, - IntPtr dispatchStubPtr, + nint dispatchStubPtr, bool isThumb, Architecture targetArch) { diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/Compiler.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/Compiler.cs index a668b5777..0d56f28c9 100644 --- a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/Compiler.cs +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/Compiler.cs @@ -24,10 +24,10 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 public readonly MemoryManagerType MemoryManagerType; public readonly TailMerger TailMerger; public readonly AddressTable FuncTable; - public readonly IntPtr DispatchStubPointer; + public readonly nint DispatchStubPointer; private readonly RegisterSaveRestore _registerSaveRestore; - private readonly IntPtr _pageTablePointer; + private readonly nint _pageTablePointer; public Context( CodeWriter writer, @@ -36,8 +36,8 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 TailMerger tailMerger, AddressTable funcTable, RegisterSaveRestore registerSaveRestore, - IntPtr dispatchStubPointer, - IntPtr pageTablePointer) + nint dispatchStubPointer, + nint pageTablePointer) { Writer = writer; RegisterAllocator = registerAllocator; @@ -226,7 +226,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 } } - public static CompiledFunction Compile(CpuPreset cpuPreset, IMemoryManager memoryManager, ulong address, AddressTable funcTable, IntPtr dispatchStubPtr, bool isThumb) + public static CompiledFunction Compile(CpuPreset cpuPreset, IMemoryManager memoryManager, ulong address, AddressTable funcTable, nint dispatchStubPtr, bool isThumb) { MultiBlock multiBlock = Decoder.DecodeMulti(cpuPreset, memoryManager, address, isThumb); diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitFlow.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitFlow.cs index 3b1ff5a2a..48bdbb573 100644 --- a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitFlow.cs +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitFlow.cs @@ -133,13 +133,17 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 TailMerger tailMerger, Action writeEpilogue, AddressTable funcTable, - IntPtr funcPtr, + nint funcPtr, int spillBaseOffset, uint nextAddress, Operand guestAddress, bool isTail = false) { int tempRegister; + int tempGuestAddress = -1; + + bool inlineLookup = guestAddress.Kind != OperandKind.Constant && + funcTable is { Sparse: true }; if (guestAddress.Kind == OperandKind.Constant) { @@ -153,9 +157,16 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 else { asm.StrRiUn(guestAddress, Register(regAlloc.FixedContextRegister), NativeContextOffsets.DispatchAddressOffset); + + if (inlineLookup && guestAddress.Value == 0) + { + // X0 will be overwritten. Move the address to a temp register. + tempGuestAddress = regAlloc.AllocateTempGprRegister(); + asm.Mov(Register(tempGuestAddress), guestAddress); + } } - tempRegister = regAlloc.FixedContextRegister == 1 ? 2 : 1; + tempRegister = NextFreeRegister(1, tempGuestAddress); if (!isTail) { @@ -176,6 +187,40 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 asm.Mov(rn, funcPtrLoc & ~0xfffUL); asm.LdrRiUn(rn, rn, (int)(funcPtrLoc & 0xfffUL)); } + else if (inlineLookup) + { + // Inline table lookup. Only enabled when the sparse function table is enabled with 2 levels. + + Operand indexReg = Register(NextFreeRegister(tempRegister + 1, tempGuestAddress)); + + if (tempGuestAddress != -1) + { + guestAddress = Register(tempGuestAddress); + } + + ulong tableBase = (ulong)funcTable.Base; + + // Index into the table. + asm.Mov(rn, tableBase); + + for (int i = 0; i < funcTable.Levels.Length; i++) + { + var level = funcTable.Levels[i]; + asm.Ubfx(indexReg, guestAddress, level.Index, level.Length); + asm.Lsl(indexReg, indexReg, Const(3)); + + // Index into the page. + asm.Add(rn, rn, indexReg); + + // Load the page address. + asm.LdrRiUn(rn, rn, 0); + } + + if (tempGuestAddress != -1) + { + regAlloc.FreeTempGprRegister(tempGuestAddress); + } + } else { asm.Mov(rn, (ulong)funcPtr); @@ -252,5 +297,20 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 { return new Operand(register, RegisterType.Integer, type); } + + private static Operand Const(long value, OperandType type = OperandType.I64) + { + return new Operand(type, (ulong)value); + } + + private static int NextFreeRegister(int start, int avoid) + { + if (start == avoid) + { + start++; + } + + return start; + } } } diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitSystem.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitSystem.cs index 07f9f86a8..21e40b6aa 100644 --- a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitSystem.cs +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitSystem.cs @@ -324,27 +324,27 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 Udf(context, encoding, 0); } - private static IntPtr GetBkptHandlerPtr() + private static nint GetBkptHandlerPtr() { return Marshal.GetFunctionPointerForDelegate(NativeInterface.Break); } - private static IntPtr GetSvcHandlerPtr() + private static nint GetSvcHandlerPtr() { return Marshal.GetFunctionPointerForDelegate(NativeInterface.SupervisorCall); } - private static IntPtr GetUdfHandlerPtr() + private static nint GetUdfHandlerPtr() { return Marshal.GetFunctionPointerForDelegate(NativeInterface.Undefined); } - private static IntPtr GetCntpctEl0Ptr() + private static nint GetCntpctEl0Ptr() { return Marshal.GetFunctionPointerForDelegate(NativeInterface.GetCntpctEl0); } - private static IntPtr CheckSynchronizationPtr() + private static nint CheckSynchronizationPtr() { return Marshal.GetFunctionPointerForDelegate(NativeInterface.CheckSynchronization); } @@ -474,11 +474,11 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 private static void WriteCall( ref Assembler asm, RegisterAllocator regAlloc, - IntPtr funcPtr, + nint funcPtr, bool skipContext, int spillBaseOffset, int? resultRegister, - params ulong[] callArgs) + params ReadOnlySpan callArgs) { uint resultMask = 0u; diff --git a/src/Ryujinx.Cpu/LightningJit/Arm64/A64Compiler.cs b/src/Ryujinx.Cpu/LightningJit/Arm64/A64Compiler.cs index b46ae3b0c..44de4cd0d 100644 --- a/src/Ryujinx.Cpu/LightningJit/Arm64/A64Compiler.cs +++ b/src/Ryujinx.Cpu/LightningJit/Arm64/A64Compiler.cs @@ -13,7 +13,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64 IMemoryManager memoryManager, ulong address, AddressTable funcTable, - IntPtr dispatchStubPtr, + nint dispatchStubPtr, Architecture targetArch) { if (targetArch == Architecture.Arm64) diff --git a/src/Ryujinx.Cpu/LightningJit/Arm64/Target/Arm64/Compiler.cs b/src/Ryujinx.Cpu/LightningJit/Arm64/Target/Arm64/Compiler.cs index 7a6d761e8..4a3c507df 100644 --- a/src/Ryujinx.Cpu/LightningJit/Arm64/Target/Arm64/Compiler.cs +++ b/src/Ryujinx.Cpu/LightningJit/Arm64/Target/Arm64/Compiler.cs @@ -20,11 +20,11 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64 public readonly RegisterAllocator RegisterAllocator; public readonly TailMerger TailMerger; public readonly AddressTable FuncTable; - public readonly IntPtr DispatchStubPointer; + public readonly nint DispatchStubPointer; private readonly MultiBlock _multiBlock; private readonly RegisterSaveRestore _registerSaveRestore; - private readonly IntPtr _pageTablePointer; + private readonly nint _pageTablePointer; public Context( CodeWriter writer, @@ -33,8 +33,8 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64 RegisterSaveRestore registerSaveRestore, MultiBlock multiBlock, AddressTable funcTable, - IntPtr dispatchStubPointer, - IntPtr pageTablePointer) + nint dispatchStubPointer, + nint pageTablePointer) { Writer = writer; RegisterAllocator = registerAllocator; @@ -304,7 +304,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64 } } - public static CompiledFunction Compile(CpuPreset cpuPreset, IMemoryManager memoryManager, ulong address, AddressTable funcTable, IntPtr dispatchStubPtr) + public static CompiledFunction Compile(CpuPreset cpuPreset, IMemoryManager memoryManager, ulong address, AddressTable funcTable, nint dispatchStubPtr) { MultiBlock multiBlock = Decoder.DecodeMulti(cpuPreset, memoryManager, address); diff --git a/src/Ryujinx.Cpu/LightningJit/Arm64/Target/Arm64/InstEmitSystem.cs b/src/Ryujinx.Cpu/LightningJit/Arm64/Target/Arm64/InstEmitSystem.cs index 82cb29d73..bf9338400 100644 --- a/src/Ryujinx.Cpu/LightningJit/Arm64/Target/Arm64/InstEmitSystem.cs +++ b/src/Ryujinx.Cpu/LightningJit/Arm64/Target/Arm64/InstEmitSystem.cs @@ -144,27 +144,27 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64 return name == InstName.Svc; } - private static IntPtr GetBrkHandlerPtr() + private static nint GetBrkHandlerPtr() { return Marshal.GetFunctionPointerForDelegate(NativeInterface.Break); } - private static IntPtr GetSvcHandlerPtr() + private static nint GetSvcHandlerPtr() { return Marshal.GetFunctionPointerForDelegate(NativeInterface.SupervisorCall); } - private static IntPtr GetUdfHandlerPtr() + private static nint GetUdfHandlerPtr() { return Marshal.GetFunctionPointerForDelegate(NativeInterface.Undefined); } - private static IntPtr GetCntpctEl0Ptr() + private static nint GetCntpctEl0Ptr() { return Marshal.GetFunctionPointerForDelegate(NativeInterface.GetCntpctEl0); } - private static IntPtr CheckSynchronizationPtr() + private static nint CheckSynchronizationPtr() { return Marshal.GetFunctionPointerForDelegate(NativeInterface.CheckSynchronization); } @@ -215,7 +215,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64 TailMerger tailMerger, Action writeEpilogue, AddressTable funcTable, - IntPtr dispatchStubPtr, + nint dispatchStubPtr, InstName name, ulong pc, uint encoding, @@ -298,13 +298,17 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64 TailMerger tailMerger, Action writeEpilogue, AddressTable funcTable, - IntPtr funcPtr, + nint funcPtr, int spillBaseOffset, ulong pc, Operand guestAddress, bool isTail = false) { int tempRegister; + int tempGuestAddress = -1; + + bool inlineLookup = guestAddress.Kind != OperandKind.Constant && + funcTable is { Sparse: true }; if (guestAddress.Kind == OperandKind.Constant) { @@ -318,9 +322,16 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64 else { asm.StrRiUn(guestAddress, Register(regAlloc.FixedContextRegister), NativeContextOffsets.DispatchAddressOffset); + + if (inlineLookup && guestAddress.Value == 0) + { + // X0 will be overwritten. Move the address to a temp register. + tempGuestAddress = regAlloc.AllocateTempGprRegister(); + asm.Mov(Register(tempGuestAddress), guestAddress); + } } - tempRegister = regAlloc.FixedContextRegister == 1 ? 2 : 1; + tempRegister = NextFreeRegister(1, tempGuestAddress); if (!isTail) { @@ -341,6 +352,40 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64 asm.Mov(rn, funcPtrLoc & ~0xfffUL); asm.LdrRiUn(rn, rn, (int)(funcPtrLoc & 0xfffUL)); } + else if (inlineLookup) + { + // Inline table lookup. Only enabled when the sparse function table is enabled with 2 levels. + + Operand indexReg = Register(NextFreeRegister(tempRegister + 1, tempGuestAddress)); + + if (tempGuestAddress != -1) + { + guestAddress = Register(tempGuestAddress); + } + + ulong tableBase = (ulong)funcTable.Base; + + // Index into the table. + asm.Mov(rn, tableBase); + + for (int i = 0; i < funcTable.Levels.Length; i++) + { + var level = funcTable.Levels[i]; + asm.Ubfx(indexReg, guestAddress, level.Index, level.Length); + asm.Lsl(indexReg, indexReg, Const(3)); + + // Index into the page. + asm.Add(rn, rn, indexReg); + + // Load the page address. + asm.LdrRiUn(rn, rn, 0); + } + + if (tempGuestAddress != -1) + { + regAlloc.FreeTempGprRegister(tempGuestAddress); + } + } else { asm.Mov(rn, (ulong)funcPtr); @@ -369,10 +414,10 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64 private static void WriteCall( ref Assembler asm, RegisterAllocator regAlloc, - IntPtr funcPtr, + nint funcPtr, int spillBaseOffset, int? resultRegister, - params ulong[] callArgs) + params ReadOnlySpan callArgs) { uint resultMask = 0u; @@ -613,5 +658,20 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64 { return new Operand(register, RegisterType.Integer, type); } + + private static Operand Const(long value, OperandType type = OperandType.I64) + { + return new Operand(type, (ulong)value); + } + + private static int NextFreeRegister(int start, int avoid) + { + if (start == avoid) + { + start++; + } + + return start; + } } } diff --git a/src/Ryujinx.Cpu/LightningJit/Cache/JitCache.cs b/src/Ryujinx.Cpu/LightningJit/Cache/JitCache.cs index 6f1191ca5..10ae050b6 100644 --- a/src/Ryujinx.Cpu/LightningJit/Cache/JitCache.cs +++ b/src/Ryujinx.Cpu/LightningJit/Cache/JitCache.cs @@ -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,12 +24,12 @@ namespace Ryujinx.Cpu.LightningJit.Cache private static readonly List _cacheEntries = new(); - private static readonly object _lock = new(); + private static readonly Lock _lock = new(); private static bool _initialized; [SupportedOSPlatform("windows")] [LibraryImport("kernel32.dll", SetLastError = true)] - public static partial IntPtr FlushInstructionCache(IntPtr hProcess, IntPtr lpAddress, UIntPtr dwSize); + public static partial nint FlushInstructionCache(nint hProcess, nint lpAddress, nuint dwSize); public static void Initialize(IJitMemoryAllocator allocator) { @@ -57,7 +58,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache } } - public unsafe static IntPtr Map(ReadOnlySpan code) + public unsafe static nint Map(ReadOnlySpan code) { lock (_lock) { @@ -65,7 +66,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache int funcOffset = Allocate(code.Length); - IntPtr funcPtr = _jitRegion.Pointer + funcOffset; + nint funcPtr = _jitRegion.Pointer + funcOffset; if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64) { @@ -73,7 +74,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache { fixed (byte* codePtr = code) { - JitSupportDarwin.Copy(funcPtr, (IntPtr)codePtr, (ulong)code.Length); + JitSupportDarwin.Copy(funcPtr, (nint)codePtr, (ulong)code.Length); } } } @@ -85,7 +86,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache if (OperatingSystem.IsWindows() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64) { - FlushInstructionCache(Process.GetCurrentProcess().Handle, funcPtr, (UIntPtr)code.Length); + FlushInstructionCache(Process.GetCurrentProcess().Handle, funcPtr, (nuint)code.Length); } else { @@ -99,7 +100,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache } } - public static void Unmap(IntPtr pointer) + public static void Unmap(nint pointer) { lock (_lock) { diff --git a/src/Ryujinx.Cpu/LightningJit/Cache/JitCacheInvalidation.cs b/src/Ryujinx.Cpu/LightningJit/Cache/JitCacheInvalidation.cs index cd5f3ede4..d0a5e4ac8 100644 --- a/src/Ryujinx.Cpu/LightningJit/Cache/JitCacheInvalidation.cs +++ b/src/Ryujinx.Cpu/LightningJit/Cache/JitCacheInvalidation.cs @@ -68,7 +68,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache } } - public void Invalidate(IntPtr basePointer, ulong size) + public void Invalidate(nint basePointer, ulong size) { if (_needsInvalidation) { diff --git a/src/Ryujinx.Cpu/LightningJit/Cache/JitSupportDarwin.cs b/src/Ryujinx.Cpu/LightningJit/Cache/JitSupportDarwin.cs index 06c81045d..ed02a9c28 100644 --- a/src/Ryujinx.Cpu/LightningJit/Cache/JitSupportDarwin.cs +++ b/src/Ryujinx.Cpu/LightningJit/Cache/JitSupportDarwin.cs @@ -8,9 +8,9 @@ namespace Ryujinx.Cpu.LightningJit.Cache static partial class JitSupportDarwin { [LibraryImport("libarmeilleure-jitsupport", EntryPoint = "armeilleure_jit_memcpy")] - public static partial void Copy(IntPtr dst, IntPtr src, ulong n); + public static partial void Copy(nint dst, nint src, ulong n); [LibraryImport("libc", EntryPoint = "sys_icache_invalidate", SetLastError = true)] - public static partial void SysIcacheInvalidate(IntPtr start, IntPtr len); + public static partial void SysIcacheInvalidate(nint start, nint len); } } diff --git a/src/Ryujinx.Cpu/LightningJit/Cache/NoWxCache.cs b/src/Ryujinx.Cpu/LightningJit/Cache/NoWxCache.cs index a71074995..e9a342aba 100644 --- a/src/Ryujinx.Cpu/LightningJit/Cache/NoWxCache.cs +++ b/src/Ryujinx.Cpu/LightningJit/Cache/NoWxCache.cs @@ -4,6 +4,7 @@ using Ryujinx.Memory; using System; using System.Collections.Generic; using System.Diagnostics; +using System.Threading; namespace Ryujinx.Cpu.LightningJit.Cache { @@ -23,7 +24,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache private readonly CacheMemoryAllocator _cacheAllocator; public CacheMemoryAllocator Allocator => _cacheAllocator; - public IntPtr Pointer => _region.Block.Pointer; + public nint Pointer => _region.Block.Pointer; public MemoryCache(IJitMemoryAllocator allocator, ulong size) { @@ -104,16 +105,16 @@ 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 { public readonly int Offset; public readonly int Size; - public readonly IntPtr FuncPtr; + public readonly nint FuncPtr; private int _useCount; - public ThreadLocalCacheEntry(int offset, int size, IntPtr funcPtr) + public ThreadLocalCacheEntry(int offset, int size, nint funcPtr) { Offset = offset; Size = size; @@ -137,12 +138,11 @@ namespace Ryujinx.Cpu.LightningJit.Cache _sharedCache = new(allocator, SharedCacheSize); _localCache = new(allocator, LocalCacheSize); _pendingMap = new(_sharedCache.ReprotectAsRx, RegisterFunction); - _lock = new(); } - public unsafe IntPtr Map(IntPtr framePointer, ReadOnlySpan code, ulong guestAddress, ulong guestSize) + public unsafe nint Map(nint framePointer, ReadOnlySpan code, ulong guestAddress, ulong guestSize) { - if (TryGetThreadLocalFunction(guestAddress, out IntPtr funcPtr)) + if (TryGetThreadLocalFunction(guestAddress, out nint funcPtr)) { return funcPtr; } @@ -167,7 +167,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache } } - public unsafe IntPtr MapPageAligned(ReadOnlySpan code) + public unsafe nint MapPageAligned(ReadOnlySpan code) { lock (_lock) { @@ -179,7 +179,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache Debug.Assert((funcOffset & ((int)MemoryBlock.GetPageSize() - 1)) == 0); - IntPtr funcPtr = _sharedCache.Pointer + funcOffset; + nint funcPtr = _sharedCache.Pointer + funcOffset; code.CopyTo(new Span((void*)funcPtr, code.Length)); _sharedCache.ReprotectAsRx(funcOffset, sizeAligned); @@ -188,7 +188,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache } } - private bool TryGetThreadLocalFunction(ulong guestAddress, out IntPtr funcPtr) + private bool TryGetThreadLocalFunction(ulong guestAddress, out nint funcPtr) { if ((_threadLocalCache ??= new()).TryGetValue(guestAddress, out var entry)) { @@ -209,12 +209,12 @@ namespace Ryujinx.Cpu.LightningJit.Cache return true; } - funcPtr = IntPtr.Zero; + funcPtr = nint.Zero; return false; } - private void ClearThreadLocalCache(IntPtr framePointer) + private void ClearThreadLocalCache(nint framePointer) { // Try to delete functions that are already on the shared cache // and no longer being executed. @@ -296,14 +296,14 @@ namespace Ryujinx.Cpu.LightningJit.Cache _threadLocalCache = null; } - private unsafe IntPtr AddThreadLocalFunction(ReadOnlySpan code, ulong guestAddress) + private unsafe nint AddThreadLocalFunction(ReadOnlySpan code, ulong guestAddress) { int alignedSize = BitUtils.AlignUp(code.Length, (int)MemoryBlock.GetPageSize()); int funcOffset = _localCache.Allocate(alignedSize); Debug.Assert((funcOffset & (int)(MemoryBlock.GetPageSize() - 1)) == 0); - IntPtr funcPtr = _localCache.Pointer + funcOffset; + nint funcPtr = _localCache.Pointer + funcOffset; code.CopyTo(new Span((void*)funcPtr, code.Length)); (_threadLocalCache ??= new()).Add(guestAddress, new(funcOffset, code.Length, funcPtr)); diff --git a/src/Ryujinx.Cpu/LightningJit/CodeGen/Arm64/StackWalker.cs b/src/Ryujinx.Cpu/LightningJit/CodeGen/Arm64/StackWalker.cs index 3b01e674b..1432c4598 100644 --- a/src/Ryujinx.Cpu/LightningJit/CodeGen/Arm64/StackWalker.cs +++ b/src/Ryujinx.Cpu/LightningJit/CodeGen/Arm64/StackWalker.cs @@ -6,13 +6,11 @@ namespace Ryujinx.Cpu.LightningJit.CodeGen.Arm64 { class StackWalker : IStackWalker { - public IEnumerable GetCallStack(IntPtr framePointer, IntPtr codeRegionStart, int codeRegionSize, IntPtr codeRegion2Start, int codeRegion2Size) + public IEnumerable GetCallStack(nint framePointer, nint codeRegionStart, int codeRegionSize, nint codeRegion2Start, int codeRegion2Size) { - List functionPointers = new(); - while (true) { - IntPtr functionPointer = Marshal.ReadIntPtr(framePointer, IntPtr.Size); + nint functionPointer = Marshal.ReadIntPtr(framePointer, nint.Size); if ((functionPointer < codeRegionStart || functionPointer >= codeRegionStart + codeRegionSize) && (functionPointer < codeRegion2Start || functionPointer >= codeRegion2Start + codeRegion2Size)) @@ -20,11 +18,9 @@ namespace Ryujinx.Cpu.LightningJit.CodeGen.Arm64 break; } - functionPointers.Add((ulong)functionPointer - 4); + yield return (ulong)functionPointer - 4; framePointer = Marshal.ReadIntPtr(framePointer); } - - return functionPointers; } } } diff --git a/src/Ryujinx.Cpu/LightningJit/IStackWalker.cs b/src/Ryujinx.Cpu/LightningJit/IStackWalker.cs index 2fddef659..375c09d26 100644 --- a/src/Ryujinx.Cpu/LightningJit/IStackWalker.cs +++ b/src/Ryujinx.Cpu/LightningJit/IStackWalker.cs @@ -5,6 +5,6 @@ namespace Ryujinx.Cpu.LightningJit { interface IStackWalker { - IEnumerable GetCallStack(IntPtr framePointer, IntPtr codeRegionStart, int codeRegionSize, IntPtr codeRegion2Start, int codeRegion2Size); + IEnumerable GetCallStack(nint framePointer, nint codeRegionStart, int codeRegionSize, nint codeRegion2Start, int codeRegion2Size); } } diff --git a/src/Ryujinx.Cpu/LightningJit/LightningJitCpuContext.cs b/src/Ryujinx.Cpu/LightningJit/LightningJitCpuContext.cs index b63636e39..0f47ffb15 100644 --- a/src/Ryujinx.Cpu/LightningJit/LightningJitCpuContext.cs +++ b/src/Ryujinx.Cpu/LightningJit/LightningJitCpuContext.cs @@ -1,3 +1,4 @@ +using ARMeilleure.Common; using ARMeilleure.Memory; using Ryujinx.Cpu.Jit; using Ryujinx.Cpu.LightningJit.State; @@ -8,11 +9,16 @@ namespace Ryujinx.Cpu.LightningJit { private readonly ITickSource _tickSource; private readonly Translator _translator; + private readonly AddressTable _functionTable; public LightningJitCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit) { _tickSource = tickSource; - _translator = new Translator(memory, for64Bit); + + _functionTable = AddressTable.CreateForArm(for64Bit, memory.Type); + + _translator = new Translator(memory, _functionTable); + memory.UnmapEvent += UnmapHandler; } @@ -40,7 +46,7 @@ namespace Ryujinx.Cpu.LightningJit } /// - public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled) + public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector) { return new DummyDiskCacheLoadState(); } @@ -48,6 +54,7 @@ namespace Ryujinx.Cpu.LightningJit /// public void PrepareCodeRange(ulong address, ulong size) { + _functionTable.SignalCodeRange(address, size); } public void Dispose() diff --git a/src/Ryujinx.Cpu/LightningJit/NativeInterface.cs b/src/Ryujinx.Cpu/LightningJit/NativeInterface.cs index da3ad9832..5f243c0ee 100644 --- a/src/Ryujinx.Cpu/LightningJit/NativeInterface.cs +++ b/src/Ryujinx.Cpu/LightningJit/NativeInterface.cs @@ -61,7 +61,7 @@ namespace Ryujinx.Cpu.LightningJit return GetContext().CntpctEl0; } - public static ulong GetFunctionAddress(IntPtr framePointer, ulong address) + public static ulong GetFunctionAddress(nint framePointer, ulong address) { return (ulong)Context.Translator.GetOrTranslatePointer(framePointer, address, GetContext().ExecutionMode); } diff --git a/src/Ryujinx.Cpu/LightningJit/State/ExecutionContext.cs b/src/Ryujinx.Cpu/LightningJit/State/ExecutionContext.cs index facb9142f..a366dcca6 100644 --- a/src/Ryujinx.Cpu/LightningJit/State/ExecutionContext.cs +++ b/src/Ryujinx.Cpu/LightningJit/State/ExecutionContext.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Cpu.LightningJit.State private readonly NativeContext _nativeContext; - internal IntPtr NativeContextPtr => _nativeContext.BasePtr; + internal nint NativeContextPtr => _nativeContext.BasePtr; private bool _interrupted; private readonly ICounter _counter; diff --git a/src/Ryujinx.Cpu/LightningJit/State/NativeContext.cs b/src/Ryujinx.Cpu/LightningJit/State/NativeContext.cs index fdb8793de..9895c78c2 100644 --- a/src/Ryujinx.Cpu/LightningJit/State/NativeContext.cs +++ b/src/Ryujinx.Cpu/LightningJit/State/NativeContext.cs @@ -25,7 +25,7 @@ namespace Ryujinx.Cpu.LightningJit.State private readonly IJitMemoryBlock _block; - public IntPtr BasePtr => _block.Pointer; + public nint BasePtr => _block.Pointer; public NativeContext(IJitMemoryAllocator allocator) { diff --git a/src/Ryujinx.Cpu/LightningJit/TranslatedFunction.cs b/src/Ryujinx.Cpu/LightningJit/TranslatedFunction.cs index a4e2c7b93..df0f52b8c 100644 --- a/src/Ryujinx.Cpu/LightningJit/TranslatedFunction.cs +++ b/src/Ryujinx.Cpu/LightningJit/TranslatedFunction.cs @@ -4,10 +4,10 @@ namespace Ryujinx.Cpu.LightningJit { class TranslatedFunction { - public IntPtr FuncPointer { get; } + public nint FuncPointer { get; } public ulong GuestSize { get; } - public TranslatedFunction(IntPtr funcPointer, ulong guestSize) + public TranslatedFunction(nint funcPointer, ulong guestSize) { FuncPointer = funcPointer; GuestSize = guestSize; diff --git a/src/Ryujinx.Cpu/LightningJit/Translator.cs b/src/Ryujinx.Cpu/LightningJit/Translator.cs index d62410253..4c4011f11 100644 --- a/src/Ryujinx.Cpu/LightningJit/Translator.cs +++ b/src/Ryujinx.Cpu/LightningJit/Translator.cs @@ -19,25 +19,6 @@ namespace Ryujinx.Cpu.LightningJit // Should be enabled on platforms that enforce W^X. private static bool IsNoWxPlatform => false; - private static readonly AddressTable.Level[] _levels64Bit = - new AddressTable.Level[] - { - new(31, 17), - new(23, 8), - new(15, 8), - new( 7, 8), - new( 2, 5), - }; - - private static readonly AddressTable.Level[] _levels32Bit = - new AddressTable.Level[] - { - new(23, 9), - new(15, 8), - new( 7, 8), - new( 1, 6), - }; - private readonly ConcurrentQueue> _oldFuncs; private readonly NoWxCache _noWxCache; private bool _disposed; @@ -47,7 +28,7 @@ namespace Ryujinx.Cpu.LightningJit internal TranslatorStubs Stubs { get; } internal IMemoryManager Memory { get; } - public Translator(IMemoryManager memory, bool for64Bits) + public Translator(IMemoryManager memory, AddressTable functionTable) { Memory = memory; @@ -63,7 +44,7 @@ namespace Ryujinx.Cpu.LightningJit } Functions = new TranslatorCache(); - FunctionTable = new AddressTable(for64Bits ? _levels64Bit : _levels32Bit); + FunctionTable = functionTable; Stubs = new TranslatorStubs(FunctionTable, _noWxCache); FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub; @@ -98,7 +79,7 @@ namespace Ryujinx.Cpu.LightningJit _noWxCache?.ClearEntireThreadLocalCache(); } - internal IntPtr GetOrTranslatePointer(IntPtr framePointer, ulong address, ExecutionMode mode) + internal nint GetOrTranslatePointer(nint framePointer, ulong address, ExecutionMode mode) { if (_noWxCache != null) { @@ -141,7 +122,7 @@ namespace Ryujinx.Cpu.LightningJit private TranslatedFunction Translate(ulong address, ExecutionMode mode) { CompiledFunction func = Compile(address, mode); - IntPtr funcPointer = JitCache.Map(func.Code); + nint funcPointer = JitCache.Map(func.Code); return new TranslatedFunction(funcPointer, (ulong)func.GuestCodeLength); } diff --git a/src/Ryujinx.Cpu/LightningJit/TranslatorStubs.cs b/src/Ryujinx.Cpu/LightningJit/TranslatorStubs.cs index 914712bb1..c5231e506 100644 --- a/src/Ryujinx.Cpu/LightningJit/TranslatorStubs.cs +++ b/src/Ryujinx.Cpu/LightningJit/TranslatorStubs.cs @@ -10,31 +10,31 @@ using System.Runtime.InteropServices; namespace Ryujinx.Cpu.LightningJit { - delegate void DispatcherFunction(IntPtr nativeContext, ulong startAddress); + delegate void DispatcherFunction(nint nativeContext, ulong startAddress); /// /// Represents a stub manager. /// class TranslatorStubs : IDisposable { - private delegate ulong GetFunctionAddressDelegate(IntPtr framePointer, ulong address); + private delegate ulong GetFunctionAddressDelegate(nint framePointer, ulong address); - private readonly Lazy _slowDispatchStub; + private readonly Lazy _slowDispatchStub; private bool _disposed; - private readonly AddressTable _functionTable; + private readonly IAddressTable _functionTable; private readonly NoWxCache _noWxCache; private readonly GetFunctionAddressDelegate _getFunctionAddressRef; - private readonly IntPtr _getFunctionAddress; - private readonly Lazy _dispatchStub; + private readonly nint _getFunctionAddress; + private readonly Lazy _dispatchStub; private readonly Lazy _dispatchLoop; /// /// Gets the dispatch stub. /// /// instance was disposed - public IntPtr DispatchStub + public nint DispatchStub { get { @@ -48,7 +48,7 @@ namespace Ryujinx.Cpu.LightningJit /// Gets the slow dispatch stub. /// /// instance was disposed - public IntPtr SlowDispatchStub + public nint SlowDispatchStub { get { @@ -79,7 +79,7 @@ namespace Ryujinx.Cpu.LightningJit /// Function table used to store pointers to the functions that the guest code will call /// Cache used on platforms that enforce W^X, otherwise should be null /// is null - public TranslatorStubs(AddressTable functionTable, NoWxCache noWxCache) + public TranslatorStubs(IAddressTable functionTable, NoWxCache noWxCache) { ArgumentNullException.ThrowIfNull(functionTable); @@ -138,7 +138,7 @@ namespace Ryujinx.Cpu.LightningJit /// Generates a . /// /// Generated - private IntPtr GenerateDispatchStub() + private nint GenerateDispatchStub() { List branchToFallbackOffsets = new(); @@ -226,7 +226,7 @@ namespace Ryujinx.Cpu.LightningJit /// Generates a . /// /// Generated - private IntPtr GenerateSlowDispatchStub() + private nint GenerateSlowDispatchStub() { CodeWriter writer = new(); @@ -350,12 +350,12 @@ namespace Ryujinx.Cpu.LightningJit throw new PlatformNotSupportedException(); } - IntPtr pointer = Map(writer.AsByteSpan()); + nint pointer = Map(writer.AsByteSpan()); return Marshal.GetDelegateForFunctionPointer(pointer); } - private IntPtr Map(ReadOnlySpan code) + private nint Map(ReadOnlySpan code) { if (_noWxCache != null) { diff --git a/src/Ryujinx.Cpu/MemoryEhMeilleure.cs b/src/Ryujinx.Cpu/MemoryEhMeilleure.cs index 379ace941..e9a3ac4aa 100644 --- a/src/Ryujinx.Cpu/MemoryEhMeilleure.cs +++ b/src/Ryujinx.Cpu/MemoryEhMeilleure.cs @@ -46,7 +46,7 @@ namespace Ryujinx.Cpu _mirrorAddress = (ulong)addressSpaceMirror.Pointer; ulong endAddressMirror = _mirrorAddress + addressSpace.Size; - bool addedMirror = NativeSignalHandler.AddTrackedRegion((nuint)_mirrorAddress, (nuint)endAddressMirror, IntPtr.Zero); + bool addedMirror = NativeSignalHandler.AddTrackedRegion((nuint)_mirrorAddress, (nuint)endAddressMirror, nint.Zero); if (!addedMirror) { diff --git a/src/Ryujinx.Cpu/Ryujinx.Cpu.csproj b/src/Ryujinx.Cpu/Ryujinx.Cpu.csproj index 5a6bf5c3d..e58a2ce97 100644 --- a/src/Ryujinx.Cpu/Ryujinx.Cpu.csproj +++ b/src/Ryujinx.Cpu/Ryujinx.Cpu.csproj @@ -1,8 +1,8 @@  - net8.0 true + $(DefaultItemExcludes);._* diff --git a/src/Ryujinx.Cpu/Signal/NativeSignalHandler.cs b/src/Ryujinx.Cpu/Signal/NativeSignalHandler.cs index 93e608329..75a6d3bf8 100644 --- a/src/Ryujinx.Cpu/Signal/NativeSignalHandler.cs +++ b/src/Ryujinx.Cpu/Signal/NativeSignalHandler.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Threading; namespace Ryujinx.Cpu.Signal { @@ -14,7 +15,7 @@ namespace Ryujinx.Cpu.Signal public int IsActive; public nuint RangeAddress; public nuint RangeEndAddress; - public IntPtr ActionPointer; + public nint ActionPointer; } [InlineArray(NativeSignalHandlerGenerator.MaxTrackedRanges)] @@ -54,12 +55,12 @@ namespace Ryujinx.Cpu.Signal static class NativeSignalHandler { - private static readonly IntPtr _handlerConfig; - private static IntPtr _signalHandlerPtr; + private static readonly nint _handlerConfig; + private static nint _signalHandlerPtr; private static MemoryBlock _codeBlock; - private static readonly object _lock = new(); + private static readonly Lock _lock = new(); private static bool _initialized; static NativeSignalHandler() @@ -70,7 +71,7 @@ namespace Ryujinx.Cpu.Signal config = new SignalHandlerConfig(); } - public static void InitializeSignalHandler(Func customSignalHandlerFactory = null) + public static void InitializeSignalHandler(Func customSignalHandlerFactory = null) { if (_initialized) { @@ -111,7 +112,7 @@ namespace Ryujinx.Cpu.Signal if (customSignalHandlerFactory != null) { - _signalHandlerPtr = customSignalHandlerFactory(IntPtr.Zero, _signalHandlerPtr); + _signalHandlerPtr = customSignalHandlerFactory(nint.Zero, _signalHandlerPtr); } WindowsSignalHandlerRegistration.RegisterExceptionHandler(_signalHandlerPtr); @@ -121,7 +122,7 @@ namespace Ryujinx.Cpu.Signal } } - private static IntPtr MapCode(ReadOnlySpan code) + private static nint MapCode(ReadOnlySpan code) { Debug.Assert(_codeBlock == null); @@ -139,7 +140,7 @@ namespace Ryujinx.Cpu.Signal return ref Unsafe.AsRef((void*)_handlerConfig); } - public static bool AddTrackedRegion(nuint address, nuint endAddress, IntPtr action) + public static bool AddTrackedRegion(nuint address, nuint endAddress, nint action) { Span ranges = GetConfigRef().Ranges; diff --git a/src/Ryujinx.Cpu/Signal/UnixSignalHandlerRegistration.cs b/src/Ryujinx.Cpu/Signal/UnixSignalHandlerRegistration.cs index e88a6c0f6..d40e7cdc9 100644 --- a/src/Ryujinx.Cpu/Signal/UnixSignalHandlerRegistration.cs +++ b/src/Ryujinx.Cpu/Signal/UnixSignalHandlerRegistration.cs @@ -14,10 +14,10 @@ namespace Ryujinx.Cpu.Signal [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct SigAction { - public IntPtr sa_handler; + public nint sa_handler; public SigSet sa_mask; public int sa_flags; - public IntPtr sa_restorer; + public nint sa_restorer; } private const int SIGSEGV = 11; @@ -28,14 +28,14 @@ namespace Ryujinx.Cpu.Signal private static partial int sigaction(int signum, ref SigAction sigAction, out SigAction oldAction); [LibraryImport("libc", SetLastError = true)] - private static partial int sigaction(int signum, IntPtr sigAction, out SigAction oldAction); + private static partial int sigaction(int signum, nint sigAction, out SigAction oldAction); [LibraryImport("libc", SetLastError = true)] private static partial int sigemptyset(ref SigSet set); public static SigAction GetSegfaultExceptionHandler() { - int result = sigaction(SIGSEGV, IntPtr.Zero, out SigAction old); + int result = sigaction(SIGSEGV, nint.Zero, out SigAction old); if (result != 0) { @@ -45,7 +45,7 @@ namespace Ryujinx.Cpu.Signal return old; } - public static SigAction RegisterExceptionHandler(IntPtr action) + public static SigAction RegisterExceptionHandler(nint action) { SigAction sig = new() { diff --git a/src/Ryujinx.Cpu/Signal/WindowsSignalHandlerRegistration.cs b/src/Ryujinx.Cpu/Signal/WindowsSignalHandlerRegistration.cs index 1fbce0f72..7ac15b816 100644 --- a/src/Ryujinx.Cpu/Signal/WindowsSignalHandlerRegistration.cs +++ b/src/Ryujinx.Cpu/Signal/WindowsSignalHandlerRegistration.cs @@ -6,17 +6,17 @@ namespace Ryujinx.Cpu.Signal static partial class WindowsSignalHandlerRegistration { [LibraryImport("kernel32.dll")] - private static partial IntPtr AddVectoredExceptionHandler(uint first, IntPtr handler); + private static partial nint AddVectoredExceptionHandler(uint first, nint handler); [LibraryImport("kernel32.dll")] - private static partial ulong RemoveVectoredExceptionHandler(IntPtr handle); + private static partial ulong RemoveVectoredExceptionHandler(nint handle); - public static IntPtr RegisterExceptionHandler(IntPtr action) + public static nint RegisterExceptionHandler(nint action) { return AddVectoredExceptionHandler(1, action); } - public static bool RemoveExceptionHandler(IntPtr handle) + public static bool RemoveExceptionHandler(nint handle) { return RemoveVectoredExceptionHandler(handle) != 0; } diff --git a/src/Ryujinx.Graphics.Device/DeviceState.cs b/src/Ryujinx.Graphics.Device/DeviceState.cs index 54178a414..0dd4f5904 100644 --- a/src/Ryujinx.Graphics.Device/DeviceState.cs +++ b/src/Ryujinx.Graphics.Device/DeviceState.cs @@ -81,7 +81,7 @@ namespace Ryujinx.Graphics.Device { uint alignedOffset = index * RegisterSize; - var readCallback = Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_readCallbacks), (IntPtr)index); + var readCallback = Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_readCallbacks), (nint)index); if (readCallback != null) { return readCallback(); @@ -106,7 +106,7 @@ namespace Ryujinx.Graphics.Device GetRefIntAlignedUncheck(index) = data; - Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_writeCallbacks), (IntPtr)index)?.Invoke(data); + Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_writeCallbacks), (nint)index)?.Invoke(data); } } @@ -123,7 +123,7 @@ namespace Ryujinx.Graphics.Device changed = storage != data; storage = data; - Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_writeCallbacks), (IntPtr)index)?.Invoke(data); + Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_writeCallbacks), (nint)index)?.Invoke(data); } else { @@ -153,13 +153,13 @@ namespace Ryujinx.Graphics.Device [MethodImpl(MethodImplOptions.AggressiveInlining)] private ref T GetRefUnchecked(uint offset) where T : unmanaged { - return ref Unsafe.As(ref Unsafe.AddByteOffset(ref State, (IntPtr)offset)); + return ref Unsafe.As(ref Unsafe.AddByteOffset(ref State, (nint)offset)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private ref int GetRefIntAlignedUncheck(ulong index) { - return ref Unsafe.Add(ref Unsafe.As(ref State), (IntPtr)index); + return ref Unsafe.Add(ref Unsafe.As(ref State), (nint)index); } } } diff --git a/src/Ryujinx.Graphics.Device/Ryujinx.Graphics.Device.csproj b/src/Ryujinx.Graphics.Device/Ryujinx.Graphics.Device.csproj index 973a9e260..cbe276355 100644 --- a/src/Ryujinx.Graphics.Device/Ryujinx.Graphics.Device.csproj +++ b/src/Ryujinx.Graphics.Device/Ryujinx.Graphics.Device.csproj @@ -1,7 +1,7 @@  - net8.0 + $(DefaultItemExcludes);._* diff --git a/src/Ryujinx.Graphics.GAL/IRenderer.cs b/src/Ryujinx.Graphics.GAL/IRenderer.cs index 85d0bd729..c2fdcbe4b 100644 --- a/src/Ryujinx.Graphics.GAL/IRenderer.cs +++ b/src/Ryujinx.Graphics.GAL/IRenderer.cs @@ -14,6 +14,8 @@ namespace Ryujinx.Graphics.GAL IWindow Window { get; } + uint ProgramCount { get; } + void BackgroundContextAction(Action action, bool alwaysBackground = false); BufferHandle CreateBuffer(int size, BufferAccess access = BufferAccess.Default); diff --git a/src/Ryujinx.Graphics.GAL/IWindow.cs b/src/Ryujinx.Graphics.GAL/IWindow.cs index 83418e709..12686cb28 100644 --- a/src/Ryujinx.Graphics.GAL/IWindow.cs +++ b/src/Ryujinx.Graphics.GAL/IWindow.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.GAL void SetSize(int width, int height); - void ChangeVSyncMode(bool vsyncEnabled); + void ChangeVSyncMode(VSyncMode vSyncMode); void SetAntiAliasing(AntiAliasing antialiasing); void SetScalingFilter(ScalingFilter type); diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs index cc3d2e5c1..6375d290c 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs @@ -55,8 +55,10 @@ namespace Ryujinx.Graphics.GAL.Multithreading private int _refProducerPtr; private int _refConsumerPtr; + public uint ProgramCount { get; set; } = 0; + private Action _interruptAction; - private readonly object _interruptLock = new(); + private readonly Lock _interruptLock = new(); public event EventHandler ScreenCaptured; @@ -307,6 +309,8 @@ namespace Ryujinx.Graphics.GAL.Multithreading Programs.Add(request); + ProgramCount++; + New().Set(Ref((IProgramRequest)request)); QueueCommand(); diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs index acda37ef3..102fdb1bb 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs @@ -31,7 +31,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading _impl.Window.SetSize(width, height); } - public void ChangeVSyncMode(bool vsyncEnabled) { } + public void ChangeVSyncMode(VSyncMode vSyncMode) { } public void SetAntiAliasing(AntiAliasing effect) { } diff --git a/src/Ryujinx.Graphics.GAL/Ryujinx.Graphics.GAL.csproj b/src/Ryujinx.Graphics.GAL/Ryujinx.Graphics.GAL.csproj index d88b641a3..b94a4ec90 100644 --- a/src/Ryujinx.Graphics.GAL/Ryujinx.Graphics.GAL.csproj +++ b/src/Ryujinx.Graphics.GAL/Ryujinx.Graphics.GAL.csproj @@ -1,7 +1,7 @@  - net8.0 + $(DefaultItemExcludes);._* diff --git a/src/Ryujinx.Graphics.GAL/VSyncMode.cs b/src/Ryujinx.Graphics.GAL/VSyncMode.cs new file mode 100644 index 000000000..c5794b8f7 --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/VSyncMode.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum VSyncMode + { + Switch, + Unbounded, + Custom + } +} diff --git a/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs b/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs index 43b701293..e3080228e 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLETable.cs @@ -96,7 +96,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME { ref var entry = ref _table[i]; - var hash = XXHash128.ComputeHash(mc[..entry.Length]); + var hash = Hash128.ComputeHash(mc[..entry.Length]); if (hash == entry.Hash) { if (IsMacroHLESupported(caps, entry.Name)) diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendFunctions.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendFunctions.cs index 0aca39075..13e5d2a86 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendFunctions.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendFunctions.cs @@ -223,7 +223,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender foreach (var entry in Table) { - Hash128 hash = XXHash128.ComputeHash(MemoryMarshal.Cast(entry.Code)); + Hash128 hash = Hash128.ComputeHash(MemoryMarshal.Cast(entry.Code)); string[] constants = new string[entry.Constants != null ? entry.Constants.Length : 0]; diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendManager.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendManager.cs index b336382d4..ce3d2c236 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendManager.cs @@ -62,7 +62,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender currentCode = currentCode[..codeLength]; } - Hash128 hash = XXHash128.ComputeHash(MemoryMarshal.Cast(currentCode)); + Hash128 hash = Hash128.ComputeHash(MemoryMarshal.Cast(currentCode)); descriptor = default; diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs index effcb7bbb..ea9fc9e31 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs @@ -109,7 +109,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed if (index < BlockSize) { - int groupIndex = Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_registerToGroupMapping), (IntPtr)index); + int groupIndex = Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_registerToGroupMapping), (nint)index); if (groupIndex != 0) { groupIndex--; diff --git a/src/Ryujinx.Graphics.Gpu/GpuContext.cs b/src/Ryujinx.Graphics.Gpu/GpuContext.cs index 048d32fb7..fb529e914 100644 --- a/src/Ryujinx.Graphics.Gpu/GpuContext.cs +++ b/src/Ryujinx.Graphics.Gpu/GpuContext.cs @@ -152,16 +152,17 @@ namespace Ryujinx.Graphics.Gpu /// Creates a new GPU memory manager. /// /// ID of the process that owns the memory manager + /// The amount of physical CPU Memory Avaiable on the device. /// The memory manager /// Thrown when is invalid - public MemoryManager CreateMemoryManager(ulong pid) + public MemoryManager CreateMemoryManager(ulong pid, ulong cpuMemorySize) { if (!PhysicalMemoryRegistry.TryGetValue(pid, out var physicalMemory)) { throw new ArgumentException("The PID is invalid or the process was not registered", nameof(pid)); } - return new MemoryManager(physicalMemory); + return new MemoryManager(physicalMemory, cpuMemorySize); } /// diff --git a/src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs b/src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs index ad6c1fecb..74967b190 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.Logging; using System; using System.Collections; using System.Collections.Generic; @@ -47,11 +48,17 @@ namespace Ryujinx.Graphics.Gpu.Image { private const int MinCountForDeletion = 32; private const int MaxCapacity = 2048; + private const ulong GiB = 1024 * 1024 * 1024; + private ulong MaxTextureSizeCapacity = 4UL * GiB; private const ulong MinTextureSizeCapacity = 512 * 1024 * 1024; - private const ulong MaxTextureSizeCapacity = 4UL * 1024 * 1024 * 1024; - private const ulong DefaultTextureSizeCapacity = 1UL * 1024 * 1024 * 1024; + private const ulong DefaultTextureSizeCapacity = 1 * GiB; + private const ulong TextureSizeCapacity6GiB = 4 * GiB; + private const ulong TextureSizeCapacity8GiB = 6 * GiB; + private const ulong TextureSizeCapacity12GiB = 12 * GiB; + + private const float MemoryScaleFactor = 0.50f; - private ulong _maxCacheMemoryUsage = 0; + private ulong _maxCacheMemoryUsage = DefaultTextureSizeCapacity; private readonly LinkedList _textures; private ulong _totalSize; @@ -66,18 +73,38 @@ namespace Ryujinx.Graphics.Gpu.Image /// /// /// If the backend GPU has 0 memory capacity, the cache size defaults to `DefaultTextureSizeCapacity`. + /// + /// Reads the current Device total CPU Memory, to determine the maximum amount of Vram available. Capped to 50% of Current GPU Memory. /// /// The GPU context that the cache belongs to - public void Initialize(GpuContext context) + /// The amount of physical CPU Memory Avaiable on the device. + public void Initialize(GpuContext context, ulong cpuMemorySize) { + var cpuMemorySizeGiB = cpuMemorySize / GiB; + + if (cpuMemorySizeGiB < 6 || context.Capabilities.MaximumGpuMemory == 0) + { + _maxCacheMemoryUsage = DefaultTextureSizeCapacity; + return; + } + else if (cpuMemorySizeGiB == 6) + { + MaxTextureSizeCapacity = TextureSizeCapacity6GiB; + } + else if (cpuMemorySizeGiB == 8) + { + MaxTextureSizeCapacity = TextureSizeCapacity8GiB; + } + else + { + MaxTextureSizeCapacity = TextureSizeCapacity12GiB; + } + var cacheMemory = (ulong)(context.Capabilities.MaximumGpuMemory * MemoryScaleFactor); _maxCacheMemoryUsage = Math.Clamp(cacheMemory, MinTextureSizeCapacity, MaxTextureSizeCapacity); - if (context.Capabilities.MaximumGpuMemory == 0) - { - _maxCacheMemoryUsage = DefaultTextureSizeCapacity; - } + Logger.Info?.Print(LogClass.Gpu, $"AutoDelete Cache Allocated VRAM : {_maxCacheMemoryUsage / GiB} GiB"); } /// diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs index 1587e2018..ff7f11142 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs @@ -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 @@ -71,9 +72,10 @@ namespace Ryujinx.Graphics.Gpu.Image /// /// Initializes the cache, setting the maximum texture capacity for the specified GPU context. /// - public void Initialize() + /// The amount of physical CPU Memory Avaiable on the device. + public void Initialize(ulong cpuMemorySize) { - _cache.Initialize(_context); + _cache.Initialize(_context, cpuMemorySize); } /// @@ -997,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)); } diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs index 526fc0c24..2db5c6290 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs @@ -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 /// True if the overlap should register copy dependencies 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 } } } + diff --git a/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs b/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs index be7cb0b89..3bf122412 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs @@ -721,7 +721,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// The format of the texture /// The texture swizzle components /// The depth-stencil mode - private static DepthStencilMode GetDepthStencilMode(Format format, params SwizzleComponent[] components) + private static DepthStencilMode GetDepthStencilMode(Format format, params ReadOnlySpan components) { // R = Depth, G = Stencil. // On 24-bits depth formats, this is inverted (Stencil is R etc). diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs index d330de638..c5a12c1fc 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs @@ -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(); /// /// 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); diff --git a/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs b/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs index d1065431d..59e618c02 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs @@ -55,7 +55,8 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Creates a new instance of the GPU memory manager. /// /// Physical memory that this memory manager will map into - internal MemoryManager(PhysicalMemory physicalMemory) + /// The amount of physical CPU Memory Avaiable on the device. + internal MemoryManager(PhysicalMemory physicalMemory, ulong cpuMemorySize) { Physical = physicalMemory; VirtualRangeCache = new VirtualRangeCache(this); @@ -65,7 +66,7 @@ namespace Ryujinx.Graphics.Gpu.Memory MemoryUnmapped += Physical.BufferCache.MemoryUnmappedHandler; MemoryUnmapped += VirtualRangeCache.MemoryUnmappedHandler; MemoryUnmapped += CounterCache.MemoryUnmappedHandler; - Physical.TextureCache.Initialize(); + Physical.TextureCache.Initialize(cpuMemorySize); } /// diff --git a/src/Ryujinx.Graphics.Gpu/Ryujinx.Graphics.Gpu.csproj b/src/Ryujinx.Graphics.Gpu/Ryujinx.Graphics.Gpu.csproj index 6f1cce6ac..4cd9c1d5c 100644 --- a/src/Ryujinx.Graphics.Gpu/Ryujinx.Graphics.Gpu.csproj +++ b/src/Ryujinx.Graphics.Gpu/Ryujinx.Graphics.Gpu.csproj @@ -1,8 +1,8 @@  - net8.0 true + $(DefaultItemExcludes);._* diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGuestStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGuestStorage.cs index 08cd3bb02..22af88d31 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGuestStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheGuestStorage.cs @@ -453,7 +453,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache /// Hash of the data private static uint CalcHash(ReadOnlySpan data) { - return (uint)XXHash128.ComputeHash(data).Low; + return (uint)Hash128.ComputeHash(data).Low; } } } diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs index 98acb6f27..1230c0580 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs @@ -743,7 +743,7 @@ namespace Ryujinx.Graphics.Gpu.Shader constantBufferUsePerStageMask &= ~(1 << index); } - if (checkTextures) + if (checkTextures && _allTextures.Length > 0) { TexturePool pool = channel.TextureManager.GetTexturePool(poolState.TexturePoolGpuVa, poolState.TexturePoolMaximumId); diff --git a/src/Ryujinx.Graphics.Host1x/Ryujinx.Graphics.Host1x.csproj b/src/Ryujinx.Graphics.Host1x/Ryujinx.Graphics.Host1x.csproj index d631d039f..3000d41de 100644 --- a/src/Ryujinx.Graphics.Host1x/Ryujinx.Graphics.Host1x.csproj +++ b/src/Ryujinx.Graphics.Host1x/Ryujinx.Graphics.Host1x.csproj @@ -1,7 +1,7 @@ - net8.0 + $(DefaultItemExcludes);._* diff --git a/src/Ryujinx.Graphics.Nvdec.FFmpeg/FFmpegContext.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/FFmpegContext.cs index 0767cc9d6..5c9e3989b 100644 --- a/src/Ryujinx.Graphics.Nvdec.FFmpeg/FFmpegContext.cs +++ b/src/Ryujinx.Graphics.Nvdec.FFmpeg/FFmpegContext.cs @@ -52,7 +52,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg int avCodecMajorVersion = avCodecRawVersion >> 16; int avCodecMinorVersion = (avCodecRawVersion >> 8) & 0xFF; - // libavcodec 59.24 changed AvCodec to move its private API and also move the codec function to an union. + // libavcodec 59.24 changed AvCodec to move its private API and also move the codec function to a union. if (avCodecMajorVersion > 59 || (avCodecMajorVersion == 59 && avCodecMinorVersion > 24)) { _decodeFrame = Marshal.GetDelegateForFunctionPointer(((FFCodec*)_codec)->CodecCallback); @@ -91,7 +91,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg FFmpegApi.av_log_format_line(ptr, level, format, vl, lineBuffer, lineSize, &printPrefix); - string line = Marshal.PtrToStringAnsi((IntPtr)lineBuffer).Trim(); + string line = Marshal.PtrToStringAnsi((nint)lineBuffer).Trim(); switch (level) { diff --git a/src/Ryujinx.Graphics.Nvdec.FFmpeg/H264/Decoder.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/H264/Decoder.cs index 14877dd55..3ec99192d 100644 --- a/src/Ryujinx.Graphics.Nvdec.FFmpeg/H264/Decoder.cs +++ b/src/Ryujinx.Graphics.Nvdec.FFmpeg/H264/Decoder.cs @@ -26,8 +26,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.H264 { Surface outSurf = (Surface)output; - if (outSurf.RequestedWidth != _oldOutputWidth || - outSurf.RequestedHeight != _oldOutputHeight) + if (outSurf.RequestedWidth != _oldOutputWidth || outSurf.RequestedHeight != _oldOutputHeight) { _context.Dispose(); _context = new FFmpegContext(AVCodecID.AV_CODEC_ID_H264); @@ -38,7 +37,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.H264 Span bs = Prepend(bitstream, SpsAndPpsReconstruction.Reconstruct(ref pictureInfo, _workBuffer)); - return _context.DecodeFrame(outSurf, bs) == 0; + return _context.DecodeFrame(outSurf, bs) is 0; } private static byte[] Prepend(ReadOnlySpan data, ReadOnlySpan prep) diff --git a/src/Ryujinx.Graphics.Nvdec.FFmpeg/H264/H264BitStreamWriter.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/H264/H264BitStreamWriter.cs index 57ab9fb53..5cb7a7234 100644 --- a/src/Ryujinx.Graphics.Nvdec.FFmpeg/H264/H264BitStreamWriter.cs +++ b/src/Ryujinx.Graphics.Nvdec.FFmpeg/H264/H264BitStreamWriter.cs @@ -3,23 +3,13 @@ using System.Numerics; namespace Ryujinx.Graphics.Nvdec.FFmpeg.H264 { - struct H264BitStreamWriter + struct H264BitStreamWriter(byte[] workBuffer) { private const int BufferSize = 8; - private readonly byte[] _workBuffer; - - private int _offset; - private int _buffer; - private int _bufferPos; - - public H264BitStreamWriter(byte[] workBuffer) - { - _workBuffer = workBuffer; - _offset = 0; - _buffer = 0; - _bufferPos = 0; - } + private int _offset = 0; + private int _buffer = 0; + private int _bufferPos = 0; public void WriteBit(bool value) { @@ -59,9 +49,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.H264 private int GetFreeBufferBits() { if (_bufferPos == BufferSize) - { Flush(); - } return BufferSize - _bufferPos; } @@ -70,7 +58,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.H264 { if (_bufferPos != 0) { - _workBuffer[_offset++] = (byte)_buffer; + workBuffer[_offset++] = (byte)_buffer; _buffer = 0; _bufferPos = 0; @@ -85,9 +73,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.H264 } public readonly Span AsSpan() - { - return new Span(_workBuffer)[.._offset]; - } + => new Span(workBuffer)[.._offset]; public void WriteU(uint value, int valueSize) => WriteBits((int)value, valueSize); public void WriteSe(int value) => WriteExpGolombCodedInt(value); diff --git a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodec.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodec.cs index 0267000c8..b5ef710b1 100644 --- a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodec.cs +++ b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodec.cs @@ -12,15 +12,15 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native public int Capabilities; public byte MaxLowRes; public unsafe AVRational* SupportedFramerates; - public IntPtr PixFmts; - public IntPtr SupportedSamplerates; - public IntPtr SampleFmts; + public nint PixFmts; + public nint SupportedSamplerates; + public nint SampleFmts; // Deprecated public unsafe ulong* ChannelLayouts; - public unsafe IntPtr PrivClass; - public IntPtr Profiles; + public unsafe nint PrivClass; + public nint Profiles; public unsafe byte* WrapperName; - public IntPtr ChLayouts; + public nint ChLayouts; #pragma warning restore CS0649 } } diff --git a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodec501.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodec501.cs index 9084f4024..d745e9f04 100644 --- a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodec501.cs +++ b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodec501.cs @@ -12,13 +12,13 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native public int Capabilities; public byte MaxLowRes; public unsafe AVRational* SupportedFramerates; - public IntPtr PixFmts; - public IntPtr SupportedSamplerates; - public IntPtr SampleFmts; + public nint PixFmts; + public nint SupportedSamplerates; + public nint SampleFmts; // Deprecated public unsafe ulong* ChannelLayouts; - public unsafe IntPtr PrivClass; - public IntPtr Profiles; + public unsafe nint PrivClass; + public nint Profiles; public unsafe byte* WrapperName; #pragma warning restore CS0649 } diff --git a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodecContext.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodecContext.cs index c743ab33e..1de0a13e4 100644 --- a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodecContext.cs +++ b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVCodecContext.cs @@ -6,22 +6,22 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native struct AVCodecContext { #pragma warning disable CS0649 // Field is never assigned to - public unsafe IntPtr AvClass; + public unsafe nint AvClass; public int LogLevelOffset; public int CodecType; public unsafe AVCodec* Codec; public AVCodecID CodecId; public uint CodecTag; - public IntPtr PrivData; - public IntPtr Internal; - public IntPtr Opaque; + public nint PrivData; + public nint Internal; + public nint Opaque; public long BitRate; public int BitRateTolerance; public int GlobalQuality; public int CompressionLevel; public int Flags; public int Flags2; - public IntPtr ExtraData; + public nint ExtraData; public int ExtraDataSize; public AVRational TimeBase; public int TicksPerFrame; @@ -32,8 +32,8 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native public int CodedHeight; public int GopSize; public int PixFmt; - public IntPtr DrawHorizBand; - public IntPtr GetFormat; + public nint DrawHorizBand; + public nint GetFormat; public int MaxBFrames; public float BQuantFactor; public float BQuantOffset; @@ -46,7 +46,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native public float PMasking; public float DarkMasking; public int SliceCount; - public IntPtr SliceOffset; + public nint SliceOffset; public AVRational SampleAspectRatio; public int MeCmp; public int MeSubCmp; @@ -60,8 +60,8 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native public int MeRange; public int SliceFlags; public int MbDecision; - public IntPtr IntraMatrix; - public IntPtr InterMatrix; + public nint IntraMatrix; + public nint InterMatrix; public int IntraDcPrecision; public int SkipTop; public int SkipBottom; @@ -89,7 +89,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native public ulong RequestChannelLayout; public int AudioServiceType; public int RequestSampleFmt; - public IntPtr GetBuffer2; + public nint GetBuffer2; public float QCompress; public float QBlur; public int QMin; @@ -97,23 +97,23 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native public int MaxQdiff; public int RcBufferSize; public int RcOverrideCount; - public IntPtr RcOverride; + public nint RcOverride; public long RcMaxRate; public long RcMinRate; public float RcMax_available_vbv_use; public float RcMin_vbv_overflow_use; public int RcInitialBufferOccupancy; public int Trellis; - public IntPtr StatsOut; - public IntPtr StatsIn; + public nint StatsOut; + public nint StatsIn; public int WorkaroundBugs; public int StrictStdCompliance; public int ErrorConcealment; public int Debug; public int ErrRecognition; public long ReorderedOpaque; - public IntPtr HwAccel; - public IntPtr HwAccelContext; + public nint HwAccel; + public nint HwAccelContext; public Array8 Error; public int DctAlgo; public int IdctAlgo; @@ -124,48 +124,48 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native public int ThreadType; public int ActiveThreadType; public int ThreadSafeCallbacks; - public IntPtr Execute; - public IntPtr Execute2; + public nint Execute; + public nint Execute2; public int NsseWeight; public int Profile; public int Level; public int SkipLoopFilter; public int SkipIdct; public int SkipFrame; - public IntPtr SubtitleHeader; + public nint SubtitleHeader; public int SubtitleHeaderSize; public int InitialPadding; public AVRational Framerate; public int SwPixFmt; public AVRational PktTimebase; - public IntPtr CodecDescriptor; + public nint CodecDescriptor; public long PtsCorrectionNumFaultyPts; public long PtsCorrectionNumFaultyDts; public long PtsCorrectionLastPts; public long PtsCorrectionLastDts; - public IntPtr SubCharenc; + public nint SubCharenc; public int SubCharencMode; public int SkipAlpha; public int SeekPreroll; public int DebugMv; - public IntPtr ChromaIntraMatrix; - public IntPtr DumpSeparator; - public IntPtr CodecWhitelist; + public nint ChromaIntraMatrix; + public nint DumpSeparator; + public nint CodecWhitelist; public uint Properties; - public IntPtr CodedSideData; + public nint CodedSideData; public int NbCodedSideData; - public IntPtr HwFramesCtx; + public nint HwFramesCtx; public int SubTextFormat; public int TrailingPadding; public long MaxPixels; - public IntPtr HwDeviceCtx; + public nint HwDeviceCtx; public int HwAccelFlags; public int applyCropping; public int ExtraHwFrames; public int DiscardDamagedPercentage; public long MaxSamples; public int ExportSideData; - public IntPtr GetEncodeBuffer; + public nint GetEncodeBuffer; #pragma warning restore CS0649 } } diff --git a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVFrame.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVFrame.cs index a1eb7a090..97c30c718 100644 --- a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVFrame.cs +++ b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/AVFrame.cs @@ -6,9 +6,9 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native struct AVFrame { #pragma warning disable CS0649 // Field is never assigned to - public Array8 Data; + public Array8 Data; public Array8 LineSize; - public IntPtr ExtendedData; + public nint ExtendedData; public int Width; public int Height; public int NumSamples; @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native public int CodedPictureNumber; public int DisplayPictureNumber; public int Quality; - public IntPtr Opaque; + public nint Opaque; public int RepeatPicture; public int InterlacedFrame; public int TopFieldFirst; diff --git a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFCodec.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFCodec.cs index ceb8a3b01..95926298c 100644 --- a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFCodec.cs +++ b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFCodec.cs @@ -8,12 +8,12 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native public T Base; public int CapsInternalOrCbType; public int PrivDataSize; - public IntPtr UpdateThreadContext; - public IntPtr UpdateThreadContextForUser; - public IntPtr Defaults; - public IntPtr InitStaticData; - public IntPtr Init; - public IntPtr CodecCallback; + public nint UpdateThreadContext; + public nint UpdateThreadContextForUser; + public nint Defaults; + public nint InitStaticData; + public nint Init; + public nint CodecCallback; #pragma warning restore CS0649 // NOTE: There is more after, but the layout kind of changed a bit and we don't need more than this. This is safe as we only manipulate this behind a reference. diff --git a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFCodecLegacy.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFCodecLegacy.cs index 03eba311c..873d2518a 100644 --- a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFCodecLegacy.cs +++ b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFCodecLegacy.cs @@ -8,14 +8,14 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native public T Base; public uint CapsInternalOrCbType; public int PrivDataSize; - public IntPtr UpdateThreadContext; - public IntPtr UpdateThreadContextForUser; - public IntPtr Defaults; - public IntPtr InitStaticData; - public IntPtr Init; - public IntPtr EncodeSub; - public IntPtr Encode2; - public IntPtr Decode; + public nint UpdateThreadContext; + public nint UpdateThreadContextForUser; + public nint Defaults; + public nint InitStaticData; + public nint Init; + public nint EncodeSub; + public nint Encode2; + public nint Decode; #pragma warning restore CS0649 // NOTE: There is more after, but the layout kind of changed a bit and we don't need more than this. This is safe as we only manipulate this behind a reference. diff --git a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFmpegApi.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFmpegApi.cs index 5167ff9fe..7b0c2a8ad 100644 --- a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFmpegApi.cs +++ b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFmpegApi.cs @@ -37,9 +37,9 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native } - private static bool TryLoadWhitelistedLibrary(string libraryName, Assembly assembly, DllImportSearchPath? searchPath, out IntPtr handle) + private static bool TryLoadWhitelistedLibrary(string libraryName, Assembly assembly, DllImportSearchPath? searchPath, out nint handle) { - handle = IntPtr.Zero; + handle = nint.Zero; if (_librariesWhitelist.TryGetValue(libraryName, out var value)) { @@ -71,7 +71,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native return handle; } - return IntPtr.Zero; + return nint.Zero; }); } diff --git a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Ryujinx.Graphics.Nvdec.FFmpeg.csproj b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Ryujinx.Graphics.Nvdec.FFmpeg.csproj index d1a6358c2..9e250d171 100644 --- a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Ryujinx.Graphics.Nvdec.FFmpeg.csproj +++ b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Ryujinx.Graphics.Nvdec.FFmpeg.csproj @@ -1,8 +1,8 @@  - net8.0 true + $(DefaultItemExcludes);._* diff --git a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Surface.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Surface.cs index 65fb7b4ad..c13cfe1aa 100644 --- a/src/Ryujinx.Graphics.Nvdec.FFmpeg/Surface.cs +++ b/src/Ryujinx.Graphics.Nvdec.FFmpeg/Surface.cs @@ -11,9 +11,9 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg public int RequestedWidth { get; } public int RequestedHeight { get; } - public Plane YPlane => new((IntPtr)Frame->Data[0], Stride * Height); - public Plane UPlane => new((IntPtr)Frame->Data[1], UvStride * UvHeight); - public Plane VPlane => new((IntPtr)Frame->Data[2], UvStride * UvHeight); + public Plane YPlane => new((nint)Frame->Data[0], Stride * Height); + public Plane UPlane => new((nint)Frame->Data[1], UvStride * UvHeight); + public Plane VPlane => new((nint)Frame->Data[2], UvStride * UvHeight); public FrameField Field => Frame->InterlacedFrame != 0 ? FrameField.Interlaced : FrameField.Progressive; diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Common/MemoryAllocator.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Common/MemoryAllocator.cs index c75cfeb0f..18ed172f8 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Common/MemoryAllocator.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Common/MemoryAllocator.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Common private struct PoolItem { - public IntPtr Pointer; + public nint Pointer; public int Length; public bool InUse; } @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Common { int lengthInBytes = Unsafe.SizeOf() * length; - IntPtr ptr = IntPtr.Zero; + nint ptr = nint.Zero; for (int i = 0; i < PoolEntries; i++) { @@ -36,7 +36,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Common } } - if (ptr == IntPtr.Zero) + if (ptr == nint.Zero) { ptr = Marshal.AllocHGlobal(lengthInBytes); @@ -47,7 +47,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Common if (!item.InUse) { item.InUse = true; - if (item.Pointer != IntPtr.Zero) + if (item.Pointer != nint.Zero) { Marshal.FreeHGlobal(item.Pointer); } @@ -63,7 +63,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Common public unsafe void Free(ArrayPtr arr) where T : unmanaged { - IntPtr ptr = (IntPtr)arr.ToPointer(); + nint ptr = (nint)arr.ToPointer(); for (int i = 0; i < PoolEntries; i++) { @@ -83,10 +83,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Common { ref PoolItem item = ref _pool[i]; - if (item.Pointer != IntPtr.Zero) + if (item.Pointer != nint.Zero) { Marshal.FreeHGlobal(item.Pointer); - item.Pointer = IntPtr.Zero; + item.Pointer = nint.Zero; } } } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Ryujinx.Graphics.Nvdec.Vp9.csproj b/src/Ryujinx.Graphics.Nvdec.Vp9/Ryujinx.Graphics.Nvdec.Vp9.csproj index d1a6358c2..9e250d171 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Ryujinx.Graphics.Nvdec.Vp9.csproj +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Ryujinx.Graphics.Nvdec.Vp9.csproj @@ -1,8 +1,8 @@  - net8.0 true + $(DefaultItemExcludes);._* diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Surface.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Surface.cs index 372b1d2b4..d9bda185b 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Surface.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Surface.cs @@ -11,9 +11,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types public ArrayPtr UBuffer; public ArrayPtr VBuffer; - public readonly unsafe Plane YPlane => new((IntPtr)YBuffer.ToPointer(), YBuffer.Length); - public readonly unsafe Plane UPlane => new((IntPtr)UBuffer.ToPointer(), UBuffer.Length); - public readonly unsafe Plane VPlane => new((IntPtr)VBuffer.ToPointer(), VBuffer.Length); + public readonly unsafe Plane YPlane => new((nint)YBuffer.ToPointer(), YBuffer.Length); + public readonly unsafe Plane UPlane => new((nint)UBuffer.ToPointer(), UBuffer.Length); + public readonly unsafe Plane VPlane => new((nint)VBuffer.ToPointer(), VBuffer.Length); public readonly FrameField Field => FrameField.Progressive; @@ -30,7 +30,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types public bool HighBd { get; } - private readonly IntPtr _pointer; + private readonly nint _pointer; public Surface(int width, int height) { @@ -53,7 +53,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types int frameSize = (HighBd ? 2 : 1) * (yplaneSize + 2 * uvplaneSize); - IntPtr pointer = Marshal.AllocHGlobal(frameSize); + nint pointer = Marshal.AllocHGlobal(frameSize); _pointer = pointer; Width = width; Height = height; diff --git a/src/Ryujinx.Graphics.Nvdec/Ryujinx.Graphics.Nvdec.csproj b/src/Ryujinx.Graphics.Nvdec/Ryujinx.Graphics.Nvdec.csproj index 6c00e9a7c..d020ae50e 100644 --- a/src/Ryujinx.Graphics.Nvdec/Ryujinx.Graphics.Nvdec.csproj +++ b/src/Ryujinx.Graphics.Nvdec/Ryujinx.Graphics.Nvdec.csproj @@ -1,8 +1,8 @@  - net8.0 true + $(DefaultItemExcludes);._* diff --git a/src/Ryujinx.Graphics.OpenGL/Buffer.cs b/src/Ryujinx.Graphics.OpenGL/Buffer.cs index 2a5143101..33ca25174 100644 --- a/src/Ryujinx.Graphics.OpenGL/Buffer.cs +++ b/src/Ryujinx.Graphics.OpenGL/Buffer.cs @@ -19,11 +19,11 @@ namespace Ryujinx.Graphics.OpenGL GL.ClearBufferSubData( BufferTarget.CopyWriteBuffer, PixelInternalFormat.Rgba8ui, - (IntPtr)offset, - (IntPtr)size, + (nint)offset, + (nint)size, PixelFormat.RgbaInteger, PixelType.UnsignedByte, - (IntPtr)valueArr); + (nint)valueArr); } } @@ -37,7 +37,7 @@ namespace Ryujinx.Graphics.OpenGL int handle = GL.GenBuffer(); GL.BindBuffer(BufferTarget.CopyWriteBuffer, handle); - GL.BufferData(BufferTarget.CopyWriteBuffer, size, IntPtr.Zero, BufferUsageHint.DynamicDraw); + GL.BufferData(BufferTarget.CopyWriteBuffer, size, nint.Zero, BufferUsageHint.DynamicDraw); return Handle.FromInt32(handle); } @@ -47,7 +47,7 @@ namespace Ryujinx.Graphics.OpenGL int handle = GL.GenBuffer(); GL.BindBuffer(BufferTarget.CopyWriteBuffer, handle); - GL.BufferStorage(BufferTarget.CopyWriteBuffer, size, IntPtr.Zero, + GL.BufferStorage(BufferTarget.CopyWriteBuffer, size, nint.Zero, BufferStorageFlags.MapPersistentBit | BufferStorageFlags.MapCoherentBit | BufferStorageFlags.ClientStorageBit | @@ -64,9 +64,9 @@ namespace Ryujinx.Graphics.OpenGL GL.CopyBufferSubData( BufferTarget.CopyReadBuffer, BufferTarget.CopyWriteBuffer, - (IntPtr)srcOffset, - (IntPtr)dstOffset, - (IntPtr)size); + (nint)srcOffset, + (nint)dstOffset, + (nint)size); } public static unsafe PinnedSpan GetData(OpenGLRenderer renderer, BufferHandle buffer, int offset, int size) @@ -74,9 +74,9 @@ namespace Ryujinx.Graphics.OpenGL // Data in the persistent buffer and host array is guaranteed to be available // until the next time the host thread requests data. - if (renderer.PersistentBuffers.TryGet(buffer, out IntPtr ptr)) + if (renderer.PersistentBuffers.TryGet(buffer, out nint ptr)) { - return new PinnedSpan(IntPtr.Add(ptr, offset).ToPointer(), size); + return new PinnedSpan(nint.Add(ptr, offset).ToPointer(), size); } else if (HwCapabilities.UsePersistentBufferForFlush) { @@ -84,11 +84,11 @@ namespace Ryujinx.Graphics.OpenGL } else { - IntPtr target = renderer.PersistentBuffers.Default.GetHostArray(size); + nint target = renderer.PersistentBuffers.Default.GetHostArray(size); GL.BindBuffer(BufferTarget.CopyReadBuffer, buffer.ToInt32()); - GL.GetBufferSubData(BufferTarget.CopyReadBuffer, (IntPtr)offset, size, target); + GL.GetBufferSubData(BufferTarget.CopyReadBuffer, (nint)offset, size, target); return new PinnedSpan(target.ToPointer(), size); } @@ -97,7 +97,7 @@ namespace Ryujinx.Graphics.OpenGL public static void Resize(BufferHandle handle, int size) { GL.BindBuffer(BufferTarget.CopyWriteBuffer, handle.ToInt32()); - GL.BufferData(BufferTarget.CopyWriteBuffer, size, IntPtr.Zero, BufferUsageHint.StreamCopy); + GL.BufferData(BufferTarget.CopyWriteBuffer, size, nint.Zero, BufferUsageHint.StreamCopy); } public static void SetData(BufferHandle buffer, int offset, ReadOnlySpan data) @@ -108,7 +108,7 @@ namespace Ryujinx.Graphics.OpenGL { fixed (byte* ptr = data) { - GL.BufferSubData(BufferTarget.CopyWriteBuffer, (IntPtr)offset, data.Length, (IntPtr)ptr); + GL.BufferSubData(BufferTarget.CopyWriteBuffer, (nint)offset, data.Length, (nint)ptr); } } } diff --git a/src/Ryujinx.Graphics.OpenGL/Debugger.cs b/src/Ryujinx.Graphics.OpenGL/Debugger.cs index 7606bdbfd..c700b3b7c 100644 --- a/src/Ryujinx.Graphics.OpenGL/Debugger.cs +++ b/src/Ryujinx.Graphics.OpenGL/Debugger.cs @@ -21,7 +21,7 @@ namespace Ryujinx.Graphics.OpenGL if (logLevel == GraphicsDebugLevel.None) { GL.Disable(EnableCap.DebugOutputSynchronous); - GL.DebugMessageCallback(null, IntPtr.Zero); + GL.DebugMessageCallback(null, nint.Zero); return; } @@ -45,7 +45,7 @@ namespace Ryujinx.Graphics.OpenGL _counter = 0; _debugCallback = GLDebugHandler; - GL.DebugMessageCallback(_debugCallback, IntPtr.Zero); + GL.DebugMessageCallback(_debugCallback, nint.Zero); Logger.Warning?.Print(LogClass.Gpu, "OpenGL Debugging is enabled. Performance will be negatively impacted."); } @@ -56,8 +56,8 @@ namespace Ryujinx.Graphics.OpenGL int id, DebugSeverity severity, int length, - IntPtr message, - IntPtr userParam) + nint message, + nint userParam) { string msg = Marshal.PtrToStringUTF8(message).Replace('\n', ' '); diff --git a/src/Ryujinx.Graphics.OpenGL/Helper/GLXHelper.cs b/src/Ryujinx.Graphics.OpenGL/Helper/GLXHelper.cs index ce2b20f7d..b722bbf04 100644 --- a/src/Ryujinx.Graphics.OpenGL/Helper/GLXHelper.cs +++ b/src/Ryujinx.Graphics.OpenGL/Helper/GLXHelper.cs @@ -15,14 +15,14 @@ namespace Ryujinx.Graphics.OpenGL.Helper { if (name != LibraryName) { - return IntPtr.Zero; + return nint.Zero; } - if (!NativeLibrary.TryLoad("libGL.so.1", assembly, path, out IntPtr result)) + if (!NativeLibrary.TryLoad("libGL.so.1", assembly, path, out nint result)) { if (!NativeLibrary.TryLoad("libGL.so", assembly, path, out result)) { - return IntPtr.Zero; + return nint.Zero; } } @@ -31,6 +31,6 @@ namespace Ryujinx.Graphics.OpenGL.Helper } [LibraryImport(LibraryName, EntryPoint = "glXGetCurrentContext")] - public static partial IntPtr GetCurrentContext(); + public static partial nint GetCurrentContext(); } } diff --git a/src/Ryujinx.Graphics.OpenGL/Helper/WGLHelper.cs b/src/Ryujinx.Graphics.OpenGL/Helper/WGLHelper.cs index be12ff999..7072bbd9f 100644 --- a/src/Ryujinx.Graphics.OpenGL/Helper/WGLHelper.cs +++ b/src/Ryujinx.Graphics.OpenGL/Helper/WGLHelper.cs @@ -10,6 +10,6 @@ namespace Ryujinx.Graphics.OpenGL.Helper private const string LibraryName = "OPENGL32.DLL"; [LibraryImport(LibraryName, EntryPoint = "wglGetCurrentContext")] - public static partial IntPtr GetCurrentContext(); + public static partial nint GetCurrentContext(); } } diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs index 22f4c04cd..8e728a2bb 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs @@ -97,7 +97,7 @@ namespace Ryujinx.Graphics.OpenGL.Image SizedInternalFormat format = (SizedInternalFormat)FormatTable.GetFormatInfo(Info.Format).PixelInternalFormat; - GL.TexBufferRange(TextureBufferTarget.TextureBuffer, format, _buffer.ToInt32(), (IntPtr)buffer.Offset, buffer.Size); + GL.TexBufferRange(TextureBufferTarget.TextureBuffer, format, _buffer.ToInt32(), (nint)buffer.Offset, buffer.Size); } public void Dispose() diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs index 89bd5e4ff..e08da7013 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs @@ -464,7 +464,7 @@ namespace Ryujinx.Graphics.OpenGL.Image _copyPboSize = requiredSize; GL.BindBuffer(BufferTarget.PixelPackBuffer, _copyPboHandle); - GL.BufferData(BufferTarget.PixelPackBuffer, requiredSize, IntPtr.Zero, BufferUsageHint.DynamicCopy); + GL.BufferData(BufferTarget.PixelPackBuffer, requiredSize, nint.Zero, BufferUsageHint.DynamicCopy); } } diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs index b0859c49e..a89dd5131 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs @@ -282,7 +282,7 @@ namespace Ryujinx.Graphics.OpenGL.Image } else { - IntPtr target = _renderer.PersistentBuffers.Default.GetHostArray(size); + nint target = _renderer.PersistentBuffers.Default.GetHostArray(size); WriteTo(target); @@ -307,7 +307,7 @@ namespace Ryujinx.Graphics.OpenGL.Image } else { - IntPtr target = _renderer.PersistentBuffers.Default.GetHostArray(size); + nint target = _renderer.PersistentBuffers.Default.GetHostArray(size); int offset = WriteTo2D(target, layer, level); @@ -339,15 +339,15 @@ namespace Ryujinx.Graphics.OpenGL.Image public void WriteToPbo(int offset, bool forceBgra) { - WriteTo(IntPtr.Zero + offset, forceBgra); + WriteTo(nint.Zero + offset, forceBgra); } public int WriteToPbo2D(int offset, int layer, int level) { - return WriteTo2D(IntPtr.Zero + offset, layer, level); + return WriteTo2D(nint.Zero + offset, layer, level); } - private int WriteTo2D(IntPtr data, int layer, int level) + private int WriteTo2D(nint data, int layer, int level) { TextureTarget target = Target.Convert(); @@ -390,7 +390,7 @@ namespace Ryujinx.Graphics.OpenGL.Image return 0; } - private void WriteTo(IntPtr data, bool forceBgra = false) + private void WriteTo(nint data, bool forceBgra = false) { TextureTarget target = Target.Convert(); @@ -457,7 +457,7 @@ namespace Ryujinx.Graphics.OpenGL.Image var dataSpan = data.Span; fixed (byte* ptr = dataSpan) { - ReadFrom((IntPtr)ptr, dataSpan.Length); + ReadFrom((nint)ptr, dataSpan.Length); } } } @@ -474,7 +474,7 @@ namespace Ryujinx.Graphics.OpenGL.Image int width = Math.Max(Info.Width >> level, 1); int height = Math.Max(Info.Height >> level, 1); - ReadFrom2D((IntPtr)ptr, layer, level, 0, 0, width, height); + ReadFrom2D((nint)ptr, layer, level, 0, 0, width, height); } } } @@ -492,7 +492,7 @@ namespace Ryujinx.Graphics.OpenGL.Image fixed (byte* ptr = data.Span) { ReadFrom2D( - (IntPtr)ptr, + (nint)ptr, layer, level, region.X, @@ -507,15 +507,15 @@ namespace Ryujinx.Graphics.OpenGL.Image public void ReadFromPbo(int offset, int size) { - ReadFrom(IntPtr.Zero + offset, size); + ReadFrom(nint.Zero + offset, size); } public void ReadFromPbo2D(int offset, int layer, int level, int width, int height) { - ReadFrom2D(IntPtr.Zero + offset, layer, level, 0, 0, width, height); + ReadFrom2D(nint.Zero + offset, layer, level, 0, 0, width, height); } - private void ReadFrom2D(IntPtr data, int layer, int level, int x, int y, int width, int height) + private void ReadFrom2D(nint data, int layer, int level, int x, int y, int width, int height) { int mipSize = Info.GetMipSize2D(level); @@ -535,7 +535,7 @@ namespace Ryujinx.Graphics.OpenGL.Image return data; } - private void ReadFrom2D(IntPtr data, int layer, int level, int x, int y, int width, int height, int mipSize) + private void ReadFrom2D(nint data, int layer, int level, int x, int y, int width, int height, int mipSize) { TextureTarget target = Target.Convert(); @@ -694,7 +694,7 @@ namespace Ryujinx.Graphics.OpenGL.Image } } - private void ReadFrom(IntPtr data, int size) + private void ReadFrom(nint data, int size) { TextureTarget target = Target.Convert(); int baseLevel = 0; diff --git a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs index 9fcdf1ad7..6ead314fd 100644 --- a/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs +++ b/src/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs @@ -29,6 +29,8 @@ namespace Ryujinx.Graphics.OpenGL private readonly Sync _sync; + public uint ProgramCount { get; set; } = 0; + public event EventHandler ScreenCaptured; internal PersistentBuffers PersistentBuffers { get; } @@ -94,6 +96,8 @@ namespace Ryujinx.Graphics.OpenGL public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info) { + ProgramCount++; + return new Program(shaders, info.FragmentOutputMap); } @@ -243,7 +247,7 @@ namespace Ryujinx.Graphics.OpenGL // This is required to disable [0, 1] clamping for SNorm outputs on compatibility profiles. // This call is expected to fail if we're running with a core profile, // as this clamp target was deprecated, but that's fine as a core profile - // should already have the desired behaviour were outputs are not clamped. + // should already have the desired behaviour where outputs are not clamped. GL.ClampColor(ClampColorTarget.ClampFragmentColor, ClampColorMode.False); } diff --git a/src/Ryujinx.Graphics.OpenGL/PersistentBuffers.cs b/src/Ryujinx.Graphics.OpenGL/PersistentBuffers.cs index ebfe3ad64..28ebe88a5 100644 --- a/src/Ryujinx.Graphics.OpenGL/PersistentBuffers.cs +++ b/src/Ryujinx.Graphics.OpenGL/PersistentBuffers.cs @@ -14,7 +14,7 @@ namespace Ryujinx.Graphics.OpenGL private readonly PersistentBuffer _main = new(); private readonly PersistentBuffer _background = new(); - private readonly Dictionary _maps = new(); + private readonly Dictionary _maps = new(); public PersistentBuffer Default => BackgroundContextWorker.InBackground ? _background : _main; @@ -27,7 +27,7 @@ namespace Ryujinx.Graphics.OpenGL public void Map(BufferHandle handle, int size) { GL.BindBuffer(BufferTarget.CopyWriteBuffer, handle.ToInt32()); - IntPtr ptr = GL.MapBufferRange(BufferTarget.CopyWriteBuffer, IntPtr.Zero, size, BufferAccessMask.MapReadBit | BufferAccessMask.MapPersistentBit); + nint ptr = GL.MapBufferRange(BufferTarget.CopyWriteBuffer, nint.Zero, size, BufferAccessMask.MapReadBit | BufferAccessMask.MapPersistentBit); _maps[handle] = ptr; } @@ -43,7 +43,7 @@ namespace Ryujinx.Graphics.OpenGL } } - public bool TryGet(BufferHandle handle, out IntPtr ptr) + public bool TryGet(BufferHandle handle, out nint ptr) { return _maps.TryGetValue(handle, out ptr); } @@ -51,12 +51,12 @@ namespace Ryujinx.Graphics.OpenGL class PersistentBuffer : IDisposable { - private IntPtr _bufferMap; + private nint _bufferMap; private int _copyBufferHandle; private int _copyBufferSize; private byte[] _data; - private IntPtr _dataMap; + private nint _dataMap; private void EnsureBuffer(int requiredSize) { @@ -73,19 +73,19 @@ namespace Ryujinx.Graphics.OpenGL _copyBufferSize = requiredSize; GL.BindBuffer(BufferTarget.CopyWriteBuffer, _copyBufferHandle); - GL.BufferStorage(BufferTarget.CopyWriteBuffer, requiredSize, IntPtr.Zero, BufferStorageFlags.MapReadBit | BufferStorageFlags.MapPersistentBit); + GL.BufferStorage(BufferTarget.CopyWriteBuffer, requiredSize, nint.Zero, BufferStorageFlags.MapReadBit | BufferStorageFlags.MapPersistentBit); - _bufferMap = GL.MapBufferRange(BufferTarget.CopyWriteBuffer, IntPtr.Zero, requiredSize, BufferAccessMask.MapReadBit | BufferAccessMask.MapPersistentBit); + _bufferMap = GL.MapBufferRange(BufferTarget.CopyWriteBuffer, nint.Zero, requiredSize, BufferAccessMask.MapReadBit | BufferAccessMask.MapPersistentBit); } } - public unsafe IntPtr GetHostArray(int requiredSize) + public unsafe nint GetHostArray(int requiredSize) { if (_data == null || _data.Length < requiredSize) { _data = GC.AllocateUninitializedArray(requiredSize, true); - _dataMap = (IntPtr)Unsafe.AsPointer(ref MemoryMarshal.GetArrayDataReference(_data)); + _dataMap = (nint)Unsafe.AsPointer(ref MemoryMarshal.GetArrayDataReference(_data)); } return _dataMap; @@ -95,7 +95,7 @@ namespace Ryujinx.Graphics.OpenGL { GL.MemoryBarrier(MemoryBarrierFlags.ClientMappedBufferBarrierBit); - IntPtr sync = GL.FenceSync(SyncCondition.SyncGpuCommandsComplete, WaitSyncFlags.None); + nint sync = GL.FenceSync(SyncCondition.SyncGpuCommandsComplete, WaitSyncFlags.None); WaitSyncStatus syncResult = GL.ClientWaitSync(sync, ClientWaitSyncFlags.SyncFlushCommandsBit, 1000000000); if (syncResult == WaitSyncStatus.TimeoutExpired) @@ -143,7 +143,7 @@ namespace Ryujinx.Graphics.OpenGL GL.BindBuffer(BufferTarget.CopyReadBuffer, buffer.ToInt32()); GL.BindBuffer(BufferTarget.CopyWriteBuffer, _copyBufferHandle); - GL.CopyBufferSubData(BufferTarget.CopyReadBuffer, BufferTarget.CopyWriteBuffer, (IntPtr)offset, IntPtr.Zero, size); + GL.CopyBufferSubData(BufferTarget.CopyReadBuffer, BufferTarget.CopyWriteBuffer, (nint)offset, nint.Zero, size); GL.BindBuffer(BufferTarget.CopyWriteBuffer, 0); diff --git a/src/Ryujinx.Graphics.OpenGL/Pipeline.cs b/src/Ryujinx.Graphics.OpenGL/Pipeline.cs index 27aacac15..096e2e5eb 100644 --- a/src/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/src/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.OpenGL private VertexArray _vertexArray; private Framebuffer _framebuffer; - private IntPtr _indexBaseOffset; + private nint _indexBaseOffset; private DrawElementsType _elementsType; @@ -358,7 +358,7 @@ namespace Ryujinx.Graphics.OpenGL break; } - IntPtr indexBaseOffset = _indexBaseOffset + firstIndex * indexElemSize; + nint indexBaseOffset = _indexBaseOffset + firstIndex * indexElemSize; if (_primitiveType == PrimitiveType.Quads && !HwCapabilities.SupportsQuads) { @@ -396,7 +396,7 @@ namespace Ryujinx.Graphics.OpenGL private void DrawQuadsIndexedImpl( int indexCount, int instanceCount, - IntPtr indexBaseOffset, + nint indexBaseOffset, int indexElemSize, int firstVertex, int firstInstance) @@ -447,7 +447,7 @@ namespace Ryujinx.Graphics.OpenGL } else { - IntPtr[] indices = new IntPtr[quadsCount]; + nint[] indices = new nint[quadsCount]; int[] counts = new int[quadsCount]; @@ -475,7 +475,7 @@ namespace Ryujinx.Graphics.OpenGL private void DrawQuadStripIndexedImpl( int indexCount, int instanceCount, - IntPtr indexBaseOffset, + nint indexBaseOffset, int indexElemSize, int firstVertex, int firstInstance) @@ -483,7 +483,7 @@ namespace Ryujinx.Graphics.OpenGL // TODO: Instanced rendering. int quadsCount = (indexCount - 2) / 2; - IntPtr[] indices = new IntPtr[quadsCount]; + nint[] indices = new nint[quadsCount]; int[] counts = new int[quadsCount]; @@ -516,7 +516,7 @@ namespace Ryujinx.Graphics.OpenGL private void DrawIndexedImpl( int indexCount, int instanceCount, - IntPtr indexBaseOffset, + nint indexBaseOffset, int firstVertex, int firstInstance) { @@ -589,7 +589,7 @@ namespace Ryujinx.Graphics.OpenGL GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle.ToInt32()); - GL.DrawElementsIndirect(_primitiveType, _elementsType, (IntPtr)indirectBuffer.Offset); + GL.DrawElementsIndirect(_primitiveType, _elementsType, (nint)indirectBuffer.Offset); _vertexArray.RestoreIndexBuffer(); @@ -614,8 +614,8 @@ namespace Ryujinx.Graphics.OpenGL GL.MultiDrawElementsIndirectCount( _primitiveType, (All)_elementsType, - (IntPtr)indirectBuffer.Offset, - (IntPtr)parameterBuffer.Offset, + (nint)indirectBuffer.Offset, + (nint)parameterBuffer.Offset, maxDrawCount, stride); @@ -636,7 +636,7 @@ namespace Ryujinx.Graphics.OpenGL GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle.ToInt32()); - GL.DrawArraysIndirect(_primitiveType, (IntPtr)indirectBuffer.Offset); + GL.DrawArraysIndirect(_primitiveType, (nint)indirectBuffer.Offset); PostDraw(); } @@ -656,8 +656,8 @@ namespace Ryujinx.Graphics.OpenGL GL.MultiDrawArraysIndirectCount( _primitiveType, - (IntPtr)indirectBuffer.Offset, - (IntPtr)parameterBuffer.Offset, + (nint)indirectBuffer.Offset, + (nint)parameterBuffer.Offset, maxDrawCount, stride); @@ -972,7 +972,7 @@ namespace Ryujinx.Graphics.OpenGL { _elementsType = type.Convert(); - _indexBaseOffset = (IntPtr)buffer.Offset; + _indexBaseOffset = (nint)buffer.Offset; EnsureVertexArray(); @@ -1450,11 +1450,11 @@ namespace Ryujinx.Graphics.OpenGL if (buffer.Handle == BufferHandle.Null) { - GL.BindBufferRange(target, assignment.Binding, 0, IntPtr.Zero, 0); + GL.BindBufferRange(target, assignment.Binding, 0, nint.Zero, 0); continue; } - GL.BindBufferRange(target, assignment.Binding, buffer.Handle.ToInt32(), (IntPtr)buffer.Offset, buffer.Size); + GL.BindBufferRange(target, assignment.Binding, buffer.Handle.ToInt32(), (nint)buffer.Offset, buffer.Size); } } diff --git a/src/Ryujinx.Graphics.OpenGL/Program.cs b/src/Ryujinx.Graphics.OpenGL/Program.cs index 19de06f8f..608a03451 100644 --- a/src/Ryujinx.Graphics.OpenGL/Program.cs +++ b/src/Ryujinx.Graphics.OpenGL/Program.cs @@ -86,7 +86,7 @@ namespace Ryujinx.Graphics.OpenGL { fixed (byte* ptr = code) { - GL.ProgramBinary(Handle, binaryFormat, (IntPtr)ptr, code.Length - 4); + GL.ProgramBinary(Handle, binaryFormat, (nint)ptr, code.Length - 4); } } } diff --git a/src/Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs b/src/Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs index 0a85970d7..a5acd8dce 100644 --- a/src/Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs +++ b/src/Ryujinx.Graphics.OpenGL/Queries/BufferedQuery.cs @@ -15,7 +15,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries public int Query { get; } private readonly int _buffer; - private readonly IntPtr _bufferMap; + private readonly nint _bufferMap; private readonly QueryTarget _type; public BufferedQuery(QueryTarget type) @@ -29,9 +29,9 @@ namespace Ryujinx.Graphics.OpenGL.Queries unsafe { long defaultValue = DefaultValue; - GL.BufferStorage(BufferTarget.QueryBuffer, sizeof(long), (IntPtr)(&defaultValue), BufferStorageFlags.MapReadBit | BufferStorageFlags.MapWriteBit | BufferStorageFlags.MapPersistentBit); + GL.BufferStorage(BufferTarget.QueryBuffer, sizeof(long), (nint)(&defaultValue), BufferStorageFlags.MapReadBit | BufferStorageFlags.MapWriteBit | BufferStorageFlags.MapPersistentBit); } - _bufferMap = GL.MapBufferRange(BufferTarget.QueryBuffer, IntPtr.Zero, sizeof(long), BufferAccessMask.MapReadBit | BufferAccessMask.MapWriteBit | BufferAccessMask.MapPersistentBit); + _bufferMap = GL.MapBufferRange(BufferTarget.QueryBuffer, nint.Zero, sizeof(long), BufferAccessMask.MapReadBit | BufferAccessMask.MapWriteBit | BufferAccessMask.MapPersistentBit); } public void Reset() diff --git a/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs b/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs index 345a99ffa..7e0311407 100644 --- a/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs +++ b/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueue.cs @@ -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 _queryPool; private readonly AutoResetEvent _queuedEvent = new(false); diff --git a/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueueEvent.cs b/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueueEvent.cs index 32b75c615..889517480 100644 --- a/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueueEvent.cs +++ b/src/Ryujinx.Graphics.OpenGL/Queries/CounterQueueEvent.cs @@ -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; diff --git a/src/Ryujinx.Graphics.OpenGL/ResourcePool.cs b/src/Ryujinx.Graphics.OpenGL/ResourcePool.cs index 6385f57b5..c8ff30a07 100644 --- a/src/Ryujinx.Graphics.OpenGL/ResourcePool.cs +++ b/src/Ryujinx.Graphics.OpenGL/ResourcePool.cs @@ -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> _textures = new(); /// diff --git a/src/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj b/src/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj index f3071f486..47cec29e2 100644 --- a/src/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj +++ b/src/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj @@ -1,8 +1,8 @@ - net8.0 true + $(DefaultItemExcludes);._* diff --git a/src/Ryujinx.Graphics.OpenGL/Sync.cs b/src/Ryujinx.Graphics.OpenGL/Sync.cs index eba1638a3..e8f7ebc00 100644 --- a/src/Ryujinx.Graphics.OpenGL/Sync.cs +++ b/src/Ryujinx.Graphics.OpenGL/Sync.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.OpenGL private class SyncHandle { public ulong ID; - public IntPtr Handle; + public nint Handle; } private ulong _firstHandle = 0; @@ -50,7 +50,7 @@ namespace Ryujinx.Graphics.OpenGL { lock (handle) { - if (handle.Handle == IntPtr.Zero) + if (handle.Handle == nint.Zero) { continue; } @@ -96,7 +96,7 @@ namespace Ryujinx.Graphics.OpenGL { lock (result) { - if (result.Handle == IntPtr.Zero) + if (result.Handle == nint.Zero) { return; } @@ -140,7 +140,7 @@ namespace Ryujinx.Graphics.OpenGL _firstHandle = first.ID + 1; _handles.RemoveAt(0); GL.DeleteSync(first.Handle); - first.Handle = IntPtr.Zero; + first.Handle = nint.Zero; } } } @@ -161,7 +161,7 @@ namespace Ryujinx.Graphics.OpenGL lock (handle) { GL.DeleteSync(handle.Handle); - handle.Handle = IntPtr.Zero; + handle.Handle = nint.Zero; } } diff --git a/src/Ryujinx.Graphics.OpenGL/VertexArray.cs b/src/Ryujinx.Graphics.OpenGL/VertexArray.cs index 32211e783..2db84421f 100644 --- a/src/Ryujinx.Graphics.OpenGL/VertexArray.cs +++ b/src/Ryujinx.Graphics.OpenGL/VertexArray.cs @@ -56,7 +56,7 @@ namespace Ryujinx.Graphics.OpenGL minVertexCount = vertexCount; } - GL.BindVertexBuffer(bindingIndex, vb.Buffer.Handle.ToInt32(), (IntPtr)vb.Buffer.Offset, vb.Stride); + GL.BindVertexBuffer(bindingIndex, vb.Buffer.Handle.ToInt32(), (nint)vb.Buffer.Offset, vb.Stride); GL.VertexBindingDivisor(bindingIndex, vb.Divisor); _vertexBuffersInUse |= 1u << bindingIndex; } @@ -64,7 +64,7 @@ namespace Ryujinx.Graphics.OpenGL { if ((_vertexBuffersInUse & (1u << bindingIndex)) != 0) { - GL.BindVertexBuffer(bindingIndex, 0, IntPtr.Zero, 0); + GL.BindVertexBuffer(bindingIndex, 0, nint.Zero, 0); _vertexBuffersInUse &= ~(1u << bindingIndex); } } @@ -188,7 +188,7 @@ namespace Ryujinx.Graphics.OpenGL Buffer.Copy(vb.Buffer.Handle, tempVertexBuffer, vb.Buffer.Offset, currentTempVbOffset, vb.Buffer.Size); Buffer.Clear(tempVertexBuffer, currentTempVbOffset + vb.Buffer.Size, requiredSize - vb.Buffer.Size, 0); - GL.BindVertexBuffer(vbIndex, tempVertexBuffer.ToInt32(), (IntPtr)currentTempVbOffset, vb.Stride); + GL.BindVertexBuffer(vbIndex, tempVertexBuffer.ToInt32(), (nint)currentTempVbOffset, vb.Stride); currentTempVbOffset += requiredSize; _vertexBuffersLimited |= 1u << vbIndex; @@ -234,7 +234,7 @@ namespace Ryujinx.Graphics.OpenGL ref var vb = ref _vertexBuffers[vbIndex]; - GL.BindVertexBuffer(vbIndex, vb.Buffer.Handle.ToInt32(), (IntPtr)vb.Buffer.Offset, vb.Stride); + GL.BindVertexBuffer(vbIndex, vb.Buffer.Handle.ToInt32(), (nint)vb.Buffer.Offset, vb.Stride); buffersLimited &= ~(1u << vbIndex); } diff --git a/src/Ryujinx.Graphics.OpenGL/Window.cs b/src/Ryujinx.Graphics.OpenGL/Window.cs index 285ab725e..1dc8a51f6 100644 --- a/src/Ryujinx.Graphics.OpenGL/Window.cs +++ b/src/Ryujinx.Graphics.OpenGL/Window.cs @@ -54,7 +54,7 @@ namespace Ryujinx.Graphics.OpenGL GL.PixelStore(PixelStoreParameter.UnpackAlignment, 4); } - public void ChangeVSyncMode(bool vsyncEnabled) { } + public void ChangeVSyncMode(VSyncMode vSyncMode) { } public void SetSize(int width, int height) { diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs index 4308b08f8..56507a2a4 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs @@ -432,7 +432,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions bool colorIsVector = isGather || !isShadow; - texCall += ")" + (colorIsVector ? GetMaskMultiDest(texOp.Index) : ""); + texCall += ")" + (colorIsVector ? GetMaskMultiDest(texOp.Index) : string.Empty); return texCall; } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs index b259dde28..105812ebf 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs @@ -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 _instructionPool; private static readonly ObjectPool _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; diff --git a/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj b/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj index 6ba6d4225..8b05d8829 100644 --- a/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj +++ b/src/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj @@ -1,7 +1,7 @@ - net8.0 + $(DefaultItemExcludes);._* diff --git a/src/Ryujinx.Graphics.Shader/Translation/FunctionMatch.cs b/src/Ryujinx.Graphics.Shader/Translation/FunctionMatch.cs index 714a9d68c..b792776df 100644 --- a/src/Ryujinx.Graphics.Shader/Translation/FunctionMatch.cs +++ b/src/Ryujinx.Graphics.Shader/Translation/FunctionMatch.cs @@ -830,12 +830,12 @@ namespace Ryujinx.Graphics.Shader.Translation if (use.Node != null) { - Console.Write($"{indentation} {separator}- ({(use.Inverted ? "INV " : "")}{use.Index})"); + Console.Write($"{indentation} {separator}- ({(use.Inverted ? "INV " : string.Empty)}{use.Index})"); PrintTreeNode(use.Node, indentation + (last ? " " : " | ")); } else { - Console.WriteLine($"{indentation} {separator}- ({(use.Inverted ? "INV " : "")}{use.Index}) NULL"); + Console.WriteLine($"{indentation} {separator}- ({(use.Inverted ? "INV " : string.Empty)}{use.Index}) NULL"); } } } @@ -852,12 +852,12 @@ namespace Ryujinx.Graphics.Shader.Translation if (use.Node != null) { - Console.Write($"{indentation} {separator}- ({(use.Inverted ? "INV " : "")}{use.Index})"); + Console.Write($"{indentation} {separator}- ({(use.Inverted ? "INV " : string.Empty)}{use.Index})"); PrintTreeNode(use.Node, indentation + (last ? " " : " | ")); } else { - Console.WriteLine($"{indentation} {separator}- ({(use.Inverted ? "INV " : "")}{use.Index}) NULL"); + Console.WriteLine($"{indentation} {separator}- ({(use.Inverted ? "INV " : string.Empty)}{use.Index}) NULL"); } } } diff --git a/src/Ryujinx.Graphics.Texture/Astc/IntegerEncoded.cs b/src/Ryujinx.Graphics.Texture/Astc/IntegerEncoded.cs index fedd90ee2..dc99de2b6 100644 --- a/src/Ryujinx.Graphics.Texture/Astc/IntegerEncoded.cs +++ b/src/Ryujinx.Graphics.Texture/Astc/IntegerEncoded.cs @@ -3,7 +3,7 @@ using System.Numerics; namespace Ryujinx.Graphics.Texture.Astc { - internal struct IntegerEncoded + internal struct IntegerEncoded(IntegerEncoded.EIntegerEncoding encoding, int numBits) { internal const int StructSize = 8; private static readonly IntegerEncoded[] _encodings; @@ -15,11 +15,11 @@ namespace Ryujinx.Graphics.Texture.Astc Trit, } - readonly EIntegerEncoding _encoding; - public byte NumberBits { get; private set; } - public byte TritValue { get; private set; } - public byte QuintValue { get; private set; } - public int BitValue { get; private set; } + readonly EIntegerEncoding _encoding = encoding; + public byte NumberBits { get; } = (byte)numBits; + public byte TritValue { get; private set; } = 0; + public byte QuintValue { get; private set; } = 0; + public int BitValue { get; private set; } = 0; static IntegerEncoded() { @@ -31,15 +31,6 @@ namespace Ryujinx.Graphics.Texture.Astc } } - public IntegerEncoded(EIntegerEncoding encoding, int numBits) - { - _encoding = encoding; - NumberBits = (byte)numBits; - BitValue = 0; - TritValue = 0; - QuintValue = 0; - } - public readonly bool MatchesEncoding(IntegerEncoded other) { return _encoding == other._encoding && NumberBits == other.NumberBits; diff --git a/src/Ryujinx.Graphics.Texture/BC6Decoder.cs b/src/Ryujinx.Graphics.Texture/BC6Decoder.cs index ae33e9cf9..f4d933643 100644 --- a/src/Ryujinx.Graphics.Texture/BC6Decoder.cs +++ b/src/Ryujinx.Graphics.Texture/BC6Decoder.cs @@ -45,7 +45,7 @@ namespace Ryujinx.Graphics.Texture if (subsetCount == 0) { // Mode is invalid, the spec mandates that hardware fills the block with - // a opaque black color. + // an opaque black color. for (int ty = 0; ty < h; ty++) { int baseOffs = ty * width; diff --git a/src/Ryujinx.Graphics.Texture/OffsetCalculator.cs b/src/Ryujinx.Graphics.Texture/OffsetCalculator.cs index 48d36cdfb..e519341e6 100644 --- a/src/Ryujinx.Graphics.Texture/OffsetCalculator.cs +++ b/src/Ryujinx.Graphics.Texture/OffsetCalculator.cs @@ -15,7 +15,7 @@ namespace Ryujinx.Graphics.Texture private readonly BlockLinearLayout _layoutConverter; - // Variables for built in iteration. + // Variables for built-in iteration. private int _yPart; public OffsetCalculator( @@ -73,69 +73,50 @@ namespace Ryujinx.Graphics.Texture public int GetOffset(int x, int y) { if (_isLinear) - { return x * _bytesPerPixel + y * _stride; - } - else - { - return _layoutConverter.GetOffset(x, y, 0); - } + + return _layoutConverter.GetOffset(x, y, 0); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public int GetOffset(int x) { if (_isLinear) - { return x * _bytesPerPixel + _yPart; - } - else - { - return _layoutConverter.GetOffset(x); - } + + return _layoutConverter.GetOffset(x); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public int GetOffsetWithLineOffset64(int x) { if (_isLinear) - { return x + _yPart; - } - else - { - return _layoutConverter.GetOffsetWithLineOffset64(x); - } + + return _layoutConverter.GetOffsetWithLineOffset64(x); } public (int offset, int size) GetRectangleRange(int x, int y, int width, int height) { - if (_isLinear) - { - int start = y * Math.Abs(_stride) + x * _bytesPerPixel; - int end = (y + height - 1) * Math.Abs(_stride) + (x + width) * _bytesPerPixel; - return (y * _stride + x * _bytesPerPixel, end - start); - } - else - { + if (!_isLinear) return _layoutConverter.GetRectangleRange(x, y, width, height); - } + + int start = y * Math.Abs(_stride) + x * _bytesPerPixel; + int end = (y + height - 1) * Math.Abs(_stride) + (x + width) * _bytesPerPixel; + return (y * _stride + x * _bytesPerPixel, end - start); } public bool LayoutMatches(OffsetCalculator other) { if (_isLinear) - { return other._isLinear && _width == other._width && _height == other._height && _stride == other._stride && _bytesPerPixel == other._bytesPerPixel; - } - else - { - return !other._isLinear && _layoutConverter.LayoutMatches(other._layoutConverter); - } + + + return !other._isLinear && _layoutConverter.LayoutMatches(other._layoutConverter); } } } diff --git a/src/Ryujinx.Graphics.Texture/Ryujinx.Graphics.Texture.csproj b/src/Ryujinx.Graphics.Texture/Ryujinx.Graphics.Texture.csproj index 51721490e..2b4445aeb 100644 --- a/src/Ryujinx.Graphics.Texture/Ryujinx.Graphics.Texture.csproj +++ b/src/Ryujinx.Graphics.Texture/Ryujinx.Graphics.Texture.csproj @@ -1,7 +1,7 @@ - net8.0 true + $(DefaultItemExcludes);._* diff --git a/src/Ryujinx.Graphics.Vic/Ryujinx.Graphics.Vic.csproj b/src/Ryujinx.Graphics.Vic/Ryujinx.Graphics.Vic.csproj index a6c4fb2bb..5505a3aa1 100644 --- a/src/Ryujinx.Graphics.Vic/Ryujinx.Graphics.Vic.csproj +++ b/src/Ryujinx.Graphics.Vic/Ryujinx.Graphics.Vic.csproj @@ -1,8 +1,8 @@ - net8.0 true + $(DefaultItemExcludes);._* diff --git a/src/Ryujinx.Graphics.Video/Plane.cs b/src/Ryujinx.Graphics.Video/Plane.cs index b895cad90..4e4e65b32 100644 --- a/src/Ryujinx.Graphics.Video/Plane.cs +++ b/src/Ryujinx.Graphics.Video/Plane.cs @@ -2,5 +2,5 @@ using System; namespace Ryujinx.Graphics.Video { - public readonly record struct Plane(IntPtr Pointer, int Length); + public readonly record struct Plane(nint Pointer, int Length); } diff --git a/src/Ryujinx.Graphics.Video/Ryujinx.Graphics.Video.csproj b/src/Ryujinx.Graphics.Video/Ryujinx.Graphics.Video.csproj index abff58a53..d64990f82 100644 --- a/src/Ryujinx.Graphics.Video/Ryujinx.Graphics.Video.csproj +++ b/src/Ryujinx.Graphics.Video/Ryujinx.Graphics.Video.csproj @@ -1,7 +1,7 @@ - net8.0 + $(DefaultItemExcludes);._* diff --git a/src/Ryujinx.Graphics.Vulkan/BackgroundResources.cs b/src/Ryujinx.Graphics.Vulkan/BackgroundResources.cs index 0290987fd..e4b68fa40 100644 --- a/src/Ryujinx.Graphics.Vulkan/BackgroundResources.cs +++ b/src/Ryujinx.Graphics.Vulkan/BackgroundResources.cs @@ -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) { diff --git a/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs b/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs index e840fdc02..6dce6abb5 100644 --- a/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs +++ b/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs @@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Vulkan public int Size { get; } - private readonly IntPtr _map; + private readonly nint _map; private readonly MultiFenceHolder _waitable; @@ -370,7 +370,7 @@ namespace Ryujinx.Graphics.Vulkan return Unsafe.As(ref handle); } - public IntPtr Map(int offset, int mappingSize) + public nint Map(int offset, int mappingSize) { return _map; } @@ -435,7 +435,7 @@ namespace Ryujinx.Graphics.Vulkan Span result; - if (_map != IntPtr.Zero) + if (_map != nint.Zero) { result = GetDataStorage(offset, size); @@ -470,7 +470,7 @@ namespace Ryujinx.Graphics.Vulkan { int mappingSize = Math.Min(size, Size - offset); - if (_map != IntPtr.Zero) + if (_map != nint.Zero) { return new Span((void*)(_map + offset), mappingSize); } @@ -515,7 +515,7 @@ namespace Ryujinx.Graphics.Vulkan bool allowMirror = _useMirrors && allowCbsWait && cbs != null && _activeType <= BufferAllocationType.HostMapped; - if (_map != IntPtr.Zero) + if (_map != nint.Zero) { // If persistently mapped, set the data directly if the buffer is not currently in use. bool isRented = _buffer.HasRentedCommandBufferDependency(_gd.CommandBufferPool); @@ -630,7 +630,7 @@ namespace Ryujinx.Graphics.Vulkan return; } - if (_map != IntPtr.Zero) + if (_map != nint.Zero) { data[..dataSize].CopyTo(new Span((void*)(_map + offset), dataSize)); } diff --git a/src/Ryujinx.Graphics.Vulkan/BufferMirrorRangeList.cs b/src/Ryujinx.Graphics.Vulkan/BufferMirrorRangeList.cs index 5722ca1ac..e79248a47 100644 --- a/src/Ryujinx.Graphics.Vulkan/BufferMirrorRangeList.cs +++ b/src/Ryujinx.Graphics.Vulkan/BufferMirrorRangeList.cs @@ -168,16 +168,14 @@ namespace Ryujinx.Graphics.Vulkan return BinarySearch(list, offset, size) >= 0; } - public readonly List FindOverlaps(int offset, int size) + public readonly IEnumerable FindOverlaps(int offset, int size) { var list = _ranges; if (list == null) { - return null; + yield break; } - List result = null; - int index = BinarySearch(list, offset, size); if (index >= 0) @@ -189,12 +187,10 @@ namespace Ryujinx.Graphics.Vulkan do { - (result ??= new List()).Add(list[index++]); + yield return list[index++]; } while (index < list.Count && list[index].OverlapsWith(offset, size)); } - - return result; } private static int BinarySearch(List list, int offset, int size) diff --git a/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs b/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs index e1fd3fb9d..ed76c6566 100644 --- a/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs +++ b/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs @@ -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) diff --git a/src/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs b/src/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs index 9ea8cec4b..09f22889c 100644 --- a/src/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs +++ b/src/Ryujinx.Graphics.Vulkan/FormatCapabilities.cs @@ -62,13 +62,13 @@ namespace Ryujinx.Graphics.Vulkan _api = api; _physicalDevice = physicalDevice; - int totalFormats = Enum.GetNames(typeof(Format)).Length; + int totalFormats = Enum.GetNames().Length; _bufferTable = new FormatFeatureFlags[totalFormats]; _optimalTable = new FormatFeatureFlags[totalFormats]; } - public bool BufferFormatsSupport(FormatFeatureFlags flags, params Format[] formats) + public bool BufferFormatsSupport(FormatFeatureFlags flags, params ReadOnlySpan 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 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; } diff --git a/src/Ryujinx.Graphics.Vulkan/FormatTable.cs b/src/Ryujinx.Graphics.Vulkan/FormatTable.cs index 98796d9bf..305224cad 100644 --- a/src/Ryujinx.Graphics.Vulkan/FormatTable.cs +++ b/src/Ryujinx.Graphics.Vulkan/FormatTable.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Graphics.Vulkan static FormatTable() { - _table = new VkFormat[Enum.GetNames(typeof(Format)).Length]; + _table = new VkFormat[Enum.GetNames().Length]; _reverseMap = new Dictionary(); #pragma warning disable IDE0055 // Disable formatting diff --git a/src/Ryujinx.Graphics.Vulkan/HostMemoryAllocator.cs b/src/Ryujinx.Graphics.Vulkan/HostMemoryAllocator.cs index ff1565246..5c8b2a761 100644 --- a/src/Ryujinx.Graphics.Vulkan/HostMemoryAllocator.cs +++ b/src/Ryujinx.Graphics.Vulkan/HostMemoryAllocator.cs @@ -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 { @@ -13,13 +14,13 @@ namespace Ryujinx.Graphics.Vulkan private readonly struct HostMemoryAllocation { public readonly Auto Allocation; - public readonly IntPtr Pointer; + public readonly nint Pointer; public readonly ulong Size; public ulong Start => (ulong)Pointer; public ulong End => (ulong)Pointer + Size; - public HostMemoryAllocation(Auto allocation, IntPtr pointer, ulong size) + public HostMemoryAllocation(Auto allocation, nint pointer, ulong size) { Allocation = allocation; Pointer = pointer; @@ -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 _allocations; private readonly IntervalTree _allocationTree; @@ -50,7 +51,7 @@ namespace Ryujinx.Graphics.Vulkan public unsafe bool TryImport( MemoryRequirements requirements, MemoryPropertyFlags flags, - IntPtr pointer, + nint pointer, ulong size) { lock (_lock) @@ -139,7 +140,7 @@ namespace Ryujinx.Graphics.Vulkan return true; } - public (Auto, ulong) GetExistingAllocation(IntPtr pointer, ulong size) + public (Auto, ulong) GetExistingAllocation(nint pointer, ulong size) { lock (_lock) { diff --git a/src/Ryujinx.Graphics.Vulkan/MemoryAllocation.cs b/src/Ryujinx.Graphics.Vulkan/MemoryAllocation.cs index 3f134e289..d0d0ac1e7 100644 --- a/src/Ryujinx.Graphics.Vulkan/MemoryAllocation.cs +++ b/src/Ryujinx.Graphics.Vulkan/MemoryAllocation.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Graphics.Vulkan private readonly HostMemoryAllocator _hostMemory; public DeviceMemory Memory { get; } - public IntPtr HostPointer { get; } + public nint HostPointer { get; } public ulong Offset { get; } public ulong Size { get; } @@ -18,7 +18,7 @@ namespace Ryujinx.Graphics.Vulkan MemoryAllocatorBlockList owner, MemoryAllocatorBlockList.Block block, DeviceMemory memory, - IntPtr hostPointer, + nint hostPointer, ulong offset, ulong size) { @@ -33,7 +33,7 @@ namespace Ryujinx.Graphics.Vulkan public MemoryAllocation( HostMemoryAllocator hostMemory, DeviceMemory memory, - IntPtr hostPointer, + nint hostPointer, ulong offset, ulong size) { diff --git a/src/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs b/src/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs index 339754db1..a28322a25 100644 --- a/src/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs +++ b/src/Ryujinx.Graphics.Vulkan/MemoryAllocator.cs @@ -76,9 +76,7 @@ namespace Ryujinx.Graphics.Vulkan } } - internal int FindSuitableMemoryTypeIndex( - uint memoryTypeBits, - MemoryPropertyFlags flags) + internal int FindSuitableMemoryTypeIndex(uint memoryTypeBits, MemoryPropertyFlags flags) { for (int i = 0; i < _physicalDevice.PhysicalDeviceMemoryProperties.MemoryTypeCount; i++) { diff --git a/src/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs b/src/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs index 3d42ed7e2..4a0cb2a74 100644 --- a/src/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs +++ b/src/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs @@ -14,9 +14,9 @@ namespace Ryujinx.Graphics.Vulkan public class Block : IComparable { public DeviceMemory Memory { get; private set; } - public IntPtr HostPointer { get; private set; } + public nint HostPointer { get; private set; } public ulong Size { get; } - public bool Mapped => HostPointer != IntPtr.Zero; + public bool Mapped => HostPointer != nint.Zero; private readonly struct Range : IComparable { @@ -37,7 +37,7 @@ namespace Ryujinx.Graphics.Vulkan private readonly List _freeRanges; - public Block(DeviceMemory memory, IntPtr hostPointer, ulong size) + public Block(DeviceMemory memory, nint hostPointer, ulong size) { Memory = memory; HostPointer = hostPointer; @@ -146,7 +146,7 @@ namespace Ryujinx.Graphics.Vulkan if (Mapped) { api.UnmapMemory(device, Memory); - HostPointer = IntPtr.Zero; + HostPointer = nint.Zero; } if (Memory.Handle != 0) @@ -222,13 +222,13 @@ namespace Ryujinx.Graphics.Vulkan _api.AllocateMemory(_device, in memoryAllocateInfo, null, out var deviceMemory).ThrowOnError(); - IntPtr hostPointer = IntPtr.Zero; + nint hostPointer = nint.Zero; if (map) { void* pointer = null; _api.MapMemory(_device, deviceMemory, 0, blockAlignedSize, 0, ref pointer).ThrowOnError(); - hostPointer = (IntPtr)pointer; + hostPointer = (nint)pointer; } var newBlock = new Block(deviceMemory, hostPointer, blockAlignedSize); @@ -241,14 +241,14 @@ namespace Ryujinx.Graphics.Vulkan return new MemoryAllocation(this, newBlock, deviceMemory, GetHostPointer(newBlock, newBlockOffset), newBlockOffset, size); } - private static IntPtr GetHostPointer(Block block, ulong offset) + private static nint GetHostPointer(Block block, ulong offset) { - if (block.HostPointer == IntPtr.Zero) + if (block.HostPointer == nint.Zero) { - return IntPtr.Zero; + return nint.Zero; } - return (IntPtr)((nuint)block.HostPointer + offset); + return (nint)((nuint)block.HostPointer + offset); } public void Free(Block block, ulong offset, ulong size) diff --git a/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKConfiguration.cs b/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKConfiguration.cs index bdf606e82..271999375 100644 --- a/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKConfiguration.cs +++ b/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKConfiguration.cs @@ -90,7 +90,7 @@ namespace Ryujinx.Graphics.Vulkan.MoltenVK public Bool32 SemaphoreUseMTLFence; public MVKVkSemaphoreSupportStyle SemaphoreSupportStyle; public MVKConfigAutoGPUCaptureScope AutoGPUCaptureScope; - public IntPtr AutoGPUCaptureOutputFilepath; + public nint AutoGPUCaptureOutputFilepath; public Bool32 Texture1DAs2D; public Bool32 PreallocateDescriptors; public Bool32 UseCommandPooling; diff --git a/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs b/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs index 930d6b525..086c4e1df 100644 --- a/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs +++ b/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs @@ -12,16 +12,16 @@ namespace Ryujinx.Graphics.Vulkan.MoltenVK private const string VulkanLib = "libvulkan.dylib"; [LibraryImport("libMoltenVK.dylib")] - private static partial Result vkGetMoltenVKConfigurationMVK(IntPtr unusedInstance, out MVKConfiguration config, in IntPtr configSize); + private static partial Result vkGetMoltenVKConfigurationMVK(nint unusedInstance, out MVKConfiguration config, in nint configSize); [LibraryImport("libMoltenVK.dylib")] - private static partial Result vkSetMoltenVKConfigurationMVK(IntPtr unusedInstance, in MVKConfiguration config, in IntPtr configSize); + private static partial Result vkSetMoltenVKConfigurationMVK(nint unusedInstance, in MVKConfiguration config, in nint configSize); public static void Initialize() { - var configSize = (IntPtr)Marshal.SizeOf(); + var configSize = (nint)Marshal.SizeOf(); - vkGetMoltenVKConfigurationMVK(IntPtr.Zero, out MVKConfiguration config, configSize); + vkGetMoltenVKConfigurationMVK(nint.Zero, out MVKConfiguration config, configSize); config.UseMetalArgumentBuffers = true; @@ -30,7 +30,7 @@ namespace Ryujinx.Graphics.Vulkan.MoltenVK config.ResumeLostDevice = true; - vkSetMoltenVKConfigurationMVK(IntPtr.Zero, config, configSize); + vkSetMoltenVKConfigurationMVK(nint.Zero, config, configSize); } private static string[] Resolver(string path) diff --git a/src/Ryujinx.Graphics.Vulkan/NativeArray.cs b/src/Ryujinx.Graphics.Vulkan/NativeArray.cs index 7678b63c8..33377962b 100644 --- a/src/Ryujinx.Graphics.Vulkan/NativeArray.cs +++ b/src/Ryujinx.Graphics.Vulkan/NativeArray.cs @@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Vulkan { if (Pointer != null) { - Marshal.FreeHGlobal((IntPtr)Pointer); + Marshal.FreeHGlobal((nint)Pointer); Pointer = null; } } diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs index 85069c6b2..8a895f927 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs @@ -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); diff --git a/src/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs b/src/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs index c9a546648..5d48a6622 100644 --- a/src/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs +++ b/src/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs @@ -21,7 +21,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries private QueryPool _queryPool; private readonly BufferHolder _buffer; - private readonly IntPtr _bufferMap; + private readonly nint _bufferMap; private readonly CounterType _type; private readonly bool _result32Bit; private readonly bool _isSupported; diff --git a/src/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs b/src/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs index 0d133e50e..fa10f13b9 100644 --- a/src/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs +++ b/src/Ryujinx.Graphics.Vulkan/Queries/CounterQueue.cs @@ -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 _queryPool; private readonly AutoResetEvent _queuedEvent = new(false); diff --git a/src/Ryujinx.Graphics.Vulkan/Queries/CounterQueueEvent.cs b/src/Ryujinx.Graphics.Vulkan/Queries/CounterQueueEvent.cs index 14d3050bf..0bac3be12 100644 --- a/src/Ryujinx.Graphics.Vulkan/Queries/CounterQueueEvent.cs +++ b/src/Ryujinx.Graphics.Vulkan/Queries/CounterQueueEvent.cs @@ -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; diff --git a/src/Ryujinx.Graphics.Vulkan/Queries/Counters.cs b/src/Ryujinx.Graphics.Vulkan/Queries/Counters.cs index 518ede5f3..c07e1c09c 100644 --- a/src/Ryujinx.Graphics.Vulkan/Queries/Counters.cs +++ b/src/Ryujinx.Graphics.Vulkan/Queries/Counters.cs @@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries { _pipeline = pipeline; - int count = Enum.GetNames(typeof(CounterType)).Length; + int count = Enum.GetNames().Length; _counterQueues = new CounterQueue[count]; diff --git a/src/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj b/src/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj index aae28733f..5481e57f2 100644 --- a/src/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj +++ b/src/Ryujinx.Graphics.Vulkan/Ryujinx.Graphics.Vulkan.csproj @@ -1,7 +1,7 @@ - net8.0 + $(DefaultItemExcludes);._* diff --git a/src/Ryujinx.Graphics.Vulkan/Shader.cs b/src/Ryujinx.Graphics.Vulkan/Shader.cs index 1c8caffd9..79e2f712a 100644 --- a/src/Ryujinx.Graphics.Vulkan/Shader.cs +++ b/src/Ryujinx.Graphics.Vulkan/Shader.cs @@ -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,9 +14,9 @@ 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 IntPtr _ptrMainEntryPointName = Marshal.StringToHGlobalAnsi("main"); + private static readonly nint _ptrMainEntryPointName = Marshal.StringToHGlobalAnsi("main"); private readonly Vk _api; private readonly Device _device; diff --git a/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs b/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs index c9aab4018..436914330 100644 --- a/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs +++ b/src/Ryujinx.Graphics.Vulkan/ShaderCollection.cs @@ -182,6 +182,16 @@ namespace Ryujinx.Graphics.Vulkan return false; } } + + //Prevent the sum of descriptors from exceeding MaxPushDescriptors + int totalDescriptors = 0; + foreach (ResourceDescriptor desc in layout.Sets.First().Descriptors) + { + if (!reserved.Contains(desc.Binding)) + totalDescriptors += desc.Count; + } + if (totalDescriptors > gd.Capabilities.MaxPushDescriptors) + return false; return true; } diff --git a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs index 10b36a3f9..51ef528d4 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs @@ -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; } diff --git a/src/Ryujinx.Graphics.Vulkan/TextureView.cs b/src/Ryujinx.Graphics.Vulkan/TextureView.cs index b7b936809..64d976a45 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureView.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureView.cs @@ -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(); diff --git a/src/Ryujinx.Graphics.Vulkan/VertexBufferState.cs b/src/Ryujinx.Graphics.Vulkan/VertexBufferState.cs index 6f27bb68b..ce1293589 100644 --- a/src/Ryujinx.Graphics.Vulkan/VertexBufferState.cs +++ b/src/Ryujinx.Graphics.Vulkan/VertexBufferState.cs @@ -55,8 +55,10 @@ namespace Ryujinx.Graphics.Vulkan if (_handle != BufferHandle.Null) { // May need to restride the vertex buffer. - - if (gd.NeedsVertexBufferAlignment(AttributeScalarAlignment, out int alignment) && (_stride % alignment) != 0) + // + // Fix divide by zero when recovering from missed draw (Oct. 16 2024) + // (fixes crash in 'Baldo: The Guardian Owls' opening cutscene) + if (gd.NeedsVertexBufferAlignment(AttributeScalarAlignment, out int alignment) && alignment != 0 && (_stride % alignment) != 0) { autoBuffer = gd.BufferManager.GetAlignedVertexBuffer(cbs, _handle, _offset, _size, _stride, alignment); diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanDebugMessenger.cs b/src/Ryujinx.Graphics.Vulkan/VulkanDebugMessenger.cs index 496a90fbe..6dfcd8b6e 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanDebugMessenger.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanDebugMessenger.cs @@ -95,7 +95,7 @@ namespace Ryujinx.Graphics.Vulkan DebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) { - var msg = Marshal.PtrToStringAnsi((IntPtr)pCallbackData->PMessage); + var msg = Marshal.PtrToStringAnsi((nint)pCallbackData->PMessage); if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt)) { diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index 2c327fdb7..352f271cc 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs @@ -94,8 +94,8 @@ namespace Ryujinx.Graphics.Vulkan ApiVersion = _maximumVulkanVersion, }; - IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length]; - IntPtr* ppEnabledLayers = stackalloc IntPtr[enabledLayers.Count]; + nint* ppEnabledExtensions = stackalloc nint[enabledExtensions.Length]; + nint* ppEnabledLayers = stackalloc nint[enabledLayers.Count]; for (int i = 0; i < enabledExtensions.Length; i++) { @@ -587,7 +587,7 @@ namespace Ryujinx.Graphics.Vulkan var enabledExtensions = _requiredExtensions.Union(_desirableExtensions.Intersect(physicalDevice.DeviceExtensions)).ToArray(); - IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length]; + nint* ppEnabledExtensions = stackalloc nint[enabledExtensions.Length]; for (int i = 0; i < enabledExtensions.Length; i++) { diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanInstance.cs b/src/Ryujinx.Graphics.Vulkan/VulkanInstance.cs index 843d34125..69b75925e 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanInstance.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanInstance.cs @@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Vulkan _api = api; Instance = instance; - if (api.GetInstanceProcAddr(instance, "vkEnumerateInstanceVersion") == IntPtr.Zero) + if (api.GetInstanceProcAddr(instance, "vkEnumerateInstanceVersion") == nint.Zero) { InstanceVersion = Vk.Version10; } @@ -94,7 +94,7 @@ namespace Ryujinx.Graphics.Vulkan unsafe { - return extensionProperties.Select(x => Marshal.PtrToStringAnsi((IntPtr)x.ExtensionName)).ToImmutableHashSet(); + return extensionProperties.Select(x => Marshal.PtrToStringAnsi((nint)x.ExtensionName)).ToImmutableHashSet(); } } @@ -110,7 +110,7 @@ namespace Ryujinx.Graphics.Vulkan unsafe { - return layerProperties.Select(x => Marshal.PtrToStringAnsi((IntPtr)x.LayerName)).ToImmutableHashSet(); + return layerProperties.Select(x => Marshal.PtrToStringAnsi((nint)x.LayerName)).ToImmutableHashSet(); } } diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanPhysicalDevice.cs b/src/Ryujinx.Graphics.Vulkan/VulkanPhysicalDevice.cs index 3bee1e9d8..b3f8fd756 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanPhysicalDevice.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanPhysicalDevice.cs @@ -31,7 +31,7 @@ namespace Ryujinx.Graphics.Vulkan unsafe { - DeviceName = Marshal.PtrToStringAnsi((IntPtr)physicalDeviceProperties.DeviceName); + DeviceName = Marshal.PtrToStringAnsi((nint)physicalDeviceProperties.DeviceName); } uint propertiesCount = 0; @@ -50,7 +50,7 @@ namespace Ryujinx.Graphics.Vulkan unsafe { - DeviceExtensions = extensionProperties.Select(x => Marshal.PtrToStringAnsi((IntPtr)x.ExtensionName)).ToImmutableHashSet(); + DeviceExtensions = extensionProperties.Select(x => Marshal.PtrToStringAnsi((nint)x.ExtensionName)).ToImmutableHashSet(); } } diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 0faaec82a..ad4b18e50 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -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; @@ -27,6 +28,8 @@ namespace Ryujinx.Graphics.Vulkan private bool _initialized; + public uint ProgramCount { get; set; } = 0; + internal FormatCapabilities FormatCapabilities { get; private set; } internal HardwareCapabilities Capabilities; @@ -43,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; } @@ -102,25 +105,27 @@ namespace Ryujinx.Graphics.Vulkan public event EventHandler ScreenCaptured; - public VulkanRenderer(Vk api, Func surfaceFunc, Func requiredExtensionsFunc, string preferredGpuId) + public VulkanRenderer(Vk api, Func getSurface, Func requiredExtensionsFunc, string preferredGpuId) { - _getSurface = surfaceFunc; + _getSurface = getSurface; _getRequiredExtensions = requiredExtensionsFunc; _preferredGpuId = preferredGpuId; Api = api; - Shaders = new HashSet(); - Textures = new HashSet(); - Samplers = new HashSet(); + Shaders = []; + Textures = []; + Samplers = []; - if (OperatingSystem.IsMacOS()) - { + // Any device running on MacOS is using MoltenVK, even Intel and AMD vendors. + if (IsMoltenVk = OperatingSystem.IsMacOS()) MVKInitialization.Initialize(); - - // Any device running on MacOS is using MoltenVK, even Intel and AMD vendors. - IsMoltenVk = true; - } } + public static VulkanRenderer Create( + string preferredGpuId, + Func getSurface, + Func getRequiredExtensions + ) => new(Vk.GetApi(), getSurface, getRequiredExtensions, preferredGpuId); + private unsafe void LoadFeatures(uint maxQueueCount, uint queueFamilyIndex) { FormatCapabilities = new FormatCapabilities(Api, _physicalDevice.PhysicalDevice); @@ -159,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() @@ -363,7 +368,7 @@ namespace Ryujinx.Graphics.Vulkan fixed (byte* deviceName = properties.DeviceName) { - GpuRenderer = Marshal.PtrToStringAnsi((IntPtr)deviceName); + GpuRenderer = Marshal.PtrToStringAnsi((nint)deviceName); } GpuVersion = $"Vulkan v{ParseStandardVulkanVersion(properties.ApiVersion)}, Driver v{ParseDriverVersion(ref properties)}"; @@ -492,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); @@ -544,6 +549,8 @@ namespace Ryujinx.Graphics.Vulkan public IProgram CreateProgram(ShaderSource[] sources, ShaderInfo info) { + ProgramCount++; + bool isCompute = sources.Length == 1 && sources[0].Stage == ShaderStage.Compute; if (info.State.HasValue || isCompute) diff --git a/src/Ryujinx.Graphics.Vulkan/Window.cs b/src/Ryujinx.Graphics.Vulkan/Window.cs index 3dc6d4e19..3e8d3b375 100644 --- a/src/Ryujinx.Graphics.Vulkan/Window.cs +++ b/src/Ryujinx.Graphics.Vulkan/Window.cs @@ -29,7 +29,7 @@ namespace Ryujinx.Graphics.Vulkan private int _width; private int _height; - private bool _vsyncEnabled; + private VSyncMode _vSyncMode; private bool _swapchainIsDirty; private VkFormat _format; private AntiAliasing _currentAntiAliasing; @@ -139,7 +139,7 @@ namespace Ryujinx.Graphics.Vulkan ImageArrayLayers = 1, PreTransform = capabilities.CurrentTransform, CompositeAlpha = ChooseCompositeAlpha(capabilities.SupportedCompositeAlpha), - PresentMode = ChooseSwapPresentMode(presentModes, _vsyncEnabled), + PresentMode = ChooseSwapPresentMode(presentModes, _vSyncMode), Clipped = true, }; @@ -279,9 +279,9 @@ namespace Ryujinx.Graphics.Vulkan } } - private static PresentModeKHR ChooseSwapPresentMode(PresentModeKHR[] availablePresentModes, bool vsyncEnabled) + private static PresentModeKHR ChooseSwapPresentMode(PresentModeKHR[] availablePresentModes, VSyncMode vSyncMode) { - if (!vsyncEnabled && availablePresentModes.Contains(PresentModeKHR.ImmediateKhr)) + if (vSyncMode == VSyncMode.Unbounded && availablePresentModes.Contains(PresentModeKHR.ImmediateKhr)) { return PresentModeKHR.ImmediateKhr; } @@ -634,9 +634,10 @@ namespace Ryujinx.Graphics.Vulkan _swapchainIsDirty = true; } - public override void ChangeVSyncMode(bool vsyncEnabled) + public override void ChangeVSyncMode(VSyncMode vSyncMode) { - _vsyncEnabled = vsyncEnabled; + _vSyncMode = vSyncMode; + //present mode may change, so mark the swapchain for recreation _swapchainIsDirty = true; } diff --git a/src/Ryujinx.Graphics.Vulkan/WindowBase.cs b/src/Ryujinx.Graphics.Vulkan/WindowBase.cs index edb9c688c..ca06ec0b8 100644 --- a/src/Ryujinx.Graphics.Vulkan/WindowBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/WindowBase.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Graphics.Vulkan public abstract void Dispose(); public abstract void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback); public abstract void SetSize(int width, int height); - public abstract void ChangeVSyncMode(bool vsyncEnabled); + public abstract void ChangeVSyncMode(VSyncMode vSyncMode); public abstract void SetAntiAliasing(AntiAliasing effect); public abstract void SetScalingFilter(ScalingFilter scalerType); public abstract void SetScalingFilterLevel(float scale); diff --git a/src/Ryujinx.Gtk3/Input/GTK3/GTK3Keyboard.cs b/src/Ryujinx.Gtk3/Input/GTK3/GTK3Keyboard.cs deleted file mode 100644 index ff7a2c3b6..000000000 --- a/src/Ryujinx.Gtk3/Input/GTK3/GTK3Keyboard.cs +++ /dev/null @@ -1,205 +0,0 @@ -using Ryujinx.Common.Configuration.Hid; -using Ryujinx.Common.Configuration.Hid.Keyboard; -using System; -using System.Collections.Generic; -using System.Numerics; -using ConfigKey = Ryujinx.Common.Configuration.Hid.Key; - -namespace Ryujinx.Input.GTK3 -{ - public class GTK3Keyboard : IKeyboard - { - private class ButtonMappingEntry - { - public readonly GamepadButtonInputId To; - public readonly Key From; - - public ButtonMappingEntry(GamepadButtonInputId to, Key from) - { - To = to; - From = from; - } - } - - private readonly object _userMappingLock = new(); - - private readonly GTK3KeyboardDriver _driver; - private StandardKeyboardInputConfig _configuration; - private readonly List _buttonsUserMapping; - - public GTK3Keyboard(GTK3KeyboardDriver driver, string id, string name) - { - _driver = driver; - Id = id; - Name = name; - _buttonsUserMapping = new List(); - } - - private bool HasConfiguration => _configuration != null; - - public string Id { get; } - - public string Name { get; } - - public bool IsConnected => true; - - public GamepadFeaturesFlag Features => GamepadFeaturesFlag.None; - - public void Dispose() - { - // No operations - GC.SuppressFinalize(this); - } - - public KeyboardStateSnapshot GetKeyboardStateSnapshot() - { - return IKeyboard.GetStateSnapshot(this); - } - - private static float ConvertRawStickValue(short value) - { - const float ConvertRate = 1.0f / (short.MaxValue + 0.5f); - - return value * ConvertRate; - } - - private static (short, short) GetStickValues(ref KeyboardStateSnapshot snapshot, JoyconConfigKeyboardStick stickConfig) - { - short stickX = 0; - short stickY = 0; - - if (snapshot.IsPressed((Key)stickConfig.StickUp)) - { - stickY += 1; - } - - if (snapshot.IsPressed((Key)stickConfig.StickDown)) - { - stickY -= 1; - } - - if (snapshot.IsPressed((Key)stickConfig.StickRight)) - { - stickX += 1; - } - - if (snapshot.IsPressed((Key)stickConfig.StickLeft)) - { - stickX -= 1; - } - - OpenTK.Mathematics.Vector2 stick = new(stickX, stickY); - - stick.NormalizeFast(); - - return ((short)(stick.X * short.MaxValue), (short)(stick.Y * short.MaxValue)); - } - - public GamepadStateSnapshot GetMappedStateSnapshot() - { - KeyboardStateSnapshot rawState = GetKeyboardStateSnapshot(); - GamepadStateSnapshot result = default; - - lock (_userMappingLock) - { - if (!HasConfiguration) - { - return result; - } - - foreach (ButtonMappingEntry entry in _buttonsUserMapping) - { - if (entry.From == Key.Unknown || entry.From == Key.Unbound || entry.To == GamepadButtonInputId.Unbound) - { - continue; - } - - // Do not touch state of button already pressed - if (!result.IsPressed(entry.To)) - { - result.SetPressed(entry.To, rawState.IsPressed(entry.From)); - } - } - - (short leftStickX, short leftStickY) = GetStickValues(ref rawState, _configuration.LeftJoyconStick); - (short rightStickX, short rightStickY) = GetStickValues(ref rawState, _configuration.RightJoyconStick); - - result.SetStick(StickInputId.Left, ConvertRawStickValue(leftStickX), ConvertRawStickValue(leftStickY)); - result.SetStick(StickInputId.Right, ConvertRawStickValue(rightStickX), ConvertRawStickValue(rightStickY)); - } - - return result; - } - - public GamepadStateSnapshot GetStateSnapshot() - { - throw new NotSupportedException(); - } - - public (float, float) GetStick(StickInputId inputId) - { - throw new NotSupportedException(); - } - - public bool IsPressed(GamepadButtonInputId inputId) - { - throw new NotSupportedException(); - } - - public bool IsPressed(Key key) - { - return _driver.IsPressed(key); - } - - public void SetConfiguration(InputConfig configuration) - { - lock (_userMappingLock) - { - _configuration = (StandardKeyboardInputConfig)configuration; - - _buttonsUserMapping.Clear(); - - // Then left joycon - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftStick, (Key)_configuration.LeftJoyconStick.StickButton)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadUp, (Key)_configuration.LeftJoycon.DpadUp)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadDown, (Key)_configuration.LeftJoycon.DpadDown)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadLeft, (Key)_configuration.LeftJoycon.DpadLeft)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadRight, (Key)_configuration.LeftJoycon.DpadRight)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Minus, (Key)_configuration.LeftJoycon.ButtonMinus)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftShoulder, (Key)_configuration.LeftJoycon.ButtonL)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftTrigger, (Key)_configuration.LeftJoycon.ButtonZl)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger0, (Key)_configuration.LeftJoycon.ButtonSr)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger0, (Key)_configuration.LeftJoycon.ButtonSl)); - - // Finally right joycon - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightStick, (Key)_configuration.RightJoyconStick.StickButton)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.A, (Key)_configuration.RightJoycon.ButtonA)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.B, (Key)_configuration.RightJoycon.ButtonB)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.X, (Key)_configuration.RightJoycon.ButtonX)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Y, (Key)_configuration.RightJoycon.ButtonY)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Plus, (Key)_configuration.RightJoycon.ButtonPlus)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightShoulder, (Key)_configuration.RightJoycon.ButtonR)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightTrigger, (Key)_configuration.RightJoycon.ButtonZr)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger1, (Key)_configuration.RightJoycon.ButtonSr)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger1, (Key)_configuration.RightJoycon.ButtonSl)); - } - } - - public void SetTriggerThreshold(float triggerThreshold) - { - // No operations - } - - public void Rumble(float lowFrequency, float highFrequency, uint durationMs) - { - // No operations - } - - public Vector3 GetMotionData(MotionInputId inputId) - { - // No operations - - return Vector3.Zero; - } - } -} diff --git a/src/Ryujinx.Gtk3/Input/GTK3/GTK3KeyboardDriver.cs b/src/Ryujinx.Gtk3/Input/GTK3/GTK3KeyboardDriver.cs deleted file mode 100644 index bd71c7933..000000000 --- a/src/Ryujinx.Gtk3/Input/GTK3/GTK3KeyboardDriver.cs +++ /dev/null @@ -1,99 +0,0 @@ -using Gdk; -using Gtk; -using System; -using System.Collections.Generic; -using GtkKey = Gdk.Key; - -namespace Ryujinx.Input.GTK3 -{ - public class GTK3KeyboardDriver : IGamepadDriver - { - private readonly Widget _widget; - private readonly HashSet _pressedKeys; - - public GTK3KeyboardDriver(Widget widget) - { - _widget = widget; - _pressedKeys = new HashSet(); - - _widget.KeyPressEvent += OnKeyPress; - _widget.KeyReleaseEvent += OnKeyRelease; - } - - public string DriverName => "GTK3"; - - private static readonly string[] _keyboardIdentifers = new string[1] { "0" }; - - public ReadOnlySpan GamepadsIds => _keyboardIdentifers; - - public event Action OnGamepadConnected - { - add { } - remove { } - } - - public event Action OnGamepadDisconnected - { - add { } - remove { } - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - _widget.KeyPressEvent -= OnKeyPress; - _widget.KeyReleaseEvent -= OnKeyRelease; - } - } - - public void Dispose() - { - GC.SuppressFinalize(this); - Dispose(true); - } - - [GLib.ConnectBefore] - protected void OnKeyPress(object sender, KeyPressEventArgs args) - { - GtkKey key = (GtkKey)Keyval.ToLower((uint)args.Event.Key); - - _pressedKeys.Add(key); - } - - [GLib.ConnectBefore] - protected void OnKeyRelease(object sender, KeyReleaseEventArgs args) - { - GtkKey key = (GtkKey)Keyval.ToLower((uint)args.Event.Key); - - _pressedKeys.Remove(key); - } - - internal bool IsPressed(Key key) - { - if (key == Key.Unbound || key == Key.Unknown) - { - return false; - } - - GtkKey nativeKey = GTK3MappingHelper.ToGtkKey(key); - - return _pressedKeys.Contains(nativeKey); - } - - public void Clear() - { - _pressedKeys.Clear(); - } - - public IGamepad GetGamepad(string id) - { - if (!_keyboardIdentifers[0].Equals(id)) - { - return null; - } - - return new GTK3Keyboard(this, _keyboardIdentifers[0], "All keyboards"); - } - } -} diff --git a/src/Ryujinx.Gtk3/Input/GTK3/GTK3MappingHelper.cs b/src/Ryujinx.Gtk3/Input/GTK3/GTK3MappingHelper.cs deleted file mode 100644 index 422a96030..000000000 --- a/src/Ryujinx.Gtk3/Input/GTK3/GTK3MappingHelper.cs +++ /dev/null @@ -1,178 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.CompilerServices; -using GtkKey = Gdk.Key; - -namespace Ryujinx.Input.GTK3 -{ - public static class GTK3MappingHelper - { - private static readonly GtkKey[] _keyMapping = new GtkKey[(int)Key.Count] - { - // NOTE: invalid - GtkKey.blank, - - GtkKey.Shift_L, - GtkKey.Shift_R, - GtkKey.Control_L, - GtkKey.Control_R, - GtkKey.Alt_L, - GtkKey.Alt_R, - GtkKey.Super_L, - GtkKey.Super_R, - GtkKey.Menu, - GtkKey.F1, - GtkKey.F2, - GtkKey.F3, - GtkKey.F4, - GtkKey.F5, - GtkKey.F6, - GtkKey.F7, - GtkKey.F8, - GtkKey.F9, - GtkKey.F10, - GtkKey.F11, - GtkKey.F12, - GtkKey.F13, - GtkKey.F14, - GtkKey.F15, - GtkKey.F16, - GtkKey.F17, - GtkKey.F18, - GtkKey.F19, - GtkKey.F20, - GtkKey.F21, - GtkKey.F22, - GtkKey.F23, - GtkKey.F24, - GtkKey.F25, - GtkKey.F26, - GtkKey.F27, - GtkKey.F28, - GtkKey.F29, - GtkKey.F30, - GtkKey.F31, - GtkKey.F32, - GtkKey.F33, - GtkKey.F34, - GtkKey.F35, - GtkKey.Up, - GtkKey.Down, - GtkKey.Left, - GtkKey.Right, - GtkKey.Return, - GtkKey.Escape, - GtkKey.space, - GtkKey.Tab, - GtkKey.BackSpace, - GtkKey.Insert, - GtkKey.Delete, - GtkKey.Page_Up, - GtkKey.Page_Down, - GtkKey.Home, - GtkKey.End, - GtkKey.Caps_Lock, - GtkKey.Scroll_Lock, - GtkKey.Print, - GtkKey.Pause, - GtkKey.Num_Lock, - GtkKey.Clear, - GtkKey.KP_0, - GtkKey.KP_1, - GtkKey.KP_2, - GtkKey.KP_3, - GtkKey.KP_4, - GtkKey.KP_5, - GtkKey.KP_6, - GtkKey.KP_7, - GtkKey.KP_8, - GtkKey.KP_9, - GtkKey.KP_Divide, - GtkKey.KP_Multiply, - GtkKey.KP_Subtract, - GtkKey.KP_Add, - GtkKey.KP_Decimal, - GtkKey.KP_Enter, - GtkKey.a, - GtkKey.b, - GtkKey.c, - GtkKey.d, - GtkKey.e, - GtkKey.f, - GtkKey.g, - GtkKey.h, - GtkKey.i, - GtkKey.j, - GtkKey.k, - GtkKey.l, - GtkKey.m, - GtkKey.n, - GtkKey.o, - GtkKey.p, - GtkKey.q, - GtkKey.r, - GtkKey.s, - GtkKey.t, - GtkKey.u, - GtkKey.v, - GtkKey.w, - GtkKey.x, - GtkKey.y, - GtkKey.z, - GtkKey.Key_0, - GtkKey.Key_1, - GtkKey.Key_2, - GtkKey.Key_3, - GtkKey.Key_4, - GtkKey.Key_5, - GtkKey.Key_6, - GtkKey.Key_7, - GtkKey.Key_8, - GtkKey.Key_9, - GtkKey.grave, - GtkKey.grave, - GtkKey.minus, - GtkKey.plus, - GtkKey.bracketleft, - GtkKey.bracketright, - GtkKey.semicolon, - GtkKey.quoteright, - GtkKey.comma, - GtkKey.period, - GtkKey.slash, - GtkKey.backslash, - - // NOTE: invalid - GtkKey.blank, - }; - - private static readonly Dictionary _gtkKeyMapping; - - static GTK3MappingHelper() - { - var inputKeys = Enum.GetValues().SkipLast(1); - - // GtkKey is not contiguous and quite large, so use a dictionary instead of an array. - _gtkKeyMapping = new Dictionary(); - - foreach (var key in inputKeys) - { - var index = ToGtkKey(key); - _gtkKeyMapping[index] = key; - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static GtkKey ToGtkKey(Key key) - { - return _keyMapping[(int)key]; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Key ToInputKey(GtkKey key) - { - return _gtkKeyMapping.GetValueOrDefault(key, Key.Unknown); - } - } -} diff --git a/src/Ryujinx.Gtk3/Input/GTK3/GTK3Mouse.cs b/src/Ryujinx.Gtk3/Input/GTK3/GTK3Mouse.cs deleted file mode 100644 index 0ab817ecb..000000000 --- a/src/Ryujinx.Gtk3/Input/GTK3/GTK3Mouse.cs +++ /dev/null @@ -1,90 +0,0 @@ -using Ryujinx.Common.Configuration.Hid; -using System; -using System.Drawing; -using System.Numerics; - -namespace Ryujinx.Input.GTK3 -{ - public class GTK3Mouse : IMouse - { - private GTK3MouseDriver _driver; - - public GamepadFeaturesFlag Features => throw new NotImplementedException(); - - public string Id => "0"; - - public string Name => "GTKMouse"; - - public bool IsConnected => true; - - public bool[] Buttons => _driver.PressedButtons; - - public GTK3Mouse(GTK3MouseDriver driver) - { - _driver = driver; - } - - public Size ClientSize => _driver.GetClientSize(); - - public Vector2 GetPosition() - { - return _driver.CurrentPosition; - } - - public Vector2 GetScroll() - { - return _driver.Scroll; - } - - public GamepadStateSnapshot GetMappedStateSnapshot() - { - throw new NotImplementedException(); - } - - public Vector3 GetMotionData(MotionInputId inputId) - { - throw new NotImplementedException(); - } - - public GamepadStateSnapshot GetStateSnapshot() - { - throw new NotImplementedException(); - } - - public (float, float) GetStick(StickInputId inputId) - { - throw new NotImplementedException(); - } - - public bool IsButtonPressed(MouseButton button) - { - return _driver.IsButtonPressed(button); - } - - public bool IsPressed(GamepadButtonInputId inputId) - { - throw new NotImplementedException(); - } - - public void Rumble(float lowFrequency, float highFrequency, uint durationMs) - { - throw new NotImplementedException(); - } - - public void SetConfiguration(InputConfig configuration) - { - throw new NotImplementedException(); - } - - public void SetTriggerThreshold(float triggerThreshold) - { - throw new NotImplementedException(); - } - - public void Dispose() - { - GC.SuppressFinalize(this); - _driver = null; - } - } -} diff --git a/src/Ryujinx.Gtk3/Input/GTK3/GTK3MouseDriver.cs b/src/Ryujinx.Gtk3/Input/GTK3/GTK3MouseDriver.cs deleted file mode 100644 index 5962bcb25..000000000 --- a/src/Ryujinx.Gtk3/Input/GTK3/GTK3MouseDriver.cs +++ /dev/null @@ -1,108 +0,0 @@ -using Gdk; -using Gtk; -using System; -using System.Numerics; -using Size = System.Drawing.Size; - -namespace Ryujinx.Input.GTK3 -{ - public class GTK3MouseDriver : IGamepadDriver - { - private Widget _widget; - private bool _isDisposed; - - public bool[] PressedButtons { get; } - - public Vector2 CurrentPosition { get; private set; } - public Vector2 Scroll { get; private set; } - - public GTK3MouseDriver(Widget parent) - { - _widget = parent; - - _widget.MotionNotifyEvent += Parent_MotionNotifyEvent; - _widget.ButtonPressEvent += Parent_ButtonPressEvent; - _widget.ButtonReleaseEvent += Parent_ButtonReleaseEvent; - _widget.ScrollEvent += Parent_ScrollEvent; - - PressedButtons = new bool[(int)MouseButton.Count]; - } - - - [GLib.ConnectBefore] - private void Parent_ScrollEvent(object o, ScrollEventArgs args) - { - Scroll = new Vector2((float)args.Event.X, (float)args.Event.Y); - } - - [GLib.ConnectBefore] - private void Parent_ButtonReleaseEvent(object o, ButtonReleaseEventArgs args) - { - PressedButtons[args.Event.Button - 1] = false; - } - - [GLib.ConnectBefore] - private void Parent_ButtonPressEvent(object o, ButtonPressEventArgs args) - { - PressedButtons[args.Event.Button - 1] = true; - } - - [GLib.ConnectBefore] - private void Parent_MotionNotifyEvent(object o, MotionNotifyEventArgs args) - { - if (args.Event.Device.InputSource == InputSource.Mouse) - { - CurrentPosition = new Vector2((float)args.Event.X, (float)args.Event.Y); - } - } - - public bool IsButtonPressed(MouseButton button) - { - return PressedButtons[(int)button]; - } - - public Size GetClientSize() - { - return new Size(_widget.AllocatedWidth, _widget.AllocatedHeight); - } - - public string DriverName => "GTK3"; - - public event Action OnGamepadConnected - { - add { } - remove { } - } - - public event Action OnGamepadDisconnected - { - add { } - remove { } - } - - public ReadOnlySpan GamepadsIds => new[] { "0" }; - - public IGamepad GetGamepad(string id) - { - return new GTK3Mouse(this); - } - - public void Dispose() - { - if (_isDisposed) - { - return; - } - - GC.SuppressFinalize(this); - - _isDisposed = true; - - _widget.MotionNotifyEvent -= Parent_MotionNotifyEvent; - _widget.ButtonPressEvent -= Parent_ButtonPressEvent; - _widget.ButtonReleaseEvent -= Parent_ButtonReleaseEvent; - - _widget = null; - } - } -} diff --git a/src/Ryujinx.Gtk3/Modules/Updater/UpdateDialog.cs b/src/Ryujinx.Gtk3/Modules/Updater/UpdateDialog.cs deleted file mode 100644 index 43bde9420..000000000 --- a/src/Ryujinx.Gtk3/Modules/Updater/UpdateDialog.cs +++ /dev/null @@ -1,95 +0,0 @@ -using Gdk; -using Gtk; -using Ryujinx.Common; -using Ryujinx.Common.Configuration; -using Ryujinx.UI; -using Ryujinx.UI.Common.Configuration; -using Ryujinx.UI.Common.Helper; -using System; -using System.Diagnostics; -using System.Reflection; - -namespace Ryujinx.Modules -{ - public class UpdateDialog : Gtk.Window - { -#pragma warning disable CS0649, IDE0044 // Field is never assigned to, Add readonly modifier - [Builder.Object] public Label MainText; - [Builder.Object] public Label SecondaryText; - [Builder.Object] public LevelBar ProgressBar; - [Builder.Object] public Button YesButton; - [Builder.Object] public Button NoButton; -#pragma warning restore CS0649, IDE0044 - - private readonly MainWindow _mainWindow; - private readonly string _buildUrl; - private bool _restartQuery; - - public UpdateDialog(MainWindow mainWindow, Version newVersion, string buildUrl) : this(new Builder("Ryujinx.Gtk3.Modules.Updater.UpdateDialog.glade"), mainWindow, newVersion, buildUrl) { } - - private UpdateDialog(Builder builder, MainWindow mainWindow, Version newVersion, string buildUrl) : base(builder.GetRawOwnedObject("UpdateDialog")) - { - builder.Autoconnect(this); - - _mainWindow = mainWindow; - _buildUrl = buildUrl; - - Icon = new Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Gtk3.UI.Common.Resources.Logo_Ryujinx.png"); - MainText.Text = "Do you want to update Ryujinx to the latest version?"; - SecondaryText.Text = $"{Program.Version} -> {newVersion}"; - - ProgressBar.Hide(); - - YesButton.Clicked += YesButton_Clicked; - NoButton.Clicked += NoButton_Clicked; - } - - private void YesButton_Clicked(object sender, EventArgs args) - { - if (_restartQuery) - { - string ryuName = OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx"; - - ProcessStartInfo processStart = new(ryuName) - { - UseShellExecute = true, - WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory - }; - - foreach (string argument in CommandLineState.Arguments) - { - processStart.ArgumentList.Add(argument); - } - - Process.Start(processStart); - - Environment.Exit(0); - } - else - { - Window.Functions = _mainWindow.Window.Functions = WMFunction.All & WMFunction.Close; - _mainWindow.ExitMenuItem.Sensitive = false; - - YesButton.Hide(); - NoButton.Hide(); - ProgressBar.Show(); - - SecondaryText.Text = ""; - _restartQuery = true; - - Updater.UpdateRyujinx(this, _buildUrl); - } - } - - private void NoButton_Clicked(object sender, EventArgs args) - { - Updater.Running = false; - _mainWindow.Window.Functions = WMFunction.All; - - _mainWindow.ExitMenuItem.Sensitive = true; - _mainWindow.UpdateMenuItem.Sensitive = true; - - Dispose(); - } - } -} diff --git a/src/Ryujinx.Gtk3/Modules/Updater/UpdateDialog.glade b/src/Ryujinx.Gtk3/Modules/Updater/UpdateDialog.glade deleted file mode 100644 index cc80167e0..000000000 --- a/src/Ryujinx.Gtk3/Modules/Updater/UpdateDialog.glade +++ /dev/null @@ -1,127 +0,0 @@ - - - - - - False - Ryujinx - Updater - False - center - 400 - 130 - - - - - - True - False - 10 - 10 - 10 - 10 - vertical - - - True - False - vertical - - - True - False - 5 - 5 - - - - - - - False - True - 0 - - - - - True - False - 5 - 5 - - - False - True - 1 - - - - - 20 - True - False - 5 - 5 - 100 - - - False - True - 2 - - - - - True - True - 0 - - - - - True - False - - - Yes - True - True - True - 5 - 5 - 5 - - - True - True - 0 - - - - - No - True - True - True - 5 - 5 - 5 - - - True - True - 1 - - - - - False - True - 1 - - - - - - diff --git a/src/Ryujinx.Gtk3/Modules/Updater/Updater.cs b/src/Ryujinx.Gtk3/Modules/Updater/Updater.cs deleted file mode 100644 index 8b006f63f..000000000 --- a/src/Ryujinx.Gtk3/Modules/Updater/Updater.cs +++ /dev/null @@ -1,622 +0,0 @@ -using Gtk; -using ICSharpCode.SharpZipLib.GZip; -using ICSharpCode.SharpZipLib.Tar; -using ICSharpCode.SharpZipLib.Zip; -using Ryujinx.Common; -using Ryujinx.Common.Logging; -using Ryujinx.Common.Utilities; -using Ryujinx.UI; -using Ryujinx.UI.Common.Models.Github; -using Ryujinx.UI.Widgets; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Net.NetworkInformation; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace Ryujinx.Modules -{ - public static class Updater - { - private const string GitHubApiUrl = "https://api.github.com"; - private const int ConnectionCount = 4; - - internal static bool Running; - - private static readonly string _homeDir = AppDomain.CurrentDomain.BaseDirectory; - private static readonly string _updateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update"); - private static readonly string _updatePublishDir = Path.Combine(_updateDir, "publish"); - - private static string _buildVer; - private static string _platformExt; - private static string _buildUrl; - private static long _buildSize; - - private static readonly GithubReleasesJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - - // On Windows, GtkSharp.Dependencies adds these extra dirs that must be cleaned during updates. - private static readonly string[] _windowsDependencyDirs = { "bin", "etc", "lib", "share" }; - - private static HttpClient ConstructHttpClient() - { - HttpClient result = new(); - - // Required by GitHub to interact with APIs. - result.DefaultRequestHeaders.Add("User-Agent", "Ryujinx-Updater/1.0.0"); - - return result; - } - - public static async Task BeginParse(MainWindow mainWindow, bool showVersionUpToDate) - { - if (Running) - { - return; - } - - Running = true; - mainWindow.UpdateMenuItem.Sensitive = false; - - int artifactIndex = -1; - - // Detect current platform - if (OperatingSystem.IsMacOS()) - { - _platformExt = "osx_x64.zip"; - artifactIndex = 1; - } - else if (OperatingSystem.IsWindows()) - { - _platformExt = "win_x64.zip"; - artifactIndex = 2; - } - else if (OperatingSystem.IsLinux()) - { - var arch = RuntimeInformation.OSArchitecture == Architecture.Arm64 ? "arm64" : "x64"; - _platformExt = $"linux_{arch}.tar.gz"; - artifactIndex = 0; - } - - if (artifactIndex == -1) - { - GtkDialog.CreateErrorDialog("Your platform is not supported!"); - - return; - } - - Version newVersion; - Version currentVersion; - - try - { - currentVersion = Version.Parse(Program.Version); - } - catch - { - GtkDialog.CreateWarningDialog("Failed to convert the current Ryujinx version.", "Cancelling Update!"); - Logger.Error?.Print(LogClass.Application, "Failed to convert the current Ryujinx version!"); - - return; - } - - // Get latest version number from GitHub API - try - { - using HttpClient jsonClient = ConstructHttpClient(); - string buildInfoUrl = $"{GitHubApiUrl}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest"; - - // Fetch latest build information - string fetchedJson = await jsonClient.GetStringAsync(buildInfoUrl); - var fetched = JsonHelper.Deserialize(fetchedJson, _serializerContext.GithubReleasesJsonResponse); - _buildVer = fetched.Name; - - foreach (var asset in fetched.Assets) - { - if (asset.Name.StartsWith("gtk-ryujinx") && asset.Name.EndsWith(_platformExt)) - { - _buildUrl = asset.BrowserDownloadUrl; - - if (asset.State != "uploaded") - { - if (showVersionUpToDate) - { - GtkDialog.CreateUpdaterInfoDialog("You are already using the latest version of Ryujinx!", ""); - } - - return; - } - - break; - } - } - - if (_buildUrl == null) - { - if (showVersionUpToDate) - { - GtkDialog.CreateUpdaterInfoDialog("You are already using the latest version of Ryujinx!", ""); - } - - return; - } - } - catch (Exception exception) - { - Logger.Error?.Print(LogClass.Application, exception.Message); - GtkDialog.CreateErrorDialog("An error occurred when trying to get release information from GitHub Release. This can be caused if a new release is being compiled by GitHub Actions. Try again in a few minutes."); - - return; - } - - try - { - newVersion = Version.Parse(_buildVer); - } - catch - { - GtkDialog.CreateWarningDialog("Failed to convert the received Ryujinx version from GitHub Release.", "Cancelling Update!"); - Logger.Error?.Print(LogClass.Application, "Failed to convert the received Ryujinx version from GitHub Release!"); - - return; - } - - if (newVersion <= currentVersion) - { - if (showVersionUpToDate) - { - GtkDialog.CreateUpdaterInfoDialog("You are already using the latest version of Ryujinx!", ""); - } - - Running = false; - mainWindow.UpdateMenuItem.Sensitive = true; - - return; - } - - // Fetch build size information to learn chunk sizes. - using HttpClient buildSizeClient = ConstructHttpClient(); - try - { - buildSizeClient.DefaultRequestHeaders.Add("Range", "bytes=0-0"); - - HttpResponseMessage message = await buildSizeClient.GetAsync(new Uri(_buildUrl), HttpCompletionOption.ResponseHeadersRead); - - _buildSize = message.Content.Headers.ContentRange.Length.Value; - } - catch (Exception ex) - { - Logger.Warning?.Print(LogClass.Application, ex.Message); - Logger.Warning?.Print(LogClass.Application, "Couldn't determine build size for update, using single-threaded updater"); - - _buildSize = -1; - } - - // Show a message asking the user if they want to update - UpdateDialog updateDialog = new(mainWindow, newVersion, _buildUrl); - updateDialog.Show(); - } - - public static void UpdateRyujinx(UpdateDialog updateDialog, string downloadUrl) - { - // Empty update dir, although it shouldn't ever have anything inside it - if (Directory.Exists(_updateDir)) - { - Directory.Delete(_updateDir, true); - } - - Directory.CreateDirectory(_updateDir); - - string updateFile = Path.Combine(_updateDir, "update.bin"); - - // Download the update .zip - updateDialog.MainText.Text = "Downloading Update..."; - updateDialog.ProgressBar.Value = 0; - updateDialog.ProgressBar.MaxValue = 100; - - if (_buildSize >= 0) - { - DoUpdateWithMultipleThreads(updateDialog, downloadUrl, updateFile); - } - else - { - DoUpdateWithSingleThread(updateDialog, downloadUrl, updateFile); - } - } - - private static void DoUpdateWithMultipleThreads(UpdateDialog updateDialog, string downloadUrl, string updateFile) - { - // Multi-Threaded Updater - long chunkSize = _buildSize / ConnectionCount; - long remainderChunk = _buildSize % ConnectionCount; - - int completedRequests = 0; - int totalProgressPercentage = 0; - int[] progressPercentage = new int[ConnectionCount]; - - List list = new(ConnectionCount); - List webClients = new(ConnectionCount); - - for (int i = 0; i < ConnectionCount; i++) - { - list.Add(Array.Empty()); - } - - for (int i = 0; i < ConnectionCount; i++) - { -#pragma warning disable SYSLIB0014 - // TODO: WebClient is obsolete and need to be replaced with a more complex logic using HttpClient. - using WebClient client = new(); -#pragma warning restore SYSLIB0014 - webClients.Add(client); - - if (i == ConnectionCount - 1) - { - client.Headers.Add("Range", $"bytes={chunkSize * i}-{(chunkSize * (i + 1) - 1) + remainderChunk}"); - } - else - { - client.Headers.Add("Range", $"bytes={chunkSize * i}-{chunkSize * (i + 1) - 1}"); - } - - client.DownloadProgressChanged += (_, args) => - { - int index = (int)args.UserState; - - Interlocked.Add(ref totalProgressPercentage, -1 * progressPercentage[index]); - Interlocked.Exchange(ref progressPercentage[index], args.ProgressPercentage); - Interlocked.Add(ref totalProgressPercentage, args.ProgressPercentage); - - updateDialog.ProgressBar.Value = totalProgressPercentage / ConnectionCount; - }; - - client.DownloadDataCompleted += (_, args) => - { - int index = (int)args.UserState; - - if (args.Cancelled) - { - webClients[index].Dispose(); - - return; - } - - list[index] = args.Result; - Interlocked.Increment(ref completedRequests); - - if (Equals(completedRequests, ConnectionCount)) - { - byte[] mergedFileBytes = new byte[_buildSize]; - for (int connectionIndex = 0, destinationOffset = 0; connectionIndex < ConnectionCount; connectionIndex++) - { - Array.Copy(list[connectionIndex], 0, mergedFileBytes, destinationOffset, list[connectionIndex].Length); - destinationOffset += list[connectionIndex].Length; - } - - File.WriteAllBytes(updateFile, mergedFileBytes); - - try - { - InstallUpdate(updateDialog, updateFile); - } - catch (Exception e) - { - Logger.Warning?.Print(LogClass.Application, e.Message); - Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater."); - - DoUpdateWithSingleThread(updateDialog, downloadUrl, updateFile); - - return; - } - } - }; - - try - { - client.DownloadDataAsync(new Uri(downloadUrl), i); - } - catch (WebException ex) - { - Logger.Warning?.Print(LogClass.Application, ex.Message); - Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater."); - - foreach (WebClient webClient in webClients) - { - webClient.CancelAsync(); - } - - DoUpdateWithSingleThread(updateDialog, downloadUrl, updateFile); - - return; - } - } - } - - private static void DoUpdateWithSingleThreadWorker(UpdateDialog updateDialog, string downloadUrl, string updateFile) - { - using HttpClient client = new(); - // We do not want to timeout while downloading - client.Timeout = TimeSpan.FromDays(1); - - using HttpResponseMessage response = client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead).Result; - using Stream remoteFileStream = response.Content.ReadAsStreamAsync().Result; - using Stream updateFileStream = File.Open(updateFile, FileMode.Create); - - long totalBytes = response.Content.Headers.ContentLength.Value; - long byteWritten = 0; - - byte[] buffer = new byte[32 * 1024]; - - while (true) - { - int readSize = remoteFileStream.Read(buffer); - - if (readSize == 0) - { - break; - } - - byteWritten += readSize; - - updateDialog.ProgressBar.Value = ((double)byteWritten / totalBytes) * 100; - updateFileStream.Write(buffer, 0, readSize); - } - - InstallUpdate(updateDialog, updateFile); - } - - private static void DoUpdateWithSingleThread(UpdateDialog updateDialog, string downloadUrl, string updateFile) - { - Thread worker = new(() => DoUpdateWithSingleThreadWorker(updateDialog, downloadUrl, updateFile)) - { - Name = "Updater.SingleThreadWorker", - }; - worker.Start(); - } - - private static async void InstallUpdate(UpdateDialog updateDialog, string updateFile) - { - // Extract Update - updateDialog.MainText.Text = "Extracting Update..."; - updateDialog.ProgressBar.Value = 0; - - if (OperatingSystem.IsLinux()) - { - using Stream inStream = File.OpenRead(updateFile); - using Stream gzipStream = new GZipInputStream(inStream); - using TarInputStream tarStream = new(gzipStream, Encoding.ASCII); - updateDialog.ProgressBar.MaxValue = inStream.Length; - - await Task.Run(() => - { - TarEntry tarEntry; - - if (!OperatingSystem.IsWindows()) - { - while ((tarEntry = tarStream.GetNextEntry()) != null) - { - if (tarEntry.IsDirectory) - { - continue; - } - - string outPath = Path.Combine(_updateDir, tarEntry.Name); - - Directory.CreateDirectory(Path.GetDirectoryName(outPath)); - - using FileStream outStream = File.OpenWrite(outPath); - tarStream.CopyEntryContents(outStream); - - File.SetUnixFileMode(outPath, (UnixFileMode)tarEntry.TarHeader.Mode); - File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc)); - - TarEntry entry = tarEntry; - - Application.Invoke(delegate - { - updateDialog.ProgressBar.Value += entry.Size; - }); - } - } - }); - - updateDialog.ProgressBar.Value = inStream.Length; - } - else - { - using Stream inStream = File.OpenRead(updateFile); - using ZipFile zipFile = new(inStream); - updateDialog.ProgressBar.MaxValue = zipFile.Count; - - await Task.Run(() => - { - foreach (ZipEntry zipEntry in zipFile) - { - if (zipEntry.IsDirectory) - { - continue; - } - - string outPath = Path.Combine(_updateDir, zipEntry.Name); - - Directory.CreateDirectory(Path.GetDirectoryName(outPath)); - - using Stream zipStream = zipFile.GetInputStream(zipEntry); - using FileStream outStream = File.OpenWrite(outPath); - zipStream.CopyTo(outStream); - - File.SetLastWriteTime(outPath, DateTime.SpecifyKind(zipEntry.DateTime, DateTimeKind.Utc)); - - Application.Invoke(delegate - { - updateDialog.ProgressBar.Value++; - }); - } - }); - } - - // Delete downloaded zip - File.Delete(updateFile); - - List allFiles = EnumerateFilesToDelete().ToList(); - - updateDialog.MainText.Text = "Renaming Old Files..."; - updateDialog.ProgressBar.Value = 0; - updateDialog.ProgressBar.MaxValue = allFiles.Count; - - // Replace old files - await Task.Run(() => - { - foreach (string file in allFiles) - { - try - { - File.Move(file, file + ".ryuold"); - - Application.Invoke(delegate - { - updateDialog.ProgressBar.Value++; - }); - } - catch - { - Logger.Warning?.Print(LogClass.Application, "Updater was unable to rename file: " + file); - } - } - - Application.Invoke(delegate - { - updateDialog.MainText.Text = "Adding New Files..."; - updateDialog.ProgressBar.Value = 0; - updateDialog.ProgressBar.MaxValue = Directory.GetFiles(_updatePublishDir, "*", SearchOption.AllDirectories).Length; - }); - - MoveAllFilesOver(_updatePublishDir, _homeDir, updateDialog); - }); - - Directory.Delete(_updateDir, true); - - updateDialog.MainText.Text = "Update Complete!"; - updateDialog.SecondaryText.Text = "Do you want to restart Ryujinx now?"; - updateDialog.Modal = true; - - updateDialog.ProgressBar.Hide(); - updateDialog.YesButton.Show(); - updateDialog.NoButton.Show(); - } - - public static bool CanUpdate(bool showWarnings) - { -#if !DISABLE_UPDATER - if (!NetworkInterface.GetIsNetworkAvailable()) - { - if (showWarnings) - { - GtkDialog.CreateWarningDialog("You are not connected to the Internet!", "Please verify that you have a working Internet connection!"); - } - - return false; - } - - if (Program.Version.Contains("dirty") || !ReleaseInformation.IsValid) - { - if (showWarnings) - { - GtkDialog.CreateWarningDialog("You cannot update a Dirty build of Ryujinx!", "Please download Ryujinx at https://ryujinx.org/ if you are looking for a supported version."); - } - - return false; - } - - return true; -#else - if (showWarnings) - { - if (ReleaseInformation.IsFlatHubBuild) - { - GtkDialog.CreateWarningDialog("Updater Disabled!", "Please update Ryujinx via FlatHub."); - } - else - { - GtkDialog.CreateWarningDialog("Updater Disabled!", "Please download Ryujinx at https://ryujinx.org/ if you are looking for a supported version."); - } - } - - return false; -#endif - } - - // NOTE: This method should always reflect the latest build layout. - private static IEnumerable EnumerateFilesToDelete() - { - var files = Directory.EnumerateFiles(_homeDir); // All files directly in base dir. - - // Determine and exclude user files only when the updater is running, not when cleaning old files - if (Running) - { - // Compare the loose files in base directory against the loose files from the incoming update, and store foreign ones in a user list. - var oldFiles = Directory.EnumerateFiles(_homeDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName); - var newFiles = Directory.EnumerateFiles(_updatePublishDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName); - var userFiles = oldFiles.Except(newFiles).Select(filename => Path.Combine(_homeDir, filename)); - - // Remove user files from the paths in files. - files = files.Except(userFiles); - } - - if (OperatingSystem.IsWindows()) - { - foreach (string dir in _windowsDependencyDirs) - { - string dirPath = Path.Combine(_homeDir, dir); - if (Directory.Exists(dirPath)) - { - files = files.Concat(Directory.EnumerateFiles(dirPath, "*", SearchOption.AllDirectories)); - } - } - } - - return files.Where(f => !new FileInfo(f).Attributes.HasFlag(FileAttributes.Hidden | FileAttributes.System)); - } - - private static void MoveAllFilesOver(string root, string dest, UpdateDialog dialog) - { - foreach (string directory in Directory.GetDirectories(root)) - { - string dirName = Path.GetFileName(directory); - - if (!Directory.Exists(Path.Combine(dest, dirName))) - { - Directory.CreateDirectory(Path.Combine(dest, dirName)); - } - - MoveAllFilesOver(directory, Path.Combine(dest, dirName), dialog); - } - - foreach (string file in Directory.GetFiles(root)) - { - File.Move(file, Path.Combine(dest, Path.GetFileName(file)), true); - - Application.Invoke(delegate - { - dialog.ProgressBar.Value++; - }); - } - } - - public static void CleanupUpdate() - { - foreach (string file in EnumerateFilesToDelete()) - { - if (Path.GetExtension(file).EndsWith(".ryuold")) - { - File.Delete(file); - } - } - } - } -} diff --git a/src/Ryujinx.Gtk3/Program.cs b/src/Ryujinx.Gtk3/Program.cs deleted file mode 100644 index 2d350374b..000000000 --- a/src/Ryujinx.Gtk3/Program.cs +++ /dev/null @@ -1,403 +0,0 @@ -using Gtk; -using Ryujinx.Common; -using Ryujinx.Common.Configuration; -using Ryujinx.Common.GraphicsDriver; -using Ryujinx.Common.Logging; -using Ryujinx.Common.SystemInterop; -using Ryujinx.Common.Utilities; -using Ryujinx.Graphics.Vulkan.MoltenVK; -using Ryujinx.Modules; -using Ryujinx.SDL2.Common; -using Ryujinx.UI; -using Ryujinx.UI.App.Common; -using Ryujinx.UI.Common; -using Ryujinx.UI.Common.Configuration; -using Ryujinx.UI.Common.Helper; -using Ryujinx.UI.Common.SystemInfo; -using Ryujinx.UI.Widgets; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Runtime.InteropServices; -using System.Threading.Tasks; - -namespace Ryujinx -{ - partial class Program - { - public static double WindowScaleFactor { get; private set; } - - public static string Version { get; private set; } - - public static string ConfigurationPath { get; set; } - - public static string CommandLineProfile { get; set; } - - private const string X11LibraryName = "libX11"; - - [LibraryImport(X11LibraryName)] - private static partial int XInitThreads(); - - [LibraryImport("user32.dll", SetLastError = true)] - public static partial int MessageBoxA(IntPtr hWnd, [MarshalAs(UnmanagedType.LPStr)] string text, [MarshalAs(UnmanagedType.LPStr)] string caption, uint type); - - private const uint MbIconWarning = 0x30; - - static Program() - { - if (OperatingSystem.IsLinux()) - { - NativeLibrary.SetDllImportResolver(typeof(Program).Assembly, (name, assembly, path) => - { - if (name != X11LibraryName) - { - return IntPtr.Zero; - } - - if (!NativeLibrary.TryLoad("libX11.so.6", assembly, path, out IntPtr result)) - { - if (!NativeLibrary.TryLoad("libX11.so", assembly, path, out result)) - { - return IntPtr.Zero; - } - } - - return result; - }); - } - } - - static void Main(string[] args) - { - Version = ReleaseInformation.Version; - - if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134)) - { - MessageBoxA(IntPtr.Zero, "You are running an outdated version of Windows.\n\nRyujinx supports Windows 10 version 1803 and newer.\n", $"Ryujinx {Version}", MbIconWarning); - } - - // Parse arguments - CommandLineState.ParseArguments(args); - - // Hook unhandled exception and process exit events. - GLib.ExceptionManager.UnhandledException += (GLib.UnhandledExceptionArgs e) => ProcessUnhandledException(e.ExceptionObject as Exception, e.IsTerminating); - AppDomain.CurrentDomain.UnhandledException += (object sender, UnhandledExceptionEventArgs e) => ProcessUnhandledException(e.ExceptionObject as Exception, e.IsTerminating); - AppDomain.CurrentDomain.ProcessExit += (object sender, EventArgs e) => Exit(); - - // Make process DPI aware for proper window sizing on high-res screens. - ForceDpiAware.Windows(); - WindowScaleFactor = ForceDpiAware.GetWindowScaleFactor(); - - // Delete backup files after updating. - Task.Run(Updater.CleanupUpdate); - - Console.Title = $"Ryujinx Console {Version}"; - - // NOTE: GTK3 doesn't init X11 in a multi threaded way. - // This ends up causing race condition and abort of XCB when a context is created by SPB (even if SPB do call XInitThreads). - if (OperatingSystem.IsLinux()) - { - if (XInitThreads() == 0) - { - throw new NotSupportedException("Failed to initialize multi-threading support."); - } - - OsUtils.SetEnvironmentVariableNoCaching("GDK_BACKEND", "x11"); - } - - if (OperatingSystem.IsMacOS()) - { - MVKInitialization.InitializeResolver(); - - string baseDirectory = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory); - string resourcesDataDir; - - if (Path.GetFileName(baseDirectory) == "MacOS") - { - resourcesDataDir = Path.Combine(Directory.GetParent(baseDirectory).FullName, "Resources"); - } - else - { - resourcesDataDir = baseDirectory; - } - - // On macOS, GTK3 needs XDG_DATA_DIRS to be set, otherwise it will try searching for "gschemas.compiled" in system directories. - OsUtils.SetEnvironmentVariableNoCaching("XDG_DATA_DIRS", Path.Combine(resourcesDataDir, "share")); - - // On macOS, GTK3 needs GDK_PIXBUF_MODULE_FILE to be set, otherwise it will try searching for "loaders.cache" in system directories. - OsUtils.SetEnvironmentVariableNoCaching("GDK_PIXBUF_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gdk-pixbuf-2.0", "2.10.0", "loaders.cache")); - - OsUtils.SetEnvironmentVariableNoCaching("GTK_IM_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gtk-3.0", "3.0.0", "immodules.cache")); - } - - string systemPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.Machine); - Environment.SetEnvironmentVariable("Path", $"{Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin")};{systemPath}"); - - // Setup base data directory. - AppDataManager.Initialize(CommandLineState.BaseDirPathArg); - - // Initialize the configuration. - ConfigurationState.Initialize(); - - // Initialize the logger system. - LoggerModule.Initialize(); - - // Initialize Discord integration. - DiscordIntegrationModule.Initialize(); - - // Initialize SDL2 driver - SDL2Driver.MainThreadDispatcher = action => - { - Application.Invoke(delegate - { - action(); - }); - }; - - string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ReleaseInformation.ConfigName); - string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, ReleaseInformation.ConfigName); - - // Now load the configuration as the other subsystems are now registered - ConfigurationPath = File.Exists(localConfigurationPath) - ? localConfigurationPath - : File.Exists(appDataConfigurationPath) - ? appDataConfigurationPath - : null; - - if (ConfigurationPath == null) - { - // No configuration, we load the default values and save it to disk - ConfigurationPath = appDataConfigurationPath; - Logger.Notice.Print(LogClass.Application, $"No configuration file found. Saving default configuration to: {ConfigurationPath}"); - - ConfigurationState.Instance.LoadDefault(); - ConfigurationState.Instance.ToFileFormat().SaveConfig(ConfigurationPath); - } - else - { - Logger.Notice.Print(LogClass.Application, $"Loading configuration from: {ConfigurationPath}"); - - if (ConfigurationFileFormat.TryLoad(ConfigurationPath, out ConfigurationFileFormat configurationFileFormat)) - { - ConfigurationState.Instance.Load(configurationFileFormat, ConfigurationPath); - } - else - { - Logger.Warning?.PrintMsg(LogClass.Application, $"Failed to load config! Loading the default config instead.\nFailed config location: {ConfigurationPath}"); - - ConfigurationState.Instance.LoadDefault(); - } - } - - // Check if graphics backend was overridden. - if (CommandLineState.OverrideGraphicsBackend != null) - { - if (CommandLineState.OverrideGraphicsBackend.ToLower() == "opengl") - { - ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.OpenGl; - } - else if (CommandLineState.OverrideGraphicsBackend.ToLower() == "vulkan") - { - ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.Vulkan; - } - } - - // Check if HideCursor was overridden. - if (CommandLineState.OverrideHideCursor is not null) - { - ConfigurationState.Instance.HideCursor.Value = CommandLineState.OverrideHideCursor!.ToLower() switch - { - "never" => HideCursorMode.Never, - "onidle" => HideCursorMode.OnIdle, - "always" => HideCursorMode.Always, - _ => ConfigurationState.Instance.HideCursor.Value, - }; - } - - // Check if docked mode was overridden. - if (CommandLineState.OverrideDockedMode.HasValue) - { - ConfigurationState.Instance.System.EnableDockedMode.Value = CommandLineState.OverrideDockedMode.Value; - } - - // Logging system information. - PrintSystemInfo(); - - // Enable OGL multithreading on the driver, and some other flags. - BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading; - DriverUtilities.InitDriverConfig(threadingMode == BackendThreading.Off); - - // Initialize Gtk. - Application.Init(); - - // Check if keys exists. - bool hasSystemProdKeys = File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys")); - bool hasCommonProdKeys = AppDataManager.Mode == AppDataManager.LaunchMode.UserProfile && File.Exists(Path.Combine(AppDataManager.KeysDirPathUser, "prod.keys")); - if (!hasSystemProdKeys && !hasCommonProdKeys) - { - UserErrorDialog.CreateUserErrorDialog(UserError.NoKeys); - } - - // Show the main window UI. - MainWindow mainWindow = new(); - mainWindow.Show(); - - // Load the game table if no application was requested by the command line - if (CommandLineState.LaunchPathArg == null) - { - mainWindow.UpdateGameTable(); - } - - if (OperatingSystem.IsLinux()) - { - int currentVmMaxMapCount = LinuxHelper.VmMaxMapCount; - - if (LinuxHelper.VmMaxMapCount < LinuxHelper.RecommendedVmMaxMapCount) - { - Logger.Warning?.Print(LogClass.Application, $"The value of vm.max_map_count is lower than {LinuxHelper.RecommendedVmMaxMapCount}. ({currentVmMaxMapCount})"); - - if (LinuxHelper.PkExecPath is not null) - { - var buttonTexts = new Dictionary() - { - { 0, "Yes, until the next restart" }, - { 1, "Yes, permanently" }, - { 2, "No" }, - }; - - ResponseType response = GtkDialog.CreateCustomDialog( - "Ryujinx - Low limit for memory mappings detected", - $"Would you like to increase the value of vm.max_map_count to {LinuxHelper.RecommendedVmMaxMapCount}?", - "Some games might try to create more memory mappings than currently allowed. " + - "Ryujinx will crash as soon as this limit gets exceeded.", - buttonTexts, - MessageType.Question); - - int rc; - - switch ((int)response) - { - case 0: - rc = LinuxHelper.RunPkExec($"echo {LinuxHelper.RecommendedVmMaxMapCount} > {LinuxHelper.VmMaxMapCountPath}"); - if (rc == 0) - { - Logger.Info?.Print(LogClass.Application, $"vm.max_map_count set to {LinuxHelper.VmMaxMapCount} until the next restart."); - } - else - { - Logger.Error?.Print(LogClass.Application, $"Unable to change vm.max_map_count. Process exited with code: {rc}"); - } - break; - case 1: - rc = LinuxHelper.RunPkExec($"echo \"vm.max_map_count = {LinuxHelper.RecommendedVmMaxMapCount}\" > {LinuxHelper.SysCtlConfigPath} && sysctl -p {LinuxHelper.SysCtlConfigPath}"); - if (rc == 0) - { - Logger.Info?.Print(LogClass.Application, $"vm.max_map_count set to {LinuxHelper.VmMaxMapCount}. Written to config: {LinuxHelper.SysCtlConfigPath}"); - } - else - { - Logger.Error?.Print(LogClass.Application, $"Unable to write new value for vm.max_map_count to config. Process exited with code: {rc}"); - } - break; - } - } - else - { - GtkDialog.CreateWarningDialog( - "Max amount of memory mappings is lower than recommended.", - $"The current value of vm.max_map_count ({currentVmMaxMapCount}) is lower than {LinuxHelper.RecommendedVmMaxMapCount}." + - "Some games might try to create more memory mappings than currently allowed. " + - "Ryujinx will crash as soon as this limit gets exceeded.\n\n" + - "You might want to either manually increase the limit or install pkexec, which allows Ryujinx to assist with that."); - } - } - } - - if (CommandLineState.LaunchPathArg != null) - { - if (mainWindow.ApplicationLibrary.TryGetApplicationsFromFile(CommandLineState.LaunchPathArg, out List applications)) - { - ApplicationData applicationData; - - if (CommandLineState.LaunchApplicationId != null) - { - applicationData = applications.Find(application => application.IdString == CommandLineState.LaunchApplicationId); - - if (applicationData != null) - { - mainWindow.RunApplication(applicationData, CommandLineState.StartFullscreenArg); - } - else - { - Logger.Error?.Print(LogClass.Application, $"Couldn't find requested application id '{CommandLineState.LaunchApplicationId}' in '{CommandLineState.LaunchPathArg}'."); - UserErrorDialog.CreateUserErrorDialog(UserError.ApplicationNotFound); - } - } - else - { - applicationData = applications[0]; - mainWindow.RunApplication(applicationData, CommandLineState.StartFullscreenArg); - } - } - else - { - Logger.Error?.Print(LogClass.Application, $"Couldn't find any application in '{CommandLineState.LaunchPathArg}'."); - UserErrorDialog.CreateUserErrorDialog(UserError.ApplicationNotFound); - } - } - - if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false)) - { - Updater.BeginParse(mainWindow, false).ContinueWith(task => - { - Logger.Error?.Print(LogClass.Application, $"Updater Error: {task.Exception}"); - }, TaskContinuationOptions.OnlyOnFaulted); - } - - Application.Run(); - } - - private static void PrintSystemInfo() - { - Logger.Notice.Print(LogClass.Application, $"Ryujinx Version: {Version}"); - SystemInfo.Gather().Print(); - - var enabledLogs = Logger.GetEnabledLevels(); - Logger.Notice.Print(LogClass.Application, $"Logs Enabled: {(enabledLogs.Count == 0 ? "" : string.Join(", ", enabledLogs))}"); - - if (AppDataManager.Mode == AppDataManager.LaunchMode.Custom) - { - Logger.Notice.Print(LogClass.Application, $"Launch Mode: Custom Path {AppDataManager.BaseDirPath}"); - } - else - { - Logger.Notice.Print(LogClass.Application, $"Launch Mode: {AppDataManager.Mode}"); - } - } - - private static void ProcessUnhandledException(Exception ex, bool isTerminating) - { - string message = $"Unhandled exception caught: {ex}"; - - Logger.Error?.PrintMsg(LogClass.Application, message); - - if (Logger.Error == null) - { - Logger.Notice.PrintMsg(LogClass.Application, message); - } - - if (isTerminating) - { - Exit(); - } - } - - public static void Exit() - { - DiscordIntegrationModule.Exit(); - - Logger.Shutdown(); - } - } -} diff --git a/src/Ryujinx.Gtk3/Ryujinx.Gtk3.csproj b/src/Ryujinx.Gtk3/Ryujinx.Gtk3.csproj deleted file mode 100644 index 722d6080b..000000000 --- a/src/Ryujinx.Gtk3/Ryujinx.Gtk3.csproj +++ /dev/null @@ -1,103 +0,0 @@ - - - - net8.0 - win-x64;osx-x64;linux-x64 - Exe - true - 1.0.0-dirty - $(DefineConstants);$(ExtraDefineConstants) - - true - true - - - - true - false - true - partial - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Always - alsoft.ini - - - Always - THIRDPARTY.md - - - Always - LICENSE.txt - - - - - - Always - - - Always - mime\Ryujinx.xml - - - - - - false - Ryujinx.ico - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Ryujinx.Gtk3/Ryujinx.ico b/src/Ryujinx.Gtk3/Ryujinx.ico deleted file mode 100644 index edf1b93f7..000000000 Binary files a/src/Ryujinx.Gtk3/Ryujinx.ico and /dev/null differ diff --git a/src/Ryujinx.Gtk3/UI/Applet/ErrorAppletDialog.cs b/src/Ryujinx.Gtk3/UI/Applet/ErrorAppletDialog.cs deleted file mode 100644 index cb8103cae..000000000 --- a/src/Ryujinx.Gtk3/UI/Applet/ErrorAppletDialog.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Gtk; -using Ryujinx.UI.Common.Configuration; -using System.Reflection; - -namespace Ryujinx.UI.Applet -{ - internal class ErrorAppletDialog : MessageDialog - { - public ErrorAppletDialog(Window parentWindow, DialogFlags dialogFlags, MessageType messageType, string[] buttons) : base(parentWindow, dialogFlags, messageType, ButtonsType.None, null) - { - Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Gtk3.UI.Common.Resources.Logo_Ryujinx.png"); - - int responseId = 0; - - if (buttons != null) - { - foreach (string buttonText in buttons) - { - AddButton(buttonText, responseId); - responseId++; - } - } - else - { - AddButton("OK", 0); - } - - ShowAll(); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Applet/GtkDynamicTextInputHandler.cs b/src/Ryujinx.Gtk3/UI/Applet/GtkDynamicTextInputHandler.cs deleted file mode 100644 index 0e560b789..000000000 --- a/src/Ryujinx.Gtk3/UI/Applet/GtkDynamicTextInputHandler.cs +++ /dev/null @@ -1,108 +0,0 @@ -using Gtk; -using Ryujinx.HLE.UI; -using Ryujinx.Input.GTK3; -using Ryujinx.UI.Widgets; -using System.Threading; - -namespace Ryujinx.UI.Applet -{ - /// - /// Class that forwards key events to a GTK Entry so they can be processed into text. - /// - internal class GtkDynamicTextInputHandler : IDynamicTextInputHandler - { - private readonly Window _parent; - private readonly OffscreenWindow _inputToTextWindow = new(); - private readonly RawInputToTextEntry _inputToTextEntry = new(); - - private bool _canProcessInput; - - public event DynamicTextChangedHandler TextChangedEvent; - public event KeyPressedHandler KeyPressedEvent; - public event KeyReleasedHandler KeyReleasedEvent; - - public bool TextProcessingEnabled - { - get - { - return Volatile.Read(ref _canProcessInput); - } - - set - { - Volatile.Write(ref _canProcessInput, value); - } - } - - public GtkDynamicTextInputHandler(Window parent) - { - _parent = parent; - _parent.KeyPressEvent += HandleKeyPressEvent; - _parent.KeyReleaseEvent += HandleKeyReleaseEvent; - - _inputToTextWindow.Add(_inputToTextEntry); - - _inputToTextEntry.TruncateMultiline = true; - - // Start with input processing turned off so the text box won't accumulate text - // if the user is playing on the keyboard. - _canProcessInput = false; - } - - [GLib.ConnectBefore()] - private void HandleKeyPressEvent(object o, KeyPressEventArgs args) - { - var key = (Ryujinx.Common.Configuration.Hid.Key)GTK3MappingHelper.ToInputKey(args.Event.Key); - - if (!(KeyPressedEvent?.Invoke(key)).GetValueOrDefault(true)) - { - return; - } - - if (_canProcessInput) - { - _inputToTextEntry.SendKeyPressEvent(o, args); - _inputToTextEntry.GetSelectionBounds(out int selectionStart, out int selectionEnd); - TextChangedEvent?.Invoke(_inputToTextEntry.Text, selectionStart, selectionEnd, _inputToTextEntry.OverwriteMode); - } - } - - [GLib.ConnectBefore()] - private void HandleKeyReleaseEvent(object o, KeyReleaseEventArgs args) - { - var key = (Ryujinx.Common.Configuration.Hid.Key)GTK3MappingHelper.ToInputKey(args.Event.Key); - - if (!(KeyReleasedEvent?.Invoke(key)).GetValueOrDefault(true)) - { - return; - } - - if (_canProcessInput) - { - // TODO (caian): This solution may have problems if the pause is sent after a key press - // and before a key release. But for now GTK Entry does not seem to use release events. - _inputToTextEntry.SendKeyReleaseEvent(o, args); - _inputToTextEntry.GetSelectionBounds(out int selectionStart, out int selectionEnd); - TextChangedEvent?.Invoke(_inputToTextEntry.Text, selectionStart, selectionEnd, _inputToTextEntry.OverwriteMode); - } - } - - public void SetText(string text, int cursorBegin) - { - _inputToTextEntry.Text = text; - _inputToTextEntry.Position = cursorBegin; - } - - public void SetText(string text, int cursorBegin, int cursorEnd) - { - _inputToTextEntry.Text = text; - _inputToTextEntry.SelectRegion(cursorBegin, cursorEnd); - } - - public void Dispose() - { - _parent.KeyPressEvent -= HandleKeyPressEvent; - _parent.KeyReleaseEvent -= HandleKeyReleaseEvent; - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Applet/GtkHostUIHandler.cs b/src/Ryujinx.Gtk3/UI/Applet/GtkHostUIHandler.cs deleted file mode 100644 index b3f509a09..000000000 --- a/src/Ryujinx.Gtk3/UI/Applet/GtkHostUIHandler.cs +++ /dev/null @@ -1,203 +0,0 @@ -using Gtk; -using Ryujinx.HLE.HOS.Applets; -using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy.Types; -using Ryujinx.HLE.UI; -using Ryujinx.UI.Widgets; -using System; -using System.Threading; - -namespace Ryujinx.UI.Applet -{ - internal class GtkHostUIHandler : IHostUIHandler - { - private readonly Window _parent; - - public IHostUITheme HostUITheme { get; } - - public GtkHostUIHandler(Window parent) - { - _parent = parent; - - HostUITheme = new GtkHostUITheme(parent); - } - - public bool DisplayMessageDialog(ControllerAppletUIArgs args) - { - string playerCount = args.PlayerCountMin == args.PlayerCountMax ? $"exactly {args.PlayerCountMin}" : $"{args.PlayerCountMin}-{args.PlayerCountMax}"; - - string message = $"Application requests {playerCount} player(s) with:\n\n" - + $"TYPES: {args.SupportedStyles}\n\n" - + $"PLAYERS: {string.Join(", ", args.SupportedPlayers)}\n\n" - + (args.IsDocked ? "Docked mode set. Handheld is also invalid.\n\n" : "") - + "Please reconfigure Input now and then press OK."; - - return DisplayMessageDialog("Controller Applet", message); - } - - public bool DisplayMessageDialog(string title, string message) - { - ManualResetEvent dialogCloseEvent = new(false); - - bool okPressed = false; - - Application.Invoke(delegate - { - MessageDialog msgDialog = null; - - try - { - msgDialog = new MessageDialog(_parent, DialogFlags.DestroyWithParent, MessageType.Info, ButtonsType.Ok, null) - { - Title = title, - Text = message, - UseMarkup = true, - }; - - msgDialog.SetDefaultSize(400, 0); - - msgDialog.Response += (object o, ResponseArgs args) => - { - if (args.ResponseId == ResponseType.Ok) - { - okPressed = true; - } - - dialogCloseEvent.Set(); - msgDialog?.Dispose(); - }; - - msgDialog.Show(); - } - catch (Exception ex) - { - GtkDialog.CreateErrorDialog($"Error displaying Message Dialog: {ex}"); - - dialogCloseEvent.Set(); - } - }); - - dialogCloseEvent.WaitOne(); - - return okPressed; - } - - public bool DisplayInputDialog(SoftwareKeyboardUIArgs args, out string userText) - { - ManualResetEvent dialogCloseEvent = new(false); - - bool okPressed = false; - bool error = false; - string inputText = args.InitialText ?? ""; - - Application.Invoke(delegate - { - try - { - var swkbdDialog = new SwkbdAppletDialog(_parent) - { - Title = "Software Keyboard", - Text = args.HeaderText, - SecondaryText = args.SubtitleText, - }; - - swkbdDialog.InputEntry.Text = inputText; - swkbdDialog.InputEntry.PlaceholderText = args.GuideText; - swkbdDialog.OkButton.Label = args.SubmitText; - - swkbdDialog.SetInputLengthValidation(args.StringLengthMin, args.StringLengthMax); - swkbdDialog.SetInputValidation(args.KeyboardMode); - - ((MainWindow)_parent).RendererWidget.NpadManager.BlockInputUpdates(); - - if (swkbdDialog.Run() == (int)ResponseType.Ok) - { - inputText = swkbdDialog.InputEntry.Text; - okPressed = true; - } - - swkbdDialog.Dispose(); - } - catch (Exception ex) - { - error = true; - - GtkDialog.CreateErrorDialog($"Error displaying Software Keyboard: {ex}"); - } - finally - { - dialogCloseEvent.Set(); - } - }); - - dialogCloseEvent.WaitOne(); - ((MainWindow)_parent).RendererWidget.NpadManager.UnblockInputUpdates(); - - userText = error ? null : inputText; - - return error || okPressed; - } - - public void ExecuteProgram(HLE.Switch device, ProgramSpecifyKind kind, ulong value) - { - device.Configuration.UserChannelPersistence.ExecuteProgram(kind, value); - ((MainWindow)_parent).RendererWidget?.Exit(); - } - - public bool DisplayErrorAppletDialog(string title, string message, string[] buttons) - { - ManualResetEvent dialogCloseEvent = new(false); - - bool showDetails = false; - - Application.Invoke(delegate - { - try - { - ErrorAppletDialog msgDialog = new(_parent, DialogFlags.DestroyWithParent, MessageType.Error, buttons) - { - Title = title, - Text = message, - UseMarkup = true, - WindowPosition = WindowPosition.CenterAlways, - }; - - msgDialog.SetDefaultSize(400, 0); - - msgDialog.Response += (object o, ResponseArgs args) => - { - if (buttons != null) - { - if (buttons.Length > 1) - { - if (args.ResponseId != (ResponseType)(buttons.Length - 1)) - { - showDetails = true; - } - } - } - - dialogCloseEvent.Set(); - msgDialog?.Dispose(); - }; - - msgDialog.Show(); - } - catch (Exception ex) - { - GtkDialog.CreateErrorDialog($"Error displaying ErrorApplet Dialog: {ex}"); - - dialogCloseEvent.Set(); - } - }); - - dialogCloseEvent.WaitOne(); - - return showDetails; - } - - public IDynamicTextInputHandler CreateDynamicTextInputHandler() - { - return new GtkDynamicTextInputHandler(_parent); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Applet/GtkHostUITheme.cs b/src/Ryujinx.Gtk3/UI/Applet/GtkHostUITheme.cs deleted file mode 100644 index 52d1123bb..000000000 --- a/src/Ryujinx.Gtk3/UI/Applet/GtkHostUITheme.cs +++ /dev/null @@ -1,90 +0,0 @@ -using Gtk; -using Ryujinx.HLE.UI; -using System.Diagnostics; - -namespace Ryujinx.UI.Applet -{ - internal class GtkHostUITheme : IHostUITheme - { - private const int RenderSurfaceWidth = 32; - private const int RenderSurfaceHeight = 32; - - public string FontFamily { get; private set; } - - public ThemeColor DefaultBackgroundColor { get; } - public ThemeColor DefaultForegroundColor { get; } - public ThemeColor DefaultBorderColor { get; } - public ThemeColor SelectionBackgroundColor { get; } - public ThemeColor SelectionForegroundColor { get; } - - public GtkHostUITheme(Window parent) - { - Entry entry = new(); - entry.SetStateFlags(StateFlags.Selected, true); - - // Get the font and some colors directly from GTK. - FontFamily = entry.PangoContext.FontDescription.Family; - - // Get foreground colors from the style context. - - var defaultForegroundColor = entry.StyleContext.GetColor(StateFlags.Normal); - var selectedForegroundColor = entry.StyleContext.GetColor(StateFlags.Selected); - - DefaultForegroundColor = new ThemeColor((float)defaultForegroundColor.Alpha, (float)defaultForegroundColor.Red, (float)defaultForegroundColor.Green, (float)defaultForegroundColor.Blue); - SelectionForegroundColor = new ThemeColor((float)selectedForegroundColor.Alpha, (float)selectedForegroundColor.Red, (float)selectedForegroundColor.Green, (float)selectedForegroundColor.Blue); - - ListBoxRow row = new(); - row.SetStateFlags(StateFlags.Selected, true); - - // Request the main thread to render some UI elements to an image to get an approximation for the color. - // NOTE (caian): This will only take the color of the top-left corner of the background, which may be incorrect - // if someone provides a custom style with a gradient or image. - - using (var surface = new Cairo.ImageSurface(Cairo.Format.Argb32, RenderSurfaceWidth, RenderSurfaceHeight)) - using (var context = new Cairo.Context(surface)) - { - context.SetSourceRGBA(1, 1, 1, 1); - context.Rectangle(0, 0, RenderSurfaceWidth, RenderSurfaceHeight); - context.Fill(); - - // The background color must be from the main Window because entry uses a different color. - parent.StyleContext.RenderBackground(context, 0, 0, RenderSurfaceWidth, RenderSurfaceHeight); - - DefaultBackgroundColor = ToThemeColor(surface.Data); - - context.SetSourceRGBA(1, 1, 1, 1); - context.Rectangle(0, 0, RenderSurfaceWidth, RenderSurfaceHeight); - context.Fill(); - - // Use the background color of the list box row when selected as the text box frame color because they are the - // same in the default theme. - row.StyleContext.RenderBackground(context, 0, 0, RenderSurfaceWidth, RenderSurfaceHeight); - - DefaultBorderColor = ToThemeColor(surface.Data); - } - - // Use the border color as the text selection color. - SelectionBackgroundColor = DefaultBorderColor; - } - - private static ThemeColor ToThemeColor(byte[] data) - { - Debug.Assert(data.Length == 4 * RenderSurfaceWidth * RenderSurfaceHeight); - - // Take the center-bottom pixel of the surface. - int position = 4 * (RenderSurfaceWidth * (RenderSurfaceHeight - 1) + RenderSurfaceWidth / 2); - - if (position + 4 > data.Length) - { - return new ThemeColor(1, 0, 0, 0); - } - - float a = data[position + 3] / 255.0f; - float r = data[position + 2] / 255.0f; - float g = data[position + 1] / 255.0f; - float b = data[position + 0] / 255.0f; - - return new ThemeColor(a, r, g, b); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Applet/SwkbdAppletDialog.cs b/src/Ryujinx.Gtk3/UI/Applet/SwkbdAppletDialog.cs deleted file mode 100644 index 8045da91e..000000000 --- a/src/Ryujinx.Gtk3/UI/Applet/SwkbdAppletDialog.cs +++ /dev/null @@ -1,127 +0,0 @@ -using Gtk; -using Ryujinx.HLE.HOS.Applets.SoftwareKeyboard; -using System; -using System.Linq; - -namespace Ryujinx.UI.Applet -{ - public class SwkbdAppletDialog : MessageDialog - { - private int _inputMin; - private int _inputMax; -#pragma warning disable IDE0052 // Remove unread private member - private KeyboardMode _mode; -#pragma warning restore IDE0052 - - private string _validationInfoText = ""; - - private Predicate _checkLength = _ => true; - private Predicate _checkInput = _ => true; - - private readonly Label _validationInfo; - - public Entry InputEntry { get; } - public Button OkButton { get; } - public Button CancelButton { get; } - - public SwkbdAppletDialog(Window parent) : base(parent, DialogFlags.Modal | DialogFlags.DestroyWithParent, MessageType.Question, ButtonsType.None, null) - { - SetDefaultSize(300, 0); - - _validationInfo = new Label() - { - Visible = false, - }; - - InputEntry = new Entry() - { - Visible = true, - }; - - InputEntry.Activated += OnInputActivated; - InputEntry.Changed += OnInputChanged; - - OkButton = (Button)AddButton("OK", ResponseType.Ok); - CancelButton = (Button)AddButton("Cancel", ResponseType.Cancel); - - ((Box)MessageArea).PackEnd(_validationInfo, true, true, 0); - ((Box)MessageArea).PackEnd(InputEntry, true, true, 4); - } - - private void ApplyValidationInfo() - { - _validationInfo.Visible = !string.IsNullOrEmpty(_validationInfoText); - _validationInfo.Markup = _validationInfoText; - } - - public void SetInputLengthValidation(int min, int max) - { - _inputMin = Math.Min(min, max); - _inputMax = Math.Max(min, max); - - _validationInfo.Visible = false; - - if (_inputMin <= 0 && _inputMax == int.MaxValue) // Disable. - { - _validationInfo.Visible = false; - - _checkLength = _ => true; - } - else if (_inputMin > 0 && _inputMax == int.MaxValue) - { - _validationInfoText = $"Must be at least {_inputMin} characters long. "; - - _checkLength = length => _inputMin <= length; - } - else - { - _validationInfoText = $"Must be {_inputMin}-{_inputMax} characters long. "; - - _checkLength = length => _inputMin <= length && length <= _inputMax; - } - - ApplyValidationInfo(); - OnInputChanged(this, EventArgs.Empty); - } - - public void SetInputValidation(KeyboardMode mode) - { - _mode = mode; - - switch (mode) - { - case KeyboardMode.Numeric: - _validationInfoText += "Must be 0-9 or '.' only."; - _checkInput = text => text.All(NumericCharacterValidation.IsNumeric); - break; - case KeyboardMode.Alphabet: - _validationInfoText += "Must be non CJK-characters only."; - _checkInput = text => text.All(value => !CJKCharacterValidation.IsCJK(value)); - break; - case KeyboardMode.ASCII: - _validationInfoText += "Must be ASCII text only."; - _checkInput = text => text.All(char.IsAscii); - break; - default: - _checkInput = _ => true; - break; - } - - ApplyValidationInfo(); - OnInputChanged(this, EventArgs.Empty); - } - - private void OnInputActivated(object sender, EventArgs e) - { - if (OkButton.IsSensitive) - { - Respond(ResponseType.Ok); - } - } - - private void OnInputChanged(object sender, EventArgs e) - { - OkButton.Sensitive = _checkLength(InputEntry.Text.Length) && _checkInput(InputEntry.Text); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Helper/ButtonHelper.cs b/src/Ryujinx.Gtk3/UI/Helper/ButtonHelper.cs deleted file mode 100644 index 5a8ca96a1..000000000 --- a/src/Ryujinx.Gtk3/UI/Helper/ButtonHelper.cs +++ /dev/null @@ -1,158 +0,0 @@ -using Ryujinx.Common.Configuration.Hid.Controller; -using Ryujinx.Input; -using System; -using System.Collections.Generic; -using Key = Ryujinx.Common.Configuration.Hid.Key; -using StickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId; - -namespace Ryujinx.UI.Helper -{ - public static class ButtonHelper - { - private static readonly Dictionary _keysMap = new() - { - { Key.Unknown, "Unknown" }, - { Key.ShiftLeft, "ShiftLeft" }, - { Key.ShiftRight, "ShiftRight" }, - { Key.ControlLeft, "CtrlLeft" }, - { Key.ControlRight, "CtrlRight" }, - { Key.AltLeft, OperatingSystem.IsMacOS() ? "OptLeft" : "AltLeft" }, - { Key.AltRight, OperatingSystem.IsMacOS() ? "OptRight" : "AltRight" }, - { Key.WinLeft, OperatingSystem.IsMacOS() ? "CmdLeft" : "WinLeft" }, - { Key.WinRight, OperatingSystem.IsMacOS() ? "CmdRight" : "WinRight" }, - { Key.Up, "Up" }, - { Key.Down, "Down" }, - { Key.Left, "Left" }, - { Key.Right, "Right" }, - { Key.Enter, "Enter" }, - { Key.Escape, "Escape" }, - { Key.Space, "Space" }, - { Key.Tab, "Tab" }, - { Key.BackSpace, "Backspace" }, - { Key.Insert, "Insert" }, - { Key.Delete, "Delete" }, - { Key.PageUp, "PageUp" }, - { Key.PageDown, "PageDown" }, - { Key.Home, "Home" }, - { Key.End, "End" }, - { Key.CapsLock, "CapsLock" }, - { Key.ScrollLock, "ScrollLock" }, - { Key.PrintScreen, "PrintScreen" }, - { Key.Pause, "Pause" }, - { Key.NumLock, "NumLock" }, - { Key.Clear, "Clear" }, - { Key.Keypad0, "Keypad0" }, - { Key.Keypad1, "Keypad1" }, - { Key.Keypad2, "Keypad2" }, - { Key.Keypad3, "Keypad3" }, - { Key.Keypad4, "Keypad4" }, - { Key.Keypad5, "Keypad5" }, - { Key.Keypad6, "Keypad6" }, - { Key.Keypad7, "Keypad7" }, - { Key.Keypad8, "Keypad8" }, - { Key.Keypad9, "Keypad9" }, - { Key.KeypadDivide, "KeypadDivide" }, - { Key.KeypadMultiply, "KeypadMultiply" }, - { Key.KeypadSubtract, "KeypadSubtract" }, - { Key.KeypadAdd, "KeypadAdd" }, - { Key.KeypadDecimal, "KeypadDecimal" }, - { Key.KeypadEnter, "KeypadEnter" }, - { Key.Number0, "0" }, - { Key.Number1, "1" }, - { Key.Number2, "2" }, - { Key.Number3, "3" }, - { Key.Number4, "4" }, - { Key.Number5, "5" }, - { Key.Number6, "6" }, - { Key.Number7, "7" }, - { Key.Number8, "8" }, - { Key.Number9, "9" }, - { Key.Tilde, "~" }, - { Key.Grave, "`" }, - { Key.Minus, "-" }, - { Key.Plus, "+" }, - { Key.BracketLeft, "[" }, - { Key.BracketRight, "]" }, - { Key.Semicolon, ";" }, - { Key.Quote, "'" }, - { Key.Comma, "," }, - { Key.Period, "." }, - { Key.Slash, "/" }, - { Key.BackSlash, "\\" }, - { Key.Unbound, "Unbound" }, - }; - - private static readonly Dictionary _gamepadInputIdMap = new() - { - { GamepadInputId.LeftStick, "LeftStick" }, - { GamepadInputId.RightStick, "RightStick" }, - { GamepadInputId.LeftShoulder, "LeftShoulder" }, - { GamepadInputId.RightShoulder, "RightShoulder" }, - { GamepadInputId.LeftTrigger, "LeftTrigger" }, - { GamepadInputId.RightTrigger, "RightTrigger" }, - { GamepadInputId.DpadUp, "DpadUp" }, - { GamepadInputId.DpadDown, "DpadDown" }, - { GamepadInputId.DpadLeft, "DpadLeft" }, - { GamepadInputId.DpadRight, "DpadRight" }, - { GamepadInputId.Minus, "Minus" }, - { GamepadInputId.Plus, "Plus" }, - { GamepadInputId.Guide, "Guide" }, - { GamepadInputId.Misc1, "Misc1" }, - { GamepadInputId.Paddle1, "Paddle1" }, - { GamepadInputId.Paddle2, "Paddle2" }, - { GamepadInputId.Paddle3, "Paddle3" }, - { GamepadInputId.Paddle4, "Paddle4" }, - { GamepadInputId.Touchpad, "Touchpad" }, - { GamepadInputId.SingleLeftTrigger0, "SingleLeftTrigger0" }, - { GamepadInputId.SingleRightTrigger0, "SingleRightTrigger0" }, - { GamepadInputId.SingleLeftTrigger1, "SingleLeftTrigger1" }, - { GamepadInputId.SingleRightTrigger1, "SingleRightTrigger1" }, - { GamepadInputId.Unbound, "Unbound" }, - }; - - private static readonly Dictionary _stickInputIdMap = new() - { - { StickInputId.Left, "StickLeft" }, - { StickInputId.Right, "StickRight" }, - { StickInputId.Unbound, "Unbound" }, - }; - - public static string ToString(Button button) - { - string keyString = ""; - - switch (button.Type) - { - case ButtonType.Key: - var key = button.AsHidType(); - - if (!_keysMap.TryGetValue(button.AsHidType(), out keyString)) - { - keyString = key.ToString(); - } - - break; - case ButtonType.GamepadButtonInputId: - var gamepadButton = button.AsHidType(); - - if (!_gamepadInputIdMap.TryGetValue(button.AsHidType(), out keyString)) - { - keyString = gamepadButton.ToString(); - } - - break; - case ButtonType.StickId: - var stickInput = button.AsHidType(); - - if (!_stickInputIdMap.TryGetValue(button.AsHidType(), out keyString)) - { - keyString = stickInput.ToString(); - } - - break; - } - - return keyString; - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Helper/MetalHelper.cs b/src/Ryujinx.Gtk3/UI/Helper/MetalHelper.cs deleted file mode 100644 index c2c32d3ae..000000000 --- a/src/Ryujinx.Gtk3/UI/Helper/MetalHelper.cs +++ /dev/null @@ -1,135 +0,0 @@ -using Gdk; -using System; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; - -namespace Ryujinx.UI.Helper -{ - public delegate void UpdateBoundsCallbackDelegate(Window window); - - [SupportedOSPlatform("macos")] - static partial class MetalHelper - { - private const string LibObjCImport = "/usr/lib/libobjc.A.dylib"; - - private readonly struct Selector - { - public readonly IntPtr NativePtr; - - public unsafe Selector(string value) - { - int size = System.Text.Encoding.UTF8.GetMaxByteCount(value.Length); - byte* data = stackalloc byte[size]; - - fixed (char* pValue = value) - { - System.Text.Encoding.UTF8.GetBytes(pValue, value.Length, data, size); - } - - NativePtr = sel_registerName(data); - } - - public static implicit operator Selector(string value) => new(value); - } - - private static unsafe IntPtr GetClass(string value) - { - int size = System.Text.Encoding.UTF8.GetMaxByteCount(value.Length); - byte* data = stackalloc byte[size]; - - fixed (char* pValue = value) - { - System.Text.Encoding.UTF8.GetBytes(pValue, value.Length, data, size); - } - - return objc_getClass(data); - } - - private struct NsPoint - { - public double X; - public double Y; - - public NsPoint(double x, double y) - { - X = x; - Y = y; - } - } - - private struct NsRect - { - public NsPoint Pos; - public NsPoint Size; - - public NsRect(double x, double y, double width, double height) - { - Pos = new NsPoint(x, y); - Size = new NsPoint(width, height); - } - } - - public static IntPtr GetMetalLayer(Display display, Window window, out IntPtr nsView, out UpdateBoundsCallbackDelegate updateBounds) - { - nsView = gdk_quartz_window_get_nsview(window.Handle); - - // Create a new CAMetalLayer. - IntPtr layerClass = GetClass("CAMetalLayer"); - IntPtr metalLayer = IntPtr_objc_msgSend(layerClass, "alloc"); - objc_msgSend(metalLayer, "init"); - - // Create a child NSView to render into. - IntPtr nsViewClass = GetClass("NSView"); - IntPtr child = IntPtr_objc_msgSend(nsViewClass, "alloc"); - objc_msgSend(child, "init", new NsRect()); - - // Add it as a child. - objc_msgSend(nsView, "addSubview:", child); - - // Make its renderer our metal layer. - objc_msgSend(child, "setWantsLayer:", (byte)1); - objc_msgSend(child, "setLayer:", metalLayer); - objc_msgSend(metalLayer, "setContentsScale:", (double)display.GetMonitorAtWindow(window).ScaleFactor); - - // Set the frame position/location. - updateBounds = (Window window) => - { - window.GetPosition(out int x, out int y); - int width = window.Width; - int height = window.Height; - objc_msgSend(child, "setFrame:", new NsRect(x, y, width, height)); - }; - - updateBounds(window); - - return metalLayer; - } - - [LibraryImport(LibObjCImport)] - private static unsafe partial IntPtr sel_registerName(byte* data); - - [LibraryImport(LibObjCImport)] - private static unsafe partial IntPtr objc_getClass(byte* data); - - [LibraryImport(LibObjCImport)] - private static partial void objc_msgSend(IntPtr receiver, Selector selector); - - [LibraryImport(LibObjCImport)] - private static partial void objc_msgSend(IntPtr receiver, Selector selector, byte value); - - [LibraryImport(LibObjCImport)] - private static partial void objc_msgSend(IntPtr receiver, Selector selector, IntPtr value); - - [LibraryImport(LibObjCImport)] - private static partial void objc_msgSend(IntPtr receiver, Selector selector, NsRect point); - - [LibraryImport(LibObjCImport)] - private static partial void objc_msgSend(IntPtr receiver, Selector selector, double value); - - [LibraryImport(LibObjCImport, EntryPoint = "objc_msgSend")] - private static partial IntPtr IntPtr_objc_msgSend(IntPtr receiver, Selector selector); - - [LibraryImport("libgdk-3.0.dylib")] - private static partial IntPtr gdk_quartz_window_get_nsview(IntPtr gdkWindow); - } -} diff --git a/src/Ryujinx.Gtk3/UI/Helper/SortHelper.cs b/src/Ryujinx.Gtk3/UI/Helper/SortHelper.cs deleted file mode 100644 index 3e3fbeaae..000000000 --- a/src/Ryujinx.Gtk3/UI/Helper/SortHelper.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Gtk; -using Ryujinx.UI.Common.Helper; -using System; - -namespace Ryujinx.UI.Helper -{ - static class SortHelper - { - public static int TimePlayedSort(ITreeModel model, TreeIter a, TreeIter b) - { - TimeSpan aTimeSpan = ValueFormatUtils.ParseTimeSpan(model.GetValue(a, 5).ToString()); - TimeSpan bTimeSpan = ValueFormatUtils.ParseTimeSpan(model.GetValue(b, 5).ToString()); - - return TimeSpan.Compare(aTimeSpan, bTimeSpan); - } - - public static int LastPlayedSort(ITreeModel model, TreeIter a, TreeIter b) - { - DateTime aDateTime = ValueFormatUtils.ParseDateTime(model.GetValue(a, 6).ToString()); - DateTime bDateTime = ValueFormatUtils.ParseDateTime(model.GetValue(b, 6).ToString()); - - return DateTime.Compare(aDateTime, bDateTime); - } - - public static int FileSizeSort(ITreeModel model, TreeIter a, TreeIter b) - { - long aSize = ValueFormatUtils.ParseFileSize(model.GetValue(a, 8).ToString()); - long bSize = ValueFormatUtils.ParseFileSize(model.GetValue(b, 8).ToString()); - - return aSize.CompareTo(bSize); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Helper/ThemeHelper.cs b/src/Ryujinx.Gtk3/UI/Helper/ThemeHelper.cs deleted file mode 100644 index e1fed1c4d..000000000 --- a/src/Ryujinx.Gtk3/UI/Helper/ThemeHelper.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Gtk; -using Ryujinx.Common; -using Ryujinx.Common.Logging; -using Ryujinx.UI.Common.Configuration; -using System.IO; - -namespace Ryujinx.UI.Helper -{ - static class ThemeHelper - { - public static void ApplyTheme() - { - if (!ConfigurationState.Instance.UI.EnableCustomTheme) - { - return; - } - - if (File.Exists(ConfigurationState.Instance.UI.CustomThemePath) && (Path.GetExtension(ConfigurationState.Instance.UI.CustomThemePath) == ".css")) - { - CssProvider cssProvider = new(); - - cssProvider.LoadFromPath(ConfigurationState.Instance.UI.CustomThemePath); - - StyleContext.AddProviderForScreen(Gdk.Screen.Default, cssProvider, 800); - } - else - { - Logger.Warning?.Print(LogClass.Application, $"The \"custom_theme_path\" section in \"{ReleaseInformation.ConfigName}\" contains an invalid path: \"{ConfigurationState.Instance.UI.CustomThemePath}\"."); - - ConfigurationState.Instance.UI.CustomThemePath.Value = ""; - ConfigurationState.Instance.UI.EnableCustomTheme.Value = false; - ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); - } - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/MainWindow.cs b/src/Ryujinx.Gtk3/UI/MainWindow.cs deleted file mode 100644 index b10dfe3f9..000000000 --- a/src/Ryujinx.Gtk3/UI/MainWindow.cs +++ /dev/null @@ -1,1993 +0,0 @@ -using Gtk; -using LibHac.Common; -using LibHac.Common.Keys; -using LibHac.Ncm; -using LibHac.Ns; -using LibHac.Tools.FsSystem; -using LibHac.Tools.FsSystem.NcaUtils; -using Ryujinx.Audio.Backends.Dummy; -using Ryujinx.Audio.Backends.OpenAL; -using Ryujinx.Audio.Backends.SDL2; -using Ryujinx.Audio.Backends.SoundIo; -using Ryujinx.Audio.Integration; -using Ryujinx.Common; -using Ryujinx.Common.Configuration; -using Ryujinx.Common.Configuration.Multiplayer; -using Ryujinx.Common.Logging; -using Ryujinx.Common.SystemInterop; -using Ryujinx.Cpu; -using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.GAL.Multithreading; -using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.HOS; -using Ryujinx.HLE.HOS.Services.Account.Acc; -using Ryujinx.HLE.HOS.SystemState; -using Ryujinx.Input.GTK3; -using Ryujinx.Input.HLE; -using Ryujinx.Input.SDL2; -using Ryujinx.Modules; -using Ryujinx.UI.App.Common; -using Ryujinx.UI.Applet; -using Ryujinx.UI.Common; -using Ryujinx.UI.Common.Configuration; -using Ryujinx.UI.Common.Helper; -using Ryujinx.UI.Helper; -using Ryujinx.UI.Widgets; -using Ryujinx.UI.Windows; -using Silk.NET.Vulkan; -using SPB.Graphics.Vulkan; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.Reflection; -using System.Threading; -using System.Threading.Tasks; -using GUI = Gtk.Builder.ObjectAttribute; -using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState; - -namespace Ryujinx.UI -{ - public class MainWindow : Window - { - private readonly VirtualFileSystem _virtualFileSystem; - private readonly ContentManager _contentManager; - private readonly AccountManager _accountManager; - private readonly LibHacHorizonManager _libHacHorizonManager; - - private UserChannelPersistence _userChannelPersistence; - - private HLE.Switch _emulationContext; - - private WindowsMultimediaTimerResolution _windowsMultimediaTimerResolution; - - private readonly GtkHostUIHandler _uiHandler; - private readonly AutoResetEvent _deviceExitStatus; - private readonly ListStore _tableStore; - - private bool _updatingGameTable; - private bool _gameLoaded; - private bool _ending; - - private ApplicationData _currentApplicationData = null; - - private string _lastScannedAmiiboId = ""; - private bool _lastScannedAmiiboShowAll = false; - - public readonly ApplicationLibrary ApplicationLibrary; - public RendererWidgetBase RendererWidget; - public InputManager InputManager; - - public bool IsFocused; - -#pragma warning disable CS0169, CS0649, IDE0044, IDE0051 // Field is never assigned to, Add readonly modifier, Remove unused private member - - [GUI] public MenuItem ExitMenuItem; - [GUI] public MenuItem UpdateMenuItem; - [GUI] MenuBar _menuBar; - [GUI] Box _footerBox; - [GUI] Box _statusBar; - [GUI] MenuItem _optionMenu; - [GUI] MenuItem _manageUserProfiles; - [GUI] MenuItem _fileMenu; - [GUI] MenuItem _loadApplicationFile; - [GUI] MenuItem _loadApplicationFolder; - [GUI] MenuItem _appletMenu; - [GUI] MenuItem _actionMenu; - [GUI] MenuItem _pauseEmulation; - [GUI] MenuItem _resumeEmulation; - [GUI] MenuItem _stopEmulation; - [GUI] MenuItem _simulateWakeUpMessage; - [GUI] MenuItem _scanAmiibo; - [GUI] MenuItem _takeScreenshot; - [GUI] MenuItem _hideUI; - [GUI] MenuItem _fullScreen; - [GUI] CheckMenuItem _startFullScreen; - [GUI] CheckMenuItem _showConsole; - [GUI] CheckMenuItem _favToggle; - [GUI] MenuItem _firmwareInstallDirectory; - [GUI] MenuItem _firmwareInstallFile; - [GUI] MenuItem _fileTypesSubMenu; - [GUI] Label _fifoStatus; - [GUI] CheckMenuItem _iconToggle; - [GUI] CheckMenuItem _developerToggle; - [GUI] CheckMenuItem _appToggle; - [GUI] CheckMenuItem _timePlayedToggle; - [GUI] CheckMenuItem _versionToggle; - [GUI] CheckMenuItem _lastPlayedToggle; - [GUI] CheckMenuItem _fileExtToggle; - [GUI] CheckMenuItem _pathToggle; - [GUI] CheckMenuItem _fileSizeToggle; - [GUI] CheckMenuItem _nspShown; - [GUI] CheckMenuItem _pfs0Shown; - [GUI] CheckMenuItem _xciShown; - [GUI] CheckMenuItem _ncaShown; - [GUI] CheckMenuItem _nroShown; - [GUI] CheckMenuItem _nsoShown; - [GUI] Label _gpuBackend; - [GUI] Label _dockedMode; - [GUI] Label _aspectRatio; - [GUI] Label _gameStatus; - [GUI] TreeView _gameTable; - [GUI] TreeSelection _gameTableSelection; - [GUI] ScrolledWindow _gameTableWindow; - [GUI] Label _gpuName; - [GUI] Label _progressLabel; - [GUI] Label _firmwareVersionLabel; - [GUI] Gtk.ProgressBar _progressBar; - [GUI] Box _viewBox; - [GUI] Label _vSyncStatus; - [GUI] Label _volumeStatus; - [GUI] Box _listStatusBox; - [GUI] Label _loadingStatusLabel; - [GUI] Gtk.ProgressBar _loadingStatusBar; - -#pragma warning restore CS0649, IDE0044, CS0169, IDE0051 - - public MainWindow() : this(new Builder("Ryujinx.Gtk3.UI.MainWindow.glade")) { } - - private MainWindow(Builder builder) : base(builder.GetRawOwnedObject("_mainWin")) - { - builder.Autoconnect(this); - - // Apply custom theme if needed. - ThemeHelper.ApplyTheme(); - - SetWindowSizePosition(); - - Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Logo_Ryujinx.png"); - Title = $"Ryujinx {Program.Version}"; - - // Hide emulation context status bar. - _statusBar.Hide(); - - // Instantiate HLE objects. - _virtualFileSystem = VirtualFileSystem.CreateInstance(); - _libHacHorizonManager = new LibHacHorizonManager(); - - _libHacHorizonManager.InitializeFsServer(_virtualFileSystem); - _libHacHorizonManager.InitializeArpServer(); - _libHacHorizonManager.InitializeBcatServer(); - _libHacHorizonManager.InitializeSystemClients(); - - // Save data created before we supported extra data in directory save data will not work properly if - // given empty extra data. Luckily some of that extra data can be created using the data from the - // save data indexer, which should be enough to check access permissions for user saves. - // Every single save data's extra data will be checked and fixed if needed each time the emulator is opened. - // Consider removing this at some point in the future when we don't need to worry about old saves. - VirtualFileSystem.FixExtraData(_libHacHorizonManager.RyujinxClient); - - _contentManager = new ContentManager(_virtualFileSystem); - _accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient, CommandLineState.Profile); - _userChannelPersistence = new UserChannelPersistence(); - - IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks - ? IntegrityCheckLevel.ErrorOnInvalid - : IntegrityCheckLevel.None; - - // Instantiate GUI objects. - ApplicationLibrary = new ApplicationLibrary(_virtualFileSystem, checkLevel) - { - DesiredLanguage = ConfigurationState.Instance.System.Language, - }; - _uiHandler = new GtkHostUIHandler(this); - _deviceExitStatus = new AutoResetEvent(false); - - WindowStateEvent += WindowStateEvent_Changed; - DeleteEvent += Window_Close; - FocusInEvent += MainWindow_FocusInEvent; - FocusOutEvent += MainWindow_FocusOutEvent; - - ApplicationLibrary.ApplicationAdded += Application_Added; - ApplicationLibrary.ApplicationCountUpdated += ApplicationCount_Updated; - - _fileMenu.StateChanged += FileMenu_StateChanged; - _actionMenu.StateChanged += ActionMenu_StateChanged; - _optionMenu.StateChanged += OptionMenu_StateChanged; - - _gameTable.ButtonReleaseEvent += Row_Clicked; - _fullScreen.Activated += FullScreen_Toggled; - - RendererWidgetBase.StatusUpdatedEvent += Update_StatusBar; - - ConfigurationState.Instance.System.IgnoreMissingServices.Event += UpdateIgnoreMissingServicesState; - ConfigurationState.Instance.Graphics.AspectRatio.Event += UpdateAspectRatioState; - ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState; - ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState; - - ConfigurationState.Instance.Multiplayer.Mode.Event += UpdateMultiplayerMode; - ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateMultiplayerLanInterfaceId; - - if (ConfigurationState.Instance.UI.StartFullscreen) - { - _startFullScreen.Active = true; - } - - _showConsole.Active = ConfigurationState.Instance.UI.ShowConsole.Value; - _showConsole.Visible = ConsoleHelper.SetConsoleWindowStateSupported; - - _actionMenu.Sensitive = false; - _pauseEmulation.Sensitive = false; - _resumeEmulation.Sensitive = false; - - _nspShown.Active = ConfigurationState.Instance.UI.ShownFileTypes.NSP.Value; - _pfs0Shown.Active = ConfigurationState.Instance.UI.ShownFileTypes.PFS0.Value; - _xciShown.Active = ConfigurationState.Instance.UI.ShownFileTypes.XCI.Value; - _ncaShown.Active = ConfigurationState.Instance.UI.ShownFileTypes.NCA.Value; - _nroShown.Active = ConfigurationState.Instance.UI.ShownFileTypes.NRO.Value; - _nsoShown.Active = ConfigurationState.Instance.UI.ShownFileTypes.NSO.Value; - - _nspShown.Toggled += NSP_Shown_Toggled; - _pfs0Shown.Toggled += PFS0_Shown_Toggled; - _xciShown.Toggled += XCI_Shown_Toggled; - _ncaShown.Toggled += NCA_Shown_Toggled; - _nroShown.Toggled += NRO_Shown_Toggled; - _nsoShown.Toggled += NSO_Shown_Toggled; - - _fileTypesSubMenu.Visible = FileAssociationHelper.IsTypeAssociationSupported; - - if (ConfigurationState.Instance.UI.GuiColumns.FavColumn) - { - _favToggle.Active = true; - } - if (ConfigurationState.Instance.UI.GuiColumns.IconColumn) - { - _iconToggle.Active = true; - } - if (ConfigurationState.Instance.UI.GuiColumns.AppColumn) - { - _appToggle.Active = true; - } - if (ConfigurationState.Instance.UI.GuiColumns.DevColumn) - { - _developerToggle.Active = true; - } - if (ConfigurationState.Instance.UI.GuiColumns.VersionColumn) - { - _versionToggle.Active = true; - } - if (ConfigurationState.Instance.UI.GuiColumns.TimePlayedColumn) - { - _timePlayedToggle.Active = true; - } - if (ConfigurationState.Instance.UI.GuiColumns.LastPlayedColumn) - { - _lastPlayedToggle.Active = true; - } - if (ConfigurationState.Instance.UI.GuiColumns.FileExtColumn) - { - _fileExtToggle.Active = true; - } - if (ConfigurationState.Instance.UI.GuiColumns.FileSizeColumn) - { - _fileSizeToggle.Active = true; - } - if (ConfigurationState.Instance.UI.GuiColumns.PathColumn) - { - _pathToggle.Active = true; - } - - _favToggle.Toggled += Fav_Toggled; - _iconToggle.Toggled += Icon_Toggled; - _appToggle.Toggled += App_Toggled; - _developerToggle.Toggled += Developer_Toggled; - _versionToggle.Toggled += Version_Toggled; - _timePlayedToggle.Toggled += TimePlayed_Toggled; - _lastPlayedToggle.Toggled += LastPlayed_Toggled; - _fileExtToggle.Toggled += FileExt_Toggled; - _fileSizeToggle.Toggled += FileSize_Toggled; - _pathToggle.Toggled += Path_Toggled; - - _gameTable.Model = _tableStore = new ListStore( - typeof(bool), - typeof(Gdk.Pixbuf), - typeof(string), - typeof(string), - typeof(string), - typeof(string), - typeof(string), - typeof(string), - typeof(string), - typeof(string), - typeof(BlitStruct)); - - _tableStore.SetSortFunc(5, SortHelper.TimePlayedSort); - _tableStore.SetSortFunc(6, SortHelper.LastPlayedSort); - _tableStore.SetSortFunc(8, SortHelper.FileSizeSort); - - int columnId = ConfigurationState.Instance.UI.ColumnSort.SortColumnId; - bool ascending = ConfigurationState.Instance.UI.ColumnSort.SortAscending; - - _tableStore.SetSortColumnId(columnId, ascending ? SortType.Ascending : SortType.Descending); - - _gameTable.EnableSearch = true; - _gameTable.SearchColumn = 2; - _gameTable.SearchEqualFunc = (model, col, key, iter) => !((string)model.GetValue(iter, col)).Contains(key, StringComparison.InvariantCultureIgnoreCase); - - _hideUI.Label = _hideUI.Label.Replace("SHOWUIKEY", ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUI.ToString()); - - UpdateColumns(); - - ConfigurationState.Instance.UI.GameDirs.Event += (sender, args) => - { - if (args.OldValue != args.NewValue) - { - UpdateGameTable(); - } - }; - - Task.Run(RefreshFirmwareLabel); - - InputManager = new InputManager(new GTK3KeyboardDriver(this), new SDL2GamepadDriver()); - } - - private void UpdateMultiplayerLanInterfaceId(object sender, ReactiveEventArgs args) - { - if (_emulationContext != null) - { - _emulationContext.Configuration.MultiplayerLanInterfaceId = args.NewValue; - } - } - - private void UpdateMultiplayerMode(object sender, ReactiveEventArgs args) - { - if (_emulationContext != null) - { - _emulationContext.Configuration.MultiplayerMode = args.NewValue; - } - } - - private void UpdateIgnoreMissingServicesState(object sender, ReactiveEventArgs args) - { - if (_emulationContext != null) - { - _emulationContext.Configuration.IgnoreMissingServices = args.NewValue; - } - } - - private void UpdateAspectRatioState(object sender, ReactiveEventArgs args) - { - if (_emulationContext != null) - { - _emulationContext.Configuration.AspectRatio = args.NewValue; - } - } - - private void UpdateDockedModeState(object sender, ReactiveEventArgs e) - { - _emulationContext?.System.ChangeDockedModeState(e.NewValue); - } - - private void UpdateAudioVolumeState(object sender, ReactiveEventArgs e) - { - _emulationContext?.SetVolume(e.NewValue); - } - - private void WindowStateEvent_Changed(object o, WindowStateEventArgs args) - { - _fullScreen.Label = args.Event.NewWindowState.HasFlag(Gdk.WindowState.Fullscreen) ? "Exit Fullscreen" : "Enter Fullscreen"; - } - - private void MainWindow_FocusOutEvent(object o, FocusOutEventArgs args) - { - IsFocused = false; - } - - private void MainWindow_FocusInEvent(object o, FocusInEventArgs args) - { - IsFocused = true; - } - - private void UpdateColumns() - { - foreach (TreeViewColumn column in _gameTable.Columns) - { - _gameTable.RemoveColumn(column); - } - - CellRendererToggle favToggle = new(); - favToggle.Toggled += FavToggle_Toggled; - - if (ConfigurationState.Instance.UI.GuiColumns.FavColumn) - { - _gameTable.AppendColumn("Fav", favToggle, "active", 0); - } - if (ConfigurationState.Instance.UI.GuiColumns.IconColumn) - { - _gameTable.AppendColumn("Icon", new CellRendererPixbuf(), "pixbuf", 1); - } - if (ConfigurationState.Instance.UI.GuiColumns.AppColumn) - { - _gameTable.AppendColumn("Application", new CellRendererText(), "text", 2); - } - if (ConfigurationState.Instance.UI.GuiColumns.DevColumn) - { - _gameTable.AppendColumn("Developer", new CellRendererText(), "text", 3); - } - if (ConfigurationState.Instance.UI.GuiColumns.VersionColumn) - { - _gameTable.AppendColumn("Version", new CellRendererText(), "text", 4); - } - if (ConfigurationState.Instance.UI.GuiColumns.TimePlayedColumn) - { - _gameTable.AppendColumn("Time Played", new CellRendererText(), "text", 5); - } - if (ConfigurationState.Instance.UI.GuiColumns.LastPlayedColumn) - { - _gameTable.AppendColumn("Last Played", new CellRendererText(), "text", 6); - } - if (ConfigurationState.Instance.UI.GuiColumns.FileExtColumn) - { - _gameTable.AppendColumn("File Ext", new CellRendererText(), "text", 7); - } - if (ConfigurationState.Instance.UI.GuiColumns.FileSizeColumn) - { - _gameTable.AppendColumn("File Size", new CellRendererText(), "text", 8); - } - if (ConfigurationState.Instance.UI.GuiColumns.PathColumn) - { - _gameTable.AppendColumn("Path", new CellRendererText(), "text", 9); - } - - foreach (TreeViewColumn column in _gameTable.Columns) - { - switch (column.Title) - { - case "Fav": - column.SortColumnId = 0; - column.Clicked += Column_Clicked; - break; - case "Application": - column.SortColumnId = 2; - column.Clicked += Column_Clicked; - break; - case "Developer": - column.SortColumnId = 3; - column.Clicked += Column_Clicked; - break; - case "Version": - column.SortColumnId = 4; - column.Clicked += Column_Clicked; - break; - case "Time Played": - column.SortColumnId = 5; - column.Clicked += Column_Clicked; - break; - case "Last Played": - column.SortColumnId = 6; - column.Clicked += Column_Clicked; - break; - case "File Ext": - column.SortColumnId = 7; - column.Clicked += Column_Clicked; - break; - case "File Size": - column.SortColumnId = 8; - column.Clicked += Column_Clicked; - break; - case "Path": - column.SortColumnId = 9; - column.Clicked += Column_Clicked; - break; - } - } - } - - protected override void OnDestroyed() - { - InputManager.Dispose(); - } - - private void InitializeSwitchInstance() - { - _virtualFileSystem.ReloadKeySet(); - - IRenderer renderer; - - if (ConfigurationState.Instance.Graphics.GraphicsBackend == GraphicsBackend.Vulkan) - { - string preferredGpu = ConfigurationState.Instance.Graphics.PreferredGpu.Value; - renderer = new Graphics.Vulkan.VulkanRenderer(Vk.GetApi(), CreateVulkanSurface, VulkanHelper.GetRequiredInstanceExtensions, preferredGpu); - } - else - { - renderer = new Graphics.OpenGL.OpenGLRenderer(); - } - - BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading; - - bool threadedGAL = threadingMode == BackendThreading.On || (threadingMode == BackendThreading.Auto && renderer.PreferThreading); - - if (threadedGAL) - { - renderer = new ThreadedRenderer(renderer); - } - - Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend Threading ({threadingMode}): {threadedGAL}"); - - IHardwareDeviceDriver deviceDriver = new DummyHardwareDeviceDriver(); - - if (ConfigurationState.Instance.System.AudioBackend.Value == AudioBackend.SDL2) - { - if (SDL2HardwareDeviceDriver.IsSupported) - { - deviceDriver = new SDL2HardwareDeviceDriver(); - } - else - { - Logger.Warning?.Print(LogClass.Audio, "SDL2 is not supported, trying to fall back to OpenAL."); - - if (OpenALHardwareDeviceDriver.IsSupported) - { - Logger.Warning?.Print(LogClass.Audio, "Found OpenAL, changing configuration."); - - ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.OpenAl; - SaveConfig(); - - deviceDriver = new OpenALHardwareDeviceDriver(); - } - else - { - Logger.Warning?.Print(LogClass.Audio, "OpenAL is not supported, trying to fall back to SoundIO."); - - if (SoundIoHardwareDeviceDriver.IsSupported) - { - Logger.Warning?.Print(LogClass.Audio, "Found SoundIO, changing configuration."); - - ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.SoundIo; - SaveConfig(); - - deviceDriver = new SoundIoHardwareDeviceDriver(); - } - else - { - Logger.Warning?.Print(LogClass.Audio, "SoundIO is not supported, falling back to dummy audio out."); - } - } - } - } - else if (ConfigurationState.Instance.System.AudioBackend.Value == AudioBackend.SoundIo) - { - if (SoundIoHardwareDeviceDriver.IsSupported) - { - deviceDriver = new SoundIoHardwareDeviceDriver(); - } - else - { - Logger.Warning?.Print(LogClass.Audio, "SoundIO is not supported, trying to fall back to SDL2."); - - if (SDL2HardwareDeviceDriver.IsSupported) - { - Logger.Warning?.Print(LogClass.Audio, "Found SDL2, changing configuration."); - - ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.SDL2; - SaveConfig(); - - deviceDriver = new SDL2HardwareDeviceDriver(); - } - else - { - Logger.Warning?.Print(LogClass.Audio, "SDL2 is not supported, trying to fall back to OpenAL."); - - if (OpenALHardwareDeviceDriver.IsSupported) - { - Logger.Warning?.Print(LogClass.Audio, "Found OpenAL, changing configuration."); - - ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.OpenAl; - SaveConfig(); - - deviceDriver = new OpenALHardwareDeviceDriver(); - } - else - { - Logger.Warning?.Print(LogClass.Audio, "OpenAL is not supported, falling back to dummy audio out."); - } - } - } - } - else if (ConfigurationState.Instance.System.AudioBackend.Value == AudioBackend.OpenAl) - { - if (OpenALHardwareDeviceDriver.IsSupported) - { - deviceDriver = new OpenALHardwareDeviceDriver(); - } - else - { - Logger.Warning?.Print(LogClass.Audio, "OpenAL is not supported, trying to fall back to SDL2."); - - if (SDL2HardwareDeviceDriver.IsSupported) - { - Logger.Warning?.Print(LogClass.Audio, "Found SDL2, changing configuration."); - - ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.SDL2; - SaveConfig(); - - deviceDriver = new SDL2HardwareDeviceDriver(); - } - else - { - Logger.Warning?.Print(LogClass.Audio, "SDL2 is not supported, trying to fall back to SoundIO."); - - if (SoundIoHardwareDeviceDriver.IsSupported) - { - Logger.Warning?.Print(LogClass.Audio, "Found SoundIO, changing configuration."); - - ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.SoundIo; - SaveConfig(); - - deviceDriver = new SoundIoHardwareDeviceDriver(); - } - else - { - Logger.Warning?.Print(LogClass.Audio, "SoundIO is not supported, falling back to dummy audio out."); - } - } - } - } - - var memoryConfiguration = ConfigurationState.Instance.System.ExpandRam.Value - ? HLE.MemoryConfiguration.MemoryConfiguration8GiB - : HLE.MemoryConfiguration.MemoryConfiguration4GiB; - - IntegrityCheckLevel fsIntegrityCheckLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None; - - HLE.HLEConfiguration configuration = new(_virtualFileSystem, - _libHacHorizonManager, - _contentManager, - _accountManager, - _userChannelPersistence, - renderer, - deviceDriver, - memoryConfiguration, - _uiHandler, - (SystemLanguage)ConfigurationState.Instance.System.Language.Value, - (RegionCode)ConfigurationState.Instance.System.Region.Value, - ConfigurationState.Instance.Graphics.EnableVsync, - ConfigurationState.Instance.System.EnableDockedMode, - ConfigurationState.Instance.System.EnablePtc, - ConfigurationState.Instance.System.EnableInternetAccess, - fsIntegrityCheckLevel, - ConfigurationState.Instance.System.FsGlobalAccessLogMode, - ConfigurationState.Instance.System.SystemTimeOffset, - ConfigurationState.Instance.System.TimeZone, - ConfigurationState.Instance.System.MemoryManagerMode, - ConfigurationState.Instance.System.IgnoreMissingServices, - ConfigurationState.Instance.Graphics.AspectRatio, - ConfigurationState.Instance.System.AudioVolume, - ConfigurationState.Instance.System.UseHypervisor, - ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value, - ConfigurationState.Instance.Multiplayer.Mode); - - _emulationContext = new HLE.Switch(configuration); - } - - private SurfaceKHR CreateVulkanSurface(Instance instance, Vk vk) - { - return new SurfaceKHR((ulong)((VulkanRenderer)RendererWidget).CreateWindowSurface(instance.Handle)); - } - - private void SetupProgressUIHandlers() - { - if (_emulationContext.Processes.ActiveApplication.DiskCacheLoadState != null) - { - _emulationContext.Processes.ActiveApplication.DiskCacheLoadState.StateChanged -= ProgressHandler; - _emulationContext.Processes.ActiveApplication.DiskCacheLoadState.StateChanged += ProgressHandler; - } - - _emulationContext.Gpu.ShaderCacheStateChanged -= ProgressHandler; - _emulationContext.Gpu.ShaderCacheStateChanged += ProgressHandler; - } - - private void ProgressHandler(T state, int current, int total) where T : Enum - { - bool visible; - string label; - - switch (state) - { - case LoadState ptcState: - visible = ptcState != LoadState.Loaded; - label = $"PTC : {current}/{total}"; - break; - case ShaderCacheLoadingState shaderCacheState: - visible = shaderCacheState != ShaderCacheLoadingState.Loaded; - label = $"Shaders : {current}/{total}"; - break; - default: - throw new ArgumentException($"Unknown Progress Handler type {typeof(T)}"); - } - - Application.Invoke(delegate - { - _loadingStatusLabel.Text = label; - _loadingStatusBar.Fraction = total > 0 ? (double)current / total : 0; - _loadingStatusBar.Visible = visible; - _loadingStatusLabel.Visible = visible; - }); - } - - public void UpdateGameTable() - { - if (_updatingGameTable || _gameLoaded) - { - return; - } - - _updatingGameTable = true; - - _tableStore.Clear(); - - Thread applicationLibraryThread = new(() => - { - ApplicationLibrary.DesiredLanguage = ConfigurationState.Instance.System.Language; - ApplicationLibrary.LoadApplications(ConfigurationState.Instance.UI.GameDirs); - - _updatingGameTable = false; - }) - { - Name = "GUI.ApplicationLibraryThread", - IsBackground = true, - }; - applicationLibraryThread.Start(); - } - - [Conditional("RELEASE")] - public void PerformanceCheck() - { - if (ConfigurationState.Instance.Logger.EnableTrace.Value) - { - MessageDialog debugWarningDialog = new(this, DialogFlags.Modal, MessageType.Warning, ButtonsType.YesNo, null) - { - Title = "Ryujinx - Warning", - Text = "You have trace logging enabled, which is designed to be used by developers only.", - SecondaryText = "For optimal performance, it's recommended to disable trace logging. Would you like to disable trace logging now?", - }; - - if (debugWarningDialog.Run() == (int)ResponseType.Yes) - { - ConfigurationState.Instance.Logger.EnableTrace.Value = false; - SaveConfig(); - } - - debugWarningDialog.Dispose(); - } - - if (!string.IsNullOrWhiteSpace(ConfigurationState.Instance.Graphics.ShadersDumpPath.Value)) - { - MessageDialog shadersDumpWarningDialog = new(this, DialogFlags.Modal, MessageType.Warning, ButtonsType.YesNo, null) - { - Title = "Ryujinx - Warning", - Text = "You have shader dumping enabled, which is designed to be used by developers only.", - SecondaryText = "For optimal performance, it's recommended to disable shader dumping. Would you like to disable shader dumping now?", - }; - - if (shadersDumpWarningDialog.Run() == (int)ResponseType.Yes) - { - ConfigurationState.Instance.Graphics.ShadersDumpPath.Value = ""; - SaveConfig(); - } - - shadersDumpWarningDialog.Dispose(); - } - } - - private bool LoadApplication(string path, ulong applicationId, bool isFirmwareTitle) - { - SystemVersion firmwareVersion = _contentManager.GetCurrentFirmwareVersion(); - - if (!SetupValidator.CanStartApplication(_contentManager, path, out UserError userError)) - { - if (SetupValidator.CanFixStartApplication(_contentManager, path, userError, out firmwareVersion)) - { - string message = $"Would you like to install the firmware embedded in this game? (Firmware {firmwareVersion.VersionString})"; - - ResponseType responseDialog = (ResponseType)GtkDialog.CreateConfirmationDialog("No Firmware Installed", message).Run(); - - if (responseDialog != ResponseType.Yes || !SetupValidator.TryFixStartApplication(_contentManager, path, userError, out _)) - { - UserErrorDialog.CreateUserErrorDialog(userError); - - return false; - } - - // Tell the user that we installed a firmware for them. - - firmwareVersion = _contentManager.GetCurrentFirmwareVersion(); - - RefreshFirmwareLabel(); - - message = $"No installed firmware was found but Ryujinx was able to install firmware {firmwareVersion.VersionString} from the provided game.\nThe emulator will now start."; - - GtkDialog.CreateInfoDialog($"Firmware {firmwareVersion.VersionString} was installed", message); - } - else - { - UserErrorDialog.CreateUserErrorDialog(userError); - - return false; - } - } - - Logger.Notice.Print(LogClass.Application, $"Using Firmware Version: {firmwareVersion?.VersionString}"); - - if (isFirmwareTitle) - { - Logger.Info?.Print(LogClass.Application, "Loading as Firmware Title (NCA)."); - - return _emulationContext.LoadNca(path); - } - - if (Directory.Exists(path)) - { - string[] romFsFiles = Directory.GetFiles(path, "*.istorage"); - - if (romFsFiles.Length == 0) - { - romFsFiles = Directory.GetFiles(path, "*.romfs"); - } - - if (romFsFiles.Length > 0) - { - Logger.Info?.Print(LogClass.Application, "Loading as cart with RomFS."); - - return _emulationContext.LoadCart(path, romFsFiles[0]); - } - - Logger.Info?.Print(LogClass.Application, "Loading as cart WITHOUT RomFS."); - - return _emulationContext.LoadCart(path); - } - - if (File.Exists(path)) - { - switch (System.IO.Path.GetExtension(path).ToLowerInvariant()) - { - case ".xci": - Logger.Info?.Print(LogClass.Application, "Loading as XCI."); - - return _emulationContext.LoadXci(path, applicationId); - case ".nca": - Logger.Info?.Print(LogClass.Application, "Loading as NCA."); - - return _emulationContext.LoadNca(path); - case ".nsp": - case ".pfs0": - Logger.Info?.Print(LogClass.Application, "Loading as NSP."); - - return _emulationContext.LoadNsp(path, applicationId); - default: - Logger.Info?.Print(LogClass.Application, "Loading as Homebrew."); - try - { - return _emulationContext.LoadProgram(path); - } - catch (ArgumentOutOfRangeException) - { - Logger.Error?.Print(LogClass.Application, "The specified file is not supported by Ryujinx."); - - return false; - } - } - } - - Logger.Warning?.Print(LogClass.Application, "Please specify a valid XCI/NCA/NSP/PFS0/NRO file."); - - return false; - } - - public void RunApplication(ApplicationData application, bool startFullscreen = false) - { - if (_gameLoaded) - { - GtkDialog.CreateInfoDialog("A game has already been loaded", "Please stop emulation or close the emulator before launching another game."); - } - else - { - PerformanceCheck(); - - Logger.RestartTime(); - - RendererWidget = CreateRendererWidget(); - - SwitchToRenderWidget(startFullscreen); - - InitializeSwitchInstance(); - - UpdateGraphicsConfig(); - - bool isFirmwareTitle = false; - - if (application.Path.StartsWith("@SystemContent")) - { - application.Path = VirtualFileSystem.SwitchPathToSystemPath(application.Path); - - isFirmwareTitle = true; - } - - if (!LoadApplication(application.Path, application.Id, isFirmwareTitle)) - { - _emulationContext.Dispose(); - SwitchToGameTable(); - - return; - } - - SetupProgressUIHandlers(); - - _currentApplicationData = application; - - _deviceExitStatus.Reset(); - - Thread windowThread = new(CreateGameWindow) - { - Name = "GUI.WindowThread", - }; - - windowThread.Start(); - - _gameLoaded = true; - _actionMenu.Sensitive = true; - UpdateMenuItem.Sensitive = false; - - _lastScannedAmiiboId = ""; - - _firmwareInstallFile.Sensitive = false; - _firmwareInstallDirectory.Sensitive = false; - - DiscordIntegrationModule.SwitchToPlayingState(_emulationContext.Processes.ActiveApplication.ProgramIdText, - _emulationContext.Processes.ActiveApplication.ApplicationControlProperties.Title[(int)_emulationContext.System.State.DesiredTitleLanguage].NameString.ToString()); - - ApplicationLibrary.LoadAndSaveMetaData(_emulationContext.Processes.ActiveApplication.ProgramIdText, appMetadata => - { - appMetadata.UpdatePreGame(); - }); - } - } - - private RendererWidgetBase CreateRendererWidget() - { - if (ConfigurationState.Instance.Graphics.GraphicsBackend == GraphicsBackend.Vulkan) - { - return new VulkanRenderer(InputManager, ConfigurationState.Instance.Logger.GraphicsDebugLevel); - } - else - { - return new OpenGLRenderer(InputManager, ConfigurationState.Instance.Logger.GraphicsDebugLevel); - } - } - - private void SwitchToRenderWidget(bool startFullscreen = false) - { - _viewBox.Remove(_gameTableWindow); - RendererWidget.Expand = true; - _viewBox.Child = RendererWidget; - - RendererWidget.ShowAll(); - EditFooterForGameRenderer(); - - if (Window.State.HasFlag(Gdk.WindowState.Fullscreen)) - { - ToggleExtraWidgets(false); - } - else if (startFullscreen || ConfigurationState.Instance.UI.StartFullscreen.Value) - { - FullScreen_Toggled(null, null); - } - } - - private void SwitchToGameTable() - { - if (Window.State.HasFlag(Gdk.WindowState.Fullscreen)) - { - ToggleExtraWidgets(true); - } - - RendererWidget.Exit(); - - if (RendererWidget.Window != Window && RendererWidget.Window != null) - { - RendererWidget.Window.Dispose(); - } - - RendererWidget.Dispose(); - - if (OperatingSystem.IsWindows()) - { - _windowsMultimediaTimerResolution?.Dispose(); - _windowsMultimediaTimerResolution = null; - } - - DisplaySleep.Restore(); - - _viewBox.Remove(RendererWidget); - _viewBox.Add(_gameTableWindow); - - _gameTableWindow.Expand = true; - - Window.Title = $"Ryujinx {Program.Version}"; - - _emulationContext = null; - _gameLoaded = false; - RendererWidget = null; - - DiscordIntegrationModule.SwitchToMainMenu(); - - RecreateFooterForMenu(); - - UpdateColumns(); - UpdateGameTable(); - - RefreshFirmwareLabel(); - HandleRelaunch(); - } - - private void CreateGameWindow() - { - if (OperatingSystem.IsWindows()) - { - _windowsMultimediaTimerResolution = new WindowsMultimediaTimerResolution(1); - } - - DisplaySleep.Prevent(); - - RendererWidget.Initialize(_emulationContext); - - RendererWidget.WaitEvent.WaitOne(); - - RendererWidget.Start(); - - _emulationContext.Dispose(); - _deviceExitStatus.Set(); - - // NOTE: Everything that is here will not be executed when you close the UI. - Application.Invoke(delegate - { - SwitchToGameTable(); - }); - } - - private void RecreateFooterForMenu() - { - _listStatusBox.Show(); - _statusBar.Hide(); - } - - private void EditFooterForGameRenderer() - { - _listStatusBox.Hide(); - _statusBar.Show(); - } - - public void ToggleExtraWidgets(bool show) - { - if (RendererWidget != null) - { - if (show) - { - _menuBar.ShowAll(); - _footerBox.Show(); - _statusBar.Show(); - } - else - { - _menuBar.Hide(); - _footerBox.Hide(); - } - } - } - - private void UpdateGameMetadata(string titleId) - { - if (_gameLoaded) - { - ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata => - { - appMetadata.UpdatePostGame(); - }); - } - } - - public static void UpdateGraphicsConfig() - { - int resScale = ConfigurationState.Instance.Graphics.ResScale; - float resScaleCustom = ConfigurationState.Instance.Graphics.ResScaleCustom; - - Graphics.Gpu.GraphicsConfig.ResScale = (resScale == -1) ? resScaleCustom : resScale; - Graphics.Gpu.GraphicsConfig.MaxAnisotropy = ConfigurationState.Instance.Graphics.MaxAnisotropy; - Graphics.Gpu.GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath; - Graphics.Gpu.GraphicsConfig.EnableShaderCache = ConfigurationState.Instance.Graphics.EnableShaderCache; - Graphics.Gpu.GraphicsConfig.EnableTextureRecompression = ConfigurationState.Instance.Graphics.EnableTextureRecompression; - Graphics.Gpu.GraphicsConfig.EnableMacroHLE = ConfigurationState.Instance.Graphics.EnableMacroHLE; - } - - public void UpdateInternetAccess() - { - if (_gameLoaded) - { - _emulationContext.Configuration.EnableInternetAccess = ConfigurationState.Instance.System.EnableInternetAccess.Value; - } - } - - public static void SaveConfig() - { - ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); - } - - private void End() - { - if (_ending) - { - return; - } - - _ending = true; - - if (_emulationContext != null) - { - UpdateGameMetadata(_emulationContext.Processes.ActiveApplication.ProgramIdText); - - if (RendererWidget != null) - { - // We tell the widget that we are exiting. - RendererWidget.Exit(); - - // Wait for the other thread to dispose the HLE context before exiting. - _deviceExitStatus.WaitOne(); - RendererWidget.Dispose(); - } - } - - Dispose(); - - Program.Exit(); - Application.Quit(); - } - - // - // Events - // - private void Application_Added(object sender, ApplicationAddedEventArgs args) - { - Application.Invoke(delegate - { - _tableStore.AppendValues( - args.AppData.Favorite, - new Gdk.Pixbuf(args.AppData.Icon, 75, 75), - $"{args.AppData.Name}\n{args.AppData.IdString.ToUpper()}", - args.AppData.Developer, - args.AppData.Version, - args.AppData.TimePlayedString, - args.AppData.LastPlayedString, - args.AppData.FileExtension, - args.AppData.FileSizeString, - args.AppData.Path, - args.AppData.ControlHolder); - }); - } - - private void ApplicationCount_Updated(object sender, ApplicationCountUpdatedEventArgs args) - { - Application.Invoke(delegate - { - _progressLabel.Text = $"{args.NumAppsLoaded}/{args.NumAppsFound} Games Loaded"; - float barValue = 0; - - if (args.NumAppsFound != 0) - { - barValue = (float)args.NumAppsLoaded / args.NumAppsFound; - } - - _progressBar.Fraction = barValue; - - // Reset the vertical scrollbar to the top when titles finish loading - if (args.NumAppsLoaded == args.NumAppsFound) - { - _gameTableWindow.Vadjustment.Value = 0; - } - }); - } - - private void Update_StatusBar(object sender, StatusUpdatedEventArgs args) - { - Application.Invoke(delegate - { - _gameStatus.Text = args.GameStatus; - _fifoStatus.Text = args.FifoStatus; - _gpuName.Text = args.GpuName; - _dockedMode.Text = args.DockedMode; - _aspectRatio.Text = args.AspectRatio; - _gpuBackend.Text = args.GpuBackend; - _volumeStatus.Text = GetVolumeLabelText(args.Volume); - - if (args.VSyncEnabled) - { - _vSyncStatus.Attributes = new Pango.AttrList(); - _vSyncStatus.Attributes.Insert(new Pango.AttrForeground(11822, 60138, 51657)); - } - else - { - _vSyncStatus.Attributes = new Pango.AttrList(); - _vSyncStatus.Attributes.Insert(new Pango.AttrForeground(ushort.MaxValue, 17733, 21588)); - } - }); - } - - private void FavToggle_Toggled(object sender, ToggledArgs args) - { - _tableStore.GetIter(out TreeIter treeIter, new TreePath(args.Path)); - - string titleId = _tableStore.GetValue(treeIter, 2).ToString().Split("\n")[1].ToLower(); - bool newToggleValue = !(bool)_tableStore.GetValue(treeIter, 0); - - _tableStore.SetValue(treeIter, 0, newToggleValue); - - ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata => - { - appMetadata.Favorite = newToggleValue; - }); - } - - private void Column_Clicked(object sender, EventArgs args) - { - TreeViewColumn column = (TreeViewColumn)sender; - - ConfigurationState.Instance.UI.ColumnSort.SortColumnId.Value = column.SortColumnId; - ConfigurationState.Instance.UI.ColumnSort.SortAscending.Value = column.SortOrder == SortType.Ascending; - - SaveConfig(); - } - - private void Row_Activated(object sender, RowActivatedArgs args) - { - _gameTableSelection.GetSelected(out TreeIter treeIter); - - ApplicationData application = new() - { - Favorite = (bool)_tableStore.GetValue(treeIter, 0), - Name = ((string)_tableStore.GetValue(treeIter, 2)).Split('\n')[0], - Id = ulong.Parse(((string)_tableStore.GetValue(treeIter, 2)).Split('\n')[1], NumberStyles.HexNumber), - Developer = (string)_tableStore.GetValue(treeIter, 3), - Version = (string)_tableStore.GetValue(treeIter, 4), - TimePlayed = ValueFormatUtils.ParseTimeSpan((string)_tableStore.GetValue(treeIter, 5)), - LastPlayed = ValueFormatUtils.ParseDateTime((string)_tableStore.GetValue(treeIter, 6)), - FileExtension = (string)_tableStore.GetValue(treeIter, 7), - FileSize = ValueFormatUtils.ParseFileSize((string)_tableStore.GetValue(treeIter, 8)), - Path = (string)_tableStore.GetValue(treeIter, 9), - ControlHolder = (BlitStruct)_tableStore.GetValue(treeIter, 10), - }; - - RunApplication(application); - } - - private void VSyncStatus_Clicked(object sender, ButtonReleaseEventArgs args) - { - _emulationContext.EnableDeviceVsync = !_emulationContext.EnableDeviceVsync; - - Logger.Info?.Print(LogClass.Application, $"VSync toggled to: {_emulationContext.EnableDeviceVsync}"); - } - - private void DockedMode_Clicked(object sender, ButtonReleaseEventArgs args) - { - ConfigurationState.Instance.System.EnableDockedMode.Value = !ConfigurationState.Instance.System.EnableDockedMode.Value; - } - - private static string GetVolumeLabelText(float volume) - { - string icon = volume == 0 ? "🔇" : "🔊"; - - return $"{icon} {(int)(volume * 100)}%"; - } - - private void VolumeStatus_Clicked(object sender, ButtonReleaseEventArgs args) - { - if (_emulationContext != null) - { - if (_emulationContext.IsAudioMuted()) - { - _emulationContext.SetVolume(ConfigurationState.Instance.System.AudioVolume); - } - else - { - _emulationContext.SetVolume(0); - } - } - } - - private void AspectRatio_Clicked(object sender, ButtonReleaseEventArgs args) - { - AspectRatio aspectRatio = ConfigurationState.Instance.Graphics.AspectRatio.Value; - - ConfigurationState.Instance.Graphics.AspectRatio.Value = ((int)aspectRatio + 1) > Enum.GetNames().Length - 1 ? AspectRatio.Fixed4x3 : aspectRatio + 1; - } - - private void Row_Clicked(object sender, ButtonReleaseEventArgs args) - { - if (args.Event.Button != 3 /* Right Click */) - { - return; - } - - _gameTableSelection.GetSelected(out TreeIter treeIter); - - if (treeIter.UserData == IntPtr.Zero) - { - return; - } - - ApplicationData application = new() - { - Favorite = (bool)_tableStore.GetValue(treeIter, 0), - Name = ((string)_tableStore.GetValue(treeIter, 2)).Split('\n')[0], - Id = ulong.Parse(((string)_tableStore.GetValue(treeIter, 2)).Split('\n')[1], NumberStyles.HexNumber), - Developer = (string)_tableStore.GetValue(treeIter, 3), - Version = (string)_tableStore.GetValue(treeIter, 4), - TimePlayed = ValueFormatUtils.ParseTimeSpan((string)_tableStore.GetValue(treeIter, 5)), - LastPlayed = ValueFormatUtils.ParseDateTime((string)_tableStore.GetValue(treeIter, 6)), - FileExtension = (string)_tableStore.GetValue(treeIter, 7), - FileSize = ValueFormatUtils.ParseFileSize((string)_tableStore.GetValue(treeIter, 8)), - Path = (string)_tableStore.GetValue(treeIter, 9), - ControlHolder = (BlitStruct)_tableStore.GetValue(treeIter, 10), - }; - - _ = new GameTableContextMenu(this, _virtualFileSystem, _accountManager, _libHacHorizonManager.RyujinxClient, application); - } - - private void Load_Application_File(object sender, EventArgs args) - { - using FileChooserNative fileChooser = new("Choose the file to open", this, FileChooserAction.Open, "Open", "Cancel"); - - FileFilter filter = new() - { - Name = "Switch Executables", - }; - filter.AddPattern("*.xci"); - filter.AddPattern("*.nsp"); - filter.AddPattern("*.pfs0"); - filter.AddPattern("*.nca"); - filter.AddPattern("*.nro"); - filter.AddPattern("*.nso"); - - fileChooser.AddFilter(filter); - - if (fileChooser.Run() == (int)ResponseType.Accept) - { - if (ApplicationLibrary.TryGetApplicationsFromFile(fileChooser.Filename, - out List applications)) - { - RunApplication(applications[0]); - } - else - { - GtkDialog.CreateErrorDialog("No applications found in selected file."); - } - } - } - - private void Load_Application_Folder(object sender, EventArgs args) - { - using FileChooserNative fileChooser = new("Choose the folder to open", this, FileChooserAction.SelectFolder, "Open", "Cancel"); - - if (fileChooser.Run() == (int)ResponseType.Accept) - { - ApplicationData applicationData = new() - { - Name = System.IO.Path.GetFileNameWithoutExtension(fileChooser.Filename), - Path = fileChooser.Filename, - }; - - RunApplication(applicationData); - } - } - - private void FileMenu_StateChanged(object o, StateChangedArgs args) - { - _appletMenu.Sensitive = _emulationContext == null && _contentManager.GetCurrentFirmwareVersion() != null && _contentManager.GetCurrentFirmwareVersion().Major > 3; - _loadApplicationFile.Sensitive = _emulationContext == null; - _loadApplicationFolder.Sensitive = _emulationContext == null; - } - - private void Load_Mii_Edit_Applet(object sender, EventArgs args) - { - string contentPath = _contentManager.GetInstalledContentPath(0x0100000000001009, StorageId.BuiltInSystem, NcaContentType.Program); - - ApplicationData applicationData = new() - { - Name = "miiEdit", - Id = 0x0100000000001009ul, - Path = contentPath, - }; - - RunApplication(applicationData); - } - - private void Open_Ryu_Folder(object sender, EventArgs args) - { - OpenHelper.OpenFolder(AppDataManager.BaseDirPath); - } - - private void OpenLogsFolder_Pressed(object sender, EventArgs args) - { - string logPath = AppDataManager.GetOrCreateLogsDir(); - if (!string.IsNullOrEmpty(logPath)) - { - OpenHelper.OpenFolder(logPath); - } - } - - private void Exit_Pressed(object sender, EventArgs args) - { - if (!_gameLoaded || !ConfigurationState.Instance.ShowConfirmExit || GtkDialog.CreateExitDialog()) - { - SaveWindowSizePosition(); - End(); - } - } - - private void Window_Close(object sender, DeleteEventArgs args) - { - if (!_gameLoaded || !ConfigurationState.Instance.ShowConfirmExit || GtkDialog.CreateExitDialog()) - { - SaveWindowSizePosition(); - End(); - } - else - { - args.RetVal = true; - } - } - - private void SetWindowSizePosition() - { - DefaultWidth = ConfigurationState.Instance.UI.WindowStartup.WindowSizeWidth; - DefaultHeight = ConfigurationState.Instance.UI.WindowStartup.WindowSizeHeight; - - Move(ConfigurationState.Instance.UI.WindowStartup.WindowPositionX, ConfigurationState.Instance.UI.WindowStartup.WindowPositionY); - - if (ConfigurationState.Instance.UI.WindowStartup.WindowMaximized) - { - Maximize(); - } - } - - private void SaveWindowSizePosition() - { - GetSize(out int windowWidth, out int windowHeight); - GetPosition(out int windowXPos, out int windowYPos); - - ConfigurationState.Instance.UI.WindowStartup.WindowMaximized.Value = IsMaximized; - ConfigurationState.Instance.UI.WindowStartup.WindowSizeWidth.Value = windowWidth; - ConfigurationState.Instance.UI.WindowStartup.WindowSizeHeight.Value = windowHeight; - ConfigurationState.Instance.UI.WindowStartup.WindowPositionX.Value = windowXPos; - ConfigurationState.Instance.UI.WindowStartup.WindowPositionY.Value = windowYPos; - - SaveConfig(); - } - - private void StopEmulation_Pressed(object sender, EventArgs args) - { - if (_emulationContext != null) - { - UpdateGameMetadata(_emulationContext.Processes.ActiveApplication.ProgramIdText); - } - - _pauseEmulation.Sensitive = false; - _resumeEmulation.Sensitive = false; - UpdateMenuItem.Sensitive = true; - RendererWidget?.Exit(); - } - - private void PauseEmulation_Pressed(object sender, EventArgs args) - { - _pauseEmulation.Sensitive = false; - _resumeEmulation.Sensitive = true; - _emulationContext.System.TogglePauseEmulation(true); - Title = TitleHelper.ActiveApplicationTitle(_emulationContext.Processes.ActiveApplication, Program.Version, "Paused"); - Logger.Info?.Print(LogClass.Emulation, "Emulation was paused"); - } - - private void ResumeEmulation_Pressed(object sender, EventArgs args) - { - _pauseEmulation.Sensitive = true; - _resumeEmulation.Sensitive = false; - _emulationContext.System.TogglePauseEmulation(false); - Title = TitleHelper.ActiveApplicationTitle(_emulationContext.Processes.ActiveApplication, Program.Version); - Logger.Info?.Print(LogClass.Emulation, "Emulation was resumed"); - } - - public void ActivatePauseMenu() - { - _pauseEmulation.Sensitive = true; - _resumeEmulation.Sensitive = false; - } - - public void TogglePause() - { - _pauseEmulation.Sensitive ^= true; - _resumeEmulation.Sensitive ^= true; - _emulationContext.System.TogglePauseEmulation(_resumeEmulation.Sensitive); - } - - private void Installer_File_Pressed(object o, EventArgs args) - { - FileChooserNative fileChooser = new("Choose the firmware file to open", this, FileChooserAction.Open, "Open", "Cancel"); - - FileFilter filter = new() - { - Name = "Switch Firmware Files", - }; - filter.AddPattern("*.zip"); - filter.AddPattern("*.xci"); - - fileChooser.AddFilter(filter); - - HandleInstallerDialog(fileChooser); - } - - private void Installer_Directory_Pressed(object o, EventArgs args) - { - FileChooserNative directoryChooser = new("Choose the firmware directory to open", this, FileChooserAction.SelectFolder, "Open", "Cancel"); - - HandleInstallerDialog(directoryChooser); - } - - private void HandleInstallerDialog(FileChooserNative fileChooser) - { - if (fileChooser.Run() == (int)ResponseType.Accept) - { - try - { - string filename = fileChooser.Filename; - - fileChooser.Dispose(); - - SystemVersion firmwareVersion = _contentManager.VerifyFirmwarePackage(filename); - - if (firmwareVersion is null) - { - GtkDialog.CreateErrorDialog($"A valid system firmware was not found in {filename}."); - - return; - } - - string dialogTitle = $"Install Firmware {firmwareVersion.VersionString}"; - - SystemVersion currentVersion = _contentManager.GetCurrentFirmwareVersion(); - - string dialogMessage = $"System version {firmwareVersion.VersionString} will be installed."; - - if (currentVersion != null) - { - dialogMessage += $"\n\nThis will replace the current system version {currentVersion.VersionString}. "; - } - - dialogMessage += "\n\nDo you want to continue?"; - - ResponseType responseInstallDialog = (ResponseType)GtkDialog.CreateConfirmationDialog(dialogTitle, dialogMessage).Run(); - - MessageDialog waitingDialog = GtkDialog.CreateWaitingDialog(dialogTitle, "Installing firmware..."); - - if (responseInstallDialog == ResponseType.Yes) - { - Logger.Info?.Print(LogClass.Application, $"Installing firmware {firmwareVersion.VersionString}"); - - Thread thread = new(() => - { - Application.Invoke(delegate - { - waitingDialog.Run(); - - }); - - try - { - _contentManager.InstallFirmware(filename); - - Application.Invoke(delegate - { - waitingDialog.Dispose(); - - string message = $"System version {firmwareVersion.VersionString} successfully installed."; - - GtkDialog.CreateInfoDialog(dialogTitle, message); - Logger.Info?.Print(LogClass.Application, message); - - // Purge Applet Cache. - - DirectoryInfo miiEditorCacheFolder = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, "0100000000001009", "cache")); - - if (miiEditorCacheFolder.Exists) - { - miiEditorCacheFolder.Delete(true); - } - }); - } - catch (Exception ex) - { - Application.Invoke(delegate - { - waitingDialog.Dispose(); - - GtkDialog.CreateErrorDialog(ex.Message); - }); - } - finally - { - RefreshFirmwareLabel(); - } - }) - { - Name = "GUI.FirmwareInstallerThread", - }; - thread.Start(); - } - } - catch (MissingKeyException ex) - { - Logger.Error?.Print(LogClass.Application, ex.ToString()); - UserErrorDialog.CreateUserErrorDialog(UserError.FirmwareParsingFailed); - } - catch (Exception ex) - { - GtkDialog.CreateErrorDialog(ex.Message); - } - } - else - { - fileChooser.Dispose(); - } - } - - private void RefreshFirmwareLabel() - { - SystemVersion currentFirmware = _contentManager.GetCurrentFirmwareVersion(); - - Application.Invoke(delegate - { - _firmwareVersionLabel.Text = currentFirmware != null ? currentFirmware.VersionString : "0.0.0"; - }); - } - - private void InstallFileTypes_Pressed(object sender, EventArgs e) - { - if (FileAssociationHelper.Install()) - { - GtkDialog.CreateInfoDialog("Install file types", "File types successfully installed!"); - } - else - { - GtkDialog.CreateErrorDialog("Failed to install file types."); - } - } - - private void UninstallFileTypes_Pressed(object sender, EventArgs e) - { - if (FileAssociationHelper.Uninstall()) - { - GtkDialog.CreateInfoDialog("Uninstall file types", "File types successfully uninstalled!"); - } - else - { - GtkDialog.CreateErrorDialog("Failed to uninstall file types."); - } - } - - private void HandleRelaunch() - { - if (_userChannelPersistence.PreviousIndex != -1 && _userChannelPersistence.ShouldRestart) - { - _userChannelPersistence.ShouldRestart = false; - - RunApplication(_currentApplicationData); - } - else - { - // otherwise, clear state. - _userChannelPersistence = new UserChannelPersistence(); - _currentApplicationData = null; - _actionMenu.Sensitive = false; - _firmwareInstallFile.Sensitive = true; - _firmwareInstallDirectory.Sensitive = true; - } - } - - private void FullScreen_Toggled(object sender, EventArgs args) - { - if (!Window.State.HasFlag(Gdk.WindowState.Fullscreen)) - { - Fullscreen(); - - ToggleExtraWidgets(false); - } - else - { - Unfullscreen(); - - ToggleExtraWidgets(true); - } - } - - private void StartFullScreen_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.StartFullscreen.Value = _startFullScreen.Active; - - SaveConfig(); - } - - private void ShowConsole_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.ShowConsole.Value = _showConsole.Active; - - SaveConfig(); - } - - private void OptionMenu_StateChanged(object o, StateChangedArgs args) - { - _manageUserProfiles.Sensitive = _emulationContext == null; - } - - private void Settings_Pressed(object sender, EventArgs args) - { - SettingsWindow settingsWindow = new(this, _virtualFileSystem, _contentManager); - - settingsWindow.SetSizeRequest((int)(settingsWindow.DefaultWidth * Program.WindowScaleFactor), (int)(settingsWindow.DefaultHeight * Program.WindowScaleFactor)); - settingsWindow.Show(); - } - - private void HideUI_Pressed(object sender, EventArgs args) - { - ToggleExtraWidgets(false); - } - - private void ManageCheats_Pressed(object sender, EventArgs args) - { - var window = new CheatWindow( - _virtualFileSystem, - _emulationContext.Processes.ActiveApplication.ProgramId, - _emulationContext.Processes.ActiveApplication.ApplicationControlProperties - .Title[(int)_emulationContext.System.State.DesiredTitleLanguage].NameString.ToString(), - _currentApplicationData.Path); - - window.Destroyed += CheatWindow_Destroyed; - window.Show(); - } - - private void CheatWindow_Destroyed(object sender, EventArgs e) - { - _emulationContext.EnableCheats(); - (sender as CheatWindow).Destroyed -= CheatWindow_Destroyed; - } - - private void ManageUserProfiles_Pressed(object sender, EventArgs args) - { - UserProfilesManagerWindow userProfilesManagerWindow = new(_accountManager, _contentManager, _virtualFileSystem); - - userProfilesManagerWindow.SetSizeRequest((int)(userProfilesManagerWindow.DefaultWidth * Program.WindowScaleFactor), (int)(userProfilesManagerWindow.DefaultHeight * Program.WindowScaleFactor)); - userProfilesManagerWindow.Show(); - } - - private void Simulate_WakeUp_Message_Pressed(object sender, EventArgs args) - { - _emulationContext?.System.SimulateWakeUpMessage(); - } - - private void ActionMenu_StateChanged(object o, StateChangedArgs args) - { - _scanAmiibo.Sensitive = _emulationContext != null && _emulationContext.System.SearchingForAmiibo(out int _); - _takeScreenshot.Sensitive = _emulationContext != null; - } - - private void Scan_Amiibo(object sender, EventArgs args) - { - if (_emulationContext.System.SearchingForAmiibo(out int deviceId)) - { - AmiiboWindow amiiboWindow = new() - { - LastScannedAmiiboShowAll = _lastScannedAmiiboShowAll, - LastScannedAmiiboId = _lastScannedAmiiboId, - DeviceId = deviceId, - TitleId = _emulationContext.Processes.ActiveApplication.ProgramIdText.ToUpper(), - }; - - amiiboWindow.DeleteEvent += AmiiboWindow_DeleteEvent; - - amiiboWindow.Show(); - } - else - { - GtkDialog.CreateInfoDialog($"Amiibo", "The game is currently not ready to receive Amiibo scan data. Ensure that you have an Amiibo-compatible game open and ready to receive Amiibo scan data."); - } - } - - private void Take_Screenshot(object sender, EventArgs args) - { - if (_emulationContext != null && RendererWidget != null) - { - RendererWidget.ScreenshotRequested = true; - } - } - - private void AmiiboWindow_DeleteEvent(object sender, DeleteEventArgs args) - { - if (((AmiiboWindow)sender).AmiiboId != "" && ((AmiiboWindow)sender).Response == ResponseType.Ok) - { - _lastScannedAmiiboId = ((AmiiboWindow)sender).AmiiboId; - _lastScannedAmiiboShowAll = ((AmiiboWindow)sender).LastScannedAmiiboShowAll; - - _emulationContext.System.ScanAmiibo(((AmiiboWindow)sender).DeviceId, ((AmiiboWindow)sender).AmiiboId, ((AmiiboWindow)sender).UseRandomUuid); - } - } - - private void Update_Pressed(object sender, EventArgs args) - { - if (Updater.CanUpdate(true)) - { - Updater.BeginParse(this, true).ContinueWith(task => - { - Logger.Error?.Print(LogClass.Application, $"Updater error: {task.Exception}"); - }, TaskContinuationOptions.OnlyOnFaulted); - } - } - - private void About_Pressed(object sender, EventArgs args) - { - AboutWindow aboutWindow = new(); - - aboutWindow.SetSizeRequest((int)(aboutWindow.DefaultWidth * Program.WindowScaleFactor), (int)(aboutWindow.DefaultHeight * Program.WindowScaleFactor)); - aboutWindow.Show(); - } - - private void Fav_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.GuiColumns.FavColumn.Value = _favToggle.Active; - - SaveConfig(); - UpdateColumns(); - } - - private void Icon_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.GuiColumns.IconColumn.Value = _iconToggle.Active; - - SaveConfig(); - UpdateColumns(); - } - - private void App_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.GuiColumns.AppColumn.Value = _appToggle.Active; - - SaveConfig(); - UpdateColumns(); - } - - private void Developer_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.GuiColumns.DevColumn.Value = _developerToggle.Active; - - SaveConfig(); - UpdateColumns(); - } - - private void Version_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.GuiColumns.VersionColumn.Value = _versionToggle.Active; - - SaveConfig(); - UpdateColumns(); - } - - private void TimePlayed_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.GuiColumns.TimePlayedColumn.Value = _timePlayedToggle.Active; - - SaveConfig(); - UpdateColumns(); - } - - private void LastPlayed_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.GuiColumns.LastPlayedColumn.Value = _lastPlayedToggle.Active; - - SaveConfig(); - UpdateColumns(); - } - - private void FileExt_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.GuiColumns.FileExtColumn.Value = _fileExtToggle.Active; - - SaveConfig(); - UpdateColumns(); - } - - private void FileSize_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.GuiColumns.FileSizeColumn.Value = _fileSizeToggle.Active; - - SaveConfig(); - UpdateColumns(); - } - - private void Path_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.GuiColumns.PathColumn.Value = _pathToggle.Active; - - SaveConfig(); - UpdateColumns(); - } - - private void NSP_Shown_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.ShownFileTypes.NSP.Value = _nspShown.Active; - - SaveConfig(); - UpdateGameTable(); - } - - private void PFS0_Shown_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.ShownFileTypes.PFS0.Value = _pfs0Shown.Active; - - SaveConfig(); - UpdateGameTable(); - } - - private void XCI_Shown_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.ShownFileTypes.XCI.Value = _xciShown.Active; - - SaveConfig(); - UpdateGameTable(); - } - - private void NCA_Shown_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.ShownFileTypes.NCA.Value = _ncaShown.Active; - - SaveConfig(); - UpdateGameTable(); - } - - private void NRO_Shown_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.ShownFileTypes.NRO.Value = _nroShown.Active; - - SaveConfig(); - UpdateGameTable(); - } - - private void NSO_Shown_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.ShownFileTypes.NSO.Value = _nsoShown.Active; - - SaveConfig(); - UpdateGameTable(); - } - - private void RefreshList_Pressed(object sender, ButtonReleaseEventArgs args) - { - UpdateGameTable(); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/MainWindow.glade b/src/Ryujinx.Gtk3/UI/MainWindow.glade deleted file mode 100644 index d1b6872a9..000000000 --- a/src/Ryujinx.Gtk3/UI/MainWindow.glade +++ /dev/null @@ -1,1006 +0,0 @@ - - - - - - False - Ryujinx - center - - - True - False - vertical - - - True - False - - - True - False - File - True - - - True - False - - - True - False - Open a file explorer to choose a Switch compatible file to load - Load Application from File - True - - - - - - True - False - Open a file explorer to choose a Switch compatible, unpacked application to load - Load Unpacked Game - True - - - - - - True - False - Load Applet - True - - - True - False - - - True - False - Open Mii Editor Applet in Standalone mode - Mii Editor - True - - - - - - - - - - True - False - - - - - True - False - Open Ryujinx filesystem folder - Open Ryujinx Folder - True - - - - - - True - False - Opens the folder where logs are written to. - Open Logs Folder - True - - - - - - True - False - - - - - True - False - Exit Ryujinx - Exit - True - - - - - - - - - - True - False - Options - True - - - True - False - - - True - False - Enter Fullscreen - True - - - - - True - False - Start Games in Fullscreen Mode - True - - - - - - True - False - Show Log Console - True - - - - - - True - False - - - - - True - False - Select which GUI columns to enable - Enable GUI Columns - True - - - True - False - - - True - False - Enable or Disable Favorite Games Column in the game list - Enable Favorite Games Column - True - - - - - True - False - Enable or Disable Icon Column in the game list - Enable Icon Column - True - - - - - True - False - Enable or Disable Title Name/ID Column in the game list - Enable Title Name/ID Column - True - - - - - True - False - Enable or Disable Developer Column in the game list - Enable Developer Column - True - - - - - True - False - Enable or Disable Version Column in the game list - Enable Version Column - True - - - - - True - False - Enable or Disable Time Played Column in the game list - Enable Time Played Column - True - - - - - True - False - Enable or Disable Last Played Column in the game list - Enable Last Played Column - True - - - - - True - False - Enable or Disable file extension column in the game list - Enable File Ext Column - True - - - - - True - False - Enable or Disable File Size Column in the game list - Enable File Size Column - True - - - - - True - False - Enable or Disable Path Column in the game list - Enable Path Column - True - - - - - - - - - True - False - Select which file types to show - Show File Types - True - - - True - False - - - True - False - Shows .NSP files in the games list - .NSP - True - - - - - True - False - Shows .PFS0 files in the games list - .PFS0 - True - - - - - True - False - Shows .XCI files in the games list - .XCI - True - - - - - True - False - Shows .NCA files in the games list - .NCA - True - - - - - True - False - Shows .NRO files in the games list - .NRO - True - - - - - True - False - Shows .NSO files in the games list - .NSO - True - - - - - - - - - True - False - - - - - True - False - Open settings window - Settings - True - - - - - - True - False - Open User Profiles Manager window - Manage User Profiles - True - - - - - - - - - - True - False - Actions - True - - - True - False - - - True - False - Pause emulation - Pause Emulation - True - - - - - - True - False - Resume emulation - Resume Emulation - True - - - - - - True - False - Stop emulation of the current game and return to game selection - Stop Emulation - True - - - - - - True - False - - - - - True - False - Simulate a Wake-up Message - Simulate Wake-up Message - True - - - - - - True - False - Scan an Amiibo - Scan an Amiibo - True - - - - - - True - False - Take a screenshot - Take Screenshot - - - - - - True - False - Hide UI (SHOWUIKEY to show) - True - - - - - - True - False - Manage Cheats - - - - - - - - - - True - False - Tools - True - - - True - False - - - True - False - Install Firmware - True - - - True - False - - - True - False - Install a firmware from XCI or ZIP - True - - - - - - True - False - Install a firmware from a directory - True - - - - - - - - - - True - False - Manage file types - True - - - True - False - - - True - False - Install file types - - - - - - True - False - Uninstall file types - - - - - - - - - - - - - - True - False - Help - True - - - True - False - - - True - False - Check for updates to Ryujinx - Check for Updates - True - - - - - - True - False - - - - - True - False - Open about window - About - True - - - - - - - - - - False - True - 0 - - - - - True - False - vertical - - - True - False - vertical - - - True - True - in - - - True - True - True - True - - - - - - - - - True - True - 0 - - - - - True - True - 0 - - - - - 19 - True - False - - - True - False - - - True - False - 5 - - - - RefreshList - True - False - gtk-refresh - - - - - False - False - 0 - - - - - True - False - 10 - 5 - 2 - 2 - 0/0 Games Loaded - - - False - True - 1 - - - - - 200 - True - False - start - 10 - 5 - 6 - - - True - True - 2 - - - - - True - True - 0 - - - - - True - False - - - True - False - - - - True - False - start - 5 - 5 - VSync - - - - - False - True - 0 - - - - - True - False - - - False - True - 1 - - - - - True - False - - - - True - False - start - 5 - 5 - - - - - False - True - 2 - - - - - True - False - - - False - True - 3 - - - - - True - False - - - - True - False - start - 5 - 5 - - - - - False - True - 4 - - - - - True - False - - - False - True - 5 - - - - - True - False - - - - True - False - start - 5 - 5 - - - - - False - True - 6 - - - - - True - False - - - False - True - 7 - - - - - True - False - start - 5 - 5 - - - False - True - 8 - - - - - True - False - - - False - True - 9 - - - - - True - False - start - 5 - 5 - - - False - True - 10 - - - - - True - False - - - False - True - 11 - - - - - True - False - start - 5 - 5 - - - False - True - 12 - - - - - True - False - - - False - True - 13 - - - - - True - False - start - 5 - 5 - - - True - True - 14 - - - - - True - True - 1 - - - - - True - False - 5 - - - True - False - System Version - - - False - True - 0 - - - - - 50 - True - False - 5 - 5 - - - False - True - end - 1 - - - - - False - True - end - 4 - - - - - False - 5 - 5 - 0/0 - - - False - True - 11 - - - - - 200 - False - 5 - 5 - 6 - - - False - True - 12 - - - - - False - True - 1 - - - - - True - True - 1 - - - - - - diff --git a/src/Ryujinx.Gtk3/UI/OpenGLRenderer.cs b/src/Ryujinx.Gtk3/UI/OpenGLRenderer.cs deleted file mode 100644 index 1fdabc754..000000000 --- a/src/Ryujinx.Gtk3/UI/OpenGLRenderer.cs +++ /dev/null @@ -1,142 +0,0 @@ -using OpenTK.Graphics.OpenGL; -using Ryujinx.Common.Configuration; -using Ryujinx.Common.Logging; -using Ryujinx.Input.HLE; -using SPB.Graphics; -using SPB.Graphics.Exceptions; -using SPB.Graphics.OpenGL; -using SPB.Platform; -using SPB.Platform.GLX; -using SPB.Platform.WGL; -using SPB.Windowing; -using System; -using System.Runtime.InteropServices; - -namespace Ryujinx.UI -{ - public partial class OpenGLRenderer : RendererWidgetBase - { - private readonly GraphicsDebugLevel _glLogLevel; - - private bool _initializedOpenGL; - - private OpenGLContextBase _openGLContext; - private SwappableNativeWindowBase _nativeWindow; - - public OpenGLRenderer(InputManager inputManager, GraphicsDebugLevel glLogLevel) : base(inputManager, glLogLevel) - { - _glLogLevel = glLogLevel; - } - - protected override bool OnDrawn(Cairo.Context cr) - { - if (!_initializedOpenGL) - { - IntializeOpenGL(); - } - - return true; - } - - private void IntializeOpenGL() - { - _nativeWindow = RetrieveNativeWindow(); - - Window.EnsureNative(); - - _openGLContext = PlatformHelper.CreateOpenGLContext(GetGraphicsMode(), 3, 3, _glLogLevel == GraphicsDebugLevel.None ? OpenGLContextFlags.Compat : OpenGLContextFlags.Compat | OpenGLContextFlags.Debug); - _openGLContext.Initialize(_nativeWindow); - _openGLContext.MakeCurrent(_nativeWindow); - - // Release the GL exclusivity that SPB gave us as we aren't going to use it in GTK Thread. - _openGLContext.MakeCurrent(null); - - WaitEvent.Set(); - - _initializedOpenGL = true; - } - - private SwappableNativeWindowBase RetrieveNativeWindow() - { - if (OperatingSystem.IsWindows()) - { - IntPtr windowHandle = gdk_win32_window_get_handle(Window.Handle); - - return new WGLWindow(new NativeHandle(windowHandle)); - } - else if (OperatingSystem.IsLinux()) - { - IntPtr displayHandle = gdk_x11_display_get_xdisplay(Display.Handle); - IntPtr windowHandle = gdk_x11_window_get_xid(Window.Handle); - - return new GLXWindow(new NativeHandle(displayHandle), new NativeHandle(windowHandle)); - } - - throw new NotImplementedException(); - } - - [LibraryImport("libgdk-3-0.dll")] - private static partial IntPtr gdk_win32_window_get_handle(IntPtr d); - - [LibraryImport("libgdk-3.so.0")] - private static partial IntPtr gdk_x11_display_get_xdisplay(IntPtr gdkDisplay); - - [LibraryImport("libgdk-3.so.0")] - private static partial IntPtr gdk_x11_window_get_xid(IntPtr gdkWindow); - - private static FramebufferFormat GetGraphicsMode() - { - return Environment.OSVersion.Platform == PlatformID.Unix ? new FramebufferFormat(new ColorFormat(8, 8, 8, 0), 16, 0, ColorFormat.Zero, 0, 2, false) : FramebufferFormat.Default; - } - - public override void InitializeRenderer() - { - // First take exclusivity on the OpenGL context. - ((Graphics.OpenGL.OpenGLRenderer)Renderer).InitializeBackgroundContext(SPBOpenGLContext.CreateBackgroundContext(_openGLContext)); - - _openGLContext.MakeCurrent(_nativeWindow); - - GL.ClearColor(0, 0, 0, 1.0f); - GL.Clear(ClearBufferMask.ColorBufferBit); - SwapBuffers(); - } - - public override void SwapBuffers() - { - _nativeWindow.SwapBuffers(); - } - - protected override string GetGpuBackendName() - { - return "OpenGL"; - } - - protected override void Dispose(bool disposing) - { - // Try to bind the OpenGL context before calling the shutdown event. - try - { - _openGLContext?.MakeCurrent(_nativeWindow); - } - catch (ContextException e) - { - Logger.Warning?.Print(LogClass.UI, $"Failed to bind OpenGL context: {e}"); - } - - Device?.DisposeGpu(); - NpadManager.Dispose(); - - // Unbind context and destroy everything. - try - { - _openGLContext?.MakeCurrent(null); - } - catch (ContextException e) - { - Logger.Warning?.Print(LogClass.UI, $"Failed to unbind OpenGL context: {e}"); - } - - _openGLContext?.Dispose(); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/OpenToolkitBindingsContext.cs b/src/Ryujinx.Gtk3/UI/OpenToolkitBindingsContext.cs deleted file mode 100644 index 1224ccfe0..000000000 --- a/src/Ryujinx.Gtk3/UI/OpenToolkitBindingsContext.cs +++ /dev/null @@ -1,20 +0,0 @@ -using SPB.Graphics; -using System; - -namespace Ryujinx.UI -{ - public class OpenToolkitBindingsContext : OpenTK.IBindingsContext - { - private readonly IBindingsContext _bindingContext; - - public OpenToolkitBindingsContext(IBindingsContext bindingsContext) - { - _bindingContext = bindingsContext; - } - - public IntPtr GetProcAddress(string procName) - { - return _bindingContext.GetProcAddress(procName); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/RendererWidgetBase.cs b/src/Ryujinx.Gtk3/UI/RendererWidgetBase.cs deleted file mode 100644 index 12139e87d..000000000 --- a/src/Ryujinx.Gtk3/UI/RendererWidgetBase.cs +++ /dev/null @@ -1,813 +0,0 @@ -using Gdk; -using Gtk; -using Ryujinx.Common; -using Ryujinx.Common.Configuration; -using Ryujinx.Common.Logging; -using Ryujinx.Common.Utilities; -using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.GAL.Multithreading; -using Ryujinx.Graphics.Gpu; -using Ryujinx.Input; -using Ryujinx.Input.GTK3; -using Ryujinx.Input.HLE; -using Ryujinx.UI.Common.Configuration; -using Ryujinx.UI.Common.Helper; -using Ryujinx.UI.Widgets; -using SkiaSharp; -using System; -using System.Diagnostics; -using System.IO; -using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Tasks; -using Key = Ryujinx.Input.Key; -using ScalingFilter = Ryujinx.Graphics.GAL.ScalingFilter; -using Switch = Ryujinx.HLE.Switch; - -namespace Ryujinx.UI -{ - public abstract class RendererWidgetBase : DrawingArea - { - private const int SwitchPanelWidth = 1280; - private const int SwitchPanelHeight = 720; - private const int TargetFps = 60; - private const float MaxResolutionScale = 4.0f; // Max resolution hotkeys can scale to before wrapping. - private const float VolumeDelta = 0.05f; - - public ManualResetEvent WaitEvent { get; set; } - public NpadManager NpadManager { get; } - public TouchScreenManager TouchScreenManager { get; } - public Switch Device { get; private set; } - public IRenderer Renderer { get; private set; } - - public bool ScreenshotRequested { get; set; } - protected int WindowWidth { get; private set; } - protected int WindowHeight { get; private set; } - - public static event EventHandler StatusUpdatedEvent; - - private bool _isActive; - private bool _isStopped; - - private bool _toggleFullscreen; - private bool _toggleDockedMode; - - private readonly long _ticksPerFrame; - - private long _ticks = 0; - private float _newVolume; - - private readonly Stopwatch _chrono; - - private KeyboardHotkeyState _prevHotkeyState; - - private readonly ManualResetEvent _exitEvent; - private readonly ManualResetEvent _gpuDoneEvent; - - private readonly CancellationTokenSource _gpuCancellationTokenSource; - - // Hide Cursor - const int CursorHideIdleTime = 5; // seconds - private static readonly Cursor _invisibleCursor = new(Display.Default, CursorType.BlankCursor); - private long _lastCursorMoveTime; - private HideCursorMode _hideCursorMode; - private readonly InputManager _inputManager; - private readonly IKeyboard _keyboardInterface; - private readonly GraphicsDebugLevel _glLogLevel; - private string _gpuBackendName; - private string _gpuDriverName; - private bool _isMouseInClient; - - public RendererWidgetBase(InputManager inputManager, GraphicsDebugLevel glLogLevel) - { - var mouseDriver = new GTK3MouseDriver(this); - - _inputManager = inputManager; - _inputManager.SetMouseDriver(mouseDriver); - NpadManager = _inputManager.CreateNpadManager(); - TouchScreenManager = _inputManager.CreateTouchScreenManager(); - _keyboardInterface = (IKeyboard)_inputManager.KeyboardDriver.GetGamepad("0"); - - WaitEvent = new ManualResetEvent(false); - - _glLogLevel = glLogLevel; - - Destroyed += Renderer_Destroyed; - - _chrono = new Stopwatch(); - - _ticksPerFrame = Stopwatch.Frequency / TargetFps; - - AddEvents((int)(EventMask.ButtonPressMask - | EventMask.ButtonReleaseMask - | EventMask.PointerMotionMask - | EventMask.ScrollMask - | EventMask.EnterNotifyMask - | EventMask.LeaveNotifyMask - | EventMask.KeyPressMask - | EventMask.KeyReleaseMask)); - - _exitEvent = new ManualResetEvent(false); - _gpuDoneEvent = new ManualResetEvent(false); - - _gpuCancellationTokenSource = new CancellationTokenSource(); - - _hideCursorMode = ConfigurationState.Instance.HideCursor; - _lastCursorMoveTime = Stopwatch.GetTimestamp(); - - ConfigurationState.Instance.HideCursor.Event += HideCursorStateChanged; - ConfigurationState.Instance.Graphics.AntiAliasing.Event += UpdateAnriAliasing; - ConfigurationState.Instance.Graphics.ScalingFilter.Event += UpdateScalingFilter; - ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel; - } - - private void UpdateScalingFilterLevel(object sender, ReactiveEventArgs e) - { - Renderer.Window.SetScalingFilter((ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value); - Renderer.Window.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value); - } - - private void UpdateScalingFilter(object sender, ReactiveEventArgs e) - { - Renderer.Window.SetScalingFilter((ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value); - Renderer.Window.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value); - } - - public abstract void InitializeRenderer(); - - public abstract void SwapBuffers(); - - protected abstract string GetGpuBackendName(); - - private string GetGpuDriverName() - { - return Renderer.GetHardwareInfo().GpuDriver; - } - - private void HideCursorStateChanged(object sender, ReactiveEventArgs state) - { - Application.Invoke(delegate - { - _hideCursorMode = state.NewValue; - - switch (_hideCursorMode) - { - case HideCursorMode.Never: - Window.Cursor = null; - break; - case HideCursorMode.OnIdle: - _lastCursorMoveTime = Stopwatch.GetTimestamp(); - break; - case HideCursorMode.Always: - Window.Cursor = _invisibleCursor; - break; - default: - throw new ArgumentOutOfRangeException(nameof(state)); - } - }); - } - - private void Renderer_Destroyed(object sender, EventArgs e) - { - ConfigurationState.Instance.HideCursor.Event -= HideCursorStateChanged; - ConfigurationState.Instance.Graphics.AntiAliasing.Event -= UpdateAnriAliasing; - ConfigurationState.Instance.Graphics.ScalingFilter.Event -= UpdateScalingFilter; - ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event -= UpdateScalingFilterLevel; - - NpadManager.Dispose(); - Dispose(); - } - - private void UpdateAnriAliasing(object sender, ReactiveEventArgs e) - { - Renderer?.Window.SetAntiAliasing((Graphics.GAL.AntiAliasing)e.NewValue); - } - - protected override bool OnMotionNotifyEvent(EventMotion evnt) - { - if (_hideCursorMode == HideCursorMode.OnIdle) - { - _lastCursorMoveTime = Stopwatch.GetTimestamp(); - } - - if (ConfigurationState.Instance.Hid.EnableMouse) - { - Window.Cursor = _invisibleCursor; - } - - _isMouseInClient = true; - - return false; - } - - protected override bool OnEnterNotifyEvent(EventCrossing evnt) - { - Window.Cursor = ConfigurationState.Instance.Hid.EnableMouse ? _invisibleCursor : null; - - _isMouseInClient = true; - - return base.OnEnterNotifyEvent(evnt); - } - - protected override bool OnLeaveNotifyEvent(EventCrossing evnt) - { - Window.Cursor = null; - - _isMouseInClient = false; - - return base.OnLeaveNotifyEvent(evnt); - } - - protected override void OnGetPreferredHeight(out int minimumHeight, out int naturalHeight) - { - Gdk.Monitor monitor = Display.GetMonitorAtWindow(Window); - - // If the monitor is at least 1080p, use the Switch panel size as minimal size. - if (monitor.Geometry.Height >= 1080) - { - minimumHeight = SwitchPanelHeight; - } - // Otherwise, we default minimal size to 480p 16:9. - else - { - minimumHeight = 480; - } - - naturalHeight = minimumHeight; - } - - protected override void OnGetPreferredWidth(out int minimumWidth, out int naturalWidth) - { - Gdk.Monitor monitor = Display.GetMonitorAtWindow(Window); - - // If the monitor is at least 1080p, use the Switch panel size as minimal size. - if (monitor.Geometry.Height >= 1080) - { - minimumWidth = SwitchPanelWidth; - } - // Otherwise, we default minimal size to 480p 16:9. - else - { - minimumWidth = 854; - } - - naturalWidth = minimumWidth; - } - - protected override bool OnConfigureEvent(EventConfigure evnt) - { - bool result = base.OnConfigureEvent(evnt); - - Gdk.Monitor monitor = Display.GetMonitorAtWindow(Window); - - WindowWidth = evnt.Width * monitor.ScaleFactor; - WindowHeight = evnt.Height * monitor.ScaleFactor; - - Renderer?.Window?.SetSize(WindowWidth, WindowHeight); - - return result; - } - - private void HandleScreenState(KeyboardStateSnapshot keyboard) - { - bool toggleFullscreen = keyboard.IsPressed(Key.F11) - || ((keyboard.IsPressed(Key.AltLeft) - || keyboard.IsPressed(Key.AltRight)) - && keyboard.IsPressed(Key.Enter)) - || keyboard.IsPressed(Key.Escape); - - bool fullScreenToggled = ParentWindow.State.HasFlag(WindowState.Fullscreen); - - if (toggleFullscreen != _toggleFullscreen) - { - if (toggleFullscreen) - { - if (fullScreenToggled) - { - ParentWindow.Unfullscreen(); - (Toplevel as MainWindow)?.ToggleExtraWidgets(true); - } - else - { - if (keyboard.IsPressed(Key.Escape)) - { - if (!ConfigurationState.Instance.ShowConfirmExit || GtkDialog.CreateExitDialog()) - { - Exit(); - } - } - else - { - ParentWindow.Fullscreen(); - (Toplevel as MainWindow)?.ToggleExtraWidgets(false); - } - } - } - } - - _toggleFullscreen = toggleFullscreen; - - bool toggleDockedMode = keyboard.IsPressed(Key.F9); - - if (toggleDockedMode != _toggleDockedMode) - { - if (toggleDockedMode) - { - ConfigurationState.Instance.System.EnableDockedMode.Value = - !ConfigurationState.Instance.System.EnableDockedMode.Value; - } - } - - _toggleDockedMode = toggleDockedMode; - - if (_isMouseInClient) - { - if (ConfigurationState.Instance.Hid.EnableMouse.Value) - { - Window.Cursor = _invisibleCursor; - } - else - { - switch (_hideCursorMode) - { - case HideCursorMode.OnIdle: - long cursorMoveDelta = Stopwatch.GetTimestamp() - _lastCursorMoveTime; - Window.Cursor = (cursorMoveDelta >= CursorHideIdleTime * Stopwatch.Frequency) ? _invisibleCursor : null; - break; - case HideCursorMode.Always: - Window.Cursor = _invisibleCursor; - break; - case HideCursorMode.Never: - Window.Cursor = null; - break; - } - } - } - } - - public void Initialize(Switch device) - { - Device = device; - - IRenderer renderer = Device.Gpu.Renderer; - - if (renderer is ThreadedRenderer tr) - { - renderer = tr.BaseRenderer; - } - - Renderer = renderer; - Renderer?.Window?.SetSize(WindowWidth, WindowHeight); - - if (Renderer != null) - { - Renderer.ScreenCaptured += Renderer_ScreenCaptured; - } - - NpadManager.Initialize(device, ConfigurationState.Instance.Hid.InputConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse); - TouchScreenManager.Initialize(device); - } - - private unsafe void Renderer_ScreenCaptured(object sender, ScreenCaptureImageInfo e) - { - if (e.Data.Length > 0 && e.Height > 0 && e.Width > 0) - { - Task.Run(() => - { - lock (this) - { - string applicationName = Device.Processes.ActiveApplication.Name; - string sanitizedApplicationName = FileSystemUtils.SanitizeFileName(applicationName); - DateTime currentTime = DateTime.Now; - - string filename = $"{sanitizedApplicationName}_{currentTime.Year}-{currentTime.Month:D2}-{currentTime.Day:D2}_{currentTime.Hour:D2}-{currentTime.Minute:D2}-{currentTime.Second:D2}.png"; - - string directory = AppDataManager.Mode switch - { - AppDataManager.LaunchMode.Portable or AppDataManager.LaunchMode.Custom => System.IO.Path.Combine(AppDataManager.BaseDirPath, "screenshots"), - _ => System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "Ryujinx"), - }; - - string path = System.IO.Path.Combine(directory, filename); - - try - { - Directory.CreateDirectory(directory); - } - catch (Exception ex) - { - Logger.Error?.Print(LogClass.Application, $"Failed to create directory at path {directory}. Error : {ex.GetType().Name}", "Screenshot"); - - return; - } - - var colorType = e.IsBgra ? SKColorType.Bgra8888 : SKColorType.Rgba8888; - using var image = new SKBitmap(new SKImageInfo(e.Width, e.Height, colorType, SKAlphaType.Premul)); - - Marshal.Copy(e.Data, 0, image.GetPixels(), e.Data.Length); - using var surface = SKSurface.Create(image.Info); - var canvas = surface.Canvas; - - if (e.FlipX || e.FlipY) - { - canvas.Clear(SKColors.Transparent); - - float scaleX = e.FlipX ? -1 : 1; - float scaleY = e.FlipY ? -1 : 1; - - var matrix = SKMatrix.CreateScale(scaleX, scaleY, image.Width / 2f, image.Height / 2f); - - canvas.SetMatrix(matrix); - } - canvas.DrawBitmap(image, new SKPoint()); - - surface.Flush(); - using var snapshot = surface.Snapshot(); - using var encoded = snapshot.Encode(SKEncodedImageFormat.Png, 80); - using var file = File.OpenWrite(path); - encoded.SaveTo(file); - - image.Dispose(); - - Logger.Notice.Print(LogClass.Application, $"Screenshot saved to {path}", "Screenshot"); - } - }); - } - else - { - Logger.Error?.Print(LogClass.Application, $"Screenshot is empty. Size : {e.Data.Length} bytes. Resolution : {e.Width}x{e.Height}", "Screenshot"); - } - } - - public void Render() - { - Gtk.Window parent = Toplevel as Gtk.Window; - parent.Present(); - - InitializeRenderer(); - - Device.Gpu.Renderer.Initialize(_glLogLevel); - - Renderer.Window.SetAntiAliasing((Graphics.GAL.AntiAliasing)ConfigurationState.Instance.Graphics.AntiAliasing.Value); - Renderer.Window.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value); - Renderer.Window.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value); - - _gpuBackendName = GetGpuBackendName(); - _gpuDriverName = GetGpuDriverName(); - - Device.Gpu.Renderer.RunLoop(() => - { - Device.Gpu.SetGpuThread(); - Device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token); - - Renderer.Window.ChangeVSyncMode(Device.EnableDeviceVsync); - - (Toplevel as MainWindow)?.ActivatePauseMenu(); - - while (_isActive) - { - if (_isStopped) - { - return; - } - - _ticks += _chrono.ElapsedTicks; - - _chrono.Restart(); - - if (Device.WaitFifo()) - { - Device.Statistics.RecordFifoStart(); - Device.ProcessFrame(); - Device.Statistics.RecordFifoEnd(); - } - - while (Device.ConsumeFrameAvailable()) - { - Device.PresentFrame(SwapBuffers); - } - - if (_ticks >= _ticksPerFrame) - { - string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? "Docked" : "Handheld"; - float scale = GraphicsConfig.ResScale; - if (scale != 1) - { - dockedMode += $" ({scale}x)"; - } - - StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs( - Device.EnableDeviceVsync, - Device.GetVolume(), - _gpuBackendName, - dockedMode, - ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(), - $"Game: {Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)", - $"FIFO: {Device.Statistics.GetFifoPercent():0.00} %", - $"GPU: {_gpuDriverName}")); - - _ticks = Math.Min(_ticks - _ticksPerFrame, _ticksPerFrame); - } - } - - // Make sure all commands in the run loop are fully executed before leaving the loop. - if (Device.Gpu.Renderer is ThreadedRenderer threaded) - { - threaded.FlushThreadedCommands(); - } - - _gpuDoneEvent.Set(); - }); - } - - public void Start() - { - _chrono.Restart(); - - _isActive = true; - - Gtk.Window parent = Toplevel as Gtk.Window; - - Application.Invoke(delegate - { - parent.Present(); - - var activeProcess = Device.Processes.ActiveApplication; - - parent.Title = TitleHelper.ActiveApplicationTitle(activeProcess, Program.Version); - }); - - Thread renderLoopThread = new(Render) - { - Name = "GUI.RenderLoop", - }; - renderLoopThread.Start(); - - Thread nvidiaStutterWorkaround = null; - if (Renderer is Graphics.OpenGL.OpenGLRenderer) - { - nvidiaStutterWorkaround = new Thread(NvidiaStutterWorkaround) - { - Name = "GUI.NvidiaStutterWorkaround", - }; - nvidiaStutterWorkaround.Start(); - } - - MainLoop(); - - // NOTE: The render loop is allowed to stay alive until the renderer itself is disposed, as it may handle resource dispose. - // We only need to wait for all commands submitted during the main gpu loop to be processed. - _gpuDoneEvent.WaitOne(); - _gpuDoneEvent.Dispose(); - nvidiaStutterWorkaround?.Join(); - - Exit(); - } - - public void Exit() - { - TouchScreenManager?.Dispose(); - NpadManager?.Dispose(); - - if (_isStopped) - { - return; - } - - _gpuCancellationTokenSource.Cancel(); - - _isStopped = true; - - if (_isActive) - { - _isActive = false; - - _exitEvent.WaitOne(); - _exitEvent.Dispose(); - } - } - - private void NvidiaStutterWorkaround() - { - while (_isActive) - { - // When NVIDIA Threaded Optimization is on, the driver will snapshot all threads in the system whenever the application creates any new ones. - // The ThreadPool has something called a "GateThread" which terminates itself after some inactivity. - // However, it immediately starts up again, since the rules regarding when to terminate and when to start differ. - // This creates a new thread every second or so. - // The main problem with this is that the thread snapshot can take 70ms, is on the OpenGL thread and will delay rendering any graphics. - // This is a little over budget on a frame time of 16ms, so creates a large stutter. - // The solution is to keep the ThreadPool active so that it never has a reason to terminate the GateThread. - - // TODO: This should be removed when the issue with the GateThread is resolved. - - ThreadPool.QueueUserWorkItem((state) => { }); - Thread.Sleep(300); - } - } - - public void MainLoop() - { - while (_isActive) - { - UpdateFrame(); - - // Polling becomes expensive if it's not slept - Thread.Sleep(1); - } - - _exitEvent.Set(); - } - - private bool UpdateFrame() - { - if (!_isActive) - { - return true; - } - - if (_isStopped) - { - return false; - } - - if ((Toplevel as MainWindow).IsFocused) - { - Application.Invoke(delegate - { - KeyboardStateSnapshot keyboard = _keyboardInterface.GetKeyboardStateSnapshot(); - - HandleScreenState(keyboard); - - if (keyboard.IsPressed(Key.Delete)) - { - if (!ParentWindow.State.HasFlag(WindowState.Fullscreen)) - { - Device.Processes.ActiveApplication.DiskCacheLoadState?.Cancel(); - } - } - }); - } - - NpadManager.Update(ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat()); - - if ((Toplevel as MainWindow).IsFocused) - { - KeyboardHotkeyState currentHotkeyState = GetHotkeyState(); - - if (currentHotkeyState.HasFlag(KeyboardHotkeyState.ToggleVSync) && - !_prevHotkeyState.HasFlag(KeyboardHotkeyState.ToggleVSync)) - { - Device.EnableDeviceVsync = !Device.EnableDeviceVsync; - } - - if ((currentHotkeyState.HasFlag(KeyboardHotkeyState.Screenshot) && - !_prevHotkeyState.HasFlag(KeyboardHotkeyState.Screenshot)) || ScreenshotRequested) - { - ScreenshotRequested = false; - - Renderer.Screenshot(); - } - - if (currentHotkeyState.HasFlag(KeyboardHotkeyState.ShowUI) && - !_prevHotkeyState.HasFlag(KeyboardHotkeyState.ShowUI)) - { - (Toplevel as MainWindow).ToggleExtraWidgets(true); - } - - if (currentHotkeyState.HasFlag(KeyboardHotkeyState.Pause) && - !_prevHotkeyState.HasFlag(KeyboardHotkeyState.Pause)) - { - (Toplevel as MainWindow)?.TogglePause(); - } - - if (currentHotkeyState.HasFlag(KeyboardHotkeyState.ToggleMute) && - !_prevHotkeyState.HasFlag(KeyboardHotkeyState.ToggleMute)) - { - if (Device.IsAudioMuted()) - { - Device.SetVolume(ConfigurationState.Instance.System.AudioVolume); - } - else - { - Device.SetVolume(0); - } - } - - if (currentHotkeyState.HasFlag(KeyboardHotkeyState.ResScaleUp) && - !_prevHotkeyState.HasFlag(KeyboardHotkeyState.ResScaleUp)) - { - GraphicsConfig.ResScale = GraphicsConfig.ResScale % MaxResolutionScale + 1; - } - - if (currentHotkeyState.HasFlag(KeyboardHotkeyState.ResScaleDown) && - !_prevHotkeyState.HasFlag(KeyboardHotkeyState.ResScaleDown)) - { - GraphicsConfig.ResScale = - (MaxResolutionScale + GraphicsConfig.ResScale - 2) % MaxResolutionScale + 1; - } - - if (currentHotkeyState.HasFlag(KeyboardHotkeyState.VolumeUp) && - !_prevHotkeyState.HasFlag(KeyboardHotkeyState.VolumeUp)) - { - _newVolume = MathF.Round((Device.GetVolume() + VolumeDelta), 2); - Device.SetVolume(_newVolume); - } - - if (currentHotkeyState.HasFlag(KeyboardHotkeyState.VolumeDown) && - !_prevHotkeyState.HasFlag(KeyboardHotkeyState.VolumeDown)) - { - _newVolume = MathF.Round((Device.GetVolume() - VolumeDelta), 2); - Device.SetVolume(_newVolume); - } - - _prevHotkeyState = currentHotkeyState; - } - - // Touchscreen - bool hasTouch = false; - - // Get screen touch position - if ((Toplevel as MainWindow).IsFocused && !ConfigurationState.Instance.Hid.EnableMouse) - { - hasTouch = TouchScreenManager.Update(true, (_inputManager.MouseDriver as GTK3MouseDriver).IsButtonPressed(MouseButton.Button1), ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat()); - } - - if (!hasTouch) - { - TouchScreenManager.Update(false); - } - - Device.Hid.DebugPad.Update(); - - return true; - } - - [Flags] - private enum KeyboardHotkeyState - { - None = 0, - ToggleVSync = 1 << 0, - Screenshot = 1 << 1, - ShowUI = 1 << 2, - Pause = 1 << 3, - ToggleMute = 1 << 4, - ResScaleUp = 1 << 5, - ResScaleDown = 1 << 6, - VolumeUp = 1 << 7, - VolumeDown = 1 << 8, - } - - private KeyboardHotkeyState GetHotkeyState() - { - KeyboardHotkeyState state = KeyboardHotkeyState.None; - - if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleVsync)) - { - state |= KeyboardHotkeyState.ToggleVSync; - } - - if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot)) - { - state |= KeyboardHotkeyState.Screenshot; - } - - if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUI)) - { - state |= KeyboardHotkeyState.ShowUI; - } - - if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Pause)) - { - state |= KeyboardHotkeyState.Pause; - } - - if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleMute)) - { - state |= KeyboardHotkeyState.ToggleMute; - } - - if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ResScaleUp)) - { - state |= KeyboardHotkeyState.ResScaleUp; - } - - if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ResScaleDown)) - { - state |= KeyboardHotkeyState.ResScaleDown; - } - - if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.VolumeUp)) - { - state |= KeyboardHotkeyState.VolumeUp; - } - - if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.VolumeDown)) - { - state |= KeyboardHotkeyState.VolumeDown; - } - - return state; - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/SPBOpenGLContext.cs b/src/Ryujinx.Gtk3/UI/SPBOpenGLContext.cs deleted file mode 100644 index 97feb4345..000000000 --- a/src/Ryujinx.Gtk3/UI/SPBOpenGLContext.cs +++ /dev/null @@ -1,49 +0,0 @@ -using OpenTK.Graphics.OpenGL; -using Ryujinx.Graphics.OpenGL; -using SPB.Graphics; -using SPB.Graphics.OpenGL; -using SPB.Platform; -using SPB.Windowing; - -namespace Ryujinx.UI -{ - class SPBOpenGLContext : IOpenGLContext - { - private readonly OpenGLContextBase _context; - private readonly NativeWindowBase _window; - - private SPBOpenGLContext(OpenGLContextBase context, NativeWindowBase window) - { - _context = context; - _window = window; - } - - public void Dispose() - { - _context.Dispose(); - _window.Dispose(); - } - - public void MakeCurrent() - { - _context.MakeCurrent(_window); - } - - public bool HasContext() => _context.IsCurrent; - - public static SPBOpenGLContext CreateBackgroundContext(OpenGLContextBase sharedContext) - { - OpenGLContextBase context = PlatformHelper.CreateOpenGLContext(FramebufferFormat.Default, 3, 3, OpenGLContextFlags.Compat, true, sharedContext); - NativeWindowBase window = PlatformHelper.CreateOpenGLWindow(FramebufferFormat.Default, 0, 0, 100, 100); - - context.Initialize(window); - context.MakeCurrent(window); - - GL.LoadBindings(new OpenToolkitBindingsContext(context)); - - context.MakeCurrent(null); - - return new SPBOpenGLContext(context, window); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/StatusUpdatedEventArgs.cs b/src/Ryujinx.Gtk3/UI/StatusUpdatedEventArgs.cs deleted file mode 100644 index db467ebfc..000000000 --- a/src/Ryujinx.Gtk3/UI/StatusUpdatedEventArgs.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; - -namespace Ryujinx.UI -{ - public class StatusUpdatedEventArgs : EventArgs - { - public bool VSyncEnabled; - public float Volume; - public string DockedMode; - public string AspectRatio; - public string GameStatus; - public string FifoStatus; - public string GpuName; - public string GpuBackend; - - public StatusUpdatedEventArgs(bool vSyncEnabled, float volume, string gpuBackend, string dockedMode, string aspectRatio, string gameStatus, string fifoStatus, string gpuName) - { - VSyncEnabled = vSyncEnabled; - Volume = volume; - GpuBackend = gpuBackend; - DockedMode = dockedMode; - AspectRatio = aspectRatio; - GameStatus = gameStatus; - FifoStatus = fifoStatus; - GpuName = gpuName; - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/VulkanRenderer.cs b/src/Ryujinx.Gtk3/UI/VulkanRenderer.cs deleted file mode 100644 index eefc56999..000000000 --- a/src/Ryujinx.Gtk3/UI/VulkanRenderer.cs +++ /dev/null @@ -1,93 +0,0 @@ -using Gdk; -using Ryujinx.Common.Configuration; -using Ryujinx.Input.HLE; -using Ryujinx.UI.Helper; -using SPB.Graphics.Vulkan; -using SPB.Platform.Metal; -using SPB.Platform.Win32; -using SPB.Platform.X11; -using SPB.Windowing; -using System; -using System.Runtime.InteropServices; - -namespace Ryujinx.UI -{ - public partial class VulkanRenderer : RendererWidgetBase - { - public NativeWindowBase NativeWindow { get; private set; } - private UpdateBoundsCallbackDelegate _updateBoundsCallback; - - public VulkanRenderer(InputManager inputManager, GraphicsDebugLevel glLogLevel) : base(inputManager, glLogLevel) { } - - private NativeWindowBase RetrieveNativeWindow() - { - if (OperatingSystem.IsWindows()) - { - IntPtr windowHandle = gdk_win32_window_get_handle(Window.Handle); - - return new SimpleWin32Window(new NativeHandle(windowHandle)); - } - else if (OperatingSystem.IsLinux()) - { - IntPtr displayHandle = gdk_x11_display_get_xdisplay(Display.Handle); - IntPtr windowHandle = gdk_x11_window_get_xid(Window.Handle); - - return new SimpleX11Window(new NativeHandle(displayHandle), new NativeHandle(windowHandle)); - } - else if (OperatingSystem.IsMacOS()) - { - IntPtr metalLayer = MetalHelper.GetMetalLayer(Display, Window, out IntPtr nsView, out _updateBoundsCallback); - - return new SimpleMetalWindow(new NativeHandle(nsView), new NativeHandle(metalLayer)); - } - - throw new NotImplementedException(); - } - - [LibraryImport("libgdk-3-0.dll")] - private static partial IntPtr gdk_win32_window_get_handle(IntPtr d); - - [LibraryImport("libgdk-3.so.0")] - private static partial IntPtr gdk_x11_display_get_xdisplay(IntPtr gdkDisplay); - - [LibraryImport("libgdk-3.so.0")] - private static partial IntPtr gdk_x11_window_get_xid(IntPtr gdkWindow); - - protected override bool OnConfigureEvent(EventConfigure evnt) - { - if (NativeWindow == null) - { - NativeWindow = RetrieveNativeWindow(); - - WaitEvent.Set(); - } - - bool result = base.OnConfigureEvent(evnt); - - _updateBoundsCallback?.Invoke(Window); - - return result; - } - - public unsafe IntPtr CreateWindowSurface(IntPtr instance) - { - return VulkanHelper.CreateWindowSurface(instance, NativeWindow); - } - - public override void InitializeRenderer() { } - - public override void SwapBuffers() { } - - protected override string GetGpuBackendName() - { - return "Vulkan"; - } - - protected override void Dispose(bool disposing) - { - Device?.DisposeGpu(); - - NpadManager.Dispose(); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Widgets/GameTableContextMenu.Designer.cs b/src/Ryujinx.Gtk3/UI/Widgets/GameTableContextMenu.Designer.cs deleted file mode 100644 index 8ee1cd2f3..000000000 --- a/src/Ryujinx.Gtk3/UI/Widgets/GameTableContextMenu.Designer.cs +++ /dev/null @@ -1,233 +0,0 @@ -using Gtk; -using System; - -namespace Ryujinx.UI.Widgets -{ - public partial class GameTableContextMenu : Menu - { - private MenuItem _openSaveUserDirMenuItem; - private MenuItem _openSaveDeviceDirMenuItem; - private MenuItem _openSaveBcatDirMenuItem; - private MenuItem _manageTitleUpdatesMenuItem; - private MenuItem _manageDlcMenuItem; - private MenuItem _manageCheatMenuItem; - private MenuItem _openTitleModDirMenuItem; - private MenuItem _openTitleSdModDirMenuItem; - private Menu _extractSubMenu; - private MenuItem _extractMenuItem; - private MenuItem _extractRomFsMenuItem; - private MenuItem _extractExeFsMenuItem; - private MenuItem _extractLogoMenuItem; - private Menu _manageSubMenu; - private MenuItem _manageCacheMenuItem; - private MenuItem _purgePtcCacheMenuItem; - private MenuItem _purgeShaderCacheMenuItem; - private MenuItem _openPtcDirMenuItem; - private MenuItem _openShaderCacheDirMenuItem; - private MenuItem _createShortcutMenuItem; - - private void InitializeComponent() - { - // - // _openSaveUserDirMenuItem - // - _openSaveUserDirMenuItem = new MenuItem("Open User Save Directory") - { - TooltipText = "Open the directory which contains Application's User Saves.", - }; - _openSaveUserDirMenuItem.Activated += OpenSaveUserDir_Clicked; - - // - // _openSaveDeviceDirMenuItem - // - _openSaveDeviceDirMenuItem = new MenuItem("Open Device Save Directory") - { - TooltipText = "Open the directory which contains Application's Device Saves.", - }; - _openSaveDeviceDirMenuItem.Activated += OpenSaveDeviceDir_Clicked; - - // - // _openSaveBcatDirMenuItem - // - _openSaveBcatDirMenuItem = new MenuItem("Open BCAT Save Directory") - { - TooltipText = "Open the directory which contains Application's BCAT Saves.", - }; - _openSaveBcatDirMenuItem.Activated += OpenSaveBcatDir_Clicked; - - // - // _manageTitleUpdatesMenuItem - // - _manageTitleUpdatesMenuItem = new MenuItem("Manage Title Updates") - { - TooltipText = "Open the Title Update management window", - }; - _manageTitleUpdatesMenuItem.Activated += ManageTitleUpdates_Clicked; - - // - // _manageDlcMenuItem - // - _manageDlcMenuItem = new MenuItem("Manage DLC") - { - TooltipText = "Open the DLC management window", - }; - _manageDlcMenuItem.Activated += ManageDlc_Clicked; - - // - // _manageCheatMenuItem - // - _manageCheatMenuItem = new MenuItem("Manage Cheats") - { - TooltipText = "Open the Cheat management window", - }; - _manageCheatMenuItem.Activated += ManageCheats_Clicked; - - // - // _openTitleModDirMenuItem - // - _openTitleModDirMenuItem = new MenuItem("Open Mods Directory") - { - TooltipText = "Open the directory which contains Application's Mods.", - }; - _openTitleModDirMenuItem.Activated += OpenTitleModDir_Clicked; - - // - // _openTitleSdModDirMenuItem - // - _openTitleSdModDirMenuItem = new MenuItem("Open Atmosphere Mods Directory") - { - TooltipText = "Open the alternative SD card atmosphere directory which contains the Application's Mods.", - }; - _openTitleSdModDirMenuItem.Activated += OpenTitleSdModDir_Clicked; - - // - // _extractSubMenu - // - _extractSubMenu = new Menu(); - - // - // _extractMenuItem - // - _extractMenuItem = new MenuItem("Extract Data") - { - Submenu = _extractSubMenu - }; - - // - // _extractRomFsMenuItem - // - _extractRomFsMenuItem = new MenuItem("RomFS") - { - TooltipText = "Extract the RomFS section from Application's current config (including updates).", - }; - _extractRomFsMenuItem.Activated += ExtractRomFs_Clicked; - - // - // _extractExeFsMenuItem - // - _extractExeFsMenuItem = new MenuItem("ExeFS") - { - TooltipText = "Extract the ExeFS section from Application's current config (including updates).", - }; - _extractExeFsMenuItem.Activated += ExtractExeFs_Clicked; - - // - // _extractLogoMenuItem - // - _extractLogoMenuItem = new MenuItem("Logo") - { - TooltipText = "Extract the Logo section from Application's current config (including updates).", - }; - _extractLogoMenuItem.Activated += ExtractLogo_Clicked; - - // - // _manageSubMenu - // - _manageSubMenu = new Menu(); - - // - // _manageCacheMenuItem - // - _manageCacheMenuItem = new MenuItem("Cache Management") - { - Submenu = _manageSubMenu, - }; - - // - // _purgePtcCacheMenuItem - // - _purgePtcCacheMenuItem = new MenuItem("Queue PPTC Rebuild") - { - TooltipText = "Trigger PPTC to rebuild at boot time on the next game launch.", - }; - _purgePtcCacheMenuItem.Activated += PurgePtcCache_Clicked; - - // - // _purgeShaderCacheMenuItem - // - _purgeShaderCacheMenuItem = new MenuItem("Purge Shader Cache") - { - TooltipText = "Delete the Application's shader cache.", - }; - _purgeShaderCacheMenuItem.Activated += PurgeShaderCache_Clicked; - - // - // _openPtcDirMenuItem - // - _openPtcDirMenuItem = new MenuItem("Open PPTC Directory") - { - TooltipText = "Open the directory which contains the Application's PPTC cache.", - }; - _openPtcDirMenuItem.Activated += OpenPtcDir_Clicked; - - // - // _openShaderCacheDirMenuItem - // - _openShaderCacheDirMenuItem = new MenuItem("Open Shader Cache Directory") - { - TooltipText = "Open the directory which contains the Application's shader cache.", - }; - _openShaderCacheDirMenuItem.Activated += OpenShaderCacheDir_Clicked; - - // - // _createShortcutMenuItem - // - _createShortcutMenuItem = new MenuItem("Create Application Shortcut") - { - TooltipText = OperatingSystem.IsMacOS() ? "Create a shortcut in macOS's Applications folder that launches the selected Application" : "Create a Desktop Shortcut that launches the selected Application." - }; - _createShortcutMenuItem.Activated += CreateShortcut_Clicked; - - ShowComponent(); - } - - private void ShowComponent() - { - _extractSubMenu.Append(_extractExeFsMenuItem); - _extractSubMenu.Append(_extractRomFsMenuItem); - _extractSubMenu.Append(_extractLogoMenuItem); - - _manageSubMenu.Append(_purgePtcCacheMenuItem); - _manageSubMenu.Append(_purgeShaderCacheMenuItem); - _manageSubMenu.Append(_openPtcDirMenuItem); - _manageSubMenu.Append(_openShaderCacheDirMenuItem); - - Add(_createShortcutMenuItem); - Add(new SeparatorMenuItem()); - Add(_openSaveUserDirMenuItem); - Add(_openSaveDeviceDirMenuItem); - Add(_openSaveBcatDirMenuItem); - Add(new SeparatorMenuItem()); - Add(_manageTitleUpdatesMenuItem); - Add(_manageDlcMenuItem); - Add(_manageCheatMenuItem); - Add(_openTitleModDirMenuItem); - Add(_openTitleSdModDirMenuItem); - Add(new SeparatorMenuItem()); - Add(_manageCacheMenuItem); - Add(_extractMenuItem); - - ShowAll(); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Widgets/GameTableContextMenu.cs b/src/Ryujinx.Gtk3/UI/Widgets/GameTableContextMenu.cs deleted file mode 100644 index a3e3d4c8c..000000000 --- a/src/Ryujinx.Gtk3/UI/Widgets/GameTableContextMenu.cs +++ /dev/null @@ -1,634 +0,0 @@ -using Gtk; -using LibHac; -using LibHac.Account; -using LibHac.Common; -using LibHac.Fs; -using LibHac.Fs.Fsa; -using LibHac.Fs.Shim; -using LibHac.FsSystem; -using LibHac.Ns; -using LibHac.Tools.Fs; -using LibHac.Tools.FsSystem; -using LibHac.Tools.FsSystem.NcaUtils; -using Ryujinx.Common; -using Ryujinx.Common.Configuration; -using Ryujinx.Common.Logging; -using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.HOS; -using Ryujinx.HLE.HOS.Services.Account.Acc; -using Ryujinx.HLE.Loaders.Processes.Extensions; -using Ryujinx.HLE.Utilities; -using Ryujinx.UI.App.Common; -using Ryujinx.UI.Common.Configuration; -using Ryujinx.UI.Common.Helper; -using Ryujinx.UI.Windows; -using System; -using System.Buffers; -using System.Collections.Generic; -using System.IO; -using System.Reflection; -using System.Threading; - -namespace Ryujinx.UI.Widgets -{ - public partial class GameTableContextMenu : Menu - { - private readonly MainWindow _parent; - private readonly VirtualFileSystem _virtualFileSystem; - private readonly AccountManager _accountManager; - private readonly HorizonClient _horizonClient; - - private readonly ApplicationData _applicationData; - - private MessageDialog _dialog; - private bool _cancel; - - public GameTableContextMenu(MainWindow parent, VirtualFileSystem virtualFileSystem, AccountManager accountManager, HorizonClient horizonClient, ApplicationData applicationData) - { - _parent = parent; - - InitializeComponent(); - - _virtualFileSystem = virtualFileSystem; - _accountManager = accountManager; - _horizonClient = horizonClient; - _applicationData = applicationData; - - if (!_applicationData.ControlHolder.ByteSpan.IsZeros()) - { - _openSaveUserDirMenuItem.Sensitive = _applicationData.ControlHolder.Value.UserAccountSaveDataSize > 0; - _openSaveDeviceDirMenuItem.Sensitive = _applicationData.ControlHolder.Value.DeviceSaveDataSize > 0; - _openSaveBcatDirMenuItem.Sensitive = _applicationData.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0; - } - else - { - _openSaveUserDirMenuItem.Sensitive = false; - _openSaveDeviceDirMenuItem.Sensitive = false; - _openSaveBcatDirMenuItem.Sensitive = false; - } - - string fileExt = System.IO.Path.GetExtension(_applicationData.Path).ToLower(); - bool hasNca = fileExt == ".nca" || fileExt == ".nsp" || fileExt == ".pfs0" || fileExt == ".xci"; - - _extractRomFsMenuItem.Sensitive = hasNca; - _extractExeFsMenuItem.Sensitive = hasNca; - _extractLogoMenuItem.Sensitive = hasNca; - - _createShortcutMenuItem.Sensitive = !ReleaseInformation.IsFlatHubBuild; - - PopupAtPointer(null); - } - - private bool TryFindSaveData(string titleName, ulong titleId, BlitStruct controlHolder, in SaveDataFilter filter, out ulong saveDataId) - { - saveDataId = default; - - Result result = _horizonClient.Fs.FindSaveDataWithFilter(out SaveDataInfo saveDataInfo, SaveDataSpaceId.User, in filter); - - if (ResultFs.TargetNotFound.Includes(result)) - { - ref ApplicationControlProperty control = ref controlHolder.Value; - - Logger.Info?.Print(LogClass.Application, $"Creating save directory for Title: {titleName} [{titleId:x16}]"); - - if (Utilities.IsZeros(controlHolder.ByteSpan)) - { - // If the current application doesn't have a loaded control property, create a dummy one - // and set the savedata sizes so a user savedata will be created. - control = ref new BlitStruct(1).Value; - - // The set sizes don't actually matter as long as they're non-zero because we use directory savedata. - control.UserAccountSaveDataSize = 0x4000; - control.UserAccountSaveDataJournalSize = 0x4000; - - Logger.Warning?.Print(LogClass.Application, "No control file was found for this game. Using a dummy one instead. This may cause inaccuracies in some games."); - } - - Uid user = new((ulong)_accountManager.LastOpenedUser.UserId.High, (ulong)_accountManager.LastOpenedUser.UserId.Low); - - result = _horizonClient.Fs.EnsureApplicationSaveData(out _, new LibHac.Ncm.ApplicationId(titleId), in control, in user); - - if (result.IsFailure()) - { - GtkDialog.CreateErrorDialog($"There was an error creating the specified savedata: {result.ToStringWithName()}"); - - return false; - } - - // Try to find the savedata again after creating it - result = _horizonClient.Fs.FindSaveDataWithFilter(out saveDataInfo, SaveDataSpaceId.User, in filter); - } - - if (result.IsSuccess()) - { - saveDataId = saveDataInfo.SaveDataId; - - return true; - } - - GtkDialog.CreateErrorDialog($"There was an error finding the specified savedata: {result.ToStringWithName()}"); - - return false; - } - - private void OpenSaveDir(in SaveDataFilter saveDataFilter) - { - if (!TryFindSaveData(_applicationData.Name, _applicationData.Id, _applicationData.ControlHolder, in saveDataFilter, out ulong saveDataId)) - { - return; - } - - string saveRootPath = System.IO.Path.Combine(VirtualFileSystem.GetNandPath(), $"user/save/{saveDataId:x16}"); - - if (!Directory.Exists(saveRootPath)) - { - // Inconsistent state. Create the directory - Directory.CreateDirectory(saveRootPath); - } - - string committedPath = System.IO.Path.Combine(saveRootPath, "0"); - string workingPath = System.IO.Path.Combine(saveRootPath, "1"); - - // If the committed directory exists, that path will be loaded the next time the savedata is mounted - if (Directory.Exists(committedPath)) - { - OpenHelper.OpenFolder(committedPath); - } - else - { - // If the working directory exists and the committed directory doesn't, - // the working directory will be loaded the next time the savedata is mounted - if (!Directory.Exists(workingPath)) - { - Directory.CreateDirectory(workingPath); - } - - OpenHelper.OpenFolder(workingPath); - } - } - - private void ExtractSection(NcaSectionType ncaSectionType, int programIndex = 0) - { - FileChooserNative fileChooser = new("Choose the folder to extract into", _parent, FileChooserAction.SelectFolder, "Extract", "Cancel"); - - ResponseType response = (ResponseType)fileChooser.Run(); - string destination = fileChooser.Filename; - - fileChooser.Dispose(); - - if (response == ResponseType.Accept) - { - Thread extractorThread = new(() => - { - Gtk.Application.Invoke(delegate - { - _dialog = new MessageDialog(null, DialogFlags.DestroyWithParent, MessageType.Info, ButtonsType.Cancel, null) - { - Title = "Ryujinx - NCA Section Extractor", - Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Gtk3.UI.Common.Resources.Logo_Ryujinx.png"), - SecondaryText = $"Extracting {ncaSectionType} section from {System.IO.Path.GetFileName(_applicationData.Path)}...", - WindowPosition = WindowPosition.Center, - }; - - int dialogResponse = _dialog.Run(); - if (dialogResponse == (int)ResponseType.Cancel || dialogResponse == (int)ResponseType.DeleteEvent) - { - _cancel = true; - _dialog.Dispose(); - } - }); - - using FileStream file = new(_applicationData.Path, FileMode.Open, FileAccess.Read); - - Nca mainNca = null; - Nca patchNca = null; - - if ((System.IO.Path.GetExtension(_applicationData.Path).ToLower() == ".nsp") || - (System.IO.Path.GetExtension(_applicationData.Path).ToLower() == ".pfs0") || - (System.IO.Path.GetExtension(_applicationData.Path).ToLower() == ".xci")) - { - IFileSystem pfs = PartitionFileSystemUtils.OpenApplicationFileSystem(_applicationData.Path, _virtualFileSystem); - - foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) - { - using var ncaFile = new UniqueRef(); - - pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - Nca nca = new(_virtualFileSystem.KeySet, ncaFile.Release().AsStorage()); - - if (nca.Header.ContentType == NcaContentType.Program) - { - int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); - - if (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection()) - { - patchNca = nca; - } - else - { - mainNca = nca; - } - } - } - } - else if (System.IO.Path.GetExtension(_applicationData.Path).ToLower() == ".nca") - { - mainNca = new Nca(_virtualFileSystem.KeySet, file.AsStorage()); - } - - if (mainNca == null) - { - Logger.Error?.Print(LogClass.Application, "Extraction failure. The main NCA is not present in the selected file."); - - Gtk.Application.Invoke(delegate - { - GtkDialog.CreateErrorDialog("Extraction failure. The main NCA is not present in the selected file."); - }); - - return; - } - - IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks - ? IntegrityCheckLevel.ErrorOnInvalid - : IntegrityCheckLevel.None; - - (Nca updatePatchNca, _) = mainNca.GetUpdateData(_virtualFileSystem, checkLevel, programIndex, out _); - - if (updatePatchNca != null) - { - patchNca = updatePatchNca; - } - - int index = Nca.GetSectionIndexFromType(ncaSectionType, mainNca.Header.ContentType); - - bool sectionExistsInPatch = false; - - if (patchNca != null) - { - sectionExistsInPatch = patchNca.CanOpenSection(index); - } - - IFileSystem ncaFileSystem = sectionExistsInPatch ? mainNca.OpenFileSystemWithPatch(patchNca, index, IntegrityCheckLevel.ErrorOnInvalid) - : mainNca.OpenFileSystem(index, IntegrityCheckLevel.ErrorOnInvalid); - - FileSystemClient fsClient = _horizonClient.Fs; - - string source = DateTime.Now.ToFileTime().ToString()[10..]; - string output = DateTime.Now.ToFileTime().ToString()[10..]; - - using var uniqueSourceFs = new UniqueRef(ncaFileSystem); - using var uniqueOutputFs = new UniqueRef(new LocalFileSystem(destination)); - - fsClient.Register(source.ToU8Span(), ref uniqueSourceFs.Ref); - fsClient.Register(output.ToU8Span(), ref uniqueOutputFs.Ref); - - (Result? resultCode, bool canceled) = CopyDirectory(fsClient, $"{source}:/", $"{output}:/"); - - if (!canceled) - { - if (resultCode.Value.IsFailure()) - { - Logger.Error?.Print(LogClass.Application, $"LibHac returned error code: {resultCode.Value.ErrorCode}"); - - Gtk.Application.Invoke(delegate - { - _dialog?.Dispose(); - - GtkDialog.CreateErrorDialog("Extraction failed. Read the log file for further information."); - }); - } - else if (resultCode.Value.IsSuccess()) - { - Gtk.Application.Invoke(delegate - { - _dialog?.Dispose(); - - MessageDialog dialog = new(null, DialogFlags.DestroyWithParent, MessageType.Info, ButtonsType.Ok, null) - { - Title = "Ryujinx - NCA Section Extractor", - Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Logo_Ryujinx.png"), - SecondaryText = "Extraction completed successfully.", - WindowPosition = WindowPosition.Center, - }; - - dialog.Run(); - dialog.Dispose(); - }); - } - } - - fsClient.Unmount(source.ToU8Span()); - fsClient.Unmount(output.ToU8Span()); - }) - { - Name = "GUI.NcaSectionExtractorThread", - IsBackground = true, - }; - extractorThread.Start(); - } - } - - private (Result? result, bool canceled) CopyDirectory(FileSystemClient fs, string sourcePath, string destPath) - { - Result rc = fs.OpenDirectory(out DirectoryHandle sourceHandle, sourcePath.ToU8Span(), OpenDirectoryMode.All); - if (rc.IsFailure()) - { - return (rc, false); - } - - using (sourceHandle) - { - foreach (DirectoryEntryEx entry in fs.EnumerateEntries(sourcePath, "*", SearchOptions.Default)) - { - if (_cancel) - { - return (null, true); - } - - string subSrcPath = PathTools.Normalize(PathTools.Combine(sourcePath, entry.Name)); - string subDstPath = PathTools.Normalize(PathTools.Combine(destPath, entry.Name)); - - if (entry.Type == DirectoryEntryType.Directory) - { - fs.EnsureDirectoryExists(subDstPath); - - (Result? result, bool canceled) = CopyDirectory(fs, subSrcPath, subDstPath); - if (canceled || result.Value.IsFailure()) - { - return (result, canceled); - } - } - - if (entry.Type == DirectoryEntryType.File) - { - fs.CreateOrOverwriteFile(subDstPath, entry.Size); - - rc = CopyFile(fs, subSrcPath, subDstPath); - if (rc.IsFailure()) - { - return (rc, false); - } - } - } - } - - return (Result.Success, false); - } - - public static Result CopyFile(FileSystemClient fs, string sourcePath, string destPath) - { - Result rc = fs.OpenFile(out FileHandle sourceHandle, sourcePath.ToU8Span(), OpenMode.Read); - if (rc.IsFailure()) - { - return rc; - } - - using (sourceHandle) - { - rc = fs.OpenFile(out FileHandle destHandle, destPath.ToU8Span(), OpenMode.Write | OpenMode.AllowAppend); - if (rc.IsFailure()) - { - return rc; - } - - using (destHandle) - { - const int MaxBufferSize = 1024 * 1024; - - rc = fs.GetFileSize(out long fileSize, sourceHandle); - if (rc.IsFailure()) - { - return rc; - } - - int bufferSize = (int)Math.Min(MaxBufferSize, fileSize); - - byte[] buffer = ArrayPool.Shared.Rent(bufferSize); - try - { - for (long offset = 0; offset < fileSize; offset += bufferSize) - { - int toRead = (int)Math.Min(fileSize - offset, bufferSize); - Span buf = buffer.AsSpan(0, toRead); - - rc = fs.ReadFile(out long _, sourceHandle, offset, buf); - if (rc.IsFailure()) - { - return rc; - } - - rc = fs.WriteFile(destHandle, offset, buf, WriteOption.None); - if (rc.IsFailure()) - { - return rc; - } - } - } - finally - { - ArrayPool.Shared.Return(buffer); - } - - rc = fs.FlushFile(destHandle); - if (rc.IsFailure()) - { - return rc; - } - } - } - - return Result.Success; - } - - // - // Events - // - private void OpenSaveUserDir_Clicked(object sender, EventArgs args) - { - var userId = new LibHac.Fs.UserId((ulong)_accountManager.LastOpenedUser.UserId.High, (ulong)_accountManager.LastOpenedUser.UserId.Low); - var saveDataFilter = SaveDataFilter.Make(_applicationData.Id, saveType: default, userId, saveDataId: default, index: default); - - OpenSaveDir(in saveDataFilter); - } - - private void OpenSaveDeviceDir_Clicked(object sender, EventArgs args) - { - var saveDataFilter = SaveDataFilter.Make(_applicationData.Id, SaveDataType.Device, userId: default, saveDataId: default, index: default); - - OpenSaveDir(in saveDataFilter); - } - - private void OpenSaveBcatDir_Clicked(object sender, EventArgs args) - { - var saveDataFilter = SaveDataFilter.Make(_applicationData.Id, SaveDataType.Bcat, userId: default, saveDataId: default, index: default); - - OpenSaveDir(in saveDataFilter); - } - - private void ManageTitleUpdates_Clicked(object sender, EventArgs args) - { - new TitleUpdateWindow(_parent, _virtualFileSystem, _applicationData).Show(); - } - - private void ManageDlc_Clicked(object sender, EventArgs args) - { - new DlcWindow(_virtualFileSystem, _applicationData.IdBaseString, _applicationData).Show(); - } - - private void ManageCheats_Clicked(object sender, EventArgs args) - { - new CheatWindow(_virtualFileSystem, _applicationData.Id, _applicationData.Name, _applicationData.Path).Show(); - } - - private void OpenTitleModDir_Clicked(object sender, EventArgs args) - { - string modsBasePath = ModLoader.GetModsBasePath(); - string titleModsPath = ModLoader.GetApplicationDir(modsBasePath, _applicationData.IdString); - - OpenHelper.OpenFolder(titleModsPath); - } - - private void OpenTitleSdModDir_Clicked(object sender, EventArgs args) - { - string sdModsBasePath = ModLoader.GetSdModsBasePath(); - string titleModsPath = ModLoader.GetApplicationDir(sdModsBasePath, _applicationData.IdString); - - OpenHelper.OpenFolder(titleModsPath); - } - - private void ExtractRomFs_Clicked(object sender, EventArgs args) - { - ExtractSection(NcaSectionType.Data); - } - - private void ExtractExeFs_Clicked(object sender, EventArgs args) - { - ExtractSection(NcaSectionType.Code); - } - - private void ExtractLogo_Clicked(object sender, EventArgs args) - { - ExtractSection(NcaSectionType.Logo); - } - - private void OpenPtcDir_Clicked(object sender, EventArgs args) - { - string ptcDir = System.IO.Path.Combine(AppDataManager.GamesDirPath, _applicationData.IdString, "cache", "cpu"); - - string mainPath = System.IO.Path.Combine(ptcDir, "0"); - string backupPath = System.IO.Path.Combine(ptcDir, "1"); - - if (!Directory.Exists(ptcDir)) - { - Directory.CreateDirectory(ptcDir); - Directory.CreateDirectory(mainPath); - Directory.CreateDirectory(backupPath); - } - - OpenHelper.OpenFolder(ptcDir); - } - - private void OpenShaderCacheDir_Clicked(object sender, EventArgs args) - { - string shaderCacheDir = System.IO.Path.Combine(AppDataManager.GamesDirPath, _applicationData.IdString, "cache", "shader"); - - if (!Directory.Exists(shaderCacheDir)) - { - Directory.CreateDirectory(shaderCacheDir); - } - - OpenHelper.OpenFolder(shaderCacheDir); - } - - private void PurgePtcCache_Clicked(object sender, EventArgs args) - { - DirectoryInfo mainDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _applicationData.IdString, "cache", "cpu", "0")); - DirectoryInfo backupDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _applicationData.IdString, "cache", "cpu", "1")); - - MessageDialog warningDialog = GtkDialog.CreateConfirmationDialog("Warning", $"You are about to queue a PPTC rebuild on the next boot of:\n\n{_applicationData.Name}\n\nAre you sure you want to proceed?"); - - List cacheFiles = new(); - - if (mainDir.Exists) - { - cacheFiles.AddRange(mainDir.EnumerateFiles("*.cache")); - } - - if (backupDir.Exists) - { - cacheFiles.AddRange(backupDir.EnumerateFiles("*.cache")); - } - - if (cacheFiles.Count > 0 && warningDialog.Run() == (int)ResponseType.Yes) - { - foreach (FileInfo file in cacheFiles) - { - try - { - file.Delete(); - } - catch (Exception e) - { - GtkDialog.CreateErrorDialog($"Error purging PPTC cache {file.Name}: {e}"); - } - } - } - - warningDialog.Dispose(); - } - - private void PurgeShaderCache_Clicked(object sender, EventArgs args) - { - DirectoryInfo shaderCacheDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _applicationData.IdString, "cache", "shader")); - - using MessageDialog warningDialog = GtkDialog.CreateConfirmationDialog("Warning", $"You are about to delete the shader cache for :\n\n{_applicationData.Name}\n\nAre you sure you want to proceed?"); - - List oldCacheDirectories = new(); - List newCacheFiles = new(); - - if (shaderCacheDir.Exists) - { - oldCacheDirectories.AddRange(shaderCacheDir.EnumerateDirectories("*")); - newCacheFiles.AddRange(shaderCacheDir.GetFiles("*.toc")); - newCacheFiles.AddRange(shaderCacheDir.GetFiles("*.data")); - } - - if ((oldCacheDirectories.Count > 0 || newCacheFiles.Count > 0) && warningDialog.Run() == (int)ResponseType.Yes) - { - foreach (DirectoryInfo directory in oldCacheDirectories) - { - try - { - directory.Delete(true); - } - catch (Exception e) - { - GtkDialog.CreateErrorDialog($"Error purging shader cache at {directory.Name}: {e}"); - } - } - - foreach (FileInfo file in newCacheFiles) - { - try - { - file.Delete(); - } - catch (Exception e) - { - GtkDialog.CreateErrorDialog($"Error purging shader cache at {file.Name}: {e}"); - } - } - } - } - - private void CreateShortcut_Clicked(object sender, EventArgs args) - { - IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks - ? IntegrityCheckLevel.ErrorOnInvalid - : IntegrityCheckLevel.None; - byte[] appIcon = new ApplicationLibrary(_virtualFileSystem, checkLevel).GetApplicationIcon(_applicationData.Path, ConfigurationState.Instance.System.Language, _applicationData.Id); - ShortcutHelper.CreateAppShortcut(_applicationData.Path, _applicationData.Name, _applicationData.IdString, appIcon); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Widgets/GtkDialog.cs b/src/Ryujinx.Gtk3/UI/Widgets/GtkDialog.cs deleted file mode 100644 index 567f9ad67..000000000 --- a/src/Ryujinx.Gtk3/UI/Widgets/GtkDialog.cs +++ /dev/null @@ -1,114 +0,0 @@ -using Gtk; -using Ryujinx.Common.Logging; -using Ryujinx.UI.Common.Configuration; -using System.Collections.Generic; -using System.Reflection; - -namespace Ryujinx.UI.Widgets -{ - internal class GtkDialog : MessageDialog - { - private static bool _isChoiceDialogOpen; - - private GtkDialog(string title, string mainText, string secondaryText, MessageType messageType = MessageType.Other, ButtonsType buttonsType = ButtonsType.Ok) - : base(null, DialogFlags.Modal, messageType, buttonsType, null) - { - Title = title; - Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Logo_Ryujinx.png"); - Text = mainText; - SecondaryText = secondaryText; - WindowPosition = WindowPosition.Center; - SecondaryUseMarkup = true; - - Response += GtkDialog_Response; - - SetSizeRequest(200, 20); - } - - private void GtkDialog_Response(object sender, ResponseArgs args) - { - Dispose(); - } - - internal static void CreateInfoDialog(string mainText, string secondaryText) - { - new GtkDialog("Ryujinx - Info", mainText, secondaryText, MessageType.Info).Run(); - } - - internal static void CreateUpdaterInfoDialog(string mainText, string secondaryText) - { - new GtkDialog("Ryujinx - Updater", mainText, secondaryText, MessageType.Info).Run(); - } - - internal static MessageDialog CreateWaitingDialog(string mainText, string secondaryText) - { - return new GtkDialog("Ryujinx - Waiting", mainText, secondaryText, MessageType.Info, ButtonsType.None); - } - - internal static void CreateWarningDialog(string mainText, string secondaryText) - { - new GtkDialog("Ryujinx - Warning", mainText, secondaryText, MessageType.Warning).Run(); - } - - internal static void CreateErrorDialog(string errorMessage) - { - Logger.Error?.Print(LogClass.Application, errorMessage); - - new GtkDialog("Ryujinx - Error", "Ryujinx has encountered an error", errorMessage, MessageType.Error).Run(); - } - - internal static MessageDialog CreateConfirmationDialog(string mainText, string secondaryText = "") - { - return new GtkDialog("Ryujinx - Confirmation", mainText, secondaryText, MessageType.Question, ButtonsType.YesNo); - } - - internal static bool CreateChoiceDialog(string title, string mainText, string secondaryText) - { - if (_isChoiceDialogOpen) - { - return false; - } - - _isChoiceDialogOpen = true; - - ResponseType response = (ResponseType)new GtkDialog(title, mainText, secondaryText, MessageType.Question, ButtonsType.YesNo).Run(); - - _isChoiceDialogOpen = false; - - return response == ResponseType.Yes; - } - - internal static ResponseType CreateCustomDialog(string title, string mainText, string secondaryText, Dictionary buttons, MessageType messageType = MessageType.Other) - { - GtkDialog gtkDialog = new(title, mainText, secondaryText, messageType, ButtonsType.None); - - foreach (var button in buttons) - { - gtkDialog.AddButton(button.Value, button.Key); - } - - return (ResponseType)gtkDialog.Run(); - } - - internal static string CreateInputDialog(Window parent, string title, string mainText, uint inputMax) - { - GtkInputDialog gtkDialog = new(parent, title, mainText, inputMax); - ResponseType response = (ResponseType)gtkDialog.Run(); - string responseText = gtkDialog.InputEntry.Text.TrimEnd(); - - gtkDialog.Dispose(); - - if (response == ResponseType.Ok) - { - return responseText; - } - - return ""; - } - - internal static bool CreateExitDialog() - { - return CreateChoiceDialog("Ryujinx - Exit", "Are you sure you want to close Ryujinx?", "All unsaved data will be lost!"); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Widgets/GtkInputDialog.cs b/src/Ryujinx.Gtk3/UI/Widgets/GtkInputDialog.cs deleted file mode 100644 index fd85248b7..000000000 --- a/src/Ryujinx.Gtk3/UI/Widgets/GtkInputDialog.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Gtk; - -namespace Ryujinx.UI.Widgets -{ - public class GtkInputDialog : MessageDialog - { - public Entry InputEntry { get; } - - public GtkInputDialog(Window parent, string title, string mainText, uint inputMax) : base(parent, DialogFlags.Modal | DialogFlags.DestroyWithParent, MessageType.Question, ButtonsType.OkCancel, null) - { - SetDefaultSize(300, 0); - - Title = title; - - Label mainTextLabel = new() - { - Text = mainText, - }; - - InputEntry = new Entry - { - MaxLength = (int)inputMax, - }; - - Label inputMaxTextLabel = new() - { - Text = $"(Max length: {inputMax})", - }; - - ((Box)MessageArea).PackStart(mainTextLabel, true, true, 0); - ((Box)MessageArea).PackStart(InputEntry, true, true, 5); - ((Box)MessageArea).PackStart(inputMaxTextLabel, true, true, 0); - - ShowAll(); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Widgets/ProfileDialog.cs b/src/Ryujinx.Gtk3/UI/Widgets/ProfileDialog.cs deleted file mode 100644 index 3b3e2fbbe..000000000 --- a/src/Ryujinx.Gtk3/UI/Widgets/ProfileDialog.cs +++ /dev/null @@ -1,57 +0,0 @@ -using Gtk; -using Ryujinx.UI.Common.Configuration; -using System; -using System.Reflection; -using GUI = Gtk.Builder.ObjectAttribute; - -namespace Ryujinx.UI.Widgets -{ - public class ProfileDialog : Dialog - { - public string FileName { get; private set; } - -#pragma warning disable CS0649, IDE0044 // Field is never assigned to, Add readonly modifier - [GUI] Entry _profileEntry; - [GUI] Label _errorMessage; -#pragma warning restore CS0649, IDE0044 - - public ProfileDialog() : this(new Builder("Ryujinx.Gtk3.UI.Widgets.ProfileDialog.glade")) { } - - private ProfileDialog(Builder builder) : base(builder.GetRawOwnedObject("_profileDialog")) - { - builder.Autoconnect(this); - Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Logo_Ryujinx.png"); - } - - private void OkToggle_Activated(object sender, EventArgs args) - { - ((ToggleButton)sender).SetStateFlags(StateFlags.Normal, true); - - bool validFileName = true; - - foreach (char invalidChar in System.IO.Path.GetInvalidFileNameChars()) - { - if (_profileEntry.Text.Contains(invalidChar)) - { - validFileName = false; - } - } - - if (validFileName && !string.IsNullOrEmpty(_profileEntry.Text)) - { - FileName = $"{_profileEntry.Text}.json"; - - Respond(ResponseType.Ok); - } - else - { - _errorMessage.Text = "The file name contains invalid characters. Please try again."; - } - } - - private void CancelToggle_Activated(object sender, EventArgs args) - { - Respond(ResponseType.Cancel); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Widgets/ProfileDialog.glade b/src/Ryujinx.Gtk3/UI/Widgets/ProfileDialog.glade deleted file mode 100644 index adaf6608f..000000000 --- a/src/Ryujinx.Gtk3/UI/Widgets/ProfileDialog.glade +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - False - Ryujinx - Profile Dialog - True - center - 400 - dialog - - - - - - False - vertical - 2 - - - False - end - - - OK - True - True - True - - - - False - True - 0 - - - - - Cancel - True - True - True - - - - False - True - 5 - 1 - - - - - False - False - 0 - - - - - True - False - vertical - - - True - False - 10 - 10 - 20 - 10 - Enter a name for the new profile: - - - True - True - 0 - - - - - True - True - 20 - 20 - 20 - - - True - True - 1 - - - - - True - False - start - 20 - 10 - 10 - 10 - - - - - - False - True - 2 - - - - - True - True - 1 - - - - - - diff --git a/src/Ryujinx.Gtk3/UI/Widgets/RawInputToTextEntry.cs b/src/Ryujinx.Gtk3/UI/Widgets/RawInputToTextEntry.cs deleted file mode 100644 index 6470790e8..000000000 --- a/src/Ryujinx.Gtk3/UI/Widgets/RawInputToTextEntry.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Gtk; - -namespace Ryujinx.UI.Widgets -{ - public class RawInputToTextEntry : Entry - { - public void SendKeyPressEvent(object o, KeyPressEventArgs args) - { - base.OnKeyPressEvent(args.Event); - } - - public void SendKeyReleaseEvent(object o, KeyReleaseEventArgs args) - { - base.OnKeyReleaseEvent(args.Event); - } - - public void SendButtonPressEvent(object o, ButtonPressEventArgs args) - { - base.OnButtonPressEvent(args.Event); - } - - public void SendButtonReleaseEvent(object o, ButtonReleaseEventArgs args) - { - base.OnButtonReleaseEvent(args.Event); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Widgets/UserErrorDialog.cs b/src/Ryujinx.Gtk3/UI/Widgets/UserErrorDialog.cs deleted file mode 100644 index f0b55cd8b..000000000 --- a/src/Ryujinx.Gtk3/UI/Widgets/UserErrorDialog.cs +++ /dev/null @@ -1,123 +0,0 @@ -using Gtk; -using Ryujinx.UI.Common; -using Ryujinx.UI.Common.Helper; - -namespace Ryujinx.UI.Widgets -{ - internal class UserErrorDialog : MessageDialog - { - private const string SetupGuideUrl = "https://github.com/Ryujinx/Ryujinx/wiki/Ryujinx-Setup-&-Configuration-Guide"; - private const int OkResponseId = 0; - private const int SetupGuideResponseId = 1; - - private readonly UserError _userError; - - private UserErrorDialog(UserError error) : base(null, DialogFlags.Modal, MessageType.Error, ButtonsType.None, null) - { - _userError = error; - - WindowPosition = WindowPosition.Center; - SecondaryUseMarkup = true; - - Response += UserErrorDialog_Response; - - SetSizeRequest(120, 50); - - AddButton("OK", OkResponseId); - - bool isInSetupGuide = IsCoveredBySetupGuide(error); - - if (isInSetupGuide) - { - AddButton("Open the Setup Guide", SetupGuideResponseId); - } - - string errorCode = GetErrorCode(error); - - SecondaryUseMarkup = true; - - Title = $"Ryujinx error ({errorCode})"; - Text = $"{errorCode}: {GetErrorTitle(error)}"; - SecondaryText = GetErrorDescription(error); - - if (isInSetupGuide) - { - SecondaryText += "\nFor more information on how to fix this error, follow our Setup Guide."; - } - } - - private static string GetErrorCode(UserError error) - { - return $"RYU-{(uint)error:X4}"; - } - - private static string GetErrorTitle(UserError error) - { - return error switch - { - UserError.NoKeys => "Keys not found", - UserError.NoFirmware => "Firmware not found", - UserError.FirmwareParsingFailed => "Firmware parsing error", - UserError.ApplicationNotFound => "Application not found", - UserError.Unknown => "Unknown error", - _ => "Undefined error", - }; - } - - private static string GetErrorDescription(UserError error) - { - return error switch - { - UserError.NoKeys => "Ryujinx was unable to find your 'prod.keys' file", - UserError.NoFirmware => "Ryujinx was unable to find any firmwares installed", - UserError.FirmwareParsingFailed => "Ryujinx was unable to parse the provided firmware. This is usually caused by outdated keys.", - UserError.ApplicationNotFound => "Ryujinx couldn't find a valid application at the given path.", - UserError.Unknown => "An unknown error occured!", - _ => "An undefined error occured! This shouldn't happen, please contact a dev!", - }; - } - - private static bool IsCoveredBySetupGuide(UserError error) - { - return error switch - { - UserError.NoKeys or - UserError.NoFirmware or - UserError.FirmwareParsingFailed => true, - _ => false, - }; - } - - private static string GetSetupGuideUrl(UserError error) - { - if (!IsCoveredBySetupGuide(error)) - { - return null; - } - - return error switch - { - UserError.NoKeys => SetupGuideUrl + "#initial-setup---placement-of-prodkeys", - UserError.NoFirmware => SetupGuideUrl + "#initial-setup-continued---installation-of-firmware", - _ => SetupGuideUrl, - }; - } - - private void UserErrorDialog_Response(object sender, ResponseArgs args) - { - int responseId = (int)args.ResponseId; - - if (responseId == SetupGuideResponseId) - { - OpenHelper.OpenUrl(GetSetupGuideUrl(_userError)); - } - - Dispose(); - } - - public static void CreateUserErrorDialog(UserError error) - { - new UserErrorDialog(error).Run(); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Windows/AboutWindow.Designer.cs b/src/Ryujinx.Gtk3/UI/Windows/AboutWindow.Designer.cs deleted file mode 100644 index fd912ef9a..000000000 --- a/src/Ryujinx.Gtk3/UI/Windows/AboutWindow.Designer.cs +++ /dev/null @@ -1,511 +0,0 @@ -using Gtk; -using Pango; -using Ryujinx.UI.Common.Configuration; -using System.Reflection; - -namespace Ryujinx.UI.Windows -{ - public partial class AboutWindow : Window - { - private Box _mainBox; - private Box _leftBox; - private Box _logoBox; - private Image _ryujinxLogo; - private Box _logoTextBox; - private Label _ryujinxLabel; - private Label _ryujinxPhoneticLabel; - private EventBox _ryujinxLink; - private Label _ryujinxLinkLabel; - private Label _versionLabel; - private Label _disclaimerLabel; - private EventBox _amiiboApiLink; - private Label _amiiboApiLinkLabel; - private Box _socialBox; - private EventBox _patreonEventBox; - private Box _patreonBox; - private Image _patreonLogo; - private Label _patreonLabel; - private EventBox _githubEventBox; - private Box _githubBox; - private Image _githubLogo; - private Label _githubLabel; - private Box _discordBox; - private EventBox _discordEventBox; - private Image _discordLogo; - private Label _discordLabel; - private EventBox _twitterEventBox; - private Box _twitterBox; - private Image _twitterLogo; - private Label _twitterLabel; - private Separator _separator; - private Box _rightBox; - private Label _aboutLabel; - private Label _aboutDescriptionLabel; - private Label _createdByLabel; - private TextView _createdByText; - private EventBox _contributorsEventBox; - private Label _contributorsLinkLabel; - private Label _patreonNamesLabel; - private ScrolledWindow _patreonNamesScrolled; - private TextView _patreonNamesText; - private EventBox _changelogEventBox; - private Label _changelogLinkLabel; - - private void InitializeComponent() - { - - // - // AboutWindow - // - CanFocus = false; - Resizable = false; - Modal = true; - WindowPosition = WindowPosition.Center; - DefaultWidth = 800; - DefaultHeight = 450; - TypeHint = Gdk.WindowTypeHint.Dialog; - - // - // _mainBox - // - _mainBox = new Box(Orientation.Horizontal, 0); - - // - // _leftBox - // - _leftBox = new Box(Orientation.Vertical, 0) - { - Margin = 15, - MarginStart = 30, - MarginEnd = 0, - }; - - // - // _logoBox - // - _logoBox = new Box(Orientation.Horizontal, 0); - - // - // _ryujinxLogo - // - _ryujinxLogo = new Image(new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Logo_Ryujinx.png", 100, 100)) - { - Margin = 10, - MarginStart = 15, - }; - - // - // _logoTextBox - // - _logoTextBox = new Box(Orientation.Vertical, 0); - - // - // _ryujinxLabel - // - _ryujinxLabel = new Label("Ryujinx") - { - MarginTop = 15, - Justify = Justification.Center, - Attributes = new AttrList(), - }; - _ryujinxLabel.Attributes.Insert(new Pango.AttrScale(2.7f)); - - // - // _ryujinxPhoneticLabel - // - _ryujinxPhoneticLabel = new Label("(REE-YOU-JINX)") - { - Justify = Justification.Center, - }; - - // - // _ryujinxLink - // - _ryujinxLink = new EventBox() - { - Margin = 5 - }; - _ryujinxLink.ButtonPressEvent += RyujinxButton_Pressed; - - // - // _ryujinxLinkLabel - // - _ryujinxLinkLabel = new Label("www.ryujinx.org") - { - TooltipText = "Click to open the Ryujinx website in your default browser.", - Justify = Justification.Center, - Attributes = new AttrList(), - }; - _ryujinxLinkLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single)); - - // - // _versionLabel - // - _versionLabel = new Label(Program.Version) - { - Expand = true, - Justify = Justification.Center, - Margin = 5, - }; - - // - // _changelogEventBox - // - _changelogEventBox = new EventBox(); - _changelogEventBox.ButtonPressEvent += ChangelogButton_Pressed; - - // - // _changelogLinkLabel - // - _changelogLinkLabel = new Label("View Changelog on GitHub") - { - TooltipText = "Click to open the changelog for this version in your default browser.", - Justify = Justification.Center, - Attributes = new AttrList(), - }; - _changelogLinkLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single)); - - // - // _disclaimerLabel - // - _disclaimerLabel = new Label("Ryujinx is not affiliated with Nintendo™,\nor any of its partners, in any way.") - { - Expand = true, - Justify = Justification.Center, - Margin = 5, - Attributes = new AttrList(), - }; - _disclaimerLabel.Attributes.Insert(new Pango.AttrScale(0.8f)); - - // - // _amiiboApiLink - // - _amiiboApiLink = new EventBox() - { - Margin = 5, - }; - _amiiboApiLink.ButtonPressEvent += AmiiboApiButton_Pressed; - - // - // _amiiboApiLinkLabel - // - _amiiboApiLinkLabel = new Label("AmiiboAPI (www.amiiboapi.com) is used\nin our Amiibo emulation.") - { - TooltipText = "Click to open the AmiiboAPI website in your default browser.", - Justify = Justification.Center, - Attributes = new AttrList(), - }; - _amiiboApiLinkLabel.Attributes.Insert(new Pango.AttrScale(0.9f)); - - // - // _socialBox - // - _socialBox = new Box(Orientation.Horizontal, 0) - { - Margin = 25, - MarginBottom = 10, - }; - - // - // _patreonEventBox - // - _patreonEventBox = new EventBox() - { - TooltipText = "Click to open the Ryujinx Patreon page in your default browser.", - }; - _patreonEventBox.ButtonPressEvent += PatreonButton_Pressed; - - // - // _patreonBox - // - _patreonBox = new Box(Orientation.Vertical, 0); - - // - // _patreonLogo - // - _patreonLogo = new Image(new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Logo_Patreon_Light.png", 30, 30)) - { - Margin = 10, - }; - - // - // _patreonLabel - // - _patreonLabel = new Label("Patreon") - { - Justify = Justification.Center, - }; - - // - // _githubEventBox - // - _githubEventBox = new EventBox() - { - TooltipText = "Click to open the Ryujinx GitHub page in your default browser.", - }; - _githubEventBox.ButtonPressEvent += GitHubButton_Pressed; - - // - // _githubBox - // - _githubBox = new Box(Orientation.Vertical, 0); - - // - // _githubLogo - // - _githubLogo = new Image(new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Logo_GitHub_Light.png", 30, 30)) - { - Margin = 10, - }; - - // - // _githubLabel - // - _githubLabel = new Label("GitHub") - { - Justify = Justification.Center, - }; - - // - // _discordBox - // - _discordBox = new Box(Orientation.Vertical, 0); - - // - // _discordEventBox - // - _discordEventBox = new EventBox() - { - TooltipText = "Click to open an invite to the Ryujinx Discord server in your default browser.", - }; - _discordEventBox.ButtonPressEvent += DiscordButton_Pressed; - - // - // _discordLogo - // - _discordLogo = new Image(new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Logo_Discord_Light.png", 30, 30)) - { - Margin = 10, - }; - - // - // _discordLabel - // - _discordLabel = new Label("Discord") - { - Justify = Justification.Center, - }; - - // - // _twitterEventBox - // - _twitterEventBox = new EventBox() - { - TooltipText = "Click to open the Ryujinx Twitter page in your default browser.", - }; - _twitterEventBox.ButtonPressEvent += TwitterButton_Pressed; - - // - // _twitterBox - // - _twitterBox = new Box(Orientation.Vertical, 0); - - // - // _twitterLogo - // - _twitterLogo = new Image(new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Logo_Twitter_Light.png", 30, 30)) - { - Margin = 10, - }; - - // - // _twitterLabel - // - _twitterLabel = new Label("Twitter") - { - Justify = Justification.Center, - }; - - // - // _separator - // - _separator = new Separator(Orientation.Vertical) - { - Margin = 15, - }; - - // - // _rightBox - // - _rightBox = new Box(Orientation.Vertical, 0) - { - Margin = 15, - MarginTop = 40, - }; - - // - // _aboutLabel - // - _aboutLabel = new Label("About :") - { - Halign = Align.Start, - Attributes = new AttrList(), - }; - _aboutLabel.Attributes.Insert(new Pango.AttrWeight(Weight.Bold)); - _aboutLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single)); - - // - // _aboutDescriptionLabel - // - _aboutDescriptionLabel = new Label("Ryujinx is an emulator for the Nintendo Switch™.\n" + - "Please support us on Patreon.\n" + - "Get all the latest news on our Twitter or Discord.\n" + - "Developers interested in contributing can find out more on our GitHub or Discord.") - { - Margin = 15, - Halign = Align.Start, - }; - - // - // _createdByLabel - // - _createdByLabel = new Label("Maintained by :") - { - Halign = Align.Start, - Attributes = new AttrList(), - }; - _createdByLabel.Attributes.Insert(new Pango.AttrWeight(Weight.Bold)); - _createdByLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single)); - - // - // _createdByText - // - _createdByText = new TextView() - { - WrapMode = Gtk.WrapMode.Word, - Editable = false, - CursorVisible = false, - Margin = 15, - MarginEnd = 30, - }; - _createdByText.Buffer.Text = "gdkchan, Ac_K, Thog, rip in peri peri, LDj3SNuD, emmaus, Thealexbarney, Xpl0itR, GoffyDude, »jD« and more..."; - - // - // _contributorsEventBox - // - _contributorsEventBox = new EventBox(); - _contributorsEventBox.ButtonPressEvent += ContributorsButton_Pressed; - - // - // _contributorsLinkLabel - // - _contributorsLinkLabel = new Label("See All Contributors...") - { - TooltipText = "Click to open the Contributors page in your default browser.", - MarginEnd = 30, - Halign = Align.End, - Attributes = new AttrList(), - }; - _contributorsLinkLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single)); - - // - // _patreonNamesLabel - // - _patreonNamesLabel = new Label("Supported on Patreon by :") - { - Halign = Align.Start, - Attributes = new AttrList(), - }; - _patreonNamesLabel.Attributes.Insert(new Pango.AttrWeight(Weight.Bold)); - _patreonNamesLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single)); - - // - // _patreonNamesScrolled - // - _patreonNamesScrolled = new ScrolledWindow() - { - Margin = 15, - MarginEnd = 30, - Expand = true, - ShadowType = ShadowType.In, - }; - _patreonNamesScrolled.SetPolicy(PolicyType.Never, PolicyType.Automatic); - - // - // _patreonNamesText - // - _patreonNamesText = new TextView() - { - WrapMode = Gtk.WrapMode.Word, - }; - _patreonNamesText.Buffer.Text = "Loading..."; - _patreonNamesText.SetProperty("editable", new GLib.Value(false)); - - ShowComponent(); - } - - private void ShowComponent() - { - _logoBox.Add(_ryujinxLogo); - - _ryujinxLink.Add(_ryujinxLinkLabel); - - _logoTextBox.Add(_ryujinxLabel); - _logoTextBox.Add(_ryujinxPhoneticLabel); - _logoTextBox.Add(_ryujinxLink); - - _logoBox.Add(_logoTextBox); - - _amiiboApiLink.Add(_amiiboApiLinkLabel); - - _patreonBox.Add(_patreonLogo); - _patreonBox.Add(_patreonLabel); - _patreonEventBox.Add(_patreonBox); - - _githubBox.Add(_githubLogo); - _githubBox.Add(_githubLabel); - _githubEventBox.Add(_githubBox); - - _discordBox.Add(_discordLogo); - _discordBox.Add(_discordLabel); - _discordEventBox.Add(_discordBox); - - _twitterBox.Add(_twitterLogo); - _twitterBox.Add(_twitterLabel); - _twitterEventBox.Add(_twitterBox); - - _socialBox.Add(_patreonEventBox); - _socialBox.Add(_githubEventBox); - _socialBox.Add(_discordEventBox); - _socialBox.Add(_twitterEventBox); - - _changelogEventBox.Add(_changelogLinkLabel); - - _leftBox.Add(_logoBox); - _leftBox.Add(_versionLabel); - _leftBox.Add(_changelogEventBox); - _leftBox.Add(_disclaimerLabel); - _leftBox.Add(_amiiboApiLink); - _leftBox.Add(_socialBox); - - _contributorsEventBox.Add(_contributorsLinkLabel); - _patreonNamesScrolled.Add(_patreonNamesText); - - _rightBox.Add(_aboutLabel); - _rightBox.Add(_aboutDescriptionLabel); - _rightBox.Add(_createdByLabel); - _rightBox.Add(_createdByText); - _rightBox.Add(_contributorsEventBox); - _rightBox.Add(_patreonNamesLabel); - _rightBox.Add(_patreonNamesScrolled); - - _mainBox.Add(_leftBox); - _mainBox.Add(_separator); - _mainBox.Add(_rightBox); - - Add(_mainBox); - - ShowAll(); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Windows/AboutWindow.cs b/src/Ryujinx.Gtk3/UI/Windows/AboutWindow.cs deleted file mode 100644 index f4bb533c3..000000000 --- a/src/Ryujinx.Gtk3/UI/Windows/AboutWindow.cs +++ /dev/null @@ -1,85 +0,0 @@ -using Gtk; -using Ryujinx.Common.Utilities; -using Ryujinx.UI.Common.Helper; -using System.Net.Http; -using System.Net.NetworkInformation; -using System.Reflection; -using System.Threading.Tasks; - -namespace Ryujinx.UI.Windows -{ - public partial class AboutWindow : Window - { - public AboutWindow() : base($"Ryujinx {Program.Version} - About") - { - Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(OpenHelper)), "Ryujinx.UI.Common.Resources.Logo_Ryujinx.png"); - InitializeComponent(); - - _ = DownloadPatronsJson(); - } - - private async Task DownloadPatronsJson() - { - if (!NetworkInterface.GetIsNetworkAvailable()) - { - _patreonNamesText.Buffer.Text = "Connection Error."; - } - - HttpClient httpClient = new(); - - try - { - string patreonJsonString = await httpClient.GetStringAsync("https://patreon.ryujinx.org/"); - - _patreonNamesText.Buffer.Text = string.Join(", ", JsonHelper.Deserialize(patreonJsonString, CommonJsonContext.Default.StringArray)); - } - catch - { - _patreonNamesText.Buffer.Text = "API Error."; - } - } - - // - // Events - // - private void RyujinxButton_Pressed(object sender, ButtonPressEventArgs args) - { - OpenHelper.OpenUrl("https://ryujinx.org"); - } - - private void AmiiboApiButton_Pressed(object sender, ButtonPressEventArgs args) - { - OpenHelper.OpenUrl("https://amiiboapi.com"); - } - - private void PatreonButton_Pressed(object sender, ButtonPressEventArgs args) - { - OpenHelper.OpenUrl("https://www.patreon.com/ryujinx"); - } - - private void GitHubButton_Pressed(object sender, ButtonPressEventArgs args) - { - OpenHelper.OpenUrl("https://github.com/Ryujinx/Ryujinx"); - } - - private void DiscordButton_Pressed(object sender, ButtonPressEventArgs args) - { - OpenHelper.OpenUrl("https://discordapp.com/invite/N2FmfVc"); - } - - private void TwitterButton_Pressed(object sender, ButtonPressEventArgs args) - { - OpenHelper.OpenUrl("https://twitter.com/RyujinxEmu"); - } - - private void ContributorsButton_Pressed(object sender, ButtonPressEventArgs args) - { - OpenHelper.OpenUrl("https://github.com/Ryujinx/Ryujinx/graphs/contributors?type=a"); - } - - private void ChangelogButton_Pressed(object sender, ButtonPressEventArgs args) - { - OpenHelper.OpenUrl("https://github.com/Ryujinx/Ryujinx/wiki/Changelog#ryujinx-changelog"); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Windows/AmiiboWindow.Designer.cs b/src/Ryujinx.Gtk3/UI/Windows/AmiiboWindow.Designer.cs deleted file mode 100644 index 3bf73318f..000000000 --- a/src/Ryujinx.Gtk3/UI/Windows/AmiiboWindow.Designer.cs +++ /dev/null @@ -1,190 +0,0 @@ -using Gtk; - -namespace Ryujinx.UI.Windows -{ - public partial class AmiiboWindow : Window - { - private Box _mainBox; - private ButtonBox _buttonBox; - private Button _scanButton; - private Button _cancelButton; - private CheckButton _randomUuidCheckBox; - private Box _amiiboBox; - private Box _amiiboHeadBox; - private Box _amiiboSeriesBox; - private Label _amiiboSeriesLabel; - private ComboBoxText _amiiboSeriesComboBox; - private Box _amiiboCharsBox; - private Label _amiiboCharsLabel; - private ComboBoxText _amiiboCharsComboBox; - private CheckButton _showAllCheckBox; - private Image _amiiboImage; - private Label _gameUsageLabel; - - private void InitializeComponent() - { - // - // AmiiboWindow - // - CanFocus = false; - Resizable = false; - Modal = true; - WindowPosition = WindowPosition.Center; - DefaultWidth = 600; - DefaultHeight = 470; - TypeHint = Gdk.WindowTypeHint.Dialog; - - // - // _mainBox - // - _mainBox = new Box(Orientation.Vertical, 2); - - // - // _buttonBox - // - _buttonBox = new ButtonBox(Orientation.Horizontal) - { - Margin = 20, - LayoutStyle = ButtonBoxStyle.End, - }; - - // - // _scanButton - // - _scanButton = new Button() - { - Label = "Scan It!", - CanFocus = true, - ReceivesDefault = true, - MarginStart = 10, - }; - _scanButton.Clicked += ScanButton_Pressed; - - // - // _randomUuidCheckBox - // - _randomUuidCheckBox = new CheckButton() - { - Label = "Hack: Use Random Tag Uuid", - TooltipText = "This allows multiple scans of a single Amiibo.\n(used in The Legend of Zelda: Breath of the Wild)", - }; - - // - // _cancelButton - // - _cancelButton = new Button() - { - Label = "Cancel", - CanFocus = true, - ReceivesDefault = true, - MarginStart = 10, - }; - _cancelButton.Clicked += CancelButton_Pressed; - - // - // _amiiboBox - // - _amiiboBox = new Box(Orientation.Vertical, 0); - - // - // _amiiboHeadBox - // - _amiiboHeadBox = new Box(Orientation.Horizontal, 0) - { - Margin = 20, - Hexpand = true, - }; - - // - // _amiiboSeriesBox - // - _amiiboSeriesBox = new Box(Orientation.Horizontal, 0) - { - Hexpand = true, - }; - - // - // _amiiboSeriesLabel - // - _amiiboSeriesLabel = new Label("Amiibo Series:"); - - // - // _amiiboSeriesComboBox - // - _amiiboSeriesComboBox = new ComboBoxText(); - - // - // _amiiboCharsBox - // - _amiiboCharsBox = new Box(Orientation.Horizontal, 0) - { - Hexpand = true, - }; - - // - // _amiiboCharsLabel - // - _amiiboCharsLabel = new Label("Character:"); - - // - // _amiiboCharsComboBox - // - _amiiboCharsComboBox = new ComboBoxText(); - - // - // _showAllCheckBox - // - _showAllCheckBox = new CheckButton() - { - Label = "Show All Amiibo", - }; - - // - // _amiiboImage - // - _amiiboImage = new Image() - { - HeightRequest = 350, - WidthRequest = 350, - }; - - // - // _gameUsageLabel - // - _gameUsageLabel = new Label("") - { - MarginTop = 20, - }; - - ShowComponent(); - } - - private void ShowComponent() - { - _buttonBox.Add(_showAllCheckBox); - _buttonBox.Add(_randomUuidCheckBox); - _buttonBox.Add(_scanButton); - _buttonBox.Add(_cancelButton); - - _amiiboSeriesBox.Add(_amiiboSeriesLabel); - _amiiboSeriesBox.Add(_amiiboSeriesComboBox); - - _amiiboCharsBox.Add(_amiiboCharsLabel); - _amiiboCharsBox.Add(_amiiboCharsComboBox); - - _amiiboHeadBox.Add(_amiiboSeriesBox); - _amiiboHeadBox.Add(_amiiboCharsBox); - - _amiiboBox.PackStart(_amiiboHeadBox, true, true, 0); - _amiiboBox.PackEnd(_gameUsageLabel, false, false, 0); - _amiiboBox.PackEnd(_amiiboImage, false, false, 0); - - _mainBox.Add(_amiiboBox); - _mainBox.PackEnd(_buttonBox, false, false, 0); - - Add(_mainBox); - - ShowAll(); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Windows/AmiiboWindow.cs b/src/Ryujinx.Gtk3/UI/Windows/AmiiboWindow.cs deleted file mode 100644 index d8c0b0c0d..000000000 --- a/src/Ryujinx.Gtk3/UI/Windows/AmiiboWindow.cs +++ /dev/null @@ -1,438 +0,0 @@ -using Gdk; -using Gtk; -using Ryujinx.Common; -using Ryujinx.Common.Configuration; -using Ryujinx.Common.Logging; -using Ryujinx.Common.Utilities; -using Ryujinx.UI.Common.Configuration; -using Ryujinx.UI.Common.Models.Amiibo; -using Ryujinx.UI.Widgets; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net.Http; -using System.Reflection; -using System.Text; -using System.Text.Json; -using System.Threading.Tasks; -using Window = Gtk.Window; - -namespace Ryujinx.UI.Windows -{ - public partial class AmiiboWindow : Window - { - private const string DefaultJson = "{ \"amiibo\": [] }"; - - public string AmiiboId { get; private set; } - - public int DeviceId { get; set; } - public string TitleId { get; set; } - public string LastScannedAmiiboId { get; set; } - public bool LastScannedAmiiboShowAll { get; set; } - - public ResponseType Response { get; private set; } - - public bool UseRandomUuid - { - get - { - return _randomUuidCheckBox.Active; - } - } - - private readonly HttpClient _httpClient; - private readonly string _amiiboJsonPath; - - private readonly byte[] _amiiboLogoBytes; - - private List _amiiboList; - - private static readonly AmiiboJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - - public AmiiboWindow() : base($"Ryujinx {Program.Version} - Amiibo") - { - Icon = new Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Logo_Ryujinx.png"); - - InitializeComponent(); - - _httpClient = new HttpClient - { - Timeout = TimeSpan.FromSeconds(30), - }; - - Directory.CreateDirectory(System.IO.Path.Join(AppDataManager.BaseDirPath, "system", "amiibo")); - - _amiiboJsonPath = System.IO.Path.Join(AppDataManager.BaseDirPath, "system", "amiibo", "Amiibo.json"); - _amiiboList = new List(); - - _amiiboLogoBytes = EmbeddedResources.Read("Ryujinx.UI.Common/Resources/Logo_Amiibo.png"); - _amiiboImage.Pixbuf = new Pixbuf(_amiiboLogoBytes); - - _scanButton.Sensitive = false; - _randomUuidCheckBox.Sensitive = false; - - _ = LoadContentAsync(); - } - - private static bool TryGetAmiiboJson(string json, out AmiiboJson amiiboJson) - { - if (string.IsNullOrEmpty(json)) - { - amiiboJson = JsonHelper.Deserialize(DefaultJson, _serializerContext.AmiiboJson); - - return false; - } - - try - { - amiiboJson = JsonHelper.Deserialize(json, _serializerContext.AmiiboJson); - - return true; - } - catch (JsonException exception) - { - Logger.Error?.Print(LogClass.Application, $"Unable to deserialize amiibo data: {exception}"); - amiiboJson = JsonHelper.Deserialize(DefaultJson, _serializerContext.AmiiboJson); - - return false; - } - } - - private async Task GetMostRecentAmiiboListOrDefaultJson() - { - bool localIsValid = false; - bool remoteIsValid = false; - AmiiboJson amiiboJson = new(); - - try - { - try - { - if (File.Exists(_amiiboJsonPath)) - { - localIsValid = TryGetAmiiboJson(await File.ReadAllTextAsync(_amiiboJsonPath), out amiiboJson); - } - } - catch (Exception exception) - { - Logger.Warning?.Print(LogClass.Application, $"Unable to read data from '{_amiiboJsonPath}': {exception}"); - } - - if (!localIsValid || await NeedsUpdate(amiiboJson.LastUpdated)) - { - remoteIsValid = TryGetAmiiboJson(await DownloadAmiiboJson(), out amiiboJson); - } - } - catch (Exception exception) - { - if (!(localIsValid || remoteIsValid)) - { - Logger.Error?.Print(LogClass.Application, $"Couldn't get valid amiibo data: {exception}"); - - // Neither local or remote files are valid JSON, close window. - ShowInfoDialog(); - Close(); - } - else if (!remoteIsValid) - { - Logger.Warning?.Print(LogClass.Application, $"Couldn't update amiibo data: {exception}"); - - // Only the local file is valid, the local one should be used - // but the user should be warned. - ShowInfoDialog(); - } - } - - return amiiboJson; - } - - private async Task LoadContentAsync() - { - AmiiboJson amiiboJson = await GetMostRecentAmiiboListOrDefaultJson(); - - _amiiboList = amiiboJson.Amiibo.OrderBy(amiibo => amiibo.AmiiboSeries).ToList(); - - if (LastScannedAmiiboShowAll) - { - _showAllCheckBox.Click(); - } - - ParseAmiiboData(); - - _showAllCheckBox.Clicked += ShowAllCheckBox_Clicked; - } - - private void ParseAmiiboData() - { - List comboxItemList = new(); - - for (int i = 0; i < _amiiboList.Count; i++) - { - if (!comboxItemList.Contains(_amiiboList[i].AmiiboSeries)) - { - if (!_showAllCheckBox.Active) - { - foreach (var game in _amiiboList[i].GamesSwitch) - { - if (game != null) - { - if (game.GameId.Contains(TitleId)) - { - comboxItemList.Add(_amiiboList[i].AmiiboSeries); - _amiiboSeriesComboBox.Append(_amiiboList[i].AmiiboSeries, _amiiboList[i].AmiiboSeries); - - break; - } - } - } - } - else - { - comboxItemList.Add(_amiiboList[i].AmiiboSeries); - _amiiboSeriesComboBox.Append(_amiiboList[i].AmiiboSeries, _amiiboList[i].AmiiboSeries); - } - } - } - - _amiiboSeriesComboBox.Changed += SeriesComboBox_Changed; - _amiiboCharsComboBox.Changed += CharacterComboBox_Changed; - - if (LastScannedAmiiboId != "") - { - SelectLastScannedAmiibo(); - } - else - { - _amiiboSeriesComboBox.Active = 0; - } - } - - private void SelectLastScannedAmiibo() - { - bool isSet = _amiiboSeriesComboBox.SetActiveId(_amiiboList.Find(amiibo => amiibo.Head + amiibo.Tail == LastScannedAmiiboId).AmiiboSeries); - isSet = _amiiboCharsComboBox.SetActiveId(LastScannedAmiiboId); - - if (isSet == false) - { - _amiiboSeriesComboBox.Active = 0; - } - } - - private async Task NeedsUpdate(DateTime oldLastModified) - { - try - { - HttpResponseMessage response = await _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, "https://amiibo.ryujinx.org/")); - - if (response.IsSuccessStatusCode) - { - return response.Content.Headers.LastModified != oldLastModified; - } - } - catch (HttpRequestException exception) - { - Logger.Error?.Print(LogClass.Application, $"Unable to check for amiibo data updates: {exception}"); - } - - return false; - } - - private async Task DownloadAmiiboJson() - { - try - { - HttpResponseMessage response = await _httpClient.GetAsync("https://amiibo.ryujinx.org/"); - - if (response.IsSuccessStatusCode) - { - string amiiboJsonString = await response.Content.ReadAsStringAsync(); - - try - { - using FileStream dlcJsonStream = File.Create(_amiiboJsonPath, 4096, FileOptions.WriteThrough); - dlcJsonStream.Write(Encoding.UTF8.GetBytes(amiiboJsonString)); - } - catch (Exception exception) - { - Logger.Warning?.Print(LogClass.Application, $"Couldn't write amiibo data to file '{_amiiboJsonPath}: {exception}'"); - } - - return amiiboJsonString; - } - - Logger.Error?.Print(LogClass.Application, $"Failed to download amiibo data. Response status code: {response.StatusCode}"); - } - catch (HttpRequestException exception) - { - Logger.Error?.Print(LogClass.Application, $"Failed to request amiibo data: {exception}"); - } - - GtkDialog.CreateInfoDialog("Amiibo API", "An error occured while fetching information from the API."); - - return null; - } - - private async Task UpdateAmiiboPreview(string imageUrl) - { - HttpResponseMessage response = await _httpClient.GetAsync(imageUrl); - - if (response.IsSuccessStatusCode) - { - byte[] amiiboPreviewBytes = await response.Content.ReadAsByteArrayAsync(); - Pixbuf amiiboPreview = new(amiiboPreviewBytes); - - float ratio = Math.Min((float)_amiiboImage.AllocatedWidth / amiiboPreview.Width, - (float)_amiiboImage.AllocatedHeight / amiiboPreview.Height); - - int resizeHeight = (int)(amiiboPreview.Height * ratio); - int resizeWidth = (int)(amiiboPreview.Width * ratio); - - _amiiboImage.Pixbuf = amiiboPreview.ScaleSimple(resizeWidth, resizeHeight, InterpType.Bilinear); - } - else - { - Logger.Error?.Print(LogClass.Application, $"Failed to get amiibo preview. Response status code: {response.StatusCode}"); - } - } - - private static void ShowInfoDialog() - { - GtkDialog.CreateInfoDialog("Amiibo API", "Unable to connect to Amiibo API server. The service may be down or you may need to verify your internet connection is online."); - } - - // - // Events - // - private void SeriesComboBox_Changed(object sender, EventArgs args) - { - _amiiboCharsComboBox.Changed -= CharacterComboBox_Changed; - - _amiiboCharsComboBox.RemoveAll(); - - List amiiboSortedList = _amiiboList.Where(amiibo => amiibo.AmiiboSeries == _amiiboSeriesComboBox.ActiveId).OrderBy(amiibo => amiibo.Name).ToList(); - - List comboxItemList = new(); - - for (int i = 0; i < amiiboSortedList.Count; i++) - { - if (!comboxItemList.Contains(amiiboSortedList[i].Head + amiiboSortedList[i].Tail)) - { - if (!_showAllCheckBox.Active) - { - foreach (var game in amiiboSortedList[i].GamesSwitch) - { - if (game != null) - { - if (game.GameId.Contains(TitleId)) - { - comboxItemList.Add(amiiboSortedList[i].Head + amiiboSortedList[i].Tail); - _amiiboCharsComboBox.Append(amiiboSortedList[i].Head + amiiboSortedList[i].Tail, amiiboSortedList[i].Name); - - break; - } - } - } - } - else - { - comboxItemList.Add(amiiboSortedList[i].Head + amiiboSortedList[i].Tail); - _amiiboCharsComboBox.Append(amiiboSortedList[i].Head + amiiboSortedList[i].Tail, amiiboSortedList[i].Name); - } - } - } - - _amiiboCharsComboBox.Changed += CharacterComboBox_Changed; - - _amiiboCharsComboBox.Active = 0; - - _scanButton.Sensitive = true; - _randomUuidCheckBox.Sensitive = true; - } - - private void CharacterComboBox_Changed(object sender, EventArgs args) - { - AmiiboId = _amiiboCharsComboBox.ActiveId; - - _amiiboImage.Pixbuf = new Pixbuf(_amiiboLogoBytes); - - string imageUrl = _amiiboList.Find(amiibo => amiibo.Head + amiibo.Tail == _amiiboCharsComboBox.ActiveId).Image; - - var usageStringBuilder = new StringBuilder(); - - for (int i = 0; i < _amiiboList.Count; i++) - { - if (_amiiboList[i].Head + _amiiboList[i].Tail == _amiiboCharsComboBox.ActiveId) - { - bool writable = false; - - foreach (var item in _amiiboList[i].GamesSwitch) - { - if (item.GameId.Contains(TitleId)) - { - foreach (AmiiboApiUsage usageItem in item.AmiiboUsage) - { - usageStringBuilder.Append(Environment.NewLine); - usageStringBuilder.Append($"- {usageItem.Usage.Replace("/", Environment.NewLine + "-")}"); - - writable = usageItem.Write; - } - } - } - - if (usageStringBuilder.Length == 0) - { - usageStringBuilder.Append("Unknown."); - } - - _gameUsageLabel.Text = $"Usage{(writable ? " (Writable)" : "")} : {usageStringBuilder}"; - } - } - - _ = UpdateAmiiboPreview(imageUrl); - } - - private void ShowAllCheckBox_Clicked(object sender, EventArgs e) - { - _amiiboImage.Pixbuf = new Pixbuf(_amiiboLogoBytes); - - _amiiboSeriesComboBox.Changed -= SeriesComboBox_Changed; - _amiiboCharsComboBox.Changed -= CharacterComboBox_Changed; - - _amiiboSeriesComboBox.RemoveAll(); - _amiiboCharsComboBox.RemoveAll(); - - _scanButton.Sensitive = false; - _randomUuidCheckBox.Sensitive = false; - - new Task(ParseAmiiboData).Start(); - } - - private void ScanButton_Pressed(object sender, EventArgs args) - { - LastScannedAmiiboShowAll = _showAllCheckBox.Active; - - Response = ResponseType.Ok; - - Close(); - } - - private void CancelButton_Pressed(object sender, EventArgs args) - { - AmiiboId = ""; - LastScannedAmiiboId = ""; - LastScannedAmiiboShowAll = false; - - Response = ResponseType.Cancel; - - Close(); - } - - protected override void Dispose(bool disposing) - { - _httpClient.Dispose(); - - base.Dispose(disposing); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Windows/AvatarWindow.cs b/src/Ryujinx.Gtk3/UI/Windows/AvatarWindow.cs deleted file mode 100644 index fcd960df0..000000000 --- a/src/Ryujinx.Gtk3/UI/Windows/AvatarWindow.cs +++ /dev/null @@ -1,298 +0,0 @@ -using Gtk; -using LibHac.Common; -using LibHac.Fs; -using LibHac.Fs.Fsa; -using LibHac.FsSystem; -using LibHac.Ncm; -using LibHac.Tools.FsSystem; -using LibHac.Tools.FsSystem.NcaUtils; -using Ryujinx.Common.Memory; -using Ryujinx.HLE.FileSystem; -using Ryujinx.UI.Common.Configuration; -using SkiaSharp; -using System; -using System.Buffers.Binary; -using System.Collections.Generic; -using System.IO; -using System.Reflection; -using System.Runtime.InteropServices; - -namespace Ryujinx.UI.Windows -{ - public class AvatarWindow : Window - { - public byte[] SelectedProfileImage; - public bool NewUser; - - private static readonly Dictionary _avatarDict = new(); - - private readonly ListStore _listStore; - private readonly IconView _iconView; - private readonly Button _setBackgroungColorButton; - private Gdk.RGBA _backgroundColor; - - public AvatarWindow() : base($"Ryujinx {Program.Version} - Manage Accounts - Avatar") - { - Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Logo_Ryujinx.png"); - - CanFocus = false; - Resizable = false; - Modal = true; - TypeHint = Gdk.WindowTypeHint.Dialog; - - SetDefaultSize(740, 400); - SetPosition(WindowPosition.Center); - - Box vbox = new(Orientation.Vertical, 0); - Add(vbox); - - ScrolledWindow scrolledWindow = new() - { - ShadowType = ShadowType.EtchedIn, - }; - scrolledWindow.SetPolicy(PolicyType.Automatic, PolicyType.Automatic); - - Box hbox = new(Orientation.Horizontal, 0); - - Button chooseButton = new() - { - Label = "Choose", - CanFocus = true, - ReceivesDefault = true, - }; - chooseButton.Clicked += ChooseButton_Pressed; - - _setBackgroungColorButton = new Button() - { - Label = "Set Background Color", - CanFocus = true, - }; - _setBackgroungColorButton.Clicked += SetBackgroungColorButton_Pressed; - - _backgroundColor.Red = 1; - _backgroundColor.Green = 1; - _backgroundColor.Blue = 1; - _backgroundColor.Alpha = 1; - - Button closeButton = new() - { - Label = "Close", - CanFocus = true, - }; - closeButton.Clicked += CloseButton_Pressed; - - vbox.PackStart(scrolledWindow, true, true, 0); - hbox.PackStart(chooseButton, true, true, 0); - hbox.PackStart(_setBackgroungColorButton, true, true, 0); - hbox.PackStart(closeButton, true, true, 0); - vbox.PackStart(hbox, false, false, 0); - - _listStore = new ListStore(typeof(string), typeof(Gdk.Pixbuf)); - _listStore.SetSortColumnId(0, SortType.Ascending); - - _iconView = new IconView(_listStore) - { - ItemWidth = 64, - ItemPadding = 10, - PixbufColumn = 1, - }; - - _iconView.SelectionChanged += IconView_SelectionChanged; - - scrolledWindow.Add(_iconView); - - _iconView.GrabFocus(); - - ProcessAvatars(); - - ShowAll(); - } - - public static void PreloadAvatars(ContentManager contentManager, VirtualFileSystem virtualFileSystem) - { - if (_avatarDict.Count > 0) - { - return; - } - - string contentPath = contentManager.GetInstalledContentPath(0x010000000000080A, StorageId.BuiltInSystem, NcaContentType.Data); - string avatarPath = VirtualFileSystem.SwitchPathToSystemPath(contentPath); - - if (!string.IsNullOrWhiteSpace(avatarPath)) - { - using IStorage ncaFileStream = new LocalStorage(avatarPath, FileAccess.Read, FileMode.Open); - - Nca nca = new(virtualFileSystem.KeySet, ncaFileStream); - IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); - - foreach (var item in romfs.EnumerateEntries()) - { - // TODO: Parse DatabaseInfo.bin and table.bin files for more accuracy. - - if (item.Type == DirectoryEntryType.File && item.FullPath.Contains("chara") && item.FullPath.Contains("szs")) - { - using var file = new UniqueRef(); - - romfs.OpenFile(ref file.Ref, ("/" + item.FullPath).ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - using MemoryStream stream = MemoryStreamManager.Shared.GetStream(); - using MemoryStream streamPng = MemoryStreamManager.Shared.GetStream(); - file.Get.AsStream().CopyTo(stream); - - stream.Position = 0; - - using var avatarImage = new SKBitmap(new SKImageInfo(256, 256, SKColorType.Rgba8888)); - var data = DecompressYaz0(stream); - Marshal.Copy(data, 0, avatarImage.GetPixels(), data.Length); - - avatarImage.Encode(streamPng, SKEncodedImageFormat.Png, 80); - - _avatarDict.Add(item.FullPath, streamPng.ToArray()); - } - } - } - } - - private void ProcessAvatars() - { - _listStore.Clear(); - - foreach (var avatar in _avatarDict) - { - _listStore.AppendValues(avatar.Key, new Gdk.Pixbuf(ProcessImage(avatar.Value), 96, 96)); - } - - _iconView.SelectPath(new TreePath(new[] { 0 })); - } - - private byte[] ProcessImage(byte[] data) - { - using MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream(); - - using var avatarImage = SKBitmap.Decode(data); - using var surface = SKSurface.Create(avatarImage.Info); - - var background = new SKColor( - (byte)(_backgroundColor.Red * 255), - (byte)(_backgroundColor.Green * 255), - (byte)(_backgroundColor.Blue * 255), - (byte)(_backgroundColor.Alpha * 255) - ); - var canvas = surface.Canvas; - canvas.Clear(background); - canvas.DrawBitmap(avatarImage, new SKPoint()); - - surface.Flush(); - using var snapshot = surface.Snapshot(); - using var encoded = snapshot.Encode(SKEncodedImageFormat.Jpeg, 80); - encoded.SaveTo(streamJpg); - - return streamJpg.ToArray(); - } - - private void CloseButton_Pressed(object sender, EventArgs e) - { - SelectedProfileImage = null; - - Close(); - } - - private void IconView_SelectionChanged(object sender, EventArgs e) - { - if (_iconView.SelectedItems.Length > 0) - { - _listStore.GetIter(out TreeIter iter, _iconView.SelectedItems[0]); - - SelectedProfileImage = ProcessImage(_avatarDict[(string)_listStore.GetValue(iter, 0)]); - } - } - - private void SetBackgroungColorButton_Pressed(object sender, EventArgs e) - { - using ColorChooserDialog colorChooserDialog = new("Set Background Color", this); - - colorChooserDialog.UseAlpha = false; - colorChooserDialog.Rgba = _backgroundColor; - - if (colorChooserDialog.Run() == (int)ResponseType.Ok) - { - _backgroundColor = colorChooserDialog.Rgba; - - ProcessAvatars(); - } - - colorChooserDialog.Hide(); - } - - private void ChooseButton_Pressed(object sender, EventArgs e) - { - Close(); - } - - private static byte[] DecompressYaz0(Stream stream) - { - using BinaryReader reader = new(stream); - - reader.ReadInt32(); // Magic - - uint decodedLength = BinaryPrimitives.ReverseEndianness(reader.ReadUInt32()); - - reader.ReadInt64(); // Padding - - byte[] input = new byte[stream.Length - stream.Position]; - stream.ReadExactly(input, 0, input.Length); - - long inputOffset = 0; - - byte[] output = new byte[decodedLength]; - long outputOffset = 0; - - ushort mask = 0; - byte header = 0; - - while (outputOffset < decodedLength) - { - if ((mask >>= 1) == 0) - { - header = input[inputOffset++]; - mask = 0x80; - } - - if ((header & mask) > 0) - { - if (outputOffset == output.Length) - { - break; - } - - output[outputOffset++] = input[inputOffset++]; - } - else - { - byte byte1 = input[inputOffset++]; - byte byte2 = input[inputOffset++]; - - int dist = ((byte1 & 0xF) << 8) | byte2; - int position = (int)outputOffset - (dist + 1); - - int length = byte1 >> 4; - if (length == 0) - { - length = input[inputOffset++] + 0x12; - } - else - { - length += 2; - } - - while (length-- > 0) - { - output[outputOffset++] = output[position++]; - } - } - } - - return output; - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Windows/CheatWindow.cs b/src/Ryujinx.Gtk3/UI/Windows/CheatWindow.cs deleted file mode 100644 index d9f01918f..000000000 --- a/src/Ryujinx.Gtk3/UI/Windows/CheatWindow.cs +++ /dev/null @@ -1,163 +0,0 @@ -using Gtk; -using LibHac.Tools.FsSystem; -using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.HOS; -using Ryujinx.UI.App.Common; -using Ryujinx.UI.Common.Configuration; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using GUI = Gtk.Builder.ObjectAttribute; - -namespace Ryujinx.UI.Windows -{ - public class CheatWindow : Window - { - private readonly string _enabledCheatsPath; - private readonly bool _noCheatsFound; - -#pragma warning disable CS0649, IDE0044 // Field is never assigned to, Add readonly modifier - [GUI] Label _baseTitleInfoLabel; - [GUI] TextView _buildIdTextView; - [GUI] TreeView _cheatTreeView; - [GUI] Button _saveButton; -#pragma warning restore CS0649, IDE0044 - - public CheatWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName, string titlePath) : this(new Builder("Ryujinx.Gtk3.UI.Windows.CheatWindow.glade"), virtualFileSystem, titleId, titleName, titlePath) { } - - private CheatWindow(Builder builder, VirtualFileSystem virtualFileSystem, ulong titleId, string titleName, string titlePath) : base(builder.GetRawOwnedObject("_cheatWindow")) - { - builder.Autoconnect(this); - - IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks - ? IntegrityCheckLevel.ErrorOnInvalid - : IntegrityCheckLevel.None; - - _baseTitleInfoLabel.Text = $"Cheats Available for {titleName} [{titleId:X16}]"; - _buildIdTextView.Buffer.Text = $"BuildId: {ApplicationData.GetBuildId(virtualFileSystem, checkLevel, titlePath)}"; - - string modsBasePath = ModLoader.GetModsBasePath(); - string titleModsPath = ModLoader.GetApplicationDir(modsBasePath, titleId.ToString("X16")); - - _enabledCheatsPath = System.IO.Path.Combine(titleModsPath, "cheats", "enabled.txt"); - - _cheatTreeView.Model = new TreeStore(typeof(bool), typeof(string), typeof(string), typeof(string)); - - CellRendererToggle enableToggle = new(); - enableToggle.Toggled += (sender, args) => - { - _cheatTreeView.Model.GetIter(out TreeIter treeIter, new TreePath(args.Path)); - bool newValue = !(bool)_cheatTreeView.Model.GetValue(treeIter, 0); - _cheatTreeView.Model.SetValue(treeIter, 0, newValue); - - if (_cheatTreeView.Model.IterChildren(out TreeIter childIter, treeIter)) - { - do - { - _cheatTreeView.Model.SetValue(childIter, 0, newValue); - } - while (_cheatTreeView.Model.IterNext(ref childIter)); - } - }; - - _cheatTreeView.AppendColumn("Enabled", enableToggle, "active", 0); - _cheatTreeView.AppendColumn("Name", new CellRendererText(), "text", 1); - _cheatTreeView.AppendColumn("Path", new CellRendererText(), "text", 2); - - var buildIdColumn = _cheatTreeView.AppendColumn("Build Id", new CellRendererText(), "text", 3); - buildIdColumn.Visible = false; - - string[] enabled = Array.Empty(); - - if (File.Exists(_enabledCheatsPath)) - { - enabled = File.ReadAllLines(_enabledCheatsPath); - } - - int cheatAdded = 0; - - var mods = new ModLoader.ModCache(); - - ModLoader.QueryContentsDir(mods, new DirectoryInfo(System.IO.Path.Combine(modsBasePath, "contents")), titleId); - - string currentCheatFile = string.Empty; - string buildId = string.Empty; - TreeIter parentIter = default; - - foreach (var cheat in mods.Cheats) - { - if (cheat.Path.FullName != currentCheatFile) - { - currentCheatFile = cheat.Path.FullName; - string parentPath = currentCheatFile.Replace(titleModsPath, ""); - - buildId = System.IO.Path.GetFileNameWithoutExtension(currentCheatFile).ToUpper(); - parentIter = ((TreeStore)_cheatTreeView.Model).AppendValues(false, buildId, parentPath, ""); - } - - string cleanName = cheat.Name[1..^7]; - ((TreeStore)_cheatTreeView.Model).AppendValues(parentIter, enabled.Contains($"{buildId}-{cheat.Name}"), cleanName, "", buildId); - - cheatAdded++; - } - - if (cheatAdded == 0) - { - ((TreeStore)_cheatTreeView.Model).AppendValues(false, "No Cheats Found", "", ""); - _cheatTreeView.GetColumn(0).Visible = false; - - _noCheatsFound = true; - - _saveButton.Visible = false; - } - - _cheatTreeView.ExpandAll(); - } - - private void SaveButton_Clicked(object sender, EventArgs args) - { - if (_noCheatsFound) - { - return; - } - - List enabledCheats = new(); - - if (_cheatTreeView.Model.GetIterFirst(out TreeIter parentIter)) - { - do - { - if (_cheatTreeView.Model.IterChildren(out TreeIter childIter, parentIter)) - { - do - { - var enabled = (bool)_cheatTreeView.Model.GetValue(childIter, 0); - - if (enabled) - { - var name = _cheatTreeView.Model.GetValue(childIter, 1).ToString(); - var buildId = _cheatTreeView.Model.GetValue(childIter, 3).ToString(); - - enabledCheats.Add($"{buildId}-<{name} Cheat>"); - } - } - while (_cheatTreeView.Model.IterNext(ref childIter)); - } - } - while (_cheatTreeView.Model.IterNext(ref parentIter)); - } - - Directory.CreateDirectory(System.IO.Path.GetDirectoryName(_enabledCheatsPath)); - - File.WriteAllLines(_enabledCheatsPath, enabledCheats); - - Dispose(); - } - - private void CancelButton_Clicked(object sender, EventArgs args) - { - Dispose(); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Windows/CheatWindow.glade b/src/Ryujinx.Gtk3/UI/Windows/CheatWindow.glade deleted file mode 100644 index 9a165f1a8..000000000 --- a/src/Ryujinx.Gtk3/UI/Windows/CheatWindow.glade +++ /dev/null @@ -1,150 +0,0 @@ - - - - - - False - Ryujinx - Cheat Manager - 440 - 550 - - - True - False - vertical - - - True - False - vertical - - - True - False - 10 - 10 - Available Cheats - - - False - True - 0 - - - - - True - 10 - center - 10 - False - False - - - False - True - 1 - - - - - True - True - 10 - 10 - in - - - True - False - - - True - True - - - - - - - - - - True - True - 2 - - - - - True - True - 0 - - - - - True - False - - - True - False - 10 - 10 - end - - - Save - True - True - True - 10 - 2 - 2 - - - - True - True - 0 - - - - - Cancel - True - True - True - 10 - 2 - 2 - - - - True - True - 1 - - - - - True - True - 1 - - - - - False - True - 1 - - - - - - - - - diff --git a/src/Ryujinx.Gtk3/UI/Windows/ControllerWindow.cs b/src/Ryujinx.Gtk3/UI/Windows/ControllerWindow.cs deleted file mode 100644 index d0b8266f4..000000000 --- a/src/Ryujinx.Gtk3/UI/Windows/ControllerWindow.cs +++ /dev/null @@ -1,1232 +0,0 @@ -using Gtk; -using Ryujinx.Common.Configuration; -using Ryujinx.Common.Configuration.Hid; -using Ryujinx.Common.Configuration.Hid.Controller; -using Ryujinx.Common.Configuration.Hid.Controller.Motion; -using Ryujinx.Common.Configuration.Hid.Keyboard; -using Ryujinx.Common.Logging; -using Ryujinx.Common.Utilities; -using Ryujinx.Input; -using Ryujinx.Input.Assigner; -using Ryujinx.Input.GTK3; -using Ryujinx.UI.Common.Configuration; -using Ryujinx.UI.Helper; -using Ryujinx.UI.Widgets; -using System; -using System.Collections.Generic; -using System.IO; -using System.Reflection; -using System.Text.Json; -using System.Threading; -using Button = Ryujinx.Input.Button; -using ConfigGamepadInputId = Ryujinx.Common.Configuration.Hid.Controller.GamepadInputId; -using ConfigStickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId; -using GUI = Gtk.Builder.ObjectAttribute; -using Key = Ryujinx.Common.Configuration.Hid.Key; - -namespace Ryujinx.UI.Windows -{ - public class ControllerWindow : Window - { - private readonly PlayerIndex _playerIndex; - private readonly InputConfig _inputConfig; - - private bool _isWaitingForInput; - -#pragma warning disable CS0649, IDE0044 // Field is never assigned to, Add readonly modifier - [GUI] Adjustment _controllerStrongRumble; - [GUI] Adjustment _controllerWeakRumble; - [GUI] Adjustment _controllerDeadzoneLeft; - [GUI] Adjustment _controllerDeadzoneRight; - [GUI] Adjustment _controllerRangeLeft; - [GUI] Adjustment _controllerRangeRight; - [GUI] Adjustment _controllerTriggerThreshold; - [GUI] Adjustment _slotNumber; - [GUI] Adjustment _altSlotNumber; - [GUI] Adjustment _sensitivity; - [GUI] Adjustment _gyroDeadzone; - [GUI] CheckButton _enableMotion; - [GUI] CheckButton _enableCemuHook; - [GUI] CheckButton _mirrorInput; - [GUI] Entry _dsuServerHost; - [GUI] Entry _dsuServerPort; - [GUI] ComboBoxText _inputDevice; - [GUI] ComboBoxText _profile; - [GUI] Box _settingsBox; - [GUI] Box _motionAltBox; - [GUI] Box _motionBox; - [GUI] Box _dsuServerHostBox; - [GUI] Box _dsuServerPortBox; - [GUI] Box _motionControllerSlot; - [GUI] Grid _leftStickKeyboard; - [GUI] Grid _leftStickController; - [GUI] Box _deadZoneLeftBox; - [GUI] Box _rangeLeftBox; - [GUI] Grid _rightStickKeyboard; - [GUI] Grid _rightStickController; - [GUI] Box _deadZoneRightBox; - [GUI] Box _rangeRightBox; - [GUI] Grid _leftSideTriggerBox; - [GUI] Grid _rightSideTriggerBox; - [GUI] Box _triggerThresholdBox; - [GUI] ComboBoxText _controllerType; - [GUI] ToggleButton _lStick; - [GUI] CheckButton _invertLStickX; - [GUI] CheckButton _invertLStickY; - [GUI] CheckButton _rotateL90CW; - [GUI] ToggleButton _lStickUp; - [GUI] ToggleButton _lStickDown; - [GUI] ToggleButton _lStickLeft; - [GUI] ToggleButton _lStickRight; - [GUI] ToggleButton _lStickButton; - [GUI] ToggleButton _dpadUp; - [GUI] ToggleButton _dpadDown; - [GUI] ToggleButton _dpadLeft; - [GUI] ToggleButton _dpadRight; - [GUI] ToggleButton _minus; - [GUI] ToggleButton _l; - [GUI] ToggleButton _zL; - [GUI] ToggleButton _rStick; - [GUI] CheckButton _invertRStickX; - [GUI] CheckButton _invertRStickY; - [GUI] CheckButton _rotateR90CW; - [GUI] ToggleButton _rStickUp; - [GUI] ToggleButton _rStickDown; - [GUI] ToggleButton _rStickLeft; - [GUI] ToggleButton _rStickRight; - [GUI] ToggleButton _rStickButton; - [GUI] ToggleButton _a; - [GUI] ToggleButton _b; - [GUI] ToggleButton _x; - [GUI] ToggleButton _y; - [GUI] ToggleButton _plus; - [GUI] ToggleButton _r; - [GUI] ToggleButton _zR; - [GUI] ToggleButton _lSl; - [GUI] ToggleButton _lSr; - [GUI] ToggleButton _rSl; - [GUI] ToggleButton _rSr; - [GUI] Image _controllerImage; - [GUI] CheckButton _enableRumble; - [GUI] Box _rumbleBox; -#pragma warning restore CS0649, IDE0044 - - private readonly MainWindow _mainWindow; - private readonly IGamepadDriver _gtk3KeyboardDriver; - private IGamepad _selectedGamepad; - private bool _mousePressed; - private bool _middleMousePressed; - - private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - - public ControllerWindow(MainWindow mainWindow, PlayerIndex controllerId) : this(mainWindow, new Builder("Ryujinx.Gtk3.UI.Windows.ControllerWindow.glade"), controllerId) { } - - private ControllerWindow(MainWindow mainWindow, Builder builder, PlayerIndex controllerId) : base(builder.GetRawOwnedObject("_controllerWin")) - { - _mainWindow = mainWindow; - _selectedGamepad = null; - - // NOTE: To get input in this window, we need to bind a custom keyboard driver instead of using the InputManager one as the main window isn't focused... - _gtk3KeyboardDriver = new GTK3KeyboardDriver(this); - - Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Logo_Ryujinx.png"); - - builder.Autoconnect(this); - - _playerIndex = controllerId; - _inputConfig = ConfigurationState.Instance.Hid.InputConfig.Value.Find(inputConfig => inputConfig.PlayerIndex == _playerIndex); - - Title = $"Ryujinx - Controller Settings - {_playerIndex}"; - - if (_playerIndex == PlayerIndex.Handheld) - { - _controllerType.Append(ControllerType.Handheld.ToString(), "Handheld"); - _controllerType.Sensitive = false; - } - else - { - _controllerType.Append(ControllerType.ProController.ToString(), "Pro Controller"); - _controllerType.Append(ControllerType.JoyconPair.ToString(), "Joycon Pair"); - _controllerType.Append(ControllerType.JoyconLeft.ToString(), "Joycon Left"); - _controllerType.Append(ControllerType.JoyconRight.ToString(), "Joycon Right"); - } - - _controllerType.Active = 0; // Set initial value to first in list. - - // Bind Events. - _lStick.Clicked += ButtonForStick_Pressed; - _lStickUp.Clicked += Button_Pressed; - _lStickDown.Clicked += Button_Pressed; - _lStickLeft.Clicked += Button_Pressed; - _lStickRight.Clicked += Button_Pressed; - _lStickButton.Clicked += Button_Pressed; - _dpadUp.Clicked += Button_Pressed; - _dpadDown.Clicked += Button_Pressed; - _dpadLeft.Clicked += Button_Pressed; - _dpadRight.Clicked += Button_Pressed; - _minus.Clicked += Button_Pressed; - _l.Clicked += Button_Pressed; - _zL.Clicked += Button_Pressed; - _lSl.Clicked += Button_Pressed; - _lSr.Clicked += Button_Pressed; - _rStick.Clicked += ButtonForStick_Pressed; - _rStickUp.Clicked += Button_Pressed; - _rStickDown.Clicked += Button_Pressed; - _rStickLeft.Clicked += Button_Pressed; - _rStickRight.Clicked += Button_Pressed; - _rStickButton.Clicked += Button_Pressed; - _a.Clicked += Button_Pressed; - _b.Clicked += Button_Pressed; - _x.Clicked += Button_Pressed; - _y.Clicked += Button_Pressed; - _plus.Clicked += Button_Pressed; - _r.Clicked += Button_Pressed; - _zR.Clicked += Button_Pressed; - _rSl.Clicked += Button_Pressed; - _rSr.Clicked += Button_Pressed; - _enableCemuHook.Clicked += CemuHookCheckButtonPressed; - - // Setup current values. - UpdateInputDeviceList(); - SetAvailableOptions(); - - ClearValues(); - if (_inputDevice.ActiveId != null) - { - SetCurrentValues(); - } - - mainWindow.InputManager.GamepadDriver.OnGamepadConnected += HandleOnGamepadConnected; - mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected; - - _mainWindow.RendererWidget?.NpadManager.BlockInputUpdates(); - } - - private void CemuHookCheckButtonPressed(object sender, EventArgs e) - { - UpdateCemuHookSpecificFieldsVisibility(); - } - - private void HandleOnGamepadDisconnected(string id) - { - Application.Invoke(delegate - { - UpdateInputDeviceList(); - }); - } - - private void HandleOnGamepadConnected(string id) - { - Application.Invoke(delegate - { - UpdateInputDeviceList(); - }); - } - - protected override void OnDestroyed() - { - _mainWindow.InputManager.GamepadDriver.OnGamepadConnected -= HandleOnGamepadConnected; - _mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected -= HandleOnGamepadDisconnected; - - _mainWindow.RendererWidget?.NpadManager.UnblockInputUpdates(); - - _selectedGamepad?.Dispose(); - - _gtk3KeyboardDriver.Dispose(); - } - - private static string GetShortGamepadName(string str) - { - const string ShrinkChars = "..."; - const int MaxSize = 50; - - if (str.Length > MaxSize) - { - return $"{str.AsSpan(0, MaxSize - ShrinkChars.Length)}{ShrinkChars}"; - } - - return str; - } - - private void UpdateInputDeviceList() - { - _inputDevice.RemoveAll(); - _inputDevice.Append("disabled", "Disabled"); - _inputDevice.SetActiveId("disabled"); - - foreach (string id in _mainWindow.InputManager.KeyboardDriver.GamepadsIds) - { - IGamepad gamepad = _mainWindow.InputManager.KeyboardDriver.GetGamepad(id); - - if (gamepad != null) - { - _inputDevice.Append($"keyboard/{id}", GetShortGamepadName($"{gamepad.Name} ({id})")); - - gamepad.Dispose(); - } - } - - foreach (string id in _mainWindow.InputManager.GamepadDriver.GamepadsIds) - { - IGamepad gamepad = _mainWindow.InputManager.GamepadDriver.GetGamepad(id); - - if (gamepad != null) - { - _inputDevice.Append($"controller/{id}", GetShortGamepadName($"{gamepad.Name} ({id})")); - - gamepad.Dispose(); - } - } - - switch (_inputConfig) - { - case StandardKeyboardInputConfig keyboard: - _inputDevice.SetActiveId($"keyboard/{keyboard.Id}"); - break; - case StandardControllerInputConfig controller: - _inputDevice.SetActiveId($"controller/{controller.Id}"); - break; - } - } - - private void UpdateCemuHookSpecificFieldsVisibility() - { - if (_enableCemuHook.Active) - { - _dsuServerHostBox.Show(); - _dsuServerPortBox.Show(); - _motionControllerSlot.Show(); - _motionAltBox.Show(); - _mirrorInput.Show(); - } - else - { - _dsuServerHostBox.Hide(); - _dsuServerPortBox.Hide(); - _motionControllerSlot.Hide(); - _motionAltBox.Hide(); - _mirrorInput.Hide(); - } - } - - private void SetAvailableOptions() - { - if (_inputDevice.ActiveId != null && _inputDevice.ActiveId.StartsWith("keyboard")) - { - ShowAll(); - _leftStickController.Hide(); - _rightStickController.Hide(); - _deadZoneLeftBox.Hide(); - _deadZoneRightBox.Hide(); - _rangeLeftBox.Hide(); - _rangeRightBox.Hide(); - _triggerThresholdBox.Hide(); - _motionBox.Hide(); - _rumbleBox.Hide(); - } - else if (_inputDevice.ActiveId != null && _inputDevice.ActiveId.StartsWith("controller")) - { - ShowAll(); - _leftStickKeyboard.Hide(); - _rightStickKeyboard.Hide(); - - UpdateCemuHookSpecificFieldsVisibility(); - } - else - { - _settingsBox.Hide(); - } - - ClearValues(); - } - - private void SetCurrentValues() - { - SetControllerSpecificFields(); - - SetProfiles(); - - if (_inputDevice.ActiveId.StartsWith("keyboard") && _inputConfig is StandardKeyboardInputConfig) - { - SetValues(_inputConfig); - } - else if (_inputDevice.ActiveId.StartsWith("controller") && _inputConfig is StandardControllerInputConfig) - { - SetValues(_inputConfig); - } - } - - private void SetControllerSpecificFields() - { - _leftSideTriggerBox.Hide(); - _rightSideTriggerBox.Hide(); - _motionAltBox.Hide(); - - switch (_controllerType.ActiveId) - { - case "JoyconLeft": - _leftSideTriggerBox.Show(); - break; - case "JoyconRight": - _rightSideTriggerBox.Show(); - break; - case "JoyconPair": - _motionAltBox.Show(); - break; - } - - if (!OperatingSystem.IsMacOS()) - { - _controllerImage.Pixbuf = _controllerType.ActiveId switch - { - "ProController" => new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Controller_ProCon.svg", 400, 400), - "JoyconLeft" => new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Controller_JoyConLeft.svg", 400, 500), - "JoyconRight" => new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Controller_JoyConRight.svg", 400, 500), - _ => new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Controller_JoyConPair.svg", 400, 500), - }; - } - } - - private void ClearValues() - { - _lStick.Label = "Unbound"; - _lStickUp.Label = "Unbound"; - _lStickDown.Label = "Unbound"; - _lStickLeft.Label = "Unbound"; - _lStickRight.Label = "Unbound"; - _lStickButton.Label = "Unbound"; - _dpadUp.Label = "Unbound"; - _dpadDown.Label = "Unbound"; - _dpadLeft.Label = "Unbound"; - _dpadRight.Label = "Unbound"; - _minus.Label = "Unbound"; - _l.Label = "Unbound"; - _zL.Label = "Unbound"; - _lSl.Label = "Unbound"; - _lSr.Label = "Unbound"; - _rStick.Label = "Unbound"; - _rStickUp.Label = "Unbound"; - _rStickDown.Label = "Unbound"; - _rStickLeft.Label = "Unbound"; - _rStickRight.Label = "Unbound"; - _rStickButton.Label = "Unbound"; - _a.Label = "Unbound"; - _b.Label = "Unbound"; - _x.Label = "Unbound"; - _y.Label = "Unbound"; - _plus.Label = "Unbound"; - _r.Label = "Unbound"; - _zR.Label = "Unbound"; - _rSl.Label = "Unbound"; - _rSr.Label = "Unbound"; - _controllerStrongRumble.Value = 1; - _controllerWeakRumble.Value = 1; - _controllerDeadzoneLeft.Value = 0; - _controllerDeadzoneRight.Value = 0; - _controllerRangeLeft.Value = 1; - _controllerRangeRight.Value = 1; - _controllerTriggerThreshold.Value = 0; - _mirrorInput.Active = false; - _enableMotion.Active = false; - _enableCemuHook.Active = false; - _slotNumber.Value = 0; - _altSlotNumber.Value = 0; - _sensitivity.Value = 100; - _gyroDeadzone.Value = 1; - _dsuServerHost.Buffer.Text = ""; - _dsuServerPort.Buffer.Text = ""; - _enableRumble.Active = false; - } - - private void SetValues(InputConfig config) - { - switch (config) - { - case StandardKeyboardInputConfig keyboardConfig: - if (!_controllerType.SetActiveId(keyboardConfig.ControllerType.ToString())) - { - _controllerType.SetActiveId(_playerIndex == PlayerIndex.Handheld - ? ControllerType.Handheld.ToString() - : ControllerType.ProController.ToString()); - } - - _lStickUp.Label = keyboardConfig.LeftJoyconStick.StickUp.ToString(); - _lStickDown.Label = keyboardConfig.LeftJoyconStick.StickDown.ToString(); - _lStickLeft.Label = keyboardConfig.LeftJoyconStick.StickLeft.ToString(); - _lStickRight.Label = keyboardConfig.LeftJoyconStick.StickRight.ToString(); - _lStickButton.Label = keyboardConfig.LeftJoyconStick.StickButton.ToString(); - _dpadUp.Label = keyboardConfig.LeftJoycon.DpadUp.ToString(); - _dpadDown.Label = keyboardConfig.LeftJoycon.DpadDown.ToString(); - _dpadLeft.Label = keyboardConfig.LeftJoycon.DpadLeft.ToString(); - _dpadRight.Label = keyboardConfig.LeftJoycon.DpadRight.ToString(); - _minus.Label = keyboardConfig.LeftJoycon.ButtonMinus.ToString(); - _l.Label = keyboardConfig.LeftJoycon.ButtonL.ToString(); - _zL.Label = keyboardConfig.LeftJoycon.ButtonZl.ToString(); - _lSl.Label = keyboardConfig.LeftJoycon.ButtonSl.ToString(); - _lSr.Label = keyboardConfig.LeftJoycon.ButtonSr.ToString(); - _rStickUp.Label = keyboardConfig.RightJoyconStick.StickUp.ToString(); - _rStickDown.Label = keyboardConfig.RightJoyconStick.StickDown.ToString(); - _rStickLeft.Label = keyboardConfig.RightJoyconStick.StickLeft.ToString(); - _rStickRight.Label = keyboardConfig.RightJoyconStick.StickRight.ToString(); - _rStickButton.Label = keyboardConfig.RightJoyconStick.StickButton.ToString(); - _a.Label = keyboardConfig.RightJoycon.ButtonA.ToString(); - _b.Label = keyboardConfig.RightJoycon.ButtonB.ToString(); - _x.Label = keyboardConfig.RightJoycon.ButtonX.ToString(); - _y.Label = keyboardConfig.RightJoycon.ButtonY.ToString(); - _plus.Label = keyboardConfig.RightJoycon.ButtonPlus.ToString(); - _r.Label = keyboardConfig.RightJoycon.ButtonR.ToString(); - _zR.Label = keyboardConfig.RightJoycon.ButtonZr.ToString(); - _rSl.Label = keyboardConfig.RightJoycon.ButtonSl.ToString(); - _rSr.Label = keyboardConfig.RightJoycon.ButtonSr.ToString(); - break; - - case StandardControllerInputConfig controllerConfig: - if (!_controllerType.SetActiveId(controllerConfig.ControllerType.ToString())) - { - _controllerType.SetActiveId(_playerIndex == PlayerIndex.Handheld - ? ControllerType.Handheld.ToString() - : ControllerType.ProController.ToString()); - } - - _lStick.Label = controllerConfig.LeftJoyconStick.Joystick.ToString(); - _invertLStickX.Active = controllerConfig.LeftJoyconStick.InvertStickX; - _invertLStickY.Active = controllerConfig.LeftJoyconStick.InvertStickY; - _rotateL90CW.Active = controllerConfig.LeftJoyconStick.Rotate90CW; - _lStickButton.Label = controllerConfig.LeftJoyconStick.StickButton.ToString(); - _dpadUp.Label = controllerConfig.LeftJoycon.DpadUp.ToString(); - _dpadDown.Label = controllerConfig.LeftJoycon.DpadDown.ToString(); - _dpadLeft.Label = controllerConfig.LeftJoycon.DpadLeft.ToString(); - _dpadRight.Label = controllerConfig.LeftJoycon.DpadRight.ToString(); - _minus.Label = controllerConfig.LeftJoycon.ButtonMinus.ToString(); - _l.Label = controllerConfig.LeftJoycon.ButtonL.ToString(); - _zL.Label = controllerConfig.LeftJoycon.ButtonZl.ToString(); - _lSl.Label = controllerConfig.LeftJoycon.ButtonSl.ToString(); - _lSr.Label = controllerConfig.LeftJoycon.ButtonSr.ToString(); - _rStick.Label = controllerConfig.RightJoyconStick.Joystick.ToString(); - _invertRStickX.Active = controllerConfig.RightJoyconStick.InvertStickX; - _invertRStickY.Active = controllerConfig.RightJoyconStick.InvertStickY; - _rotateR90CW.Active = controllerConfig.RightJoyconStick.Rotate90CW; - _rStickButton.Label = controllerConfig.RightJoyconStick.StickButton.ToString(); - _a.Label = controllerConfig.RightJoycon.ButtonA.ToString(); - _b.Label = controllerConfig.RightJoycon.ButtonB.ToString(); - _x.Label = controllerConfig.RightJoycon.ButtonX.ToString(); - _y.Label = controllerConfig.RightJoycon.ButtonY.ToString(); - _plus.Label = controllerConfig.RightJoycon.ButtonPlus.ToString(); - _r.Label = controllerConfig.RightJoycon.ButtonR.ToString(); - _zR.Label = controllerConfig.RightJoycon.ButtonZr.ToString(); - _rSl.Label = controllerConfig.RightJoycon.ButtonSl.ToString(); - _rSr.Label = controllerConfig.RightJoycon.ButtonSr.ToString(); - _controllerStrongRumble.Value = controllerConfig.Rumble.StrongRumble; - _controllerWeakRumble.Value = controllerConfig.Rumble.WeakRumble; - _enableRumble.Active = controllerConfig.Rumble.EnableRumble; - _controllerDeadzoneLeft.Value = controllerConfig.DeadzoneLeft; - _controllerDeadzoneRight.Value = controllerConfig.DeadzoneRight; - _controllerRangeLeft.Value = controllerConfig.RangeLeft; - _controllerRangeRight.Value = controllerConfig.RangeRight; - _controllerTriggerThreshold.Value = controllerConfig.TriggerThreshold; - _sensitivity.Value = controllerConfig.Motion.Sensitivity; - _gyroDeadzone.Value = controllerConfig.Motion.GyroDeadzone; - _enableMotion.Active = controllerConfig.Motion.EnableMotion; - _enableCemuHook.Active = controllerConfig.Motion.MotionBackend == MotionInputBackendType.CemuHook; - - // If both stick ranges are 0 (usually indicative of an outdated profile load) then both sticks will be set to 1.0. - if (_controllerRangeLeft.Value <= 0.0 && _controllerRangeRight.Value <= 0.0) - { - _controllerRangeLeft.Value = 1.0; - _controllerRangeRight.Value = 1.0; - - Logger.Info?.Print(LogClass.Application, $"{config.PlayerIndex} stick range reset. Save the profile now to update your configuration"); - } - - if (controllerConfig.Motion is CemuHookMotionConfigController cemuHookMotionConfig) - { - _slotNumber.Value = cemuHookMotionConfig.Slot; - _altSlotNumber.Value = cemuHookMotionConfig.AltSlot; - _mirrorInput.Active = cemuHookMotionConfig.MirrorInput; - _dsuServerHost.Buffer.Text = cemuHookMotionConfig.DsuServerHost; - _dsuServerPort.Buffer.Text = cemuHookMotionConfig.DsuServerPort.ToString(); - } - - break; - } - } - - private InputConfig GetValues() - { - if (_inputDevice.ActiveId.StartsWith("keyboard")) - { -#pragma warning disable CA1806, IDE0055 // Disable formatting - Enum.TryParse(_lStickUp.Label, out Key lStickUp); - Enum.TryParse(_lStickDown.Label, out Key lStickDown); - Enum.TryParse(_lStickLeft.Label, out Key lStickLeft); - Enum.TryParse(_lStickRight.Label, out Key lStickRight); - Enum.TryParse(_lStickButton.Label, out Key lStickButton); - Enum.TryParse(_dpadUp.Label, out Key lDPadUp); - Enum.TryParse(_dpadDown.Label, out Key lDPadDown); - Enum.TryParse(_dpadLeft.Label, out Key lDPadLeft); - Enum.TryParse(_dpadRight.Label, out Key lDPadRight); - Enum.TryParse(_minus.Label, out Key lButtonMinus); - Enum.TryParse(_l.Label, out Key lButtonL); - Enum.TryParse(_zL.Label, out Key lButtonZl); - Enum.TryParse(_lSl.Label, out Key lButtonSl); - Enum.TryParse(_lSr.Label, out Key lButtonSr); - - Enum.TryParse(_rStickUp.Label, out Key rStickUp); - Enum.TryParse(_rStickDown.Label, out Key rStickDown); - Enum.TryParse(_rStickLeft.Label, out Key rStickLeft); - Enum.TryParse(_rStickRight.Label, out Key rStickRight); - Enum.TryParse(_rStickButton.Label, out Key rStickButton); - Enum.TryParse(_a.Label, out Key rButtonA); - Enum.TryParse(_b.Label, out Key rButtonB); - Enum.TryParse(_x.Label, out Key rButtonX); - Enum.TryParse(_y.Label, out Key rButtonY); - Enum.TryParse(_plus.Label, out Key rButtonPlus); - Enum.TryParse(_r.Label, out Key rButtonR); - Enum.TryParse(_zR.Label, out Key rButtonZr); - Enum.TryParse(_rSl.Label, out Key rButtonSl); - Enum.TryParse(_rSr.Label, out Key rButtonSr); -#pragma warning restore CA1806, IDE0055 - - return new StandardKeyboardInputConfig - { - Backend = InputBackendType.WindowKeyboard, - Version = InputConfig.CurrentVersion, - Id = _inputDevice.ActiveId.Split("/")[1], - ControllerType = Enum.Parse(_controllerType.ActiveId), - PlayerIndex = _playerIndex, - LeftJoycon = new LeftJoyconCommonConfig - { - ButtonMinus = lButtonMinus, - ButtonL = lButtonL, - ButtonZl = lButtonZl, - ButtonSl = lButtonSl, - ButtonSr = lButtonSr, - DpadUp = lDPadUp, - DpadDown = lDPadDown, - DpadLeft = lDPadLeft, - DpadRight = lDPadRight, - }, - LeftJoyconStick = new JoyconConfigKeyboardStick - { - StickUp = lStickUp, - StickDown = lStickDown, - StickLeft = lStickLeft, - StickRight = lStickRight, - StickButton = lStickButton, - }, - RightJoycon = new RightJoyconCommonConfig - { - ButtonA = rButtonA, - ButtonB = rButtonB, - ButtonX = rButtonX, - ButtonY = rButtonY, - ButtonPlus = rButtonPlus, - ButtonR = rButtonR, - ButtonZr = rButtonZr, - ButtonSl = rButtonSl, - ButtonSr = rButtonSr, - }, - RightJoyconStick = new JoyconConfigKeyboardStick - { - StickUp = rStickUp, - StickDown = rStickDown, - StickLeft = rStickLeft, - StickRight = rStickRight, - StickButton = rStickButton, - }, - }; - } - - if (_inputDevice.ActiveId.StartsWith("controller")) - { -#pragma warning disable CA1806, IDE0055 // Disable formatting - Enum.TryParse(_lStick.Label, out ConfigStickInputId lStick); - Enum.TryParse(_lStickButton.Label, out ConfigGamepadInputId lStickButton); - Enum.TryParse(_minus.Label, out ConfigGamepadInputId lButtonMinus); - Enum.TryParse(_l.Label, out ConfigGamepadInputId lButtonL); - Enum.TryParse(_zL.Label, out ConfigGamepadInputId lButtonZl); - Enum.TryParse(_lSl.Label, out ConfigGamepadInputId lButtonSl); - Enum.TryParse(_lSr.Label, out ConfigGamepadInputId lButtonSr); - Enum.TryParse(_dpadUp.Label, out ConfigGamepadInputId lDPadUp); - Enum.TryParse(_dpadDown.Label, out ConfigGamepadInputId lDPadDown); - Enum.TryParse(_dpadLeft.Label, out ConfigGamepadInputId lDPadLeft); - Enum.TryParse(_dpadRight.Label, out ConfigGamepadInputId lDPadRight); - - Enum.TryParse(_rStick.Label, out ConfigStickInputId rStick); - Enum.TryParse(_rStickButton.Label, out ConfigGamepadInputId rStickButton); - Enum.TryParse(_a.Label, out ConfigGamepadInputId rButtonA); - Enum.TryParse(_b.Label, out ConfigGamepadInputId rButtonB); - Enum.TryParse(_x.Label, out ConfigGamepadInputId rButtonX); - Enum.TryParse(_y.Label, out ConfigGamepadInputId rButtonY); - Enum.TryParse(_plus.Label, out ConfigGamepadInputId rButtonPlus); - Enum.TryParse(_r.Label, out ConfigGamepadInputId rButtonR); - Enum.TryParse(_zR.Label, out ConfigGamepadInputId rButtonZr); - Enum.TryParse(_rSl.Label, out ConfigGamepadInputId rButtonSl); - Enum.TryParse(_rSr.Label, out ConfigGamepadInputId rButtonSr); - - int.TryParse(_dsuServerPort.Buffer.Text, out int port); -#pragma warning restore CA1806, IDE0055 - - MotionConfigController motionConfig; - - if (_enableCemuHook.Active) - { - motionConfig = new CemuHookMotionConfigController - { - MotionBackend = MotionInputBackendType.CemuHook, - EnableMotion = _enableMotion.Active, - Sensitivity = (int)_sensitivity.Value, - GyroDeadzone = _gyroDeadzone.Value, - MirrorInput = _mirrorInput.Active, - Slot = (int)_slotNumber.Value, - AltSlot = (int)_altSlotNumber.Value, - DsuServerHost = _dsuServerHost.Buffer.Text, - DsuServerPort = port, - }; - } - else - { - motionConfig = new StandardMotionConfigController - { - MotionBackend = MotionInputBackendType.GamepadDriver, - EnableMotion = _enableMotion.Active, - Sensitivity = (int)_sensitivity.Value, - GyroDeadzone = _gyroDeadzone.Value, - }; - } - - return new StandardControllerInputConfig - { - Backend = InputBackendType.GamepadSDL2, - Version = InputConfig.CurrentVersion, - Id = _inputDevice.ActiveId.Split("/")[1].Split(" ")[0], - ControllerType = Enum.Parse(_controllerType.ActiveId), - PlayerIndex = _playerIndex, - DeadzoneLeft = (float)_controllerDeadzoneLeft.Value, - DeadzoneRight = (float)_controllerDeadzoneRight.Value, - RangeLeft = (float)_controllerRangeLeft.Value, - RangeRight = (float)_controllerRangeRight.Value, - TriggerThreshold = (float)_controllerTriggerThreshold.Value, - LeftJoycon = new LeftJoyconCommonConfig - { - ButtonMinus = lButtonMinus, - ButtonL = lButtonL, - ButtonZl = lButtonZl, - ButtonSl = lButtonSl, - ButtonSr = lButtonSr, - DpadUp = lDPadUp, - DpadDown = lDPadDown, - DpadLeft = lDPadLeft, - DpadRight = lDPadRight, - }, - LeftJoyconStick = new JoyconConfigControllerStick - { - InvertStickX = _invertLStickX.Active, - Joystick = lStick, - InvertStickY = _invertLStickY.Active, - StickButton = lStickButton, - Rotate90CW = _rotateL90CW.Active, - }, - RightJoycon = new RightJoyconCommonConfig - { - ButtonA = rButtonA, - ButtonB = rButtonB, - ButtonX = rButtonX, - ButtonY = rButtonY, - ButtonPlus = rButtonPlus, - ButtonR = rButtonR, - ButtonZr = rButtonZr, - ButtonSl = rButtonSl, - ButtonSr = rButtonSr, - }, - RightJoyconStick = new JoyconConfigControllerStick - { - InvertStickX = _invertRStickX.Active, - Joystick = rStick, - InvertStickY = _invertRStickY.Active, - StickButton = rStickButton, - Rotate90CW = _rotateR90CW.Active, - }, - Motion = motionConfig, - Rumble = new RumbleConfigController - { - StrongRumble = (float)_controllerStrongRumble.Value, - WeakRumble = (float)_controllerWeakRumble.Value, - EnableRumble = _enableRumble.Active, - }, - }; - } - - if (!_inputDevice.ActiveId.StartsWith("disabled")) - { - GtkDialog.CreateErrorDialog("Invalid data detected in one or more fields; the configuration was not saved."); - } - - return null; - } - - private string GetProfileBasePath() - { - if (_inputDevice.ActiveId.StartsWith("keyboard")) - { - return System.IO.Path.Combine(AppDataManager.ProfilesDirPath, "keyboard"); - } - else if (_inputDevice.ActiveId.StartsWith("controller")) - { - return System.IO.Path.Combine(AppDataManager.ProfilesDirPath, "controller"); - } - - return AppDataManager.ProfilesDirPath; - } - - // - // Events - // - private void InputDevice_Changed(object sender, EventArgs args) - { - SetAvailableOptions(); - SetControllerSpecificFields(); - - _selectedGamepad?.Dispose(); - _selectedGamepad = null; - - if (_inputDevice.ActiveId != null) - { - SetProfiles(); - - string id = GetCurrentGamepadId(); - - if (_inputDevice.ActiveId.StartsWith("keyboard")) - { - if (_inputConfig is StandardKeyboardInputConfig) - { - SetValues(_inputConfig); - } - - if (_mainWindow.InputManager.KeyboardDriver is GTK3KeyboardDriver) - { - // NOTE: To get input in this window, we need to bind a custom keyboard driver instead of using the InputManager one as the main window isn't focused... - _selectedGamepad = _gtk3KeyboardDriver.GetGamepad(id); - } - else - { - _selectedGamepad = _mainWindow.InputManager.KeyboardDriver.GetGamepad(id); - } - } - else if (_inputDevice.ActiveId.StartsWith("controller")) - { - if (_inputConfig is StandardControllerInputConfig) - { - SetValues(_inputConfig); - } - - _selectedGamepad = _mainWindow.InputManager.GamepadDriver.GetGamepad(id); - } - } - } - - private string GetCurrentGamepadId() - { - if (_inputDevice.ActiveId == null || _inputDevice.ActiveId == "disabled") - { - return null; - } - - return _inputDevice.ActiveId.Split("/")[1].Split(" ")[0]; - } - - private void Controller_Changed(object sender, EventArgs args) - { - SetControllerSpecificFields(); - } - - private IButtonAssigner CreateButtonAssigner(bool forStick) - { - IButtonAssigner assigner; - - if (_inputDevice.ActiveId.StartsWith("keyboard")) - { - assigner = new KeyboardKeyAssigner((IKeyboard)_selectedGamepad); - } - else if (_inputDevice.ActiveId.StartsWith("controller")) - { - assigner = new GamepadButtonAssigner(_selectedGamepad, (float)_controllerTriggerThreshold.Value, forStick); - } - else - { - throw new Exception("Controller not supported"); - } - - return assigner; - } - - private void HandleButtonPressed(ToggleButton button, bool forStick) - { - if (_isWaitingForInput) - { - button.Active = false; - - return; - } - - _mousePressed = false; - - ButtonPressEvent += MouseClick; - - IButtonAssigner assigner = CreateButtonAssigner(forStick); - - _isWaitingForInput = true; - - // Open GTK3 keyboard for cancel operations - IKeyboard keyboard = (IKeyboard)_gtk3KeyboardDriver.GetGamepad("0"); - - Thread inputThread = new(() => - { - assigner.Initialize(); - - while (true) - { - Thread.Sleep(10); - assigner.ReadInput(); - - if (_mousePressed || keyboard.IsPressed(Ryujinx.Input.Key.Escape) || assigner.IsAnyButtonPressed() || assigner.ShouldCancel()) - { - break; - } - } - - string pressedButton = ButtonHelper.ToString(assigner.GetPressedButton() ?? new Button(Input.Key.Unknown)); - - Application.Invoke(delegate - { - if (_middleMousePressed) - { - button.Label = "Unbound"; - } - else if (pressedButton != "") - { - button.Label = pressedButton; - } - - _middleMousePressed = false; - - ButtonPressEvent -= MouseClick; - keyboard.Dispose(); - - button.Active = false; - _isWaitingForInput = false; - }); - }) - { - Name = "GUI.InputThread", - IsBackground = true, - }; - inputThread.Start(); - } - - private void Button_Pressed(object sender, EventArgs args) - { - HandleButtonPressed((ToggleButton)sender, false); - } - - private void ButtonForStick_Pressed(object sender, EventArgs args) - { - HandleButtonPressed((ToggleButton)sender, true); - } - - private void MouseClick(object sender, ButtonPressEventArgs args) - { - _mousePressed = true; - _middleMousePressed = args.Event.Button == 2; - } - - private void SetProfiles() - { - _profile.RemoveAll(); - - string basePath = GetProfileBasePath(); - - if (!Directory.Exists(basePath)) - { - Directory.CreateDirectory(basePath); - } - - if (_inputDevice.ActiveId == null || _inputDevice.ActiveId.Equals("disabled")) - { - _profile.Append("default", "None"); - } - else - { - _profile.Append("default", "Default"); - - foreach (string profile in Directory.GetFiles(basePath, "*.*", SearchOption.AllDirectories)) - { - _profile.Append(System.IO.Path.GetFileName(profile), System.IO.Path.GetFileNameWithoutExtension(profile)); - } - } - - _profile.SetActiveId("default"); - } - - private void ProfileLoad_Activated(object sender, EventArgs args) - { - ((ToggleButton)sender).SetStateFlags(StateFlags.Normal, true); - - if (_inputDevice.ActiveId == "disabled" || _profile.ActiveId == null) - { - return; - } - - InputConfig config = null; - int pos = _profile.Active; - - if (_profile.ActiveId == "default") - { - if (_inputDevice.ActiveId.StartsWith("keyboard")) - { - config = new StandardKeyboardInputConfig - { - Version = InputConfig.CurrentVersion, - Backend = InputBackendType.WindowKeyboard, - Id = null, - ControllerType = ControllerType.ProController, - LeftJoycon = new LeftJoyconCommonConfig - { - DpadUp = Key.Up, - DpadDown = Key.Down, - DpadLeft = Key.Left, - DpadRight = Key.Right, - ButtonMinus = Key.Minus, - ButtonL = Key.E, - ButtonZl = Key.Q, - ButtonSl = Key.Unbound, - ButtonSr = Key.Unbound, - }, - - LeftJoyconStick = new JoyconConfigKeyboardStick - { - StickUp = Key.W, - StickDown = Key.S, - StickLeft = Key.A, - StickRight = Key.D, - StickButton = Key.F, - }, - - RightJoycon = new RightJoyconCommonConfig - { - ButtonA = Key.Z, - ButtonB = Key.X, - ButtonX = Key.C, - ButtonY = Key.V, - ButtonPlus = Key.Plus, - ButtonR = Key.U, - ButtonZr = Key.O, - ButtonSl = Key.Unbound, - ButtonSr = Key.Unbound, - }, - - RightJoyconStick = new JoyconConfigKeyboardStick - { - StickUp = Key.I, - StickDown = Key.K, - StickLeft = Key.J, - StickRight = Key.L, - StickButton = Key.H, - }, - }; - } - else if (_inputDevice.ActiveId.StartsWith("controller")) - { - bool isNintendoStyle = _inputDevice.ActiveText.Contains("Nintendo"); - - config = new StandardControllerInputConfig - { - Version = InputConfig.CurrentVersion, - Backend = InputBackendType.GamepadSDL2, - Id = null, - ControllerType = ControllerType.JoyconPair, - DeadzoneLeft = 0.1f, - DeadzoneRight = 0.1f, - RangeLeft = 1.0f, - RangeRight = 1.0f, - TriggerThreshold = 0.5f, - LeftJoycon = new LeftJoyconCommonConfig - { - DpadUp = ConfigGamepadInputId.DpadUp, - DpadDown = ConfigGamepadInputId.DpadDown, - DpadLeft = ConfigGamepadInputId.DpadLeft, - DpadRight = ConfigGamepadInputId.DpadRight, - ButtonMinus = ConfigGamepadInputId.Minus, - ButtonL = ConfigGamepadInputId.LeftShoulder, - ButtonZl = ConfigGamepadInputId.LeftTrigger, - ButtonSl = ConfigGamepadInputId.Unbound, - ButtonSr = ConfigGamepadInputId.Unbound, - }, - - LeftJoyconStick = new JoyconConfigControllerStick - { - Joystick = ConfigStickInputId.Left, - StickButton = ConfigGamepadInputId.LeftStick, - InvertStickX = false, - InvertStickY = false, - Rotate90CW = false, - }, - - RightJoycon = new RightJoyconCommonConfig - { - ButtonA = isNintendoStyle ? ConfigGamepadInputId.A : ConfigGamepadInputId.B, - ButtonB = isNintendoStyle ? ConfigGamepadInputId.B : ConfigGamepadInputId.A, - ButtonX = isNintendoStyle ? ConfigGamepadInputId.X : ConfigGamepadInputId.Y, - ButtonY = isNintendoStyle ? ConfigGamepadInputId.Y : ConfigGamepadInputId.X, - ButtonPlus = ConfigGamepadInputId.Plus, - ButtonR = ConfigGamepadInputId.RightShoulder, - ButtonZr = ConfigGamepadInputId.RightTrigger, - ButtonSl = ConfigGamepadInputId.Unbound, - ButtonSr = ConfigGamepadInputId.Unbound, - }, - - RightJoyconStick = new JoyconConfigControllerStick - { - Joystick = ConfigStickInputId.Right, - StickButton = ConfigGamepadInputId.RightStick, - InvertStickX = false, - InvertStickY = false, - Rotate90CW = false, - }, - - Motion = new StandardMotionConfigController - { - MotionBackend = MotionInputBackendType.GamepadDriver, - EnableMotion = true, - Sensitivity = 100, - GyroDeadzone = 1, - }, - Rumble = new RumbleConfigController - { - StrongRumble = 1f, - WeakRumble = 1f, - EnableRumble = false, - }, - }; - } - } - else - { - string path = System.IO.Path.Combine(GetProfileBasePath(), _profile.ActiveId); - - if (!File.Exists(path)) - { - if (pos >= 0) - { - _profile.Remove(pos); - } - - return; - } - - try - { - config = JsonHelper.DeserializeFromFile(path, _serializerContext.InputConfig); - } - catch (JsonException) { } - } - - SetValues(config); - } - - private void ProfileAdd_Activated(object sender, EventArgs args) - { - ((ToggleButton)sender).SetStateFlags(StateFlags.Normal, true); - - if (_inputDevice.ActiveId == "disabled") - { - return; - } - - InputConfig inputConfig = GetValues(); - ProfileDialog profileDialog = new(); - - if (inputConfig == null) - { - return; - } - - if (profileDialog.Run() == (int)ResponseType.Ok) - { - string path = System.IO.Path.Combine(GetProfileBasePath(), profileDialog.FileName); - string jsonString = JsonHelper.Serialize(inputConfig, _serializerContext.InputConfig); - - File.WriteAllText(path, jsonString); - } - - profileDialog.Dispose(); - - SetProfiles(); - } - - private void ProfileRemove_Activated(object sender, EventArgs args) - { - ((ToggleButton)sender).SetStateFlags(StateFlags.Normal, true); - - if (_inputDevice.ActiveId == "disabled" || _profile.ActiveId == "default" || _profile.ActiveId == null) - { - return; - } - - MessageDialog confirmDialog = GtkDialog.CreateConfirmationDialog("Deleting Profile", "This action is irreversible, are you sure you want to continue?"); - - if (confirmDialog.Run() == (int)ResponseType.Yes) - { - string path = System.IO.Path.Combine(GetProfileBasePath(), _profile.ActiveId); - - if (File.Exists(path)) - { - File.Delete(path); - } - - SetProfiles(); - } - } - - private void SaveToggle_Activated(object sender, EventArgs args) - { - InputConfig inputConfig = GetValues(); - - var newConfig = new List(); - newConfig.AddRange(ConfigurationState.Instance.Hid.InputConfig.Value); - - if (_inputConfig == null && inputConfig != null) - { - newConfig.Add(inputConfig); - } - else - { - if (_inputDevice.ActiveId == "disabled") - { - newConfig.Remove(_inputConfig); - } - else if (inputConfig != null) - { - int index = newConfig.IndexOf(_inputConfig); - - newConfig[index] = inputConfig; - } - } - - _mainWindow.RendererWidget?.NpadManager.ReloadConfiguration(newConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse); - - // Atomically replace and signal input change. - // NOTE: Do not modify InputConfig.Value directly as other code depends on the on-change event. - ConfigurationState.Instance.Hid.InputConfig.Value = newConfig; - - ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); - - Dispose(); - } - - private void CloseToggle_Activated(object sender, EventArgs args) - { - Dispose(); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Windows/ControllerWindow.glade b/src/Ryujinx.Gtk3/UI/Windows/ControllerWindow.glade deleted file mode 100644 index e433f5cc4..000000000 --- a/src/Ryujinx.Gtk3/UI/Windows/ControllerWindow.glade +++ /dev/null @@ -1,2241 +0,0 @@ - - - - - - 4 - 1 - 4 - - - 0.1 - 10 - 1.0 - 0.1 - 1.0 - - - 0.1 - 10 - 1.0 - 0.1 - 1.0 - - - 1 - 0.050000000000000003 - 0.01 - 0.10000000000000001 - - - 1 - 0.050000000000000003 - 0.01 - 0.10000000000000001 - - - 2 - 1.000000000000000003 - 0.01 - 0.10000000000000001 - - - 2 - 1.000000000000000003 - 0.01 - 0.10000000000000001 - - - 1 - 0.5 - 0.01 - 0.10000000000000001 - - - 100 - 0.01 - 0.01 - 0.10000000000000001 - 0.10000000000000001 - - - 1000 - 100 - 1 - 4 - - - 4 - 1 - 4 - - - False - Ryujinx - Controller Settings - True - center - 1200 - 720 - - - - - - True - False - vertical - - - True - True - in - - - True - False - - - True - False - vertical - - - True - False - 10 - 10 - 10 - - - True - False - - - True - False - 5 - Input Device - - - False - True - 0 - - - - - True - False - 0 - disabled - - Disabled - - - - - True - True - 1 - - - - - False - True - 0 - - - - - True - False - 20 - - - True - False - The controller's type - center - 5 - Controller Type: - - - False - True - 0 - - - - - True - False - The controller's type - 0 - - - - False - True - 1 - - - - - False - True - 1 - - - - - True - False - 20 - - - True - False - 5 - Profile: - - - False - True - 0 - - - - - True - False - 5 - 0 - default - - - False - True - 1 - - - - - Load - 60 - True - True - True - 5 - - - - False - True - 2 - - - - - Add - 60 - True - True - True - 5 - - - - False - True - 3 - - - - - Remove - 60 - True - True - True - - - - False - True - 4 - - - - - False - True - 2 - - - - - False - True - 0 - - - - - True - False - - - True - False - vertical - - - True - False - 10 - 5 - - - 156 - True - False - 10 - vertical - - - True - False - 5 - 5 - Buttons - - - - - - False - True - 0 - - - - - True - False - 3 - 10 - - - 80 - True - False - A - - - 0 - 0 - - - - - 80 - True - False - B - - - 0 - 1 - - - - - 80 - True - False - X - - - 0 - 2 - - - - - 80 - True - False - Y - - - 0 - 3 - - - - - - 70 - True - True - True - - - 1 - 0 - - - - - - 70 - True - True - True - - - 1 - 1 - - - - - - 70 - True - True - True - - - 1 - 2 - - - - - - 70 - True - True - True - - - 1 - 3 - - - - - 80 - True - False - + - - - 0 - 4 - - - - - 80 - True - False - - - - - 0 - 5 - - - - - - 70 - True - True - True - - - 1 - 5 - - - - - - 70 - True - True - True - - - 1 - 4 - - - - - False - True - 1 - - - - - False - True - 0 - - - - - True - False - - - False - True - 1 - - - - - 160 - True - False - 10 - 10 - vertical - - - True - False - 5 - 5 - Left Stick - - - - - - False - True - 0 - - - - - True - False - 5 - 3 - 10 - - - 80 - True - False - LStick Button - 0 - - - 0 - 0 - - - - - - 65 - True - True - True - - - 1 - 0 - - - - - False - True - 1 - - - - - True - False - 3 - 10 - - - - 65 - True - True - True - - - 1 - 1 - - - - - - 65 - True - True - True - - - 1 - 0 - - - - - - 65 - True - True - True - - - 1 - 2 - - - - - - 65 - True - True - True - - - 1 - 3 - - - - - 80 - True - False - LStick Down - 0 - - - 0 - 1 - - - - - 80 - True - False - LStick Up - 0 - - - 0 - 0 - - - - - 80 - True - False - LStick Right - 0 - - - 0 - 3 - - - - - 80 - True - False - LStick Left - 0 - - - 0 - 2 - - - - - False - True - 2 - - - - - True - False - 3 - 10 - - - 80 - True - False - LStick - 0 - - - 0 - 0 - - - - - - 65 - True - True - True - - - 1 - 0 - - - - - Invert Stick X - True - True - False - True - - - 2 - 0 - - - - - Invert Stick Y - True - True - False - True - - - 2 - 1 - - - - - Rotate 90° Clockwise - True - True - False - True - - - 2 - 2 - - - - - False - True - 3 - - - - - True - False - 10 - vertical - - - True - False - start - Deadzone Left - - - False - True - 0 - - - - - True - True - _controllerDeadzoneLeft - 2 - 2 - - - True - True - 1 - - - - - False - True - 4 - - - - - True - False - 10 - vertical - - - True - False - start - Range Left - - - False - True - 0 - - - - - True - True - _controllerRangeLeft - 2 - 2 - - - True - True - 1 - - - - - False - True - 5 - - - - - False - True - 2 - - - - - True - False - - - False - True - 3 - - - - - 150 - True - False - 10 - 10 - vertical - - - True - False - 5 - 5 - Triggers - - - - - - False - True - 0 - - - - - True - False - 3 - 10 - - - 80 - True - False - L - - - 0 - 0 - - - - - 80 - True - False - R - - - 0 - 1 - - - - - - 65 - True - True - True - - - 1 - 0 - - - - - - 65 - True - True - True - - - 1 - 1 - - - - - 80 - True - False - ZL - - - 0 - 2 - - - - - 80 - True - False - ZR - - - 0 - 3 - - - - - - 65 - True - True - True - - - 1 - 2 - - - - - - 65 - True - True - True - - - 1 - 3 - - - - - False - True - 1 - - - - - _sideTriggerBox - True - False - 5 - 3 - 10 - - - 80 - True - False - Left SL - - - 0 - 0 - - - - - 80 - True - False - Left SR - - - 0 - 1 - - - - - - 65 - True - True - True - - - 1 - 0 - - - - - - 65 - True - True - True - - - 1 - 1 - - - - - False - True - 2 - - - - - _sideTriggerBox - True - False - 5 - 3 - 10 - - - 80 - True - False - Right SL - - - 0 - 0 - - - - - 80 - True - False - Right SR - - - 0 - 1 - - - - - - 65 - True - True - True - - - 1 - 0 - - - - - - 65 - True - True - True - - - 1 - 1 - - - - - False - True - 3 - - - - - True - False - 10 - vertical - - - True - False - start - 10 - Trigger Threshold - - - False - True - 0 - - - - - True - True - _controllerTriggerThreshold - 2 - 2 - - - True - True - 1 - - - - - False - True - 4 - - - - - False - True - 4 - - - - - False - True - 0 - - - - - True - False - 10 - - - 156 - True - False - 10 - 10 - vertical - - - True - False - 5 - 5 - Directional Pad - - - - - - False - True - 0 - - - - - True - False - 3 - 10 - - - 80 - True - False - Dpad Up - 0 - - - 0 - 0 - - - - - 80 - True - False - Dpad Down - 0 - - - 0 - 1 - - - - - 80 - True - False - Dpad Left - 0 - - - 0 - 2 - - - - - 80 - True - False - Dpad Right - 0 - - - 0 - 3 - - - - - - 70 - True - True - True - - - 1 - 0 - - - - - - 70 - True - True - True - - - 1 - 1 - - - - - - 70 - True - True - True - - - 1 - 2 - - - - - - 70 - True - True - True - - - 1 - 3 - - - - - False - True - 1 - - - - - True - False - 10 - vertical - - - True - False - 10 - 5 - Rumble - - - - - - False - True - 0 - - - - - Enable - True - True - False - True - - - False - True - 1 - - - - - True - False - 10 - vertical - - - True - False - start - Strong rumble multiplier - - - False - True - 0 - - - - - True - True - _controllerStrongRumble - 1 - 1 - - - True - True - 1 - - - - - False - True - 2 - - - - - True - False - 10 - vertical - - - True - False - start - Weak rumble multiplier - - - False - True - 0 - - - - - True - True - _controllerWeakRumble - 1 - 1 - - - True - True - 1 - - - - - False - True - 3 - - - - - False - True - 2 - - - - - False - True - 0 - - - - - True - False - - - False - True - 1 - - - - - 160 - True - False - 10 - 10 - vertical - - - True - False - 5 - 5 - Right Stick - - - - - - False - True - 0 - - - - - True - False - 5 - 3 - 10 - - - 80 - True - False - RStick Button - 0 - - - 0 - 0 - - - - - - 65 - True - True - True - - - 1 - 0 - - - - - False - True - 1 - - - - - True - False - 3 - 10 - - - 80 - True - False - RStick Up - 0 - - - 0 - 0 - - - - - 80 - True - False - RStick Down - 0 - - - 0 - 1 - - - - - 80 - True - False - RStick Left - 0 - - - 0 - 2 - - - - - 80 - True - False - RStick Right - 0 - - - 0 - 3 - - - - - - 65 - True - True - True - - - 1 - 0 - - - - - - 65 - True - True - True - - - 1 - 1 - - - - - - 65 - True - True - True - - - 1 - 2 - - - - - - 65 - True - True - True - - - 1 - 3 - - - - - False - True - 2 - - - - - True - False - 3 - 10 - - - 80 - True - False - RStick - 0 - - - 0 - 0 - - - - - - 65 - True - True - True - - - 1 - 0 - - - - - Invert Stick X - True - True - False - True - - - 2 - 0 - - - - - Invert Stick Y - True - True - False - True - - - 2 - 1 - - - - - Rotate 90° Clockwise - True - True - False - True - - - 2 - 2 - - - - - False - True - 3 - - - - - True - False - 10 - vertical - - - True - False - start - Deadzone Right - - - False - True - 0 - - - - - True - True - _controllerDeadzoneRight - 2 - 2 - - - True - True - 1 - - - - - False - True - 4 - - - - - True - False - 10 - vertical - - - True - False - start - Range Right - - - False - True - 0 - - - - - True - True - _controllerRangeRight - 2 - 2 - - - True - True - 1 - - - - - False - True - 5 - - - - - False - True - 2 - - - - - True - False - - - False - True - 3 - - - - - True - False - 10 - 10 - vertical - 5 - - - True - False - 5 - 5 - Motion - - - - - - False - True - 0 - - - - - Enable Motion Controls - True - True - False - True - - - False - True - 1 - - - - - Use CemuHook compatible motion - True - True - False - True - - - False - True - 2 - - - - - True - False - 10 - - - True - False - 17 - Controller Slot - - - False - True - 5 - 0 - - - - - True - True - 10 - _slotNumber - 1 - True - True - - - False - True - 1 - - - - - False - True - 5 - 3 - - - - - True - False - 10 - - - True - False - 5 - Gyro Sensitivity % - - - False - True - 5 - 0 - - - - - True - True - 0 - _sensitivity - 1 - True - True - - - False - True - 1 - - - - - False - True - 5 - 4 - - - - - True - False - vertical - - - Mirror Input - True - True - False - True - - - False - True - 0 - - - - - True - False - 10 - - - True - False - Right JoyCon Slot - - - False - True - 5 - 0 - - - - - True - True - 0 - _altSlotNumber - 1 - True - True - - - False - True - 1 - - - - - False - True - 5 - 1 - - - - - False - True - 5 - - - - - True - False - 30 - - - True - False - Server Host - - - False - True - 5 - 0 - - - - - True - True - - - False - True - 1 - - - - - False - True - 5 - 6 - - - - - True - False - 30 - - - True - False - Server Port - - - False - True - 5 - 0 - - - - - True - True - - - False - True - 1 - - - - - False - True - 5 - 7 - - - - - True - False - start - Gyro Deadzone - - - False - True - 8 - - - - - True - True - _gyroDeadzone - 2 - 2 - - - True - True - 9 - - - - - False - True - 4 - - - - - False - True - 1 - - - - - True - True - 0 - - - - - True - False - 10 - 20 - 5 - 5 - - - True - True - 1 - - - - - True - True - 1 - - - - - - - - - True - True - 0 - - - - - True - False - 5 - 3 - 3 - end - - - Save - True - True - True - - - - False - True - 0 - - - - - Close - True - True - True - 4 - - - - False - True - 5 - 1 - - - - - False - False - 1 - - - - - - diff --git a/src/Ryujinx.Gtk3/UI/Windows/DlcWindow.cs b/src/Ryujinx.Gtk3/UI/Windows/DlcWindow.cs deleted file mode 100644 index fb3189e1c..000000000 --- a/src/Ryujinx.Gtk3/UI/Windows/DlcWindow.cs +++ /dev/null @@ -1,288 +0,0 @@ -using Gtk; -using LibHac.Common; -using LibHac.Fs; -using LibHac.Fs.Fsa; -using LibHac.Tools.Fs; -using LibHac.Tools.FsSystem; -using LibHac.Tools.FsSystem.NcaUtils; -using Ryujinx.Common.Configuration; -using Ryujinx.Common.Utilities; -using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.Loaders.Processes.Extensions; -using Ryujinx.HLE.Utilities; -using Ryujinx.UI.App.Common; -using Ryujinx.UI.Widgets; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using GUI = Gtk.Builder.ObjectAttribute; - -namespace Ryujinx.UI.Windows -{ - public class DlcWindow : Window - { - private readonly VirtualFileSystem _virtualFileSystem; - private readonly string _applicationIdBase; - private readonly string _dlcJsonPath; - private readonly List _dlcContainerList; - - private static readonly DownloadableContentJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - -#pragma warning disable CS0649, IDE0044 // Field is never assigned to, Add readonly modifier - [GUI] Label _baseTitleInfoLabel; - [GUI] TreeView _dlcTreeView; - [GUI] TreeSelection _dlcTreeSelection; -#pragma warning restore CS0649, IDE0044 - - public DlcWindow(VirtualFileSystem virtualFileSystem, string applicationIdBase, ApplicationData applicationData) : this(new Builder("Ryujinx.Gtk3.UI.Windows.DlcWindow.glade"), virtualFileSystem, applicationIdBase, applicationData) { } - - private DlcWindow(Builder builder, VirtualFileSystem virtualFileSystem, string applicationIdBase, ApplicationData applicationData) : base(builder.GetRawOwnedObject("_dlcWindow")) - { - builder.Autoconnect(this); - - _applicationIdBase = applicationIdBase; - _virtualFileSystem = virtualFileSystem; - _dlcJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, _applicationIdBase, "dlc.json"); - _baseTitleInfoLabel.Text = $"DLC Available for {applicationData.Name} [{applicationIdBase.ToUpper()}]"; - - try - { - _dlcContainerList = JsonHelper.DeserializeFromFile(_dlcJsonPath, _serializerContext.ListDownloadableContentContainer); - } - catch - { - _dlcContainerList = new List(); - } - - _dlcTreeView.Model = new TreeStore(typeof(bool), typeof(string), typeof(string)); - - CellRendererToggle enableToggle = new(); - enableToggle.Toggled += (sender, args) => - { - _dlcTreeView.Model.GetIter(out TreeIter treeIter, new TreePath(args.Path)); - bool newValue = !(bool)_dlcTreeView.Model.GetValue(treeIter, 0); - _dlcTreeView.Model.SetValue(treeIter, 0, newValue); - - if (_dlcTreeView.Model.IterChildren(out TreeIter childIter, treeIter)) - { - do - { - _dlcTreeView.Model.SetValue(childIter, 0, newValue); - } - while (_dlcTreeView.Model.IterNext(ref childIter)); - } - }; - - _dlcTreeView.AppendColumn("Enabled", enableToggle, "active", 0); - _dlcTreeView.AppendColumn("ApplicationId", new CellRendererText(), "text", 1); - _dlcTreeView.AppendColumn("Path", new CellRendererText(), "text", 2); - - foreach (DownloadableContentContainer dlcContainer in _dlcContainerList) - { - if (File.Exists(dlcContainer.ContainerPath)) - { - // The parent tree item has its own "enabled" check box, but it's the actual - // nca entries that store the enabled / disabled state. A bit of a UI inconsistency. - // Maybe a tri-state check box would be better, but for now we check the parent - // "enabled" box if all child NCAs are enabled. Usually fine since each nsp has only one nca. - bool areAllContentPacksEnabled = dlcContainer.DownloadableContentNcaList.TrueForAll((nca) => nca.Enabled); - TreeIter parentIter = ((TreeStore)_dlcTreeView.Model).AppendValues(areAllContentPacksEnabled, "", dlcContainer.ContainerPath); - - using IFileSystem partitionFileSystem = PartitionFileSystemUtils.OpenApplicationFileSystem(dlcContainer.ContainerPath, _virtualFileSystem, false); - - if (partitionFileSystem == null) - { - continue; - } - - foreach (DownloadableContentNca dlcNca in dlcContainer.DownloadableContentNcaList) - { - using var ncaFile = new UniqueRef(); - - partitionFileSystem.OpenFile(ref ncaFile.Ref, dlcNca.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), dlcContainer.ContainerPath); - - if (nca != null) - { - ((TreeStore)_dlcTreeView.Model).AppendValues(parentIter, dlcNca.Enabled, nca.Header.TitleId.ToString("X16"), dlcNca.FullPath); - } - } - } - else - { - // DLC file moved or renamed. Allow the user to remove it without crashing the whole dialog. - TreeIter parentIter = ((TreeStore)_dlcTreeView.Model).AppendValues(false, "", $"(MISSING) {dlcContainer.ContainerPath}"); - } - } - - // NOTE: Try to load downloadable contents from PFS last to preserve enabled state. - AddDlc(applicationData.Path, true); - } - - private Nca TryCreateNca(IStorage ncaStorage, string containerPath) - { - try - { - return new Nca(_virtualFileSystem.KeySet, ncaStorage); - } - catch (Exception exception) - { - GtkDialog.CreateErrorDialog($"{exception.Message}. Errored File: {containerPath}"); - } - - return null; - } - - private void AddDlc(string path, bool ignoreNotFound = false) - { - if (!File.Exists(path) || _dlcContainerList.Any(x => x.ContainerPath == path)) - { - return; - } - - using IFileSystem partitionFileSystem = PartitionFileSystemUtils.OpenApplicationFileSystem(path, _virtualFileSystem); - - bool containsDlc = false; - - TreeIter? parentIter = null; - - foreach (DirectoryEntryEx fileEntry in partitionFileSystem.EnumerateEntries("/", "*.nca")) - { - using var ncaFile = new UniqueRef(); - - partitionFileSystem.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), path); - - if (nca == null) - { - continue; - } - - if (nca.Header.ContentType == NcaContentType.PublicData) - { - if (nca.GetProgramIdBase() != ulong.Parse(_applicationIdBase, NumberStyles.HexNumber)) - { - continue; - } - - parentIter ??= ((TreeStore)_dlcTreeView.Model).AppendValues(true, "", path); - - ((TreeStore)_dlcTreeView.Model).AppendValues(parentIter.Value, true, nca.Header.TitleId.ToString("X16"), fileEntry.FullPath); - containsDlc = true; - } - } - - if (!containsDlc && !ignoreNotFound) - { - GtkDialog.CreateErrorDialog("The specified file does not contain DLC for the selected title!"); - } - } - - private void AddButton_Clicked(object sender, EventArgs args) - { - FileChooserNative fileChooser = new("Select DLC files", this, FileChooserAction.Open, "Add", "Cancel") - { - SelectMultiple = true, - }; - - FileFilter filter = new() - { - Name = "Switch Game DLCs", - }; - filter.AddPattern("*.nsp"); - - fileChooser.AddFilter(filter); - - if (fileChooser.Run() == (int)ResponseType.Accept) - { - foreach (string containerPath in fileChooser.Filenames) - { - AddDlc(containerPath); - } - } - - fileChooser.Dispose(); - } - - private void RemoveButton_Clicked(object sender, EventArgs args) - { - if (_dlcTreeSelection.GetSelected(out ITreeModel treeModel, out TreeIter treeIter)) - { - if (_dlcTreeView.Model.IterParent(out TreeIter parentIter, treeIter) && _dlcTreeView.Model.IterNChildren(parentIter) <= 1) - { - ((TreeStore)treeModel).Remove(ref parentIter); - } - else - { - ((TreeStore)treeModel).Remove(ref treeIter); - } - } - } - - private void RemoveAllButton_Clicked(object sender, EventArgs args) - { - List toRemove = new(); - - if (_dlcTreeView.Model.GetIterFirst(out TreeIter iter)) - { - do - { - toRemove.Add(iter); - } - while (_dlcTreeView.Model.IterNext(ref iter)); - } - - foreach (TreeIter i in toRemove) - { - TreeIter j = i; - ((TreeStore)_dlcTreeView.Model).Remove(ref j); - } - } - - private void SaveButton_Clicked(object sender, EventArgs args) - { - _dlcContainerList.Clear(); - - if (_dlcTreeView.Model.GetIterFirst(out TreeIter parentIter)) - { - do - { - if (_dlcTreeView.Model.IterChildren(out TreeIter childIter, parentIter)) - { - DownloadableContentContainer dlcContainer = new() - { - ContainerPath = (string)_dlcTreeView.Model.GetValue(parentIter, 2), - DownloadableContentNcaList = new List(), - }; - - do - { - dlcContainer.DownloadableContentNcaList.Add(new DownloadableContentNca - { - Enabled = (bool)_dlcTreeView.Model.GetValue(childIter, 0), - TitleId = Convert.ToUInt64(_dlcTreeView.Model.GetValue(childIter, 1).ToString(), 16), - FullPath = (string)_dlcTreeView.Model.GetValue(childIter, 2), - }); - } - while (_dlcTreeView.Model.IterNext(ref childIter)); - - _dlcContainerList.Add(dlcContainer); - } - } - while (_dlcTreeView.Model.IterNext(ref parentIter)); - } - - JsonHelper.SerializeToFile(_dlcJsonPath, _dlcContainerList, _serializerContext.ListDownloadableContentContainer); - - Dispose(); - } - - private void CancelButton_Clicked(object sender, EventArgs args) - { - Dispose(); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Windows/DlcWindow.glade b/src/Ryujinx.Gtk3/UI/Windows/DlcWindow.glade deleted file mode 100644 index bdb0e647a..000000000 --- a/src/Ryujinx.Gtk3/UI/Windows/DlcWindow.glade +++ /dev/null @@ -1,202 +0,0 @@ - - - - - - False - Ryujinx - DLC Manager - True - center - 550 - 350 - - - True - False - vertical - - - True - False - vertical - - - True - False - 10 - 10 - 10 - 10 - Available DLC - - - False - True - 0 - - - - - True - True - 10 - 10 - in - - - True - False - - - True - True - False - - - - - - - - - - True - True - 1 - - - - - True - True - 0 - - - - - True - False - - - True - False - 10 - 10 - start - - - Add - True - True - True - Adds a DLC to this list - 10 - - - - True - True - 0 - - - - - Remove - True - True - True - Removes the selected DLC - 10 - - - - True - True - 1 - - - - - Remove All - True - True - True - Removes all DLCs - 10 - - - - True - True - 2 - - - - - True - True - 0 - - - - - True - False - 10 - 10 - end - - - Save - True - True - True - 10 - 2 - 2 - - - - True - True - 0 - - - - - Cancel - True - True - True - 10 - 2 - 2 - - - - True - True - 1 - - - - - True - True - 1 - - - - - False - True - 1 - - - - - - - - - diff --git a/src/Ryujinx.Gtk3/UI/Windows/SettingsWindow.cs b/src/Ryujinx.Gtk3/UI/Windows/SettingsWindow.cs deleted file mode 100644 index dc467c0f2..000000000 --- a/src/Ryujinx.Gtk3/UI/Windows/SettingsWindow.cs +++ /dev/null @@ -1,847 +0,0 @@ -using Gtk; -using LibHac.Tools.FsSystem; -using Ryujinx.Audio.Backends.OpenAL; -using Ryujinx.Audio.Backends.SDL2; -using Ryujinx.Audio.Backends.SoundIo; -using Ryujinx.Common.Configuration; -using Ryujinx.Common.Configuration.Hid; -using Ryujinx.Common.Configuration.Multiplayer; -using Ryujinx.Common.GraphicsDriver; -using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.HOS.Services.Time.TimeZone; -using Ryujinx.UI.Common.Configuration; -using Ryujinx.UI.Common.Configuration.System; -using Ryujinx.UI.Helper; -using Ryujinx.UI.Widgets; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Net.NetworkInformation; -using System.Reflection; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using GUI = Gtk.Builder.ObjectAttribute; - -namespace Ryujinx.UI.Windows -{ - public class SettingsWindow : Window - { - private readonly MainWindow _parent; - private readonly ListStore _gameDirsBoxStore; - private readonly ListStore _audioBackendStore; - private readonly TimeZoneContentManager _timeZoneContentManager; - private readonly HashSet _validTzRegions; - - private long _systemTimeOffset; - private float _previousVolumeLevel; - private bool _directoryChanged = false; - -#pragma warning disable CS0649, IDE0044 // Field is never assigned to, Add readonly modifier - [GUI] CheckButton _traceLogToggle; - [GUI] CheckButton _errorLogToggle; - [GUI] CheckButton _warningLogToggle; - [GUI] CheckButton _infoLogToggle; - [GUI] CheckButton _stubLogToggle; - [GUI] CheckButton _debugLogToggle; - [GUI] CheckButton _fileLogToggle; - [GUI] CheckButton _guestLogToggle; - [GUI] CheckButton _fsAccessLogToggle; - [GUI] Adjustment _fsLogSpinAdjustment; - [GUI] ComboBoxText _graphicsDebugLevel; - [GUI] CheckButton _dockedModeToggle; - [GUI] CheckButton _discordToggle; - [GUI] CheckButton _checkUpdatesToggle; - [GUI] CheckButton _showConfirmExitToggle; - [GUI] RadioButton _hideCursorNever; - [GUI] RadioButton _hideCursorOnIdle; - [GUI] RadioButton _hideCursorAlways; - [GUI] CheckButton _vSyncToggle; - [GUI] CheckButton _shaderCacheToggle; - [GUI] CheckButton _textureRecompressionToggle; - [GUI] CheckButton _macroHLEToggle; - [GUI] CheckButton _ptcToggle; - [GUI] CheckButton _internetToggle; - [GUI] CheckButton _fsicToggle; - [GUI] RadioButton _mmSoftware; - [GUI] RadioButton _mmHost; - [GUI] RadioButton _mmHostUnsafe; - [GUI] CheckButton _expandRamToggle; - [GUI] CheckButton _ignoreToggle; - [GUI] CheckButton _directKeyboardAccess; - [GUI] CheckButton _directMouseAccess; - [GUI] ComboBoxText _systemLanguageSelect; - [GUI] ComboBoxText _systemRegionSelect; - [GUI] Entry _systemTimeZoneEntry; - [GUI] EntryCompletion _systemTimeZoneCompletion; - [GUI] Box _audioBackendBox; - [GUI] ComboBox _audioBackendSelect; - [GUI] Label _audioVolumeLabel; - [GUI] Scale _audioVolumeSlider; - [GUI] SpinButton _systemTimeYearSpin; - [GUI] SpinButton _systemTimeMonthSpin; - [GUI] SpinButton _systemTimeDaySpin; - [GUI] SpinButton _systemTimeHourSpin; - [GUI] SpinButton _systemTimeMinuteSpin; - [GUI] Adjustment _systemTimeYearSpinAdjustment; - [GUI] Adjustment _systemTimeMonthSpinAdjustment; - [GUI] Adjustment _systemTimeDaySpinAdjustment; - [GUI] Adjustment _systemTimeHourSpinAdjustment; - [GUI] Adjustment _systemTimeMinuteSpinAdjustment; - [GUI] ComboBoxText _multiLanSelect; - [GUI] ComboBoxText _multiModeSelect; - [GUI] CheckButton _custThemeToggle; - [GUI] Entry _custThemePath; - [GUI] ToggleButton _browseThemePath; - [GUI] Label _custThemePathLabel; - [GUI] TreeView _gameDirsBox; - [GUI] Entry _addGameDirBox; - [GUI] ComboBoxText _galThreading; - [GUI] Entry _graphicsShadersDumpPath; - [GUI] ComboBoxText _anisotropy; - [GUI] ComboBoxText _aspectRatio; - [GUI] ComboBoxText _antiAliasing; - [GUI] ComboBoxText _scalingFilter; - [GUI] ComboBoxText _graphicsBackend; - [GUI] ComboBoxText _preferredGpu; - [GUI] ComboBoxText _resScaleCombo; - [GUI] Entry _resScaleText; - [GUI] Adjustment _scalingFilterLevel; - [GUI] Scale _scalingFilterSlider; - [GUI] ToggleButton _configureController1; - [GUI] ToggleButton _configureController2; - [GUI] ToggleButton _configureController3; - [GUI] ToggleButton _configureController4; - [GUI] ToggleButton _configureController5; - [GUI] ToggleButton _configureController6; - [GUI] ToggleButton _configureController7; - [GUI] ToggleButton _configureController8; - [GUI] ToggleButton _configureControllerH; - -#pragma warning restore CS0649, IDE0044 - - public SettingsWindow(MainWindow parent, VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this(parent, new Builder("Ryujinx.Gtk3.UI.Windows.SettingsWindow.glade"), virtualFileSystem, contentManager) { } - - private SettingsWindow(MainWindow parent, Builder builder, VirtualFileSystem virtualFileSystem, ContentManager contentManager) : base(builder.GetRawOwnedObject("_settingsWin")) - { - Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Logo_Ryujinx.png"); - - _parent = parent; - - builder.Autoconnect(this); - - _timeZoneContentManager = new TimeZoneContentManager(); - _timeZoneContentManager.InitializeInstance(virtualFileSystem, contentManager, IntegrityCheckLevel.None); - - _validTzRegions = new HashSet(_timeZoneContentManager.LocationNameCache.Length, StringComparer.Ordinal); // Zone regions are identifiers. Must match exactly. - - // Bind Events. - _configureController1.Pressed += (sender, args) => ConfigureController_Pressed(sender, PlayerIndex.Player1); - _configureController2.Pressed += (sender, args) => ConfigureController_Pressed(sender, PlayerIndex.Player2); - _configureController3.Pressed += (sender, args) => ConfigureController_Pressed(sender, PlayerIndex.Player3); - _configureController4.Pressed += (sender, args) => ConfigureController_Pressed(sender, PlayerIndex.Player4); - _configureController5.Pressed += (sender, args) => ConfigureController_Pressed(sender, PlayerIndex.Player5); - _configureController6.Pressed += (sender, args) => ConfigureController_Pressed(sender, PlayerIndex.Player6); - _configureController7.Pressed += (sender, args) => ConfigureController_Pressed(sender, PlayerIndex.Player7); - _configureController8.Pressed += (sender, args) => ConfigureController_Pressed(sender, PlayerIndex.Player8); - _configureControllerH.Pressed += (sender, args) => ConfigureController_Pressed(sender, PlayerIndex.Handheld); - _systemTimeZoneEntry.FocusOutEvent += TimeZoneEntry_FocusOut; - - _resScaleCombo.Changed += (sender, args) => _resScaleText.Visible = _resScaleCombo.ActiveId == "-1"; - _scalingFilter.Changed += (sender, args) => _scalingFilterSlider.Visible = _scalingFilter.ActiveId == "2"; - _galThreading.Changed += (sender, args) => - { - if (_galThreading.ActiveId != ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString()) - { - GtkDialog.CreateInfoDialog("Warning - Backend Threading", "Ryujinx must be restarted after changing this option for it to apply fully. Depending on your platform, you may need to manually disable your driver's own multithreading when using Ryujinx's."); - } - }; - - // Setup Currents. - if (ConfigurationState.Instance.Logger.EnableTrace) - { - _traceLogToggle.Click(); - } - - if (ConfigurationState.Instance.Logger.EnableFileLog) - { - _fileLogToggle.Click(); - } - - if (ConfigurationState.Instance.Logger.EnableError) - { - _errorLogToggle.Click(); - } - - if (ConfigurationState.Instance.Logger.EnableWarn) - { - _warningLogToggle.Click(); - } - - if (ConfigurationState.Instance.Logger.EnableInfo) - { - _infoLogToggle.Click(); - } - - if (ConfigurationState.Instance.Logger.EnableStub) - { - _stubLogToggle.Click(); - } - - if (ConfigurationState.Instance.Logger.EnableDebug) - { - _debugLogToggle.Click(); - } - - if (ConfigurationState.Instance.Logger.EnableGuest) - { - _guestLogToggle.Click(); - } - - if (ConfigurationState.Instance.Logger.EnableFsAccessLog) - { - _fsAccessLogToggle.Click(); - } - - foreach (GraphicsDebugLevel level in Enum.GetValues()) - { - _graphicsDebugLevel.Append(level.ToString(), level.ToString()); - } - - _graphicsDebugLevel.SetActiveId(ConfigurationState.Instance.Logger.GraphicsDebugLevel.Value.ToString()); - - if (ConfigurationState.Instance.System.EnableDockedMode) - { - _dockedModeToggle.Click(); - } - - if (ConfigurationState.Instance.EnableDiscordIntegration) - { - _discordToggle.Click(); - } - - if (ConfigurationState.Instance.CheckUpdatesOnStart) - { - _checkUpdatesToggle.Click(); - } - - if (ConfigurationState.Instance.ShowConfirmExit) - { - _showConfirmExitToggle.Click(); - } - - switch (ConfigurationState.Instance.HideCursor.Value) - { - case HideCursorMode.Never: - _hideCursorNever.Click(); - break; - case HideCursorMode.OnIdle: - _hideCursorOnIdle.Click(); - break; - case HideCursorMode.Always: - _hideCursorAlways.Click(); - break; - } - - if (ConfigurationState.Instance.Graphics.EnableVsync) - { - _vSyncToggle.Click(); - } - - if (ConfigurationState.Instance.Graphics.EnableShaderCache) - { - _shaderCacheToggle.Click(); - } - - if (ConfigurationState.Instance.Graphics.EnableTextureRecompression) - { - _textureRecompressionToggle.Click(); - } - - if (ConfigurationState.Instance.Graphics.EnableMacroHLE) - { - _macroHLEToggle.Click(); - } - - if (ConfigurationState.Instance.System.EnablePtc) - { - _ptcToggle.Click(); - } - - if (ConfigurationState.Instance.System.EnableInternetAccess) - { - _internetToggle.Click(); - } - - if (ConfigurationState.Instance.System.EnableFsIntegrityChecks) - { - _fsicToggle.Click(); - } - - switch (ConfigurationState.Instance.System.MemoryManagerMode.Value) - { - case MemoryManagerMode.SoftwarePageTable: - _mmSoftware.Click(); - break; - case MemoryManagerMode.HostMapped: - _mmHost.Click(); - break; - case MemoryManagerMode.HostMappedUnsafe: - _mmHostUnsafe.Click(); - break; - } - - if (ConfigurationState.Instance.System.ExpandRam) - { - _expandRamToggle.Click(); - } - - if (ConfigurationState.Instance.System.IgnoreMissingServices) - { - _ignoreToggle.Click(); - } - - if (ConfigurationState.Instance.Hid.EnableKeyboard) - { - _directKeyboardAccess.Click(); - } - - if (ConfigurationState.Instance.Hid.EnableMouse) - { - _directMouseAccess.Click(); - } - - if (ConfigurationState.Instance.UI.EnableCustomTheme) - { - _custThemeToggle.Click(); - } - - // Custom EntryCompletion Columns. If added to glade, need to override more signals - ListStore tzList = new(typeof(string), typeof(string), typeof(string)); - _systemTimeZoneCompletion.Model = tzList; - - CellRendererText offsetCol = new(); - CellRendererText abbrevCol = new(); - - _systemTimeZoneCompletion.PackStart(offsetCol, false); - _systemTimeZoneCompletion.AddAttribute(offsetCol, "text", 0); - _systemTimeZoneCompletion.TextColumn = 1; // Regions Column - _systemTimeZoneCompletion.PackStart(abbrevCol, false); - _systemTimeZoneCompletion.AddAttribute(abbrevCol, "text", 2); - - int maxLocationLength = 0; - - foreach (var (offset, location, abbr) in _timeZoneContentManager.ParseTzOffsets()) - { - var hours = Math.DivRem(offset, 3600, out int seconds); - var minutes = Math.Abs(seconds) / 60; - - var abbr2 = (abbr.StartsWith('+') || abbr.StartsWith('-')) ? string.Empty : abbr; - - tzList.AppendValues($"UTC{hours:+0#;-0#;+00}:{minutes:D2} ", location, abbr2); - _validTzRegions.Add(location); - - maxLocationLength = Math.Max(maxLocationLength, location.Length); - } - - _systemTimeZoneEntry.WidthChars = Math.Max(20, maxLocationLength + 1); // Ensure minimum Entry width - _systemTimeZoneEntry.Text = _timeZoneContentManager.SanityCheckDeviceLocationName(ConfigurationState.Instance.System.TimeZone); - - _systemTimeZoneCompletion.MatchFunc = TimeZoneMatchFunc; - - _systemLanguageSelect.SetActiveId(ConfigurationState.Instance.System.Language.Value.ToString()); - _systemRegionSelect.SetActiveId(ConfigurationState.Instance.System.Region.Value.ToString()); - _galThreading.SetActiveId(ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString()); - _resScaleCombo.SetActiveId(ConfigurationState.Instance.Graphics.ResScale.Value.ToString()); - _anisotropy.SetActiveId(ConfigurationState.Instance.Graphics.MaxAnisotropy.Value.ToString()); - _aspectRatio.SetActiveId(((int)ConfigurationState.Instance.Graphics.AspectRatio.Value).ToString()); - _graphicsBackend.SetActiveId(((int)ConfigurationState.Instance.Graphics.GraphicsBackend.Value).ToString()); - _antiAliasing.SetActiveId(((int)ConfigurationState.Instance.Graphics.AntiAliasing.Value).ToString()); - _scalingFilter.SetActiveId(((int)ConfigurationState.Instance.Graphics.ScalingFilter.Value).ToString()); - - UpdatePreferredGpuComboBox(); - - _graphicsBackend.Changed += (sender, e) => UpdatePreferredGpuComboBox(); - PopulateNetworkInterfaces(); - _multiLanSelect.SetActiveId(ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value); - _multiModeSelect.SetActiveId(ConfigurationState.Instance.Multiplayer.Mode.Value.ToString()); - - _custThemePath.Buffer.Text = ConfigurationState.Instance.UI.CustomThemePath; - _resScaleText.Buffer.Text = ConfigurationState.Instance.Graphics.ResScaleCustom.Value.ToString(); - _scalingFilterLevel.Value = ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value; - _resScaleText.Visible = _resScaleCombo.ActiveId == "-1"; - _scalingFilterSlider.Visible = _scalingFilter.ActiveId == "2"; - _graphicsShadersDumpPath.Buffer.Text = ConfigurationState.Instance.Graphics.ShadersDumpPath; - _fsLogSpinAdjustment.Value = ConfigurationState.Instance.System.FsGlobalAccessLogMode; - _systemTimeOffset = ConfigurationState.Instance.System.SystemTimeOffset; - - _gameDirsBox.AppendColumn("", new CellRendererText(), "text", 0); - _gameDirsBoxStore = new ListStore(typeof(string)); - _gameDirsBox.Model = _gameDirsBoxStore; - - foreach (string gameDir in ConfigurationState.Instance.UI.GameDirs.Value) - { - _gameDirsBoxStore.AppendValues(gameDir); - } - - if (_custThemeToggle.Active == false) - { - _custThemePath.Sensitive = false; - _custThemePathLabel.Sensitive = false; - _browseThemePath.Sensitive = false; - } - - // Setup system time spinners - UpdateSystemTimeSpinners(); - - _audioBackendStore = new ListStore(typeof(string), typeof(AudioBackend)); - - TreeIter openAlIter = _audioBackendStore.AppendValues("OpenAL", AudioBackend.OpenAl); - TreeIter soundIoIter = _audioBackendStore.AppendValues("SoundIO", AudioBackend.SoundIo); - TreeIter sdl2Iter = _audioBackendStore.AppendValues("SDL2", AudioBackend.SDL2); - TreeIter dummyIter = _audioBackendStore.AppendValues("Dummy", AudioBackend.Dummy); - - _audioBackendSelect = ComboBox.NewWithModelAndEntry(_audioBackendStore); - _audioBackendSelect.EntryTextColumn = 0; - _audioBackendSelect.Entry.IsEditable = false; - - switch (ConfigurationState.Instance.System.AudioBackend.Value) - { - case AudioBackend.OpenAl: - _audioBackendSelect.SetActiveIter(openAlIter); - break; - case AudioBackend.SoundIo: - _audioBackendSelect.SetActiveIter(soundIoIter); - break; - case AudioBackend.SDL2: - _audioBackendSelect.SetActiveIter(sdl2Iter); - break; - case AudioBackend.Dummy: - _audioBackendSelect.SetActiveIter(dummyIter); - break; - default: - throw new InvalidOperationException($"{nameof(ConfigurationState.Instance.System.AudioBackend)} contains an invalid value: {ConfigurationState.Instance.System.AudioBackend.Value}"); - } - - _audioBackendBox.Add(_audioBackendSelect); - _audioBackendSelect.Show(); - - _previousVolumeLevel = ConfigurationState.Instance.System.AudioVolume; - _audioVolumeLabel = new Label("Volume: "); - _audioVolumeSlider = new Scale(Orientation.Horizontal, 0, 100, 1); - _audioVolumeLabel.MarginStart = 10; - _audioVolumeSlider.ValuePos = PositionType.Right; - _audioVolumeSlider.WidthRequest = 200; - - _audioVolumeSlider.Value = _previousVolumeLevel * 100; - _audioVolumeSlider.ValueChanged += VolumeSlider_OnChange; - _audioBackendBox.Add(_audioVolumeLabel); - _audioBackendBox.Add(_audioVolumeSlider); - _audioVolumeLabel.Show(); - _audioVolumeSlider.Show(); - - bool openAlIsSupported = false; - bool soundIoIsSupported = false; - bool sdl2IsSupported = false; - - Task.Run(() => - { - openAlIsSupported = OpenALHardwareDeviceDriver.IsSupported; - soundIoIsSupported = !OperatingSystem.IsMacOS() && SoundIoHardwareDeviceDriver.IsSupported; - sdl2IsSupported = SDL2HardwareDeviceDriver.IsSupported; - }); - - // This function runs whenever the dropdown is opened - _audioBackendSelect.SetCellDataFunc(_audioBackendSelect.Cells[0], (layout, cell, model, iter) => - { - cell.Sensitive = ((AudioBackend)_audioBackendStore.GetValue(iter, 1)) switch - { - AudioBackend.OpenAl => openAlIsSupported, - AudioBackend.SoundIo => soundIoIsSupported, - AudioBackend.SDL2 => sdl2IsSupported, - AudioBackend.Dummy => true, - _ => throw new InvalidOperationException($"{nameof(_audioBackendStore)} contains an invalid value for iteration {iter}: {_audioBackendStore.GetValue(iter, 1)}"), - }; - }); - - if (OperatingSystem.IsMacOS()) - { - var store = (_graphicsBackend.Model as ListStore); - store.GetIter(out TreeIter openglIter, new TreePath(new[] { 1 })); - store.Remove(ref openglIter); - - _graphicsBackend.Model = store; - } - } - - private void UpdatePreferredGpuComboBox() - { - _preferredGpu.RemoveAll(); - - if (Enum.Parse(_graphicsBackend.ActiveId) == GraphicsBackend.Vulkan) - { - var devices = Graphics.Vulkan.VulkanRenderer.GetPhysicalDevices(); - string preferredGpuIdFromConfig = ConfigurationState.Instance.Graphics.PreferredGpu.Value; - string preferredGpuId = preferredGpuIdFromConfig; - bool noGpuId = string.IsNullOrEmpty(preferredGpuIdFromConfig); - - foreach (var device in devices) - { - string dGpu = device.IsDiscrete ? " (dGPU)" : ""; - _preferredGpu.Append(device.Id, $"{device.Name}{dGpu}"); - - // If there's no GPU selected yet, we just pick the first GPU. - // If there's a discrete GPU available, we always prefer that over the previous selection, - // as it is likely to have better performance and more features. - // If the configuration file already has a GPU selection, we always prefer that instead. - if (noGpuId && (string.IsNullOrEmpty(preferredGpuId) || device.IsDiscrete)) - { - preferredGpuId = device.Id; - } - } - - if (!string.IsNullOrEmpty(preferredGpuId)) - { - _preferredGpu.SetActiveId(preferredGpuId); - } - } - } - - private void PopulateNetworkInterfaces() - { - NetworkInterface[] interfaces = NetworkInterface.GetAllNetworkInterfaces(); - - foreach (NetworkInterface nif in interfaces) - { - string guid = nif.Id; - string name = nif.Name; - - _multiLanSelect.Append(guid, name); - } - } - - private void UpdateSystemTimeSpinners() - { - //Bind system time events - _systemTimeYearSpin.ValueChanged -= SystemTimeSpin_ValueChanged; - _systemTimeMonthSpin.ValueChanged -= SystemTimeSpin_ValueChanged; - _systemTimeDaySpin.ValueChanged -= SystemTimeSpin_ValueChanged; - _systemTimeHourSpin.ValueChanged -= SystemTimeSpin_ValueChanged; - _systemTimeMinuteSpin.ValueChanged -= SystemTimeSpin_ValueChanged; - - //Apply actual system time + SystemTimeOffset to system time spin buttons - DateTime systemTime = DateTime.Now.AddSeconds(_systemTimeOffset); - - _systemTimeYearSpinAdjustment.Value = systemTime.Year; - _systemTimeMonthSpinAdjustment.Value = systemTime.Month; - _systemTimeDaySpinAdjustment.Value = systemTime.Day; - _systemTimeHourSpinAdjustment.Value = systemTime.Hour; - _systemTimeMinuteSpinAdjustment.Value = systemTime.Minute; - - //Format spin buttons text to include leading zeros - _systemTimeYearSpin.Text = systemTime.Year.ToString("0000"); - _systemTimeMonthSpin.Text = systemTime.Month.ToString("00"); - _systemTimeDaySpin.Text = systemTime.Day.ToString("00"); - _systemTimeHourSpin.Text = systemTime.Hour.ToString("00"); - _systemTimeMinuteSpin.Text = systemTime.Minute.ToString("00"); - - //Bind system time events - _systemTimeYearSpin.ValueChanged += SystemTimeSpin_ValueChanged; - _systemTimeMonthSpin.ValueChanged += SystemTimeSpin_ValueChanged; - _systemTimeDaySpin.ValueChanged += SystemTimeSpin_ValueChanged; - _systemTimeHourSpin.ValueChanged += SystemTimeSpin_ValueChanged; - _systemTimeMinuteSpin.ValueChanged += SystemTimeSpin_ValueChanged; - } - - private void SaveSettings() - { - if (_directoryChanged) - { - List gameDirs = new(); - - _gameDirsBoxStore.GetIterFirst(out TreeIter treeIter); - - for (int i = 0; i < _gameDirsBoxStore.IterNChildren(); i++) - { - gameDirs.Add((string)_gameDirsBoxStore.GetValue(treeIter, 0)); - - _gameDirsBoxStore.IterNext(ref treeIter); - } - - ConfigurationState.Instance.UI.GameDirs.Value = gameDirs; - - _directoryChanged = false; - } - - HideCursorMode hideCursor = HideCursorMode.Never; - - if (_hideCursorOnIdle.Active) - { - hideCursor = HideCursorMode.OnIdle; - } - - if (_hideCursorAlways.Active) - { - hideCursor = HideCursorMode.Always; - } - - if (!float.TryParse(_resScaleText.Buffer.Text, out float resScaleCustom) || resScaleCustom <= 0.0f) - { - resScaleCustom = 1.0f; - } - - if (_validTzRegions.Contains(_systemTimeZoneEntry.Text)) - { - ConfigurationState.Instance.System.TimeZone.Value = _systemTimeZoneEntry.Text; - } - - MemoryManagerMode memoryMode = MemoryManagerMode.SoftwarePageTable; - - if (_mmHost.Active) - { - memoryMode = MemoryManagerMode.HostMapped; - } - - if (_mmHostUnsafe.Active) - { - memoryMode = MemoryManagerMode.HostMappedUnsafe; - } - - BackendThreading backendThreading = Enum.Parse(_galThreading.ActiveId); - if (ConfigurationState.Instance.Graphics.BackendThreading != backendThreading) - { - DriverUtilities.ToggleOGLThreading(backendThreading == BackendThreading.Off); - } - - ConfigurationState.Instance.Logger.EnableError.Value = _errorLogToggle.Active; - ConfigurationState.Instance.Logger.EnableTrace.Value = _traceLogToggle.Active; - ConfigurationState.Instance.Logger.EnableWarn.Value = _warningLogToggle.Active; - ConfigurationState.Instance.Logger.EnableInfo.Value = _infoLogToggle.Active; - ConfigurationState.Instance.Logger.EnableStub.Value = _stubLogToggle.Active; - ConfigurationState.Instance.Logger.EnableDebug.Value = _debugLogToggle.Active; - ConfigurationState.Instance.Logger.EnableGuest.Value = _guestLogToggle.Active; - ConfigurationState.Instance.Logger.EnableFsAccessLog.Value = _fsAccessLogToggle.Active; - ConfigurationState.Instance.Logger.EnableFileLog.Value = _fileLogToggle.Active; - ConfigurationState.Instance.Logger.GraphicsDebugLevel.Value = Enum.Parse(_graphicsDebugLevel.ActiveId); - ConfigurationState.Instance.System.EnableDockedMode.Value = _dockedModeToggle.Active; - ConfigurationState.Instance.EnableDiscordIntegration.Value = _discordToggle.Active; - ConfigurationState.Instance.CheckUpdatesOnStart.Value = _checkUpdatesToggle.Active; - ConfigurationState.Instance.ShowConfirmExit.Value = _showConfirmExitToggle.Active; - ConfigurationState.Instance.HideCursor.Value = hideCursor; - ConfigurationState.Instance.Graphics.EnableVsync.Value = _vSyncToggle.Active; - ConfigurationState.Instance.Graphics.EnableShaderCache.Value = _shaderCacheToggle.Active; - ConfigurationState.Instance.Graphics.EnableTextureRecompression.Value = _textureRecompressionToggle.Active; - ConfigurationState.Instance.Graphics.EnableMacroHLE.Value = _macroHLEToggle.Active; - ConfigurationState.Instance.System.EnablePtc.Value = _ptcToggle.Active; - ConfigurationState.Instance.System.EnableInternetAccess.Value = _internetToggle.Active; - ConfigurationState.Instance.System.EnableFsIntegrityChecks.Value = _fsicToggle.Active; - ConfigurationState.Instance.System.MemoryManagerMode.Value = memoryMode; - ConfigurationState.Instance.System.ExpandRam.Value = _expandRamToggle.Active; - ConfigurationState.Instance.System.IgnoreMissingServices.Value = _ignoreToggle.Active; - ConfigurationState.Instance.Hid.EnableKeyboard.Value = _directKeyboardAccess.Active; - ConfigurationState.Instance.Hid.EnableMouse.Value = _directMouseAccess.Active; - ConfigurationState.Instance.UI.EnableCustomTheme.Value = _custThemeToggle.Active; - ConfigurationState.Instance.System.Language.Value = Enum.Parse(_systemLanguageSelect.ActiveId); - ConfigurationState.Instance.System.Region.Value = Enum.Parse(_systemRegionSelect.ActiveId); - ConfigurationState.Instance.System.SystemTimeOffset.Value = _systemTimeOffset; - ConfigurationState.Instance.UI.CustomThemePath.Value = _custThemePath.Buffer.Text; - ConfigurationState.Instance.Graphics.ShadersDumpPath.Value = _graphicsShadersDumpPath.Buffer.Text; - ConfigurationState.Instance.System.FsGlobalAccessLogMode.Value = (int)_fsLogSpinAdjustment.Value; - ConfigurationState.Instance.Graphics.MaxAnisotropy.Value = float.Parse(_anisotropy.ActiveId, CultureInfo.InvariantCulture); - ConfigurationState.Instance.Graphics.AspectRatio.Value = Enum.Parse(_aspectRatio.ActiveId); - ConfigurationState.Instance.Graphics.BackendThreading.Value = backendThreading; - ConfigurationState.Instance.Graphics.GraphicsBackend.Value = Enum.Parse(_graphicsBackend.ActiveId); - ConfigurationState.Instance.Graphics.PreferredGpu.Value = _preferredGpu.ActiveId; - ConfigurationState.Instance.Graphics.ResScale.Value = int.Parse(_resScaleCombo.ActiveId); - ConfigurationState.Instance.Graphics.ResScaleCustom.Value = resScaleCustom; - ConfigurationState.Instance.System.AudioVolume.Value = (float)_audioVolumeSlider.Value / 100.0f; - ConfigurationState.Instance.Graphics.AntiAliasing.Value = Enum.Parse(_antiAliasing.ActiveId); - ConfigurationState.Instance.Graphics.ScalingFilter.Value = Enum.Parse(_scalingFilter.ActiveId); - ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value = (int)_scalingFilterLevel.Value; - ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value = _multiLanSelect.ActiveId; - - _previousVolumeLevel = ConfigurationState.Instance.System.AudioVolume.Value; - - ConfigurationState.Instance.Multiplayer.Mode.Value = Enum.Parse(_multiModeSelect.ActiveId); - ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value = _multiLanSelect.ActiveId; - - if (_audioBackendSelect.GetActiveIter(out TreeIter activeIter)) - { - ConfigurationState.Instance.System.AudioBackend.Value = (AudioBackend)_audioBackendStore.GetValue(activeIter, 1); - } - - ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); - - _parent.UpdateInternetAccess(); - MainWindow.UpdateGraphicsConfig(); - ThemeHelper.ApplyTheme(); - } - - // - // Events - // - private void TimeZoneEntry_FocusOut(object sender, FocusOutEventArgs e) - { - if (!_validTzRegions.Contains(_systemTimeZoneEntry.Text)) - { - _systemTimeZoneEntry.Text = _timeZoneContentManager.SanityCheckDeviceLocationName(ConfigurationState.Instance.System.TimeZone); - } - } - - private bool TimeZoneMatchFunc(EntryCompletion compl, string key, TreeIter iter) - { - key = key.Trim().Replace(' ', '_'); - - return ((string)compl.Model.GetValue(iter, 1)).Contains(key, StringComparison.OrdinalIgnoreCase) || // region - ((string)compl.Model.GetValue(iter, 2)).StartsWith(key, StringComparison.OrdinalIgnoreCase) || // abbr - ((string)compl.Model.GetValue(iter, 0))[3..].StartsWith(key); // offset - } - - private void SystemTimeSpin_ValueChanged(object sender, EventArgs e) - { - int year = _systemTimeYearSpin.ValueAsInt; - int month = _systemTimeMonthSpin.ValueAsInt; - int day = _systemTimeDaySpin.ValueAsInt; - int hour = _systemTimeHourSpin.ValueAsInt; - int minute = _systemTimeMinuteSpin.ValueAsInt; - - if (!DateTime.TryParse(year + "-" + month + "-" + day + " " + hour + ":" + minute, out DateTime newTime)) - { - UpdateSystemTimeSpinners(); - - return; - } - - newTime = newTime.AddSeconds(DateTime.Now.Second).AddMilliseconds(DateTime.Now.Millisecond); - - long systemTimeOffset = (long)Math.Ceiling((newTime - DateTime.Now).TotalMinutes) * 60L; - - if (_systemTimeOffset != systemTimeOffset) - { - _systemTimeOffset = systemTimeOffset; - UpdateSystemTimeSpinners(); - } - } - - private void AddDir_Pressed(object sender, EventArgs args) - { - if (Directory.Exists(_addGameDirBox.Buffer.Text)) - { - _gameDirsBoxStore.AppendValues(_addGameDirBox.Buffer.Text); - _directoryChanged = true; - } - else - { - FileChooserNative fileChooser = new("Choose the game directory to add to the list", this, FileChooserAction.SelectFolder, "Add", "Cancel") - { - SelectMultiple = true, - }; - - if (fileChooser.Run() == (int)ResponseType.Accept) - { - _directoryChanged = false; - foreach (string directory in fileChooser.Filenames) - { - if (_gameDirsBoxStore.GetIterFirst(out TreeIter treeIter)) - { - do - { - if (directory.Equals((string)_gameDirsBoxStore.GetValue(treeIter, 0))) - { - break; - } - } while (_gameDirsBoxStore.IterNext(ref treeIter)); - } - - if (!_directoryChanged) - { - _gameDirsBoxStore.AppendValues(directory); - } - } - - _directoryChanged = true; - } - - fileChooser.Dispose(); - } - - _addGameDirBox.Buffer.Text = ""; - - ((ToggleButton)sender).SetStateFlags(StateFlags.Normal, true); - } - - private void RemoveDir_Pressed(object sender, EventArgs args) - { - TreeSelection selection = _gameDirsBox.Selection; - - if (selection.GetSelected(out TreeIter treeIter)) - { - _gameDirsBoxStore.Remove(ref treeIter); - - _directoryChanged = true; - } - - ((ToggleButton)sender).SetStateFlags(StateFlags.Normal, true); - } - - private void CustThemeToggle_Activated(object sender, EventArgs args) - { - _custThemePath.Sensitive = _custThemeToggle.Active; - _custThemePathLabel.Sensitive = _custThemeToggle.Active; - _browseThemePath.Sensitive = _custThemeToggle.Active; - } - - private void BrowseThemeDir_Pressed(object sender, EventArgs args) - { - using (FileChooserNative fileChooser = new("Choose the theme to load", this, FileChooserAction.Open, "Select", "Cancel")) - { - FileFilter filter = new() - { - Name = "Theme Files", - }; - filter.AddPattern("*.css"); - - fileChooser.AddFilter(filter); - - if (fileChooser.Run() == (int)ResponseType.Accept) - { - _custThemePath.Buffer.Text = fileChooser.Filename; - } - } - - _browseThemePath.SetStateFlags(StateFlags.Normal, true); - } - - private void ConfigureController_Pressed(object sender, PlayerIndex playerIndex) - { - ((ToggleButton)sender).SetStateFlags(StateFlags.Normal, true); - - ControllerWindow controllerWindow = new(_parent, playerIndex); - - controllerWindow.SetSizeRequest((int)(controllerWindow.DefaultWidth * Program.WindowScaleFactor), (int)(controllerWindow.DefaultHeight * Program.WindowScaleFactor)); - controllerWindow.Show(); - } - - private void VolumeSlider_OnChange(object sender, EventArgs args) - { - ConfigurationState.Instance.System.AudioVolume.Value = (float)(_audioVolumeSlider.Value / 100); - } - - private void SaveToggle_Activated(object sender, EventArgs args) - { - SaveSettings(); - Dispose(); - } - - private void ApplyToggle_Activated(object sender, EventArgs args) - { - SaveSettings(); - } - - private void CloseToggle_Activated(object sender, EventArgs args) - { - ConfigurationState.Instance.System.AudioVolume.Value = _previousVolumeLevel; - Dispose(); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Windows/SettingsWindow.glade b/src/Ryujinx.Gtk3/UI/Windows/SettingsWindow.glade deleted file mode 100644 index f0dbd6b63..000000000 --- a/src/Ryujinx.Gtk3/UI/Windows/SettingsWindow.glade +++ /dev/null @@ -1,3221 +0,0 @@ - - - - - - 3 - 1 - 10 - - - 101 - 1 - 5 - 1 - - - 1 - 31 - 1 - 5 - - - 23 - 1 - 5 - - - 59 - 1 - 5 - - - 1 - 12 - 1 - 5 - - - 2000 - 2060 - 1 - 10 - - - 0 - True - True - - - False - Ryujinx - Settings - True - center - 650 - 650 - - - True - False - vertical - - - True - True - in - - - True - False - - - True - True - - - True - False - 5 - 10 - 5 - vertical - - - True - False - 5 - 5 - vertical - - - True - False - start - 5 - General - - - - - - False - True - 0 - - - - - True - False - 10 - 10 - vertical - - - Enable Discord Rich Presence - True - True - False - Choose whether or not to display Ryujinx on your "currently playing" Discord activity - start - True - - - False - True - 5 - 0 - - - - - Check for Updates on Launch - True - True - False - start - True - - - False - True - 5 - 1 - - - - - Show "Confirm Exit" Dialog - True - True - False - start - True - - - False - True - 5 - 2 - - - - - True - False - - - True - False - end - Hide Cursor: - - - False - True - 5 - 2 - - - - - Never - True - True - False - start - 5 - 5 - True - True - - - False - True - 3 - - - - - On Idle - True - True - False - start - 5 - 5 - True - _hideCursorNever - - - False - True - 4 - - - - - Always - True - True - False - start - 5 - 5 - True - _hideCursorNever - - - False - True - 5 - - - - - False - True - 5 - 4 - - - - - True - True - 1 - - - - - False - True - 5 - 1 - - - - - True - False - 5 - 5 - - - False - True - 5 - 2 - - - - - True - False - 5 - 5 - vertical - - - True - False - start - 5 - Game Directories - - - - - - False - True - 0 - - - - - True - False - 10 - 10 - vertical - - - True - True - 10 - in - - - True - True - False - False - - - - - - - - - True - True - 0 - - - - - True - False - - - True - True - Enter a game directory to add to the list - - - True - True - 0 - - - - - Add - 80 - True - True - True - Add a game directory to the list - 5 - - - - False - True - 1 - - - - - Remove - 80 - True - True - True - Remove selected game directory - 5 - - - - False - True - 3 - - - - - False - True - 1 - - - - - True - True - 1 - - - - - True - True - 5 - 4 - - - - - True - False - 5 - 5 - - - False - True - 5 - 5 - - - - - True - False - 5 - 5 - vertical - - - True - False - start - 5 - Themes - - - - - - False - True - 0 - - - - - True - False - 10 - 10 - vertical - - - Use Custom Theme - True - True - False - Enable or disable custom themes in the GUI - start - True - - - - False - True - 5 - 1 - - - - - True - False - - - True - False - Path to custom GUI theme - Custom Theme Path: - - - False - True - 5 - 0 - - - - - True - True - Path to custom GUI theme - center - - - True - True - 1 - - - - - Browse... - 80 - True - True - True - Browse for a custom GUI theme - 5 - - - - False - True - 2 - - - - - False - True - 10 - 2 - - - - - False - True - 1 - - - - - False - True - 5 - 6 - - - - - - - True - False - General - - - False - - - - - True - False - 5 - 10 - 5 - vertical - - - True - False - 5 - 5 - - - Enable Docked Mode - True - True - False - Docked mode makes the emulated system behave as a docked Nintendo Switch. This improves graphical fidelity in most games. Conversely, disabling this will make the emulated system behave as a handheld Nintendo Switch, reducing graphics quality. Configure player 1 controls if planning to use docked mode; configure handheld controls if planning to use handheld mode. Leave ON if unsure. - True - - - False - True - 10 - 0 - - - - - Direct Keyboard Access - True - True - False - Direct keyboard access (HID) support. Provides games access to your keyboard as a text entry device. - True - - - False - False - 10 - 1 - - - - - Direct Mouse Access - True - True - False - Direct mouse access (HID) support. Provides games access to your mouse as a pointing device. - True - - - False - False - 10 - 2 - - - - - False - True - 5 - 0 - - - - - True - False - - - False - True - 1 - - - - - - True - False - center - center - 20 - - - True - False - vertical - - - True - False - 20 - 20 - Player 1 - - - False - True - 0 - - - - - Configure - True - True - True - 20 - 20 - 20 - 20 - - - False - True - 1 - - - - - 0 - 0 - - - - - True - False - vertical - - - True - False - 20 - 20 - Player 3 - - - False - True - 0 - - - - - Configure - True - True - True - 20 - 20 - 20 - 20 - - - False - True - 1 - - - - - 4 - 0 - - - - - True - False - vertical - - - True - False - 20 - 20 - Player 2 - - - False - True - 0 - - - - - Configure - True - True - True - 20 - 20 - 20 - 20 - - - False - True - 1 - - - - - 2 - 0 - - - - - True - False - vertical - - - True - False - 20 - 20 - Handheld - - - False - True - 0 - - - - - Configure - True - True - True - 20 - 20 - 20 - 20 - - - False - True - 1 - - - - - 4 - 4 - - - - - True - False - vertical - - - True - False - 20 - 20 - Player 6 - - - False - True - 0 - - - - - Configure - True - True - True - 20 - 20 - 20 - 20 - - - False - True - 1 - - - - - 4 - 2 - - - - - True - False - vertical - - - True - False - 20 - 20 - Player 5 - - - False - True - 0 - - - - - Configure - True - True - True - 20 - 20 - 20 - 20 - - - False - True - 1 - - - - - 2 - 2 - - - - - True - False - vertical - - - True - False - 20 - 20 - Player 7 - - - False - True - 0 - - - - - Configure - True - True - True - 20 - 20 - 20 - 20 - - - False - True - 1 - - - - - 0 - 4 - - - - - True - False - vertical - - - True - False - 20 - 20 - Player 4 - - - False - True - 0 - - - - - Configure - True - True - True - 20 - 20 - 20 - 20 - - - False - True - 1 - - - - - 0 - 2 - - - - - True - False - vertical - - - True - False - 20 - 20 - Player 8 - - - False - True - 0 - - - - - Configure - True - True - True - 20 - 20 - 20 - 20 - - - False - True - 1 - - - - - 2 - 4 - - - - - True - False - - - 1 - 0 - - - - - True - False - - - 3 - 0 - - - - - True - False - - - 3 - 2 - - - - - True - False - - - 3 - 4 - - - - - True - False - - - 1 - 2 - - - - - True - False - - - 1 - 4 - - - - - True - False - - - 1 - 1 - - - - - True - False - - - 1 - 3 - - - - - True - False - - - 3 - 1 - - - - - True - False - - - 3 - 3 - - - - - True - False - - - 0 - 1 - - - - - True - False - - - 2 - 1 - - - - - True - False - - - 4 - 1 - - - - - True - False - - - 0 - 3 - - - - - True - False - - - 2 - 3 - - - - - True - False - - - 4 - 3 - - - - - True - True - 2 - - - - - True - False - - - False - True - 3 - - - - - 1 - - - - - True - False - Input - - - 1 - False - - - - - True - False - 5 - 10 - 5 - vertical - - - True - False - start - 5 - 5 - vertical - - - True - False - start - 5 - Core - - - - - - False - True - 0 - - - - - True - False - 10 - 10 - vertical - - - True - False - - - True - False - Change System Region - end - System Region: - - - False - True - 5 - 2 - - - - - True - False - Change System Region - 5 - - Japan - USA - Europe - Australia - China - Korea - Taiwan - - - - False - True - 3 - - - - - False - True - 5 - 0 - - - - - True - False - - - True - False - Change System Language - end - System Language: - - - False - True - 5 - 0 - - - - - True - False - Change System Language - - American English - British English - Canadian French - Chinese - Dutch - French - German - Italian - Japanese - Korean - Latin American Spanish - Portuguese - Russian - Simplified Chinese - Spanish - Taiwanese - Traditional Chinese - Brazilian Portuguese - - - - False - True - 1 - - - - - False - True - 5 - 1 - - - - - True - False - - - True - False - Change System TimeZone - end - System TimeZone: - - - False - True - 5 - 1 - - - - - True - True - Change System TimeZone - 5 - _systemTimeZoneCompletion - - - False - True - 2 - - - - - False - True - 5 - 2 - - - - - True - False - - - True - False - Change System Time - end - System Time: - - - False - True - 5 - 0 - - - - - True - True - 2000 - vertical - _systemTimeYearSpinAdjustment - True - 2000 - - - False - True - 1 - - - - - True - False - end - - - - - False - True - 5 - 2 - - - - - True - True - 1 - vertical - _systemTimeMonthSpinAdjustment - True - 1 - - - False - True - 3 - - - - - True - False - end - - - - - False - True - 5 - 4 - - - - - True - True - 1 - vertical - _systemTimeDaySpinAdjustment - True - 1 - - - False - True - 5 - - - - - True - True - 0 - vertical - _systemTimeHourSpinAdjustment - True - - - False - True - 6 - - - - - True - False - end - : - - - False - True - 5 - 7 - - - - - True - True - 0 - vertical - _systemTimeMinuteSpinAdjustment - True - - - False - True - 8 - - - - - False - True - 5 - 3 - - - - - Enable VSync - True - True - False - Emulated console's Vertical Sync. Essentially a frame-limiter for the majority of games; disabling it may cause games to run at higher speed or make loading screens take longer or get stuck. Can be toggled in-game with a hotkey of your preference. We recommend doing this if you plan on disabling it. Leave ON if unsure. - start - 5 - 5 - True - - - False - True - 4 - - - - - Enable PPTC (Profiled Persistent Translation Cache) - True - True - False - Saves translated JIT functions so that they do not need to be translated every time the game loads. Reduces stuttering and significantly speeds up boot times after the first boot of a game. Leave ON if unsure. - start - 5 - 5 - True - - - False - True - 6 - - - - - Enable Guest Internet Access - True - True - False - Allows the emulated application to connect to the Internet. Games with a LAN mode can connect to each other when this is enabled and the systems are connected to the same access point. This includes real consoles as well. Does NOT allow connecting to Nintendo servers. May cause crashing in certain games that try to connect to the Internet. Leave OFF if unsure. - start - 5 - 5 - True - - - False - True - 7 - - - - - Enable FS Integrity Checks - True - True - False - Checks for corrupt files when booting a game, and if corrupt files are detected, displays a hash error in the log. Has no impact on performance and is meant to help troubleshooting. Leave ON if unsure. - start - 5 - 5 - True - - - False - True - 8 - - - - - True - True - 1 - - - - - True - False - - - - - - True - False - Changes the backend used to render audio. SDL2 is the preferred one, while OpenAL and SoundIO are used as fallbacks. Dummy will have no sound. Set to SDL2 if unsure. - end - 5 - Audio Backend: - - - False - True - 5 - 2 - - - - - False - True - 5 - 2 - - - - - True - False - - - - - - True - False - Change how guest memory is mapped and accessed. Greatly affects emulated CPU performance. Set to HOST UNCHECKED if unsure. - end - 5 - Memory Manager Mode: - - - False - True - 5 - 2 - - - - - Software - True - True - False - Use a software page table for address translation. Highest accuracy but slowest performance. - start - 5 - 5 - True - - - False - True - 3 - - - - - Host (fast) - True - True - False - Directly map memory in the host address space. Much faster JIT compilation and execution. - start - 5 - 5 - True - _mmSoftware - - - False - True - 4 - - - - - Host Unchecked (fastest, unsafe) - True - True - False - Directly map memory, but do not mask the address within the guest address space before access. Faster, but at the cost of safety. The guest application can access memory from anywhere in Ryujinx, so only run programs you trust with this mode. - start - 5 - 5 - True - _mmSoftware - - - False - True - 5 - - - - - False - True - 5 - 3 - - - - - False - True - 5 - 0 - - - - - True - False - 5 - 5 - - - False - True - 5 - 1 - - - - - True - False - start - 5 - 5 - vertical - - - True - False - - - True - False - start - 5 - Hacks - - - - - - False - True - 0 - - - - - True - False - start - 5 - (may cause instability) - - - False - True - 1 - - - - - False - True - 1 - - - - - True - False - 10 - 10 - vertical - - - Use alternative memory layout (Developers) - True - True - False - Utilizes an alternative MemoryMode layout to mimic a Switch development model. This is only useful for higher-resolution texture packs or 4k resolution mods. Does NOT improve performance. Leave OFF if unsure. - start - 5 - 5 - True - - - False - True - 0 - - - - - Ignore Missing Services - True - True - False - Ignores unimplemented Horizon OS services. This may help in bypassing crashes when booting certain games. Leave OFF if unsure. - start - 5 - 5 - True - - - False - True - 1 - - - - - True - True - 2 - - - - - False - True - 5 - 4 - - - - - 2 - - - - - True - False - end - System - - - 2 - False - - - - - True - False - 5 - vertical - - - True - False - 5 - 5 - vertical - - - True - False - start - 5 - 5 - 5 - Features - - - - - - False - True - 0 - - - - - True - False - 10 - 10 - vertical - - - True - False - 5 - 5 - - - True - False - Executes graphics backend commands on a second thread. Speeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading. Set to AUTO if unsure. - Graphics Backend Multithreading: - - - False - True - 5 - 0 - - - - - True - False - Executes graphics backend commands on a second thread. Speeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading. Set to AUTO if unsure. - -1 - - Auto - Off - On - - - - False - True - 1 - - - - - False - True - 5 - 0 - - - - - True - False - 5 - 5 - - - True - False - Graphics Backend to use - Graphics Backend: - - - False - True - 5 - 0 - - - - - True - False - Graphics Backend to use - -1 - - Vulkan - OpenGL - - - - False - True - 1 - - - - - False - True - 5 - 1 - - - - - True - False - 5 - 5 - - - True - False - Preferred GPU (Vulkan only) - Preferred GPU: - - - False - True - 5 - 0 - - - - - True - False - Preferred GPU (Vulkan only) - -1 - - - False - True - 1 - - - - - False - True - 5 - 2 - - - - - False - True - 2 - - - - - False - True - 5 - 0 - - - - - True - False - 5 - 5 - vertical - - - True - False - start - 5 - 5 - 5 - Enhancements - - - - - - False - True - 0 - - - - - True - False - 10 - 10 - vertical - - - Enable Shader Cache - True - True - False - Saves a disk shader cache which reduces stuttering in subsequent runs. Leave ON if unsure. - start - 5 - 5 - True - - - False - True - 0 - - - - - Enable Texture Recompression - True - True - False - Enables or disables Texture Recompression. Reduces VRAM usage at the cost of texture quality, and may also increase stuttering - start - 5 - 5 - True - - - False - True - 1 - - - - - Enable Macro HLE - True - True - False - Enables or disables high-level emulation of Macro code. Improves performance but may cause graphical glitches in some games - start - 5 - 5 - True - - - False - True - 2 - - - - - True - False - 5 - 5 - - - True - False - Resolution Scale applied to applicable render targets. - Resolution Scale: - - - False - True - 5 - 0 - - - - - True - False - Resolution Scale applied to applicable render targets. - 1 - - Native (720p/1080p) - 2x (1440p/2160p) - 3x (2160p/3240p) - 4x (2880p/4320p) - Custom (not recommended) - - - - False - True - 1 - - - - - True - True - Floating point resolution scale, such as 1.5. Non-integral scales are more likely to cause issues or crash. - center - False - 1.0 - number - - - True - True - 2 - - - - - False - True - 5 - 3 - - - - - True - False - 5 - 5 - - - True - False - Applies a final effect to the game render - Post Processing Effect: - - - False - True - 5 - 0 - - - - - True - False - Applies anti-aliasing to the game render - 1 - - None - FXAA - SMAA Low - SMAA Medium - SMAA High - SMAA Ultra - - - - False - True - 1 - - - - - False - True - 5 - 4 - - - - - 100 - True - False - 5 - 5 - - - True - False - Enables Framebuffer Upscaling - Upscale: - - - False - True - 5 - 0 - - - - - True - False - Enables Framebuffer Upscaling - 1 - - Bilinear - Nearest - FSR - - - - False - True - 1 - - - - - 200 - True - True - 5 - _scalingFilterLevel - 1 - right - - - False - True - 3 - - - - - False - True - 5 - 5 - - - - - True - False - 5 - 5 - - - True - False - Level of Anisotropic Filtering (set to Auto to use the value requested by the game) - Anisotropic Filtering: - - - False - True - 5 - 0 - - - - - True - False - Level of Anisotropic Filtering (set to Auto to use the value requested by the game) - -1 - - Auto - 2x - 4x - 8x - 16x - - - - False - True - 1 - - - - - False - True - 5 - 6 - - - - - True - False - 5 - 5 - - - True - False - Aspect Ratio applied to the renderer window. - Aspect Ratio: - - - False - True - 5 - 0 - - - - - True - False - Aspect Ratio applied to the renderer window. - 1 - - 4:3 - 16:9 - 16:10 - 21:9 - 32:9 - Stretch to Fit Window - - - - False - True - 1 - - - - - False - True - 5 - 7 - - - - - False - True - 2 - - - - - False - True - 5 - 2 - - - - - True - False - - - False - True - 5 - 3 - - - - - True - False - 5 - 5 - vertical - - - True - False - start - 5 - 5 - 5 - Developer Options - - - - - - False - True - 0 - - - - - True - False - 10 - 10 - vertical - - - True - False - 5 - 5 - - - True - False - Graphics Shaders Dump Path - Graphics Shaders Dump Path: - - - False - True - 5 - 0 - - - - - True - True - Graphics Shaders Dump Path - center - False - - - True - True - 1 - - - - - False - True - 5 - 0 - - - - - False - True - 1 - - - - - False - True - 5 - 4 - - - - - 3 - - - - - True - False - Graphics - - - 3 - False - - - - - True - False - 5 - 10 - 5 - vertical - - - True - False - 5 - 5 - vertical - - - True - False - start - 5 - Logging - - - - - - False - True - 0 - - - - - True - False - start - 10 - 10 - vertical - - - Enable Logging to File - True - True - False - Saves console logging to a log file on disk. Does not affect performance. - start - 5 - 5 - True - - - False - True - 0 - - - - - Enable Stub Logs - True - True - False - Prints stub log messages in the console. Does not affect performance. - start - 5 - 5 - True - - - False - True - 3 - - - - - Enable Info Logs - True - True - False - Prints info log messages in the console. Does not affect performance. - start - 5 - 5 - True - - - False - True - 4 - - - - - Enable Warning Logs - True - True - False - Prints warning log messages in the console. Does not affect performance. - start - 5 - 5 - True - - - False - True - 5 - - - - - Enable Error Logs - True - True - False - Prints error log messages in the console. Does not affect performance. - start - 5 - 5 - True - - - False - True - 6 - - - - - Enable Guest Logs - True - True - False - Prints guest log messages in the console. Does not affect performance. - start - 5 - 5 - True - - - False - True - 7 - - - - - Enable Fs Access Logs - True - True - False - Enables FS access log output to the console. Possible modes are 0-3 - start - 5 - 5 - True - - - False - True - 8 - - - - - True - False - - - True - False - Enables FS access log output to the console. Possible modes are 0-3 - Fs Global Access Log Mode: - - - False - True - 5 - 0 - - - - - True - True - Enables FS access log output to the console. Possible modes are 0-3 - 0 - _fsLogSpinAdjustment - - - True - True - 1 - - - - - False - True - 5 - 9 - - - - - True - True - 1 - - - - - False - True - 5 - 0 - - - - - True - False - 5 - 5 - 10 - vertical - - - True - False - Use with care - start - 5 - Developer Options (WARNING: Will reduce performance) - - - - - - False - True - 0 - - - - - True - False - start - 10 - 10 - vertical - - - True - False - 5 - - - True - False - Requires appropriate log levels enabled. - Graphics Backend Log Level - - - False - True - 5 - 22 - - - - - True - False - Requires appropriate log levels enabled. - 5 - - - False - True - 22 - - - - - False - True - 1 - - - - - Enable Debug Logs - True - True - False - Prints debug log messages in the console. Only use this if specifically instructed by a staff member, as it will make logs difficult to read and worsen emulator performance. - start - 5 - 5 - True - - - False - True - 21 - - - - - Enable Trace Logs - True - True - False - Prints trace log messages in the console. Does not affect performance. - start - 5 - 5 - True - - - False - True - 22 - - - - - False - True - 1 - - - - - False - True - 5 - 22 - - - - - 4 - - - - - True - False - Logging - - - 4 - False - - - - - True - False - 5 - 10 - 5 - vertical - - - True - False - start - 5 - 5 - vertical - - - True - False - start - 5 - Multiplayer - - - - - - False - True - 0 - - - - - True - False - start - 10 - 10 - vertical - - - True - False - - - True - False - Change Multiplayer Mode - end - Mode: - - - False - True - 5 - 0 - - - - - True - False - Change Multiplayer Mode - Disabled - - Disabled - ldn_mitm - - - - False - True - 1 - - - - - False - True - 3 - - - - - True - True - 2 - - - - - False - True - 5 - 0 - - - - - True - False - start - 5 - 5 - vertical - - - True - False - start - 5 - LAN Mode - - - - - - False - True - 0 - - - - - True - False - start - 10 - 10 - vertical - - - True - False - - - True - False - The network interface used for LAN/LDN features - end - Network Interface: - - - False - True - 5 - 0 - - - - - True - False - The network interface used for LAN/LDN features - 0 - - Default - - - - False - True - 1 - - - - - False - True - 5 - 1 - - - - - True - False - start - 5 - To use LAN functionality in games, Enable Guest Internet Access must be checked in System. - True - - - False - True - 1 - - - - - True - True - 2 - - - - - False - True - 5 - 1 - - - - - 5 - - - - - True - False - Multiplayer - - - 5 - False - - - - - - - - - True - True - 0 - - - - - True - False - 5 - 3 - 3 - 5 - end - - - Save - True - True - True - - - - False - False - 0 - - - - - Close - True - True - True - - - - False - False - 1 - - - - - Apply - True - True - True - - - - True - True - 2 - - - - - False - False - 1 - - - - - - diff --git a/src/Ryujinx.Gtk3/UI/Windows/TitleUpdateWindow.cs b/src/Ryujinx.Gtk3/UI/Windows/TitleUpdateWindow.cs deleted file mode 100644 index a08f59597..000000000 --- a/src/Ryujinx.Gtk3/UI/Windows/TitleUpdateWindow.cs +++ /dev/null @@ -1,234 +0,0 @@ -using Gtk; -using LibHac.Common; -using LibHac.Fs; -using LibHac.Fs.Fsa; -using LibHac.Ncm; -using LibHac.Ns; -using LibHac.Tools.FsSystem; -using LibHac.Tools.FsSystem.NcaUtils; -using Ryujinx.Common.Configuration; -using Ryujinx.Common.Utilities; -using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.Loaders.Processes.Extensions; -using Ryujinx.HLE.Utilities; -using Ryujinx.UI.App.Common; -using Ryujinx.UI.Common.Configuration; -using Ryujinx.UI.Widgets; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using GUI = Gtk.Builder.ObjectAttribute; -using SpanHelpers = LibHac.Common.SpanHelpers; - -namespace Ryujinx.UI.Windows -{ - public class TitleUpdateWindow : Window - { - private readonly MainWindow _parent; - private readonly VirtualFileSystem _virtualFileSystem; - private readonly ApplicationData _applicationData; - private readonly string _updateJsonPath; - - private TitleUpdateMetadata _titleUpdateWindowData; - - private readonly Dictionary _radioButtonToPathDictionary; - private static readonly TitleUpdateMetadataJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - -#pragma warning disable CS0649, IDE0044 // Field is never assigned to, Add readonly modifier - [GUI] Label _baseTitleInfoLabel; - [GUI] Box _availableUpdatesBox; - [GUI] RadioButton _noUpdateRadioButton; -#pragma warning restore CS0649, IDE0044 - - public TitleUpdateWindow(MainWindow parent, VirtualFileSystem virtualFileSystem, ApplicationData applicationData) : this(new Builder("Ryujinx.Gtk3.UI.Windows.TitleUpdateWindow.glade"), parent, virtualFileSystem, applicationData) { } - - private TitleUpdateWindow(Builder builder, MainWindow parent, VirtualFileSystem virtualFileSystem, ApplicationData applicationData) : base(builder.GetRawOwnedObject("_titleUpdateWindow")) - { - _parent = parent; - - builder.Autoconnect(this); - - _applicationData = applicationData; - _virtualFileSystem = virtualFileSystem; - _updateJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, applicationData.IdBaseString, "updates.json"); - _radioButtonToPathDictionary = new Dictionary(); - - try - { - _titleUpdateWindowData = JsonHelper.DeserializeFromFile(_updateJsonPath, _serializerContext.TitleUpdateMetadata); - } - catch - { - _titleUpdateWindowData = new TitleUpdateMetadata - { - Selected = "", - Paths = new List(), - }; - } - - _baseTitleInfoLabel.Text = $"Updates Available for {applicationData.Name} [{applicationData.IdBaseString}]"; - - // Try to get updates from PFS first - AddUpdate(_applicationData.Path, true); - - foreach (string path in _titleUpdateWindowData.Paths) - { - AddUpdate(path); - } - - if (_titleUpdateWindowData.Selected == "") - { - _noUpdateRadioButton.Active = true; - } - else - { - foreach ((RadioButton update, var _) in _radioButtonToPathDictionary.Where(keyValuePair => keyValuePair.Value == _titleUpdateWindowData.Selected)) - { - update.Active = true; - } - } - } - - private void AddUpdate(string path, bool ignoreNotFound = false) - { - if (!File.Exists(path) || _radioButtonToPathDictionary.ContainsValue(path)) - { - return; - } - - IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks - ? IntegrityCheckLevel.ErrorOnInvalid - : IntegrityCheckLevel.None; - - try - { - using IFileSystem pfs = PartitionFileSystemUtils.OpenApplicationFileSystem(path, _virtualFileSystem); - - Dictionary updates = pfs.GetContentData(ContentMetaType.Patch, _virtualFileSystem, checkLevel); - - Nca patchNca = null; - Nca controlNca = null; - - if (updates.TryGetValue(_applicationData.Id, out ContentMetaData update)) - { - patchNca = update.GetNcaByType(_virtualFileSystem.KeySet, LibHac.Ncm.ContentType.Program); - controlNca = update.GetNcaByType(_virtualFileSystem.KeySet, LibHac.Ncm.ContentType.Control); - } - - if (controlNca != null && patchNca != null) - { - ApplicationControlProperty controlData = new(); - - using var nacpFile = new UniqueRef(); - - controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); - nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure(); - - string radioLabel = $"Version {controlData.DisplayVersionString.ToString()} - {path}"; - - if (System.IO.Path.GetExtension(path).ToLower() == ".xci") - { - radioLabel = "Bundled: " + radioLabel; - } - - RadioButton radioButton = new(radioLabel); - radioButton.JoinGroup(_noUpdateRadioButton); - - _availableUpdatesBox.Add(radioButton); - _radioButtonToPathDictionary.Add(radioButton, path); - - radioButton.Show(); - radioButton.Active = true; - } - else - { - if (!ignoreNotFound) - { - GtkDialog.CreateErrorDialog("The specified file does not contain an update for the selected title!"); - } - } - } - catch (Exception exception) - { - GtkDialog.CreateErrorDialog($"{exception.Message}. Errored File: {path}"); - } - } - - private void RemoveUpdates(bool removeSelectedOnly = false) - { - foreach (RadioButton radioButton in _noUpdateRadioButton.Group) - { - if (radioButton.Label != "No Update" && (!removeSelectedOnly || radioButton.Active)) - { - _availableUpdatesBox.Remove(radioButton); - _radioButtonToPathDictionary.Remove(radioButton); - radioButton.Dispose(); - } - } - } - - private void AddButton_Clicked(object sender, EventArgs args) - { - using FileChooserNative fileChooser = new("Select update files", this, FileChooserAction.Open, "Add", "Cancel"); - - fileChooser.SelectMultiple = true; - - FileFilter filter = new() - { - Name = "Switch Game Updates", - }; - filter.AddPattern("*.nsp"); - - fileChooser.AddFilter(filter); - - if (fileChooser.Run() == (int)ResponseType.Accept) - { - foreach (string path in fileChooser.Filenames) - { - AddUpdate(path); - } - } - } - - private void RemoveButton_Clicked(object sender, EventArgs args) - { - RemoveUpdates(true); - } - - private void RemoveAllButton_Clicked(object sender, EventArgs args) - { - RemoveUpdates(); - } - - private void SaveButton_Clicked(object sender, EventArgs args) - { - _titleUpdateWindowData.Paths.Clear(); - _titleUpdateWindowData.Selected = ""; - - foreach (string paths in _radioButtonToPathDictionary.Values) - { - _titleUpdateWindowData.Paths.Add(paths); - } - - foreach (RadioButton radioButton in _noUpdateRadioButton.Group) - { - if (radioButton.Active) - { - _titleUpdateWindowData.Selected = _radioButtonToPathDictionary.TryGetValue(radioButton, out string updatePath) ? updatePath : ""; - } - } - - JsonHelper.SerializeToFile(_updateJsonPath, _titleUpdateWindowData, _serializerContext.TitleUpdateMetadata); - - _parent.UpdateGameTable(); - - Dispose(); - } - - private void CancelButton_Clicked(object sender, EventArgs args) - { - Dispose(); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Windows/TitleUpdateWindow.glade b/src/Ryujinx.Gtk3/UI/Windows/TitleUpdateWindow.glade deleted file mode 100644 index cfbac86dd..000000000 --- a/src/Ryujinx.Gtk3/UI/Windows/TitleUpdateWindow.glade +++ /dev/null @@ -1,214 +0,0 @@ - - - - - - False - Ryujinx - Title Update Manager - True - center - 550 - 250 - - - True - False - vertical - - - True - False - vertical - - - True - False - 10 - 10 - 10 - 10 - Available Updates - - - False - True - 0 - - - - - True - True - 10 - 10 - in - - - True - False - - - True - False - vertical - - - No Update - True - True - False - True - True - - - False - True - 0 - - - - - - - - - True - True - 1 - - - - - True - True - 0 - - - - - True - False - - - True - False - 10 - 10 - start - - - Add - True - True - True - Adds an update to this list - 10 - - - - True - True - 0 - - - - - Remove - True - True - True - Removes the selected update - 10 - - - - True - True - 1 - - - - - Remove All - True - True - True - Removes all the updates - 10 - - - - True - True - 2 - - - - - True - True - 0 - - - - - True - False - 10 - 10 - end - - - Save - True - True - True - 10 - 2 - 2 - - - - True - True - 0 - - - - - Cancel - True - True - True - 10 - 2 - 2 - - - - True - True - 1 - - - - - True - True - 1 - - - - - False - True - 1 - - - - - - - - - diff --git a/src/Ryujinx.Gtk3/UI/Windows/UserProfilesManagerWindow.Designer.cs b/src/Ryujinx.Gtk3/UI/Windows/UserProfilesManagerWindow.Designer.cs deleted file mode 100644 index bc5a18f95..000000000 --- a/src/Ryujinx.Gtk3/UI/Windows/UserProfilesManagerWindow.Designer.cs +++ /dev/null @@ -1,255 +0,0 @@ -using Gtk; -using Pango; -using System; - -namespace Ryujinx.UI.Windows -{ - public partial class UserProfilesManagerWindow : Window - { - private Box _mainBox; - private Label _selectedLabel; - private Box _selectedUserBox; - private Image _selectedUserImage; - private Box _selectedUserInfoBox; - private Entry _selectedUserNameEntry; - private Label _selectedUserIdLabel; - private Box _selectedUserButtonsBox; - private Button _saveProfileNameButton; - private Button _changeProfileImageButton; - private Box _usersTreeViewBox; - private Label _availableUsersLabel; - private ScrolledWindow _usersTreeViewWindow; - private ListStore _tableStore; - private TreeView _usersTreeView; - private Box _bottomBox; - private Button _addButton; - private Button _deleteButton; - private Button _closeButton; - - private void InitializeComponent() - { - // - // UserProfilesManagerWindow - // - CanFocus = false; - Resizable = false; - Modal = true; - WindowPosition = WindowPosition.Center; - DefaultWidth = 620; - DefaultHeight = 548; - TypeHint = Gdk.WindowTypeHint.Dialog; - - // - // _mainBox - // - _mainBox = new Box(Orientation.Vertical, 0); - - // - // _selectedLabel - // - _selectedLabel = new Label("Selected User Profile:") - { - Margin = 15, - Attributes = new AttrList(), - Halign = Align.Start, - }; - _selectedLabel.Attributes.Insert(new Pango.AttrWeight(Weight.Bold)); - - // - // _viewBox - // - _usersTreeViewBox = new Box(Orientation.Vertical, 0); - - // - // _SelectedUserBox - // - _selectedUserBox = new Box(Orientation.Horizontal, 0) - { - MarginStart = 30, - }; - - // - // _selectedUserImage - // - _selectedUserImage = new Image(); - - // - // _selectedUserInfoBox - // - _selectedUserInfoBox = new Box(Orientation.Vertical, 0) - { - Homogeneous = true, - }; - - // - // _selectedUserNameEntry - // - _selectedUserNameEntry = new Entry("") - { - MarginStart = 15, - MaxLength = (int)MaxProfileNameLength, - }; - _selectedUserNameEntry.KeyReleaseEvent += SelectedUserNameEntry_KeyReleaseEvent; - - // - // _selectedUserIdLabel - // - _selectedUserIdLabel = new Label("") - { - MarginTop = 15, - MarginStart = 15, - }; - - // - // _selectedUserButtonsBox - // - _selectedUserButtonsBox = new Box(Orientation.Vertical, 0) - { - MarginEnd = 30, - }; - - // - // _saveProfileNameButton - // - _saveProfileNameButton = new Button() - { - Label = "Save Profile Name", - CanFocus = true, - ReceivesDefault = true, - Sensitive = false, - }; - _saveProfileNameButton.Clicked += EditProfileNameButton_Pressed; - - // - // _changeProfileImageButton - // - _changeProfileImageButton = new Button() - { - Label = "Change Profile Image", - CanFocus = true, - ReceivesDefault = true, - MarginTop = 10, - }; - _changeProfileImageButton.Clicked += ChangeProfileImageButton_Pressed; - - // - // _availableUsersLabel - // - _availableUsersLabel = new Label("Available User Profiles:") - { - Margin = 15, - Attributes = new AttrList(), - Halign = Align.Start, - }; - _availableUsersLabel.Attributes.Insert(new Pango.AttrWeight(Weight.Bold)); - - // - // _usersTreeViewWindow - // - _usersTreeViewWindow = new ScrolledWindow() - { - ShadowType = ShadowType.In, - CanFocus = true, - Expand = true, - MarginStart = 30, - MarginEnd = 30, - MarginBottom = 15, - }; - - // - // _tableStore - // - _tableStore = new ListStore(typeof(bool), typeof(Gdk.Pixbuf), typeof(string), typeof(Gdk.RGBA)); - - // - // _usersTreeView - // - _usersTreeView = new TreeView(_tableStore) - { - HoverSelection = true, - HeadersVisible = false, - }; - _usersTreeView.RowActivated += UsersTreeView_Activated; - - // - // _bottomBox - // - _bottomBox = new Box(Orientation.Horizontal, 0) - { - MarginStart = 30, - MarginEnd = 30, - MarginBottom = 15, - }; - - // - // _addButton - // - _addButton = new Button() - { - Label = "Add New Profile", - CanFocus = true, - ReceivesDefault = true, - HeightRequest = 35, - }; - _addButton.Clicked += AddButton_Pressed; - - // - // _deleteButton - // - _deleteButton = new Button() - { - Label = "Delete Selected Profile", - CanFocus = true, - ReceivesDefault = true, - HeightRequest = 35, - MarginStart = 10, - }; - _deleteButton.Clicked += DeleteButton_Pressed; - - // - // _closeButton - // - _closeButton = new Button() - { - Label = "Close", - CanFocus = true, - ReceivesDefault = true, - HeightRequest = 35, - WidthRequest = 80, - }; - _closeButton.Clicked += CloseButton_Pressed; - - ShowComponent(); - } - - private void ShowComponent() - { - _usersTreeViewWindow.Add(_usersTreeView); - - _usersTreeViewBox.Add(_usersTreeViewWindow); - _bottomBox.PackStart(_addButton, false, false, 0); - _bottomBox.PackStart(_deleteButton, false, false, 0); - _bottomBox.PackEnd(_closeButton, false, false, 0); - - _selectedUserInfoBox.Add(_selectedUserNameEntry); - _selectedUserInfoBox.Add(_selectedUserIdLabel); - - _selectedUserButtonsBox.Add(_saveProfileNameButton); - _selectedUserButtonsBox.Add(_changeProfileImageButton); - - _selectedUserBox.Add(_selectedUserImage); - _selectedUserBox.PackStart(_selectedUserInfoBox, false, false, 0); - _selectedUserBox.PackEnd(_selectedUserButtonsBox, false, false, 0); - - _mainBox.PackStart(_selectedLabel, false, false, 0); - _mainBox.PackStart(_selectedUserBox, false, true, 0); - _mainBox.PackStart(_availableUsersLabel, false, false, 0); - _mainBox.Add(_usersTreeViewBox); - _mainBox.Add(_bottomBox); - - Add(_mainBox); - - ShowAll(); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Windows/UserProfilesManagerWindow.cs b/src/Ryujinx.Gtk3/UI/Windows/UserProfilesManagerWindow.cs deleted file mode 100644 index 77afc5d1f..000000000 --- a/src/Ryujinx.Gtk3/UI/Windows/UserProfilesManagerWindow.cs +++ /dev/null @@ -1,326 +0,0 @@ -using Gtk; -using Ryujinx.Common.Memory; -using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.HOS.Services.Account.Acc; -using Ryujinx.UI.Common.Configuration; -using Ryujinx.UI.Widgets; -using SkiaSharp; -using System; -using System.Collections.Generic; -using System.IO; -using System.Reflection; -using System.Threading; -using System.Threading.Tasks; - -namespace Ryujinx.UI.Windows -{ - public partial class UserProfilesManagerWindow : Window - { - private const uint MaxProfileNameLength = 0x20; - - private readonly AccountManager _accountManager; - private readonly ContentManager _contentManager; - - private byte[] _bufferImageProfile; - private string _tempNewProfileName; - - private Gdk.RGBA _selectedColor; - - private readonly ManualResetEvent _avatarsPreloadingEvent = new(false); - - public UserProfilesManagerWindow(AccountManager accountManager, ContentManager contentManager, VirtualFileSystem virtualFileSystem) : base($"Ryujinx {Program.Version} - Manage User Profiles") - { - Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Logo_Ryujinx.png"); - - InitializeComponent(); - - _selectedColor.Red = 0.212; - _selectedColor.Green = 0.843; - _selectedColor.Blue = 0.718; - _selectedColor.Alpha = 1; - - _accountManager = accountManager; - _contentManager = contentManager; - - CellRendererToggle userSelectedToggle = new(); - userSelectedToggle.Toggled += UserSelectedToggle_Toggled; - - // NOTE: Uncomment following line when multiple selection of user profiles is supported. - //_usersTreeView.AppendColumn("Selected", userSelectedToggle, "active", 0); - _usersTreeView.AppendColumn("User Icon", new CellRendererPixbuf(), "pixbuf", 1); - _usersTreeView.AppendColumn("User Info", new CellRendererText(), "text", 2, "background-rgba", 3); - - _tableStore.SetSortColumnId(0, SortType.Descending); - - RefreshList(); - - if (_contentManager.GetCurrentFirmwareVersion() != null) - { - Task.Run(() => - { - AvatarWindow.PreloadAvatars(contentManager, virtualFileSystem); - _avatarsPreloadingEvent.Set(); - }); - } - } - - public void RefreshList() - { - _tableStore.Clear(); - - foreach (UserProfile userProfile in _accountManager.GetAllUsers()) - { - _tableStore.AppendValues(userProfile.AccountState == AccountState.Open, new Gdk.Pixbuf(userProfile.Image, 96, 96), $"{userProfile.Name}\n{userProfile.UserId}", Gdk.RGBA.Zero); - - if (userProfile.AccountState == AccountState.Open) - { - _selectedUserImage.Pixbuf = new Gdk.Pixbuf(userProfile.Image, 96, 96); - _selectedUserIdLabel.Text = userProfile.UserId.ToString(); - _selectedUserNameEntry.Text = userProfile.Name; - - _deleteButton.Sensitive = userProfile.UserId != AccountManager.DefaultUserId; - - _usersTreeView.Model.GetIterFirst(out TreeIter firstIter); - _tableStore.SetValue(firstIter, 3, _selectedColor); - } - } - } - - // - // Events - // - - private void UsersTreeView_Activated(object o, RowActivatedArgs args) - { - SelectUserTreeView(); - } - - private void UserSelectedToggle_Toggled(object o, ToggledArgs args) - { - SelectUserTreeView(); - } - - private void SelectUserTreeView() - { - // Get selected item informations. - _usersTreeView.Selection.GetSelected(out TreeIter selectedIter); - - Gdk.Pixbuf userPicture = (Gdk.Pixbuf)_tableStore.GetValue(selectedIter, 1); - - string userName = _tableStore.GetValue(selectedIter, 2).ToString().Split("\n")[0]; - string userId = _tableStore.GetValue(selectedIter, 2).ToString().Split("\n")[1]; - - // Unselect the first user. - _usersTreeView.Model.GetIterFirst(out TreeIter firstIter); - _tableStore.SetValue(firstIter, 0, false); - _tableStore.SetValue(firstIter, 3, Gdk.RGBA.Zero); - - // Set new informations. - _tableStore.SetValue(selectedIter, 0, true); - - _selectedUserImage.Pixbuf = userPicture; - _selectedUserNameEntry.Text = userName; - _selectedUserIdLabel.Text = userId; - _saveProfileNameButton.Sensitive = false; - - // Open the selected one. - _accountManager.OpenUser(new UserId(userId)); - - _deleteButton.Sensitive = userId != AccountManager.DefaultUserId.ToString(); - - _tableStore.SetValue(selectedIter, 3, _selectedColor); - } - - private void SelectedUserNameEntry_KeyReleaseEvent(object o, KeyReleaseEventArgs args) - { - if (_saveProfileNameButton.Sensitive == false) - { - _saveProfileNameButton.Sensitive = true; - } - } - - private void AddButton_Pressed(object sender, EventArgs e) - { - _tempNewProfileName = GtkDialog.CreateInputDialog(this, "Choose the Profile Name", "Please Enter a Profile Name", MaxProfileNameLength); - - if (_tempNewProfileName != "") - { - SelectProfileImage(true); - - if (_bufferImageProfile != null) - { - AddUser(); - } - } - } - - private void DeleteButton_Pressed(object sender, EventArgs e) - { - if (GtkDialog.CreateChoiceDialog("Delete User Profile", "Are you sure you want to delete the profile ?", "Deleting this profile will also delete all associated save data.")) - { - _accountManager.DeleteUser(GetSelectedUserId()); - - RefreshList(); - } - } - - private void EditProfileNameButton_Pressed(object sender, EventArgs e) - { - _saveProfileNameButton.Sensitive = false; - - _accountManager.SetUserName(GetSelectedUserId(), _selectedUserNameEntry.Text); - - RefreshList(); - } - - private void ProcessProfileImage(byte[] buffer) - { - using var image = SKBitmap.Decode(buffer); - - image.Resize(new SKImageInfo(256, 256), SKFilterQuality.High); - - using MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream(); - - image.Encode(streamJpg, SKEncodedImageFormat.Jpeg, 80); - - _bufferImageProfile = streamJpg.ToArray(); - } - - private void ProfileImageFileChooser() - { - FileChooserNative fileChooser = new("Import Custom Profile Image", this, FileChooserAction.Open, "Import", "Cancel") - { - SelectMultiple = false, - }; - - FileFilter filter = new() - { - Name = "Custom Profile Images", - }; - filter.AddPattern("*.jpg"); - filter.AddPattern("*.jpeg"); - filter.AddPattern("*.png"); - filter.AddPattern("*.bmp"); - - fileChooser.AddFilter(filter); - - if (fileChooser.Run() == (int)ResponseType.Accept) - { - ProcessProfileImage(File.ReadAllBytes(fileChooser.Filename)); - } - - fileChooser.Dispose(); - } - - private void SelectProfileImage(bool newUser = false) - { - if (_contentManager.GetCurrentFirmwareVersion() == null) - { - ProfileImageFileChooser(); - } - else - { - Dictionary buttons = new() - { - { 0, "Import Image File" }, - { 1, "Select Firmware Avatar" }, - }; - - ResponseType responseDialog = GtkDialog.CreateCustomDialog("Profile Image Selection", - "Choose a Profile Image", - "You may import a custom profile image, or select an avatar from the system firmware.", - buttons, MessageType.Question); - - if (responseDialog == 0) - { - ProfileImageFileChooser(); - } - else if (responseDialog == (ResponseType)1) - { - AvatarWindow avatarWindow = new() - { - NewUser = newUser, - }; - - avatarWindow.DeleteEvent += AvatarWindow_DeleteEvent; - - avatarWindow.SetSizeRequest((int)(avatarWindow.DefaultWidth * Program.WindowScaleFactor), (int)(avatarWindow.DefaultHeight * Program.WindowScaleFactor)); - avatarWindow.Show(); - } - } - } - - private void ChangeProfileImageButton_Pressed(object sender, EventArgs e) - { - if (_contentManager.GetCurrentFirmwareVersion() != null) - { - _avatarsPreloadingEvent.WaitOne(); - } - - SelectProfileImage(); - - if (_bufferImageProfile != null) - { - SetUserImage(); - } - } - - private void AvatarWindow_DeleteEvent(object sender, DeleteEventArgs args) - { - _bufferImageProfile = ((AvatarWindow)sender).SelectedProfileImage; - - if (_bufferImageProfile != null) - { - if (((AvatarWindow)sender).NewUser) - { - AddUser(); - } - else - { - SetUserImage(); - } - } - } - - private void AddUser() - { - _accountManager.AddUser(_tempNewProfileName, _bufferImageProfile); - - _bufferImageProfile = null; - _tempNewProfileName = ""; - - RefreshList(); - } - - private void SetUserImage() - { - _accountManager.SetUserImage(GetSelectedUserId(), _bufferImageProfile); - - _bufferImageProfile = null; - - RefreshList(); - } - - private UserId GetSelectedUserId() - { - if (_usersTreeView.Model.GetIterFirst(out TreeIter iter)) - { - do - { - if ((bool)_tableStore.GetValue(iter, 0)) - { - break; - } - } - while (_usersTreeView.Model.IterNext(ref iter)); - } - - return new UserId(_tableStore.GetValue(iter, 2).ToString().Split("\n")[1]); - } - - private void CloseButton_Pressed(object sender, EventArgs e) - { - Close(); - } - } -} diff --git a/src/Ryujinx.HLE.Generators/IpcServiceGenerator.cs b/src/Ryujinx.HLE.Generators/IpcServiceGenerator.cs index 19fdbe197..5cac4d13a 100644 --- a/src/Ryujinx.HLE.Generators/IpcServiceGenerator.cs +++ b/src/Ryujinx.HLE.Generators/IpcServiceGenerator.cs @@ -13,6 +13,7 @@ namespace Ryujinx.HLE.Generators var syntaxReceiver = (ServiceSyntaxReceiver)context.SyntaxReceiver; CodeGenerator generator = new CodeGenerator(); + generator.AppendLine("#nullable enable"); generator.AppendLine("using System;"); generator.EnterScope($"namespace Ryujinx.HLE.HOS.Services.Sm"); generator.EnterScope($"partial class IUserInterface"); @@ -22,10 +23,10 @@ namespace Ryujinx.HLE.Generators { if (className.Modifiers.Any(SyntaxKind.AbstractKeyword) || className.Modifiers.Any(SyntaxKind.PrivateKeyword) || !className.AttributeLists.Any(x => x.Attributes.Any(y => y.ToString().StartsWith("Service")))) continue; - var name = GetFullName(className, context).Replace("global::", ""); + var name = GetFullName(className, context).Replace("global::", string.Empty); if (!name.StartsWith("Ryujinx.HLE.HOS.Services")) continue; - var constructors = className.ChildNodes().Where(x => x.IsKind(SyntaxKind.ConstructorDeclaration)).Select(y => y as ConstructorDeclarationSyntax); + var constructors = className.ChildNodes().Where(x => x.IsKind(SyntaxKind.ConstructorDeclaration)).Select(y => y as ConstructorDeclarationSyntax).ToArray(); if (!constructors.Any(x => x.ParameterList.Parameters.Count >= 1)) continue; @@ -58,6 +59,7 @@ namespace Ryujinx.HLE.Generators generator.LeaveScope(); generator.LeaveScope(); + generator.AppendLine("#nullable disable"); context.AddSource($"IUserInterface.g.cs", generator.ToString()); } diff --git a/src/Ryujinx.HLE.Generators/Ryujinx.HLE.Generators.csproj b/src/Ryujinx.HLE.Generators/Ryujinx.HLE.Generators.csproj index eeab9c0e9..4791a3b27 100644 --- a/src/Ryujinx.HLE.Generators/Ryujinx.HLE.Generators.csproj +++ b/src/Ryujinx.HLE.Generators/Ryujinx.HLE.Generators.csproj @@ -6,6 +6,7 @@ true Generated true + $(DefaultItemExcludes);._* diff --git a/src/Ryujinx.HLE/FileSystem/ContentManager.cs b/src/Ryujinx.HLE/FileSystem/ContentManager.cs index e6c0fce08..9bda759a5 100644 --- a/src/Ryujinx.HLE/FileSystem/ContentManager.cs +++ b/src/Ryujinx.HLE/FileSystem/ContentManager.cs @@ -21,6 +21,8 @@ using System.IO; 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 @@ -54,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) { @@ -395,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) { @@ -423,7 +425,7 @@ namespace Ryujinx.HLE.FileSystem { LinkedList 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) @@ -474,6 +476,74 @@ namespace Ryujinx.HLE.FileSystem FinishInstallation(temporaryDirectory, registeredDirectory); } + public void InstallKeys(string keysSource, string installDirectory) + { + if (Directory.Exists(keysSource)) + { + foreach (var filePath in Directory.EnumerateFiles(keysSource, "*.keys")) + { + VerifyKeysFile(filePath); + File.Copy(filePath, Path.Combine(installDirectory, Path.GetFileName(filePath)), true); + } + + return; + } + + if (!File.Exists(keysSource)) + { + throw new FileNotFoundException("Keys file does not exist."); + } + + FileInfo info = new(keysSource); + + using FileStream file = File.OpenRead(keysSource); + + switch (info.Extension) + { + case ".zip": + using (ZipArchive archive = ZipFile.OpenRead(keysSource)) + { + InstallKeysFromZip(archive, installDirectory); + } + break; + case ".keys": + VerifyKeysFile(keysSource); + File.Copy(keysSource, Path.Combine(installDirectory, info.Name), true); + break; + default: + throw new InvalidFirmwarePackageException("Input file is not a valid key package"); + } + } + + private void InstallKeysFromZip(ZipArchive archive, string installDirectory) + { + string temporaryDirectory = Path.Combine(installDirectory, "temp"); + if (Directory.Exists(temporaryDirectory)) + { + Directory.Delete(temporaryDirectory, true); + } + Directory.CreateDirectory(temporaryDirectory); + foreach (var entry in archive.Entries) + { + if (Path.GetExtension(entry.FullName).Equals(".keys", StringComparison.OrdinalIgnoreCase)) + { + string extractDestination = Path.Combine(temporaryDirectory, entry.Name); + entry.ExtractToFile(extractDestination, overwrite: true); + try + { + VerifyKeysFile(extractDestination); + File.Move(extractDestination, Path.Combine(installDirectory, entry.Name), true); + } + catch (Exception) + { + Directory.Delete(temporaryDirectory, true); + throw; + } + } + } + Directory.Delete(temporaryDirectory, true); + } + private void FinishInstallation(string temporaryDirectory, string registeredDirectory) { if (Directory.Exists(registeredDirectory)) @@ -523,7 +593,7 @@ namespace Ryujinx.HLE.FileSystem { // Clean up the name and get the NcaId - string[] pathComponents = entry.FullName.Replace(".cnmt", "").Split('/'); + string[] pathComponents = entry.FullName.Replace(".cnmt", string.Empty).Split('/'); string ncaId = pathComponents[^1]; @@ -588,21 +658,15 @@ namespace Ryujinx.HLE.FileSystem // LibHac.NcaHeader's DecryptHeader doesn't check if HeaderKey is empty and throws InvalidDataException instead // So, we check it early for a better user experience. if (_virtualFileSystem.KeySet.HeaderKey.IsZeros()) - { throw new MissingKeyException("HeaderKey is empty. Cannot decrypt NCA headers."); - } Dictionary> updateNcas = new(); if (Directory.Exists(firmwarePackage)) - { return VerifyAndGetVersionDirectory(firmwarePackage); - } if (!File.Exists(firmwarePackage)) - { throw new FileNotFoundException("Firmware file does not exist."); - } FileInfo info = new(firmwarePackage); @@ -612,30 +676,22 @@ namespace Ryujinx.HLE.FileSystem { case ".zip": using (ZipArchive archive = ZipFile.OpenRead(firmwarePackage)) - { return VerifyAndGetVersionZip(archive); - } case ".xci": Xci xci = new(_virtualFileSystem.KeySet, file.AsStorage()); - if (xci.HasPartition(XciPartitionType.Update)) - { - XciPartition partition = xci.OpenPartition(XciPartitionType.Update); - - return VerifyAndGetVersion(partition); - } - else - { + if (!xci.HasPartition(XciPartitionType.Update)) throw new InvalidFirmwarePackageException("Update not found in xci file."); - } - default: - break; + + XciPartition partition = xci.OpenPartition(XciPartitionType.Update); + + return VerifyAndGetVersion(partition); } + return null; + SystemVersion VerifyAndGetVersionDirectory(string firmwareDirectory) - { - return VerifyAndGetVersion(new LocalFileSystem(firmwareDirectory)); - } + => VerifyAndGetVersion(new LocalFileSystem(firmwareDirectory)); SystemVersion VerifyAndGetVersionZip(ZipArchive archive) { @@ -664,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; @@ -700,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()); @@ -719,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. @@ -860,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. @@ -925,8 +981,6 @@ namespace Ryujinx.HLE.FileSystem return systemVersion; } - - return null; } public SystemVersion GetCurrentFirmwareVersion() @@ -963,5 +1017,70 @@ namespace Ryujinx.HLE.FileSystem return null; } + + public void VerifyKeysFile(string filePath) + { + // Verify the keys file format refers to https://github.com/Thealexbarney/LibHac/blob/master/KEYS.md + string genericPattern = @"^[a-z0-9_]+ = [a-z0-9]+$"; + string titlePattern = @"^[a-z0-9]{32} = [a-z0-9]{32}$"; + + if (File.Exists(filePath)) + { + // Read all lines from the file + string fileName = Path.GetFileName(filePath); + string[] lines = File.ReadAllLines(filePath); + + bool verified = false; + switch (fileName) + { + case "prod.keys": + verified = verifyKeys(lines, genericPattern); + break; + case "title.keys": + verified = verifyKeys(lines, titlePattern); + break; + case "console.keys": + verified = verifyKeys(lines, genericPattern); + break; + case "dev.keys": + verified = verifyKeys(lines, genericPattern); + break; + default: + throw new FormatException($"Keys file name \"{fileName}\" not supported. Only \"prod.keys\", \"title.keys\", \"console.keys\", \"dev.keys\" are supported."); + } + if (!verified) + { + throw new FormatException($"Invalid \"{filePath}\" file format."); + } + } else + { + throw new FileNotFoundException($"Keys file not found at \"{filePath}\"."); + } + } + + private bool verifyKeys(string[] lines, string regex) + { + foreach (string line in lines) + { + if (!Regex.IsMatch(line, regex)) + { + return false; + } + } + return true; + } + + public bool AreKeysAlredyPresent(string pathToCheck) + { + string[] fileNames = { "prod.keys", "title.keys", "console.keys", "dev.keys" }; + foreach (var file in fileNames) + { + if (File.Exists(Path.Combine(pathToCheck, file))) + { + return true; + } + } + return false; + } } } diff --git a/src/Ryujinx.HLE/FileSystem/ContentMetaData.cs b/src/Ryujinx.HLE/FileSystem/ContentMetaData.cs index aebcf7988..8cdb889be 100644 --- a/src/Ryujinx.HLE/FileSystem/ContentMetaData.cs +++ b/src/Ryujinx.HLE/FileSystem/ContentMetaData.cs @@ -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) diff --git a/src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs b/src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs index 0827266a1..ef9c493a8 100644 --- a/src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs +++ b/src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs @@ -132,7 +132,7 @@ namespace Ryujinx.HLE.FileSystem if (systemPath.StartsWith(baseSystemPath)) { - string rawPath = systemPath.Replace(baseSystemPath, ""); + string rawPath = systemPath.Replace(baseSystemPath, string.Empty); int firstSeparatorOffset = rawPath.IndexOf(Path.DirectorySeparatorChar); if (firstSeparatorOffset == -1) @@ -223,9 +223,10 @@ namespace Ryujinx.HLE.FileSystem { KeySet ??= KeySet.CreateDefaultKeySet(); - string keyFile = null; + string prodKeyFile = null; string titleKeyFile = null; string consoleKeyFile = null; + string devKeyFile = null; if (AppDataManager.Mode == AppDataManager.LaunchMode.UserProfile) { @@ -236,13 +237,14 @@ namespace Ryujinx.HLE.FileSystem void LoadSetAtPath(string basePath) { - string localKeyFile = Path.Combine(basePath, "prod.keys"); + string localProdKeyFile = Path.Combine(basePath, "prod.keys"); string localTitleKeyFile = Path.Combine(basePath, "title.keys"); string localConsoleKeyFile = Path.Combine(basePath, "console.keys"); + string localDevKeyFile = Path.Combine(basePath, "dev.keys"); - if (File.Exists(localKeyFile)) + if (File.Exists(localProdKeyFile)) { - keyFile = localKeyFile; + prodKeyFile = localProdKeyFile; } if (File.Exists(localTitleKeyFile)) @@ -254,9 +256,14 @@ namespace Ryujinx.HLE.FileSystem { consoleKeyFile = localConsoleKeyFile; } + + if (File.Exists(localDevKeyFile)) + { + devKeyFile = localDevKeyFile; + } } - ExternalKeyReader.ReadKeyFile(KeySet, keyFile, titleKeyFile, consoleKeyFile, null); + ExternalKeyReader.ReadKeyFile(KeySet, prodKeyFile, devKeyFile, titleKeyFile, consoleKeyFile, null); } public void ImportTickets(IFileSystem fs) @@ -629,8 +636,8 @@ namespace Ryujinx.HLE.FileSystem } private static readonly ExtraDataFixInfo[] _systemExtraDataFixInfo = - { - new ExtraDataFixInfo() + [ + new() { StaticSaveDataId = 0x8000000000000030, OwnerId = 0x010000000000001F, @@ -638,15 +645,15 @@ namespace Ryujinx.HLE.FileSystem DataSize = 0x10000, JournalSize = 0x10000, }, - new ExtraDataFixInfo() + new() { StaticSaveDataId = 0x8000000000001040, OwnerId = 0x0100000000001009, Flags = SaveDataFlags.None, DataSize = 0xC000, JournalSize = 0xC000, - }, - }; + } + ]; public void Dispose() { diff --git a/src/Ryujinx.HLE/HLEConfiguration.cs b/src/Ryujinx.HLE/HLEConfiguration.cs index 955fee4b5..f75ead588 100644 --- a/src/Ryujinx.HLE/HLEConfiguration.cs +++ b/src/Ryujinx.HLE/HLEConfiguration.cs @@ -9,6 +9,7 @@ using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.UI; using System; +using VSyncMode = Ryujinx.Common.Configuration.VSyncMode; namespace Ryujinx.HLE { @@ -84,9 +85,14 @@ namespace Ryujinx.HLE internal readonly RegionCode Region; /// - /// Control the initial state of the vertical sync in the SurfaceFlinger service. + /// Control the initial state of the present interval in the SurfaceFlinger service (previously Vsync). /// - internal readonly bool EnableVsync; + internal readonly VSyncMode VSyncMode; + + /// + /// Control the custom VSync interval, if enabled and active. + /// + internal readonly int CustomVSyncInterval; /// /// Control the initial state of the docked mode. @@ -164,6 +170,21 @@ namespace Ryujinx.HLE /// public MultiplayerMode MultiplayerMode { internal get; set; } + /// + /// Disable P2P mode + /// + public bool MultiplayerDisableP2p { internal get; set; } + + /// + /// Multiplayer Passphrase + /// + public string MultiplayerLdnPassphrase { internal get; set; } + + /// + /// LDN Server + /// + public string MultiplayerLdnServer { internal get; set; } + /// /// An action called when HLE force a refresh of output after docked mode changed. /// @@ -180,7 +201,7 @@ namespace Ryujinx.HLE IHostUIHandler hostUIHandler, SystemLanguage systemLanguage, RegionCode region, - bool enableVsync, + VSyncMode vSyncMode, bool enableDockedMode, bool enablePtc, bool enableInternetAccess, @@ -194,7 +215,11 @@ namespace Ryujinx.HLE float audioVolume, bool useHypervisor, string multiplayerLanInterfaceId, - MultiplayerMode multiplayerMode) + MultiplayerMode multiplayerMode, + bool multiplayerDisableP2p, + string multiplayerLdnPassphrase, + string multiplayerLdnServer, + int customVSyncInterval) { VirtualFileSystem = virtualFileSystem; LibHacHorizonManager = libHacHorizonManager; @@ -207,7 +232,8 @@ namespace Ryujinx.HLE HostUIHandler = hostUIHandler; SystemLanguage = systemLanguage; Region = region; - EnableVsync = enableVsync; + VSyncMode = vSyncMode; + CustomVSyncInterval = customVSyncInterval; EnableDockedMode = enableDockedMode; EnablePtc = enablePtc; EnableInternetAccess = enableInternetAccess; @@ -222,6 +248,9 @@ namespace Ryujinx.HLE UseHypervisor = useHypervisor; MultiplayerLanInterfaceId = multiplayerLanInterfaceId; MultiplayerMode = multiplayerMode; + MultiplayerDisableP2p = multiplayerDisableP2p; + MultiplayerLdnPassphrase = multiplayerLdnPassphrase; + MultiplayerLdnServer = multiplayerLdnServer; } } } diff --git a/src/Ryujinx.HLE/HOS/Applets/AppletManager.cs b/src/Ryujinx.HLE/HOS/Applets/AppletManager.cs index 3c34d5c78..5895c67bb 100644 --- a/src/Ryujinx.HLE/HOS/Applets/AppletManager.cs +++ b/src/Ryujinx.HLE/HOS/Applets/AppletManager.cs @@ -1,4 +1,7 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Applets.Browser; +using Ryujinx.HLE.HOS.Applets.Cabinet; +using Ryujinx.HLE.HOS.Applets.Dummy; using Ryujinx.HLE.HOS.Applets.Error; using Ryujinx.HLE.HOS.Services.Am.AppletAE; using System; @@ -21,14 +24,18 @@ namespace Ryujinx.HLE.HOS.Applets case AppletId.SoftwareKeyboard: return new SoftwareKeyboardApplet(system); case AppletId.LibAppletWeb: - return new BrowserApplet(system); case AppletId.LibAppletShop: - return new BrowserApplet(system); case AppletId.LibAppletOff: - return new BrowserApplet(system); + return new BrowserApplet(); + case AppletId.MiiEdit: + Logger.Warning?.Print(LogClass.Application, $"Please use the MiiEdit inside File/Open Applet"); + return new DummyApplet(system); + case AppletId.Cabinet: + return new CabinetApplet(system); } - throw new NotImplementedException($"{applet} applet is not implemented."); + Logger.Warning?.Print(LogClass.Application, $"Applet {applet} not implemented!"); + return new DummyApplet(system); } } } diff --git a/src/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs b/src/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs index 6afbe4a72..c5f13dab3 100644 --- a/src/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs +++ b/src/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs @@ -18,13 +18,6 @@ namespace Ryujinx.HLE.HOS.Applets.Browser private List _arguments; private ShimKind _shimKind; - public BrowserApplet(Horizon system) { } - - public ResultCode GetResult() - { - return ResultCode.Success; - } - public ResultCode Start(AppletSession normalSession, AppletSession interactiveSession) { _normalSession = normalSession; diff --git a/src/Ryujinx.HLE/HOS/Applets/Cabinet/CabinetApplet.cs b/src/Ryujinx.HLE/HOS/Applets/Cabinet/CabinetApplet.cs new file mode 100644 index 000000000..294b8d1f6 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Applets/Cabinet/CabinetApplet.cs @@ -0,0 +1,182 @@ +using Ryujinx.Common.Logging; +using Ryujinx.Common.Memory; +using Ryujinx.HLE.HOS.Services.Am.AppletAE; +using Ryujinx.HLE.HOS.Services.Hid.HidServer; +using Ryujinx.HLE.HOS.Services.Hid; +using Ryujinx.HLE.HOS.Services.Nfc.Nfp; +using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; + +namespace Ryujinx.HLE.HOS.Applets.Cabinet +{ + internal unsafe class CabinetApplet : IApplet + { + private readonly Horizon _system; + private AppletSession _normalSession; + + public event EventHandler AppletStateChanged; + + public CabinetApplet(Horizon system) + { + _system = system; + } + + public ResultCode Start(AppletSession normalSession, AppletSession interactiveSession) + { + _normalSession = normalSession; + + byte[] launchParams = _normalSession.Pop(); + byte[] startParamBytes = _normalSession.Pop(); + + StartParamForAmiiboSettings startParam = IApplet.ReadStruct(startParamBytes); + + Logger.Stub?.PrintStub(LogClass.ServiceAm, $"CabinetApplet Start Type: {startParam.Type}"); + + switch (startParam.Type) + { + case 0: + StartNicknameAndOwnerSettings(ref startParam); + break; + case 1: + case 3: + StartFormatter(ref startParam); + break; + default: + Logger.Error?.Print(LogClass.ServiceAm, $"Unknown AmiiboSettings type: {startParam.Type}"); + break; + } + + // Prepare the response + ReturnValueForAmiiboSettings returnValue = new() + { + AmiiboSettingsReturnFlag = (byte)AmiiboSettingsReturnFlag.HasRegisterInfo, + DeviceHandle = new DeviceHandle + { + Handle = 0 // Dummy device handle + }, + RegisterInfo = startParam.RegisterInfo + }; + + // Push the response + _normalSession.Push(BuildResponse(returnValue)); + AppletStateChanged?.Invoke(this, null); + + _system.ReturnFocus(); + + return ResultCode.Success; + } + + public ResultCode GetResult() + { + _system.Device.System.NfpDevices.RemoveAt(0); + return ResultCode.Success; + } + + private void StartFormatter(ref StartParamForAmiiboSettings startParam) + { + // Initialize RegisterInfo + startParam.RegisterInfo = new RegisterInfo(); + } + + private void StartNicknameAndOwnerSettings(ref StartParamForAmiiboSettings startParam) + { + _system.Device.UIHandler.DisplayCabinetDialog(out string newName); + byte[] nameBytes = Encoding.UTF8.GetBytes(newName); + Array41 nickName = new Array41(); + nameBytes.CopyTo(nickName.AsSpan()); + startParam.RegisterInfo.Nickname = nickName; + NfpDevice devicePlayer1 = new() + { + NpadIdType = NpadIdType.Player1, + Handle = HidUtils.GetIndexFromNpadIdType(NpadIdType.Player1), + State = NfpDeviceState.SearchingForTag, + }; + _system.Device.System.NfpDevices.Add(devicePlayer1); + _system.Device.UIHandler.DisplayCabinetMessageDialog(); + string amiiboId = string.Empty; + bool scanned = false; + while (!scanned) + { + for (int i = 0; i < _system.Device.System.NfpDevices.Count; i++) + { + if (_system.Device.System.NfpDevices[i].State == NfpDeviceState.TagFound) + { + amiiboId = _system.Device.System.NfpDevices[i].AmiiboId; + scanned = true; + } + } + } + VirtualAmiibo.UpdateNickName(amiiboId, newName); + } + + private static byte[] BuildResponse(ReturnValueForAmiiboSettings returnValue) + { + int size = Unsafe.SizeOf(); + byte[] bytes = new byte[size]; + + fixed (byte* bytesPtr = bytes) + { + Unsafe.Write(bytesPtr, returnValue); + } + + return bytes; + } + + #region Structs + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public unsafe struct TagInfo + { + public fixed byte Data[0x58]; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public unsafe struct StartParamForAmiiboSettings + { + public byte ZeroValue; // Left at zero by sdknso + public byte Type; + public byte Flags; + public byte AmiiboSettingsStartParamOffset28; + public ulong AmiiboSettingsStartParam0; + + public TagInfo TagInfo; // Only enabled when flags bit 1 is set + public RegisterInfo RegisterInfo; // Only enabled when flags bit 2 is set + + public fixed byte StartParamExtraData[0x20]; + + public fixed byte Reserved[0x24]; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public unsafe struct ReturnValueForAmiiboSettings + { + public byte AmiiboSettingsReturnFlag; + private byte Padding1; + private byte Padding2; + private byte Padding3; + public DeviceHandle DeviceHandle; + public TagInfo TagInfo; + public RegisterInfo RegisterInfo; + public fixed byte IgnoredBySdknso[0x24]; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct DeviceHandle + { + public ulong Handle; + } + + public enum AmiiboSettingsReturnFlag : byte + { + Cancel = 0, + HasTagInfo = 2, + HasRegisterInfo = 4, + HasTagInfoAndRegisterInfo = 6 + } + + #endregion + } +} diff --git a/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs b/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs index 5ec9d4b08..3a7b29ab5 100644 --- a/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs +++ b/src/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs @@ -117,11 +117,6 @@ namespace Ryujinx.HLE.HOS.Applets return ResultCode.Success; } - public ResultCode GetResult() - { - return ResultCode.Success; - } - private static byte[] BuildResponse(ControllerSupportResultInfo result) { using MemoryStream stream = MemoryStreamManager.Shared.GetStream(); diff --git a/src/Ryujinx.HLE/HOS/Applets/Dummy/DummyApplet.cs b/src/Ryujinx.HLE/HOS/Applets/Dummy/DummyApplet.cs new file mode 100644 index 000000000..6b16aee7b --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Applets/Dummy/DummyApplet.cs @@ -0,0 +1,39 @@ +using Ryujinx.Common.Logging; +using Ryujinx.Common.Memory; +using Ryujinx.HLE.HOS.Applets; +using Ryujinx.HLE.HOS.Services.Am.AppletAE; +using System; +using System.IO; +using System.Runtime.InteropServices; +namespace Ryujinx.HLE.HOS.Applets.Dummy +{ + internal class DummyApplet : IApplet + { + private readonly Horizon _system; + private AppletSession _normalSession; + + public event EventHandler AppletStateChanged; + + public DummyApplet(Horizon system) + { + _system = system; + } + + public ResultCode Start(AppletSession normalSession, AppletSession interactiveSession) + { + _normalSession = normalSession; + _normalSession.Push(BuildResponse()); + AppletStateChanged?.Invoke(this, null); + _system.ReturnFocus(); + return ResultCode.Success; + } + + private static byte[] BuildResponse() + { + using MemoryStream stream = MemoryStreamManager.Shared.GetStream(); + using BinaryWriter writer = new(stream); + writer.Write((ulong)ResultCode.Success); + return stream.ToArray(); + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Applets/Error/ErrorApplet.cs b/src/Ryujinx.HLE/HOS/Applets/Error/ErrorApplet.cs index 7ee9b9e90..0e043cc45 100644 --- a/src/Ryujinx.HLE/HOS/Applets/Error/ErrorApplet.cs +++ b/src/Ryujinx.HLE/HOS/Applets/Error/ErrorApplet.cs @@ -107,7 +107,7 @@ namespace Ryujinx.HLE.HOS.Applets.Error private static string CleanText(string value) { - return CleanTextRegex().Replace(value, "").Replace("\0", ""); + return CleanTextRegex().Replace(value, string.Empty).Replace("\0", string.Empty); } private string GetMessageText(uint module, uint description, string key) @@ -129,17 +129,15 @@ namespace Ryujinx.HLE.HOS.Applets.Error return CleanText(reader.ReadToEnd()); } - else - { - return ""; - } + + return string.Empty; } private string[] GetButtonsText(uint module, uint description, string key) { string buttonsText = GetMessageText(module, description, key); - return (buttonsText == "") ? null : buttonsText.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None); + return (buttonsText == string.Empty) ? null : buttonsText.Split(["\r\n", "\r", "\n"], StringSplitOptions.None); } private void ParseErrorCommonArg() @@ -156,12 +154,9 @@ namespace Ryujinx.HLE.HOS.Applets.Error string message = GetMessageText(module, description, "DlgMsg"); - if (message == "") + if (message == string.Empty) { - message = "An error has occured.\n\n" - + "Please try again later.\n\n" - + "If the problem persists, please refer to the Ryujinx website.\n" - + "www.ryujinx.org"; + message = "An error has occured.\n\nPlease try again later."; } string[] buttons = GetButtonsText(module, description, "DlgBtn"); @@ -193,7 +188,7 @@ namespace Ryujinx.HLE.HOS.Applets.Error // TODO: Handle the LanguageCode to return the translated "OK" and "Details". - if (detailsText.Trim() != "") + if (detailsText.Trim() != string.Empty) { buttons.Add("Details"); } @@ -208,10 +203,5 @@ namespace Ryujinx.HLE.HOS.Applets.Error _horizon.Device.UIHandler.DisplayErrorAppletDialog($"Error Number: {applicationErrorArg.ErrorNumber} (Details)", "\n" + detailsText, buttons.ToArray()); } } - - public ResultCode GetResult() - { - return ResultCode.Success; - } } } diff --git a/src/Ryujinx.HLE/HOS/Applets/IApplet.cs b/src/Ryujinx.HLE/HOS/Applets/IApplet.cs index 985887c47..4500b2f63 100644 --- a/src/Ryujinx.HLE/HOS/Applets/IApplet.cs +++ b/src/Ryujinx.HLE/HOS/Applets/IApplet.cs @@ -13,16 +13,10 @@ namespace Ryujinx.HLE.HOS.Applets ResultCode Start(AppletSession normalSession, AppletSession interactiveSession); - ResultCode GetResult(); + ResultCode GetResult() => ResultCode.Success; - bool DrawTo(RenderingSurfaceInfo surfaceInfo, IVirtualMemoryManager destination, ulong position) - { - return false; - } + bool DrawTo(RenderingSurfaceInfo surfaceInfo, IVirtualMemoryManager destination, ulong position) => false; - static T ReadStruct(ReadOnlySpan data) where T : unmanaged - { - return MemoryMarshal.Cast(data)[0]; - } + static T ReadStruct(ReadOnlySpan data) where T : unmanaged => MemoryMarshal.Cast(data)[0]; } } diff --git a/src/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs b/src/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs index ccc761ba1..05bddc76f 100644 --- a/src/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs +++ b/src/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs @@ -37,11 +37,6 @@ namespace Ryujinx.HLE.HOS.Applets return ResultCode.Success; } - public ResultCode GetResult() - { - return ResultCode.Success; - } - private byte[] BuildResponse() { UserProfile currentUser = _system.AccountManager.LastOpenedUser; diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs index 0462a5b00..8fda00a7d 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs @@ -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 { @@ -51,7 +52,7 @@ namespace Ryujinx.HLE.HOS.Applets private byte[] _transferMemory; - private string _textValue = ""; + private string _textValue = string.Empty; private int _cursorBegin = 0; private Encoding _encoding = Encoding.Unicode; private KeyboardResult _lastResult = KeyboardResult.NotSet; @@ -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; @@ -144,11 +145,6 @@ namespace Ryujinx.HLE.HOS.Applets } } - public ResultCode GetResult() - { - return ResultCode.Success; - } - private bool IsKeyboardActive() { return _backgroundState >= InlineKeyboardState.Appearing && _backgroundState < InlineKeyboardState.Disappearing; diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs index cc62eca1d..e17b36911 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs @@ -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; @@ -305,7 +306,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard { SKRect bounds = SKRect.Empty; - if (text == "") + if (text == string.Empty) { paint.MeasureText(" ", ref bounds); } @@ -321,7 +322,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard { SKRect bounds = SKRect.Empty; - if (text == "") + if (text == string.Empty) { paint.MeasureText(" ", ref bounds); } diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUIState.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUIState.cs index 6199ff666..da5220364 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUIState.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardUIState.cs @@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard /// internal class SoftwareKeyboardUIState { - public string InputText = ""; + public string InputText = string.Empty; public int CursorBegin = 0; public int CursorEnd = 0; public bool AcceptPressed = false; diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TRef.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TRef.cs index 32d9e68da..51571401f 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TRef.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TRef.cs @@ -2,7 +2,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard { /// /// Wraps a type in a class so it gets stored in the GC managed heap. This is used as communication mechanism - /// between classed that need to be disposed and, thus, can't share their references. + /// between classes that need to be disposed and, thus, can't share their references. /// /// The internal type. class TRef diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TimedAction.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TimedAction.cs index 3eaf64596..a8b137df2 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TimedAction.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TimedAction.cs @@ -27,7 +27,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard private TRef _cancelled = null; private Thread _thread = null; - private readonly object _lock = new(); + private readonly Lock _lock = new(); public bool IsRunning { diff --git a/src/Ryujinx.HLE/HOS/ArmProcessContext.cs b/src/Ryujinx.HLE/HOS/ArmProcessContext.cs index fde489ab7..09a721644 100644 --- a/src/Ryujinx.HLE/HOS/ArmProcessContext.cs +++ b/src/Ryujinx.HLE/HOS/ArmProcessContext.cs @@ -13,7 +13,8 @@ namespace Ryujinx.HLE.HOS string displayVersion, bool diskCacheEnabled, ulong codeAddress, - ulong codeSize); + ulong codeSize, + string cacheSelector); } class ArmProcessContext : IArmProcessContext where T : class, IVirtualMemoryManagerTracked, IMemoryManager @@ -67,10 +68,11 @@ namespace Ryujinx.HLE.HOS string displayVersion, bool diskCacheEnabled, ulong codeAddress, - ulong codeSize) + ulong codeSize, + string cacheSelector) { _cpuContext.PrepareCodeRange(codeAddress, codeSize); - return _cpuContext.LoadDiskCache(titleIdText, displayVersion, diskCacheEnabled); + return _cpuContext.LoadDiskCache(titleIdText, displayVersion, diskCacheEnabled, cacheSelector); } public void InvalidateCacheRegion(ulong address, ulong size) diff --git a/src/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs b/src/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs index 6646826cb..14775fb1d 100644 --- a/src/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs +++ b/src/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs @@ -114,7 +114,7 @@ namespace Ryujinx.HLE.HOS } } - DiskCacheLoadState = processContext.Initialize(_titleIdText, _displayVersion, _diskCacheEnabled, _codeAddress, _codeSize); + DiskCacheLoadState = processContext.Initialize(_titleIdText, _displayVersion, _diskCacheEnabled, _codeAddress, _codeSize, "default"); //Ready for exefs profiles return processContext; } diff --git a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs index 171a083f3..2e7b8ee76 100644 --- a/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs +++ b/src/Ryujinx.HLE/HOS/Diagnostics/Demangler/Demangler.cs @@ -2463,7 +2463,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler return ParseIntegerLiteral("unsigned short"); case 'i': _position++; - return ParseIntegerLiteral(""); + return ParseIntegerLiteral(string.Empty); case 'j': _position++; return ParseIntegerLiteral("u"); diff --git a/src/Ryujinx.HLE/HOS/Horizon.cs b/src/Ryujinx.HLE/HOS/Horizon.cs index 64b08e309..f9c5ddecf 100644 --- a/src/Ryujinx.HLE/HOS/Horizon.cs +++ b/src/Ryujinx.HLE/HOS/Horizon.cs @@ -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 = Array.Empty(); + 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 = Array.Empty(); + } + 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) { diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs index 3f16f8c24..1763edce2 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Common/KResourceLimit.cs @@ -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,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Common private readonly long[] _current2; private readonly long[] _peak; - private readonly object _lock; + // type is not Lock due to Monitor class usage + private readonly object _lock = new(); private readonly LinkedList _waitingThreads; @@ -27,8 +29,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(); } diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/KSystemControl.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/KSystemControl.cs index 3f194e0ed..119d18d27 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Common/KSystemControl.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Common/KSystemControl.cs @@ -30,6 +30,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common MemoryArrange.MemoryArrange4GiBAppletDev => 2048 * MiB, MemoryArrange.MemoryArrange6GiB => 4916 * MiB, MemoryArrange.MemoryArrange8GiB => 6964 * MiB, + MemoryArrange.MemoryArrange12GiB => 11060 * MiB, _ => throw new ArgumentException($"Invalid memory arrange \"{arrange}\"."), }; } @@ -43,7 +44,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Common MemoryArrange.MemoryArrange4GiBSystemDev => 448 * MiB, MemoryArrange.MemoryArrange6GiB => 562 * MiB, MemoryArrange.MemoryArrange6GiBAppletDev => 2193 * MiB, - MemoryArrange.MemoryArrange8GiB => 562 * MiB, + MemoryArrange.MemoryArrange8GiB or + MemoryArrange.MemoryArrange12GiB => 562 * MiB, _ => throw new ArgumentException($"Invalid memory arrange \"{arrange}\"."), }; } @@ -71,6 +73,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common MemorySize.MemorySize4GiB => 4 * GiB, MemorySize.MemorySize6GiB => 6 * GiB, MemorySize.MemorySize8GiB => 8 * GiB, + MemorySize.MemorySize12GiB => 12 * GiB, _ => throw new ArgumentException($"Invalid memory size \"{size}\"."), }; } diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/MemoryArrange.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/MemoryArrange.cs index 2c88d8b35..d06c97b68 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Common/MemoryArrange.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Common/MemoryArrange.cs @@ -8,5 +8,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Common MemoryArrange6GiB, MemoryArrange6GiBAppletDev, MemoryArrange8GiB, + MemoryArrange12GiB, } } diff --git a/src/Ryujinx.HLE/HOS/Kernel/Common/MemorySize.cs b/src/Ryujinx.HLE/HOS/Kernel/Common/MemorySize.cs index 7cc34a722..f92859db4 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Common/MemorySize.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Common/MemorySize.cs @@ -5,5 +5,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Common MemorySize4GiB = 0, MemorySize6GiB = 1, MemorySize8GiB = 2, + MemorySize12GiB = 3, } } diff --git a/src/Ryujinx.HLE/HOS/Kernel/Memory/KCodeMemory.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KCodeMemory.cs index c725501b0..e6d96d803 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Memory/KCodeMemory.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Memory/KCodeMemory.cs @@ -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) diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs index 7578f1d2f..ff3de4a17 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/HleProcessDebugger.cs @@ -238,7 +238,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process } else { - info.SubName = ""; + info.SubName = string.Empty; } info.ImageName = GetGuessedNsoNameFromIndex(imageIndex); diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs index 422f03c64..b4aa5ca5c 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs @@ -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; } diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs index f6b9a112c..0c63c7e0e 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KAddressArbiter.cs @@ -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); } diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KCriticalSection.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KCriticalSection.cs index 3d6744882..b3fe5f1b6 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/KCriticalSection.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KCriticalSection.cs @@ -5,20 +5,19 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading class KCriticalSection { private readonly KernelContext _context; - private readonly object _lock; private int _recursionCount; - - public object Lock => _lock; + + // type is not Lock due to Monitor class usage + public object Lock { get; } = new(); public KCriticalSection(KernelContext context) { _context = context; - _lock = new object(); } public void Enter() { - Monitor.Enter(_lock); + Monitor.Enter(Lock); _recursionCount++; } @@ -34,7 +33,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading { ulong scheduledCoresMask = KScheduler.SelectThreads(_context); - Monitor.Exit(_lock); + Monitor.Exit(Lock); KThread currentThread = KernelStatic.GetCurrentThread(); bool isCurrentThreadSchedulable = currentThread != null && currentThread.IsSchedulable; @@ -57,7 +56,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading } else { - Monitor.Exit(_lock); + Monitor.Exit(Lock); } } } diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs index 835bf5d40..4abc0ddf3 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs @@ -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) { diff --git a/src/Ryujinx.HLE/HOS/ModLoader.cs b/src/Ryujinx.HLE/HOS/ModLoader.cs index ee179c929..4bd695ae5 100644 --- a/src/Ryujinx.HLE/HOS/ModLoader.cs +++ b/src/Ryujinx.HLE/HOS/ModLoader.cs @@ -116,18 +116,13 @@ namespace Ryujinx.HLE.HOS private readonly Dictionary _appMods; // key is ApplicationId private PatchCache _patches; - private static readonly EnumerationOptions _dirEnumOptions; - - static ModLoader() + private static readonly EnumerationOptions _dirEnumOptions = new() { - _dirEnumOptions = new EnumerationOptions - { - MatchCasing = MatchCasing.CaseInsensitive, - MatchType = MatchType.Simple, - RecurseSubdirectories = false, - ReturnSpecialDirectories = false, - }; - } + MatchCasing = MatchCasing.CaseInsensitive, + MatchType = MatchType.Simple, + RecurseSubdirectories = false, + ReturnSpecialDirectories = false, + }; public ModLoader() { @@ -169,11 +164,11 @@ namespace Ryujinx.HLE.HOS foreach (var modDir in dir.EnumerateDirectories()) { types.Clear(); - Mod mod = new("", null, true); + Mod mod = new(string.Empty, null, true); 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(dir.Name, modDir, enabled)); @@ -181,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(dir.Name, modDir, enabled)); @@ -280,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($"<{applicationDir.Name} RomFs>", fsFile, enabled)); @@ -289,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($"<{applicationDir.Name} ExeFs>", fsFile, enabled)); @@ -362,7 +357,6 @@ namespace Ryujinx.HLE.HOS { string cheatName = DefaultCheatName; List instructions = new(); - List cheats = new(); using StreamReader cheatData = cheatFile.OpenText(); while (cheatData.ReadLine() is { } line) @@ -378,13 +372,13 @@ namespace Ryujinx.HLE.HOS Logger.Warning?.Print(LogClass.ModLoader, $"Ignoring cheat '{cheatFile.FullName}' because it is malformed"); - return Array.Empty(); + yield break; } // Add the previous section to the list. if (instructions.Count > 0) { - cheats.Add(new Cheat($"<{cheatName} Cheat>", cheatFile, instructions)); + yield return new Cheat($"<{cheatName} Cheat>", cheatFile, instructions); } // Start a new cheat section. @@ -401,14 +395,12 @@ namespace Ryujinx.HLE.HOS // Add the last section being processed. if (instructions.Count > 0) { - cheats.Add(new Cheat($"<{cheatName} Cheat>", cheatFile, instructions)); + yield return new Cheat($"<{cheatName} Cheat>", cheatFile, instructions); } - - return cheats; } // Assumes searchDirPaths don't overlap - private static void CollectMods(Dictionary modCaches, PatchCache patches, params string[] searchDirPaths) + private static void CollectMods(Dictionary modCaches, PatchCache patches, params ReadOnlySpan searchDirPaths) { static bool IsPatchesDir(string name) => StrEquals(AmsNsoPatchDir, name) || StrEquals(AmsNroPatchDir, name) || @@ -458,7 +450,7 @@ namespace Ryujinx.HLE.HOS patches.Initialized = true; } - public void CollectMods(IEnumerable applications, params string[] searchDirPaths) + public void CollectMods(IEnumerable applications, params ReadOnlySpan searchDirPaths) { Clear(); @@ -685,7 +677,7 @@ namespace Ryujinx.HLE.HOS ApplyProgramPatches(nroPatches, 0, nro); } - internal bool ApplyNsoPatches(ulong applicationId, params IExecutable[] programs) + internal bool ApplyNsoPatches(ulong applicationId, params ReadOnlySpan programs) { IEnumerable> nsoMods = _patches.NsoPatches; @@ -749,7 +741,7 @@ namespace Ryujinx.HLE.HOS } } - private static bool ApplyProgramPatches(IEnumerable> mods, int protectedOffset, params IExecutable[] programs) + private static bool ApplyProgramPatches(IEnumerable> mods, int protectedOffset, params ReadOnlySpan programs) { int count = 0; @@ -760,12 +752,18 @@ namespace Ryujinx.HLE.HOS patches[i] = new MemPatch(); } - var buildIds = programs.Select(p => p switch + var buildIds = new List(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 diff --git a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs index c724660ea..d2da9e248 100644 --- a/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Account/Acc/AccountManager.cs @@ -64,7 +64,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc { if (userId.IsNull) { - userId = new UserId(Guid.NewGuid().ToString().Replace("-", "")); + userId = new UserId(Guid.NewGuid().ToString().Replace("-", string.Empty)); } UserProfile profile = new(userId, name, image); diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs index 148628136..ffeddbb72 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs @@ -111,7 +111,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib { // NOTE: This call reset two internal fields to 0 and one internal field to "true". // It seems to be used only with software keyboard inline. - // Since we doesn't support applets for now, it's fine to stub it. + // Since we don't support applets for now, it's fine to stub it. Logger.Stub?.PrintStub(LogClass.ServiceAm); diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs index 85898f138..8e0f515ba 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs @@ -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. diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs index 0a032562a..b8741b22b 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs @@ -1,4 +1,5 @@ using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService; +using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService; namespace Ryujinx.HLE.HOS.Services.Am.AppletAE { @@ -25,5 +26,14 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE return ResultCode.Success; } + + [CommandCmif(350)] + // OpenSystemApplicationProxy(u64, pid, handle) -> object + public ResultCode OpenSystemApplicationProxy(ServiceCtx context) + { + MakeObject(context, new IApplicationProxy(context.Request.HandleDesc.PId)); + + return ResultCode.Success; + } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs index 9a7fdcc16..12c046f56 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs @@ -659,7 +659,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati if (string.IsNullOrWhiteSpace(filePath)) { - throw new InvalidSystemResourceException("JIT (010000000000003B) system title not found! The JIT will not work, provide the system archive to fix this error. (See https://github.com/Ryujinx/Ryujinx#requirements for more information)"); + throw new InvalidSystemResourceException("JIT (010000000000003B) system title not found! The JIT will not work, provide the system archive to fix this error. (See https://github.com/GreemDev/Ryujinx#requirements for more information)"); } context.Device.LoadNca(filePath); diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Hid.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Hid.cs index dbcbe1870..66b5a5cba 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Hid.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Hid.cs @@ -5,6 +5,7 @@ using Ryujinx.HLE.Exceptions; using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory; using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common; +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugMouse; using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad; using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard; using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse; @@ -28,6 +29,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid public DebugPadDevice DebugPad; public TouchDevice Touchscreen; public MouseDevice Mouse; + public DebugMouseDevice DebugMouse; public KeyboardDevice Keyboard; public NpadDevices Npads; @@ -44,6 +46,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid CheckTypeSizeOrThrow>(0x2c8); CheckTypeSizeOrThrow>(0x2C38); CheckTypeSizeOrThrow>(0x350); + CheckTypeSizeOrThrow>(0x350); CheckTypeSizeOrThrow>(0x3D8); CheckTypeSizeOrThrow>(0x32000); CheckTypeSizeOrThrow(Horizon.HidSize); @@ -64,6 +67,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid DebugPad = new DebugPadDevice(_device, true); Touchscreen = new TouchDevice(_device, true); Mouse = new MouseDevice(_device, false); + DebugMouse = new DebugMouseDevice(_device, false); Keyboard = new KeyboardDevice(_device, false); Npads = new NpadDevices(_device, true); } diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/DebugMouseDevice.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/DebugMouseDevice.cs new file mode 100644 index 000000000..cb917444b --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/DebugMouseDevice.cs @@ -0,0 +1,29 @@ +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common; +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugMouse; + +namespace Ryujinx.HLE.HOS.Services.Hid +{ + public class DebugMouseDevice : BaseDevice + { + public DebugMouseDevice(Switch device, bool active) : base(device, active) { } + + public void Update() + { + ref RingLifo lifo = ref _device.Hid.SharedMemory.DebugMouse; + + ref DebugMouseState previousEntry = ref lifo.GetCurrentEntryRef(); + + DebugMouseState newState = new() + { + SamplingNumber = previousEntry.SamplingNumber + 1, + }; + + if (Active) + { + // TODO: This is a debug device only present in dev environment, do we want to support it? + } + + lifo.Write(ref newState); + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs index 86c6a825f..834bee6f0 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs @@ -123,7 +123,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid return true; } - public void Configure(params ControllerConfig[] configs) + public void Configure(params ReadOnlySpan configs) { _configuredTypes = new ControllerType[MaxControllers]; diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/TouchDevice.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/TouchDevice.cs index 35ac1a16f..e8a56933b 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/TouchDevice.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/TouchDevice.cs @@ -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 points) { ref RingLifo lifo = ref _device.Hid.SharedMemory.TouchScreen; diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs b/src/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs index e3f505f37..556e35ea6 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs @@ -130,6 +130,26 @@ namespace Ryujinx.HLE.HOS.Services.Hid return ResultCode.Success; } + + [CommandCmif(26)] + // ActivateDebugMouse(nn::applet::AppletResourceUserId) + public ResultCode ActivateDebugMouse(ServiceCtx context) + { + long appletResourceUserId = context.RequestData.ReadInt64(); + + context.Device.Hid.DebugMouse.Active = true; + + // Initialize entries to avoid issues with some games. + + for (int entry = 0; entry < Hid.SharedMemEntryCount; entry++) + { + context.Device.Hid.DebugMouse.Update(); + } + + Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId }); + + return ResultCode.Success; + } [CommandCmif(31)] // ActivateKeyboard(nn::applet::AppletResourceUserId) diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugMouse/DebugMouseAttribute.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugMouse/DebugMouseAttribute.cs new file mode 100644 index 000000000..0b55277d4 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugMouse/DebugMouseAttribute.cs @@ -0,0 +1,12 @@ +using System; + +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugMouse +{ + [Flags] + enum DebugMouseAttribute : uint + { + None = 0, + Transferable = 1 << 0, + IsConnected = 1 << 1, + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugMouse/DebugMouseButton.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugMouse/DebugMouseButton.cs new file mode 100644 index 000000000..c07fa84af --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugMouse/DebugMouseButton.cs @@ -0,0 +1,15 @@ +using System; + +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugMouse +{ + [Flags] + enum DebugMouseButton : uint + { + None = 0, + Left = 1 << 0, + Right = 1 << 1, + Middle = 1 << 2, + Forward = 1 << 3, + Back = 1 << 4, + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugMouse/DebugMouseState.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugMouse/DebugMouseState.cs new file mode 100644 index 000000000..e2860c9f5 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/DebugMouse/DebugMouseState.cs @@ -0,0 +1,19 @@ +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugMouse +{ + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct DebugMouseState : ISampledDataStruct + { + public ulong SamplingNumber; + public int X; + public int Y; + public int DeltaX; + public int DeltaY; + public int WheelDeltaX; + public int WheelDeltaY; + public DebugMouseButton Buttons; + public DebugMouseAttribute Attributes; + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs index d6283eb57..59d8f4489 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs @@ -1,5 +1,6 @@ using Ryujinx.Common.Memory; using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common; +using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugMouse; using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.DebugPad; using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard; using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse; @@ -44,6 +45,12 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory /// [FieldOffset(0x9A00)] public Array10 Npads; + + /// + /// Debug mouse. + /// + [FieldOffset(0x3DC00)] + public RingLifo DebugMouse; public static SharedMemory Create() { diff --git a/src/Ryujinx.HLE/HOS/Services/IpcService.cs b/src/Ryujinx.HLE/HOS/Services/IpcService.cs index 808f21c0e..cd3df4edf 100644 --- a/src/Ryujinx.HLE/HOS/Services/IpcService.cs +++ b/src/Ryujinx.HLE/HOS/Services/IpcService.cs @@ -23,18 +23,18 @@ namespace Ryujinx.HLE.HOS.Services public IpcService(ServerBase server = null) { - CmifCommands = typeof(IpcService).Assembly.GetTypes() + CmifCommands = GetType().Assembly.GetTypes() .Where(type => type == GetType()) .SelectMany(type => type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public)) - .SelectMany(methodInfo => methodInfo.GetCustomAttributes(typeof(CommandCmifAttribute)) - .Select(command => (((CommandCmifAttribute)command).Id, methodInfo))) + .SelectMany(methodInfo => methodInfo.GetCustomAttributes() + .Select(command => (command.Id, methodInfo))) .ToDictionary(command => command.Id, command => command.methodInfo); - TipcCommands = typeof(IpcService).Assembly.GetTypes() + TipcCommands = GetType().Assembly.GetTypes() .Where(type => type == GetType()) .SelectMany(type => type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public)) - .SelectMany(methodInfo => methodInfo.GetCustomAttributes(typeof(CommandTipcAttribute)) - .Select(command => (((CommandTipcAttribute)command).Id, methodInfo))) + .SelectMany(methodInfo => methodInfo.GetCustomAttributes() + .Select(command => (command.Id, methodInfo))) .ToDictionary(command => command.Id, command => command.methodInfo); Server = server; diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/Lp2p/IServiceCreator.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/Lp2p/IServiceCreator.cs index 797a7a9bd..705e5f258 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/Lp2p/IServiceCreator.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/Lp2p/IServiceCreator.cs @@ -5,5 +5,23 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.Lp2p class IServiceCreator : IpcService { public IServiceCreator(ServiceCtx context) { } + + [CommandCmif(0)] + // CreateNetworkService(pid, u64, u32) -> object + public ResultCode CreateNetworkService(ServiceCtx context) + { + MakeObject(context, new ISfService(context)); + + return ResultCode.Success; + } + + [CommandCmif(8)] + // CreateNetworkServiceMonitor(pid, u64) -> object + public ResultCode CreateNetworkServiceMonitor(ServiceCtx context) + { + MakeObject(context, new ISfServiceMonitor(context)); + + return ResultCode.Success; + } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/Lp2p/ISfService.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/Lp2p/ISfService.cs new file mode 100644 index 000000000..8f9f0e3e4 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/Lp2p/ISfService.cs @@ -0,0 +1,54 @@ +using Ryujinx.Common.Logging; + +namespace Ryujinx.HLE.HOS.Services.Ldn.Lp2p +{ + class ISfService : IpcService + { + public ISfService(ServiceCtx context) { } + + [CommandCmif(0)] + // Initialize() + public ResultCode Initialize(ServiceCtx context) + { + context.ResponseData.Write(0); + + return ResultCode.Success; + } + + [CommandCmif(768)] + // CreateGroup(buffer) + public ResultCode SendToOtherGroup(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceLdn); + + return ResultCode.Success; + } + + [CommandCmif(1544)] + // RecvFromOtherGroup(u32, buffer) -> (nn::lp2p::MacAddress, u16, s16, u32, s32) + public ResultCode RecvFromOtherGroup(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceLdn); + + return ResultCode.Success; + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/Lp2p/ISfServiceMonitor.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/Lp2p/ISfServiceMonitor.cs new file mode 100644 index 000000000..d3a8bead2 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/Lp2p/ISfServiceMonitor.cs @@ -0,0 +1,86 @@ +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.Horizon.Common; +using System; + +namespace Ryujinx.HLE.HOS.Services.Ldn.Lp2p +{ + class ISfServiceMonitor : IpcService + { + private readonly KEvent _stateChangeEvent; + private readonly KEvent _jointEvent; + private int _stateChangeEventHandle = 0; + private int _jointEventHandle = 0; + + public ISfServiceMonitor(ServiceCtx context) + { + _stateChangeEvent = new KEvent(context.Device.System.KernelContext); + _jointEvent = new KEvent(context.Device.System.KernelContext); + } + + [CommandCmif(0)] + // Initialize() + public ResultCode Initialize(ServiceCtx context) + { + context.ResponseData.Write(0); + + return ResultCode.Success; + } + + [CommandCmif(256)] + // AttachNetworkInterfaceStateChangeEvent() -> handle + public ResultCode AttachNetworkInterfaceStateChangeEvent(ServiceCtx context) + { + if (context.Process.HandleTable.GenerateHandle(_stateChangeEvent.ReadableEvent, out _stateChangeEventHandle) != Result.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_stateChangeEventHandle); + + return ResultCode.Success; + } + + [CommandCmif(288)] + // GetGroupInfo(buffer) + public ResultCode GetGroupInfo(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceLdn); + + return ResultCode.Success; + } + + [CommandCmif(296)] + // GetGroupInfo2(buffer, buffer) + public ResultCode GetGroupInfo2(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceLdn); + + return ResultCode.Success; + } + + [CommandCmif(312)] + // GetIpConfig(buffer, 0x1a>) + public ResultCode GetIpConfig(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceLdn); + + return ResultCode.Success; + } + + [CommandCmif(328)] + // AttachNetworkInterfaceStateChangeEvent() -> handle + public ResultCode AttachJoinEvent(ServiceCtx context) + { + if (context.Process.HandleTable.GenerateHandle(_jointEvent.ReadableEvent, out _jointEventHandle) != Result.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_jointEventHandle); + + return ResultCode.Success; + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/Types/NetworkConfig.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/Types/NetworkConfig.cs index 4da5fe42b..c6d6ac944 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/Types/NetworkConfig.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/Types/NetworkConfig.cs @@ -3,7 +3,7 @@ using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Ldn.Types { - [StructLayout(LayoutKind.Sequential, Size = 0x20)] + [StructLayout(LayoutKind.Sequential, Size = 0x20, Pack = 8)] struct NetworkConfig { public IntentId IntentId; diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/Types/NodeLatestUpdate.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/Types/NodeLatestUpdate.cs index 0461e783e..826a50458 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/Types/NodeLatestUpdate.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/Types/NodeLatestUpdate.cs @@ -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 array, Array8 beforeNodes, Array8 afterNodes) { diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/Types/ScanFilter.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/Types/ScanFilter.cs index 449c923cc..f3ab1edd5 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/Types/ScanFilter.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/Types/ScanFilter.cs @@ -3,7 +3,7 @@ using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Ldn.Types { - [StructLayout(LayoutKind.Sequential, Size = 0x60)] + [StructLayout(LayoutKind.Sequential, Size = 0x60, Pack = 8)] struct ScanFilter { public NetworkId NetworkId; diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/Types/SecurityConfig.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/Types/SecurityConfig.cs index 5939a1394..f3968aab4 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/Types/SecurityConfig.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/Types/SecurityConfig.cs @@ -3,7 +3,7 @@ using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Ldn.Types { - [StructLayout(LayoutKind.Sequential, Size = 0x44)] + [StructLayout(LayoutKind.Sequential, Size = 0x44, Pack = 2)] struct SecurityConfig { public SecurityMode SecurityMode; diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/Types/SecurityParameter.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/Types/SecurityParameter.cs index dbcaa9eeb..e564a2ec9 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/Types/SecurityParameter.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/Types/SecurityParameter.cs @@ -3,7 +3,7 @@ using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Ldn.Types { - [StructLayout(LayoutKind.Sequential, Size = 0x20)] + [StructLayout(LayoutKind.Sequential, Size = 0x20, Pack = 1)] struct SecurityParameter { public Array16 Data; diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/Types/UserConfig.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/Types/UserConfig.cs index 3820f936e..7246f6f80 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/Types/UserConfig.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/Types/UserConfig.cs @@ -3,7 +3,7 @@ using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Ldn.Types { - [StructLayout(LayoutKind.Sequential, Size = 0x30)] + [StructLayout(LayoutKind.Sequential, Size = 0x30, Pack = 1)] struct UserConfig { public Array33 UserName; diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/AccessPoint.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/AccessPoint.cs index 78ebcac82..bd00a3139 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/AccessPoint.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/AccessPoint.cs @@ -15,6 +15,8 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator public Array8 LatestUpdates = new(); public bool Connected { get; private set; } + public ProxyConfig Config => _parent.NetworkClient.Config; + public AccessPoint(IUserLocalCommunicationService parent) { _parent = parent; @@ -24,9 +26,12 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator public void Dispose() { - _parent.NetworkClient.DisconnectNetwork(); + if (_parent?.NetworkClient != null) + { + _parent.NetworkClient.DisconnectNetwork(); - _parent.NetworkClient.NetworkChange -= NetworkChanged; + _parent.NetworkClient.NetworkChange -= NetworkChanged; + } } private void NetworkChanged(object sender, NetworkChangeEventArgs e) diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/INetworkClient.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/INetworkClient.cs index 7ad6de51d..028ab6cfc 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/INetworkClient.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/INetworkClient.cs @@ -6,6 +6,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator { interface INetworkClient : IDisposable { + ProxyConfig Config { get; } bool NeedsRealId { get; } event EventHandler NetworkChange; diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs index 1d4b5485e..b8b3014f1 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/IUserLocalCommunicationService.cs @@ -9,6 +9,8 @@ using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Services.Ldn.Types; using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnMitm; +using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu; +using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types; using Ryujinx.Horizon.Common; using Ryujinx.Memory; using System; @@ -21,6 +23,9 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator { class IUserLocalCommunicationService : IpcService, IDisposable { + public static string DefaultLanPlayHost = "ryuldn.vudjun.com"; + public static short LanPlayPort = 30456; + public INetworkClient NetworkClient { get; private set; } private const int NifmRequestID = 90; @@ -175,19 +180,37 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator if (_state == NetworkState.AccessPointCreated || _state == NetworkState.StationConnected) { - (_, UnicastIPAddressInformation unicastAddress) = NetworkHelpers.GetLocalInterface(context.Device.Configuration.MultiplayerLanInterfaceId); - - if (unicastAddress == null) + ProxyConfig config = _state switch { - context.ResponseData.Write(NetworkHelpers.ConvertIpv4Address(DefaultIPAddress)); - context.ResponseData.Write(NetworkHelpers.ConvertIpv4Address(DefaultSubnetMask)); + NetworkState.AccessPointCreated => _accessPoint.Config, + NetworkState.StationConnected => _station.Config, + + _ => default + }; + + if (config.ProxyIp == 0) + { + (_, UnicastIPAddressInformation unicastAddress) = NetworkHelpers.GetLocalInterface(context.Device.Configuration.MultiplayerLanInterfaceId); + + if (unicastAddress == null) + { + context.ResponseData.Write(NetworkHelpers.ConvertIpv4Address(DefaultIPAddress)); + context.ResponseData.Write(NetworkHelpers.ConvertIpv4Address(DefaultSubnetMask)); + } + else + { + Logger.Info?.Print(LogClass.ServiceLdn, $"Console's LDN IP is \"{unicastAddress.Address}\"."); + + context.ResponseData.Write(NetworkHelpers.ConvertIpv4Address(unicastAddress.Address)); + context.ResponseData.Write(NetworkHelpers.ConvertIpv4Address(unicastAddress.IPv4Mask)); + } } else { - Logger.Info?.Print(LogClass.ServiceLdn, $"Console's LDN IP is \"{unicastAddress.Address}\"."); + Logger.Info?.Print(LogClass.ServiceLdn, $"LDN obtained proxy IP."); - context.ResponseData.Write(NetworkHelpers.ConvertIpv4Address(unicastAddress.Address)); - context.ResponseData.Write(NetworkHelpers.ConvertIpv4Address(unicastAddress.IPv4Mask)); + context.ResponseData.Write(config.ProxyIp); + context.ResponseData.Write(config.ProxySubnetMask); } } else @@ -421,7 +444,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator private ResultCode ScanInternal(IVirtualMemoryManager memory, ushort channel, ScanFilter scanFilter, ulong bufferPosition, ulong bufferSize, out ulong counter) { - ulong networkInfoSize = (ulong)Marshal.SizeOf(typeof(NetworkInfo)); + ulong networkInfoSize = (ulong)Marshal.SizeOf(); ulong maxGames = bufferSize / networkInfoSize; MemoryHelper.FillWithZeros(memory, bufferPosition, (int)bufferSize); @@ -1066,6 +1089,27 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator switch (mode) { + case MultiplayerMode.LdnRyu: + try + { + string ldnServer = context.Device.Configuration.MultiplayerLdnServer; + if (string.IsNullOrEmpty(ldnServer)) + { + ldnServer = DefaultLanPlayHost; + } + if (!IPAddress.TryParse(ldnServer, out IPAddress ipAddress)) + { + ipAddress = Dns.GetHostEntry(ldnServer).AddressList[0]; + } + NetworkClient = new LdnMasterProxyClient(ipAddress.ToString(), LanPlayPort, context.Device.Configuration); + } + catch (Exception ex) + { + Logger.Error?.Print(LogClass.ServiceLdn, "Could not locate LdnRyu server. Defaulting to stubbed wireless."); + Logger.Error?.Print(LogClass.ServiceLdn, ex.Message); + NetworkClient = new LdnDisabledClient(); + } + break; case MultiplayerMode.LdnMitm: NetworkClient = new LdnMitmClient(context.Device.Configuration); break; @@ -1103,7 +1147,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator _accessPoint?.Dispose(); _accessPoint = null; - NetworkClient?.Dispose(); + NetworkClient?.DisconnectAndStop(); NetworkClient = null; } } diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnDisabledClient.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnDisabledClient.cs index e3385a1ed..2e8bb8d83 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnDisabledClient.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnDisabledClient.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Services.Ldn.Types; using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types; using System; @@ -6,12 +7,14 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator { class LdnDisabledClient : INetworkClient { + public ProxyConfig Config { get; } public bool NeedsRealId => true; public event EventHandler NetworkChange; public NetworkError Connect(ConnectRequest request) { + Logger.Warning?.PrintMsg(LogClass.ServiceLdn, "Attempted to connect to a network, but Multiplayer is disabled!"); NetworkChange?.Invoke(this, new NetworkChangeEventArgs(new NetworkInfo(), false)); return NetworkError.None; @@ -19,6 +22,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator public NetworkError ConnectPrivate(ConnectPrivateRequest request) { + Logger.Warning?.PrintMsg(LogClass.ServiceLdn, "Attempted to connect to a network, but Multiplayer is disabled!"); NetworkChange?.Invoke(this, new NetworkChangeEventArgs(new NetworkInfo(), false)); return NetworkError.None; @@ -26,6 +30,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator public bool CreateNetwork(CreateAccessPointRequest request, byte[] advertiseData) { + Logger.Warning?.PrintMsg(LogClass.ServiceLdn, "Attempted to create a network, but Multiplayer is disabled!"); NetworkChange?.Invoke(this, new NetworkChangeEventArgs(new NetworkInfo(), false)); return true; @@ -33,6 +38,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator public bool CreateNetworkPrivate(CreateAccessPointPrivateRequest request, byte[] advertiseData) { + Logger.Warning?.PrintMsg(LogClass.ServiceLdn, "Attempted to create a network, but Multiplayer is disabled!"); NetworkChange?.Invoke(this, new NetworkChangeEventArgs(new NetworkInfo(), false)); return true; @@ -49,6 +55,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator public NetworkInfo[] Scan(ushort channel, ScanFilter scanFilter) { + Logger.Warning?.PrintMsg(LogClass.ServiceLdn, "Attempted to scan for networks, but Multiplayer is disabled!"); return Array.Empty(); } diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnMitm/LanDiscovery.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnMitm/LanDiscovery.cs index b5f643d23..8b7af42a0 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnMitm/LanDiscovery.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnMitm/LanDiscovery.cs @@ -29,7 +29,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnMitm private ILdnTcpSocket _tcp; private LdnProxyUdpServer _udp, _udp2; private readonly List _stations = new(); - private readonly object _lock = new(); + private readonly Lock _lock = new(); private readonly AutoResetEvent _apConnected = new(false); diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnMitm/LanProtocol.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnMitm/LanProtocol.cs index 4b01bfe31..b60b70d80 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnMitm/LanProtocol.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnMitm/LanProtocol.cs @@ -1,3 +1,4 @@ +using Gommon; using Ryujinx.Common.Logging; using Ryujinx.Common.Memory; using Ryujinx.Common.Utilities; @@ -143,7 +144,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnMitm if (decompressedLdnData.Length != header.DecompressLength) { Logger.Error?.PrintMsg(LogClass.ServiceLdn, $"Decompress error: length does not match. ({decompressedLdnData.Length} != {header.DecompressLength})"); - Logger.Error?.PrintMsg(LogClass.ServiceLdn, $"Decompress error data: '{string.Join("", decompressedLdnData.Select(x => (int)x).ToArray())}'"); + Logger.Error?.PrintMsg(LogClass.ServiceLdn, $"Decompress error data: '{decompressedLdnData.Select(x => (int)x).JoinToString(string.Empty)}'"); return; } diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnMitm/LdnMitmClient.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnMitm/LdnMitmClient.cs index 273acdd5e..40697d122 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnMitm/LdnMitmClient.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnMitm/LdnMitmClient.cs @@ -12,6 +12,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnMitm /// internal class LdnMitmClient : INetworkClient { + public ProxyConfig Config { get; } public bool NeedsRealId => false; public event EventHandler NetworkChange; diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnMitm/Proxy/LdnProxyUdpServer.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnMitm/Proxy/LdnProxyUdpServer.cs index 0f5875a1d..536ae476d 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnMitm/Proxy/LdnProxyUdpServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnMitm/Proxy/LdnProxyUdpServer.cs @@ -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 _scanResultsLast = new(); private Dictionary _scanResults = new(); diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/IProxyClient.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/IProxyClient.cs new file mode 100644 index 000000000..a7c435506 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/IProxyClient.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu +{ + interface IProxyClient + { + bool SendAsync(byte[] buffer); + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/LdnMasterProxyClient.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/LdnMasterProxyClient.cs new file mode 100644 index 000000000..4c7814b8e --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/LdnMasterProxyClient.cs @@ -0,0 +1,645 @@ +using Ryujinx.Common.Logging; +using Ryujinx.Common.Utilities; +using Ryujinx.HLE.HOS.Services.Ldn.Types; +using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy; +using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types; +using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types; +using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Proxy; +using Ryujinx.HLE.Utilities; +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.NetworkInformation; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using TcpClient = NetCoreServer.TcpClient; + +namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu +{ + class LdnMasterProxyClient : TcpClient, INetworkClient, IProxyClient + { + public bool NeedsRealId => true; + + private static InitializeMessage InitializeMemory = new InitializeMessage(); + + private const int InactiveTimeout = 6000; + private const int FailureTimeout = 4000; + private const int ScanTimeout = 1000; + + private bool _useP2pProxy; + private NetworkError _lastError; + + private readonly ManualResetEvent _connected = new ManualResetEvent(false); + private readonly ManualResetEvent _error = new ManualResetEvent(false); + private readonly ManualResetEvent _scan = new ManualResetEvent(false); + private readonly ManualResetEvent _reject = new ManualResetEvent(false); + private readonly AutoResetEvent _apConnected = new AutoResetEvent(false); + + private readonly RyuLdnProtocol _protocol; + private readonly NetworkTimeout _timeout; + + private readonly List _availableGames = new List(); + private DisconnectReason _disconnectReason; + + private P2pProxyServer _hostedProxy; + private P2pProxyClient _connectedProxy; + + private bool _networkConnected; + + private string _passphrase; + private byte[] _gameVersion = new byte[0x10]; + + private readonly HLEConfiguration _config; + + public event EventHandler NetworkChange; + + public ProxyConfig Config { get; private set; } + + public LdnMasterProxyClient(string address, int port, HLEConfiguration config) : base(address, port) + { + if (ProxyHelpers.SupportsNoDelay()) + { + OptionNoDelay = true; + } + + _protocol = new RyuLdnProtocol(); + _timeout = new NetworkTimeout(InactiveTimeout, TimeoutConnection); + + _protocol.Initialize += HandleInitialize; + _protocol.Connected += HandleConnected; + _protocol.Reject += HandleReject; + _protocol.RejectReply += HandleRejectReply; + _protocol.SyncNetwork += HandleSyncNetwork; + _protocol.ProxyConfig += HandleProxyConfig; + _protocol.Disconnected += HandleDisconnected; + + _protocol.ScanReply += HandleScanReply; + _protocol.ScanReplyEnd += HandleScanReplyEnd; + _protocol.ExternalProxy += HandleExternalProxy; + + _protocol.Ping += HandlePing; + _protocol.NetworkError += HandleNetworkError; + + _config = config; + _useP2pProxy = !config.MultiplayerDisableP2p; + } + + private void TimeoutConnection() + { + _connected.Reset(); + + DisconnectAsync(); + + while (IsConnected) + { + Thread.Yield(); + } + } + + private bool EnsureConnected() + { + if (IsConnected) + { + return true; + } + + _error.Reset(); + + ConnectAsync(); + + int index = WaitHandle.WaitAny(new WaitHandle[] { _connected, _error }, FailureTimeout); + + if (IsConnected) + { + SendAsync(_protocol.Encode(PacketId.Initialize, InitializeMemory)); + } + + return index == 0 && IsConnected; + } + + private void UpdatePassphraseIfNeeded() + { + string passphrase = _config.MultiplayerLdnPassphrase ?? ""; + if (passphrase != _passphrase) + { + _passphrase = passphrase; + + SendAsync(_protocol.Encode(PacketId.Passphrase, StringUtils.GetFixedLengthBytes(passphrase, 0x80, Encoding.UTF8))); + } + } + + protected override void OnConnected() + { + Logger.Info?.PrintMsg(LogClass.ServiceLdn, $"LDN TCP client connected a new session with Id {Id}"); + + UpdatePassphraseIfNeeded(); + + _connected.Set(); + } + + protected override void OnDisconnected() + { + Logger.Info?.PrintMsg(LogClass.ServiceLdn, $"LDN TCP client disconnected a session with Id {Id}"); + + _passphrase = null; + + _connected.Reset(); + + if (_networkConnected) + { + DisconnectInternal(); + } + } + + public void DisconnectAndStop() + { + _timeout.Dispose(); + + DisconnectAsync(); + + while (IsConnected) + { + Thread.Yield(); + } + + Dispose(); + } + + protected override void OnReceived(byte[] buffer, long offset, long size) + { + _protocol.Read(buffer, (int)offset, (int)size); + } + + protected override void OnError(SocketError error) + { + Logger.Info?.PrintMsg(LogClass.ServiceLdn, $"LDN TCP client caught an error with code {error}"); + + _error.Set(); + } + + + + private void HandleInitialize(LdnHeader header, InitializeMessage initialize) + { + InitializeMemory = initialize; + } + + private void HandleExternalProxy(LdnHeader header, ExternalProxyConfig config) + { + int length = config.AddressFamily switch + { + AddressFamily.InterNetwork => 4, + AddressFamily.InterNetworkV6 => 16, + _ => 0 + }; + + if (length == 0) + { + return; // Invalid external proxy. + } + + IPAddress address = new(config.ProxyIp.AsSpan()[..length].ToArray()); + P2pProxyClient proxy = new(address.ToString(), config.ProxyPort); + + _connectedProxy = proxy; + + bool success = proxy.PerformAuth(config); + + if (!success) + { + DisconnectInternal(); + } + } + + private void HandlePing(LdnHeader header, PingMessage ping) + { + if (ping.Requester == 0) // Server requested. + { + // Send the ping message back. + + SendAsync(_protocol.Encode(PacketId.Ping, ping)); + } + } + + private void HandleNetworkError(LdnHeader header, NetworkErrorMessage error) + { + if (error.Error == NetworkError.PortUnreachable) + { + _useP2pProxy = false; + } + else + { + _lastError = error.Error; + } + } + + private NetworkError ConsumeNetworkError() + { + NetworkError result = _lastError; + + _lastError = NetworkError.None; + + return result; + } + + private void HandleSyncNetwork(LdnHeader header, NetworkInfo info) + { + NetworkChange?.Invoke(this, new NetworkChangeEventArgs(info, true)); + } + + private void HandleConnected(LdnHeader header, NetworkInfo info) + { + _networkConnected = true; + _disconnectReason = DisconnectReason.None; + + _apConnected.Set(); + + NetworkChange?.Invoke(this, new NetworkChangeEventArgs(info, true)); + } + + private void HandleDisconnected(LdnHeader header, DisconnectMessage message) + { + DisconnectInternal(); + } + + private void HandleReject(LdnHeader header, RejectRequest reject) + { + // When the client receives a Reject request, we have been rejected and will be disconnected shortly. + _disconnectReason = reject.DisconnectReason; + } + + private void HandleRejectReply(LdnHeader header) + { + _reject.Set(); + } + + private void HandleScanReply(LdnHeader header, NetworkInfo info) + { + _availableGames.Add(info); + } + + private void HandleScanReplyEnd(LdnHeader obj) + { + _scan.Set(); + } + + private void DisconnectInternal() + { + if (_networkConnected) + { + _networkConnected = false; + + _hostedProxy?.Dispose(); + _hostedProxy = null; + + _connectedProxy?.Dispose(); + _connectedProxy = null; + + _apConnected.Reset(); + + NetworkChange?.Invoke(this, new NetworkChangeEventArgs(new NetworkInfo(), false, _disconnectReason)); + + if (IsConnected) + { + _timeout.RefreshTimeout(); + } + } + } + + public void DisconnectNetwork() + { + if (_networkConnected) + { + SendAsync(_protocol.Encode(PacketId.Disconnect, new DisconnectMessage())); + + DisconnectInternal(); + } + } + + public ResultCode Reject(DisconnectReason disconnectReason, uint nodeId) + { + if (_networkConnected) + { + _reject.Reset(); + + SendAsync(_protocol.Encode(PacketId.Reject, new RejectRequest(disconnectReason, nodeId))); + + int index = WaitHandle.WaitAny(new WaitHandle[] { _reject, _error }, InactiveTimeout); + + if (index == 0) + { + return (ConsumeNetworkError() != NetworkError.None) ? ResultCode.InvalidState : ResultCode.Success; + } + } + + return ResultCode.InvalidState; + } + + public void SetAdvertiseData(byte[] data) + { + // TODO: validate we're the owner (the server will do this anyways tho) + if (_networkConnected) + { + SendAsync(_protocol.Encode(PacketId.SetAdvertiseData, data)); + } + } + + public void SetGameVersion(byte[] versionString) + { + _gameVersion = versionString; + + if (_gameVersion.Length < 0x10) + { + Array.Resize(ref _gameVersion, 0x10); + } + } + + public void SetStationAcceptPolicy(AcceptPolicy acceptPolicy) + { + // TODO: validate we're the owner (the server will do this anyways tho) + if (_networkConnected) + { + SendAsync(_protocol.Encode(PacketId.SetAcceptPolicy, new SetAcceptPolicyRequest + { + StationAcceptPolicy = acceptPolicy + })); + } + } + + private void DisposeProxy() + { + _hostedProxy?.Dispose(); + _hostedProxy = null; + } + + private void ConfigureAccessPoint(ref RyuNetworkConfig request) + { + _gameVersion.AsSpan().CopyTo(request.GameVersion.AsSpan()); + + if (_useP2pProxy) + { + // Before sending the request, attempt to set up a proxy server. + // This can be on a range of private ports, which can be exposed on a range of public + // ports via UPnP. If any of this fails, we just fall back to using the master server. + + int i = 0; + for (; i < P2pProxyServer.PrivatePortRange; i++) + { + _hostedProxy = new P2pProxyServer(this, (ushort)(P2pProxyServer.PrivatePortBase + i), _protocol); + + try + { + _hostedProxy.Start(); + + break; + } + catch (SocketException e) + { + _hostedProxy.Dispose(); + _hostedProxy = null; + + if (e.SocketErrorCode != SocketError.AddressAlreadyInUse) + { + i = P2pProxyServer.PrivatePortRange; // Immediately fail. + } + } + } + + bool openSuccess = i < P2pProxyServer.PrivatePortRange; + + if (openSuccess) + { + Task natPunchResult = _hostedProxy.NatPunch(); + + try + { + if (natPunchResult.Result != 0) + { + // Tell the server that we are hosting the proxy. + request.ExternalProxyPort = natPunchResult.Result; + } + } + catch (Exception) { } + + if (request.ExternalProxyPort == 0) + { + Logger.Warning?.Print(LogClass.ServiceLdn, "Failed to open a port with UPnP for P2P connection. Proxying through the master server instead. Expect higher latency."); + _hostedProxy.Dispose(); + } + else + { + Logger.Info?.Print(LogClass.ServiceLdn, $"Created a wireless P2P network on port {request.ExternalProxyPort}."); + _hostedProxy.Start(); + + (_, UnicastIPAddressInformation unicastAddress) = NetworkHelpers.GetLocalInterface(); + + unicastAddress.Address.GetAddressBytes().AsSpan().CopyTo(request.PrivateIp.AsSpan()); + request.InternalProxyPort = _hostedProxy.PrivatePort; + request.AddressFamily = unicastAddress.Address.AddressFamily; + } + } + else + { + Logger.Warning?.Print(LogClass.ServiceLdn, "Cannot create a P2P server. Proxying through the master server instead. Expect higher latency."); + } + } + } + + private bool CreateNetworkCommon() + { + bool signalled = _apConnected.WaitOne(FailureTimeout); + + if (!_useP2pProxy && _hostedProxy != null) + { + Logger.Warning?.Print(LogClass.ServiceLdn, "Locally hosted proxy server was not externally reachable. Proxying through the master server instead. Expect higher latency."); + + DisposeProxy(); + } + + if (signalled && _connectedProxy != null) + { + _connectedProxy.EnsureProxyReady(); + + Config = _connectedProxy.ProxyConfig; + } + else + { + DisposeProxy(); + } + + return signalled; + } + + public bool CreateNetwork(CreateAccessPointRequest request, byte[] advertiseData) + { + _timeout.DisableTimeout(); + + ConfigureAccessPoint(ref request.RyuNetworkConfig); + + if (!EnsureConnected()) + { + DisposeProxy(); + + return false; + } + + UpdatePassphraseIfNeeded(); + + SendAsync(_protocol.Encode(PacketId.CreateAccessPoint, request, advertiseData)); + + // Send a network change event with dummy data immediately. Necessary to avoid crashes in some games + var networkChangeEvent = new NetworkChangeEventArgs(new NetworkInfo() + { + Common = new CommonNetworkInfo() + { + MacAddress = InitializeMemory.MacAddress, + Channel = request.NetworkConfig.Channel, + LinkLevel = 3, + NetworkType = 2, + Ssid = new Ssid() + { + Length = 32 + } + }, + Ldn = new LdnNetworkInfo() + { + AdvertiseDataSize = (ushort)advertiseData.Length, + AuthenticationId = 0, + NodeCount = 1, + NodeCountMax = request.NetworkConfig.NodeCountMax, + SecurityMode = (ushort)request.SecurityConfig.SecurityMode + } + }, true); + networkChangeEvent.Info.Ldn.Nodes[0] = new NodeInfo() + { + Ipv4Address = 175243265, + IsConnected = 1, + LocalCommunicationVersion = request.NetworkConfig.LocalCommunicationVersion, + MacAddress = InitializeMemory.MacAddress, + NodeId = 0, + UserName = request.UserConfig.UserName + }; + "12345678123456781234567812345678"u8.ToArray().CopyTo(networkChangeEvent.Info.Common.Ssid.Name.AsSpan()); + NetworkChange?.Invoke(this, networkChangeEvent); + + return CreateNetworkCommon(); + } + + public bool CreateNetworkPrivate(CreateAccessPointPrivateRequest request, byte[] advertiseData) + { + _timeout.DisableTimeout(); + + ConfigureAccessPoint(ref request.RyuNetworkConfig); + + if (!EnsureConnected()) + { + DisposeProxy(); + + return false; + } + + UpdatePassphraseIfNeeded(); + + SendAsync(_protocol.Encode(PacketId.CreateAccessPointPrivate, request, advertiseData)); + + return CreateNetworkCommon(); + } + + public NetworkInfo[] Scan(ushort channel, ScanFilter scanFilter) + { + if (!_networkConnected) + { + _timeout.RefreshTimeout(); + } + + _availableGames.Clear(); + + int index = -1; + + if (EnsureConnected()) + { + UpdatePassphraseIfNeeded(); + + _scan.Reset(); + + SendAsync(_protocol.Encode(PacketId.Scan, scanFilter)); + + index = WaitHandle.WaitAny(new WaitHandle[] { _scan, _error }, ScanTimeout); + } + + if (index != 0) + { + // An error occurred or timeout. Write 0 games. + return Array.Empty(); + } + + return _availableGames.ToArray(); + } + + private NetworkError ConnectCommon() + { + bool signalled = _apConnected.WaitOne(FailureTimeout); + + NetworkError error = ConsumeNetworkError(); + + if (error != NetworkError.None) + { + return error; + } + + if (signalled && _connectedProxy != null) + { + _connectedProxy.EnsureProxyReady(); + + Config = _connectedProxy.ProxyConfig; + } + + return signalled ? NetworkError.None : NetworkError.ConnectTimeout; + } + + public NetworkError Connect(ConnectRequest request) + { + _timeout.DisableTimeout(); + + if (!EnsureConnected()) + { + return NetworkError.Unknown; + } + + SendAsync(_protocol.Encode(PacketId.Connect, request)); + + var networkChangeEvent = new NetworkChangeEventArgs(new NetworkInfo() + { + Common = request.NetworkInfo.Common, + Ldn = request.NetworkInfo.Ldn + }, true); + + NetworkChange?.Invoke(this, networkChangeEvent); + + return ConnectCommon(); + } + + public NetworkError ConnectPrivate(ConnectPrivateRequest request) + { + _timeout.DisableTimeout(); + + if (!EnsureConnected()) + { + return NetworkError.Unknown; + } + + SendAsync(_protocol.Encode(PacketId.ConnectPrivate, request)); + + return ConnectCommon(); + } + + private void HandleProxyConfig(LdnHeader header, ProxyConfig config) + { + Config = config; + + SocketHelpers.RegisterProxy(new LdnProxy(config, this, _protocol)); + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/NetworkTimeout.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/NetworkTimeout.cs new file mode 100644 index 000000000..f7a9d77a4 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/NetworkTimeout.cs @@ -0,0 +1,83 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu +{ + class NetworkTimeout : IDisposable + { + private readonly int _idleTimeout; + private readonly Action _timeoutCallback; + private CancellationTokenSource _cancel; + + private readonly Lock _lock = new(); + + public NetworkTimeout(int idleTimeout, Action timeoutCallback) + { + _idleTimeout = idleTimeout; + _timeoutCallback = timeoutCallback; + } + + private async Task TimeoutTask() + { + CancellationTokenSource cts; + + lock (_lock) + { + cts = _cancel; + } + + if (cts == null) + { + return; + } + + try + { + await Task.Delay(_idleTimeout, cts.Token); + } + catch (TaskCanceledException) + { + return; // Timeout cancelled. + } + + lock (_lock) + { + // Run the timeout callback. If the cancel token source has been replaced, we have _just_ been cancelled. + if (cts == _cancel) + { + _timeoutCallback(); + } + } + } + + public bool RefreshTimeout() + { + lock (_lock) + { + _cancel?.Cancel(); + + _cancel = new CancellationTokenSource(); + + Task.Run(TimeoutTask); + } + + return true; + } + + public void DisableTimeout() + { + lock (_lock) + { + _cancel?.Cancel(); + + _cancel = new CancellationTokenSource(); + } + } + + public void Dispose() + { + DisableTimeout(); + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/EphemeralPortPool.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/EphemeralPortPool.cs new file mode 100644 index 000000000..9ea56d050 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/EphemeralPortPool.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using System.Threading; + +namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy +{ + public class EphemeralPortPool + { + private const ushort EphemeralBase = 49152; + + private readonly List _ephemeralPorts = new List(); + + private readonly Lock _lock = new(); + + public ushort Get() + { + ushort port = EphemeralBase; + lock (_lock) + { + // Starting at the ephemeral port base, return an ephemeral port that is not in use. + // Returns 0 if the range is exhausted. + + for (int i = 0; i < _ephemeralPorts.Count; i++) + { + ushort existingPort = _ephemeralPorts[i]; + + if (existingPort > port) + { + // The port was free - take it. + _ephemeralPorts.Insert(i, port); + + return port; + } + + port++; + } + + if (port != 0) + { + _ephemeralPorts.Add(port); + } + + return port; + } + } + + public void Return(ushort port) + { + lock (_lock) + { + _ephemeralPorts.Remove(port); + } + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/LdnProxy.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/LdnProxy.cs new file mode 100644 index 000000000..bb390d49a --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/LdnProxy.cs @@ -0,0 +1,254 @@ +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types; +using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types; +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; + +namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy +{ + class LdnProxy : IDisposable + { + public EndPoint LocalEndpoint { get; } + public IPAddress LocalAddress { get; } + + private readonly List _sockets = new List(); + private readonly Dictionary _ephemeralPorts = new Dictionary(); + + private readonly IProxyClient _parent; + private RyuLdnProtocol _protocol; + private readonly uint _subnetMask; + private readonly uint _localIp; + private readonly uint _broadcast; + + public LdnProxy(ProxyConfig config, IProxyClient client, RyuLdnProtocol protocol) + { + _parent = client; + _protocol = protocol; + + _ephemeralPorts[ProtocolType.Udp] = new EphemeralPortPool(); + _ephemeralPorts[ProtocolType.Tcp] = new EphemeralPortPool(); + + byte[] address = BitConverter.GetBytes(config.ProxyIp); + Array.Reverse(address); + LocalAddress = new IPAddress(address); + + _subnetMask = config.ProxySubnetMask; + _localIp = config.ProxyIp; + _broadcast = _localIp | (~_subnetMask); + + RegisterHandlers(protocol); + } + + public bool Supported(AddressFamily domain, SocketType type, ProtocolType protocol) + { + if (protocol == ProtocolType.Tcp) + { + Logger.Error?.PrintMsg(LogClass.ServiceLdn, "Tcp proxy networking is untested. Please report this game so that it can be tested."); + } + return domain == AddressFamily.InterNetwork && (protocol == ProtocolType.Tcp || protocol == ProtocolType.Udp); + } + + private void RegisterHandlers(RyuLdnProtocol protocol) + { + protocol.ProxyConnect += HandleConnectionRequest; + protocol.ProxyConnectReply += HandleConnectionResponse; + protocol.ProxyData += HandleData; + protocol.ProxyDisconnect += HandleDisconnect; + + _protocol = protocol; + } + + public void UnregisterHandlers(RyuLdnProtocol protocol) + { + protocol.ProxyConnect -= HandleConnectionRequest; + protocol.ProxyConnectReply -= HandleConnectionResponse; + protocol.ProxyData -= HandleData; + protocol.ProxyDisconnect -= HandleDisconnect; + } + + public ushort GetEphemeralPort(ProtocolType type) + { + return _ephemeralPorts[type].Get(); + } + + public void ReturnEphemeralPort(ProtocolType type, ushort port) + { + _ephemeralPorts[type].Return(port); + } + + public void RegisterSocket(LdnProxySocket socket) + { + lock (_sockets) + { + _sockets.Add(socket); + } + } + + public void UnregisterSocket(LdnProxySocket socket) + { + lock (_sockets) + { + _sockets.Remove(socket); + } + } + + private void ForRoutedSockets(ProxyInfo info, Action action) + { + lock (_sockets) + { + foreach (LdnProxySocket socket in _sockets) + { + // Must match protocol and destination port. + if (socket.ProtocolType != info.Protocol || socket.LocalEndPoint is not IPEndPoint endpoint || endpoint.Port != info.DestPort) + { + continue; + } + + // We can assume packets routed to us have been sent to our destination. + // They will either be sent to us, or broadcast packets. + + action(socket); + } + } + } + + public void HandleConnectionRequest(LdnHeader header, ProxyConnectRequest request) + { + ForRoutedSockets(request.Info, (socket) => + { + socket.HandleConnectRequest(request); + }); + } + + public void HandleConnectionResponse(LdnHeader header, ProxyConnectResponse response) + { + ForRoutedSockets(response.Info, (socket) => + { + socket.HandleConnectResponse(response); + }); + } + + public void HandleData(LdnHeader header, ProxyDataHeader proxyHeader, byte[] data) + { + ProxyDataPacket packet = new ProxyDataPacket() { Header = proxyHeader, Data = data }; + + ForRoutedSockets(proxyHeader.Info, (socket) => + { + socket.IncomingData(packet); + }); + } + + public void HandleDisconnect(LdnHeader header, ProxyDisconnectMessage disconnect) + { + ForRoutedSockets(disconnect.Info, (socket) => + { + socket.HandleDisconnect(disconnect); + }); + } + + private uint GetIpV4(IPEndPoint endpoint) + { + if (endpoint.AddressFamily != AddressFamily.InterNetwork) + { + throw new NotSupportedException(); + } + + byte[] address = endpoint.Address.GetAddressBytes(); + Array.Reverse(address); + + return BitConverter.ToUInt32(address); + } + + private ProxyInfo MakeInfo(IPEndPoint localEp, IPEndPoint remoteEP, ProtocolType type) + { + return new ProxyInfo + { + SourceIpV4 = GetIpV4(localEp), + SourcePort = (ushort)localEp.Port, + + DestIpV4 = GetIpV4(remoteEP), + DestPort = (ushort)remoteEP.Port, + + Protocol = type + }; + } + + public void RequestConnection(IPEndPoint localEp, IPEndPoint remoteEp, ProtocolType type) + { + // We must ask the other side to initialize a connection, so they can accept a socket for us. + + ProxyConnectRequest request = new ProxyConnectRequest + { + Info = MakeInfo(localEp, remoteEp, type) + }; + + _parent.SendAsync(_protocol.Encode(PacketId.ProxyConnect, request)); + } + + public void SignalConnected(IPEndPoint localEp, IPEndPoint remoteEp, ProtocolType type) + { + // We must tell the other side that we have accepted their request for connection. + + ProxyConnectResponse request = new ProxyConnectResponse + { + Info = MakeInfo(localEp, remoteEp, type) + }; + + _parent.SendAsync(_protocol.Encode(PacketId.ProxyConnectReply, request)); + } + + public void EndConnection(IPEndPoint localEp, IPEndPoint remoteEp, ProtocolType type) + { + // We must tell the other side that our connection is dropped. + + ProxyDisconnectMessage request = new ProxyDisconnectMessage + { + Info = MakeInfo(localEp, remoteEp, type), + DisconnectReason = 0 // TODO + }; + + _parent.SendAsync(_protocol.Encode(PacketId.ProxyDisconnect, request)); + } + + public int SendTo(ReadOnlySpan buffer, SocketFlags flags, IPEndPoint localEp, IPEndPoint remoteEp, ProtocolType type) + { + // We send exactly as much as the user wants us to, currently instantly. + // TODO: handle over "virtual mtu" (we have a max packet size to worry about anyways). fragment if tcp? throw if udp? + + ProxyDataHeader request = new ProxyDataHeader + { + Info = MakeInfo(localEp, remoteEp, type), + DataLength = (uint)buffer.Length + }; + + _parent.SendAsync(_protocol.Encode(PacketId.ProxyData, request, buffer.ToArray())); + + return buffer.Length; + } + + public bool IsBroadcast(uint ip) + { + return ip == _broadcast; + } + + public bool IsMyself(uint ip) + { + return ip == _localIp; + } + + public void Dispose() + { + UnregisterHandlers(_protocol); + + lock (_sockets) + { + foreach (LdnProxySocket socket in _sockets) + { + socket.ProxyDestroyed(); + } + } + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/LdnProxySocket.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/LdnProxySocket.cs new file mode 100644 index 000000000..ed7a9c751 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/LdnProxySocket.cs @@ -0,0 +1,797 @@ +using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types; +using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl; +using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Proxy; +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; +using System.Threading; + +namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy +{ + /// + /// This socket is forwarded through a TCP stream that goes through the Ldn server. + /// The Ldn server will then route the packets we send (or need to receive) within the virtual adhoc network. + /// + class LdnProxySocket : ISocketImpl + { + private readonly LdnProxy _proxy; + + private bool _isListening; + private readonly List _listenSockets = new List(); + + private readonly Queue _connectRequests = new Queue(); + + private readonly AutoResetEvent _acceptEvent = new AutoResetEvent(false); + private readonly int _acceptTimeout = -1; + + private readonly Queue _errors = new Queue(); + + private readonly AutoResetEvent _connectEvent = new AutoResetEvent(false); + private ProxyConnectResponse _connectResponse; + + private int _receiveTimeout = -1; + private readonly AutoResetEvent _receiveEvent = new AutoResetEvent(false); + private readonly Queue _receiveQueue = new Queue(); + + // private int _sendTimeout = -1; // Sends are techically instant right now, so not _really_ used. + + private bool _connecting; + private bool _broadcast; + private bool _readShutdown; + // private bool _writeShutdown; + private bool _closed; + + private readonly Dictionary _socketOptions = new Dictionary() + { + { SocketOptionName.Broadcast, 0 }, //TODO: honor this value + { SocketOptionName.DontLinger, 0 }, + { SocketOptionName.Debug, 0 }, + { SocketOptionName.Error, 0 }, + { SocketOptionName.KeepAlive, 0 }, + { SocketOptionName.OutOfBandInline, 0 }, + { SocketOptionName.ReceiveBuffer, 131072 }, + { SocketOptionName.ReceiveTimeout, -1 }, + { SocketOptionName.SendBuffer, 131072 }, + { SocketOptionName.SendTimeout, -1 }, + { SocketOptionName.Type, 0 }, + { SocketOptionName.ReuseAddress, 0 } //TODO: honor this value + }; + + public EndPoint RemoteEndPoint { get; private set; } + + public EndPoint LocalEndPoint { get; private set; } + + public bool Connected { get; private set; } + + public bool IsBound { get; private set; } + + public AddressFamily AddressFamily { get; } + + public SocketType SocketType { get; } + + public ProtocolType ProtocolType { get; } + + public bool Blocking { get; set; } + + public int Available + { + get + { + int result = 0; + + lock (_receiveQueue) + { + foreach (ProxyDataPacket data in _receiveQueue) + { + result += data.Data.Length; + } + } + + return result; + } + } + + public bool Readable + { + get + { + if (_isListening) + { + lock (_connectRequests) + { + return _connectRequests.Count > 0; + } + } + else + { + if (_readShutdown) + { + return true; + } + + lock (_receiveQueue) + { + return _receiveQueue.Count > 0; + } + } + + } + } + public bool Writable => Connected || ProtocolType == ProtocolType.Udp; + public bool Error => false; + + public LdnProxySocket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType, LdnProxy proxy) + { + AddressFamily = addressFamily; + SocketType = socketType; + ProtocolType = protocolType; + + _proxy = proxy; + _socketOptions[SocketOptionName.Type] = (int)socketType; + + proxy.RegisterSocket(this); + } + + private IPEndPoint EnsureLocalEndpoint(bool replace) + { + if (LocalEndPoint != null) + { + if (replace) + { + _proxy.ReturnEphemeralPort(ProtocolType, (ushort)((IPEndPoint)LocalEndPoint).Port); + } + else + { + return (IPEndPoint)LocalEndPoint; + } + } + + IPEndPoint localEp = new IPEndPoint(_proxy.LocalAddress, _proxy.GetEphemeralPort(ProtocolType)); + LocalEndPoint = localEp; + + return localEp; + } + + public LdnProxySocket AsAccepted(IPEndPoint remoteEp) + { + Connected = true; + RemoteEndPoint = remoteEp; + + IPEndPoint localEp = EnsureLocalEndpoint(true); + + _proxy.SignalConnected(localEp, remoteEp, ProtocolType); + + return this; + } + + private void SignalError(WsaError error) + { + lock (_errors) + { + _errors.Enqueue((int)error); + } + } + + private IPEndPoint GetEndpoint(uint ipv4, ushort port) + { + byte[] address = BitConverter.GetBytes(ipv4); + Array.Reverse(address); + + return new IPEndPoint(new IPAddress(address), port); + } + + public void IncomingData(ProxyDataPacket packet) + { + bool isBroadcast = _proxy.IsBroadcast(packet.Header.Info.DestIpV4); + + if (!_closed && (_broadcast || !isBroadcast)) + { + lock (_receiveQueue) + { + _receiveQueue.Enqueue(packet); + } + } + } + + public ISocketImpl Accept() + { + if (!_isListening) + { + throw new InvalidOperationException(); + } + + // Accept a pending request to this socket. + + lock (_connectRequests) + { + if (!Blocking && _connectRequests.Count == 0) + { + throw new SocketException((int)WsaError.WSAEWOULDBLOCK); + } + } + + while (true) + { + _acceptEvent.WaitOne(_acceptTimeout); + + lock (_connectRequests) + { + while (_connectRequests.Count > 0) + { + ProxyConnectRequest request = _connectRequests.Dequeue(); + + if (_connectRequests.Count > 0) + { + _acceptEvent.Set(); // Still more accepts to do. + } + + // Is this request made for us? + IPEndPoint endpoint = GetEndpoint(request.Info.DestIpV4, request.Info.DestPort); + + if (Equals(endpoint, LocalEndPoint)) + { + // Yes - let's accept. + IPEndPoint remoteEndpoint = GetEndpoint(request.Info.SourceIpV4, request.Info.SourcePort); + + LdnProxySocket socket = new LdnProxySocket(AddressFamily, SocketType, ProtocolType, _proxy).AsAccepted(remoteEndpoint); + + lock (_listenSockets) + { + _listenSockets.Add(socket); + } + + return socket; + } + } + } + } + } + + public void Bind(EndPoint localEP) + { + ArgumentNullException.ThrowIfNull(localEP); + + if (LocalEndPoint != null) + { + _proxy.ReturnEphemeralPort(ProtocolType, (ushort)((IPEndPoint)LocalEndPoint).Port); + } + var asIPEndpoint = (IPEndPoint)localEP; + if (asIPEndpoint.Port == 0) + { + asIPEndpoint.Port = (ushort)_proxy.GetEphemeralPort(ProtocolType); + } + + LocalEndPoint = (IPEndPoint)localEP; + + IsBound = true; + } + + public void Close() + { + _closed = true; + + _proxy.UnregisterSocket(this); + + if (Connected) + { + Disconnect(false); + } + + lock (_listenSockets) + { + foreach (LdnProxySocket socket in _listenSockets) + { + socket.Close(); + } + } + + _isListening = false; + } + + public void Connect(EndPoint remoteEP) + { + if (_isListening || !IsBound) + { + throw new InvalidOperationException(); + } + + if (remoteEP is not IPEndPoint) + { + throw new NotSupportedException(); + } + + IPEndPoint localEp = EnsureLocalEndpoint(true); + + _connecting = true; + + _proxy.RequestConnection(localEp, (IPEndPoint)remoteEP, ProtocolType); + + if (!Blocking && ProtocolType == ProtocolType.Tcp) + { + throw new SocketException((int)WsaError.WSAEWOULDBLOCK); + } + + _connectEvent.WaitOne(); //timeout? + + if (_connectResponse.Info.SourceIpV4 == 0) + { + throw new SocketException((int)WsaError.WSAECONNREFUSED); + } + + _connectResponse = default; + } + + public void HandleConnectResponse(ProxyConnectResponse obj) + { + if (!_connecting) + { + return; + } + + _connecting = false; + + if (_connectResponse.Info.SourceIpV4 != 0) + { + IPEndPoint remoteEp = GetEndpoint(obj.Info.SourceIpV4, obj.Info.SourcePort); + RemoteEndPoint = remoteEp; + + Connected = true; + } + else + { + // Connection failed + + SignalError(WsaError.WSAECONNREFUSED); + } + } + + public void Disconnect(bool reuseSocket) + { + if (Connected) + { + ConnectionEnded(); + + // The other side needs to be notified that connection ended. + _proxy.EndConnection(LocalEndPoint as IPEndPoint, RemoteEndPoint as IPEndPoint, ProtocolType); + } + } + + private void ConnectionEnded() + { + if (Connected) + { + RemoteEndPoint = null; + Connected = false; + } + } + + public void GetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, byte[] optionValue) + { + if (optionLevel != SocketOptionLevel.Socket) + { + throw new NotImplementedException(); + } + + if (_socketOptions.TryGetValue(optionName, out int result)) + { + byte[] data = BitConverter.GetBytes(result); + Array.Copy(data, 0, optionValue, 0, Math.Min(data.Length, optionValue.Length)); + } + else + { + throw new NotImplementedException(); + } + } + + public void Listen(int backlog) + { + if (!IsBound) + { + throw new SocketException(); + } + + _isListening = true; + } + + public void HandleConnectRequest(ProxyConnectRequest obj) + { + lock (_connectRequests) + { + _connectRequests.Enqueue(obj); + } + + _connectEvent.Set(); + } + + public void HandleDisconnect(ProxyDisconnectMessage message) + { + Disconnect(false); + } + + public int Receive(Span buffer) + { + EndPoint dummy = new IPEndPoint(IPAddress.Any, 0); + + return ReceiveFrom(buffer, SocketFlags.None, ref dummy); + } + + public int Receive(Span buffer, SocketFlags flags) + { + EndPoint dummy = new IPEndPoint(IPAddress.Any, 0); + + return ReceiveFrom(buffer, flags, ref dummy); + } + + public int Receive(Span buffer, SocketFlags flags, out SocketError socketError) + { + EndPoint dummy = new IPEndPoint(IPAddress.Any, 0); + + return ReceiveFrom(buffer, flags, out socketError, ref dummy); + } + + public int ReceiveFrom(Span buffer, SocketFlags flags, ref EndPoint remoteEp) + { + // We just receive all packets meant for us anyways regardless of EP in the actual implementation. + // The point is mostly to return the endpoint that we got the data from. + + if (!Connected && ProtocolType == ProtocolType.Tcp) + { + throw new SocketException((int)WsaError.WSAECONNRESET); + } + + lock (_receiveQueue) + { + if (_receiveQueue.Count > 0) + { + return ReceiveFromQueue(buffer, flags, ref remoteEp); + } + else if (_readShutdown) + { + return 0; + } + else if (!Blocking) + { + throw new SocketException((int)WsaError.WSAEWOULDBLOCK); + } + } + + int timeout = _receiveTimeout; + + _receiveEvent.WaitOne(timeout == 0 ? -1 : timeout); + + if (!Connected && ProtocolType == ProtocolType.Tcp) + { + throw new SocketException((int)WsaError.WSAECONNRESET); + } + + lock (_receiveQueue) + { + if (_receiveQueue.Count > 0) + { + return ReceiveFromQueue(buffer, flags, ref remoteEp); + } + else if (_readShutdown) + { + return 0; + } + else + { + throw new SocketException((int)WsaError.WSAETIMEDOUT); + } + } + } + + public int ReceiveFrom(Span buffer, SocketFlags flags, out SocketError socketError, ref EndPoint remoteEp) + { + // We just receive all packets meant for us anyways regardless of EP in the actual implementation. + // The point is mostly to return the endpoint that we got the data from. + + if (!Connected && ProtocolType == ProtocolType.Tcp) + { + socketError = SocketError.ConnectionReset; + return -1; + } + + lock (_receiveQueue) + { + if (_receiveQueue.Count > 0) + { + return ReceiveFromQueue(buffer, flags, out socketError, ref remoteEp); + } + else if (_readShutdown) + { + socketError = SocketError.Success; + return 0; + } + else if (!Blocking) + { + throw new SocketException((int)WsaError.WSAEWOULDBLOCK); + } + } + + int timeout = _receiveTimeout; + + _receiveEvent.WaitOne(timeout == 0 ? -1 : timeout); + + if (!Connected && ProtocolType == ProtocolType.Tcp) + { + throw new SocketException((int)WsaError.WSAECONNRESET); + } + + lock (_receiveQueue) + { + if (_receiveQueue.Count > 0) + { + return ReceiveFromQueue(buffer, flags, out socketError, ref remoteEp); + } + else if (_readShutdown) + { + socketError = SocketError.Success; + return 0; + } + else + { + socketError = SocketError.TimedOut; + return -1; + } + } + } + + private int ReceiveFromQueue(Span buffer, SocketFlags flags, ref EndPoint remoteEp) + { + int size = buffer.Length; + + // Assumes we have the receive queue lock, and at least one item in the queue. + ProxyDataPacket packet = _receiveQueue.Peek(); + + remoteEp = GetEndpoint(packet.Header.Info.SourceIpV4, packet.Header.Info.SourcePort); + + bool peek = (flags & SocketFlags.Peek) != 0; + + int read; + + if (packet.Data.Length > size) + { + read = size; + + // Cannot fit in the output buffer. Copy up to what we've got. + packet.Data.AsSpan(0, size).CopyTo(buffer); + + if (ProtocolType == ProtocolType.Udp) + { + // Udp overflows, loses the data, then throws an exception. + + if (!peek) + { + _receiveQueue.Dequeue(); + } + + throw new SocketException((int)WsaError.WSAEMSGSIZE); + } + else if (ProtocolType == ProtocolType.Tcp) + { + // Split the data at the buffer boundary. It will stay on the recieve queue. + + byte[] newData = new byte[packet.Data.Length - size]; + Array.Copy(packet.Data, size, newData, 0, newData.Length); + + packet.Data = newData; + } + } + else + { + read = packet.Data.Length; + + packet.Data.AsSpan(0, packet.Data.Length).CopyTo(buffer); + + if (!peek) + { + _receiveQueue.Dequeue(); + } + } + + return read; + } + + private int ReceiveFromQueue(Span buffer, SocketFlags flags, out SocketError socketError, ref EndPoint remoteEp) + { + int size = buffer.Length; + + // Assumes we have the receive queue lock, and at least one item in the queue. + ProxyDataPacket packet = _receiveQueue.Peek(); + + remoteEp = GetEndpoint(packet.Header.Info.SourceIpV4, packet.Header.Info.SourcePort); + + bool peek = (flags & SocketFlags.Peek) != 0; + + int read; + + if (packet.Data.Length > size) + { + read = size; + + // Cannot fit in the output buffer. Copy up to what we've got. + packet.Data.AsSpan(0, size).CopyTo(buffer); + + if (ProtocolType == ProtocolType.Udp) + { + // Udp overflows, loses the data, then throws an exception. + + if (!peek) + { + _receiveQueue.Dequeue(); + } + + socketError = SocketError.MessageSize; + return -1; + } + else if (ProtocolType == ProtocolType.Tcp) + { + // Split the data at the buffer boundary. It will stay on the recieve queue. + + byte[] newData = new byte[packet.Data.Length - size]; + Array.Copy(packet.Data, size, newData, 0, newData.Length); + + packet.Data = newData; + } + } + else + { + read = packet.Data.Length; + + packet.Data.AsSpan(0, packet.Data.Length).CopyTo(buffer); + + if (!peek) + { + _receiveQueue.Dequeue(); + } + } + + socketError = SocketError.Success; + + return read; + } + + public int Send(ReadOnlySpan buffer) + { + // Send to the remote host chosen when we "connect" or "accept". + if (!Connected) + { + throw new SocketException(); + } + + return SendTo(buffer, SocketFlags.None, RemoteEndPoint); + } + + public int Send(ReadOnlySpan buffer, SocketFlags flags) + { + // Send to the remote host chosen when we "connect" or "accept". + if (!Connected) + { + throw new SocketException(); + } + + return SendTo(buffer, flags, RemoteEndPoint); + } + + public int Send(ReadOnlySpan buffer, SocketFlags flags, out SocketError socketError) + { + // Send to the remote host chosen when we "connect" or "accept". + if (!Connected) + { + throw new SocketException(); + } + + return SendTo(buffer, flags, out socketError, RemoteEndPoint); + } + + public int SendTo(ReadOnlySpan buffer, SocketFlags flags, EndPoint remoteEP) + { + if (!Connected && ProtocolType == ProtocolType.Tcp) + { + throw new SocketException((int)WsaError.WSAECONNRESET); + } + + IPEndPoint localEp = EnsureLocalEndpoint(false); + + if (remoteEP is not IPEndPoint) + { + throw new NotSupportedException(); + } + + return _proxy.SendTo(buffer, flags, localEp, (IPEndPoint)remoteEP, ProtocolType); + } + + public int SendTo(ReadOnlySpan buffer, SocketFlags flags, out SocketError socketError, EndPoint remoteEP) + { + if (!Connected && ProtocolType == ProtocolType.Tcp) + { + socketError = SocketError.ConnectionReset; + return -1; + } + + IPEndPoint localEp = EnsureLocalEndpoint(false); + + if (remoteEP is not IPEndPoint) + { + // throw new NotSupportedException(); + socketError = SocketError.OperationNotSupported; + return -1; + } + + socketError = SocketError.Success; + + return _proxy.SendTo(buffer, flags, localEp, (IPEndPoint)remoteEP, ProtocolType); + } + + public bool Poll(int microSeconds, SelectMode mode) + { + return mode switch + { + SelectMode.SelectRead => Readable, + SelectMode.SelectWrite => Writable, + SelectMode.SelectError => Error, + _ => false + }; + } + + public void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, int optionValue) + { + if (optionLevel != SocketOptionLevel.Socket) + { + throw new NotImplementedException(); + } + + switch (optionName) + { + case SocketOptionName.SendTimeout: + //_sendTimeout = optionValue; + break; + case SocketOptionName.ReceiveTimeout: + _receiveTimeout = optionValue; + break; + case SocketOptionName.Broadcast: + _broadcast = optionValue != 0; + break; + } + + lock (_socketOptions) + { + _socketOptions[optionName] = optionValue; + } + } + + public void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, object optionValue) + { + // Just linger uses this for now in BSD, which we ignore. + } + + public void Shutdown(SocketShutdown how) + { + switch (how) + { + case SocketShutdown.Both: + _readShutdown = true; + // _writeShutdown = true; + break; + case SocketShutdown.Receive: + _readShutdown = true; + break; + case SocketShutdown.Send: + // _writeShutdown = true; + break; + } + } + + public void ProxyDestroyed() + { + // Do nothing, for now. Will likely be more useful with TCP. + } + + public void Dispose() + { + + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/P2pProxyClient.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/P2pProxyClient.cs new file mode 100644 index 000000000..7da1aa998 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/P2pProxyClient.cs @@ -0,0 +1,93 @@ +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types; +using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types; +using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Proxy; +using System.Net.Sockets; +using System.Threading; +using TcpClient = NetCoreServer.TcpClient; + +namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy +{ + class P2pProxyClient : TcpClient, IProxyClient + { + private const int FailureTimeout = 4000; + + public ProxyConfig ProxyConfig { get; private set; } + + private readonly RyuLdnProtocol _protocol; + + private readonly ManualResetEvent _connected = new ManualResetEvent(false); + private readonly ManualResetEvent _ready = new ManualResetEvent(false); + private readonly AutoResetEvent _error = new AutoResetEvent(false); + + public P2pProxyClient(string address, int port) : base(address, port) + { + if (ProxyHelpers.SupportsNoDelay()) + { + OptionNoDelay = true; + } + + _protocol = new RyuLdnProtocol(); + + _protocol.ProxyConfig += HandleProxyConfig; + + ConnectAsync(); + } + + protected override void OnConnected() + { + Logger.Info?.PrintMsg(LogClass.ServiceLdn, $"Proxy TCP client connected a new session with Id {Id}"); + + _connected.Set(); + } + + protected override void OnDisconnected() + { + Logger.Info?.PrintMsg(LogClass.ServiceLdn, $"Proxy TCP client disconnected a session with Id {Id}"); + + SocketHelpers.UnregisterProxy(); + + _connected.Reset(); + } + + protected override void OnReceived(byte[] buffer, long offset, long size) + { + _protocol.Read(buffer, (int)offset, (int)size); + } + + protected override void OnError(SocketError error) + { + Logger.Info?.PrintMsg(LogClass.ServiceLdn, $"Proxy TCP client caught an error with code {error}"); + + _error.Set(); + } + + private void HandleProxyConfig(LdnHeader header, ProxyConfig config) + { + ProxyConfig = config; + + SocketHelpers.RegisterProxy(new LdnProxy(config, this, _protocol)); + + _ready.Set(); + } + + public bool EnsureProxyReady() + { + return _ready.WaitOne(FailureTimeout); + } + + public bool PerformAuth(ExternalProxyConfig config) + { + bool signalled = _connected.WaitOne(FailureTimeout); + + if (!signalled) + { + return false; + } + + SendAsync(_protocol.Encode(PacketId.ExternalProxy, config)); + + return true; + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/P2pProxyServer.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/P2pProxyServer.cs new file mode 100644 index 000000000..fbce5c10c --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/P2pProxyServer.cs @@ -0,0 +1,396 @@ +using Gommon; +using Humanizer; +using NetCoreServer; +using Open.Nat; +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types; +using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy +{ + class P2pProxyServer : TcpServer, IDisposable + { + public const ushort PrivatePortBase = 39990; + public const int PrivatePortRange = 10; + + private const ushort PublicPortBase = 39990; + private const int PublicPortRange = 10; + + private const ushort PortLeaseLength = 60; + private const ushort PortLeaseRenew = 50; + + private const ushort AuthWaitSeconds = 1; + + private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); + + public ushort PrivatePort { get; } + + private ushort _publicPort; + + private bool _disposed; + private readonly CancellationTokenSource _disposedCancellation = new CancellationTokenSource(); + + private NatDevice _natDevice; + private Mapping _portMapping; + + private readonly List _players = new List(); + + private readonly List _waitingTokens = new List(); + private readonly AutoResetEvent _tokenEvent = new AutoResetEvent(false); + + private uint _broadcastAddress; + + private readonly LdnMasterProxyClient _master; + private readonly RyuLdnProtocol _masterProtocol; + private readonly RyuLdnProtocol _protocol; + + public P2pProxyServer(LdnMasterProxyClient master, ushort port, RyuLdnProtocol masterProtocol) : base(IPAddress.Any, port) + { + if (ProxyHelpers.SupportsNoDelay()) + { + OptionNoDelay = true; + } + + PrivatePort = port; + + _master = master; + _masterProtocol = masterProtocol; + + _masterProtocol.ExternalProxyState += HandleStateChange; + _masterProtocol.ExternalProxyToken += HandleToken; + + _protocol = new RyuLdnProtocol(); + } + + private void HandleToken(LdnHeader header, ExternalProxyToken token) + { + _lock.EnterWriteLock(); + + _waitingTokens.Add(token); + + _lock.ExitWriteLock(); + + _tokenEvent.Set(); + } + + private void HandleStateChange(LdnHeader header, ExternalProxyConnectionState state) + { + if (!state.Connected) + { + _lock.EnterWriteLock(); + + _waitingTokens.RemoveAll(token => token.VirtualIp == state.IpAddress); + + _players.RemoveAll(player => + { + if (player.VirtualIpAddress == state.IpAddress) + { + player.DisconnectAndStop(); + + return true; + } + + return false; + }); + + _lock.ExitWriteLock(); + } + } + + public void Configure(ProxyConfig config) + { + _broadcastAddress = config.ProxyIp | (~config.ProxySubnetMask); + } + + public async Task NatPunch() + { + NatDiscoverer discoverer = new NatDiscoverer(); + CancellationTokenSource cts = new CancellationTokenSource(1000); + + NatDevice device; + + try + { + device = await discoverer.DiscoverDeviceAsync(PortMapper.Upnp, cts); + } + catch (NatDeviceNotFoundException) + { + return 0; + } + + _publicPort = PublicPortBase; + + for (int i = 0; i < PublicPortRange; i++) + { + try + { + _portMapping = new Mapping(Protocol.Tcp, PrivatePort, _publicPort, PortLeaseLength, "Ryujinx Local Multiplayer"); + + await device.CreatePortMapAsync(_portMapping); + + break; + } + catch (MappingException) + { + _publicPort++; + } + catch (Exception) + { + return 0; + } + + if (i == PublicPortRange - 1) + { + _publicPort = 0; + } + } + + if (_publicPort != 0) + { + _ = Executor.ExecuteAfterDelayAsync( + PortLeaseRenew.Seconds(), + _disposedCancellation.Token, + RefreshLease); + } + + _natDevice = device; + + return _publicPort; + } + + // Proxy handlers + + private void RouteMessage(P2pProxySession sender, ref ProxyInfo info, Action action) + { + if (info.SourceIpV4 == 0) + { + // If they sent from a connection bound on 0.0.0.0, make others see it as them. + info.SourceIpV4 = sender.VirtualIpAddress; + } + else if (info.SourceIpV4 != sender.VirtualIpAddress) + { + // Can't pretend to be somebody else. + return; + } + + uint destIp = info.DestIpV4; + + if (destIp == 0xc0a800ff) + { + destIp = _broadcastAddress; + } + + bool isBroadcast = destIp == _broadcastAddress; + + _lock.EnterReadLock(); + + if (isBroadcast) + { + _players.ForEach(player => + { + action(player); + }); + } + else + { + P2pProxySession target = _players.FirstOrDefault(player => player.VirtualIpAddress == destIp); + + if (target != null) + { + action(target); + } + } + + _lock.ExitReadLock(); + } + + public void HandleProxyDisconnect(P2pProxySession sender, LdnHeader header, ProxyDisconnectMessage message) + { + RouteMessage(sender, ref message.Info, (target) => + { + target.SendAsync(sender.Protocol.Encode(PacketId.ProxyDisconnect, message)); + }); + } + + public void HandleProxyData(P2pProxySession sender, LdnHeader header, ProxyDataHeader message, byte[] data) + { + RouteMessage(sender, ref message.Info, (target) => + { + target.SendAsync(sender.Protocol.Encode(PacketId.ProxyData, message, data)); + }); + } + + public void HandleProxyConnectReply(P2pProxySession sender, LdnHeader header, ProxyConnectResponse message) + { + RouteMessage(sender, ref message.Info, (target) => + { + target.SendAsync(sender.Protocol.Encode(PacketId.ProxyConnectReply, message)); + }); + } + + public void HandleProxyConnect(P2pProxySession sender, LdnHeader header, ProxyConnectRequest message) + { + RouteMessage(sender, ref message.Info, (target) => + { + target.SendAsync(sender.Protocol.Encode(PacketId.ProxyConnect, message)); + }); + } + + // End proxy handlers + + private async Task RefreshLease() + { + if (_disposed || _natDevice == null) + { + return; + } + + try + { + await _natDevice.CreatePortMapAsync(_portMapping); + } + catch (Exception) + { + + } + + _ = Executor.ExecuteAfterDelayAsync( + PortLeaseRenew.Milliseconds(), + _disposedCancellation.Token, + RefreshLease); + } + + public bool TryRegisterUser(P2pProxySession session, ExternalProxyConfig config) + { + _lock.EnterWriteLock(); + + // Attempt to find matching configuration. If we don't find one, wait for a bit and try again. + // Woken by new tokens coming in from the master server. + + IPAddress address = (session.Socket.RemoteEndPoint as IPEndPoint).Address; + byte[] addressBytes = ProxyHelpers.AddressTo16Byte(address); + + long time; + long endTime = Stopwatch.GetTimestamp() + Stopwatch.Frequency * AuthWaitSeconds; + + do + { + for (int i = 0; i < _waitingTokens.Count; i++) + { + ExternalProxyToken waitToken = _waitingTokens[i]; + + // Allow any client that has a private IP to connect. (indicated by the server as all 0 in the token) + + bool isPrivate = waitToken.PhysicalIp.AsSpan().SequenceEqual(new byte[16]); + bool ipEqual = isPrivate || waitToken.AddressFamily == address.AddressFamily && waitToken.PhysicalIp.AsSpan().SequenceEqual(addressBytes); + + if (ipEqual && waitToken.Token.AsSpan().SequenceEqual(config.Token.AsSpan())) + { + // This is a match. + + _waitingTokens.RemoveAt(i); + + session.SetIpv4(waitToken.VirtualIp); + + ProxyConfig pconfig = new ProxyConfig + { + ProxyIp = session.VirtualIpAddress, + ProxySubnetMask = 0xFFFF0000 // TODO: Use from server. + }; + + if (_players.Count == 0) + { + Configure(pconfig); + } + + _players.Add(session); + + session.SendAsync(_protocol.Encode(PacketId.ProxyConfig, pconfig)); + + _lock.ExitWriteLock(); + + return true; + } + } + + // Couldn't find the token. + // It may not have arrived yet, so wait for one to arrive. + + _lock.ExitWriteLock(); + + time = Stopwatch.GetTimestamp(); + int remainingMs = (int)((endTime - time) / (Stopwatch.Frequency / 1000)); + + if (remainingMs < 0) + { + remainingMs = 0; + } + + _tokenEvent.WaitOne(remainingMs); + + _lock.EnterWriteLock(); + + } while (time < endTime); + + _lock.ExitWriteLock(); + + return false; + } + + public void DisconnectProxyClient(P2pProxySession session) + { + _lock.EnterWriteLock(); + + bool removed = _players.Remove(session); + + if (removed) + { + _master.SendAsync(_masterProtocol.Encode(PacketId.ExternalProxyState, new ExternalProxyConnectionState + { + IpAddress = session.VirtualIpAddress, + Connected = false + })); + } + + _lock.ExitWriteLock(); + } + + public new void Dispose() + { + base.Dispose(); + + _disposed = true; + _disposedCancellation.Cancel(); + + try + { + Task delete = _natDevice?.DeletePortMapAsync(new Mapping(Protocol.Tcp, PrivatePort, _publicPort, 60, "Ryujinx Local Multiplayer")); + + // Just absorb any exceptions. + delete?.ContinueWith((task) => { }); + } + catch (Exception) + { + // Fail silently. + } + } + + protected override TcpSession CreateSession() + { + return new P2pProxySession(this); + } + + protected override void OnError(SocketError error) + { + Logger.Info?.PrintMsg(LogClass.ServiceLdn, $"Proxy TCP server caught an error with code {error}"); + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/P2pProxySession.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/P2pProxySession.cs new file mode 100644 index 000000000..515feeac5 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/P2pProxySession.cs @@ -0,0 +1,90 @@ +using NetCoreServer; +using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types; +using System; + +namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy +{ + class P2pProxySession : TcpSession + { + public uint VirtualIpAddress { get; private set; } + public RyuLdnProtocol Protocol { get; } + + private readonly P2pProxyServer _parent; + + private bool _masterClosed; + + public P2pProxySession(P2pProxyServer server) : base(server) + { + _parent = server; + + Protocol = new RyuLdnProtocol(); + + Protocol.ProxyDisconnect += HandleProxyDisconnect; + Protocol.ProxyData += HandleProxyData; + Protocol.ProxyConnectReply += HandleProxyConnectReply; + Protocol.ProxyConnect += HandleProxyConnect; + + Protocol.ExternalProxy += HandleAuthentication; + } + + private void HandleAuthentication(LdnHeader header, ExternalProxyConfig token) + { + if (!_parent.TryRegisterUser(this, token)) + { + Disconnect(); + } + } + + public void SetIpv4(uint ip) + { + VirtualIpAddress = ip; + } + + public void DisconnectAndStop() + { + _masterClosed = true; + + Disconnect(); + } + + protected override void OnDisconnected() + { + if (!_masterClosed) + { + _parent.DisconnectProxyClient(this); + } + } + + protected override void OnReceived(byte[] buffer, long offset, long size) + { + try + { + Protocol.Read(buffer, (int)offset, (int)size); + } + catch (Exception) + { + Disconnect(); + } + } + + private void HandleProxyDisconnect(LdnHeader header, ProxyDisconnectMessage message) + { + _parent.HandleProxyDisconnect(this, header, message); + } + + private void HandleProxyData(LdnHeader header, ProxyDataHeader message, byte[] data) + { + _parent.HandleProxyData(this, header, message, data); + } + + private void HandleProxyConnectReply(LdnHeader header, ProxyConnectResponse data) + { + _parent.HandleProxyConnectReply(this, header, data); + } + + private void HandleProxyConnect(LdnHeader header, ProxyConnectRequest message) + { + _parent.HandleProxyConnect(this, header, message); + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/ProxyHelpers.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/ProxyHelpers.cs new file mode 100644 index 000000000..42b1ab6a2 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Proxy/ProxyHelpers.cs @@ -0,0 +1,24 @@ +using System; +using System.Net; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy +{ + static class ProxyHelpers + { + public static byte[] AddressTo16Byte(IPAddress address) + { + byte[] ipBytes = new byte[16]; + byte[] srcBytes = address.GetAddressBytes(); + + Array.Copy(srcBytes, 0, ipBytes, 0, srcBytes.Length); + + return ipBytes; + } + + public static bool SupportsNoDelay() + { + return RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/RyuLdnProtocol.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/RyuLdnProtocol.cs new file mode 100644 index 000000000..d0eeaf125 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/RyuLdnProtocol.cs @@ -0,0 +1,380 @@ +using Ryujinx.Common.Utilities; +using Ryujinx.HLE.HOS.Services.Ldn.Types; +using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types; +using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types; +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu +{ + class RyuLdnProtocol + { + private const byte CurrentProtocolVersion = 1; + private const int Magic = ('R' << 0) | ('L' << 8) | ('D' << 16) | ('N' << 24); + private const int MaxPacketSize = 131072; + + private readonly int _headerSize = Marshal.SizeOf(); + + private readonly byte[] _buffer = new byte[MaxPacketSize]; + private int _bufferEnd = 0; + + // Client Packets. + public event Action Initialize; + public event Action Passphrase; + public event Action Connected; + public event Action SyncNetwork; + public event Action ScanReply; + public event Action ScanReplyEnd; + public event Action Disconnected; + + // External Proxy Packets. + public event Action ExternalProxy; + public event Action ExternalProxyState; + public event Action ExternalProxyToken; + + // Server Packets. + public event Action CreateAccessPoint; + public event Action CreateAccessPointPrivate; + public event Action Reject; + public event Action RejectReply; + public event Action SetAcceptPolicy; + public event Action SetAdvertiseData; + public event Action Connect; + public event Action ConnectPrivate; + public event Action Scan; + + // Proxy Packets. + public event Action ProxyConfig; + public event Action ProxyConnect; + public event Action ProxyConnectReply; + public event Action ProxyData; + public event Action ProxyDisconnect; + + // Lifecycle Packets. + public event Action NetworkError; + public event Action Ping; + + public RyuLdnProtocol() { } + + public void Reset() + { + _bufferEnd = 0; + } + + public void Read(byte[] data, int offset, int size) + { + int index = 0; + + while (index < size) + { + if (_bufferEnd < _headerSize) + { + // Assemble the header first. + + int copyable = Math.Min(size - index, Math.Min(size, _headerSize - _bufferEnd)); + + Array.Copy(data, index + offset, _buffer, _bufferEnd, copyable); + + index += copyable; + _bufferEnd += copyable; + } + + if (_bufferEnd >= _headerSize) + { + // The header is available. Make sure we received all the data (size specified in the header) + + LdnHeader ldnHeader = MemoryMarshal.Cast(_buffer)[0]; + + if (ldnHeader.Magic != Magic) + { + throw new InvalidOperationException("Invalid magic number in received packet."); + } + + if (ldnHeader.Version != CurrentProtocolVersion) + { + throw new InvalidOperationException($"Protocol version mismatch. Expected ${CurrentProtocolVersion}, was ${ldnHeader.Version}."); + } + + int finalSize = _headerSize + ldnHeader.DataSize; + + if (finalSize >= MaxPacketSize) + { + throw new InvalidOperationException($"Max packet size {MaxPacketSize} exceeded."); + } + + int copyable = Math.Min(size - index, Math.Min(size, finalSize - _bufferEnd)); + + Array.Copy(data, index + offset, _buffer, _bufferEnd, copyable); + + index += copyable; + _bufferEnd += copyable; + + if (finalSize == _bufferEnd) + { + // The full packet has been retrieved. Send it to be decoded. + + byte[] ldnData = new byte[ldnHeader.DataSize]; + + Array.Copy(_buffer, _headerSize, ldnData, 0, ldnData.Length); + + DecodeAndHandle(ldnHeader, ldnData); + + Reset(); + } + } + } + } + + private (T, byte[]) ParseWithData(byte[] data) where T : struct + { + T str = default; + int size = Marshal.SizeOf(str); + + byte[] remainder = new byte[data.Length - size]; + + if (remainder.Length > 0) + { + Array.Copy(data, size, remainder, 0, remainder.Length); + } + + return (MemoryMarshal.Read(data), remainder); + } + + private void DecodeAndHandle(LdnHeader header, byte[] data) + { + switch ((PacketId)header.Type) + { + // Client Packets. + case PacketId.Initialize: + { + Initialize?.Invoke(header, MemoryMarshal.Read(data)); + + break; + } + case PacketId.Passphrase: + { + Passphrase?.Invoke(header, MemoryMarshal.Read(data)); + + break; + } + case PacketId.Connected: + { + Connected?.Invoke(header, MemoryMarshal.Read(data)); + + break; + } + case PacketId.SyncNetwork: + { + SyncNetwork?.Invoke(header, MemoryMarshal.Read(data)); + + break; + } + case PacketId.ScanReply: + { + ScanReply?.Invoke(header, MemoryMarshal.Read(data)); + + break; + } + + case PacketId.ScanReplyEnd: + { + ScanReplyEnd?.Invoke(header); + + break; + } + case PacketId.Disconnect: + { + Disconnected?.Invoke(header, MemoryMarshal.Read(data)); + + break; + } + + // External Proxy Packets. + case PacketId.ExternalProxy: + { + ExternalProxy?.Invoke(header, MemoryMarshal.Read(data)); + + break; + } + case PacketId.ExternalProxyState: + { + ExternalProxyState?.Invoke(header, MemoryMarshal.Read(data)); + + break; + } + case PacketId.ExternalProxyToken: + { + ExternalProxyToken?.Invoke(header, MemoryMarshal.Read(data)); + + break; + } + + // Server Packets. + case PacketId.CreateAccessPoint: + { + (CreateAccessPointRequest packet, byte[] extraData) = ParseWithData(data); + CreateAccessPoint?.Invoke(header, packet, extraData); + break; + } + case PacketId.CreateAccessPointPrivate: + { + (CreateAccessPointPrivateRequest packet, byte[] extraData) = ParseWithData(data); + CreateAccessPointPrivate?.Invoke(header, packet, extraData); + break; + } + case PacketId.Reject: + { + Reject?.Invoke(header, MemoryMarshal.Read(data)); + + break; + } + case PacketId.RejectReply: + { + RejectReply?.Invoke(header); + + break; + } + case PacketId.SetAcceptPolicy: + { + SetAcceptPolicy?.Invoke(header, MemoryMarshal.Read(data)); + + break; + } + case PacketId.SetAdvertiseData: + { + SetAdvertiseData?.Invoke(header, data); + + break; + } + case PacketId.Connect: + { + Connect?.Invoke(header, MemoryMarshal.Read(data)); + + break; + } + case PacketId.ConnectPrivate: + { + ConnectPrivate?.Invoke(header, MemoryMarshal.Read(data)); + + break; + } + case PacketId.Scan: + { + Scan?.Invoke(header, MemoryMarshal.Read(data)); + + break; + } + + // Proxy Packets + case PacketId.ProxyConfig: + { + ProxyConfig?.Invoke(header, MemoryMarshal.Read(data)); + + break; + } + case PacketId.ProxyConnect: + { + ProxyConnect?.Invoke(header, MemoryMarshal.Read(data)); + + break; + } + case PacketId.ProxyConnectReply: + { + ProxyConnectReply?.Invoke(header, MemoryMarshal.Read(data)); + + break; + } + case PacketId.ProxyData: + { + (ProxyDataHeader packet, byte[] extraData) = ParseWithData(data); + + ProxyData?.Invoke(header, packet, extraData); + + break; + } + case PacketId.ProxyDisconnect: + { + ProxyDisconnect?.Invoke(header, MemoryMarshal.Read(data)); + + break; + } + + // Lifecycle Packets. + case PacketId.Ping: + { + Ping?.Invoke(header, MemoryMarshal.Read(data)); + + break; + } + case PacketId.NetworkError: + { + NetworkError?.Invoke(header, MemoryMarshal.Read(data)); + + break; + } + + default: + break; + } + } + + private static LdnHeader GetHeader(PacketId type, int dataSize) + { + return new LdnHeader() + { + Magic = Magic, + Version = CurrentProtocolVersion, + Type = (byte)type, + DataSize = dataSize + }; + } + + public byte[] Encode(PacketId type) + { + LdnHeader header = GetHeader(type, 0); + + return SpanHelpers.AsSpan(ref header).ToArray(); + } + + public byte[] Encode(PacketId type, byte[] data) + { + LdnHeader header = GetHeader(type, data.Length); + + byte[] result = SpanHelpers.AsSpan(ref header).ToArray(); + + Array.Resize(ref result, result.Length + data.Length); + Array.Copy(data, 0, result, Marshal.SizeOf(), data.Length); + + return result; + } + + public byte[] Encode(PacketId type, T packet) where T : unmanaged + { + byte[] packetData = SpanHelpers.AsSpan(ref packet).ToArray(); + + LdnHeader header = GetHeader(type, packetData.Length); + + byte[] result = SpanHelpers.AsSpan(ref header).ToArray(); + + Array.Resize(ref result, result.Length + packetData.Length); + Array.Copy(packetData, 0, result, Marshal.SizeOf(), packetData.Length); + + return result; + } + + public byte[] Encode(PacketId type, T packet, byte[] data) where T : unmanaged + { + byte[] packetData = SpanHelpers.AsSpan(ref packet).ToArray(); + + LdnHeader header = GetHeader(type, packetData.Length + data.Length); + + byte[] result = SpanHelpers.AsSpan(ref header).ToArray(); + + Array.Resize(ref result, result.Length + packetData.Length + data.Length); + Array.Copy(packetData, 0, result, Marshal.SizeOf(), packetData.Length); + Array.Copy(data, 0, result, Marshal.SizeOf() + packetData.Length, data.Length); + + return result; + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/DisconnectMessage.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/DisconnectMessage.cs new file mode 100644 index 000000000..448d33f29 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/DisconnectMessage.cs @@ -0,0 +1,10 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types +{ + [StructLayout(LayoutKind.Sequential, Size = 0x4)] + struct DisconnectMessage + { + public uint DisconnectIP; + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ExternalProxyConfig.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ExternalProxyConfig.cs new file mode 100644 index 000000000..9cbb80242 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ExternalProxyConfig.cs @@ -0,0 +1,19 @@ +using Ryujinx.Common.Memory; +using System.Net.Sockets; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types +{ + /// + /// Sent by the server to point a client towards an external server being used as a proxy. + /// The client then forwards this to the external proxy after connecting, to verify the connection worked. + /// + [StructLayout(LayoutKind.Sequential, Size = 0x26, Pack = 1)] + struct ExternalProxyConfig + { + public Array16 ProxyIp; + public AddressFamily AddressFamily; + public ushort ProxyPort; + public Array16 Token; + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ExternalProxyConnectionState.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ExternalProxyConnectionState.cs new file mode 100644 index 000000000..ecf4e14f7 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ExternalProxyConnectionState.cs @@ -0,0 +1,18 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types +{ + /// + /// Indicates a change in connection state for the given client. + /// Is sent to notify the master server when connection is first established. + /// Can be sent by the external proxy to the master server to notify it of a proxy disconnect. + /// Can be sent by the master server to notify the external proxy of a user leaving a room. + /// Both will result in a force kick. + /// + [StructLayout(LayoutKind.Sequential, Size = 0x8, Pack = 4)] + struct ExternalProxyConnectionState + { + public uint IpAddress; + public bool Connected; + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ExternalProxyToken.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ExternalProxyToken.cs new file mode 100644 index 000000000..0a8980c37 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ExternalProxyToken.cs @@ -0,0 +1,20 @@ +using Ryujinx.Common.Memory; +using System.Net.Sockets; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types +{ + /// + /// Sent by the master server to an external proxy to tell them someone is going to connect. + /// This drives authentication, and lets the proxy know what virtual IP to give to each joiner, + /// as these are managed by the master server. + /// + [StructLayout(LayoutKind.Sequential, Size = 0x28)] + struct ExternalProxyToken + { + public uint VirtualIp; + public Array16 Token; + public Array16 PhysicalIp; + public AddressFamily AddressFamily; + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/InitializeMessage.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/InitializeMessage.cs new file mode 100644 index 000000000..36ddc65fe --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/InitializeMessage.cs @@ -0,0 +1,20 @@ +using Ryujinx.Common.Memory; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types +{ + /// + /// This message is first sent by the client to identify themselves. + /// If the server has a token+mac combo that matches the submission, then they are returned their new ID and mac address. (the mac is also reassigned to the new id) + /// Otherwise, they are returned a random mac address. + /// + [StructLayout(LayoutKind.Sequential, Size = 0x16)] + struct InitializeMessage + { + // All 0 if we don't have an ID yet. + public Array16 Id; + + // All 0 if we don't have a mac yet. + public Array6 MacAddress; + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/LdnHeader.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/LdnHeader.cs new file mode 100644 index 000000000..f41f15ab4 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/LdnHeader.cs @@ -0,0 +1,13 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types +{ + [StructLayout(LayoutKind.Sequential, Size = 0xA)] + struct LdnHeader + { + public uint Magic; + public byte Type; + public byte Version; + public int DataSize; + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/PacketId.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/PacketId.cs new file mode 100644 index 000000000..b8ef5fbc1 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/PacketId.cs @@ -0,0 +1,36 @@ +namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types +{ + enum PacketId + { + Initialize, + Passphrase, + + CreateAccessPoint, + CreateAccessPointPrivate, + ExternalProxy, + ExternalProxyToken, + ExternalProxyState, + SyncNetwork, + Reject, + RejectReply, + Scan, + ScanReply, + ScanReplyEnd, + Connect, + ConnectPrivate, + Connected, + Disconnect, + + ProxyConfig, + ProxyConnect, + ProxyConnectReply, + ProxyData, + ProxyDisconnect, + + SetAcceptPolicy, + SetAdvertiseData, + + Ping = 254, + NetworkError = 255 + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/PassphraseMessage.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/PassphraseMessage.cs new file mode 100644 index 000000000..0deba0b07 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/PassphraseMessage.cs @@ -0,0 +1,11 @@ +using Ryujinx.Common.Memory; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types +{ + [StructLayout(LayoutKind.Sequential, Size = 0x80)] + struct PassphraseMessage + { + public Array128 Passphrase; + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/PingMessage.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/PingMessage.cs new file mode 100644 index 000000000..135e39caa --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/PingMessage.cs @@ -0,0 +1,11 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types +{ + [StructLayout(LayoutKind.Sequential, Size = 0x2)] + struct PingMessage + { + public byte Requester; + public byte Id; + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyConnectRequest.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyConnectRequest.cs new file mode 100644 index 000000000..ffce77791 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyConnectRequest.cs @@ -0,0 +1,10 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types +{ + [StructLayout(LayoutKind.Sequential, Size = 0x10)] + struct ProxyConnectRequest + { + public ProxyInfo Info; + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyConnectResponse.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyConnectResponse.cs new file mode 100644 index 000000000..de2e430fb --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyConnectResponse.cs @@ -0,0 +1,10 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types +{ + [StructLayout(LayoutKind.Sequential, Size = 0x10)] + struct ProxyConnectResponse + { + public ProxyInfo Info; + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyDataHeader.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyDataHeader.cs new file mode 100644 index 000000000..e46a40692 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyDataHeader.cs @@ -0,0 +1,14 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types +{ + /// + /// Represents data sent over a transport layer. + /// + [StructLayout(LayoutKind.Sequential, Size = 0x14)] + struct ProxyDataHeader + { + public ProxyInfo Info; + public uint DataLength; // Followed by the data with the specified byte length. + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyDataPacket.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyDataPacket.cs new file mode 100644 index 000000000..eb3648413 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyDataPacket.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types +{ + class ProxyDataPacket + { + public ProxyDataHeader Header; + public byte[] Data; + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyDisconnectMessage.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyDisconnectMessage.cs new file mode 100644 index 000000000..2154ae109 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyDisconnectMessage.cs @@ -0,0 +1,11 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types +{ + [StructLayout(LayoutKind.Sequential, Size = 0x14)] + struct ProxyDisconnectMessage + { + public ProxyInfo Info; + public int DisconnectReason; + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyInfo.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyInfo.cs new file mode 100644 index 000000000..d9338f244 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/ProxyInfo.cs @@ -0,0 +1,20 @@ +using System.Net.Sockets; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types +{ + /// + /// Information included in all proxied communication. + /// + [StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 1)] + struct ProxyInfo + { + public uint SourceIpV4; + public ushort SourcePort; + + public uint DestIpV4; + public ushort DestPort; + + public ProtocolType Protocol; + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/RejectRequest.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/RejectRequest.cs new file mode 100644 index 000000000..1c2ce1f8b --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/RejectRequest.cs @@ -0,0 +1,18 @@ +using Ryujinx.HLE.HOS.Services.Ldn.Types; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types +{ + [StructLayout(LayoutKind.Sequential, Size = 0x8)] + struct RejectRequest + { + public uint NodeId; + public DisconnectReason DisconnectReason; + + public RejectRequest(DisconnectReason disconnectReason, uint nodeId) + { + DisconnectReason = disconnectReason; + NodeId = nodeId; + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/RyuNetworkConfig.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/RyuNetworkConfig.cs new file mode 100644 index 000000000..f3bd72023 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/RyuNetworkConfig.cs @@ -0,0 +1,23 @@ +using Ryujinx.Common.Memory; +using System.Net.Sockets; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types +{ + [StructLayout(LayoutKind.Sequential, Size = 0x28, Pack = 1)] + struct RyuNetworkConfig + { + public Array16 GameVersion; + + // PrivateIp is included for external proxies for the case where a client attempts to join from + // their own LAN. UPnP forwarding can fail when connecting devices on the same network over the public IP, + // so if their public IP is identical, the internal address should be sent instead. + + // The fields below are 0 if not hosting a p2p proxy. + + public Array16 PrivateIp; + public AddressFamily AddressFamily; + public ushort ExternalProxyPort; + public ushort InternalProxyPort; + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/SetAcceptPolicyRequest.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/SetAcceptPolicyRequest.cs new file mode 100644 index 000000000..c4a969901 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnRyu/Types/SetAcceptPolicyRequest.cs @@ -0,0 +1,11 @@ +using Ryujinx.HLE.HOS.Services.Ldn.Types; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types +{ + [StructLayout(LayoutKind.Sequential, Size = 0x1, Pack = 1)] + struct SetAcceptPolicyRequest + { + public AcceptPolicy StationAcceptPolicy; + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/Station.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/Station.cs index e39c01978..fa43f789e 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/Station.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/Station.cs @@ -14,6 +14,8 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator public bool Connected { get; private set; } + public ProxyConfig Config => _parent.NetworkClient.Config; + public Station(IUserLocalCommunicationService parent) { _parent = parent; @@ -48,9 +50,12 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator public void Dispose() { - _parent.NetworkClient.DisconnectNetwork(); + if (_parent.NetworkClient != null) + { + _parent.NetworkClient.DisconnectNetwork(); - _parent.NetworkClient.NetworkChange -= NetworkChanged; + _parent.NetworkClient.NetworkChange -= NetworkChanged; + } } private ResultCode NetworkErrorToResult(NetworkError error) diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/Types/CreateAccessPointPrivateRequest.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/Types/CreateAccessPointPrivateRequest.cs index ac0ff7d94..0972c21c0 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/Types/CreateAccessPointPrivateRequest.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/Types/CreateAccessPointPrivateRequest.cs @@ -1,4 +1,5 @@ using Ryujinx.HLE.HOS.Services.Ldn.Types; +using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types; using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types @@ -14,5 +15,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types public UserConfig UserConfig; public NetworkConfig NetworkConfig; public AddressList AddressList; + + public RyuNetworkConfig RyuNetworkConfig; } } diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/Types/CreateAccessPointRequest.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/Types/CreateAccessPointRequest.cs index f67f0aac9..d2dc5b698 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/Types/CreateAccessPointRequest.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/Types/CreateAccessPointRequest.cs @@ -1,4 +1,5 @@ using Ryujinx.HLE.HOS.Services.Ldn.Types; +using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Types; using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types @@ -6,11 +7,13 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types /// /// Advertise data is appended separately (remaining data in the buffer). /// - [StructLayout(LayoutKind.Sequential, Size = 0x94, CharSet = CharSet.Ansi)] + [StructLayout(LayoutKind.Sequential, Size = 0xBC, Pack = 1)] struct CreateAccessPointRequest { public SecurityConfig SecurityConfig; public UserConfig UserConfig; public NetworkConfig NetworkConfig; + + public RyuNetworkConfig RyuNetworkConfig; } } diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/Types/ProxyConfig.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/Types/ProxyConfig.cs new file mode 100644 index 000000000..c89c08bbe --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/Types/ProxyConfig.cs @@ -0,0 +1,11 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types +{ + [StructLayout(LayoutKind.Sequential, Size = 0x8)] + struct ProxyConfig + { + public uint ProxyIp; + public uint ProxySubnetMask; + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Mii/UtilityImpl.cs b/src/Ryujinx.HLE/HOS/Services/Mii/UtilityImpl.cs index 32dbb4946..ea54a6ad6 100644 --- a/src/Ryujinx.HLE/HOS/Services/Mii/UtilityImpl.cs +++ b/src/Ryujinx.HLE/HOS/Services/Mii/UtilityImpl.cs @@ -63,7 +63,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii public CreateId MakeCreateId() { - UInt128 value = UInt128Utils.CreateRandom(); + UInt128 value = Random.Shared.NextUInt128(); // Ensure the random ID generated is valid as a create id. value &= ~new UInt128(0xC0, 0); diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboBinReader.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboBinReader.cs new file mode 100644 index 000000000..03063cb53 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboBinReader.cs @@ -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 HasAmiiboKeyFile => 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; + } + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboDecryptor.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboDecryptor.cs new file mode 100644 index 000000000..cc6d02ea2 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboDecryptor.cs @@ -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; + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboDump.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboDump.cs new file mode 100644 index 000000000..37d587dac --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboDump.cs @@ -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 seed = []; + + // 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; + } + } + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboMasterKey.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboMasterKey.cs new file mode 100644 index 000000000..940dc4c85 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/AmiiboDecryption/AmiiboMasterKey.cs @@ -0,0 +1,39 @@ +using System; +using System.Linq; + +namespace Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption +{ + public class AmiiboMasterKey + { + public byte[] HmacKey { get; private set; } // 16 bytes + public byte[] TypeString { get; private set; } // 14 bytes + public byte Rfu { get; private set; } // 1 byte + public byte MagicSize { get; private set; } // 1 byte + public byte[] MagicBytes { get; private set; } // 16 bytes + public byte[] XorPad { get; private set; } // 32 bytes + + public AmiiboMasterKey(byte[] data) + { + if (data.Length != 80) + throw new ArgumentException("Master key data must be 80 bytes."); + + HmacKey = data.Take(16).ToArray(); + TypeString = data.Skip(16).Take(14).ToArray(); + Rfu = data[30]; + MagicSize = data[31]; + MagicBytes = data.Skip(32).Take(16).ToArray(); + XorPad = data.Skip(48).Take(32).ToArray(); + } + + public static (AmiiboMasterKey DataKey, AmiiboMasterKey TagKey) FromCombinedBin(byte[] combinedBin) + { + if (combinedBin.Length != 160) + throw new ArgumentException($"Data is {combinedBin.Length} bytes (should be 160)."); + + byte[] dataBin = combinedBin.Take(80).ToArray(); + byte[] tagBin = combinedBin.Skip(80).Take(80).ToArray(); + + return (new AmiiboMasterKey(dataBin), new AmiiboMasterKey(tagBin)); + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/INfp.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/INfp.cs index 20f67a4ef..3256684f4 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/INfp.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/INfp.cs @@ -78,7 +78,6 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp if (_state == State.Initialized) { _cancelTokenSource?.Cancel(); - // NOTE: All events are destroyed here. context.Device.System.NfpDevices.Clear(); @@ -146,9 +145,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp break; } } - _cancelTokenSource = new CancellationTokenSource(); - Task.Run(() => { while (true) @@ -199,7 +196,6 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp break; } } - return ResultCode.Success; } @@ -229,7 +225,6 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp } // TODO: Found how the MountTarget is handled. - for (int i = 0; i < context.Device.System.NfpDevices.Count; i++) { if (context.Device.System.NfpDevices[i].Handle == (PlayerIndex)deviceHandle) @@ -488,14 +483,12 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp #pragma warning disable IDE0059 // Remove unnecessary value assignment uint deviceHandle = (uint)context.RequestData.ReadUInt64(); #pragma warning restore IDE0059 - if (context.Device.System.NfpDevices.Count == 0) { return ResultCode.DeviceNotFound; } // NOTE: Since we handle amiibo through VirtualAmiibo, we don't have to flush anything in our case. - return ResultCode.Success; } @@ -884,7 +877,6 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return ResultCode.Success; } } - return ResultCode.DeviceNotFound; } diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/VirtualAmiiboFile.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/VirtualAmiiboFile.cs index 65d380979..9450e1db5 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/VirtualAmiiboFile.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/NfpManager/Types/VirtualAmiiboFile.cs @@ -3,18 +3,19 @@ using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager { - struct VirtualAmiiboFile + public struct VirtualAmiiboFile { public uint FileVersion { get; set; } public byte[] TagUuid { get; set; } public string AmiiboId { get; set; } + public string NickName { get; set; } public DateTime FirstWriteDate { get; set; } public DateTime LastWriteDate { get; set; } public ushort WriteCounter { get; set; } public List ApplicationAreas { get; set; } } - struct VirtualAmiiboApplicationArea + public struct VirtualAmiiboApplicationArea { public uint ApplicationAreaId { get; set; } public byte[] ApplicationArea { get; set; } diff --git a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs index ba4a81e0e..2cb35472f 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nfc/Nfp/VirtualAmiibo.cs @@ -4,19 +4,22 @@ using Ryujinx.Common.Utilities; using Ryujinx.Cpu; using Ryujinx.HLE.HOS.Services.Mii; using Ryujinx.HLE.HOS.Services.Mii.Types; +using Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption; using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager; using System; using System.Collections.Generic; using System.IO; +using System.Linq; namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp { static class VirtualAmiibo { - private static uint _openedApplicationAreaId; - + public static uint OpenedApplicationAreaId; + public static byte[] ApplicationBytes = Array.Empty(); + public static string InputBin = string.Empty; + public static string NickName = string.Empty; private static readonly AmiiboJsonSerializerContext _serializerContext = AmiiboJsonSerializerContext.Default; - public static byte[] GenerateUuid(string amiiboId, bool useRandomUuid) { if (useRandomUuid) @@ -64,16 +67,22 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp }; } - public static RegisterInfo GetRegisterInfo(ITickSource tickSource, string amiiboId, string nickname) + public static RegisterInfo GetRegisterInfo(ITickSource tickSource, string amiiboId, string userName) { VirtualAmiiboFile amiiboFile = LoadAmiiboFile(amiiboId); - + string nickname = amiiboFile.NickName ?? "Ryujinx"; + if (NickName != string.Empty) + { + nickname = NickName; + NickName = string.Empty; + } UtilityImpl utilityImpl = new(tickSource); CharInfo charInfo = new(); charInfo.SetFromStoreData(StoreData.BuildDefault(utilityImpl, 0)); - charInfo.Nickname = Nickname.FromString(nickname); + // This is the player's name + charInfo.Nickname = Nickname.FromString(userName); RegisterInfo registerInfo = new() { @@ -85,18 +94,37 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp Reserved1 = new Array64(), Reserved2 = new Array58(), }; - "Ryujinx"u8.CopyTo(registerInfo.Nickname.AsSpan()); + // This is the amiibo's name + byte[] nicknameBytes = System.Text.Encoding.UTF8.GetBytes(nickname); + nicknameBytes.CopyTo(registerInfo.Nickname.AsSpan()); return registerInfo; } + public static void UpdateNickName(string amiiboId, string newNickName) + { + VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId); + virtualAmiiboFile.NickName = newNickName; + if (InputBin != string.Empty) + { + AmiiboBinReader.SaveBinFile(InputBin, virtualAmiiboFile.NickName); + return; + } + SaveAmiiboFile(virtualAmiiboFile); + } + public static bool OpenApplicationArea(string amiiboId, uint applicationAreaId) { VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId); - - if (virtualAmiiboFile.ApplicationAreas.Exists(item => item.ApplicationAreaId == applicationAreaId)) + if (ApplicationBytes.Length > 0) { - _openedApplicationAreaId = applicationAreaId; + OpenedApplicationAreaId = applicationAreaId; + return true; + } + + if (virtualAmiiboFile.ApplicationAreas.Any(item => item.ApplicationAreaId == applicationAreaId)) + { + OpenedApplicationAreaId = applicationAreaId; return true; } @@ -106,11 +134,17 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp public static byte[] GetApplicationArea(string amiiboId) { + if (ApplicationBytes.Length > 0) + { + byte[] bytes = ApplicationBytes; + ApplicationBytes = Array.Empty(); + return bytes; + } VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId); foreach (VirtualAmiiboApplicationArea applicationArea in virtualAmiiboFile.ApplicationAreas) { - if (applicationArea.ApplicationAreaId == _openedApplicationAreaId) + if (applicationArea.ApplicationAreaId == OpenedApplicationAreaId) { return applicationArea.ApplicationArea; } @@ -123,7 +157,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp { VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId); - if (virtualAmiiboFile.ApplicationAreas.Exists(item => item.ApplicationAreaId == applicationAreaId)) + if (virtualAmiiboFile.ApplicationAreas.Any(item => item.ApplicationAreaId == applicationAreaId)) { return false; } @@ -141,17 +175,22 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp public static void SetApplicationArea(string amiiboId, byte[] applicationAreaData) { + if (InputBin != string.Empty) + { + AmiiboBinReader.SaveBinFile(InputBin, applicationAreaData); + return; + } VirtualAmiiboFile virtualAmiiboFile = LoadAmiiboFile(amiiboId); - if (virtualAmiiboFile.ApplicationAreas.Exists(item => item.ApplicationAreaId == _openedApplicationAreaId)) + if (virtualAmiiboFile.ApplicationAreas.Any(item => item.ApplicationAreaId == OpenedApplicationAreaId)) { for (int i = 0; i < virtualAmiiboFile.ApplicationAreas.Count; i++) { - if (virtualAmiiboFile.ApplicationAreas[i].ApplicationAreaId == _openedApplicationAreaId) + if (virtualAmiiboFile.ApplicationAreas[i].ApplicationAreaId == OpenedApplicationAreaId) { virtualAmiiboFile.ApplicationAreas[i] = new VirtualAmiiboApplicationArea() { - ApplicationAreaId = _openedApplicationAreaId, + ApplicationAreaId = OpenedApplicationAreaId, ApplicationArea = applicationAreaData, }; @@ -194,10 +233,21 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp return virtualAmiiboFile; } - private static void SaveAmiiboFile(VirtualAmiiboFile virtualAmiiboFile) + public static void SaveAmiiboFile(VirtualAmiiboFile virtualAmiiboFile) { string filePath = Path.Join(AppDataManager.BaseDirPath, "system", "amiibo", $"{virtualAmiiboFile.AmiiboId}.json"); JsonHelper.SerializeToFile(filePath, virtualAmiiboFile, _serializerContext.VirtualAmiiboFile); } + + public static bool SaveFileExists(VirtualAmiiboFile virtualAmiiboFile) + { + if (InputBin != string.Empty) + { + SaveAmiiboFile(virtualAmiiboFile); + return true; + + } + return File.Exists(Path.Join(AppDataManager.BaseDirPath, "system", "amiibo", $"{virtualAmiiboFile.AmiiboId}.json")); + } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Ngct/NgctServer.cs b/src/Ryujinx.HLE/HOS/Services/Ngct/NgctServer.cs index f652ecda8..b076958eb 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ngct/NgctServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ngct/NgctServer.cs @@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Services.Ngct ulong bufferSize = context.Request.PtrBuff[0].Size; bool isMatch = false; - string text = ""; + string text = string.Empty; if (bufferSize != 0) { @@ -57,8 +57,8 @@ namespace Ryujinx.HLE.HOS.Services.Ngct ulong bufferFilteredPosition = context.Request.RecvListBuff[0].Position; - string text = ""; - string textFiltered = ""; + string text = string.Empty; + string textFiltered = string.Empty; if (bufferSize != 0) { diff --git a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs index 581a2906b..a5a822db3 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs @@ -78,7 +78,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService NetworkProfileData networkProfile = new() { - Uuid = UInt128Utils.CreateRandom(), + Uuid = Random.Shared.NextUInt128(), }; networkProfile.IpSettingData.IpAddressSetting = new IpAddressSetting(interfaceProperties, unicastAddress); diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs index ff9a67644..0f5d7547c 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs @@ -42,7 +42,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu public NvHostAsGpuDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, ulong owner) : base(context, owner) { - _asContext = new AddressSpaceContext(context.Device.Gpu.CreateMemoryManager(owner)); + _asContext = new AddressSpaceContext(context.Device.Gpu.CreateMemoryManager(owner, context.Device.Memory.Size)); _memoryAllocator = new NvMemoryAllocator(); } diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostEvent.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostEvent.cs index 8f851f37a..48622a224 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostEvent.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostEvent.cs @@ -26,7 +26,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl private NvFence _previousFailingFence; private uint _failingCount; - public readonly object Lock = new(); + public readonly Lock Lock = new(); /// /// Max failing count until waiting on CPU. diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostSyncPt.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostSyncPt.cs index b83c642e5..49637f757 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostSyncPt.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostCtrl/Types/NvHostSyncPt.cs @@ -17,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl private readonly Switch _device; - private readonly object _syncpointAllocatorLock = new(); + private readonly Lock _syncpointAllocatorLock = new(); public NvHostSyncpt(Switch device) { diff --git a/src/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs b/src/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs index 641795890..ea3bd84df 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs @@ -105,7 +105,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl titleName = "Unknown"; } - throw new InvalidSystemResourceException($"{titleName} ({fontTitle:x8}) system title not found! This font will not work, provide the system archive to fix this error. (See https://github.com/Ryujinx/Ryujinx#requirements for more information)"); + throw new InvalidSystemResourceException($"{titleName} ({fontTitle:x8}) system title not found! This font will not work, provide the system archive to fix this error. (See https://github.com/GreemDev/Ryujinx#requirements for more information)"); } } else diff --git a/src/Ryujinx.HLE/HOS/Services/Settings/NxSettings.cs b/src/Ryujinx.HLE/HOS/Services/Settings/NxSettings.cs index b2d4d55cc..b0b854f74 100644 --- a/src/Ryujinx.HLE/HOS/Services/Settings/NxSettings.cs +++ b/src/Ryujinx.HLE/HOS/Services/Settings/NxSettings.cs @@ -73,1529 +73,1529 @@ namespace Ryujinx.HLE.HOS.Services.Settings { "ns.sdcard!compare_sdcard", 0 }, { "ns.gamecard!mount_gamecard_result_value", 0 }, { "ns.gamecard!try_gamecard_access_result_value", 0 }, - { "nv!00008600", "" }, - { "nv!0007b25e", "" }, - { "nv!0083e1", "" }, - { "nv!01621887", "" }, - { "nv!03134743", "" }, - { "nv!0356afd0", "" }, - { "nv!0356afd1", "" }, - { "nv!0356afd2", "" }, - { "nv!0356afd3", "" }, - { "nv!094313", "" }, - { "nv!0x04dc09", "" }, - { "nv!0x111133", "" }, - { "nv!0x1aa483", "" }, - { "nv!0x1cb1cf", "" }, - { "nv!0x1cb1d0", "" }, - { "nv!0x1e3221", "" }, - { "nv!0x300fc8", "" }, - { "nv!0x301fc8", "" }, - { "nv!0x302fc8", "" }, - { "nv!0x3eec59", "" }, - { "nv!0x46b3ed", "" }, - { "nv!0x523dc0", "" }, - { "nv!0x523dc1", "" }, - { "nv!0x523dc2", "" }, - { "nv!0x523dc3", "" }, - { "nv!0x523dc4", "" }, - { "nv!0x523dc5", "" }, - { "nv!0x523dc6", "" }, - { "nv!0x523dd0", "" }, - { "nv!0x523dd1", "" }, - { "nv!0x523dd3", "" }, - { "nv!0x5344bb", "" }, - { "nv!0x555237", "" }, - { "nv!0x58a234", "" }, - { "nv!0x7b4428", "" }, - { "nv!0x923dc0", "" }, - { "nv!0x923dc1", "" }, - { "nv!0x923dc2", "" }, - { "nv!0x923dc3", "" }, - { "nv!0x923dc4", "" }, - { "nv!0x923dd3", "" }, - { "nv!0x9abdc5", "" }, - { "nv!0x9abdc6", "" }, - { "nv!0xaaa36c", "" }, - { "nv!0xb09da0", "" }, - { "nv!0xb09da1", "" }, - { "nv!0xb09da2", "" }, - { "nv!0xb09da3", "" }, - { "nv!0xb09da4", "" }, - { "nv!0xb09da5", "" }, - { "nv!0xb0b348", "" }, - { "nv!0xb0b349", "" }, - { "nv!0xbb558f", "" }, - { "nv!0xbd10fb", "" }, - { "nv!0xc32ad3", "" }, - { "nv!0xce2348", "" }, - { "nv!0xcfd81f", "" }, - { "nv!0xe0036b", "" }, - { "nv!0xe01f2d", "" }, - { "nv!0xe17212", "" }, - { "nv!0xeae966", "" }, - { "nv!0xed4f82", "" }, - { "nv!0xf12335", "" }, - { "nv!0xf12336", "" }, - { "nv!10261989", "" }, - { "nv!1042d483", "" }, - { "nv!10572898", "" }, - { "nv!115631", "" }, - { "nv!12950094", "" }, - { "nv!1314f311", "" }, - { "nv!1314f312", "" }, - { "nv!13279512", "" }, - { "nv!13813496", "" }, - { "nv!14507179", "" }, - { "nv!15694569", "" }, - { "nv!16936964", "" }, - { "nv!17aa230c", "" }, - { "nv!182054", "" }, - { "nv!18273275", "" }, - { "nv!18273276", "" }, - { "nv!1854d03b", "" }, - { "nv!18add00d", "" }, - { "nv!19156670", "" }, - { "nv!19286545", "" }, - { "nv!1a298e9f", "" }, - { "nv!1acf43fe", "" }, - { "nv!1bda43fe", "" }, - { "nv!1c3b92", "" }, - { "nv!21509920", "" }, - { "nv!215323457", "" }, - { "nv!2165ad", "" }, - { "nv!2165ae", "" }, - { "nv!21be9c", "" }, - { "nv!233264316", "" }, - { "nv!234557580", "" }, - { "nv!23cd0e", "" }, - { "nv!24189123", "" }, - { "nv!2443266", "" }, - { "nv!25025519", "" }, - { "nv!255e39", "" }, - { "nv!2583364", "" }, - { "nv!2888c1", "" }, - { "nv!28ca3e", "" }, - { "nv!29871243", "" }, - { "nv!2a1f64", "" }, - { "nv!2dc432", "" }, - { "nv!2de437", "" }, - { "nv!2f3bb89c", "" }, - { "nv!2fd652", "" }, - { "nv!3001ac", "" }, - { "nv!31298772", "" }, - { "nv!313233", "" }, - { "nv!31f7d603", "" }, - { "nv!320ce4", "" }, - { "nv!32153248", "" }, - { "nv!32153249", "" }, - { "nv!335bca", "" }, - { "nv!342abb", "" }, - { "nv!34dfe6", "" }, - { "nv!34dfe7", "" }, - { "nv!34dfe8", "" }, - { "nv!34dfe9", "" }, - { "nv!35201578", "" }, - { "nv!359278", "" }, - { "nv!37f53a", "" }, - { "nv!38144972", "" }, - { "nv!38542646", "" }, - { "nv!3b74c9", "" }, - { "nv!3c136f", "" }, - { "nv!3cf72823", "" }, - { "nv!3d7af029", "" }, - { "nv!3ff34782", "" }, - { "nv!4129618", "" }, - { "nv!4189fac3", "" }, - { "nv!420bd4", "" }, - { "nv!42a699", "" }, - { "nv!441369", "" }, - { "nv!4458713e", "" }, - { "nv!4554b6", "" }, - { "nv!457425", "" }, - { "nv!4603b207", "" }, - { "nv!46574957", "" }, - { "nv!46574958", "" }, - { "nv!46813529", "" }, - { "nv!46f1e13d", "" }, - { "nv!47534c43", "" }, - { "nv!48550336", "" }, - { "nv!48576893", "" }, - { "nv!48576894", "" }, - { "nv!4889ac02", "" }, - { "nv!49005740", "" }, - { "nv!49867584", "" }, - { "nv!49960973", "" }, - { "nv!4a5341", "" }, - { "nv!4f4e48", "" }, - { "nv!4f8a0a", "" }, - { "nv!50299698", "" }, - { "nv!50299699", "" }, - { "nv!50361291", "" }, - { "nv!5242ae", "" }, - { "nv!53d30c", "" }, - { "nv!56347a", "" }, - { "nv!563a95f1", "" }, - { "nv!573823", "" }, - { "nv!58027529", "" }, - { "nv!5d2d63", "" }, - { "nv!5f7e3b", "" }, - { "nv!60461793", "" }, - { "nv!60d355", "" }, - { "nv!616627aa", "" }, - { "nv!62317182", "" }, - { "nv!6253fa2e", "" }, - { "nv!64100768", "" }, - { "nv!64100769", "" }, - { "nv!64100770", "" }, - { "nv!647395", "" }, - { "nv!66543234", "" }, - { "nv!67674763", "" }, - { "nv!67739784", "" }, - { "nv!68fb9c", "" }, - { "nv!69801276", "" }, - { "nv!6af9fa2f", "" }, - { "nv!6af9fa3f", "" }, - { "nv!6af9fa4f", "" }, - { "nv!6bd8c7", "" }, - { "nv!6c7691", "" }, - { "nv!6d4296ce", "" }, - { "nv!6dd7e7", "" }, - { "nv!6dd7e8", "" }, - { "nv!6fe11ec1", "" }, - { "nv!716511763", "" }, - { "nv!72504593", "" }, - { "nv!73304097", "" }, - { "nv!73314098", "" }, - { "nv!74095213", "" }, - { "nv!74095213a", "" }, - { "nv!74095213b", "" }, - { "nv!74095214", "" }, - { "nv!748f9649", "" }, - { "nv!75494732", "" }, - { "nv!78452832", "" }, - { "nv!784561", "" }, - { "nv!78e16b9c", "" }, - { "nv!79251225", "" }, - { "nv!7c128b", "" }, - { "nv!7ccd93", "" }, - { "nv!7df8d1", "" }, - { "nv!800c2310", "" }, - { "nv!80546710", "" }, - { "nv!80772310", "" }, - { "nv!808ee280", "" }, - { "nv!81131154", "" }, - { "nv!81274457", "" }, - { "nv!8292291f", "" }, - { "nv!83498426", "" }, - { "nv!84993794", "" }, - { "nv!84995585", "" }, - { "nv!84a0a0", "" }, - { "nv!852142", "" }, - { "nv!85612309", "" }, - { "nv!85612310", "" }, - { "nv!85612311", "" }, - { "nv!85612312", "" }, - { "nv!8623ff27", "" }, - { "nv!87364952", "" }, - { "nv!87f6275666", "" }, - { "nv!886748", "" }, - { "nv!89894423", "" }, - { "nv!8ad8a75", "" }, - { "nv!8ad8ad00", "" }, - { "nv!8bb815", "" }, - { "nv!8bb817", "" }, - { "nv!8bb818", "" }, - { "nv!8bb819", "" }, - { "nv!8e640cd1", "" }, - { "nv!8f34971a", "" }, - { "nv!8f773984", "" }, - { "nv!8f7a7d", "" }, - { "nv!902486209", "" }, - { "nv!90482571", "" }, - { "nv!91214835", "" }, - { "nv!912848290", "" }, - { "nv!915e56", "" }, - { "nv!92179063", "" }, - { "nv!92179064", "" }, - { "nv!92179065", "" }, - { "nv!92179066", "" }, - { "nv!92350358", "" }, - { "nv!92809063", "" }, - { "nv!92809064", "" }, - { "nv!92809065", "" }, - { "nv!92809066", "" }, - { "nv!92920143", "" }, - { "nv!93a89b12", "" }, - { "nv!93a89c0b", "" }, - { "nv!94812574", "" }, - { "nv!95282304", "" }, - { "nv!95394027", "" }, - { "nv!959b1f", "" }, - { "nv!9638af", "" }, - { "nv!96fd59", "" }, - { "nv!97f6275666", "" }, - { "nv!97f6275667", "" }, - { "nv!97f6275668", "" }, - { "nv!97f6275669", "" }, - { "nv!97f627566a", "" }, - { "nv!97f627566b", "" }, - { "nv!97f627566d", "" }, - { "nv!97f627566e", "" }, - { "nv!97f627566f", "" }, - { "nv!97f6275670", "" }, - { "nv!97f6275671", "" }, - { "nv!97f727566e", "" }, - { "nv!98480775", "" }, - { "nv!98480776", "" }, - { "nv!98480777", "" }, - { "nv!992431", "" }, - { "nv!9aa29065", "" }, - { "nv!9af32c", "" }, - { "nv!9af32d", "" }, - { "nv!9af32e", "" }, - { "nv!9c108b71", "" }, - { "nv!9f279065", "" }, - { "nv!a01bc728", "" }, - { "nv!a13b46c80", "" }, - { "nv!a22eb0", "" }, - { "nv!a2fb451e", "" }, - { "nv!a3456abe", "" }, - { "nv!a7044887", "" }, - { "nv!a7149200", "" }, - { "nv!a766215670", "" }, - { "nv!aac_drc_boost", "" }, - { "nv!aac_drc_cut", "" }, - { "nv!aac_drc_enc_target_level", "" }, - { "nv!aac_drc_heavy", "" }, - { "nv!aac_drc_reference_level", "" }, - { "nv!aalinegamma", "" }, - { "nv!aalinetweaks", "" }, - { "nv!ab34ee01", "" }, - { "nv!ab34ee02", "" }, - { "nv!ab34ee03", "" }, - { "nv!ac0274", "" }, - { "nv!af73c63e", "" }, - { "nv!af73c63f", "" }, - { "nv!af9927", "" }, - { "nv!afoverride", "" }, - { "nv!allocdeviceevents", "" }, - { "nv!applicationkey", "" }, - { "nv!appreturnonlybasicglsltype", "" }, - { "nv!app_softimage", "" }, - { "nv!app_supportbits2", "" }, - { "nv!assumetextureismipmappedatcreation", "" }, - { "nv!b1fb0f01", "" }, - { "nv!b3edd5", "" }, - { "nv!b40d9e03d", "" }, - { "nv!b7f6275666", "" }, - { "nv!b812c1", "" }, - { "nv!ba14ba1a", "" }, - { "nv!ba14ba1b", "" }, - { "nv!bd7559", "" }, - { "nv!bd755a", "" }, - { "nv!bd755c", "" }, - { "nv!bd755d", "" }, - { "nv!be58bb", "" }, - { "nv!be92cb", "" }, - { "nv!beefcba3", "" }, - { "nv!beefcba4", "" }, - { "nv!c023777f", "" }, - { "nv!c09dc8", "" }, - { "nv!c0d340", "" }, - { "nv!c2ff374c", "" }, - { "nv!c5e9d7a3", "" }, - { "nv!c5e9d7a4", "" }, - { "nv!c5e9d7b4", "" }, - { "nv!c618f9", "" }, - { "nv!ca345840", "" }, - { "nv!cachedisable", "" }, - { "nv!cast.on", "" }, - { "nv!cde", "" }, - { "nv!channelpriorityoverride", "" }, - { "nv!cleardatastorevidmem", "" }, - { "nv!cmdbufmemoryspaceenables", "" }, - { "nv!cmdbufminwords", "" }, - { "nv!cmdbufsizewords", "" }, - { "nv!conformantblitframebufferscissor", "" }, - { "nv!conformantincompletetextures", "" }, - { "nv!copybuffermethod", "" }, - { "nv!cubemapaniso", "" }, - { "nv!cubemapfiltering", "" }, - { "nv!d0e9a4d7", "" }, - { "nv!d13733f12", "" }, - { "nv!d1b399", "" }, - { "nv!d2983c32", "" }, - { "nv!d2983c33", "" }, - { "nv!d2e71b", "" }, - { "nv!d377dc", "" }, - { "nv!d377dd", "" }, - { "nv!d489f4", "" }, - { "nv!d4bce1", "" }, - { "nv!d518cb", "" }, - { "nv!d518cd", "" }, - { "nv!d518ce", "" }, - { "nv!d518d0", "" }, - { "nv!d518d1", "" }, - { "nv!d518d2", "" }, - { "nv!d518d3", "" }, - { "nv!d518d4", "" }, - { "nv!d518d5", "" }, - { "nv!d59eda", "" }, - { "nv!d83cbd", "" }, - { "nv!d8e777", "" }, - { "nv!debug_level", "" }, - { "nv!debug_mask", "" }, - { "nv!debug_options", "" }, - { "nv!devshmpageableallocations", "" }, - { "nv!df1f9812", "" }, - { "nv!df783c", "" }, - { "nv!diagenable", "" }, - { "nv!disallowcemask", "" }, - { "nv!disallowz16", "" }, - { "nv!dlmemoryspaceenables", "" }, - { "nv!e0bfec", "" }, - { "nv!e433456d", "" }, - { "nv!e435563f", "" }, - { "nv!e4cd9c", "" }, - { "nv!e5c972", "" }, - { "nv!e639ef", "" }, - { "nv!e802af", "" }, - { "nv!eae964", "" }, - { "nv!earlytexturehwallocation", "" }, - { "nv!eb92a3", "" }, - { "nv!ebca56", "" }, - { "nv!enable-noaud", "" }, - { "nv!enable-noavs", "" }, - { "nv!enable-prof", "" }, - { "nv!enable-sxesmode", "" }, - { "nv!enable-ulld", "" }, - { "nv!expert_detail_level", "" }, - { "nv!expert_output_mask", "" }, - { "nv!expert_report_mask", "" }, - { "nv!extensionstringnvarch", "" }, - { "nv!extensionstringversion", "" }, - { "nv!f00f1938", "" }, - { "nv!f10736", "" }, - { "nv!f1846870", "" }, - { "nv!f33bc370", "" }, - { "nv!f392a874", "" }, - { "nv!f49ae8", "" }, - { "nv!fa345cce", "" }, - { "nv!fa35cc4", "" }, - { "nv!faa14a", "" }, - { "nv!faf8a723", "" }, - { "nv!fastgs", "" }, - { "nv!fbf4ac45", "" }, - { "nv!fbo_blit_ignore_srgb", "" }, - { "nv!fc64c7", "" }, - { "nv!ff54ec97", "" }, - { "nv!ff54ec98", "" }, - { "nv!forceexitprocessdetach", "" }, - { "nv!forcerequestedesversion", "" }, - { "nv!__gl_", "" }, - { "nv!__gl_00008600", "" }, - { "nv!__gl_0007b25e", "" }, - { "nv!__gl_0083e1", "" }, - { "nv!__gl_01621887", "" }, - { "nv!__gl_03134743", "" }, - { "nv!__gl_0356afd0", "" }, - { "nv!__gl_0356afd1", "" }, - { "nv!__gl_0356afd2", "" }, - { "nv!__gl_0356afd3", "" }, - { "nv!__gl_094313", "" }, - { "nv!__gl_0x04dc09", "" }, - { "nv!__gl_0x111133", "" }, - { "nv!__gl_0x1aa483", "" }, - { "nv!__gl_0x1cb1cf", "" }, - { "nv!__gl_0x1cb1d0", "" }, - { "nv!__gl_0x1e3221", "" }, - { "nv!__gl_0x300fc8", "" }, - { "nv!__gl_0x301fc8", "" }, - { "nv!__gl_0x302fc8", "" }, - { "nv!__gl_0x3eec59", "" }, - { "nv!__gl_0x46b3ed", "" }, - { "nv!__gl_0x523dc0", "" }, - { "nv!__gl_0x523dc1", "" }, - { "nv!__gl_0x523dc2", "" }, - { "nv!__gl_0x523dc3", "" }, - { "nv!__gl_0x523dc4", "" }, - { "nv!__gl_0x523dc5", "" }, - { "nv!__gl_0x523dc6", "" }, - { "nv!__gl_0x523dd0", "" }, - { "nv!__gl_0x523dd1", "" }, - { "nv!__gl_0x523dd3", "" }, - { "nv!__gl_0x5344bb", "" }, - { "nv!__gl_0x555237", "" }, - { "nv!__gl_0x58a234", "" }, - { "nv!__gl_0x7b4428", "" }, - { "nv!__gl_0x923dc0", "" }, - { "nv!__gl_0x923dc1", "" }, - { "nv!__gl_0x923dc2", "" }, - { "nv!__gl_0x923dc3", "" }, - { "nv!__gl_0x923dc4", "" }, - { "nv!__gl_0x923dd3", "" }, - { "nv!__gl_0x9abdc5", "" }, - { "nv!__gl_0x9abdc6", "" }, - { "nv!__gl_0xaaa36c", "" }, - { "nv!__gl_0xb09da0", "" }, - { "nv!__gl_0xb09da1", "" }, - { "nv!__gl_0xb09da2", "" }, - { "nv!__gl_0xb09da3", "" }, - { "nv!__gl_0xb09da4", "" }, - { "nv!__gl_0xb09da5", "" }, - { "nv!__gl_0xb0b348", "" }, - { "nv!__gl_0xb0b349", "" }, - { "nv!__gl_0xbb558f", "" }, - { "nv!__gl_0xbd10fb", "" }, - { "nv!__gl_0xc32ad3", "" }, - { "nv!__gl_0xce2348", "" }, - { "nv!__gl_0xcfd81f", "" }, - { "nv!__gl_0xe0036b", "" }, - { "nv!__gl_0xe01f2d", "" }, - { "nv!__gl_0xe17212", "" }, - { "nv!__gl_0xeae966", "" }, - { "nv!__gl_0xed4f82", "" }, - { "nv!__gl_0xf12335", "" }, - { "nv!__gl_0xf12336", "" }, - { "nv!__gl_10261989", "" }, - { "nv!__gl_1042d483", "" }, - { "nv!__gl_10572898", "" }, - { "nv!__gl_115631", "" }, - { "nv!__gl_12950094", "" }, - { "nv!__gl_1314f311", "" }, - { "nv!__gl_1314f312", "" }, - { "nv!__gl_13279512", "" }, - { "nv!__gl_13813496", "" }, - { "nv!__gl_14507179", "" }, - { "nv!__gl_15694569", "" }, - { "nv!__gl_16936964", "" }, - { "nv!__gl_17aa230c", "" }, - { "nv!__gl_182054", "" }, - { "nv!__gl_18273275", "" }, - { "nv!__gl_18273276", "" }, - { "nv!__gl_1854d03b", "" }, - { "nv!__gl_18add00d", "" }, - { "nv!__gl_19156670", "" }, - { "nv!__gl_19286545", "" }, - { "nv!__gl_1a298e9f", "" }, - { "nv!__gl_1acf43fe", "" }, - { "nv!__gl_1bda43fe", "" }, - { "nv!__gl_1c3b92", "" }, - { "nv!__gl_21509920", "" }, - { "nv!__gl_215323457", "" }, - { "nv!__gl_2165ad", "" }, - { "nv!__gl_2165ae", "" }, - { "nv!__gl_21be9c", "" }, - { "nv!__gl_233264316", "" }, - { "nv!__gl_234557580", "" }, - { "nv!__gl_23cd0e", "" }, - { "nv!__gl_24189123", "" }, - { "nv!__gl_2443266", "" }, - { "nv!__gl_25025519", "" }, - { "nv!__gl_255e39", "" }, - { "nv!__gl_2583364", "" }, - { "nv!__gl_2888c1", "" }, - { "nv!__gl_28ca3e", "" }, - { "nv!__gl_29871243", "" }, - { "nv!__gl_2a1f64", "" }, - { "nv!__gl_2dc432", "" }, - { "nv!__gl_2de437", "" }, - { "nv!__gl_2f3bb89c", "" }, - { "nv!__gl_2fd652", "" }, - { "nv!__gl_3001ac", "" }, - { "nv!__gl_31298772", "" }, - { "nv!__gl_313233", "" }, - { "nv!__gl_31f7d603", "" }, - { "nv!__gl_320ce4", "" }, - { "nv!__gl_32153248", "" }, - { "nv!__gl_32153249", "" }, - { "nv!__gl_335bca", "" }, - { "nv!__gl_342abb", "" }, - { "nv!__gl_34dfe6", "" }, - { "nv!__gl_34dfe7", "" }, - { "nv!__gl_34dfe8", "" }, - { "nv!__gl_34dfe9", "" }, - { "nv!__gl_35201578", "" }, - { "nv!__gl_359278", "" }, - { "nv!__gl_37f53a", "" }, - { "nv!__gl_38144972", "" }, - { "nv!__gl_38542646", "" }, - { "nv!__gl_3b74c9", "" }, - { "nv!__gl_3c136f", "" }, - { "nv!__gl_3cf72823", "" }, - { "nv!__gl_3d7af029", "" }, - { "nv!__gl_3ff34782", "" }, - { "nv!__gl_4129618", "" }, - { "nv!__gl_4189fac3", "" }, - { "nv!__gl_420bd4", "" }, - { "nv!__gl_42a699", "" }, - { "nv!__gl_441369", "" }, - { "nv!__gl_4458713e", "" }, - { "nv!__gl_4554b6", "" }, - { "nv!__gl_457425", "" }, - { "nv!__gl_4603b207", "" }, - { "nv!__gl_46574957", "" }, - { "nv!__gl_46574958", "" }, - { "nv!__gl_46813529", "" }, - { "nv!__gl_46f1e13d", "" }, - { "nv!__gl_47534c43", "" }, - { "nv!__gl_48550336", "" }, - { "nv!__gl_48576893", "" }, - { "nv!__gl_48576894", "" }, - { "nv!__gl_4889ac02", "" }, - { "nv!__gl_49005740", "" }, - { "nv!__gl_49867584", "" }, - { "nv!__gl_49960973", "" }, - { "nv!__gl_4a5341", "" }, - { "nv!__gl_4f4e48", "" }, - { "nv!__gl_4f8a0a", "" }, - { "nv!__gl_50299698", "" }, - { "nv!__gl_50299699", "" }, - { "nv!__gl_50361291", "" }, - { "nv!__gl_5242ae", "" }, - { "nv!__gl_53d30c", "" }, - { "nv!__gl_56347a", "" }, - { "nv!__gl_563a95f1", "" }, - { "nv!__gl_573823", "" }, - { "nv!__gl_58027529", "" }, - { "nv!__gl_5d2d63", "" }, - { "nv!__gl_5f7e3b", "" }, - { "nv!__gl_60461793", "" }, - { "nv!__gl_60d355", "" }, - { "nv!__gl_616627aa", "" }, - { "nv!__gl_62317182", "" }, - { "nv!__gl_6253fa2e", "" }, - { "nv!__gl_64100768", "" }, - { "nv!__gl_64100769", "" }, - { "nv!__gl_64100770", "" }, - { "nv!__gl_647395", "" }, - { "nv!__gl_66543234", "" }, - { "nv!__gl_67674763", "" }, - { "nv!__gl_67739784", "" }, - { "nv!__gl_68fb9c", "" }, - { "nv!__gl_69801276", "" }, - { "nv!__gl_6af9fa2f", "" }, - { "nv!__gl_6af9fa3f", "" }, - { "nv!__gl_6af9fa4f", "" }, - { "nv!__gl_6bd8c7", "" }, - { "nv!__gl_6c7691", "" }, - { "nv!__gl_6d4296ce", "" }, - { "nv!__gl_6dd7e7", "" }, - { "nv!__gl_6dd7e8", "" }, - { "nv!__gl_6fe11ec1", "" }, - { "nv!__gl_716511763", "" }, - { "nv!__gl_72504593", "" }, - { "nv!__gl_73304097", "" }, - { "nv!__gl_73314098", "" }, - { "nv!__gl_74095213", "" }, - { "nv!__gl_74095213a", "" }, - { "nv!__gl_74095213b", "" }, - { "nv!__gl_74095214", "" }, - { "nv!__gl_748f9649", "" }, - { "nv!__gl_75494732", "" }, - { "nv!__gl_78452832", "" }, - { "nv!__gl_784561", "" }, - { "nv!__gl_78e16b9c", "" }, - { "nv!__gl_79251225", "" }, - { "nv!__gl_7c128b", "" }, - { "nv!__gl_7ccd93", "" }, - { "nv!__gl_7df8d1", "" }, - { "nv!__gl_800c2310", "" }, - { "nv!__gl_80546710", "" }, - { "nv!__gl_80772310", "" }, - { "nv!__gl_808ee280", "" }, - { "nv!__gl_81131154", "" }, - { "nv!__gl_81274457", "" }, - { "nv!__gl_8292291f", "" }, - { "nv!__gl_83498426", "" }, - { "nv!__gl_84993794", "" }, - { "nv!__gl_84995585", "" }, - { "nv!__gl_84a0a0", "" }, - { "nv!__gl_852142", "" }, - { "nv!__gl_85612309", "" }, - { "nv!__gl_85612310", "" }, - { "nv!__gl_85612311", "" }, - { "nv!__gl_85612312", "" }, - { "nv!__gl_8623ff27", "" }, - { "nv!__gl_87364952", "" }, - { "nv!__gl_87f6275666", "" }, - { "nv!__gl_886748", "" }, - { "nv!__gl_89894423", "" }, - { "nv!__gl_8ad8a75", "" }, - { "nv!__gl_8ad8ad00", "" }, - { "nv!__gl_8bb815", "" }, - { "nv!__gl_8bb817", "" }, - { "nv!__gl_8bb818", "" }, - { "nv!__gl_8bb819", "" }, - { "nv!__gl_8e640cd1", "" }, - { "nv!__gl_8f34971a", "" }, - { "nv!__gl_8f773984", "" }, - { "nv!__gl_8f7a7d", "" }, - { "nv!__gl_902486209", "" }, - { "nv!__gl_90482571", "" }, - { "nv!__gl_91214835", "" }, - { "nv!__gl_912848290", "" }, - { "nv!__gl_915e56", "" }, - { "nv!__gl_92179063", "" }, - { "nv!__gl_92179064", "" }, - { "nv!__gl_92179065", "" }, - { "nv!__gl_92179066", "" }, - { "nv!__gl_92350358", "" }, - { "nv!__gl_92809063", "" }, - { "nv!__gl_92809064", "" }, - { "nv!__gl_92809065", "" }, - { "nv!__gl_92809066", "" }, - { "nv!__gl_92920143", "" }, - { "nv!__gl_93a89b12", "" }, - { "nv!__gl_93a89c0b", "" }, - { "nv!__gl_94812574", "" }, - { "nv!__gl_95282304", "" }, - { "nv!__gl_95394027", "" }, - { "nv!__gl_959b1f", "" }, - { "nv!__gl_9638af", "" }, - { "nv!__gl_96fd59", "" }, - { "nv!__gl_97f6275666", "" }, - { "nv!__gl_97f6275667", "" }, - { "nv!__gl_97f6275668", "" }, - { "nv!__gl_97f6275669", "" }, - { "nv!__gl_97f627566a", "" }, - { "nv!__gl_97f627566b", "" }, - { "nv!__gl_97f627566d", "" }, - { "nv!__gl_97f627566e", "" }, - { "nv!__gl_97f627566f", "" }, - { "nv!__gl_97f6275670", "" }, - { "nv!__gl_97f6275671", "" }, - { "nv!__gl_97f727566e", "" }, - { "nv!__gl_98480775", "" }, - { "nv!__gl_98480776", "" }, - { "nv!__gl_98480777", "" }, - { "nv!__gl_992431", "" }, - { "nv!__gl_9aa29065", "" }, - { "nv!__gl_9af32c", "" }, - { "nv!__gl_9af32d", "" }, - { "nv!__gl_9af32e", "" }, - { "nv!__gl_9c108b71", "" }, - { "nv!__gl_9f279065", "" }, - { "nv!__gl_a01bc728", "" }, - { "nv!__gl_a13b46c80", "" }, - { "nv!__gl_a22eb0", "" }, - { "nv!__gl_a2fb451e", "" }, - { "nv!__gl_a3456abe", "" }, - { "nv!__gl_a7044887", "" }, - { "nv!__gl_a7149200", "" }, - { "nv!__gl_a766215670", "" }, - { "nv!__gl_aalinegamma", "" }, - { "nv!__gl_aalinetweaks", "" }, - { "nv!__gl_ab34ee01", "" }, - { "nv!__gl_ab34ee02", "" }, - { "nv!__gl_ab34ee03", "" }, - { "nv!__gl_ac0274", "" }, - { "nv!__gl_af73c63e", "" }, - { "nv!__gl_af73c63f", "" }, - { "nv!__gl_af9927", "" }, - { "nv!__gl_afoverride", "" }, - { "nv!__gl_allocdeviceevents", "" }, - { "nv!__gl_applicationkey", "" }, - { "nv!__gl_appreturnonlybasicglsltype", "" }, - { "nv!__gl_app_softimage", "" }, - { "nv!__gl_app_supportbits2", "" }, - { "nv!__gl_assumetextureismipmappedatcreation", "" }, - { "nv!__gl_b1fb0f01", "" }, - { "nv!__gl_b3edd5", "" }, - { "nv!__gl_b40d9e03d", "" }, - { "nv!__gl_b7f6275666", "" }, - { "nv!__gl_b812c1", "" }, - { "nv!__gl_ba14ba1a", "" }, - { "nv!__gl_ba14ba1b", "" }, - { "nv!__gl_bd7559", "" }, - { "nv!__gl_bd755a", "" }, - { "nv!__gl_bd755c", "" }, - { "nv!__gl_bd755d", "" }, - { "nv!__gl_be58bb", "" }, - { "nv!__gl_be92cb", "" }, - { "nv!__gl_beefcba3", "" }, - { "nv!__gl_beefcba4", "" }, - { "nv!__gl_c023777f", "" }, - { "nv!__gl_c09dc8", "" }, - { "nv!__gl_c0d340", "" }, - { "nv!__gl_c2ff374c", "" }, - { "nv!__gl_c5e9d7a3", "" }, - { "nv!__gl_c5e9d7a4", "" }, - { "nv!__gl_c5e9d7b4", "" }, - { "nv!__gl_c618f9", "" }, - { "nv!__gl_ca345840", "" }, - { "nv!__gl_cachedisable", "" }, - { "nv!__gl_channelpriorityoverride", "" }, - { "nv!__gl_cleardatastorevidmem", "" }, - { "nv!__gl_cmdbufmemoryspaceenables", "" }, - { "nv!__gl_cmdbufminwords", "" }, - { "nv!__gl_cmdbufsizewords", "" }, - { "nv!__gl_conformantblitframebufferscissor", "" }, - { "nv!__gl_conformantincompletetextures", "" }, - { "nv!__gl_copybuffermethod", "" }, - { "nv!__gl_cubemapaniso", "" }, - { "nv!__gl_cubemapfiltering", "" }, - { "nv!__gl_d0e9a4d7", "" }, - { "nv!__gl_d13733f12", "" }, - { "nv!__gl_d1b399", "" }, - { "nv!__gl_d2983c32", "" }, - { "nv!__gl_d2983c33", "" }, - { "nv!__gl_d2e71b", "" }, - { "nv!__gl_d377dc", "" }, - { "nv!__gl_d377dd", "" }, - { "nv!__gl_d489f4", "" }, - { "nv!__gl_d4bce1", "" }, - { "nv!__gl_d518cb", "" }, - { "nv!__gl_d518cd", "" }, - { "nv!__gl_d518ce", "" }, - { "nv!__gl_d518d0", "" }, - { "nv!__gl_d518d1", "" }, - { "nv!__gl_d518d2", "" }, - { "nv!__gl_d518d3", "" }, - { "nv!__gl_d518d4", "" }, - { "nv!__gl_d518d5", "" }, - { "nv!__gl_d59eda", "" }, - { "nv!__gl_d83cbd", "" }, - { "nv!__gl_d8e777", "" }, - { "nv!__gl_debug_level", "" }, - { "nv!__gl_debug_mask", "" }, - { "nv!__gl_debug_options", "" }, - { "nv!__gl_devshmpageableallocations", "" }, - { "nv!__gl_df1f9812", "" }, - { "nv!__gl_df783c", "" }, - { "nv!__gl_diagenable", "" }, - { "nv!__gl_disallowcemask", "" }, - { "nv!__gl_disallowz16", "" }, - { "nv!__gl_dlmemoryspaceenables", "" }, - { "nv!__gl_e0bfec", "" }, - { "nv!__gl_e433456d", "" }, - { "nv!__gl_e435563f", "" }, - { "nv!__gl_e4cd9c", "" }, - { "nv!__gl_e5c972", "" }, - { "nv!__gl_e639ef", "" }, - { "nv!__gl_e802af", "" }, - { "nv!__gl_eae964", "" }, - { "nv!__gl_earlytexturehwallocation", "" }, - { "nv!__gl_eb92a3", "" }, - { "nv!__gl_ebca56", "" }, - { "nv!__gl_expert_detail_level", "" }, - { "nv!__gl_expert_output_mask", "" }, - { "nv!__gl_expert_report_mask", "" }, - { "nv!__gl_extensionstringnvarch", "" }, - { "nv!__gl_extensionstringversion", "" }, - { "nv!__gl_f00f1938", "" }, - { "nv!__gl_f10736", "" }, - { "nv!__gl_f1846870", "" }, - { "nv!__gl_f33bc370", "" }, - { "nv!__gl_f392a874", "" }, - { "nv!__gl_f49ae8", "" }, - { "nv!__gl_fa345cce", "" }, - { "nv!__gl_fa35cc4", "" }, - { "nv!__gl_faa14a", "" }, - { "nv!__gl_faf8a723", "" }, - { "nv!__gl_fastgs", "" }, - { "nv!__gl_fbf4ac45", "" }, - { "nv!__gl_fbo_blit_ignore_srgb", "" }, - { "nv!__gl_fc64c7", "" }, - { "nv!__gl_ff54ec97", "" }, - { "nv!__gl_ff54ec98", "" }, - { "nv!__gl_forceexitprocessdetach", "" }, - { "nv!__gl_forcerequestedesversion", "" }, - { "nv!__gl_glsynctovblank", "" }, - { "nv!__gl_gvitimeoutcontrol", "" }, - { "nv!__gl_hcctrl", "" }, - { "nv!__gl_hwstate_per_ctx", "" }, - { "nv!__gl_machinecachelimit", "" }, - { "nv!__gl_maxframesallowed", "" }, - { "nv!__gl_memmgrcachedalloclimit", "" }, - { "nv!__gl_memmgrcachedalloclimitratio", "" }, - { "nv!__gl_memmgrsysheapalloclimit", "" }, - { "nv!__gl_memmgrsysheapalloclimitratio", "" }, - { "nv!__gl_memmgrvidheapalloclimit", "" }, - { "nv!__gl_mosaic_clip_to_subdev", "" }, - { "nv!__gl_mosaic_clip_to_subdev_h_overlap", "" }, - { "nv!__gl_mosaic_clip_to_subdev_v_overlap", "" }, - { "nv!__gl_overlaymergeblittimerms", "" }, - { "nv!__gl_perfmon_mode", "" }, - { "nv!__gl_pixbar_mode", "" }, - { "nv!__gl_qualityenhancements", "" }, - { "nv!__gl_r27s18q28", "" }, - { "nv!__gl_r2d7c1d8", "" }, - { "nv!__gl_renderer", "" }, - { "nv!__gl_renderqualityflags", "" }, - { "nv!__gl_s3tcquality", "" }, - { "nv!__gl_shaderatomics", "" }, - { "nv!__gl_shadercacheinitsize", "" }, - { "nv!__gl_shader_disk_cache_path", "" }, - { "nv!__gl_shader_disk_cache_read_only", "" }, - { "nv!__gl_shaderobjects", "" }, - { "nv!__gl_shaderportabilitywarnings", "" }, - { "nv!__gl_shaderwarningsaserrors", "" }, - { "nv!__gl_skiptexturehostcopies", "" }, - { "nv!__glslc_debug_level", "" }, - { "nv!__glslc_debug_mask", "" }, - { "nv!__glslc_debug_options", "" }, - { "nv!__glslc_debug_filename", "" }, - { "nv!__gl_sli_dli_control", "" }, - { "nv!__gl_sparsetexture", "" }, - { "nv!__gl_spinlooptimeout", "" }, - { "nv!__gl_sync_to_vblank", "" }, - { "nv!glsynctovblank", "" }, - { "nv!__gl_sysheapreuseratio", "" }, - { "nv!__gl_sysmemtexturepromotion", "" }, - { "nv!__gl_targetflushcount", "" }, - { "nv!__gl_tearingfreeswappresent", "" }, - { "nv!__gl_texclampbehavior", "" }, - { "nv!__gl_texlodbias", "" }, - { "nv!__gl_texmemoryspaceenables", "" }, - { "nv!__gl_textureprecache", "" }, - { "nv!__gl_threadcontrol", "" }, - { "nv!__gl_threadcontrol2", "" }, - { "nv!__gl_usegvievents", "" }, - { "nv!__gl_vbomemoryspaceenables", "" }, - { "nv!__gl_vertexlimit", "" }, - { "nv!__gl_vidheapreuseratio", "" }, - { "nv!__gl_vpipe", "" }, - { "nv!__gl_vpipeformatbloatlimit", "" }, - { "nv!__gl_wglmessageboxonabort", "" }, - { "nv!__gl_writeinfolog", "" }, - { "nv!__gl_writeprogramobjectassembly", "" }, - { "nv!__gl_writeprogramobjectsource", "" }, - { "nv!__gl_xnvadapterpresent", "" }, - { "nv!__gl_yield", "" }, - { "nv!__gl_yieldfunction", "" }, - { "nv!__gl_yieldfunctionfast", "" }, - { "nv!__gl_yieldfunctionslow", "" }, - { "nv!__gl_yieldfunctionwaitfordcqueue", "" }, - { "nv!__gl_yieldfunctionwaitforframe", "" }, - { "nv!__gl_yieldfunctionwaitforgpu", "" }, - { "nv!__gl_zbctableaddhysteresis", "" }, - { "nv!gpu_debug_mode", "" }, - { "nv!gpu_stay_on", "" }, - { "nv!gpu_timeout_ms_max", "" }, - { "nv!gvitimeoutcontrol", "" }, - { "nv!hcctrl", "" }, - { "nv!hwstate_per_ctx", "" }, - { "nv!libandroid_enable_log", "" }, - { "nv!machinecachelimit", "" }, - { "nv!maxframesallowed", "" }, - { "nv!media.aac_51_output_enabled", "" }, - { "nv!memmgrcachedalloclimit", "" }, - { "nv!memmgrcachedalloclimitratio", "" }, - { "nv!memmgrsysheapalloclimit", "" }, - { "nv!memmgrsysheapalloclimitratio", "" }, - { "nv!memmgrvidheapalloclimit", "" }, - { "nv!mosaic_clip_to_subdev", "" }, - { "nv!mosaic_clip_to_subdev_h_overlap", "" }, - { "nv!mosaic_clip_to_subdev_v_overlap", "" }, - { "nv!nvblit.dump", "" }, - { "nv!nvblit.profile", "" }, - { "nv!nvblit.twod", "" }, - { "nv!nvblit.vic", "" }, - { "nv!nvddk_vic_prevent_use", "" }, - { "nv!nv_decompression", "" }, + { "nv!00008600", string.Empty }, + { "nv!0007b25e", string.Empty }, + { "nv!0083e1", string.Empty }, + { "nv!01621887", string.Empty }, + { "nv!03134743", string.Empty }, + { "nv!0356afd0", string.Empty }, + { "nv!0356afd1", string.Empty }, + { "nv!0356afd2", string.Empty }, + { "nv!0356afd3", string.Empty }, + { "nv!094313", string.Empty }, + { "nv!0x04dc09", string.Empty }, + { "nv!0x111133", string.Empty }, + { "nv!0x1aa483", string.Empty }, + { "nv!0x1cb1cf", string.Empty }, + { "nv!0x1cb1d0", string.Empty }, + { "nv!0x1e3221", string.Empty }, + { "nv!0x300fc8", string.Empty }, + { "nv!0x301fc8", string.Empty }, + { "nv!0x302fc8", string.Empty }, + { "nv!0x3eec59", string.Empty }, + { "nv!0x46b3ed", string.Empty }, + { "nv!0x523dc0", string.Empty }, + { "nv!0x523dc1", string.Empty }, + { "nv!0x523dc2", string.Empty }, + { "nv!0x523dc3", string.Empty }, + { "nv!0x523dc4", string.Empty }, + { "nv!0x523dc5", string.Empty }, + { "nv!0x523dc6", string.Empty }, + { "nv!0x523dd0", string.Empty }, + { "nv!0x523dd1", string.Empty }, + { "nv!0x523dd3", string.Empty }, + { "nv!0x5344bb", string.Empty }, + { "nv!0x555237", string.Empty }, + { "nv!0x58a234", string.Empty }, + { "nv!0x7b4428", string.Empty }, + { "nv!0x923dc0", string.Empty }, + { "nv!0x923dc1", string.Empty }, + { "nv!0x923dc2", string.Empty }, + { "nv!0x923dc3", string.Empty }, + { "nv!0x923dc4", string.Empty }, + { "nv!0x923dd3", string.Empty }, + { "nv!0x9abdc5", string.Empty }, + { "nv!0x9abdc6", string.Empty }, + { "nv!0xaaa36c", string.Empty }, + { "nv!0xb09da0", string.Empty }, + { "nv!0xb09da1", string.Empty }, + { "nv!0xb09da2", string.Empty }, + { "nv!0xb09da3", string.Empty }, + { "nv!0xb09da4", string.Empty }, + { "nv!0xb09da5", string.Empty }, + { "nv!0xb0b348", string.Empty }, + { "nv!0xb0b349", string.Empty }, + { "nv!0xbb558f", string.Empty }, + { "nv!0xbd10fb", string.Empty }, + { "nv!0xc32ad3", string.Empty }, + { "nv!0xce2348", string.Empty }, + { "nv!0xcfd81f", string.Empty }, + { "nv!0xe0036b", string.Empty }, + { "nv!0xe01f2d", string.Empty }, + { "nv!0xe17212", string.Empty }, + { "nv!0xeae966", string.Empty }, + { "nv!0xed4f82", string.Empty }, + { "nv!0xf12335", string.Empty }, + { "nv!0xf12336", string.Empty }, + { "nv!10261989", string.Empty }, + { "nv!1042d483", string.Empty }, + { "nv!10572898", string.Empty }, + { "nv!115631", string.Empty }, + { "nv!12950094", string.Empty }, + { "nv!1314f311", string.Empty }, + { "nv!1314f312", string.Empty }, + { "nv!13279512", string.Empty }, + { "nv!13813496", string.Empty }, + { "nv!14507179", string.Empty }, + { "nv!15694569", string.Empty }, + { "nv!16936964", string.Empty }, + { "nv!17aa230c", string.Empty }, + { "nv!182054", string.Empty }, + { "nv!18273275", string.Empty }, + { "nv!18273276", string.Empty }, + { "nv!1854d03b", string.Empty }, + { "nv!18add00d", string.Empty }, + { "nv!19156670", string.Empty }, + { "nv!19286545", string.Empty }, + { "nv!1a298e9f", string.Empty }, + { "nv!1acf43fe", string.Empty }, + { "nv!1bda43fe", string.Empty }, + { "nv!1c3b92", string.Empty }, + { "nv!21509920", string.Empty }, + { "nv!215323457", string.Empty }, + { "nv!2165ad", string.Empty }, + { "nv!2165ae", string.Empty }, + { "nv!21be9c", string.Empty }, + { "nv!233264316", string.Empty }, + { "nv!234557580", string.Empty }, + { "nv!23cd0e", string.Empty }, + { "nv!24189123", string.Empty }, + { "nv!2443266", string.Empty }, + { "nv!25025519", string.Empty }, + { "nv!255e39", string.Empty }, + { "nv!2583364", string.Empty }, + { "nv!2888c1", string.Empty }, + { "nv!28ca3e", string.Empty }, + { "nv!29871243", string.Empty }, + { "nv!2a1f64", string.Empty }, + { "nv!2dc432", string.Empty }, + { "nv!2de437", string.Empty }, + { "nv!2f3bb89c", string.Empty }, + { "nv!2fd652", string.Empty }, + { "nv!3001ac", string.Empty }, + { "nv!31298772", string.Empty }, + { "nv!313233", string.Empty }, + { "nv!31f7d603", string.Empty }, + { "nv!320ce4", string.Empty }, + { "nv!32153248", string.Empty }, + { "nv!32153249", string.Empty }, + { "nv!335bca", string.Empty }, + { "nv!342abb", string.Empty }, + { "nv!34dfe6", string.Empty }, + { "nv!34dfe7", string.Empty }, + { "nv!34dfe8", string.Empty }, + { "nv!34dfe9", string.Empty }, + { "nv!35201578", string.Empty }, + { "nv!359278", string.Empty }, + { "nv!37f53a", string.Empty }, + { "nv!38144972", string.Empty }, + { "nv!38542646", string.Empty }, + { "nv!3b74c9", string.Empty }, + { "nv!3c136f", string.Empty }, + { "nv!3cf72823", string.Empty }, + { "nv!3d7af029", string.Empty }, + { "nv!3ff34782", string.Empty }, + { "nv!4129618", string.Empty }, + { "nv!4189fac3", string.Empty }, + { "nv!420bd4", string.Empty }, + { "nv!42a699", string.Empty }, + { "nv!441369", string.Empty }, + { "nv!4458713e", string.Empty }, + { "nv!4554b6", string.Empty }, + { "nv!457425", string.Empty }, + { "nv!4603b207", string.Empty }, + { "nv!46574957", string.Empty }, + { "nv!46574958", string.Empty }, + { "nv!46813529", string.Empty }, + { "nv!46f1e13d", string.Empty }, + { "nv!47534c43", string.Empty }, + { "nv!48550336", string.Empty }, + { "nv!48576893", string.Empty }, + { "nv!48576894", string.Empty }, + { "nv!4889ac02", string.Empty }, + { "nv!49005740", string.Empty }, + { "nv!49867584", string.Empty }, + { "nv!49960973", string.Empty }, + { "nv!4a5341", string.Empty }, + { "nv!4f4e48", string.Empty }, + { "nv!4f8a0a", string.Empty }, + { "nv!50299698", string.Empty }, + { "nv!50299699", string.Empty }, + { "nv!50361291", string.Empty }, + { "nv!5242ae", string.Empty }, + { "nv!53d30c", string.Empty }, + { "nv!56347a", string.Empty }, + { "nv!563a95f1", string.Empty }, + { "nv!573823", string.Empty }, + { "nv!58027529", string.Empty }, + { "nv!5d2d63", string.Empty }, + { "nv!5f7e3b", string.Empty }, + { "nv!60461793", string.Empty }, + { "nv!60d355", string.Empty }, + { "nv!616627aa", string.Empty }, + { "nv!62317182", string.Empty }, + { "nv!6253fa2e", string.Empty }, + { "nv!64100768", string.Empty }, + { "nv!64100769", string.Empty }, + { "nv!64100770", string.Empty }, + { "nv!647395", string.Empty }, + { "nv!66543234", string.Empty }, + { "nv!67674763", string.Empty }, + { "nv!67739784", string.Empty }, + { "nv!68fb9c", string.Empty }, + { "nv!69801276", string.Empty }, + { "nv!6af9fa2f", string.Empty }, + { "nv!6af9fa3f", string.Empty }, + { "nv!6af9fa4f", string.Empty }, + { "nv!6bd8c7", string.Empty }, + { "nv!6c7691", string.Empty }, + { "nv!6d4296ce", string.Empty }, + { "nv!6dd7e7", string.Empty }, + { "nv!6dd7e8", string.Empty }, + { "nv!6fe11ec1", string.Empty }, + { "nv!716511763", string.Empty }, + { "nv!72504593", string.Empty }, + { "nv!73304097", string.Empty }, + { "nv!73314098", string.Empty }, + { "nv!74095213", string.Empty }, + { "nv!74095213a", string.Empty }, + { "nv!74095213b", string.Empty }, + { "nv!74095214", string.Empty }, + { "nv!748f9649", string.Empty }, + { "nv!75494732", string.Empty }, + { "nv!78452832", string.Empty }, + { "nv!784561", string.Empty }, + { "nv!78e16b9c", string.Empty }, + { "nv!79251225", string.Empty }, + { "nv!7c128b", string.Empty }, + { "nv!7ccd93", string.Empty }, + { "nv!7df8d1", string.Empty }, + { "nv!800c2310", string.Empty }, + { "nv!80546710", string.Empty }, + { "nv!80772310", string.Empty }, + { "nv!808ee280", string.Empty }, + { "nv!81131154", string.Empty }, + { "nv!81274457", string.Empty }, + { "nv!8292291f", string.Empty }, + { "nv!83498426", string.Empty }, + { "nv!84993794", string.Empty }, + { "nv!84995585", string.Empty }, + { "nv!84a0a0", string.Empty }, + { "nv!852142", string.Empty }, + { "nv!85612309", string.Empty }, + { "nv!85612310", string.Empty }, + { "nv!85612311", string.Empty }, + { "nv!85612312", string.Empty }, + { "nv!8623ff27", string.Empty }, + { "nv!87364952", string.Empty }, + { "nv!87f6275666", string.Empty }, + { "nv!886748", string.Empty }, + { "nv!89894423", string.Empty }, + { "nv!8ad8a75", string.Empty }, + { "nv!8ad8ad00", string.Empty }, + { "nv!8bb815", string.Empty }, + { "nv!8bb817", string.Empty }, + { "nv!8bb818", string.Empty }, + { "nv!8bb819", string.Empty }, + { "nv!8e640cd1", string.Empty }, + { "nv!8f34971a", string.Empty }, + { "nv!8f773984", string.Empty }, + { "nv!8f7a7d", string.Empty }, + { "nv!902486209", string.Empty }, + { "nv!90482571", string.Empty }, + { "nv!91214835", string.Empty }, + { "nv!912848290", string.Empty }, + { "nv!915e56", string.Empty }, + { "nv!92179063", string.Empty }, + { "nv!92179064", string.Empty }, + { "nv!92179065", string.Empty }, + { "nv!92179066", string.Empty }, + { "nv!92350358", string.Empty }, + { "nv!92809063", string.Empty }, + { "nv!92809064", string.Empty }, + { "nv!92809065", string.Empty }, + { "nv!92809066", string.Empty }, + { "nv!92920143", string.Empty }, + { "nv!93a89b12", string.Empty }, + { "nv!93a89c0b", string.Empty }, + { "nv!94812574", string.Empty }, + { "nv!95282304", string.Empty }, + { "nv!95394027", string.Empty }, + { "nv!959b1f", string.Empty }, + { "nv!9638af", string.Empty }, + { "nv!96fd59", string.Empty }, + { "nv!97f6275666", string.Empty }, + { "nv!97f6275667", string.Empty }, + { "nv!97f6275668", string.Empty }, + { "nv!97f6275669", string.Empty }, + { "nv!97f627566a", string.Empty }, + { "nv!97f627566b", string.Empty }, + { "nv!97f627566d", string.Empty }, + { "nv!97f627566e", string.Empty }, + { "nv!97f627566f", string.Empty }, + { "nv!97f6275670", string.Empty }, + { "nv!97f6275671", string.Empty }, + { "nv!97f727566e", string.Empty }, + { "nv!98480775", string.Empty }, + { "nv!98480776", string.Empty }, + { "nv!98480777", string.Empty }, + { "nv!992431", string.Empty }, + { "nv!9aa29065", string.Empty }, + { "nv!9af32c", string.Empty }, + { "nv!9af32d", string.Empty }, + { "nv!9af32e", string.Empty }, + { "nv!9c108b71", string.Empty }, + { "nv!9f279065", string.Empty }, + { "nv!a01bc728", string.Empty }, + { "nv!a13b46c80", string.Empty }, + { "nv!a22eb0", string.Empty }, + { "nv!a2fb451e", string.Empty }, + { "nv!a3456abe", string.Empty }, + { "nv!a7044887", string.Empty }, + { "nv!a7149200", string.Empty }, + { "nv!a766215670", string.Empty }, + { "nv!aac_drc_boost", string.Empty }, + { "nv!aac_drc_cut", string.Empty }, + { "nv!aac_drc_enc_target_level", string.Empty }, + { "nv!aac_drc_heavy", string.Empty }, + { "nv!aac_drc_reference_level", string.Empty }, + { "nv!aalinegamma", string.Empty }, + { "nv!aalinetweaks", string.Empty }, + { "nv!ab34ee01", string.Empty }, + { "nv!ab34ee02", string.Empty }, + { "nv!ab34ee03", string.Empty }, + { "nv!ac0274", string.Empty }, + { "nv!af73c63e", string.Empty }, + { "nv!af73c63f", string.Empty }, + { "nv!af9927", string.Empty }, + { "nv!afoverride", string.Empty }, + { "nv!allocdeviceevents", string.Empty }, + { "nv!applicationkey", string.Empty }, + { "nv!appreturnonlybasicglsltype", string.Empty }, + { "nv!app_softimage", string.Empty }, + { "nv!app_supportbits2", string.Empty }, + { "nv!assumetextureismipmappedatcreation", string.Empty }, + { "nv!b1fb0f01", string.Empty }, + { "nv!b3edd5", string.Empty }, + { "nv!b40d9e03d", string.Empty }, + { "nv!b7f6275666", string.Empty }, + { "nv!b812c1", string.Empty }, + { "nv!ba14ba1a", string.Empty }, + { "nv!ba14ba1b", string.Empty }, + { "nv!bd7559", string.Empty }, + { "nv!bd755a", string.Empty }, + { "nv!bd755c", string.Empty }, + { "nv!bd755d", string.Empty }, + { "nv!be58bb", string.Empty }, + { "nv!be92cb", string.Empty }, + { "nv!beefcba3", string.Empty }, + { "nv!beefcba4", string.Empty }, + { "nv!c023777f", string.Empty }, + { "nv!c09dc8", string.Empty }, + { "nv!c0d340", string.Empty }, + { "nv!c2ff374c", string.Empty }, + { "nv!c5e9d7a3", string.Empty }, + { "nv!c5e9d7a4", string.Empty }, + { "nv!c5e9d7b4", string.Empty }, + { "nv!c618f9", string.Empty }, + { "nv!ca345840", string.Empty }, + { "nv!cachedisable", string.Empty }, + { "nv!cast.on", string.Empty }, + { "nv!cde", string.Empty }, + { "nv!channelpriorityoverride", string.Empty }, + { "nv!cleardatastorevidmem", string.Empty }, + { "nv!cmdbufmemoryspaceenables", string.Empty }, + { "nv!cmdbufminwords", string.Empty }, + { "nv!cmdbufsizewords", string.Empty }, + { "nv!conformantblitframebufferscissor", string.Empty }, + { "nv!conformantincompletetextures", string.Empty }, + { "nv!copybuffermethod", string.Empty }, + { "nv!cubemapaniso", string.Empty }, + { "nv!cubemapfiltering", string.Empty }, + { "nv!d0e9a4d7", string.Empty }, + { "nv!d13733f12", string.Empty }, + { "nv!d1b399", string.Empty }, + { "nv!d2983c32", string.Empty }, + { "nv!d2983c33", string.Empty }, + { "nv!d2e71b", string.Empty }, + { "nv!d377dc", string.Empty }, + { "nv!d377dd", string.Empty }, + { "nv!d489f4", string.Empty }, + { "nv!d4bce1", string.Empty }, + { "nv!d518cb", string.Empty }, + { "nv!d518cd", string.Empty }, + { "nv!d518ce", string.Empty }, + { "nv!d518d0", string.Empty }, + { "nv!d518d1", string.Empty }, + { "nv!d518d2", string.Empty }, + { "nv!d518d3", string.Empty }, + { "nv!d518d4", string.Empty }, + { "nv!d518d5", string.Empty }, + { "nv!d59eda", string.Empty }, + { "nv!d83cbd", string.Empty }, + { "nv!d8e777", string.Empty }, + { "nv!debug_level", string.Empty }, + { "nv!debug_mask", string.Empty }, + { "nv!debug_options", string.Empty }, + { "nv!devshmpageableallocations", string.Empty }, + { "nv!df1f9812", string.Empty }, + { "nv!df783c", string.Empty }, + { "nv!diagenable", string.Empty }, + { "nv!disallowcemask", string.Empty }, + { "nv!disallowz16", string.Empty }, + { "nv!dlmemoryspaceenables", string.Empty }, + { "nv!e0bfec", string.Empty }, + { "nv!e433456d", string.Empty }, + { "nv!e435563f", string.Empty }, + { "nv!e4cd9c", string.Empty }, + { "nv!e5c972", string.Empty }, + { "nv!e639ef", string.Empty }, + { "nv!e802af", string.Empty }, + { "nv!eae964", string.Empty }, + { "nv!earlytexturehwallocation", string.Empty }, + { "nv!eb92a3", string.Empty }, + { "nv!ebca56", string.Empty }, + { "nv!enable-noaud", string.Empty }, + { "nv!enable-noavs", string.Empty }, + { "nv!enable-prof", string.Empty }, + { "nv!enable-sxesmode", string.Empty }, + { "nv!enable-ulld", string.Empty }, + { "nv!expert_detail_level", string.Empty }, + { "nv!expert_output_mask", string.Empty }, + { "nv!expert_report_mask", string.Empty }, + { "nv!extensionstringnvarch", string.Empty }, + { "nv!extensionstringversion", string.Empty }, + { "nv!f00f1938", string.Empty }, + { "nv!f10736", string.Empty }, + { "nv!f1846870", string.Empty }, + { "nv!f33bc370", string.Empty }, + { "nv!f392a874", string.Empty }, + { "nv!f49ae8", string.Empty }, + { "nv!fa345cce", string.Empty }, + { "nv!fa35cc4", string.Empty }, + { "nv!faa14a", string.Empty }, + { "nv!faf8a723", string.Empty }, + { "nv!fastgs", string.Empty }, + { "nv!fbf4ac45", string.Empty }, + { "nv!fbo_blit_ignore_srgb", string.Empty }, + { "nv!fc64c7", string.Empty }, + { "nv!ff54ec97", string.Empty }, + { "nv!ff54ec98", string.Empty }, + { "nv!forceexitprocessdetach", string.Empty }, + { "nv!forcerequestedesversion", string.Empty }, + { "nv!__gl_", string.Empty }, + { "nv!__gl_00008600", string.Empty }, + { "nv!__gl_0007b25e", string.Empty }, + { "nv!__gl_0083e1", string.Empty }, + { "nv!__gl_01621887", string.Empty }, + { "nv!__gl_03134743", string.Empty }, + { "nv!__gl_0356afd0", string.Empty }, + { "nv!__gl_0356afd1", string.Empty }, + { "nv!__gl_0356afd2", string.Empty }, + { "nv!__gl_0356afd3", string.Empty }, + { "nv!__gl_094313", string.Empty }, + { "nv!__gl_0x04dc09", string.Empty }, + { "nv!__gl_0x111133", string.Empty }, + { "nv!__gl_0x1aa483", string.Empty }, + { "nv!__gl_0x1cb1cf", string.Empty }, + { "nv!__gl_0x1cb1d0", string.Empty }, + { "nv!__gl_0x1e3221", string.Empty }, + { "nv!__gl_0x300fc8", string.Empty }, + { "nv!__gl_0x301fc8", string.Empty }, + { "nv!__gl_0x302fc8", string.Empty }, + { "nv!__gl_0x3eec59", string.Empty }, + { "nv!__gl_0x46b3ed", string.Empty }, + { "nv!__gl_0x523dc0", string.Empty }, + { "nv!__gl_0x523dc1", string.Empty }, + { "nv!__gl_0x523dc2", string.Empty }, + { "nv!__gl_0x523dc3", string.Empty }, + { "nv!__gl_0x523dc4", string.Empty }, + { "nv!__gl_0x523dc5", string.Empty }, + { "nv!__gl_0x523dc6", string.Empty }, + { "nv!__gl_0x523dd0", string.Empty }, + { "nv!__gl_0x523dd1", string.Empty }, + { "nv!__gl_0x523dd3", string.Empty }, + { "nv!__gl_0x5344bb", string.Empty }, + { "nv!__gl_0x555237", string.Empty }, + { "nv!__gl_0x58a234", string.Empty }, + { "nv!__gl_0x7b4428", string.Empty }, + { "nv!__gl_0x923dc0", string.Empty }, + { "nv!__gl_0x923dc1", string.Empty }, + { "nv!__gl_0x923dc2", string.Empty }, + { "nv!__gl_0x923dc3", string.Empty }, + { "nv!__gl_0x923dc4", string.Empty }, + { "nv!__gl_0x923dd3", string.Empty }, + { "nv!__gl_0x9abdc5", string.Empty }, + { "nv!__gl_0x9abdc6", string.Empty }, + { "nv!__gl_0xaaa36c", string.Empty }, + { "nv!__gl_0xb09da0", string.Empty }, + { "nv!__gl_0xb09da1", string.Empty }, + { "nv!__gl_0xb09da2", string.Empty }, + { "nv!__gl_0xb09da3", string.Empty }, + { "nv!__gl_0xb09da4", string.Empty }, + { "nv!__gl_0xb09da5", string.Empty }, + { "nv!__gl_0xb0b348", string.Empty }, + { "nv!__gl_0xb0b349", string.Empty }, + { "nv!__gl_0xbb558f", string.Empty }, + { "nv!__gl_0xbd10fb", string.Empty }, + { "nv!__gl_0xc32ad3", string.Empty }, + { "nv!__gl_0xce2348", string.Empty }, + { "nv!__gl_0xcfd81f", string.Empty }, + { "nv!__gl_0xe0036b", string.Empty }, + { "nv!__gl_0xe01f2d", string.Empty }, + { "nv!__gl_0xe17212", string.Empty }, + { "nv!__gl_0xeae966", string.Empty }, + { "nv!__gl_0xed4f82", string.Empty }, + { "nv!__gl_0xf12335", string.Empty }, + { "nv!__gl_0xf12336", string.Empty }, + { "nv!__gl_10261989", string.Empty }, + { "nv!__gl_1042d483", string.Empty }, + { "nv!__gl_10572898", string.Empty }, + { "nv!__gl_115631", string.Empty }, + { "nv!__gl_12950094", string.Empty }, + { "nv!__gl_1314f311", string.Empty }, + { "nv!__gl_1314f312", string.Empty }, + { "nv!__gl_13279512", string.Empty }, + { "nv!__gl_13813496", string.Empty }, + { "nv!__gl_14507179", string.Empty }, + { "nv!__gl_15694569", string.Empty }, + { "nv!__gl_16936964", string.Empty }, + { "nv!__gl_17aa230c", string.Empty }, + { "nv!__gl_182054", string.Empty }, + { "nv!__gl_18273275", string.Empty }, + { "nv!__gl_18273276", string.Empty }, + { "nv!__gl_1854d03b", string.Empty }, + { "nv!__gl_18add00d", string.Empty }, + { "nv!__gl_19156670", string.Empty }, + { "nv!__gl_19286545", string.Empty }, + { "nv!__gl_1a298e9f", string.Empty }, + { "nv!__gl_1acf43fe", string.Empty }, + { "nv!__gl_1bda43fe", string.Empty }, + { "nv!__gl_1c3b92", string.Empty }, + { "nv!__gl_21509920", string.Empty }, + { "nv!__gl_215323457", string.Empty }, + { "nv!__gl_2165ad", string.Empty }, + { "nv!__gl_2165ae", string.Empty }, + { "nv!__gl_21be9c", string.Empty }, + { "nv!__gl_233264316", string.Empty }, + { "nv!__gl_234557580", string.Empty }, + { "nv!__gl_23cd0e", string.Empty }, + { "nv!__gl_24189123", string.Empty }, + { "nv!__gl_2443266", string.Empty }, + { "nv!__gl_25025519", string.Empty }, + { "nv!__gl_255e39", string.Empty }, + { "nv!__gl_2583364", string.Empty }, + { "nv!__gl_2888c1", string.Empty }, + { "nv!__gl_28ca3e", string.Empty }, + { "nv!__gl_29871243", string.Empty }, + { "nv!__gl_2a1f64", string.Empty }, + { "nv!__gl_2dc432", string.Empty }, + { "nv!__gl_2de437", string.Empty }, + { "nv!__gl_2f3bb89c", string.Empty }, + { "nv!__gl_2fd652", string.Empty }, + { "nv!__gl_3001ac", string.Empty }, + { "nv!__gl_31298772", string.Empty }, + { "nv!__gl_313233", string.Empty }, + { "nv!__gl_31f7d603", string.Empty }, + { "nv!__gl_320ce4", string.Empty }, + { "nv!__gl_32153248", string.Empty }, + { "nv!__gl_32153249", string.Empty }, + { "nv!__gl_335bca", string.Empty }, + { "nv!__gl_342abb", string.Empty }, + { "nv!__gl_34dfe6", string.Empty }, + { "nv!__gl_34dfe7", string.Empty }, + { "nv!__gl_34dfe8", string.Empty }, + { "nv!__gl_34dfe9", string.Empty }, + { "nv!__gl_35201578", string.Empty }, + { "nv!__gl_359278", string.Empty }, + { "nv!__gl_37f53a", string.Empty }, + { "nv!__gl_38144972", string.Empty }, + { "nv!__gl_38542646", string.Empty }, + { "nv!__gl_3b74c9", string.Empty }, + { "nv!__gl_3c136f", string.Empty }, + { "nv!__gl_3cf72823", string.Empty }, + { "nv!__gl_3d7af029", string.Empty }, + { "nv!__gl_3ff34782", string.Empty }, + { "nv!__gl_4129618", string.Empty }, + { "nv!__gl_4189fac3", string.Empty }, + { "nv!__gl_420bd4", string.Empty }, + { "nv!__gl_42a699", string.Empty }, + { "nv!__gl_441369", string.Empty }, + { "nv!__gl_4458713e", string.Empty }, + { "nv!__gl_4554b6", string.Empty }, + { "nv!__gl_457425", string.Empty }, + { "nv!__gl_4603b207", string.Empty }, + { "nv!__gl_46574957", string.Empty }, + { "nv!__gl_46574958", string.Empty }, + { "nv!__gl_46813529", string.Empty }, + { "nv!__gl_46f1e13d", string.Empty }, + { "nv!__gl_47534c43", string.Empty }, + { "nv!__gl_48550336", string.Empty }, + { "nv!__gl_48576893", string.Empty }, + { "nv!__gl_48576894", string.Empty }, + { "nv!__gl_4889ac02", string.Empty }, + { "nv!__gl_49005740", string.Empty }, + { "nv!__gl_49867584", string.Empty }, + { "nv!__gl_49960973", string.Empty }, + { "nv!__gl_4a5341", string.Empty }, + { "nv!__gl_4f4e48", string.Empty }, + { "nv!__gl_4f8a0a", string.Empty }, + { "nv!__gl_50299698", string.Empty }, + { "nv!__gl_50299699", string.Empty }, + { "nv!__gl_50361291", string.Empty }, + { "nv!__gl_5242ae", string.Empty }, + { "nv!__gl_53d30c", string.Empty }, + { "nv!__gl_56347a", string.Empty }, + { "nv!__gl_563a95f1", string.Empty }, + { "nv!__gl_573823", string.Empty }, + { "nv!__gl_58027529", string.Empty }, + { "nv!__gl_5d2d63", string.Empty }, + { "nv!__gl_5f7e3b", string.Empty }, + { "nv!__gl_60461793", string.Empty }, + { "nv!__gl_60d355", string.Empty }, + { "nv!__gl_616627aa", string.Empty }, + { "nv!__gl_62317182", string.Empty }, + { "nv!__gl_6253fa2e", string.Empty }, + { "nv!__gl_64100768", string.Empty }, + { "nv!__gl_64100769", string.Empty }, + { "nv!__gl_64100770", string.Empty }, + { "nv!__gl_647395", string.Empty }, + { "nv!__gl_66543234", string.Empty }, + { "nv!__gl_67674763", string.Empty }, + { "nv!__gl_67739784", string.Empty }, + { "nv!__gl_68fb9c", string.Empty }, + { "nv!__gl_69801276", string.Empty }, + { "nv!__gl_6af9fa2f", string.Empty }, + { "nv!__gl_6af9fa3f", string.Empty }, + { "nv!__gl_6af9fa4f", string.Empty }, + { "nv!__gl_6bd8c7", string.Empty }, + { "nv!__gl_6c7691", string.Empty }, + { "nv!__gl_6d4296ce", string.Empty }, + { "nv!__gl_6dd7e7", string.Empty }, + { "nv!__gl_6dd7e8", string.Empty }, + { "nv!__gl_6fe11ec1", string.Empty }, + { "nv!__gl_716511763", string.Empty }, + { "nv!__gl_72504593", string.Empty }, + { "nv!__gl_73304097", string.Empty }, + { "nv!__gl_73314098", string.Empty }, + { "nv!__gl_74095213", string.Empty }, + { "nv!__gl_74095213a", string.Empty }, + { "nv!__gl_74095213b", string.Empty }, + { "nv!__gl_74095214", string.Empty }, + { "nv!__gl_748f9649", string.Empty }, + { "nv!__gl_75494732", string.Empty }, + { "nv!__gl_78452832", string.Empty }, + { "nv!__gl_784561", string.Empty }, + { "nv!__gl_78e16b9c", string.Empty }, + { "nv!__gl_79251225", string.Empty }, + { "nv!__gl_7c128b", string.Empty }, + { "nv!__gl_7ccd93", string.Empty }, + { "nv!__gl_7df8d1", string.Empty }, + { "nv!__gl_800c2310", string.Empty }, + { "nv!__gl_80546710", string.Empty }, + { "nv!__gl_80772310", string.Empty }, + { "nv!__gl_808ee280", string.Empty }, + { "nv!__gl_81131154", string.Empty }, + { "nv!__gl_81274457", string.Empty }, + { "nv!__gl_8292291f", string.Empty }, + { "nv!__gl_83498426", string.Empty }, + { "nv!__gl_84993794", string.Empty }, + { "nv!__gl_84995585", string.Empty }, + { "nv!__gl_84a0a0", string.Empty }, + { "nv!__gl_852142", string.Empty }, + { "nv!__gl_85612309", string.Empty }, + { "nv!__gl_85612310", string.Empty }, + { "nv!__gl_85612311", string.Empty }, + { "nv!__gl_85612312", string.Empty }, + { "nv!__gl_8623ff27", string.Empty }, + { "nv!__gl_87364952", string.Empty }, + { "nv!__gl_87f6275666", string.Empty }, + { "nv!__gl_886748", string.Empty }, + { "nv!__gl_89894423", string.Empty }, + { "nv!__gl_8ad8a75", string.Empty }, + { "nv!__gl_8ad8ad00", string.Empty }, + { "nv!__gl_8bb815", string.Empty }, + { "nv!__gl_8bb817", string.Empty }, + { "nv!__gl_8bb818", string.Empty }, + { "nv!__gl_8bb819", string.Empty }, + { "nv!__gl_8e640cd1", string.Empty }, + { "nv!__gl_8f34971a", string.Empty }, + { "nv!__gl_8f773984", string.Empty }, + { "nv!__gl_8f7a7d", string.Empty }, + { "nv!__gl_902486209", string.Empty }, + { "nv!__gl_90482571", string.Empty }, + { "nv!__gl_91214835", string.Empty }, + { "nv!__gl_912848290", string.Empty }, + { "nv!__gl_915e56", string.Empty }, + { "nv!__gl_92179063", string.Empty }, + { "nv!__gl_92179064", string.Empty }, + { "nv!__gl_92179065", string.Empty }, + { "nv!__gl_92179066", string.Empty }, + { "nv!__gl_92350358", string.Empty }, + { "nv!__gl_92809063", string.Empty }, + { "nv!__gl_92809064", string.Empty }, + { "nv!__gl_92809065", string.Empty }, + { "nv!__gl_92809066", string.Empty }, + { "nv!__gl_92920143", string.Empty }, + { "nv!__gl_93a89b12", string.Empty }, + { "nv!__gl_93a89c0b", string.Empty }, + { "nv!__gl_94812574", string.Empty }, + { "nv!__gl_95282304", string.Empty }, + { "nv!__gl_95394027", string.Empty }, + { "nv!__gl_959b1f", string.Empty }, + { "nv!__gl_9638af", string.Empty }, + { "nv!__gl_96fd59", string.Empty }, + { "nv!__gl_97f6275666", string.Empty }, + { "nv!__gl_97f6275667", string.Empty }, + { "nv!__gl_97f6275668", string.Empty }, + { "nv!__gl_97f6275669", string.Empty }, + { "nv!__gl_97f627566a", string.Empty }, + { "nv!__gl_97f627566b", string.Empty }, + { "nv!__gl_97f627566d", string.Empty }, + { "nv!__gl_97f627566e", string.Empty }, + { "nv!__gl_97f627566f", string.Empty }, + { "nv!__gl_97f6275670", string.Empty }, + { "nv!__gl_97f6275671", string.Empty }, + { "nv!__gl_97f727566e", string.Empty }, + { "nv!__gl_98480775", string.Empty }, + { "nv!__gl_98480776", string.Empty }, + { "nv!__gl_98480777", string.Empty }, + { "nv!__gl_992431", string.Empty }, + { "nv!__gl_9aa29065", string.Empty }, + { "nv!__gl_9af32c", string.Empty }, + { "nv!__gl_9af32d", string.Empty }, + { "nv!__gl_9af32e", string.Empty }, + { "nv!__gl_9c108b71", string.Empty }, + { "nv!__gl_9f279065", string.Empty }, + { "nv!__gl_a01bc728", string.Empty }, + { "nv!__gl_a13b46c80", string.Empty }, + { "nv!__gl_a22eb0", string.Empty }, + { "nv!__gl_a2fb451e", string.Empty }, + { "nv!__gl_a3456abe", string.Empty }, + { "nv!__gl_a7044887", string.Empty }, + { "nv!__gl_a7149200", string.Empty }, + { "nv!__gl_a766215670", string.Empty }, + { "nv!__gl_aalinegamma", string.Empty }, + { "nv!__gl_aalinetweaks", string.Empty }, + { "nv!__gl_ab34ee01", string.Empty }, + { "nv!__gl_ab34ee02", string.Empty }, + { "nv!__gl_ab34ee03", string.Empty }, + { "nv!__gl_ac0274", string.Empty }, + { "nv!__gl_af73c63e", string.Empty }, + { "nv!__gl_af73c63f", string.Empty }, + { "nv!__gl_af9927", string.Empty }, + { "nv!__gl_afoverride", string.Empty }, + { "nv!__gl_allocdeviceevents", string.Empty }, + { "nv!__gl_applicationkey", string.Empty }, + { "nv!__gl_appreturnonlybasicglsltype", string.Empty }, + { "nv!__gl_app_softimage", string.Empty }, + { "nv!__gl_app_supportbits2", string.Empty }, + { "nv!__gl_assumetextureismipmappedatcreation", string.Empty }, + { "nv!__gl_b1fb0f01", string.Empty }, + { "nv!__gl_b3edd5", string.Empty }, + { "nv!__gl_b40d9e03d", string.Empty }, + { "nv!__gl_b7f6275666", string.Empty }, + { "nv!__gl_b812c1", string.Empty }, + { "nv!__gl_ba14ba1a", string.Empty }, + { "nv!__gl_ba14ba1b", string.Empty }, + { "nv!__gl_bd7559", string.Empty }, + { "nv!__gl_bd755a", string.Empty }, + { "nv!__gl_bd755c", string.Empty }, + { "nv!__gl_bd755d", string.Empty }, + { "nv!__gl_be58bb", string.Empty }, + { "nv!__gl_be92cb", string.Empty }, + { "nv!__gl_beefcba3", string.Empty }, + { "nv!__gl_beefcba4", string.Empty }, + { "nv!__gl_c023777f", string.Empty }, + { "nv!__gl_c09dc8", string.Empty }, + { "nv!__gl_c0d340", string.Empty }, + { "nv!__gl_c2ff374c", string.Empty }, + { "nv!__gl_c5e9d7a3", string.Empty }, + { "nv!__gl_c5e9d7a4", string.Empty }, + { "nv!__gl_c5e9d7b4", string.Empty }, + { "nv!__gl_c618f9", string.Empty }, + { "nv!__gl_ca345840", string.Empty }, + { "nv!__gl_cachedisable", string.Empty }, + { "nv!__gl_channelpriorityoverride", string.Empty }, + { "nv!__gl_cleardatastorevidmem", string.Empty }, + { "nv!__gl_cmdbufmemoryspaceenables", string.Empty }, + { "nv!__gl_cmdbufminwords", string.Empty }, + { "nv!__gl_cmdbufsizewords", string.Empty }, + { "nv!__gl_conformantblitframebufferscissor", string.Empty }, + { "nv!__gl_conformantincompletetextures", string.Empty }, + { "nv!__gl_copybuffermethod", string.Empty }, + { "nv!__gl_cubemapaniso", string.Empty }, + { "nv!__gl_cubemapfiltering", string.Empty }, + { "nv!__gl_d0e9a4d7", string.Empty }, + { "nv!__gl_d13733f12", string.Empty }, + { "nv!__gl_d1b399", string.Empty }, + { "nv!__gl_d2983c32", string.Empty }, + { "nv!__gl_d2983c33", string.Empty }, + { "nv!__gl_d2e71b", string.Empty }, + { "nv!__gl_d377dc", string.Empty }, + { "nv!__gl_d377dd", string.Empty }, + { "nv!__gl_d489f4", string.Empty }, + { "nv!__gl_d4bce1", string.Empty }, + { "nv!__gl_d518cb", string.Empty }, + { "nv!__gl_d518cd", string.Empty }, + { "nv!__gl_d518ce", string.Empty }, + { "nv!__gl_d518d0", string.Empty }, + { "nv!__gl_d518d1", string.Empty }, + { "nv!__gl_d518d2", string.Empty }, + { "nv!__gl_d518d3", string.Empty }, + { "nv!__gl_d518d4", string.Empty }, + { "nv!__gl_d518d5", string.Empty }, + { "nv!__gl_d59eda", string.Empty }, + { "nv!__gl_d83cbd", string.Empty }, + { "nv!__gl_d8e777", string.Empty }, + { "nv!__gl_debug_level", string.Empty }, + { "nv!__gl_debug_mask", string.Empty }, + { "nv!__gl_debug_options", string.Empty }, + { "nv!__gl_devshmpageableallocations", string.Empty }, + { "nv!__gl_df1f9812", string.Empty }, + { "nv!__gl_df783c", string.Empty }, + { "nv!__gl_diagenable", string.Empty }, + { "nv!__gl_disallowcemask", string.Empty }, + { "nv!__gl_disallowz16", string.Empty }, + { "nv!__gl_dlmemoryspaceenables", string.Empty }, + { "nv!__gl_e0bfec", string.Empty }, + { "nv!__gl_e433456d", string.Empty }, + { "nv!__gl_e435563f", string.Empty }, + { "nv!__gl_e4cd9c", string.Empty }, + { "nv!__gl_e5c972", string.Empty }, + { "nv!__gl_e639ef", string.Empty }, + { "nv!__gl_e802af", string.Empty }, + { "nv!__gl_eae964", string.Empty }, + { "nv!__gl_earlytexturehwallocation", string.Empty }, + { "nv!__gl_eb92a3", string.Empty }, + { "nv!__gl_ebca56", string.Empty }, + { "nv!__gl_expert_detail_level", string.Empty }, + { "nv!__gl_expert_output_mask", string.Empty }, + { "nv!__gl_expert_report_mask", string.Empty }, + { "nv!__gl_extensionstringnvarch", string.Empty }, + { "nv!__gl_extensionstringversion", string.Empty }, + { "nv!__gl_f00f1938", string.Empty }, + { "nv!__gl_f10736", string.Empty }, + { "nv!__gl_f1846870", string.Empty }, + { "nv!__gl_f33bc370", string.Empty }, + { "nv!__gl_f392a874", string.Empty }, + { "nv!__gl_f49ae8", string.Empty }, + { "nv!__gl_fa345cce", string.Empty }, + { "nv!__gl_fa35cc4", string.Empty }, + { "nv!__gl_faa14a", string.Empty }, + { "nv!__gl_faf8a723", string.Empty }, + { "nv!__gl_fastgs", string.Empty }, + { "nv!__gl_fbf4ac45", string.Empty }, + { "nv!__gl_fbo_blit_ignore_srgb", string.Empty }, + { "nv!__gl_fc64c7", string.Empty }, + { "nv!__gl_ff54ec97", string.Empty }, + { "nv!__gl_ff54ec98", string.Empty }, + { "nv!__gl_forceexitprocessdetach", string.Empty }, + { "nv!__gl_forcerequestedesversion", string.Empty }, + { "nv!__gl_glsynctovblank", string.Empty }, + { "nv!__gl_gvitimeoutcontrol", string.Empty }, + { "nv!__gl_hcctrl", string.Empty }, + { "nv!__gl_hwstate_per_ctx", string.Empty }, + { "nv!__gl_machinecachelimit", string.Empty }, + { "nv!__gl_maxframesallowed", string.Empty }, + { "nv!__gl_memmgrcachedalloclimit", string.Empty }, + { "nv!__gl_memmgrcachedalloclimitratio", string.Empty }, + { "nv!__gl_memmgrsysheapalloclimit", string.Empty }, + { "nv!__gl_memmgrsysheapalloclimitratio", string.Empty }, + { "nv!__gl_memmgrvidheapalloclimit", string.Empty }, + { "nv!__gl_mosaic_clip_to_subdev", string.Empty }, + { "nv!__gl_mosaic_clip_to_subdev_h_overlap", string.Empty }, + { "nv!__gl_mosaic_clip_to_subdev_v_overlap", string.Empty }, + { "nv!__gl_overlaymergeblittimerms", string.Empty }, + { "nv!__gl_perfmon_mode", string.Empty }, + { "nv!__gl_pixbar_mode", string.Empty }, + { "nv!__gl_qualityenhancements", string.Empty }, + { "nv!__gl_r27s18q28", string.Empty }, + { "nv!__gl_r2d7c1d8", string.Empty }, + { "nv!__gl_renderer", string.Empty }, + { "nv!__gl_renderqualityflags", string.Empty }, + { "nv!__gl_s3tcquality", string.Empty }, + { "nv!__gl_shaderatomics", string.Empty }, + { "nv!__gl_shadercacheinitsize", string.Empty }, + { "nv!__gl_shader_disk_cache_path", string.Empty }, + { "nv!__gl_shader_disk_cache_read_only", string.Empty }, + { "nv!__gl_shaderobjects", string.Empty }, + { "nv!__gl_shaderportabilitywarnings", string.Empty }, + { "nv!__gl_shaderwarningsaserrors", string.Empty }, + { "nv!__gl_skiptexturehostcopies", string.Empty }, + { "nv!__glslc_debug_level", string.Empty }, + { "nv!__glslc_debug_mask", string.Empty }, + { "nv!__glslc_debug_options", string.Empty }, + { "nv!__glslc_debug_filename", string.Empty }, + { "nv!__gl_sli_dli_control", string.Empty }, + { "nv!__gl_sparsetexture", string.Empty }, + { "nv!__gl_spinlooptimeout", string.Empty }, + { "nv!__gl_sync_to_vblank", string.Empty }, + { "nv!glsynctovblank", string.Empty }, + { "nv!__gl_sysheapreuseratio", string.Empty }, + { "nv!__gl_sysmemtexturepromotion", string.Empty }, + { "nv!__gl_targetflushcount", string.Empty }, + { "nv!__gl_tearingfreeswappresent", string.Empty }, + { "nv!__gl_texclampbehavior", string.Empty }, + { "nv!__gl_texlodbias", string.Empty }, + { "nv!__gl_texmemoryspaceenables", string.Empty }, + { "nv!__gl_textureprecache", string.Empty }, + { "nv!__gl_threadcontrol", string.Empty }, + { "nv!__gl_threadcontrol2", string.Empty }, + { "nv!__gl_usegvievents", string.Empty }, + { "nv!__gl_vbomemoryspaceenables", string.Empty }, + { "nv!__gl_vertexlimit", string.Empty }, + { "nv!__gl_vidheapreuseratio", string.Empty }, + { "nv!__gl_vpipe", string.Empty }, + { "nv!__gl_vpipeformatbloatlimit", string.Empty }, + { "nv!__gl_wglmessageboxonabort", string.Empty }, + { "nv!__gl_writeinfolog", string.Empty }, + { "nv!__gl_writeprogramobjectassembly", string.Empty }, + { "nv!__gl_writeprogramobjectsource", string.Empty }, + { "nv!__gl_xnvadapterpresent", string.Empty }, + { "nv!__gl_yield", string.Empty }, + { "nv!__gl_yieldfunction", string.Empty }, + { "nv!__gl_yieldfunctionfast", string.Empty }, + { "nv!__gl_yieldfunctionslow", string.Empty }, + { "nv!__gl_yieldfunctionwaitfordcqueue", string.Empty }, + { "nv!__gl_yieldfunctionwaitforframe", string.Empty }, + { "nv!__gl_yieldfunctionwaitforgpu", string.Empty }, + { "nv!__gl_zbctableaddhysteresis", string.Empty }, + { "nv!gpu_debug_mode", string.Empty }, + { "nv!gpu_stay_on", string.Empty }, + { "nv!gpu_timeout_ms_max", string.Empty }, + { "nv!gvitimeoutcontrol", string.Empty }, + { "nv!hcctrl", string.Empty }, + { "nv!hwstate_per_ctx", string.Empty }, + { "nv!libandroid_enable_log", string.Empty }, + { "nv!machinecachelimit", string.Empty }, + { "nv!maxframesallowed", string.Empty }, + { "nv!media.aac_51_output_enabled", string.Empty }, + { "nv!memmgrcachedalloclimit", string.Empty }, + { "nv!memmgrcachedalloclimitratio", string.Empty }, + { "nv!memmgrsysheapalloclimit", string.Empty }, + { "nv!memmgrsysheapalloclimitratio", string.Empty }, + { "nv!memmgrvidheapalloclimit", string.Empty }, + { "nv!mosaic_clip_to_subdev", string.Empty }, + { "nv!mosaic_clip_to_subdev_h_overlap", string.Empty }, + { "nv!mosaic_clip_to_subdev_v_overlap", string.Empty }, + { "nv!nvblit.dump", string.Empty }, + { "nv!nvblit.profile", string.Empty }, + { "nv!nvblit.twod", string.Empty }, + { "nv!nvblit.vic", string.Empty }, + { "nv!nvddk_vic_prevent_use", string.Empty }, + { "nv!nv_decompression", string.Empty }, { "nv!nvdisp_bl_ctrl", "0" }, - { "nv!nvdisp_debug_mask", "" }, + { "nv!nvdisp_debug_mask", string.Empty }, { "nv!nvdisp_enable_ts", "0" }, { "nv!nvhdcp_timeout_ms", "12000" }, { "nv!nvhdcp_max_retries", "5" }, - { "nv!nv_emc_dvfs_test", "" }, - { "nv!nv_emc_init_rate_hz", "" }, - { "nv!nv_gmmu_va_page_split", "" }, - { "nv!nv_gmmu_va_range", "" }, - { "nv!nvhost_debug_mask", "" }, - { "nv!nvidia.hwc.dump_config", "" }, - { "nv!nvidia.hwc.dump_layerlist", "" }, - { "nv!nvidia.hwc.dump_windows", "" }, - { "nv!nvidia.hwc.enable_disp_trans", "" }, - { "nv!nvidia.hwc.ftrace_enable", "" }, - { "nv!nvidia.hwc.hdcp_enable", "" }, - { "nv!nvidia.hwc.hidden_window_mask0", "" }, - { "nv!nvidia.hwc.hidden_window_mask1", "" }, - { "nv!nvidia.hwc.immediate_modeset", "" }, - { "nv!nvidia.hwc.imp_enable", "" }, - { "nv!nvidia.hwc.no_egl", "" }, - { "nv!nvidia.hwc.no_scratchblit", "" }, - { "nv!nvidia.hwc.no_vic", "" }, - { "nv!nvidia.hwc.null_display", "" }, - { "nv!nvidia.hwc.scan_props", "" }, - { "nv!nvidia.hwc.swap_interval", "" }, + { "nv!nv_emc_dvfs_test", string.Empty }, + { "nv!nv_emc_init_rate_hz", string.Empty }, + { "nv!nv_gmmu_va_page_split", string.Empty }, + { "nv!nv_gmmu_va_range", string.Empty }, + { "nv!nvhost_debug_mask", string.Empty }, + { "nv!nvidia.hwc.dump_config", string.Empty }, + { "nv!nvidia.hwc.dump_layerlist", string.Empty }, + { "nv!nvidia.hwc.dump_windows", string.Empty }, + { "nv!nvidia.hwc.enable_disp_trans", string.Empty }, + { "nv!nvidia.hwc.ftrace_enable", string.Empty }, + { "nv!nvidia.hwc.hdcp_enable", string.Empty }, + { "nv!nvidia.hwc.hidden_window_mask0", string.Empty }, + { "nv!nvidia.hwc.hidden_window_mask1", string.Empty }, + { "nv!nvidia.hwc.immediate_modeset", string.Empty }, + { "nv!nvidia.hwc.imp_enable", string.Empty }, + { "nv!nvidia.hwc.no_egl", string.Empty }, + { "nv!nvidia.hwc.no_scratchblit", string.Empty }, + { "nv!nvidia.hwc.no_vic", string.Empty }, + { "nv!nvidia.hwc.null_display", string.Empty }, + { "nv!nvidia.hwc.scan_props", string.Empty }, + { "nv!nvidia.hwc.swap_interval", string.Empty }, { "nv!nvidia.hwc.war_1515812", "0" }, - { "nv!nvmap_debug_mask", "" }, - { "nv!nv_memory_profiler", "" }, - { "nv!nvnflinger_enable_log", "" }, - { "nv!nvnflinger_flip_policy", "" }, + { "nv!nvmap_debug_mask", string.Empty }, + { "nv!nv_memory_profiler", string.Empty }, + { "nv!nvnflinger_enable_log", string.Empty }, + { "nv!nvnflinger_flip_policy", string.Empty }, { "nv!nvnflinger_hotplug_autoswitch", "0" }, { "nv!nvnflinger_prefer_primary_layer", "0" }, - { "nv!nvnflinger_service_priority", "" }, - { "nv!nvnflinger_service_threads", "" }, - { "nv!nvnflinger_swap_interval", "" }, - { "nv!nvnflinger_track_perf", "" }, + { "nv!nvnflinger_service_priority", string.Empty }, + { "nv!nvnflinger_service_threads", string.Empty }, + { "nv!nvnflinger_swap_interval", string.Empty }, + { "nv!nvnflinger_track_perf", string.Empty }, { "nv!nvnflinger_virtualdisplay_policy", "60hz" }, { "nv!nvn_no_vsync_capability", false }, - { "nv!nvn_through_opengl", "" }, - { "nv!nv_pllcx_always_on", "" }, - { "nv!nv_pllcx_safe_div", "" }, - { "nv!nvrm_gpu_channel_interleave", "" }, - { "nv!nvrm_gpu_channel_priority", "" }, - { "nv!nvrm_gpu_channel_timeslice", "" }, - { "nv!nvrm_gpu_default_device_index", "" }, - { "nv!nvrm_gpu_dummy", "" }, - { "nv!nvrm_gpu_help", "" }, - { "nv!nvrm_gpu_nvgpu_disable", "" }, - { "nv!nvrm_gpu_nvgpu_do_nfa_partial_map", "" }, - { "nv!nvrm_gpu_nvgpu_ecc_overrides", "" }, - { "nv!nvrm_gpu_nvgpu_no_as_get_va_regions", "" }, - { "nv!nvrm_gpu_nvgpu_no_channel_abort", "" }, - { "nv!nvrm_gpu_nvgpu_no_cyclestats", "" }, - { "nv!nvrm_gpu_nvgpu_no_fixed", "" }, - { "nv!nvrm_gpu_nvgpu_no_gpu_characteristics", "" }, - { "nv!nvrm_gpu_nvgpu_no_ioctl_mutex", "" }, - { "nv!nvrm_gpu_nvgpu_no_map_buffer_ex", "" }, - { "nv!nvrm_gpu_nvgpu_no_robustness", "" }, - { "nv!nvrm_gpu_nvgpu_no_sparse", "" }, - { "nv!nvrm_gpu_nvgpu_no_syncpoints", "" }, - { "nv!nvrm_gpu_nvgpu_no_tsg", "" }, - { "nv!nvrm_gpu_nvgpu_no_zbc", "" }, - { "nv!nvrm_gpu_nvgpu_no_zcull", "" }, - { "nv!nvrm_gpu_nvgpu_wrap_channels_in_tsgs", "" }, - { "nv!nvrm_gpu_prevent_use", "" }, - { "nv!nvrm_gpu_trace", "" }, - { "nv!nvsched_debug_mask", "" }, - { "nv!nvsched_force_enable", "" }, - { "nv!nvsched_force_log", "" }, - { "nv!nv_usb_plls_hw_ctrl", "" }, - { "nv!nv_winsys", "" }, - { "nv!nvwsi_dump", "" }, - { "nv!nvwsi_fill", "" }, - { "nv!ogl_", "" }, - { "nv!ogl_0356afd0", "" }, - { "nv!ogl_0356afd1", "" }, - { "nv!ogl_0356afd2", "" }, - { "nv!ogl_0356afd3", "" }, - { "nv!ogl_0x923dc0", "" }, - { "nv!ogl_0x923dc1", "" }, - { "nv!ogl_0x923dc2", "" }, - { "nv!ogl_0x923dc3", "" }, - { "nv!ogl_0x923dc4", "" }, - { "nv!ogl_0x923dd3", "" }, - { "nv!ogl_0x9abdc5", "" }, - { "nv!ogl_0x9abdc6", "" }, - { "nv!ogl_0xbd10fb", "" }, - { "nv!ogl_0xce2348", "" }, - { "nv!ogl_10261989", "" }, - { "nv!ogl_1042d483", "" }, - { "nv!ogl_10572898", "" }, - { "nv!ogl_115631", "" }, - { "nv!ogl_12950094", "" }, - { "nv!ogl_1314f311", "" }, - { "nv!ogl_1314f312", "" }, - { "nv!ogl_13279512", "" }, - { "nv!ogl_13813496", "" }, - { "nv!ogl_14507179", "" }, - { "nv!ogl_15694569", "" }, - { "nv!ogl_16936964", "" }, - { "nv!ogl_17aa230c", "" }, - { "nv!ogl_182054", "" }, - { "nv!ogl_18273275", "" }, - { "nv!ogl_18273276", "" }, - { "nv!ogl_1854d03b", "" }, - { "nv!ogl_18add00d", "" }, - { "nv!ogl_19156670", "" }, - { "nv!ogl_19286545", "" }, - { "nv!ogl_1a298e9f", "" }, - { "nv!ogl_1acf43fe", "" }, - { "nv!ogl_1bda43fe", "" }, - { "nv!ogl_1c3b92", "" }, - { "nv!ogl_21509920", "" }, - { "nv!ogl_215323457", "" }, - { "nv!ogl_2165ad", "" }, - { "nv!ogl_2165ae", "" }, - { "nv!ogl_21be9c", "" }, - { "nv!ogl_233264316", "" }, - { "nv!ogl_234557580", "" }, - { "nv!ogl_23cd0e", "" }, - { "nv!ogl_24189123", "" }, - { "nv!ogl_2443266", "" }, - { "nv!ogl_25025519", "" }, - { "nv!ogl_255e39", "" }, - { "nv!ogl_2583364", "" }, - { "nv!ogl_2888c1", "" }, - { "nv!ogl_28ca3e", "" }, - { "nv!ogl_29871243", "" }, - { "nv!ogl_2a1f64", "" }, - { "nv!ogl_2dc432", "" }, - { "nv!ogl_2de437", "" }, - { "nv!ogl_2f3bb89c", "" }, - { "nv!ogl_2fd652", "" }, - { "nv!ogl_3001ac", "" }, - { "nv!ogl_31298772", "" }, - { "nv!ogl_313233", "" }, - { "nv!ogl_31f7d603", "" }, - { "nv!ogl_320ce4", "" }, - { "nv!ogl_32153248", "" }, - { "nv!ogl_32153249", "" }, - { "nv!ogl_335bca", "" }, - { "nv!ogl_342abb", "" }, - { "nv!ogl_34dfe6", "" }, - { "nv!ogl_34dfe7", "" }, - { "nv!ogl_34dfe8", "" }, - { "nv!ogl_34dfe9", "" }, - { "nv!ogl_35201578", "" }, - { "nv!ogl_359278", "" }, - { "nv!ogl_37f53a", "" }, - { "nv!ogl_38144972", "" }, - { "nv!ogl_38542646", "" }, - { "nv!ogl_3b74c9", "" }, - { "nv!ogl_3c136f", "" }, - { "nv!ogl_3cf72823", "" }, - { "nv!ogl_3d7af029", "" }, - { "nv!ogl_3ff34782", "" }, - { "nv!ogl_4129618", "" }, - { "nv!ogl_4189fac3", "" }, - { "nv!ogl_420bd4", "" }, - { "nv!ogl_42a699", "" }, - { "nv!ogl_441369", "" }, - { "nv!ogl_4458713e", "" }, - { "nv!ogl_4554b6", "" }, - { "nv!ogl_457425", "" }, - { "nv!ogl_4603b207", "" }, - { "nv!ogl_46574957", "" }, - { "nv!ogl_46574958", "" }, - { "nv!ogl_46813529", "" }, - { "nv!ogl_46f1e13d", "" }, - { "nv!ogl_47534c43", "" }, - { "nv!ogl_48550336", "" }, - { "nv!ogl_48576893", "" }, - { "nv!ogl_48576894", "" }, - { "nv!ogl_4889ac02", "" }, - { "nv!ogl_49005740", "" }, - { "nv!ogl_49867584", "" }, - { "nv!ogl_49960973", "" }, - { "nv!ogl_4a5341", "" }, - { "nv!ogl_4f4e48", "" }, - { "nv!ogl_4f8a0a", "" }, - { "nv!ogl_50299698", "" }, - { "nv!ogl_50299699", "" }, - { "nv!ogl_50361291", "" }, - { "nv!ogl_5242ae", "" }, - { "nv!ogl_53d30c", "" }, - { "nv!ogl_56347a", "" }, - { "nv!ogl_563a95f1", "" }, - { "nv!ogl_573823", "" }, - { "nv!ogl_58027529", "" }, - { "nv!ogl_5d2d63", "" }, - { "nv!ogl_5f7e3b", "" }, - { "nv!ogl_60461793", "" }, - { "nv!ogl_60d355", "" }, - { "nv!ogl_616627aa", "" }, - { "nv!ogl_62317182", "" }, - { "nv!ogl_6253fa2e", "" }, - { "nv!ogl_64100768", "" }, - { "nv!ogl_64100769", "" }, - { "nv!ogl_64100770", "" }, - { "nv!ogl_647395", "" }, - { "nv!ogl_66543234", "" }, - { "nv!ogl_67674763", "" }, - { "nv!ogl_67739784", "" }, - { "nv!ogl_68fb9c", "" }, - { "nv!ogl_69801276", "" }, - { "nv!ogl_6af9fa2f", "" }, - { "nv!ogl_6af9fa3f", "" }, - { "nv!ogl_6af9fa4f", "" }, - { "nv!ogl_6bd8c7", "" }, - { "nv!ogl_6c7691", "" }, - { "nv!ogl_6d4296ce", "" }, - { "nv!ogl_6dd7e7", "" }, - { "nv!ogl_6dd7e8", "" }, - { "nv!ogl_6fe11ec1", "" }, - { "nv!ogl_716511763", "" }, - { "nv!ogl_72504593", "" }, - { "nv!ogl_73304097", "" }, - { "nv!ogl_73314098", "" }, - { "nv!ogl_74095213", "" }, - { "nv!ogl_74095213a", "" }, - { "nv!ogl_74095213b", "" }, - { "nv!ogl_74095214", "" }, - { "nv!ogl_748f9649", "" }, - { "nv!ogl_75494732", "" }, - { "nv!ogl_78452832", "" }, - { "nv!ogl_784561", "" }, - { "nv!ogl_78e16b9c", "" }, - { "nv!ogl_79251225", "" }, - { "nv!ogl_7c128b", "" }, - { "nv!ogl_7ccd93", "" }, - { "nv!ogl_7df8d1", "" }, - { "nv!ogl_800c2310", "" }, - { "nv!ogl_80546710", "" }, - { "nv!ogl_80772310", "" }, - { "nv!ogl_808ee280", "" }, - { "nv!ogl_81131154", "" }, - { "nv!ogl_81274457", "" }, - { "nv!ogl_8292291f", "" }, - { "nv!ogl_83498426", "" }, - { "nv!ogl_84993794", "" }, - { "nv!ogl_84995585", "" }, - { "nv!ogl_84a0a0", "" }, - { "nv!ogl_852142", "" }, - { "nv!ogl_85612309", "" }, - { "nv!ogl_85612310", "" }, - { "nv!ogl_85612311", "" }, - { "nv!ogl_85612312", "" }, - { "nv!ogl_8623ff27", "" }, - { "nv!ogl_87364952", "" }, - { "nv!ogl_87f6275666", "" }, - { "nv!ogl_886748", "" }, - { "nv!ogl_89894423", "" }, - { "nv!ogl_8ad8a75", "" }, - { "nv!ogl_8ad8ad00", "" }, - { "nv!ogl_8bb815", "" }, - { "nv!ogl_8bb817", "" }, - { "nv!ogl_8bb818", "" }, - { "nv!ogl_8bb819", "" }, - { "nv!ogl_8e640cd1", "" }, - { "nv!ogl_8f34971a", "" }, - { "nv!ogl_8f773984", "" }, - { "nv!ogl_8f7a7d", "" }, - { "nv!ogl_902486209", "" }, - { "nv!ogl_90482571", "" }, - { "nv!ogl_91214835", "" }, - { "nv!ogl_912848290", "" }, - { "nv!ogl_915e56", "" }, - { "nv!ogl_92179063", "" }, - { "nv!ogl_92179064", "" }, - { "nv!ogl_92179065", "" }, - { "nv!ogl_92179066", "" }, - { "nv!ogl_92350358", "" }, - { "nv!ogl_92809063", "" }, - { "nv!ogl_92809064", "" }, - { "nv!ogl_92809065", "" }, - { "nv!ogl_92809066", "" }, - { "nv!ogl_92920143", "" }, - { "nv!ogl_93a89b12", "" }, - { "nv!ogl_93a89c0b", "" }, - { "nv!ogl_94812574", "" }, - { "nv!ogl_95282304", "" }, - { "nv!ogl_95394027", "" }, - { "nv!ogl_959b1f", "" }, - { "nv!ogl_9638af", "" }, - { "nv!ogl_96fd59", "" }, - { "nv!ogl_97f6275666", "" }, - { "nv!ogl_97f6275667", "" }, - { "nv!ogl_97f6275668", "" }, - { "nv!ogl_97f6275669", "" }, - { "nv!ogl_97f627566a", "" }, - { "nv!ogl_97f627566b", "" }, - { "nv!ogl_97f627566d", "" }, - { "nv!ogl_97f627566e", "" }, - { "nv!ogl_97f627566f", "" }, - { "nv!ogl_97f6275670", "" }, - { "nv!ogl_97f6275671", "" }, - { "nv!ogl_97f727566e", "" }, - { "nv!ogl_98480775", "" }, - { "nv!ogl_98480776", "" }, - { "nv!ogl_98480777", "" }, - { "nv!ogl_992431", "" }, - { "nv!ogl_9aa29065", "" }, - { "nv!ogl_9af32c", "" }, - { "nv!ogl_9af32d", "" }, - { "nv!ogl_9af32e", "" }, - { "nv!ogl_9c108b71", "" }, - { "nv!ogl_9f279065", "" }, - { "nv!ogl_a01bc728", "" }, - { "nv!ogl_a13b46c80", "" }, - { "nv!ogl_a22eb0", "" }, - { "nv!ogl_a2fb451e", "" }, - { "nv!ogl_a3456abe", "" }, - { "nv!ogl_a7044887", "" }, - { "nv!ogl_a7149200", "" }, - { "nv!ogl_a766215670", "" }, - { "nv!ogl_aalinegamma", "" }, - { "nv!ogl_aalinetweaks", "" }, - { "nv!ogl_ab34ee01", "" }, - { "nv!ogl_ab34ee02", "" }, - { "nv!ogl_ab34ee03", "" }, - { "nv!ogl_ac0274", "" }, - { "nv!ogl_af73c63e", "" }, - { "nv!ogl_af73c63f", "" }, - { "nv!ogl_af9927", "" }, - { "nv!ogl_afoverride", "" }, - { "nv!ogl_allocdeviceevents", "" }, - { "nv!ogl_applicationkey", "" }, - { "nv!ogl_appreturnonlybasicglsltype", "" }, - { "nv!ogl_app_softimage", "" }, - { "nv!ogl_app_supportbits2", "" }, - { "nv!ogl_assumetextureismipmappedatcreation", "" }, - { "nv!ogl_b1fb0f01", "" }, - { "nv!ogl_b3edd5", "" }, - { "nv!ogl_b40d9e03d", "" }, - { "nv!ogl_b7f6275666", "" }, - { "nv!ogl_b812c1", "" }, - { "nv!ogl_ba14ba1a", "" }, - { "nv!ogl_ba14ba1b", "" }, - { "nv!ogl_bd7559", "" }, - { "nv!ogl_bd755a", "" }, - { "nv!ogl_bd755c", "" }, - { "nv!ogl_bd755d", "" }, - { "nv!ogl_be58bb", "" }, - { "nv!ogl_be92cb", "" }, - { "nv!ogl_beefcba3", "" }, - { "nv!ogl_beefcba4", "" }, - { "nv!ogl_c023777f", "" }, - { "nv!ogl_c09dc8", "" }, - { "nv!ogl_c0d340", "" }, - { "nv!ogl_c2ff374c", "" }, - { "nv!ogl_c5e9d7a3", "" }, - { "nv!ogl_c5e9d7a4", "" }, - { "nv!ogl_c5e9d7b4", "" }, - { "nv!ogl_c618f9", "" }, - { "nv!ogl_ca345840", "" }, - { "nv!ogl_cachedisable", "" }, - { "nv!ogl_channelpriorityoverride", "" }, - { "nv!ogl_cleardatastorevidmem", "" }, - { "nv!ogl_cmdbufmemoryspaceenables", "" }, - { "nv!ogl_cmdbufminwords", "" }, - { "nv!ogl_cmdbufsizewords", "" }, - { "nv!ogl_conformantblitframebufferscissor", "" }, - { "nv!ogl_conformantincompletetextures", "" }, - { "nv!ogl_copybuffermethod", "" }, - { "nv!ogl_cubemapaniso", "" }, - { "nv!ogl_cubemapfiltering", "" }, - { "nv!ogl_d0e9a4d7", "" }, - { "nv!ogl_d13733f12", "" }, - { "nv!ogl_d1b399", "" }, - { "nv!ogl_d2983c32", "" }, - { "nv!ogl_d2983c33", "" }, - { "nv!ogl_d2e71b", "" }, - { "nv!ogl_d377dc", "" }, - { "nv!ogl_d377dd", "" }, - { "nv!ogl_d489f4", "" }, - { "nv!ogl_d4bce1", "" }, - { "nv!ogl_d518cb", "" }, - { "nv!ogl_d518cd", "" }, - { "nv!ogl_d518ce", "" }, - { "nv!ogl_d518d0", "" }, - { "nv!ogl_d518d1", "" }, - { "nv!ogl_d518d2", "" }, - { "nv!ogl_d518d3", "" }, - { "nv!ogl_d518d4", "" }, - { "nv!ogl_d518d5", "" }, - { "nv!ogl_d59eda", "" }, - { "nv!ogl_d83cbd", "" }, - { "nv!ogl_d8e777", "" }, - { "nv!ogl_debug_level", "" }, - { "nv!ogl_debug_mask", "" }, - { "nv!ogl_debug_options", "" }, - { "nv!ogl_devshmpageableallocations", "" }, - { "nv!ogl_df1f9812", "" }, - { "nv!ogl_df783c", "" }, - { "nv!ogl_diagenable", "" }, - { "nv!ogl_disallowcemask", "" }, - { "nv!ogl_disallowz16", "" }, - { "nv!ogl_dlmemoryspaceenables", "" }, - { "nv!ogl_e0bfec", "" }, - { "nv!ogl_e433456d", "" }, - { "nv!ogl_e435563f", "" }, - { "nv!ogl_e4cd9c", "" }, - { "nv!ogl_e5c972", "" }, - { "nv!ogl_e639ef", "" }, - { "nv!ogl_e802af", "" }, - { "nv!ogl_eae964", "" }, - { "nv!ogl_earlytexturehwallocation", "" }, - { "nv!ogl_eb92a3", "" }, - { "nv!ogl_ebca56", "" }, - { "nv!ogl_expert_detail_level", "" }, - { "nv!ogl_expert_output_mask", "" }, - { "nv!ogl_expert_report_mask", "" }, - { "nv!ogl_extensionstringnvarch", "" }, - { "nv!ogl_extensionstringversion", "" }, - { "nv!ogl_f00f1938", "" }, - { "nv!ogl_f10736", "" }, - { "nv!ogl_f1846870", "" }, - { "nv!ogl_f33bc370", "" }, - { "nv!ogl_f392a874", "" }, - { "nv!ogl_f49ae8", "" }, - { "nv!ogl_fa345cce", "" }, - { "nv!ogl_fa35cc4", "" }, - { "nv!ogl_faa14a", "" }, - { "nv!ogl_faf8a723", "" }, - { "nv!ogl_fastgs", "" }, - { "nv!ogl_fbf4ac45", "" }, - { "nv!ogl_fbo_blit_ignore_srgb", "" }, - { "nv!ogl_fc64c7", "" }, - { "nv!ogl_ff54ec97", "" }, - { "nv!ogl_ff54ec98", "" }, - { "nv!ogl_forceexitprocessdetach", "" }, - { "nv!ogl_forcerequestedesversion", "" }, - { "nv!ogl_glsynctovblank", "" }, - { "nv!ogl_gvitimeoutcontrol", "" }, - { "nv!ogl_hcctrl", "" }, - { "nv!ogl_hwstate_per_ctx", "" }, - { "nv!ogl_machinecachelimit", "" }, - { "nv!ogl_maxframesallowed", "" }, - { "nv!ogl_memmgrcachedalloclimit", "" }, - { "nv!ogl_memmgrcachedalloclimitratio", "" }, - { "nv!ogl_memmgrsysheapalloclimit", "" }, - { "nv!ogl_memmgrsysheapalloclimitratio", "" }, - { "nv!ogl_memmgrvidheapalloclimit", "" }, - { "nv!ogl_mosaic_clip_to_subdev", "" }, - { "nv!ogl_mosaic_clip_to_subdev_h_overlap", "" }, - { "nv!ogl_mosaic_clip_to_subdev_v_overlap", "" }, - { "nv!ogl_overlaymergeblittimerms", "" }, - { "nv!ogl_perfmon_mode", "" }, - { "nv!ogl_pixbar_mode", "" }, - { "nv!ogl_qualityenhancements", "" }, - { "nv!ogl_r27s18q28", "" }, - { "nv!ogl_r2d7c1d8", "" }, - { "nv!ogl_renderer", "" }, - { "nv!ogl_renderqualityflags", "" }, - { "nv!ogl_s3tcquality", "" }, - { "nv!ogl_shaderatomics", "" }, - { "nv!ogl_shadercacheinitsize", "" }, - { "nv!ogl_shader_disk_cache_path", "" }, - { "nv!ogl_shader_disk_cache_read_only", "" }, - { "nv!ogl_shaderobjects", "" }, - { "nv!ogl_shaderportabilitywarnings", "" }, - { "nv!ogl_shaderwarningsaserrors", "" }, - { "nv!ogl_skiptexturehostcopies", "" }, - { "nv!ogl_sli_dli_control", "" }, - { "nv!ogl_sparsetexture", "" }, - { "nv!ogl_spinlooptimeout", "" }, - { "nv!ogl_sync_to_vblank", "" }, - { "nv!ogl_sysheapreuseratio", "" }, - { "nv!ogl_sysmemtexturepromotion", "" }, - { "nv!ogl_targetflushcount", "" }, - { "nv!ogl_tearingfreeswappresent", "" }, - { "nv!ogl_texclampbehavior", "" }, - { "nv!ogl_texlodbias", "" }, - { "nv!ogl_texmemoryspaceenables", "" }, - { "nv!ogl_textureprecache", "" }, - { "nv!ogl_threadcontrol", "" }, - { "nv!ogl_threadcontrol2", "" }, - { "nv!ogl_usegvievents", "" }, - { "nv!ogl_vbomemoryspaceenables", "" }, - { "nv!ogl_vertexlimit", "" }, - { "nv!ogl_vidheapreuseratio", "" }, - { "nv!ogl_vpipe", "" }, - { "nv!ogl_vpipeformatbloatlimit", "" }, - { "nv!ogl_wglmessageboxonabort", "" }, - { "nv!ogl_writeinfolog", "" }, - { "nv!ogl_writeprogramobjectassembly", "" }, - { "nv!ogl_writeprogramobjectsource", "" }, - { "nv!ogl_xnvadapterpresent", "" }, - { "nv!ogl_yield", "" }, - { "nv!ogl_yieldfunction", "" }, - { "nv!ogl_yieldfunctionfast", "" }, - { "nv!ogl_yieldfunctionslow", "" }, - { "nv!ogl_yieldfunctionwaitfordcqueue", "" }, - { "nv!ogl_yieldfunctionwaitforframe", "" }, - { "nv!ogl_yieldfunctionwaitforgpu", "" }, - { "nv!ogl_zbctableaddhysteresis", "" }, - { "nv!overlaymergeblittimerms", "" }, - { "nv!perfmon_mode", "" }, - { "nv!persist.sys.display.resolution", "" }, - { "nv!persist.tegra.composite.fallb", "" }, - { "nv!persist.tegra.composite.policy", "" }, - { "nv!persist.tegra.composite.range", "" }, - { "nv!persist.tegra.compositor", "" }, - { "nv!persist.tegra.compositor.virt", "" }, - { "nv!persist.tegra.compression", "" }, - { "nv!persist.tegra.cursor.enable", "" }, - { "nv!persist.tegra.didim.enable", "" }, - { "nv!persist.tegra.didim.normal", "" }, - { "nv!persist.tegra.didim.video", "" }, - { "nv!persist.tegra.disp.heads", "" }, - { "nv!persist.tegra.gamma_correction", "" }, - { "nv!persist.tegra.gpu_mapping_cache", "" }, - { "nv!persist.tegra.grlayout", "" }, - { "nv!persist.tegra.hdmi.2020.10", "" }, - { "nv!persist.tegra.hdmi.2020.fake", "" }, - { "nv!persist.tegra.hdmi.2020.force", "" }, - { "nv!persist.tegra.hdmi.autorotate", "" }, - { "nv!persist.tegra.hdmi.hdr.fake", "" }, - { "nv!persist.tegra.hdmi.ignore_ratio", "" }, - { "nv!persist.tegra.hdmi.limit.clock", "" }, - { "nv!persist.tegra.hdmi.only_16_9", "" }, - { "nv!persist.tegra.hdmi.range", "" }, - { "nv!persist.tegra.hdmi.resolution", "" }, - { "nv!persist.tegra.hdmi.underscan", "" }, - { "nv!persist.tegra.hdmi.yuv.422", "" }, - { "nv!persist.tegra.hdmi.yuv.444", "" }, - { "nv!persist.tegra.hdmi.yuv.enable", "" }, - { "nv!persist.tegra.hdmi.yuv.force", "" }, - { "nv!persist.tegra.hwc.nvdc", "" }, - { "nv!persist.tegra.idle.minimum_fps", "" }, - { "nv!persist.tegra.panel.rotation", "" }, - { "nv!persist.tegra.scan_props", "" }, - { "nv!persist.tegra.stb.mode", "" }, - { "nv!persist.tegra.zbc_override", "" }, - { "nv!pixbar_mode", "" }, - { "nv!qualityenhancements", "" }, - { "nv!r27s18q28", "" }, - { "nv!r2d7c1d8", "" }, - { "nv!renderer", "" }, - { "nv!renderqualityflags", "" }, - { "nv!rmos_debug_mask", "" }, - { "nv!rmos_set_production_mode", "" }, - { "nv!s3tcquality", "" }, - { "nv!shaderatomics", "" }, - { "nv!shadercacheinitsize", "" }, - { "nv!shader_disk_cache_path", "" }, - { "nv!shader_disk_cache_read_only", "" }, - { "nv!shaderobjects", "" }, - { "nv!shaderportabilitywarnings", "" }, - { "nv!shaderwarningsaserrors", "" }, - { "nv!skiptexturehostcopies", "" }, - { "nv!sli_dli_control", "" }, - { "nv!sparsetexture", "" }, - { "nv!spinlooptimeout", "" }, - { "nv!sync_to_vblank", "" }, - { "nv!sysheapreuseratio", "" }, - { "nv!sysmemtexturepromotion", "" }, - { "nv!targetflushcount", "" }, - { "nv!tearingfreeswappresent", "" }, - { "nv!tegra.refresh", "" }, - { "nv!texclampbehavior", "" }, - { "nv!texlodbias", "" }, - { "nv!texmemoryspaceenables", "" }, - { "nv!textureprecache", "" }, - { "nv!threadcontrol", "" }, - { "nv!threadcontrol2", "" }, - { "nv!tvmr.avp.logs", "" }, - { "nv!tvmr.buffer.logs", "" }, - { "nv!tvmr.dec.prof", "" }, - { "nv!tvmr.deint.logs", "" }, - { "nv!tvmr.dfs.logs", "" }, - { "nv!tvmr.ffprof.logs", "" }, - { "nv!tvmr.game.stream", "" }, - { "nv!tvmr.general.logs", "" }, - { "nv!tvmr.input.dump", "" }, - { "nv!tvmr.seeking.logs", "" }, - { "nv!tvmr.ts_pulldown", "" }, - { "nv!usegvievents", "" }, - { "nv!vbomemoryspaceenables", "" }, - { "nv!vcc_debug_ip", "" }, - { "nv!vcc_verbose_level", "" }, - { "nv!vertexlimit", "" }, - { "nv!viccomposer.filter", "" }, - { "nv!videostats-enable", "" }, - { "nv!vidheapreuseratio", "" }, - { "nv!vpipe", "" }, - { "nv!vpipeformatbloatlimit", "" }, - { "nv!wglmessageboxonabort", "" }, - { "nv!writeinfolog", "" }, - { "nv!writeprogramobjectassembly", "" }, - { "nv!writeprogramobjectsource", "" }, - { "nv!xnvadapterpresent", "" }, - { "nv!yield", "" }, - { "nv!yieldfunction", "" }, - { "nv!yieldfunctionfast", "" }, - { "nv!yieldfunctionslow", "" }, - { "nv!yieldfunctionwaitfordcqueue", "" }, - { "nv!yieldfunctionwaitforframe", "" }, - { "nv!yieldfunctionwaitforgpu", "" }, - { "nv!zbctableaddhysteresis", "" }, + { "nv!nvn_through_opengl", string.Empty }, + { "nv!nv_pllcx_always_on", string.Empty }, + { "nv!nv_pllcx_safe_div", string.Empty }, + { "nv!nvrm_gpu_channel_interleave", string.Empty }, + { "nv!nvrm_gpu_channel_priority", string.Empty }, + { "nv!nvrm_gpu_channel_timeslice", string.Empty }, + { "nv!nvrm_gpu_default_device_index", string.Empty }, + { "nv!nvrm_gpu_dummy", string.Empty }, + { "nv!nvrm_gpu_help", string.Empty }, + { "nv!nvrm_gpu_nvgpu_disable", string.Empty }, + { "nv!nvrm_gpu_nvgpu_do_nfa_partial_map", string.Empty }, + { "nv!nvrm_gpu_nvgpu_ecc_overrides", string.Empty }, + { "nv!nvrm_gpu_nvgpu_no_as_get_va_regions", string.Empty }, + { "nv!nvrm_gpu_nvgpu_no_channel_abort", string.Empty }, + { "nv!nvrm_gpu_nvgpu_no_cyclestats", string.Empty }, + { "nv!nvrm_gpu_nvgpu_no_fixed", string.Empty }, + { "nv!nvrm_gpu_nvgpu_no_gpu_characteristics", string.Empty }, + { "nv!nvrm_gpu_nvgpu_no_ioctl_mutex", string.Empty }, + { "nv!nvrm_gpu_nvgpu_no_map_buffer_ex", string.Empty }, + { "nv!nvrm_gpu_nvgpu_no_robustness", string.Empty }, + { "nv!nvrm_gpu_nvgpu_no_sparse", string.Empty }, + { "nv!nvrm_gpu_nvgpu_no_syncpoints", string.Empty }, + { "nv!nvrm_gpu_nvgpu_no_tsg", string.Empty }, + { "nv!nvrm_gpu_nvgpu_no_zbc", string.Empty }, + { "nv!nvrm_gpu_nvgpu_no_zcull", string.Empty }, + { "nv!nvrm_gpu_nvgpu_wrap_channels_in_tsgs", string.Empty }, + { "nv!nvrm_gpu_prevent_use", string.Empty }, + { "nv!nvrm_gpu_trace", string.Empty }, + { "nv!nvsched_debug_mask", string.Empty }, + { "nv!nvsched_force_enable", string.Empty }, + { "nv!nvsched_force_log", string.Empty }, + { "nv!nv_usb_plls_hw_ctrl", string.Empty }, + { "nv!nv_winsys", string.Empty }, + { "nv!nvwsi_dump", string.Empty }, + { "nv!nvwsi_fill", string.Empty }, + { "nv!ogl_", string.Empty }, + { "nv!ogl_0356afd0", string.Empty }, + { "nv!ogl_0356afd1", string.Empty }, + { "nv!ogl_0356afd2", string.Empty }, + { "nv!ogl_0356afd3", string.Empty }, + { "nv!ogl_0x923dc0", string.Empty }, + { "nv!ogl_0x923dc1", string.Empty }, + { "nv!ogl_0x923dc2", string.Empty }, + { "nv!ogl_0x923dc3", string.Empty }, + { "nv!ogl_0x923dc4", string.Empty }, + { "nv!ogl_0x923dd3", string.Empty }, + { "nv!ogl_0x9abdc5", string.Empty }, + { "nv!ogl_0x9abdc6", string.Empty }, + { "nv!ogl_0xbd10fb", string.Empty }, + { "nv!ogl_0xce2348", string.Empty }, + { "nv!ogl_10261989", string.Empty }, + { "nv!ogl_1042d483", string.Empty }, + { "nv!ogl_10572898", string.Empty }, + { "nv!ogl_115631", string.Empty }, + { "nv!ogl_12950094", string.Empty }, + { "nv!ogl_1314f311", string.Empty }, + { "nv!ogl_1314f312", string.Empty }, + { "nv!ogl_13279512", string.Empty }, + { "nv!ogl_13813496", string.Empty }, + { "nv!ogl_14507179", string.Empty }, + { "nv!ogl_15694569", string.Empty }, + { "nv!ogl_16936964", string.Empty }, + { "nv!ogl_17aa230c", string.Empty }, + { "nv!ogl_182054", string.Empty }, + { "nv!ogl_18273275", string.Empty }, + { "nv!ogl_18273276", string.Empty }, + { "nv!ogl_1854d03b", string.Empty }, + { "nv!ogl_18add00d", string.Empty }, + { "nv!ogl_19156670", string.Empty }, + { "nv!ogl_19286545", string.Empty }, + { "nv!ogl_1a298e9f", string.Empty }, + { "nv!ogl_1acf43fe", string.Empty }, + { "nv!ogl_1bda43fe", string.Empty }, + { "nv!ogl_1c3b92", string.Empty }, + { "nv!ogl_21509920", string.Empty }, + { "nv!ogl_215323457", string.Empty }, + { "nv!ogl_2165ad", string.Empty }, + { "nv!ogl_2165ae", string.Empty }, + { "nv!ogl_21be9c", string.Empty }, + { "nv!ogl_233264316", string.Empty }, + { "nv!ogl_234557580", string.Empty }, + { "nv!ogl_23cd0e", string.Empty }, + { "nv!ogl_24189123", string.Empty }, + { "nv!ogl_2443266", string.Empty }, + { "nv!ogl_25025519", string.Empty }, + { "nv!ogl_255e39", string.Empty }, + { "nv!ogl_2583364", string.Empty }, + { "nv!ogl_2888c1", string.Empty }, + { "nv!ogl_28ca3e", string.Empty }, + { "nv!ogl_29871243", string.Empty }, + { "nv!ogl_2a1f64", string.Empty }, + { "nv!ogl_2dc432", string.Empty }, + { "nv!ogl_2de437", string.Empty }, + { "nv!ogl_2f3bb89c", string.Empty }, + { "nv!ogl_2fd652", string.Empty }, + { "nv!ogl_3001ac", string.Empty }, + { "nv!ogl_31298772", string.Empty }, + { "nv!ogl_313233", string.Empty }, + { "nv!ogl_31f7d603", string.Empty }, + { "nv!ogl_320ce4", string.Empty }, + { "nv!ogl_32153248", string.Empty }, + { "nv!ogl_32153249", string.Empty }, + { "nv!ogl_335bca", string.Empty }, + { "nv!ogl_342abb", string.Empty }, + { "nv!ogl_34dfe6", string.Empty }, + { "nv!ogl_34dfe7", string.Empty }, + { "nv!ogl_34dfe8", string.Empty }, + { "nv!ogl_34dfe9", string.Empty }, + { "nv!ogl_35201578", string.Empty }, + { "nv!ogl_359278", string.Empty }, + { "nv!ogl_37f53a", string.Empty }, + { "nv!ogl_38144972", string.Empty }, + { "nv!ogl_38542646", string.Empty }, + { "nv!ogl_3b74c9", string.Empty }, + { "nv!ogl_3c136f", string.Empty }, + { "nv!ogl_3cf72823", string.Empty }, + { "nv!ogl_3d7af029", string.Empty }, + { "nv!ogl_3ff34782", string.Empty }, + { "nv!ogl_4129618", string.Empty }, + { "nv!ogl_4189fac3", string.Empty }, + { "nv!ogl_420bd4", string.Empty }, + { "nv!ogl_42a699", string.Empty }, + { "nv!ogl_441369", string.Empty }, + { "nv!ogl_4458713e", string.Empty }, + { "nv!ogl_4554b6", string.Empty }, + { "nv!ogl_457425", string.Empty }, + { "nv!ogl_4603b207", string.Empty }, + { "nv!ogl_46574957", string.Empty }, + { "nv!ogl_46574958", string.Empty }, + { "nv!ogl_46813529", string.Empty }, + { "nv!ogl_46f1e13d", string.Empty }, + { "nv!ogl_47534c43", string.Empty }, + { "nv!ogl_48550336", string.Empty }, + { "nv!ogl_48576893", string.Empty }, + { "nv!ogl_48576894", string.Empty }, + { "nv!ogl_4889ac02", string.Empty }, + { "nv!ogl_49005740", string.Empty }, + { "nv!ogl_49867584", string.Empty }, + { "nv!ogl_49960973", string.Empty }, + { "nv!ogl_4a5341", string.Empty }, + { "nv!ogl_4f4e48", string.Empty }, + { "nv!ogl_4f8a0a", string.Empty }, + { "nv!ogl_50299698", string.Empty }, + { "nv!ogl_50299699", string.Empty }, + { "nv!ogl_50361291", string.Empty }, + { "nv!ogl_5242ae", string.Empty }, + { "nv!ogl_53d30c", string.Empty }, + { "nv!ogl_56347a", string.Empty }, + { "nv!ogl_563a95f1", string.Empty }, + { "nv!ogl_573823", string.Empty }, + { "nv!ogl_58027529", string.Empty }, + { "nv!ogl_5d2d63", string.Empty }, + { "nv!ogl_5f7e3b", string.Empty }, + { "nv!ogl_60461793", string.Empty }, + { "nv!ogl_60d355", string.Empty }, + { "nv!ogl_616627aa", string.Empty }, + { "nv!ogl_62317182", string.Empty }, + { "nv!ogl_6253fa2e", string.Empty }, + { "nv!ogl_64100768", string.Empty }, + { "nv!ogl_64100769", string.Empty }, + { "nv!ogl_64100770", string.Empty }, + { "nv!ogl_647395", string.Empty }, + { "nv!ogl_66543234", string.Empty }, + { "nv!ogl_67674763", string.Empty }, + { "nv!ogl_67739784", string.Empty }, + { "nv!ogl_68fb9c", string.Empty }, + { "nv!ogl_69801276", string.Empty }, + { "nv!ogl_6af9fa2f", string.Empty }, + { "nv!ogl_6af9fa3f", string.Empty }, + { "nv!ogl_6af9fa4f", string.Empty }, + { "nv!ogl_6bd8c7", string.Empty }, + { "nv!ogl_6c7691", string.Empty }, + { "nv!ogl_6d4296ce", string.Empty }, + { "nv!ogl_6dd7e7", string.Empty }, + { "nv!ogl_6dd7e8", string.Empty }, + { "nv!ogl_6fe11ec1", string.Empty }, + { "nv!ogl_716511763", string.Empty }, + { "nv!ogl_72504593", string.Empty }, + { "nv!ogl_73304097", string.Empty }, + { "nv!ogl_73314098", string.Empty }, + { "nv!ogl_74095213", string.Empty }, + { "nv!ogl_74095213a", string.Empty }, + { "nv!ogl_74095213b", string.Empty }, + { "nv!ogl_74095214", string.Empty }, + { "nv!ogl_748f9649", string.Empty }, + { "nv!ogl_75494732", string.Empty }, + { "nv!ogl_78452832", string.Empty }, + { "nv!ogl_784561", string.Empty }, + { "nv!ogl_78e16b9c", string.Empty }, + { "nv!ogl_79251225", string.Empty }, + { "nv!ogl_7c128b", string.Empty }, + { "nv!ogl_7ccd93", string.Empty }, + { "nv!ogl_7df8d1", string.Empty }, + { "nv!ogl_800c2310", string.Empty }, + { "nv!ogl_80546710", string.Empty }, + { "nv!ogl_80772310", string.Empty }, + { "nv!ogl_808ee280", string.Empty }, + { "nv!ogl_81131154", string.Empty }, + { "nv!ogl_81274457", string.Empty }, + { "nv!ogl_8292291f", string.Empty }, + { "nv!ogl_83498426", string.Empty }, + { "nv!ogl_84993794", string.Empty }, + { "nv!ogl_84995585", string.Empty }, + { "nv!ogl_84a0a0", string.Empty }, + { "nv!ogl_852142", string.Empty }, + { "nv!ogl_85612309", string.Empty }, + { "nv!ogl_85612310", string.Empty }, + { "nv!ogl_85612311", string.Empty }, + { "nv!ogl_85612312", string.Empty }, + { "nv!ogl_8623ff27", string.Empty }, + { "nv!ogl_87364952", string.Empty }, + { "nv!ogl_87f6275666", string.Empty }, + { "nv!ogl_886748", string.Empty }, + { "nv!ogl_89894423", string.Empty }, + { "nv!ogl_8ad8a75", string.Empty }, + { "nv!ogl_8ad8ad00", string.Empty }, + { "nv!ogl_8bb815", string.Empty }, + { "nv!ogl_8bb817", string.Empty }, + { "nv!ogl_8bb818", string.Empty }, + { "nv!ogl_8bb819", string.Empty }, + { "nv!ogl_8e640cd1", string.Empty }, + { "nv!ogl_8f34971a", string.Empty }, + { "nv!ogl_8f773984", string.Empty }, + { "nv!ogl_8f7a7d", string.Empty }, + { "nv!ogl_902486209", string.Empty }, + { "nv!ogl_90482571", string.Empty }, + { "nv!ogl_91214835", string.Empty }, + { "nv!ogl_912848290", string.Empty }, + { "nv!ogl_915e56", string.Empty }, + { "nv!ogl_92179063", string.Empty }, + { "nv!ogl_92179064", string.Empty }, + { "nv!ogl_92179065", string.Empty }, + { "nv!ogl_92179066", string.Empty }, + { "nv!ogl_92350358", string.Empty }, + { "nv!ogl_92809063", string.Empty }, + { "nv!ogl_92809064", string.Empty }, + { "nv!ogl_92809065", string.Empty }, + { "nv!ogl_92809066", string.Empty }, + { "nv!ogl_92920143", string.Empty }, + { "nv!ogl_93a89b12", string.Empty }, + { "nv!ogl_93a89c0b", string.Empty }, + { "nv!ogl_94812574", string.Empty }, + { "nv!ogl_95282304", string.Empty }, + { "nv!ogl_95394027", string.Empty }, + { "nv!ogl_959b1f", string.Empty }, + { "nv!ogl_9638af", string.Empty }, + { "nv!ogl_96fd59", string.Empty }, + { "nv!ogl_97f6275666", string.Empty }, + { "nv!ogl_97f6275667", string.Empty }, + { "nv!ogl_97f6275668", string.Empty }, + { "nv!ogl_97f6275669", string.Empty }, + { "nv!ogl_97f627566a", string.Empty }, + { "nv!ogl_97f627566b", string.Empty }, + { "nv!ogl_97f627566d", string.Empty }, + { "nv!ogl_97f627566e", string.Empty }, + { "nv!ogl_97f627566f", string.Empty }, + { "nv!ogl_97f6275670", string.Empty }, + { "nv!ogl_97f6275671", string.Empty }, + { "nv!ogl_97f727566e", string.Empty }, + { "nv!ogl_98480775", string.Empty }, + { "nv!ogl_98480776", string.Empty }, + { "nv!ogl_98480777", string.Empty }, + { "nv!ogl_992431", string.Empty }, + { "nv!ogl_9aa29065", string.Empty }, + { "nv!ogl_9af32c", string.Empty }, + { "nv!ogl_9af32d", string.Empty }, + { "nv!ogl_9af32e", string.Empty }, + { "nv!ogl_9c108b71", string.Empty }, + { "nv!ogl_9f279065", string.Empty }, + { "nv!ogl_a01bc728", string.Empty }, + { "nv!ogl_a13b46c80", string.Empty }, + { "nv!ogl_a22eb0", string.Empty }, + { "nv!ogl_a2fb451e", string.Empty }, + { "nv!ogl_a3456abe", string.Empty }, + { "nv!ogl_a7044887", string.Empty }, + { "nv!ogl_a7149200", string.Empty }, + { "nv!ogl_a766215670", string.Empty }, + { "nv!ogl_aalinegamma", string.Empty }, + { "nv!ogl_aalinetweaks", string.Empty }, + { "nv!ogl_ab34ee01", string.Empty }, + { "nv!ogl_ab34ee02", string.Empty }, + { "nv!ogl_ab34ee03", string.Empty }, + { "nv!ogl_ac0274", string.Empty }, + { "nv!ogl_af73c63e", string.Empty }, + { "nv!ogl_af73c63f", string.Empty }, + { "nv!ogl_af9927", string.Empty }, + { "nv!ogl_afoverride", string.Empty }, + { "nv!ogl_allocdeviceevents", string.Empty }, + { "nv!ogl_applicationkey", string.Empty }, + { "nv!ogl_appreturnonlybasicglsltype", string.Empty }, + { "nv!ogl_app_softimage", string.Empty }, + { "nv!ogl_app_supportbits2", string.Empty }, + { "nv!ogl_assumetextureismipmappedatcreation", string.Empty }, + { "nv!ogl_b1fb0f01", string.Empty }, + { "nv!ogl_b3edd5", string.Empty }, + { "nv!ogl_b40d9e03d", string.Empty }, + { "nv!ogl_b7f6275666", string.Empty }, + { "nv!ogl_b812c1", string.Empty }, + { "nv!ogl_ba14ba1a", string.Empty }, + { "nv!ogl_ba14ba1b", string.Empty }, + { "nv!ogl_bd7559", string.Empty }, + { "nv!ogl_bd755a", string.Empty }, + { "nv!ogl_bd755c", string.Empty }, + { "nv!ogl_bd755d", string.Empty }, + { "nv!ogl_be58bb", string.Empty }, + { "nv!ogl_be92cb", string.Empty }, + { "nv!ogl_beefcba3", string.Empty }, + { "nv!ogl_beefcba4", string.Empty }, + { "nv!ogl_c023777f", string.Empty }, + { "nv!ogl_c09dc8", string.Empty }, + { "nv!ogl_c0d340", string.Empty }, + { "nv!ogl_c2ff374c", string.Empty }, + { "nv!ogl_c5e9d7a3", string.Empty }, + { "nv!ogl_c5e9d7a4", string.Empty }, + { "nv!ogl_c5e9d7b4", string.Empty }, + { "nv!ogl_c618f9", string.Empty }, + { "nv!ogl_ca345840", string.Empty }, + { "nv!ogl_cachedisable", string.Empty }, + { "nv!ogl_channelpriorityoverride", string.Empty }, + { "nv!ogl_cleardatastorevidmem", string.Empty }, + { "nv!ogl_cmdbufmemoryspaceenables", string.Empty }, + { "nv!ogl_cmdbufminwords", string.Empty }, + { "nv!ogl_cmdbufsizewords", string.Empty }, + { "nv!ogl_conformantblitframebufferscissor", string.Empty }, + { "nv!ogl_conformantincompletetextures", string.Empty }, + { "nv!ogl_copybuffermethod", string.Empty }, + { "nv!ogl_cubemapaniso", string.Empty }, + { "nv!ogl_cubemapfiltering", string.Empty }, + { "nv!ogl_d0e9a4d7", string.Empty }, + { "nv!ogl_d13733f12", string.Empty }, + { "nv!ogl_d1b399", string.Empty }, + { "nv!ogl_d2983c32", string.Empty }, + { "nv!ogl_d2983c33", string.Empty }, + { "nv!ogl_d2e71b", string.Empty }, + { "nv!ogl_d377dc", string.Empty }, + { "nv!ogl_d377dd", string.Empty }, + { "nv!ogl_d489f4", string.Empty }, + { "nv!ogl_d4bce1", string.Empty }, + { "nv!ogl_d518cb", string.Empty }, + { "nv!ogl_d518cd", string.Empty }, + { "nv!ogl_d518ce", string.Empty }, + { "nv!ogl_d518d0", string.Empty }, + { "nv!ogl_d518d1", string.Empty }, + { "nv!ogl_d518d2", string.Empty }, + { "nv!ogl_d518d3", string.Empty }, + { "nv!ogl_d518d4", string.Empty }, + { "nv!ogl_d518d5", string.Empty }, + { "nv!ogl_d59eda", string.Empty }, + { "nv!ogl_d83cbd", string.Empty }, + { "nv!ogl_d8e777", string.Empty }, + { "nv!ogl_debug_level", string.Empty }, + { "nv!ogl_debug_mask", string.Empty }, + { "nv!ogl_debug_options", string.Empty }, + { "nv!ogl_devshmpageableallocations", string.Empty }, + { "nv!ogl_df1f9812", string.Empty }, + { "nv!ogl_df783c", string.Empty }, + { "nv!ogl_diagenable", string.Empty }, + { "nv!ogl_disallowcemask", string.Empty }, + { "nv!ogl_disallowz16", string.Empty }, + { "nv!ogl_dlmemoryspaceenables", string.Empty }, + { "nv!ogl_e0bfec", string.Empty }, + { "nv!ogl_e433456d", string.Empty }, + { "nv!ogl_e435563f", string.Empty }, + { "nv!ogl_e4cd9c", string.Empty }, + { "nv!ogl_e5c972", string.Empty }, + { "nv!ogl_e639ef", string.Empty }, + { "nv!ogl_e802af", string.Empty }, + { "nv!ogl_eae964", string.Empty }, + { "nv!ogl_earlytexturehwallocation", string.Empty }, + { "nv!ogl_eb92a3", string.Empty }, + { "nv!ogl_ebca56", string.Empty }, + { "nv!ogl_expert_detail_level", string.Empty }, + { "nv!ogl_expert_output_mask", string.Empty }, + { "nv!ogl_expert_report_mask", string.Empty }, + { "nv!ogl_extensionstringnvarch", string.Empty }, + { "nv!ogl_extensionstringversion", string.Empty }, + { "nv!ogl_f00f1938", string.Empty }, + { "nv!ogl_f10736", string.Empty }, + { "nv!ogl_f1846870", string.Empty }, + { "nv!ogl_f33bc370", string.Empty }, + { "nv!ogl_f392a874", string.Empty }, + { "nv!ogl_f49ae8", string.Empty }, + { "nv!ogl_fa345cce", string.Empty }, + { "nv!ogl_fa35cc4", string.Empty }, + { "nv!ogl_faa14a", string.Empty }, + { "nv!ogl_faf8a723", string.Empty }, + { "nv!ogl_fastgs", string.Empty }, + { "nv!ogl_fbf4ac45", string.Empty }, + { "nv!ogl_fbo_blit_ignore_srgb", string.Empty }, + { "nv!ogl_fc64c7", string.Empty }, + { "nv!ogl_ff54ec97", string.Empty }, + { "nv!ogl_ff54ec98", string.Empty }, + { "nv!ogl_forceexitprocessdetach", string.Empty }, + { "nv!ogl_forcerequestedesversion", string.Empty }, + { "nv!ogl_glsynctovblank", string.Empty }, + { "nv!ogl_gvitimeoutcontrol", string.Empty }, + { "nv!ogl_hcctrl", string.Empty }, + { "nv!ogl_hwstate_per_ctx", string.Empty }, + { "nv!ogl_machinecachelimit", string.Empty }, + { "nv!ogl_maxframesallowed", string.Empty }, + { "nv!ogl_memmgrcachedalloclimit", string.Empty }, + { "nv!ogl_memmgrcachedalloclimitratio", string.Empty }, + { "nv!ogl_memmgrsysheapalloclimit", string.Empty }, + { "nv!ogl_memmgrsysheapalloclimitratio", string.Empty }, + { "nv!ogl_memmgrvidheapalloclimit", string.Empty }, + { "nv!ogl_mosaic_clip_to_subdev", string.Empty }, + { "nv!ogl_mosaic_clip_to_subdev_h_overlap", string.Empty }, + { "nv!ogl_mosaic_clip_to_subdev_v_overlap", string.Empty }, + { "nv!ogl_overlaymergeblittimerms", string.Empty }, + { "nv!ogl_perfmon_mode", string.Empty }, + { "nv!ogl_pixbar_mode", string.Empty }, + { "nv!ogl_qualityenhancements", string.Empty }, + { "nv!ogl_r27s18q28", string.Empty }, + { "nv!ogl_r2d7c1d8", string.Empty }, + { "nv!ogl_renderer", string.Empty }, + { "nv!ogl_renderqualityflags", string.Empty }, + { "nv!ogl_s3tcquality", string.Empty }, + { "nv!ogl_shaderatomics", string.Empty }, + { "nv!ogl_shadercacheinitsize", string.Empty }, + { "nv!ogl_shader_disk_cache_path", string.Empty }, + { "nv!ogl_shader_disk_cache_read_only", string.Empty }, + { "nv!ogl_shaderobjects", string.Empty }, + { "nv!ogl_shaderportabilitywarnings", string.Empty }, + { "nv!ogl_shaderwarningsaserrors", string.Empty }, + { "nv!ogl_skiptexturehostcopies", string.Empty }, + { "nv!ogl_sli_dli_control", string.Empty }, + { "nv!ogl_sparsetexture", string.Empty }, + { "nv!ogl_spinlooptimeout", string.Empty }, + { "nv!ogl_sync_to_vblank", string.Empty }, + { "nv!ogl_sysheapreuseratio", string.Empty }, + { "nv!ogl_sysmemtexturepromotion", string.Empty }, + { "nv!ogl_targetflushcount", string.Empty }, + { "nv!ogl_tearingfreeswappresent", string.Empty }, + { "nv!ogl_texclampbehavior", string.Empty }, + { "nv!ogl_texlodbias", string.Empty }, + { "nv!ogl_texmemoryspaceenables", string.Empty }, + { "nv!ogl_textureprecache", string.Empty }, + { "nv!ogl_threadcontrol", string.Empty }, + { "nv!ogl_threadcontrol2", string.Empty }, + { "nv!ogl_usegvievents", string.Empty }, + { "nv!ogl_vbomemoryspaceenables", string.Empty }, + { "nv!ogl_vertexlimit", string.Empty }, + { "nv!ogl_vidheapreuseratio", string.Empty }, + { "nv!ogl_vpipe", string.Empty }, + { "nv!ogl_vpipeformatbloatlimit", string.Empty }, + { "nv!ogl_wglmessageboxonabort", string.Empty }, + { "nv!ogl_writeinfolog", string.Empty }, + { "nv!ogl_writeprogramobjectassembly", string.Empty }, + { "nv!ogl_writeprogramobjectsource", string.Empty }, + { "nv!ogl_xnvadapterpresent", string.Empty }, + { "nv!ogl_yield", string.Empty }, + { "nv!ogl_yieldfunction", string.Empty }, + { "nv!ogl_yieldfunctionfast", string.Empty }, + { "nv!ogl_yieldfunctionslow", string.Empty }, + { "nv!ogl_yieldfunctionwaitfordcqueue", string.Empty }, + { "nv!ogl_yieldfunctionwaitforframe", string.Empty }, + { "nv!ogl_yieldfunctionwaitforgpu", string.Empty }, + { "nv!ogl_zbctableaddhysteresis", string.Empty }, + { "nv!overlaymergeblittimerms", string.Empty }, + { "nv!perfmon_mode", string.Empty }, + { "nv!persist.sys.display.resolution", string.Empty }, + { "nv!persist.tegra.composite.fallb", string.Empty }, + { "nv!persist.tegra.composite.policy", string.Empty }, + { "nv!persist.tegra.composite.range", string.Empty }, + { "nv!persist.tegra.compositor", string.Empty }, + { "nv!persist.tegra.compositor.virt", string.Empty }, + { "nv!persist.tegra.compression", string.Empty }, + { "nv!persist.tegra.cursor.enable", string.Empty }, + { "nv!persist.tegra.didim.enable", string.Empty }, + { "nv!persist.tegra.didim.normal", string.Empty }, + { "nv!persist.tegra.didim.video", string.Empty }, + { "nv!persist.tegra.disp.heads", string.Empty }, + { "nv!persist.tegra.gamma_correction", string.Empty }, + { "nv!persist.tegra.gpu_mapping_cache", string.Empty }, + { "nv!persist.tegra.grlayout", string.Empty }, + { "nv!persist.tegra.hdmi.2020.10", string.Empty }, + { "nv!persist.tegra.hdmi.2020.fake", string.Empty }, + { "nv!persist.tegra.hdmi.2020.force", string.Empty }, + { "nv!persist.tegra.hdmi.autorotate", string.Empty }, + { "nv!persist.tegra.hdmi.hdr.fake", string.Empty }, + { "nv!persist.tegra.hdmi.ignore_ratio", string.Empty }, + { "nv!persist.tegra.hdmi.limit.clock", string.Empty }, + { "nv!persist.tegra.hdmi.only_16_9", string.Empty }, + { "nv!persist.tegra.hdmi.range", string.Empty }, + { "nv!persist.tegra.hdmi.resolution", string.Empty }, + { "nv!persist.tegra.hdmi.underscan", string.Empty }, + { "nv!persist.tegra.hdmi.yuv.422", string.Empty }, + { "nv!persist.tegra.hdmi.yuv.444", string.Empty }, + { "nv!persist.tegra.hdmi.yuv.enable", string.Empty }, + { "nv!persist.tegra.hdmi.yuv.force", string.Empty }, + { "nv!persist.tegra.hwc.nvdc", string.Empty }, + { "nv!persist.tegra.idle.minimum_fps", string.Empty }, + { "nv!persist.tegra.panel.rotation", string.Empty }, + { "nv!persist.tegra.scan_props", string.Empty }, + { "nv!persist.tegra.stb.mode", string.Empty }, + { "nv!persist.tegra.zbc_override", string.Empty }, + { "nv!pixbar_mode", string.Empty }, + { "nv!qualityenhancements", string.Empty }, + { "nv!r27s18q28", string.Empty }, + { "nv!r2d7c1d8", string.Empty }, + { "nv!renderer", string.Empty }, + { "nv!renderqualityflags", string.Empty }, + { "nv!rmos_debug_mask", string.Empty }, + { "nv!rmos_set_production_mode", string.Empty }, + { "nv!s3tcquality", string.Empty }, + { "nv!shaderatomics", string.Empty }, + { "nv!shadercacheinitsize", string.Empty }, + { "nv!shader_disk_cache_path", string.Empty }, + { "nv!shader_disk_cache_read_only", string.Empty }, + { "nv!shaderobjects", string.Empty }, + { "nv!shaderportabilitywarnings", string.Empty }, + { "nv!shaderwarningsaserrors", string.Empty }, + { "nv!skiptexturehostcopies", string.Empty }, + { "nv!sli_dli_control", string.Empty }, + { "nv!sparsetexture", string.Empty }, + { "nv!spinlooptimeout", string.Empty }, + { "nv!sync_to_vblank", string.Empty }, + { "nv!sysheapreuseratio", string.Empty }, + { "nv!sysmemtexturepromotion", string.Empty }, + { "nv!targetflushcount", string.Empty }, + { "nv!tearingfreeswappresent", string.Empty }, + { "nv!tegra.refresh", string.Empty }, + { "nv!texclampbehavior", string.Empty }, + { "nv!texlodbias", string.Empty }, + { "nv!texmemoryspaceenables", string.Empty }, + { "nv!textureprecache", string.Empty }, + { "nv!threadcontrol", string.Empty }, + { "nv!threadcontrol2", string.Empty }, + { "nv!tvmr.avp.logs", string.Empty }, + { "nv!tvmr.buffer.logs", string.Empty }, + { "nv!tvmr.dec.prof", string.Empty }, + { "nv!tvmr.deint.logs", string.Empty }, + { "nv!tvmr.dfs.logs", string.Empty }, + { "nv!tvmr.ffprof.logs", string.Empty }, + { "nv!tvmr.game.stream", string.Empty }, + { "nv!tvmr.general.logs", string.Empty }, + { "nv!tvmr.input.dump", string.Empty }, + { "nv!tvmr.seeking.logs", string.Empty }, + { "nv!tvmr.ts_pulldown", string.Empty }, + { "nv!usegvievents", string.Empty }, + { "nv!vbomemoryspaceenables", string.Empty }, + { "nv!vcc_debug_ip", string.Empty }, + { "nv!vcc_verbose_level", string.Empty }, + { "nv!vertexlimit", string.Empty }, + { "nv!viccomposer.filter", string.Empty }, + { "nv!videostats-enable", string.Empty }, + { "nv!vidheapreuseratio", string.Empty }, + { "nv!vpipe", string.Empty }, + { "nv!vpipeformatbloatlimit", string.Empty }, + { "nv!wglmessageboxonabort", string.Empty }, + { "nv!writeinfolog", string.Empty }, + { "nv!writeprogramobjectassembly", string.Empty }, + { "nv!writeprogramobjectsource", string.Empty }, + { "nv!xnvadapterpresent", string.Empty }, + { "nv!yield", string.Empty }, + { "nv!yieldfunction", string.Empty }, + { "nv!yieldfunctionfast", string.Empty }, + { "nv!yieldfunctionslow", string.Empty }, + { "nv!yieldfunctionwaitfordcqueue", string.Empty }, + { "nv!yieldfunctionwaitforframe", string.Empty }, + { "nv!yieldfunctionwaitforgpu", string.Empty }, + { "nv!zbctableaddhysteresis", string.Empty }, { "pcm!enable", true }, { "pctl!intermittent_task_interval_seconds", 21600 }, { "prepo!devmenu_prepo_page_view", false }, @@ -1611,7 +1611,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings { "systempowerstate!always_reboot", false }, { "systempowerstate!power_state_message_emulation_trigger_time", 0 }, { "systempowerstate!power_state_message_to_emulate", 0 }, - { "target_manager!device_name", "" }, + { "target_manager!device_name", string.Empty }, { "vulnerability!needs_update_vulnerability_policy", 0 }, { "apm!performance_mode_policy", "auto" }, { "apm!sdev_throttling_enabled", true }, diff --git a/src/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs b/src/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs index 7a90c664e..271b8fc84 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs @@ -94,7 +94,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm { if (_services.TryGetValue(name, out Type type)) { - ServiceAttribute serviceAttribute = (ServiceAttribute)type.GetCustomAttributes(typeof(ServiceAttribute)).First(service => ((ServiceAttribute)service).Name == name); + ServiceAttribute serviceAttribute = type.GetCustomAttributes().First(service => service.Name == name); IpcService service = GetServiceInstance(type, context, serviceAttribute.Parameter); diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/BsdContext.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/BsdContext.cs index 7ecd6835d..9d5bb8d01 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/BsdContext.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/BsdContext.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Numerics; +using System.Threading; namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd { @@ -10,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd { private static readonly ConcurrentDictionary _registry = new(); - private readonly object _lock = new(); + private readonly Lock _lock = new(); private readonly List _fds; diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs index 21d48288e..3a40a4ac5 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs @@ -95,7 +95,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd } } - ISocket newBsdSocket = new ManagedSocket(netDomain, (SocketType)type, protocol) + ISocket newBsdSocket = new ManagedSocket(netDomain, (SocketType)type, protocol, context.Device.Configuration.MultiplayerLanInterfaceId) { Blocking = !creationFlags.HasFlag(BsdSocketCreationFlags.NonBlocking), }; diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/ISocket.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/ISocket.cs index fe2f8477f..cf32daf3b 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/ISocket.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/ISocket.cs @@ -16,7 +16,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd ProtocolType ProtocolType { get; } - IntPtr Handle { get; } + nint Handle { get; } LinuxError Receive(out int receiveSize, Span buffer, BsdSocketFlags flags); diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs index 5b9e6811d..05fc91d32 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/EventFileDescriptor.cs @@ -10,6 +10,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl private ulong _value; private readonly EventFdFlags _flags; + // type is not Lock due to Monitor class usage private readonly object _lock = new(); public bool Blocking { get => !_flags.HasFlag(EventFdFlags.NonBlocking); set => throw new NotSupportedException(); } diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocket.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocket.cs index c42b7201b..981fe0a8f 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocket.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocket.cs @@ -1,4 +1,5 @@ using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Proxy; using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types; using System; using System.Collections.Generic; @@ -21,21 +22,21 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl public bool Blocking { get => Socket.Blocking; set => Socket.Blocking = value; } - public IntPtr Handle => Socket.Handle; + public nint Handle => IntPtr.Zero; public IPEndPoint RemoteEndPoint => Socket.RemoteEndPoint as IPEndPoint; public IPEndPoint LocalEndPoint => Socket.LocalEndPoint as IPEndPoint; - public Socket Socket { get; } + public ISocketImpl Socket { get; } - public ManagedSocket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType) + public ManagedSocket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType, string lanInterfaceId) { - Socket = new Socket(addressFamily, socketType, protocolType); + Socket = SocketHelpers.CreateSocket(addressFamily, socketType, protocolType, lanInterfaceId); Refcount = 1; } - private ManagedSocket(Socket socket) + private ManagedSocket(ISocketImpl socket) { Socket = socket; Refcount = 1; @@ -185,6 +186,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl } } + bool hasEmittedBlockingWarning = false; + public LinuxError Receive(out int receiveSize, Span buffer, BsdSocketFlags flags) { LinuxError result; @@ -199,6 +202,12 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl shouldBlockAfterOperation = true; } + if (Blocking && !hasEmittedBlockingWarning) + { + Logger.Warning?.PrintMsg(LogClass.ServiceBsd, "Blocking socket operations are not yet working properly. Expect network errors."); + hasEmittedBlockingWarning = true; + } + receiveSize = Socket.Receive(buffer, ConvertBsdSocketFlags(flags)); result = LinuxError.SUCCESS; @@ -236,6 +245,12 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl shouldBlockAfterOperation = true; } + if (Blocking && !hasEmittedBlockingWarning) + { + Logger.Warning?.PrintMsg(LogClass.ServiceBsd, "Blocking socket operations are not yet working properly. Expect network errors."); + hasEmittedBlockingWarning = true; + } + if (!Socket.IsBound) { receiveSize = -1; @@ -313,7 +328,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl Logger.Warning?.Print(LogClass.ServiceBsd, $"Unsupported GetSockOpt Option: {option} Level: {level}"); optionValue.Clear(); - return LinuxError.SUCCESS; + return LinuxError.EOPNOTSUPP; } byte[] tempOptionValue = new byte[optionValue.Length]; @@ -347,7 +362,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl { Logger.Warning?.Print(LogClass.ServiceBsd, $"Unsupported SetSockOpt Option: {option} Level: {level}"); - return LinuxError.SUCCESS; + return LinuxError.EOPNOTSUPP; } int value = optionValue.Length >= 4 ? MemoryMarshal.Read(optionValue) : MemoryMarshal.Read(optionValue); @@ -493,7 +508,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl try { - int receiveSize = Socket.Receive(ConvertMessagesToBuffer(message), ConvertBsdSocketFlags(flags), out SocketError socketError); + int receiveSize = (Socket as DefaultSocket).BaseSocket.Receive(ConvertMessagesToBuffer(message), ConvertBsdSocketFlags(flags), out SocketError socketError); if (receiveSize > 0) { @@ -531,7 +546,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl try { - int sendSize = Socket.Send(ConvertMessagesToBuffer(message), ConvertBsdSocketFlags(flags), out SocketError socketError); + int sendSize = (Socket as DefaultSocket).BaseSocket.Send(ConvertMessagesToBuffer(message), ConvertBsdSocketFlags(flags), out SocketError socketError); if (sendSize > 0) { diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocketPollManager.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocketPollManager.cs index d0db44086..e870e8aea 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocketPollManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Impl/ManagedSocketPollManager.cs @@ -1,4 +1,5 @@ using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Proxy; using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types; using System.Collections.Generic; using System.Net.Sockets; @@ -26,45 +27,46 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl public LinuxError Poll(List events, int timeoutMilliseconds, out int updatedCount) { - List readEvents = new(); - List writeEvents = new(); - List errorEvents = new(); + List readEvents = new(); + List writeEvents = new(); + List errorEvents = new(); updatedCount = 0; foreach (PollEvent evnt in events) { - ManagedSocket socket = (ManagedSocket)evnt.FileDescriptor; - - bool isValidEvent = evnt.Data.InputEvents == 0; - - errorEvents.Add(socket.Socket); - - if ((evnt.Data.InputEvents & PollEventTypeMask.Input) != 0) + if (evnt.FileDescriptor is ManagedSocket ms) { - readEvents.Add(socket.Socket); + bool isValidEvent = evnt.Data.InputEvents == 0; - isValidEvent = true; - } + errorEvents.Add(ms.Socket); - if ((evnt.Data.InputEvents & PollEventTypeMask.UrgentInput) != 0) - { - readEvents.Add(socket.Socket); + if ((evnt.Data.InputEvents & PollEventTypeMask.Input) != 0) + { + readEvents.Add(ms.Socket); - isValidEvent = true; - } + isValidEvent = true; + } - if ((evnt.Data.InputEvents & PollEventTypeMask.Output) != 0) - { - writeEvents.Add(socket.Socket); + if ((evnt.Data.InputEvents & PollEventTypeMask.UrgentInput) != 0) + { + readEvents.Add(ms.Socket); - isValidEvent = true; - } + isValidEvent = true; + } - if (!isValidEvent) - { - Logger.Warning?.Print(LogClass.ServiceBsd, $"Unsupported Poll input event type: {evnt.Data.InputEvents}"); - return LinuxError.EINVAL; + if ((evnt.Data.InputEvents & PollEventTypeMask.Output) != 0) + { + writeEvents.Add(ms.Socket); + + isValidEvent = true; + } + + if (!isValidEvent) + { + Logger.Warning?.Print(LogClass.ServiceBsd, $"Unsupported Poll input event type: {evnt.Data.InputEvents}"); + return LinuxError.EINVAL; + } } } @@ -72,7 +74,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl { int actualTimeoutMicroseconds = timeoutMilliseconds == -1 ? -1 : timeoutMilliseconds * 1000; - Socket.Select(readEvents, writeEvents, errorEvents, actualTimeoutMicroseconds); + SocketHelpers.Select(readEvents, writeEvents, errorEvents, actualTimeoutMicroseconds); } catch (SocketException exception) { @@ -81,34 +83,37 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl foreach (PollEvent evnt in events) { - Socket socket = ((ManagedSocket)evnt.FileDescriptor).Socket; - - PollEventTypeMask outputEvents = evnt.Data.OutputEvents & ~evnt.Data.InputEvents; - - if (errorEvents.Contains(socket)) + if (evnt.FileDescriptor is ManagedSocket ms) { - outputEvents |= PollEventTypeMask.Error; + ISocketImpl socket = ms.Socket; - if (!socket.Connected || !socket.IsBound) + PollEventTypeMask outputEvents = evnt.Data.OutputEvents & ~evnt.Data.InputEvents; + + if (errorEvents.Contains(ms.Socket)) { - outputEvents |= PollEventTypeMask.Disconnected; - } - } + outputEvents |= PollEventTypeMask.Error; - if (readEvents.Contains(socket)) - { - if ((evnt.Data.InputEvents & PollEventTypeMask.Input) != 0) + if (!socket.Connected || !socket.IsBound) + { + outputEvents |= PollEventTypeMask.Disconnected; + } + } + + if (readEvents.Contains(ms.Socket)) { - outputEvents |= PollEventTypeMask.Input; + if ((evnt.Data.InputEvents & PollEventTypeMask.Input) != 0) + { + outputEvents |= PollEventTypeMask.Input; + } } - } - if (writeEvents.Contains(socket)) - { - outputEvents |= PollEventTypeMask.Output; - } + if (writeEvents.Contains(ms.Socket)) + { + outputEvents |= PollEventTypeMask.Output; + } - evnt.Data.OutputEvents = outputEvents; + evnt.Data.OutputEvents = outputEvents; + } } updatedCount = readEvents.Count + writeEvents.Count + errorEvents.Count; @@ -118,53 +123,55 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl public LinuxError Select(List events, int timeout, out int updatedCount) { - List readEvents = new(); - List writeEvents = new(); - List errorEvents = new(); + List readEvents = new(); + List writeEvents = new(); + List errorEvents = new(); updatedCount = 0; foreach (PollEvent pollEvent in events) { - ManagedSocket socket = (ManagedSocket)pollEvent.FileDescriptor; - - if (pollEvent.Data.InputEvents.HasFlag(PollEventTypeMask.Input)) + if (pollEvent.FileDescriptor is ManagedSocket ms) { - readEvents.Add(socket.Socket); - } + if (pollEvent.Data.InputEvents.HasFlag(PollEventTypeMask.Input)) + { + readEvents.Add(ms.Socket); + } - if (pollEvent.Data.InputEvents.HasFlag(PollEventTypeMask.Output)) - { - writeEvents.Add(socket.Socket); - } + if (pollEvent.Data.InputEvents.HasFlag(PollEventTypeMask.Output)) + { + writeEvents.Add(ms.Socket); + } - if (pollEvent.Data.InputEvents.HasFlag(PollEventTypeMask.Error)) - { - errorEvents.Add(socket.Socket); + if (pollEvent.Data.InputEvents.HasFlag(PollEventTypeMask.Error)) + { + errorEvents.Add(ms.Socket); + } } } - Socket.Select(readEvents, writeEvents, errorEvents, timeout); + SocketHelpers.Select(readEvents, writeEvents, errorEvents, timeout); updatedCount = readEvents.Count + writeEvents.Count + errorEvents.Count; foreach (PollEvent pollEvent in events) { - ManagedSocket socket = (ManagedSocket)pollEvent.FileDescriptor; - - if (readEvents.Contains(socket.Socket)) + if (pollEvent.FileDescriptor is ManagedSocket ms) { - pollEvent.Data.OutputEvents |= PollEventTypeMask.Input; - } + if (readEvents.Contains(ms.Socket)) + { + pollEvent.Data.OutputEvents |= PollEventTypeMask.Input; + } - if (writeEvents.Contains(socket.Socket)) - { - pollEvent.Data.OutputEvents |= PollEventTypeMask.Output; - } + if (writeEvents.Contains(ms.Socket)) + { + pollEvent.Data.OutputEvents |= PollEventTypeMask.Output; + } - if (errorEvents.Contains(socket.Socket)) - { - pollEvent.Data.OutputEvents |= PollEventTypeMask.Error; + if (errorEvents.Contains(ms.Socket)) + { + pollEvent.Data.OutputEvents |= PollEventTypeMask.Error; + } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Proxy/DefaultSocket.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Proxy/DefaultSocket.cs new file mode 100644 index 000000000..f1040e799 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Proxy/DefaultSocket.cs @@ -0,0 +1,178 @@ +using Ryujinx.Common.Utilities; +using System; +using System.Net; +using System.Net.NetworkInformation; +using System.Net.Sockets; + +namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Proxy +{ + class DefaultSocket : ISocketImpl + { + public Socket BaseSocket { get; } + + public EndPoint RemoteEndPoint => BaseSocket.RemoteEndPoint; + + public EndPoint LocalEndPoint => BaseSocket.LocalEndPoint; + + public bool Connected => BaseSocket.Connected; + + public bool IsBound => BaseSocket.IsBound; + + public AddressFamily AddressFamily => BaseSocket.AddressFamily; + + public SocketType SocketType => BaseSocket.SocketType; + + public ProtocolType ProtocolType => BaseSocket.ProtocolType; + + public bool Blocking { get => BaseSocket.Blocking; set => BaseSocket.Blocking = value; } + + public int Available => BaseSocket.Available; + + private readonly string _lanInterfaceId; + + public DefaultSocket(Socket baseSocket, string lanInterfaceId) + { + _lanInterfaceId = lanInterfaceId; + + BaseSocket = baseSocket; + } + + public DefaultSocket(AddressFamily domain, SocketType type, ProtocolType protocol, string lanInterfaceId) + { + _lanInterfaceId = lanInterfaceId; + + BaseSocket = new Socket(domain, type, protocol); + } + + private void EnsureNetworkInterfaceBound() + { + if (_lanInterfaceId != "0" && !BaseSocket.IsBound) + { + (_, UnicastIPAddressInformation ipInfo) = NetworkHelpers.GetLocalInterface(_lanInterfaceId); + + BaseSocket.Bind(new IPEndPoint(ipInfo.Address, 0)); + } + } + + public ISocketImpl Accept() + { + return new DefaultSocket(BaseSocket.Accept(), _lanInterfaceId); + } + + public void Bind(EndPoint localEP) + { + // NOTE: The guest is able to receive on 0.0.0.0 without it being limited to the chosen network interface. + // This is because it must get loopback traffic as well. This could allow other network traffic to leak in. + + BaseSocket.Bind(localEP); + } + + public void Close() + { + BaseSocket.Close(); + } + + public void Connect(EndPoint remoteEP) + { + EnsureNetworkInterfaceBound(); + + BaseSocket.Connect(remoteEP); + } + + public void Disconnect(bool reuseSocket) + { + BaseSocket.Disconnect(reuseSocket); + } + + public void GetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, byte[] optionValue) + { + BaseSocket.GetSocketOption(optionLevel, optionName, optionValue); + } + + public void Listen(int backlog) + { + BaseSocket.Listen(backlog); + } + + public int Receive(Span buffer) + { + EnsureNetworkInterfaceBound(); + + return BaseSocket.Receive(buffer); + } + + public int Receive(Span buffer, SocketFlags flags) + { + EnsureNetworkInterfaceBound(); + + return BaseSocket.Receive(buffer, flags); + } + + public int Receive(Span buffer, SocketFlags flags, out SocketError socketError) + { + EnsureNetworkInterfaceBound(); + + return BaseSocket.Receive(buffer, flags, out socketError); + } + + public int ReceiveFrom(Span buffer, SocketFlags flags, ref EndPoint remoteEP) + { + EnsureNetworkInterfaceBound(); + + return BaseSocket.ReceiveFrom(buffer, flags, ref remoteEP); + } + + public int Send(ReadOnlySpan buffer) + { + EnsureNetworkInterfaceBound(); + + return BaseSocket.Send(buffer); + } + + public int Send(ReadOnlySpan buffer, SocketFlags flags) + { + EnsureNetworkInterfaceBound(); + + return BaseSocket.Send(buffer, flags); + } + + public int Send(ReadOnlySpan buffer, SocketFlags flags, out SocketError socketError) + { + EnsureNetworkInterfaceBound(); + + return BaseSocket.Send(buffer, flags, out socketError); + } + + public int SendTo(ReadOnlySpan buffer, SocketFlags flags, EndPoint remoteEP) + { + EnsureNetworkInterfaceBound(); + + return BaseSocket.SendTo(buffer, flags, remoteEP); + } + + public bool Poll(int microSeconds, SelectMode mode) + { + return BaseSocket.Poll(microSeconds, mode); + } + + public void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, int optionValue) + { + BaseSocket.SetSocketOption(optionLevel, optionName, optionValue); + } + + public void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, object optionValue) + { + BaseSocket.SetSocketOption(optionLevel, optionName, optionValue); + } + + public void Shutdown(SocketShutdown how) + { + BaseSocket.Shutdown(how); + } + + public void Dispose() + { + BaseSocket.Dispose(); + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Proxy/ISocket.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Proxy/ISocket.cs new file mode 100644 index 000000000..b7055f08b --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Proxy/ISocket.cs @@ -0,0 +1,47 @@ +using System; +using System.Net; +using System.Net.Sockets; + +namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Proxy +{ + interface ISocketImpl : IDisposable + { + EndPoint RemoteEndPoint { get; } + EndPoint LocalEndPoint { get; } + bool Connected { get; } + bool IsBound { get; } + + AddressFamily AddressFamily { get; } + SocketType SocketType { get; } + ProtocolType ProtocolType { get; } + + bool Blocking { get; set; } + int Available { get; } + + int Receive(Span buffer); + int Receive(Span buffer, SocketFlags flags); + int Receive(Span buffer, SocketFlags flags, out SocketError socketError); + int ReceiveFrom(Span buffer, SocketFlags flags, ref EndPoint remoteEP); + + int Send(ReadOnlySpan buffer); + int Send(ReadOnlySpan buffer, SocketFlags flags); + int Send(ReadOnlySpan buffer, SocketFlags flags, out SocketError socketError); + int SendTo(ReadOnlySpan buffer, SocketFlags flags, EndPoint remoteEP); + + bool Poll(int microSeconds, SelectMode mode); + + ISocketImpl Accept(); + + void Bind(EndPoint localEP); + void Connect(EndPoint remoteEP); + void Listen(int backlog); + + void GetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, byte[] optionValue); + void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, int optionValue); + void SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, object optionValue); + + void Shutdown(SocketShutdown how); + void Disconnect(bool reuseSocket); + void Close(); + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Proxy/SocketHelpers.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Proxy/SocketHelpers.cs new file mode 100644 index 000000000..485a7f86b --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Bsd/Proxy/SocketHelpers.cs @@ -0,0 +1,74 @@ +using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Sockets; + +namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Proxy +{ + static class SocketHelpers + { + private static LdnProxy _proxy; + + public static void Select(List readEvents, List writeEvents, List errorEvents, int timeout) + { + var readDefault = readEvents.Select(x => (x as DefaultSocket)?.BaseSocket).Where(x => x != null).ToList(); + var writeDefault = writeEvents.Select(x => (x as DefaultSocket)?.BaseSocket).Where(x => x != null).ToList(); + var errorDefault = errorEvents.Select(x => (x as DefaultSocket)?.BaseSocket).Where(x => x != null).ToList(); + + if (readDefault.Count != 0 || writeDefault.Count != 0 || errorDefault.Count != 0) + { + Socket.Select(readDefault, writeDefault, errorDefault, timeout); + } + + void FilterSockets(List removeFrom, List selectedSockets, Func ldnCheck) + { + removeFrom.RemoveAll(socket => + { + switch (socket) + { + case DefaultSocket dsocket: + return !selectedSockets.Contains(dsocket.BaseSocket); + case LdnProxySocket psocket: + return !ldnCheck(psocket); + default: + throw new NotImplementedException(); + } + }); + }; + + FilterSockets(readEvents, readDefault, (socket) => socket.Readable); + FilterSockets(writeEvents, writeDefault, (socket) => socket.Writable); + FilterSockets(errorEvents, errorDefault, (socket) => socket.Error); + } + + public static void RegisterProxy(LdnProxy proxy) + { + if (_proxy != null) + { + UnregisterProxy(); + } + + _proxy = proxy; + } + + public static void UnregisterProxy() + { + _proxy?.Dispose(); + _proxy = null; + } + + public static ISocketImpl CreateSocket(AddressFamily domain, SocketType type, ProtocolType protocol, string lanInterfaceId) + { + if (_proxy != null) + { + if (_proxy.Supported(domain, type, protocol)) + { + return new LdnProxySocket(domain, type, protocol, _proxy); + } + } + + return new DefaultSocket(domain, type, protocol, lanInterfaceId); + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Manager/FqdnResolver.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Manager/FqdnResolver.cs index 2ec0f744e..3ff48b883 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Manager/FqdnResolver.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Manager/FqdnResolver.cs @@ -36,7 +36,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd.Manager // TODO: Load Environment from the savedata. address = address.Replace("%", IManager.NsdSettings.Environment); - resolvedAddress = ""; + resolvedAddress = string.Empty; if (IManager.NsdSettings == null) { diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs index 39af90383..5b2de13f0 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/IResolver.cs @@ -292,7 +292,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres { string host = MemoryHelper.ReadAsciiString(context.Memory, inputBufferPosition, (int)inputBufferSize); - if (!context.Device.Configuration.EnableInternetAccess) + if (host != "localhost" && !context.Device.Configuration.EnableInternetAccess) { Logger.Info?.Print(LogClass.ServiceSfdnsres, $"Guest network access disabled, DNS Blocked: {host}"); diff --git a/src/Ryujinx.HLE/HOS/Services/Ssl/BuiltInCertificateManager.cs b/src/Ryujinx.HLE/HOS/Services/Ssl/BuiltInCertificateManager.cs index 5d2e06a4f..622b62c16 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ssl/BuiltInCertificateManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ssl/BuiltInCertificateManager.cs @@ -16,6 +16,7 @@ using System.Collections.Generic; using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Threading; namespace Ryujinx.HLE.HOS.Services.Ssl { @@ -23,7 +24,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl { private const long CertStoreTitleId = 0x0100000000000800; - private const string CertStoreTitleMissingErrorMessage = "CertStore system title not found! SSL CA retrieving will not work, provide the system archive to fix this error. (See https://github.com/Ryujinx/Ryujinx/wiki/Ryujinx-Setup-&-Configuration-Guide#initial-setup-continued---installation-of-firmware for more information)"; + private const string CertStoreTitleMissingErrorMessage = "CertStore system title not found! SSL CA retrieving will not work, provide the system archive to fix this error."; private static BuiltInCertificateManager _instance; @@ -43,7 +44,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl private bool _initialized; private Dictionary _certificates; - private readonly object _lock = new(); + private readonly Lock _lock = new(); private struct CertStoreFileHeader { diff --git a/src/Ryujinx.HLE/HOS/Services/Ssl/SslService/SslManagedSocketConnection.cs b/src/Ryujinx.HLE/HOS/Services/Ssl/SslService/SslManagedSocketConnection.cs index 8cc761baf..dc33dd6a5 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ssl/SslService/SslManagedSocketConnection.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ssl/SslService/SslManagedSocketConnection.cs @@ -1,5 +1,6 @@ using Ryujinx.HLE.HOS.Services.Sockets.Bsd; using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl; +using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Proxy; using Ryujinx.HLE.HOS.Services.Ssl.Types; using System; using System.IO; @@ -116,7 +117,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService public ResultCode Handshake(string hostName) { StartSslOperation(); - _stream = new SslStream(new NetworkStream(((ManagedSocket)Socket).Socket, false), false, null, null); + _stream = new SslStream(new NetworkStream(((DefaultSocket)((ManagedSocket)Socket).Socket).BaseSocket, false), false, null, null); hostName = RetrieveHostName(hostName); _stream.AuthenticateAsClient(hostName, null, TranslateSslVersion(_sslVersion), false); EndSslOperation(); diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/ConsumerBase.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/ConsumerBase.cs index f8ee84842..017365c6f 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/ConsumerBase.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/ConsumerBase.cs @@ -1,5 +1,6 @@ using Ryujinx.HLE.HOS.Services.SurfaceFlinger.Types; using System; +using System.Threading; namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { @@ -23,7 +24,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger protected BufferQueueConsumer Consumer; - protected readonly object Lock = new(); + protected readonly Lock Lock = new(); private readonly IConsumerListener _listener; diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/HOSBinderDriverServer.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/HOSBinderDriverServer.cs index bc7bffcb6..5151930a5 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/HOSBinderDriverServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/HOSBinderDriverServer.cs @@ -2,6 +2,7 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Kernel.Threading; using System; using System.Collections.Generic; +using System.Threading; namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { @@ -11,7 +12,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger private static int _lastBinderId = 0; - private static readonly object _lock = new(); + private static readonly Lock _lock = new(); public static int RegisterBinderObject(IBinder binder) { diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Parcel.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Parcel.cs index 1df280dce..25c89baec 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Parcel.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Parcel.cs @@ -63,7 +63,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger if (size < 0) { - return ""; + return string.Empty; } ReadOnlySpan data = ReadInPlace((size + 1) * 2); diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs index 4c17e7aed..23bf8bcfc 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs @@ -10,13 +10,12 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; +using VSyncMode = Ryujinx.Common.Configuration.VSyncMode; namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { class SurfaceFlinger : IConsumerListener, IDisposable { - private const int TargetFps = 60; - private readonly Switch _device; private readonly Dictionary _layers; @@ -32,10 +31,13 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger private readonly long _spinTicks; private readonly long _1msTicks; + private VSyncMode _vSyncMode; + private long _targetVSyncInterval; + private int _swapInterval; private int _swapIntervalDelay; - private readonly object _lock = new(); + private readonly Lock _lock = new(); public long RenderLayerId { get; private set; } @@ -88,7 +90,8 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger } else { - _ticksPerFrame = Stopwatch.Frequency / TargetFps; + _ticksPerFrame = Stopwatch.Frequency / _device.TargetVSyncInterval; + _targetVSyncInterval = _device.TargetVSyncInterval; } } @@ -370,15 +373,20 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger if (acquireStatus == Status.Success) { - // If device vsync is disabled, reflect the change. - if (!_device.EnableDeviceVsync) + if (_device.VSyncMode == VSyncMode.Unbounded) { if (_swapInterval != 0) { UpdateSwapInterval(0); + _vSyncMode = _device.VSyncMode; } } - else if (item.SwapInterval != _swapInterval) + else if (_device.VSyncMode != _vSyncMode) + { + UpdateSwapInterval(_device.VSyncMode == VSyncMode.Unbounded ? 0 : item.SwapInterval); + _vSyncMode = _device.VSyncMode; + } + else if (item.SwapInterval != _swapInterval || _device.TargetVSyncInterval != _targetVSyncInterval) { UpdateSwapInterval(item.SwapInterval); } diff --git a/src/Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs b/src/Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs index 95cf2a899..98094d35c 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs @@ -12,7 +12,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock public SteadyClockCore() { - _clockSourceId = UInt128Utils.CreateRandom(); + _clockSourceId = Random.Shared.NextUInt128(); _isRtcResetDetected = false; _isInitialized = false; } diff --git a/src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/SteadyClockTimePoint.cs b/src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/SteadyClockTimePoint.cs index e4878483d..bce15c672 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/SteadyClockTimePoint.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/Clock/Types/SteadyClockTimePoint.cs @@ -36,7 +36,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock return new SteadyClockTimePoint { TimePoint = 0, - ClockSourceId = UInt128Utils.CreateRandom(), + ClockSourceId = Random.Shared.NextUInt128(), }; } } diff --git a/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs b/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs index abf3cd7d6..222698a7f 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs @@ -23,7 +23,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone { private const long TimeZoneBinaryTitleId = 0x010000000000080E; - private const string TimeZoneSystemTitleMissingErrorMessage = "TimeZoneBinary system title not found! TimeZone conversions will not work, provide the system archive to fix this error. (See https://github.com/Ryujinx/Ryujinx/wiki/Ryujinx-Setup-&-Configuration-Guide#initial-setup-continued---installation-of-firmware for more information)"; + private const string TimeZoneSystemTitleMissingErrorMessage = "TimeZoneBinary system title not found! TimeZone conversions will not work, provide the system archive to fix this error."; private VirtualFileSystem _virtualFileSystem; private IntegrityCheckLevel _fsIntegrityCheckLevel; diff --git a/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneManager.cs b/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneManager.cs index 3b57b1805..155733d2e 100644 --- a/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneManager.cs @@ -2,6 +2,7 @@ using Ryujinx.Common.Memory; using Ryujinx.HLE.HOS.Services.Time.Clock; using System; using System.IO; +using System.Threading; namespace Ryujinx.HLE.HOS.Services.Time.TimeZone { @@ -13,7 +14,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone private UInt128 _timeZoneRuleVersion; private uint _totalLocationNameCount; private SteadyClockTimePoint _timeZoneUpdateTimePoint; - private readonly object _lock = new(); + private readonly Lock _lock = new(); public TimeZoneManager() { diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs index a2b1fb524..edb441a0a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs @@ -166,7 +166,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService private ResultCode OpenDisplayImpl(ServiceCtx context, string name) { - if (name == "") + if (name == string.Empty) { return ResultCode.InvalidValue; } diff --git a/src/Ryujinx.HLE/Loaders/Executables/NsoExecutable.cs b/src/Ryujinx.HLE/Loaders/Executables/NsoExecutable.cs index 83ad5d7e8..1caedb51e 100644 --- a/src/Ryujinx.HLE/Loaders/Executables/NsoExecutable.cs +++ b/src/Ryujinx.HLE/Loaders/Executables/NsoExecutable.cs @@ -102,7 +102,7 @@ namespace Ryujinx.HLE.Loaders.Executables Match fsSdkMatch = FsSdkRegex().Match(rawTextBuffer); if (fsSdkMatch.Success) { - stringBuilder.AppendLine($" FS SDK Version: {fsSdkMatch.Value.Replace("sdk_version: ", "")}"); + stringBuilder.AppendLine($" FS SDK Version: {fsSdkMatch.Value.Replace("sdk_version: ", string.Empty)}"); } MatchCollection sdkMwMatches = SdkMwRegex().Matches(rawTextBuffer); diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs index 3904d660e..cd215781f 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/FileSystemExtensions.cs @@ -89,7 +89,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions Logger.Warning?.Print(LogClass.Ptc, "Detected unsupported ExeFs modifications. PTC disabled."); } - string programName = ""; + string programName = string.Empty; if (!isHomebrew && programId > 0x010000000000FFFF) { diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs index 6c2a19894..e3ae9bf5f 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/LocalFileSystemExtensions.cs @@ -15,7 +15,7 @@ namespace Ryujinx.HLE.Loaders.Processes var nacpData = new BlitStruct(1); ulong programId = metaLoader.GetProgramId(); - device.Configuration.VirtualFileSystem.ModLoader.CollectMods(new[] { programId }); + device.Configuration.VirtualFileSystem.ModLoader.CollectMods([programId]); if (programId != 0) { diff --git a/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs b/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs index 2928ac7fe..361a9159e 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/Extensions/NcaExtensions.cs @@ -26,7 +26,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions { private static readonly TitleUpdateMetadataJsonSerializerContext _applicationSerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - public static ProcessResult Load(this Nca nca, Switch device, Nca patchNca, Nca controlNca) + public static ProcessResult Load(this Nca nca, Switch device, Nca patchNca, Nca controlNca, BlitStruct? customNacpData = null) { // Extract RomFs and ExeFs from NCA. IStorage romFs = nca.GetRomFs(device, patchNca); @@ -55,6 +55,10 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions { nacpData = controlNca.GetNacp(device); } + else if (customNacpData != null) // if the Application doesn't provide a nacp file but the Application provides an override, use the provided nacp override + { + nacpData = (BlitStruct)customNacpData; + } /* TODO: Rework this since it's wrong and doesn't work as it takes the DisplayVersion from a "potential" non-existent update. diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs index 12d9c8bd9..fe8360f04 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoader.cs @@ -98,12 +98,12 @@ namespace Ryujinx.HLE.Loaders.Processes return false; } - public bool LoadNca(string path) + public bool LoadNca(string path, BlitStruct? customNacpData = null) { FileStream file = new(path, FileMode.Open, FileAccess.Read); Nca nca = new(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage(false)); - ProcessResult processResult = nca.Load(_device, null, null); + ProcessResult processResult = nca.Load(_device, null, null, customNacpData); if (processResult.ProcessId != 0 && _processesByPid.TryAdd(processResult.ProcessId, processResult)) { @@ -145,7 +145,7 @@ namespace Ryujinx.HLE.Loaders.Processes IFileSystem dummyExeFs = null; Stream romfsStream = null; - string programName = ""; + string programName = string.Empty; ulong programId = 0000000000000000; // Load executable. diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs index cf4eb416e..e4286ae90 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessLoaderHelper.cs @@ -231,7 +231,7 @@ namespace Ryujinx.HLE.Loaders.Processes ulong programId, byte programIndex, byte[] arguments = null, - params IExecutable[] executables) + params ReadOnlySpan executables) { context.Device.System.ServiceTable.WaitServicesReady(); @@ -251,12 +251,17 @@ namespace Ryujinx.HLE.Loaders.Processes ulong codeStart = ((meta.Flags & 1) != 0 ? 0x8000000UL : 0x200000UL) + CodeStartOffset; uint codeSize = 0; - var buildIds = executables.Select(e => (e switch + var buildIds = new string[executables.Length]; + + for (int i = 0; i < executables.Length; i++) { - NsoExecutable nso => Convert.ToHexString(nso.BuildId.ItemsRo.ToArray()), - NroExecutable nro => Convert.ToHexString(nro.Header.BuildId), - _ => "", - }).ToUpper()); + buildIds[i] = (executables[i] switch + { + NsoExecutable nso => Convert.ToHexString(nso.BuildId.ItemsRo.ToArray()), + NroExecutable nro => Convert.ToHexString(nro.Header.BuildId), + _ => string.Empty + }).ToUpper(); + } ulong[] nsoBase = new ulong[executables.Length]; diff --git a/src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs b/src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs index 1804d045c..3a7042670 100644 --- a/src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs +++ b/src/Ryujinx.HLE/Loaders/Processes/ProcessResult.cs @@ -84,10 +84,19 @@ namespace Ryujinx.HLE.Loaders.Processes return false; } - // TODO: LibHac npdm currently doesn't support version field. - string version = ProgramId > 0x0100000000007FFF ? DisplayVersion : device.System.ContentManager.GetCurrentFirmwareVersion()?.VersionString ?? "?"; + bool isFirmware = ProgramId is >= 0x0100000000000819 and <= 0x010000000000081C; + bool isFirmwareApplication = ProgramId <= 0x0100000000007FFF; - Logger.Info?.Print(LogClass.Loader, $"Application Loaded: {Name} v{version} [{ProgramIdText}] [{(Is64Bit ? "64-bit" : "32-bit")}]"); + string name = !isFirmware + ? (isFirmwareApplication ? "Firmware Application " : "") + (!string.IsNullOrWhiteSpace(Name) ? Name : "") + : "Firmware"; + + // TODO: LibHac npdm currently doesn't support version field. + string version = !isFirmware + ? (!string.IsNullOrWhiteSpace(DisplayVersion) ? DisplayVersion : "") + : device.System.ContentManager.GetCurrentFirmwareVersion()?.VersionString ?? "?"; + + Logger.Info?.Print(LogClass.Loader, $"Application Loaded: {name} v{version} [{ProgramIdText}] [{(Is64Bit ? "64-bit" : "32-bit")}]"); return true; } diff --git a/src/Ryujinx.HLE/MemoryConfiguration.cs b/src/Ryujinx.HLE/MemoryConfiguration.cs index 45e8927db..21ecd737f 100644 --- a/src/Ryujinx.HLE/MemoryConfiguration.cs +++ b/src/Ryujinx.HLE/MemoryConfiguration.cs @@ -6,11 +6,12 @@ namespace Ryujinx.HLE public enum MemoryConfiguration { MemoryConfiguration4GiB = 0, - MemoryConfiguration4GiBAppletDev = 1, - MemoryConfiguration4GiBSystemDev = 2, - MemoryConfiguration6GiB = 3, - MemoryConfiguration6GiBAppletDev = 4, - MemoryConfiguration8GiB = 5, + MemoryConfiguration6GiB = 1, + MemoryConfiguration8GiB = 2, + MemoryConfiguration12GiB = 3, + MemoryConfiguration4GiBAppletDev = 4, + MemoryConfiguration4GiBSystemDev = 5, + MemoryConfiguration6GiBAppletDev = 6, } static class MemoryConfigurationExtensions @@ -28,6 +29,7 @@ namespace Ryujinx.HLE MemoryConfiguration.MemoryConfiguration6GiB => MemoryArrange.MemoryArrange6GiB, MemoryConfiguration.MemoryConfiguration6GiBAppletDev => MemoryArrange.MemoryArrange6GiBAppletDev, MemoryConfiguration.MemoryConfiguration8GiB => MemoryArrange.MemoryArrange8GiB, + MemoryConfiguration.MemoryConfiguration12GiB => MemoryArrange.MemoryArrange12GiB, _ => throw new AggregateException($"Invalid memory configuration \"{configuration}\"."), }; } @@ -42,6 +44,7 @@ namespace Ryujinx.HLE MemoryConfiguration.MemoryConfiguration6GiB or MemoryConfiguration.MemoryConfiguration6GiBAppletDev => MemorySize.MemorySize6GiB, MemoryConfiguration.MemoryConfiguration8GiB => MemorySize.MemorySize8GiB, + MemoryConfiguration.MemoryConfiguration12GiB => MemorySize.MemorySize12GiB, _ => throw new AggregateException($"Invalid memory configuration \"{configuration}\"."), }; } @@ -56,6 +59,7 @@ namespace Ryujinx.HLE MemoryConfiguration.MemoryConfiguration6GiB or MemoryConfiguration.MemoryConfiguration6GiBAppletDev => 6 * GiB, MemoryConfiguration.MemoryConfiguration8GiB => 8 * GiB, + MemoryConfiguration.MemoryConfiguration12GiB => 12 * GiB, _ => throw new AggregateException($"Invalid memory configuration \"{configuration}\"."), }; } diff --git a/src/Ryujinx.HLE/PerformanceStatistics.cs b/src/Ryujinx.HLE/PerformanceStatistics.cs index 3767a7fb4..890bce8bc 100644 --- a/src/Ryujinx.HLE/PerformanceStatistics.cs +++ b/src/Ryujinx.HLE/PerformanceStatistics.cs @@ -1,4 +1,5 @@ using Ryujinx.Common; +using System.Threading; using System.Timers; namespace Ryujinx.HLE @@ -20,12 +21,12 @@ namespace Ryujinx.HLE private readonly long[] _framesRendered; private readonly double[] _percentTime; - private readonly object[] _frameLock; - private readonly object[] _percentLock; + private readonly Lock[] _frameLock = [new()]; + private readonly Lock[] _percentLock = [new()]; private readonly double _ticksToSeconds; - private readonly Timer _resetTimer; + private readonly System.Timers.Timer _resetTimer; public PerformanceStatistics() { @@ -41,10 +42,7 @@ namespace Ryujinx.HLE _framesRendered = new long[1]; _percentTime = new double[1]; - _frameLock = new[] { new object() }; - _percentLock = new[] { new object() }; - - _resetTimer = new Timer(750); + _resetTimer = new(750); _resetTimer.Elapsed += ResetTimerElapsed; _resetTimer.AutoReset = true; diff --git a/src/Ryujinx.HLE/Ryujinx.HLE.csproj b/src/Ryujinx.HLE/Ryujinx.HLE.csproj index a7bb3cd7f..d42ecf8b8 100644 --- a/src/Ryujinx.HLE/Ryujinx.HLE.csproj +++ b/src/Ryujinx.HLE/Ryujinx.HLE.csproj @@ -1,8 +1,8 @@ - net8.0 true + $(DefaultItemExcludes);._* @@ -29,6 +29,7 @@ + diff --git a/src/Ryujinx.HLE/StructHelpers.cs b/src/Ryujinx.HLE/StructHelpers.cs new file mode 100644 index 000000000..6e6af8cea --- /dev/null +++ b/src/Ryujinx.HLE/StructHelpers.cs @@ -0,0 +1,37 @@ +using LibHac.Common; +using LibHac.Ns; +using System; +using System.Text; + +namespace Ryujinx.HLE +{ + public static class StructHelpers + { + public static BlitStruct CreateCustomNacpData(string name, string version) + { + // https://switchbrew.org/wiki/NACP + const int OffsetOfDisplayVersion = 0x3060; + + // https://switchbrew.org/wiki/NACP#ApplicationTitle + const int TotalApplicationTitles = 0x10; + const int SizeOfApplicationTitle = 0x300; + const int OffsetOfApplicationPublisherStrings = 0x200; + + + var nacpData = new BlitStruct(1); + + // name and publisher buffer + // repeat once for each locale (the ApplicationControlProperty has 16 locales) + for (int i = 0; i < TotalApplicationTitles; i++) + { + Encoding.ASCII.GetBytes(name).AsSpan().CopyTo(nacpData.ByteSpan[(i * SizeOfApplicationTitle)..]); + "Ryujinx"u8.CopyTo(nacpData.ByteSpan[(i * SizeOfApplicationTitle + OffsetOfApplicationPublisherStrings)..]); + } + + // version buffer + Encoding.ASCII.GetBytes(version).AsSpan().CopyTo(nacpData.ByteSpan[OffsetOfDisplayVersion..]); + + return nacpData; + } + } +} diff --git a/src/Ryujinx.HLE/Switch.cs b/src/Ryujinx.HLE/Switch.cs index 9dfc69892..d0afdf173 100644 --- a/src/Ryujinx.HLE/Switch.cs +++ b/src/Ryujinx.HLE/Switch.cs @@ -1,3 +1,5 @@ +using LibHac.Common; +using LibHac.Ns; using Ryujinx.Audio.Backends.CompatLayer; using Ryujinx.Audio.Integration; using Ryujinx.Common.Configuration; @@ -27,7 +29,11 @@ namespace Ryujinx.HLE public TamperMachine TamperMachine { get; } public IHostUIHandler UIHandler { get; } - public bool EnableDeviceVsync { get; set; } = true; + public VSyncMode VSyncMode { get; set; } = VSyncMode.Switch; + public bool CustomVSyncIntervalEnabled { get; set; } = false; + public int CustomVSyncInterval { get; set; } + + public long TargetVSyncInterval { get; set; } = 60; public bool IsFrameAvailable => Gpu.Window.IsFrameAvailable; @@ -59,45 +65,17 @@ namespace Ryujinx.HLE System.State.SetLanguage(Configuration.SystemLanguage); System.State.SetRegion(Configuration.Region); - EnableDeviceVsync = Configuration.EnableVsync; + VSyncMode = Configuration.VSyncMode; + CustomVSyncInterval = Configuration.CustomVSyncInterval; System.State.DockedMode = Configuration.EnableDockedMode; System.PerformanceState.PerformanceMode = System.State.DockedMode ? PerformanceMode.Boost : PerformanceMode.Default; System.EnablePtc = Configuration.EnablePtc; System.FsIntegrityCheckLevel = Configuration.FsIntegrityCheckLevel; System.GlobalAccessLogMode = Configuration.FsGlobalAccessLogMode; + UpdateVSyncInterval(); #pragma warning restore IDE0055 } - public bool LoadCart(string exeFsDir, string romFsFile = null) - { - return Processes.LoadUnpackedNca(exeFsDir, romFsFile); - } - - public bool LoadXci(string xciFile, ulong applicationId = 0) - { - return Processes.LoadXci(xciFile, applicationId); - } - - public bool LoadNca(string ncaFile) - { - return Processes.LoadNca(ncaFile); - } - - public bool LoadNsp(string nspFile, ulong applicationId = 0) - { - return Processes.LoadNsp(nspFile, applicationId); - } - - public bool LoadProgram(string fileName) - { - return Processes.LoadNxo(fileName); - } - - public bool WaitFifo() - { - return Gpu.GPFifo.WaitForCommands(); - } - public void ProcessFrame() { Gpu.ProcessShaderCacheQueue(); @@ -105,40 +83,50 @@ namespace Ryujinx.HLE Gpu.GPFifo.DispatchCalls(); } - public bool ConsumeFrameAvailable() + public void IncrementCustomVSyncInterval() { - return Gpu.Window.ConsumeFrameAvailable(); + CustomVSyncInterval += 1; + UpdateVSyncInterval(); } - public void PresentFrame(Action swapBuffersCallback) + public void DecrementCustomVSyncInterval() { - Gpu.Window.Present(swapBuffersCallback); + CustomVSyncInterval -= 1; + UpdateVSyncInterval(); } - public void SetVolume(float volume) + public void UpdateVSyncInterval() { - AudioDeviceDriver.Volume = Math.Clamp(volume, 0f, 1f); + switch (VSyncMode) + { + case VSyncMode.Custom: + TargetVSyncInterval = CustomVSyncInterval; + break; + case VSyncMode.Switch: + TargetVSyncInterval = 60; + break; + case VSyncMode.Unbounded: + TargetVSyncInterval = 1; + break; + } } - public float GetVolume() - { - return AudioDeviceDriver.Volume; - } + public bool LoadCart(string exeFsDir, string romFsFile = null) => Processes.LoadUnpackedNca(exeFsDir, romFsFile); + public bool LoadXci(string xciFile, ulong applicationId = 0) => Processes.LoadXci(xciFile, applicationId); + public bool LoadNca(string ncaFile, BlitStruct? customNacpData = null) => Processes.LoadNca(ncaFile, customNacpData); + public bool LoadNsp(string nspFile, ulong applicationId = 0) => Processes.LoadNsp(nspFile, applicationId); + public bool LoadProgram(string fileName) => Processes.LoadNxo(fileName); - public void EnableCheats() - { - ModLoader.EnableCheats(Processes.ActiveApplication.ProgramId, TamperMachine); - } + public void SetVolume(float volume) => AudioDeviceDriver.Volume = Math.Clamp(volume, 0f, 1f); + public float GetVolume() => AudioDeviceDriver.Volume; + public bool IsAudioMuted() => AudioDeviceDriver.Volume == 0; - public bool IsAudioMuted() - { - return AudioDeviceDriver.Volume == 0; - } + public void EnableCheats() => ModLoader.EnableCheats(Processes.ActiveApplication.ProgramId, TamperMachine); - public void DisposeGpu() - { - Gpu.Dispose(); - } + public bool WaitFifo() => Gpu.GPFifo.WaitForCommands(); + public bool ConsumeFrameAvailable() => Gpu.Window.ConsumeFrameAvailable(); + public void PresentFrame(Action swapBuffersCallback) => Gpu.Window.Present(swapBuffersCallback); + public void DisposeGpu() => Gpu.Dispose(); public void Dispose() { diff --git a/src/Ryujinx.HLE/UI/IHostUIHandler.cs b/src/Ryujinx.HLE/UI/IHostUIHandler.cs index 3b3a430ee..88af83735 100644 --- a/src/Ryujinx.HLE/UI/IHostUIHandler.cs +++ b/src/Ryujinx.HLE/UI/IHostUIHandler.cs @@ -25,7 +25,19 @@ namespace Ryujinx.HLE.UI bool DisplayMessageDialog(ControllerAppletUIArgs args); /// - /// Tell the UI that we need to transisition to another program. + /// Displays an Input Dialog box to the user so they can enter the Amiibo's new name + /// + /// Text that the user entered. Set to `null` on internal errors + /// True when OK is pressed, False otherwise. Also returns True on internal errors + bool DisplayCabinetDialog(out string userText); + + /// + /// Displays a Message Dialog box to the user to notify them to scan the Amiibo. + /// + void DisplayCabinetMessageDialog(); + + /// + /// Tell the UI that we need to transition to another program. /// /// The device instance. /// The program kind. diff --git a/src/Ryujinx.Headless.SDL2/HeadlessDynamicTextInputHandler.cs b/src/Ryujinx.Headless.SDL2/HeadlessDynamicTextInputHandler.cs index 503874ff1..40eb5ba98 100644 --- a/src/Ryujinx.Headless.SDL2/HeadlessDynamicTextInputHandler.cs +++ b/src/Ryujinx.Headless.SDL2/HeadlessDynamicTextInputHandler.cs @@ -17,10 +17,7 @@ namespace Ryujinx.Headless.SDL2 public bool TextProcessingEnabled { - get - { - return Volatile.Read(ref _canProcessInput); - } + get => Volatile.Read(ref _canProcessInput); set { diff --git a/src/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs b/src/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs index 7ea6e1481..8c4854a11 100644 --- a/src/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs +++ b/src/Ryujinx.Headless.SDL2/OpenGL/OpenGLWindow.cs @@ -40,7 +40,7 @@ namespace Ryujinx.Headless.SDL2.OpenGL private class OpenToolkitBindingsContext : IBindingsContext { - public IntPtr GetProcAddress(string procName) + public nint GetProcAddress(string procName) { return SDL_GL_GetProcAddress(procName); } @@ -48,11 +48,11 @@ namespace Ryujinx.Headless.SDL2.OpenGL private class SDL2OpenGLContext : IOpenGLContext { - private readonly IntPtr _context; - private readonly IntPtr _window; + private readonly nint _context; + private readonly nint _window; private readonly bool _shouldDisposeWindow; - public SDL2OpenGLContext(IntPtr context, IntPtr window, bool shouldDisposeWindow = true) + public SDL2OpenGLContext(nint context, nint window, bool shouldDisposeWindow = true) { _context = context; _window = window; @@ -65,14 +65,14 @@ namespace Ryujinx.Headless.SDL2.OpenGL // Ensure we share our contexts. SetupOpenGLAttributes(true, GraphicsDebugLevel.None); - IntPtr windowHandle = SDL_CreateWindow("Ryujinx background context window", 0, 0, 1, 1, SDL_WindowFlags.SDL_WINDOW_OPENGL | SDL_WindowFlags.SDL_WINDOW_HIDDEN); - IntPtr context = SDL_GL_CreateContext(windowHandle); + nint windowHandle = SDL_CreateWindow("Ryujinx background context window", 0, 0, 1, 1, SDL_WindowFlags.SDL_WINDOW_OPENGL | SDL_WindowFlags.SDL_WINDOW_HIDDEN); + nint context = SDL_GL_CreateContext(windowHandle); GL.LoadBindings(new OpenToolkitBindingsContext()); CheckResult(SDL_GL_SetAttribute(SDL_GLattr.SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 0)); - CheckResult(SDL_GL_MakeCurrent(windowHandle, IntPtr.Zero)); + CheckResult(SDL_GL_MakeCurrent(windowHandle, nint.Zero)); return new SDL2OpenGLContext(context, windowHandle); } @@ -96,7 +96,7 @@ namespace Ryujinx.Headless.SDL2.OpenGL } } - public bool HasContext() => SDL_GL_GetCurrentContext() != IntPtr.Zero; + public bool HasContext() => SDL_GL_GetCurrentContext() != nint.Zero; public void Dispose() { @@ -117,8 +117,9 @@ namespace Ryujinx.Headless.SDL2.OpenGL GraphicsDebugLevel glLogLevel, AspectRatio aspectRatio, bool enableMouse, - HideCursorMode hideCursorMode) - : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursorMode) + HideCursorMode hideCursorMode, + bool ignoreControllerApplet) + : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursorMode, ignoreControllerApplet) { _glLogLevel = glLogLevel; } @@ -129,10 +130,10 @@ namespace Ryujinx.Headless.SDL2.OpenGL { // Ensure to not share this context with other contexts before this point. SetupOpenGLAttributes(false, _glLogLevel); - IntPtr context = SDL_GL_CreateContext(WindowHandle); + nint context = SDL_GL_CreateContext(WindowHandle); CheckResult(SDL_GL_SetSwapInterval(1)); - if (context == IntPtr.Zero) + if (context == nint.Zero) { string errorMessage = $"SDL_GL_CreateContext failed with error \"{SDL_GetError()}\""; @@ -190,7 +191,7 @@ namespace Ryujinx.Headless.SDL2.OpenGL Device.DisposeGpu(); // Unbind context and destroy everything - CheckResult(SDL_GL_MakeCurrent(WindowHandle, IntPtr.Zero)); + CheckResult(SDL_GL_MakeCurrent(WindowHandle, nint.Zero)); _openGLContext.Dispose(); } diff --git a/src/Ryujinx.Headless.SDL2/Options.cs b/src/Ryujinx.Headless.SDL2/Options.cs index ef8849eea..4e2ad5b58 100644 --- a/src/Ryujinx.Headless.SDL2/Options.cs +++ b/src/Ryujinx.Headless.SDL2/Options.cs @@ -1,5 +1,6 @@ using CommandLine; using Ryujinx.Common.Configuration; +using Ryujinx.HLE; using Ryujinx.HLE.HOS.SystemState; namespace Ryujinx.Headless.SDL2 @@ -114,8 +115,11 @@ namespace Ryujinx.Headless.SDL2 [Option("fs-global-access-log-mode", Required = false, Default = 0, HelpText = "Enables FS access log output to the console.")] public int FsGlobalAccessLogMode { get; set; } - [Option("disable-vsync", Required = false, HelpText = "Disables Vertical Sync.")] - public bool DisableVSync { get; set; } + [Option("vsync-mode", Required = false, Default = VSyncMode.Switch, HelpText = "Sets the emulated VSync mode (Switch, Unbounded, or Custom).")] + public VSyncMode VSyncMode { get; set; } + + [Option("custom-refresh-rate", Required = false, Default = 90, HelpText = "Sets the custom refresh rate target value (integer).")] + public int CustomVSyncInterval { get; set; } [Option("disable-shader-cache", Required = false, HelpText = "Disables Shader cache.")] public bool DisableShaderCache { get; set; } @@ -219,11 +223,14 @@ namespace Ryujinx.Headless.SDL2 // Hacks - [Option("expand-ram", Required = false, Default = false, HelpText = "Expands the RAM amount on the emulated system from 4GiB to 8GiB.")] - public bool ExpandRAM { get; set; } + [Option("dram-size", Required = false, Default = MemoryConfiguration.MemoryConfiguration4GiB, HelpText = "Set the RAM amount on the emulated system.")] + public MemoryConfiguration DramSize { get; set; } [Option("ignore-missing-services", Required = false, Default = false, HelpText = "Enable ignoring missing services.")] public bool IgnoreMissingServices { get; set; } + + [Option("ignore-controller-applet", Required = false, Default = false, HelpText = "Enable ignoring the controller applet when your game loses connection to your controller.")] + public bool IgnoreControllerApplet { get; set; } // Values diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs index 715d58dda..ea789095e 100644 --- a/src/Ryujinx.Headless.SDL2/Program.cs +++ b/src/Ryujinx.Headless.SDL2/Program.cs @@ -1,4 +1,5 @@ using CommandLine; +using Gommon; using LibHac.Tools.FsSystem; using Ryujinx.Audio.Backends.SDL2; using Ryujinx.Common; @@ -98,8 +99,13 @@ namespace Ryujinx.Headless.SDL2 } Parser.Default.ParseArguments(args) - .WithParsed(Load) - .WithNotParsed(errors => errors.Output()); + .WithParsed(Load) + .WithNotParsed(errors => + { + Logger.Error?.PrintMsg(LogClass.Application, "Error parsing command-line arguments:"); + + errors.ForEach(err => Logger.Error?.PrintMsg(LogClass.Application, $" - {err.Tag}")); + }); } private static InputConfig HandlePlayerConfiguration(string inputProfileName, string inputId, PlayerIndex index) @@ -121,12 +127,10 @@ namespace Ryujinx.Headless.SDL2 } } - IGamepad gamepad; + IGamepad gamepad = _inputManager.KeyboardDriver.GetGamepad(inputId); bool isKeyboard = true; - gamepad = _inputManager.KeyboardDriver.GetGamepad(inputId); - if (gamepad == null) { gamepad = _inputManager.GamepadDriver.GetGamepad(inputId); @@ -448,8 +452,7 @@ namespace Ryujinx.Headless.SDL2 { Logger.AddTarget(new AsyncLogTargetWrapper( new FileLogTarget("file", logFile), - 1000, - AsyncLogTargetOverflowAction.Block + 1000 )); } else @@ -511,11 +514,11 @@ namespace Ryujinx.Headless.SDL2 { return options.GraphicsBackend switch { - GraphicsBackend.Vulkan => new VulkanWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode), + GraphicsBackend.Vulkan => new VulkanWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode, options.IgnoreControllerApplet), GraphicsBackend.Metal => OperatingSystem.IsMacOS() ? - new MetalWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableKeyboard, options.HideCursorMode) : + new MetalWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableKeyboard, options.HideCursorMode, options.IgnoreControllerApplet) : throw new Exception("Attempted to use Metal renderer on non-macOS platform!"), - _ => new OpenGLWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode) + _ => new OpenGLWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode, options.IgnoreControllerApplet) }; } @@ -574,11 +577,11 @@ namespace Ryujinx.Headless.SDL2 _userChannelPersistence, renderer, new SDL2HardwareDeviceDriver(), - options.ExpandRAM ? MemoryConfiguration.MemoryConfiguration8GiB : MemoryConfiguration.MemoryConfiguration4GiB, + options.DramSize, window, options.SystemLanguage, options.SystemRegion, - !options.DisableVSync, + options.VSyncMode, !options.DisableDockedMode, !options.DisablePTC, options.EnableInternetAccess, @@ -592,7 +595,11 @@ namespace Ryujinx.Headless.SDL2 options.AudioVolume, options.UseHypervisor ?? true, options.MultiplayerLanInterfaceId, - Common.Configuration.Multiplayer.MultiplayerMode.Disabled); + Common.Configuration.Multiplayer.MultiplayerMode.Disabled, + false, + string.Empty, + string.Empty, + options.CustomVSyncInterval); return new Switch(configuration); } diff --git a/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj b/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj index 6846a6f7f..d39f2b481 100644 --- a/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj +++ b/src/Ryujinx.Headless.SDL2/Ryujinx.Headless.SDL2.csproj @@ -1,7 +1,6 @@ - + - net8.0 win-x64;osx-x64;linux-x64 Exe true @@ -9,6 +8,7 @@ $(DefineConstants);$(ExtraDefineConstants) - true + $(DefaultItemExcludes);._* @@ -17,7 +17,7 @@ - + diff --git a/src/Ryujinx.Headless.SDL2/StatusUpdatedEventArgs.cs b/src/Ryujinx.Headless.SDL2/StatusUpdatedEventArgs.cs index 0b199d128..c1dd3805f 100644 --- a/src/Ryujinx.Headless.SDL2/StatusUpdatedEventArgs.cs +++ b/src/Ryujinx.Headless.SDL2/StatusUpdatedEventArgs.cs @@ -2,23 +2,20 @@ using System; namespace Ryujinx.Headless.SDL2 { - class StatusUpdatedEventArgs : EventArgs + class StatusUpdatedEventArgs( + string vSyncMode, + string dockedMode, + string aspectRatio, + string gameStatus, + string fifoStatus, + string gpuName) + : EventArgs { - public bool VSyncEnabled; - public string DockedMode; - public string AspectRatio; - public string GameStatus; - public string FifoStatus; - public string GpuName; - - public StatusUpdatedEventArgs(bool vSyncEnabled, string dockedMode, string aspectRatio, string gameStatus, string fifoStatus, string gpuName) - { - VSyncEnabled = vSyncEnabled; - DockedMode = dockedMode; - AspectRatio = aspectRatio; - GameStatus = gameStatus; - FifoStatus = fifoStatus; - GpuName = gpuName; - } + public string VSyncMode = vSyncMode; + public string DockedMode = dockedMode; + public string AspectRatio = aspectRatio; + public string GameStatus = gameStatus; + public string FifoStatus = fifoStatus; + public string GpuName = gpuName; } } diff --git a/src/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs b/src/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs index e5572c936..b88e0fe83 100644 --- a/src/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs +++ b/src/Ryujinx.Headless.SDL2/Vulkan/VulkanWindow.cs @@ -17,8 +17,9 @@ namespace Ryujinx.Headless.SDL2.Vulkan GraphicsDebugLevel glLogLevel, AspectRatio aspectRatio, bool enableMouse, - HideCursorMode hideCursorMode) - : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursorMode) + HideCursorMode hideCursorMode, + bool ignoreControllerApplet) + : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursorMode, ignoreControllerApplet) { _glLogLevel = glLogLevel; } @@ -46,7 +47,7 @@ namespace Ryujinx.Headless.SDL2.Vulkan action(); } - public IntPtr CreateWindowSurface(IntPtr instance) + public nint CreateWindowSurface(nint instance) { ulong surfaceHandle = 0; @@ -71,19 +72,19 @@ namespace Ryujinx.Headless.SDL2.Vulkan CreateSurface(); } - return (IntPtr)surfaceHandle; + return (nint)surfaceHandle; } public unsafe string[] GetRequiredInstanceExtensions() { - if (SDL_Vulkan_GetInstanceExtensions(WindowHandle, out uint extensionsCount, IntPtr.Zero) == SDL_bool.SDL_TRUE) + if (SDL_Vulkan_GetInstanceExtensions(WindowHandle, out uint extensionsCount, nint.Zero) == SDL_bool.SDL_TRUE) { - IntPtr[] rawExtensions = new IntPtr[(int)extensionsCount]; + nint[] rawExtensions = new nint[(int)extensionsCount]; string[] extensions = new string[(int)extensionsCount]; - fixed (IntPtr* rawExtensionsPtr = rawExtensions) + fixed (nint* rawExtensionsPtr = rawExtensions) { - if (SDL_Vulkan_GetInstanceExtensions(WindowHandle, out extensionsCount, (IntPtr)rawExtensionsPtr) == SDL_bool.SDL_TRUE) + if (SDL_Vulkan_GetInstanceExtensions(WindowHandle, out extensionsCount, (nint)rawExtensionsPtr) == SDL_bool.SDL_TRUE) { for (int i = 0; i < extensions.Length; i++) { diff --git a/src/Ryujinx.Headless.SDL2/WindowBase.cs b/src/Ryujinx.Headless.SDL2/WindowBase.cs index 8768913f5..fbe7cb49c 100644 --- a/src/Ryujinx.Headless.SDL2/WindowBase.cs +++ b/src/Ryujinx.Headless.SDL2/WindowBase.cs @@ -1,3 +1,5 @@ +using Humanizer; +using LibHac.Tools.Fs; using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Logging; @@ -10,6 +12,7 @@ using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationPr using Ryujinx.HLE.UI; using Ryujinx.Input; using Ryujinx.Input.HLE; +using Ryujinx.Input.SDL2; using Ryujinx.SDL2.Common; using System; using System.Collections.Concurrent; @@ -37,7 +40,7 @@ namespace Ryujinx.Headless.SDL2 [LibraryImport("SDL2")] // TODO: Remove this as soon as SDL2-CS was updated to expose this method publicly - private static partial IntPtr SDL_LoadBMP_RW(IntPtr src, int freesrc); + private static partial nint SDL_LoadBMP_RW(nint src, int freesrc); public static void QueueMainThreadAction(Action action) { @@ -51,7 +54,7 @@ namespace Ryujinx.Headless.SDL2 public event EventHandler StatusUpdatedEvent; - protected IntPtr WindowHandle { get; set; } + protected nint WindowHandle { get; set; } public IHostUITheme HostUITheme { get; } public int Width { get; private set; } @@ -84,13 +87,15 @@ namespace Ryujinx.Headless.SDL2 private readonly AspectRatio _aspectRatio; private readonly bool _enableMouse; + private readonly bool _ignoreControllerApplet; public WindowBase( InputManager inputManager, GraphicsDebugLevel glLogLevel, AspectRatio aspectRatio, bool enableMouse, - HideCursorMode hideCursorMode) + HideCursorMode hideCursorMode, + bool ignoreControllerApplet) { MouseDriver = new SDL2MouseDriver(hideCursorMode); _inputManager = inputManager; @@ -106,6 +111,7 @@ namespace Ryujinx.Headless.SDL2 _gpuDoneEvent = new ManualResetEvent(false); _aspectRatio = aspectRatio; _enableMouse = enableMouse; + _ignoreControllerApplet = ignoreControllerApplet; HostUITheme = new HeadlessHostUiTheme(); SDL2Driver.Instance.Initialize(); @@ -147,8 +153,8 @@ namespace Ryujinx.Headless.SDL2 { fixed (byte* iconPtr = iconBytes) { - IntPtr rwOpsStruct = SDL_RWFromConstMem((IntPtr)iconPtr, iconBytes.Length); - IntPtr iconHandle = SDL_LoadBMP_RW(rwOpsStruct, 1); + nint rwOpsStruct = SDL_RWFromConstMem((nint)iconPtr, iconBytes.Length); + nint iconHandle = SDL_LoadBMP_RW(rwOpsStruct, 1); SDL_SetWindowIcon(WindowHandle, iconHandle); SDL_FreeSurface(iconHandle); @@ -186,7 +192,7 @@ namespace Ryujinx.Headless.SDL2 WindowHandle = SDL_CreateWindow($"Ryujinx {Program.Version}{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}", SDL_WINDOWPOS_CENTERED_DISPLAY(DisplayId), SDL_WINDOWPOS_CENTERED_DISPLAY(DisplayId), Width, Height, DefaultFlags | FullscreenFlag | GetWindowFlags()); - if (WindowHandle == IntPtr.Zero) + if (WindowHandle == nint.Zero) { string errorMessage = $"SDL_CreateWindow failed with error \"{SDL_GetError()}\""; @@ -309,7 +315,7 @@ namespace Ryujinx.Headless.SDL2 } StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs( - Device.EnableDeviceVsync, + Device.VSyncMode.ToString(), dockedMode, Device.Configuration.AspectRatio.ToText(), $"Game: {Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)", @@ -480,14 +486,29 @@ namespace Ryujinx.Headless.SDL2 return true; } + public bool DisplayCabinetDialog(out string userText) + { + // SDL2 doesn't support input dialogs + userText = "Ryujinx"; + + return true; + } + + public void DisplayCabinetMessageDialog() + { + SDL_ShowSimpleMessageBox(SDL_MessageBoxFlags.SDL_MESSAGEBOX_INFORMATION, "Cabinet Dialog", "Please scan your Amiibo now.", WindowHandle); + } + public bool DisplayMessageDialog(ControllerAppletUIArgs args) { + if (_ignoreControllerApplet) return false; + string playerCount = args.PlayerCountMin == args.PlayerCountMax ? $"exactly {args.PlayerCountMin}" : $"{args.PlayerCountMin}-{args.PlayerCountMax}"; - string message = $"Application requests {playerCount} player(s) with:\n\n" + string message = $"Application requests {playerCount} {"player".ToQuantity(args.PlayerCountMin + args.PlayerCountMax, ShowQuantityAs.None)} with:\n\n" + $"TYPES: {args.SupportedStyles}\n\n" + $"PLAYERS: {string.Join(", ", args.SupportedPlayers)}\n\n" - + (args.IsDocked ? "Docked mode set. Handheld is also invalid.\n\n" : "") + + (args.IsDocked ? "Docked mode set. Handheld is also invalid.\n\n" : string.Empty) + "Please reconfigure Input now and then press OK."; return DisplayMessageDialog("Controller Applet", message); diff --git a/src/Ryujinx.Horizon.Common/ResultNames.cs b/src/Ryujinx.Horizon.Common/ResultNames.cs index 55a33d680..25d04b308 100644 --- a/src/Ryujinx.Horizon.Common/ResultNames.cs +++ b/src/Ryujinx.Horizon.Common/ResultNames.cs @@ -1235,14 +1235,14 @@ namespace Ryujinx.Horizon.Common { 0x412, "NotFound" }, { 0x612, "NotEnoughBuffer" }, { 0xCA12, "Cancelled" }, - { 0x7FE12, "" }, - { 0xFA212, "" }, + { 0x7FE12, string.Empty }, + { 0xFA212, string.Empty }, { 0xFA612, "InvalidTaskId" }, { 0xFB612, "InvalidSize" }, { 0xFCA12, "TaskCancelled" }, { 0xFCC12, "TaskNotCompleted" }, { 0xFCE12, "TaskQueueNotAvailable" }, - { 0x106A12, "" }, + { 0x106A12, string.Empty }, { 0x106C12, "OutOfRpcTask" }, { 0x109612, "InvalidCategory" }, { 0x214, "OutOfKeyResource" }, diff --git a/src/Ryujinx.Horizon.Common/Ryujinx.Horizon.Common.csproj b/src/Ryujinx.Horizon.Common/Ryujinx.Horizon.Common.csproj index fa1544c4f..b1caf6780 100644 --- a/src/Ryujinx.Horizon.Common/Ryujinx.Horizon.Common.csproj +++ b/src/Ryujinx.Horizon.Common/Ryujinx.Horizon.Common.csproj @@ -1,7 +1,7 @@ - net8.0 + $(DefaultItemExcludes);._* diff --git a/src/Ryujinx.Horizon.Generators/Ryujinx.Horizon.Generators.csproj b/src/Ryujinx.Horizon.Generators/Ryujinx.Horizon.Generators.csproj index d58803993..416eefc27 100644 --- a/src/Ryujinx.Horizon.Generators/Ryujinx.Horizon.Generators.csproj +++ b/src/Ryujinx.Horizon.Generators/Ryujinx.Horizon.Generators.csproj @@ -3,6 +3,7 @@ netstandard2.0 true + $(DefaultItemExcludes);._* diff --git a/src/Ryujinx.Horizon.Kernel.Generators/Ryujinx.Horizon.Kernel.Generators.csproj b/src/Ryujinx.Horizon.Kernel.Generators/Ryujinx.Horizon.Kernel.Generators.csproj index d58803993..02a8ec2c6 100644 --- a/src/Ryujinx.Horizon.Kernel.Generators/Ryujinx.Horizon.Kernel.Generators.csproj +++ b/src/Ryujinx.Horizon.Kernel.Generators/Ryujinx.Horizon.Kernel.Generators.csproj @@ -3,6 +3,8 @@ netstandard2.0 true + $(DefaultItemExcludes);._* + $(DefaultItemExcludes);._* diff --git a/src/Ryujinx.Horizon.Kernel.Generators/SyscallGenerator.cs b/src/Ryujinx.Horizon.Kernel.Generators/SyscallGenerator.cs index 7419a839a..06b98e09d 100644 --- a/src/Ryujinx.Horizon.Kernel.Generators/SyscallGenerator.cs +++ b/src/Ryujinx.Horizon.Kernel.Generators/SyscallGenerator.cs @@ -34,14 +34,14 @@ namespace Ryujinx.Horizon.Kernel.Generators private const string TypeResult = NamespaceHorizonCommon + "." + TypeResultName; private const string TypeExecutionContext = "IExecutionContext"; - private static readonly string[] _expectedResults = new string[] - { + private static readonly string[] _expectedResults = + [ $"{TypeResultName}.Success", $"{TypeKernelResultName}.TimedOut", $"{TypeKernelResultName}.Cancelled", $"{TypeKernelResultName}.PortRemoteClosed", $"{TypeKernelResultName}.InvalidState", - }; + ]; private readonly struct OutParameter { diff --git a/src/Ryujinx.Horizon/Ryujinx.Horizon.csproj b/src/Ryujinx.Horizon/Ryujinx.Horizon.csproj index bf34ddd17..727513484 100644 --- a/src/Ryujinx.Horizon/Ryujinx.Horizon.csproj +++ b/src/Ryujinx.Horizon/Ryujinx.Horizon.csproj @@ -1,7 +1,7 @@ - net8.0 + $(DefaultItemExcludes);._* diff --git a/src/Ryujinx.Horizon/Sdk/Audio/AudioResult.cs b/src/Ryujinx.Horizon/Sdk/Audio/AudioResult.cs index c18bfee9f..5914a747c 100644 --- a/src/Ryujinx.Horizon/Sdk/Audio/AudioResult.cs +++ b/src/Ryujinx.Horizon/Sdk/Audio/AudioResult.cs @@ -8,5 +8,6 @@ namespace Ryujinx.Horizon.Sdk.Audio public static Result DeviceNotFound => new(ModuleId, 1); public static Result UnsupportedRevision => new(ModuleId, 2); + public static Result NotImplemented => new(ModuleId, 513); } } diff --git a/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioDevice.cs b/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioDevice.cs index f67ea7298..2d3aa7ba9 100644 --- a/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioDevice.cs +++ b/src/Ryujinx.Horizon/Sdk/Audio/Detail/AudioDevice.cs @@ -233,6 +233,48 @@ namespace Ryujinx.Horizon.Sdk.Audio.Detail return Result.Success; } + [CmifCommand(15)] // 17.0.0+ + public Result AcquireAudioOutputDeviceNotification([CopyHandle] out int eventHandle, ulong deviceId) + { + eventHandle = 0; + + return AudioResult.NotImplemented; + } + + [CmifCommand(16)] // 17.0.0+ + public Result ReleaseAudioOutputDeviceNotification(ulong deviceId) + { + return AudioResult.NotImplemented; + } + + [CmifCommand(17)] // 17.0.0+ + public Result AcquireAudioInputDeviceNotification([CopyHandle] out int eventHandle, ulong deviceId) + { + eventHandle = 0; + + return AudioResult.NotImplemented; + } + + [CmifCommand(18)] // 17.0.0+ + public Result ReleaseAudioInputDeviceNotification(ulong deviceId) + { + return AudioResult.NotImplemented; + } + + [CmifCommand(19)] // 18.0.0+ + public Result SetAudioDeviceOutputVolumeAutoTuneEnabled(bool enabled) + { + return AudioResult.NotImplemented; + } + + [CmifCommand(20)] // 18.0.0+ + public Result IsAudioDeviceOutputVolumeAutoTuneEnabled(out bool enabled) + { + enabled = false; + + return AudioResult.NotImplemented; + } + protected virtual void Dispose(bool disposing) { if (disposing) diff --git a/src/Ryujinx.Horizon/Sdk/Friends/Detail/Ipc/NotificationService.cs b/src/Ryujinx.Horizon/Sdk/Friends/Detail/Ipc/NotificationService.cs index 534bf63ed..585b40df3 100644 --- a/src/Ryujinx.Horizon/Sdk/Friends/Detail/Ipc/NotificationService.cs +++ b/src/Ryujinx.Horizon/Sdk/Friends/Detail/Ipc/NotificationService.cs @@ -4,6 +4,7 @@ using Ryujinx.Horizon.Sdk.OsTypes; using Ryujinx.Horizon.Sdk.Sf; using System; using System.Collections.Generic; +using System.Threading; namespace Ryujinx.Horizon.Sdk.Friends.Detail.Ipc { @@ -13,7 +14,7 @@ namespace Ryujinx.Horizon.Sdk.Friends.Detail.Ipc private readonly Uid _userId; private readonly FriendsServicePermissionLevel _permissionLevel; - private readonly object _lock = new(); + private readonly Lock _lock = new(); private SystemEventType _notificationEvent; diff --git a/src/Ryujinx.Horizon/Sdk/Ngc/Detail/AhoCorasick.cs b/src/Ryujinx.Horizon/Sdk/Ngc/Detail/AhoCorasick.cs index 6acb9be97..03f61c218 100644 --- a/src/Ryujinx.Horizon/Sdk/Ngc/Detail/AhoCorasick.cs +++ b/src/Ryujinx.Horizon/Sdk/Ngc/Detail/AhoCorasick.cs @@ -221,7 +221,7 @@ namespace Ryujinx.Horizon.Sdk.Ngc.Detail if (includeMultiWord) { int lastMultiWordIndex = 0; - string multiWord = ""; + string multiWord = string.Empty; while (_multiWordMap.Has(nodePlainIndex)) { diff --git a/src/Ryujinx.Horizon/Sdk/Ngc/Detail/ContentsReader.cs b/src/Ryujinx.Horizon/Sdk/Ngc/Detail/ContentsReader.cs index 6a0fc4f9e..d3ed37e78 100644 --- a/src/Ryujinx.Horizon/Sdk/Ngc/Detail/ContentsReader.cs +++ b/src/Ryujinx.Horizon/Sdk/Ngc/Detail/ContentsReader.cs @@ -3,6 +3,7 @@ using Ryujinx.Horizon.Sdk.Fs; using System; using System.IO; using System.IO.Compression; +using System.Threading; namespace Ryujinx.Horizon.Sdk.Ngc.Detail { @@ -22,13 +23,12 @@ namespace Ryujinx.Horizon.Sdk.Ngc.Detail } private readonly IFsClient _fsClient; - private readonly object _lock; + private readonly Lock _lock = new(); private bool _intialized; private ulong _cacheSize; public ContentsReader(IFsClient fsClient) { - _lock = new(); _fsClient = fsClient; } diff --git a/src/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs b/src/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs index 406352003..879a3a58f 100644 --- a/src/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs +++ b/src/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs @@ -2,6 +2,7 @@ using Ryujinx.Common; using Ryujinx.Horizon.Common; using System; using System.Collections.Generic; +using System.Threading; namespace Ryujinx.Horizon.Sdk.OsTypes.Impl { @@ -13,7 +14,7 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl private readonly List _multiWaits; - private readonly object _lock = new(); + private readonly Lock _lock = new(); private int _waitingThreadHandle; diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainManager.cs b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainManager.cs index 7762345af..13f9fb7a9 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainManager.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Cmif/ServerDomainManager.cs @@ -1,6 +1,7 @@ using Ryujinx.Horizon.Common; using System; using System.Collections.Generic; +using System.Threading; namespace Ryujinx.Horizon.Sdk.Sf.Cmif { @@ -209,14 +210,13 @@ namespace Ryujinx.Horizon.Sdk.Sf.Cmif } private readonly EntryManager _entryManager; - private readonly object _entryOwnerLock; + private readonly Lock _entryOwnerLock = new(); private readonly HashSet _domains; private readonly int _maxDomains; public ServerDomainManager(int entryCount, int maxDomains) { _entryManager = new EntryManager(entryCount); - _entryOwnerLock = new object(); _domains = new HashSet(); _maxDomains = maxDomains; } diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManager.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManager.cs index e8957b758..6aa32faee 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManager.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManager.cs @@ -4,6 +4,7 @@ using Ryujinx.Horizon.Sdk.Sm; using System; using System.Collections.Generic; using System.Numerics; +using System.Threading; namespace Ryujinx.Horizon.Sdk.Sf.Hipc { @@ -17,7 +18,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc private readonly ulong _pointerBuffersBaseAddress; private readonly ulong _savedMessagesBaseAddress; - private readonly object _resourceLock; + private readonly Lock _resourceLock = new(); private readonly ulong[] _sessionAllocationBitmap; private readonly HashSet _sessions; private readonly HashSet _servers; @@ -42,7 +43,6 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc } } - _resourceLock = new object(); _sessionAllocationBitmap = new ulong[(maxSessions + 63) / 64]; _sessions = new HashSet(); _servers = new HashSet(); diff --git a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs index 570e3c802..31ca264e3 100644 --- a/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs +++ b/src/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs @@ -4,6 +4,7 @@ using Ryujinx.Horizon.Sdk.Sf.Cmif; using Ryujinx.Horizon.Sdk.Sm; using System; using System.Linq; +using System.Threading; namespace Ryujinx.Horizon.Sdk.Sf.Hipc { @@ -16,8 +17,8 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc private readonly MultiWait _multiWait; private readonly MultiWait _waitList; - private readonly object _multiWaitSelectionLock; - private readonly object _waitListLock; + private readonly Lock _multiWaitSelectionLock = new(); + private readonly Lock _waitListLock = new(); private readonly Event _requestStopEvent; private readonly Event _notifyEvent; @@ -39,9 +40,6 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc _multiWait = new MultiWait(); _waitList = new MultiWait(); - _multiWaitSelectionLock = new object(); - _waitListLock = new object(); - _requestStopEvent = new Event(EventClearMode.ManualClear); _notifyEvent = new Event(EventClearMode.ManualClear); diff --git a/src/Ryujinx.Input.SDL2/Ryujinx.Input.SDL2.csproj b/src/Ryujinx.Input.SDL2/Ryujinx.Input.SDL2.csproj index 1ab79d08a..89f5adda1 100644 --- a/src/Ryujinx.Input.SDL2/Ryujinx.Input.SDL2.csproj +++ b/src/Ryujinx.Input.SDL2/Ryujinx.Input.SDL2.csproj @@ -1,8 +1,8 @@  - net8.0 true + $(DefaultItemExcludes);._* diff --git a/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs b/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs index 187ca48dd..12bfab4bb 100644 --- a/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs +++ b/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs @@ -4,6 +4,7 @@ using Ryujinx.Common.Logging; using System; using System.Collections.Generic; using System.Numerics; +using System.Threading; using static SDL2.SDL; namespace Ryujinx.Input.SDL2 @@ -12,7 +13,10 @@ namespace Ryujinx.Input.SDL2 { private bool HasConfiguration => _configuration != null; - private record struct ButtonMappingEntry(GamepadButtonInputId To, GamepadButtonInputId From); + private readonly record struct ButtonMappingEntry(GamepadButtonInputId To, GamepadButtonInputId From) + { + public bool IsValid => To is not GamepadButtonInputId.Unbound && From is not GamepadButtonInputId.Unbound; + } private StandardControllerInputConfig _configuration; @@ -55,7 +59,7 @@ namespace Ryujinx.Input.SDL2 SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_INVALID, }; - private readonly object _userMappingLock = new(); + private readonly Lock _userMappingLock = new(); private readonly List _buttonsUserMapping; @@ -68,11 +72,11 @@ namespace Ryujinx.Input.SDL2 public GamepadFeaturesFlag Features { get; } - private IntPtr _gamepadHandle; + private nint _gamepadHandle; private float _triggerThreshold; - public SDL2Gamepad(IntPtr gamepadHandle, string driverId) + public SDL2Gamepad(nint gamepadHandle, string driverId) { _gamepadHandle = gamepadHandle; _buttonsUserMapping = new List(20); @@ -124,11 +128,11 @@ namespace Ryujinx.Input.SDL2 protected virtual void Dispose(bool disposing) { - if (disposing && _gamepadHandle != IntPtr.Zero) + if (disposing && _gamepadHandle != nint.Zero) { SDL_GameControllerClose(_gamepadHandle); - _gamepadHandle = IntPtr.Zero; + _gamepadHandle = nint.Zero; } } @@ -144,86 +148,65 @@ namespace Ryujinx.Input.SDL2 public void Rumble(float lowFrequency, float highFrequency, uint durationMs) { - if (Features.HasFlag(GamepadFeaturesFlag.Rumble)) - { - ushort lowFrequencyRaw = (ushort)(lowFrequency * ushort.MaxValue); - ushort highFrequencyRaw = (ushort)(highFrequency * ushort.MaxValue); + if (!Features.HasFlag(GamepadFeaturesFlag.Rumble)) + return; - if (durationMs == uint.MaxValue) - { - if (SDL_GameControllerRumble(_gamepadHandle, lowFrequencyRaw, highFrequencyRaw, SDL_HAPTIC_INFINITY) != 0) - { - Logger.Error?.Print(LogClass.Hid, "Rumble is not supported on this game controller."); - } - } - else if (durationMs > SDL_HAPTIC_INFINITY) - { - Logger.Error?.Print(LogClass.Hid, $"Unsupported rumble duration {durationMs}"); - } - else - { - if (SDL_GameControllerRumble(_gamepadHandle, lowFrequencyRaw, highFrequencyRaw, durationMs) != 0) - { - Logger.Error?.Print(LogClass.Hid, "Rumble is not supported on this game controller."); - } - } + ushort lowFrequencyRaw = (ushort)(lowFrequency * ushort.MaxValue); + ushort highFrequencyRaw = (ushort)(highFrequency * ushort.MaxValue); + + if (durationMs == uint.MaxValue) + { + if (SDL_GameControllerRumble(_gamepadHandle, lowFrequencyRaw, highFrequencyRaw, SDL_HAPTIC_INFINITY) != 0) + Logger.Error?.Print(LogClass.Hid, "Rumble is not supported on this game controller."); + } + else if (durationMs > SDL_HAPTIC_INFINITY) + { + Logger.Error?.Print(LogClass.Hid, $"Unsupported rumble duration {durationMs}"); + } + else + { + if (SDL_GameControllerRumble(_gamepadHandle, lowFrequencyRaw, highFrequencyRaw, durationMs) != 0) + Logger.Error?.Print(LogClass.Hid, "Rumble is not supported on this game controller."); } } public Vector3 GetMotionData(MotionInputId inputId) { - SDL_SensorType sensorType = SDL_SensorType.SDL_SENSOR_INVALID; - - if (inputId == MotionInputId.Accelerometer) + SDL_SensorType sensorType = inputId switch { - sensorType = SDL_SensorType.SDL_SENSOR_ACCEL; - } - else if (inputId == MotionInputId.Gyroscope) - { - sensorType = SDL_SensorType.SDL_SENSOR_GYRO; - } + MotionInputId.Accelerometer => SDL_SensorType.SDL_SENSOR_ACCEL, + MotionInputId.Gyroscope => SDL_SensorType.SDL_SENSOR_GYRO, + _ => SDL_SensorType.SDL_SENSOR_INVALID + }; - if (Features.HasFlag(GamepadFeaturesFlag.Motion) && sensorType != SDL_SensorType.SDL_SENSOR_INVALID) - { - const int ElementCount = 3; + if (!Features.HasFlag(GamepadFeaturesFlag.Motion) || sensorType is SDL_SensorType.SDL_SENSOR_INVALID) + return Vector3.Zero; - unsafe + const int ElementCount = 3; + + unsafe + { + float* values = stackalloc float[ElementCount]; + + int result = SDL_GameControllerGetSensorData(_gamepadHandle, sensorType, (nint)values, ElementCount); + + if (result != 0) + return Vector3.Zero; + + Vector3 value = new(values[0], values[1], values[2]); + + return inputId switch { - float* values = stackalloc float[ElementCount]; - - int result = SDL_GameControllerGetSensorData(_gamepadHandle, sensorType, (IntPtr)values, ElementCount); - - if (result == 0) - { - Vector3 value = new(values[0], values[1], values[2]); - - if (inputId == MotionInputId.Gyroscope) - { - return RadToDegree(value); - } - - if (inputId == MotionInputId.Accelerometer) - { - return GsToMs2(value); - } - - return value; - } - } + MotionInputId.Gyroscope => RadToDegree(value), + MotionInputId.Accelerometer => GsToMs2(value), + _ => value + }; } - - return Vector3.Zero; } - private static Vector3 RadToDegree(Vector3 rad) - { - return rad * (180 / MathF.PI); - } + private static Vector3 RadToDegree(Vector3 rad) => rad * (180 / MathF.PI); - private static Vector3 GsToMs2(Vector3 gs) - { - return gs / SDL_STANDARD_GRAVITY; - } + private static Vector3 GsToMs2(Vector3 gs) => gs / SDL_STANDARD_GRAVITY; public void SetConfiguration(InputConfig configuration) { @@ -278,16 +261,14 @@ namespace Ryujinx.Input.SDL2 lock (_userMappingLock) { if (_buttonsUserMapping.Count == 0) - { return rawState; - } + + // ReSharper disable once ForeachCanBePartlyConvertedToQueryUsingAnotherGetEnumerator foreach (ButtonMappingEntry entry in _buttonsUserMapping) { - if (entry.From == GamepadButtonInputId.Unbound || entry.To == GamepadButtonInputId.Unbound) - { + if (!entry.IsValid) continue; - } // Do not touch state of button already pressed if (!result.IsPressed(entry.To)) @@ -313,70 +294,79 @@ namespace Ryujinx.Input.SDL2 return value * ConvertRate; } + private JoyconConfigControllerStick GetLogicalJoyStickConfig(StickInputId inputId) + { + switch (inputId) + { + case StickInputId.Left: + if (_configuration.RightJoyconStick.Joystick == Common.Configuration.Hid.Controller.StickInputId.Left) + return _configuration.RightJoyconStick; + else + return _configuration.LeftJoyconStick; + case StickInputId.Right: + if (_configuration.LeftJoyconStick.Joystick == Common.Configuration.Hid.Controller.StickInputId.Right) + return _configuration.LeftJoyconStick; + else + return _configuration.RightJoyconStick; + } + return null; + } + public (float, float) GetStick(StickInputId inputId) { if (inputId == StickInputId.Unbound) - { return (0.0f, 0.0f); - } - short stickX; - short stickY; - - if (inputId == StickInputId.Left) - { - stickX = SDL_GameControllerGetAxis(_gamepadHandle, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_LEFTX); - stickY = SDL_GameControllerGetAxis(_gamepadHandle, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_LEFTY); - } - else if (inputId == StickInputId.Right) - { - stickX = SDL_GameControllerGetAxis(_gamepadHandle, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_RIGHTX); - stickY = SDL_GameControllerGetAxis(_gamepadHandle, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_RIGHTY); - } - else - { - throw new NotSupportedException($"Unsupported stick {inputId}"); - } + (short stickX, short stickY) = GetStickXY(inputId); float resultX = ConvertRawStickValue(stickX); float resultY = -ConvertRawStickValue(stickY); if (HasConfiguration) { - if ((inputId == StickInputId.Left && _configuration.LeftJoyconStick.InvertStickX) || - (inputId == StickInputId.Right && _configuration.RightJoyconStick.InvertStickX)) - { - resultX = -resultX; - } + var joyconStickConfig = GetLogicalJoyStickConfig(inputId); - if ((inputId == StickInputId.Left && _configuration.LeftJoyconStick.InvertStickY) || - (inputId == StickInputId.Right && _configuration.RightJoyconStick.InvertStickY)) + if (joyconStickConfig != null) { - resultY = -resultY; - } + if (joyconStickConfig.InvertStickX) + resultX = -resultX; - if ((inputId == StickInputId.Left && _configuration.LeftJoyconStick.Rotate90CW) || - (inputId == StickInputId.Right && _configuration.RightJoyconStick.Rotate90CW)) - { - float temp = resultX; - resultX = resultY; - resultY = -temp; + if (joyconStickConfig.InvertStickY) + resultY = -resultY; + + if (joyconStickConfig.Rotate90CW) + { + float temp = resultX; + resultX = resultY; + resultY = -temp; + } } } return (resultX, resultY); } + // ReSharper disable once InconsistentNaming + private (short, short) GetStickXY(StickInputId inputId) => + inputId switch + { + StickInputId.Left => ( + SDL_GameControllerGetAxis(_gamepadHandle, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_LEFTX), + SDL_GameControllerGetAxis(_gamepadHandle, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_LEFTY)), + StickInputId.Right => ( + SDL_GameControllerGetAxis(_gamepadHandle, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_RIGHTX), + SDL_GameControllerGetAxis(_gamepadHandle, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_RIGHTY)), + _ => throw new NotSupportedException($"Unsupported stick {inputId}") + }; + public bool IsPressed(GamepadButtonInputId inputId) { - if (inputId == GamepadButtonInputId.LeftTrigger) + switch (inputId) { - return ConvertRawStickValue(SDL_GameControllerGetAxis(_gamepadHandle, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_TRIGGERLEFT)) > _triggerThreshold; - } - - if (inputId == GamepadButtonInputId.RightTrigger) - { - return ConvertRawStickValue(SDL_GameControllerGetAxis(_gamepadHandle, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_TRIGGERRIGHT)) > _triggerThreshold; + case GamepadButtonInputId.LeftTrigger: + return ConvertRawStickValue(SDL_GameControllerGetAxis(_gamepadHandle, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_TRIGGERLEFT)) > _triggerThreshold; + case GamepadButtonInputId.RightTrigger: + return ConvertRawStickValue(SDL_GameControllerGetAxis(_gamepadHandle, SDL_GameControllerAxis.SDL_CONTROLLER_AXIS_TRIGGERRIGHT)) > _triggerThreshold; } if (_buttonsDriverMapping[(int)inputId] == SDL_GameControllerButton.SDL_CONTROLLER_BUTTON_INVALID) diff --git a/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs b/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs index c741493c1..c580e4e7d 100644 --- a/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs +++ b/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs @@ -1,6 +1,7 @@ using Ryujinx.SDL2.Common; using System; using System.Collections.Generic; +using System.Threading; using static SDL2.SDL; namespace Ryujinx.Input.SDL2 @@ -9,7 +10,7 @@ namespace Ryujinx.Input.SDL2 { private readonly Dictionary _gamepadsInstanceIdsMapping; private readonly List _gamepadsIds; - private readonly object _lock = new object(); + private readonly Lock _lock = new(); public ReadOnlySpan GamepadsIds { @@ -82,17 +83,15 @@ namespace Ryujinx.Input.SDL2 private void HandleJoyStickDisconnected(int joystickInstanceId) { - if (_gamepadsInstanceIdsMapping.TryGetValue(joystickInstanceId, out string id)) + if (!_gamepadsInstanceIdsMapping.Remove(joystickInstanceId, out string id)) + return; + + lock (_lock) { - _gamepadsInstanceIdsMapping.Remove(joystickInstanceId); - - lock (_lock) - { - _gamepadsIds.Remove(id); - } - - OnGamepadDisconnected?.Invoke(id); + _gamepadsIds.Remove(id); } + + OnGamepadDisconnected?.Invoke(id); } private void HandleJoyStickConnected(int joystickDeviceId, int joystickInstanceId) @@ -117,7 +116,10 @@ namespace Ryujinx.Input.SDL2 { lock (_lock) { - _gamepadsIds.Add(id); + if (joystickDeviceId <= _gamepadsIds.FindLastIndex(_ => true)) + _gamepadsIds.Insert(joystickDeviceId, id); + else + _gamepadsIds.Add(id); } OnGamepadConnected?.Invoke(id); @@ -162,9 +164,9 @@ namespace Ryujinx.Input.SDL2 return null; } - IntPtr gamepadHandle = SDL_GameControllerOpen(joystickIndex); + nint gamepadHandle = SDL_GameControllerOpen(joystickIndex); - if (gamepadHandle == IntPtr.Zero) + if (gamepadHandle == nint.Zero) { return null; } diff --git a/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs b/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs index bc0a7e660..8d6a30d11 100644 --- a/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs +++ b/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Numerics; using System.Runtime.CompilerServices; +using System.Threading; using static SDL2.SDL; using ConfigKey = Ryujinx.Common.Configuration.Hid.Key; @@ -12,19 +13,12 @@ namespace Ryujinx.Input.SDL2 { class SDL2Keyboard : IKeyboard { - private class ButtonMappingEntry + private readonly record struct ButtonMappingEntry(GamepadButtonInputId To, Key From) { - public readonly GamepadButtonInputId To; - public readonly Key From; - - public ButtonMappingEntry(GamepadButtonInputId to, Key from) - { - To = to; - From = from; - } + public bool IsValid => To is not GamepadButtonInputId.Unbound && From is not Key.Unbound; } - private readonly object _userMappingLock = new(); + private readonly Lock _userMappingLock = new(); #pragma warning disable IDE0052 // Remove unread private member private readonly SDL2KeyboardDriver _driver; @@ -232,7 +226,7 @@ namespace Ryujinx.Input.SDL2 unsafe { - IntPtr statePtr = SDL_GetKeyboardState(out int numKeys); + nint statePtr = SDL_GetKeyboardState(out int numKeys); rawKeyboardState = new ReadOnlySpan((byte*)statePtr, numKeys); } diff --git a/src/Ryujinx.Headless.SDL2/SDL2Mouse.cs b/src/Ryujinx.Input.SDL2/SDL2Mouse.cs similarity index 95% rename from src/Ryujinx.Headless.SDL2/SDL2Mouse.cs rename to src/Ryujinx.Input.SDL2/SDL2Mouse.cs index de64b4f8f..37b356b76 100644 --- a/src/Ryujinx.Headless.SDL2/SDL2Mouse.cs +++ b/src/Ryujinx.Input.SDL2/SDL2Mouse.cs @@ -1,12 +1,11 @@ using Ryujinx.Common.Configuration.Hid; -using Ryujinx.Input; using System; using System.Drawing; using System.Numerics; -namespace Ryujinx.Headless.SDL2 +namespace Ryujinx.Input.SDL2 { - class SDL2Mouse : IMouse + public class SDL2Mouse : IMouse { private SDL2MouseDriver _driver; @@ -84,6 +83,7 @@ namespace Ryujinx.Headless.SDL2 public void Dispose() { + GC.SuppressFinalize(this); _driver = null; } } diff --git a/src/Ryujinx.Headless.SDL2/SDL2MouseDriver.cs b/src/Ryujinx.Input.SDL2/SDL2MouseDriver.cs similarity index 96% rename from src/Ryujinx.Headless.SDL2/SDL2MouseDriver.cs rename to src/Ryujinx.Input.SDL2/SDL2MouseDriver.cs index 8983091f5..768ea8c62 100644 --- a/src/Ryujinx.Headless.SDL2/SDL2MouseDriver.cs +++ b/src/Ryujinx.Input.SDL2/SDL2MouseDriver.cs @@ -1,6 +1,5 @@ using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; -using Ryujinx.Input; using System; using System.Diagnostics; using System.Drawing; @@ -8,9 +7,9 @@ using System.Numerics; using System.Runtime.CompilerServices; using static SDL2.SDL; -namespace Ryujinx.Headless.SDL2 +namespace Ryujinx.Input.SDL2 { - class SDL2MouseDriver : IGamepadDriver + public class SDL2MouseDriver : IGamepadDriver { private const int CursorHideIdleTime = 5; // seconds @@ -44,7 +43,7 @@ namespace Ryujinx.Headless.SDL2 [MethodImpl(MethodImplOptions.AggressiveInlining)] private static MouseButton DriverButtonToMouseButton(uint rawButton) { - Debug.Assert(rawButton > 0 && rawButton <= (int)MouseButton.Count); + Debug.Assert(rawButton is > 0 and <= (int)MouseButton.Count); return (MouseButton)(rawButton - 1); } @@ -172,6 +171,7 @@ namespace Ryujinx.Headless.SDL2 return; } + GC.SuppressFinalize(this); _isDisposed = true; } } diff --git a/src/Ryujinx.Input/HLE/InputManager.cs b/src/Ryujinx.Input/HLE/InputManager.cs index 7111f5502..2825542a0 100644 --- a/src/Ryujinx.Input/HLE/InputManager.cs +++ b/src/Ryujinx.Input/HLE/InputManager.cs @@ -2,18 +2,13 @@ using System; namespace Ryujinx.Input.HLE { - public class InputManager : IDisposable + public class InputManager(IGamepadDriver keyboardDriver, IGamepadDriver gamepadDriver) + : IDisposable { - public IGamepadDriver KeyboardDriver { get; private set; } - public IGamepadDriver GamepadDriver { get; private set; } + public IGamepadDriver KeyboardDriver { get; } = keyboardDriver; + public IGamepadDriver GamepadDriver { get; } = gamepadDriver; public IGamepadDriver MouseDriver { get; private set; } - public InputManager(IGamepadDriver keyboardDriver, IGamepadDriver gamepadDriver) - { - KeyboardDriver = keyboardDriver; - GamepadDriver = gamepadDriver; - } - public void SetMouseDriver(IGamepadDriver mouseDriver) { MouseDriver?.Dispose(); diff --git a/src/Ryujinx.Input/HLE/NpadManager.cs b/src/Ryujinx.Input/HLE/NpadManager.cs index 1dc87358d..08f222a91 100644 --- a/src/Ryujinx.Input/HLE/NpadManager.cs +++ b/src/Ryujinx.Input/HLE/NpadManager.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; +using System.Threading; using CemuHookClient = Ryujinx.Input.Motion.CemuHook.Client; using ControllerType = Ryujinx.Common.Configuration.Hid.ControllerType; using PlayerIndex = Ryujinx.HLE.HOS.Services.Hid.PlayerIndex; @@ -18,7 +19,7 @@ namespace Ryujinx.Input.HLE { private readonly CemuHookClient _cemuHookClient; - private readonly object _lock = new(); + private readonly Lock _lock = new(); private bool _blockInputUpdates; @@ -320,7 +321,7 @@ namespace Ryujinx.Input.HLE { lock (_lock) { - return _inputConfig.Find(x => x.PlayerIndex == (Common.Configuration.Hid.PlayerIndex)index); + return _inputConfig.FirstOrDefault(x => x.PlayerIndex == (Common.Configuration.Hid.PlayerIndex)index); } } diff --git a/src/Ryujinx.Input/Ryujinx.Input.csproj b/src/Ryujinx.Input/Ryujinx.Input.csproj index 59a9eeb61..6741a2b3e 100644 --- a/src/Ryujinx.Input/Ryujinx.Input.csproj +++ b/src/Ryujinx.Input/Ryujinx.Input.csproj @@ -1,8 +1,8 @@ - net8.0 true + $(DefaultItemExcludes);._* diff --git a/src/Ryujinx.Memory/AddressSpaceManager.cs b/src/Ryujinx.Memory/AddressSpaceManager.cs index 807c5c0f4..7bd572d7a 100644 --- a/src/Ryujinx.Memory/AddressSpaceManager.cs +++ b/src/Ryujinx.Memory/AddressSpaceManager.cs @@ -106,10 +106,13 @@ namespace Ryujinx.Memory { if (size == 0) { - return Enumerable.Empty(); + yield break; } - return GetHostRegionsImpl(va, size); + foreach (var hostRegion in GetHostRegionsImpl(va, size)) + { + yield return hostRegion; + } } /// @@ -117,51 +120,36 @@ namespace Ryujinx.Memory { if (size == 0) { - return Enumerable.Empty(); + yield break; } var hostRegions = GetHostRegionsImpl(va, size); if (hostRegions == null) { - return null; + yield break; } - var regions = new MemoryRange[hostRegions.Count]; - ulong backingStart = (ulong)_backingMemory.Pointer; ulong backingEnd = backingStart + _backingMemory.Size; - int count = 0; - - for (int i = 0; i < regions.Length; i++) + foreach (var hostRegion in hostRegions) { - var hostRegion = hostRegions[i]; - if (hostRegion.Address >= backingStart && hostRegion.Address < backingEnd) { - regions[count++] = new MemoryRange(hostRegion.Address - backingStart, hostRegion.Size); + yield return new MemoryRange(hostRegion.Address - backingStart, hostRegion.Size); } } - - if (count != regions.Length) - { - return new ArraySegment(regions, 0, count); - } - - return regions; } - private List GetHostRegionsImpl(ulong va, ulong size) + private IEnumerable GetHostRegionsImpl(ulong va, ulong size) { if (!ValidateAddress(va) || !ValidateAddressAndSize(va, size)) { - return null; + yield break; } int pages = GetPagesCount(va, size, out va); - var regions = new List(); - nuint regionStart = GetHostAddress(va); ulong regionSize = PageSize; @@ -169,14 +157,14 @@ namespace Ryujinx.Memory { if (!ValidateAddress(va + PageSize)) { - return null; + yield break; } nuint newHostAddress = GetHostAddress(va + PageSize); if (GetHostAddress(va) + PageSize != newHostAddress) { - regions.Add(new HostMemoryRange(regionStart, regionSize)); + yield return new HostMemoryRange(regionStart, regionSize); regionStart = newHostAddress; regionSize = 0; } @@ -185,9 +173,7 @@ namespace Ryujinx.Memory regionSize += PageSize; } - regions.Add(new HostMemoryRange(regionStart, regionSize)); - - return regions; + yield return new HostMemoryRange(regionStart, regionSize); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/Ryujinx.Memory/MemoryBlock.cs b/src/Ryujinx.Memory/MemoryBlock.cs index 59ee269bb..c2ba51f82 100644 --- a/src/Ryujinx.Memory/MemoryBlock.cs +++ b/src/Ryujinx.Memory/MemoryBlock.cs @@ -13,13 +13,13 @@ namespace Ryujinx.Memory private readonly bool _isMirror; private readonly bool _viewCompatible; private readonly bool _forJit; - private IntPtr _sharedMemory; - private IntPtr _pointer; + private nint _sharedMemory; + private nint _pointer; /// /// Pointer to the memory block data. /// - public IntPtr Pointer => _pointer; + public nint Pointer => _pointer; /// /// Size of the memory block. @@ -68,7 +68,7 @@ namespace Ryujinx.Memory /// Shared memory to use as backing storage for this block /// Throw when there's an error while mapping the shared memory /// Throw when the current platform is not supported - private MemoryBlock(ulong size, IntPtr sharedMemory) + private MemoryBlock(ulong size, nint sharedMemory) { _pointer = MemoryManagement.MapSharedMemory(sharedMemory, size); Size = size; @@ -86,7 +86,7 @@ namespace Ryujinx.Memory /// Throw when the current platform is not supported public MemoryBlock CreateMirror() { - if (_sharedMemory == IntPtr.Zero) + if (_sharedMemory == nint.Zero) { throw new NotSupportedException("Mirroring is not supported on the memory block because the Mirrorable flag was not set."); } @@ -134,7 +134,7 @@ namespace Ryujinx.Memory /// Throw when either or are out of range public void MapView(MemoryBlock srcBlock, ulong srcOffset, ulong dstOffset, ulong size) { - if (srcBlock._sharedMemory == IntPtr.Zero) + if (srcBlock._sharedMemory == nint.Zero) { throw new ArgumentException("The source memory block is not mirrorable, and thus cannot be mapped on the current block."); } @@ -273,9 +273,9 @@ namespace Ryujinx.Memory [MethodImpl(MethodImplOptions.AggressiveInlining)] public unsafe ref T GetRef(ulong offset) where T : unmanaged { - IntPtr ptr = _pointer; + nint ptr = _pointer; - ObjectDisposedException.ThrowIf(ptr == IntPtr.Zero, this); + ObjectDisposedException.ThrowIf(ptr == nint.Zero, this); int size = Unsafe.SizeOf(); @@ -298,14 +298,14 @@ namespace Ryujinx.Memory /// Throw when the memory block has already been disposed /// Throw when either or are out of range [MethodImpl(MethodImplOptions.AggressiveInlining)] - public IntPtr GetPointer(ulong offset, ulong size) => GetPointerInternal(offset, size); + public nint GetPointer(ulong offset, ulong size) => GetPointerInternal(offset, size); [MethodImpl(MethodImplOptions.AggressiveInlining)] - private IntPtr GetPointerInternal(ulong offset, ulong size) + private nint GetPointerInternal(ulong offset, ulong size) { - IntPtr ptr = _pointer; + nint ptr = _pointer; - ObjectDisposedException.ThrowIf(ptr == IntPtr.Zero, this); + ObjectDisposedException.ThrowIf(ptr == nint.Zero, this); ulong endOffset = offset + size; @@ -364,9 +364,9 @@ namespace Ryujinx.Memory /// Native pointer /// Offset to add /// Native pointer with the added offset - private static IntPtr PtrAddr(IntPtr pointer, ulong offset) + private static nint PtrAddr(nint pointer, ulong offset) { - return new IntPtr(pointer.ToInt64() + (long)offset); + return new nint(pointer.ToInt64() + (long)offset); } /// @@ -386,10 +386,10 @@ namespace Ryujinx.Memory private void FreeMemory() { - IntPtr ptr = Interlocked.Exchange(ref _pointer, IntPtr.Zero); + nint ptr = Interlocked.Exchange(ref _pointer, nint.Zero); // If pointer is null, the memory was already freed or never allocated. - if (ptr != IntPtr.Zero) + if (ptr != nint.Zero) { if (_usesSharedMemory) { @@ -403,9 +403,9 @@ namespace Ryujinx.Memory if (!_isMirror) { - IntPtr sharedMemory = Interlocked.Exchange(ref _sharedMemory, IntPtr.Zero); + nint sharedMemory = Interlocked.Exchange(ref _sharedMemory, nint.Zero); - if (sharedMemory != IntPtr.Zero) + if (sharedMemory != nint.Zero) { MemoryManagement.DestroySharedMemory(sharedMemory); } diff --git a/src/Ryujinx.Memory/MemoryManagement.cs b/src/Ryujinx.Memory/MemoryManagement.cs index 860d3f368..276cc2a4c 100644 --- a/src/Ryujinx.Memory/MemoryManagement.cs +++ b/src/Ryujinx.Memory/MemoryManagement.cs @@ -4,11 +4,11 @@ namespace Ryujinx.Memory { public static class MemoryManagement { - public static IntPtr Allocate(ulong size, bool forJit) + public static nint Allocate(ulong size, bool forJit) { if (OperatingSystem.IsWindows()) { - return MemoryManagementWindows.Allocate((IntPtr)size); + return MemoryManagementWindows.Allocate((nint)size); } else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) { @@ -20,11 +20,11 @@ namespace Ryujinx.Memory } } - public static IntPtr Reserve(ulong size, bool forJit, bool viewCompatible) + public static nint Reserve(ulong size, bool forJit, bool viewCompatible) { if (OperatingSystem.IsWindows()) { - return MemoryManagementWindows.Reserve((IntPtr)size, viewCompatible); + return MemoryManagementWindows.Reserve((nint)size, viewCompatible); } else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) { @@ -36,11 +36,11 @@ namespace Ryujinx.Memory } } - public static void Commit(IntPtr address, ulong size, bool forJit) + public static void Commit(nint address, ulong size, bool forJit) { if (OperatingSystem.IsWindows()) { - MemoryManagementWindows.Commit(address, (IntPtr)size); + MemoryManagementWindows.Commit(address, (nint)size); } else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) { @@ -52,11 +52,11 @@ namespace Ryujinx.Memory } } - public static void Decommit(IntPtr address, ulong size) + public static void Decommit(nint address, ulong size) { if (OperatingSystem.IsWindows()) { - MemoryManagementWindows.Decommit(address, (IntPtr)size); + MemoryManagementWindows.Decommit(address, (nint)size); } else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) { @@ -68,11 +68,11 @@ namespace Ryujinx.Memory } } - public static void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr address, ulong size, MemoryBlock owner) + public static void MapView(nint sharedMemory, ulong srcOffset, nint address, ulong size, MemoryBlock owner) { if (OperatingSystem.IsWindows()) { - MemoryManagementWindows.MapView(sharedMemory, srcOffset, address, (IntPtr)size, owner); + MemoryManagementWindows.MapView(sharedMemory, srcOffset, address, (nint)size, owner); } else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) { @@ -84,11 +84,11 @@ namespace Ryujinx.Memory } } - public static void UnmapView(IntPtr sharedMemory, IntPtr address, ulong size, MemoryBlock owner) + public static void UnmapView(nint sharedMemory, nint address, ulong size, MemoryBlock owner) { if (OperatingSystem.IsWindows()) { - MemoryManagementWindows.UnmapView(sharedMemory, address, (IntPtr)size, owner); + MemoryManagementWindows.UnmapView(sharedMemory, address, (nint)size, owner); } else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) { @@ -100,13 +100,13 @@ namespace Ryujinx.Memory } } - public static void Reprotect(IntPtr address, ulong size, MemoryPermission permission, bool forView, bool throwOnFail) + public static void Reprotect(nint address, ulong size, MemoryPermission permission, bool forView, bool throwOnFail) { bool result; if (OperatingSystem.IsWindows()) { - result = MemoryManagementWindows.Reprotect(address, (IntPtr)size, permission, forView); + result = MemoryManagementWindows.Reprotect(address, (nint)size, permission, forView); } else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) { @@ -123,11 +123,11 @@ namespace Ryujinx.Memory } } - public static bool Free(IntPtr address, ulong size) + public static bool Free(nint address, ulong size) { if (OperatingSystem.IsWindows()) { - return MemoryManagementWindows.Free(address, (IntPtr)size); + return MemoryManagementWindows.Free(address, (nint)size); } else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) { @@ -139,11 +139,11 @@ namespace Ryujinx.Memory } } - public static IntPtr CreateSharedMemory(ulong size, bool reserve) + public static nint CreateSharedMemory(ulong size, bool reserve) { if (OperatingSystem.IsWindows()) { - return MemoryManagementWindows.CreateSharedMemory((IntPtr)size, reserve); + return MemoryManagementWindows.CreateSharedMemory((nint)size, reserve); } else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) { @@ -155,7 +155,7 @@ namespace Ryujinx.Memory } } - public static void DestroySharedMemory(IntPtr handle) + public static void DestroySharedMemory(nint handle) { if (OperatingSystem.IsWindows()) { @@ -171,7 +171,7 @@ namespace Ryujinx.Memory } } - public static IntPtr MapSharedMemory(IntPtr handle, ulong size) + public static nint MapSharedMemory(nint handle, ulong size) { if (OperatingSystem.IsWindows()) { @@ -187,7 +187,7 @@ namespace Ryujinx.Memory } } - public static void UnmapSharedMemory(IntPtr address, ulong size) + public static void UnmapSharedMemory(nint address, ulong size) { if (OperatingSystem.IsWindows()) { diff --git a/src/Ryujinx.Memory/MemoryManagementUnix.cs b/src/Ryujinx.Memory/MemoryManagementUnix.cs index e132dbbb8..76a63a466 100644 --- a/src/Ryujinx.Memory/MemoryManagementUnix.cs +++ b/src/Ryujinx.Memory/MemoryManagementUnix.cs @@ -10,19 +10,19 @@ namespace Ryujinx.Memory [SupportedOSPlatform("macos")] static class MemoryManagementUnix { - private static readonly ConcurrentDictionary _allocations = new(); + private static readonly ConcurrentDictionary _allocations = new(); - public static IntPtr Allocate(ulong size, bool forJit) + public static nint Allocate(ulong size, bool forJit) { return AllocateInternal(size, MmapProts.PROT_READ | MmapProts.PROT_WRITE, forJit); } - public static IntPtr Reserve(ulong size, bool forJit) + public static nint Reserve(ulong size, bool forJit) { return AllocateInternal(size, MmapProts.PROT_NONE, forJit); } - private static IntPtr AllocateInternal(ulong size, MmapProts prot, bool forJit, bool shared = false) + private static nint AllocateInternal(ulong size, MmapProts prot, bool forJit, bool shared = false) { MmapFlags flags = MmapFlags.MAP_ANONYMOUS; @@ -50,7 +50,7 @@ namespace Ryujinx.Memory } } - IntPtr ptr = Mmap(IntPtr.Zero, size, prot, flags, -1, 0); + nint ptr = Mmap(nint.Zero, size, prot, flags, -1, 0); if (ptr == MAP_FAILED) { @@ -66,7 +66,7 @@ namespace Ryujinx.Memory return ptr; } - public static void Commit(IntPtr address, ulong size, bool forJit) + public static void Commit(nint address, ulong size, bool forJit) { MmapProts prot = MmapProts.PROT_READ | MmapProts.PROT_WRITE; @@ -81,7 +81,7 @@ namespace Ryujinx.Memory } } - public static void Decommit(IntPtr address, ulong size) + public static void Decommit(nint address, ulong size) { // Must be writable for madvise to work properly. if (mprotect(address, size, MmapProts.PROT_READ | MmapProts.PROT_WRITE) != 0) @@ -100,7 +100,7 @@ namespace Ryujinx.Memory } } - public static bool Reprotect(IntPtr address, ulong size, MemoryPermission permission) + public static bool Reprotect(nint address, ulong size, MemoryPermission permission) { return mprotect(address, size, GetProtection(permission)) == 0; } @@ -119,7 +119,7 @@ namespace Ryujinx.Memory }; } - public static bool Free(IntPtr address) + public static bool Free(nint address) { if (_allocations.TryRemove(address, out ulong size)) { @@ -129,12 +129,12 @@ namespace Ryujinx.Memory return false; } - public static bool Unmap(IntPtr address, ulong size) + public static bool Unmap(nint address, ulong size) { return munmap(address, size) == 0; } - public unsafe static IntPtr CreateSharedMemory(ulong size, bool reserve) + public unsafe static nint CreateSharedMemory(ulong size, bool reserve) { int fd; @@ -144,13 +144,13 @@ namespace Ryujinx.Memory fixed (byte* pMemName = memName) { - fd = shm_open((IntPtr)pMemName, 0x2 | 0x200 | 0x800 | 0x400, 384); // O_RDWR | O_CREAT | O_EXCL | O_TRUNC, 0600 + fd = shm_open((nint)pMemName, 0x2 | 0x200 | 0x800 | 0x400, 384); // O_RDWR | O_CREAT | O_EXCL | O_TRUNC, 0600 if (fd == -1) { throw new SystemException(Marshal.GetLastPInvokeErrorMessage()); } - if (shm_unlink((IntPtr)pMemName) != 0) + if (shm_unlink((nint)pMemName) != 0) { throw new SystemException(Marshal.GetLastPInvokeErrorMessage()); } @@ -162,20 +162,20 @@ namespace Ryujinx.Memory fixed (byte* pFileName = fileName) { - fd = mkstemp((IntPtr)pFileName); + fd = mkstemp((nint)pFileName); if (fd == -1) { throw new SystemException(Marshal.GetLastPInvokeErrorMessage()); } - if (unlink((IntPtr)pFileName) != 0) + if (unlink((nint)pFileName) != 0) { throw new SystemException(Marshal.GetLastPInvokeErrorMessage()); } } } - if (ftruncate(fd, (IntPtr)size) != 0) + if (ftruncate(fd, (nint)size) != 0) { throw new SystemException(Marshal.GetLastPInvokeErrorMessage()); } @@ -183,27 +183,27 @@ namespace Ryujinx.Memory return fd; } - public static void DestroySharedMemory(IntPtr handle) + public static void DestroySharedMemory(nint handle) { close(handle.ToInt32()); } - public static IntPtr MapSharedMemory(IntPtr handle, ulong size) + public static nint MapSharedMemory(nint handle, ulong size) { - return Mmap(IntPtr.Zero, size, MmapProts.PROT_READ | MmapProts.PROT_WRITE, MmapFlags.MAP_SHARED, handle.ToInt32(), 0); + return Mmap(nint.Zero, size, MmapProts.PROT_READ | MmapProts.PROT_WRITE, MmapFlags.MAP_SHARED, handle.ToInt32(), 0); } - public static void UnmapSharedMemory(IntPtr address, ulong size) + public static void UnmapSharedMemory(nint address, ulong size) { munmap(address, size); } - public static void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr location, ulong size) + public static void MapView(nint sharedMemory, ulong srcOffset, nint location, ulong size) { Mmap(location, size, MmapProts.PROT_READ | MmapProts.PROT_WRITE, MmapFlags.MAP_FIXED | MmapFlags.MAP_SHARED, sharedMemory.ToInt32(), (long)srcOffset); } - public static void UnmapView(IntPtr location, ulong size) + public static void UnmapView(nint location, ulong size) { Mmap(location, size, MmapProts.PROT_NONE, MmapFlags.MAP_FIXED | MmapFlags.MAP_PRIVATE | MmapFlags.MAP_ANONYMOUS | MmapFlags.MAP_NORESERVE, -1, 0); } diff --git a/src/Ryujinx.Memory/MemoryManagementWindows.cs b/src/Ryujinx.Memory/MemoryManagementWindows.cs index 742ef6c96..468355dd0 100644 --- a/src/Ryujinx.Memory/MemoryManagementWindows.cs +++ b/src/Ryujinx.Memory/MemoryManagementWindows.cs @@ -12,16 +12,16 @@ namespace Ryujinx.Memory private static readonly PlaceholderManager _placeholders = new(); - public static IntPtr Allocate(IntPtr size) + public static nint Allocate(nint size) { return AllocateInternal(size, AllocationType.Reserve | AllocationType.Commit); } - public static IntPtr Reserve(IntPtr size, bool viewCompatible) + public static nint Reserve(nint size, bool viewCompatible) { if (viewCompatible) { - IntPtr baseAddress = AllocateInternal2(size, AllocationType.Reserve | AllocationType.ReservePlaceholder); + nint baseAddress = AllocateInternal2(size, AllocationType.Reserve | AllocationType.ReservePlaceholder); _placeholders.ReserveRange((ulong)baseAddress, (ulong)size); @@ -31,11 +31,11 @@ namespace Ryujinx.Memory return AllocateInternal(size, AllocationType.Reserve); } - private static IntPtr AllocateInternal(IntPtr size, AllocationType flags = 0) + private static nint AllocateInternal(nint size, AllocationType flags = 0) { - IntPtr ptr = WindowsApi.VirtualAlloc(IntPtr.Zero, size, flags, MemoryProtection.ReadWrite); + nint ptr = WindowsApi.VirtualAlloc(nint.Zero, size, flags, MemoryProtection.ReadWrite); - if (ptr == IntPtr.Zero) + if (ptr == nint.Zero) { throw new SystemException(Marshal.GetLastPInvokeErrorMessage()); } @@ -43,11 +43,11 @@ namespace Ryujinx.Memory return ptr; } - private static IntPtr AllocateInternal2(IntPtr size, AllocationType flags = 0) + private static nint AllocateInternal2(nint size, AllocationType flags = 0) { - IntPtr ptr = WindowsApi.VirtualAlloc2(WindowsApi.CurrentProcessHandle, IntPtr.Zero, size, flags, MemoryProtection.NoAccess, IntPtr.Zero, 0); + nint ptr = WindowsApi.VirtualAlloc2(WindowsApi.CurrentProcessHandle, nint.Zero, size, flags, MemoryProtection.NoAccess, nint.Zero, 0); - if (ptr == IntPtr.Zero) + if (ptr == nint.Zero) { throw new SystemException(Marshal.GetLastPInvokeErrorMessage()); } @@ -55,15 +55,15 @@ namespace Ryujinx.Memory return ptr; } - public static void Commit(IntPtr location, IntPtr size) + public static void Commit(nint location, nint size) { - if (WindowsApi.VirtualAlloc(location, size, AllocationType.Commit, MemoryProtection.ReadWrite) == IntPtr.Zero) + if (WindowsApi.VirtualAlloc(location, size, AllocationType.Commit, MemoryProtection.ReadWrite) == nint.Zero) { throw new SystemException(Marshal.GetLastPInvokeErrorMessage()); } } - public static void Decommit(IntPtr location, IntPtr size) + public static void Decommit(nint location, nint size) { if (!WindowsApi.VirtualFree(location, size, AllocationType.Decommit)) { @@ -71,17 +71,17 @@ namespace Ryujinx.Memory } } - public static void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size, MemoryBlock owner) + public static void MapView(nint sharedMemory, ulong srcOffset, nint location, nint size, MemoryBlock owner) { _placeholders.MapView(sharedMemory, srcOffset, location, size, owner); } - public static void UnmapView(IntPtr sharedMemory, IntPtr location, IntPtr size, MemoryBlock owner) + public static void UnmapView(nint sharedMemory, nint location, nint size, MemoryBlock owner) { _placeholders.UnmapView(sharedMemory, location, size, owner); } - public static bool Reprotect(IntPtr address, IntPtr size, MemoryPermission permission, bool forView) + public static bool Reprotect(nint address, nint size, MemoryPermission permission, bool forView) { if (forView) { @@ -93,26 +93,26 @@ namespace Ryujinx.Memory } } - public static bool Free(IntPtr address, IntPtr size) + public static bool Free(nint address, nint size) { _placeholders.UnreserveRange((ulong)address, (ulong)size); - return WindowsApi.VirtualFree(address, IntPtr.Zero, AllocationType.Release); + return WindowsApi.VirtualFree(address, nint.Zero, AllocationType.Release); } - public static IntPtr CreateSharedMemory(IntPtr size, bool reserve) + public static nint CreateSharedMemory(nint size, bool reserve) { var prot = reserve ? FileMapProtection.SectionReserve : FileMapProtection.SectionCommit; - IntPtr handle = WindowsApi.CreateFileMapping( + nint handle = WindowsApi.CreateFileMapping( WindowsApi.InvalidHandleValue, - IntPtr.Zero, + nint.Zero, FileMapProtection.PageReadWrite | prot, (uint)(size.ToInt64() >> 32), (uint)size.ToInt64(), null); - if (handle == IntPtr.Zero) + if (handle == nint.Zero) { throw new SystemException(Marshal.GetLastPInvokeErrorMessage()); } @@ -120,7 +120,7 @@ namespace Ryujinx.Memory return handle; } - public static void DestroySharedMemory(IntPtr handle) + public static void DestroySharedMemory(nint handle) { if (!WindowsApi.CloseHandle(handle)) { @@ -128,11 +128,11 @@ namespace Ryujinx.Memory } } - public static IntPtr MapSharedMemory(IntPtr handle) + public static nint MapSharedMemory(nint handle) { - IntPtr ptr = WindowsApi.MapViewOfFile(handle, 4 | 2, 0, 0, IntPtr.Zero); + nint ptr = WindowsApi.MapViewOfFile(handle, 4 | 2, 0, 0, nint.Zero); - if (ptr == IntPtr.Zero) + if (ptr == nint.Zero) { throw new SystemException(Marshal.GetLastPInvokeErrorMessage()); } @@ -140,7 +140,7 @@ namespace Ryujinx.Memory return ptr; } - public static void UnmapSharedMemory(IntPtr address) + public static void UnmapSharedMemory(nint address) { if (!WindowsApi.UnmapViewOfFile(address)) { diff --git a/src/Ryujinx.Memory/MemoryManagerUnixHelper.cs b/src/Ryujinx.Memory/MemoryManagerUnixHelper.cs index 43888c85b..e5a0b7a4d 100644 --- a/src/Ryujinx.Memory/MemoryManagerUnixHelper.cs +++ b/src/Ryujinx.Memory/MemoryManagerUnixHelper.cs @@ -44,7 +44,7 @@ namespace Ryujinx.Memory O_SYNC = 256, } - public const IntPtr MAP_FAILED = -1; + public const nint MAP_FAILED = -1; private const int MAP_ANONYMOUS_LINUX_GENERIC = 0x20; private const int MAP_NORESERVE_LINUX_GENERIC = 0x4000; @@ -57,37 +57,37 @@ namespace Ryujinx.Memory public const int MADV_REMOVE = 9; [LibraryImport("libc", EntryPoint = "mmap", SetLastError = true)] - private static partial IntPtr Internal_mmap(IntPtr address, ulong length, MmapProts prot, int flags, int fd, long offset); + private static partial nint Internal_mmap(nint address, ulong length, MmapProts prot, int flags, int fd, long offset); [LibraryImport("libc", SetLastError = true)] - public static partial int mprotect(IntPtr address, ulong length, MmapProts prot); + public static partial int mprotect(nint address, ulong length, MmapProts prot); [LibraryImport("libc", SetLastError = true)] - public static partial int munmap(IntPtr address, ulong length); + public static partial int munmap(nint address, ulong length); [LibraryImport("libc", SetLastError = true)] - public static partial IntPtr mremap(IntPtr old_address, ulong old_size, ulong new_size, int flags, IntPtr new_address); + public static partial nint mremap(nint old_address, ulong old_size, ulong new_size, int flags, nint new_address); [LibraryImport("libc", SetLastError = true)] - public static partial int madvise(IntPtr address, ulong size, int advice); + public static partial int madvise(nint address, ulong size, int advice); [LibraryImport("libc", SetLastError = true)] - public static partial int mkstemp(IntPtr template); + public static partial int mkstemp(nint template); [LibraryImport("libc", SetLastError = true)] - public static partial int unlink(IntPtr pathname); + public static partial int unlink(nint pathname); [LibraryImport("libc", SetLastError = true)] - public static partial int ftruncate(int fildes, IntPtr length); + public static partial int ftruncate(int fildes, nint length); [LibraryImport("libc", SetLastError = true)] public static partial int close(int fd); [LibraryImport("libc", SetLastError = true)] - public static partial int shm_open(IntPtr name, int oflag, uint mode); + public static partial int shm_open(nint name, int oflag, uint mode); [LibraryImport("libc", SetLastError = true)] - public static partial int shm_unlink(IntPtr name); + public static partial int shm_unlink(nint name); private static int MmapFlagsToSystemFlags(MmapFlags flags) { @@ -164,7 +164,7 @@ namespace Ryujinx.Memory return result; } - public static IntPtr Mmap(IntPtr address, ulong length, MmapProts prot, MmapFlags flags, int fd, long offset) + public static nint Mmap(nint address, ulong length, MmapProts prot, MmapFlags flags, int fd, long offset) { return Internal_mmap(address, length, prot, MmapFlagsToSystemFlags(flags), fd, offset); } diff --git a/src/Ryujinx.Memory/Ryujinx.Memory.csproj b/src/Ryujinx.Memory/Ryujinx.Memory.csproj index 8310a3e5c..eda3ed17f 100644 --- a/src/Ryujinx.Memory/Ryujinx.Memory.csproj +++ b/src/Ryujinx.Memory/Ryujinx.Memory.csproj @@ -1,8 +1,8 @@  - net8.0 true + $(DefaultItemExcludes);._* diff --git a/src/Ryujinx.Memory/SparseMemoryBlock.cs b/src/Ryujinx.Memory/SparseMemoryBlock.cs new file mode 100644 index 000000000..5717d7b64 --- /dev/null +++ b/src/Ryujinx.Memory/SparseMemoryBlock.cs @@ -0,0 +1,125 @@ +using Ryujinx.Common; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; + +namespace Ryujinx.Memory +{ + public delegate void PageInitDelegate(Span page); + + public class SparseMemoryBlock : IDisposable + { + private const ulong MapGranularity = 1UL << 17; + + private readonly PageInitDelegate _pageInit; + + private readonly Lock _lock = new(); + private readonly ulong _pageSize; + private readonly MemoryBlock _reservedBlock; + private readonly List _mappedBlocks; + private ulong _mappedBlockUsage; + + private readonly ulong[] _mappedPageBitmap; + + public MemoryBlock Block => _reservedBlock; + + public SparseMemoryBlock(ulong size, PageInitDelegate pageInit, MemoryBlock fill) + { + _pageSize = MemoryBlock.GetPageSize(); + _reservedBlock = new MemoryBlock(size, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible); + _mappedBlocks = new List(); + _pageInit = pageInit; + + int pages = (int)BitUtils.DivRoundUp(size, _pageSize); + int bitmapEntries = BitUtils.DivRoundUp(pages, 64); + _mappedPageBitmap = new ulong[bitmapEntries]; + + if (fill != null) + { + // Fill the block with mappings from the fill block. + + if (fill.Size % _pageSize != 0) + { + throw new ArgumentException("Fill memory block should be page aligned.", nameof(fill)); + } + + int repeats = (int)BitUtils.DivRoundUp(size, fill.Size); + + ulong offset = 0; + for (int i = 0; i < repeats; i++) + { + _reservedBlock.MapView(fill, 0, offset, Math.Min(fill.Size, size - offset)); + offset += fill.Size; + } + } + + // If a fill block isn't provided, the pages that aren't EnsureMapped are unmapped. + // The caller can rely on signal handler to fill empty pages instead. + } + + private void MapPage(ulong pageOffset) + { + // Take a page from the latest mapped block. + MemoryBlock block = _mappedBlocks.LastOrDefault(); + + if (block == null || _mappedBlockUsage == MapGranularity) + { + // Need to map some more memory. + + block = new MemoryBlock(MapGranularity, MemoryAllocationFlags.Mirrorable); + + _mappedBlocks.Add(block); + + _mappedBlockUsage = 0; + } + + _pageInit(block.GetSpan(_mappedBlockUsage, (int)_pageSize)); + _reservedBlock.MapView(block, _mappedBlockUsage, pageOffset, _pageSize); + + _mappedBlockUsage += _pageSize; + } + + public void EnsureMapped(ulong offset) + { + int pageIndex = (int)(offset / _pageSize); + int bitmapIndex = pageIndex >> 6; + + ref ulong entry = ref _mappedPageBitmap[bitmapIndex]; + ulong bit = 1UL << (pageIndex & 63); + + if ((Volatile.Read(ref entry) & bit) == 0) + { + // Not mapped. + + lock (_lock) + { + // Check the bit while locked to make sure that this only happens once. + + ulong lockedEntry = Volatile.Read(ref entry); + + if ((lockedEntry & bit) == 0) + { + MapPage(offset & ~(_pageSize - 1)); + + lockedEntry |= bit; + + Interlocked.Exchange(ref entry, lockedEntry); + } + } + } + } + + public void Dispose() + { + _reservedBlock.Dispose(); + + foreach (MemoryBlock block in _mappedBlocks) + { + block.Dispose(); + } + + GC.SuppressFinalize(this); + } + } +} diff --git a/src/Ryujinx.Memory/Tracking/RegionHandle.cs b/src/Ryujinx.Memory/Tracking/RegionHandle.cs index a94ffa43c..4e81a9723 100644 --- a/src/Ryujinx.Memory/Tracking/RegionHandle.cs +++ b/src/Ryujinx.Memory/Tracking/RegionHandle.cs @@ -51,7 +51,7 @@ namespace Ryujinx.Memory.Tracking private event Action OnDirty; - private readonly object _preActionLock = new(); + private readonly Lock _preActionLock = new(); private RegionSignal _preAction; // Action to perform before a read or write. This will block the memory access. private PreciseRegionSignal _preciseAction; // Action to perform on a precise read or write. private readonly List _regions; diff --git a/src/Ryujinx.Memory/WindowsShared/PlaceholderManager.cs b/src/Ryujinx.Memory/WindowsShared/PlaceholderManager.cs index b68a076c4..2a294bba9 100644 --- a/src/Ryujinx.Memory/WindowsShared/PlaceholderManager.cs +++ b/src/Ryujinx.Memory/WindowsShared/PlaceholderManager.cs @@ -18,7 +18,7 @@ namespace Ryujinx.Memory.WindowsShared private readonly MappingTree _mappings; private readonly MappingTree _protections; - private readonly IntPtr _partialUnmapStatePtr; + private readonly nint _partialUnmapStatePtr; private readonly Thread _partialUnmapTrimThread; /// @@ -100,7 +100,7 @@ namespace Ryujinx.Memory.WindowsShared if (IsMapped(node.Value)) { - if (!WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (IntPtr)node.Start, 2)) + if (!WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (nint)node.Start, 2)) { throw new WindowsApiException("UnmapViewOfFile2"); } @@ -126,7 +126,7 @@ namespace Ryujinx.Memory.WindowsShared /// Address to map the view into /// Size of the view in bytes /// Memory block that owns the mapping - public void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size, MemoryBlock owner) + public void MapView(nint sharedMemory, ulong srcOffset, nint location, nint size, MemoryBlock owner) { ref var partialUnmapLock = ref GetPartialUnmapState().PartialUnmapLock; partialUnmapLock.AcquireReaderLock(); @@ -151,7 +151,7 @@ namespace Ryujinx.Memory.WindowsShared /// Size of the view in bytes /// Indicates if the memory protections should be updated after the map /// Thrown when the Windows API returns an error mapping the memory - private void MapViewInternal(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size, bool updateProtection) + private void MapViewInternal(nint sharedMemory, ulong srcOffset, nint location, nint size, bool updateProtection) { SplitForMap((ulong)location, (ulong)size, srcOffset); @@ -163,10 +163,10 @@ namespace Ryujinx.Memory.WindowsShared size, 0x4000, MemoryProtection.ReadWrite, - IntPtr.Zero, + nint.Zero, 0); - if (ptr == IntPtr.Zero) + if (ptr == nint.Zero) { throw new WindowsApiException("MapViewOfFile3"); } @@ -210,8 +210,8 @@ namespace Ryujinx.Memory.WindowsShared if (overlapStartsBefore && overlapEndsAfter) { CheckFreeResult(WindowsApi.VirtualFree( - (IntPtr)address, - (IntPtr)size, + (nint)address, + (nint)size, AllocationType.Release | AllocationType.PreservePlaceholder)); _mappings.Add(new RangeNode(overlapStart, address, overlapValue)); @@ -222,8 +222,8 @@ namespace Ryujinx.Memory.WindowsShared ulong overlappedSize = overlapEnd - address; CheckFreeResult(WindowsApi.VirtualFree( - (IntPtr)address, - (IntPtr)overlappedSize, + (nint)address, + (nint)overlappedSize, AllocationType.Release | AllocationType.PreservePlaceholder)); _mappings.Add(new RangeNode(overlapStart, address, overlapValue)); @@ -233,8 +233,8 @@ namespace Ryujinx.Memory.WindowsShared ulong overlappedSize = endAddress - overlapStart; CheckFreeResult(WindowsApi.VirtualFree( - (IntPtr)overlapStart, - (IntPtr)overlappedSize, + (nint)overlapStart, + (nint)overlappedSize, AllocationType.Release | AllocationType.PreservePlaceholder)); _mappings.Add(new RangeNode(endAddress, overlapEnd, AddBackingOffset(overlapValue, overlappedSize))); @@ -255,7 +255,7 @@ namespace Ryujinx.Memory.WindowsShared /// Address to unmap /// Size of the region to unmap in bytes /// Memory block that owns the mapping - public void UnmapView(IntPtr sharedMemory, IntPtr location, IntPtr size, MemoryBlock owner) + public void UnmapView(nint sharedMemory, nint location, nint size, MemoryBlock owner) { ref var partialUnmapLock = ref GetPartialUnmapState().PartialUnmapLock; partialUnmapLock.AcquireReaderLock(); @@ -283,7 +283,7 @@ namespace Ryujinx.Memory.WindowsShared /// Memory block that owns the mapping /// Indicates if the memory protections should be updated after the unmap /// Thrown when the Windows API returns an error unmapping or remapping the memory - private void UnmapViewInternal(IntPtr sharedMemory, IntPtr location, IntPtr size, MemoryBlock owner, bool updateProtection) + private void UnmapViewInternal(nint sharedMemory, nint location, nint size, MemoryBlock owner, bool updateProtection) { ulong startAddress = (ulong)location; ulong unmapSize = (ulong)size; @@ -327,7 +327,7 @@ namespace Ryujinx.Memory.WindowsShared { partialUnmapState.PartialUnmapsCount++; - if (!WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (IntPtr)overlap.Start, 2)) + if (!WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (nint)overlap.Start, 2)) { throw new WindowsApiException("UnmapViewOfFile2"); } @@ -336,7 +336,7 @@ namespace Ryujinx.Memory.WindowsShared { ulong remapSize = startAddress - overlap.Start; - MapViewInternal(sharedMemory, overlap.Value, (IntPtr)overlap.Start, (IntPtr)remapSize, updateProtection: false); + MapViewInternal(sharedMemory, overlap.Value, (nint)overlap.Start, (nint)remapSize, updateProtection: false); RestoreRangeProtection(overlap.Start, remapSize); } @@ -347,7 +347,7 @@ namespace Ryujinx.Memory.WindowsShared ulong remapAddress = overlap.Start + overlappedSize; ulong remapSize = overlap.End - endAddress; - MapViewInternal(sharedMemory, remapBackingOffset, (IntPtr)remapAddress, (IntPtr)remapSize, updateProtection: false); + MapViewInternal(sharedMemory, remapBackingOffset, (nint)remapAddress, (nint)remapSize, updateProtection: false); RestoreRangeProtection(remapAddress, remapSize); } } @@ -356,7 +356,7 @@ namespace Ryujinx.Memory.WindowsShared partialUnmapLock.DowngradeFromWriterLock(); } } - else if (!WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (IntPtr)overlap.Start, 2)) + else if (!WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (nint)overlap.Start, 2)) { throw new WindowsApiException("UnmapViewOfFile2"); } @@ -441,8 +441,8 @@ namespace Ryujinx.Memory.WindowsShared size = endAddress - address; CheckFreeResult(WindowsApi.VirtualFree( - (IntPtr)address, - (IntPtr)size, + (nint)address, + (nint)size, AllocationType.Release | AllocationType.CoalescePlaceholders)); } } @@ -454,7 +454,7 @@ namespace Ryujinx.Memory.WindowsShared /// Size of the region to reprotect in bytes /// New permissions /// True if the reprotection was successful, false otherwise - public bool ReprotectView(IntPtr address, IntPtr size, MemoryPermission permission) + public bool ReprotectView(nint address, nint size, MemoryPermission permission) { ref var partialUnmapLock = ref GetPartialUnmapState().PartialUnmapLock; partialUnmapLock.AcquireReaderLock(); @@ -478,7 +478,7 @@ namespace Ryujinx.Memory.WindowsShared /// Throw an exception instead of returning an error if the operation fails /// True if the reprotection was successful or if is true, false otherwise /// If is true, it is thrown when the Windows API returns an error reprotecting the memory - private bool ReprotectViewInternal(IntPtr address, IntPtr size, MemoryPermission permission, bool throwOnError) + private bool ReprotectViewInternal(nint address, nint size, MemoryPermission permission, bool throwOnError) { ulong reprotectAddress = (ulong)address; ulong reprotectSize = (ulong)size; @@ -514,7 +514,7 @@ namespace Ryujinx.Memory.WindowsShared mappedSize -= delta; } - if (!WindowsApi.VirtualProtect((IntPtr)mappedAddress, (IntPtr)mappedSize, WindowsApi.GetProtection(permission), out _)) + if (!WindowsApi.VirtualProtect((nint)mappedAddress, (nint)mappedSize, WindowsApi.GetProtection(permission), out _)) { if (throwOnError) { @@ -729,7 +729,7 @@ namespace Ryujinx.Memory.WindowsShared protEndAddress = endAddress; } - ReprotectViewInternal((IntPtr)protAddress, (IntPtr)(protEndAddress - protAddress), protection.Value, true); + ReprotectViewInternal((nint)protAddress, (nint)(protEndAddress - protAddress), protection.Value, true); } } } diff --git a/src/Ryujinx.Memory/WindowsShared/WindowsApi.cs b/src/Ryujinx.Memory/WindowsShared/WindowsApi.cs index 82903c05f..0d002dada 100644 --- a/src/Ryujinx.Memory/WindowsShared/WindowsApi.cs +++ b/src/Ryujinx.Memory/WindowsShared/WindowsApi.cs @@ -7,42 +7,42 @@ namespace Ryujinx.Memory.WindowsShared [SupportedOSPlatform("windows")] static partial class WindowsApi { - public static readonly IntPtr InvalidHandleValue = new(-1); - public static readonly IntPtr CurrentProcessHandle = new(-1); + public static readonly nint InvalidHandleValue = new(-1); + public static readonly nint CurrentProcessHandle = new(-1); [LibraryImport("kernel32.dll", SetLastError = true)] - public static partial IntPtr VirtualAlloc( - IntPtr lpAddress, - IntPtr dwSize, + public static partial nint VirtualAlloc( + nint lpAddress, + nint dwSize, AllocationType flAllocationType, MemoryProtection flProtect); [LibraryImport("KernelBase.dll", SetLastError = true)] - public static partial IntPtr VirtualAlloc2( - IntPtr process, - IntPtr lpAddress, - IntPtr dwSize, + public static partial nint VirtualAlloc2( + nint process, + nint lpAddress, + nint dwSize, AllocationType flAllocationType, MemoryProtection flProtect, - IntPtr extendedParameters, + nint extendedParameters, ulong parameterCount); [LibraryImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static partial bool VirtualProtect( - IntPtr lpAddress, - IntPtr dwSize, + nint lpAddress, + nint dwSize, MemoryProtection flNewProtect, out MemoryProtection lpflOldProtect); [LibraryImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] - public static partial bool VirtualFree(IntPtr lpAddress, IntPtr dwSize, AllocationType dwFreeType); + public static partial bool VirtualFree(nint lpAddress, nint dwSize, AllocationType dwFreeType); [LibraryImport("kernel32.dll", SetLastError = true, EntryPoint = "CreateFileMappingW")] - public static partial IntPtr CreateFileMapping( - IntPtr hFile, - IntPtr lpFileMappingAttributes, + public static partial nint CreateFileMapping( + nint hFile, + nint lpFileMappingAttributes, FileMapProtection flProtect, uint dwMaximumSizeHigh, uint dwMaximumSizeLow, @@ -50,35 +50,35 @@ namespace Ryujinx.Memory.WindowsShared [LibraryImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] - public static partial bool CloseHandle(IntPtr hObject); + public static partial bool CloseHandle(nint hObject); [LibraryImport("kernel32.dll", SetLastError = true)] - public static partial IntPtr MapViewOfFile( - IntPtr hFileMappingObject, + public static partial nint MapViewOfFile( + nint hFileMappingObject, uint dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, - IntPtr dwNumberOfBytesToMap); + nint dwNumberOfBytesToMap); [LibraryImport("KernelBase.dll", SetLastError = true)] - public static partial IntPtr MapViewOfFile3( - IntPtr hFileMappingObject, - IntPtr process, - IntPtr baseAddress, + public static partial nint MapViewOfFile3( + nint hFileMappingObject, + nint process, + nint baseAddress, ulong offset, - IntPtr dwNumberOfBytesToMap, + nint dwNumberOfBytesToMap, ulong allocationType, MemoryProtection dwDesiredAccess, - IntPtr extendedParameters, + nint extendedParameters, ulong parameterCount); [LibraryImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] - public static partial bool UnmapViewOfFile(IntPtr lpBaseAddress); + public static partial bool UnmapViewOfFile(nint lpBaseAddress); [LibraryImport("KernelBase.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] - public static partial bool UnmapViewOfFile2(IntPtr process, IntPtr lpBaseAddress, ulong unmapFlags); + public static partial bool UnmapViewOfFile2(nint process, nint lpBaseAddress, ulong unmapFlags); [LibraryImport("kernel32.dll")] public static partial uint GetLastError(); diff --git a/src/Ryujinx.SDL2.Common/Ryujinx.SDL2.Common.csproj b/src/Ryujinx.SDL2.Common/Ryujinx.SDL2.Common.csproj index 8e7953045..895d1a9ce 100644 --- a/src/Ryujinx.SDL2.Common/Ryujinx.SDL2.Common.csproj +++ b/src/Ryujinx.SDL2.Common/Ryujinx.SDL2.Common.csproj @@ -1,7 +1,7 @@ - net8.0 + $(DefaultItemExcludes);._* diff --git a/src/Ryujinx.SDL2.Common/SDL2Driver.cs b/src/Ryujinx.SDL2.Common/SDL2Driver.cs index 9827156d0..851c07867 100644 --- a/src/Ryujinx.SDL2.Common/SDL2Driver.cs +++ b/src/Ryujinx.SDL2.Common/SDL2Driver.cs @@ -36,7 +36,7 @@ namespace Ryujinx.SDL2.Common private ConcurrentDictionary> _registeredWindowHandlers; - private readonly object _lock = new(); + private readonly Lock _lock = new(); private SDL2Driver() { } @@ -143,7 +143,7 @@ namespace Ryujinx.SDL2.Common OnJoystickDisconnected?.Invoke(evnt.cbutton.which); } - else if (evnt.type == SDL_EventType.SDL_WINDOWEVENT || evnt.type == SDL_EventType.SDL_MOUSEBUTTONDOWN || evnt.type == SDL_EventType.SDL_MOUSEBUTTONUP) + else if (evnt.type is SDL_EventType.SDL_WINDOWEVENT or SDL_EventType.SDL_MOUSEBUTTONDOWN or SDL_EventType.SDL_MOUSEBUTTONUP) { if (_registeredWindowHandlers.TryGetValue(evnt.window.windowID, out Action handler)) { diff --git a/src/Ryujinx.ShaderTools/Ryujinx.ShaderTools.csproj b/src/Ryujinx.ShaderTools/Ryujinx.ShaderTools.csproj index ab89fb5c7..08d587f9c 100644 --- a/src/Ryujinx.ShaderTools/Ryujinx.ShaderTools.csproj +++ b/src/Ryujinx.ShaderTools/Ryujinx.ShaderTools.csproj @@ -1,9 +1,9 @@ - net8.0 Exe Debug;Release + $(DefaultItemExcludes);._* diff --git a/src/Ryujinx.Tests.Memory/Ryujinx.Tests.Memory.csproj b/src/Ryujinx.Tests.Memory/Ryujinx.Tests.Memory.csproj index f05060838..e5ebca789 100644 --- a/src/Ryujinx.Tests.Memory/Ryujinx.Tests.Memory.csproj +++ b/src/Ryujinx.Tests.Memory/Ryujinx.Tests.Memory.csproj @@ -1,8 +1,8 @@ - net8.0 false + $(DefaultItemExcludes);._* diff --git a/src/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj b/src/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj index befacfb22..f9f42416f 100644 --- a/src/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj +++ b/src/Ryujinx.Tests.Unicorn/Ryujinx.Tests.Unicorn.csproj @@ -1,9 +1,9 @@ - net8.0 true Debug;Release + $(DefaultItemExcludes);._* diff --git a/src/Ryujinx.Tests/Audio/Renderer/Server/BehaviourContextTests.cs b/src/Ryujinx.Tests/Audio/Renderer/Server/BehaviourContextTests.cs index 3e48a5b4e..0b0ed7a54 100644 --- a/src/Ryujinx.Tests/Audio/Renderer/Server/BehaviourContextTests.cs +++ b/src/Ryujinx.Tests/Audio/Renderer/Server/BehaviourContextTests.cs @@ -55,6 +55,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); Assert.AreEqual(0.70f, behaviourContext.GetAudioRendererProcessingTimeLimit()); Assert.AreEqual(1, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); @@ -83,6 +84,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); Assert.AreEqual(0.70f, behaviourContext.GetAudioRendererProcessingTimeLimit()); Assert.AreEqual(1, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); @@ -111,6 +113,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); Assert.AreEqual(0.70f, behaviourContext.GetAudioRendererProcessingTimeLimit()); Assert.AreEqual(1, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); @@ -139,6 +142,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); Assert.AreEqual(0.75f, behaviourContext.GetAudioRendererProcessingTimeLimit()); Assert.AreEqual(1, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); @@ -167,6 +171,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit()); Assert.AreEqual(2, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); @@ -195,6 +200,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit()); Assert.AreEqual(2, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); @@ -223,6 +229,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit()); Assert.AreEqual(2, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); @@ -251,6 +258,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit()); Assert.AreEqual(3, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); @@ -279,6 +287,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing()); Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit()); Assert.AreEqual(3, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); @@ -307,6 +316,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server Assert.IsTrue(behaviourContext.UseMultiTapBiquadFilterProcessing()); Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported()); Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit()); Assert.AreEqual(4, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); @@ -335,6 +345,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server Assert.IsTrue(behaviourContext.UseMultiTapBiquadFilterProcessing()); Assert.IsTrue(behaviourContext.IsNewEffectChannelMappingSupported()); Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit()); Assert.AreEqual(5, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); @@ -363,6 +374,36 @@ namespace Ryujinx.Tests.Audio.Renderer.Server Assert.IsTrue(behaviourContext.UseMultiTapBiquadFilterProcessing()); Assert.IsTrue(behaviourContext.IsNewEffectChannelMappingSupported()); Assert.IsTrue(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported()); + + Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit()); + Assert.AreEqual(5, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); + Assert.AreEqual(2, behaviourContext.GetPerformanceMetricsDataFormat()); + } + + [Test] + public void TestRevision13() + { + BehaviourContext behaviourContext = new(); + + behaviourContext.SetUserRevision(BehaviourContext.BaseRevisionMagic + BehaviourContext.Revision13); + + Assert.IsTrue(behaviourContext.IsAdpcmLoopContextBugFixed()); + Assert.IsTrue(behaviourContext.IsSplitterSupported()); + Assert.IsTrue(behaviourContext.IsLongSizePreDelaySupported()); + Assert.IsTrue(behaviourContext.IsAudioUsbDeviceOutputSupported()); + Assert.IsTrue(behaviourContext.IsFlushVoiceWaveBuffersSupported()); + Assert.IsTrue(behaviourContext.IsSplitterBugFixed()); + Assert.IsTrue(behaviourContext.IsElapsedFrameCountSupported()); + Assert.IsTrue(behaviourContext.IsDecodingBehaviourFlagSupported()); + Assert.IsTrue(behaviourContext.IsBiquadFilterEffectStateClearBugFixed()); + Assert.IsTrue(behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported()); + Assert.IsTrue(behaviourContext.IsWaveBufferVersion2Supported()); + Assert.IsTrue(behaviourContext.IsEffectInfoVersion2Supported()); + Assert.IsTrue(behaviourContext.UseMultiTapBiquadFilterProcessing()); + Assert.IsTrue(behaviourContext.IsNewEffectChannelMappingSupported()); + Assert.IsTrue(behaviourContext.IsBiquadFilterParameterForSplitterEnabled()); + Assert.IsTrue(behaviourContext.IsSplitterPrevVolumeResetSupported()); Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit()); Assert.AreEqual(5, behaviourContext.GetCommandProcessingTimeEstimatorVersion()); diff --git a/src/Ryujinx.Tests/Cpu/CpuContext.cs b/src/Ryujinx.Tests/Cpu/CpuContext.cs index 96b4965a2..81e8ba8c9 100644 --- a/src/Ryujinx.Tests/Cpu/CpuContext.cs +++ b/src/Ryujinx.Tests/Cpu/CpuContext.cs @@ -1,3 +1,4 @@ +using ARMeilleure.Common; using ARMeilleure.Memory; using ARMeilleure.State; using ARMeilleure.Translation; @@ -12,7 +13,7 @@ namespace Ryujinx.Tests.Cpu public CpuContext(IMemoryManager memory, bool for64Bit) { - _translator = new Translator(new JitMemoryAllocator(), memory, for64Bit); + _translator = new Translator(new JitMemoryAllocator(), memory, AddressTable.CreateForArm(for64Bit, memory.Type)); memory.UnmapEvent += UnmapHandler; } diff --git a/src/Ryujinx.Tests/Cpu/EnvironmentTests.cs b/src/Ryujinx.Tests/Cpu/EnvironmentTests.cs index 2a4775a31..43c84c193 100644 --- a/src/Ryujinx.Tests/Cpu/EnvironmentTests.cs +++ b/src/Ryujinx.Tests/Cpu/EnvironmentTests.cs @@ -1,3 +1,5 @@ +using ARMeilleure.Common; +using ARMeilleure.Memory; using ARMeilleure.Translation; using NUnit.Framework; using Ryujinx.Cpu.Jit; @@ -17,7 +19,10 @@ namespace Ryujinx.Tests.Cpu private static void EnsureTranslator() { // Create a translator, as one is needed to register the signal handler or emit methods. - _translator ??= new Translator(new JitMemoryAllocator(), new MockMemoryManager(), true); + _translator ??= new Translator( + new JitMemoryAllocator(), + new MockMemoryManager(), + AddressTable.CreateForArm(true, MemoryManagerType.SoftwarePageTable)); } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] diff --git a/src/Ryujinx.Tests/Memory/MockMemoryManager.cs b/src/Ryujinx.Tests/Memory/MockMemoryManager.cs index 20c318de6..207d28f50 100644 --- a/src/Ryujinx.Tests/Memory/MockMemoryManager.cs +++ b/src/Ryujinx.Tests/Memory/MockMemoryManager.cs @@ -7,7 +7,7 @@ namespace Ryujinx.Tests.Memory { public int AddressSpaceBits => throw new NotImplementedException(); - public IntPtr PageTablePointer => throw new NotImplementedException(); + public nint PageTablePointer => throw new NotImplementedException(); public MemoryManagerType Type => MemoryManagerType.HostMappedUnsafe; diff --git a/src/Ryujinx.Tests/Memory/PartialUnmaps.cs b/src/Ryujinx.Tests/Memory/PartialUnmaps.cs index ace68e5c2..3e5b47423 100644 --- a/src/Ryujinx.Tests/Memory/PartialUnmaps.cs +++ b/src/Ryujinx.Tests/Memory/PartialUnmaps.cs @@ -1,3 +1,5 @@ +using ARMeilleure.Common; +using ARMeilleure.Memory; using ARMeilleure.Signal; using ARMeilleure.Translation; using NUnit.Framework; @@ -53,7 +55,10 @@ namespace Ryujinx.Tests.Memory private static void EnsureTranslator() { // Create a translator, as one is needed to register the signal handler or emit methods. - _translator ??= new Translator(new JitMemoryAllocator(), new MockMemoryManager(), true); + _translator ??= new Translator( + new JitMemoryAllocator(), + new MockMemoryManager(), + AddressTable.CreateForArm(true, MemoryManagerType.SoftwarePageTable)); } [Test] @@ -237,7 +242,7 @@ namespace Ryujinx.Tests.Memory mainMemory.MapView(backing, 0, 0, vaSize); var writeFunc = TestMethods.GenerateDebugNativeWriteLoop(); - IntPtr writePtr = mainMemory.GetPointer(vaSize - 0x1000, 4); + nint writePtr = mainMemory.GetPointer(vaSize - 0x1000, 4); Thread testThread = new(() => { @@ -330,7 +335,7 @@ namespace Ryujinx.Tests.Memory fixed (void* localMap = &state.LocalCounts) { - var getOrReserve = TestMethods.GenerateDebugThreadLocalMapGetOrReserve((IntPtr)localMap); + var getOrReserve = TestMethods.GenerateDebugThreadLocalMapGetOrReserve((nint)localMap); for (int i = 0; i < ThreadLocalMap.MapSize; i++) { diff --git a/src/Ryujinx.Tests/Ryujinx.Tests.csproj b/src/Ryujinx.Tests/Ryujinx.Tests.csproj index 3be9787a3..daa2b4287 100644 --- a/src/Ryujinx.Tests/Ryujinx.Tests.csproj +++ b/src/Ryujinx.Tests/Ryujinx.Tests.csproj @@ -1,7 +1,6 @@ - net8.0 Exe false @@ -10,6 +9,7 @@ linux Debug;Release $(MSBuildProjectDirectory)\.runsettings + $(DefaultItemExcludes);._* diff --git a/src/Ryujinx.UI.Common/App/ApplicationAddedEventArgs.cs b/src/Ryujinx.UI.Common/App/ApplicationAddedEventArgs.cs deleted file mode 100644 index 58e066b9d..000000000 --- a/src/Ryujinx.UI.Common/App/ApplicationAddedEventArgs.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace Ryujinx.UI.App.Common -{ - public class ApplicationAddedEventArgs : EventArgs - { - public ApplicationData AppData { get; set; } - } -} diff --git a/src/Ryujinx.UI.Common/App/ApplicationData.cs b/src/Ryujinx.UI.Common/App/ApplicationData.cs index 08bd2677d..657b9a022 100644 --- a/src/Ryujinx.UI.Common/App/ApplicationData.cs +++ b/src/Ryujinx.UI.Common/App/ApplicationData.cs @@ -19,12 +19,16 @@ namespace Ryujinx.UI.App.Common { public class ApplicationData { + public static Func LocalizedNever { get; set; } = () => "Never"; + public bool Favorite { get; set; } public byte[] Icon { get; set; } public string Name { get; set; } = "Unknown"; public ulong Id { get; set; } public string Developer { get; set; } = "Unknown"; public string Version { get; set; } = "0"; + public int PlayerCount { get; set; } + public int GameCount { get; set; } public TimeSpan TimePlayed { get; set; } public DateTime? LastPlayed { get; set; } public string FileExtension { get; set; } @@ -34,7 +38,7 @@ namespace Ryujinx.UI.App.Common public string TimePlayedString => ValueFormatUtils.FormatTimeSpan(TimePlayed); - public string LastPlayedString => ValueFormatUtils.FormatDateTime(LastPlayed); + public string LastPlayedString => ValueFormatUtils.FormatDateTime(LastPlayed)?.Replace(" ", "\n") ?? LocalizedNever(); public string FileSizeString => ValueFormatUtils.FormatFileSize(FileSize); @@ -160,7 +164,7 @@ namespace Ryujinx.UI.App.Common NsoReader reader = new(); reader.Initialize(nsoFile.Release().AsStorage().AsFile(OpenMode.Read)).ThrowIfFailure(); - return BitConverter.ToString(reader.Header.ModuleId.ItemsRo.ToArray()).Replace("-", "").ToUpper()[..16]; + return Convert.ToHexString(reader.Header.ModuleId.ItemsRo.ToArray()).Replace("-", string.Empty).ToUpper()[..16]; } } } diff --git a/src/Ryujinx.UI.Common/App/ApplicationLibrary.cs b/src/Ryujinx.UI.Common/App/ApplicationLibrary.cs index 2defc1f6c..cb6467f5e 100644 --- a/src/Ryujinx.UI.Common/App/ApplicationLibrary.cs +++ b/src/Ryujinx.UI.Common/App/ApplicationLibrary.cs @@ -1,6 +1,7 @@ +using DynamicData; +using Gommon; using LibHac; using LibHac.Common; -using LibHac.Common.Keys; using LibHac.Fs; using LibHac.Fs.Fsa; using LibHac.FsSystem; @@ -10,23 +11,30 @@ using LibHac.Tools.Fs; using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Common.Configuration; +using Ryujinx.Common.Configuration.Multiplayer; using Ryujinx.Common.Logging; using Ryujinx.Common.Utilities; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.Loaders.Npdm; using Ryujinx.HLE.Loaders.Processes.Extensions; +using Ryujinx.HLE.Utilities; using Ryujinx.UI.Common.Configuration; using Ryujinx.UI.Common.Configuration.System; +using Ryujinx.UI.Common.Helper; +using Ryujinx.UI.Common.Models; using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net.Http; using System.Reflection; using System.Text; using System.Text.Json; using System.Threading; +using System.Threading.Tasks; using ContentType = LibHac.Ncm.ContentType; +using MissingKeyException = LibHac.Common.Keys.MissingKeyException; using Path = System.IO.Path; using TimeSpan = System.TimeSpan; @@ -34,9 +42,14 @@ namespace Ryujinx.UI.App.Common { public class ApplicationLibrary { + public const string DefaultLanPlayWebHost = "ryuldnweb.vudjun.com"; public Language DesiredLanguage { get; set; } - public event EventHandler ApplicationAdded; public event EventHandler ApplicationCountUpdated; + public event EventHandler LdnGameDataReceived; + + public readonly IObservableCache Applications; + public readonly IObservableCache<(TitleUpdateModel TitleUpdate, bool IsSelected), TitleUpdateModel> TitleUpdates; + public readonly IObservableCache<(DownloadableContentModel Dlc, bool IsEnabled), DownloadableContentModel> DownloadableContents; private readonly byte[] _nspIcon; private readonly byte[] _xciIcon; @@ -47,14 +60,22 @@ namespace Ryujinx.UI.App.Common private readonly VirtualFileSystem _virtualFileSystem; private readonly IntegrityCheckLevel _checkLevel; private CancellationTokenSource _cancellationToken; + private readonly SourceCache _applications = new(it => it.Id); + private readonly SourceCache<(TitleUpdateModel TitleUpdate, bool IsSelected), TitleUpdateModel> _titleUpdates = new(it => it.TitleUpdate); + private readonly SourceCache<(DownloadableContentModel Dlc, bool IsEnabled), DownloadableContentModel> _downloadableContents = new(it => it.Dlc); private static readonly ApplicationJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + private static readonly LdnGameDataSerializerContext _ldnDataSerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); public ApplicationLibrary(VirtualFileSystem virtualFileSystem, IntegrityCheckLevel checkLevel) { _virtualFileSystem = virtualFileSystem; _checkLevel = checkLevel; + Applications = _applications.AsObservableCache(); + TitleUpdates = _titleUpdates.AsObservableCache(); + DownloadableContents = _downloadableContents.AsObservableCache(); + _nspIcon = GetResourceBytes("Ryujinx.UI.Common.Resources.Icon_NSP.png"); _xciIcon = GetResourceBytes("Ryujinx.UI.Common.Resources.Icon_XCI.png"); _ncaIcon = GetResourceBytes("Ryujinx.UI.Common.Resources.Icon_NCA.png"); @@ -64,7 +85,7 @@ namespace Ryujinx.UI.App.Common private static byte[] GetResourceBytes(string resourceName) { - Stream resourceStream = Assembly.GetCallingAssembly().GetManifestResourceStream(resourceName); + Stream resourceStream = Assembly.GetCallingAssembly().GetManifestResourceStream(resourceName)!; byte[] resourceByteArray = new byte[resourceStream.Length]; resourceStream.ReadExactly(resourceByteArray); @@ -100,7 +121,7 @@ namespace Ryujinx.UI.App.Common return data; } - /// The configured key set is missing a key. + /// The configured key set is missing a key. /// The NCA header could not be decrypted. /// The NCA version is not supported. /// An error occured while reading PFS data. @@ -168,15 +189,12 @@ namespace Ryujinx.UI.App.Common } } - if (isExeFs) - { - return GetApplicationFromExeFs(pfs, filePath); - } - - return null; + return isExeFs + ? GetApplicationFromExeFs(pfs, filePath) + : null; } - /// The configured key set is missing a key. + /// The configured key set is missing a key. /// The NCA header could not be decrypted. /// The NCA version is not supported. /// An error occured while reading PFS data. @@ -474,6 +492,144 @@ namespace Ryujinx.UI.App.Common return true; } + public bool TryGetDownloadableContentFromFile(string filePath, out List titleUpdates) + { + titleUpdates = []; + + try + { + string extension = Path.GetExtension(filePath).ToLower(); + + using FileStream file = new(filePath, FileMode.Open, FileAccess.Read); + + switch (extension) + { + case ".xci": + case ".nsp": + { + using IFileSystem pfs = PartitionFileSystemUtils.OpenApplicationFileSystem(filePath, _virtualFileSystem); + + foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) + { + using var ncaFile = new UniqueRef(); + + pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + + Nca nca = TryOpenNca(ncaFile.Get.AsStorage()); + if (nca == null) + { + continue; + } + + if (nca.Header.ContentType == NcaContentType.PublicData) + { + titleUpdates.Add(new DownloadableContentModel(nca.Header.TitleId, filePath, fileEntry.FullPath)); + } + } + + return titleUpdates.Count != 0; + } + } + } + catch (MissingKeyException exception) + { + Logger.Warning?.Print(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}"); + } + catch (InvalidDataException) + { + Logger.Warning?.Print(LogClass.Application, $"The header key is incorrect or missing and therefore the NCA header content type check has failed. Errored File: {filePath}"); + } + catch (IOException exception) + { + Logger.Warning?.Print(LogClass.Application, exception.Message); + } + catch (Exception exception) + { + Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. File: '{filePath}' Error: {exception}"); + } + + return false; + } + + public bool TryGetTitleUpdatesFromFile(string filePath, out List titleUpdates) + { + titleUpdates = []; + + try + { + string extension = Path.GetExtension(filePath).ToLower(); + + using FileStream file = new(filePath, FileMode.Open, FileAccess.Read); + + switch (extension) + { + case ".xci": + case ".nsp": + { + IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks + ? IntegrityCheckLevel.ErrorOnInvalid + : IntegrityCheckLevel.None; + + using IFileSystem pfs = + PartitionFileSystemUtils.OpenApplicationFileSystem(filePath, _virtualFileSystem); + + Dictionary updates = + pfs.GetContentData(ContentMetaType.Patch, _virtualFileSystem, checkLevel); + + if (updates.Count == 0) + { + return false; + } + + foreach ((_, ContentMetaData content) in updates) + { + Nca patchNca = content.GetNcaByType(_virtualFileSystem.KeySet, ContentType.Program); + Nca controlNca = content.GetNcaByType(_virtualFileSystem.KeySet, ContentType.Control); + + if (controlNca != null && patchNca != null) + { + ApplicationControlProperty controlData = new(); + + using UniqueRef nacpFile = new(); + + controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None) + .OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read) + .ThrowIfFailure(); + nacpFile.Get.Read(out _, 0, LibHac.Common.SpanHelpers.AsByteSpan(ref controlData), + ReadOption.None).ThrowIfFailure(); + + var displayVersion = controlData.DisplayVersionString.ToString(); + var update = new TitleUpdateModel(content.ApplicationId, content.Version.Version, + displayVersion, filePath); + + titleUpdates.Add(update); + } + } + + return true; + } + } + } + catch (MissingKeyException exception) + { + Logger.Warning?.Print(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}"); + } + catch (InvalidDataException) + { + Logger.Warning?.Print(LogClass.Application, $"The header key is incorrect or missing and therefore the NCA header content type check has failed. Errored File: {filePath}"); + } + catch (IOException exception) + { + Logger.Warning?.Print(LogClass.Application, exception.Message); + } + catch (Exception exception) + { + Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. File: '{filePath}' Error: {exception}"); + } + + return false; + } + public void CancelLoading() { _cancellationToken?.Cancel(); @@ -493,6 +649,7 @@ namespace Ryujinx.UI.App.Common int numApplicationsLoaded = 0; _cancellationToken = new CancellationTokenSource(); + _applications.Clear(); // Builds the applications list with paths to found applications List applicationPaths = new(); @@ -518,19 +675,18 @@ namespace Ryujinx.UI.App.Common EnumerationOptions options = new() { RecurseSubdirectories = true, - IgnoreInaccessible = false, + IgnoreInaccessible = false }; - IEnumerable files = Directory.EnumerateFiles(appDir, "*", options).Where(file => - { - return - (Path.GetExtension(file).ToLower() is ".nsp" && ConfigurationState.Instance.UI.ShownFileTypes.NSP.Value) || - (Path.GetExtension(file).ToLower() is ".pfs0" && ConfigurationState.Instance.UI.ShownFileTypes.PFS0.Value) || - (Path.GetExtension(file).ToLower() is ".xci" && ConfigurationState.Instance.UI.ShownFileTypes.XCI.Value) || - (Path.GetExtension(file).ToLower() is ".nca" && ConfigurationState.Instance.UI.ShownFileTypes.NCA.Value) || - (Path.GetExtension(file).ToLower() is ".nro" && ConfigurationState.Instance.UI.ShownFileTypes.NRO.Value) || - (Path.GetExtension(file).ToLower() is ".nso" && ConfigurationState.Instance.UI.ShownFileTypes.NSO.Value); - }); + IEnumerable files = Directory.EnumerateFiles(appDir, "*", options) + .Where(file => + (Path.GetExtension(file).ToLower() is ".nsp" && ConfigurationState.Instance.UI.ShownFileTypes.NSP) || + (Path.GetExtension(file).ToLower() is ".pfs0" && ConfigurationState.Instance.UI.ShownFileTypes.PFS0) || + (Path.GetExtension(file).ToLower() is ".xci" && ConfigurationState.Instance.UI.ShownFileTypes.XCI) || + (Path.GetExtension(file).ToLower() is ".nca" && ConfigurationState.Instance.UI.ShownFileTypes.NCA) || + (Path.GetExtension(file).ToLower() is ".nro" && ConfigurationState.Instance.UI.ShownFileTypes.NRO) || + (Path.GetExtension(file).ToLower() is ".nso" && ConfigurationState.Instance.UI.ShownFileTypes.NSO) + ); foreach (string app in files) { @@ -560,6 +716,7 @@ namespace Ryujinx.UI.App.Common } } + // Loops through applications list, creating a struct and then firing an event containing the struct for each application foreach (string applicationPath in applicationPaths) { @@ -570,13 +727,19 @@ namespace Ryujinx.UI.App.Common if (TryGetApplicationsFromFile(applicationPath, out List applications)) { - foreach (var application in applications) + _applications.Edit(it => { - OnApplicationAdded(new ApplicationAddedEventArgs + foreach (var application in applications) { - AppData = application, - }); - } + it.AddOrUpdate(application); + LoadDlcForApplication(application); + if (LoadTitleUpdatesForApplication(application)) + { + // Trigger a reload of the version data + RefreshApplicationInfo(application.IdBase); + } + } + }); if (applications.Count > 1) { @@ -610,9 +773,327 @@ namespace Ryujinx.UI.App.Common } } - protected void OnApplicationAdded(ApplicationAddedEventArgs e) + public async Task RefreshLdn() { - ApplicationAdded?.Invoke(null, e); + + if (ConfigurationState.Instance.Multiplayer.Mode == MultiplayerMode.LdnRyu) + { + try + { + string ldnWebHost = ConfigurationState.Instance.Multiplayer.LdnServer; + if (string.IsNullOrEmpty(ldnWebHost)) + { + ldnWebHost = DefaultLanPlayWebHost; + } + IEnumerable ldnGameDataArray = Array.Empty(); + using HttpClient httpClient = new HttpClient(); + string ldnGameDataArrayString = await httpClient.GetStringAsync($"https://{ldnWebHost}/api/public_games"); + ldnGameDataArray = JsonHelper.Deserialize(ldnGameDataArrayString, _ldnDataSerializerContext.IEnumerableLdnGameData); + var evt = new LdnGameDataReceivedEventArgs + { + LdnData = ldnGameDataArray + }; + LdnGameDataReceived?.Invoke(null, evt); + } + catch (Exception ex) + { + Logger.Warning?.Print(LogClass.Application, $"Failed to fetch the public games JSON from the API. Player and game count in the game list will be unavailable.\n{ex.Message}"); + LdnGameDataReceived?.Invoke(null, new LdnGameDataReceivedEventArgs() + { + LdnData = Array.Empty() + }); + } + } + else + { + LdnGameDataReceived?.Invoke(null, new LdnGameDataReceivedEventArgs() + { + LdnData = Array.Empty() + }); + } + } + + // Replace the currently stored DLC state for the game with the provided DLC state. + public void SaveDownloadableContentsForGame(ApplicationData application, List<(DownloadableContentModel, bool IsEnabled)> dlcs) + { + _downloadableContents.Edit(it => + { + DownloadableContentsHelper.SaveDownloadableContentsJson(application.IdBase, dlcs); + + it.Remove(it.Items.Where(item => item.Dlc.TitleIdBase == application.IdBase)); + it.AddOrUpdate(dlcs); + }); + } + + // Replace the currently stored update state for the game with the provided update state. + public void SaveTitleUpdatesForGame(ApplicationData application, List<(TitleUpdateModel, bool IsSelected)> updates) + { + _titleUpdates.Edit(it => + { + TitleUpdatesHelper.SaveTitleUpdatesJson(application.IdBase, updates); + + it.Remove(it.Items.Where(item => item.TitleUpdate.TitleIdBase == application.IdBase)); + it.AddOrUpdate(updates); + RefreshApplicationInfo(application.IdBase); + }); + } + + // Searches the provided directories for DLC NSP files that are _valid for the currently detected games in the + // library_, and then enables those DLC. + public int AutoLoadDownloadableContents(List appDirs, out int numDlcRemoved) + { + _cancellationToken = new CancellationTokenSource(); + + List dlcPaths = new(); + int newDlcLoaded = 0; + numDlcRemoved = 0; + + try + { + // Remove any downloadable content which can no longer be located on disk + Logger.Notice.Print(LogClass.Application, $"Removing non-existing Title DLCs"); + var dlcToRemove = _downloadableContents.Items + .Where(dlc => !File.Exists(dlc.Dlc.ContainerPath)) + .ToList(); + dlcToRemove.ForEach(dlc => + Logger.Warning?.Print(LogClass.Application, $"Title DLC removed: {dlc.Dlc.ContainerPath}") + ); + numDlcRemoved += dlcToRemove.Distinct().Count(); + _downloadableContents.RemoveKeys(dlcToRemove.Select(dlc => dlc.Dlc)); + + foreach (string appDir in appDirs) + { + Logger.Notice.Print(LogClass.Application, $"Auto loading DLC from: {appDir}"); + + if (_cancellationToken.Token.IsCancellationRequested) + { + return newDlcLoaded; + } + + if (!Directory.Exists(appDir)) + { + Logger.Warning?.Print(LogClass.Application, + $"The specified autoload directory \"{appDir}\" does not exist."); + + continue; + } + + try + { + EnumerationOptions options = new() + { + RecurseSubdirectories = true, + IgnoreInaccessible = false, + }; + + IEnumerable files = Directory.EnumerateFiles(appDir, "*", options).Where( + file => Path.GetExtension(file).ToLower() is ".nsp"); + + foreach (string app in files) + { + if (_cancellationToken.Token.IsCancellationRequested) + { + return newDlcLoaded; + } + + var fileInfo = new FileInfo(app); + + try + { + var fullPath = fileInfo.ResolveLinkTarget(true)?.FullName ?? fileInfo.FullName; + + dlcPaths.Add(fullPath); + } + catch (IOException exception) + { + Logger.Warning?.Print(LogClass.Application, + $"Failed to resolve the full path to file: \"{app}\" Error: {exception}"); + } + } + } + catch (UnauthorizedAccessException) + { + Logger.Warning?.Print(LogClass.Application, + $"Failed to get access to directory: \"{appDir}\""); + } + } + + var appIdLookup = Applications.Items.Select(it => it.IdBase).ToHashSet(); + + foreach (string dlcPath in dlcPaths) + { + if (_cancellationToken.Token.IsCancellationRequested) + { + return newDlcLoaded; + } + + if (TryGetDownloadableContentFromFile(dlcPath, out var foundDlcs)) + { + foreach (var dlc in foundDlcs.Where(it => appIdLookup.Contains(it.TitleIdBase))) + { + if (!_downloadableContents.Lookup(dlc).HasValue) + { + _downloadableContents.AddOrUpdate((dlc, true)); + SaveDownloadableContentsForGame(dlc.TitleIdBase); + newDlcLoaded++; + } + } + } + } + } + finally + { + _cancellationToken.Dispose(); + _cancellationToken = null; + } + + return newDlcLoaded; + } + + // Searches the provided directories for update NSP files that are _valid for the currently detected games in the + // library_, and then applies those updates. If a newly-detected update is a newer version than the currently + // selected update (or if no update is currently selected), then that update will be selected. + public int AutoLoadTitleUpdates(List appDirs, out int numUpdatesRemoved) + { + _cancellationToken = new CancellationTokenSource(); + + List updatePaths = new(); + int numUpdatesLoaded = 0; + numUpdatesRemoved = 0; + + try + { + var titleIdsToSave = new HashSet(); + var titleIdsToRefresh = new HashSet(); + + // Remove any updates which can no longer be located on disk + Logger.Notice.Print(LogClass.Application, $"Removing non-existing Title Updates"); + var updatesToRemove = _titleUpdates.Items + .Where(it => !File.Exists(it.TitleUpdate.Path)) + .ToList(); + + numUpdatesRemoved += updatesToRemove.Select(it => it.TitleUpdate).Distinct().Count(); + updatesToRemove.ForEach(ti => + Logger.Warning?.Print(LogClass.Application, $"Title update removed: {ti.TitleUpdate.Path}") + ); + _titleUpdates.RemoveKeys(updatesToRemove.Select(it => it.TitleUpdate)); + titleIdsToSave.UnionWith(updatesToRemove.Select(it => it.TitleUpdate.TitleIdBase)); + titleIdsToRefresh.UnionWith(updatesToRemove.Where(it => it.IsSelected).Select(update => update.TitleUpdate.TitleIdBase)); + + foreach (string appDir in appDirs) + { + Logger.Notice.Print(LogClass.Application, $"Auto loading updates from: {appDir}"); + + if (_cancellationToken.Token.IsCancellationRequested) + { + return numUpdatesLoaded; + } + + if (!Directory.Exists(appDir)) + { + Logger.Warning?.Print(LogClass.Application, + $"The specified autoload directory \"{appDir}\" does not exist."); + + continue; + } + + try + { + EnumerationOptions options = new() + { + RecurseSubdirectories = true, + IgnoreInaccessible = false, + }; + + IEnumerable files = Directory.EnumerateFiles(appDir, "*", options).Where( + file => Path.GetExtension(file).ToLower() is ".nsp"); + + foreach (string app in files) + { + if (_cancellationToken.Token.IsCancellationRequested) + { + return numUpdatesLoaded; + } + + var fileInfo = new FileInfo(app); + + try + { + var fullPath = fileInfo.ResolveLinkTarget(true)?.FullName ?? fileInfo.FullName; + + updatePaths.Add(fullPath); + } + catch (IOException exception) + { + Logger.Warning?.Print(LogClass.Application, + $"Failed to resolve the full path to file: \"{app}\" Error: {exception}"); + } + } + } + catch (UnauthorizedAccessException) + { + Logger.Warning?.Print(LogClass.Application, + $"Failed to get access to directory: \"{appDir}\""); + } + } + + var appIdLookup = Applications.Items.Select(it => it.IdBase).ToHashSet(); + + foreach (string updatePath in updatePaths) + { + if (_cancellationToken.Token.IsCancellationRequested) + { + return numUpdatesLoaded; + } + + if (TryGetTitleUpdatesFromFile(updatePath, out var foundUpdates)) + { + foreach (var update in foundUpdates.Where(it => appIdLookup.Contains(it.TitleIdBase))) + { + if (!_titleUpdates.Lookup(update).HasValue) + { + bool shouldSelect = AddAndAutoSelectUpdate(update); + titleIdsToSave.Add(update.TitleIdBase); + numUpdatesLoaded++; + + if (shouldSelect) + { + titleIdsToRefresh.Add(update.TitleIdBase); + } + } + } + } + } + + titleIdsToSave.ForEach(titleId => SaveTitleUpdatesForGame(titleId)); + titleIdsToRefresh.ForEach(titleId => RefreshApplicationInfo(titleId)); + } + finally + { + _cancellationToken.Dispose(); + _cancellationToken = null; + } + + return numUpdatesLoaded; + } + + private bool AddAndAutoSelectUpdate(TitleUpdateModel update) + { + if (update == null) return false; + + var currentlySelected = TitleUpdates.Items.FindFirst(it => + it.TitleUpdate.TitleIdBase == update.TitleIdBase && it.IsSelected); + + var shouldSelect = currentlySelected.Check(curr => curr.TitleUpdate?.Version < update.Version); + + _titleUpdates.AddOrUpdate((update, shouldSelect)); + + if (currentlySelected.HasValue && shouldSelect) + { + _titleUpdates.AddOrUpdate((currentlySelected.Value.TitleUpdate, false)); + } + + return shouldSelect; } protected void OnApplicationCountUpdated(ApplicationCountUpdatedEventArgs e) @@ -689,7 +1170,7 @@ namespace Ryujinx.UI.App.Common using FileStream file = new(applicationPath, FileMode.Open, FileAccess.Read); - if (extension == ".nsp" || extension == ".pfs0" || extension == ".xci") + if (extension is ".nsp" or ".pfs0" or ".xci") { try { @@ -936,5 +1417,129 @@ namespace Ryujinx.UI.App.Common return false; } + + private Nca TryOpenNca(IStorage ncaStorage) + { + try + { + return new Nca(_virtualFileSystem.KeySet, ncaStorage); + } + catch (Exception) { } + + return null; + } + + // Does a two-phase load of DLC. First reading the metadata on disk, then loading anything bundled in the game + // file itself + private void LoadDlcForApplication(ApplicationData application) + { + _downloadableContents.Edit(it => + { + var savedDlc = + DownloadableContentsHelper.LoadDownloadableContentsJson(_virtualFileSystem, application.IdBase); + it.AddOrUpdate(savedDlc); + + if (TryGetDownloadableContentFromFile(application.Path, out var bundledDlc)) + { + var savedDlcLookup = savedDlc.Select(dlc => dlc.Item1).ToHashSet(); + + bool addedNewDlc = false; + foreach (var dlc in bundledDlc) + { + if (!savedDlcLookup.Contains(dlc)) + { + addedNewDlc = true; + it.AddOrUpdate((dlc, true)); + } + } + + if (addedNewDlc) + { + var gameDlcs = it.Items.Where(dlc => dlc.Dlc.TitleIdBase == application.IdBase).ToList(); + DownloadableContentsHelper.SaveDownloadableContentsJson(application.IdBase, + gameDlcs); + } + } + }); + } + + // Does a two-phase load of updates. First reading the metadata on disk, then loading anything bundled in the game + // file itself + private bool LoadTitleUpdatesForApplication(ApplicationData application) + { + var modifiedVersion = false; + + _titleUpdates.Edit(it => + { + var savedUpdates = + TitleUpdatesHelper.LoadTitleUpdatesJson(_virtualFileSystem, application.IdBase); + it.AddOrUpdate(savedUpdates); + + var selectedUpdate = savedUpdates.FindFirst(update => update.IsSelected); + + if (TryGetTitleUpdatesFromFile(application.Path, out var bundledUpdates)) + { + var savedUpdateLookup = savedUpdates.Select(update => update.Update).ToHashSet(); + bool updatesChanged = false; + + foreach (var update in bundledUpdates.OrderByDescending(bundled => bundled.Version)) + { + if (!savedUpdateLookup.Contains(update)) + { + bool shouldSelect = false; + if (selectedUpdate.Check(su => su.Update?.Version < update.Version)) + { + shouldSelect = true; + _titleUpdates.AddOrUpdate((selectedUpdate.Value.Update, false)); + selectedUpdate = (update, true); + } + + modifiedVersion = modifiedVersion || shouldSelect; + it.AddOrUpdate((update, shouldSelect)); + + updatesChanged = true; + } + } + + if (updatesChanged) + { + var gameUpdates = it.Items.Where(update => update.TitleUpdate.TitleIdBase == application.IdBase).ToList(); + TitleUpdatesHelper.SaveTitleUpdatesJson(application.IdBase, gameUpdates); + } + } + }); + + return modifiedVersion; + } + + // Save the _currently tracked_ DLC state for the game + private void SaveDownloadableContentsForGame(ulong titleIdBase) + { + var dlcs = DownloadableContents.Items.Where(dlc => dlc.Dlc.TitleIdBase == titleIdBase).ToList(); + DownloadableContentsHelper.SaveDownloadableContentsJson(titleIdBase, dlcs); + } + + // Save the _currently tracked_ update state for the game + private void SaveTitleUpdatesForGame(ulong titleIdBase) + { + var updates = TitleUpdates.Items.Where(update => update.TitleUpdate.TitleIdBase == titleIdBase).ToList(); + TitleUpdatesHelper.SaveTitleUpdatesJson(titleIdBase, updates); + } + + // ApplicationData isnt live-updating (e.g. when an update gets applied) and so this is meant to trigger a refresh + // of its state + private void RefreshApplicationInfo(ulong appIdBase) + { + var application = _applications.Lookup(appIdBase); + + if (!application.HasValue) + return; + + if (!TryGetApplicationsFromFile(application.Value.Path, out List newApplications)) + return; + + var newApplication = newApplications.First(it => it.IdBase == appIdBase); + _applications.AddOrUpdate(newApplication); + } } } diff --git a/src/Ryujinx.UI.Common/App/LdnGameData.cs b/src/Ryujinx.UI.Common/App/LdnGameData.cs new file mode 100644 index 000000000..6c784c991 --- /dev/null +++ b/src/Ryujinx.UI.Common/App/LdnGameData.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace Ryujinx.UI.App.Common +{ + public struct LdnGameData + { + public string Id { get; set; } + public int PlayerCount { get; set; } + public int MaxPlayerCount { get; set; } + public string GameName { get; set; } + public string TitleId { get; set; } + public string Mode { get; set; } + public string Status { get; set; } + public IEnumerable Players { get; set; } + } +} diff --git a/src/Ryujinx.UI.Common/App/LdnGameDataReceivedEventArgs.cs b/src/Ryujinx.UI.Common/App/LdnGameDataReceivedEventArgs.cs new file mode 100644 index 000000000..7c7454411 --- /dev/null +++ b/src/Ryujinx.UI.Common/App/LdnGameDataReceivedEventArgs.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; + +namespace Ryujinx.UI.App.Common +{ + public class LdnGameDataReceivedEventArgs : EventArgs + { + public IEnumerable LdnData { get; set; } + } +} diff --git a/src/Ryujinx.UI.Common/App/LdnGameDataSerializerContext.cs b/src/Ryujinx.UI.Common/App/LdnGameDataSerializerContext.cs new file mode 100644 index 000000000..ce8edcdb6 --- /dev/null +++ b/src/Ryujinx.UI.Common/App/LdnGameDataSerializerContext.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Ryujinx.UI.App.Common +{ + [JsonSerializable(typeof(IEnumerable))] + internal partial class LdnGameDataSerializerContext : JsonSerializerContext + { + + } +} diff --git a/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs b/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs index 8a0be4028..027e1052b 100644 --- a/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs +++ b/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs @@ -1,8 +1,10 @@ +using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Multiplayer; using Ryujinx.Common.Logging; using Ryujinx.Common.Utilities; +using Ryujinx.HLE; using Ryujinx.UI.Common.Configuration.System; using Ryujinx.UI.Common.Configuration.UI; using System.Collections.Generic; @@ -15,7 +17,7 @@ namespace Ryujinx.UI.Common.Configuration /// /// The current version of the file format /// - public const int CurrentVersion = 51; + public const int CurrentVersion = 57; /// /// Version of the configuration file format @@ -162,11 +164,21 @@ namespace Ryujinx.UI.Common.Configuration /// public bool ShowConfirmExit { get; set; } + /// + /// ignore "Applet" dialog + /// + public bool IgnoreApplet { get; set; } + /// /// Enables or disables save window size, position and state on close. /// public bool RememberWindowState { get; set; } + /// + /// Enables or disables the redesigned title bar + /// + public bool ShowTitleBar { get; set; } + /// /// Enables hardware-accelerated rendering for Avalonia /// @@ -180,8 +192,25 @@ namespace Ryujinx.UI.Common.Configuration /// /// Enables or disables Vertical Sync /// + /// Kept for file format compatibility (to avoid possible failure when parsing configuration on old versions) + /// TODO: Remove this when those older versions aren't in use anymore. public bool EnableVsync { get; set; } + /// + /// Current VSync mode; 60 (Switch), unbounded ("Vsync off"), or custom + /// + public VSyncMode VSyncMode { get; set; } + + /// + /// Enables or disables the custom present interval + /// + public bool EnableCustomVSyncInterval { get; set; } + + /// + /// The custom present interval value + /// + public int CustomVSyncInterval { get; set; } + /// /// Enables or disables Shader cache /// @@ -207,6 +236,11 @@ namespace Ryujinx.UI.Common.Configuration /// public bool EnablePtc { get; set; } + /// + /// Enables or disables low-power profiled translation cache persistency loading + /// + public bool EnableLowPowerPtc { get; set; } + /// /// Enables or disables guest Internet access /// @@ -240,7 +274,7 @@ namespace Ryujinx.UI.Common.Configuration /// /// Expands the RAM amount on the emulated system from 4GiB to 8GiB /// - public bool ExpandRam { get; set; } + public MemoryConfiguration DramSize { get; set; } /// /// Enable or disable ignoring missing services @@ -262,6 +296,11 @@ namespace Ryujinx.UI.Common.Configuration /// public List GameDirs { get; set; } + /// + /// A list of directories containing DLC/updates the user wants to autoload during library refreshes + /// + public List AutoloadDirs { get; set; } + /// /// A list of file types to be hidden in the games List /// @@ -277,16 +316,6 @@ namespace Ryujinx.UI.Common.Configuration /// public string LanguageCode { get; set; } - /// - /// Enable or disable custom themes in the GUI - /// - public bool EnableCustomTheme { get; set; } - - /// - /// Path to custom GUI theme - /// - public string CustomThemePath { get; set; } - /// /// Chooses the base style // Not Used /// @@ -381,6 +410,21 @@ namespace Ryujinx.UI.Common.Configuration /// public string MultiplayerLanInterfaceId { get; set; } + /// + /// Disable P2p Toggle + /// + public bool MultiplayerDisableP2p { get; set; } + + /// + /// Local network passphrase, for private networks. + /// + public string MultiplayerLdnPassphrase { get; set; } + + /// + /// Custom LDN Server + /// + public string LdnServer { get; set; } + /// /// Uses Hypervisor over JIT if available /// diff --git a/src/Ryujinx.UI.Common/Configuration/ConfigurationState.Migration.cs b/src/Ryujinx.UI.Common/Configuration/ConfigurationState.Migration.cs new file mode 100644 index 000000000..a41ea2cd7 --- /dev/null +++ b/src/Ryujinx.UI.Common/Configuration/ConfigurationState.Migration.cs @@ -0,0 +1,747 @@ +using Ryujinx.Common.Configuration; +using Ryujinx.Common.Configuration.Hid; +using Ryujinx.Common.Configuration.Hid.Controller; +using Ryujinx.Common.Configuration.Hid.Keyboard; +using Ryujinx.Common.Configuration.Multiplayer; +using Ryujinx.Common.Logging; +using Ryujinx.HLE; +using Ryujinx.UI.Common.Configuration.System; +using Ryujinx.UI.Common.Configuration.UI; +using System; +using System.Collections.Generic; + +namespace Ryujinx.UI.Common.Configuration +{ + public partial class ConfigurationState + { + public void Load(ConfigurationFileFormat configurationFileFormat, string configurationFilePath) + { + bool configurationFileUpdated = false; + + if (configurationFileFormat.Version is < 0 or > ConfigurationFileFormat.CurrentVersion) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Unsupported configuration version {configurationFileFormat.Version}, loading default."); + + LoadDefault(); + } + + if (configurationFileFormat.Version < 2) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 2."); + + configurationFileFormat.SystemRegion = Region.USA; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 3) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 3."); + + configurationFileFormat.SystemTimeZone = "UTC"; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 4) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 4."); + + configurationFileFormat.MaxAnisotropy = -1; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 5) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 5."); + + configurationFileFormat.SystemTimeOffset = 0; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 8) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 8."); + + configurationFileFormat.EnablePtc = true; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 9) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 9."); + + configurationFileFormat.ColumnSort = new ColumnSort + { + SortColumnId = 0, + SortAscending = false, + }; + + configurationFileFormat.Hotkeys = new KeyboardHotkeys + { + ToggleVSyncMode = Key.F1, + }; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 10) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 10."); + + configurationFileFormat.AudioBackend = AudioBackend.OpenAl; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 11) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 11."); + + configurationFileFormat.ResScale = 1; + configurationFileFormat.ResScaleCustom = 1.0f; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 12) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 12."); + + configurationFileFormat.LoggingGraphicsDebugLevel = GraphicsDebugLevel.None; + + configurationFileUpdated = true; + } + + // configurationFileFormat.Version == 13 -> LDN1 + + if (configurationFileFormat.Version < 14) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 14."); + + configurationFileFormat.CheckUpdatesOnStart = true; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 16) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 16."); + + configurationFileFormat.EnableShaderCache = true; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 17) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 17."); + + configurationFileFormat.StartFullscreen = false; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 18) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 18."); + + configurationFileFormat.AspectRatio = AspectRatio.Fixed16x9; + + configurationFileUpdated = true; + } + + // configurationFileFormat.Version == 19 -> LDN2 + + if (configurationFileFormat.Version < 20) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 20."); + + configurationFileFormat.ShowConfirmExit = true; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 21) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 21."); + + // Initialize network config. + + configurationFileFormat.MultiplayerMode = MultiplayerMode.Disabled; + configurationFileFormat.MultiplayerLanInterfaceId = "0"; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 22) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 22."); + + configurationFileFormat.HideCursor = HideCursorMode.Never; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 24) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 24."); + + configurationFileFormat.InputConfig = new List + { + new StandardKeyboardInputConfig + { + Version = InputConfig.CurrentVersion, + Backend = InputBackendType.WindowKeyboard, + Id = "0", + PlayerIndex = PlayerIndex.Player1, + ControllerType = ControllerType.ProController, + LeftJoycon = new LeftJoyconCommonConfig + { + DpadUp = Key.Up, + DpadDown = Key.Down, + DpadLeft = Key.Left, + DpadRight = Key.Right, + ButtonMinus = Key.Minus, + ButtonL = Key.E, + ButtonZl = Key.Q, + ButtonSl = Key.Unbound, + ButtonSr = Key.Unbound, + }, + LeftJoyconStick = new JoyconConfigKeyboardStick + { + StickUp = Key.W, + StickDown = Key.S, + StickLeft = Key.A, + StickRight = Key.D, + StickButton = Key.F, + }, + RightJoycon = new RightJoyconCommonConfig + { + ButtonA = Key.Z, + ButtonB = Key.X, + ButtonX = Key.C, + ButtonY = Key.V, + ButtonPlus = Key.Plus, + ButtonR = Key.U, + ButtonZr = Key.O, + ButtonSl = Key.Unbound, + ButtonSr = Key.Unbound, + }, + RightJoyconStick = new JoyconConfigKeyboardStick + { + StickUp = Key.I, + StickDown = Key.K, + StickLeft = Key.J, + StickRight = Key.L, + StickButton = Key.H, + }, + }, + }; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 25) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 25."); + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 26) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 26."); + + configurationFileFormat.MemoryManagerMode = MemoryManagerMode.HostMappedUnsafe; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 27) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 27."); + + configurationFileFormat.EnableMouse = false; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 28) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 28."); + + configurationFileFormat.Hotkeys = new KeyboardHotkeys + { + ToggleVSyncMode = Key.F1, + Screenshot = Key.F8, + }; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 29) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 29."); + + configurationFileFormat.Hotkeys = new KeyboardHotkeys + { + ToggleVSyncMode = Key.F1, + Screenshot = Key.F8, + ShowUI = Key.F4, + }; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 30) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 30."); + + foreach (InputConfig config in configurationFileFormat.InputConfig) + { + if (config is StandardControllerInputConfig controllerConfig) + { + controllerConfig.Rumble = new RumbleConfigController + { + EnableRumble = false, + StrongRumble = 1f, + WeakRumble = 1f, + }; + } + } + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 31) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 31."); + + configurationFileFormat.BackendThreading = BackendThreading.Auto; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 32) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 32."); + + configurationFileFormat.Hotkeys = new KeyboardHotkeys + { + ToggleVSyncMode = configurationFileFormat.Hotkeys.ToggleVSyncMode, + Screenshot = configurationFileFormat.Hotkeys.Screenshot, + ShowUI = configurationFileFormat.Hotkeys.ShowUI, + Pause = Key.F5, + }; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 33) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 33."); + + configurationFileFormat.Hotkeys = new KeyboardHotkeys + { + ToggleVSyncMode = configurationFileFormat.Hotkeys.ToggleVSyncMode, + Screenshot = configurationFileFormat.Hotkeys.Screenshot, + ShowUI = configurationFileFormat.Hotkeys.ShowUI, + Pause = configurationFileFormat.Hotkeys.Pause, + ToggleMute = Key.F2, + }; + + configurationFileFormat.AudioVolume = 1; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 34) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 34."); + + configurationFileFormat.EnableInternetAccess = false; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 35) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 35."); + + foreach (InputConfig config in configurationFileFormat.InputConfig) + { + if (config is StandardControllerInputConfig controllerConfig) + { + controllerConfig.RangeLeft = 1.0f; + controllerConfig.RangeRight = 1.0f; + } + } + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 36) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 36."); + + configurationFileFormat.LoggingEnableTrace = false; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 37) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 37."); + + configurationFileFormat.ShowConsole = true; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 38) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 38."); + + configurationFileFormat.BaseStyle = "Dark"; + configurationFileFormat.GameListViewMode = 0; + configurationFileFormat.ShowNames = true; + configurationFileFormat.GridSize = 2; + configurationFileFormat.LanguageCode = "en_US"; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 39) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 39."); + + configurationFileFormat.Hotkeys = new KeyboardHotkeys + { + ToggleVSyncMode = configurationFileFormat.Hotkeys.ToggleVSyncMode, + Screenshot = configurationFileFormat.Hotkeys.Screenshot, + ShowUI = configurationFileFormat.Hotkeys.ShowUI, + Pause = configurationFileFormat.Hotkeys.Pause, + ToggleMute = configurationFileFormat.Hotkeys.ToggleMute, + ResScaleUp = Key.Unbound, + ResScaleDown = Key.Unbound, + }; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 40) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 40."); + + configurationFileFormat.GraphicsBackend = GraphicsBackend.OpenGl; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 41) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 41."); + + configurationFileFormat.Hotkeys = new KeyboardHotkeys + { + ToggleVSyncMode = configurationFileFormat.Hotkeys.ToggleVSyncMode, + Screenshot = configurationFileFormat.Hotkeys.Screenshot, + ShowUI = configurationFileFormat.Hotkeys.ShowUI, + Pause = configurationFileFormat.Hotkeys.Pause, + ToggleMute = configurationFileFormat.Hotkeys.ToggleMute, + ResScaleUp = configurationFileFormat.Hotkeys.ResScaleUp, + ResScaleDown = configurationFileFormat.Hotkeys.ResScaleDown, + VolumeUp = Key.Unbound, + VolumeDown = Key.Unbound, + }; + } + + if (configurationFileFormat.Version < 42) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 42."); + + configurationFileFormat.EnableMacroHLE = true; + } + + if (configurationFileFormat.Version < 43) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 43."); + + configurationFileFormat.UseHypervisor = true; + } + + if (configurationFileFormat.Version < 44) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 44."); + + configurationFileFormat.AntiAliasing = AntiAliasing.None; + configurationFileFormat.ScalingFilter = ScalingFilter.Bilinear; + configurationFileFormat.ScalingFilterLevel = 80; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 45) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 45."); + + configurationFileFormat.ShownFileTypes = new ShownFileTypes + { + NSP = true, + PFS0 = true, + XCI = true, + NCA = true, + NRO = true, + NSO = true, + }; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 46) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 46."); + + configurationFileFormat.MultiplayerLanInterfaceId = "0"; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 47) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 47."); + + configurationFileFormat.WindowStartup = new WindowStartup + { + WindowPositionX = 0, + WindowPositionY = 0, + WindowSizeHeight = 760, + WindowSizeWidth = 1280, + WindowMaximized = false, + }; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 48) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 48."); + + configurationFileFormat.EnableColorSpacePassthrough = false; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 49) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 49."); + + if (OperatingSystem.IsMacOS()) + { + AppDataManager.FixMacOSConfigurationFolders(); + } + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 50) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 50."); + + configurationFileFormat.EnableHardwareAcceleration = true; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 51) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 51."); + + configurationFileFormat.RememberWindowState = true; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 52) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 52."); + + configurationFileFormat.AutoloadDirs = []; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 53) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 53."); + + configurationFileFormat.EnableLowPowerPtc = false; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 54) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 54."); + + configurationFileFormat.DramSize = MemoryConfiguration.MemoryConfiguration4GiB; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 55) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 55."); + + configurationFileFormat.IgnoreApplet = false; + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 56) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 56."); + + configurationFileFormat.ShowTitleBar = !OperatingSystem.IsWindows(); + + configurationFileUpdated = true; + } + + if (configurationFileFormat.Version < 57) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 57."); + + configurationFileFormat.VSyncMode = VSyncMode.Switch; + configurationFileFormat.EnableCustomVSyncInterval = false; + + configurationFileFormat.Hotkeys = new KeyboardHotkeys + { + ToggleVSyncMode = Key.F1, + Screenshot = configurationFileFormat.Hotkeys.Screenshot, + ShowUI = configurationFileFormat.Hotkeys.ShowUI, + Pause = configurationFileFormat.Hotkeys.Pause, + ToggleMute = configurationFileFormat.Hotkeys.ToggleMute, + ResScaleUp = configurationFileFormat.Hotkeys.ResScaleUp, + ResScaleDown = configurationFileFormat.Hotkeys.ResScaleDown, + VolumeUp = configurationFileFormat.Hotkeys.VolumeUp, + VolumeDown = configurationFileFormat.Hotkeys.VolumeDown, + CustomVSyncIntervalIncrement = Key.Unbound, + CustomVSyncIntervalDecrement = Key.Unbound, + }; + + configurationFileFormat.CustomVSyncInterval = 120; + + configurationFileUpdated = true; + } + + Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog; + Graphics.ResScale.Value = configurationFileFormat.ResScale; + Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom; + Graphics.MaxAnisotropy.Value = configurationFileFormat.MaxAnisotropy; + Graphics.AspectRatio.Value = configurationFileFormat.AspectRatio; + Graphics.ShadersDumpPath.Value = configurationFileFormat.GraphicsShadersDumpPath; + Graphics.BackendThreading.Value = configurationFileFormat.BackendThreading; + Graphics.GraphicsBackend.Value = configurationFileFormat.GraphicsBackend; + Graphics.PreferredGpu.Value = configurationFileFormat.PreferredGpu; + Graphics.AntiAliasing.Value = configurationFileFormat.AntiAliasing; + Graphics.ScalingFilter.Value = configurationFileFormat.ScalingFilter; + Graphics.ScalingFilterLevel.Value = configurationFileFormat.ScalingFilterLevel; + Logger.EnableDebug.Value = configurationFileFormat.LoggingEnableDebug; + Logger.EnableStub.Value = configurationFileFormat.LoggingEnableStub; + Logger.EnableInfo.Value = configurationFileFormat.LoggingEnableInfo; + Logger.EnableWarn.Value = configurationFileFormat.LoggingEnableWarn; + Logger.EnableError.Value = configurationFileFormat.LoggingEnableError; + Logger.EnableTrace.Value = configurationFileFormat.LoggingEnableTrace; + Logger.EnableGuest.Value = configurationFileFormat.LoggingEnableGuest; + Logger.EnableFsAccessLog.Value = configurationFileFormat.LoggingEnableFsAccessLog; + Logger.FilteredClasses.Value = configurationFileFormat.LoggingFilteredClasses; + Logger.GraphicsDebugLevel.Value = configurationFileFormat.LoggingGraphicsDebugLevel; + System.Language.Value = configurationFileFormat.SystemLanguage; + System.Region.Value = configurationFileFormat.SystemRegion; + System.TimeZone.Value = configurationFileFormat.SystemTimeZone; + System.SystemTimeOffset.Value = configurationFileFormat.SystemTimeOffset; + System.EnableDockedMode.Value = configurationFileFormat.DockedMode; + EnableDiscordIntegration.Value = configurationFileFormat.EnableDiscordIntegration; + CheckUpdatesOnStart.Value = configurationFileFormat.CheckUpdatesOnStart; + ShowConfirmExit.Value = configurationFileFormat.ShowConfirmExit; + IgnoreApplet.Value = configurationFileFormat.IgnoreApplet; + RememberWindowState.Value = configurationFileFormat.RememberWindowState; + ShowTitleBar.Value = configurationFileFormat.ShowTitleBar; + EnableHardwareAcceleration.Value = configurationFileFormat.EnableHardwareAcceleration; + HideCursor.Value = configurationFileFormat.HideCursor; + Graphics.VSyncMode.Value = configurationFileFormat.VSyncMode; + Graphics.EnableCustomVSyncInterval.Value = configurationFileFormat.EnableCustomVSyncInterval; + Graphics.CustomVSyncInterval.Value = configurationFileFormat.CustomVSyncInterval; + Graphics.EnableShaderCache.Value = configurationFileFormat.EnableShaderCache; + Graphics.EnableTextureRecompression.Value = configurationFileFormat.EnableTextureRecompression; + Graphics.EnableMacroHLE.Value = configurationFileFormat.EnableMacroHLE; + Graphics.EnableColorSpacePassthrough.Value = configurationFileFormat.EnableColorSpacePassthrough; + System.EnablePtc.Value = configurationFileFormat.EnablePtc; + System.EnableLowPowerPtc.Value = configurationFileFormat.EnableLowPowerPtc; + System.EnableInternetAccess.Value = configurationFileFormat.EnableInternetAccess; + System.EnableFsIntegrityChecks.Value = configurationFileFormat.EnableFsIntegrityChecks; + System.FsGlobalAccessLogMode.Value = configurationFileFormat.FsGlobalAccessLogMode; + System.AudioBackend.Value = configurationFileFormat.AudioBackend; + System.AudioVolume.Value = configurationFileFormat.AudioVolume; + System.MemoryManagerMode.Value = configurationFileFormat.MemoryManagerMode; + System.DramSize.Value = configurationFileFormat.DramSize; + System.IgnoreMissingServices.Value = configurationFileFormat.IgnoreMissingServices; + System.UseHypervisor.Value = configurationFileFormat.UseHypervisor; + UI.GuiColumns.FavColumn.Value = configurationFileFormat.GuiColumns.FavColumn; + UI.GuiColumns.IconColumn.Value = configurationFileFormat.GuiColumns.IconColumn; + UI.GuiColumns.AppColumn.Value = configurationFileFormat.GuiColumns.AppColumn; + UI.GuiColumns.DevColumn.Value = configurationFileFormat.GuiColumns.DevColumn; + UI.GuiColumns.VersionColumn.Value = configurationFileFormat.GuiColumns.VersionColumn; + UI.GuiColumns.TimePlayedColumn.Value = configurationFileFormat.GuiColumns.TimePlayedColumn; + UI.GuiColumns.LastPlayedColumn.Value = configurationFileFormat.GuiColumns.LastPlayedColumn; + UI.GuiColumns.FileExtColumn.Value = configurationFileFormat.GuiColumns.FileExtColumn; + UI.GuiColumns.FileSizeColumn.Value = configurationFileFormat.GuiColumns.FileSizeColumn; + UI.GuiColumns.PathColumn.Value = configurationFileFormat.GuiColumns.PathColumn; + UI.ColumnSort.SortColumnId.Value = configurationFileFormat.ColumnSort.SortColumnId; + UI.ColumnSort.SortAscending.Value = configurationFileFormat.ColumnSort.SortAscending; + UI.GameDirs.Value = configurationFileFormat.GameDirs; + UI.AutoloadDirs.Value = configurationFileFormat.AutoloadDirs ?? []; + UI.ShownFileTypes.NSP.Value = configurationFileFormat.ShownFileTypes.NSP; + UI.ShownFileTypes.PFS0.Value = configurationFileFormat.ShownFileTypes.PFS0; + UI.ShownFileTypes.XCI.Value = configurationFileFormat.ShownFileTypes.XCI; + UI.ShownFileTypes.NCA.Value = configurationFileFormat.ShownFileTypes.NCA; + UI.ShownFileTypes.NRO.Value = configurationFileFormat.ShownFileTypes.NRO; + UI.ShownFileTypes.NSO.Value = configurationFileFormat.ShownFileTypes.NSO; + UI.LanguageCode.Value = configurationFileFormat.LanguageCode; + UI.BaseStyle.Value = configurationFileFormat.BaseStyle; + UI.GameListViewMode.Value = configurationFileFormat.GameListViewMode; + UI.ShowNames.Value = configurationFileFormat.ShowNames; + UI.IsAscendingOrder.Value = configurationFileFormat.IsAscendingOrder; + UI.GridSize.Value = configurationFileFormat.GridSize; + UI.ApplicationSort.Value = configurationFileFormat.ApplicationSort; + UI.StartFullscreen.Value = configurationFileFormat.StartFullscreen; + UI.ShowConsole.Value = configurationFileFormat.ShowConsole; + UI.WindowStartup.WindowSizeWidth.Value = configurationFileFormat.WindowStartup.WindowSizeWidth; + UI.WindowStartup.WindowSizeHeight.Value = configurationFileFormat.WindowStartup.WindowSizeHeight; + UI.WindowStartup.WindowPositionX.Value = configurationFileFormat.WindowStartup.WindowPositionX; + UI.WindowStartup.WindowPositionY.Value = configurationFileFormat.WindowStartup.WindowPositionY; + UI.WindowStartup.WindowMaximized.Value = configurationFileFormat.WindowStartup.WindowMaximized; + Hid.EnableKeyboard.Value = configurationFileFormat.EnableKeyboard; + Hid.EnableMouse.Value = configurationFileFormat.EnableMouse; + Hid.Hotkeys.Value = configurationFileFormat.Hotkeys; + Hid.InputConfig.Value = configurationFileFormat.InputConfig ?? []; + + Multiplayer.LanInterfaceId.Value = configurationFileFormat.MultiplayerLanInterfaceId; + Multiplayer.Mode.Value = configurationFileFormat.MultiplayerMode; + Multiplayer.DisableP2p.Value = configurationFileFormat.MultiplayerDisableP2p; + Multiplayer.LdnPassphrase.Value = configurationFileFormat.MultiplayerLdnPassphrase; + Multiplayer.LdnServer.Value = configurationFileFormat.LdnServer; + + if (configurationFileUpdated) + { + ToFileFormat().SaveConfig(configurationFilePath); + + Ryujinx.Common.Logging.Logger.Notice.Print(LogClass.Application, $"Configuration file updated to version {ConfigurationFileFormat.CurrentVersion}"); + } + } + } +} diff --git a/src/Ryujinx.UI.Common/Configuration/ConfigurationState.Model.cs b/src/Ryujinx.UI.Common/Configuration/ConfigurationState.Model.cs new file mode 100644 index 000000000..f28ce0348 --- /dev/null +++ b/src/Ryujinx.UI.Common/Configuration/ConfigurationState.Model.cs @@ -0,0 +1,714 @@ +using ARMeilleure; +using Ryujinx.Common; +using Ryujinx.Common.Configuration; +using Ryujinx.Common.Configuration.Hid; +using Ryujinx.Common.Configuration.Multiplayer; +using Ryujinx.Common.Logging; +using Ryujinx.HLE; +using Ryujinx.UI.Common.Configuration.System; +using Ryujinx.UI.Common.Helper; +using System.Collections.Generic; + +namespace Ryujinx.UI.Common.Configuration +{ + public partial class ConfigurationState + { + /// + /// UI configuration section + /// + public class UISection + { + public class Columns + { + public ReactiveObject FavColumn { get; private set; } + public ReactiveObject IconColumn { get; private set; } + public ReactiveObject AppColumn { get; private set; } + public ReactiveObject DevColumn { get; private set; } + public ReactiveObject VersionColumn { get; private set; } + public ReactiveObject LdnInfoColumn { get; private set; } + public ReactiveObject TimePlayedColumn { get; private set; } + public ReactiveObject LastPlayedColumn { get; private set; } + public ReactiveObject FileExtColumn { get; private set; } + public ReactiveObject FileSizeColumn { get; private set; } + public ReactiveObject PathColumn { get; private set; } + + public Columns() + { + FavColumn = new ReactiveObject(); + IconColumn = new ReactiveObject(); + AppColumn = new ReactiveObject(); + DevColumn = new ReactiveObject(); + VersionColumn = new ReactiveObject(); + LdnInfoColumn = new ReactiveObject(); + TimePlayedColumn = new ReactiveObject(); + LastPlayedColumn = new ReactiveObject(); + FileExtColumn = new ReactiveObject(); + FileSizeColumn = new ReactiveObject(); + PathColumn = new ReactiveObject(); + } + } + + public class ColumnSortSettings + { + public ReactiveObject SortColumnId { get; private set; } + public ReactiveObject SortAscending { get; private set; } + + public ColumnSortSettings() + { + SortColumnId = new ReactiveObject(); + SortAscending = new ReactiveObject(); + } + } + + /// + /// Used to toggle which file types are shown in the UI + /// + public class ShownFileTypeSettings + { + public ReactiveObject NSP { get; private set; } + public ReactiveObject PFS0 { get; private set; } + public ReactiveObject XCI { get; private set; } + public ReactiveObject NCA { get; private set; } + public ReactiveObject NRO { get; private set; } + public ReactiveObject NSO { get; private set; } + + public ShownFileTypeSettings() + { + NSP = new ReactiveObject(); + PFS0 = new ReactiveObject(); + XCI = new ReactiveObject(); + NCA = new ReactiveObject(); + NRO = new ReactiveObject(); + NSO = new ReactiveObject(); + } + } + + // + /// Determines main window start-up position, size and state + /// + public class WindowStartupSettings + { + public ReactiveObject WindowSizeWidth { get; private set; } + public ReactiveObject WindowSizeHeight { get; private set; } + public ReactiveObject WindowPositionX { get; private set; } + public ReactiveObject WindowPositionY { get; private set; } + public ReactiveObject WindowMaximized { get; private set; } + + public WindowStartupSettings() + { + WindowSizeWidth = new ReactiveObject(); + WindowSizeHeight = new ReactiveObject(); + WindowPositionX = new ReactiveObject(); + WindowPositionY = new ReactiveObject(); + WindowMaximized = new ReactiveObject(); + } + } + + /// + /// Used to toggle columns in the GUI + /// + public Columns GuiColumns { get; private set; } + + /// + /// Used to configure column sort settings in the GUI + /// + public ColumnSortSettings ColumnSort { get; private set; } + + /// + /// A list of directories containing games to be used to load games into the games list + /// + public ReactiveObject> GameDirs { get; private set; } + + /// + /// A list of directories containing DLC/updates the user wants to autoload during library refreshes + /// + public ReactiveObject> AutoloadDirs { get; private set; } + + /// + /// A list of file types to be hidden in the games List + /// + public ShownFileTypeSettings ShownFileTypes { get; private set; } + + /// + /// Determines main window start-up position, size and state + /// + public WindowStartupSettings WindowStartup { get; private set; } + + /// + /// Language Code for the UI + /// + public ReactiveObject LanguageCode { get; private set; } + + /// + /// Selects the base style + /// + public ReactiveObject BaseStyle { get; private set; } + + /// + /// Start games in fullscreen mode + /// + public ReactiveObject StartFullscreen { get; private set; } + + /// + /// Hide / Show Console Window + /// + public ReactiveObject ShowConsole { get; private set; } + + /// + /// View Mode of the Game list + /// + public ReactiveObject GameListViewMode { get; private set; } + + /// + /// Show application name in Grid Mode + /// + public ReactiveObject ShowNames { get; private set; } + + /// + /// Sets App Icon Size in Grid Mode + /// + public ReactiveObject GridSize { get; private set; } + + /// + /// Sorts Apps in Grid Mode + /// + public ReactiveObject ApplicationSort { get; private set; } + + /// + /// Sets if Grid is ordered in Ascending Order + /// + public ReactiveObject IsAscendingOrder { get; private set; } + + public UISection() + { + GuiColumns = new Columns(); + ColumnSort = new ColumnSortSettings(); + GameDirs = new ReactiveObject>(); + AutoloadDirs = new ReactiveObject>(); + ShownFileTypes = new ShownFileTypeSettings(); + WindowStartup = new WindowStartupSettings(); + BaseStyle = new ReactiveObject(); + StartFullscreen = new ReactiveObject(); + GameListViewMode = new ReactiveObject(); + ShowNames = new ReactiveObject(); + GridSize = new ReactiveObject(); + ApplicationSort = new ReactiveObject(); + IsAscendingOrder = new ReactiveObject(); + LanguageCode = new ReactiveObject(); + ShowConsole = new ReactiveObject(); + ShowConsole.Event += static (_, e) => ConsoleHelper.SetConsoleWindowState(e.NewValue); + } + } + + /// + /// Logger configuration section + /// + public class LoggerSection + { + /// + /// Enables printing debug log messages + /// + public ReactiveObject EnableDebug { get; private set; } + + /// + /// Enables printing stub log messages + /// + public ReactiveObject EnableStub { get; private set; } + + /// + /// Enables printing info log messages + /// + public ReactiveObject EnableInfo { get; private set; } + + /// + /// Enables printing warning log messages + /// + public ReactiveObject EnableWarn { get; private set; } + + /// + /// Enables printing error log messages + /// + public ReactiveObject EnableError { get; private set; } + + /// + /// Enables printing trace log messages + /// + public ReactiveObject EnableTrace { get; private set; } + + /// + /// Enables printing guest log messages + /// + public ReactiveObject EnableGuest { get; private set; } + + /// + /// Enables printing FS access log messages + /// + public ReactiveObject EnableFsAccessLog { get; private set; } + + /// + /// Controls which log messages are written to the log targets + /// + public ReactiveObject FilteredClasses { get; private set; } + + /// + /// Enables or disables logging to a file on disk + /// + public ReactiveObject EnableFileLog { get; private set; } + + /// + /// Controls which OpenGL log messages are recorded in the log + /// + public ReactiveObject GraphicsDebugLevel { get; private set; } + + public LoggerSection() + { + EnableDebug = new ReactiveObject(); + EnableDebug.LogChangesToValue(nameof(EnableDebug)); + EnableStub = new ReactiveObject(); + EnableInfo = new ReactiveObject(); + EnableWarn = new ReactiveObject(); + EnableError = new ReactiveObject(); + EnableTrace = new ReactiveObject(); + EnableGuest = new ReactiveObject(); + EnableFsAccessLog = new ReactiveObject(); + FilteredClasses = new ReactiveObject(); + EnableFileLog = new ReactiveObject(); + EnableFileLog.LogChangesToValue(nameof(EnableFileLog)); + GraphicsDebugLevel = new ReactiveObject(); + } + } + + /// + /// System configuration section + /// + public class SystemSection + { + /// + /// Change System Language + /// + public ReactiveObject Language { get; private set; } + + /// + /// Change System Region + /// + public ReactiveObject Region { get; private set; } + + /// + /// Change System TimeZone + /// + public ReactiveObject TimeZone { get; private set; } + + /// + /// System Time Offset in Seconds + /// + public ReactiveObject SystemTimeOffset { get; private set; } + + /// + /// Enables or disables Docked Mode + /// + public ReactiveObject EnableDockedMode { get; private set; } + + /// + /// Enables or disables persistent profiled translation cache + /// + public ReactiveObject EnablePtc { get; private set; } + + /// + /// Enables or disables low-power persistent profiled translation cache loading + /// + public ReactiveObject EnableLowPowerPtc { get; private set; } + + /// + /// Enables or disables guest Internet access + /// + public ReactiveObject EnableInternetAccess { get; private set; } + + /// + /// Enables integrity checks on Game content files + /// + public ReactiveObject EnableFsIntegrityChecks { get; private set; } + + /// + /// Enables FS access log output to the console. Possible modes are 0-3 + /// + public ReactiveObject FsGlobalAccessLogMode { get; private set; } + + /// + /// The selected audio backend + /// + public ReactiveObject AudioBackend { get; private set; } + + /// + /// The audio backend volume + /// + public ReactiveObject AudioVolume { get; private set; } + + /// + /// The selected memory manager mode + /// + public ReactiveObject MemoryManagerMode { get; private set; } + + /// + /// Defines the amount of RAM available on the emulated system, and how it is distributed + /// + public ReactiveObject DramSize { get; private set; } + + /// + /// Enable or disable ignoring missing services + /// + public ReactiveObject IgnoreMissingServices { get; private set; } + + /// + /// Uses Hypervisor over JIT if available + /// + public ReactiveObject UseHypervisor { get; private set; } + + public SystemSection() + { + Language = new ReactiveObject(); + Language.LogChangesToValue(nameof(Language)); + Region = new ReactiveObject(); + Region.LogChangesToValue(nameof(Region)); + TimeZone = new ReactiveObject(); + TimeZone.LogChangesToValue(nameof(TimeZone)); + SystemTimeOffset = new ReactiveObject(); + SystemTimeOffset.LogChangesToValue(nameof(SystemTimeOffset)); + EnableDockedMode = new ReactiveObject(); + EnableDockedMode.LogChangesToValue(nameof(EnableDockedMode)); + EnablePtc = new ReactiveObject(); + EnablePtc.LogChangesToValue(nameof(EnablePtc)); + EnableLowPowerPtc = new ReactiveObject(); + EnableLowPowerPtc.LogChangesToValue(nameof(EnableLowPowerPtc)); + EnableLowPowerPtc.Event += (_, evnt) + => Optimizations.LowPower = evnt.NewValue; + EnableInternetAccess = new ReactiveObject(); + EnableInternetAccess.LogChangesToValue(nameof(EnableInternetAccess)); + EnableFsIntegrityChecks = new ReactiveObject(); + EnableFsIntegrityChecks.LogChangesToValue(nameof(EnableFsIntegrityChecks)); + FsGlobalAccessLogMode = new ReactiveObject(); + FsGlobalAccessLogMode.LogChangesToValue(nameof(FsGlobalAccessLogMode)); + AudioBackend = new ReactiveObject(); + AudioBackend.LogChangesToValue(nameof(AudioBackend)); + MemoryManagerMode = new ReactiveObject(); + MemoryManagerMode.LogChangesToValue(nameof(MemoryManagerMode)); + DramSize = new ReactiveObject(); + DramSize.LogChangesToValue(nameof(DramSize)); + IgnoreMissingServices = new ReactiveObject(); + IgnoreMissingServices.LogChangesToValue(nameof(IgnoreMissingServices)); + AudioVolume = new ReactiveObject(); + AudioVolume.LogChangesToValue(nameof(AudioVolume)); + UseHypervisor = new ReactiveObject(); + UseHypervisor.LogChangesToValue(nameof(UseHypervisor)); + } + } + + /// + /// Hid configuration section + /// + public class HidSection + { + /// + /// Enable or disable keyboard support (Independent from controllers binding) + /// + public ReactiveObject EnableKeyboard { get; private set; } + + /// + /// Enable or disable mouse support (Independent from controllers binding) + /// + public ReactiveObject EnableMouse { get; private set; } + + /// + /// Hotkey Keyboard Bindings + /// + public ReactiveObject Hotkeys { get; private set; } + + /// + /// Input device configuration. + /// NOTE: This ReactiveObject won't issue an event when the List has elements added or removed. + /// TODO: Implement a ReactiveList class. + /// + public ReactiveObject> InputConfig { get; private set; } + + public HidSection() + { + EnableKeyboard = new ReactiveObject(); + EnableMouse = new ReactiveObject(); + Hotkeys = new ReactiveObject(); + InputConfig = new ReactiveObject>(); + } + } + + /// + /// Graphics configuration section + /// + public class GraphicsSection + { + /// + /// Whether or not backend threading is enabled. The "Auto" setting will determine whether threading should be enabled at runtime. + /// + public ReactiveObject BackendThreading { get; private set; } + + /// + /// Max Anisotropy. Values range from 0 - 16. Set to -1 to let the game decide. + /// + public ReactiveObject MaxAnisotropy { get; private set; } + + /// + /// Aspect Ratio applied to the renderer window. + /// + public ReactiveObject AspectRatio { get; private set; } + + /// + /// Resolution Scale. An integer scale applied to applicable render targets. Values 1-4, or -1 to use a custom floating point scale instead. + /// + public ReactiveObject ResScale { get; private set; } + + /// + /// Custom Resolution Scale. A custom floating point scale applied to applicable render targets. Only active when Resolution Scale is -1. + /// + public ReactiveObject ResScaleCustom { get; private set; } + + /// + /// Dumps shaders in this local directory + /// + public ReactiveObject ShadersDumpPath { get; private set; } + + /// + /// Toggles the present interval mode. Options are Switch (60Hz), Unbounded (previously Vsync off), and Custom, if enabled. + /// + public ReactiveObject VSyncMode { get; private set; } + + /// + /// Enables or disables the custom present interval mode. + /// + public ReactiveObject EnableCustomVSyncInterval { get; private set; } + + /// + /// Changes the custom present interval. + /// + public ReactiveObject CustomVSyncInterval { get; private set; } + + /// + /// Enables or disables Shader cache + /// + public ReactiveObject EnableShaderCache { get; private set; } + + /// + /// Enables or disables texture recompression + /// + public ReactiveObject EnableTextureRecompression { get; private set; } + + /// + /// Enables or disables Macro high-level emulation + /// + public ReactiveObject EnableMacroHLE { get; private set; } + + /// + /// Enables or disables color space passthrough, if available. + /// + public ReactiveObject EnableColorSpacePassthrough { get; private set; } + + /// + /// Graphics backend + /// + public ReactiveObject GraphicsBackend { get; private set; } + + /// + /// Applies anti-aliasing to the renderer. + /// + public ReactiveObject AntiAliasing { get; private set; } + + /// + /// Sets the framebuffer upscaling type. + /// + public ReactiveObject ScalingFilter { get; private set; } + + /// + /// Sets the framebuffer upscaling level. + /// + public ReactiveObject ScalingFilterLevel { get; private set; } + + /// + /// Preferred GPU + /// + public ReactiveObject PreferredGpu { get; private set; } + + public GraphicsSection() + { + BackendThreading = new ReactiveObject(); + BackendThreading.LogChangesToValue(nameof(BackendThreading)); + ResScale = new ReactiveObject(); + ResScale.LogChangesToValue(nameof(ResScale)); + ResScaleCustom = new ReactiveObject(); + ResScaleCustom.LogChangesToValue(nameof(ResScaleCustom)); + MaxAnisotropy = new ReactiveObject(); + MaxAnisotropy.LogChangesToValue(nameof(MaxAnisotropy)); + AspectRatio = new ReactiveObject(); + AspectRatio.LogChangesToValue(nameof(AspectRatio)); + ShadersDumpPath = new ReactiveObject(); + VSyncMode = new ReactiveObject(); + VSyncMode.LogChangesToValue(nameof(VSyncMode)); + EnableCustomVSyncInterval = new ReactiveObject(); + EnableCustomVSyncInterval.LogChangesToValue(nameof(EnableCustomVSyncInterval)); + CustomVSyncInterval = new ReactiveObject(); + CustomVSyncInterval.LogChangesToValue(nameof(CustomVSyncInterval)); + EnableShaderCache = new ReactiveObject(); + EnableShaderCache.LogChangesToValue(nameof(EnableShaderCache)); + EnableTextureRecompression = new ReactiveObject(); + EnableTextureRecompression.LogChangesToValue(nameof(EnableTextureRecompression)); + GraphicsBackend = new ReactiveObject(); + GraphicsBackend.LogChangesToValue(nameof(GraphicsBackend)); + PreferredGpu = new ReactiveObject(); + PreferredGpu.LogChangesToValue(nameof(PreferredGpu)); + EnableMacroHLE = new ReactiveObject(); + EnableMacroHLE.LogChangesToValue(nameof(EnableMacroHLE)); + EnableColorSpacePassthrough = new ReactiveObject(); + EnableColorSpacePassthrough.LogChangesToValue(nameof(EnableColorSpacePassthrough)); + AntiAliasing = new ReactiveObject(); + AntiAliasing.LogChangesToValue(nameof(AntiAliasing)); + ScalingFilter = new ReactiveObject(); + ScalingFilter.LogChangesToValue(nameof(ScalingFilter)); + ScalingFilterLevel = new ReactiveObject(); + ScalingFilterLevel.LogChangesToValue(nameof(ScalingFilterLevel)); + } + } + + /// + /// Multiplayer configuration section + /// + public class MultiplayerSection + { + /// + /// GUID for the network interface used by LAN (or 0 for default) + /// + public ReactiveObject LanInterfaceId { get; private set; } + + /// + /// Multiplayer Mode + /// + public ReactiveObject Mode { get; private set; } + + /// + /// Disable P2P + /// + public ReactiveObject DisableP2p { get; private set; } + + /// + /// LDN PassPhrase + /// + public ReactiveObject LdnPassphrase { get; private set; } + + /// + /// LDN Server + /// + public ReactiveObject LdnServer { get; private set; } + + public MultiplayerSection() + { + LanInterfaceId = new ReactiveObject(); + Mode = new ReactiveObject(); + Mode.LogChangesToValue(nameof(MultiplayerMode)); + DisableP2p = new ReactiveObject(); + DisableP2p.LogChangesToValue(nameof(DisableP2p)); + LdnPassphrase = new ReactiveObject(); + LdnPassphrase.LogChangesToValue(nameof(LdnPassphrase)); + LdnServer = new ReactiveObject(); + LdnServer.LogChangesToValue(nameof(LdnServer)); + } + } + + /// + /// The default configuration instance + /// + public static ConfigurationState Instance { get; private set; } + + /// + /// The UI section + /// + public UISection UI { get; private set; } + + /// + /// The Logger section + /// + public LoggerSection Logger { get; private set; } + + /// + /// The System section + /// + public SystemSection System { get; private set; } + + /// + /// The Graphics section + /// + public GraphicsSection Graphics { get; private set; } + + /// + /// The Hid section + /// + public HidSection Hid { get; private set; } + + /// + /// The Multiplayer section + /// + public MultiplayerSection Multiplayer { get; private set; } + + /// + /// Enables or disables Discord Rich Presence + /// + public ReactiveObject EnableDiscordIntegration { get; private set; } + + /// + /// Checks for updates when Ryujinx starts when enabled + /// + public ReactiveObject CheckUpdatesOnStart { get; private set; } + + /// + /// Show "Confirm Exit" Dialog + /// + public ReactiveObject ShowConfirmExit { get; private set; } + + /// + /// Ignore Applet + /// + public ReactiveObject IgnoreApplet { get; private set; } + + /// + /// Enables or disables save window size, position and state on close. + /// + public ReactiveObject RememberWindowState { get; private set; } + + /// + /// Enables or disables the redesigned title bar + /// + public ReactiveObject ShowTitleBar { get; private set; } + + /// + /// Enables hardware-accelerated rendering for Avalonia + /// + public ReactiveObject EnableHardwareAcceleration { get; private set; } + + /// + /// Hide Cursor on Idle + /// + public ReactiveObject HideCursor { get; private set; } + + private ConfigurationState() + { + UI = new UISection(); + Logger = new LoggerSection(); + System = new SystemSection(); + Graphics = new GraphicsSection(); + Hid = new HidSection(); + Multiplayer = new MultiplayerSection(); + EnableDiscordIntegration = new ReactiveObject(); + CheckUpdatesOnStart = new ReactiveObject(); + ShowConfirmExit = new ReactiveObject(); + IgnoreApplet = new ReactiveObject(); + IgnoreApplet.LogChangesToValue(nameof(IgnoreApplet)); + RememberWindowState = new ReactiveObject(); + ShowTitleBar = new ReactiveObject(); + EnableHardwareAcceleration = new ReactiveObject(); + HideCursor = new ReactiveObject(); + } + } +} diff --git a/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs b/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs index 8420dc5d9..04ddd442f 100644 --- a/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs +++ b/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs @@ -1,661 +1,25 @@ -using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Hid; -using Ryujinx.Common.Configuration.Hid.Controller; using Ryujinx.Common.Configuration.Hid.Keyboard; using Ryujinx.Common.Configuration.Multiplayer; -using Ryujinx.Common.Logging; using Ryujinx.Graphics.Vulkan; +using Ryujinx.HLE; using Ryujinx.UI.Common.Configuration.System; using Ryujinx.UI.Common.Configuration.UI; -using Ryujinx.UI.Common.Helper; using System; -using System.Collections.Generic; -using System.Globalization; -using System.Text.Json.Nodes; namespace Ryujinx.UI.Common.Configuration { - public class ConfigurationState + public partial class ConfigurationState { - /// - /// UI configuration section - /// - public class UISection + public static void Initialize() { - public class Columns + if (Instance != null) { - public ReactiveObject FavColumn { get; private set; } - public ReactiveObject IconColumn { get; private set; } - public ReactiveObject AppColumn { get; private set; } - public ReactiveObject DevColumn { get; private set; } - public ReactiveObject VersionColumn { get; private set; } - public ReactiveObject TimePlayedColumn { get; private set; } - public ReactiveObject LastPlayedColumn { get; private set; } - public ReactiveObject FileExtColumn { get; private set; } - public ReactiveObject FileSizeColumn { get; private set; } - public ReactiveObject PathColumn { get; private set; } - - public Columns() - { - FavColumn = new ReactiveObject(); - IconColumn = new ReactiveObject(); - AppColumn = new ReactiveObject(); - DevColumn = new ReactiveObject(); - VersionColumn = new ReactiveObject(); - TimePlayedColumn = new ReactiveObject(); - LastPlayedColumn = new ReactiveObject(); - FileExtColumn = new ReactiveObject(); - FileSizeColumn = new ReactiveObject(); - PathColumn = new ReactiveObject(); - } + throw new InvalidOperationException("Configuration is already initialized"); } - public class ColumnSortSettings - { - public ReactiveObject SortColumnId { get; private set; } - public ReactiveObject SortAscending { get; private set; } - - public ColumnSortSettings() - { - SortColumnId = new ReactiveObject(); - SortAscending = new ReactiveObject(); - } - } - - /// - /// Used to toggle which file types are shown in the UI - /// - public class ShownFileTypeSettings - { - public ReactiveObject NSP { get; private set; } - public ReactiveObject PFS0 { get; private set; } - public ReactiveObject XCI { get; private set; } - public ReactiveObject NCA { get; private set; } - public ReactiveObject NRO { get; private set; } - public ReactiveObject NSO { get; private set; } - - public ShownFileTypeSettings() - { - NSP = new ReactiveObject(); - PFS0 = new ReactiveObject(); - XCI = new ReactiveObject(); - NCA = new ReactiveObject(); - NRO = new ReactiveObject(); - NSO = new ReactiveObject(); - } - } - - // - /// Determines main window start-up position, size and state - /// - public class WindowStartupSettings - { - public ReactiveObject WindowSizeWidth { get; private set; } - public ReactiveObject WindowSizeHeight { get; private set; } - public ReactiveObject WindowPositionX { get; private set; } - public ReactiveObject WindowPositionY { get; private set; } - public ReactiveObject WindowMaximized { get; private set; } - - public WindowStartupSettings() - { - WindowSizeWidth = new ReactiveObject(); - WindowSizeHeight = new ReactiveObject(); - WindowPositionX = new ReactiveObject(); - WindowPositionY = new ReactiveObject(); - WindowMaximized = new ReactiveObject(); - } - } - - /// - /// Used to toggle columns in the GUI - /// - public Columns GuiColumns { get; private set; } - - /// - /// Used to configure column sort settings in the GUI - /// - public ColumnSortSettings ColumnSort { get; private set; } - - /// - /// A list of directories containing games to be used to load games into the games list - /// - public ReactiveObject> GameDirs { get; private set; } - - /// - /// A list of file types to be hidden in the games List - /// - public ShownFileTypeSettings ShownFileTypes { get; private set; } - - /// - /// Determines main window start-up position, size and state - /// - public WindowStartupSettings WindowStartup { get; private set; } - - /// - /// Language Code for the UI - /// - public ReactiveObject LanguageCode { get; private set; } - - /// - /// Enable or disable custom themes in the GUI - /// - public ReactiveObject EnableCustomTheme { get; private set; } - - /// - /// Path to custom GUI theme - /// - public ReactiveObject CustomThemePath { get; private set; } - - /// - /// Selects the base style - /// - public ReactiveObject BaseStyle { get; private set; } - - /// - /// Start games in fullscreen mode - /// - public ReactiveObject StartFullscreen { get; private set; } - - /// - /// Hide / Show Console Window - /// - public ReactiveObject ShowConsole { get; private set; } - - /// - /// View Mode of the Game list - /// - public ReactiveObject GameListViewMode { get; private set; } - - /// - /// Show application name in Grid Mode - /// - public ReactiveObject ShowNames { get; private set; } - - /// - /// Sets App Icon Size in Grid Mode - /// - public ReactiveObject GridSize { get; private set; } - - /// - /// Sorts Apps in Grid Mode - /// - public ReactiveObject ApplicationSort { get; private set; } - - /// - /// Sets if Grid is ordered in Ascending Order - /// - public ReactiveObject IsAscendingOrder { get; private set; } - - public UISection() - { - GuiColumns = new Columns(); - ColumnSort = new ColumnSortSettings(); - GameDirs = new ReactiveObject>(); - ShownFileTypes = new ShownFileTypeSettings(); - WindowStartup = new WindowStartupSettings(); - EnableCustomTheme = new ReactiveObject(); - CustomThemePath = new ReactiveObject(); - BaseStyle = new ReactiveObject(); - StartFullscreen = new ReactiveObject(); - GameListViewMode = new ReactiveObject(); - ShowNames = new ReactiveObject(); - GridSize = new ReactiveObject(); - ApplicationSort = new ReactiveObject(); - IsAscendingOrder = new ReactiveObject(); - LanguageCode = new ReactiveObject(); - ShowConsole = new ReactiveObject(); - ShowConsole.Event += static (s, e) => { ConsoleHelper.SetConsoleWindowState(e.NewValue); }; - } - } - - /// - /// Logger configuration section - /// - public class LoggerSection - { - /// - /// Enables printing debug log messages - /// - public ReactiveObject EnableDebug { get; private set; } - - /// - /// Enables printing stub log messages - /// - public ReactiveObject EnableStub { get; private set; } - - /// - /// Enables printing info log messages - /// - public ReactiveObject EnableInfo { get; private set; } - - /// - /// Enables printing warning log messages - /// - public ReactiveObject EnableWarn { get; private set; } - - /// - /// Enables printing error log messages - /// - public ReactiveObject EnableError { get; private set; } - - /// - /// Enables printing trace log messages - /// - public ReactiveObject EnableTrace { get; private set; } - - /// - /// Enables printing guest log messages - /// - public ReactiveObject EnableGuest { get; private set; } - - /// - /// Enables printing FS access log messages - /// - public ReactiveObject EnableFsAccessLog { get; private set; } - - /// - /// Controls which log messages are written to the log targets - /// - public ReactiveObject FilteredClasses { get; private set; } - - /// - /// Enables or disables logging to a file on disk - /// - public ReactiveObject EnableFileLog { get; private set; } - - /// - /// Controls which OpenGL log messages are recorded in the log - /// - public ReactiveObject GraphicsDebugLevel { get; private set; } - - public LoggerSection() - { - EnableDebug = new ReactiveObject(); - EnableStub = new ReactiveObject(); - EnableInfo = new ReactiveObject(); - EnableWarn = new ReactiveObject(); - EnableError = new ReactiveObject(); - EnableTrace = new ReactiveObject(); - EnableGuest = new ReactiveObject(); - EnableFsAccessLog = new ReactiveObject(); - FilteredClasses = new ReactiveObject(); - EnableFileLog = new ReactiveObject(); - EnableFileLog.Event += static (sender, e) => LogValueChange(e, nameof(EnableFileLog)); - GraphicsDebugLevel = new ReactiveObject(); - } - } - - /// - /// System configuration section - /// - public class SystemSection - { - /// - /// Change System Language - /// - public ReactiveObject Language { get; private set; } - - /// - /// Change System Region - /// - public ReactiveObject Region { get; private set; } - - /// - /// Change System TimeZone - /// - public ReactiveObject TimeZone { get; private set; } - - /// - /// System Time Offset in Seconds - /// - public ReactiveObject SystemTimeOffset { get; private set; } - - /// - /// Enables or disables Docked Mode - /// - public ReactiveObject EnableDockedMode { get; private set; } - - /// - /// Enables or disables profiled translation cache persistency - /// - public ReactiveObject EnablePtc { get; private set; } - - /// - /// Enables or disables guest Internet access - /// - public ReactiveObject EnableInternetAccess { get; private set; } - - /// - /// Enables integrity checks on Game content files - /// - public ReactiveObject EnableFsIntegrityChecks { get; private set; } - - /// - /// Enables FS access log output to the console. Possible modes are 0-3 - /// - public ReactiveObject FsGlobalAccessLogMode { get; private set; } - - /// - /// The selected audio backend - /// - public ReactiveObject AudioBackend { get; private set; } - - /// - /// The audio backend volume - /// - public ReactiveObject AudioVolume { get; private set; } - - /// - /// The selected memory manager mode - /// - public ReactiveObject MemoryManagerMode { get; private set; } - - /// - /// Defines the amount of RAM available on the emulated system, and how it is distributed - /// - public ReactiveObject ExpandRam { get; private set; } - - /// - /// Enable or disable ignoring missing services - /// - public ReactiveObject IgnoreMissingServices { get; private set; } - - /// - /// Uses Hypervisor over JIT if available - /// - public ReactiveObject UseHypervisor { get; private set; } - - public SystemSection() - { - Language = new ReactiveObject(); - Region = new ReactiveObject(); - TimeZone = new ReactiveObject(); - SystemTimeOffset = new ReactiveObject(); - EnableDockedMode = new ReactiveObject(); - EnableDockedMode.Event += static (sender, e) => LogValueChange(e, nameof(EnableDockedMode)); - EnablePtc = new ReactiveObject(); - EnablePtc.Event += static (sender, e) => LogValueChange(e, nameof(EnablePtc)); - EnableInternetAccess = new ReactiveObject(); - EnableInternetAccess.Event += static (sender, e) => LogValueChange(e, nameof(EnableInternetAccess)); - EnableFsIntegrityChecks = new ReactiveObject(); - EnableFsIntegrityChecks.Event += static (sender, e) => LogValueChange(e, nameof(EnableFsIntegrityChecks)); - FsGlobalAccessLogMode = new ReactiveObject(); - FsGlobalAccessLogMode.Event += static (sender, e) => LogValueChange(e, nameof(FsGlobalAccessLogMode)); - AudioBackend = new ReactiveObject(); - AudioBackend.Event += static (sender, e) => LogValueChange(e, nameof(AudioBackend)); - MemoryManagerMode = new ReactiveObject(); - MemoryManagerMode.Event += static (sender, e) => LogValueChange(e, nameof(MemoryManagerMode)); - ExpandRam = new ReactiveObject(); - ExpandRam.Event += static (sender, e) => LogValueChange(e, nameof(ExpandRam)); - IgnoreMissingServices = new ReactiveObject(); - IgnoreMissingServices.Event += static (sender, e) => LogValueChange(e, nameof(IgnoreMissingServices)); - AudioVolume = new ReactiveObject(); - AudioVolume.Event += static (sender, e) => LogValueChange(e, nameof(AudioVolume)); - UseHypervisor = new ReactiveObject(); - UseHypervisor.Event += static (sender, e) => LogValueChange(e, nameof(UseHypervisor)); - } - } - - /// - /// Hid configuration section - /// - public class HidSection - { - /// - /// Enable or disable keyboard support (Independent from controllers binding) - /// - public ReactiveObject EnableKeyboard { get; private set; } - - /// - /// Enable or disable mouse support (Independent from controllers binding) - /// - public ReactiveObject EnableMouse { get; private set; } - - /// - /// Hotkey Keyboard Bindings - /// - public ReactiveObject Hotkeys { get; private set; } - - /// - /// Input device configuration. - /// NOTE: This ReactiveObject won't issue an event when the List has elements added or removed. - /// TODO: Implement a ReactiveList class. - /// - public ReactiveObject> InputConfig { get; private set; } - - public HidSection() - { - EnableKeyboard = new ReactiveObject(); - EnableMouse = new ReactiveObject(); - Hotkeys = new ReactiveObject(); - InputConfig = new ReactiveObject>(); - } - } - - /// - /// Graphics configuration section - /// - public class GraphicsSection - { - /// - /// Whether or not backend threading is enabled. The "Auto" setting will determine whether threading should be enabled at runtime. - /// - public ReactiveObject BackendThreading { get; private set; } - - /// - /// Max Anisotropy. Values range from 0 - 16. Set to -1 to let the game decide. - /// - public ReactiveObject MaxAnisotropy { get; private set; } - - /// - /// Aspect Ratio applied to the renderer window. - /// - public ReactiveObject AspectRatio { get; private set; } - - /// - /// Resolution Scale. An integer scale applied to applicable render targets. Values 1-4, or -1 to use a custom floating point scale instead. - /// - public ReactiveObject ResScale { get; private set; } - - /// - /// Custom Resolution Scale. A custom floating point scale applied to applicable render targets. Only active when Resolution Scale is -1. - /// - public ReactiveObject ResScaleCustom { get; private set; } - - /// - /// Dumps shaders in this local directory - /// - public ReactiveObject ShadersDumpPath { get; private set; } - - /// - /// Enables or disables Vertical Sync - /// - public ReactiveObject EnableVsync { get; private set; } - - /// - /// Enables or disables Shader cache - /// - public ReactiveObject EnableShaderCache { get; private set; } - - /// - /// Enables or disables texture recompression - /// - public ReactiveObject EnableTextureRecompression { get; private set; } - - /// - /// Enables or disables Macro high-level emulation - /// - public ReactiveObject EnableMacroHLE { get; private set; } - - /// - /// Enables or disables color space passthrough, if available. - /// - public ReactiveObject EnableColorSpacePassthrough { get; private set; } - - /// - /// Graphics backend - /// - public ReactiveObject GraphicsBackend { get; private set; } - - /// - /// Applies anti-aliasing to the renderer. - /// - public ReactiveObject AntiAliasing { get; private set; } - - /// - /// Sets the framebuffer upscaling type. - /// - public ReactiveObject ScalingFilter { get; private set; } - - /// - /// Sets the framebuffer upscaling level. - /// - public ReactiveObject ScalingFilterLevel { get; private set; } - - /// - /// Preferred GPU - /// - public ReactiveObject PreferredGpu { get; private set; } - - public GraphicsSection() - { - BackendThreading = new ReactiveObject(); - BackendThreading.Event += static (sender, e) => LogValueChange(e, nameof(BackendThreading)); - ResScale = new ReactiveObject(); - ResScale.Event += static (sender, e) => LogValueChange(e, nameof(ResScale)); - ResScaleCustom = new ReactiveObject(); - ResScaleCustom.Event += static (sender, e) => LogValueChange(e, nameof(ResScaleCustom)); - MaxAnisotropy = new ReactiveObject(); - MaxAnisotropy.Event += static (sender, e) => LogValueChange(e, nameof(MaxAnisotropy)); - AspectRatio = new ReactiveObject(); - AspectRatio.Event += static (sender, e) => LogValueChange(e, nameof(AspectRatio)); - ShadersDumpPath = new ReactiveObject(); - EnableVsync = new ReactiveObject(); - EnableVsync.Event += static (sender, e) => LogValueChange(e, nameof(EnableVsync)); - EnableShaderCache = new ReactiveObject(); - EnableShaderCache.Event += static (sender, e) => LogValueChange(e, nameof(EnableShaderCache)); - EnableTextureRecompression = new ReactiveObject(); - EnableTextureRecompression.Event += static (sender, e) => LogValueChange(e, nameof(EnableTextureRecompression)); - GraphicsBackend = new ReactiveObject(); - GraphicsBackend.Event += static (sender, e) => LogValueChange(e, nameof(GraphicsBackend)); - PreferredGpu = new ReactiveObject(); - PreferredGpu.Event += static (sender, e) => LogValueChange(e, nameof(PreferredGpu)); - EnableMacroHLE = new ReactiveObject(); - EnableMacroHLE.Event += static (sender, e) => LogValueChange(e, nameof(EnableMacroHLE)); - EnableColorSpacePassthrough = new ReactiveObject(); - EnableColorSpacePassthrough.Event += static (sender, e) => LogValueChange(e, nameof(EnableColorSpacePassthrough)); - AntiAliasing = new ReactiveObject(); - AntiAliasing.Event += static (sender, e) => LogValueChange(e, nameof(AntiAliasing)); - ScalingFilter = new ReactiveObject(); - ScalingFilter.Event += static (sender, e) => LogValueChange(e, nameof(ScalingFilter)); - ScalingFilterLevel = new ReactiveObject(); - ScalingFilterLevel.Event += static (sender, e) => LogValueChange(e, nameof(ScalingFilterLevel)); - } - } - - /// - /// Multiplayer configuration section - /// - public class MultiplayerSection - { - /// - /// GUID for the network interface used by LAN (or 0 for default) - /// - public ReactiveObject LanInterfaceId { get; private set; } - - /// - /// Multiplayer Mode - /// - public ReactiveObject Mode { get; private set; } - - public MultiplayerSection() - { - LanInterfaceId = new ReactiveObject(); - Mode = new ReactiveObject(); - Mode.Event += static (_, e) => LogValueChange(e, nameof(MultiplayerMode)); - } - } - - /// - /// The default configuration instance - /// - public static ConfigurationState Instance { get; private set; } - - /// - /// The UI section - /// - public UISection UI { get; private set; } - - /// - /// The Logger section - /// - public LoggerSection Logger { get; private set; } - - /// - /// The System section - /// - public SystemSection System { get; private set; } - - /// - /// The Graphics section - /// - public GraphicsSection Graphics { get; private set; } - - /// - /// The Hid section - /// - public HidSection Hid { get; private set; } - - /// - /// The Multiplayer section - /// - public MultiplayerSection Multiplayer { get; private set; } - - /// - /// Enables or disables Discord Rich Presence - /// - public ReactiveObject EnableDiscordIntegration { get; private set; } - - /// - /// Checks for updates when Ryujinx starts when enabled - /// - public ReactiveObject CheckUpdatesOnStart { get; private set; } - - /// - /// Show "Confirm Exit" Dialog - /// - public ReactiveObject ShowConfirmExit { get; private set; } - - /// - /// Enables or disables save window size, position and state on close. - /// - public ReactiveObject RememberWindowState { get; private set; } - - /// - /// Enables hardware-accelerated rendering for Avalonia - /// - public ReactiveObject EnableHardwareAcceleration { get; private set; } - - /// - /// Hide Cursor on Idle - /// - public ReactiveObject HideCursor { get; private set; } - - private ConfigurationState() - { - UI = new UISection(); - Logger = new LoggerSection(); - System = new SystemSection(); - Graphics = new GraphicsSection(); - Hid = new HidSection(); - Multiplayer = new MultiplayerSection(); - EnableDiscordIntegration = new ReactiveObject(); - CheckUpdatesOnStart = new ReactiveObject(); - ShowConfirmExit = new ReactiveObject(); - RememberWindowState = new ReactiveObject(); - EnableHardwareAcceleration = new ReactiveObject(); - HideCursor = new ReactiveObject(); + Instance = new ConfigurationState(); } public ConfigurationFileFormat ToFileFormat() @@ -691,22 +55,27 @@ namespace Ryujinx.UI.Common.Configuration EnableDiscordIntegration = EnableDiscordIntegration, CheckUpdatesOnStart = CheckUpdatesOnStart, ShowConfirmExit = ShowConfirmExit, + IgnoreApplet = IgnoreApplet, RememberWindowState = RememberWindowState, + ShowTitleBar = ShowTitleBar, EnableHardwareAcceleration = EnableHardwareAcceleration, HideCursor = HideCursor, - EnableVsync = Graphics.EnableVsync, + VSyncMode = Graphics.VSyncMode, + EnableCustomVSyncInterval = Graphics.EnableCustomVSyncInterval, + CustomVSyncInterval = Graphics.CustomVSyncInterval, EnableShaderCache = Graphics.EnableShaderCache, EnableTextureRecompression = Graphics.EnableTextureRecompression, EnableMacroHLE = Graphics.EnableMacroHLE, EnableColorSpacePassthrough = Graphics.EnableColorSpacePassthrough, EnablePtc = System.EnablePtc, + EnableLowPowerPtc = System.EnableLowPowerPtc, EnableInternetAccess = System.EnableInternetAccess, EnableFsIntegrityChecks = System.EnableFsIntegrityChecks, FsGlobalAccessLogMode = System.FsGlobalAccessLogMode, AudioBackend = System.AudioBackend, AudioVolume = System.AudioVolume, MemoryManagerMode = System.MemoryManagerMode, - ExpandRam = System.ExpandRam, + DramSize = System.DramSize, IgnoreMissingServices = System.IgnoreMissingServices, UseHypervisor = System.UseHypervisor, GuiColumns = new GuiColumns @@ -716,6 +85,7 @@ namespace Ryujinx.UI.Common.Configuration AppColumn = UI.GuiColumns.AppColumn, DevColumn = UI.GuiColumns.DevColumn, VersionColumn = UI.GuiColumns.VersionColumn, + LdnInfoColumn = UI.GuiColumns.LdnInfoColumn, TimePlayedColumn = UI.GuiColumns.TimePlayedColumn, LastPlayedColumn = UI.GuiColumns.LastPlayedColumn, FileExtColumn = UI.GuiColumns.FileExtColumn, @@ -728,6 +98,7 @@ namespace Ryujinx.UI.Common.Configuration SortAscending = UI.ColumnSort.SortAscending, }, GameDirs = UI.GameDirs, + AutoloadDirs = UI.AutoloadDirs, ShownFileTypes = new ShownFileTypes { NSP = UI.ShownFileTypes.NSP, @@ -746,8 +117,6 @@ namespace Ryujinx.UI.Common.Configuration WindowMaximized = UI.WindowStartup.WindowMaximized, }, LanguageCode = UI.LanguageCode, - EnableCustomTheme = UI.EnableCustomTheme, - CustomThemePath = UI.CustomThemePath, BaseStyle = UI.BaseStyle, GameListViewMode = UI.GameListViewMode, ShowNames = UI.ShowNames, @@ -759,13 +128,16 @@ namespace Ryujinx.UI.Common.Configuration EnableKeyboard = Hid.EnableKeyboard, EnableMouse = Hid.EnableMouse, Hotkeys = Hid.Hotkeys, - KeyboardConfig = new List(), - ControllerConfig = new List(), + KeyboardConfig = [], + ControllerConfig = [], InputConfig = Hid.InputConfig, GraphicsBackend = Graphics.GraphicsBackend, PreferredGpu = Graphics.PreferredGpu, MultiplayerLanInterfaceId = Multiplayer.LanInterfaceId, MultiplayerMode = Multiplayer.Mode, + MultiplayerDisableP2p = Multiplayer.DisableP2p, + MultiplayerLdnPassphrase = Multiplayer.LdnPassphrase, + LdnServer = Multiplayer.LdnServer, }; return configurationFile; @@ -780,8 +152,8 @@ namespace Ryujinx.UI.Common.Configuration Graphics.MaxAnisotropy.Value = -1.0f; Graphics.AspectRatio.Value = AspectRatio.Fixed16x9; Graphics.GraphicsBackend.Value = DefaultGraphicsBackend(); - Graphics.PreferredGpu.Value = ""; - Graphics.ShadersDumpPath.Value = ""; + Graphics.PreferredGpu.Value = string.Empty; + Graphics.ShadersDumpPath.Value = string.Empty; Logger.EnableDebug.Value = false; Logger.EnableStub.Value = true; Logger.EnableInfo.Value = true; @@ -790,7 +162,7 @@ namespace Ryujinx.UI.Common.Configuration Logger.EnableTrace.Value = false; Logger.EnableGuest.Value = true; Logger.EnableFsAccessLog.Value = false; - Logger.FilteredClasses.Value = Array.Empty(); + Logger.FilteredClasses.Value = []; Logger.GraphicsDebugLevel.Value = GraphicsDebugLevel.None; System.Language.Value = Language.AmericanEnglish; System.Region.Value = Region.USA; @@ -800,10 +172,14 @@ namespace Ryujinx.UI.Common.Configuration EnableDiscordIntegration.Value = true; CheckUpdatesOnStart.Value = true; ShowConfirmExit.Value = true; + IgnoreApplet.Value = false; RememberWindowState.Value = true; + ShowTitleBar.Value = !OperatingSystem.IsWindows(); EnableHardwareAcceleration.Value = true; HideCursor.Value = HideCursorMode.OnIdle; - Graphics.EnableVsync.Value = true; + Graphics.VSyncMode.Value = VSyncMode.Switch; + Graphics.CustomVSyncInterval.Value = 120; + Graphics.EnableCustomVSyncInterval.Value = false; Graphics.EnableShaderCache.Value = true; Graphics.EnableTextureRecompression.Value = false; Graphics.EnableMacroHLE.Value = true; @@ -818,11 +194,14 @@ namespace Ryujinx.UI.Common.Configuration System.AudioBackend.Value = AudioBackend.SDL2; System.AudioVolume.Value = 1; System.MemoryManagerMode.Value = MemoryManagerMode.HostMappedUnsafe; - System.ExpandRam.Value = false; + System.DramSize.Value = MemoryConfiguration.MemoryConfiguration4GiB; System.IgnoreMissingServices.Value = false; System.UseHypervisor.Value = true; Multiplayer.LanInterfaceId.Value = "0"; Multiplayer.Mode.Value = MultiplayerMode.Disabled; + Multiplayer.DisableP2p.Value = false; + Multiplayer.LdnPassphrase.Value = ""; + Multiplayer.LdnServer.Value = ""; UI.GuiColumns.FavColumn.Value = true; UI.GuiColumns.IconColumn.Value = true; UI.GuiColumns.AppColumn.Value = true; @@ -835,16 +214,15 @@ namespace Ryujinx.UI.Common.Configuration UI.GuiColumns.PathColumn.Value = true; UI.ColumnSort.SortColumnId.Value = 0; UI.ColumnSort.SortAscending.Value = false; - UI.GameDirs.Value = new List(); + UI.GameDirs.Value = []; + UI.AutoloadDirs.Value = []; UI.ShownFileTypes.NSP.Value = true; UI.ShownFileTypes.PFS0.Value = true; UI.ShownFileTypes.XCI.Value = true; UI.ShownFileTypes.NCA.Value = true; UI.ShownFileTypes.NRO.Value = true; UI.ShownFileTypes.NSO.Value = true; - UI.EnableCustomTheme.Value = true; UI.LanguageCode.Value = "en_US"; - UI.CustomThemePath.Value = ""; UI.BaseStyle.Value = "Dark"; UI.GameListViewMode.Value = 0; UI.ShowNames.Value = true; @@ -862,7 +240,7 @@ namespace Ryujinx.UI.Common.Configuration Hid.EnableMouse.Value = false; Hid.Hotkeys.Value = new KeyboardHotkeys { - ToggleVsync = Key.F1, + ToggleVSyncMode = Key.F1, ToggleMute = Key.F2, Screenshot = Key.F8, ShowUI = Key.F4, @@ -872,15 +250,15 @@ namespace Ryujinx.UI.Common.Configuration VolumeUp = Key.Unbound, VolumeDown = Key.Unbound, }; - Hid.InputConfig.Value = new List - { + Hid.InputConfig.Value = + [ new StandardKeyboardInputConfig { Version = InputConfig.CurrentVersion, Backend = InputBackendType.WindowKeyboard, Id = "0", PlayerIndex = PlayerIndex.Player1, - ControllerType = ControllerType.JoyconPair, + ControllerType = ControllerType.ProController, LeftJoycon = new LeftJoyconCommonConfig { DpadUp = Key.Up, @@ -921,664 +299,8 @@ namespace Ryujinx.UI.Common.Configuration StickRight = Key.L, StickButton = Key.H, }, - }, - }; - } - - public void Load(ConfigurationFileFormat configurationFileFormat, string configurationFilePath) - { - bool configurationFileUpdated = false; - - if (configurationFileFormat.Version < 0 || configurationFileFormat.Version > ConfigurationFileFormat.CurrentVersion) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Unsupported configuration version {configurationFileFormat.Version}, loading default."); - - LoadDefault(); - } - - if (configurationFileFormat.Version < 2) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 2."); - - configurationFileFormat.SystemRegion = Region.USA; - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 3) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 3."); - - configurationFileFormat.SystemTimeZone = "UTC"; - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 4) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 4."); - - configurationFileFormat.MaxAnisotropy = -1; - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 5) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 5."); - - configurationFileFormat.SystemTimeOffset = 0; - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 8) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 8."); - - configurationFileFormat.EnablePtc = true; - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 9) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 9."); - - configurationFileFormat.ColumnSort = new ColumnSort - { - SortColumnId = 0, - SortAscending = false, - }; - - configurationFileFormat.Hotkeys = new KeyboardHotkeys - { - ToggleVsync = Key.F1, - }; - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 10) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 10."); - - configurationFileFormat.AudioBackend = AudioBackend.OpenAl; - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 11) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 11."); - - configurationFileFormat.ResScale = 1; - configurationFileFormat.ResScaleCustom = 1.0f; - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 12) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 12."); - - configurationFileFormat.LoggingGraphicsDebugLevel = GraphicsDebugLevel.None; - - configurationFileUpdated = true; - } - - // configurationFileFormat.Version == 13 -> LDN1 - - if (configurationFileFormat.Version < 14) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 14."); - - configurationFileFormat.CheckUpdatesOnStart = true; - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 16) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 16."); - - configurationFileFormat.EnableShaderCache = true; - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 17) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 17."); - - configurationFileFormat.StartFullscreen = false; - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 18) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 18."); - - configurationFileFormat.AspectRatio = AspectRatio.Fixed16x9; - - configurationFileUpdated = true; - } - - // configurationFileFormat.Version == 19 -> LDN2 - - if (configurationFileFormat.Version < 20) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 20."); - - configurationFileFormat.ShowConfirmExit = true; - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 21) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 21."); - - // Initialize network config. - - configurationFileFormat.MultiplayerMode = MultiplayerMode.Disabled; - configurationFileFormat.MultiplayerLanInterfaceId = "0"; - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 22) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 22."); - - configurationFileFormat.HideCursor = HideCursorMode.Never; - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 24) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 24."); - - configurationFileFormat.InputConfig = new List - { - new StandardKeyboardInputConfig - { - Version = InputConfig.CurrentVersion, - Backend = InputBackendType.WindowKeyboard, - Id = "0", - PlayerIndex = PlayerIndex.Player1, - ControllerType = ControllerType.JoyconPair, - LeftJoycon = new LeftJoyconCommonConfig - { - DpadUp = Key.Up, - DpadDown = Key.Down, - DpadLeft = Key.Left, - DpadRight = Key.Right, - ButtonMinus = Key.Minus, - ButtonL = Key.E, - ButtonZl = Key.Q, - ButtonSl = Key.Unbound, - ButtonSr = Key.Unbound, - }, - LeftJoyconStick = new JoyconConfigKeyboardStick - { - StickUp = Key.W, - StickDown = Key.S, - StickLeft = Key.A, - StickRight = Key.D, - StickButton = Key.F, - }, - RightJoycon = new RightJoyconCommonConfig - { - ButtonA = Key.Z, - ButtonB = Key.X, - ButtonX = Key.C, - ButtonY = Key.V, - ButtonPlus = Key.Plus, - ButtonR = Key.U, - ButtonZr = Key.O, - ButtonSl = Key.Unbound, - ButtonSr = Key.Unbound, - }, - RightJoyconStick = new JoyconConfigKeyboardStick - { - StickUp = Key.I, - StickDown = Key.K, - StickLeft = Key.J, - StickRight = Key.L, - StickButton = Key.H, - }, - }, - }; - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 25) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 25."); - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 26) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 26."); - - configurationFileFormat.MemoryManagerMode = MemoryManagerMode.HostMappedUnsafe; - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 27) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 27."); - - configurationFileFormat.EnableMouse = false; - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 28) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 28."); - - configurationFileFormat.Hotkeys = new KeyboardHotkeys - { - ToggleVsync = Key.F1, - Screenshot = Key.F8, - }; - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 29) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 29."); - - configurationFileFormat.Hotkeys = new KeyboardHotkeys - { - ToggleVsync = Key.F1, - Screenshot = Key.F8, - ShowUI = Key.F4, - }; - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 30) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 30."); - - foreach (InputConfig config in configurationFileFormat.InputConfig) - { - if (config is StandardControllerInputConfig controllerConfig) - { - controllerConfig.Rumble = new RumbleConfigController - { - EnableRumble = false, - StrongRumble = 1f, - WeakRumble = 1f, - }; - } } - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 31) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 31."); - - configurationFileFormat.BackendThreading = BackendThreading.Auto; - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 32) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 32."); - - configurationFileFormat.Hotkeys = new KeyboardHotkeys - { - ToggleVsync = configurationFileFormat.Hotkeys.ToggleVsync, - Screenshot = configurationFileFormat.Hotkeys.Screenshot, - ShowUI = configurationFileFormat.Hotkeys.ShowUI, - Pause = Key.F5, - }; - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 33) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 33."); - - configurationFileFormat.Hotkeys = new KeyboardHotkeys - { - ToggleVsync = configurationFileFormat.Hotkeys.ToggleVsync, - Screenshot = configurationFileFormat.Hotkeys.Screenshot, - ShowUI = configurationFileFormat.Hotkeys.ShowUI, - Pause = configurationFileFormat.Hotkeys.Pause, - ToggleMute = Key.F2, - }; - - configurationFileFormat.AudioVolume = 1; - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 34) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 34."); - - configurationFileFormat.EnableInternetAccess = false; - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 35) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 35."); - - foreach (InputConfig config in configurationFileFormat.InputConfig) - { - if (config is StandardControllerInputConfig controllerConfig) - { - controllerConfig.RangeLeft = 1.0f; - controllerConfig.RangeRight = 1.0f; - } - } - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 36) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 36."); - - configurationFileFormat.LoggingEnableTrace = false; - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 37) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 37."); - - configurationFileFormat.ShowConsole = true; - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 38) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 38."); - - configurationFileFormat.BaseStyle = "Dark"; - configurationFileFormat.GameListViewMode = 0; - configurationFileFormat.ShowNames = true; - configurationFileFormat.GridSize = 2; - configurationFileFormat.LanguageCode = "en_US"; - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 39) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 39."); - - configurationFileFormat.Hotkeys = new KeyboardHotkeys - { - ToggleVsync = configurationFileFormat.Hotkeys.ToggleVsync, - Screenshot = configurationFileFormat.Hotkeys.Screenshot, - ShowUI = configurationFileFormat.Hotkeys.ShowUI, - Pause = configurationFileFormat.Hotkeys.Pause, - ToggleMute = configurationFileFormat.Hotkeys.ToggleMute, - ResScaleUp = Key.Unbound, - ResScaleDown = Key.Unbound, - }; - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 40) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 40."); - - configurationFileFormat.GraphicsBackend = GraphicsBackend.OpenGl; - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 41) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 41."); - - configurationFileFormat.Hotkeys = new KeyboardHotkeys - { - ToggleVsync = configurationFileFormat.Hotkeys.ToggleVsync, - Screenshot = configurationFileFormat.Hotkeys.Screenshot, - ShowUI = configurationFileFormat.Hotkeys.ShowUI, - Pause = configurationFileFormat.Hotkeys.Pause, - ToggleMute = configurationFileFormat.Hotkeys.ToggleMute, - ResScaleUp = configurationFileFormat.Hotkeys.ResScaleUp, - ResScaleDown = configurationFileFormat.Hotkeys.ResScaleDown, - VolumeUp = Key.Unbound, - VolumeDown = Key.Unbound, - }; - } - - if (configurationFileFormat.Version < 42) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 42."); - - configurationFileFormat.EnableMacroHLE = true; - } - - if (configurationFileFormat.Version < 43) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 43."); - - configurationFileFormat.UseHypervisor = true; - } - - if (configurationFileFormat.Version < 44) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 44."); - - configurationFileFormat.AntiAliasing = AntiAliasing.None; - configurationFileFormat.ScalingFilter = ScalingFilter.Bilinear; - configurationFileFormat.ScalingFilterLevel = 80; - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 45) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 45."); - - configurationFileFormat.ShownFileTypes = new ShownFileTypes - { - NSP = true, - PFS0 = true, - XCI = true, - NCA = true, - NRO = true, - NSO = true, - }; - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 46) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 46."); - - configurationFileFormat.MultiplayerLanInterfaceId = "0"; - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 47) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 47."); - - configurationFileFormat.WindowStartup = new WindowStartup - { - WindowPositionX = 0, - WindowPositionY = 0, - WindowSizeHeight = 760, - WindowSizeWidth = 1280, - WindowMaximized = false, - }; - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 48) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 48."); - - configurationFileFormat.EnableColorSpacePassthrough = false; - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 49) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 49."); - - if (OperatingSystem.IsMacOS()) - { - AppDataManager.FixMacOSConfigurationFolders(); - } - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 50) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 50."); - - configurationFileFormat.EnableHardwareAcceleration = true; - - configurationFileUpdated = true; - } - - if (configurationFileFormat.Version < 51) - { - Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 51."); - - configurationFileFormat.RememberWindowState = true; - - configurationFileUpdated = true; - } - - Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog; - Graphics.ResScale.Value = configurationFileFormat.ResScale; - Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom; - Graphics.MaxAnisotropy.Value = configurationFileFormat.MaxAnisotropy; - Graphics.AspectRatio.Value = configurationFileFormat.AspectRatio; - Graphics.ShadersDumpPath.Value = configurationFileFormat.GraphicsShadersDumpPath; - Graphics.BackendThreading.Value = configurationFileFormat.BackendThreading; - Graphics.GraphicsBackend.Value = configurationFileFormat.GraphicsBackend; - Graphics.PreferredGpu.Value = configurationFileFormat.PreferredGpu; - Graphics.AntiAliasing.Value = configurationFileFormat.AntiAliasing; - Graphics.ScalingFilter.Value = configurationFileFormat.ScalingFilter; - Graphics.ScalingFilterLevel.Value = configurationFileFormat.ScalingFilterLevel; - Logger.EnableDebug.Value = configurationFileFormat.LoggingEnableDebug; - Logger.EnableStub.Value = configurationFileFormat.LoggingEnableStub; - Logger.EnableInfo.Value = configurationFileFormat.LoggingEnableInfo; - Logger.EnableWarn.Value = configurationFileFormat.LoggingEnableWarn; - Logger.EnableError.Value = configurationFileFormat.LoggingEnableError; - Logger.EnableTrace.Value = configurationFileFormat.LoggingEnableTrace; - Logger.EnableGuest.Value = configurationFileFormat.LoggingEnableGuest; - Logger.EnableFsAccessLog.Value = configurationFileFormat.LoggingEnableFsAccessLog; - Logger.FilteredClasses.Value = configurationFileFormat.LoggingFilteredClasses; - Logger.GraphicsDebugLevel.Value = configurationFileFormat.LoggingGraphicsDebugLevel; - System.Language.Value = configurationFileFormat.SystemLanguage; - System.Region.Value = configurationFileFormat.SystemRegion; - System.TimeZone.Value = configurationFileFormat.SystemTimeZone; - System.SystemTimeOffset.Value = configurationFileFormat.SystemTimeOffset; - System.EnableDockedMode.Value = configurationFileFormat.DockedMode; - EnableDiscordIntegration.Value = configurationFileFormat.EnableDiscordIntegration; - CheckUpdatesOnStart.Value = configurationFileFormat.CheckUpdatesOnStart; - ShowConfirmExit.Value = configurationFileFormat.ShowConfirmExit; - RememberWindowState.Value = configurationFileFormat.RememberWindowState; - EnableHardwareAcceleration.Value = configurationFileFormat.EnableHardwareAcceleration; - HideCursor.Value = configurationFileFormat.HideCursor; - Graphics.EnableVsync.Value = configurationFileFormat.EnableVsync; - Graphics.EnableShaderCache.Value = configurationFileFormat.EnableShaderCache; - Graphics.EnableTextureRecompression.Value = configurationFileFormat.EnableTextureRecompression; - Graphics.EnableMacroHLE.Value = configurationFileFormat.EnableMacroHLE; - Graphics.EnableColorSpacePassthrough.Value = configurationFileFormat.EnableColorSpacePassthrough; - System.EnablePtc.Value = configurationFileFormat.EnablePtc; - System.EnableInternetAccess.Value = configurationFileFormat.EnableInternetAccess; - System.EnableFsIntegrityChecks.Value = configurationFileFormat.EnableFsIntegrityChecks; - System.FsGlobalAccessLogMode.Value = configurationFileFormat.FsGlobalAccessLogMode; - System.AudioBackend.Value = configurationFileFormat.AudioBackend; - System.AudioVolume.Value = configurationFileFormat.AudioVolume; - System.MemoryManagerMode.Value = configurationFileFormat.MemoryManagerMode; - System.ExpandRam.Value = configurationFileFormat.ExpandRam; - System.IgnoreMissingServices.Value = configurationFileFormat.IgnoreMissingServices; - System.UseHypervisor.Value = configurationFileFormat.UseHypervisor; - UI.GuiColumns.FavColumn.Value = configurationFileFormat.GuiColumns.FavColumn; - UI.GuiColumns.IconColumn.Value = configurationFileFormat.GuiColumns.IconColumn; - UI.GuiColumns.AppColumn.Value = configurationFileFormat.GuiColumns.AppColumn; - UI.GuiColumns.DevColumn.Value = configurationFileFormat.GuiColumns.DevColumn; - UI.GuiColumns.VersionColumn.Value = configurationFileFormat.GuiColumns.VersionColumn; - UI.GuiColumns.TimePlayedColumn.Value = configurationFileFormat.GuiColumns.TimePlayedColumn; - UI.GuiColumns.LastPlayedColumn.Value = configurationFileFormat.GuiColumns.LastPlayedColumn; - UI.GuiColumns.FileExtColumn.Value = configurationFileFormat.GuiColumns.FileExtColumn; - UI.GuiColumns.FileSizeColumn.Value = configurationFileFormat.GuiColumns.FileSizeColumn; - UI.GuiColumns.PathColumn.Value = configurationFileFormat.GuiColumns.PathColumn; - UI.ColumnSort.SortColumnId.Value = configurationFileFormat.ColumnSort.SortColumnId; - UI.ColumnSort.SortAscending.Value = configurationFileFormat.ColumnSort.SortAscending; - UI.GameDirs.Value = configurationFileFormat.GameDirs; - UI.ShownFileTypes.NSP.Value = configurationFileFormat.ShownFileTypes.NSP; - UI.ShownFileTypes.PFS0.Value = configurationFileFormat.ShownFileTypes.PFS0; - UI.ShownFileTypes.XCI.Value = configurationFileFormat.ShownFileTypes.XCI; - UI.ShownFileTypes.NCA.Value = configurationFileFormat.ShownFileTypes.NCA; - UI.ShownFileTypes.NRO.Value = configurationFileFormat.ShownFileTypes.NRO; - UI.ShownFileTypes.NSO.Value = configurationFileFormat.ShownFileTypes.NSO; - UI.EnableCustomTheme.Value = configurationFileFormat.EnableCustomTheme; - UI.LanguageCode.Value = configurationFileFormat.LanguageCode; - UI.CustomThemePath.Value = configurationFileFormat.CustomThemePath; - UI.BaseStyle.Value = configurationFileFormat.BaseStyle; - UI.GameListViewMode.Value = configurationFileFormat.GameListViewMode; - UI.ShowNames.Value = configurationFileFormat.ShowNames; - UI.IsAscendingOrder.Value = configurationFileFormat.IsAscendingOrder; - UI.GridSize.Value = configurationFileFormat.GridSize; - UI.ApplicationSort.Value = configurationFileFormat.ApplicationSort; - UI.StartFullscreen.Value = configurationFileFormat.StartFullscreen; - UI.ShowConsole.Value = configurationFileFormat.ShowConsole; - UI.WindowStartup.WindowSizeWidth.Value = configurationFileFormat.WindowStartup.WindowSizeWidth; - UI.WindowStartup.WindowSizeHeight.Value = configurationFileFormat.WindowStartup.WindowSizeHeight; - UI.WindowStartup.WindowPositionX.Value = configurationFileFormat.WindowStartup.WindowPositionX; - UI.WindowStartup.WindowPositionY.Value = configurationFileFormat.WindowStartup.WindowPositionY; - UI.WindowStartup.WindowMaximized.Value = configurationFileFormat.WindowStartup.WindowMaximized; - Hid.EnableKeyboard.Value = configurationFileFormat.EnableKeyboard; - Hid.EnableMouse.Value = configurationFileFormat.EnableMouse; - Hid.Hotkeys.Value = configurationFileFormat.Hotkeys; - Hid.InputConfig.Value = configurationFileFormat.InputConfig; - - if (Hid.InputConfig.Value == null) - { - Hid.InputConfig.Value = new List(); - } - - Multiplayer.LanInterfaceId.Value = configurationFileFormat.MultiplayerLanInterfaceId; - Multiplayer.Mode.Value = configurationFileFormat.MultiplayerMode; - - if (configurationFileUpdated) - { - ToFileFormat().SaveConfig(configurationFilePath); - - Ryujinx.Common.Logging.Logger.Notice.Print(LogClass.Application, $"Configuration file updated to version {ConfigurationFileFormat.CurrentVersion}"); - } + ]; } private static GraphicsBackend DefaultGraphicsBackend() @@ -1592,22 +314,5 @@ namespace Ryujinx.UI.Common.Configuration return GraphicsBackend.OpenGl; } - - private static void LogValueChange(ReactiveEventArgs eventArgs, string valueName) - { - string message = string.Create(CultureInfo.InvariantCulture, $"{valueName} set to: {eventArgs.NewValue}"); - - Ryujinx.Common.Logging.Logger.Info?.Print(LogClass.Configuration, message); - } - - public static void Initialize() - { - if (Instance != null) - { - throw new InvalidOperationException("Configuration is already initialized"); } - - Instance = new ConfigurationState(); } - } -} diff --git a/src/Ryujinx.UI.Common/Configuration/LoggerModule.cs b/src/Ryujinx.UI.Common/Configuration/LoggerModule.cs index 9cb283593..a7913f142 100644 --- a/src/Ryujinx.UI.Common/Configuration/LoggerModule.cs +++ b/src/Ryujinx.UI.Common/Configuration/LoggerModule.cs @@ -1,4 +1,3 @@ -using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.Common.Logging.Targets; @@ -11,103 +10,69 @@ namespace Ryujinx.UI.Common.Configuration { public static void Initialize() { - ConfigurationState.Instance.Logger.EnableDebug.Event += ReloadEnableDebug; - ConfigurationState.Instance.Logger.EnableStub.Event += ReloadEnableStub; - ConfigurationState.Instance.Logger.EnableInfo.Event += ReloadEnableInfo; - ConfigurationState.Instance.Logger.EnableWarn.Event += ReloadEnableWarning; - ConfigurationState.Instance.Logger.EnableError.Event += ReloadEnableError; - ConfigurationState.Instance.Logger.EnableTrace.Event += ReloadEnableTrace; - ConfigurationState.Instance.Logger.EnableGuest.Event += ReloadEnableGuest; - ConfigurationState.Instance.Logger.EnableFsAccessLog.Event += ReloadEnableFsAccessLog; - ConfigurationState.Instance.Logger.FilteredClasses.Event += ReloadFilteredClasses; - ConfigurationState.Instance.Logger.EnableFileLog.Event += ReloadFileLogger; - } - - private static void ReloadEnableDebug(object sender, ReactiveEventArgs e) - { - Logger.SetEnable(LogLevel.Debug, e.NewValue); - } - - private static void ReloadEnableStub(object sender, ReactiveEventArgs e) - { - Logger.SetEnable(LogLevel.Stub, e.NewValue); - } - - private static void ReloadEnableInfo(object sender, ReactiveEventArgs e) - { - Logger.SetEnable(LogLevel.Info, e.NewValue); - } - - private static void ReloadEnableWarning(object sender, ReactiveEventArgs e) - { - Logger.SetEnable(LogLevel.Warning, e.NewValue); - } - - private static void ReloadEnableError(object sender, ReactiveEventArgs e) - { - Logger.SetEnable(LogLevel.Error, e.NewValue); - } - - private static void ReloadEnableTrace(object sender, ReactiveEventArgs e) - { - Logger.SetEnable(LogLevel.Trace, e.NewValue); - } - - private static void ReloadEnableGuest(object sender, ReactiveEventArgs e) - { - Logger.SetEnable(LogLevel.Guest, e.NewValue); - } - - private static void ReloadEnableFsAccessLog(object sender, ReactiveEventArgs e) - { - Logger.SetEnable(LogLevel.AccessLog, e.NewValue); - } - - private static void ReloadFilteredClasses(object sender, ReactiveEventArgs e) - { - bool noFilter = e.NewValue.Length == 0; - - foreach (var logClass in Enum.GetValues()) + ConfigurationState.Instance.Logger.EnableDebug.Event += + (_, e) => Logger.SetEnable(LogLevel.Debug, e.NewValue); + ConfigurationState.Instance.Logger.EnableStub.Event += + (_, e) => Logger.SetEnable(LogLevel.Stub, e.NewValue); + ConfigurationState.Instance.Logger.EnableInfo.Event += + (_, e) => Logger.SetEnable(LogLevel.Info, e.NewValue); + ConfigurationState.Instance.Logger.EnableWarn.Event += + (_, e) => Logger.SetEnable(LogLevel.Warning, e.NewValue); + ConfigurationState.Instance.Logger.EnableError.Event += + (_, e) => Logger.SetEnable(LogLevel.Error, e.NewValue); + ConfigurationState.Instance.Logger.EnableTrace.Event += + (_, e) => Logger.SetEnable(LogLevel.Trace, e.NewValue); + ConfigurationState.Instance.Logger.EnableGuest.Event += + (_, e) => Logger.SetEnable(LogLevel.Guest, e.NewValue); + ConfigurationState.Instance.Logger.EnableFsAccessLog.Event += + (_, e) => Logger.SetEnable(LogLevel.AccessLog, e.NewValue); + + ConfigurationState.Instance.Logger.FilteredClasses.Event += (_, e) => { - Logger.SetEnable(logClass, noFilter); - } + bool noFilter = e.NewValue.Length == 0; - foreach (var logClass in e.NewValue) - { - Logger.SetEnable(logClass, true); - } - } - - private static void ReloadFileLogger(object sender, ReactiveEventArgs e) - { - if (e.NewValue) - { - string logDir = AppDataManager.LogsDirPath; - FileStream logFile = null; - - if (!string.IsNullOrEmpty(logDir)) + foreach (var logClass in Enum.GetValues()) { - logFile = FileLogTarget.PrepareLogFile(logDir); + Logger.SetEnable(logClass, noFilter); } - if (logFile == null) + foreach (var logClass in e.NewValue) + { + Logger.SetEnable(logClass, true); + } + }; + + ConfigurationState.Instance.Logger.EnableFileLog.Event += (_, e) => + { + if (e.NewValue) + { + string logDir = AppDataManager.LogsDirPath; + FileStream logFile = null; + + if (!string.IsNullOrEmpty(logDir)) + { + logFile = FileLogTarget.PrepareLogFile(logDir); + } + + if (logFile == null) + { + Logger.Error?.Print(LogClass.Application, + "No writable log directory available. Make sure either the Logs directory, Application Data, or the Ryujinx directory is writable."); + Logger.RemoveTarget("file"); + + return; + } + + Logger.AddTarget(new AsyncLogTargetWrapper( + new FileLogTarget("file", logFile), + 1000 + )); + } + else { - Logger.Error?.Print(LogClass.Application, "No writable log directory available. Make sure either the Logs directory, Application Data, or the Ryujinx directory is writable."); Logger.RemoveTarget("file"); - - return; } - - Logger.AddTarget(new AsyncLogTargetWrapper( - new FileLogTarget("file", logFile), - 1000, - AsyncLogTargetOverflowAction.Block - )); - } - else - { - Logger.RemoveTarget("file"); - } + }; } } } diff --git a/src/Ryujinx.UI.Common/Configuration/UI/GuiColumns.cs b/src/Ryujinx.UI.Common/Configuration/UI/GuiColumns.cs index c778ef1f1..c486492e0 100644 --- a/src/Ryujinx.UI.Common/Configuration/UI/GuiColumns.cs +++ b/src/Ryujinx.UI.Common/Configuration/UI/GuiColumns.cs @@ -7,6 +7,7 @@ namespace Ryujinx.UI.Common.Configuration.UI public bool AppColumn { get; set; } public bool DevColumn { get; set; } public bool VersionColumn { get; set; } + public bool LdnInfoColumn { get; set; } public bool TimePlayedColumn { get; set; } public bool LastPlayedColumn { get; set; } public bool FileExtColumn { get; set; } diff --git a/src/Ryujinx.UI.Common/DiscordIntegrationModule.cs b/src/Ryujinx.UI.Common/DiscordIntegrationModule.cs index 6966038b6..338d28531 100644 --- a/src/Ryujinx.UI.Common/DiscordIntegrationModule.cs +++ b/src/Ryujinx.UI.Common/DiscordIntegrationModule.cs @@ -1,14 +1,28 @@ using DiscordRPC; +using Humanizer; +using Humanizer.Localisation; using Ryujinx.Common; +using Ryujinx.HLE.Loaders.Processes; +using Ryujinx.UI.App.Common; using Ryujinx.UI.Common.Configuration; +using System.Linq; using System.Text; namespace Ryujinx.UI.Common { public static class DiscordIntegrationModule { - private const string Description = "A simple, experimental Nintendo Switch emulator."; - private const string ApplicationId = "1216775165866807456"; + public static Timestamps StartedAt { get; set; } + + private static string VersionString + => (ReleaseInformation.IsCanaryBuild ? "Canary " : string.Empty) + $"v{ReleaseInformation.Version}"; + + private static readonly string _description = + ReleaseInformation.IsValid + ? $"{VersionString} {ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelSourceRepo}@{ReleaseInformation.BuildGitHash}" + : "dev build"; + + private const string ApplicationId = "1293250299716173864"; private const int ApplicationByteLimit = 128; private const string Ellipsis = "…"; @@ -23,19 +37,11 @@ namespace Ryujinx.UI.Common Assets = new Assets { LargeImageKey = "ryujinx", - LargeImageText = Description, + LargeImageText = TruncateToByteLength(_description) }, Details = "Main Menu", State = "Idling", - Timestamps = Timestamps.Now, - Buttons = - [ - new Button - { - Label = "Website", - Url = "https://ryujinx.org/", - }, - ], + Timestamps = StartedAt }; ConfigurationState.Instance.EnableDiscordIntegration.Event += Update; @@ -64,45 +70,36 @@ namespace Ryujinx.UI.Common } } - public static void SwitchToPlayingState(string titleId, string applicationName) + public static void SwitchToPlayingState(ApplicationMetadata appMeta, ProcessResult procRes) { _discordClient?.SetPresence(new RichPresence { Assets = new Assets { - LargeImageKey = "game", - LargeImageText = TruncateToByteLength(applicationName, ApplicationByteLimit), + LargeImageKey = _discordGameAssetKeys.Contains(procRes.ProgramIdText) ? procRes.ProgramIdText : "game", + LargeImageText = TruncateToByteLength($"{appMeta.Title} (v{procRes.DisplayVersion})"), SmallImageKey = "ryujinx", - SmallImageText = Description, + SmallImageText = TruncateToByteLength(_description) }, - Details = TruncateToByteLength($"Playing {applicationName}", ApplicationByteLimit), - State = (titleId == "0000000000000000") ? "Homebrew" : titleId.ToUpper(), - Timestamps = Timestamps.Now, - Buttons = - [ - new Button - { - Label = "Website", - Url = "https://ryujinx.org/", - }, - ], + Details = TruncateToByteLength($"Playing {appMeta.Title}"), + State = appMeta.LastPlayed.HasValue && appMeta.TimePlayed.TotalSeconds > 5 + ? $"Total play time: {appMeta.TimePlayed.Humanize(2, false, maxUnit: TimeUnit.Hour)}" + : "Never played", + Timestamps = Timestamps.Now }); } - public static void SwitchToMainMenu() - { - _discordClient?.SetPresence(_discordPresenceMain); - } + public static void SwitchToMainState() => _discordClient?.SetPresence(_discordPresenceMain); - private static string TruncateToByteLength(string input, int byteLimit) + private static string TruncateToByteLength(string input) { - if (Encoding.UTF8.GetByteCount(input) <= byteLimit) + if (Encoding.UTF8.GetByteCount(input) <= ApplicationByteLimit) { return input; } // Find the length to trim the string to guarantee we have space for the trailing ellipsis. - int trimLimit = byteLimit - Encoding.UTF8.GetByteCount(Ellipsis); + int trimLimit = ApplicationByteLimit - Encoding.UTF8.GetByteCount(Ellipsis); // Make sure the string is long enough to perform the basic trim. // Amount of bytes != Length of the string @@ -125,5 +122,151 @@ namespace Ryujinx.UI.Common { _discordClient?.Dispose(); } + + private static readonly string[] _discordGameAssetKeys = + [ + "010055d009f78000", // Fire Emblem: Three Houses + "0100a12011cc8000", // Fire Emblem: Shadow Dragon + "0100a6301214e000", // Fire Emblem Engage + "0100f15003e64000", // Fire Emblem Warriors + "010071f0143ea000", // Fire Emblem Warriors: Three Hopes + + "01007e3006dda000", // Kirby Star Allies + "01004d300c5ae000", // Kirby and the Forgotten Land + "01006b601380e000", // Kirby's Return to Dream Land Deluxe + "01003fb00c5a8000", // Super Kirby Clash + "0100227010460000", // Kirby Fighters 2 + "0100a8e016236000", // Kirby's Dream Buffet + + "01007ef00011e000", // The Legend of Zelda: Breath of the Wild + "01006bb00c6f0000", // The Legend of Zelda: Link's Awakening + "01002da013484000", // The Legend of Zelda: Skyward Sword HD + "0100f2c0115b6000", // The Legend of Zelda: Tears of the Kingdom + "01008cf01baac000", // The Legend of Zelda: Echoes of Wisdom + "01000b900d8b0000", // Cadence of Hyrule + "0100ae00096ea000", // Hyrule Warriors: Definitive Edition + "01002b00111a2000", // Hyrule Warriors: Age of Calamity + + "010048701995e000", // Luigi's Mansion 2 HD + "0100dca0064a6000", // Luigi's Mansion 3 + + "010093801237c000", // Metroid Dread + "010012101468c000", // Metroid Prime Remastered + + "0100000000010000", // SUPER MARIO ODYSSEY + "0100ea80032ea000", // Super Mario Bros. U Deluxe + "01009b90006dc000", // Super Mario Maker 2 + "010049900f546000", // Super Mario 3D All-Stars + "010049900F546001", // ^ 64 + "010049900F546002", // ^ Sunshine + "010049900F546003", // ^ Galaxy + "010028600ebda000", // Super Mario 3D World + Bowser's Fury + "010015100b514000", // Super Mario Bros. Wonder + "0100152000022000", // Mario Kart 8 Deluxe + "010036b0034e4000", // Super Mario Party + "01006fe013472000", // Mario Party Superstars + "0100965017338000", // Super Mario Party Jamboree + "01006d0017f7a000", // Mario & Luigi: Brothership + "010067300059a000", // Mario + Rabbids: Kingdom Battle + "0100317013770000", // Mario + Rabbids: Sparks of Hope + "0100a3900c3e2000", // Paper Mario: The Origami King + "0100ecd018ebe000", // Paper Mario: The Thousand-Year Door + "0100bc0018138000", // Super Mario RPG + "0100bde00862a000", // Mario Tennis Aces + "0100c9c00e25c000", // Mario Golf: Super Rush + "010019401051c000", // Mario Strikers: Battle League + "010003000e146000", // Mario & Sonic at the Olympic Games Tokyo 2020 + "0100b99019412000", // Mario vs. Donkey Kong + + "0100aa80194b0000", // Pikmin 1 + "0100d680194b2000", // Pikmin 2 + "0100f4c009322000", // Pikmin 3 Deluxe + "0100b7c00933a000", // Pikmin 4 + + "010003f003a34000", // Pokémon: Let's Go Pikachu! + "0100187003a36000", // Pokémon: Let's Go Eevee! + "0100abf008968000", // Pokémon Sword + "01008db008c2c000", // Pokémon Shield + "0100000011d90000", // Pokémon Brilliant Diamond + "010018e011d92000", // Pokémon Shining Pearl + "01001f5010dfa000", // Pokémon Legends: Arceus + "0100a3d008c5c000", // Pokémon Scarlet + "01008f6008c5e000", // Pokémon Violet + "0100b3f000be2000", // Pokkén Tournament DX + "0100f4300bf2c000", // New Pokémon Snap + + "01003bc0000a0000", // Splatoon 2 (US) + "0100f8f0000a2000", // Splatoon 2 (EU) + "01003c700009c000", // Splatoon 2 (JP) + "0100c2500fc20000", // Splatoon 3 + "0100ba0018500000", // Splatoon 3: Splatfest World Premiere + + "010040600c5ce000", // Tetris 99 + "0100277011f1a000", // Super Mario Bros. 35 + "0100ad9012510000", // PAC-MAN 99 + "0100ccf019c8c000", // F-ZERO 99 + "0100d870045b6000", // NES - Nintendo Switch Online + "01008d300c50c000", // SNES - Nintendo Switch Online + "0100c9a00ece6000", // N64 - Nintendo Switch Online + "0100e0601c632000", // N64 - Nintendo Switch Online 18+ + "0100c62011050000", // GB - Nintendo Switch Online + "010012f017576000", // GBA - Nintendo Switch Online + + "01000320000cc000", // 1-2 Switch + "0100300012f2a000", // Advance Wars 1+2: Re-Boot Camp + "01006f8002326000", // Animal Crossing: New Horizons + "0100620012d6e000", // Big Brain Academy: Brain vs. Brain + "010018300d006000", // BOXBOY! + BOXGIRL! + "0100c1f0051b6000", // Donkey Kong Country: Tropical Freeze + "0100ed000d390000", // Dr. Kawashima's Brain Training + "010067b017588000", // Endless Ocean Luminous + "0100d2f00d5c0000", // Nintendo Switch Sports + "01006b5012b32000", // Part Time UFO + "0100704000B3A000", // Snipperclips + "01006a800016e000", // Super Smash Bros. Ultimate + "0100a9400c9c2000", // Tokyo Mirage Sessions #FE Encore + + "010076f0049a2000", // Bayonetta + "01007960049a0000", // Bayonetta 2 + "01004a4010fea000", // Bayonetta 3 + "0100cf5010fec000", // Bayonetta Origins: Cereza and the Lost Demon + + "0100dcd01525a000", // Persona 3 Portable + "010062b01525c000", // Persona 4 Golden + "010075a016a3a000", // Persona 4 Arena Ultimax + "01005ca01580e000", // Persona 5 Royal + "0100801011c3e000", // Persona 5 Strikers + "010087701b092000", // Persona 5 Tactica + + "01009aa000faa000", // Sonic Mania + "01004ad014bf0000", // Sonic Frontiers + "01005ea01c0fc000", // SONIC X SHADOW GENERATIONS + "01005ea01c0fc001", // ^ + + "010056e00853a000", // A Hat in Time + "0100dbf01000a000", // Burnout Paradise Remastered + "0100744001588000", // Cars 3: Driven to Win + "0100b41013c82000", // Cruis'n Blast + "01001b300b9be000", // Diablo III: Eternal Collection + "01008c8012920000", // Dying Light Platinum Edition + "010073c01af34000", // LEGO Horizon Adventures + "0100770008dd8000", // Monster Hunter Generations Ultimate + "0100b04011742000", // Monster Hunter Rise + "0100853015e86000", // No Man's Sky + "01007bb017812000", // Portal + "0100abd01785c000", // Portal 2 + "01008e200c5c2000", // Muse Dash + "01007820196a6000", // Red Dead Redemption + "01002f7013224000", // Rune Factory 5 + "01008d100d43e000", // Saints Row IV + "0100de600beee000", // Saints Row: The Third - The Full Package + "01001180021fa000", // Shovel Knight: Specter of Torment + "0100d7a01b7a2000", // Star Wars: Bounty Hunter + "0100800015926000", // Suika Game + "0100e46006708000", // Terraria + "01000a10041ea000", // The Elder Scrolls V: Skyrim + "010057a01e4d4000", // TSUKIHIME -A piece of blue glass moon- + "010080b00ad66000", // Undertale + ]; } } diff --git a/src/Ryujinx.UI.Common/Helper/CommandLineState.cs b/src/Ryujinx.UI.Common/Helper/CommandLineState.cs index ae0e4d904..3a96a55c8 100644 --- a/src/Ryujinx.UI.Common/Helper/CommandLineState.cs +++ b/src/Ryujinx.UI.Common/Helper/CommandLineState.cs @@ -16,6 +16,7 @@ namespace Ryujinx.UI.Common.Helper public static string LaunchPathArg { get; private set; } public static string LaunchApplicationId { get; private set; } public static bool StartFullscreenArg { get; private set; } + public static bool HideAvailableUpdates { get; private set; } public static void ParseArguments(string[] args) { @@ -93,6 +94,9 @@ namespace Ryujinx.UI.Common.Helper OverrideHideCursor = args[++i]; break; + case "--hide-updates": + HideAvailableUpdates = true; + break; case "--software-gui": OverrideHardwareAcceleration = false; break; diff --git a/src/Ryujinx.UI.Common/Helper/ConsoleHelper.cs b/src/Ryujinx.UI.Common/Helper/ConsoleHelper.cs index 208ff5c9d..99b209c6e 100644 --- a/src/Ryujinx.UI.Common/Helper/ConsoleHelper.cs +++ b/src/Ryujinx.UI.Common/Helper/ConsoleHelper.cs @@ -7,6 +7,24 @@ namespace Ryujinx.UI.Common.Helper { public static partial class ConsoleHelper { + [SupportedOSPlatform("windows")] + [LibraryImport("kernel32")] + private static partial nint GetConsoleWindow(); + + [SupportedOSPlatform("windows")] + [LibraryImport("user32")] + [return: MarshalAs(UnmanagedType.Bool)] + private static partial bool ShowWindow(nint hWnd, int nCmdShow); + + [SupportedOSPlatform("windows")] + [LibraryImport("user32")] + private static partial nint GetForegroundWindow(); + + [SupportedOSPlatform("windows")] + [LibraryImport("user32")] + [return: MarshalAs(UnmanagedType.Bool)] + private static partial bool SetForegroundWindow(nint hWnd); + public static bool SetConsoleWindowStateSupported => OperatingSystem.IsWindows(); public static void SetConsoleWindowState(bool show) @@ -27,24 +45,19 @@ namespace Ryujinx.UI.Common.Helper const int SW_HIDE = 0; const int SW_SHOW = 5; - IntPtr hWnd = GetConsoleWindow(); + nint hWnd = GetConsoleWindow(); - if (hWnd == IntPtr.Zero) + if (hWnd == nint.Zero) { Logger.Warning?.Print(LogClass.Application, "Attempted to show/hide console window but console window does not exist"); return; } + SetForegroundWindow(hWnd); + + hWnd = GetForegroundWindow(); + ShowWindow(hWnd, show ? SW_SHOW : SW_HIDE); } - - [SupportedOSPlatform("windows")] - [LibraryImport("kernel32")] - private static partial IntPtr GetConsoleWindow(); - - [SupportedOSPlatform("windows")] - [LibraryImport("user32")] - [return: MarshalAs(UnmanagedType.Bool)] - private static partial bool ShowWindow(IntPtr hWnd, int nCmdShow); } } diff --git a/src/Ryujinx.UI.Common/Helper/DownloadableContentsHelper.cs b/src/Ryujinx.UI.Common/Helper/DownloadableContentsHelper.cs new file mode 100644 index 000000000..020529b55 --- /dev/null +++ b/src/Ryujinx.UI.Common/Helper/DownloadableContentsHelper.cs @@ -0,0 +1,135 @@ +using LibHac.Common; +using LibHac.Fs; +using LibHac.Fs.Fsa; +using LibHac.Tools.FsSystem; +using LibHac.Tools.FsSystem.NcaUtils; +using Ryujinx.Common.Configuration; +using Ryujinx.Common.Logging; +using Ryujinx.Common.Utilities; +using Ryujinx.HLE.FileSystem; +using Ryujinx.HLE.Utilities; +using Ryujinx.UI.Common.Models; +using System; +using System.Collections.Generic; +using System.IO; +using Path = System.IO.Path; + +namespace Ryujinx.UI.Common.Helper +{ + public static class DownloadableContentsHelper + { + private static readonly DownloadableContentJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + + public static List<(DownloadableContentModel, bool IsEnabled)> LoadDownloadableContentsJson(VirtualFileSystem vfs, ulong applicationIdBase) + { + var downloadableContentJsonPath = PathToGameDLCJson(applicationIdBase); + + if (!File.Exists(downloadableContentJsonPath)) + { + return []; + } + + try + { + var downloadableContentContainerList = JsonHelper.DeserializeFromFile(downloadableContentJsonPath, + _serializerContext.ListDownloadableContentContainer); + return LoadDownloadableContents(vfs, downloadableContentContainerList); + } + catch + { + Logger.Error?.Print(LogClass.Configuration, "Downloadable Content JSON failed to deserialize."); + return []; + } + } + + public static void SaveDownloadableContentsJson(ulong applicationIdBase, List<(DownloadableContentModel, bool IsEnabled)> dlcs) + { + DownloadableContentContainer container = default; + List downloadableContentContainerList = new(); + + foreach ((DownloadableContentModel dlc, bool isEnabled) in dlcs) + { + if (container.ContainerPath != dlc.ContainerPath) + { + if (!string.IsNullOrWhiteSpace(container.ContainerPath)) + { + downloadableContentContainerList.Add(container); + } + + container = new DownloadableContentContainer + { + ContainerPath = dlc.ContainerPath, + DownloadableContentNcaList = [], + }; + } + + container.DownloadableContentNcaList.Add(new DownloadableContentNca + { + Enabled = isEnabled, + TitleId = dlc.TitleId, + FullPath = dlc.FullPath, + }); + } + + if (!string.IsNullOrWhiteSpace(container.ContainerPath)) + { + downloadableContentContainerList.Add(container); + } + + var downloadableContentJsonPath = PathToGameDLCJson(applicationIdBase); + JsonHelper.SerializeToFile(downloadableContentJsonPath, downloadableContentContainerList, _serializerContext.ListDownloadableContentContainer); + } + + private static List<(DownloadableContentModel, bool IsEnabled)> LoadDownloadableContents(VirtualFileSystem vfs, List downloadableContentContainers) + { + var result = new List<(DownloadableContentModel, bool IsEnabled)>(); + + foreach (DownloadableContentContainer downloadableContentContainer in downloadableContentContainers) + { + if (!File.Exists(downloadableContentContainer.ContainerPath)) + { + continue; + } + + using IFileSystem partitionFileSystem = PartitionFileSystemUtils.OpenApplicationFileSystem(downloadableContentContainer.ContainerPath, vfs); + + foreach (DownloadableContentNca downloadableContentNca in downloadableContentContainer.DownloadableContentNcaList) + { + using UniqueRef ncaFile = new(); + + partitionFileSystem.OpenFile(ref ncaFile.Ref, downloadableContentNca.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + + Nca nca = TryOpenNca(vfs, ncaFile.Get.AsStorage()); + if (nca == null) + { + continue; + } + + var content = new DownloadableContentModel(nca.Header.TitleId, + downloadableContentContainer.ContainerPath, + downloadableContentNca.FullPath); + + result.Add((content, downloadableContentNca.Enabled)); + } + } + + return result; + } + + private static Nca TryOpenNca(VirtualFileSystem vfs, IStorage ncaStorage) + { + try + { + return new Nca(vfs.KeySet, ncaStorage); + } + catch (Exception) { } + + return null; + } + + private static string PathToGameDLCJson(ulong applicationIdBase) + { + return Path.Combine(AppDataManager.GamesDirPath, applicationIdBase.ToString("x16"), "dlc.json"); + } + } +} diff --git a/src/Ryujinx.UI.Common/Helper/FileAssociationHelper.cs b/src/Ryujinx.UI.Common/Helper/FileAssociationHelper.cs index 7ed020319..44860d080 100644 --- a/src/Ryujinx.UI.Common/Helper/FileAssociationHelper.cs +++ b/src/Ryujinx.UI.Common/Helper/FileAssociationHelper.cs @@ -4,6 +4,7 @@ using Ryujinx.Common.Logging; using System; using System.Diagnostics; using System.IO; +using System.Linq; using System.Runtime.InteropServices; using System.Runtime.Versioning; @@ -11,7 +12,7 @@ namespace Ryujinx.UI.Common.Helper { public static partial class FileAssociationHelper { - private static readonly string[] _fileExtensions = { ".nca", ".nro", ".nso", ".nsp", ".xci" }; + private static readonly string[] _fileExtensions = [".nca", ".nro", ".nso", ".nsp", ".xci"]; [SupportedOSPlatform("linux")] private static readonly string _mimeDbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share", "mime"); @@ -20,9 +21,29 @@ namespace Ryujinx.UI.Common.Helper private const int SHCNF_FLUSH = 0x1000; [LibraryImport("shell32.dll", SetLastError = true)] - public static partial void SHChangeNotify(uint wEventId, uint uFlags, IntPtr dwItem1, IntPtr dwItem2); + public static partial void SHChangeNotify(uint wEventId, uint uFlags, nint dwItem1, nint dwItem2); - public static bool IsTypeAssociationSupported => (OperatingSystem.IsLinux() || OperatingSystem.IsWindows()) && !ReleaseInformation.IsFlatHubBuild; + public static bool IsTypeAssociationSupported => (OperatingSystem.IsLinux() || OperatingSystem.IsWindows()); + + public static bool AreMimeTypesRegistered + { + get + { + if (OperatingSystem.IsLinux()) + { + return AreMimeTypesRegisteredLinux(); + } + + if (OperatingSystem.IsWindows()) + { + return AreMimeTypesRegisteredWindows(); + } + + // TODO: Add macOS support. + + return false; + } + } [SupportedOSPlatform("linux")] private static bool AreMimeTypesRegisteredLinux() => File.Exists(Path.Combine(_mimeDbPath, "packages", "Ryujinx.xml")); @@ -35,7 +56,7 @@ namespace Ryujinx.UI.Common.Helper if ((uninstall && AreMimeTypesRegisteredLinux()) || (!uninstall && !AreMimeTypesRegisteredLinux())) { string mimeTypesFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "mime", "Ryujinx.xml"); - string additionalArgs = !uninstall ? "--novendor" : ""; + string additionalArgs = !uninstall ? "--novendor" : string.Empty; using Process mimeProcess = new(); @@ -72,35 +93,39 @@ namespace Ryujinx.UI.Common.Helper [SupportedOSPlatform("windows")] private static bool AreMimeTypesRegisteredWindows() { + return _fileExtensions.Aggregate(false, + (current, ext) => current | CheckRegistering(ext) + ); + static bool CheckRegistering(string ext) { RegistryKey key = Registry.CurrentUser.OpenSubKey(@$"Software\Classes\{ext}"); - if (key is null) + var openCmd = key?.OpenSubKey(@"shell\open\command"); + + if (openCmd is null) { return false; } - - var openCmd = key.OpenSubKey(@"shell\open\command"); - - string keyValue = (string)openCmd.GetValue(""); + + string keyValue = (string)openCmd.GetValue(string.Empty); return keyValue is not null && (keyValue.Contains("Ryujinx") || keyValue.Contains(AppDomain.CurrentDomain.FriendlyName)); } - - bool registered = false; - - foreach (string ext in _fileExtensions) - { - registered |= CheckRegistering(ext); - } - - return registered; } [SupportedOSPlatform("windows")] private static bool InstallWindowsMimeTypes(bool uninstall = false) { + bool registered = _fileExtensions.Aggregate(false, + (current, ext) => current | RegisterExtension(ext, uninstall) + ); + + // Notify Explorer the file association has been changed. + SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_FLUSH, nint.Zero, nint.Zero); + + return registered; + static bool RegisterExtension(string ext, bool uninstall = false) { string keyString = @$"Software\Classes\{ext}"; @@ -127,42 +152,13 @@ namespace Ryujinx.UI.Common.Helper Logger.Debug?.Print(LogClass.Application, $"Adding type association {ext}"); using var openCmd = key.CreateSubKey(@"shell\open\command"); - openCmd.SetValue("", $"\"{Environment.ProcessPath}\" \"%1\""); + openCmd.SetValue(string.Empty, $"\"{Environment.ProcessPath}\" \"%1\""); Logger.Debug?.Print(LogClass.Application, $"Added type association {ext}"); } return true; } - - bool registered = false; - - foreach (string ext in _fileExtensions) - { - registered |= RegisterExtension(ext, uninstall); - } - - // Notify Explorer the file association has been changed. - SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_FLUSH, IntPtr.Zero, IntPtr.Zero); - - return registered; - } - - public static bool AreMimeTypesRegistered() - { - if (OperatingSystem.IsLinux()) - { - return AreMimeTypesRegisteredLinux(); - } - - if (OperatingSystem.IsWindows()) - { - return AreMimeTypesRegisteredWindows(); - } - - // TODO: Add macOS support. - - return false; } public static bool Install() diff --git a/src/Ryujinx.UI.Common/Helper/ObjectiveC.cs b/src/Ryujinx.UI.Common/Helper/ObjectiveC.cs index 6aba377a3..f8f972098 100644 --- a/src/Ryujinx.UI.Common/Helper/ObjectiveC.cs +++ b/src/Ryujinx.UI.Common/Helper/ObjectiveC.cs @@ -10,44 +10,44 @@ namespace Ryujinx.UI.Common.Helper private const string ObjCRuntime = "/usr/lib/libobjc.A.dylib"; [LibraryImport(ObjCRuntime, StringMarshalling = StringMarshalling.Utf8)] - private static partial IntPtr sel_getUid(string name); + private static partial nint sel_getUid(string name); [LibraryImport(ObjCRuntime, StringMarshalling = StringMarshalling.Utf8)] - private static partial IntPtr objc_getClass(string name); + private static partial nint objc_getClass(string name); [LibraryImport(ObjCRuntime)] - private static partial void objc_msgSend(IntPtr receiver, Selector selector); + private static partial void objc_msgSend(nint receiver, Selector selector); [LibraryImport(ObjCRuntime)] - private static partial void objc_msgSend(IntPtr receiver, Selector selector, byte value); + private static partial void objc_msgSend(nint receiver, Selector selector, byte value); [LibraryImport(ObjCRuntime)] - private static partial void objc_msgSend(IntPtr receiver, Selector selector, IntPtr value); + private static partial void objc_msgSend(nint receiver, Selector selector, nint value); [LibraryImport(ObjCRuntime)] - private static partial void objc_msgSend(IntPtr receiver, Selector selector, NSRect point); + private static partial void objc_msgSend(nint receiver, Selector selector, NSRect point); [LibraryImport(ObjCRuntime)] - private static partial void objc_msgSend(IntPtr receiver, Selector selector, double value); + private static partial void objc_msgSend(nint receiver, Selector selector, double value); [LibraryImport(ObjCRuntime, EntryPoint = "objc_msgSend")] - private static partial IntPtr IntPtr_objc_msgSend(IntPtr receiver, Selector selector); + private static partial nint nint_objc_msgSend(nint receiver, Selector selector); [LibraryImport(ObjCRuntime, EntryPoint = "objc_msgSend")] - private static partial IntPtr IntPtr_objc_msgSend(IntPtr receiver, Selector selector, IntPtr param); + private static partial nint nint_objc_msgSend(nint receiver, Selector selector, nint param); [LibraryImport(ObjCRuntime, EntryPoint = "objc_msgSend", StringMarshalling = StringMarshalling.Utf8)] - private static partial IntPtr IntPtr_objc_msgSend(IntPtr receiver, Selector selector, string param); + private static partial nint nint_objc_msgSend(nint receiver, Selector selector, string param); [LibraryImport(ObjCRuntime, EntryPoint = "objc_msgSend")] [return: MarshalAs(UnmanagedType.Bool)] - private static partial bool bool_objc_msgSend(IntPtr receiver, Selector selector, IntPtr param); + private static partial bool bool_objc_msgSend(nint receiver, Selector selector, nint param); public readonly struct Object { - public readonly IntPtr ObjPtr; + public readonly nint ObjPtr; - private Object(IntPtr pointer) + private Object(nint pointer) { ObjPtr = pointer; } @@ -84,22 +84,22 @@ namespace Ryujinx.UI.Common.Helper public Object GetFromMessage(Selector selector) { - return new Object(IntPtr_objc_msgSend(ObjPtr, selector)); + return new Object(nint_objc_msgSend(ObjPtr, selector)); } public Object GetFromMessage(Selector selector, Object obj) { - return new Object(IntPtr_objc_msgSend(ObjPtr, selector, obj.ObjPtr)); + return new Object(nint_objc_msgSend(ObjPtr, selector, obj.ObjPtr)); } public Object GetFromMessage(Selector selector, NSString nsString) { - return new Object(IntPtr_objc_msgSend(ObjPtr, selector, nsString.StrPtr)); + return new Object(nint_objc_msgSend(ObjPtr, selector, nsString.StrPtr)); } public Object GetFromMessage(Selector selector, string param) { - return new Object(IntPtr_objc_msgSend(ObjPtr, selector, param)); + return new Object(nint_objc_msgSend(ObjPtr, selector, param)); } public bool GetBoolFromMessage(Selector selector, Object obj) @@ -110,7 +110,7 @@ namespace Ryujinx.UI.Common.Helper public readonly struct Selector { - public readonly IntPtr SelPtr; + public readonly nint SelPtr; private Selector(string name) { @@ -122,15 +122,15 @@ namespace Ryujinx.UI.Common.Helper public readonly struct NSString { - public readonly IntPtr StrPtr; + public readonly nint StrPtr; public NSString(string aString) { - IntPtr nsString = objc_getClass("NSString"); - StrPtr = IntPtr_objc_msgSend(nsString, "stringWithUTF8String:", aString); + nint nsString = objc_getClass("NSString"); + StrPtr = nint_objc_msgSend(nsString, "stringWithUTF8String:", aString); } - public static implicit operator IntPtr(NSString nsString) => nsString.StrPtr; + public static implicit operator nint(NSString nsString) => nsString.StrPtr; } public readonly struct NSPoint diff --git a/src/Ryujinx.UI.Common/Helper/OpenHelper.cs b/src/Ryujinx.UI.Common/Helper/OpenHelper.cs index af6170afe..bf398a355 100644 --- a/src/Ryujinx.UI.Common/Helper/OpenHelper.cs +++ b/src/Ryujinx.UI.Common/Helper/OpenHelper.cs @@ -1,3 +1,4 @@ +using Gommon; using Ryujinx.Common.Logging; using System; using System.Diagnostics; @@ -9,13 +10,13 @@ namespace Ryujinx.UI.Common.Helper public static partial class OpenHelper { [LibraryImport("shell32.dll", SetLastError = true)] - private static partial int SHOpenFolderAndSelectItems(IntPtr pidlFolder, uint cidl, IntPtr apidl, uint dwFlags); + private static partial int SHOpenFolderAndSelectItems(nint pidlFolder, uint cidl, nint apidl, uint dwFlags); [LibraryImport("shell32.dll", SetLastError = true)] - private static partial void ILFree(IntPtr pidlList); + private static partial void ILFree(nint pidlList); [LibraryImport("shell32.dll", SetLastError = true)] - private static partial IntPtr ILCreateFromPathW([MarshalAs(UnmanagedType.LPWStr)] string pszPath); + private static partial nint ILCreateFromPathW([MarshalAs(UnmanagedType.LPWStr)] string pszPath); public static void OpenFolder(string path) { @@ -34,18 +35,20 @@ namespace Ryujinx.UI.Common.Helper } } + public static void OpenFolder(FilePath path) => OpenFolder(path.Path); + public static void LocateFile(string path) { if (File.Exists(path)) { if (OperatingSystem.IsWindows()) { - IntPtr pidlList = ILCreateFromPathW(path); - if (pidlList != IntPtr.Zero) + nint pidlList = ILCreateFromPathW(path); + if (pidlList != nint.Zero) { try { - Marshal.ThrowExceptionForHR(SHOpenFolderAndSelectItems(pidlList, 0, IntPtr.Zero, 0)); + Marshal.ThrowExceptionForHR(SHOpenFolderAndSelectItems(pidlList, 0, nint.Zero, 0)); } finally { diff --git a/src/Ryujinx.UI.Common/Helper/SetupValidator.cs b/src/Ryujinx.UI.Common/Helper/SetupValidator.cs index a954be26f..45d9f8f0d 100644 --- a/src/Ryujinx.UI.Common/Helper/SetupValidator.cs +++ b/src/Ryujinx.UI.Common/Helper/SetupValidator.cs @@ -12,18 +12,11 @@ namespace Ryujinx.UI.Common.Helper { public static bool IsFirmwareValid(ContentManager contentManager, out UserError error) { - bool hasFirmware = contentManager.GetCurrentFirmwareVersion() != null; + error = contentManager.GetCurrentFirmwareVersion() != null + ? UserError.Success + : UserError.NoFirmware; - if (hasFirmware) - { - error = UserError.Success; - - return true; - } - - error = UserError.NoFirmware; - - return false; + return error is UserError.Success; } public static bool CanFixStartApplication(ContentManager contentManager, string baseApplicationPath, UserError error, out SystemVersion firmwareVersion) @@ -75,12 +68,11 @@ namespace Ryujinx.UI.Common.Helper return true; } - catch (Exception) { } + catch + { + // ignored + } } - - outError = error; - - return false; } } @@ -96,13 +88,12 @@ namespace Ryujinx.UI.Common.Helper string baseApplicationExtension = Path.GetExtension(baseApplicationPath).ToLowerInvariant(); // NOTE: We don't force homebrew developers to install a system firmware. - if (baseApplicationExtension == ".nro" || baseApplicationExtension == ".nso") + if (baseApplicationExtension is ".nro" or ".nso") { error = UserError.Success; - return true; } - + return IsFirmwareValid(contentManager, out error); } diff --git a/src/Ryujinx.UI.Common/Helper/ShortcutHelper.cs b/src/Ryujinx.UI.Common/Helper/ShortcutHelper.cs index 1849f40cb..8c006a227 100644 --- a/src/Ryujinx.UI.Common/Helper/ShortcutHelper.cs +++ b/src/Ryujinx.UI.Common/Helper/ShortcutHelper.cs @@ -140,11 +140,11 @@ namespace Ryujinx.UI.Common.Helper argsList.Add($"\"{appFilePath}\""); - return String.Join(" ", argsList); + return string.Join(" ", argsList); } /// - /// Creates a Icon (.ico) file using the source bitmap image at the specified file path. + /// Creates an Icon (.ico) file using the source bitmap image at the specified file path. /// /// The source bitmap image that will be saved as an .ico file /// The location that the new .ico file will be saved too (Make sure to include '.ico' in the path). diff --git a/src/Ryujinx.UI.Common/Helper/TitleHelper.cs b/src/Ryujinx.UI.Common/Helper/TitleHelper.cs index 8b47ac38b..9d73aea75 100644 --- a/src/Ryujinx.UI.Common/Helper/TitleHelper.cs +++ b/src/Ryujinx.UI.Common/Helper/TitleHelper.cs @@ -8,9 +8,7 @@ namespace Ryujinx.UI.Common.Helper public static string ActiveApplicationTitle(ProcessResult activeProcess, string applicationVersion, string pauseString = "") { if (activeProcess == null) - { - return String.Empty; - } + return string.Empty; string titleNameSection = string.IsNullOrWhiteSpace(activeProcess.Name) ? string.Empty : $" {activeProcess.Name}"; string titleVersionSection = string.IsNullOrWhiteSpace(activeProcess.DisplayVersion) ? string.Empty : $" v{activeProcess.DisplayVersion}"; @@ -19,12 +17,9 @@ namespace Ryujinx.UI.Common.Helper string appTitle = $"Ryujinx {applicationVersion} -{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}"; - if (!string.IsNullOrEmpty(pauseString)) - { - appTitle += $" ({pauseString})"; - } - - return appTitle; + return !string.IsNullOrEmpty(pauseString) + ? appTitle + $" ({pauseString})" + : appTitle; } } } diff --git a/src/Ryujinx.UI.Common/Helper/TitleUpdatesHelper.cs b/src/Ryujinx.UI.Common/Helper/TitleUpdatesHelper.cs new file mode 100644 index 000000000..36de8b31a --- /dev/null +++ b/src/Ryujinx.UI.Common/Helper/TitleUpdatesHelper.cs @@ -0,0 +1,151 @@ +using LibHac.Common; +using LibHac.Common.Keys; +using LibHac.Fs; +using LibHac.Fs.Fsa; +using LibHac.Ncm; +using LibHac.Ns; +using LibHac.Tools.FsSystem; +using LibHac.Tools.FsSystem.NcaUtils; +using Ryujinx.Common.Configuration; +using Ryujinx.Common.Logging; +using Ryujinx.Common.Utilities; +using Ryujinx.HLE.FileSystem; +using Ryujinx.HLE.Loaders.Processes.Extensions; +using Ryujinx.HLE.Utilities; +using Ryujinx.UI.Common.Configuration; +using Ryujinx.UI.Common.Models; +using System; +using System.Collections.Generic; +using System.IO; +using ContentType = LibHac.Ncm.ContentType; +using Path = System.IO.Path; +using SpanHelpers = LibHac.Common.SpanHelpers; +using TitleUpdateMetadata = Ryujinx.Common.Configuration.TitleUpdateMetadata; + +namespace Ryujinx.UI.Common.Helper +{ + public static class TitleUpdatesHelper + { + private static readonly TitleUpdateMetadataJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); + + public static List<(TitleUpdateModel Update, bool IsSelected)> LoadTitleUpdatesJson(VirtualFileSystem vfs, ulong applicationIdBase) + { + var titleUpdatesJsonPath = PathToGameUpdatesJson(applicationIdBase); + + if (!File.Exists(titleUpdatesJsonPath)) + { + return []; + } + + try + { + var titleUpdateWindowData = JsonHelper.DeserializeFromFile(titleUpdatesJsonPath, _serializerContext.TitleUpdateMetadata); + return LoadTitleUpdates(vfs, titleUpdateWindowData, applicationIdBase); + } + catch + { + Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {applicationIdBase:x16} at {titleUpdatesJsonPath}"); + return []; + } + } + + public static void SaveTitleUpdatesJson(ulong applicationIdBase, List<(TitleUpdateModel, bool IsSelected)> updates) + { + var titleUpdateWindowData = new TitleUpdateMetadata + { + Selected = string.Empty, + Paths = [], + }; + + foreach ((TitleUpdateModel update, bool isSelected) in updates) + { + titleUpdateWindowData.Paths.Add(update.Path); + if (isSelected) + { + if (!string.IsNullOrEmpty(titleUpdateWindowData.Selected)) + { + Logger.Error?.Print(LogClass.Application, + $"Tried to save two updates as 'IsSelected' for {applicationIdBase:x16}"); + return; + } + + titleUpdateWindowData.Selected = update.Path; + } + } + + var titleUpdatesJsonPath = PathToGameUpdatesJson(applicationIdBase); + JsonHelper.SerializeToFile(titleUpdatesJsonPath, titleUpdateWindowData, _serializerContext.TitleUpdateMetadata); + } + + private static List<(TitleUpdateModel Update, bool IsSelected)> LoadTitleUpdates(VirtualFileSystem vfs, TitleUpdateMetadata titleUpdateMetadata, ulong applicationIdBase) + { + var result = new List<(TitleUpdateModel, bool IsSelected)>(); + + IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks + ? IntegrityCheckLevel.ErrorOnInvalid + : IntegrityCheckLevel.None; + + foreach (string path in titleUpdateMetadata.Paths) + { + if (!File.Exists(path)) + continue; + + try + { + using IFileSystem pfs = PartitionFileSystemUtils.OpenApplicationFileSystem(path, vfs); + + Dictionary updates = + pfs.GetContentData(ContentMetaType.Patch, vfs, checkLevel); + + if (!updates.TryGetValue(applicationIdBase, out ContentMetaData content)) + continue; + + Nca patchNca = content.GetNcaByType(vfs.KeySet, ContentType.Program); + Nca controlNca = content.GetNcaByType(vfs.KeySet, ContentType.Control); + + if (controlNca is null || patchNca is null) + continue; + + ApplicationControlProperty controlData = new(); + + using UniqueRef nacpFile = new(); + + controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None) + .OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); + nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None) + .ThrowIfFailure(); + + var displayVersion = controlData.DisplayVersionString.ToString(); + var update = new TitleUpdateModel(content.ApplicationId, content.Version.Version, + displayVersion, path); + + result.Add((update, path == titleUpdateMetadata.Selected)); + } + catch (MissingKeyException exception) + { + Logger.Warning?.Print(LogClass.Application, + $"Your key set is missing a key with the name: {exception.Name}"); + } + catch (InvalidDataException) + { + Logger.Warning?.Print(LogClass.Application, + $"The header key is incorrect or missing and therefore the NCA header content type check has failed. Malformed File: {path}"); + } + catch (IOException exception) + { + Logger.Warning?.Print(LogClass.Application, exception.Message); + } + catch (Exception exception) + { + Logger.Warning?.Print(LogClass.Application, + $"The file encountered was not of a valid type. File: '{path}' Error: {exception}"); + } + } + + return result; + } + + private static string PathToGameUpdatesJson(ulong applicationIdBase) + => Path.Combine(AppDataManager.GamesDirPath, applicationIdBase.ToString("x16"), "updates.json"); + } +} diff --git a/src/Ryujinx.UI.Common/Helper/ValueFormatUtils.cs b/src/Ryujinx.UI.Common/Helper/ValueFormatUtils.cs index 8ea3e721f..c203834f5 100644 --- a/src/Ryujinx.UI.Common/Helper/ValueFormatUtils.cs +++ b/src/Ryujinx.UI.Common/Helper/ValueFormatUtils.cs @@ -75,13 +75,7 @@ namespace Ryujinx.UI.Common.Helper { culture ??= CultureInfo.CurrentCulture; - if (!utcDateTime.HasValue) - { - // In the Avalonia UI, this is turned into a localized version of "Never" by LocalizedNeverConverter. - return "Never"; - } - - return utcDateTime.Value.ToLocalTime().ToString(culture); + return utcDateTime?.ToLocalTime().ToString(culture); } /// diff --git a/src/Ryujinx.UI.Common/Models/DownloadableContentModel.cs b/src/Ryujinx.UI.Common/Models/DownloadableContentModel.cs new file mode 100644 index 000000000..95c64f078 --- /dev/null +++ b/src/Ryujinx.UI.Common/Models/DownloadableContentModel.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.UI.Common.Models +{ + // NOTE: most consuming code relies on this model being value-comparable + public record DownloadableContentModel(ulong TitleId, string ContainerPath, string FullPath) + { + public bool IsBundled { get; } = System.IO.Path.GetExtension(ContainerPath)?.ToLower() == ".xci"; + + public string FileName => System.IO.Path.GetFileName(ContainerPath); + public string TitleIdStr => TitleId.ToString("x16"); + public ulong TitleIdBase => TitleId & ~0x1FFFUL; + } +} diff --git a/src/Ryujinx.UI.Common/Models/Github/GithubReleasesJsonResponse.cs b/src/Ryujinx.UI.Common/Models/Github/GithubReleasesJsonResponse.cs index 0250e1094..7bec1bcdc 100644 --- a/src/Ryujinx.UI.Common/Models/Github/GithubReleasesJsonResponse.cs +++ b/src/Ryujinx.UI.Common/Models/Github/GithubReleasesJsonResponse.cs @@ -5,6 +5,8 @@ namespace Ryujinx.UI.Common.Models.Github public class GithubReleasesJsonResponse { public string Name { get; set; } + + public string TagName { get; set; } public List Assets { get; set; } } } diff --git a/src/Ryujinx.UI.Common/Models/TitleUpdateModel.cs b/src/Ryujinx.UI.Common/Models/TitleUpdateModel.cs new file mode 100644 index 000000000..5422e1303 --- /dev/null +++ b/src/Ryujinx.UI.Common/Models/TitleUpdateModel.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.UI.Common.Models +{ + // NOTE: most consuming code relies on this model being value-comparable + public record TitleUpdateModel(ulong TitleId, ulong Version, string DisplayVersion, string Path) + { + public bool IsBundled { get; } = System.IO.Path.GetExtension(Path)?.ToLower() == ".xci"; + + public string TitleIdStr => TitleId.ToString("x16"); + public ulong TitleIdBase => TitleId & ~0x1FFFUL; + } +} diff --git a/src/Ryujinx.UI.Common/Models/XCITrimmerFileModel.cs b/src/Ryujinx.UI.Common/Models/XCITrimmerFileModel.cs new file mode 100644 index 000000000..95fb3985b --- /dev/null +++ b/src/Ryujinx.UI.Common/Models/XCITrimmerFileModel.cs @@ -0,0 +1,55 @@ +using Ryujinx.Common.Logging; +using Ryujinx.Common.Utilities; +using Ryujinx.UI.App.Common; + +namespace Ryujinx.UI.Common.Models +{ + public record XCITrimmerFileModel( + string Name, + string Path, + bool Trimmable, + bool Untrimmable, + long PotentialSavingsB, + long CurrentSavingsB, + int? PercentageProgress, + XCIFileTrimmer.OperationOutcome ProcessingOutcome) + { + public static XCITrimmerFileModel FromApplicationData(ApplicationData applicationData, XCIFileTrimmerLog logger) + { + var trimmer = new XCIFileTrimmer(applicationData.Path, logger); + + return new XCITrimmerFileModel( + applicationData.Name, + applicationData.Path, + trimmer.CanBeTrimmed, + trimmer.CanBeUntrimmed, + trimmer.DiskSpaceSavingsB, + trimmer.DiskSpaceSavedB, + null, + XCIFileTrimmer.OperationOutcome.Undetermined + ); + } + + public bool IsFailed + { + get + { + return ProcessingOutcome != XCIFileTrimmer.OperationOutcome.Undetermined && + ProcessingOutcome != XCIFileTrimmer.OperationOutcome.Successful; + } + } + + public virtual bool Equals(XCITrimmerFileModel obj) + { + if (obj == null) + return false; + + return this.Path == obj.Path; + } + + public override int GetHashCode() + { + return this.Path.GetHashCode(); + } + } +} diff --git a/src/Ryujinx.UI.Common/Resources/Logo_Patreon_Dark.png b/src/Ryujinx.UI.Common/Resources/Logo_Patreon_Dark.png deleted file mode 100644 index 9a521e3fd..000000000 Binary files a/src/Ryujinx.UI.Common/Resources/Logo_Patreon_Dark.png and /dev/null differ diff --git a/src/Ryujinx.UI.Common/Resources/Logo_Patreon_Light.png b/src/Ryujinx.UI.Common/Resources/Logo_Patreon_Light.png deleted file mode 100644 index 44da0ac45..000000000 Binary files a/src/Ryujinx.UI.Common/Resources/Logo_Patreon_Light.png and /dev/null differ diff --git a/src/Ryujinx.UI.Common/Resources/Logo_Ryujinx.png b/src/Ryujinx.UI.Common/Resources/Logo_Ryujinx.png index 0e8da15e6..e61358a95 100644 Binary files a/src/Ryujinx.UI.Common/Resources/Logo_Ryujinx.png and b/src/Ryujinx.UI.Common/Resources/Logo_Ryujinx.png differ diff --git a/src/Ryujinx.UI.Common/Resources/Logo_Thiccjinx.png b/src/Ryujinx.UI.Common/Resources/Logo_Thiccjinx.png new file mode 100644 index 000000000..be807a40a Binary files /dev/null and b/src/Ryujinx.UI.Common/Resources/Logo_Thiccjinx.png differ diff --git a/src/Ryujinx.UI.Common/Resources/Logo_Twitter_Dark.png b/src/Ryujinx.UI.Common/Resources/Logo_Twitter_Dark.png deleted file mode 100644 index 66962e7d3..000000000 Binary files a/src/Ryujinx.UI.Common/Resources/Logo_Twitter_Dark.png and /dev/null differ diff --git a/src/Ryujinx.UI.Common/Resources/Logo_Twitter_Light.png b/src/Ryujinx.UI.Common/Resources/Logo_Twitter_Light.png deleted file mode 100644 index 040ca1699..000000000 Binary files a/src/Ryujinx.UI.Common/Resources/Logo_Twitter_Light.png and /dev/null differ diff --git a/src/Ryujinx.UI.Common/Ryujinx.UI.Common.csproj b/src/Ryujinx.UI.Common/Ryujinx.UI.Common.csproj index 387e998b0..c43f95e4a 100644 --- a/src/Ryujinx.UI.Common/Ryujinx.UI.Common.csproj +++ b/src/Ryujinx.UI.Common/Ryujinx.UI.Common.csproj @@ -1,8 +1,8 @@ - net8.0 true + $(DefaultItemExcludes);._* @@ -18,9 +18,7 @@ - - @@ -35,14 +33,11 @@ + - - - - @@ -56,6 +51,7 @@ + diff --git a/src/Ryujinx.UI.Common/SystemInfo/MacOSSystemInfo.cs b/src/Ryujinx.UI.Common/SystemInfo/MacOSSystemInfo.cs index 36deaf35f..894c3cadc 100644 --- a/src/Ryujinx.UI.Common/SystemInfo/MacOSSystemInfo.cs +++ b/src/Ryujinx.UI.Common/SystemInfo/MacOSSystemInfo.cs @@ -68,11 +68,11 @@ namespace Ryujinx.UI.Common.SystemInfo private const string SystemLibraryName = "libSystem.dylib"; [LibraryImport(SystemLibraryName, SetLastError = true)] - private static partial int sysctlbyname([MarshalAs(UnmanagedType.LPStr)] string name, IntPtr oldValue, ref ulong oldSize, IntPtr newValue, ulong newValueSize); + private static partial int sysctlbyname([MarshalAs(UnmanagedType.LPStr)] string name, nint oldValue, ref ulong oldSize, nint newValue, ulong newValueSize); - private static int SysctlByName(string name, IntPtr oldValue, ref ulong oldSize) + private static int SysctlByName(string name, nint oldValue, ref ulong oldSize) { - if (sysctlbyname(name, oldValue, ref oldSize, IntPtr.Zero, 0) == -1) + if (sysctlbyname(name, oldValue, ref oldSize, nint.Zero, 0) == -1) { int err = Marshal.GetLastWin32Error(); @@ -90,7 +90,7 @@ namespace Ryujinx.UI.Common.SystemInfo { ulong oldValueSize = (ulong)Unsafe.SizeOf(); - return SysctlByName(name, (IntPtr)Unsafe.AsPointer(ref oldValue), ref oldValueSize); + return SysctlByName(name, (nint)Unsafe.AsPointer(ref oldValue), ref oldValueSize); } } @@ -100,7 +100,7 @@ namespace Ryujinx.UI.Common.SystemInfo ulong strSize = 0; - int res = SysctlByName(name, IntPtr.Zero, ref strSize); + int res = SysctlByName(name, nint.Zero, ref strSize); if (res == 0) { @@ -110,7 +110,7 @@ namespace Ryujinx.UI.Common.SystemInfo { fixed (byte* rawDataPtr = rawData) { - res = SysctlByName(name, (IntPtr)rawDataPtr, ref strSize); + res = SysctlByName(name, (nint)rawDataPtr, ref strSize); } if (res == 0) diff --git a/src/Ryujinx.UI.Common/SystemInfo/SystemInfo.cs b/src/Ryujinx.UI.Common/SystemInfo/SystemInfo.cs index 38728b9cf..2dfa9160d 100644 --- a/src/Ryujinx.UI.Common/SystemInfo/SystemInfo.cs +++ b/src/Ryujinx.UI.Common/SystemInfo/SystemInfo.cs @@ -33,17 +33,13 @@ namespace Ryujinx.UI.Common.SystemInfo public static SystemInfo Gather() { if (OperatingSystem.IsWindows()) - { return new WindowsSystemInfo(); - } - else if (OperatingSystem.IsLinux()) - { + + if (OperatingSystem.IsLinux()) return new LinuxSystemInfo(); - } - else if (OperatingSystem.IsMacOS()) - { + + if (OperatingSystem.IsMacOS()) return new MacOSSystemInfo(); - } Logger.Error?.Print(LogClass.Application, "SystemInfo unsupported on this platform"); diff --git a/src/Ryujinx.UI.Common/SystemInfo/WindowsSystemInfo.cs b/src/Ryujinx.UI.Common/SystemInfo/WindowsSystemInfo.cs index bf49c2a66..4a2c8795d 100644 --- a/src/Ryujinx.UI.Common/SystemInfo/WindowsSystemInfo.cs +++ b/src/Ryujinx.UI.Common/SystemInfo/WindowsSystemInfo.cs @@ -40,7 +40,7 @@ namespace Ryujinx.UI.Common.SystemInfo } } - return Environment.GetEnvironmentVariable("PROCESSOR_IDENTIFIER").Trim(); + return Environment.GetEnvironmentVariable("PROCESSOR_IDENTIFIER")?.Trim(); } [StructLayout(LayoutKind.Sequential)] diff --git a/src/Ryujinx.UI.LocaleGenerator/LocaleGenerator.cs b/src/Ryujinx.UI.LocaleGenerator/LocaleGenerator.cs index bb4918d55..c69eca7ee 100644 --- a/src/Ryujinx.UI.LocaleGenerator/LocaleGenerator.cs +++ b/src/Ryujinx.UI.LocaleGenerator/LocaleGenerator.cs @@ -9,13 +9,14 @@ namespace Ryujinx.UI.LocaleGenerator { public void Initialize(IncrementalGeneratorInitializationContext context) { - var englishLocaleFile = context.AdditionalTextsProvider.Where(static x => x.Path.EndsWith("en_US.json")); + var localeFile = context.AdditionalTextsProvider.Where(static x => x.Path.EndsWith("locales.json")); - IncrementalValuesProvider contents = englishLocaleFile.Select((text, cancellationToken) => text.GetText(cancellationToken)!.ToString()); + IncrementalValuesProvider contents = localeFile.Select((text, cancellationToken) => text.GetText(cancellationToken)!.ToString()); context.RegisterSourceOutput(contents, (spc, content) => { - var lines = content.Split('\n').Where(x => x.Trim().StartsWith("\"")).Select(x => x.Split(':')[0].Trim().Replace("\"", "")); + var lines = content.Split('\n').Where(x => x.Trim().StartsWith("\"ID\":")).Select(x => x.Split(':')[1].Trim().Replace("\"", string.Empty).Replace(",", string.Empty)); + StringBuilder enumSourceBuilder = new(); enumSourceBuilder.AppendLine("namespace Ryujinx.Ava.Common.Locale;"); enumSourceBuilder.AppendLine("internal enum LocaleKeys"); diff --git a/src/Ryujinx.UI.LocaleGenerator/Ryujinx.UI.LocaleGenerator.csproj b/src/Ryujinx.UI.LocaleGenerator/Ryujinx.UI.LocaleGenerator.csproj index 05cbc7644..49cb39713 100644 --- a/src/Ryujinx.UI.LocaleGenerator/Ryujinx.UI.LocaleGenerator.csproj +++ b/src/Ryujinx.UI.LocaleGenerator/Ryujinx.UI.LocaleGenerator.csproj @@ -3,8 +3,8 @@ netstandard2.0 enable - latest true + $(DefaultItemExcludes);._* diff --git a/src/Ryujinx/App.axaml b/src/Ryujinx/App.axaml index eab318b7b..5c96f97f2 100644 --- a/src/Ryujinx/App.axaml +++ b/src/Ryujinx/App.axaml @@ -11,7 +11,7 @@ - - + + - \ No newline at end of file + diff --git a/src/Ryujinx/App.axaml.cs b/src/Ryujinx/App.axaml.cs index 24d8a70a1..15ada201c 100644 --- a/src/Ryujinx/App.axaml.cs +++ b/src/Ryujinx/App.axaml.cs @@ -4,6 +4,8 @@ using Avalonia.Markup.Xaml; using Avalonia.Platform; using Avalonia.Styling; using Avalonia.Threading; +using FluentAvalonia.UI.Windowing; +using Gommon; using Ryujinx.Ava.Common; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Helpers; @@ -19,9 +21,25 @@ namespace Ryujinx.Ava { public class App : Application { + internal static string FormatTitle(LocaleKeys? windowTitleKey = null) + => windowTitleKey is null + ? $"{FullAppName} {Program.Version}" + : $"{FullAppName} {Program.Version} - {LocaleManager.Instance[windowTitleKey.Value]}"; + + public static readonly string FullAppName = ReleaseInformation.IsCanaryBuild ? "Ryujinx Canary" : "Ryujinx"; + + public static MainWindow MainWindow => Current! + .ApplicationLifetime.Cast() + .MainWindow.Cast(); + + public static void SetTaskbarProgress(TaskBarProgressBarState state) => MainWindow.PlatformFeatures.SetTaskBarProgressBarState(state); + public static void SetTaskbarProgressValue(ulong current, ulong total) => MainWindow.PlatformFeatures.SetTaskBarProgressBarValue(current, total); + public static void SetTaskbarProgressValue(long current, long total) => SetTaskbarProgressValue(Convert.ToUInt64(current), Convert.ToUInt64(total)); + + public override void Initialize() { - Name = $"Ryujinx {Program.Version}"; + Name = FormatTitle(); AvaloniaXamlLoader.Load(this); @@ -42,23 +60,15 @@ namespace Ryujinx.Ava if (Program.PreviewerDetached) { - ApplyConfiguredTheme(); + ApplyConfiguredTheme(ConfigurationState.Instance.UI.BaseStyle); ConfigurationState.Instance.UI.BaseStyle.Event += ThemeChanged_Event; - ConfigurationState.Instance.UI.CustomThemePath.Event += ThemeChanged_Event; - ConfigurationState.Instance.UI.EnableCustomTheme.Event += CustomThemeChanged_Event; } } - private void CustomThemeChanged_Event(object sender, ReactiveEventArgs e) - { - ApplyConfiguredTheme(); - } - private void ShowRestartDialog() { -#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - Dispatcher.UIThread.InvokeAsync(async () => + _ = Dispatcher.UIThread.InvokeAsync(async () => { if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { @@ -71,27 +81,20 @@ namespace Ryujinx.Ava if (result == UserResult.Yes) { - var path = Environment.ProcessPath; - var proc = Process.Start(path, CommandLineState.Arguments); + _ = Process.Start(Environment.ProcessPath!, CommandLineState.Arguments); desktop.Shutdown(); Environment.Exit(0); } } }); -#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed } - private void ThemeChanged_Event(object sender, ReactiveEventArgs e) - { - ApplyConfiguredTheme(); - } + private void ThemeChanged_Event(object _, ReactiveEventArgs rArgs) => ApplyConfiguredTheme(rArgs.NewValue); - public void ApplyConfiguredTheme() + public void ApplyConfiguredTheme(string baseStyle) { try { - string baseStyle = ConfigurationState.Instance.UI.BaseStyle; - if (string.IsNullOrWhiteSpace(baseStyle)) { ConfigurationState.Instance.UI.BaseStyle.Value = "Auto"; @@ -99,13 +102,11 @@ namespace Ryujinx.Ava baseStyle = ConfigurationState.Instance.UI.BaseStyle; } - ThemeVariant systemTheme = DetectSystemTheme(); - ThemeManager.OnThemeChanged(); RequestedThemeVariant = baseStyle switch { - "Auto" => systemTheme, + "Auto" => DetectSystemTheme(), "Light" => ThemeVariant.Light, "Dark" => ThemeVariant.Dark, _ => ThemeVariant.Default, @@ -113,7 +114,7 @@ namespace Ryujinx.Ava } catch (Exception) { - Logger.Warning?.Print(LogClass.Application, "Failed to Apply Theme. A restart is needed to apply the selected theme"); + Logger.Warning?.Print(LogClass.Application, "Failed to apply theme. A restart is needed to apply the selected theme."); ShowRestartDialog(); } @@ -130,16 +131,9 @@ namespace Ryujinx.Ava _ => ThemeVariant.Default, }; - public static ThemeVariant DetectSystemTheme() - { - if (Application.Current is App app) - { - var colorValues = app.PlatformSettings.GetColorValues(); - - return ConvertThemeVariant(colorValues.ThemeVariant); - } - - return ThemeVariant.Default; - } + public static ThemeVariant DetectSystemTheme() => + Current is App { PlatformSettings: not null } app + ? ConvertThemeVariant(app.PlatformSettings.GetColorValues().ThemeVariant) + : ThemeVariant.Default; } } diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs index f88bac0cd..c07cf1454 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -3,6 +3,8 @@ using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Input; using Avalonia.Threading; +using LibHac.Common; +using LibHac.Ns; using LibHac.Tools.FsSystem; using Ryujinx.Audio.Backends.Dummy; using Ryujinx.Audio.Backends.OpenAL; @@ -59,6 +61,7 @@ using MouseButton = Ryujinx.Input.MouseButton; using ScalingFilter = Ryujinx.Common.Configuration.ScalingFilter; using Size = Avalonia.Size; using Switch = Ryujinx.HLE.Switch; +using VSyncMode = Ryujinx.Common.Configuration.VSyncMode; namespace Ryujinx.Ava { @@ -70,8 +73,8 @@ namespace Ryujinx.Ava private const float VolumeDelta = 0.05f; private static readonly Cursor _invisibleCursor = new(StandardCursorType.None); - private readonly IntPtr _invisibleCursorWin; - private readonly IntPtr _defaultCursorWin; + private readonly nint _invisibleCursorWin; + private readonly nint _defaultCursorWin; private readonly long _ticksPerFrame; private readonly Stopwatch _chrono; @@ -104,6 +107,10 @@ namespace Ryujinx.Ava private CursorStates _cursorState = !ConfigurationState.Instance.Hid.EnableMouse.Value ? CursorStates.CursorIsVisible : CursorStates.CursorIsHidden; + private DateTime _lastShaderReset; + private uint _displayCount; + private uint _previousCount = 0; + private bool _isStopped; private bool _isActive; private bool _renderingStarted; @@ -118,17 +125,16 @@ namespace Ryujinx.Ava private bool _dialogShown; private readonly bool _isFirmwareTitle; - private readonly object _lockObject = new(); + private readonly Lock _lockObject = new(); public event EventHandler AppExit; - public event EventHandler StatusInitEvent; public event EventHandler StatusUpdatedEvent; public VirtualFileSystem VirtualFileSystem { get; } public ContentManager ContentManager { get; } public NpadManager NpadManager { get; } public TouchScreenManager TouchScreenManager { get; } - public Switch Device { get; set; } + public HLE.Switch Device { get; set; } public int Width { get; private set; } public int Height { get; private set; } @@ -202,10 +208,16 @@ namespace Ryujinx.Ava ConfigurationState.Instance.Graphics.ScalingFilter.Event += UpdateScalingFilter; ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel; ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Event += UpdateColorSpacePassthrough; + ConfigurationState.Instance.Graphics.VSyncMode.Event += UpdateVSyncMode; + ConfigurationState.Instance.Graphics.CustomVSyncInterval.Event += UpdateCustomVSyncIntervalValue; + ConfigurationState.Instance.Graphics.EnableCustomVSyncInterval.Event += UpdateCustomVSyncIntervalEnabled; ConfigurationState.Instance.System.EnableInternetAccess.Event += UpdateEnableInternetAccessState; ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState; ConfigurationState.Instance.Multiplayer.Mode.Event += UpdateMultiplayerModeState; + ConfigurationState.Instance.Multiplayer.LdnPassphrase.Event += UpdateLdnPassphraseState; + ConfigurationState.Instance.Multiplayer.LdnServer.Event += UpdateLdnServerState; + ConfigurationState.Instance.Multiplayer.DisableP2p.Event += UpdateDisableP2pState; _gpuCancellationTokenSource = new CancellationTokenSource(); _gpuDoneEvent = new ManualResetEvent(false); @@ -291,6 +303,66 @@ namespace Ryujinx.Ava _renderer.Window?.SetColorSpacePassthrough((bool)ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Value); } + public void UpdateVSyncMode(object sender, ReactiveEventArgs e) + { + if (Device != null) + { + Device.VSyncMode = e.NewValue; + Device.UpdateVSyncInterval(); + } + _renderer.Window?.ChangeVSyncMode((Ryujinx.Graphics.GAL.VSyncMode)e.NewValue); + + _viewModel.ShowCustomVSyncIntervalPicker = (e.NewValue == VSyncMode.Custom); + } + + public void VSyncModeToggle() + { + VSyncMode oldVSyncMode = Device.VSyncMode; + VSyncMode newVSyncMode = VSyncMode.Switch; + bool customVSyncIntervalEnabled = ConfigurationState.Instance.Graphics.EnableCustomVSyncInterval.Value; + + switch (oldVSyncMode) + { + case VSyncMode.Switch: + newVSyncMode = VSyncMode.Unbounded; + break; + case VSyncMode.Unbounded: + if (customVSyncIntervalEnabled) + { + newVSyncMode = VSyncMode.Custom; + } + else + { + newVSyncMode = VSyncMode.Switch; + } + + break; + case VSyncMode.Custom: + newVSyncMode = VSyncMode.Switch; + break; + } + + UpdateVSyncMode(this, new ReactiveEventArgs(oldVSyncMode, newVSyncMode)); + } + + private void UpdateCustomVSyncIntervalValue(object sender, ReactiveEventArgs e) + { + if (Device != null) + { + Device.TargetVSyncInterval = e.NewValue; + Device.UpdateVSyncInterval(); + } + } + + private void UpdateCustomVSyncIntervalEnabled(object sender, ReactiveEventArgs e) + { + if (Device != null) + { + Device.CustomVSyncIntervalEnabled = e.NewValue; + Device.UpdateVSyncInterval(); + } + } + private void ShowCursor() { Dispatcher.UIThread.Post(() => @@ -348,11 +420,7 @@ namespace Ryujinx.Ava string filename = $"{sanitizedApplicationName}_{currentTime.Year}-{currentTime.Month:D2}-{currentTime.Day:D2}_{currentTime.Hour:D2}-{currentTime.Minute:D2}-{currentTime.Second:D2}.png"; - string directory = AppDataManager.Mode switch - { - AppDataManager.LaunchMode.Portable or AppDataManager.LaunchMode.Custom => Path.Combine(AppDataManager.BaseDirPath, "screenshots"), - _ => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "Ryujinx"), - }; + string directory = Path.Combine(AppDataManager.BaseDirPath, "screenshots"); string path = Path.Combine(directory, filename); @@ -368,12 +436,12 @@ namespace Ryujinx.Ava } var colorType = e.IsBgra ? SKColorType.Bgra8888 : SKColorType.Rgba8888; - using SKBitmap bitmap = new SKBitmap(new SKImageInfo(e.Width, e.Height, colorType, SKAlphaType.Premul)); + using SKBitmap bitmap = new(new SKImageInfo(e.Width, e.Height, colorType, SKAlphaType.Premul)); Marshal.Copy(e.Data, 0, bitmap.GetPixels(), e.Data.Length); - using SKBitmap bitmapToSave = new SKBitmap(bitmap.Width, bitmap.Height); - using SKCanvas canvas = new SKCanvas(bitmapToSave); + using SKBitmap bitmapToSave = new(bitmap.Width, bitmap.Height); + using SKCanvas canvas = new(bitmapToSave); canvas.Clear(SKColors.Black); @@ -397,7 +465,7 @@ namespace Ryujinx.Ava } } - private void SaveBitmapAsPng(SKBitmap bitmap, string path) + private static void SaveBitmapAsPng(SKBitmap bitmap, string path) { using var data = bitmap.Encode(SKEncodedImageFormat.Png, 100); using var stream = File.OpenWrite(path); @@ -490,15 +558,25 @@ namespace Ryujinx.Ava Device.Configuration.MultiplayerMode = e.NewValue; } - public void ToggleVSync() + private void UpdateLdnPassphraseState(object sender, ReactiveEventArgs e) { - Device.EnableDeviceVsync = !Device.EnableDeviceVsync; - _renderer.Window.ChangeVSyncMode(Device.EnableDeviceVsync); + Device.Configuration.MultiplayerLdnPassphrase = e.NewValue; + } + + private void UpdateLdnServerState(object sender, ReactiveEventArgs e) + { + Device.Configuration.MultiplayerLdnServer = e.NewValue; + } + + private void UpdateDisableP2pState(object sender, ReactiveEventArgs e) + { + Device.Configuration.MultiplayerDisableP2p = e.NewValue; } public void Stop() { _isActive = false; + DiscordIntegrationModule.SwitchToMainState(); } private void Exit() @@ -511,7 +589,7 @@ namespace Ryujinx.Ava } _isStopped = true; - _isActive = false; + Stop(); } public void DisposeContext() @@ -539,9 +617,8 @@ namespace Ryujinx.Ava private void Dispose() { if (Device.Processes != null) - { MainWindowViewModel.UpdateGameMetadata(Device.Processes.ActiveApplication.ProgramIdText); - } + ConfigurationState.Instance.System.IgnoreMissingServices.Event -= UpdateIgnoreMissingServicesState; ConfigurationState.Instance.Graphics.AspectRatio.Event -= UpdateAspectRatioState; @@ -596,68 +673,66 @@ namespace Ryujinx.Ava _cursorState = CursorStates.ForceChangeCursor; } - public async Task LoadGuestApplication() + public async Task LoadGuestApplication(BlitStruct? customNacpData = null) { InitializeSwitchInstance(); MainWindow.UpdateGraphicsConfig(); SystemVersion firmwareVersion = ContentManager.GetCurrentFirmwareVersion(); - if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime) { if (!SetupValidator.CanStartApplication(ContentManager, ApplicationPath, out UserError userError)) { + if (SetupValidator.CanFixStartApplication(ContentManager, ApplicationPath, userError, out firmwareVersion)) { - if (SetupValidator.CanFixStartApplication(ContentManager, ApplicationPath, userError, out firmwareVersion)) + if (userError is UserError.NoFirmware) { - if (userError == UserError.NoFirmware) - { - UserResult result = await ContentDialogHelper.CreateConfirmationDialog( - LocaleManager.Instance[LocaleKeys.DialogFirmwareNoFirmwareInstalledMessage], - LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallEmbeddedMessage, firmwareVersion.VersionString), - LocaleManager.Instance[LocaleKeys.InputDialogYes], - LocaleManager.Instance[LocaleKeys.InputDialogNo], - ""); + UserResult result = await ContentDialogHelper.CreateConfirmationDialog( + LocaleManager.Instance[LocaleKeys.DialogFirmwareNoFirmwareInstalledMessage], + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallEmbeddedMessage, firmwareVersion.VersionString), + LocaleManager.Instance[LocaleKeys.InputDialogYes], + LocaleManager.Instance[LocaleKeys.InputDialogNo], + string.Empty); - if (result != UserResult.Yes) - { - await UserErrorDialog.ShowUserErrorDialog(userError); - Device.Dispose(); - - return false; - } - } - - if (!SetupValidator.TryFixStartApplication(ContentManager, ApplicationPath, userError, out _)) + if (result != UserResult.Yes) { await UserErrorDialog.ShowUserErrorDialog(userError); Device.Dispose(); return false; } - - // Tell the user that we installed a firmware for them. - if (userError == UserError.NoFirmware) - { - firmwareVersion = ContentManager.GetCurrentFirmwareVersion(); - - _viewModel.RefreshFirmwareStatus(); - - await ContentDialogHelper.CreateInfoDialog( - LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstalledMessage, firmwareVersion.VersionString), - LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallEmbeddedSuccessMessage, firmwareVersion.VersionString), - LocaleManager.Instance[LocaleKeys.InputDialogOk], - "", - LocaleManager.Instance[LocaleKeys.RyujinxInfo]); - } } - else + + if (!SetupValidator.TryFixStartApplication(ContentManager, ApplicationPath, userError, out _)) { await UserErrorDialog.ShowUserErrorDialog(userError); Device.Dispose(); return false; } + + // Tell the user that we installed a firmware for them. + if (userError is UserError.NoFirmware) + { + firmwareVersion = ContentManager.GetCurrentFirmwareVersion(); + + _viewModel.RefreshFirmwareStatus(); + + await ContentDialogHelper.CreateInfoDialog( + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstalledMessage, firmwareVersion.VersionString), + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallEmbeddedSuccessMessage, firmwareVersion.VersionString), + LocaleManager.Instance[LocaleKeys.InputDialogOk], + string.Empty, + LocaleManager.Instance[LocaleKeys.RyujinxInfo]); + } + } + else + { + await UserErrorDialog.ShowUserErrorDialog(userError); + Device.Dispose(); + + return false; } } } @@ -668,7 +743,7 @@ namespace Ryujinx.Ava { Logger.Info?.Print(LogClass.Application, "Loading as Firmware Title (NCA)."); - if (!Device.LoadNca(ApplicationPath)) + if (!Device.LoadNca(ApplicationPath, customNacpData)) { Device.Dispose(); @@ -786,12 +861,11 @@ namespace Ryujinx.Ava return false; } - DiscordIntegrationModule.SwitchToPlayingState(Device.Processes.ActiveApplication.ProgramIdText, Device.Processes.ActiveApplication.Name); + ApplicationMetadata appMeta = ApplicationLibrary.LoadAndSaveMetaData(Device.Processes.ActiveApplication.ProgramIdText, + appMetadata => appMetadata.UpdatePreGame() + ); - ApplicationLibrary.LoadAndSaveMetaData(Device.Processes.ActiveApplication.ProgramIdText, appMetadata => - { - appMetadata.UpdatePreGame(); - }); + DiscordIntegrationModule.SwitchToPlayingState(appMeta, Device.Processes.ActiveApplication); return true; } @@ -825,10 +899,9 @@ namespace Ryujinx.Ava if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Vulkan) { renderer = new VulkanRenderer( - Vk.GetApi(), - (RendererHost.EmbeddedWindow as EmbeddedWindowVulkan).CreateSurface, - VulkanHelper.GetRequiredInstanceExtensions, - ConfigurationState.Instance.Graphics.PreferredGpu.Value); + ConfigurationState.Instance.Graphics.PreferredGpu, + (RendererHost.EmbeddedWindow as EmbeddedWindowVulkan)!.CreateSurface, + VulkanHelper.GetRequiredInstanceExtensions); } else if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Metal && OperatingSystem.IsMacOS()) { @@ -850,36 +923,39 @@ namespace Ryujinx.Ava Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend Threading ({threadingMode}): {isGALThreaded}"); // Initialize Configuration. - var memoryConfiguration = ConfigurationState.Instance.System.ExpandRam.Value ? MemoryConfiguration.MemoryConfiguration8GiB : MemoryConfiguration.MemoryConfiguration4GiB; + var memoryConfiguration = ConfigurationState.Instance.System.DramSize.Value; - HLEConfiguration configuration = new(VirtualFileSystem, - _viewModel.LibHacHorizonManager, - ContentManager, - _accountManager, - _userChannelPersistence, - renderer, - InitializeAudio(), - memoryConfiguration, - _viewModel.UiHandler, - (SystemLanguage)ConfigurationState.Instance.System.Language.Value, - (RegionCode)ConfigurationState.Instance.System.Region.Value, - ConfigurationState.Instance.Graphics.EnableVsync, - ConfigurationState.Instance.System.EnableDockedMode, - ConfigurationState.Instance.System.EnablePtc, - ConfigurationState.Instance.System.EnableInternetAccess, - ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None, - ConfigurationState.Instance.System.FsGlobalAccessLogMode, - ConfigurationState.Instance.System.SystemTimeOffset, - ConfigurationState.Instance.System.TimeZone, - ConfigurationState.Instance.System.MemoryManagerMode, - ConfigurationState.Instance.System.IgnoreMissingServices, - ConfigurationState.Instance.Graphics.AspectRatio, - ConfigurationState.Instance.System.AudioVolume, - ConfigurationState.Instance.System.UseHypervisor, - ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value, - ConfigurationState.Instance.Multiplayer.Mode); - - Device = new Switch(configuration); + Device = new HLE.Switch(new HLEConfiguration( + VirtualFileSystem, + _viewModel.LibHacHorizonManager, + ContentManager, + _accountManager, + _userChannelPersistence, + renderer, + InitializeAudio(), + memoryConfiguration, + _viewModel.UiHandler, + (SystemLanguage)ConfigurationState.Instance.System.Language.Value, + (RegionCode)ConfigurationState.Instance.System.Region.Value, + ConfigurationState.Instance.Graphics.VSyncMode, + ConfigurationState.Instance.System.EnableDockedMode, + ConfigurationState.Instance.System.EnablePtc, + ConfigurationState.Instance.System.EnableInternetAccess, + ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None, + ConfigurationState.Instance.System.FsGlobalAccessLogMode, + ConfigurationState.Instance.System.SystemTimeOffset, + ConfigurationState.Instance.System.TimeZone, + ConfigurationState.Instance.System.MemoryManagerMode, + ConfigurationState.Instance.System.IgnoreMissingServices, + ConfigurationState.Instance.Graphics.AspectRatio, + ConfigurationState.Instance.System.AudioVolume, + ConfigurationState.Instance.System.UseHypervisor, + ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value, + ConfigurationState.Instance.Multiplayer.Mode, + ConfigurationState.Instance.Multiplayer.DisableP2p, + ConfigurationState.Instance.Multiplayer.LdnPassphrase, + ConfigurationState.Instance.Multiplayer.LdnServer, + ConfigurationState.Instance.Graphics.CustomVSyncInterval.Value)); } private static IHardwareDeviceDriver InitializeAudio() @@ -953,10 +1029,8 @@ namespace Ryujinx.Ava private void MainLoop() { - while (_isActive) + while (UpdateFrame()) { - UpdateFrame(); - // Polling becomes expensive if it's not slept. Thread.Sleep(1); } @@ -971,7 +1045,7 @@ namespace Ryujinx.Ava _viewModel.WindowState = WindowState.FullScreen; } - if (_viewModel.WindowState == WindowState.FullScreen) + if (_viewModel.WindowState is WindowState.FullScreen) { _viewModel.ShowMenuAndStatusBar = false; } @@ -1002,7 +1076,7 @@ namespace Ryujinx.Ava Device.Gpu.SetGpuThread(); Device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token); - _renderer.Window.ChangeVSyncMode(Device.EnableDeviceVsync); + _renderer.Window.ChangeVSyncMode((Ryujinx.Graphics.GAL.VSyncMode)Device.VSyncMode); while (_isActive) { @@ -1049,21 +1123,24 @@ namespace Ryujinx.Ava public void InitStatus() { - StatusInitEvent?.Invoke(this, new StatusInitEventArgs( - ConfigurationState.Instance.Graphics.GraphicsBackend.Value switch - { - GraphicsBackend.Vulkan => "Vulkan", - GraphicsBackend.OpenGl => "OpenGL", - GraphicsBackend.Metal => "Metal", - _ => throw new NotImplementedException() - }, - $"GPU: {_renderer.GetHardwareInfo().GpuDriver}")); + _viewModel.BackendText = ConfigurationState.Instance.Graphics.GraphicsBackend.Value switch + { + GraphicsBackend.Vulkan => "Vulkan", + GraphicsBackend.OpenGl => "OpenGL", + GraphicsBackend.Metal => "Metal", + _ => throw new NotImplementedException() + }; + + _viewModel.GpuNameText = $"GPU: {_renderer.GetHardwareInfo().GpuDriver}"; } public void UpdateStatus() { // Run a status update only when a frame is to be drawn. This prevents from updating the ui and wasting a render when no frame is queued. string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? LocaleManager.Instance[LocaleKeys.Docked] : LocaleManager.Instance[LocaleKeys.Handheld]; + string vSyncMode = Device.VSyncMode.ToString(); + + UpdateShaderCount(); if (GraphicsConfig.ResScale != 1) { @@ -1071,12 +1148,13 @@ namespace Ryujinx.Ava } StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs( - Device.EnableDeviceVsync, + vSyncMode, LocaleManager.Instance[LocaleKeys.VolumeShort] + $": {(int)(Device.GetVolume() * 100)}%", dockedMode, ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(), - LocaleManager.Instance[LocaleKeys.Game] + $": {Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)", - $"FIFO: {Device.Statistics.GetFifoPercent():00.00} %")); + $"{Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)", + $"FIFO: {Device.Statistics.GetFifoPercent():00.00} %", + _displayCount)); } public async Task ShowExitPrompt() @@ -1102,6 +1180,24 @@ namespace Ryujinx.Ava } } + private void UpdateShaderCount() + { + // If there is a mismatch between total program compile and previous count + // this means new shaders have been compiled and should be displayed. + if (_renderer.ProgramCount != _previousCount) + { + _displayCount += _renderer.ProgramCount - _previousCount; + _lastShaderReset = DateTime.Now; + _previousCount = _renderer.ProgramCount; + } + // Check if 5s has passed since any new shaders were compiled. + // If yes, reset the counter. + else if (_lastShaderReset.AddSeconds(5) <= DateTime.Now) + { + _displayCount = 0; + } + } + private bool UpdateFrame() { if (!_isActive) @@ -1143,7 +1239,7 @@ namespace Ryujinx.Ava Dispatcher.UIThread.Post(() => { - if (_keyboardInterface.GetKeyboardStateSnapshot().IsPressed(Key.Delete) && _viewModel.WindowState != WindowState.FullScreen) + if (_keyboardInterface.GetKeyboardStateSnapshot().IsPressed(Key.Delete) && _viewModel.WindowState is not WindowState.FullScreen) { Device.Processes.ActiveApplication.DiskCacheLoadState?.Cancel(); } @@ -1155,8 +1251,16 @@ namespace Ryujinx.Ava { switch (currentHotkeyState) { - case KeyboardHotkeyState.ToggleVSync: - ToggleVSync(); + case KeyboardHotkeyState.ToggleVSyncMode: + VSyncModeToggle(); + break; + case KeyboardHotkeyState.CustomVSyncIntervalDecrement: + Device.DecrementCustomVSyncInterval(); + _viewModel.CustomVSyncInterval -= 1; + break; + case KeyboardHotkeyState.CustomVSyncIntervalIncrement: + Device.IncrementCustomVSyncInterval(); + _viewModel.CustomVSyncInterval += 1; break; case KeyboardHotkeyState.Screenshot: ScreenshotRequested = true; @@ -1243,9 +1347,9 @@ namespace Ryujinx.Ava { KeyboardHotkeyState state = KeyboardHotkeyState.None; - if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleVsync)) + if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleVSyncMode)) { - state = KeyboardHotkeyState.ToggleVSync; + state = KeyboardHotkeyState.ToggleVSyncMode; } else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot)) { @@ -1279,6 +1383,14 @@ namespace Ryujinx.Ava { state = KeyboardHotkeyState.VolumeDown; } + else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.CustomVSyncIntervalIncrement)) + { + state = KeyboardHotkeyState.CustomVSyncIntervalIncrement; + } + else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.CustomVSyncIntervalDecrement)) + { + state = KeyboardHotkeyState.CustomVSyncIntervalDecrement; + } return state; } diff --git a/src/Ryujinx/Assets/Locales/ar_SA.json b/src/Ryujinx/Assets/Locales/ar_SA.json deleted file mode 100644 index 73e55633b..000000000 --- a/src/Ryujinx/Assets/Locales/ar_SA.json +++ /dev/null @@ -1,780 +0,0 @@ -{ - "Language": "اَلْعَرَبِيَّةُ", - "MenuBarFileOpenApplet": "فتح التطبيق المصغر", - "MenuBarFileOpenAppletOpenMiiAppletToolTip": "‫افتح تطبيق تحرير Mii في الوضع المستقل", - "SettingsTabInputDirectMouseAccess": "الوصول المباشر للفأرة", - "SettingsTabSystemMemoryManagerMode": "وضع إدارة الذاكرة:", - "SettingsTabSystemMemoryManagerModeSoftware": "البرنامج", - "SettingsTabSystemMemoryManagerModeHost": "المُضيف (سريع)", - "SettingsTabSystemMemoryManagerModeHostUnchecked": "المضيف (غير مفحوص) (أسرع، غير آمن)", - "SettingsTabSystemUseHypervisor": "استخدم مراقب الأجهزة الافتراضية", - "MenuBarFile": "_ملف", - "MenuBarFileOpenFromFile": "_تحميل تطبيق من ملف", - "MenuBarFileOpenUnpacked": "تحميل لُعْبَة غير محزومة", - "MenuBarFileOpenEmuFolder": "‫فتح مجلد Ryujinx", - "MenuBarFileOpenLogsFolder": "فتح مجلد السجلات", - "MenuBarFileExit": "_خروج", - "MenuBarOptions": "_خيارات", - "MenuBarOptionsToggleFullscreen": "التبديل إلى وضع ملء الشاشة", - "MenuBarOptionsStartGamesInFullscreen": "ابدأ الألعاب في وضع ملء الشاشة", - "MenuBarOptionsStopEmulation": "إيقاف المحاكاة", - "MenuBarOptionsSettings": "_الإعدادات", - "MenuBarOptionsManageUserProfiles": "_إدارة الملفات الشخصية للمستخدم", - "MenuBarActions": "_الإجراءات", - "MenuBarOptionsSimulateWakeUpMessage": "محاكاة رسالة الاستيقاظ", - "MenuBarActionsScanAmiibo": "‫فحص Amiibo", - "MenuBarTools": "_الأدوات", - "MenuBarToolsInstallFirmware": "تثبيت البرنامج الثابت", - "MenuBarFileToolsInstallFirmwareFromFile": "تثبيت برنامج ثابت من XCI أو ZIP", - "MenuBarFileToolsInstallFirmwareFromDirectory": "تثبيت برنامج ثابت من مجلد", - "MenuBarToolsManageFileTypes": "إدارة أنواع الملفات", - "MenuBarToolsInstallFileTypes": "تثبيت أنواع الملفات", - "MenuBarToolsUninstallFileTypes": "إزالة أنواع الملفات", - "MenuBarView": "_عرض", - "MenuBarViewWindow": "حجم النافذة", - "MenuBarViewWindow720": "720p", - "MenuBarViewWindow1080": "1080p", - "MenuBarHelp": "_مساعدة", - "MenuBarHelpCheckForUpdates": "تحقق من التحديثات", - "MenuBarHelpAbout": "حول", - "MenuSearch": "بحث...", - "GameListHeaderFavorite": "مفضلة", - "GameListHeaderIcon": "الأيقونة", - "GameListHeaderApplication": "الاسم", - "GameListHeaderDeveloper": "المطور", - "GameListHeaderVersion": "الإصدار", - "GameListHeaderTimePlayed": "وقت اللعب", - "GameListHeaderLastPlayed": "آخر مرة لُعبت", - "GameListHeaderFileExtension": "صيغة الملف", - "GameListHeaderFileSize": "حجم الملف", - "GameListHeaderPath": "المسار", - "GameListContextMenuOpenUserSaveDirectory": "فتح مجلد حفظ المستخدم", - "GameListContextMenuOpenUserSaveDirectoryToolTip": "يفتح المجلد الذي يحتوي على حفظ المستخدم للتطبيق", - "GameListContextMenuOpenDeviceSaveDirectory": "فتح مجلد حفظ الجهاز", - "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "يفتح المجلد الذي يحتوي على حفظ الجهاز للتطبيق", - "GameListContextMenuOpenBcatSaveDirectory": "‫فتح مجلد حفظ الـBCAT", - "GameListContextMenuOpenBcatSaveDirectoryToolTip": "‫يفتح المجلد الذي يحتوي على حفظ الـBCAT للتطبيق", - "GameListContextMenuManageTitleUpdates": "إدارة تحديثات اللُعبة", - "GameListContextMenuManageTitleUpdatesToolTip": "يفتح نافذة إدارة تحديث اللُعبة", - "GameListContextMenuManageDlc": "إدارة المحتوي الإضافي", - "GameListContextMenuManageDlcToolTip": "يفتح نافذة إدارة المحتوي الإضافي", - "GameListContextMenuCacheManagement": "إدارة ذاكرة التخزين المؤقت", - "GameListContextMenuCacheManagementPurgePptc": "قائمة انتظار إعادة بناء الـ‫PPTC", - "GameListContextMenuCacheManagementPurgePptcToolTip": "تنشيط ‫PPTC لإعادة البناء في وقت الإقلاع عند بدء تشغيل اللعبة التالي", - "GameListContextMenuCacheManagementPurgeShaderCache": "تنظيف ذاكرة مرشحات الفيديو المؤقتة", - "GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "يحذف ذاكرة مرشحات الفيديو المؤقتة الخاصة بالتطبيق", - "GameListContextMenuCacheManagementOpenPptcDirectory": "‫فتح مجلد PPTC", - "GameListContextMenuCacheManagementOpenPptcDirectoryToolTip": "‫‫يفتح المجلد الذي يحتوي على ذاكرة التخزين المؤقت للترجمة المستمرة (PPTC) للتطبيق", - "GameListContextMenuCacheManagementOpenShaderCacheDirectory": "فتح مجلد الذاكرة المؤقتة لمرشحات الفيديو ", - "GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip": "يفتح المجلد الذي يحتوي على ذاكرة المظللات المؤقتة للتطبيق", - "GameListContextMenuExtractData": "استخراج البيانات", - "GameListContextMenuExtractDataExeFS": "ExeFS", - "GameListContextMenuExtractDataExeFSToolTip": "‫‫ استخراج قسم نظام الملفات القابل للتنفيذ (ExeFS) من الإعدادات الحالية للتطبيقات (يتضمن التحديثات)", - "GameListContextMenuExtractDataRomFS": "RomFS", - "GameListContextMenuExtractDataRomFSToolTip": "استخراج قسم RomFS من الإعدادات الحالية للتطبيقات (يتضمن التحديثات)", - "GameListContextMenuExtractDataLogo": "شعار", - "GameListContextMenuExtractDataLogoToolTip": "استخراج قسم الشعار من الإعدادات الحالية للتطبيقات (يتضمن التحديثات)", - "GameListContextMenuCreateShortcut": "إنشاء اختصار للتطبيق", - "GameListContextMenuCreateShortcutToolTip": "أنشئ اختصار سطح مكتب لتشغيل التطبيق المحدد", - "GameListContextMenuCreateShortcutToolTipMacOS": "أنشئ اختصار يُشغل التطبيق المحدد في مجلد تطبيقات ‫macOS", - "GameListContextMenuOpenModsDirectory": "‫فتح مجلد التعديلات (Mods)", - "GameListContextMenuOpenModsDirectoryToolTip": "يفتح المجلد الذي يحتوي على تعديلات‫(mods) التطبيق", - "GameListContextMenuOpenSdModsDirectory": "فتح مجلد تعديلات‫(mods) أتموسفير", - "GameListContextMenuOpenSdModsDirectoryToolTip": "يفتح مجلد أتموسفير لبطاقة SD البديلة الذي يحتوي على تعديلات التطبيق. مفيد للتعديلات التي تم تعبئتها للأجهزة الحقيقية.", - "StatusBarGamesLoaded": "{0}/{1} لعبة تم تحميلها", - "StatusBarSystemVersion": "إصدار النظام: {0}", - "LinuxVmMaxMapCountDialogTitle": "الحد الأدنى لتعيينات الذاكرة المكتشفة", - "LinuxVmMaxMapCountDialogTextPrimary": "هل ترغب في زيادة قيمة vm.max_map_count إلى {0}", - "LinuxVmMaxMapCountDialogTextSecondary": "قد تحاول بعض الألعاب إنشاء المزيد من تعيينات الذاكرة أكثر مما هو مسموح به حاليا. سيغلق ريوجينكس بمجرد تجاوز هذا الحد.", - "LinuxVmMaxMapCountDialogButtonUntilRestart": "نعم، حتى إعادة التشغيل التالية", - "LinuxVmMaxMapCountDialogButtonPersistent": "نعم، دائمًا", - "LinuxVmMaxMapCountWarningTextPrimary": "الحد الأقصى لمقدار تعيينات الذاكرة أقل من الموصى به.", - "LinuxVmMaxMapCountWarningTextSecondary": "القيمة الحالية لـ vm.max_map_count ({0}) أقل من {1}. قد تحاول بعض الألعاب إنشاء المزيد من تعيينات الذاكرة أكثر مما هو مسموح به حاليا. سيغلق ريوجينكس بمجرد تجاوز هذا الحد.\n\nقد ترغب إما في زيادة الحد يدويا أو تثبيت pkexec، مما يسمح لـ ريوجينكس بالمساعدة في ذلك.", - "Settings": "إعدادات", - "SettingsTabGeneral": "واجهة المستخدم", - "SettingsTabGeneralGeneral": "عام", - "SettingsTabGeneralEnableDiscordRichPresence": "تمكين وجود ديسكورد الغني", - "SettingsTabGeneralCheckUpdatesOnLaunch": "التحقق من وجود تحديثات عند التشغيل", - "SettingsTabGeneralShowConfirmExitDialog": "إظهار مربع حوار \"تأكيد الخروج\"", - "SettingsTabGeneralRememberWindowState": "تذكر حجم/موضع النافذة", - "SettingsTabGeneralHideCursor": "إخفاء المؤشر:", - "SettingsTabGeneralHideCursorNever": "مطلقا", - "SettingsTabGeneralHideCursorOnIdle": "عند الخمول", - "SettingsTabGeneralHideCursorAlways": "دائما", - "SettingsTabGeneralGameDirectories": "مجلدات الألعاب", - "SettingsTabGeneralAdd": "إضافة", - "SettingsTabGeneralRemove": "إزالة", - "SettingsTabSystem": "النظام", - "SettingsTabSystemCore": "النواة", - "SettingsTabSystemSystemRegion": "منطقة النظام:", - "SettingsTabSystemSystemRegionJapan": "اليابان", - "SettingsTabSystemSystemRegionUSA": "الولايات المتحدة الأمريكية", - "SettingsTabSystemSystemRegionEurope": "أوروبا", - "SettingsTabSystemSystemRegionAustralia": "أستراليا", - "SettingsTabSystemSystemRegionChina": "الصين", - "SettingsTabSystemSystemRegionKorea": "كوريا", - "SettingsTabSystemSystemRegionTaiwan": "تايوان", - "SettingsTabSystemSystemLanguage": "لغة النظام:", - "SettingsTabSystemSystemLanguageJapanese": "اليابانية", - "SettingsTabSystemSystemLanguageAmericanEnglish": "الإنجليزية الأمريكية", - "SettingsTabSystemSystemLanguageFrench": "الفرنسية", - "SettingsTabSystemSystemLanguageGerman": "الألمانية", - "SettingsTabSystemSystemLanguageItalian": "الإيطالية", - "SettingsTabSystemSystemLanguageSpanish": "الإسبانية", - "SettingsTabSystemSystemLanguageChinese": "الصينية", - "SettingsTabSystemSystemLanguageKorean": "الكورية", - "SettingsTabSystemSystemLanguageDutch": "الهولندية", - "SettingsTabSystemSystemLanguagePortuguese": "البرتغالية", - "SettingsTabSystemSystemLanguageRussian": "الروسية", - "SettingsTabSystemSystemLanguageTaiwanese": "التايوانية", - "SettingsTabSystemSystemLanguageBritishEnglish": "الإنجليزية البريطانية", - "SettingsTabSystemSystemLanguageCanadianFrench": "الفرنسية الكندية", - "SettingsTabSystemSystemLanguageLatinAmericanSpanish": "إسبانية أمريكا اللاتينية", - "SettingsTabSystemSystemLanguageSimplifiedChinese": "الصينية المبسطة", - "SettingsTabSystemSystemLanguageTraditionalChinese": "الصينية التقليدية", - "SettingsTabSystemSystemTimeZone": "النطاق الزمني للنظام:", - "SettingsTabSystemSystemTime": "توقيت النظام:", - "SettingsTabSystemEnableVsync": "VSync", - "SettingsTabSystemEnablePptc": "PPTC (ذاكرة التخزين المؤقت للترجمة المستمرة)", - "SettingsTabSystemEnableFsIntegrityChecks": "التحقق من سلامة نظام الملفات", - "SettingsTabSystemAudioBackend": "خلفية الصوت:", - "SettingsTabSystemAudioBackendDummy": "زائف", - "SettingsTabSystemAudioBackendOpenAL": "OpenAL", - "SettingsTabSystemAudioBackendSoundIO": "SoundIO", - "SettingsTabSystemAudioBackendSDL2": "SDL2", - "SettingsTabSystemHacks": "هاكات", - "SettingsTabSystemHacksNote": "قد يتسبب في عدم الاستقرار", - "SettingsTabSystemExpandDramSize": "استخدام تخطيط الذاكرة البديل (المطورين)", - "SettingsTabSystemIgnoreMissingServices": "تجاهل الخدمات المفقودة", - "SettingsTabGraphics": "الرسومات", - "SettingsTabGraphicsAPI": "API الرسومات ", - "SettingsTabGraphicsEnableShaderCache": "تفعيل ذاكرة المظللات المؤقتة", - "SettingsTabGraphicsAnisotropicFiltering": "تصفية:", - "SettingsTabGraphicsAnisotropicFilteringAuto": "تلقائي", - "SettingsTabGraphicsAnisotropicFiltering2x": "2x", - "SettingsTabGraphicsAnisotropicFiltering4x": "4×", - "SettingsTabGraphicsAnisotropicFiltering8x": "8x", - "SettingsTabGraphicsAnisotropicFiltering16x": "16x", - "SettingsTabGraphicsResolutionScale": "مقياس الدقة", - "SettingsTabGraphicsResolutionScaleCustom": "مخصص (لا ينصح به)", - "SettingsTabGraphicsResolutionScaleNative": "الأصل ‫(720p/1080p)", - "SettingsTabGraphicsResolutionScale2x": "2x (1440p/2160p)", - "SettingsTabGraphicsResolutionScale3x": "3x (2160p/3240p)", - "SettingsTabGraphicsResolutionScale4x": "4x (2880p/4320p) (لا ينصح به)", - "SettingsTabGraphicsAspectRatio": "نسبة الارتفاع إلى العرض:", - "SettingsTabGraphicsAspectRatio4x3": "4:3", - "SettingsTabGraphicsAspectRatio16x9": "16:9", - "SettingsTabGraphicsAspectRatio16x10": "16:10", - "SettingsTabGraphicsAspectRatio21x9": "21:9", - "SettingsTabGraphicsAspectRatio32x9": "32:9", - "SettingsTabGraphicsAspectRatioStretch": "تمديد لتناسب النافذة", - "SettingsTabGraphicsDeveloperOptions": "خيارات المطور", - "SettingsTabGraphicsShaderDumpPath": "مسار تفريغ المظللات:", - "SettingsTabLogging": "تسجيل", - "SettingsTabLoggingLogging": "تسجيل", - "SettingsTabLoggingEnableLoggingToFile": "تفعيل التسجيل إلى ملف", - "SettingsTabLoggingEnableStubLogs": "تفعيل سجلات الـStub", - "SettingsTabLoggingEnableInfoLogs": "تفعيل سجلات المعلومات", - "SettingsTabLoggingEnableWarningLogs": "تفعيل سجلات التحذير", - "SettingsTabLoggingEnableErrorLogs": "تفعيل سجلات الأخطاء", - "SettingsTabLoggingEnableTraceLogs": "تفعيل سجلات التتبع", - "SettingsTabLoggingEnableGuestLogs": "تفعيل سجلات الضيوف", - "SettingsTabLoggingEnableFsAccessLogs": "تمكين سجلات الوصول إلى نظام الملفات", - "SettingsTabLoggingFsGlobalAccessLogMode": "وضع سجل الوصول العالمي لنظام الملفات:", - "SettingsTabLoggingDeveloperOptions": "خيارات المطور", - "SettingsTabLoggingDeveloperOptionsNote": "تحذير: سوف يقلل من الأداء", - "SettingsTabLoggingGraphicsBackendLogLevel": "مستوى سجل خلفية الرسومات:", - "SettingsTabLoggingGraphicsBackendLogLevelNone": "لا شيء", - "SettingsTabLoggingGraphicsBackendLogLevelError": "خطأ", - "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "تباطؤ", - "SettingsTabLoggingGraphicsBackendLogLevelAll": "الكل", - "SettingsTabLoggingEnableDebugLogs": "تمكين سجلات التصحيح", - "SettingsTabInput": "الإدخال", - "SettingsTabInputEnableDockedMode": "تركيب بالمنصة", - "SettingsTabInputDirectKeyboardAccess": "الوصول المباشر للوحة المفاتيح", - "SettingsButtonSave": "حفظ", - "SettingsButtonClose": "إغلاق", - "SettingsButtonOk": "موافق", - "SettingsButtonCancel": "إلغاء", - "SettingsButtonApply": "تطبيق", - "ControllerSettingsPlayer": "اللاعب", - "ControllerSettingsPlayer1": "اللاعب 1", - "ControllerSettingsPlayer2": "اللاعب 2", - "ControllerSettingsPlayer3": "اللاعب 3", - "ControllerSettingsPlayer4": "اللاعب 4", - "ControllerSettingsPlayer5": "اللاعب 5", - "ControllerSettingsPlayer6": "اللاعب 6", - "ControllerSettingsPlayer7": "اللاعب 7", - "ControllerSettingsPlayer8": "اللاعب 8", - "ControllerSettingsHandheld": "محمول", - "ControllerSettingsInputDevice": "جهاز الإدخال", - "ControllerSettingsRefresh": "تحديث", - "ControllerSettingsDeviceDisabled": "معطل", - "ControllerSettingsControllerType": "نوع وحدة التحكم", - "ControllerSettingsControllerTypeHandheld": "محمول", - "ControllerSettingsControllerTypeProController": "وحدة تحكم برو", - "ControllerSettingsControllerTypeJoyConPair": "زوج جوي كون", - "ControllerSettingsControllerTypeJoyConLeft": "جوي كون اليسار ", - "ControllerSettingsControllerTypeJoyConRight": " جوي كون اليمين", - "ControllerSettingsProfile": "الملف الشخصي", - "ControllerSettingsProfileDefault": "افتراضي", - "ControllerSettingsLoad": "تحميل", - "ControllerSettingsAdd": "إضافة", - "ControllerSettingsRemove": "إزالة", - "ControllerSettingsButtons": "الأزرار", - "ControllerSettingsButtonA": "A", - "ControllerSettingsButtonB": "B", - "ControllerSettingsButtonX": "X", - "ControllerSettingsButtonY": "Y", - "ControllerSettingsButtonPlus": "+", - "ControllerSettingsButtonMinus": "-", - "ControllerSettingsDPad": "أسهم الاتجاهات", - "ControllerSettingsDPadUp": "اعلى", - "ControllerSettingsDPadDown": "أسفل", - "ControllerSettingsDPadLeft": "يسار", - "ControllerSettingsDPadRight": "يمين", - "ControllerSettingsStickButton": "زر", - "ControllerSettingsStickUp": "فوق", - "ControllerSettingsStickDown": "أسفل", - "ControllerSettingsStickLeft": "يسار", - "ControllerSettingsStickRight": "يمين", - "ControllerSettingsStickStick": "عصا", - "ControllerSettingsStickInvertXAxis": "عكس عرض العصا", - "ControllerSettingsStickInvertYAxis": "عكس أفق العصا", - "ControllerSettingsStickDeadzone": "المنطقة الميتة:", - "ControllerSettingsLStick": "العصا اليسرى", - "ControllerSettingsRStick": "العصا اليمنى", - "ControllerSettingsTriggersLeft": "الأزندة اليسرى", - "ControllerSettingsTriggersRight": "الأزندة اليمني", - "ControllerSettingsTriggersButtonsLeft": "أزرار الزناد اليسرى", - "ControllerSettingsTriggersButtonsRight": "أزرار الزناد اليمنى", - "ControllerSettingsTriggers": "أزندة", - "ControllerSettingsTriggerL": "L", - "ControllerSettingsTriggerR": "R", - "ControllerSettingsTriggerZL": "ZL", - "ControllerSettingsTriggerZR": "ZR", - "ControllerSettingsLeftSL": "SL", - "ControllerSettingsLeftSR": "SR", - "ControllerSettingsRightSL": "SL", - "ControllerSettingsRightSR": "SR", - "ControllerSettingsExtraButtonsLeft": "الأزرار اليسار", - "ControllerSettingsExtraButtonsRight": "الأزرار اليمين", - "ControllerSettingsMisc": "إعدادات إضافية", - "ControllerSettingsTriggerThreshold": "قوة التحفيز:", - "ControllerSettingsMotion": "الحركة", - "ControllerSettingsMotionUseCemuhookCompatibleMotion": "استخدام الحركة المتوافقة مع CemuHook", - "ControllerSettingsMotionControllerSlot": "خانة وحدة التحكم:", - "ControllerSettingsMotionMirrorInput": "إعادة الإدخال", - "ControllerSettingsMotionRightJoyConSlot": "خانة جويكون اليمين :", - "ControllerSettingsMotionServerHost": "مضيف الخادم:", - "ControllerSettingsMotionGyroSensitivity": "حساسية مستشعر الحركة:", - "ControllerSettingsMotionGyroDeadzone": "منطقة مستشعر الحركة الميتة:", - "ControllerSettingsSave": "حفظ", - "ControllerSettingsClose": "إغلاق", - "KeyUnknown": "مجهول", - "KeyShiftLeft": "زر ‫Shift الأيسر", - "KeyShiftRight": "زر ‫Shift الأيمن", - "KeyControlLeft": "زر ‫Ctrl الأيسر", - "KeyMacControlLeft": "زر ⌃ الأيسر", - "KeyControlRight": "زر ‫Ctrl الأيمن", - "KeyMacControlRight": "زر ⌃ الأيمن", - "KeyAltLeft": "زر ‫Alt الأيسر", - "KeyMacAltLeft": "زر ⌥ الأيسر", - "KeyAltRight": "زر ‫Alt الأيمن", - "KeyMacAltRight": "زر ⌥ الأيمن", - "KeyWinLeft": "زر ⊞ الأيسر", - "KeyMacWinLeft": "زر ⌘ الأيسر", - "KeyWinRight": "زر ⊞ الأيمن", - "KeyMacWinRight": "زر ⌘ الأيمن", - "KeyMenu": "زر القائمة", - "KeyUp": "فوق", - "KeyDown": "اسفل", - "KeyLeft": "يسار", - "KeyRight": "يمين", - "KeyEnter": "مفتاح الإدخال", - "KeyEscape": "زر ‫Escape", - "KeySpace": "مسافة", - "KeyTab": "زر ‫Tab", - "KeyBackSpace": "زر المسح للخلف", - "KeyInsert": "زر Insert", - "KeyDelete": "زر الحذف", - "KeyPageUp": "زر ‫Page Up", - "KeyPageDown": "زر ‫Page Down", - "KeyHome": "زر ‫Home", - "KeyEnd": "زر ‫End", - "KeyCapsLock": "زر الحروف الكبيرة", - "KeyScrollLock": "زر ‫Scroll Lock", - "KeyPrintScreen": "زر ‫Print Screen", - "KeyPause": "زر Pause", - "KeyNumLock": "زر Num Lock", - "KeyClear": "زر Clear", - "KeyKeypad0": "لوحة الأرقام 0", - "KeyKeypad1": "لوحة الأرقام 1", - "KeyKeypad2": "لوحة الأرقام 2", - "KeyKeypad3": "لوحة الأرقام 3", - "KeyKeypad4": "لوحة الأرقام 4", - "KeyKeypad5": "لوحة الأرقام 5", - "KeyKeypad6": "لوحة الأرقام 6", - "KeyKeypad7": "لوحة الأرقام 7", - "KeyKeypad8": "لوحة الأرقام 8", - "KeyKeypad9": "لوحة الأرقام 9", - "KeyKeypadDivide": "لوحة الأرقام علامة القسمة", - "KeyKeypadMultiply": "لوحة الأرقام علامة الضرب", - "KeyKeypadSubtract": "لوحة الأرقام علامة الطرح\n", - "KeyKeypadAdd": "لوحة الأرقام علامة الزائد", - "KeyKeypadDecimal": "لوحة الأرقام الفاصلة العشرية", - "KeyKeypadEnter": "لوحة الأرقام زر الإدخال", - "KeyNumber0": "٠", - "KeyNumber1": "١", - "KeyNumber2": "٢", - "KeyNumber3": "٣", - "KeyNumber4": "٤", - "KeyNumber5": "٥", - "KeyNumber6": "٦", - "KeyNumber7": "٧", - "KeyNumber8": "٨", - "KeyNumber9": "٩", - "KeyTilde": "~", - "KeyGrave": "`", - "KeyMinus": "-", - "KeyPlus": "+", - "KeyBracketLeft": "[", - "KeyBracketRight": "]", - "KeySemicolon": ";", - "KeyQuote": "\"", - "KeyComma": ",", - "KeyPeriod": ".", - "KeySlash": "/", - "KeyBackSlash": "\\", - "KeyUnbound": "غير مرتبط", - "GamepadLeftStick": "زر عصا التحكم اليسرى", - "GamepadRightStick": "زر عصا التحكم اليمنى", - "GamepadLeftShoulder": "زر الكتف الأيسر‫ L", - "GamepadRightShoulder": "زر الكتف الأيمن‫ R", - "GamepadLeftTrigger": "زر الزناد الأيسر‫ (ZL)", - "GamepadRightTrigger": "زر الزناد الأيمن‫ (ZR)", - "GamepadDpadUp": "فوق", - "GamepadDpadDown": "اسفل", - "GamepadDpadLeft": "يسار", - "GamepadDpadRight": "يمين", - "GamepadMinus": "-", - "GamepadPlus": "+", - "GamepadGuide": "دليل", - "GamepadMisc1": "متنوع", - "GamepadPaddle1": "Paddle 1", - "GamepadPaddle2": "Paddle 2", - "GamepadPaddle3": "Paddle 3", - "GamepadPaddle4": "Paddle 4", - "GamepadTouchpad": "لوحة اللمس", - "GamepadSingleLeftTrigger0": "زر الزناد الأيسر 0", - "GamepadSingleRightTrigger0": "زر الزناد الأيمن 0", - "GamepadSingleLeftTrigger1": "زر الزناد الأيسر 1", - "GamepadSingleRightTrigger1": "زر الزناد الأيمن 1", - "StickLeft": "عصا التحكم اليسرى", - "StickRight": "عصا التحكم اليمنى", - "UserProfilesSelectedUserProfile": "الملف الشخصي المحدد للمستخدم:", - "UserProfilesSaveProfileName": "حفظ اسم الملف الشخصي", - "UserProfilesChangeProfileImage": "تغيير صورة الملف الشخصي", - "UserProfilesAvailableUserProfiles": "الملفات الشخصية للمستخدم المتاحة:", - "UserProfilesAddNewProfile": "إنشاء ملف الشخصي", - "UserProfilesDelete": "حذف", - "UserProfilesClose": "إغلاق", - "ProfileNameSelectionWatermark": "اختر اسم مستعار", - "ProfileImageSelectionTitle": "تحديد صورة الملف الشخصي", - "ProfileImageSelectionHeader": "اختر صورة الملف الشخصي", - "ProfileImageSelectionNote": "يمكنك استيراد صورة ملف شخصي مخصصة، أو تحديد صورة رمزية من البرامج الثابتة للنظام", - "ProfileImageSelectionImportImage": "استيراد ملف الصورة", - "ProfileImageSelectionSelectAvatar": "حدد الصورة الرمزية من البرنامج الثابتة", - "InputDialogTitle": "حوار الإدخال", - "InputDialogOk": "موافق", - "InputDialogCancel": "إلغاء", - "InputDialogAddNewProfileTitle": "اختر اسم الملف الشخصي", - "InputDialogAddNewProfileHeader": "الرجاء إدخال اسم الملف الشخصي", - "InputDialogAddNewProfileSubtext": "(الطول الأقصى: {0})", - "AvatarChoose": "اختر الصورة الرمزية", - "AvatarSetBackgroundColor": "تعيين لون الخلفية", - "AvatarClose": "إغلاق", - "ControllerSettingsLoadProfileToolTip": "تحميل الملف الشخصي", - "ControllerSettingsAddProfileToolTip": "إضافة ملف شخصي", - "ControllerSettingsRemoveProfileToolTip": "إزالة الملف الشخصي", - "ControllerSettingsSaveProfileToolTip": "حفظ الملف الشخصي", - "MenuBarFileToolsTakeScreenshot": "أخذ لقطة للشاشة", - "MenuBarFileToolsHideUi": "إخفاء واجهة المستخدم", - "GameListContextMenuRunApplication": "تشغيل التطبيق", - "GameListContextMenuToggleFavorite": "تعيين كمفضل", - "GameListContextMenuToggleFavoriteToolTip": "تبديل الحالة المفضلة للعبة", - "SettingsTabGeneralTheme": "السمة:", - "SettingsTabGeneralThemeDark": "داكن", - "SettingsTabGeneralThemeLight": "فاتح", - "ControllerSettingsConfigureGeneral": "ضبط", - "ControllerSettingsRumble": "الاهتزاز", - "ControllerSettingsRumbleStrongMultiplier": "مضاعف اهتزاز قوي", - "ControllerSettingsRumbleWeakMultiplier": "مضاعف اهتزاز ضعيف", - "DialogMessageSaveNotAvailableMessage": "لا توجد بيانات الحفظ لـ {0} [{1:x16}]", - "DialogMessageSaveNotAvailableCreateSaveMessage": "هل ترغب في إنشاء بيانات الحفظ لهذه اللعبة؟", - "DialogConfirmationTitle": "ريوجينكس - تأكيد", - "DialogUpdaterTitle": "ريوجينكس - المحدث", - "DialogErrorTitle": "ريوجينكس - خطأ", - "DialogWarningTitle": "ريوجينكس - تحذير", - "DialogExitTitle": "ريوجينكس - الخروج", - "DialogErrorMessage": "واجه ريوجينكس خطأ", - "DialogExitMessage": "هل أنت متأكد من أنك تريد إغلاق ريوجينكس؟", - "DialogExitSubMessage": "سيتم فقدان كافة البيانات غير المحفوظة!", - "DialogMessageCreateSaveErrorMessage": "حدث خطأ أثناء إنشاء بيانات الحفظ المحددة: {0}", - "DialogMessageFindSaveErrorMessage": "حدث خطأ أثناء البحث عن بيانات الحفظ المحددة: {0}", - "FolderDialogExtractTitle": "اختر المجلد الذي تريد الاستخراج إليه", - "DialogNcaExtractionMessage": "استخراج قسم {0} من {1}...", - "DialogNcaExtractionTitle": "ريوجينكس - مستخرج قسم NCA", - "DialogNcaExtractionMainNcaNotFoundErrorMessage": "فشل الاستخراج. لم يكن NCA الرئيسي موجودا في الملف المحدد.", - "DialogNcaExtractionCheckLogErrorMessage": "فشل الاستخراج. اقرأ ملف التسجيل لمزيد من المعلومات.", - "DialogNcaExtractionSuccessMessage": "تم الاستخراج بنجاح.", - "DialogUpdaterConvertFailedMessage": "فشل تحويل إصدار ريوجينكس الحالي.", - "DialogUpdaterCancelUpdateMessage": "إلغاء التحديث", - "DialogUpdaterAlreadyOnLatestVersionMessage": "أنت تستخدم بالفعل أحدث إصدار من ريوجينكس!", - "DialogUpdaterFailedToGetVersionMessage": "حدث خطأ أثناء محاولة الحصول على معلومات الإصدار من إصدار غيت هاب. يمكن أن يحدث هذا إذا تم تجميع إصدار جديد بواسطة إجراءات غيت هاب. جرب مجددا بعد دقائق.", - "DialogUpdaterConvertFailedGithubMessage": "فشل تحويل إصدار ريوجينكس المستلم من إصدار غيت هاب.", - "DialogUpdaterDownloadingMessage": "جاري تنزيل التحديث...", - "DialogUpdaterExtractionMessage": "جاري استخراج التحديث...", - "DialogUpdaterRenamingMessage": "إعادة تسمية التحديث...", - "DialogUpdaterAddingFilesMessage": "إضافة تحديث جديد...", - "DialogUpdaterCompleteMessage": "اكتمل التحديث", - "DialogUpdaterRestartMessage": "هل تريد إعادة تشغيل ريوجينكس الآن؟", - "DialogUpdaterNoInternetMessage": "أنت غير متصل بالإنترنت.", - "DialogUpdaterNoInternetSubMessage": "يرجى التحقق من أن لديك اتصال إنترنت فعال!", - "DialogUpdaterDirtyBuildMessage": "لا يمكنك تحديث نسخة القذرة من ريوجينكس!", - "DialogUpdaterDirtyBuildSubMessage": "الرجاء تحميل ريوجينكس من https://ryujinx.org إذا كنت تبحث عن إصدار مدعوم.", - "DialogRestartRequiredMessage": "يتطلب إعادة التشغيل", - "DialogThemeRestartMessage": "تم حفظ السمة. إعادة التشغيل مطلوبة لتطبيق السمة.", - "DialogThemeRestartSubMessage": "هل تريد إعادة التشغيل", - "DialogFirmwareInstallEmbeddedMessage": "هل ترغب في تثبيت البرنامج الثابت المدمج في هذه اللعبة؟ (البرنامج الثابت {0})", - "DialogFirmwareInstallEmbeddedSuccessMessage": "لم يتم العثور على أي برنامج ثابت مثبت ولكن ريوجينكس كان قادرا على تثبيت البرنامج الثابت {0} من اللعبة المقدمة.\nسيبدأ المحاكي الآن.", - "DialogFirmwareNoFirmwareInstalledMessage": "لا يوجد برنامج ثابت مثبت", - "DialogFirmwareInstalledMessage": "تم تثبيت البرنامج الثابت {0}", - "DialogInstallFileTypesSuccessMessage": "تم تثبيت أنواع الملفات بنجاح!", - "DialogInstallFileTypesErrorMessage": "فشل تثبيت أنواع الملفات.", - "DialogUninstallFileTypesSuccessMessage": "تم إلغاء تثبيت أنواع الملفات بنجاح!", - "DialogUninstallFileTypesErrorMessage": "فشل إلغاء تثبيت أنواع الملفات.", - "DialogOpenSettingsWindowLabel": "فتح نافذة الإعدادات", - "DialogControllerAppletTitle": "تطبيق وحدة التحكم المصغر", - "DialogMessageDialogErrorExceptionMessage": "خطأ في عرض مربع حوار الرسالة: {0}", - "DialogSoftwareKeyboardErrorExceptionMessage": "خطأ في عرض لوحة مفاتيح البرامج: {0}", - "DialogErrorAppletErrorExceptionMessage": "خطأ في عرض مربع حوار خطأ التطبيق المصغر: {0}", - "DialogUserErrorDialogMessage": "{0}: {1}", - "DialogUserErrorDialogInfoMessage": "لمزيد من المعلومات حول كيفية إصلاح هذا الخطأ، اتبع دليل الإعداد الخاص بنا.", - "DialogUserErrorDialogTitle": "خطأ ريوجينكس ({0})", - "DialogAmiiboApiTitle": "أميبو API", - "DialogAmiiboApiFailFetchMessage": "حدث خطأ أثناء جلب المعلومات من API.", - "DialogAmiiboApiConnectErrorMessage": "غير قادر على الاتصال بخادم API أميبو. قد تكون الخدمة معطلة أو قد تحتاج إلى التحقق من اتصالك بالإنترنت.", - "DialogProfileInvalidProfileErrorMessage": "الملف الشخصي {0} غير متوافق مع نظام تكوين الإدخال الحالي.", - "DialogProfileDefaultProfileOverwriteErrorMessage": "لا يمكن الكتابة فوق الملف الشخصي الافتراضي", - "DialogProfileDeleteProfileTitle": "حذف الملف الشخصي", - "DialogProfileDeleteProfileMessage": "هذا الإجراء لا رجعة فيه، هل أنت متأكد من أنك تريد المتابعة؟", - "DialogWarning": "تحذير", - "DialogPPTCDeletionMessage": "أنت على وشك الإنتظار لإعادة بناء ذاكرة التخزين المؤقت للترجمة المستمرة (PPTC) عند الإقلاع التالي لـ:\n\n{0}\n\nأمتأكد من رغبتك في المتابعة؟", - "DialogPPTCDeletionErrorMessage": "خطأ خلال تنظيف ذاكرة التخزين المؤقت للترجمة المستمرة (PPTC) في {0}: {1}", - "DialogShaderDeletionMessage": "أنت على وشك حذف ذاكرة المظللات المؤقتة ل:\n\n{0}\n\nهل انت متأكد انك تريد المتابعة؟", - "DialogShaderDeletionErrorMessage": "حدث خطأ أثناء تنظيف ذاكرة المظللات المؤقتة في {0}: {1}", - "DialogRyujinxErrorMessage": "واجه ريوجينكس خطأ", - "DialogInvalidTitleIdErrorMessage": "خطأ في واجهة المستخدم: اللعبة المحددة لم يكن لديها معرف عنوان صالح", - "DialogFirmwareInstallerFirmwareNotFoundErrorMessage": "لم يتم العثور على برنامج ثابت للنظام صالح في {0}.", - "DialogFirmwareInstallerFirmwareInstallTitle": "تثبيت البرنامج الثابت {0}", - "DialogFirmwareInstallerFirmwareInstallMessage": "سيتم تثبيت إصدار النظام {0}.", - "DialogFirmwareInstallerFirmwareInstallSubMessage": "\n\nهذا سيحل محل إصدار النظام الحالي {0}.", - "DialogFirmwareInstallerFirmwareInstallConfirmMessage": "\nهل تريد المتابعة؟", - "DialogFirmwareInstallerFirmwareInstallWaitMessage": "تثبيت البرنامج الثابت...", - "DialogFirmwareInstallerFirmwareInstallSuccessMessage": "تم تثبيت إصدار النظام {0} بنجاح.", - "DialogUserProfileDeletionWarningMessage": "لن تكون هناك ملفات الشخصية أخرى لفتحها إذا تم حذف الملف الشخصي المحدد", - "DialogUserProfileDeletionConfirmMessage": "هل تريد حذف الملف الشخصي المحدد", - "DialogUserProfileUnsavedChangesTitle": "تحذير - التغييرات غير محفوظة", - "DialogUserProfileUnsavedChangesMessage": "لقد قمت بإجراء تغييرات على الملف الشخصي لهذا المستخدم هذا ولم يتم حفظها.", - "DialogUserProfileUnsavedChangesSubMessage": "هل تريد تجاهل التغييرات؟", - "DialogControllerSettingsModifiedConfirmMessage": "تم تحديث إعدادات وحدة التحكم الحالية.", - "DialogControllerSettingsModifiedConfirmSubMessage": "هل تريد الحفظ ؟", - "DialogLoadFileErrorMessage": "{0}. ملف خاطئ: {1}", - "DialogModAlreadyExistsMessage": "التعديل موجود بالفعل", - "DialogModInvalidMessage": "المجلد المحدد لا يحتوي على تعديل!", - "DialogModDeleteNoParentMessage": "فشل الحذف: لم يمكن العثور على المجلد الرئيسي للتعديل\"{0}\"!", - "DialogDlcNoDlcErrorMessage": "الملف المحدد لا يحتوي على محتوى إضافي للعنوان المحدد!", - "DialogPerformanceCheckLoggingEnabledMessage": "لقد تم تمكين تسجيل التتبع، والذي تم تصميمه ليتم استخدامه من قبل المطورين فقط.", - "DialogPerformanceCheckLoggingEnabledConfirmMessage": "للحصول على الأداء الأمثل، يوصى بتعطيل تسجيل التتبع. هل ترغب في تعطيل تسجيل التتبع الآن؟", - "DialogPerformanceCheckShaderDumpEnabledMessage": "لقد قمت بتمكين تفريغ المظللات، والذي تم تصميمه ليستخدمه المطورون فقط.", - "DialogPerformanceCheckShaderDumpEnabledConfirmMessage": "للحصول على الأداء الأمثل، يوصى بتعطيل تفريغ المظللات. هل ترغب في تعطيل تفريغ المظللات الآن؟", - "DialogLoadAppGameAlreadyLoadedMessage": "تم تحميل لعبة بالفعل", - "DialogLoadAppGameAlreadyLoadedSubMessage": "الرجاء إيقاف المحاكاة أو إغلاق المحاكي قبل بدء لعبة أخرى.", - "DialogUpdateAddUpdateErrorMessage": "الملف المحدد لا يحتوي على تحديث للعنوان المحدد!", - "DialogSettingsBackendThreadingWarningTitle": "تحذير - خلفية متعددة المسارات", - "DialogSettingsBackendThreadingWarningMessage": "يجب إعادة تشغيل ريوجينكس بعد تغيير هذا الخيار حتى يتم تطبيقه بالكامل. اعتمادا على النظام الأساسي الخاص بك، قد تحتاج إلى تعطيل تعدد المسارات الخاص ببرنامج الرسومات التشغيل الخاص بك يدويًا عند استخدام الخاص بريوجينكس.", - "DialogModManagerDeletionWarningMessage": "أنت على وشك حذف التعديل: {0}\n\nهل انت متأكد انك تريد المتابعة؟", - "DialogModManagerDeletionAllWarningMessage": "أنت على وشك حذف كافة التعديلات لهذا العنوان.\n\nهل انت متأكد انك تريد المتابعة؟", - "SettingsTabGraphicsFeaturesOptions": "المميزات", - "SettingsTabGraphicsBackendMultithreading": "تعدد المسارات لخلفية الرسومات:", - "CommonAuto": "تلقائي", - "CommonOff": "معطل", - "CommonOn": "تشغيل", - "InputDialogYes": "نعم", - "InputDialogNo": "لا", - "DialogProfileInvalidProfileNameErrorMessage": "يحتوي اسم الملف على أحرف غير صالحة. يرجى المحاولة مرة أخرى.", - "MenuBarOptionsPauseEmulation": "إيقاف مؤقت", - "MenuBarOptionsResumeEmulation": "استئناف", - "AboutUrlTooltipMessage": "انقر لفتح موقع ريوجينكس في متصفحك الافتراضي.", - "AboutDisclaimerMessage": "ريوجينكس لا ينتمي إلى نينتندو™،\nأو أي من شركائها بأي شكل من الأشكال.", - "AboutAmiiboDisclaimerMessage": "AmiiboAPI (www.amiiboapi.com) يتم \nاستخدامه في محاكاة أمبيو لدينا.", - "AboutPatreonUrlTooltipMessage": "انقر لفتح صفحة ريوجينكس في باتريون في متصفحك الافتراضي.", - "AboutGithubUrlTooltipMessage": "انقر لفتح صفحة ريوجينكس في غيت هاب في متصفحك الافتراضي.", - "AboutDiscordUrlTooltipMessage": "انقر لفتح دعوة إلى خادم ريوجينكس في ديكسورد في متصفحك الافتراضي.", - "AboutTwitterUrlTooltipMessage": "انقر لفتح صفحة ريوجينكس في تويتر في متصفحك الافتراضي.", - "AboutRyujinxAboutTitle": "حول:", - "AboutRyujinxAboutContent": "ريوجينكس هو محاكي لجهاز نينتندو سويتش™.\nمن فضلك ادعمنا على باتريون.\nاحصل على آخر الأخبار على تويتر أو ديسكورد.\nيمكن للمطورين المهتمين بالمساهمة معرفة المزيد على غيت هاب أو ديسكورد.", - "AboutRyujinxMaintainersTitle": "تتم صيانته بواسطة:", - "AboutRyujinxMaintainersContentTooltipMessage": "انقر لفتح صفحة المساهمين في متصفحك الافتراضي.", - "AboutRyujinxSupprtersTitle": "مدعوم على باتريون بواسطة:", - "AmiiboSeriesLabel": "مجموعة أميبو", - "AmiiboCharacterLabel": "شخصية", - "AmiiboScanButtonLabel": "فحصه", - "AmiiboOptionsShowAllLabel": "إظهار كل أميبو", - "AmiiboOptionsUsRandomTagLabel": "هاك: استخدم علامة Uuid عشوائية ", - "DlcManagerTableHeadingEnabledLabel": "مفعل", - "DlcManagerTableHeadingTitleIdLabel": "معرف العنوان", - "DlcManagerTableHeadingContainerPathLabel": "مسار الحاوية", - "DlcManagerTableHeadingFullPathLabel": "المسار كاملا", - "DlcManagerRemoveAllButton": "حذف الكل", - "DlcManagerEnableAllButton": "تشغيل الكل", - "DlcManagerDisableAllButton": "تعطيل الكل", - "ModManagerDeleteAllButton": "حذف الكل", - "MenuBarOptionsChangeLanguage": "تغيير اللغة", - "MenuBarShowFileTypes": "إظهار أنواع الملفات", - "CommonSort": "فرز", - "CommonShowNames": "عرض الأسماء", - "CommonFavorite": "المفضلة", - "OrderAscending": "تصاعدي", - "OrderDescending": "تنازلي", - "SettingsTabGraphicsFeatures": "الميزات والتحسينات", - "ErrorWindowTitle": "نافذة الخطأ", - "ToggleDiscordTooltip": "اختر ما إذا كنت تريد عرض ريوجينكس في نشاط ديسكورد \"يتم تشغيله حاليا\" أم لا", - "AddGameDirBoxTooltip": "أدخل مجلد اللعبة لإضافته إلى القائمة", - "AddGameDirTooltip": "إضافة مجلد اللعبة إلى القائمة", - "RemoveGameDirTooltip": "إزالة مجلد اللعبة المحدد", - "CustomThemeCheckTooltip": "استخدم سمة أفالونيا المخصصة لواجهة المستخدم الرسومية لتغيير مظهر قوائم المحاكي", - "CustomThemePathTooltip": "مسار سمة واجهة المستخدم المخصصة", - "CustomThemeBrowseTooltip": "تصفح للحصول على سمة واجهة المستخدم المخصصة", - "DockModeToggleTooltip": "يجعل وضع تركيب بالمنصة النظام الذي تمت محاكاته بمثابة جهاز نينتندو سويتش الذي تم تركيبه بالمنصة. يؤدي هذا إلى تحسين الدقة الرسومية في معظم الألعاب. على العكس من ذلك، سيؤدي تعطيل هذا إلى جعل النظام الذي تمت محاكاته يعمل كجهاز نينتندو سويتش محمول، مما يقلل من جودة الرسومات.\n\nقم بتكوين عناصر تحكم اللاعب 1 إذا كنت تخطط لاستخدام وضع تركيب بالمنصة؛ قم بتكوين عناصر التحكم المحمولة إذا كنت تخطط لاستخدام الوضع المحمول.\n\nاتركه مشغل إذا لم تكن متأكدا.", - "DirectKeyboardTooltip": "دعم الوصول المباشر للوحة المفاتيح (HID). يوفر وصول الألعاب إلى لوحة المفاتيح الخاصة بك كجهاز لإدخال النص.\n\nيعمل فقط مع الألعاب التي تدعم استخدام لوحة المفاتيح في الأصل على أجهزة سويتش.\n\nاتركه معطلا إذا كنت غير متأكد.", - "DirectMouseTooltip": "دعم الوصول المباشر للوحة المفاتيح (HID). يوفر وصول الألعاب إلى لوحة المفاتيح الخاصة بك كجهاز لإدخال النص.\n\nيعمل فقط مع الألعاب التي تدعم استخدام لوحة المفاتيح في الأصل على أجهزة سويتش.\n\nاتركه معطلا إذا كنت غير متأكد.", - "RegionTooltip": "تغيير منطقة النظام", - "LanguageTooltip": "تغيير لغة النظام", - "TimezoneTooltip": "تغيير النطاق الزمني للنظام", - "TimeTooltip": "تغيير وقت النظام", - "VSyncToggleTooltip": "محاكاة المزامنة العمودية للجهاز. في الأساس محدد الإطار لغالبية الألعاب؛ قد يؤدي تعطيله إلى تشغيل الألعاب بسرعة أعلى أو جعل شاشات التحميل تستغرق وقتا أطول أو تتعطل.\n\nيمكن تبديله داخل اللعبة باستخدام مفتاح التشغيل السريع الذي تفضله (F1 افتراضيا). نوصي بالقيام بذلك إذا كنت تخطط لتعطيله.\n\nاتركه ممكنا إذا لم تكن متأكدا.", - "PptcToggleTooltip": "يحفظ وظائف JIT المترجمة بحيث لا تحتاج إلى ترجمتها في كل مرة يتم فيها تحميل اللعبة.\n\nيقلل من التقطيع ويسرع بشكل ملحوظ أوقات التشغيل بعد التشغيل الأول للعبة.\n\nاتركه ممكنا إذا لم تكن متأكدا.", - "FsIntegrityToggleTooltip": "يتحقق من وجود ملفات تالفة عند تشغيل لعبة ما، وإذا تم اكتشاف ملفات تالفة، فسيتم عرض خطأ تجزئة في السجل.\n\nليس له أي تأثير على الأداء ويهدف إلى المساعدة في استكشاف الأخطاء وإصلاحها.\n\nاتركه مفعلا إذا كنت غير متأكد.", - "AudioBackendTooltip": "يغير الواجهة الخلفية المستخدمة لتقديم الصوت.\n\nSDL2 هو الخيار المفضل، بينما يتم استخدام OpenAL وSoundIO كبديلين. زائف لن يكون لها صوت.\n\nاضبط على SDL2 إذا لم تكن متأكدا.", - "MemoryManagerTooltip": "تغيير كيفية تعيين ذاكرة الضيف والوصول إليها. يؤثر بشكل كبير على أداء وحدة المعالجة المركزية التي تمت محاكاتها.\n\nاضبط على المضيف غير محدد إذا لم تكن متأكدا.", - "MemoryManagerSoftwareTooltip": "استخدام جدول الصفحات البرمجي لترجمة العناوين. أعلى دقة ولكن أبطأ أداء.", - "MemoryManagerHostTooltip": "تعيين الذاكرة مباشرة في مساحة عنوان المضيف. تجميع وتنفيذ JIT أسرع بكثير.", - "MemoryManagerUnsafeTooltip": "تعيين الذاكرة مباشرة، ولكن لا تخفي العنوان داخل مساحة عنوان الضيف قبل الوصول. أسرع، ولكن على حساب السلامة. يمكن لتطبيق الضيف الوصول إلى الذاكرة من أي مكان في ريوجينكس، لذا قم بتشغيل البرامج التي تثق بها فقط مع هذا الوضع.", - "UseHypervisorTooltip": "استخدم هايبرڤايزور بدلا من JIT. يعمل على تحسين الأداء بشكل كبير عند توفره، ولكنه قد يكون غير مستقر في حالته الحالية.", - "DRamTooltip": "يستخدم تخطيط وضع الذاكرة البديل لتقليد نموذج سويتش المطورين.\n\nيعد هذا مفيدا فقط لحزم النسيج عالية الدقة أو تعديلات دقة 4K. لا يحسن الأداء.\n\nاتركه معطلا إذا لم تكن متأكدا.", - "IgnoreMissingServicesTooltip": "يتجاهل خدمات نظام هوريزون غير المنفذة. قد يساعد هذا في تجاوز الأعطال عند تشغيل ألعاب معينة.\n\nاتركه معطلا إذا كنت غير متأكد.", - "GraphicsBackendThreadingTooltip": "ينفذ أوامر الواجهة الخلفية للرسومات على مسار ثاني.\n\nيعمل على تسريع عملية تجميع المظللات وتقليل التقطيع وتحسين الأداء على برامج تشغيل وحدة الرسوميات دون دعم المسارات المتعددة الخاصة بهم. أداء أفضل قليلا على برامج التشغيل ذات المسارات المتعددة.\n\nاضبط على تلقائي إذا لم تكن متأكدا.", - "GalThreadingTooltip": "ينفذ أوامر الواجهة الخلفية للرسومات على مسار ثاني.\n\nيعمل على تسريع عملية تجميع المظللات وتقليل التقطيع وتحسين الأداء على برامج تشغيل وحدة الرسوميات دون دعم المسارات المتعددة الخاصة بهم. أداء أفضل قليلا على برامج التشغيل ذات المسارات المتعددة.\n\nاضبط على تلقائي إذا لم تكن متأكدا.", - "ShaderCacheToggleTooltip": "يحفظ ذاكرة المظللات المؤقتة على القرص مما يقلل من التقطيع في عمليات التشغيل اللاحقة.\n\nاتركه مفعلا إذا لم تكن متأكدا.", - "ResolutionScaleTooltip": "يضاعف دقة عرض اللعبة.\n\nقد لا تعمل بعض الألعاب مع هذا وتبدو منقطة حتى عند زيادة الدقة؛ بالنسبة لهذه الألعاب، قد تحتاج إلى العثور على تعديلات تزيل تنعيم الحواف أو تزيد من دقة العرض الداخلي. لاستخدام الأخير، من المحتمل أن ترغب في تحديد أصلي.\n\nيمكن تغيير هذا الخيار أثناء تشغيل اللعبة بالنقر فوق \"تطبيق\" أدناه؛ يمكنك ببساطة تحريك نافذة الإعدادات جانبًا والتجربة حتى تجد المظهر المفضل للعبة.\n\nضع في اعتبارك أن 4x مبالغة في أي إعداد تقريبًا.", - "ResolutionScaleEntryTooltip": "مقياس دقة النقطة العائمة، مثل 1.5. من المرجح أن تتسبب المقاييس غير المتكاملة في حدوث مشكلات أو تعطل.", - "AnisotropyTooltip": "مستوى تصفية. اضبط على تلقائي لاستخدام القيمة التي تطلبها اللعبة.", - "AspectRatioTooltip": "يتم تطبيق نسبة العرض إلى الارتفاع على نافذة العارض.\n\nقم بتغيير هذا فقط إذا كنت تستخدم تعديل نسبة العرض إلى الارتفاع للعبتك، وإلا سيتم تمديد الرسومات.\n\nاتركه16:9 إذا لم تكن متأكدا.", - "ShaderDumpPathTooltip": "مسار تفريغ المظللات", - "FileLogTooltip": "حفظ تسجيل وحدة التحكم إلى ملف سجل على القرص. لا يؤثر على الأداء.", - "StubLogTooltip": "طباعة رسائل سجل stub في وحدة التحكم. لا يؤثر على الأداء.", - "InfoLogTooltip": "طباعة رسائل سجل المعلومات في وحدة التحكم. لا يؤثر على الأداء.", - "WarnLogTooltip": "طباعة رسائل سجل التحذير في وحدة التحكم. لا يؤثر على الأداء.", - "ErrorLogTooltip": "طباعة رسائل سجل الأخطاء في وحدة التحكم. لا يؤثر على الأداء.", - "TraceLogTooltip": "طباعة رسائل سجل التتبع في وحدة التحكم. لا يؤثر على الأداء.", - "GuestLogTooltip": "طباعة رسائل سجل الضيف في وحدة التحكم. لا يؤثر على الأداء.", - "FileAccessLogTooltip": "طباعة رسائل سجل الوصول إلى الملفات في وحدة التحكم.", - "FSAccessLogModeTooltip": "تمكين إخراج سجل الوصول إلى نظام الملفات إلى وحدة التحكم. الأوضاع الممكنة هي 0-3", - "DeveloperOptionTooltip": "استخدمه بعناية", - "OpenGlLogLevel": "يتطلب تمكين مستويات السجل المناسبة", - "DebugLogTooltip": "طباعة رسائل سجل التصحيح في وحدة التحكم.\n\nاستخدم هذا فقط إذا طلب منك أحد الموظفين تحديدًا ذلك، لأنه سيجعل من الصعب قراءة السجلات وسيؤدي إلى تدهور أداء المحاكي.", - "LoadApplicationFileTooltip": "افتح مستكشف الملفات لاختيار ملف متوافق مع سويتش لتحميله", - "LoadApplicationFolderTooltip": "افتح مستكشف الملفات لاختيار تطبيق متوافق مع سويتش للتحميل", - "OpenRyujinxFolderTooltip": "فتح مجلد نظام ملفات ريوجينكس", - "OpenRyujinxLogsTooltip": "يفتح المجلد الذي تتم كتابة السجلات إليه", - "ExitTooltip": "الخروج من ريوجينكس", - "OpenSettingsTooltip": "فتح نافذة الإعدادات", - "OpenProfileManagerTooltip": "فتح نافذة إدارة الملفات الشخصية للمستخدمين", - "StopEmulationTooltip": "إيقاف محاكاة اللعبة الحالية والعودة إلى اختيار اللعبة", - "CheckUpdatesTooltip": "التحقق من وجود تحديثات لريوجينكس", - "OpenAboutTooltip": "فتح حول النافذة", - "GridSize": "حجم الشبكة", - "GridSizeTooltip": "تغيير حجم عناصر الشبكة", - "SettingsTabSystemSystemLanguageBrazilianPortuguese": "البرتغالية البرازيلية", - "AboutRyujinxContributorsButtonHeader": "رؤية جميع المساهمين", - "SettingsTabSystemAudioVolume": "مستوى الصوت:", - "AudioVolumeTooltip": "تغيير مستوى الصوت", - "SettingsTabSystemEnableInternetAccess": "الوصول إلى إنترنت كضيف/وضع LAN", - "EnableInternetAccessTooltip": "للسماح للتطبيق الذي تمت محاكاته بالاتصال بالإنترنت.\n\nيمكن للألعاب التي تحتوي على وضع LAN الاتصال ببعضها البعض عند تمكين ذلك وتوصيل الأنظمة بنفس نقطة الوصول. وهذا يشمل الأجهزة الحقيقية أيضا.\n\nلا يسمح بالاتصال بخوادم نينتندو. قد يتسبب في حدوث عطل في بعض الألعاب التي تحاول الاتصال بالإنترنت.\n\nاتركه معطلا إذا لم تكن متأكدا.", - "GameListContextMenuManageCheatToolTip": "إدارة الغش", - "GameListContextMenuManageCheat": "إدارة الغش", - "GameListContextMenuManageModToolTip": "إدارة التعديلات", - "GameListContextMenuManageMod": "إدارة التعديلات", - "ControllerSettingsStickRange": "نطاق:", - "DialogStopEmulationTitle": "ريوجينكس - إيقاف المحاكاة", - "DialogStopEmulationMessage": "هل أنت متأكد أنك تريد إيقاف المحاكاة؟", - "SettingsTabCpu": "المعالج", - "SettingsTabAudio": "الصوت", - "SettingsTabNetwork": "الشبكة", - "SettingsTabNetworkConnection": "اتصال الشبكة", - "SettingsTabCpuCache": "ذاكرة المعالج المؤقت", - "SettingsTabCpuMemory": "وضع المعالج", - "DialogUpdaterFlatpakNotSupportedMessage": "الرجاء تحديث ريوجينكس عبر فلات هاب.", - "UpdaterDisabledWarningTitle": "المحدث معطل!", - "ControllerSettingsRotate90": "تدوير 90 درجة في اتجاه عقارب الساعة", - "IconSize": "حجم الأيقونة", - "IconSizeTooltip": "تغيير حجم أيقونات اللعبة", - "MenuBarOptionsShowConsole": "عرض وحدة التحكم", - "ShaderCachePurgeError": "حدث خطأ أثناء تنظيف ذاكرة المظللات المؤقتة في {0}: {1}", - "UserErrorNoKeys": "المفاتيح غير موجودة", - "UserErrorNoFirmware": "لم يتم العثور على البرنامج الثابت", - "UserErrorFirmwareParsingFailed": "خطأ في تحليل البرنامج الثابت", - "UserErrorApplicationNotFound": "التطبيق غير موجود", - "UserErrorUnknown": "خطأ غير معروف", - "UserErrorUndefined": "خطأ غير محدد", - "UserErrorNoKeysDescription": "لم يتمكن ريوجينكس من العثور على ملف 'prod.keys' الخاص بك", - "UserErrorNoFirmwareDescription": "لم يتمكن ريوجينكس من العثور على أية برامج ثابتة مثبتة", - "UserErrorFirmwareParsingFailedDescription": "لم يتمكن ريوجينكس من تحليل البرامج الثابتة المتوفرة. يحدث هذا عادة بسبب المفاتيح القديمة.", - "UserErrorApplicationNotFoundDescription": "تعذر على ريوجينكس العثور على تطبيق صالح في المسار المحدد.", - "UserErrorUnknownDescription": "حدث خطأ غير معروف!", - "UserErrorUndefinedDescription": "حدث خطأ غير محدد! لا ينبغي أن يحدث هذا، يرجى الاتصال بمطور!", - "OpenSetupGuideMessage": "فتح دليل الإعداد", - "NoUpdate": "لا يوجد تحديث", - "TitleUpdateVersionLabel": "الإصدار: {0}", - "RyujinxInfo": "ريوجينكس - معلومات", - "RyujinxConfirm": "ريوجينكس - تأكيد", - "FileDialogAllTypes": "كل الأنواع", - "Never": "مطلقا", - "SwkbdMinCharacters": "يجب أن يبلغ طوله {0} حرفا على الأقل", - "SwkbdMinRangeCharacters": "يجب أن يتكون من {0}-{1} حرفا", - "SoftwareKeyboard": "لوحة المفاتيح البرمجية", - "SoftwareKeyboardModeNumeric": "يجب أن يكون 0-9 أو '.' فقط", - "SoftwareKeyboardModeAlphabet": "يجب أن تكون الأحرف غير CJK فقط", - "SoftwareKeyboardModeASCII": "يجب أن يكون نص ASCII فقط", - "ControllerAppletControllers": "وحدات التحكم المدعومة:", - "ControllerAppletPlayers": "اللاعبين:", - "ControllerAppletDescription": "الإعدادات الحالية غير صالحة. افتح الإعدادات وأعد تكوين المدخلات الخاصة بك.", - "ControllerAppletDocked": "تم ضبط وضع تركيب بالمنصة. يجب تعطيل التحكم المحمول.", - "UpdaterRenaming": "إعادة تسمية الملفات القديمة...", - "UpdaterRenameFailed": "المحدث غير قادر على إعادة تسمية الملف: {0}", - "UpdaterAddingFiles": "إضافة ملفات جديدة...", - "UpdaterExtracting": "استخراج التحديث...", - "UpdaterDownloading": "تحميل التحديث...", - "Game": "لعبة", - "Docked": "تركيب بالمنصة", - "Handheld": "محمول", - "ConnectionError": "خطأ في الاتصال", - "AboutPageDeveloperListMore": "{0} والمزيد...", - "ApiError": "خطأ في API.", - "LoadingHeading": "جاري تحميل {0}", - "CompilingPPTC": "تجميع الـ‫(PPTC)", - "CompilingShaders": "تجميع المظللات", - "AllKeyboards": "كل لوحات المفاتيح", - "OpenFileDialogTitle": "حدد ملف مدعوم لفتحه", - "OpenFolderDialogTitle": "حدد مجلدا يحتوي على لعبة غير مضغوطة", - "AllSupportedFormats": "كل التنسيقات المدعومة", - "RyujinxUpdater": "محدث ريوجينكس", - "SettingsTabHotkeys": "مفاتيح الاختصار في لوحة المفاتيح", - "SettingsTabHotkeysHotkeys": "مفاتيح الاختصار في لوحة المفاتيح", - "SettingsTabHotkeysToggleVsyncHotkey": "تبديل المزامنة العمودية:", - "SettingsTabHotkeysScreenshotHotkey": "لقطة الشاشة:", - "SettingsTabHotkeysShowUiHotkey": "عرض واجهة المستخدم:", - "SettingsTabHotkeysPauseHotkey": "إيقاف مؤقت:", - "SettingsTabHotkeysToggleMuteHotkey": "كتم:", - "ControllerMotionTitle": "إعدادات التحكم بالحركة", - "ControllerRumbleTitle": "إعدادات الهزاز", - "SettingsSelectThemeFileDialogTitle": "حدد ملف السمة", - "SettingsXamlThemeFile": "ملف سمة Xaml", - "AvatarWindowTitle": "إدارة الحسابات - الصورة الرمزية", - "Amiibo": "أميبو", - "Unknown": "غير معروف", - "Usage": "الاستخدام", - "Writable": "قابل للكتابة", - "SelectDlcDialogTitle": "حدد ملفات المحتوي الإضافي", - "SelectUpdateDialogTitle": "حدد ملفات التحديث", - "SelectModDialogTitle": "حدد مجلد التعديل", - "UserProfileWindowTitle": "مدير الملفات الشخصية للمستخدمين", - "CheatWindowTitle": "مدير الغش", - "DlcWindowTitle": "إدارة المحتوى القابل للتنزيل لـ {0} ({1})", - "ModWindowTitle": "إدارة التعديلات لـ {0} ({1})", - "UpdateWindowTitle": "مدير تحديث العنوان", - "CheatWindowHeading": "الغش متوفر لـ {0} [{1}]", - "BuildId": "معرف البناء:", - "DlcWindowHeading": "المحتويات القابلة للتنزيل {0}", - "ModWindowHeading": "{0} تعديل", - "UserProfilesEditProfile": "تعديل المحدد", - "Cancel": "إلغاء", - "Save": "حفظ", - "Discard": "تجاهل", - "Paused": "متوقف مؤقتا", - "UserProfilesSetProfileImage": "تعيين صورة الملف الشخصي", - "UserProfileEmptyNameError": "الاسم مطلوب", - "UserProfileNoImageError": "يجب تعيين صورة الملف الشخصي", - "GameUpdateWindowHeading": "إدارة التحديثات لـ {0} ({1})", - "SettingsTabHotkeysResScaleUpHotkey": "زيادة الدقة:", - "SettingsTabHotkeysResScaleDownHotkey": "خفض الدقة:", - "UserProfilesName": "الاسم:", - "UserProfilesUserId": "معرف المستخدم:", - "SettingsTabGraphicsBackend": "خلفية الرسومات", - "SettingsTabGraphicsBackendTooltip": "حدد الواجهة الخلفية للرسومات التي سيتم استخدامها في المحاكي.\n\nيعد برنامج فولكان أفضل بشكل عام لجميع بطاقات الرسومات الحديثة، طالما أن برامج التشغيل الخاصة بها محدثة. يتميز فولكان أيضا بتجميع مظللات أسرع (أقل تقطيعا) على جميع بائعي وحدات معالجة الرسومات.\n\nقد يحقق أوبن جي أل نتائج أفضل على وحدات معالجة الرسومات إنفيديا القديمة، أو على وحدات معالجة الرسومات إي إم دي القديمة على لينكس، أو على وحدات معالجة الرسومات ذات ذاكرة الوصول العشوائي للفيديوالأقل، على الرغم من أن تعثرات تجميع المظللات ستكون أكبر.\n\nاضبط على فولكان إذا لم تكن متأكدا. اضبط على أوبن جي أل إذا كانت وحدة معالجة الرسومات الخاصة بك لا تدعم فولكان حتى مع أحدث برامج تشغيل الرسومات.", - "SettingsEnableTextureRecompression": "تمكين إعادة ضغط التكستر", - "SettingsEnableTextureRecompressionTooltip": "يضغط تكستر ASTC من أجل تقليل استخدام ذاكرة الوصول العشوائي للفيديو.\n\nتتضمن الألعاب التي تستخدم تنسيق النسيج هذا Astral Chain وBayonetta 3 وFire Emblem Engage وMetroid Prime Remastered وSuper Mario Bros. Wonder وThe Legend of Zelda: Tears of the Kingdom.\n\nمن المحتمل أن تتعطل بطاقات الرسومات التي تحتوي على 4 جيجا بايت من ذاكرة الوصول العشوائي للفيديو أو أقل في مرحلة ما أثناء تشغيل هذه الألعاب.\n\nقم بالتمكين فقط في حالة نفاد ذاكرة الوصول العشوائي للفيديو في الألعاب المذكورة أعلاه. اتركه معطلا إذا لم تكن متأكدا.", - "SettingsTabGraphicsPreferredGpu": "وحدة معالجة الرسوميات المفضلة", - "SettingsTabGraphicsPreferredGpuTooltip": "حدد بطاقة الرسومات التي سيتم استخدامها مع الواجهة الخلفية لرسومات فولكان.\n\nلا يؤثر على وحدة معالجة الرسومات التي سيستخدمها أوبن جي أل.\n\nاضبط على وحدة معالجة الرسومات التي تم وضع علامة عليها كـ \"dGPU\" إذا لم تكن متأكدًا. إذا لم يكن هناك واحد، اتركه.", - "SettingsAppRequiredRestartMessage": "مطلوب إعادة تشغيل ريوجينكس", - "SettingsGpuBackendRestartMessage": "تم تعديل إعدادات الواجهة الخلفية للرسومات أو وحدة معالجة الرسومات. سيتطلب هذا إعادة التشغيل ليتم تطبيقه", - "SettingsGpuBackendRestartSubMessage": "\n\nهل تريد إعادة التشغيل الآن؟", - "RyujinxUpdaterMessage": "هل تريد تحديث ريوجينكس إلى أحدث إصدار؟", - "SettingsTabHotkeysVolumeUpHotkey": "زيادة مستوى الصوت:", - "SettingsTabHotkeysVolumeDownHotkey": "خفض مستوى الصوت:", - "SettingsEnableMacroHLE": "تمكين Maro HLE", - "SettingsEnableMacroHLETooltip": "محاكاة عالية المستوى لكود مايكرو وحدة معالجة الرسوميات.\n\nيعمل على تحسين الأداء، ولكنه قد يسبب خللا رسوميا في بعض الألعاب.\n\nاتركه مفعلا إذا لم تكن متأكدا.", - "SettingsEnableColorSpacePassthrough": "عبور مساحة اللون", - "SettingsEnableColorSpacePassthroughTooltip": "يوجه واجهة فولكان الخلفية لتمرير معلومات الألوان دون تحديد مساحة اللون. بالنسبة للمستخدمين الذين لديهم شاشات ذات نطاق واسع، قد يؤدي ذلك إلى الحصول على ألوان أكثر حيوية، على حساب صحة الألوان.", - "VolumeShort": "مستوى", - "UserProfilesManageSaves": "إدارة الحفظ", - "DeleteUserSave": "هل تريد حذف حفظ المستخدم لهذه اللعبة؟", - "IrreversibleActionNote": "هذا الإجراء لا يمكن التراجع عنه.", - "SaveManagerHeading": "إدارة الحفظ لـ {0} ({1})", - "SaveManagerTitle": "مدير الحفظ", - "Name": "الاسم", - "Size": "الحجم", - "Search": "بحث", - "UserProfilesRecoverLostAccounts": "استعادة الحسابات المفقودة", - "Recover": "استعادة", - "UserProfilesRecoverHeading": "تم العثور على حفظ للحسابات التالية", - "UserProfilesRecoverEmptyList": "لا توجد ملفات شخصية لاستردادها", - "GraphicsAATooltip": "يتم تطبيق تنعيم الحواف على عرض اللعبة.\n\nسوف يقوم FXAA بتعتيم معظم الصورة، بينما سيحاول SMAA العثور على حواف خشنة وتنعيمها.\n\nلا ينصح باستخدامه مع فلتر FSR لتكبير.\n\nيمكن تغيير هذا الخيار أثناء تشغيل اللعبة بالنقر فوق \"تطبيق\" أدناه؛ يمكنك ببساطة تحريك نافذة الإعدادات جانبا والتجربة حتى تجد المظهر المفضل للعبة.\n\nاتركه على لا شيء إذا لم تكن متأكدا.", - "GraphicsAALabel": "تنعيم الحواف:", - "GraphicsScalingFilterLabel": "فلتر التكبير:", - "GraphicsScalingFilterTooltip": "اختر فلتر التكبير الذي سيتم تطبيقه عند استخدام مقياس الدقة.\n\nيعمل Bilinear بشكل جيد مع الألعاب ثلاثية الأبعاد وهو خيار افتراضي آمن.\n\nيوصى باستخدام Nearest لألعاب البكسل الفنية.\n\nFSR 1.0 هو مجرد مرشح توضيحي، ولا ينصح باستخدامه مع FXAA أو SMAA.\n\nيمكن تغيير هذا الخيار أثناء تشغيل اللعبة بالنقر فوق \"تطبيق\" أدناه؛ يمكنك ببساطة تحريك نافذة الإعدادات جانبا والتجربة حتى تجد المظهر المفضل للعبة.\n\nاتركه على Bilinear إذا لم تكن متأكدا.", - "GraphicsScalingFilterBilinear": "Bilinear", - "GraphicsScalingFilterNearest": "Nearest", - "GraphicsScalingFilterFsr": "FSR", - "GraphicsScalingFilterLevelLabel": "المستوى", - "GraphicsScalingFilterLevelTooltip": "اضبط مستوى وضوح FSR 1.0. الأعلى هو أكثر وضوحا.", - "SmaaLow": "SMAA منخفض", - "SmaaMedium": "SMAA متوسط", - "SmaaHigh": "SMAA عالي", - "SmaaUltra": "SMAA فائق", - "UserEditorTitle": "تعديل المستخدم", - "UserEditorTitleCreate": "إنشاء مستخدم", - "SettingsTabNetworkInterface": "واجهة الشبكة:", - "NetworkInterfaceTooltip": "واجهة الشبكة مستخدمة لميزات LAN/LDN.\n\nبالاشتراك مع VPN أو XLink Kai ولعبة تدعم LAN، يمكن استخدامها لتزييف اتصال الشبكة نفسها عبر الإنترنت.\n\nاتركه على الافتراضي إذا لم تكن متأكدا.", - "NetworkInterfaceDefault": "افتراضي", - "PackagingShaders": "تعبئة المظللات", - "AboutChangelogButton": "عرض سجل التغييرات على غيت هاب", - "AboutChangelogButtonTooltipMessage": "انقر لفتح سجل التغيير لهذا الإصدار في متصفحك الافتراضي.", - "SettingsTabNetworkMultiplayer": "لعب جماعي", - "MultiplayerMode": "الوضع:", - "MultiplayerModeTooltip": "تغيير وضع LDN متعدد اللاعبين.\n\nسوف يقوم LdnMitm بتعديل وظيفة اللعب المحلية/اللاسلكية المحلية في الألعاب لتعمل كما لو كانت شبكة LAN، مما يسمح باتصالات الشبكة المحلية نفسها مع محاكيات ريوجينكس الأخرى وأجهزة نينتندو سويتش المخترقة التي تم تثبيت وحدة ldn_mitm عليها.\n\nيتطلب وضع اللاعبين المتعددين أن يكون جميع اللاعبين على نفس إصدار اللعبة (على سبيل المثال، يتعذر على الإصدار 13.0.1 من سوبر سماش برذرز ألتميت الاتصال بالإصدار 13.0.0).\n\nاتركه معطلا إذا لم تكن متأكدا.", - "MultiplayerModeDisabled": "معطل", - "MultiplayerModeLdnMitm": "ldn_mitm" -} diff --git a/src/Ryujinx/Assets/Locales/de_DE.json b/src/Ryujinx/Assets/Locales/de_DE.json deleted file mode 100644 index 401293198..000000000 --- a/src/Ryujinx/Assets/Locales/de_DE.json +++ /dev/null @@ -1,780 +0,0 @@ -{ - "Language": "Deutsch", - "MenuBarFileOpenApplet": "Öffne Anwendung", - "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Öffnet das Mii-Editor-Applet im Standalone-Modus", - "SettingsTabInputDirectMouseAccess": "Direkter Mauszugriff", - "SettingsTabSystemMemoryManagerMode": "Speichermanagermodus:", - "SettingsTabSystemMemoryManagerModeSoftware": "Software", - "SettingsTabSystemMemoryManagerModeHost": "Host (schnell)", - "SettingsTabSystemMemoryManagerModeHostUnchecked": "Host ungeprüft (am schnellsten, unsicher)", - "SettingsTabSystemUseHypervisor": "Hypervisor verwenden", - "MenuBarFile": "_Datei", - "MenuBarFileOpenFromFile": "Datei _öffnen", - "MenuBarFileOpenUnpacked": "_Entpacktes Spiel öffnen", - "MenuBarFileOpenEmuFolder": "Ryujinx-Ordner öffnen", - "MenuBarFileOpenLogsFolder": "Logs-Ordner öffnen", - "MenuBarFileExit": "_Beenden", - "MenuBarOptions": "_Optionen", - "MenuBarOptionsToggleFullscreen": "Vollbild", - "MenuBarOptionsStartGamesInFullscreen": "Spiele im Vollbildmodus starten", - "MenuBarOptionsStopEmulation": "Emulation beenden", - "MenuBarOptionsSettings": "_Einstellungen", - "MenuBarOptionsManageUserProfiles": "_Benutzerprofile verwalten", - "MenuBarActions": "_Aktionen", - "MenuBarOptionsSimulateWakeUpMessage": "Aufwachnachricht simulieren", - "MenuBarActionsScanAmiibo": "Amiibo scannen", - "MenuBarTools": "_Tools", - "MenuBarToolsInstallFirmware": "Firmware installieren", - "MenuBarFileToolsInstallFirmwareFromFile": "Firmware von einer XCI- oder einer ZIP-Datei installieren", - "MenuBarFileToolsInstallFirmwareFromDirectory": "Firmware aus einem Verzeichnis installieren", - "MenuBarToolsManageFileTypes": "Dateitypen verwalten", - "MenuBarToolsInstallFileTypes": "Dateitypen installieren", - "MenuBarToolsUninstallFileTypes": "Dateitypen deinstallieren", - "MenuBarView": "_Ansicht", - "MenuBarViewWindow": "Fenstergröße", - "MenuBarViewWindow720": "720p", - "MenuBarViewWindow1080": "1080p", - "MenuBarHelp": "_Hilfe", - "MenuBarHelpCheckForUpdates": "Nach Updates suchen", - "MenuBarHelpAbout": "Über Ryujinx", - "MenuSearch": "Suchen...", - "GameListHeaderFavorite": "Favorit", - "GameListHeaderIcon": "Icon", - "GameListHeaderApplication": "Name", - "GameListHeaderDeveloper": "Entwickler", - "GameListHeaderVersion": "Version", - "GameListHeaderTimePlayed": "Spielzeit", - "GameListHeaderLastPlayed": "Zuletzt gespielt", - "GameListHeaderFileExtension": "Dateiformat", - "GameListHeaderFileSize": "Dateigröße", - "GameListHeaderPath": "Pfad", - "GameListContextMenuOpenUserSaveDirectory": "Spielstand-Verzeichnis öffnen", - "GameListContextMenuOpenUserSaveDirectoryToolTip": "Öffnet das Verzeichnis, welches den Benutzer-Spielstand beinhaltet", - "GameListContextMenuOpenDeviceSaveDirectory": "Benutzer-Geräte-Verzeichnis öffnen", - "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Öffnet das Verzeichnis, welches den Geräte-Spielstände beinhaltet", - "GameListContextMenuOpenBcatSaveDirectory": "Benutzer-BCAT-Vezeichnis öffnen", - "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Öffnet das Verzeichnis, welches den BCAT-Cache des Spiels beinhaltet", - "GameListContextMenuManageTitleUpdates": "Verwalte Spiel-Updates", - "GameListContextMenuManageTitleUpdatesToolTip": "Öffnet den Spiel-Update-Manager", - "GameListContextMenuManageDlc": "Verwalten von DLC", - "GameListContextMenuManageDlcToolTip": "Öffnet den DLC-Manager", - "GameListContextMenuCacheManagement": "Cache-Verwaltung", - "GameListContextMenuCacheManagementPurgePptc": "PPTC als ungültig markieren", - "GameListContextMenuCacheManagementPurgePptcToolTip": "Markiert den PPTC als ungültig, sodass dieser beim nächsten Spielstart neu erstellt wird", - "GameListContextMenuCacheManagementPurgeShaderCache": "Shader Cache löschen", - "GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "Löscht den Shader-Cache der Anwendung", - "GameListContextMenuCacheManagementOpenPptcDirectory": "PPTC-Verzeichnis öffnen", - "GameListContextMenuCacheManagementOpenPptcDirectoryToolTip": "Öffnet das Verzeichnis, das den PPTC-Cache der Anwendung beinhaltet", - "GameListContextMenuCacheManagementOpenShaderCacheDirectory": "Shader-Cache-Verzeichnis öffnen", - "GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip": "Öffnet das Verzeichnis, das den Shader Cache der Anwendung beinhaltet", - "GameListContextMenuExtractData": "Daten extrahieren", - "GameListContextMenuExtractDataExeFS": "ExeFS", - "GameListContextMenuExtractDataExeFSToolTip": "Extrahiert das ExeFS aus der aktuellen Anwendungskonfiguration (einschließlich Updates)", - "GameListContextMenuExtractDataRomFS": "RomFS", - "GameListContextMenuExtractDataRomFSToolTip": "Extrahiert das RomFS aus der aktuellen Anwendungskonfiguration (einschließlich Updates)", - "GameListContextMenuExtractDataLogo": "Logo", - "GameListContextMenuExtractDataLogoToolTip": "Extrahiert das Logo aus der aktuellen Anwendungskonfiguration (einschließlich Updates)", - "GameListContextMenuCreateShortcut": "Erstelle Anwendungsverknüpfung", - "GameListContextMenuCreateShortcutToolTip": "Erstelle eine Desktop-Verknüpfung die die gewählte Anwendung startet", - "GameListContextMenuCreateShortcutToolTipMacOS": "Erstellen Sie eine Verknüpfung im MacOS-Programme-Ordner, die die ausgewählte Anwendung startet", - "GameListContextMenuOpenModsDirectory": "Mod-Verzeichnis öffnen", - "GameListContextMenuOpenModsDirectoryToolTip": "Öffnet das Verzeichnis, welches Mods für die Spiele beinhaltet", - "GameListContextMenuOpenSdModsDirectory": "Atmosphere-Mod-Verzeichnis öffnen", - "GameListContextMenuOpenSdModsDirectoryToolTip": "Öffnet das alternative SD-Karten-Atmosphere-Verzeichnis, das die Mods der Anwendung enthält. Dieser Ordner ist nützlich für Mods, die für echte Hardware erstellt worden sind.", - "StatusBarGamesLoaded": "{0}/{1} Spiele geladen", - "StatusBarSystemVersion": "Systemversion: {0}", - "LinuxVmMaxMapCountDialogTitle": "Niedriges Limit für Speicherzuordnungen erkannt", - "LinuxVmMaxMapCountDialogTextPrimary": "Möchtest Du den Wert von vm.max_map_count auf {0} erhöhen", - "LinuxVmMaxMapCountDialogTextSecondary": "Einige Spiele könnten versuchen, mehr Speicherzuordnungen zu erstellen, als derzeit erlaubt. Ryujinx wird abstürzen, sobald dieses Limit überschritten wird.", - "LinuxVmMaxMapCountDialogButtonUntilRestart": "Ja, bis zum nächsten Neustart", - "LinuxVmMaxMapCountDialogButtonPersistent": "Ja, permanent", - "LinuxVmMaxMapCountWarningTextPrimary": "Maximale Anzahl an Speicherzuordnungen ist niedriger als empfohlen.", - "LinuxVmMaxMapCountWarningTextSecondary": "Der aktuelle Wert von vm.max_map_count ({0}) ist kleiner als {1}. Einige Spiele könnten versuchen, mehr Speicherzuordnungen zu erstellen, als derzeit erlaubt. Ryujinx wird abstürzen, sobald dieses Limit überschritten wird.\n\nDu kannst das Limit entweder manuell erhöhen oder pkexec installieren, damit Ryujinx Dir dabei hilft.", - "Settings": "Einstellungen", - "SettingsTabGeneral": "Oberfläche", - "SettingsTabGeneralGeneral": "Allgemein", - "SettingsTabGeneralEnableDiscordRichPresence": "Aktiviere die Statusanzeige für Discord", - "SettingsTabGeneralCheckUpdatesOnLaunch": "Beim Start nach Updates suchen", - "SettingsTabGeneralShowConfirmExitDialog": "Zeige den \"Beenden bestätigen\"-Dialog", - "SettingsTabGeneralRememberWindowState": "Fenstergröße/-position merken", - "SettingsTabGeneralHideCursor": "Mauszeiger ausblenden", - "SettingsTabGeneralHideCursorNever": "Niemals", - "SettingsTabGeneralHideCursorOnIdle": "Mauszeiger bei Inaktivität ausblenden", - "SettingsTabGeneralHideCursorAlways": "Immer", - "SettingsTabGeneralGameDirectories": "Spielverzeichnisse", - "SettingsTabGeneralAdd": "Hinzufügen", - "SettingsTabGeneralRemove": "Entfernen", - "SettingsTabSystem": "System", - "SettingsTabSystemCore": "Kern", - "SettingsTabSystemSystemRegion": "Systemregion:", - "SettingsTabSystemSystemRegionJapan": "Japan", - "SettingsTabSystemSystemRegionUSA": "USA", - "SettingsTabSystemSystemRegionEurope": "Europa", - "SettingsTabSystemSystemRegionAustralia": "Australien", - "SettingsTabSystemSystemRegionChina": "China", - "SettingsTabSystemSystemRegionKorea": "Korea", - "SettingsTabSystemSystemRegionTaiwan": "Taiwan", - "SettingsTabSystemSystemLanguage": "Systemsprache:", - "SettingsTabSystemSystemLanguageJapanese": "Japanisch", - "SettingsTabSystemSystemLanguageAmericanEnglish": "Amerikanisches Englisch", - "SettingsTabSystemSystemLanguageFrench": "Französisch", - "SettingsTabSystemSystemLanguageGerman": "Deutsch", - "SettingsTabSystemSystemLanguageItalian": "Italienisch", - "SettingsTabSystemSystemLanguageSpanish": "Spanisch", - "SettingsTabSystemSystemLanguageChinese": "Chinesisch", - "SettingsTabSystemSystemLanguageKorean": "Koreanisch", - "SettingsTabSystemSystemLanguageDutch": "Niederländisch", - "SettingsTabSystemSystemLanguagePortuguese": "Portugiesisch", - "SettingsTabSystemSystemLanguageRussian": "Russisch", - "SettingsTabSystemSystemLanguageTaiwanese": "Taiwanesisch", - "SettingsTabSystemSystemLanguageBritishEnglish": "Britisches Englisch", - "SettingsTabSystemSystemLanguageCanadianFrench": "Kanadisches Französisch", - "SettingsTabSystemSystemLanguageLatinAmericanSpanish": "Lateinamerikanisches Spanisch", - "SettingsTabSystemSystemLanguageSimplifiedChinese": "Vereinfachtes Chinesisch", - "SettingsTabSystemSystemLanguageTraditionalChinese": "Traditionelles Chinesisch", - "SettingsTabSystemSystemTimeZone": "System-Zeitzone:", - "SettingsTabSystemSystemTime": "Systemzeit:", - "SettingsTabSystemEnableVsync": "VSync", - "SettingsTabSystemEnablePptc": "PPTC (Profiled Persistent Translation Cache)", - "SettingsTabSystemEnableFsIntegrityChecks": "FS Integritätsprüfung", - "SettingsTabSystemAudioBackend": "Audio-Backend:", - "SettingsTabSystemAudioBackendDummy": "Ohne Funktion", - "SettingsTabSystemAudioBackendOpenAL": "OpenAL", - "SettingsTabSystemAudioBackendSoundIO": "SoundIO", - "SettingsTabSystemAudioBackendSDL2": "SDL2", - "SettingsTabSystemHacks": "Hacks", - "SettingsTabSystemHacksNote": " (Kann Fehler verursachen)", - "SettingsTabSystemExpandDramSize": "Erweitere DRAM Größe auf 6GiB", - "SettingsTabSystemIgnoreMissingServices": "Ignoriere fehlende Dienste", - "SettingsTabGraphics": "Grafik", - "SettingsTabGraphicsAPI": "Grafik-API", - "SettingsTabGraphicsEnableShaderCache": "Shader-Cache aktivieren", - "SettingsTabGraphicsAnisotropicFiltering": "Anisotrope Filterung:", - "SettingsTabGraphicsAnisotropicFilteringAuto": "Auto", - "SettingsTabGraphicsAnisotropicFiltering2x": "2x", - "SettingsTabGraphicsAnisotropicFiltering4x": "4x", - "SettingsTabGraphicsAnisotropicFiltering8x": "8x", - "SettingsTabGraphicsAnisotropicFiltering16x": "16x", - "SettingsTabGraphicsResolutionScale": "Auflösungsskalierung:", - "SettingsTabGraphicsResolutionScaleCustom": "Benutzerdefiniert (nicht empfohlen)", - "SettingsTabGraphicsResolutionScaleNative": "Nativ (720p/1080p)", - "SettingsTabGraphicsResolutionScale2x": "2x (1440p/2160p)", - "SettingsTabGraphicsResolutionScale3x": "3x (2160p/3240p)", - "SettingsTabGraphicsResolutionScale4x": "4x (2880p/4320p) (Nicht empfohlen)", - "SettingsTabGraphicsAspectRatio": "Bildseitenverhältnis:", - "SettingsTabGraphicsAspectRatio4x3": "4:3", - "SettingsTabGraphicsAspectRatio16x9": "16:9", - "SettingsTabGraphicsAspectRatio16x10": "16:10", - "SettingsTabGraphicsAspectRatio21x9": "21:9", - "SettingsTabGraphicsAspectRatio32x9": "32:9", - "SettingsTabGraphicsAspectRatioStretch": "An Fenster anpassen", - "SettingsTabGraphicsDeveloperOptions": "Optionen für Entwickler", - "SettingsTabGraphicsShaderDumpPath": "Grafik-Shader-Dump-Pfad:", - "SettingsTabLogging": "Logs", - "SettingsTabLoggingLogging": "Logs", - "SettingsTabLoggingEnableLoggingToFile": "Protokollierung in Datei aktivieren", - "SettingsTabLoggingEnableStubLogs": "Aktiviere Stub-Logs", - "SettingsTabLoggingEnableInfoLogs": "Aktiviere Info-Logs", - "SettingsTabLoggingEnableWarningLogs": "Aktiviere Warn-Logs", - "SettingsTabLoggingEnableErrorLogs": "Aktiviere Fehler-Logs", - "SettingsTabLoggingEnableTraceLogs": "Aktiviere Trace-Logs", - "SettingsTabLoggingEnableGuestLogs": "Aktiviere Gast-Logs", - "SettingsTabLoggingEnableFsAccessLogs": "Aktiviere Fs Zugriff-Logs", - "SettingsTabLoggingFsGlobalAccessLogMode": "Fs Globaler Zugriff-Log-Modus:", - "SettingsTabLoggingDeveloperOptions": "Entwickleroptionen", - "SettingsTabLoggingDeveloperOptionsNote": "ACHTUNG: Wird die Leistung reduzieren", - "SettingsTabLoggingGraphicsBackendLogLevel": "Protokollstufe des Grafik-Backends:", - "SettingsTabLoggingGraphicsBackendLogLevelNone": "Keine", - "SettingsTabLoggingGraphicsBackendLogLevelError": "Fehler", - "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "Verlangsamungen", - "SettingsTabLoggingGraphicsBackendLogLevelAll": "Alle", - "SettingsTabLoggingEnableDebugLogs": "Aktiviere Debug-Log", - "SettingsTabInput": "Eingabe", - "SettingsTabInputEnableDockedMode": "Angedockter Modus", - "SettingsTabInputDirectKeyboardAccess": "Direkter Tastaturzugriff", - "SettingsButtonSave": "Speichern", - "SettingsButtonClose": "Schließen", - "SettingsButtonOk": "OK", - "SettingsButtonCancel": "Abbrechen", - "SettingsButtonApply": "Übernehmen", - "ControllerSettingsPlayer": "Spieler", - "ControllerSettingsPlayer1": "Spieler 1", - "ControllerSettingsPlayer2": "Spieler 2", - "ControllerSettingsPlayer3": "Spieler 3", - "ControllerSettingsPlayer4": "Spieler 4", - "ControllerSettingsPlayer5": "Spieler 5", - "ControllerSettingsPlayer6": "Spieler 6", - "ControllerSettingsPlayer7": "Spieler 7", - "ControllerSettingsPlayer8": "Spieler 8", - "ControllerSettingsHandheld": "Handheld", - "ControllerSettingsInputDevice": "Eingabegerät", - "ControllerSettingsRefresh": "Aktualisieren", - "ControllerSettingsDeviceDisabled": "Deaktiviert", - "ControllerSettingsControllerType": "Controller-Typ", - "ControllerSettingsControllerTypeHandheld": "Handheld", - "ControllerSettingsControllerTypeProController": "Pro Controller", - "ControllerSettingsControllerTypeJoyConPair": "Joy-Con-Paar", - "ControllerSettingsControllerTypeJoyConLeft": "Linker Joy-Con", - "ControllerSettingsControllerTypeJoyConRight": "Rechter Joy-Con", - "ControllerSettingsProfile": "Profil", - "ControllerSettingsProfileDefault": "Standard", - "ControllerSettingsLoad": "Laden", - "ControllerSettingsAdd": "Hinzufügen", - "ControllerSettingsRemove": "Entfernen", - "ControllerSettingsButtons": "Aktionstasten", - "ControllerSettingsButtonA": "A", - "ControllerSettingsButtonB": "B", - "ControllerSettingsButtonX": "X", - "ControllerSettingsButtonY": "Y", - "ControllerSettingsButtonPlus": "+", - "ControllerSettingsButtonMinus": "-", - "ControllerSettingsDPad": "Steuerkreuz", - "ControllerSettingsDPadUp": "Hoch", - "ControllerSettingsDPadDown": "Runter", - "ControllerSettingsDPadLeft": "Links", - "ControllerSettingsDPadRight": "Rechts", - "ControllerSettingsStickButton": "Button", - "ControllerSettingsStickUp": "Hoch", - "ControllerSettingsStickDown": "Runter", - "ControllerSettingsStickLeft": "Links", - "ControllerSettingsStickRight": "Rechts", - "ControllerSettingsStickStick": "Stick", - "ControllerSettingsStickInvertXAxis": "X-Achse invertieren", - "ControllerSettingsStickInvertYAxis": "Y-Achse invertieren", - "ControllerSettingsStickDeadzone": "Deadzone:", - "ControllerSettingsLStick": "Linker Analogstick", - "ControllerSettingsRStick": "Rechter Analogstick", - "ControllerSettingsTriggersLeft": "Linker Trigger", - "ControllerSettingsTriggersRight": "Rechter Trigger", - "ControllerSettingsTriggersButtonsLeft": "Linke Schultertaste", - "ControllerSettingsTriggersButtonsRight": "Rechte Schultertaste", - "ControllerSettingsTriggers": "Trigger", - "ControllerSettingsTriggerL": "L", - "ControllerSettingsTriggerR": "R", - "ControllerSettingsTriggerZL": "ZL", - "ControllerSettingsTriggerZR": "ZR", - "ControllerSettingsLeftSL": "SL", - "ControllerSettingsLeftSR": "SR", - "ControllerSettingsRightSL": "SL", - "ControllerSettingsRightSR": "SR", - "ControllerSettingsExtraButtonsLeft": "Linke Aktionstasten", - "ControllerSettingsExtraButtonsRight": "Rechte Aktionstasten", - "ControllerSettingsMisc": "Verschiedenes", - "ControllerSettingsTriggerThreshold": "Empfindlichkeit:", - "ControllerSettingsMotion": "Bewegung", - "ControllerSettingsMotionUseCemuhookCompatibleMotion": "CemuHook kompatible Bewegungssteuerung", - "ControllerSettingsMotionControllerSlot": "Controller-Slot:", - "ControllerSettingsMotionMirrorInput": "Eingabe spiegeln", - "ControllerSettingsMotionRightJoyConSlot": "Rechter Joy-Con-Slot:", - "ControllerSettingsMotionServerHost": "Server Host:", - "ControllerSettingsMotionGyroSensitivity": "Gyro-Empfindlichkeit:", - "ControllerSettingsMotionGyroDeadzone": "Gyro-Deadzone:", - "ControllerSettingsSave": "Speichern", - "ControllerSettingsClose": "Schließen", - "KeyUnknown": "Unbekannt", - "KeyShiftLeft": "Shift Left", - "KeyShiftRight": "Shift Right", - "KeyControlLeft": "Ctrl Left", - "KeyMacControlLeft": "⌃ Left", - "KeyControlRight": "Ctrl Right", - "KeyMacControlRight": "⌃ Right", - "KeyAltLeft": "Alt Left", - "KeyMacAltLeft": "⌥ Left", - "KeyAltRight": "Alt Right", - "KeyMacAltRight": "⌥ Right", - "KeyWinLeft": "⊞ Left", - "KeyMacWinLeft": "⌘ Left", - "KeyWinRight": "⊞ Right", - "KeyMacWinRight": "⌘ Right", - "KeyMenu": "Menu", - "KeyUp": "Up", - "KeyDown": "Down", - "KeyLeft": "Left", - "KeyRight": "Right", - "KeyEnter": "Enter", - "KeyEscape": "Escape", - "KeySpace": "Space", - "KeyTab": "Tab", - "KeyBackSpace": "Backspace", - "KeyInsert": "Insert", - "KeyDelete": "Delete", - "KeyPageUp": "Page Up", - "KeyPageDown": "Page Down", - "KeyHome": "Home", - "KeyEnd": "End", - "KeyCapsLock": "Caps Lock", - "KeyScrollLock": "Scroll Lock", - "KeyPrintScreen": "Print Screen", - "KeyPause": "Pause", - "KeyNumLock": "Num Lock", - "KeyClear": "Clear", - "KeyKeypad0": "Keypad 0", - "KeyKeypad1": "Keypad 1", - "KeyKeypad2": "Keypad 2", - "KeyKeypad3": "Keypad 3", - "KeyKeypad4": "Keypad 4", - "KeyKeypad5": "Keypad 5", - "KeyKeypad6": "Keypad 6", - "KeyKeypad7": "Keypad 7", - "KeyKeypad8": "Keypad 8", - "KeyKeypad9": "Keypad 9", - "KeyKeypadDivide": "Keypad Divide", - "KeyKeypadMultiply": "Keypad Multiply", - "KeyKeypadSubtract": "Keypad Subtract", - "KeyKeypadAdd": "Keypad Add", - "KeyKeypadDecimal": "Keypad Decimal", - "KeyKeypadEnter": "Keypad Enter", - "KeyNumber0": "0", - "KeyNumber1": "1", - "KeyNumber2": "2", - "KeyNumber3": "3", - "KeyNumber4": "4", - "KeyNumber5": "5", - "KeyNumber6": "6", - "KeyNumber7": "7", - "KeyNumber8": "8", - "KeyNumber9": "9", - "KeyTilde": "~", - "KeyGrave": "`", - "KeyMinus": "-", - "KeyPlus": "+", - "KeyBracketLeft": "[", - "KeyBracketRight": "]", - "KeySemicolon": ";", - "KeyQuote": "\"", - "KeyComma": ",", - "KeyPeriod": ".", - "KeySlash": "/", - "KeyBackSlash": "\\", - "KeyUnbound": "Unbound", - "GamepadLeftStick": "L Stick Button", - "GamepadRightStick": "R Stick Button", - "GamepadLeftShoulder": "Left Shoulder", - "GamepadRightShoulder": "Right Shoulder", - "GamepadLeftTrigger": "Left Trigger", - "GamepadRightTrigger": "Right Trigger", - "GamepadDpadUp": "Up", - "GamepadDpadDown": "Down", - "GamepadDpadLeft": "Left", - "GamepadDpadRight": "Right", - "GamepadMinus": "-", - "GamepadPlus": "+", - "GamepadGuide": "Guide", - "GamepadMisc1": "Misc", - "GamepadPaddle1": "Paddle 1", - "GamepadPaddle2": "Paddle 2", - "GamepadPaddle3": "Paddle 3", - "GamepadPaddle4": "Paddle 4", - "GamepadTouchpad": "Touchpad", - "GamepadSingleLeftTrigger0": "Left Trigger 0", - "GamepadSingleRightTrigger0": "Right Trigger 0", - "GamepadSingleLeftTrigger1": "Left Trigger 1", - "GamepadSingleRightTrigger1": "Right Trigger 1", - "StickLeft": "Left Stick", - "StickRight": "Right Stick", - "UserProfilesSelectedUserProfile": "Ausgewähltes Profil:", - "UserProfilesSaveProfileName": "Profilname speichern", - "UserProfilesChangeProfileImage": "Profilbild ändern", - "UserProfilesAvailableUserProfiles": "Verfügbare Profile:", - "UserProfilesAddNewProfile": "Neues Profil", - "UserProfilesDelete": "Löschen", - "UserProfilesClose": "Schließen", - "ProfileNameSelectionWatermark": "Wähle einen Spitznamen", - "ProfileImageSelectionTitle": "Auswahl des Profilbildes", - "ProfileImageSelectionHeader": "Wähle ein Profilbild aus", - "ProfileImageSelectionNote": "Es kann ein eigenes Profilbild importiert werden oder ein Avatar aus der System-Firmware", - "ProfileImageSelectionImportImage": "Bilddatei importieren", - "ProfileImageSelectionSelectAvatar": "Firmware-Avatar auswählen", - "InputDialogTitle": "Eingabe-Dialog", - "InputDialogOk": "OK", - "InputDialogCancel": "Abbrechen", - "InputDialogAddNewProfileTitle": "Wähle den Profilnamen", - "InputDialogAddNewProfileHeader": "Bitte gebe einen Profilnamen ein", - "InputDialogAddNewProfileSubtext": "(Maximale Länge: {0})", - "AvatarChoose": "Bestätigen", - "AvatarSetBackgroundColor": "Hintergrundfarbe auswählen", - "AvatarClose": "Schließen", - "ControllerSettingsLoadProfileToolTip": "Lädt ein Profil", - "ControllerSettingsAddProfileToolTip": "Fügt ein Profil hinzu", - "ControllerSettingsRemoveProfileToolTip": "Entfernt ein Profil", - "ControllerSettingsSaveProfileToolTip": "Speichert ein Profil", - "MenuBarFileToolsTakeScreenshot": "Screenshot aufnehmen", - "MenuBarFileToolsHideUi": "Oberfläche ausblenden", - "GameListContextMenuRunApplication": "Anwendung ausführen", - "GameListContextMenuToggleFavorite": "Als Favoriten hinzufügen/entfernen", - "GameListContextMenuToggleFavoriteToolTip": "Aktiviert den Favoriten-Status des Spiels", - "SettingsTabGeneralTheme": "Design:", - "SettingsTabGeneralThemeDark": "Dunkel", - "SettingsTabGeneralThemeLight": "Hell", - "ControllerSettingsConfigureGeneral": "Konfigurieren", - "ControllerSettingsRumble": "Vibration", - "ControllerSettingsRumbleStrongMultiplier": "Starker Vibrations-Multiplikator", - "ControllerSettingsRumbleWeakMultiplier": "Schwacher Vibrations-Multiplikator", - "DialogMessageSaveNotAvailableMessage": "Es existieren keine Speicherdaten für {0} [{1:x16}]", - "DialogMessageSaveNotAvailableCreateSaveMessage": "Sollen Speicherdaten für dieses Spiel erstellt werden?", - "DialogConfirmationTitle": "Ryujinx - Bestätigung", - "DialogUpdaterTitle": "Ryujinx - Updater", - "DialogErrorTitle": "Ryujinx - Fehler", - "DialogWarningTitle": "Ryujinx - Warnung", - "DialogExitTitle": "Ryujinx - Beenden", - "DialogErrorMessage": "Ein Fehler ist aufgetreten", - "DialogExitMessage": "Ryujinx wirklich schließen?", - "DialogExitSubMessage": "Alle nicht gespeicherten Daten gehen verloren!", - "DialogMessageCreateSaveErrorMessage": "Es ist ein Fehler bei der Erstellung der angegebenen Speicherdaten aufgetreten: {0}", - "DialogMessageFindSaveErrorMessage": "Es ist ein Fehler beim Suchen der angegebenen Speicherdaten aufgetreten: {0}", - "FolderDialogExtractTitle": "Wähle den Ordner, in welchen die Dateien entpackt werden sollen", - "DialogNcaExtractionMessage": "Extrahiert {0} abschnitt von {1}...", - "DialogNcaExtractionTitle": "Ryujinx - NCA-Abschnitt-Extraktor", - "DialogNcaExtractionMainNcaNotFoundErrorMessage": "Extraktion fehlgeschlagen. Der Hauptheader der NCA war in der ausgewählten Datei nicht vorhanden.", - "DialogNcaExtractionCheckLogErrorMessage": "Extraktion fehlgeschlagen. Überprüfe die Logs für weitere Informationen.", - "DialogNcaExtractionSuccessMessage": "Extraktion erfolgreich abgeschlossen.", - "DialogUpdaterConvertFailedMessage": "Die Konvertierung der aktuellen Ryujinx-Version ist fehlgeschlagen.", - "DialogUpdaterCancelUpdateMessage": "Update wird abgebrochen!", - "DialogUpdaterAlreadyOnLatestVersionMessage": "Es wird bereits die aktuellste Version von Ryujinx benutzt", - "DialogUpdaterFailedToGetVersionMessage": "Beim Versuch, Veröffentlichungs-Info von GitHub Release zu erhalten, ist ein Fehler aufgetreten. Dies kann aufgrund einer neuen Veröffentlichung, die gerade von GitHub Actions kompiliert wird, verursacht werden.", - "DialogUpdaterConvertFailedGithubMessage": "Fehler beim Konvertieren der erhaltenen Ryujinx-Version von GitHub Release.", - "DialogUpdaterDownloadingMessage": "Update wird heruntergeladen...", - "DialogUpdaterExtractionMessage": "Update wird entpackt...", - "DialogUpdaterRenamingMessage": "Update wird umbenannt...", - "DialogUpdaterAddingFilesMessage": "Update wird hinzugefügt...", - "DialogUpdaterCompleteMessage": "Update abgeschlossen!", - "DialogUpdaterRestartMessage": "Ryujinx jetzt neu starten?", - "DialogUpdaterNoInternetMessage": "Es besteht keine Verbindung mit dem Internet!", - "DialogUpdaterNoInternetSubMessage": "Bitte vergewissern, dass eine funktionierende Internetverbindung existiert!", - "DialogUpdaterDirtyBuildMessage": "Inoffizielle Versionen von Ryujinx können nicht aktualisiert werden", - "DialogUpdaterDirtyBuildSubMessage": "Lade Ryujinx bitte von hier herunter, um eine unterstützte Version zu erhalten: https://ryujinx.org/", - "DialogRestartRequiredMessage": "Neustart erforderlich", - "DialogThemeRestartMessage": "Das Design wurde gespeichert. Ein Neustart ist erforderlich, um das Design anzuwenden.", - "DialogThemeRestartSubMessage": "Jetzt neu starten?", - "DialogFirmwareInstallEmbeddedMessage": "Die in diesem Spiel enthaltene Firmware installieren? (Firmware {0})", - "DialogFirmwareInstallEmbeddedSuccessMessage": "Es wurde keine installierte Firmware gefunden, aber Ryujinx konnte die Firmware {0} aus dem bereitgestellten Spiel installieren.\nRyujinx wird nun gestartet.", - "DialogFirmwareNoFirmwareInstalledMessage": "Keine Firmware installiert", - "DialogFirmwareInstalledMessage": "Firmware {0} wurde installiert", - "DialogInstallFileTypesSuccessMessage": "Dateitypen erfolgreich installiert!", - "DialogInstallFileTypesErrorMessage": "Dateitypen konnten nicht installiert werden.", - "DialogUninstallFileTypesSuccessMessage": "Dateitypen erfolgreich deinstalliert!", - "DialogUninstallFileTypesErrorMessage": "Deinstallation der Dateitypen fehlgeschlagen.", - "DialogOpenSettingsWindowLabel": "Fenster-Einstellungen öffnen", - "DialogControllerAppletTitle": "Controller-Applet", - "DialogMessageDialogErrorExceptionMessage": "Fehler bei der Anzeige des Meldungs-Dialogs: {0}", - "DialogSoftwareKeyboardErrorExceptionMessage": "Fehler bei der Anzeige der Software-Tastatur: {0}", - "DialogErrorAppletErrorExceptionMessage": "Fehler beim Anzeigen des ErrorApplet-Dialogs: {0}", - "DialogUserErrorDialogMessage": "{0}: {1}", - "DialogUserErrorDialogInfoMessage": "\nWeitere Informationen zur Behebung dieses Fehlers können in unserem Setup-Guide gefunden werden.", - "DialogUserErrorDialogTitle": "Ryujinx Fehler ({0})", - "DialogAmiiboApiTitle": "Amiibo-API", - "DialogAmiiboApiFailFetchMessage": "Beim Abrufen von Informationen aus der API ist ein Fehler aufgetreten.", - "DialogAmiiboApiConnectErrorMessage": "Verbindung zum Amiibo API Server kann nicht hergestellt werden. Der Dienst ist möglicherweise nicht verfügbar oder es existiert keine Internetverbindung.", - "DialogProfileInvalidProfileErrorMessage": "Das Profil {0} ist mit dem aktuellen Eingabekonfigurationssystem nicht kompatibel.", - "DialogProfileDefaultProfileOverwriteErrorMessage": "Das Standardprofil kann nicht überschrieben werden", - "DialogProfileDeleteProfileTitle": "Profil löschen", - "DialogProfileDeleteProfileMessage": "Diese Aktion kann nicht rückgängig gemacht werden. Wirklich fortfahren?", - "DialogWarning": "Warnung", - "DialogPPTCDeletionMessage": "Du bist dabei den PPTC für das folgende Spiel als ungültig zu markieren:\n\n{0}\n\nWirklich fortfahren?", - "DialogPPTCDeletionErrorMessage": "Fehler bei der Löschung des PPTC Caches bei {0}: {1}", - "DialogShaderDeletionMessage": "Du bist dabei, den Shader Cache zu löschen für :\n\n{0}\n\nWirklich fortfahren?", - "DialogShaderDeletionErrorMessage": "Es ist ein Fehler bei der Löschung des Shader Caches bei {0}: {1} aufgetreten", - "DialogRyujinxErrorMessage": "Ein Fehler ist aufgetreten", - "DialogInvalidTitleIdErrorMessage": "UI Fehler: Das ausgewählte Spiel hat keine gültige Titel-ID", - "DialogFirmwareInstallerFirmwareNotFoundErrorMessage": "Es wurde keine gültige System-Firmware gefunden in {0}.", - "DialogFirmwareInstallerFirmwareInstallTitle": "Installiere Firmware {0}", - "DialogFirmwareInstallerFirmwareInstallMessage": "Systemversion {0} wird jetzt installiert.", - "DialogFirmwareInstallerFirmwareInstallSubMessage": "\n\nDies wird die aktuelle Systemversion {0} ersetzen.", - "DialogFirmwareInstallerFirmwareInstallConfirmMessage": "\n\nMöchtest du fortfahren?", - "DialogFirmwareInstallerFirmwareInstallWaitMessage": "Firmware wird installiert...", - "DialogFirmwareInstallerFirmwareInstallSuccessMessage": "Systemversion {0} wurde erfolgreich installiert.", - "DialogUserProfileDeletionWarningMessage": "Es können keine anderen Profile geöffnet werden, wenn das ausgewählte Profil gelöscht wird.", - "DialogUserProfileDeletionConfirmMessage": "Möchtest du das ausgewählte Profil löschen?", - "DialogUserProfileUnsavedChangesTitle": "Warnung - Nicht gespeicherte Änderungen", - "DialogUserProfileUnsavedChangesMessage": "Sie haben Änderungen an diesem Nutzerprofil vorgenommen, die nicht gespeichert wurden.", - "DialogUserProfileUnsavedChangesSubMessage": "Möchten Sie Ihre Änderungen wirklich verwerfen?", - "DialogControllerSettingsModifiedConfirmMessage": "Die aktuellen Controller-Einstellungen wurden aktualisiert.", - "DialogControllerSettingsModifiedConfirmSubMessage": "Controller-Einstellungen speichern?", - "DialogLoadFileErrorMessage": "{0}. Fehlerhafte Datei: {1}", - "DialogModAlreadyExistsMessage": "Mod ist bereits vorhanden", - "DialogModInvalidMessage": "Das angegebene Verzeichnis enthält keine Mods!", - "DialogModDeleteNoParentMessage": "Löschen fehlgeschlagen: Das übergeordnete Verzeichnis für den Mod \"{0}\" konnte nicht gefunden werden!", - "DialogDlcNoDlcErrorMessage": "Die angegebene Datei enthält keinen DLC für den ausgewählten Titel!", - "DialogPerformanceCheckLoggingEnabledMessage": "Es wurde die Debug Protokollierung aktiviert", - "DialogPerformanceCheckLoggingEnabledConfirmMessage": "Um eine optimale Leistung zu erzielen, wird empfohlen, die Debug Protokollierung zu deaktivieren. Debug Protokollierung jetzt deaktivieren?", - "DialogPerformanceCheckShaderDumpEnabledMessage": "Es wurde das Shader Dumping aktiviert, das nur von Entwicklern verwendet werden soll.", - "DialogPerformanceCheckShaderDumpEnabledConfirmMessage": "Für eine optimale Leistung wird empfohlen, das Shader Dumping zu deaktivieren. Shader Dumping jetzt deaktivieren?", - "DialogLoadAppGameAlreadyLoadedMessage": "Es wurde bereits ein Spiel gestartet", - "DialogLoadAppGameAlreadyLoadedSubMessage": "Bitte beende die Emulation oder schließe den Emulator, vor dem Starten eines neuen Spiels", - "DialogUpdateAddUpdateErrorMessage": "Die angegebene Datei enthält keine Updates für den ausgewählten Titel!", - "DialogSettingsBackendThreadingWarningTitle": "Warnung - Render Threading", - "DialogSettingsBackendThreadingWarningMessage": "Ryujinx muss muss neu gestartet werden, damit die Änderungen wirksam werden. Abhängig von dem Betriebssystem muss möglicherweise das Multithreading des Treibers manuell deaktiviert werden, wenn Ryujinx verwendet wird.", - "DialogModManagerDeletionWarningMessage": "Du bist dabei, diesen Mod zu lösche. {0}\n\nMöchtest du wirklich fortfahren?", - "DialogModManagerDeletionAllWarningMessage": "Du bist dabei, alle Mods für diesen Titel zu löschen.\n\nMöchtest du wirklich fortfahren?", - "SettingsTabGraphicsFeaturesOptions": "Erweiterungen", - "SettingsTabGraphicsBackendMultithreading": "Grafik-Backend Multithreading:", - "CommonAuto": "Auto", - "CommonOff": "Aus", - "CommonOn": "An", - "InputDialogYes": "Ja", - "InputDialogNo": "Nein", - "DialogProfileInvalidProfileNameErrorMessage": "Der Dateiname enthält ungültige Zeichen. Bitte erneut versuchen.", - "MenuBarOptionsPauseEmulation": "Pause", - "MenuBarOptionsResumeEmulation": "Fortsetzen", - "AboutUrlTooltipMessage": "Klicke hier, um die Ryujinx Website im Standardbrowser zu öffnen.", - "AboutDisclaimerMessage": "Ryujinx ist in keinster Weise weder mit Nintendo™, \nnoch mit deren Partnern verbunden.", - "AboutAmiiboDisclaimerMessage": "AmiiboAPI (www.amiiboapi.com) wird in unserer Amiibo \nEmulation benutzt.", - "AboutPatreonUrlTooltipMessage": "Klicke hier, um die Ryujinx Patreon Seite im Standardbrowser zu öffnen.", - "AboutGithubUrlTooltipMessage": "Klicke hier, um die Ryujinx GitHub Seite im Standardbrowser zu öffnen.", - "AboutDiscordUrlTooltipMessage": "Klicke hier, um eine Einladung zum Ryujinx Discord Server im Standardbrowser zu öffnen.", - "AboutTwitterUrlTooltipMessage": "Klicke hier, um die Ryujinx Twitter Seite im Standardbrowser zu öffnen.", - "AboutRyujinxAboutTitle": "Über:", - "AboutRyujinxAboutContent": "Ryujinx ist ein Nintendo Switch™ Emulator.\nBitte unterstütze uns auf Patreon.\nAuf Twitter oder Discord erfährst du alle Neuigkeiten.\nEntwickler, die an einer Mitarbeit interessiert sind, können auf GitHub oder Discord mehr erfahren.", - "AboutRyujinxMaintainersTitle": "Entwickelt von:", - "AboutRyujinxMaintainersContentTooltipMessage": "Klicke hier, um die Liste der Mitwirkenden im Standardbrowser zu öffnen.", - "AboutRyujinxSupprtersTitle": "Unterstützt auf Patreon von:", - "AmiiboSeriesLabel": "Amiibo-Serie", - "AmiiboCharacterLabel": "Charakter", - "AmiiboScanButtonLabel": "Einscannen", - "AmiiboOptionsShowAllLabel": "Zeige alle Amiibos", - "AmiiboOptionsUsRandomTagLabel": "Hack: Benutze zufällige Tag-UUID", - "DlcManagerTableHeadingEnabledLabel": "Aktiviert", - "DlcManagerTableHeadingTitleIdLabel": "Title-ID", - "DlcManagerTableHeadingContainerPathLabel": "Container-Pfad", - "DlcManagerTableHeadingFullPathLabel": "Vollständiger-Pfad", - "DlcManagerRemoveAllButton": "Entferne alle", - "DlcManagerEnableAllButton": "Alle aktivieren", - "DlcManagerDisableAllButton": "Alle deaktivieren", - "ModManagerDeleteAllButton": "Alle löschen", - "MenuBarOptionsChangeLanguage": "Sprache ändern", - "MenuBarShowFileTypes": "Dateitypen anzeigen", - "CommonSort": "Sortieren", - "CommonShowNames": "Spiel-Namen anzeigen", - "CommonFavorite": "Favoriten", - "OrderAscending": "Aufsteigend", - "OrderDescending": "Absteigend", - "SettingsTabGraphicsFeatures": "Erweiterungen", - "ErrorWindowTitle": "Fehler-Fenster", - "ToggleDiscordTooltip": "Zeige momentanes Spiel auf Discord", - "AddGameDirBoxTooltip": "Gibt das Spielverzeichnis an, das der Liste hinzuzufügt wird", - "AddGameDirTooltip": "Fügt ein neues Spielverzeichnis hinzu", - "RemoveGameDirTooltip": "Entfernt das ausgewähltes Spielverzeichnis", - "CustomThemeCheckTooltip": "Verwende ein eigenes Design für die Emulator-Benutzeroberfläche", - "CustomThemePathTooltip": "Gibt den Pfad zum Design für die Emulator-Benutzeroberfläche an", - "CustomThemeBrowseTooltip": "Ermöglicht die Suche nach einem benutzerdefinierten Design für die Emulator-Benutzeroberfläche", - "DockModeToggleTooltip": "Im gedockten Modus verhält sich das emulierte System wie eine Nintendo Switch im TV Modus. Dies verbessert die grafische Qualität der meisten Spiele. Umgekehrt führt die Deaktivierung dazu, dass sich das emulierte System wie eine Nintendo Switch im Handheld Modus verhält, was die Grafikqualität beeinträchtigt.\n\nKonfiguriere das Eingabegerät für Spieler 1, um im Docked Modus zu spielen; konfiguriere das Controllerprofil via der Handheld Option, wenn geplant wird den Handheld Modus zu nutzen.\n\nIm Zweifelsfall AN lassen.", - "DirectKeyboardTooltip": "Direkter Zugriff auf die Tastatur (HID). Bietet Spielen Zugriff auf Ihre Tastatur als Texteingabegerät.\n\nFunktioniert nur mit Spielen, die die Tastaturnutzung auf Switch-Hardware nativ unterstützen.\n\nAus lassen, wenn unsicher.", - "DirectMouseTooltip": "Unterstützt den direkten Mauszugriff (HID). Bietet Spielen Zugriff auf Ihre Maus als Zeigegerät.\n\nFunktioniert nur mit Spielen, die nativ die Steuerung mit der Maus auf Switch-Hardware unterstützen (nur sehr wenige).\n\nTouchscreen-Funktionalität ist möglicherweise eingeschränkt, wenn dies aktiviert ist.\n\n Aus lassen, wenn unsicher.", - "RegionTooltip": "Ändert die Systemregion", - "LanguageTooltip": "Ändert die Systemsprache", - "TimezoneTooltip": "Ändert die Systemzeitzone", - "TimeTooltip": "Ändert die Systemzeit", - "VSyncToggleTooltip": "Vertikale Synchronisierung der emulierten Konsole. Diese Option ist quasi ein Frame-Limiter für die meisten Spiele; die Deaktivierung kann dazu führen, dass Spiele mit höherer Geschwindigkeit laufen oder Ladebildschirme länger benötigen/hängen bleiben.\n\nKann beim Spielen mit einem frei wählbaren Hotkey ein- und ausgeschaltet werden (standardmäßig F1). \n\nIm Zweifelsfall AN lassen.", - "PptcToggleTooltip": "Speichert übersetzte JIT-Funktionen, sodass jene nicht jedes Mal übersetzt werden müssen, wenn das Spiel geladen wird.\n\nVerringert Stottern und die Zeit beim zweiten und den darauffolgenden Startvorgängen eines Spiels erheblich.\n\nIm Zweifelsfall AN lassen.", - "FsIntegrityToggleTooltip": "Prüft beim Startvorgang auf beschädigte Dateien und zeigt bei beschädigten Dateien einen Hash-Fehler (Hash Error) im Log an.\n\nDiese Einstellung hat keinen Einfluss auf die Leistung und hilft bei der Fehlersuche.\n\nIm Zweifelsfall AN lassen.", - "AudioBackendTooltip": "Ändert das Backend, das zum Rendern von Audio verwendet wird.\n\nSDL2 ist das bevorzugte Audio-Backend, OpenAL und SoundIO sind als Alternativen vorhanden. Dummy wird keinen Audio-Output haben.\n\nIm Zweifelsfall SDL2 auswählen.", - "MemoryManagerTooltip": "Ändert wie der Gastspeicher abgebildet wird und wie auf ihn zugegriffen wird. Beinflusst die Leistung der emulierten CPU erheblich.\n\nIm Zweifelsfall Host ungeprüft auswählen.", - "MemoryManagerSoftwareTooltip": "Verwendung einer Software-Seitentabelle für die Adressumsetzung. Höchste Genauigkeit, aber langsamste Leistung.", - "MemoryManagerHostTooltip": "Direkte Zuordnung von Speicher im Host-Adressraum. Viel schnellere JIT-Kompilierung und Ausführung.", - "MemoryManagerUnsafeTooltip": "Direkte Zuordnung des Speichers, aber keine Maskierung der Adresse innerhalb des Gastadressraums vor dem Zugriff. Schneller, aber auf Kosten der Sicherheit. Die Gastanwendung kann von überall in Ryujinx auf den Speicher zugreifen, daher sollte in diesem Modus nur Programme ausgeführt werden denen vertraut wird.", - "UseHypervisorTooltip": "Verwende Hypervisor anstelle von JIT. Verbessert die Leistung stark, falls vorhanden, kann jedoch in seinem aktuellen Zustand instabil sein.", - "DRamTooltip": "Erhöht den Arbeitsspeicher des emulierten Systems von 4 GiB auf 6 GiB.\n\nDies ist nur für Texturenpakete mit höherer Auflösung oder Mods mit 4K-Auflösung nützlich. Diese Option verbessert NICHT die Leistung.\n\nIm Zweifelsfall AUS lassen.", - "IgnoreMissingServicesTooltip": "Durch diese Option werden nicht implementierte Dienste der Switch-Firmware ignoriert. Dies kann dabei helfen, Abstürze beim Starten bestimmter Spiele zu umgehen.\n\nIm Zweifelsfall AUS lassen.", - "GraphicsBackendThreadingTooltip": "Führt Grafik-Backend Befehle auf einem zweiten Thread aus.\n\nDies beschleunigt die Shader-Kompilierung, reduziert Stottern und verbessert die Leistung auf GPU-Treibern ohne eigene Multithreading-Unterstützung. Geringfügig bessere Leistung bei Treibern mit Multithreading.\n\nIm Zweifelsfall auf AUTO stellen.", - "GalThreadingTooltip": "Führt Grafik-Backend Befehle auf einem zweiten Thread aus.\n\nDies Beschleunigt die Shader-Kompilierung, reduziert Stottern und verbessert die Leistung auf GPU-Treibern ohne eigene Multithreading-Unterstützung. Geringfügig bessere Leistung bei Treibern mit Multithreading.\n\nIm Zweifelsfall auf auf AUTO stellen.", - "ShaderCacheToggleTooltip": "Speichert einen persistenten Shader Cache, der das Stottern bei nachfolgenden Durchläufen reduziert.\n\nIm Zweifelsfall AN lassen.", - "ResolutionScaleTooltip": "Multipliziert die Rendering-Auflösung des Spiels.\n\nEinige wenige Spiele funktionieren damit nicht und sehen auch bei höherer Auflösung pixelig aus; für diese Spiele müssen Sie möglicherweise Mods finden, die Anti-Aliasing entfernen oder die interne Rendering-Auflösung erhöhen. Für die Verwendung von Letzterem sollten Sie Native wählen.\n\nSie können diese Option ändern, während ein Spiel läuft, indem Sie unten auf \"Übernehmen\" klicken; Sie können das Einstellungsfenster einfach zur Seite schieben und experimentieren, bis Sie Ihr bevorzugtes Aussehen für ein Spiel gefunden haben.\n\nDenken Sie daran, dass 4x für praktisch jedes Setup Overkill ist.", - "ResolutionScaleEntryTooltip": "Fließkomma Auflösungsskalierung, wie 1,5.\n Bei nicht ganzzahligen Werten ist die Wahrscheinlichkeit größer, dass Probleme entstehen, die auch zum Absturz führen können.", - "AnisotropyTooltip": "Stufe der Anisotropen Filterung. Auf Auto setzen, um den vom Spiel geforderten Wert zu verwenden.", - "AspectRatioTooltip": "Seitenverhältnis, das auf das Renderer-Fenster angewendet wird.\n\nÄndern Sie dies nur, wenn Sie einen Seitenverhältnis-Mod für Ihr Spiel verwenden, da sonst die Grafik gestreckt wird.\n\nLassen Sie es auf 16:9, wenn Sie unsicher sind.", - "ShaderDumpPathTooltip": "Grafik-Shader-Dump-Pfad", - "FileLogTooltip": "Speichert die Konsolenausgabe in einer Log-Datei auf der Festplatte. Hat keinen Einfluss auf die Leistung.", - "StubLogTooltip": "Ausgabe von Stub-Logs in der Konsole. Hat keinen Einfluss auf die Leistung.", - "InfoLogTooltip": "Ausgabe von Info-Logs in der Konsole. Hat keinen Einfluss auf die Leistung.", - "WarnLogTooltip": "Ausgabe von Warn-Logs in der Konsole. Hat keinen Einfluss auf die Leistung.", - "ErrorLogTooltip": "Ausgabe von Fehler-Logs in der Konsole. Hat keinen Einfluss auf die Leistung.", - "TraceLogTooltip": "Ausgabe von Trace-Log in der Konsole. Hat keinen Einfluss auf die Leistung.", - "GuestLogTooltip": "Ausgabe von Gast-Logs in der Konsole. Hat keinen Einfluss auf die Leistung.", - "FileAccessLogTooltip": "Ausgabe von FS-Zugriff-Logs in der Konsole.", - "FSAccessLogModeTooltip": "Aktiviert die Ausgabe des FS-Zugriff-Logs in der Konsole. Mögliche Modi sind 0-3", - "DeveloperOptionTooltip": "Mit Vorsicht verwenden", - "OpenGlLogLevel": "Erfordert die Aktivierung der entsprechenden Log-Level", - "DebugLogTooltip": "Ausgabe von Debug-Logs in der Konsole.\n\nVerwende diese Option nur auf ausdrückliche Anweisung von Ryujinx Entwicklern, da sie das Lesen der Protokolle erschwert und die Leistung des Emulators verschlechtert.", - "LoadApplicationFileTooltip": "Öffnet die Dateiauswahl um Datei zu laden, welche mit der Switch kompatibel ist", - "LoadApplicationFolderTooltip": "Öffnet die Dateiauswahl um ein Spiel zu laden, welches mit der Switch kompatibel ist", - "OpenRyujinxFolderTooltip": "Öffnet den Ordner, der das Ryujinx Dateisystem enthält", - "OpenRyujinxLogsTooltip": "Öffnet den Ordner, in welchem die Logs gespeichert werden", - "ExitTooltip": "Beendet Ryujinx", - "OpenSettingsTooltip": "Öffnet das Einstellungsfenster", - "OpenProfileManagerTooltip": "Öffnet das Profilverwaltungsfenster", - "StopEmulationTooltip": "Beendet die Emulation des derzeitigen Spiels und kehrt zu der Spielauswahl zurück", - "CheckUpdatesTooltip": "Sucht nach Updates für Ryujinx", - "OpenAboutTooltip": "Öffnet das 'Über Ryujinx'-Fenster", - "GridSize": "Rastergröße", - "GridSizeTooltip": "Ändert die Größe der Rasterelemente", - "SettingsTabSystemSystemLanguageBrazilianPortuguese": "Brasilianisches Portugiesisch", - "AboutRyujinxContributorsButtonHeader": "Alle Mitwirkenden anzeigen", - "SettingsTabSystemAudioVolume": "Lautstärke: ", - "AudioVolumeTooltip": "Ändert die Lautstärke", - "SettingsTabSystemEnableInternetAccess": "Gast-Internet-Zugang/LAN Modus", - "EnableInternetAccessTooltip": "Erlaubt es der emulierten Anwendung sich mit dem Internet zu verbinden.\n\nSpiele die den LAN-Modus unterstützen, ermöglichen es Ryujinx sich sowohl mit anderen Ryujinx-Systemen, als auch mit offiziellen Nintendo Switch Konsolen zu verbinden. Allerdings nur, wenn diese Option aktiviert ist und die Systeme mit demselben lokalen Netzwerk verbunden sind.\n\nDies erlaubt KEINE Verbindung zu Nintendo-Servern. Kann bei bestimmten Spielen die versuchen sich mit dem Internet zu verbinden zum Absturz führen.\n\nIm Zweifelsfall AUS lassen", - "GameListContextMenuManageCheatToolTip": "Öffnet den Cheat-Manager", - "GameListContextMenuManageCheat": "Cheats verwalten", - "GameListContextMenuManageModToolTip": "Mods verwalten", - "GameListContextMenuManageMod": "Mods verwalten", - "ControllerSettingsStickRange": "Bereich:", - "DialogStopEmulationTitle": "Ryujinx - Beende Emulation", - "DialogStopEmulationMessage": "Emulation wirklich beenden?", - "SettingsTabCpu": "CPU", - "SettingsTabAudio": "Audio", - "SettingsTabNetwork": "Netzwerk", - "SettingsTabNetworkConnection": "Netwerkverbindung", - "SettingsTabCpuCache": "CPU-Cache", - "SettingsTabCpuMemory": "CPU-Speicher", - "DialogUpdaterFlatpakNotSupportedMessage": "Bitte aktualisiere Ryujinx über FlatHub", - "UpdaterDisabledWarningTitle": "Updater deaktiviert!", - "ControllerSettingsRotate90": "Um 90° rotieren", - "IconSize": "Cover Größe", - "IconSizeTooltip": "Ändert die Größe der Spiel-Cover", - "MenuBarOptionsShowConsole": "Zeige Konsole", - "ShaderCachePurgeError": "Es ist ein Fehler beim löschen des Shader Caches aufgetreten bei {0}: {1}", - "UserErrorNoKeys": "Keys nicht gefunden", - "UserErrorNoFirmware": "Firmware nicht gefunden", - "UserErrorFirmwareParsingFailed": "Firmware-Analysierung-Fehler", - "UserErrorApplicationNotFound": "Anwendung nicht gefunden", - "UserErrorUnknown": "Unbekannter Fehler", - "UserErrorUndefined": "Undefinierter Fehler", - "UserErrorNoKeysDescription": "Ryujinx konnte deine 'prod.keys' Datei nicht finden", - "UserErrorNoFirmwareDescription": "Ryujinx konnte keine installierte Firmware finden!", - "UserErrorFirmwareParsingFailedDescription": "Ryujinx konnte die zu verfügung gestellte Firmware nicht analysieren. Ein möglicher Grund dafür sind veraltete keys.", - "UserErrorApplicationNotFoundDescription": "Ryujinx konnte keine valide Anwendung an dem gegeben Pfad finden.", - "UserErrorUnknownDescription": "Ein unbekannter Fehler ist aufgetreten!", - "UserErrorUndefinedDescription": "Ein undefinierter Fehler ist aufgetreten! Dies sollte nicht passieren. Bitte kontaktiere einen Entwickler!", - "OpenSetupGuideMessage": "Öffne den 'Setup Guide'", - "NoUpdate": "Kein Update", - "TitleUpdateVersionLabel": "Version {0} - {1}", - "RyujinxInfo": "Ryujinx - Info", - "RyujinxConfirm": "Ryujinx - Bestätigung", - "FileDialogAllTypes": "Alle Typen", - "Never": "Niemals", - "SwkbdMinCharacters": "Muss mindestens {0} Zeichen lang sein", - "SwkbdMinRangeCharacters": "Muss {0}-{1} Zeichen lang sein", - "SoftwareKeyboard": "Software-Tastatur", - "SoftwareKeyboardModeNumeric": "Darf nur 0-9 oder \".\" sein", - "SoftwareKeyboardModeAlphabet": "Keine CJK-Zeichen", - "SoftwareKeyboardModeASCII": "Nur ASCII-Text", - "ControllerAppletControllers": "Unterstützte Controller:", - "ControllerAppletPlayers": "Spieler:", - "ControllerAppletDescription": "Ihre aktuelle Konfiguration ist ungültig. Öffnen Sie die Einstellungen und konfigurieren Sie Ihre Eingaben neu.", - "ControllerAppletDocked": "Andockmodus gesetzt. Handheld-Steuerung sollte deaktiviert worden sein.", - "UpdaterRenaming": "Alte Dateien umbenennen...", - "UpdaterRenameFailed": "Der Updater konnte die folgende Datei nicht umbenennen: {0}", - "UpdaterAddingFiles": "Neue Dateien hinzufügen...", - "UpdaterExtracting": "Update extrahieren...", - "UpdaterDownloading": "Update herunterladen...", - "Game": "Spiel", - "Docked": "Docked", - "Handheld": "Handheld", - "ConnectionError": "Verbindungsfehler.", - "AboutPageDeveloperListMore": "{0} und mehr...", - "ApiError": "API Fehler.", - "LoadingHeading": "{0} wird gestartet", - "CompilingPPTC": "PTC wird kompiliert", - "CompilingShaders": "Shader werden kompiliert", - "AllKeyboards": "Alle Tastaturen", - "OpenFileDialogTitle": "Wähle eine unterstützte Datei", - "OpenFolderDialogTitle": "Wähle einen Ordner mit einem entpackten Spiel", - "AllSupportedFormats": "Alle unterstützten Formate", - "RyujinxUpdater": "Ryujinx - Updater", - "SettingsTabHotkeys": "Tastatur-Hotkeys", - "SettingsTabHotkeysHotkeys": "Tastatur-Hotkeys", - "SettingsTabHotkeysToggleVsyncHotkey": "VSync:", - "SettingsTabHotkeysScreenshotHotkey": "Screenshot:", - "SettingsTabHotkeysShowUiHotkey": "Zeige UI:", - "SettingsTabHotkeysPauseHotkey": "Pausieren:", - "SettingsTabHotkeysToggleMuteHotkey": "Stummschalten:", - "ControllerMotionTitle": "Bewegungssteuerung - Einstellungen", - "ControllerRumbleTitle": "Vibration - Einstellungen", - "SettingsSelectThemeFileDialogTitle": "Wähle ein Design für die Emulator-Benutzeroberfläche", - "SettingsXamlThemeFile": "Xaml Design-Datei", - "AvatarWindowTitle": "Profile verwalten - Avatar", - "Amiibo": "Amiibo", - "Unknown": "Unbekannt", - "Usage": "Nutzung", - "Writable": "Beschreibbar", - "SelectDlcDialogTitle": "DLC-Dateien auswählen", - "SelectUpdateDialogTitle": "Update-Datei auswählen", - "SelectModDialogTitle": "Mod-Ordner auswählen", - "UserProfileWindowTitle": "Benutzerprofile verwalten", - "CheatWindowTitle": "Spiel-Cheats verwalten", - "DlcWindowTitle": "Spiel-DLC verwalten", - "ModWindowTitle": "Manage Mods for {0} ({1})", - "UpdateWindowTitle": "Spiel-Updates verwalten", - "CheatWindowHeading": "Cheats verfügbar für {0} [{1}]", - "BuildId": "BuildId:", - "DlcWindowHeading": "DLC verfügbar für {0} [{1}]", - "ModWindowHeading": "{0} Mod(s)", - "UserProfilesEditProfile": "Profil bearbeiten", - "Cancel": "Abbrechen", - "Save": "Speichern", - "Discard": "Verwerfen", - "Paused": "Pausiert", - "UserProfilesSetProfileImage": "Profilbild einrichten", - "UserProfileEmptyNameError": "Name ist erforderlich", - "UserProfileNoImageError": "Bitte ein Profilbild auswählen", - "GameUpdateWindowHeading": "Update verfügbar für {0} [{1}]", - "SettingsTabHotkeysResScaleUpHotkey": "Auflösung erhöhen:", - "SettingsTabHotkeysResScaleDownHotkey": "Auflösung verringern:", - "UserProfilesName": "Name:", - "UserProfilesUserId": "Benutzer-ID:", - "SettingsTabGraphicsBackend": "Grafik-Backend:", - "SettingsTabGraphicsBackendTooltip": "Wählen Sie das Grafik-Backend, das im Emulator verwendet werden soll.\n\nVulkan ist insgesamt besser für alle modernen Grafikkarten geeignet, sofern deren Treiber auf dem neuesten Stand sind. Vulkan bietet auch eine schnellere Shader-Kompilierung (weniger Stottern) auf allen GPU-Anbietern.\n\nOpenGL kann auf alten Nvidia-GPUs, alten AMD-GPUs unter Linux oder auf GPUs mit geringerem VRAM bessere Ergebnisse erzielen, obwohl die Shader-Kompilierung stärker stottert.\n\nSetzen Sie auf Vulkan, wenn Sie unsicher sind. Stellen Sie OpenGL ein, wenn Ihr Grafikprozessor selbst mit den neuesten Grafiktreibern Vulkan nicht unterstützt.", - "SettingsEnableTextureRecompression": "Textur-Rekompression", - "SettingsEnableTextureRecompressionTooltip": "Komprimiert ASTC-Texturen, um die VRAM-Nutzung zu reduzieren.\n\nZu den Spielen, die dieses Texturformat verwenden, gehören Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder und The Legend of Zelda: Tears of the Kingdom.\n\nGrafikkarten mit 4GiB VRAM oder weniger werden beim Ausführen dieser Spiele wahrscheinlich irgendwann abstürzen.\n\nAktivieren Sie diese Option nur, wenn Ihnen bei den oben genannten Spielen der VRAM ausgeht. Lassen Sie es aus, wenn Sie unsicher sind.", - "SettingsTabGraphicsPreferredGpu": "Bevorzugte GPU:", - "SettingsTabGraphicsPreferredGpuTooltip": "Wähle die Grafikkarte aus, die mit dem Vulkan Grafik-Backend verwendet werden soll.\n\nDies hat keinen Einfluss auf die GPU die OpenGL verwendet.\n\nIm Zweifelsfall die als \"dGPU\" gekennzeichnete GPU auswählen. Diese Einstellung unberührt lassen, wenn keine zur Auswahl steht.", - "SettingsAppRequiredRestartMessage": "Ein Neustart von Ryujinx ist erforderlich", - "SettingsGpuBackendRestartMessage": "Das Grafik-Backend oder die Grafikkarteneinstellungen wurden geändert. Ein Neustart ist erforderlich um diese Einstellungen anzuwenden.", - "SettingsGpuBackendRestartSubMessage": "Ryujinx jetzt neu starten?", - "RyujinxUpdaterMessage": "Möchtest du Ryujinx auf die neueste Version aktualisieren?", - "SettingsTabHotkeysVolumeUpHotkey": "Lautstärke erhöhen:", - "SettingsTabHotkeysVolumeDownHotkey": "Lautstärke verringern:", - "SettingsEnableMacroHLE": "HLE Makros aktivieren", - "SettingsEnableMacroHLETooltip": "High-Level-Emulation von GPU-Makrocode.\n\nVerbessert die Leistung, kann aber in einigen Spielen zu Grafikfehlern führen.\n\nBei Unsicherheit AKTIVIEREN.", - "SettingsEnableColorSpacePassthrough": "Farbraum Passthrough", - "SettingsEnableColorSpacePassthroughTooltip": "Weist das Vulkan-Backend an, Farbinformationen ohne Angabe eines Farbraums weiterzuleiten. Für Benutzer mit Wide-Gamut-Displays kann dies zu lebendigeren Farben führen, allerdings auf Kosten der Farbkorrektheit.", - "VolumeShort": "Vol", - "UserProfilesManageSaves": "Speicherstände verwalten", - "DeleteUserSave": "Möchtest du den Spielerstand für dieses Spiel löschen?", - "IrreversibleActionNote": "Diese Option kann nicht rückgängig gemacht werden.", - "SaveManagerHeading": "Spielstände für {0} verwalten", - "SaveManagerTitle": "Speicherdaten Manager", - "Name": "Name", - "Size": "Größe", - "Search": "Suche", - "UserProfilesRecoverLostAccounts": "Konto wiederherstellen", - "Recover": "Wiederherstellen", - "UserProfilesRecoverHeading": "Speicherstände wurden für die folgenden Konten gefunden", - "UserProfilesRecoverEmptyList": "Keine Profile zum Wiederherstellen", - "GraphicsAATooltip": "Wendet Anti-Aliasing auf das Rendering des Spiels an.\n\nFXAA verwischt den größten Teil des Bildes, während SMAA versucht, gezackte Kanten zu finden und sie zu glätten.\n\nEs wird nicht empfohlen, diese Option in Verbindung mit dem FSR-Skalierungsfilter zu verwenden.\n\nDiese Option kann geändert werden, während ein Spiel läuft, indem Sie unten auf \"Anwenden\" klicken; Sie können das Einstellungsfenster einfach zur Seite schieben und experimentieren, bis Sie Ihr bevorzugtes Aussehen für ein Spiel gefunden haben.\n\nLassen Sie die Option auf NONE, wenn Sie unsicher sind.", - "GraphicsAALabel": "Antialiasing:", - "GraphicsScalingFilterLabel": "Skalierungsfilter:", - "GraphicsScalingFilterTooltip": "Wählen Sie den Skalierungsfilter, der bei der Auflösungsskalierung angewendet werden soll.\n\nBilinear eignet sich gut für 3D-Spiele und ist eine sichere Standardoption.\n\nNearest wird für Pixel-Art-Spiele empfohlen.\n\nFSR 1.0 ist lediglich ein Schärfungsfilter und wird nicht für die Verwendung mit FXAA oder SMAA empfohlen.\n\nDiese Option kann geändert werden, während ein Spiel läuft, indem Sie unten auf \"Anwenden\" klicken; Sie können das Einstellungsfenster einfach zur Seite schieben und experimentieren, bis Sie Ihr bevorzugtes Aussehen für ein Spiel gefunden haben.\n\nBleiben Sie auf BILINEAR, wenn Sie unsicher sind.", - "GraphicsScalingFilterBilinear": "Bilinear", - "GraphicsScalingFilterNearest": "Nächstes", - "GraphicsScalingFilterFsr": "FSR", - "GraphicsScalingFilterLevelLabel": "Stufe", - "GraphicsScalingFilterLevelTooltip": "FSR 1.0 Schärfelevel festlegen. Höher ist schärfer.", - "SmaaLow": "SMAA Niedrig", - "SmaaMedium": "SMAA Mittel", - "SmaaHigh": "SMAA Hoch", - "SmaaUltra": "SMAA Ultra", - "UserEditorTitle": "Nutzer bearbeiten", - "UserEditorTitleCreate": "Nutzer erstellen", - "SettingsTabNetworkInterface": "Netzwerkschnittstelle:", - "NetworkInterfaceTooltip": "Die für LAN/LDN-Funktionen verwendete Netzwerkschnittstelle.\n\nIn Verbindung mit einem VPN oder XLink Kai und einem Spiel mit LAN-Unterstützung kann eine Verbindung mit demselben Netzwerk über das Internet vorgetäuscht werden.\n\nIm Zweifelsfall auf DEFAULT belassen.", - "NetworkInterfaceDefault": "Standard", - "PackagingShaders": "Verpackt Shader", - "AboutChangelogButton": "Changelog in GitHub öffnen", - "AboutChangelogButtonTooltipMessage": "Klicke hier, um das Changelog für diese Version in Ihrem Standardbrowser zu öffnen.", - "SettingsTabNetworkMultiplayer": "Mehrspieler", - "MultiplayerMode": "Modus:", - "MultiplayerModeTooltip": "Ändert den LDN-Mehrspielermodus.\n\nLdnMitm ändert die lokale drahtlose/lokale Spielfunktionalität in Spielen so, dass sie wie ein LAN funktioniert und lokale, netzwerkgleiche Verbindungen mit anderen Ryujinx-Instanzen und gehackten Nintendo Switch-Konsolen ermöglicht, auf denen das ldn_mitm-Modul installiert ist.\n\nMultiplayer erfordert, dass alle Spieler die gleiche Spielversion verwenden (d.h. Super Smash Bros. Ultimate v13.0.1 kann sich nicht mit v13.0.0 verbinden).\n\nIm Zweifelsfall auf DISABLED lassen.", - "MultiplayerModeDisabled": "Deaktiviert", - "MultiplayerModeLdnMitm": "ldn_mitm" -} diff --git a/src/Ryujinx/Assets/Locales/el_GR.json b/src/Ryujinx/Assets/Locales/el_GR.json deleted file mode 100644 index ccdf6e0e4..000000000 --- a/src/Ryujinx/Assets/Locales/el_GR.json +++ /dev/null @@ -1,780 +0,0 @@ -{ - "Language": "Ελληνικά", - "MenuBarFileOpenApplet": "Άνοιγμα Applet", - "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Άνοιγμα του Mii Editor Applet σε Αυτόνομη λειτουργία", - "SettingsTabInputDirectMouseAccess": "Άμεση Πρόσβαση Ποντικιού", - "SettingsTabSystemMemoryManagerMode": "Λειτουργία Διαχείρισης Μνήμης:", - "SettingsTabSystemMemoryManagerModeSoftware": "Λογισμικό", - "SettingsTabSystemMemoryManagerModeHost": "Υπολογιστής (γρήγορο)", - "SettingsTabSystemMemoryManagerModeHostUnchecked": "Χωρίς Ελέγχους (γρηγορότερο, μη ασφαλές)", - "SettingsTabSystemUseHypervisor": "Χρήση Hypervisor", - "MenuBarFile": "_Αρχείο", - "MenuBarFileOpenFromFile": "_Φόρτωση Αρχείου Εφαρμογής", - "MenuBarFileOpenUnpacked": "Φόρτωση Απακετάριστου _Παιχνιδιού", - "MenuBarFileOpenEmuFolder": "Άνοιγμα Φακέλου Ryujinx", - "MenuBarFileOpenLogsFolder": "Άνοιγμα Φακέλου Καταγραφής", - "MenuBarFileExit": "_Έξοδος", - "MenuBarOptions": "_Επιλογές", - "MenuBarOptionsToggleFullscreen": "Λειτουργία Πλήρους Οθόνης", - "MenuBarOptionsStartGamesInFullscreen": "Εκκίνηση Παιχνιδιών σε Πλήρη Οθόνη", - "MenuBarOptionsStopEmulation": "Διακοπή Εξομοίωσης", - "MenuBarOptionsSettings": "_Ρυθμίσεις", - "MenuBarOptionsManageUserProfiles": "Διαχείριση Προφίλ _Χρηστών", - "MenuBarActions": "_Δράσεις", - "MenuBarOptionsSimulateWakeUpMessage": "Προσομοίωση Μηνύματος Αφύπνισης", - "MenuBarActionsScanAmiibo": "Σάρωση Amiibo", - "MenuBarTools": "_Εργαλεία", - "MenuBarToolsInstallFirmware": "Εγκατάσταση Firmware", - "MenuBarFileToolsInstallFirmwareFromFile": "Εγκατάσταση Firmware από XCI ή ZIP", - "MenuBarFileToolsInstallFirmwareFromDirectory": "Εγκατάσταση Firmware από τοποθεσία", - "MenuBarToolsManageFileTypes": "Διαχείριση τύπων αρχείων", - "MenuBarToolsInstallFileTypes": "Εγκαταστήσετε τύπους αρχείων.", - "MenuBarToolsUninstallFileTypes": "Απεγκαταστήσετε τύπους αρχείων", - "MenuBarView": "_View", - "MenuBarViewWindow": "Window Size", - "MenuBarViewWindow720": "720p", - "MenuBarViewWindow1080": "1080p", - "MenuBarHelp": "_Βοήθεια", - "MenuBarHelpCheckForUpdates": "Έλεγχος για Ενημερώσεις", - "MenuBarHelpAbout": "Σχετικά με", - "MenuSearch": "Αναζήτηση...", - "GameListHeaderFavorite": "Αγαπημένο", - "GameListHeaderIcon": "Εικονίδιο", - "GameListHeaderApplication": "Όνομα", - "GameListHeaderDeveloper": "Προγραμματιστής", - "GameListHeaderVersion": "Έκδοση", - "GameListHeaderTimePlayed": "Χρόνος", - "GameListHeaderLastPlayed": "Παίχτηκε", - "GameListHeaderFileExtension": "Κατάληξη", - "GameListHeaderFileSize": "Μέγεθος Αρχείου", - "GameListHeaderPath": "Τοποθεσία", - "GameListContextMenuOpenUserSaveDirectory": "Άνοιγμα Τοποθεσίας Αποθήκευσης Χρήστη", - "GameListContextMenuOpenUserSaveDirectoryToolTip": "Ανοίγει την τοποθεσία που περιέχει την Αποθήκευση Χρήστη της εφαρμογής", - "GameListContextMenuOpenDeviceSaveDirectory": "Άνοιγμα Τοποθεσίας Συσκευής Χρήστη", - "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Ανοίγει την τοποθεσία που περιέχει την Αποθήκευση Συσκευής της εφαρμογής", - "GameListContextMenuOpenBcatSaveDirectory": "Άνοιγμα Τοποθεσίας BCAT", - "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Ανοίγει την τοποθεσία που περιέχει την Αποθήκευση BCAT της εφαρμογής", - "GameListContextMenuManageTitleUpdates": "Διαχείριση Ενημερώσεων Παιχνιδιού", - "GameListContextMenuManageTitleUpdatesToolTip": "Ανοίγει το παράθυρο διαχείρισης Ενημερώσεων Παιχνιδιού", - "GameListContextMenuManageDlc": "Διαχείριση DLC", - "GameListContextMenuManageDlcToolTip": "Ανοίγει το παράθυρο διαχείρισης DLC", - "GameListContextMenuCacheManagement": "Διαχείριση Προσωρινής Μνήμης", - "GameListContextMenuCacheManagementPurgePptc": "Εκκαθάριση Προσωρινής Μνήμης PPTC", - "GameListContextMenuCacheManagementPurgePptcToolTip": "Διαγράφει την προσωρινή μνήμη PPTC της εφαρμογής", - "GameListContextMenuCacheManagementPurgeShaderCache": "Εκκαθάριση Προσωρινής Μνήμης Shader", - "GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "Διαγράφει την προσωρινή μνήμη Shader της εφαρμογής", - "GameListContextMenuCacheManagementOpenPptcDirectory": "Άνοιγμα Τοποθεσίας PPTC", - "GameListContextMenuCacheManagementOpenPptcDirectoryToolTip": "Ανοίγει την τοποθεσία που περιέχει τη προσωρινή μνήμη PPTC της εφαρμογής", - "GameListContextMenuCacheManagementOpenShaderCacheDirectory": "Άνοιγμα τοποθεσίας προσωρινής μνήμης Shader", - "GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip": "Ανοίγει την τοποθεσία που περιέχει την προσωρινή μνήμη Shader της εφαρμογής", - "GameListContextMenuExtractData": "Εξαγωγή Δεδομένων", - "GameListContextMenuExtractDataExeFS": "ExeFS", - "GameListContextMenuExtractDataExeFSToolTip": "Εξαγωγή της ενότητας ExeFS από την τρέχουσα διαμόρφωση της εφαρμογής (συμπεριλαμβανομένου ενημερώσεων)", - "GameListContextMenuExtractDataRomFS": "RomFS", - "GameListContextMenuExtractDataRomFSToolTip": "Εξαγωγή της ενότητας RomFS από την τρέχουσα διαμόρφωση της εφαρμογής (συμπεριλαμβανομένου ενημερώσεων)", - "GameListContextMenuExtractDataLogo": "Λογότυπο", - "GameListContextMenuExtractDataLogoToolTip": "Εξαγωγή της ενότητας Logo από την τρέχουσα διαμόρφωση της εφαρμογής (συμπεριλαμβανομένου ενημερώσεων)", - "GameListContextMenuCreateShortcut": "Δημιουργία Συντόμευσης Εφαρμογής", - "GameListContextMenuCreateShortcutToolTip": "Δημιουργία συντόμευσης επιφάνειας εργασίας που ανοίγει την επιλεγμένη εφαρμογή", - "GameListContextMenuCreateShortcutToolTipMacOS": "Create a shortcut in macOS's Applications folder that launches the selected Application", - "GameListContextMenuOpenModsDirectory": "Open Mods Directory", - "GameListContextMenuOpenModsDirectoryToolTip": "Opens the directory which contains Application's Mods", - "GameListContextMenuOpenSdModsDirectory": "Open Atmosphere Mods Directory", - "GameListContextMenuOpenSdModsDirectoryToolTip": "Opens the alternative SD card Atmosphere directory which contains Application's Mods. Useful for mods that are packaged for real hardware.", - "StatusBarGamesLoaded": "{0}/{1} Φορτωμένα Παιχνίδια", - "StatusBarSystemVersion": "Έκδοση Συστήματος: {0}", - "LinuxVmMaxMapCountDialogTitle": "Εντοπίστηκε χαμηλό όριο για αντιστοιχίσεις μνήμης", - "LinuxVmMaxMapCountDialogTextPrimary": "Θα θέλατε να αυξήσετε την τιμή του vm.max_map_count σε {0}", - "LinuxVmMaxMapCountDialogTextSecondary": "Μερικά παιχνίδια μπορεί να προσπαθήσουν να δημιουργήσουν περισσότερες αντιστοιχίσεις μνήμης από αυτές που επιτρέπονται τώρα. Ο Ryujinx θα καταρρεύσει μόλις ξεπεραστεί αυτό το όριο.", - "LinuxVmMaxMapCountDialogButtonUntilRestart": "Ναι, μέχρι την επόμενη επανεκκίνηση", - "LinuxVmMaxMapCountDialogButtonPersistent": "Ναι, μόνιμα", - "LinuxVmMaxMapCountWarningTextPrimary": "Ο μέγιστος αριθμός αντιστοιχίσεων μνήμης είναι μικρότερος από τον συνιστώμενο.", - "LinuxVmMaxMapCountWarningTextSecondary": "Η τρέχουσα τιμή του vm.max_map_count ({0}) είναι χαμηλότερη από {1}. Ορισμένα παιχνίδια μπορεί να προσπαθήσουν να δημιουργήσουν περισσότερες αντιστοιχίσεις μνήμης από αυτές που επιτρέπονται τώρα. Ο Ryujinx θα συντριβεί μόλις ξεπεραστεί το όριο.\n\nΜπορεί να θέλετε είτε να αυξήσετε χειροκίνητα το όριο ή να εγκαταστήσετε το pkexec, το οποίο επιτρέπει Ryujinx να βοηθήσει με αυτό.", - "Settings": "Ρυθμίσεις", - "SettingsTabGeneral": "Εμφάνιση", - "SettingsTabGeneralGeneral": "Γενικά", - "SettingsTabGeneralEnableDiscordRichPresence": "Ενεργοποίηση Εμπλουτισμένης Παρουσίας Discord", - "SettingsTabGeneralCheckUpdatesOnLaunch": "Έλεγχος για Ενημερώσεις στην Εκκίνηση", - "SettingsTabGeneralShowConfirmExitDialog": "Εμφάνιση διαλόγου \"Επιβεβαίωση Εξόδου\".", - "SettingsTabGeneralRememberWindowState": "Remember Window Size/Position", - "SettingsTabGeneralHideCursor": "Απόκρυψη Κέρσορα:", - "SettingsTabGeneralHideCursorNever": "Ποτέ", - "SettingsTabGeneralHideCursorOnIdle": "Απόκρυψη Δρομέα στην Αδράνεια", - "SettingsTabGeneralHideCursorAlways": "Πάντα", - "SettingsTabGeneralGameDirectories": "Τοποθεσίες παιχνιδιών", - "SettingsTabGeneralAdd": "Προσθήκη", - "SettingsTabGeneralRemove": "Αφαίρεση", - "SettingsTabSystem": "Σύστημα", - "SettingsTabSystemCore": "Πυρήνας", - "SettingsTabSystemSystemRegion": "Περιοχή Συστήματος:", - "SettingsTabSystemSystemRegionJapan": "Ιαπωνία", - "SettingsTabSystemSystemRegionUSA": "ΗΠΑ", - "SettingsTabSystemSystemRegionEurope": "Ευρώπη", - "SettingsTabSystemSystemRegionAustralia": "Αυστραλία", - "SettingsTabSystemSystemRegionChina": "Κίνα", - "SettingsTabSystemSystemRegionKorea": "Κορέα", - "SettingsTabSystemSystemRegionTaiwan": "Ταϊβάν", - "SettingsTabSystemSystemLanguage": "Γλώσσα Συστήματος:", - "SettingsTabSystemSystemLanguageJapanese": "Ιαπωνικά", - "SettingsTabSystemSystemLanguageAmericanEnglish": "Αμερικάνικα Αγγλικά", - "SettingsTabSystemSystemLanguageFrench": "Γαλλικά", - "SettingsTabSystemSystemLanguageGerman": "Γερμανικά", - "SettingsTabSystemSystemLanguageItalian": "Ιταλικά", - "SettingsTabSystemSystemLanguageSpanish": "Ισπανικά", - "SettingsTabSystemSystemLanguageChinese": "Κινέζικα", - "SettingsTabSystemSystemLanguageKorean": "Κορεάτικα", - "SettingsTabSystemSystemLanguageDutch": "Ολλανδικά", - "SettingsTabSystemSystemLanguagePortuguese": "Πορτογαλικά", - "SettingsTabSystemSystemLanguageRussian": "Ρώσικα", - "SettingsTabSystemSystemLanguageTaiwanese": "Ταϊβανέζικα", - "SettingsTabSystemSystemLanguageBritishEnglish": "Βρετανικά Αγγλικά", - "SettingsTabSystemSystemLanguageCanadianFrench": "Καναδικά Γαλλικά", - "SettingsTabSystemSystemLanguageLatinAmericanSpanish": "Λατινοαμερικάνικα Ισπανικά", - "SettingsTabSystemSystemLanguageSimplifiedChinese": "Απλοποιημένα Κινέζικα", - "SettingsTabSystemSystemLanguageTraditionalChinese": "Παραδοσιακά Κινεζικά", - "SettingsTabSystemSystemTimeZone": "Ζώνη Ώρας Συστήματος:", - "SettingsTabSystemSystemTime": "Ώρα Συστήματος:", - "SettingsTabSystemEnableVsync": "Ενεργοποίηση Κατακόρυφου Συγχρονισμού", - "SettingsTabSystemEnablePptc": "Ενεργοποίηση PPTC (Profiled Persistent Translation Cache)", - "SettingsTabSystemEnableFsIntegrityChecks": "Ενεργοποίηση Ελέγχων Ακεραιότητας FS", - "SettingsTabSystemAudioBackend": "Backend Ήχου:", - "SettingsTabSystemAudioBackendDummy": "Απενεργοποιημένο", - "SettingsTabSystemAudioBackendOpenAL": "OpenAL", - "SettingsTabSystemAudioBackendSoundIO": "SoundIO", - "SettingsTabSystemAudioBackendSDL2": "SDL2", - "SettingsTabSystemHacks": "Μικροδιορθώσεις", - "SettingsTabSystemHacksNote": " (Μπορεί να προκαλέσουν αστάθεια)", - "SettingsTabSystemExpandDramSize": "Επέκταση μεγέθους DRAM στα 6GiB", - "SettingsTabSystemIgnoreMissingServices": "Αγνόηση υπηρεσιών που λείπουν", - "SettingsTabGraphics": "Γραφικά", - "SettingsTabGraphicsAPI": "API Γραφικά", - "SettingsTabGraphicsEnableShaderCache": "Ενεργοποίηση Προσωρινής Μνήμης Shader", - "SettingsTabGraphicsAnisotropicFiltering": "Ανισότροπο Φιλτράρισμα:", - "SettingsTabGraphicsAnisotropicFilteringAuto": "Αυτόματο", - "SettingsTabGraphicsAnisotropicFiltering2x": "2x", - "SettingsTabGraphicsAnisotropicFiltering4x": "4x", - "SettingsTabGraphicsAnisotropicFiltering8x": "8x", - "SettingsTabGraphicsAnisotropicFiltering16x": "16x", - "SettingsTabGraphicsResolutionScale": "Κλίμακα Ανάλυσης:", - "SettingsTabGraphicsResolutionScaleCustom": "Προσαρμοσμένο (Δεν συνιστάται)", - "SettingsTabGraphicsResolutionScaleNative": "Εγγενής (720p/1080p)", - "SettingsTabGraphicsResolutionScale2x": "2x (1440p/2160p)", - "SettingsTabGraphicsResolutionScale3x": "3x (2160p/3240p)", - "SettingsTabGraphicsResolutionScale4x": "4x (2880p/4320p) (Not recommended)", - "SettingsTabGraphicsAspectRatio": "Αναλογία Απεικόνισης:", - "SettingsTabGraphicsAspectRatio4x3": "4:3", - "SettingsTabGraphicsAspectRatio16x9": "16:9", - "SettingsTabGraphicsAspectRatio16x10": "16:10", - "SettingsTabGraphicsAspectRatio21x9": "21:9", - "SettingsTabGraphicsAspectRatio32x9": "32:9", - "SettingsTabGraphicsAspectRatioStretch": "Έκταση σε όλο το παράθυρο", - "SettingsTabGraphicsDeveloperOptions": "Επιλογές Προγραμματιστή", - "SettingsTabGraphicsShaderDumpPath": "Τοποθεσία Shaders Γραφικών:", - "SettingsTabLogging": "Καταγραφή", - "SettingsTabLoggingLogging": "Καταγραφή", - "SettingsTabLoggingEnableLoggingToFile": "Ενεργοποίηση Καταγραφής Αρχείου", - "SettingsTabLoggingEnableStubLogs": "Ενεργοποίηση Καταγραφής Stub", - "SettingsTabLoggingEnableInfoLogs": "Ενεργοποίηση Καταγραφής Πληροφοριών", - "SettingsTabLoggingEnableWarningLogs": "Ενεργοποίηση Καταγραφής Προειδοποίησης", - "SettingsTabLoggingEnableErrorLogs": "Ενεργοποίηση Καταγραφής Σφαλμάτων", - "SettingsTabLoggingEnableTraceLogs": "Ενεργοποίηση Καταγραφής Ιχνών", - "SettingsTabLoggingEnableGuestLogs": "Ενεργοποίηση Καταγραφής Επισκεπτών", - "SettingsTabLoggingEnableFsAccessLogs": "Ενεργοποίηση Καταγραφής Πρόσβασης FS", - "SettingsTabLoggingFsGlobalAccessLogMode": "Λειτουργία Καταγραφής Καθολικής Πρόσβασης FS:", - "SettingsTabLoggingDeveloperOptions": "Επιλογές Προγραμματιστή (ΠΡΟΕΙΔΟΠΟΙΗΣΗ: Η απόδοση Θα μειωθεί)", - "SettingsTabLoggingDeveloperOptionsNote": "ΠΡΟΕΙΔΟΠΟΙΗΣΗ: Θα μειώσει την απόδοση", - "SettingsTabLoggingGraphicsBackendLogLevel": "Επίπεδο Καταγραφής Διεπαφής Γραφικών:", - "SettingsTabLoggingGraphicsBackendLogLevelNone": "Κανένα", - "SettingsTabLoggingGraphicsBackendLogLevelError": "Σφάλμα", - "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "Επιβραδύνσεις", - "SettingsTabLoggingGraphicsBackendLogLevelAll": "Όλα", - "SettingsTabLoggingEnableDebugLogs": "Ενεργοποίηση Αρχείων Καταγραφής Εντοπισμού Σφαλμάτων", - "SettingsTabInput": "Χειρισμός", - "SettingsTabInputEnableDockedMode": "Ενεργοποίηση Docked Mode", - "SettingsTabInputDirectKeyboardAccess": "Άμεση Πρόσβαση στο Πληκτρολόγιο", - "SettingsButtonSave": "Αποθήκευση", - "SettingsButtonClose": "Κλείσιμο", - "SettingsButtonOk": "ΟΚ", - "SettingsButtonCancel": "Ακύρωση", - "SettingsButtonApply": "Εφαρμογή", - "ControllerSettingsPlayer": "Παίχτης", - "ControllerSettingsPlayer1": "Παίχτης 1", - "ControllerSettingsPlayer2": "Παίχτης 2", - "ControllerSettingsPlayer3": "Παίχτης 3", - "ControllerSettingsPlayer4": "Παίχτης 4", - "ControllerSettingsPlayer5": "Παίχτης 5", - "ControllerSettingsPlayer6": "Παίχτης 6", - "ControllerSettingsPlayer7": "Παίχτης 7", - "ControllerSettingsPlayer8": "Παίχτης 8", - "ControllerSettingsHandheld": "Χειροκίνητο", - "ControllerSettingsInputDevice": "Συσκευή Χειρισμού", - "ControllerSettingsRefresh": "Ανανέωση", - "ControllerSettingsDeviceDisabled": "Απενεργοποιημένο", - "ControllerSettingsControllerType": "Τύπος Χειριστηρίου", - "ControllerSettingsControllerTypeHandheld": "Φορητό", - "ControllerSettingsControllerTypeProController": "Pro Controller", - "ControllerSettingsControllerTypeJoyConPair": "Ζεύγος JoyCon", - "ControllerSettingsControllerTypeJoyConLeft": "Αριστερό JoyCon", - "ControllerSettingsControllerTypeJoyConRight": "Δεξί JoyCon", - "ControllerSettingsProfile": "Προφίλ", - "ControllerSettingsProfileDefault": "Προκαθορισμένο", - "ControllerSettingsLoad": "Φόρτωση", - "ControllerSettingsAdd": "Προσθήκη", - "ControllerSettingsRemove": "Αφαίρεση", - "ControllerSettingsButtons": "Κουμπιά", - "ControllerSettingsButtonA": "Α", - "ControllerSettingsButtonB": "B", - "ControllerSettingsButtonX": "X", - "ControllerSettingsButtonY": "Y", - "ControllerSettingsButtonPlus": "+", - "ControllerSettingsButtonMinus": "-", - "ControllerSettingsDPad": "Κατευθυντικό Pad", - "ControllerSettingsDPadUp": "Πάνω", - "ControllerSettingsDPadDown": "Κάτω", - "ControllerSettingsDPadLeft": "Αριστερά", - "ControllerSettingsDPadRight": "Δεξιά", - "ControllerSettingsStickButton": "Κουμπί", - "ControllerSettingsStickUp": "Πάνω", - "ControllerSettingsStickDown": "Κάτω", - "ControllerSettingsStickLeft": "Αριστερά", - "ControllerSettingsStickRight": "Δεξιά", - "ControllerSettingsStickStick": "Μοχλός", - "ControllerSettingsStickInvertXAxis": "Αντιστροφή Μοχλού X", - "ControllerSettingsStickInvertYAxis": "Αντιστροφή Μοχλού Y", - "ControllerSettingsStickDeadzone": "Νεκρή Ζώνη:", - "ControllerSettingsLStick": "Αριστερός Μοχλός", - "ControllerSettingsRStick": "Δεξιός Μοχλός", - "ControllerSettingsTriggersLeft": "Αριστερή Σκανδάλη", - "ControllerSettingsTriggersRight": "Δεξιά Σκανδάλη", - "ControllerSettingsTriggersButtonsLeft": "Αριστερά Κουμπιά Σκανδάλης", - "ControllerSettingsTriggersButtonsRight": "Δεξιά Κουμπιά Σκανδάλης", - "ControllerSettingsTriggers": "Σκανδάλες", - "ControllerSettingsTriggerL": "L", - "ControllerSettingsTriggerR": "R", - "ControllerSettingsTriggerZL": "ZL", - "ControllerSettingsTriggerZR": "ZR", - "ControllerSettingsLeftSL": "SL", - "ControllerSettingsLeftSR": "SR", - "ControllerSettingsRightSL": "SL", - "ControllerSettingsRightSR": "SR", - "ControllerSettingsExtraButtonsLeft": "Αριστερά Κουμπιά", - "ControllerSettingsExtraButtonsRight": "Δεξιά Κουμπιά", - "ControllerSettingsMisc": "Διάφορα", - "ControllerSettingsTriggerThreshold": "Κατώφλι Σκανδάλης:", - "ControllerSettingsMotion": "Κίνηση", - "ControllerSettingsMotionUseCemuhookCompatibleMotion": "Κίνηση συμβατή με CemuHook", - "ControllerSettingsMotionControllerSlot": "Υποδοχή Χειριστηρίου:", - "ControllerSettingsMotionMirrorInput": "Καθρεπτισμός Χειρισμού", - "ControllerSettingsMotionRightJoyConSlot": "Δεξιά Υποδοχή JoyCon:", - "ControllerSettingsMotionServerHost": "Κεντρικός Υπολογιστής Διακομιστή:", - "ControllerSettingsMotionGyroSensitivity": "Ευαισθησία Γυροσκοπίου:", - "ControllerSettingsMotionGyroDeadzone": "Νεκρή Ζώνη Γυροσκοπίου:", - "ControllerSettingsSave": "Αποθήκευση", - "ControllerSettingsClose": "Κλείσιμο", - "KeyUnknown": "Unknown", - "KeyShiftLeft": "Shift Left", - "KeyShiftRight": "Shift Right", - "KeyControlLeft": "Ctrl Left", - "KeyMacControlLeft": "⌃ Left", - "KeyControlRight": "Ctrl Right", - "KeyMacControlRight": "⌃ Right", - "KeyAltLeft": "Alt Left", - "KeyMacAltLeft": "⌥ Left", - "KeyAltRight": "Alt Right", - "KeyMacAltRight": "⌥ Right", - "KeyWinLeft": "⊞ Left", - "KeyMacWinLeft": "⌘ Left", - "KeyWinRight": "⊞ Right", - "KeyMacWinRight": "⌘ Right", - "KeyMenu": "Menu", - "KeyUp": "Up", - "KeyDown": "Down", - "KeyLeft": "Left", - "KeyRight": "Right", - "KeyEnter": "Enter", - "KeyEscape": "Escape", - "KeySpace": "Space", - "KeyTab": "Tab", - "KeyBackSpace": "Backspace", - "KeyInsert": "Insert", - "KeyDelete": "Delete", - "KeyPageUp": "Page Up", - "KeyPageDown": "Page Down", - "KeyHome": "Home", - "KeyEnd": "End", - "KeyCapsLock": "Caps Lock", - "KeyScrollLock": "Scroll Lock", - "KeyPrintScreen": "Print Screen", - "KeyPause": "Pause", - "KeyNumLock": "Num Lock", - "KeyClear": "Clear", - "KeyKeypad0": "Keypad 0", - "KeyKeypad1": "Keypad 1", - "KeyKeypad2": "Keypad 2", - "KeyKeypad3": "Keypad 3", - "KeyKeypad4": "Keypad 4", - "KeyKeypad5": "Keypad 5", - "KeyKeypad6": "Keypad 6", - "KeyKeypad7": "Keypad 7", - "KeyKeypad8": "Keypad 8", - "KeyKeypad9": "Keypad 9", - "KeyKeypadDivide": "Keypad Divide", - "KeyKeypadMultiply": "Keypad Multiply", - "KeyKeypadSubtract": "Keypad Subtract", - "KeyKeypadAdd": "Keypad Add", - "KeyKeypadDecimal": "Keypad Decimal", - "KeyKeypadEnter": "Keypad Enter", - "KeyNumber0": "0", - "KeyNumber1": "1", - "KeyNumber2": "2", - "KeyNumber3": "3", - "KeyNumber4": "4", - "KeyNumber5": "5", - "KeyNumber6": "6", - "KeyNumber7": "7", - "KeyNumber8": "8", - "KeyNumber9": "9", - "KeyTilde": "~", - "KeyGrave": "`", - "KeyMinus": "-", - "KeyPlus": "+", - "KeyBracketLeft": "[", - "KeyBracketRight": "]", - "KeySemicolon": ";", - "KeyQuote": "\"", - "KeyComma": ",", - "KeyPeriod": ".", - "KeySlash": "/", - "KeyBackSlash": "\\", - "KeyUnbound": "Unbound", - "GamepadLeftStick": "L Stick Button", - "GamepadRightStick": "R Stick Button", - "GamepadLeftShoulder": "Left Shoulder", - "GamepadRightShoulder": "Right Shoulder", - "GamepadLeftTrigger": "Left Trigger", - "GamepadRightTrigger": "Right Trigger", - "GamepadDpadUp": "Up", - "GamepadDpadDown": "Down", - "GamepadDpadLeft": "Left", - "GamepadDpadRight": "Right", - "GamepadMinus": "-", - "GamepadPlus": "+", - "GamepadGuide": "Guide", - "GamepadMisc1": "Misc", - "GamepadPaddle1": "Paddle 1", - "GamepadPaddle2": "Paddle 2", - "GamepadPaddle3": "Paddle 3", - "GamepadPaddle4": "Paddle 4", - "GamepadTouchpad": "Touchpad", - "GamepadSingleLeftTrigger0": "Left Trigger 0", - "GamepadSingleRightTrigger0": "Right Trigger 0", - "GamepadSingleLeftTrigger1": "Left Trigger 1", - "GamepadSingleRightTrigger1": "Right Trigger 1", - "StickLeft": "Left Stick", - "StickRight": "Right Stick", - "UserProfilesSelectedUserProfile": "Επιλεγμένο Προφίλ Χρήστη:", - "UserProfilesSaveProfileName": "Αποθήκευση Ονόματος Προφίλ", - "UserProfilesChangeProfileImage": "Αλλαγή Εικόνας Προφίλ", - "UserProfilesAvailableUserProfiles": "Διαθέσιμα Προφίλ Χρηστών:", - "UserProfilesAddNewProfile": "Προσθήκη Νέου Προφίλ", - "UserProfilesDelete": "Διαγράφω", - "UserProfilesClose": "Κλείσιμο", - "ProfileNameSelectionWatermark": "Επιλέξτε ψευδώνυμο", - "ProfileImageSelectionTitle": "Επιλογή Εικόνας Προφίλ", - "ProfileImageSelectionHeader": "Επιλέξτε μία Εικόνα Προφίλ", - "ProfileImageSelectionNote": "Μπορείτε να εισαγάγετε μία προσαρμοσμένη εικόνα προφίλ ή να επιλέξετε ένα avatar από το Firmware", - "ProfileImageSelectionImportImage": "Εισαγωγή Αρχείου Εικόνας", - "ProfileImageSelectionSelectAvatar": "Επιλέξτε Avatar από Firmware", - "InputDialogTitle": "Διάλογος Εισαγωγής", - "InputDialogOk": "ΟΚ", - "InputDialogCancel": "Ακύρωση", - "InputDialogAddNewProfileTitle": "Επιλογή Ονόματος Προφίλ", - "InputDialogAddNewProfileHeader": "Εισαγωγή Ονόματος Προφίλ", - "InputDialogAddNewProfileSubtext": "(Σύνολο Χαρακτήρων: {0})", - "AvatarChoose": "Επιλογή", - "AvatarSetBackgroundColor": "Ορισμός Χρώματος Φόντου", - "AvatarClose": "Κλείσιμο", - "ControllerSettingsLoadProfileToolTip": "Φόρτωση Προφίλ", - "ControllerSettingsAddProfileToolTip": "Προσθήκη Προφίλ", - "ControllerSettingsRemoveProfileToolTip": "Κατάργηση Προφίλ", - "ControllerSettingsSaveProfileToolTip": "Αποθήκευση Προφίλ", - "MenuBarFileToolsTakeScreenshot": "Λήψη Στιγμιότυπου", - "MenuBarFileToolsHideUi": "Απόκρυψη UI", - "GameListContextMenuRunApplication": "Εκτέλεση Εφαρμογής", - "GameListContextMenuToggleFavorite": "Εναλλαγή Αγαπημένου", - "GameListContextMenuToggleFavoriteToolTip": "Εναλλαγή της Κατάστασης Αγαπημένο του Παιχνιδιού", - "SettingsTabGeneralTheme": "Theme:", - "SettingsTabGeneralThemeDark": "Dark", - "SettingsTabGeneralThemeLight": "Light", - "ControllerSettingsConfigureGeneral": "Παραμέτρων", - "ControllerSettingsRumble": "Δόνηση", - "ControllerSettingsRumbleStrongMultiplier": "Ισχυρός Πολλαπλασιαστής Δόνησης", - "ControllerSettingsRumbleWeakMultiplier": "Αδύναμος Πολλαπλασιαστής Δόνησης", - "DialogMessageSaveNotAvailableMessage": "Δεν υπάρχουν αποθηκευμένα δεδομένα για το {0} [{1:x16}]", - "DialogMessageSaveNotAvailableCreateSaveMessage": "Θέλετε να αποθηκεύσετε δεδομένα για αυτό το παιχνίδι;", - "DialogConfirmationTitle": "Ryujinx - Επιβεβαίωση", - "DialogUpdaterTitle": "Ryujinx - Ενημερωτής", - "DialogErrorTitle": "Ryujinx - Σφάλμα", - "DialogWarningTitle": "Ryujinx - Προειδοποίηση", - "DialogExitTitle": "Ryujinx - Έξοδος", - "DialogErrorMessage": "Το Ryujinx αντιμετώπισε σφάλμα", - "DialogExitMessage": "Είστε βέβαιοι ότι θέλετε να κλείσετε το Ryujinx;", - "DialogExitSubMessage": "Όλα τα μη αποθηκευμένα δεδομένα θα χαθούν!", - "DialogMessageCreateSaveErrorMessage": "Σφάλμα κατά τη δημιουργία των αποθηκευμένων δεδομένων: {0}", - "DialogMessageFindSaveErrorMessage": "Σφάλμα κατά την εύρεση των αποθηκευμένων δεδομένων: {0}", - "FolderDialogExtractTitle": "Επιλέξτε τον φάκελο στον οποίο θέλετε να εξαγάγετε", - "DialogNcaExtractionMessage": "Εξαγωγή ενότητας {0} από {1}...", - "DialogNcaExtractionTitle": "Ryujinx - NCA Εξαγωγέας Τμημάτων", - "DialogNcaExtractionMainNcaNotFoundErrorMessage": "Αποτυχία εξαγωγής. Η κύρια NCA δεν υπήρχε στο επιλεγμένο αρχείο.", - "DialogNcaExtractionCheckLogErrorMessage": "Αποτυχία εξαγωγής. Διαβάστε το αρχείο καταγραφής για περισσότερες πληροφορίες.", - "DialogNcaExtractionSuccessMessage": "Η εξαγωγή ολοκληρώθηκε με επιτυχία.", - "DialogUpdaterConvertFailedMessage": "Αποτυχία μετατροπής της τρέχουσας έκδοσης Ryujinx.", - "DialogUpdaterCancelUpdateMessage": "Ακύρωση Ενημέρωσης!", - "DialogUpdaterAlreadyOnLatestVersionMessage": "Χρησιμοποιείτε ήδη την πιο ενημερωμένη έκδοση του Ryujinx!", - "DialogUpdaterFailedToGetVersionMessage": "Προέκυψε ένα σφάλμα στη λήψη πληροφοριών έκδοσης από τα GitHub Releases. Αυτό δύναται να συμβεί αν μία έκδοση χτίζεται αυτή τη στιγμή στα GitHub Actions. Παρακαλούμε προσπαθήστε αργότερα.", - "DialogUpdaterConvertFailedGithubMessage": "Αποτυχία μετατροπής της ληφθείσας έκδοσης Ryujinx από την έκδοση GitHub.", - "DialogUpdaterDownloadingMessage": "Λήψη Ενημέρωσης...", - "DialogUpdaterExtractionMessage": "Εξαγωγή Ενημέρωσης...", - "DialogUpdaterRenamingMessage": "Μετονομασία Ενημέρωσης...", - "DialogUpdaterAddingFilesMessage": "Προσθήκη Νέας Ενημέρωσης...", - "DialogUpdaterCompleteMessage": "Η Ενημέρωση Ολοκληρώθηκε!", - "DialogUpdaterRestartMessage": "Θέλετε να επανεκκινήσετε το Ryujinx τώρα;", - "DialogUpdaterNoInternetMessage": "Δεν είστε συνδεδεμένοι στο Διαδίκτυο!", - "DialogUpdaterNoInternetSubMessage": "Επαληθεύστε ότι έχετε σύνδεση στο Διαδίκτυο που λειτουργεί!", - "DialogUpdaterDirtyBuildMessage": "Δεν μπορείτε να ενημερώσετε μία Πρόχειρη Έκδοση του Ryujinx!", - "DialogUpdaterDirtyBuildSubMessage": "Κάντε λήψη του Ryujinx στη διεύθυνση https://ryujinx.org/ εάν αναζητάτε μία υποστηριζόμενη έκδοση.", - "DialogRestartRequiredMessage": "Απαιτείται Επανεκκίνηση", - "DialogThemeRestartMessage": "Το θέμα έχει αποθηκευτεί. Απαιτείται επανεκκίνηση για την εφαρμογή του θέματος.", - "DialogThemeRestartSubMessage": "Θέλετε να κάνετε επανεκκίνηση", - "DialogFirmwareInstallEmbeddedMessage": "Θα θέλατε να εγκαταστήσετε το Firmware που είναι ενσωματωμένο σε αυτό το παιχνίδι; (Firmware {0})", - "DialogFirmwareInstallEmbeddedSuccessMessage": "No installed firmware was found but Ryujinx was able to install firmware {0} from the provided game.\nThe emulator will now start.", - "DialogFirmwareNoFirmwareInstalledMessage": "Δεν έχει εγκατασταθεί Firmware", - "DialogFirmwareInstalledMessage": "Το Firmware {0} εγκαταστάθηκε", - "DialogInstallFileTypesSuccessMessage": "Επιτυχής εγκατάσταση τύπων αρχείων!", - "DialogInstallFileTypesErrorMessage": "Απέτυχε η εγκατάσταση τύπων αρχείων.", - "DialogUninstallFileTypesSuccessMessage": "Επιτυχής απεγκατάσταση τύπων αρχείων!", - "DialogUninstallFileTypesErrorMessage": "Αποτυχία απεγκατάστασης τύπων αρχείων.", - "DialogOpenSettingsWindowLabel": "Άνοιγμα Παραθύρου Ρυθμίσεων", - "DialogControllerAppletTitle": "Applet Χειρισμού", - "DialogMessageDialogErrorExceptionMessage": "Σφάλμα εμφάνισης του διαλόγου Μηνυμάτων: {0}", - "DialogSoftwareKeyboardErrorExceptionMessage": "Σφάλμα εμφάνισης Λογισμικού Πληκτρολογίου: {0}", - "DialogErrorAppletErrorExceptionMessage": "Σφάλμα εμφάνισης του διαλόγου ErrorApplet: {0}", - "DialogUserErrorDialogMessage": "{0}: {1}", - "DialogUserErrorDialogInfoMessage": "\nΓια πληροφορίες σχετικά με τον τρόπο διόρθωσης του σφάλματος, ακολουθήστε τον Οδηγό Εγκατάστασης.", - "DialogUserErrorDialogTitle": "Σφάλμα Ryujinx ({0})", - "DialogAmiiboApiTitle": "API για Amiibo.", - "DialogAmiiboApiFailFetchMessage": "Παρουσιάστηκε σφάλμα κατά την ανάκτηση πληροφοριών από το API.", - "DialogAmiiboApiConnectErrorMessage": "Δεν είναι δυνατή η σύνδεση με τον διακομιστή Amiibo API. Η υπηρεσία μπορεί να είναι εκτός λειτουργίας ή μπορεί να χρειαστεί να επαληθεύσετε ότι έχετε ενεργή σύνδεσή στο Διαδίκτυο.", - "DialogProfileInvalidProfileErrorMessage": "Το προφίλ {0} δεν είναι συμβατό με το τρέχον σύστημα χειρισμού.", - "DialogProfileDefaultProfileOverwriteErrorMessage": "Το προεπιλεγμένο προφίλ δεν μπορεί να αντικατασταθεί", - "DialogProfileDeleteProfileTitle": "Διαγραφή Προφίλ", - "DialogProfileDeleteProfileMessage": "Αυτή η ενέργεια είναι μη αναστρέψιμη, είστε βέβαιοι ότι θέλετε να συνεχίσετε;", - "DialogWarning": "Προειδοποίηση", - "DialogPPTCDeletionMessage": "Πρόκειται να διαγράψετε την προσωρινή μνήμη PPTC για :\n\n{0}\n\nΕίστε βέβαιοι ότι θέλετε να συνεχίσετε;", - "DialogPPTCDeletionErrorMessage": "Σφάλμα κατά την εκκαθάριση προσωρινής μνήμης PPTC στο {0}: {1}", - "DialogShaderDeletionMessage": "Πρόκειται να διαγράψετε την προσωρινή μνήμη Shader για :\n\n{0}\n\nΕίστε βέβαιοι ότι θέλετε να συνεχίσετε;", - "DialogShaderDeletionErrorMessage": "Σφάλμα κατά την εκκαθάριση προσωρινής μνήμης Shader στο {0}: {1}", - "DialogRyujinxErrorMessage": "Το Ryujinx αντιμετώπισε σφάλμα", - "DialogInvalidTitleIdErrorMessage": "Σφάλμα UI: Το επιλεγμένο παιχνίδι δεν έχει έγκυρο αναγνωριστικό τίτλου", - "DialogFirmwareInstallerFirmwareNotFoundErrorMessage": "Δεν βρέθηκε έγκυρο Firmware συστήματος στο {0}.", - "DialogFirmwareInstallerFirmwareInstallTitle": "Εγκατάσταση Firmware {0}", - "DialogFirmwareInstallerFirmwareInstallMessage": "Θα εγκατασταθεί η έκδοση συστήματος {0}.", - "DialogFirmwareInstallerFirmwareInstallSubMessage": "\n\nΑυτό θα αντικαταστήσει την τρέχουσα έκδοση συστήματος {0}.", - "DialogFirmwareInstallerFirmwareInstallConfirmMessage": "\n\nΘέλετε να συνεχίσετε;", - "DialogFirmwareInstallerFirmwareInstallWaitMessage": "Εγκατάσταση Firmware...", - "DialogFirmwareInstallerFirmwareInstallSuccessMessage": "Η έκδοση συστήματος {0} εγκαταστάθηκε με επιτυχία.", - "DialogUserProfileDeletionWarningMessage": "Δεν θα υπάρχουν άλλα προφίλ εάν διαγραφεί το επιλεγμένο", - "DialogUserProfileDeletionConfirmMessage": "Θέλετε να διαγράψετε το επιλεγμένο προφίλ", - "DialogUserProfileUnsavedChangesTitle": "Προσοχή - Μην Αποθηκευμένες Αλλαγές.", - "DialogUserProfileUnsavedChangesMessage": "Έχετε κάνει αλλαγές σε αυτό το προφίλ χρήστη που δεν έχουν αποθηκευτεί.", - "DialogUserProfileUnsavedChangesSubMessage": "Θέλετε να απορρίψετε τις αλλαγές σας;", - "DialogControllerSettingsModifiedConfirmMessage": "Οι τρέχουσες ρυθμίσεις χειρισμού έχουν ενημερωθεί.", - "DialogControllerSettingsModifiedConfirmSubMessage": "Θέλετε να αποθηκεύσετε;", - "DialogLoadFileErrorMessage": "{0}. Errored File: {1}", - "DialogModAlreadyExistsMessage": "Mod already exists", - "DialogModInvalidMessage": "The specified directory does not contain a mod!", - "DialogModDeleteNoParentMessage": "Failed to Delete: Could not find the parent directory for mod \"{0}\"!", - "DialogDlcNoDlcErrorMessage": "Το αρχείο δεν περιέχει DLC για τον επιλεγμένο τίτλο!", - "DialogPerformanceCheckLoggingEnabledMessage": "Έχετε ενεργοποιημένη την καταγραφή εντοπισμού σφαλμάτων, η οποία έχει σχεδιαστεί για χρήση μόνο από προγραμματιστές.", - "DialogPerformanceCheckLoggingEnabledConfirmMessage": "Για βέλτιστη απόδοση, συνιστάται η απενεργοποίηση καταγραφής εντοπισμού σφαλμάτων. Θέλετε να απενεργοποιήσετε την καταγραφή τώρα;", - "DialogPerformanceCheckShaderDumpEnabledMessage": "Έχετε ενεργοποιήσει το Shader Dumping, το οποίο έχει σχεδιαστεί για χρήση μόνο από προγραμματιστές.", - "DialogPerformanceCheckShaderDumpEnabledConfirmMessage": "Για βέλτιστη απόδοση, συνιστάται να απενεργοποιήσετε το Shader Dumping. Θέλετε να απενεργοποιήσετε τώρα το Shader Dumping;", - "DialogLoadAppGameAlreadyLoadedMessage": "Ένα παιχνίδι έχει ήδη φορτωθεί", - "DialogLoadAppGameAlreadyLoadedSubMessage": "Σταματήστε την εξομοίωση ή κλείστε τον εξομοιωτή πριν ξεκινήσετε ένα άλλο παιχνίδι.", - "DialogUpdateAddUpdateErrorMessage": "Το αρχείο δεν περιέχει ενημέρωση για τον επιλεγμένο τίτλο!", - "DialogSettingsBackendThreadingWarningTitle": "Προειδοποίηση - Backend Threading", - "DialogSettingsBackendThreadingWarningMessage": "Το Ryujinx πρέπει να επανεκκινηθεί αφού αλλάξει αυτή η επιλογή για να εφαρμοστεί πλήρως. Ανάλογα με την πλατφόρμα σας, μπορεί να χρειαστεί να απενεργοποιήσετε με μη αυτόματο τρόπο το multithreading του ίδιου του προγράμματος οδήγησης όταν χρησιμοποιείτε το Ryujinx.", - "DialogModManagerDeletionWarningMessage": "You are about to delete the mod: {0}\n\nAre you sure you want to proceed?", - "DialogModManagerDeletionAllWarningMessage": "You are about to delete all mods for this title.\n\nAre you sure you want to proceed?", - "SettingsTabGraphicsFeaturesOptions": "Χαρακτηριστικά", - "SettingsTabGraphicsBackendMultithreading": "Πολυνηματική Επεξεργασία Γραφικών:", - "CommonAuto": "Αυτόματο", - "CommonOff": "Ανενεργό", - "CommonOn": "Ενεργό", - "InputDialogYes": "Ναι", - "InputDialogNo": "Όχι", - "DialogProfileInvalidProfileNameErrorMessage": "Το όνομα αρχείου περιέχει μη έγκυρους χαρακτήρες. Παρακαλώ προσπαθήστε ξανά.", - "MenuBarOptionsPauseEmulation": "Παύση", - "MenuBarOptionsResumeEmulation": "Συνέχιση", - "AboutUrlTooltipMessage": "Κάντε κλικ για να ανοίξετε τον ιστότοπο Ryujinx στο προεπιλεγμένο πρόγραμμα περιήγησης.", - "AboutDisclaimerMessage": "Το Ryujinx δεν είναι συνδεδεμένο με τη Nintendo™,\nούτε με κανέναν από τους συνεργάτες της, με οποιονδήποτε τρόπο.", - "AboutAmiiboDisclaimerMessage": "Το AmiiboAPI (www.amiiboapi.com) χρησιμοποιείται\nστην προσομοίωση Amiibo.", - "AboutPatreonUrlTooltipMessage": "Κάντε κλικ για να ανοίξετε τη σελίδα Ryujinx Patreon στο προεπιλεγμένο πρόγραμμα περιήγησης.", - "AboutGithubUrlTooltipMessage": "Κάντε κλικ για να ανοίξετε τη σελίδα Ryujinx GitHub στο προεπιλεγμένο πρόγραμμα περιήγησης.", - "AboutDiscordUrlTooltipMessage": "Κάντε κλικ για να ανοίξετε μία πρόσκληση στον διακομιστή Ryujinx Discord στο προεπιλεγμένο πρόγραμμα περιήγησης.", - "AboutTwitterUrlTooltipMessage": "Κάντε κλικ για να ανοίξετε τη σελίδα Ryujinx Twitter στο προεπιλεγμένο πρόγραμμα περιήγησης.", - "AboutRyujinxAboutTitle": "Σχετικά με:", - "AboutRyujinxAboutContent": "Το Ryujinx είναι ένας εξομοιωτής για το Nintendo Switch™.\nΥποστηρίξτε μας στο Patreon.\nΛάβετε όλα τα τελευταία νέα στο Twitter ή στο Discord.\nΟι προγραμματιστές που ενδιαφέρονται να συνεισφέρουν μπορούν να μάθουν περισσότερα στο GitHub ή στο Discord μας.", - "AboutRyujinxMaintainersTitle": "Συντηρείται από:", - "AboutRyujinxMaintainersContentTooltipMessage": "Κάντε κλικ για να ανοίξετε τη σελίδα Συνεισφέροντες στο προεπιλεγμένο πρόγραμμα περιήγησης.", - "AboutRyujinxSupprtersTitle": "Υποστηρίζεται στο Patreon από:", - "AmiiboSeriesLabel": "Σειρά Amiibo", - "AmiiboCharacterLabel": "Χαρακτήρας", - "AmiiboScanButtonLabel": "Σαρώστε το", - "AmiiboOptionsShowAllLabel": "Εμφάνιση όλων των Amiibo", - "AmiiboOptionsUsRandomTagLabel": "Hack: Χρησιμοποιήστε τυχαίο αναγνωριστικό UUID", - "DlcManagerTableHeadingEnabledLabel": "Ενεργοποιημένο", - "DlcManagerTableHeadingTitleIdLabel": "Αναγνωριστικό τίτλου", - "DlcManagerTableHeadingContainerPathLabel": "Τοποθεσία DLC", - "DlcManagerTableHeadingFullPathLabel": "Πλήρης τοποθεσία", - "DlcManagerRemoveAllButton": "Αφαίρεση όλων", - "DlcManagerEnableAllButton": "Ενεργοποίηση Όλων", - "DlcManagerDisableAllButton": "Απενεργοποίηση Όλων", - "ModManagerDeleteAllButton": "Delete All", - "MenuBarOptionsChangeLanguage": "Αλλαξε γλώσσα", - "MenuBarShowFileTypes": "Εμφάνιση Τύπων Αρχείων", - "CommonSort": "Κατάταξη", - "CommonShowNames": "Εμφάνιση ονομάτων", - "CommonFavorite": "Αγαπημένα", - "OrderAscending": "Αύξουσα", - "OrderDescending": "Φθίνουσα", - "SettingsTabGraphicsFeatures": "Χαρακτηριστικά & Βελτιώσεις", - "ErrorWindowTitle": "Παράθυρο σφάλματος", - "ToggleDiscordTooltip": "Ενεργοποιεί ή απενεργοποιεί την Εμπλουτισμένη Παρουσία σας στο Discord", - "AddGameDirBoxTooltip": "Εισαγάγετε μία τοποθεσία παιχνιδιών για προσθήκη στη λίστα", - "AddGameDirTooltip": "Προσθέστε μία τοποθεσία παιχνιδιών στη λίστα", - "RemoveGameDirTooltip": "Αφαιρέστε την επιλεγμένη τοποθεσία παιχνιδιών", - "CustomThemeCheckTooltip": "Ενεργοποίηση ή απενεργοποίηση προσαρμοσμένων θεμάτων στο GUI", - "CustomThemePathTooltip": "Διαδρομή προς το προσαρμοσμένο θέμα GUI", - "CustomThemeBrowseTooltip": "Αναζητήστε ένα προσαρμοσμένο θέμα GUI", - "DockModeToggleTooltip": "Ενεργοποιήστε ή απενεργοποιήστε τη λειτουργία σύνδεσης", - "DirectKeyboardTooltip": "Direct keyboard access (HID) support. Provides games access to your keyboard as a text entry device.\n\nOnly works with games that natively support keyboard usage on Switch hardware.\n\nLeave OFF if unsure.", - "DirectMouseTooltip": "Direct mouse access (HID) support. Provides games access to your mouse as a pointing device.\n\nOnly works with games that natively support mouse controls on Switch hardware, which are few and far between.\n\nWhen enabled, touch screen functionality may not work.\n\nLeave OFF if unsure.", - "RegionTooltip": "Αλλαγή Περιοχής Συστήματος", - "LanguageTooltip": "Αλλαγή Γλώσσας Συστήματος", - "TimezoneTooltip": "Αλλαγή Ζώνης Ώρας Συστήματος", - "TimeTooltip": "Αλλαγή Ώρας Συστήματος", - "VSyncToggleTooltip": "Emulated console's Vertical Sync. Essentially a frame-limiter for the majority of games; disabling it may cause games to run at higher speed or make loading screens take longer or get stuck.\n\nCan be toggled in-game with a hotkey of your preference (F1 by default). We recommend doing this if you plan on disabling it.\n\nLeave ON if unsure.", - "PptcToggleTooltip": "Ενεργοποιεί ή απενεργοποιεί το PPTC", - "FsIntegrityToggleTooltip": "Ενεργοποιεί τους ελέγχους ακεραιότητας σε αρχεία περιεχομένου παιχνιδιού", - "AudioBackendTooltip": "Αλλαγή ήχου υποστήριξης", - "MemoryManagerTooltip": "Αλλάξτε τον τρόπο αντιστοίχισης και πρόσβασης στη μνήμη επισκέπτη. Επηρεάζει σε μεγάλο βαθμό την απόδοση της προσομοίωσης της CPU.", - "MemoryManagerSoftwareTooltip": "Χρησιμοποιήστε έναν πίνακα σελίδων λογισμικού για τη μετάφραση διευθύνσεων. Υψηλότερη ακρίβεια αλλά πιο αργή απόδοση.", - "MemoryManagerHostTooltip": "Απευθείας αντιστοίχιση της μνήμης στον χώρο διευθύνσεων υπολογιστή υποδοχής. Πολύ πιο γρήγορη μεταγλώττιση και εκτέλεση JIT.", - "MemoryManagerUnsafeTooltip": "Απευθείας χαρτογράφηση της μνήμης, αλλά μην καλύπτετε τη διεύθυνση εντός του χώρου διευθύνσεων επισκέπτη πριν από την πρόσβαση. Πιο γρήγορα, αλλά με κόστος ασφάλειας. Η εφαρμογή μπορεί να έχει πρόσβαση στη μνήμη από οπουδήποτε στο Ryujinx, επομένως εκτελείτε μόνο προγράμματα που εμπιστεύεστε με αυτήν τη λειτουργία.", - "UseHypervisorTooltip": "Χρησιμοποιήστε Hypervisor αντί για JIT. Βελτιώνει σημαντικά την απόδοση όταν διατίθεται, αλλά μπορεί να είναι ασταθής στην τρέχουσα κατάστασή του.", - "DRamTooltip": "Επεκτείνει την ποσότητα της μνήμης στο εξομοιούμενο σύστημα από 4 GiB σε 6 GiB", - "IgnoreMissingServicesTooltip": "Ενεργοποίηση ή απενεργοποίηση της αγνοώησης για υπηρεσίες που λείπουν", - "GraphicsBackendThreadingTooltip": "Ενεργοποίηση Πολυνηματικής Επεξεργασίας Γραφικών", - "GalThreadingTooltip": "Εκτελεί εντολές γραφικών σε ένα δεύτερο νήμα. Επιτρέπει την πολυνηματική μεταγλώττιση Shader σε χρόνο εκτέλεσης, μειώνει το τρεμόπαιγμα και βελτιώνει την απόδοση των προγραμμάτων οδήγησης χωρίς τη δική τους υποστήριξη πολλαπλών νημάτων. Ποικίλες κορυφαίες επιδόσεις σε προγράμματα οδήγησης με multithreading. Μπορεί να χρειαστεί επανεκκίνηση του Ryujinx για να απενεργοποιήσετε σωστά την ενσωματωμένη λειτουργία πολλαπλών νημάτων του προγράμματος οδήγησης ή ίσως χρειαστεί να το κάνετε χειροκίνητα για να έχετε την καλύτερη απόδοση.", - "ShaderCacheToggleTooltip": "Ενεργοποιεί ή απενεργοποιεί την Προσωρινή Μνήμη Shader", - "ResolutionScaleTooltip": "Multiplies the game's rendering resolution.\n\nA few games may not work with this and look pixelated even when the resolution is increased; for those games, you may need to find mods that remove anti-aliasing or that increase their internal rendering resolution. For using the latter, you'll likely want to select Native.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nKeep in mind 4x is overkill for virtually any setup.", - "ResolutionScaleEntryTooltip": "Κλίμακα ανάλυσης κινητής υποδιαστολής, όπως 1,5. Οι μη αναπόσπαστες τιμές είναι πιθανό να προκαλέσουν προβλήματα ή σφάλματα.", - "AnisotropyTooltip": "Level of Anisotropic Filtering. Set to Auto to use the value requested by the game.", - "AspectRatioTooltip": "Aspect Ratio applied to the renderer window.\n\nOnly change this if you're using an aspect ratio mod for your game, otherwise the graphics will be stretched.\n\nLeave on 16:9 if unsure.", - "ShaderDumpPathTooltip": "Τοποθεσία Εναπόθεσης Προσωρινής Μνήμης Shaders", - "FileLogTooltip": "Ενεργοποιεί ή απενεργοποιεί την καταγραφή σε ένα αρχείο στο δίσκο", - "StubLogTooltip": "Ενεργοποιεί την εκτύπωση μηνυμάτων καταγραφής ατελειών", - "InfoLogTooltip": "Ενεργοποιεί την εκτύπωση μηνυμάτων αρχείου καταγραφής πληροφοριών", - "WarnLogTooltip": "Ενεργοποιεί την εκτύπωση μηνυμάτων καταγραφής προειδοποιήσεων", - "ErrorLogTooltip": "Ενεργοποιεί την εκτύπωση μηνυμάτων αρχείου καταγραφής σφαλμάτων", - "TraceLogTooltip": "Ενεργοποιεί την εκτύπωση μηνυμάτων αρχείου καταγραφής ιχνών", - "GuestLogTooltip": "Ενεργοποιεί την εκτύπωση μηνυμάτων καταγραφής επισκεπτών", - "FileAccessLogTooltip": "Ενεργοποιεί την εκτύπωση μηνυμάτων αρχείου καταγραφής πρόσβασης", - "FSAccessLogModeTooltip": "Ενεργοποιεί την έξοδο καταγραφής πρόσβασης FS στην κονσόλα. Οι πιθανοί τρόποι λειτουργίας είναι 0-3", - "DeveloperOptionTooltip": "Χρησιμοποιήστε με προσοχή", - "OpenGlLogLevel": "Απαιτεί τα κατάλληλα επίπεδα καταγραφής ενεργοποιημένα", - "DebugLogTooltip": "Ενεργοποιεί την εκτύπωση μηνυμάτων αρχείου καταγραφής εντοπισμού σφαλμάτων", - "LoadApplicationFileTooltip": "Ανοίξτε έναν επιλογέα αρχείων για να επιλέξετε ένα αρχείο συμβατό με το Switch για φόρτωση", - "LoadApplicationFolderTooltip": "Ανοίξτε έναν επιλογέα αρχείων για να επιλέξετε μία μη συσκευασμένη εφαρμογή, συμβατή με το Switch για φόρτωση", - "OpenRyujinxFolderTooltip": "Ανοίξτε το φάκελο συστήματος αρχείων Ryujinx", - "OpenRyujinxLogsTooltip": "Ανοίξτε το φάκελο στον οποίο διατηρούνται τα αρχεία καταγραφής", - "ExitTooltip": "Έξοδος από το Ryujinx", - "OpenSettingsTooltip": "Ανοίξτε το παράθυρο Ρυθμίσεων", - "OpenProfileManagerTooltip": "Ανοίξτε το παράθυρο Διαχείρισης Προφίλ Χρήστη", - "StopEmulationTooltip": "Σταματήστε την εξομοίωση του τρέχοντος παιχνιδιού και επιστρέψτε στην επιλογή παιχνιδιού", - "CheckUpdatesTooltip": "Ελέγξτε για ενημερώσεις του Ryujinx", - "OpenAboutTooltip": "Ανοίξτε το Παράθυρο Σχετικά", - "GridSize": "Μέγεθος Πλέγματος", - "GridSizeTooltip": "Αλλαγή μεγέθους στοιχείων πλέγματος", - "SettingsTabSystemSystemLanguageBrazilianPortuguese": "Πορτογαλικά Βραζιλίας", - "AboutRyujinxContributorsButtonHeader": "Δείτε Όλους τους Συντελεστές", - "SettingsTabSystemAudioVolume": "Ενταση Ήχου: ", - "AudioVolumeTooltip": "Αλλαγή Έντασης Ήχου", - "SettingsTabSystemEnableInternetAccess": "Ενεργοποίηση πρόσβασης επισκέπτη στο Διαδίκτυο", - "EnableInternetAccessTooltip": "Επιτρέπει την πρόσβαση επισκέπτη στο Διαδίκτυο. Εάν ενεργοποιηθεί, η εξομοιωμένη κονσόλα Switch θα συμπεριφέρεται σαν να είναι συνδεδεμένη στο Διαδίκτυο. Λάβετε υπόψη ότι σε ορισμένες περιπτώσεις, οι εφαρμογές ενδέχεται να εξακολουθούν να έχουν πρόσβαση στο Διαδίκτυο, ακόμη και όταν αυτή η επιλογή είναι απενεργοποιημένη", - "GameListContextMenuManageCheatToolTip": "Διαχείριση Κόλπων", - "GameListContextMenuManageCheat": "Διαχείριση Κόλπων", - "GameListContextMenuManageModToolTip": "Manage Mods", - "GameListContextMenuManageMod": "Manage Mods", - "ControllerSettingsStickRange": "Εύρος:", - "DialogStopEmulationTitle": "Ryujinx - Διακοπή εξομοίωσης", - "DialogStopEmulationMessage": "Είστε βέβαιοι ότι θέλετε να σταματήσετε την εξομοίωση;", - "SettingsTabCpu": "Επεξεργαστής", - "SettingsTabAudio": "Ήχος", - "SettingsTabNetwork": "Δίκτυο", - "SettingsTabNetworkConnection": "Σύνδεση δικτύου", - "SettingsTabCpuCache": "Προσωρινή Μνήμη CPU", - "SettingsTabCpuMemory": "Μνήμη CPU", - "DialogUpdaterFlatpakNotSupportedMessage": "Παρακαλούμε ενημερώστε το Ryujinx μέσω FlatHub.", - "UpdaterDisabledWarningTitle": "Ο Διαχειριστής Ενημερώσεων Είναι Απενεργοποιημένος!", - "ControllerSettingsRotate90": "Περιστροφή 90° Δεξιόστροφα", - "IconSize": "Μέγεθος Εικονιδίου", - "IconSizeTooltip": "Αλλάξτε μέγεθος εικονιδίων των παιχνιδιών", - "MenuBarOptionsShowConsole": "Εμφάνιση Κονσόλας", - "ShaderCachePurgeError": "Σφάλμα κατά την εκκαθάριση του shader cache στο {0}: {1}", - "UserErrorNoKeys": "Τα κλειδιά δεν βρέθηκαν", - "UserErrorNoFirmware": "Το firmware δε βρέθηκε", - "UserErrorFirmwareParsingFailed": "Σφάλμα ανάλυσης firmware", - "UserErrorApplicationNotFound": "Η εφαρμογή δε βρέθηκε", - "UserErrorUnknown": "Άγνωστο σφάλμα", - "UserErrorUndefined": "Αόριστο σφάλμα", - "UserErrorNoKeysDescription": "Το Ryujinx δεν κατάφερε να εντοπίσει το αρχείο 'prod.keys'", - "UserErrorNoFirmwareDescription": "Το Ryujinx δεν κατάφερε να εντοπίσει κανένα εγκατεστημένο firmware", - "UserErrorFirmwareParsingFailedDescription": "Το Ryujinx δεν κατάφερε να αναλύσει το συγκεκριμένο firmware. Αυτό συνήθως οφείλετε σε ξεπερασμένα/παλιά κλειδιά.", - "UserErrorApplicationNotFoundDescription": "Το Ryujinx δεν κατάφερε να εντοπίσει έγκυρη εφαρμογή στη συγκεκριμένη διαδρομή.", - "UserErrorUnknownDescription": "Παρουσιάστηκε άγνωστο σφάλμα.", - "UserErrorUndefinedDescription": "Παρουσιάστηκε ένα άγνωστο σφάλμα! Αυτό δεν πρέπει να συμβεί, παρακαλώ επικοινωνήστε με έναν προγραμματιστή!", - "OpenSetupGuideMessage": "Ανοίξτε τον Οδηγό Εγκατάστασης.", - "NoUpdate": "Καμία Eνημέρωση", - "TitleUpdateVersionLabel": "Version {0} - {1}", - "RyujinxInfo": "Ryujinx - Πληροφορίες", - "RyujinxConfirm": "Ryujinx - Επιβεβαίωση", - "FileDialogAllTypes": "Όλοι οι τύποι", - "Never": "Ποτέ", - "SwkbdMinCharacters": "Πρέπει να έχει μήκος τουλάχιστον {0} χαρακτήρες", - "SwkbdMinRangeCharacters": "Πρέπει να έχει μήκος {0}-{1} χαρακτήρες", - "SoftwareKeyboard": "Εικονικό Πληκτρολόγιο", - "SoftwareKeyboardModeNumeric": "Πρέπει να είναι 0-9 ή '.' μόνο", - "SoftwareKeyboardModeAlphabet": "Πρέπει να μην είναι μόνο χαρακτήρες CJK", - "SoftwareKeyboardModeASCII": "Πρέπει να είναι μόνο κείμενο ASCII", - "ControllerAppletControllers": "Supported Controllers:", - "ControllerAppletPlayers": "Players:", - "ControllerAppletDescription": "Your current configuration is invalid. Open settings and reconfigure your inputs.", - "ControllerAppletDocked": "Docked mode set. Handheld control should be disabled.", - "UpdaterRenaming": "Μετονομασία Παλαιών Αρχείων...", - "UpdaterRenameFailed": "Δεν ήταν δυνατή η μετονομασία του αρχείου: {0}", - "UpdaterAddingFiles": "Προσθήκη Νέων Αρχείων...", - "UpdaterExtracting": "Εξαγωγή Ενημέρωσης...", - "UpdaterDownloading": "Λήψη Ενημέρωσης...", - "Game": "Παιχνίδι", - "Docked": "Προσκολλημένο", - "Handheld": "Χειροκίνητο", - "ConnectionError": "Σφάλμα Σύνδεσης.", - "AboutPageDeveloperListMore": "{0} και περισσότερα...", - "ApiError": "Σφάλμα API.", - "LoadingHeading": "Φόρτωση {0}", - "CompilingPPTC": "Μεταγλώττιση του PTC", - "CompilingShaders": "Σύνταξη των Shaders", - "AllKeyboards": "Όλα τα πληκτρολόγια", - "OpenFileDialogTitle": "Επιλέξτε ένα υποστηριζόμενο αρχείο για άνοιγμα", - "OpenFolderDialogTitle": "Επιλέξτε ένα φάκελο με ένα αποσυμπιεσμένο παιχνίδι", - "AllSupportedFormats": "Όλες Οι Υποστηριζόμενες Μορφές", - "RyujinxUpdater": "Ryujinx Ενημερωτής", - "SettingsTabHotkeys": "Συντομεύσεις Πληκτρολογίου", - "SettingsTabHotkeysHotkeys": "Συντομεύσεις Πληκτρολογίου", - "SettingsTabHotkeysToggleVsyncHotkey": "Εναλλαγή VSync:", - "SettingsTabHotkeysScreenshotHotkey": "Στιγμιότυπο Οθόνης:", - "SettingsTabHotkeysShowUiHotkey": "Εμφάνιση Διεπαφής Χρήστη:", - "SettingsTabHotkeysPauseHotkey": "Παύση:", - "SettingsTabHotkeysToggleMuteHotkey": "Σίγαση:", - "ControllerMotionTitle": "Ρυθμίσεις Ελέγχου Κίνησης", - "ControllerRumbleTitle": "Ρυθμίσεις Δόνησης", - "SettingsSelectThemeFileDialogTitle": "Επιλογή Αρχείου Θέματος", - "SettingsXamlThemeFile": "Αρχείο Θέματος Xaml", - "AvatarWindowTitle": "Διαχείριση Λογαριασμών - Avatar", - "Amiibo": "Amiibo", - "Unknown": "Άγνωστο", - "Usage": "Χρήση", - "Writable": "Εγγράψιμο", - "SelectDlcDialogTitle": "Επιλογή αρχείων DLC", - "SelectUpdateDialogTitle": "Επιλογή αρχείων ενημέρωσης", - "SelectModDialogTitle": "Select mod directory", - "UserProfileWindowTitle": "Διαχειριστής Προφίλ Χρήστη", - "CheatWindowTitle": "Διαχειριστής των Cheats", - "DlcWindowTitle": "Downloadable Content Manager", - "ModWindowTitle": "Manage Mods for {0} ({1})", - "UpdateWindowTitle": "Διαχειριστής Ενημερώσεων Τίτλου", - "CheatWindowHeading": "Διαθέσιμα Cheats για {0} [{1}]", - "BuildId": "BuildId:", - "DlcWindowHeading": "{0} Downloadable Content(s) available for {1} ({2})", - "ModWindowHeading": "{0} Mod(s)", - "UserProfilesEditProfile": "Επεξεργασία Επιλεγμένων", - "Cancel": "Ακύρωση", - "Save": "Αποθήκευση", - "Discard": "Απόρριψη", - "Paused": "Σε παύση", - "UserProfilesSetProfileImage": "Ορισμός Εικόνας Προφίλ", - "UserProfileEmptyNameError": "Απαιτείται όνομα", - "UserProfileNoImageError": "Η εικόνα προφίλ πρέπει να οριστεί", - "GameUpdateWindowHeading": "{0} Update(s) available for {1} ({2})", - "SettingsTabHotkeysResScaleUpHotkey": "Αύξηση της ανάλυσης:", - "SettingsTabHotkeysResScaleDownHotkey": "Μείωση της ανάλυσης:", - "UserProfilesName": "Όνομα:", - "UserProfilesUserId": "User Id:", - "SettingsTabGraphicsBackend": "Σύστημα Υποστήριξης Γραφικών", - "SettingsTabGraphicsBackendTooltip": "Select the graphics backend that will be used in the emulator.\n\nVulkan is overall better for all modern graphics cards, as long as their drivers are up to date. Vulkan also features faster shader compilation (less stuttering) on all GPU vendors.\n\nOpenGL may achieve better results on old Nvidia GPUs, on old AMD GPUs on Linux, or on GPUs with lower VRAM, though shader compilation stutters will be greater.\n\nSet to Vulkan if unsure. Set to OpenGL if your GPU does not support Vulkan even with the latest graphics drivers.", - "SettingsEnableTextureRecompression": "Ενεργοποίηση Επανασυμπίεσης Των Texture", - "SettingsEnableTextureRecompressionTooltip": "Compresses ASTC textures in order to reduce VRAM usage.\n\nGames using this texture format include Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder and The Legend of Zelda: Tears of the Kingdom.\n\nGraphics cards with 4GiB VRAM or less will likely crash at some point while running these games.\n\nEnable only if you're running out of VRAM on the aforementioned games. Leave OFF if unsure.", - "SettingsTabGraphicsPreferredGpu": "Προτιμώμενη GPU", - "SettingsTabGraphicsPreferredGpuTooltip": "Επιλέξτε την κάρτα γραφικών η οποία θα χρησιμοποιηθεί από το Vulkan.\n\nΔεν επηρεάζει το OpenGL.\n\nΔιαλέξτε την GPU που διαθέτει την υπόδειξη \"dGPU\" αν δεν είστε βέβαιοι. Αν δεν υπάρχει κάποιαν, το πειράξετε", - "SettingsAppRequiredRestartMessage": "Απαιτείται Επανεκκίνηση Του Ryujinx", - "SettingsGpuBackendRestartMessage": "Οι ρυθμίσεις GPU έχουν αλλαχτεί. Θα χρειαστεί επανεκκίνηση του Ryujinx για να τεθούν σε ισχύ.", - "SettingsGpuBackendRestartSubMessage": "Θέλετε να κάνετε επανεκκίνηση τώρα;", - "RyujinxUpdaterMessage": "Θέλετε να ενημερώσετε το Ryujinx στην πιο πρόσφατη έκδοση:", - "SettingsTabHotkeysVolumeUpHotkey": "Αύξηση Έντασης:", - "SettingsTabHotkeysVolumeDownHotkey": "Μείωση Έντασης:", - "SettingsEnableMacroHLE": "Ενεργοποίηση του Macro HLE", - "SettingsEnableMacroHLETooltip": "Προσομοίωση του κώδικα GPU Macro .\n\nΒελτιώνει την απόδοση, αλλά μπορεί να προκαλέσει γραφικά προβλήματα σε μερικά παιχνίδια.\n\nΑφήστε ΕΝΕΡΓΟ αν δεν είστε σίγουροι.", - "SettingsEnableColorSpacePassthrough": "Διέλευση Χρωματικού Χώρου", - "SettingsEnableColorSpacePassthroughTooltip": "Σκηνοθετεί το σύστημα υποστήριξης του Vulkan για να περάσει από πληροφορίες χρώματος χωρίς να καθορίσει έναν χρωματικό χώρο. Για χρήστες με ευρείες οθόνες γκάμας, αυτό μπορεί να οδηγήσει σε πιο ζωηρά χρώματα, με κόστος την ορθότητα του χρώματος.", - "VolumeShort": "Έντ.", - "UserProfilesManageSaves": "Διαχείριση Των Save", - "DeleteUserSave": "Επιθυμείτε να διαγράψετε το save χρήστη για το συγκεκριμένο παιχνίδι;", - "IrreversibleActionNote": "Αυτή η ενέργεια είναι μη αναστρέψιμη.", - "SaveManagerHeading": "Manage Saves for {0}", - "SaveManagerTitle": "Διαχειριστής Save", - "Name": "Όνομα", - "Size": "Μέγεθος", - "Search": "Αναζήτηση", - "UserProfilesRecoverLostAccounts": "Ανάκτηση Χαμένων Λογαριασμών", - "Recover": "Ανάκτηση", - "UserProfilesRecoverHeading": "Βρέθηκαν save για τους ακόλουθους λογαριασμούς", - "UserProfilesRecoverEmptyList": "Δεν υπάρχουν προφίλ για ανάκτηση", - "GraphicsAATooltip": "Applies anti-aliasing to the game render.\n\nFXAA will blur most of the image, while SMAA will attempt to find jagged edges and smooth them out.\n\nNot recommended to use in conjunction with the FSR scaling filter.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on NONE if unsure.", - "GraphicsAALabel": "Anti-Aliasing", - "GraphicsScalingFilterLabel": "Φίλτρο Κλιμάκωσης:", - "GraphicsScalingFilterTooltip": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.", - "GraphicsScalingFilterBilinear": "Bilinear", - "GraphicsScalingFilterNearest": "Nearest", - "GraphicsScalingFilterFsr": "FSR", - "GraphicsScalingFilterLevelLabel": "Επίπεδο", - "GraphicsScalingFilterLevelTooltip": "Set FSR 1.0 sharpening level. Higher is sharper.", - "SmaaLow": "Χαμηλό SMAA", - "SmaaMedium": " Μεσαίο SMAA", - "SmaaHigh": "Υψηλό SMAA", - "SmaaUltra": "Oύλτρα SMAA", - "UserEditorTitle": "Επεξεργασία Χρήστη", - "UserEditorTitleCreate": "Δημιουργία Χρήστη", - "SettingsTabNetworkInterface": "Διεπαφή Δικτύου", - "NetworkInterfaceTooltip": "The network interface used for LAN/LDN features.\n\nIn conjunction with a VPN or XLink Kai and a game with LAN support, can be used to spoof a same-network connection over the Internet.\n\nLeave on DEFAULT if unsure.", - "NetworkInterfaceDefault": "Προεπιλογή", - "PackagingShaders": "Shaders Συσκευασίας", - "AboutChangelogButton": "Προβολή αρχείου αλλαγών στο GitHub", - "AboutChangelogButtonTooltipMessage": "Κάντε κλικ για να ανοίξετε το αρχείο αλλαγών για αυτήν την έκδοση στο προεπιλεγμένο πρόγραμμα περιήγησης σας.", - "SettingsTabNetworkMultiplayer": "Πολλαπλοί παίκτες", - "MultiplayerMode": "Λειτουργία:", - "MultiplayerModeTooltip": "Change LDN multiplayer mode.\n\nLdnMitm will modify local wireless/local play functionality in games to function as if it were LAN, allowing for local, same-network connections with other Ryujinx instances and hacked Nintendo Switch consoles that have the ldn_mitm module installed.\n\nMultiplayer requires all players to be on the same game version (i.e. Super Smash Bros. Ultimate v13.0.1 can't connect to v13.0.0).\n\nLeave DISABLED if unsure.", - "MultiplayerModeDisabled": "Disabled", - "MultiplayerModeLdnMitm": "ldn_mitm" -} diff --git a/src/Ryujinx/Assets/Locales/en_US.json b/src/Ryujinx/Assets/Locales/en_US.json deleted file mode 100644 index b3cab7f5f..000000000 --- a/src/Ryujinx/Assets/Locales/en_US.json +++ /dev/null @@ -1,785 +0,0 @@ -{ - "Language": "English (US)", - "MenuBarFileOpenApplet": "Open Applet", - "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Open Mii Editor Applet in Standalone mode", - "SettingsTabInputDirectMouseAccess": "Direct Mouse Access", - "SettingsTabSystemMemoryManagerMode": "Memory Manager Mode:", - "SettingsTabSystemMemoryManagerModeSoftware": "Software", - "SettingsTabSystemMemoryManagerModeHost": "Host (fast)", - "SettingsTabSystemMemoryManagerModeHostUnchecked": "Host Unchecked (fastest, unsafe)", - "SettingsTabSystemUseHypervisor": "Use Hypervisor", - "MenuBarFile": "_File", - "MenuBarFileOpenFromFile": "_Load Application From File", - "MenuBarFileOpenFromFileError": "No applications found in selected file.", - "MenuBarFileOpenUnpacked": "Load _Unpacked Game", - "MenuBarFileOpenEmuFolder": "Open Ryujinx Folder", - "MenuBarFileOpenLogsFolder": "Open Logs Folder", - "MenuBarFileExit": "_Exit", - "MenuBarOptions": "_Options", - "MenuBarOptionsToggleFullscreen": "Toggle Fullscreen", - "MenuBarOptionsStartGamesInFullscreen": "Start Games in Fullscreen Mode", - "MenuBarOptionsStopEmulation": "Stop Emulation", - "MenuBarOptionsSettings": "_Settings", - "MenuBarOptionsManageUserProfiles": "_Manage User Profiles", - "MenuBarActions": "_Actions", - "MenuBarOptionsSimulateWakeUpMessage": "Simulate Wake-up message", - "MenuBarActionsScanAmiibo": "Scan An Amiibo", - "MenuBarTools": "_Tools", - "MenuBarToolsInstallFirmware": "Install Firmware", - "MenuBarFileToolsInstallFirmwareFromFile": "Install a firmware from XCI or ZIP", - "MenuBarFileToolsInstallFirmwareFromDirectory": "Install a firmware from a directory", - "MenuBarToolsManageFileTypes": "Manage file types", - "MenuBarToolsInstallFileTypes": "Install file types", - "MenuBarToolsUninstallFileTypes": "Uninstall file types", - "MenuBarView": "_View", - "MenuBarViewWindow": "Window Size", - "MenuBarViewWindow720": "720p", - "MenuBarViewWindow1080": "1080p", - "MenuBarHelp": "_Help", - "MenuBarHelpCheckForUpdates": "Check for Updates", - "MenuBarHelpAbout": "About", - "MenuSearch": "Search...", - "GameListHeaderFavorite": "Fav", - "GameListHeaderIcon": "Icon", - "GameListHeaderApplication": "Name", - "GameListHeaderDeveloper": "Developer", - "GameListHeaderVersion": "Version", - "GameListHeaderTimePlayed": "Play Time", - "GameListHeaderLastPlayed": "Last Played", - "GameListHeaderFileExtension": "File Ext", - "GameListHeaderFileSize": "File Size", - "GameListHeaderPath": "Path", - "GameListContextMenuOpenUserSaveDirectory": "Open User Save Directory", - "GameListContextMenuOpenUserSaveDirectoryToolTip": "Opens the directory which contains Application's User Save", - "GameListContextMenuOpenDeviceSaveDirectory": "Open Device Save Directory", - "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Opens the directory which contains Application's Device Save", - "GameListContextMenuOpenBcatSaveDirectory": "Open BCAT Save Directory", - "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Opens the directory which contains Application's BCAT Save", - "GameListContextMenuManageTitleUpdates": "Manage Title Updates", - "GameListContextMenuManageTitleUpdatesToolTip": "Opens the Title Update management window", - "GameListContextMenuManageDlc": "Manage DLC", - "GameListContextMenuManageDlcToolTip": "Opens the DLC management window", - "GameListContextMenuCacheManagement": "Cache Management", - "GameListContextMenuCacheManagementPurgePptc": "Queue PPTC Rebuild", - "GameListContextMenuCacheManagementPurgePptcToolTip": "Trigger PPTC to rebuild at boot time on the next game launch", - "GameListContextMenuCacheManagementPurgeShaderCache": "Purge Shader Cache", - "GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "Deletes Application's shader cache", - "GameListContextMenuCacheManagementOpenPptcDirectory": "Open PPTC Directory", - "GameListContextMenuCacheManagementOpenPptcDirectoryToolTip": "Opens the directory which contains Application's PPTC cache", - "GameListContextMenuCacheManagementOpenShaderCacheDirectory": "Open Shader Cache Directory", - "GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip": "Opens the directory which contains Application's shader cache", - "GameListContextMenuExtractData": "Extract Data", - "GameListContextMenuExtractDataExeFS": "ExeFS", - "GameListContextMenuExtractDataExeFSToolTip": "Extract the ExeFS section from Application's current config (including updates)", - "GameListContextMenuExtractDataRomFS": "RomFS", - "GameListContextMenuExtractDataRomFSToolTip": "Extract the RomFS section from Application's current config (including updates)", - "GameListContextMenuExtractDataLogo": "Logo", - "GameListContextMenuExtractDataLogoToolTip": "Extract the Logo section from Application's current config (including updates)", - "GameListContextMenuCreateShortcut": "Create Application Shortcut", - "GameListContextMenuCreateShortcutToolTip": "Create a Desktop Shortcut that launches the selected Application", - "GameListContextMenuCreateShortcutToolTipMacOS": "Create a shortcut in macOS's Applications folder that launches the selected Application", - "GameListContextMenuOpenModsDirectory": "Open Mods Directory", - "GameListContextMenuOpenModsDirectoryToolTip": "Opens the directory which contains Application's Mods", - "GameListContextMenuOpenSdModsDirectory": "Open Atmosphere Mods Directory", - "GameListContextMenuOpenSdModsDirectoryToolTip": "Opens the alternative SD card Atmosphere directory which contains Application's Mods. Useful for mods that are packaged for real hardware.", - "StatusBarGamesLoaded": "{0}/{1} Games Loaded", - "StatusBarSystemVersion": "System Version: {0}", - "LinuxVmMaxMapCountDialogTitle": "Low limit for memory mappings detected", - "LinuxVmMaxMapCountDialogTextPrimary": "Would you like to increase the value of vm.max_map_count to {0}", - "LinuxVmMaxMapCountDialogTextSecondary": "Some games might try to create more memory mappings than currently allowed. Ryujinx will crash as soon as this limit gets exceeded.", - "LinuxVmMaxMapCountDialogButtonUntilRestart": "Yes, until the next restart", - "LinuxVmMaxMapCountDialogButtonPersistent": "Yes, permanently", - "LinuxVmMaxMapCountWarningTextPrimary": "Max amount of memory mappings is lower than recommended.", - "LinuxVmMaxMapCountWarningTextSecondary": "The current value of vm.max_map_count ({0}) is lower than {1}. Some games might try to create more memory mappings than currently allowed. Ryujinx will crash as soon as this limit gets exceeded.\n\nYou might want to either manually increase the limit or install pkexec, which allows Ryujinx to assist with that.", - "Settings": "Settings", - "SettingsTabGeneral": "User Interface", - "SettingsTabGeneralGeneral": "General", - "SettingsTabGeneralEnableDiscordRichPresence": "Enable Discord Rich Presence", - "SettingsTabGeneralCheckUpdatesOnLaunch": "Check for Updates on Launch", - "SettingsTabGeneralShowConfirmExitDialog": "Show \"Confirm Exit\" Dialog", - "SettingsTabGeneralRememberWindowState": "Remember Window Size/Position", - "SettingsTabGeneralHideCursor": "Hide Cursor:", - "SettingsTabGeneralHideCursorNever": "Never", - "SettingsTabGeneralHideCursorOnIdle": "On Idle", - "SettingsTabGeneralHideCursorAlways": "Always", - "SettingsTabGeneralGameDirectories": "Game Directories", - "SettingsTabGeneralAdd": "Add", - "SettingsTabGeneralRemove": "Remove", - "SettingsTabSystem": "System", - "SettingsTabSystemCore": "Core", - "SettingsTabSystemSystemRegion": "System Region:", - "SettingsTabSystemSystemRegionJapan": "Japan", - "SettingsTabSystemSystemRegionUSA": "USA", - "SettingsTabSystemSystemRegionEurope": "Europe", - "SettingsTabSystemSystemRegionAustralia": "Australia", - "SettingsTabSystemSystemRegionChina": "China", - "SettingsTabSystemSystemRegionKorea": "Korea", - "SettingsTabSystemSystemRegionTaiwan": "Taiwan", - "SettingsTabSystemSystemLanguage": "System Language:", - "SettingsTabSystemSystemLanguageJapanese": "Japanese", - "SettingsTabSystemSystemLanguageAmericanEnglish": "American English", - "SettingsTabSystemSystemLanguageFrench": "French", - "SettingsTabSystemSystemLanguageGerman": "German", - "SettingsTabSystemSystemLanguageItalian": "Italian", - "SettingsTabSystemSystemLanguageSpanish": "Spanish", - "SettingsTabSystemSystemLanguageChinese": "Chinese", - "SettingsTabSystemSystemLanguageKorean": "Korean", - "SettingsTabSystemSystemLanguageDutch": "Dutch", - "SettingsTabSystemSystemLanguagePortuguese": "Portuguese", - "SettingsTabSystemSystemLanguageRussian": "Russian", - "SettingsTabSystemSystemLanguageTaiwanese": "Taiwanese", - "SettingsTabSystemSystemLanguageBritishEnglish": "British English", - "SettingsTabSystemSystemLanguageCanadianFrench": "Canadian French", - "SettingsTabSystemSystemLanguageLatinAmericanSpanish": "Latin American Spanish", - "SettingsTabSystemSystemLanguageSimplifiedChinese": "Simplified Chinese", - "SettingsTabSystemSystemLanguageTraditionalChinese": "Traditional Chinese", - "SettingsTabSystemSystemTimeZone": "System TimeZone:", - "SettingsTabSystemSystemTime": "System Time:", - "SettingsTabSystemEnableVsync": "VSync", - "SettingsTabSystemEnablePptc": "PPTC (Profiled Persistent Translation Cache)", - "SettingsTabSystemEnableFsIntegrityChecks": "FS Integrity Checks", - "SettingsTabSystemAudioBackend": "Audio Backend:", - "SettingsTabSystemAudioBackendDummy": "Dummy", - "SettingsTabSystemAudioBackendOpenAL": "OpenAL", - "SettingsTabSystemAudioBackendSoundIO": "SoundIO", - "SettingsTabSystemAudioBackendSDL2": "SDL2", - "SettingsTabSystemHacks": "Hacks", - "SettingsTabSystemHacksNote": "May cause instability", - "SettingsTabSystemExpandDramSize": "Expand DRAM to 8GiB", - "SettingsTabSystemIgnoreMissingServices": "Ignore Missing Services", - "SettingsTabGraphics": "Graphics", - "SettingsTabGraphicsAPI": "Graphics API", - "SettingsTabGraphicsEnableShaderCache": "Enable Shader Cache", - "SettingsTabGraphicsAnisotropicFiltering": "Anisotropic Filtering:", - "SettingsTabGraphicsAnisotropicFilteringAuto": "Auto", - "SettingsTabGraphicsAnisotropicFiltering2x": "2x", - "SettingsTabGraphicsAnisotropicFiltering4x": "4x", - "SettingsTabGraphicsAnisotropicFiltering8x": "8x", - "SettingsTabGraphicsAnisotropicFiltering16x": "16x", - "SettingsTabGraphicsResolutionScale": "Resolution Scale:", - "SettingsTabGraphicsResolutionScaleCustom": "Custom (Not recommended)", - "SettingsTabGraphicsResolutionScaleNative": "Native (720p/1080p)", - "SettingsTabGraphicsResolutionScale2x": "2x (1440p/2160p)", - "SettingsTabGraphicsResolutionScale3x": "3x (2160p/3240p)", - "SettingsTabGraphicsResolutionScale4x": "4x (2880p/4320p) (Not recommended)", - "SettingsTabGraphicsAspectRatio": "Aspect Ratio:", - "SettingsTabGraphicsAspectRatio4x3": "4:3", - "SettingsTabGraphicsAspectRatio16x9": "16:9", - "SettingsTabGraphicsAspectRatio16x10": "16:10", - "SettingsTabGraphicsAspectRatio21x9": "21:9", - "SettingsTabGraphicsAspectRatio32x9": "32:9", - "SettingsTabGraphicsAspectRatioStretch": "Stretch to Fit Window", - "SettingsTabGraphicsDeveloperOptions": "Developer Options", - "SettingsTabGraphicsShaderDumpPath": "Graphics Shader Dump Path:", - "SettingsTabLogging": "Logging", - "SettingsTabLoggingLogging": "Logging", - "SettingsTabLoggingEnableLoggingToFile": "Enable Logging to File", - "SettingsTabLoggingEnableStubLogs": "Enable Stub Logs", - "SettingsTabLoggingEnableInfoLogs": "Enable Info Logs", - "SettingsTabLoggingEnableWarningLogs": "Enable Warning Logs", - "SettingsTabLoggingEnableErrorLogs": "Enable Error Logs", - "SettingsTabLoggingEnableTraceLogs": "Enable Trace Logs", - "SettingsTabLoggingEnableGuestLogs": "Enable Guest Logs", - "SettingsTabLoggingEnableFsAccessLogs": "Enable Fs Access Logs", - "SettingsTabLoggingFsGlobalAccessLogMode": "Fs Global Access Log Mode:", - "SettingsTabLoggingDeveloperOptions": "Developer Options", - "SettingsTabLoggingDeveloperOptionsNote": "WARNING: Will reduce performance", - "SettingsTabLoggingGraphicsBackendLogLevel": "Graphics Backend Log Level:", - "SettingsTabLoggingGraphicsBackendLogLevelNone": "None", - "SettingsTabLoggingGraphicsBackendLogLevelError": "Error", - "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "Slowdowns", - "SettingsTabLoggingGraphicsBackendLogLevelAll": "All", - "SettingsTabLoggingEnableDebugLogs": "Enable Debug Logs", - "SettingsTabInput": "Input", - "SettingsTabInputEnableDockedMode": "Docked Mode", - "SettingsTabInputDirectKeyboardAccess": "Direct Keyboard Access", - "SettingsButtonSave": "Save", - "SettingsButtonClose": "Close", - "SettingsButtonOk": "OK", - "SettingsButtonCancel": "Cancel", - "SettingsButtonApply": "Apply", - "ControllerSettingsPlayer": "Player", - "ControllerSettingsPlayer1": "Player 1", - "ControllerSettingsPlayer2": "Player 2", - "ControllerSettingsPlayer3": "Player 3", - "ControllerSettingsPlayer4": "Player 4", - "ControllerSettingsPlayer5": "Player 5", - "ControllerSettingsPlayer6": "Player 6", - "ControllerSettingsPlayer7": "Player 7", - "ControllerSettingsPlayer8": "Player 8", - "ControllerSettingsHandheld": "Handheld", - "ControllerSettingsInputDevice": "Input Device", - "ControllerSettingsRefresh": "Refresh", - "ControllerSettingsDeviceDisabled": "Disabled", - "ControllerSettingsControllerType": "Controller Type", - "ControllerSettingsControllerTypeHandheld": "Handheld", - "ControllerSettingsControllerTypeProController": "Pro Controller", - "ControllerSettingsControllerTypeJoyConPair": "JoyCon Pair", - "ControllerSettingsControllerTypeJoyConLeft": "JoyCon Left", - "ControllerSettingsControllerTypeJoyConRight": "JoyCon Right", - "ControllerSettingsProfile": "Profile", - "ControllerSettingsProfileDefault": "Default", - "ControllerSettingsLoad": "Load", - "ControllerSettingsAdd": "Add", - "ControllerSettingsRemove": "Remove", - "ControllerSettingsButtons": "Buttons", - "ControllerSettingsButtonA": "A", - "ControllerSettingsButtonB": "B", - "ControllerSettingsButtonX": "X", - "ControllerSettingsButtonY": "Y", - "ControllerSettingsButtonPlus": "+", - "ControllerSettingsButtonMinus": "-", - "ControllerSettingsDPad": "Directional Pad", - "ControllerSettingsDPadUp": "Up", - "ControllerSettingsDPadDown": "Down", - "ControllerSettingsDPadLeft": "Left", - "ControllerSettingsDPadRight": "Right", - "ControllerSettingsStickButton": "Button", - "ControllerSettingsStickUp": "Up", - "ControllerSettingsStickDown": "Down", - "ControllerSettingsStickLeft": "Left", - "ControllerSettingsStickRight": "Right", - "ControllerSettingsStickStick": "Stick", - "ControllerSettingsStickInvertXAxis": "Invert Stick X", - "ControllerSettingsStickInvertYAxis": "Invert Stick Y", - "ControllerSettingsStickDeadzone": "Deadzone:", - "ControllerSettingsLStick": "Left Stick", - "ControllerSettingsRStick": "Right Stick", - "ControllerSettingsTriggersLeft": "Triggers Left", - "ControllerSettingsTriggersRight": "Triggers Right", - "ControllerSettingsTriggersButtonsLeft": "Trigger Buttons Left", - "ControllerSettingsTriggersButtonsRight": "Trigger Buttons Right", - "ControllerSettingsTriggers": "Triggers", - "ControllerSettingsTriggerL": "L", - "ControllerSettingsTriggerR": "R", - "ControllerSettingsTriggerZL": "ZL", - "ControllerSettingsTriggerZR": "ZR", - "ControllerSettingsLeftSL": "SL", - "ControllerSettingsLeftSR": "SR", - "ControllerSettingsRightSL": "SL", - "ControllerSettingsRightSR": "SR", - "ControllerSettingsExtraButtonsLeft": "Buttons Left", - "ControllerSettingsExtraButtonsRight": "Buttons Right", - "ControllerSettingsMisc": "Miscellaneous", - "ControllerSettingsTriggerThreshold": "Trigger Threshold:", - "ControllerSettingsMotion": "Motion", - "ControllerSettingsMotionUseCemuhookCompatibleMotion": "Use CemuHook compatible motion", - "ControllerSettingsMotionControllerSlot": "Controller Slot:", - "ControllerSettingsMotionMirrorInput": "Mirror Input", - "ControllerSettingsMotionRightJoyConSlot": "Right JoyCon Slot:", - "ControllerSettingsMotionServerHost": "Server Host:", - "ControllerSettingsMotionGyroSensitivity": "Gyro Sensitivity:", - "ControllerSettingsMotionGyroDeadzone": "Gyro Deadzone:", - "ControllerSettingsSave": "Save", - "ControllerSettingsClose": "Close", - "KeyUnknown": "Unknown", - "KeyShiftLeft": "Shift Left", - "KeyShiftRight": "Shift Right", - "KeyControlLeft": "Ctrl Left", - "KeyMacControlLeft": "⌃ Left", - "KeyControlRight": "Ctrl Right", - "KeyMacControlRight": "⌃ Right", - "KeyAltLeft": "Alt Left", - "KeyMacAltLeft": "⌥ Left", - "KeyAltRight": "Alt Right", - "KeyMacAltRight": "⌥ Right", - "KeyWinLeft": "⊞ Left", - "KeyMacWinLeft": "⌘ Left", - "KeyWinRight": "⊞ Right", - "KeyMacWinRight": "⌘ Right", - "KeyMenu": "Menu", - "KeyUp": "Up", - "KeyDown": "Down", - "KeyLeft": "Left", - "KeyRight": "Right", - "KeyEnter": "Enter", - "KeyEscape": "Escape", - "KeySpace": "Space", - "KeyTab": "Tab", - "KeyBackSpace": "Backspace", - "KeyInsert": "Insert", - "KeyDelete": "Delete", - "KeyPageUp": "Page Up", - "KeyPageDown": "Page Down", - "KeyHome": "Home", - "KeyEnd": "End", - "KeyCapsLock": "Caps Lock", - "KeyScrollLock": "Scroll Lock", - "KeyPrintScreen": "Print Screen", - "KeyPause": "Pause", - "KeyNumLock": "Num Lock", - "KeyClear": "Clear", - "KeyKeypad0": "Keypad 0", - "KeyKeypad1": "Keypad 1", - "KeyKeypad2": "Keypad 2", - "KeyKeypad3": "Keypad 3", - "KeyKeypad4": "Keypad 4", - "KeyKeypad5": "Keypad 5", - "KeyKeypad6": "Keypad 6", - "KeyKeypad7": "Keypad 7", - "KeyKeypad8": "Keypad 8", - "KeyKeypad9": "Keypad 9", - "KeyKeypadDivide": "Keypad Divide", - "KeyKeypadMultiply": "Keypad Multiply", - "KeyKeypadSubtract": "Keypad Subtract", - "KeyKeypadAdd": "Keypad Add", - "KeyKeypadDecimal": "Keypad Decimal", - "KeyKeypadEnter": "Keypad Enter", - "KeyNumber0": "0", - "KeyNumber1": "1", - "KeyNumber2": "2", - "KeyNumber3": "3", - "KeyNumber4": "4", - "KeyNumber5": "5", - "KeyNumber6": "6", - "KeyNumber7": "7", - "KeyNumber8": "8", - "KeyNumber9": "9", - "KeyTilde": "~", - "KeyGrave": "`", - "KeyMinus": "-", - "KeyPlus": "+", - "KeyBracketLeft": "[", - "KeyBracketRight": "]", - "KeySemicolon": ";", - "KeyQuote": "\"", - "KeyComma": ",", - "KeyPeriod": ".", - "KeySlash": "/", - "KeyBackSlash": "\\", - "KeyUnbound": "Unbound", - "GamepadLeftStick": "L Stick Button", - "GamepadRightStick": "R Stick Button", - "GamepadLeftShoulder": "Left Shoulder", - "GamepadRightShoulder": "Right Shoulder", - "GamepadLeftTrigger": "Left Trigger", - "GamepadRightTrigger": "Right Trigger", - "GamepadDpadUp": "Up", - "GamepadDpadDown": "Down", - "GamepadDpadLeft": "Left", - "GamepadDpadRight": "Right", - "GamepadMinus": "-", - "GamepadPlus": "+", - "GamepadGuide": "Guide", - "GamepadMisc1": "Misc", - "GamepadPaddle1": "Paddle 1", - "GamepadPaddle2": "Paddle 2", - "GamepadPaddle3": "Paddle 3", - "GamepadPaddle4": "Paddle 4", - "GamepadTouchpad": "Touchpad", - "GamepadSingleLeftTrigger0": "Left Trigger 0", - "GamepadSingleRightTrigger0": "Right Trigger 0", - "GamepadSingleLeftTrigger1": "Left Trigger 1", - "GamepadSingleRightTrigger1": "Right Trigger 1", - "StickLeft": "Left Stick", - "StickRight": "Right Stick", - "UserProfilesSelectedUserProfile": "Selected User Profile:", - "UserProfilesSaveProfileName": "Save Profile Name", - "UserProfilesChangeProfileImage": "Change Profile Image", - "UserProfilesAvailableUserProfiles": "Available User Profiles:", - "UserProfilesAddNewProfile": "Create Profile", - "UserProfilesDelete": "Delete", - "UserProfilesClose": "Close", - "ProfileNameSelectionWatermark": "Choose a nickname", - "ProfileImageSelectionTitle": "Profile Image Selection", - "ProfileImageSelectionHeader": "Choose a profile Image", - "ProfileImageSelectionNote": "You may import a custom profile image, or select an avatar from system firmware", - "ProfileImageSelectionImportImage": "Import Image File", - "ProfileImageSelectionSelectAvatar": "Select Firmware Avatar", - "InputDialogTitle": "Input Dialog", - "InputDialogOk": "OK", - "InputDialogCancel": "Cancel", - "InputDialogAddNewProfileTitle": "Choose the Profile Name", - "InputDialogAddNewProfileHeader": "Please Enter a Profile Name", - "InputDialogAddNewProfileSubtext": "(Max Length: {0})", - "AvatarChoose": "Choose Avatar", - "AvatarSetBackgroundColor": "Set Background Color", - "AvatarClose": "Close", - "ControllerSettingsLoadProfileToolTip": "Load Profile", - "ControllerSettingsAddProfileToolTip": "Add Profile", - "ControllerSettingsRemoveProfileToolTip": "Remove Profile", - "ControllerSettingsSaveProfileToolTip": "Save Profile", - "MenuBarFileToolsTakeScreenshot": "Take Screenshot", - "MenuBarFileToolsHideUi": "Hide UI", - "GameListContextMenuRunApplication": "Run Application", - "GameListContextMenuToggleFavorite": "Toggle Favorite", - "GameListContextMenuToggleFavoriteToolTip": "Toggle Favorite status of Game", - "SettingsTabGeneralTheme": "Theme:", - "SettingsTabGeneralThemeAuto": "Auto", - "SettingsTabGeneralThemeDark": "Dark", - "SettingsTabGeneralThemeLight": "Light", - "ControllerSettingsConfigureGeneral": "Configure", - "ControllerSettingsRumble": "Rumble", - "ControllerSettingsRumbleStrongMultiplier": "Strong Rumble Multiplier", - "ControllerSettingsRumbleWeakMultiplier": "Weak Rumble Multiplier", - "DialogMessageSaveNotAvailableMessage": "There is no savedata for {0} [{1:x16}]", - "DialogMessageSaveNotAvailableCreateSaveMessage": "Would you like to create savedata for this game?", - "DialogConfirmationTitle": "Ryujinx - Confirmation", - "DialogUpdaterTitle": "Ryujinx - Updater", - "DialogErrorTitle": "Ryujinx - Error", - "DialogWarningTitle": "Ryujinx - Warning", - "DialogExitTitle": "Ryujinx - Exit", - "DialogErrorMessage": "Ryujinx has encountered an error", - "DialogExitMessage": "Are you sure you want to close Ryujinx?", - "DialogExitSubMessage": "All unsaved data will be lost!", - "DialogMessageCreateSaveErrorMessage": "There was an error creating the specified savedata: {0}", - "DialogMessageFindSaveErrorMessage": "There was an error finding the specified savedata: {0}", - "FolderDialogExtractTitle": "Choose the folder to extract into", - "DialogNcaExtractionMessage": "Extracting {0} section from {1}...", - "DialogNcaExtractionTitle": "Ryujinx - NCA Section Extractor", - "DialogNcaExtractionMainNcaNotFoundErrorMessage": "Extraction failure. The main NCA was not present in the selected file.", - "DialogNcaExtractionCheckLogErrorMessage": "Extraction failure. Read the log file for further information.", - "DialogNcaExtractionSuccessMessage": "Extraction completed successfully.", - "DialogUpdaterConvertFailedMessage": "Failed to convert the current Ryujinx version.", - "DialogUpdaterCancelUpdateMessage": "Cancelling Update!", - "DialogUpdaterAlreadyOnLatestVersionMessage": "You are already using the most updated version of Ryujinx!", - "DialogUpdaterFailedToGetVersionMessage": "An error has occurred when trying to get release information from GitHub Release. This can be caused if a new release is being compiled by GitHub Actions. Try again in a few minutes.", - "DialogUpdaterConvertFailedGithubMessage": "Failed to convert the received Ryujinx version from Github Release.", - "DialogUpdaterDownloadingMessage": "Downloading Update...", - "DialogUpdaterExtractionMessage": "Extracting Update...", - "DialogUpdaterRenamingMessage": "Renaming Update...", - "DialogUpdaterAddingFilesMessage": "Adding New Update...", - "DialogUpdaterCompleteMessage": "Update Complete!", - "DialogUpdaterRestartMessage": "Do you want to restart Ryujinx now?", - "DialogUpdaterNoInternetMessage": "You are not connected to the Internet!", - "DialogUpdaterNoInternetSubMessage": "Please verify that you have a working Internet connection!", - "DialogUpdaterDirtyBuildMessage": "You Cannot update a Dirty build of Ryujinx!", - "DialogUpdaterDirtyBuildSubMessage": "Please download Ryujinx at https://ryujinx.org/ if you are looking for a supported version.", - "DialogRestartRequiredMessage": "Restart Required", - "DialogThemeRestartMessage": "Theme has been saved. A restart is needed to apply the theme.", - "DialogThemeRestartSubMessage": "Do you want to restart", - "DialogFirmwareInstallEmbeddedMessage": "Would you like to install the firmware embedded in this game? (Firmware {0})", - "DialogFirmwareInstallEmbeddedSuccessMessage": "No installed firmware was found but Ryujinx was able to install firmware {0} from the provided game.\nThe emulator will now start.", - "DialogFirmwareNoFirmwareInstalledMessage": "No Firmware Installed", - "DialogFirmwareInstalledMessage": "Firmware {0} was installed", - "DialogInstallFileTypesSuccessMessage": "Successfully installed file types!", - "DialogInstallFileTypesErrorMessage": "Failed to install file types.", - "DialogUninstallFileTypesSuccessMessage": "Successfully uninstalled file types!", - "DialogUninstallFileTypesErrorMessage": "Failed to uninstall file types.", - "DialogOpenSettingsWindowLabel": "Open Settings Window", - "DialogControllerAppletTitle": "Controller Applet", - "DialogMessageDialogErrorExceptionMessage": "Error displaying Message Dialog: {0}", - "DialogSoftwareKeyboardErrorExceptionMessage": "Error displaying Software Keyboard: {0}", - "DialogErrorAppletErrorExceptionMessage": "Error displaying ErrorApplet Dialog: {0}", - "DialogUserErrorDialogMessage": "{0}: {1}", - "DialogUserErrorDialogInfoMessage": "\nFor more information on how to fix this error, follow our Setup Guide.", - "DialogUserErrorDialogTitle": "Ryujinx Error ({0})", - "DialogAmiiboApiTitle": "Amiibo API", - "DialogAmiiboApiFailFetchMessage": "An error occured while fetching information from the API.", - "DialogAmiiboApiConnectErrorMessage": "Unable to connect to Amiibo API server. The service may be down or you may need to verify your internet connection is online.", - "DialogProfileInvalidProfileErrorMessage": "Profile {0} is incompatible with the current input configuration system.", - "DialogProfileDefaultProfileOverwriteErrorMessage": "Default Profile can not be overwritten", - "DialogProfileDeleteProfileTitle": "Deleting Profile", - "DialogProfileDeleteProfileMessage": "This action is irreversible, are you sure you want to continue?", - "DialogWarning": "Warning", - "DialogPPTCDeletionMessage": "You are about to queue a PPTC rebuild on the next boot of:\n\n{0}\n\nAre you sure you want to proceed?", - "DialogPPTCDeletionErrorMessage": "Error purging PPTC cache at {0}: {1}", - "DialogShaderDeletionMessage": "You are about to delete the Shader cache for :\n\n{0}\n\nAre you sure you want to proceed?", - "DialogShaderDeletionErrorMessage": "Error purging Shader cache at {0}: {1}", - "DialogRyujinxErrorMessage": "Ryujinx has encountered an error", - "DialogInvalidTitleIdErrorMessage": "UI error: The selected game did not have a valid title ID", - "DialogFirmwareInstallerFirmwareNotFoundErrorMessage": "A valid system firmware was not found in {0}.", - "DialogFirmwareInstallerFirmwareInstallTitle": "Install Firmware {0}", - "DialogFirmwareInstallerFirmwareInstallMessage": "System version {0} will be installed.", - "DialogFirmwareInstallerFirmwareInstallSubMessage": "\n\nThis will replace the current system version {0}.", - "DialogFirmwareInstallerFirmwareInstallConfirmMessage": "\n\nDo you want to continue?", - "DialogFirmwareInstallerFirmwareInstallWaitMessage": "Installing firmware...", - "DialogFirmwareInstallerFirmwareInstallSuccessMessage": "System version {0} successfully installed.", - "DialogUserProfileDeletionWarningMessage": "There would be no other profiles to be opened if selected profile is deleted", - "DialogUserProfileDeletionConfirmMessage": "Do you want to delete the selected profile", - "DialogUserProfileUnsavedChangesTitle": "Warning - Unsaved Changes", - "DialogUserProfileUnsavedChangesMessage": "You have made changes to this user profile that have not been saved.", - "DialogUserProfileUnsavedChangesSubMessage": "Do you want to discard your changes?", - "DialogControllerSettingsModifiedConfirmMessage": "The current controller settings has been updated.", - "DialogControllerSettingsModifiedConfirmSubMessage": "Do you want to save?", - "DialogLoadFileErrorMessage": "{0}. Errored File: {1}", - "DialogModAlreadyExistsMessage": "Mod already exists", - "DialogModInvalidMessage": "The specified directory does not contain a mod!", - "DialogModDeleteNoParentMessage": "Failed to Delete: Could not find the parent directory for mod \"{0}\"!", - "DialogDlcNoDlcErrorMessage": "The specified file does not contain a DLC for the selected title!", - "DialogPerformanceCheckLoggingEnabledMessage": "You have trace logging enabled, which is designed to be used by developers only.", - "DialogPerformanceCheckLoggingEnabledConfirmMessage": "For optimal performance, it's recommended to disable trace logging. Would you like to disable trace logging now?", - "DialogPerformanceCheckShaderDumpEnabledMessage": "You have shader dumping enabled, which is designed to be used by developers only.", - "DialogPerformanceCheckShaderDumpEnabledConfirmMessage": "For optimal performance, it's recommended to disable shader dumping. Would you like to disable shader dumping now?", - "DialogLoadAppGameAlreadyLoadedMessage": "A game has already been loaded", - "DialogLoadAppGameAlreadyLoadedSubMessage": "Please stop emulation or close the emulator before launching another game.", - "DialogUpdateAddUpdateErrorMessage": "The specified file does not contain an update for the selected title!", - "DialogSettingsBackendThreadingWarningTitle": "Warning - Backend Threading", - "DialogSettingsBackendThreadingWarningMessage": "Ryujinx must be restarted after changing this option for it to apply fully. Depending on your platform, you may need to manually disable your driver's own multithreading when using Ryujinx's.", - "DialogModManagerDeletionWarningMessage": "You are about to delete the mod: {0}\n\nAre you sure you want to proceed?", - "DialogModManagerDeletionAllWarningMessage": "You are about to delete all mods for this title.\n\nAre you sure you want to proceed?", - "SettingsTabGraphicsFeaturesOptions": "Features", - "SettingsTabGraphicsBackendMultithreading": "Graphics Backend Multithreading:", - "CommonAuto": "Auto", - "CommonOff": "Off", - "CommonOn": "On", - "InputDialogYes": "Yes", - "InputDialogNo": "No", - "DialogProfileInvalidProfileNameErrorMessage": "The file name contains invalid characters. Please try again.", - "MenuBarOptionsPauseEmulation": "Pause", - "MenuBarOptionsResumeEmulation": "Resume", - "AboutUrlTooltipMessage": "Click to open the Ryujinx website in your default browser.", - "AboutDisclaimerMessage": "Ryujinx is not affiliated with Nintendo™,\nor any of its partners, in any way.", - "AboutAmiiboDisclaimerMessage": "AmiiboAPI (www.amiiboapi.com) is used\nin our Amiibo emulation.", - "AboutPatreonUrlTooltipMessage": "Click to open the Ryujinx Patreon page in your default browser.", - "AboutGithubUrlTooltipMessage": "Click to open the Ryujinx GitHub page in your default browser.", - "AboutDiscordUrlTooltipMessage": "Click to open an invite to the Ryujinx Discord server in your default browser.", - "AboutTwitterUrlTooltipMessage": "Click to open the Ryujinx Twitter page in your default browser.", - "AboutRyujinxAboutTitle": "About:", - "AboutRyujinxAboutContent": "Ryujinx is an emulator for the Nintendo Switch™.\nPlease support us on Patreon.\nGet all the latest news on our Twitter or Discord.\nDevelopers interested in contributing can find out more on our GitHub or Discord.", - "AboutRyujinxMaintainersTitle": "Maintained By:", - "AboutRyujinxMaintainersContentTooltipMessage": "Click to open the Contributors page in your default browser.", - "AboutRyujinxSupprtersTitle": "Supported on Patreon By:", - "AmiiboSeriesLabel": "Amiibo Series", - "AmiiboCharacterLabel": "Character", - "AmiiboScanButtonLabel": "Scan It", - "AmiiboOptionsShowAllLabel": "Show All Amiibo", - "AmiiboOptionsUsRandomTagLabel": "Hack: Use Random tag Uuid", - "DlcManagerTableHeadingEnabledLabel": "Enabled", - "DlcManagerTableHeadingTitleIdLabel": "Title ID", - "DlcManagerTableHeadingContainerPathLabel": "Container Path", - "DlcManagerTableHeadingFullPathLabel": "Full Path", - "DlcManagerRemoveAllButton": "Remove All", - "DlcManagerEnableAllButton": "Enable All", - "DlcManagerDisableAllButton": "Disable All", - "ModManagerDeleteAllButton": "Delete All", - "MenuBarOptionsChangeLanguage": "Change Language", - "MenuBarShowFileTypes": "Show File Types", - "CommonSort": "Sort", - "CommonShowNames": "Show Names", - "CommonFavorite": "Favorite", - "OrderAscending": "Ascending", - "OrderDescending": "Descending", - "SettingsTabGraphicsFeatures": "Features & Enhancements", - "ErrorWindowTitle": "Error Window", - "ToggleDiscordTooltip": "Choose whether or not to display Ryujinx on your \"currently playing\" Discord activity", - "AddGameDirBoxTooltip": "Enter a game directory to add to the list", - "AddGameDirTooltip": "Add a game directory to the list", - "RemoveGameDirTooltip": "Remove selected game directory", - "CustomThemeCheckTooltip": "Use a custom Avalonia theme for the GUI to change the appearance of the emulator menus", - "CustomThemePathTooltip": "Path to custom GUI theme", - "CustomThemeBrowseTooltip": "Browse for a custom GUI theme", - "DockModeToggleTooltip": "Docked mode makes the emulated system behave as a docked Nintendo Switch. This improves graphical fidelity in most games. Conversely, disabling this will make the emulated system behave as a handheld Nintendo Switch, reducing graphics quality.\n\nConfigure player 1 controls if planning to use docked mode; configure handheld controls if planning to use handheld mode.\n\nLeave ON if unsure.", - "DirectKeyboardTooltip": "Direct keyboard access (HID) support. Provides games access to your keyboard as a text entry device.\n\nOnly works with games that natively support keyboard usage on Switch hardware.\n\nLeave OFF if unsure.", - "DirectMouseTooltip": "Direct mouse access (HID) support. Provides games access to your mouse as a pointing device.\n\nOnly works with games that natively support mouse controls on Switch hardware, which are few and far between.\n\nWhen enabled, touch screen functionality may not work.\n\nLeave OFF if unsure.", - "RegionTooltip": "Change System Region", - "LanguageTooltip": "Change System Language", - "TimezoneTooltip": "Change System TimeZone", - "TimeTooltip": "Change System Time", - "VSyncToggleTooltip": "Emulated console's Vertical Sync. Essentially a frame-limiter for the majority of games; disabling it may cause games to run at higher speed or make loading screens take longer or get stuck.\n\nCan be toggled in-game with a hotkey of your preference (F1 by default). We recommend doing this if you plan on disabling it.\n\nLeave ON if unsure.", - "PptcToggleTooltip": "Saves translated JIT functions so that they do not need to be translated every time the game loads.\n\nReduces stuttering and significantly speeds up boot times after the first boot of a game.\n\nLeave ON if unsure.", - "FsIntegrityToggleTooltip": "Checks for corrupt files when booting a game, and if corrupt files are detected, displays a hash error in the log.\n\nHas no impact on performance and is meant to help troubleshooting.\n\nLeave ON if unsure.", - "AudioBackendTooltip": "Changes the backend used to render audio.\n\nSDL2 is the preferred one, while OpenAL and SoundIO are used as fallbacks. Dummy will have no sound.\n\nSet to SDL2 if unsure.", - "MemoryManagerTooltip": "Change how guest memory is mapped and accessed. Greatly affects emulated CPU performance.\n\nSet to HOST UNCHECKED if unsure.", - "MemoryManagerSoftwareTooltip": "Use a software page table for address translation. Highest accuracy but slowest performance.", - "MemoryManagerHostTooltip": "Directly map memory in the host address space. Much faster JIT compilation and execution.", - "MemoryManagerUnsafeTooltip": "Directly map memory, but do not mask the address within the guest address space before access. Faster, but at the cost of safety. The guest application can access memory from anywhere in Ryujinx, so only run programs you trust with this mode.", - "UseHypervisorTooltip": "Use Hypervisor instead of JIT. Greatly improves performance when available, but can be unstable in its current state.", - "DRamTooltip": "Utilizes an alternative memory mode with 8GiB of DRAM to mimic a Switch development model.\n\nThis is only useful for higher-resolution texture packs or 4k resolution mods. Does NOT improve performance.\n\nLeave OFF if unsure.", - "IgnoreMissingServicesTooltip": "Ignores unimplemented Horizon OS services. This may help in bypassing crashes when booting certain games.\n\nLeave OFF if unsure.", - "GraphicsBackendThreadingTooltip": "Executes graphics backend commands on a second thread.\n\nSpeeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading.\n\nSet to AUTO if unsure.", - "GalThreadingTooltip": "Executes graphics backend commands on a second thread.\n\nSpeeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading.\n\nSet to AUTO if unsure.", - "ShaderCacheToggleTooltip": "Saves a disk shader cache which reduces stuttering in subsequent runs.\n\nLeave ON if unsure.", - "ResolutionScaleTooltip": "Multiplies the game's rendering resolution.\n\nA few games may not work with this and look pixelated even when the resolution is increased; for those games, you may need to find mods that remove anti-aliasing or that increase their internal rendering resolution. For using the latter, you'll likely want to select Native.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nKeep in mind 4x is overkill for virtually any setup.", - "ResolutionScaleEntryTooltip": "Floating point resolution scale, such as 1.5. Non-integral scales are more likely to cause issues or crash.", - "AnisotropyTooltip": "Level of Anisotropic Filtering. Set to Auto to use the value requested by the game.", - "AspectRatioTooltip": "Aspect Ratio applied to the renderer window.\n\nOnly change this if you're using an aspect ratio mod for your game, otherwise the graphics will be stretched.\n\nLeave on 16:9 if unsure.", - "ShaderDumpPathTooltip": "Graphics Shaders Dump Path", - "FileLogTooltip": "Saves console logging to a log file on disk. Does not affect performance.", - "StubLogTooltip": "Prints stub log messages in the console. Does not affect performance.", - "InfoLogTooltip": "Prints info log messages in the console. Does not affect performance.", - "WarnLogTooltip": "Prints warning log messages in the console. Does not affect performance.", - "ErrorLogTooltip": "Prints error log messages in the console. Does not affect performance.", - "TraceLogTooltip": "Prints trace log messages in the console. Does not affect performance.", - "GuestLogTooltip": "Prints guest log messages in the console. Does not affect performance.", - "FileAccessLogTooltip": "Prints file access log messages in the console.", - "FSAccessLogModeTooltip": "Enables FS access log output to the console. Possible modes are 0-3", - "DeveloperOptionTooltip": "Use with care", - "OpenGlLogLevel": "Requires appropriate log levels enabled", - "DebugLogTooltip": "Prints debug log messages in the console.\n\nOnly use this if specifically instructed by a staff member, as it will make logs difficult to read and worsen emulator performance.", - "LoadApplicationFileTooltip": "Open a file explorer to choose a Switch compatible file to load", - "LoadApplicationFolderTooltip": "Open a file explorer to choose a Switch compatible, unpacked application to load", - "OpenRyujinxFolderTooltip": "Open Ryujinx filesystem folder", - "OpenRyujinxLogsTooltip": "Opens the folder where logs are written to", - "ExitTooltip": "Exit Ryujinx", - "OpenSettingsTooltip": "Open settings window", - "OpenProfileManagerTooltip": "Open User Profiles Manager window", - "StopEmulationTooltip": "Stop emulation of the current game and return to game selection", - "CheckUpdatesTooltip": "Check for updates to Ryujinx", - "OpenAboutTooltip": "Open About Window", - "GridSize": "Grid Size", - "GridSizeTooltip": "Change the size of grid items", - "SettingsTabSystemSystemLanguageBrazilianPortuguese": "Brazilian Portuguese", - "AboutRyujinxContributorsButtonHeader": "See All Contributors", - "SettingsTabSystemAudioVolume": "Volume: ", - "AudioVolumeTooltip": "Change Audio Volume", - "SettingsTabSystemEnableInternetAccess": "Guest Internet Access/LAN Mode", - "EnableInternetAccessTooltip": "Allows the emulated application to connect to the Internet.\n\nGames with a LAN mode can connect to each other when this is enabled and the systems are connected to the same access point. This includes real consoles as well.\n\nDoes NOT allow connecting to Nintendo servers. May cause crashing in certain games that try to connect to the Internet.\n\nLeave OFF if unsure.", - "GameListContextMenuManageCheatToolTip": "Manage Cheats", - "GameListContextMenuManageCheat": "Manage Cheats", - "GameListContextMenuManageModToolTip": "Manage Mods", - "GameListContextMenuManageMod": "Manage Mods", - "ControllerSettingsStickRange": "Range:", - "DialogStopEmulationTitle": "Ryujinx - Stop Emulation", - "DialogStopEmulationMessage": "Are you sure you want to stop emulation?", - "SettingsTabCpu": "CPU", - "SettingsTabAudio": "Audio", - "SettingsTabNetwork": "Network", - "SettingsTabNetworkConnection": "Network Connection", - "SettingsTabCpuCache": "CPU Cache", - "SettingsTabCpuMemory": "CPU Mode", - "DialogUpdaterFlatpakNotSupportedMessage": "Please update Ryujinx via FlatHub.", - "UpdaterDisabledWarningTitle": "Updater Disabled!", - "ControllerSettingsRotate90": "Rotate 90° Clockwise", - "IconSize": "Icon Size", - "IconSizeTooltip": "Change the size of game icons", - "MenuBarOptionsShowConsole": "Show Console", - "ShaderCachePurgeError": "Error purging shader cache at {0}: {1}", - "UserErrorNoKeys": "Keys not found", - "UserErrorNoFirmware": "Firmware not found", - "UserErrorFirmwareParsingFailed": "Firmware parsing error", - "UserErrorApplicationNotFound": "Application not found", - "UserErrorUnknown": "Unknown error", - "UserErrorUndefined": "Undefined error", - "UserErrorNoKeysDescription": "Ryujinx was unable to find your 'prod.keys' file", - "UserErrorNoFirmwareDescription": "Ryujinx was unable to find any firmwares installed", - "UserErrorFirmwareParsingFailedDescription": "Ryujinx was unable to parse the provided firmware. This is usually caused by outdated keys.", - "UserErrorApplicationNotFoundDescription": "Ryujinx couldn't find a valid application at the given path.", - "UserErrorUnknownDescription": "An unknown error occured!", - "UserErrorUndefinedDescription": "An undefined error occured! This shouldn't happen, please contact a dev!", - "OpenSetupGuideMessage": "Open the Setup Guide", - "NoUpdate": "No Update", - "TitleUpdateVersionLabel": "Version {0}", - "TitleBundledUpdateVersionLabel": "Bundled: Version {0}", - "TitleBundledDlcLabel": "Bundled:", - "RyujinxInfo": "Ryujinx - Info", - "RyujinxConfirm": "Ryujinx - Confirmation", - "FileDialogAllTypes": "All types", - "Never": "Never", - "SwkbdMinCharacters": "Must be at least {0} characters long", - "SwkbdMinRangeCharacters": "Must be {0}-{1} characters long", - "SoftwareKeyboard": "Software Keyboard", - "SoftwareKeyboardModeNumeric": "Must be 0-9 or '.' only", - "SoftwareKeyboardModeAlphabet": "Must be non CJK-characters only", - "SoftwareKeyboardModeASCII": "Must be ASCII text only", - "ControllerAppletControllers": "Supported Controllers:", - "ControllerAppletPlayers": "Players:", - "ControllerAppletDescription": "Your current configuration is invalid. Open settings and reconfigure your inputs.", - "ControllerAppletDocked": "Docked mode set. Handheld control should be disabled.", - "UpdaterRenaming": "Renaming Old Files...", - "UpdaterRenameFailed": "Updater was unable to rename file: {0}", - "UpdaterAddingFiles": "Adding New Files...", - "UpdaterExtracting": "Extracting Update...", - "UpdaterDownloading": "Downloading Update...", - "Game": "Game", - "Docked": "Docked", - "Handheld": "Handheld", - "ConnectionError": "Connection Error.", - "AboutPageDeveloperListMore": "{0} and more...", - "ApiError": "API Error.", - "LoadingHeading": "Loading {0}", - "CompilingPPTC": "Compiling PTC", - "CompilingShaders": "Compiling Shaders", - "AllKeyboards": "All keyboards", - "OpenFileDialogTitle": "Select a supported file to open", - "OpenFolderDialogTitle": "Select a folder with an unpacked game", - "AllSupportedFormats": "All Supported Formats", - "RyujinxUpdater": "Ryujinx Updater", - "SettingsTabHotkeys": "Keyboard Hotkeys", - "SettingsTabHotkeysHotkeys": "Keyboard Hotkeys", - "SettingsTabHotkeysToggleVsyncHotkey": "Toggle VSync:", - "SettingsTabHotkeysScreenshotHotkey": "Screenshot:", - "SettingsTabHotkeysShowUiHotkey": "Show UI:", - "SettingsTabHotkeysPauseHotkey": "Pause:", - "SettingsTabHotkeysToggleMuteHotkey": "Mute:", - "ControllerMotionTitle": "Motion Control Settings", - "ControllerRumbleTitle": "Rumble Settings", - "SettingsSelectThemeFileDialogTitle": "Select Theme File", - "SettingsXamlThemeFile": "Xaml Theme File", - "AvatarWindowTitle": "Manage Accounts - Avatar", - "Amiibo": "Amiibo", - "Unknown": "Unknown", - "Usage": "Usage", - "Writable": "Writable", - "SelectDlcDialogTitle": "Select DLC files", - "SelectUpdateDialogTitle": "Select update files", - "SelectModDialogTitle": "Select mod directory", - "UserProfileWindowTitle": "User Profiles Manager", - "CheatWindowTitle": "Cheats Manager", - "DlcWindowTitle": "Manage Downloadable Content for {0} ({1})", - "ModWindowTitle": "Manage Mods for {0} ({1})", - "UpdateWindowTitle": "Title Update Manager", - "CheatWindowHeading": "Cheats Available for {0} [{1}]", - "BuildId": "BuildId:", - "DlcWindowHeading": "{0} Downloadable Content(s)", - "ModWindowHeading": "{0} Mod(s)", - "UserProfilesEditProfile": "Edit Selected", - "Cancel": "Cancel", - "Save": "Save", - "Discard": "Discard", - "Paused": "Paused", - "UserProfilesSetProfileImage": "Set Profile Image", - "UserProfileEmptyNameError": "Name is required", - "UserProfileNoImageError": "Profile image must be set", - "GameUpdateWindowHeading": "Manage Updates for {0} ({1})", - "SettingsTabHotkeysResScaleUpHotkey": "Increase resolution:", - "SettingsTabHotkeysResScaleDownHotkey": "Decrease resolution:", - "UserProfilesName": "Name:", - "UserProfilesUserId": "User ID:", - "SettingsTabGraphicsBackend": "Graphics Backend", - "SettingsTabGraphicsBackendTooltip": "Select the graphics backend that will be used in the emulator.\n\nVulkan is overall better for all modern graphics cards, as long as their drivers are up to date. Vulkan also features faster shader compilation (less stuttering) on all GPU vendors.\n\nOpenGL may achieve better results on old Nvidia GPUs, on old AMD GPUs on Linux, or on GPUs with lower VRAM, though shader compilation stutters will be greater.\n\nSet to Vulkan if unsure. Set to OpenGL if your GPU does not support Vulkan even with the latest graphics drivers.", - "SettingsEnableTextureRecompression": "Enable Texture Recompression", - "SettingsEnableTextureRecompressionTooltip": "Compresses ASTC textures in order to reduce VRAM usage.\n\nGames using this texture format include Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder and The Legend of Zelda: Tears of the Kingdom.\n\nGraphics cards with 4GiB VRAM or less will likely crash at some point while running these games.\n\nEnable only if you're running out of VRAM on the aforementioned games. Leave OFF if unsure.", - "SettingsTabGraphicsPreferredGpu": "Preferred GPU", - "SettingsTabGraphicsPreferredGpuTooltip": "Select the graphics card that will be used with the Vulkan graphics backend.\n\nDoes not affect the GPU that OpenGL will use.\n\nSet to the GPU flagged as \"dGPU\" if unsure. If there isn't one, leave untouched.", - "SettingsAppRequiredRestartMessage": "Ryujinx Restart Required", - "SettingsGpuBackendRestartMessage": "Graphics Backend or GPU settings have been modified. This will require a restart to be applied", - "SettingsGpuBackendRestartSubMessage": "Do you want to restart now?", - "RyujinxUpdaterMessage": "Do you want to update Ryujinx to the latest version?", - "SettingsTabHotkeysVolumeUpHotkey": "Increase Volume:", - "SettingsTabHotkeysVolumeDownHotkey": "Decrease Volume:", - "SettingsEnableMacroHLE": "Enable Macro HLE", - "SettingsEnableMacroHLETooltip": "High-level emulation of GPU Macro code.\n\nImproves performance, but may cause graphical glitches in some games.\n\nLeave ON if unsure.", - "SettingsEnableColorSpacePassthrough": "Color Space Passthrough", - "SettingsEnableColorSpacePassthroughTooltip": "Directs the Vulkan backend to pass through color information without specifying a color space. For users with wide gamut displays, this may result in more vibrant colors, at the cost of color correctness.", - "VolumeShort": "Vol", - "UserProfilesManageSaves": "Manage Saves", - "DeleteUserSave": "Do you want to delete user save for this game?", - "IrreversibleActionNote": "This action is not reversible.", - "SaveManagerHeading": "Manage Saves for {0} ({1})", - "SaveManagerTitle": "Save Manager", - "Name": "Name", - "Size": "Size", - "Search": "Search", - "UserProfilesRecoverLostAccounts": "Recover Lost Accounts", - "Recover": "Recover", - "UserProfilesRecoverHeading": "Saves were found for the following accounts", - "UserProfilesRecoverEmptyList": "No profiles to recover", - "GraphicsAATooltip": "Applies anti-aliasing to the game render.\n\nFXAA will blur most of the image, while SMAA will attempt to find jagged edges and smooth them out.\n\nNot recommended to use in conjunction with the FSR scaling filter.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on NONE if unsure.", - "GraphicsAALabel": "Anti-Aliasing:", - "GraphicsScalingFilterLabel": "Scaling Filter:", - "GraphicsScalingFilterTooltip": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nArea scaling is recommended when downscaling resolutions that are larger than the output window. It can be used to achieve a supersampled anti-aliasing effect when downscaling by more than 2x.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.", - "GraphicsScalingFilterBilinear": "Bilinear", - "GraphicsScalingFilterNearest": "Nearest", - "GraphicsScalingFilterFsr": "FSR", - "GraphicsScalingFilterArea": "Area", - "GraphicsScalingFilterLevelLabel": "Level", - "GraphicsScalingFilterLevelTooltip": "Set FSR 1.0 sharpening level. Higher is sharper.", - "SmaaLow": "SMAA Low", - "SmaaMedium": "SMAA Medium", - "SmaaHigh": "SMAA High", - "SmaaUltra": "SMAA Ultra", - "UserEditorTitle": "Edit User", - "UserEditorTitleCreate": "Create User", - "SettingsTabNetworkInterface": "Network Interface:", - "NetworkInterfaceTooltip": "The network interface used for LAN/LDN features.\n\nIn conjunction with a VPN or XLink Kai and a game with LAN support, can be used to spoof a same-network connection over the Internet.\n\nLeave on DEFAULT if unsure.", - "NetworkInterfaceDefault": "Default", - "PackagingShaders": "Packaging Shaders", - "AboutChangelogButton": "View Changelog on GitHub", - "AboutChangelogButtonTooltipMessage": "Click to open the changelog for this version in your default browser.", - "SettingsTabNetworkMultiplayer": "Multiplayer", - "MultiplayerMode": "Mode:", - "MultiplayerModeTooltip": "Change LDN multiplayer mode.\n\nLdnMitm will modify local wireless/local play functionality in games to function as if it were LAN, allowing for local, same-network connections with other Ryujinx instances and hacked Nintendo Switch consoles that have the ldn_mitm module installed.\n\nMultiplayer requires all players to be on the same game version (i.e. Super Smash Bros. Ultimate v13.0.1 can't connect to v13.0.0).\n\nLeave DISABLED if unsure.", - "MultiplayerModeDisabled": "Disabled", - "MultiplayerModeLdnMitm": "ldn_mitm" -} diff --git a/src/Ryujinx/Assets/Locales/es_ES.json b/src/Ryujinx/Assets/Locales/es_ES.json deleted file mode 100644 index e58fa5dcf..000000000 --- a/src/Ryujinx/Assets/Locales/es_ES.json +++ /dev/null @@ -1,780 +0,0 @@ -{ - "Language": "Español (ES)", - "MenuBarFileOpenApplet": "Abrir applet", - "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Abre el editor de Mii en modo autónomo", - "SettingsTabInputDirectMouseAccess": "Acceso directo al ratón", - "SettingsTabSystemMemoryManagerMode": "Modo del administrador de memoria:", - "SettingsTabSystemMemoryManagerModeSoftware": "Software", - "SettingsTabSystemMemoryManagerModeHost": "Host (rápido)", - "SettingsTabSystemMemoryManagerModeHostUnchecked": "Host sin verificación (más rápido, inseguro)", - "SettingsTabSystemUseHypervisor": "Usar hipervisor", - "MenuBarFile": "_Archivo", - "MenuBarFileOpenFromFile": "_Cargar aplicación desde un archivo", - "MenuBarFileOpenUnpacked": "Cargar juego _desempaquetado", - "MenuBarFileOpenEmuFolder": "Abrir carpeta de Ryujinx", - "MenuBarFileOpenLogsFolder": "Abrir carpeta de registros", - "MenuBarFileExit": "_Salir", - "MenuBarOptions": "_Opciones", - "MenuBarOptionsToggleFullscreen": "Cambiar a pantalla completa.", - "MenuBarOptionsStartGamesInFullscreen": "Iniciar juegos en pantalla completa", - "MenuBarOptionsStopEmulation": "Detener emulación", - "MenuBarOptionsSettings": "_Configuración", - "MenuBarOptionsManageUserProfiles": "_Gestionar perfiles de usuario", - "MenuBarActions": "_Acciones", - "MenuBarOptionsSimulateWakeUpMessage": "Simular mensaje de reactivación", - "MenuBarActionsScanAmiibo": "Escanear Amiibo", - "MenuBarTools": "_Herramientas", - "MenuBarToolsInstallFirmware": "Instalar firmware", - "MenuBarFileToolsInstallFirmwareFromFile": "Instalar firmware desde un archivo XCI o ZIP", - "MenuBarFileToolsInstallFirmwareFromDirectory": "Instalar firmware desde una carpeta", - "MenuBarToolsManageFileTypes": "Administrar tipos de archivo", - "MenuBarToolsInstallFileTypes": "Instalar tipos de archivo", - "MenuBarToolsUninstallFileTypes": "Desinstalar tipos de archivo", - "MenuBarView": "_View", - "MenuBarViewWindow": "Window Size", - "MenuBarViewWindow720": "720p", - "MenuBarViewWindow1080": "1080p", - "MenuBarHelp": "_Ayuda", - "MenuBarHelpCheckForUpdates": "Buscar actualizaciones", - "MenuBarHelpAbout": "Acerca de", - "MenuSearch": "Buscar...", - "GameListHeaderFavorite": "Favoritos", - "GameListHeaderIcon": "Icono", - "GameListHeaderApplication": "Nombre", - "GameListHeaderDeveloper": "Desarrollador", - "GameListHeaderVersion": "Versión", - "GameListHeaderTimePlayed": "Tiempo jugado", - "GameListHeaderLastPlayed": "Jugado por última vez", - "GameListHeaderFileExtension": "Extensión", - "GameListHeaderFileSize": "Tamaño del archivo", - "GameListHeaderPath": "Directorio", - "GameListContextMenuOpenUserSaveDirectory": "Abrir carpeta de guardado de este usuario", - "GameListContextMenuOpenUserSaveDirectoryToolTip": "Abre la carpeta que contiene la partida guardada del usuario para esta aplicación", - "GameListContextMenuOpenDeviceSaveDirectory": "Abrir carpeta de guardado del sistema para el usuario actual", - "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Abre la carpeta que contiene la partida guardada del sistema para esta aplicación", - "GameListContextMenuOpenBcatSaveDirectory": "Abrir carpeta de guardado BCAT del usuario", - "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Abrir la carpeta que contiene el guardado BCAT de esta aplicación", - "GameListContextMenuManageTitleUpdates": "Gestionar actualizaciones del juego", - "GameListContextMenuManageTitleUpdatesToolTip": "Abrir la ventana de gestión de actualizaciones de esta aplicación", - "GameListContextMenuManageDlc": "Gestionar DLC", - "GameListContextMenuManageDlcToolTip": "Abrir la ventana de gestión del DLC", - "GameListContextMenuCacheManagement": "Gestión de caché ", - "GameListContextMenuCacheManagementPurgePptc": "Reconstruir PPTC en cola", - "GameListContextMenuCacheManagementPurgePptcToolTip": "Elimina la caché de PPTC de esta aplicación", - "GameListContextMenuCacheManagementPurgeShaderCache": "Limpiar caché de sombreadores", - "GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "Eliminar la caché de sombreadores de esta aplicación", - "GameListContextMenuCacheManagementOpenPptcDirectory": "Abrir carpeta de PPTC", - "GameListContextMenuCacheManagementOpenPptcDirectoryToolTip": "Abrir la carpeta que contiene la caché de PPTC de esta aplicación", - "GameListContextMenuCacheManagementOpenShaderCacheDirectory": "Abrir carpeta de caché de sombreadores", - "GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip": "Abrir la carpeta que contiene la caché de sombreadores de esta aplicación", - "GameListContextMenuExtractData": "Extraer datos", - "GameListContextMenuExtractDataExeFS": "ExeFS", - "GameListContextMenuExtractDataExeFSToolTip": "Extraer la sección ExeFS de la configuración actual de la aplicación (incluyendo actualizaciones)", - "GameListContextMenuExtractDataRomFS": "RomFS", - "GameListContextMenuExtractDataRomFSToolTip": "Extraer la sección RomFS de la configuración actual de la aplicación (incluyendo actualizaciones)", - "GameListContextMenuExtractDataLogo": "Logotipo", - "GameListContextMenuExtractDataLogoToolTip": "Extraer la sección Logo de la configuración actual de la aplicación (incluyendo actualizaciones)", - "GameListContextMenuCreateShortcut": "Crear acceso directo de aplicación", - "GameListContextMenuCreateShortcutToolTip": "Crear un acceso directo en el escritorio que lance la aplicación seleccionada", - "GameListContextMenuCreateShortcutToolTipMacOS": "Crea un acceso directo en la carpeta de Aplicaciones de macOS que inicie la Aplicación seleccionada", - "GameListContextMenuOpenModsDirectory": "Abrir Directorio de Mods", - "GameListContextMenuOpenModsDirectoryToolTip": "Abre el directorio que contiene los Mods de la Aplicación.", - "GameListContextMenuOpenSdModsDirectory": "Abrir Directorio de Mods de Atmosphere\n\n\n\n\n\n", - "GameListContextMenuOpenSdModsDirectoryToolTip": "Abre el directorio alternativo de la tarjeta SD de Atmosphere que contiene los Mods de la Aplicación. Útil para los mods que están empaquetados para el hardware real.", - "StatusBarGamesLoaded": "{0}/{1} juegos cargados", - "StatusBarSystemVersion": "Versión del sistema: {0}", - "LinuxVmMaxMapCountDialogTitle": "Límite inferior para mapeos de memoria detectado", - "LinuxVmMaxMapCountDialogTextPrimary": "¿Quieres aumentar el valor de vm.max_map_count a {0}?", - "LinuxVmMaxMapCountDialogTextSecondary": "Algunos juegos podrían intentar crear más mapeos de memoria de los permitidos. Ryujinx se bloqueará tan pronto como se supere este límite.", - "LinuxVmMaxMapCountDialogButtonUntilRestart": "Sí, hasta el próximo reinicio", - "LinuxVmMaxMapCountDialogButtonPersistent": "Si, permanentemente", - "LinuxVmMaxMapCountWarningTextPrimary": "La cantidad máxima de mapeos de memoria es menor de lo recomendado.", - "LinuxVmMaxMapCountWarningTextSecondary": "El valor actual de vm.max_map_count ({0}) es menor que {1}. Algunos juegos podrían intentar crear más mapeos de memoria de los permitidos actualmente. Ryujinx se bloqueará tan pronto como se supere este límite.\n\nPuede que desee aumentar manualmente el límite o instalar pkexec, lo que permite a Ryujinx ayudar con eso.", - "Settings": "Configuración", - "SettingsTabGeneral": "Interfaz de usuario", - "SettingsTabGeneralGeneral": "General", - "SettingsTabGeneralEnableDiscordRichPresence": "Habilitar estado en Discord", - "SettingsTabGeneralCheckUpdatesOnLaunch": "Buscar actualizaciones al iniciar", - "SettingsTabGeneralShowConfirmExitDialog": "Mostrar diálogo de confirmación al cerrar", - "SettingsTabGeneralRememberWindowState": "Remember Window Size/Position", - "SettingsTabGeneralHideCursor": "Esconder el cursor:", - "SettingsTabGeneralHideCursorNever": "Nunca", - "SettingsTabGeneralHideCursorOnIdle": "Ocultar cursor cuando esté inactivo", - "SettingsTabGeneralHideCursorAlways": "Siempre", - "SettingsTabGeneralGameDirectories": "Carpetas de juegos", - "SettingsTabGeneralAdd": "Agregar", - "SettingsTabGeneralRemove": "Quitar", - "SettingsTabSystem": "Sistema", - "SettingsTabSystemCore": "Núcleo", - "SettingsTabSystemSystemRegion": "Región del sistema:", - "SettingsTabSystemSystemRegionJapan": "Japón", - "SettingsTabSystemSystemRegionUSA": "EEUU", - "SettingsTabSystemSystemRegionEurope": "Europa", - "SettingsTabSystemSystemRegionAustralia": "Australia", - "SettingsTabSystemSystemRegionChina": "China", - "SettingsTabSystemSystemRegionKorea": "Corea", - "SettingsTabSystemSystemRegionTaiwan": "Taiwán", - "SettingsTabSystemSystemLanguage": "Idioma del sistema:", - "SettingsTabSystemSystemLanguageJapanese": "Japonés", - "SettingsTabSystemSystemLanguageAmericanEnglish": "Inglés americano", - "SettingsTabSystemSystemLanguageFrench": "Francés", - "SettingsTabSystemSystemLanguageGerman": "Alemán", - "SettingsTabSystemSystemLanguageItalian": "Italiano", - "SettingsTabSystemSystemLanguageSpanish": "Español", - "SettingsTabSystemSystemLanguageChinese": "Chino", - "SettingsTabSystemSystemLanguageKorean": "Coreano", - "SettingsTabSystemSystemLanguageDutch": "Neerlandés/Holandés", - "SettingsTabSystemSystemLanguagePortuguese": "Portugués", - "SettingsTabSystemSystemLanguageRussian": "Ruso", - "SettingsTabSystemSystemLanguageTaiwanese": "Taiwanés", - "SettingsTabSystemSystemLanguageBritishEnglish": "Inglés británico", - "SettingsTabSystemSystemLanguageCanadianFrench": "Francés canadiense", - "SettingsTabSystemSystemLanguageLatinAmericanSpanish": "Español latinoamericano", - "SettingsTabSystemSystemLanguageSimplifiedChinese": "Chino simplificado", - "SettingsTabSystemSystemLanguageTraditionalChinese": "Chino tradicional", - "SettingsTabSystemSystemTimeZone": "Zona horaria del sistema:", - "SettingsTabSystemSystemTime": "Hora del sistema:", - "SettingsTabSystemEnableVsync": "Sincronización vertical", - "SettingsTabSystemEnablePptc": "PPTC (Cache de Traducción de Perfil Persistente)", - "SettingsTabSystemEnableFsIntegrityChecks": "Comprobar integridad de los archivos", - "SettingsTabSystemAudioBackend": "Motor de audio:", - "SettingsTabSystemAudioBackendDummy": "Vacio", - "SettingsTabSystemAudioBackendOpenAL": "OpenAL", - "SettingsTabSystemAudioBackendSoundIO": "SoundIO", - "SettingsTabSystemAudioBackendSDL2": "SDL2", - "SettingsTabSystemHacks": "Hacks", - "SettingsTabSystemHacksNote": " (Pueden causar inestabilidad)", - "SettingsTabSystemExpandDramSize": "Usar diseño alternativo de memoria (Desarrolladores)", - "SettingsTabSystemIgnoreMissingServices": "Ignorar servicios no implementados", - "SettingsTabGraphics": "Gráficos", - "SettingsTabGraphicsAPI": "API de gráficos", - "SettingsTabGraphicsEnableShaderCache": "Habilitar caché de sombreadores", - "SettingsTabGraphicsAnisotropicFiltering": "Filtro anisotrópico:", - "SettingsTabGraphicsAnisotropicFilteringAuto": "Automático", - "SettingsTabGraphicsAnisotropicFiltering2x": "x2", - "SettingsTabGraphicsAnisotropicFiltering4x": "x4", - "SettingsTabGraphicsAnisotropicFiltering8x": "x8", - "SettingsTabGraphicsAnisotropicFiltering16x": "x16", - "SettingsTabGraphicsResolutionScale": "Escala de resolución:", - "SettingsTabGraphicsResolutionScaleCustom": "Personalizada (no recomendado)", - "SettingsTabGraphicsResolutionScaleNative": "Nativa (720p/1080p)", - "SettingsTabGraphicsResolutionScale2x": "x2 (1440p/2160p)", - "SettingsTabGraphicsResolutionScale3x": "x3 (2160p/3240p)", - "SettingsTabGraphicsResolutionScale4x": "4x (2880p/4320p) (no recomendado)", - "SettingsTabGraphicsAspectRatio": "Relación de aspecto:", - "SettingsTabGraphicsAspectRatio4x3": "4:3", - "SettingsTabGraphicsAspectRatio16x9": "16:9", - "SettingsTabGraphicsAspectRatio16x10": "16:10", - "SettingsTabGraphicsAspectRatio21x9": "21:9", - "SettingsTabGraphicsAspectRatio32x9": "32:9", - "SettingsTabGraphicsAspectRatioStretch": "Estirar a la ventana", - "SettingsTabGraphicsDeveloperOptions": "Opciones de desarrollador", - "SettingsTabGraphicsShaderDumpPath": "Directorio de volcado de sombreadores:", - "SettingsTabLogging": "Registros", - "SettingsTabLoggingLogging": "Registros", - "SettingsTabLoggingEnableLoggingToFile": "Habilitar registro a archivo", - "SettingsTabLoggingEnableStubLogs": "Habilitar registros de Stub", - "SettingsTabLoggingEnableInfoLogs": "Habilitar registros de Info", - "SettingsTabLoggingEnableWarningLogs": "Habilitar registros de Advertencia", - "SettingsTabLoggingEnableErrorLogs": "Habilitar registros de Error", - "SettingsTabLoggingEnableTraceLogs": "Habilitar registros de Rastro", - "SettingsTabLoggingEnableGuestLogs": "Habilitar registros de Guest", - "SettingsTabLoggingEnableFsAccessLogs": "Habilitar registros de Fs Access", - "SettingsTabLoggingFsGlobalAccessLogMode": "Modo de registros Fs Global Access:", - "SettingsTabLoggingDeveloperOptions": "Opciones de desarrollador (ADVERTENCIA: empeorarán el rendimiento)", - "SettingsTabLoggingDeveloperOptionsNote": "ADVERTENCIA: Reducirá el rendimiento", - "SettingsTabLoggingGraphicsBackendLogLevel": "Nivel de registro de backend gráficos:", - "SettingsTabLoggingGraphicsBackendLogLevelNone": "Nada", - "SettingsTabLoggingGraphicsBackendLogLevelError": "Errores", - "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "Ralentizaciones", - "SettingsTabLoggingGraphicsBackendLogLevelAll": "Todo", - "SettingsTabLoggingEnableDebugLogs": "Habilitar registros de debug", - "SettingsTabInput": "Entrada", - "SettingsTabInputEnableDockedMode": "Modo dock/TV", - "SettingsTabInputDirectKeyboardAccess": "Acceso directo al teclado", - "SettingsButtonSave": "Guardar", - "SettingsButtonClose": "Cerrar", - "SettingsButtonOk": "Aceptar", - "SettingsButtonCancel": "Cancelar", - "SettingsButtonApply": "Aplicar", - "ControllerSettingsPlayer": "Jugador", - "ControllerSettingsPlayer1": "Jugador 1", - "ControllerSettingsPlayer2": "Jugador 2", - "ControllerSettingsPlayer3": "Jugador 3", - "ControllerSettingsPlayer4": "Jugador 4", - "ControllerSettingsPlayer5": "Jugador 5", - "ControllerSettingsPlayer6": "Jugador 6", - "ControllerSettingsPlayer7": "Jugador 7", - "ControllerSettingsPlayer8": "Jugador 8", - "ControllerSettingsHandheld": "Portátil", - "ControllerSettingsInputDevice": "Dispositivo de entrada", - "ControllerSettingsRefresh": "Actualizar", - "ControllerSettingsDeviceDisabled": "Deshabilitado", - "ControllerSettingsControllerType": "Tipo de Mando", - "ControllerSettingsControllerTypeHandheld": "Portátil", - "ControllerSettingsControllerTypeProController": "Mando Pro", - "ControllerSettingsControllerTypeJoyConPair": "Doble Joy-Con", - "ControllerSettingsControllerTypeJoyConLeft": "Joy-Con Izquierdo", - "ControllerSettingsControllerTypeJoyConRight": "Joy-Con Derecho", - "ControllerSettingsProfile": "Perfil", - "ControllerSettingsProfileDefault": "Predeterminado", - "ControllerSettingsLoad": "Cargar", - "ControllerSettingsAdd": "Agregar", - "ControllerSettingsRemove": "Quitar", - "ControllerSettingsButtons": "Botones", - "ControllerSettingsButtonA": "A", - "ControllerSettingsButtonB": "B", - "ControllerSettingsButtonX": "X", - "ControllerSettingsButtonY": "Y", - "ControllerSettingsButtonPlus": "+", - "ControllerSettingsButtonMinus": "-", - "ControllerSettingsDPad": "Pad direccional", - "ControllerSettingsDPadUp": "Arriba", - "ControllerSettingsDPadDown": "Abajo", - "ControllerSettingsDPadLeft": "Izquierda", - "ControllerSettingsDPadRight": "Derecha", - "ControllerSettingsStickButton": "Botón", - "ControllerSettingsStickUp": "Arriba", - "ControllerSettingsStickDown": "Abajo", - "ControllerSettingsStickLeft": "Izquierda", - "ControllerSettingsStickRight": "Derecha", - "ControllerSettingsStickStick": "Palanca", - "ControllerSettingsStickInvertXAxis": "Invertir eje X", - "ControllerSettingsStickInvertYAxis": "Invertir eje Y", - "ControllerSettingsStickDeadzone": "Zona muerta:", - "ControllerSettingsLStick": "Palanca izquierda", - "ControllerSettingsRStick": "Palanca derecha", - "ControllerSettingsTriggersLeft": "Gatillos izquierdos", - "ControllerSettingsTriggersRight": "Gatillos derechos", - "ControllerSettingsTriggersButtonsLeft": "Botones de gatillo izquierdos", - "ControllerSettingsTriggersButtonsRight": "Botones de gatillo derechos", - "ControllerSettingsTriggers": "Gatillos", - "ControllerSettingsTriggerL": "L", - "ControllerSettingsTriggerR": "R", - "ControllerSettingsTriggerZL": "ZL", - "ControllerSettingsTriggerZR": "ZR", - "ControllerSettingsLeftSL": "SL", - "ControllerSettingsLeftSR": "SR", - "ControllerSettingsRightSL": "SL", - "ControllerSettingsRightSR": "SR", - "ControllerSettingsExtraButtonsLeft": "Botones izquierdos", - "ControllerSettingsExtraButtonsRight": "Botones derechos", - "ControllerSettingsMisc": "Misceláneo", - "ControllerSettingsTriggerThreshold": "Límite de gatillos:", - "ControllerSettingsMotion": "Movimiento", - "ControllerSettingsMotionUseCemuhookCompatibleMotion": "Usar movimiento compatible con CemuHook", - "ControllerSettingsMotionControllerSlot": "Puerto del mando:", - "ControllerSettingsMotionMirrorInput": "Paralelizar derecho e izquierdo", - "ControllerSettingsMotionRightJoyConSlot": "Puerto del Joy-Con derecho:", - "ControllerSettingsMotionServerHost": "Host del servidor:", - "ControllerSettingsMotionGyroSensitivity": "Sensibilidad de Gyro:", - "ControllerSettingsMotionGyroDeadzone": "Zona muerta de Gyro:", - "ControllerSettingsSave": "Guardar", - "ControllerSettingsClose": "Cerrar", - "KeyUnknown": "Desconocido", - "KeyShiftLeft": "Shift Left", - "KeyShiftRight": "Shift Right", - "KeyControlLeft": "Ctrl Left", - "KeyMacControlLeft": "⌃ Left", - "KeyControlRight": "Ctrl Right", - "KeyMacControlRight": "⌃ Right", - "KeyAltLeft": "Alt Left", - "KeyMacAltLeft": "⌥ Left", - "KeyAltRight": "Alt Right", - "KeyMacAltRight": "⌥ Right", - "KeyWinLeft": "⊞ Left", - "KeyMacWinLeft": "⌘ Left", - "KeyWinRight": "⊞ Right", - "KeyMacWinRight": "⌘ Right", - "KeyMenu": "Menu", - "KeyUp": "Up", - "KeyDown": "Down", - "KeyLeft": "Left", - "KeyRight": "Right", - "KeyEnter": "Enter", - "KeyEscape": "Escape", - "KeySpace": "Space", - "KeyTab": "Tab", - "KeyBackSpace": "Backspace", - "KeyInsert": "Insert", - "KeyDelete": "Delete", - "KeyPageUp": "Page Up", - "KeyPageDown": "Page Down", - "KeyHome": "Home", - "KeyEnd": "End", - "KeyCapsLock": "Caps Lock", - "KeyScrollLock": "Scroll Lock", - "KeyPrintScreen": "Print Screen", - "KeyPause": "Pause", - "KeyNumLock": "Num Lock", - "KeyClear": "Clear", - "KeyKeypad0": "Keypad 0", - "KeyKeypad1": "Keypad 1", - "KeyKeypad2": "Keypad 2", - "KeyKeypad3": "Keypad 3", - "KeyKeypad4": "Keypad 4", - "KeyKeypad5": "Keypad 5", - "KeyKeypad6": "Keypad 6", - "KeyKeypad7": "Keypad 7", - "KeyKeypad8": "Keypad 8", - "KeyKeypad9": "Keypad 9", - "KeyKeypadDivide": "Keypad Divide", - "KeyKeypadMultiply": "Keypad Multiply", - "KeyKeypadSubtract": "Keypad Subtract", - "KeyKeypadAdd": "Keypad Add", - "KeyKeypadDecimal": "Keypad Decimal", - "KeyKeypadEnter": "Keypad Enter", - "KeyNumber0": "0", - "KeyNumber1": "1", - "KeyNumber2": "2", - "KeyNumber3": "3", - "KeyNumber4": "4", - "KeyNumber5": "5", - "KeyNumber6": "6", - "KeyNumber7": "7", - "KeyNumber8": "8", - "KeyNumber9": "9", - "KeyTilde": "~", - "KeyGrave": "`", - "KeyMinus": "-", - "KeyPlus": "+", - "KeyBracketLeft": "[", - "KeyBracketRight": "]", - "KeySemicolon": ";", - "KeyQuote": "\"", - "KeyComma": ",", - "KeyPeriod": ".", - "KeySlash": "/", - "KeyBackSlash": "\\", - "KeyUnbound": "Unbound", - "GamepadLeftStick": "L Stick Button", - "GamepadRightStick": "R Stick Button", - "GamepadLeftShoulder": "Left Shoulder", - "GamepadRightShoulder": "Right Shoulder", - "GamepadLeftTrigger": "Left Trigger", - "GamepadRightTrigger": "Right Trigger", - "GamepadDpadUp": "Up", - "GamepadDpadDown": "Down", - "GamepadDpadLeft": "Left", - "GamepadDpadRight": "Right", - "GamepadMinus": "-", - "GamepadPlus": "+", - "GamepadGuide": "Guide", - "GamepadMisc1": "Misc", - "GamepadPaddle1": "Paddle 1", - "GamepadPaddle2": "Paddle 2", - "GamepadPaddle3": "Paddle 3", - "GamepadPaddle4": "Paddle 4", - "GamepadTouchpad": "Touchpad", - "GamepadSingleLeftTrigger0": "Left Trigger 0", - "GamepadSingleRightTrigger0": "Right Trigger 0", - "GamepadSingleLeftTrigger1": "Left Trigger 1", - "GamepadSingleRightTrigger1": "Right Trigger 1", - "StickLeft": "Left Stick", - "StickRight": "Right Stick", - "UserProfilesSelectedUserProfile": "Perfil de usuario seleccionado:", - "UserProfilesSaveProfileName": "Guardar nombre de perfil", - "UserProfilesChangeProfileImage": "Cambiar imagen de perfil", - "UserProfilesAvailableUserProfiles": "Perfiles de usuario disponibles:", - "UserProfilesAddNewProfile": "Añadir nuevo perfil", - "UserProfilesDelete": "Eliminar", - "UserProfilesClose": "Cerrar", - "ProfileNameSelectionWatermark": "Escoge un apodo", - "ProfileImageSelectionTitle": "Selección de imagen de perfil", - "ProfileImageSelectionHeader": "Elige una imagen de perfil", - "ProfileImageSelectionNote": "Puedes importar una imagen de perfil personalizada, o seleccionar un avatar del firmware de sistema", - "ProfileImageSelectionImportImage": "Importar imagen", - "ProfileImageSelectionSelectAvatar": "Seleccionar avatar del firmware", - "InputDialogTitle": "Cuadro de diálogo de entrada", - "InputDialogOk": "Aceptar", - "InputDialogCancel": "Cancelar", - "InputDialogAddNewProfileTitle": "Introducir nombre de perfil", - "InputDialogAddNewProfileHeader": "Por favor elige un nombre de usuario", - "InputDialogAddNewProfileSubtext": "(Máximo de caracteres: {0})", - "AvatarChoose": "Escoger", - "AvatarSetBackgroundColor": "Establecer color de fondo", - "AvatarClose": "Cerrar", - "ControllerSettingsLoadProfileToolTip": "Cargar perfil", - "ControllerSettingsAddProfileToolTip": "Agregar perfil", - "ControllerSettingsRemoveProfileToolTip": "Eliminar perfil", - "ControllerSettingsSaveProfileToolTip": "Guardar perfil", - "MenuBarFileToolsTakeScreenshot": "Captura de pantalla", - "MenuBarFileToolsHideUi": "Ocultar interfaz", - "GameListContextMenuRunApplication": "Ejecutar aplicación", - "GameListContextMenuToggleFavorite": "Marcar favorito", - "GameListContextMenuToggleFavoriteToolTip": "Marca o desmarca el juego como favorito", - "SettingsTabGeneralTheme": "Tema:", - "SettingsTabGeneralThemeDark": "Oscuro", - "SettingsTabGeneralThemeLight": "Claro", - "ControllerSettingsConfigureGeneral": "Configurar", - "ControllerSettingsRumble": "Vibración", - "ControllerSettingsRumbleStrongMultiplier": "Multiplicador de vibraciones fuertes", - "ControllerSettingsRumbleWeakMultiplier": "Multiplicador de vibraciones débiles", - "DialogMessageSaveNotAvailableMessage": "No hay datos de guardado para {0} [{1:x16}]", - "DialogMessageSaveNotAvailableCreateSaveMessage": "¿Quieres crear datos de guardado para este juego?", - "DialogConfirmationTitle": "Ryujinx - Confirmación", - "DialogUpdaterTitle": "Ryujinx - Actualizador", - "DialogErrorTitle": "Ryujinx - Error", - "DialogWarningTitle": "Ryujinx - Advertencia", - "DialogExitTitle": "Ryujinx - Salir", - "DialogErrorMessage": "Ryujinx encontró un error", - "DialogExitMessage": "¿Seguro que quieres cerrar Ryujinx?", - "DialogExitSubMessage": "¡Se perderán los datos no guardados!", - "DialogMessageCreateSaveErrorMessage": "Hubo un error al crear los datos de guardado especificados: {0}", - "DialogMessageFindSaveErrorMessage": "Hubo un error encontrando los datos de guardado especificados: {0}", - "FolderDialogExtractTitle": "Elige la carpeta en la que deseas extraer", - "DialogNcaExtractionMessage": "Extrayendo {0} sección de {1}...", - "DialogNcaExtractionTitle": "Ryujinx - Extractor de sección NCA", - "DialogNcaExtractionMainNcaNotFoundErrorMessage": "Fallo de extracción. El NCA principal no estaba presente en el archivo seleccionado.", - "DialogNcaExtractionCheckLogErrorMessage": "Fallo de extracción. Lee el registro para más información.", - "DialogNcaExtractionSuccessMessage": "Se completó la extracción con éxito.", - "DialogUpdaterConvertFailedMessage": "No se pudo convertir la versión actual de Ryujinx.", - "DialogUpdaterCancelUpdateMessage": "¡Cancelando actualización!", - "DialogUpdaterAlreadyOnLatestVersionMessage": "¡Ya tienes la versión más reciente de Ryujinx!", - "DialogUpdaterFailedToGetVersionMessage": "Se ha producido un error al intentar obtener información de liberación de GitHub Release. Esto puede ser causado si una nueva versión está siendo compilada por GitHub Actions. Inténtalo de nuevo en unos minutos.", - "DialogUpdaterConvertFailedGithubMessage": "No se pudo convertir la versión de Ryujinx recibida de GitHub Release.", - "DialogUpdaterDownloadingMessage": "Descargando actualización...", - "DialogUpdaterExtractionMessage": "Extrayendo actualización...", - "DialogUpdaterRenamingMessage": "Renombrando actualización...", - "DialogUpdaterAddingFilesMessage": "Aplicando actualización...", - "DialogUpdaterCompleteMessage": "¡Actualización completa!", - "DialogUpdaterRestartMessage": "¿Quieres reiniciar Ryujinx?", - "DialogUpdaterNoInternetMessage": "¡No estás conectado a internet!", - "DialogUpdaterNoInternetSubMessage": "¡Por favor, verifica que tu conexión a Internet funciona!", - "DialogUpdaterDirtyBuildMessage": "¡No puedes actualizar una versión \"dirty\" de Ryujinx!", - "DialogUpdaterDirtyBuildSubMessage": "Por favor, descarga Ryujinx en https://ryujinx.org/ si buscas una versión con soporte.", - "DialogRestartRequiredMessage": "Se necesita reiniciar", - "DialogThemeRestartMessage": "Tema guardado. Se necesita reiniciar para aplicar el tema.", - "DialogThemeRestartSubMessage": "¿Quieres reiniciar?", - "DialogFirmwareInstallEmbeddedMessage": "¿Quieres instalar el firmware incluido en este juego? (Firmware versión {0})", - "DialogFirmwareInstallEmbeddedSuccessMessage": "No installed firmware was found but Ryujinx was able to install firmware {0} from the provided game.\nThe emulator will now start.", - "DialogFirmwareNoFirmwareInstalledMessage": "No hay firmware instalado", - "DialogFirmwareInstalledMessage": "Se instaló el firmware {0}", - "DialogInstallFileTypesSuccessMessage": "¡Tipos de archivos instalados con éxito!", - "DialogInstallFileTypesErrorMessage": "No se pudo desinstalar los tipos de archivo.", - "DialogUninstallFileTypesSuccessMessage": "¡Tipos de archivos desinstalados con éxito!", - "DialogUninstallFileTypesErrorMessage": "No se pudo desinstalar los tipos de archivo.", - "DialogOpenSettingsWindowLabel": "Abrir ventana de opciones", - "DialogControllerAppletTitle": "Applet de mandos", - "DialogMessageDialogErrorExceptionMessage": "Error al mostrar cuadro de diálogo: {0}", - "DialogSoftwareKeyboardErrorExceptionMessage": "Error al mostrar teclado de software: {0}", - "DialogErrorAppletErrorExceptionMessage": "Error al mostrar díalogo ErrorApplet: {0}", - "DialogUserErrorDialogMessage": "{0}: {1}", - "DialogUserErrorDialogInfoMessage": "\nPara más información sobre cómo arreglar este error, sigue nuestra Guía de Instalación.", - "DialogUserErrorDialogTitle": "Ryujinx Error ({0})", - "DialogAmiiboApiTitle": "Amiibo API", - "DialogAmiiboApiFailFetchMessage": "Ocurrió un error al recibir información de la API.", - "DialogAmiiboApiConnectErrorMessage": "No se pudo conectar al servidor de la API Amiibo. El servicio puede estar caído o tu conexión a internet puede haberse desconectado.", - "DialogProfileInvalidProfileErrorMessage": "El perfil {0} no es compatible con el sistema actual de configuración de entrada.", - "DialogProfileDefaultProfileOverwriteErrorMessage": "El perfil predeterminado no se puede sobreescribir", - "DialogProfileDeleteProfileTitle": "Eliminando perfil", - "DialogProfileDeleteProfileMessage": "Esta acción es irreversible, ¿estás seguro de querer continuar?", - "DialogWarning": "Advertencia", - "DialogPPTCDeletionMessage": "Vas a borrar la caché de PPTC para:\n\n{0}\n\n¿Estás seguro de querer continuar?", - "DialogPPTCDeletionErrorMessage": "Error purgando la caché de PPTC en {0}: {1}", - "DialogShaderDeletionMessage": "Vas a borrar la caché de sombreadores para:\n\n{0}\n\n¿Estás seguro de querer continuar?", - "DialogShaderDeletionErrorMessage": "Error purgando la caché de sombreadores en {0}: {1}", - "DialogRyujinxErrorMessage": "Ryujinx ha encontrado un error", - "DialogInvalidTitleIdErrorMessage": "Error de interfaz: El juego seleccionado no tiene una ID válida", - "DialogFirmwareInstallerFirmwareNotFoundErrorMessage": "No se pudo encontrar un firmware válido en {0}.", - "DialogFirmwareInstallerFirmwareInstallTitle": "Instalar firmware {0}", - "DialogFirmwareInstallerFirmwareInstallMessage": "Se instalará la versión de sistema {0}.", - "DialogFirmwareInstallerFirmwareInstallSubMessage": "\n\nEsto reemplazará la versión de sistema actual, {0}.", - "DialogFirmwareInstallerFirmwareInstallConfirmMessage": "\n\n¿Continuar?", - "DialogFirmwareInstallerFirmwareInstallWaitMessage": "Instalando firmware...", - "DialogFirmwareInstallerFirmwareInstallSuccessMessage": "Versión de sistema {0} instalada con éxito.", - "DialogUserProfileDeletionWarningMessage": "Si eliminas el perfil seleccionado no quedará ningún otro perfil", - "DialogUserProfileDeletionConfirmMessage": "¿Quieres eliminar el perfil seleccionado?", - "DialogUserProfileUnsavedChangesTitle": "Advertencia - Cambios sin guardar", - "DialogUserProfileUnsavedChangesMessage": "Ha realizado cambios en este perfil de usuario que no han sido guardados.", - "DialogUserProfileUnsavedChangesSubMessage": "¿Quieres descartar los cambios realizados?", - "DialogControllerSettingsModifiedConfirmMessage": "Se ha actualizado la configuración del mando actual.", - "DialogControllerSettingsModifiedConfirmSubMessage": "¿Guardar cambios?", - "DialogLoadFileErrorMessage": "{0}. Archivo con error: {1}", - "DialogModAlreadyExistsMessage": "El mod ya existe", - "DialogModInvalidMessage": "¡El directorio especificado no contiene un mod!", - "DialogModDeleteNoParentMessage": "Error al eliminar: ¡No se pudo encontrar el directorio principal para el mod \"{0}\"!", - "DialogDlcNoDlcErrorMessage": "¡Ese archivo no contiene contenido descargable para el título seleccionado!", - "DialogPerformanceCheckLoggingEnabledMessage": "Has habilitado los registros debug, diseñados solo para uso de los desarrolladores.", - "DialogPerformanceCheckLoggingEnabledConfirmMessage": "Para un rendimiento óptimo, se recomienda deshabilitar los registros debug. ¿Quieres deshabilitarlos ahora?", - "DialogPerformanceCheckShaderDumpEnabledMessage": "Has habilitado el volcado de sombreadores, diseñado solo para uso de los desarrolladores.", - "DialogPerformanceCheckShaderDumpEnabledConfirmMessage": "Para un rendimiento óptimo, se recomienda deshabilitar el volcado de sombreadores. ¿Quieres deshabilitarlo ahora?", - "DialogLoadAppGameAlreadyLoadedMessage": "Ya has cargado un juego", - "DialogLoadAppGameAlreadyLoadedSubMessage": "Por favor, detén la emulación o cierra el emulador antes de iniciar otro juego.", - "DialogUpdateAddUpdateErrorMessage": "¡Ese archivo no contiene una actualización para el título seleccionado!", - "DialogSettingsBackendThreadingWarningTitle": "Advertencia - multihilado de gráficos", - "DialogSettingsBackendThreadingWarningMessage": "Ryujinx debe reiniciarse para aplicar este cambio. Dependiendo de tu plataforma, puede que tengas que desactivar manualmente la optimización enlazada de tus controladores gráficos para usar el multihilo de Ryujinx.", - "DialogModManagerDeletionWarningMessage": "Estás a punto de eliminar el mod: {0}\n\n¿Estás seguro de que quieres continuar?", - "DialogModManagerDeletionAllWarningMessage": "Estás a punto de eliminar todos los Mods para este título.\n\n¿Estás seguro de que quieres continuar?", - "SettingsTabGraphicsFeaturesOptions": "Funcionalidades", - "SettingsTabGraphicsBackendMultithreading": "Multihilado del motor gráfico:", - "CommonAuto": "Automático", - "CommonOff": "Desactivado", - "CommonOn": "Activado", - "InputDialogYes": "Sí", - "InputDialogNo": "No", - "DialogProfileInvalidProfileNameErrorMessage": "El nombre de archivo contiene caracteres inválidos. Por favor, inténtalo de nuevo.", - "MenuBarOptionsPauseEmulation": "Pausar", - "MenuBarOptionsResumeEmulation": "Reanudar", - "AboutUrlTooltipMessage": "Haz clic para abrir el sitio web de Ryujinx en tu navegador predeterminado.", - "AboutDisclaimerMessage": "Ryujinx no tiene afiliación alguna con Nintendo™,\nni con ninguno de sus socios.", - "AboutAmiiboDisclaimerMessage": "Utilizamos AmiiboAPI (www.amiiboapi.com)\nen nuestra emulación de Amiibo.", - "AboutPatreonUrlTooltipMessage": "Haz clic para abrir el Patreon de Ryujinx en tu navegador predeterminado.", - "AboutGithubUrlTooltipMessage": "Haz clic para abrir el GitHub de Ryujinx en tu navegador predeterminado.", - "AboutDiscordUrlTooltipMessage": "Haz clic para recibir una invitación al Discord de Ryujinx en tu navegador predeterminado.", - "AboutTwitterUrlTooltipMessage": "Haz clic para abrir el Twitter de Ryujinx en tu navegador predeterminado.", - "AboutRyujinxAboutTitle": "Acerca de:", - "AboutRyujinxAboutContent": "Ryujinx es un emulador para Nintendo Switch™.\nPor favor, apóyanos en Patreon.\nEncuentra las noticias más recientes en nuestro Twitter o Discord.\nDesarrolladores interesados en contribuir pueden encontrar más información en GitHub o Discord.", - "AboutRyujinxMaintainersTitle": "Mantenido por:", - "AboutRyujinxMaintainersContentTooltipMessage": "Haz clic para abrir la página de contribuidores en tu navegador predeterminado.", - "AboutRyujinxSupprtersTitle": "Apoyado en Patreon Por:", - "AmiiboSeriesLabel": "Serie de Amiibo", - "AmiiboCharacterLabel": "Personaje", - "AmiiboScanButtonLabel": "Escanear", - "AmiiboOptionsShowAllLabel": "Mostrar todos los Amiibo", - "AmiiboOptionsUsRandomTagLabel": "Hack: usar etiqueta aleatoria Uuid", - "DlcManagerTableHeadingEnabledLabel": "Habilitado", - "DlcManagerTableHeadingTitleIdLabel": "ID de título", - "DlcManagerTableHeadingContainerPathLabel": "Directorio del contenedor", - "DlcManagerTableHeadingFullPathLabel": "Directorio completo", - "DlcManagerRemoveAllButton": "Quitar todo", - "DlcManagerEnableAllButton": "Activar todas", - "DlcManagerDisableAllButton": "Desactivar todos", - "ModManagerDeleteAllButton": "Eliminar Todo", - "MenuBarOptionsChangeLanguage": "Cambiar idioma", - "MenuBarShowFileTypes": "Mostrar tipos de archivo", - "CommonSort": "Orden", - "CommonShowNames": "Mostrar nombres", - "CommonFavorite": "Favorito", - "OrderAscending": "Ascendente", - "OrderDescending": "Descendente", - "SettingsTabGraphicsFeatures": "Funcionalidades Y Mejoras", - "ErrorWindowTitle": "Ventana de error", - "ToggleDiscordTooltip": "Elige si muestras Ryujinx o no en tu actividad de Discord cuando lo estés usando", - "AddGameDirBoxTooltip": "Elige un directorio de juegos para mostrar en la ventana principal", - "AddGameDirTooltip": "Agrega un directorio de juegos a la lista", - "RemoveGameDirTooltip": "Quita el directorio seleccionado de la lista", - "CustomThemeCheckTooltip": "Activa o desactiva los temas personalizados para la interfaz", - "CustomThemePathTooltip": "Carpeta que contiene los temas personalizados para la interfaz", - "CustomThemeBrowseTooltip": "Busca un tema personalizado para la interfaz", - "DockModeToggleTooltip": "El modo dock o modo TV hace que la consola emulada se comporte como una Nintendo Switch en su dock. Esto mejora la calidad gráfica en la mayoría de los juegos. Del mismo modo, si lo desactivas, el sistema emulado se comportará como una Nintendo Switch en modo portátil, reduciendo la cálidad de los gráficos.\n\nConfigura los controles de \"Jugador\" 1 si planeas jugar en modo dock/TV; configura los controles de \"Portátil\" si planeas jugar en modo portátil.\n\nActívalo si no sabes qué hacer.", - "DirectKeyboardTooltip": "Direct keyboard access (HID) support. Provides games access to your keyboard as a text entry device.\n\nOnly works with games that natively support keyboard usage on Switch hardware.\n\nLeave OFF if unsure.", - "DirectMouseTooltip": "Direct mouse access (HID) support. Provides games access to your mouse as a pointing device.\n\nOnly works with games that natively support mouse controls on Switch hardware, which are few and far between.\n\nWhen enabled, touch screen functionality may not work.\n\nLeave OFF if unsure.", - "RegionTooltip": "Cambia la región del sistema", - "LanguageTooltip": "Cambia el idioma del sistema", - "TimezoneTooltip": "Cambia la zona horaria del sistema", - "TimeTooltip": "Cambia la hora del sistema", - "VSyncToggleTooltip": "Emulated console's Vertical Sync. Essentially a frame-limiter for the majority of games; disabling it may cause games to run at higher speed or make loading screens take longer or get stuck.\n\nCan be toggled in-game with a hotkey of your preference (F1 by default). We recommend doing this if you plan on disabling it.\n\nLeave ON if unsure.", - "PptcToggleTooltip": "Guarda funciones de JIT traducidas para que no sea necesario traducirlas cada vez que el juego carga.\n\nReduce los tirones y acelera significativamente el tiempo de inicio de los juegos después de haberlos ejecutado al menos una vez.\n\nActívalo si no sabes qué hacer.", - "FsIntegrityToggleTooltip": "Comprueba si hay archivos corruptos en los juegos que ejecutes al abrirlos, y si detecta archivos corruptos, muestra un error de Hash en los registros.\n\nEsto no tiene impacto alguno en el rendimiento y está pensado para ayudar a resolver problemas.\n\nActívalo si no sabes qué hacer.", - "AudioBackendTooltip": "Cambia el motor usado para renderizar audio.\n\nSDL2 es el preferido, mientras que OpenAL y SoundIO se usan si hay problemas con este. Dummy no produce audio.\n\nSelecciona SDL2 si no sabes qué hacer.", - "MemoryManagerTooltip": "Cambia la forma de mapear y acceder a la memoria del guest. Afecta en gran medida al rendimiento de la CPU emulada.\n\nSelecciona \"Host sin verificación\" si no sabes qué hacer.", - "MemoryManagerSoftwareTooltip": "Usa una tabla de paginación de software para traducir direcciones. Ofrece la precisión más exacta pero el rendimiento más lento.", - "MemoryManagerHostTooltip": "Mapea la memoria directamente en la dirección de espacio del host. Compilación y ejecución JIT mucho más rápida.", - "MemoryManagerUnsafeTooltip": "Mapea la memoria directamente, pero no enmascara la dirección dentro del espacio de dirección del guest antes del acceso. El modo más rápido, pero a costa de seguridad. La aplicación guest puede acceder a la memoria desde cualquier parte en Ryujinx, así que ejecuta solo programas en los que confíes cuando uses este modo.", - "UseHypervisorTooltip": "Usar Hypervisor en lugar de JIT. Mejora enormemente el rendimiento cuando está disponible, pero puede ser inestable en su estado actual.", - "DRamTooltip": "Expande la memoria DRAM del sistema emulado de 4GiB a 6GiB.\n\nUtilizar solo con packs de texturas HD o mods de resolución 4K. NO mejora el rendimiento.\n\nDesactívalo si no sabes qué hacer.", - "IgnoreMissingServicesTooltip": "Hack para ignorar servicios no implementados del Horizon OS. Esto puede ayudar a sobrepasar crasheos cuando inicies ciertos juegos.\n\nDesactívalo si no sabes qué hacer.", - "GraphicsBackendThreadingTooltip": "Ejecuta los comandos del motor gráfico en un segundo hilo. Acelera la compilación de sombreadores, reduce los tirones, y mejora el rendimiento en controladores gráficos que no realicen su propio procesamiento con múltiples hilos. Rendimiento ligeramente superior en controladores gráficos que soporten múltiples hilos.\n\nSelecciona \"Auto\" si no sabes qué hacer.", - "GalThreadingTooltip": "Ejecuta los comandos del motor gráfico en un segundo hilo. Acelera la compilación de sombreadores, reduce los tirones, y mejora el rendimiento en controladores gráficos que no realicen su propio procesamiento con múltiples hilos. Rendimiento ligeramente superior en controladores gráficos que soporten múltiples hilos.\n\nSelecciona \"Auto\" si no sabes qué hacer.", - "ShaderCacheToggleTooltip": "Guarda una caché de sombreadores en disco, la cual reduce los tirones a medida que vas jugando.\n\nActívalo si no sabes qué hacer.", - "ResolutionScaleTooltip": "Multiplies the game's rendering resolution.\n\nA few games may not work with this and look pixelated even when the resolution is increased; for those games, you may need to find mods that remove anti-aliasing or that increase their internal rendering resolution. For using the latter, you'll likely want to select Native.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nKeep in mind 4x is overkill for virtually any setup.", - "ResolutionScaleEntryTooltip": "Escalado de resolución de coma flotante, como por ejemplo 1,5. Los valores no íntegros pueden causar errores gráficos o crashes.", - "AnisotropyTooltip": "Level of Anisotropic Filtering. Set to Auto to use the value requested by the game.", - "AspectRatioTooltip": "Aspect Ratio applied to the renderer window.\n\nOnly change this if you're using an aspect ratio mod for your game, otherwise the graphics will be stretched.\n\nLeave on 16:9 if unsure.", - "ShaderDumpPathTooltip": "Directorio en el cual se volcarán los sombreadores de los gráficos", - "FileLogTooltip": "Guarda los registros de la consola en archivos en disco. No afectan al rendimiento.", - "StubLogTooltip": "Escribe mensajes de Stub en la consola. No afectan al rendimiento.", - "InfoLogTooltip": "Escribe mensajes de Info en la consola. No afectan al rendimiento.", - "WarnLogTooltip": "Escribe mensajes de Advertencia en la consola. No afectan al rendimiento.", - "ErrorLogTooltip": "Escribe mensajes de Error en la consola. No afectan al rendimiento.", - "TraceLogTooltip": "Escribe mensajes de Rastro en la consola. No afectan al rendimiento.", - "GuestLogTooltip": "Escribe mensajes de Guest en la consola. No afectan al rendimiento.", - "FileAccessLogTooltip": "Activa mensajes de acceso a archivo en la consola", - "FSAccessLogModeTooltip": "Activa registros FS Access en la consola. Los modos posibles son entre 0 y 3", - "DeveloperOptionTooltip": "Usar con cuidado", - "OpenGlLogLevel": "Requiere activar los niveles de registro apropiados", - "DebugLogTooltip": "Escribe mensajes de debug en la consola\n\nActiva esto solo si un miembro del equipo te lo pide expresamente, pues hará que el registro sea difícil de leer y empeorará el rendimiento del emulador.", - "LoadApplicationFileTooltip": "Abre el explorador de archivos para elegir un archivo compatible con Switch para cargar", - "LoadApplicationFolderTooltip": "Abre el explorador de archivos para elegir un archivo desempaquetado y compatible con Switch para cargar", - "OpenRyujinxFolderTooltip": "Abre la carpeta de sistema de Ryujinx", - "OpenRyujinxLogsTooltip": "Abre la carpeta en la que se guardan los registros", - "ExitTooltip": "Cierra Ryujinx", - "OpenSettingsTooltip": "Abre la ventana de configuración", - "OpenProfileManagerTooltip": "Abre la ventana para gestionar los perfiles de usuario", - "StopEmulationTooltip": "Detiene la emulación del juego actual y regresa a la selección de juegos", - "CheckUpdatesTooltip": "Busca actualizaciones para Ryujinx", - "OpenAboutTooltip": "Abre la ventana \"Acerca de\"", - "GridSize": "Tamaño de cuadrícula", - "GridSizeTooltip": "Cambia el tamaño de los objetos en la cuadrícula", - "SettingsTabSystemSystemLanguageBrazilianPortuguese": "Portugués brasileño", - "AboutRyujinxContributorsButtonHeader": "Ver todos los contribuidores", - "SettingsTabSystemAudioVolume": "Volumen: ", - "AudioVolumeTooltip": "Ajusta el nivel de volumen", - "SettingsTabSystemEnableInternetAccess": "Conectar guest a Internet/Modo LAN", - "EnableInternetAccessTooltip": "Permite a la aplicación emulada conectarse a Internet.\n\nLos juegos que tengan modo LAN podrán conectarse entre sí habilitando esta opción y estando conectados al mismo módem. Asimismo, esto permite conexiones con consolas reales.\n\nNO permite conectar con los servidores de Nintendo Online. Puede causar que ciertos juegos crasheen al intentar conectarse a sus servidores.\n\nDesactívalo si no estás seguro.", - "GameListContextMenuManageCheatToolTip": "Activa o desactiva los cheats", - "GameListContextMenuManageCheat": "Administrar cheats", - "GameListContextMenuManageModToolTip": "Gestionar Mods", - "GameListContextMenuManageMod": "Gestionar Mods", - "ControllerSettingsStickRange": "Alcance:", - "DialogStopEmulationTitle": "Ryujinx - Detener emulación", - "DialogStopEmulationMessage": "¿Seguro que quieres detener la emulación actual?", - "SettingsTabCpu": "CPU", - "SettingsTabAudio": "Sonido", - "SettingsTabNetwork": "Red", - "SettingsTabNetworkConnection": "Conexión de red", - "SettingsTabCpuCache": "Caché de CPU", - "SettingsTabCpuMemory": "Memoria de CPU", - "DialogUpdaterFlatpakNotSupportedMessage": "Por favor, actualiza Ryujinx a través de FlatHub.", - "UpdaterDisabledWarningTitle": "¡Actualizador deshabilitado!", - "ControllerSettingsRotate90": "Rotar 90° en el sentido de las agujas del reloj", - "IconSize": "Tamaño de iconos", - "IconSizeTooltip": "Cambia el tamaño de los iconos de juegos", - "MenuBarOptionsShowConsole": "Mostrar consola", - "ShaderCachePurgeError": "Error al eliminar la caché de sombreadores en {0}: {1}", - "UserErrorNoKeys": "No se encontraron keys", - "UserErrorNoFirmware": "No se encontró firmware", - "UserErrorFirmwareParsingFailed": "Error al analizar el firmware", - "UserErrorApplicationNotFound": "No se encontró la aplicación", - "UserErrorUnknown": "Error desconocido", - "UserErrorUndefined": "Error indefinido", - "UserErrorNoKeysDescription": "Ryujinx no pudo encontrar tus 'prod.keys'.", - "UserErrorNoFirmwareDescription": "Ryujinx no pudo encontrar un firmware instalado.", - "UserErrorFirmwareParsingFailedDescription": "Ryujinx no pudo analizar el firmware. Normalmente esto ocurre debido a keys desfasadas.", - "UserErrorApplicationNotFoundDescription": "Ryujinx no pudo encontrar una aplicación válida en ese camino.", - "UserErrorUnknownDescription": "¡Ocurrió un error desconocido!", - "UserErrorUndefinedDescription": "¡Ocurrió un error indefinido! Esto no debería pasar, por favor, ¡contacta con un desarrollador!", - "OpenSetupGuideMessage": "Abrir la guía de instalación", - "NoUpdate": "No actualizado", - "TitleUpdateVersionLabel": "Versión {0} - {1}", - "RyujinxInfo": "Ryujinx - Info", - "RyujinxConfirm": "Ryujinx - Confirmación", - "FileDialogAllTypes": "Todos los tipos", - "Never": "Nunca", - "SwkbdMinCharacters": "Debe tener al menos {0} caracteres", - "SwkbdMinRangeCharacters": "Debe tener {0}-{1} caracteres", - "SoftwareKeyboard": "Teclado de software", - "SoftwareKeyboardModeNumeric": "Debe ser sólo 0-9 o '.'", - "SoftwareKeyboardModeAlphabet": "Solo deben ser caracteres no CJK", - "SoftwareKeyboardModeASCII": "Solo deben ser texto ASCII", - "ControllerAppletControllers": "Controladores Compatibles:", - "ControllerAppletPlayers": "Jugadores:", - "ControllerAppletDescription": "Tu configuración actual no es válida. Abre la configuración y vuelve a configurar tus entradas", - "ControllerAppletDocked": "Modo acoplado activado. El modo portátil debería estar desactivado.", - "UpdaterRenaming": "Renombrando archivos viejos...", - "UpdaterRenameFailed": "El actualizador no pudo renombrar el archivo: {0}", - "UpdaterAddingFiles": "Añadiendo nuevos archivos...", - "UpdaterExtracting": "Extrayendo actualización...", - "UpdaterDownloading": "Descargando actualización...", - "Game": "Juego", - "Docked": "Dock/TV", - "Handheld": "Portátil", - "ConnectionError": "Error de conexión.", - "AboutPageDeveloperListMore": "{0} y más...", - "ApiError": "Error de API.", - "LoadingHeading": "Cargando {0}", - "CompilingPPTC": "Compilando PTC", - "CompilingShaders": "Compilando sombreadores", - "AllKeyboards": "Todos los teclados", - "OpenFileDialogTitle": "Selecciona un archivo soportado para cargar", - "OpenFolderDialogTitle": "Selecciona una carpeta con un juego desempaquetado", - "AllSupportedFormats": "Todos los formatos soportados", - "RyujinxUpdater": "Actualizador de Ryujinx", - "SettingsTabHotkeys": "Atajos de teclado", - "SettingsTabHotkeysHotkeys": "Atajos de teclado", - "SettingsTabHotkeysToggleVsyncHotkey": "Alternar la sincronización vertical:", - "SettingsTabHotkeysScreenshotHotkey": "Captura de pantalla:", - "SettingsTabHotkeysShowUiHotkey": "Mostrar interfaz:", - "SettingsTabHotkeysPauseHotkey": "Pausar:", - "SettingsTabHotkeysToggleMuteHotkey": "Silenciar:", - "ControllerMotionTitle": "Opciones de controles de movimiento", - "ControllerRumbleTitle": "Opciones de vibración", - "SettingsSelectThemeFileDialogTitle": "Selecciona un archivo de tema", - "SettingsXamlThemeFile": "Archivo de tema Xaml", - "AvatarWindowTitle": "Administrar cuentas - Avatar", - "Amiibo": "Amiibo", - "Unknown": "Desconocido", - "Usage": "Uso", - "Writable": "Escribible", - "SelectDlcDialogTitle": "Selecciona archivo(s) de DLC", - "SelectUpdateDialogTitle": "Selecciona archivo(s) de actualización", - "SelectModDialogTitle": "Seleccionar un directorio de Mods", - "UserProfileWindowTitle": "Administrar perfiles de usuario", - "CheatWindowTitle": "Administrar cheats", - "DlcWindowTitle": "Administrar contenido descargable", - "ModWindowTitle": "Manage Mods for {0} ({1})", - "UpdateWindowTitle": "Administrar actualizaciones", - "CheatWindowHeading": "Cheats disponibles para {0} [{1}]", - "BuildId": "Id de compilación:", - "DlcWindowHeading": "Contenido descargable disponible para {0} [{1}]", - "ModWindowHeading": "{0} Mod(s)", - "UserProfilesEditProfile": "Editar selección", - "Cancel": "Cancelar", - "Save": "Guardar", - "Discard": "Descartar", - "Paused": "Pausado", - "UserProfilesSetProfileImage": "Elegir Imagen de Perfil ", - "UserProfileEmptyNameError": "El nombre es obligatorio", - "UserProfileNoImageError": "Debe establecerse la imagen de perfil", - "GameUpdateWindowHeading": "Actualizaciones disponibles para {0} [{1}]", - "SettingsTabHotkeysResScaleUpHotkey": "Aumentar la resolución:", - "SettingsTabHotkeysResScaleDownHotkey": "Disminuir la resolución:", - "UserProfilesName": "Nombre:", - "UserProfilesUserId": "Id de Usuario:", - "SettingsTabGraphicsBackend": "Fondo de gráficos", - "SettingsTabGraphicsBackendTooltip": "Select the graphics backend that will be used in the emulator.\n\nVulkan is overall better for all modern graphics cards, as long as their drivers are up to date. Vulkan also features faster shader compilation (less stuttering) on all GPU vendors.\n\nOpenGL may achieve better results on old Nvidia GPUs, on old AMD GPUs on Linux, or on GPUs with lower VRAM, though shader compilation stutters will be greater.\n\nSet to Vulkan if unsure. Set to OpenGL if your GPU does not support Vulkan even with the latest graphics drivers.", - "SettingsEnableTextureRecompression": "Activar recompresión de texturas", - "SettingsEnableTextureRecompressionTooltip": "Compresses ASTC textures in order to reduce VRAM usage.\n\nGames using this texture format include Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder and The Legend of Zelda: Tears of the Kingdom.\n\nGraphics cards with 4GiB VRAM or less will likely crash at some point while running these games.\n\nEnable only if you're running out of VRAM on the aforementioned games. Leave OFF if unsure.", - "SettingsTabGraphicsPreferredGpu": "GPU preferida", - "SettingsTabGraphicsPreferredGpuTooltip": "Selecciona la tarjeta gráfica que se utilizará con los back-end de gráficos Vulkan.\n\nNo afecta la GPU que utilizará OpenGL.\n\nFije a la GPU marcada como \"dGUP\" ante dudas. Si no hay una, no haga modificaciones.", - "SettingsAppRequiredRestartMessage": "Reinicio de Ryujinx requerido.", - "SettingsGpuBackendRestartMessage": "La configuración de la GPU o del back-end de los gráficos fue modificada. Es necesario reiniciar para que se aplique.", - "SettingsGpuBackendRestartSubMessage": "¿Quieres reiniciar ahora?", - "RyujinxUpdaterMessage": "¿Quieres actualizar Ryujinx a la última versión?", - "SettingsTabHotkeysVolumeUpHotkey": "Aumentar volumen:", - "SettingsTabHotkeysVolumeDownHotkey": "Disminuir volumen:", - "SettingsEnableMacroHLE": "Activar Macros HLE", - "SettingsEnableMacroHLETooltip": "Emulación alto-nivel del código de Macros de GPU\n\nIncrementa el rendimiento, pero puede causar errores gráficos en algunos juegos.\n\nDeja esta opción activada si no estás seguro.", - "SettingsEnableColorSpacePassthrough": "Paso de espacio de color", - "SettingsEnableColorSpacePassthroughTooltip": "Dirige el backend de Vulkan a pasar a través de la información del color sin especificar un espacio de color. Para los usuarios con pantallas de gran gama, esto puede resultar en colores más vibrantes, a costa de la corrección del color.", - "VolumeShort": "Volumen", - "UserProfilesManageSaves": "Administrar mis partidas guardadas", - "DeleteUserSave": "¿Quieres borrar los datos de usuario de este juego?", - "IrreversibleActionNote": "Esta acción no es reversible.", - "SaveManagerHeading": "Manage Saves for {0}", - "SaveManagerTitle": "Administrador de datos de guardado.", - "Name": "Nombre", - "Size": "Tamaño", - "Search": "Buscar", - "UserProfilesRecoverLostAccounts": "Recuperar cuentas perdidas", - "Recover": "Recuperar", - "UserProfilesRecoverHeading": "Datos de guardado fueron encontrados para las siguientes cuentas", - "UserProfilesRecoverEmptyList": "No hay perfiles a recuperar", - "GraphicsAATooltip": "Applies anti-aliasing to the game render.\n\nFXAA will blur most of the image, while SMAA will attempt to find jagged edges and smooth them out.\n\nNot recommended to use in conjunction with the FSR scaling filter.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on NONE if unsure.", - "GraphicsAALabel": "Suavizado de bordes:", - "GraphicsScalingFilterLabel": "Filtro de escalado:", - "GraphicsScalingFilterTooltip": "Elija el filtro de escala que se aplicará al utilizar la escala de resolución.\n\nBilinear funciona bien para juegos 3D y es una opción predeterminada segura.\n\nSe recomienda el bilinear para juegos de pixel art.\n\nFSR 1.0 es simplemente un filtro de afilado, no se recomienda su uso con FXAA o SMAA.\n\nEsta opción se puede cambiar mientras se ejecuta un juego haciendo clic en \"Aplicar\" a continuación; simplemente puedes mover la ventana de configuración a un lado y experimentar hasta que encuentres tu estilo preferido para un juego.\n\nDéjelo en BILINEAR si no está seguro.", - "GraphicsScalingFilterBilinear": "Bilinear\n", - "GraphicsScalingFilterNearest": "Cercano", - "GraphicsScalingFilterFsr": "FSR", - "GraphicsScalingFilterLevelLabel": "Nivel", - "GraphicsScalingFilterLevelTooltip": "Ajuste el nivel de nitidez FSR 1.0. Mayor es más nítido.", - "SmaaLow": "SMAA Bajo", - "SmaaMedium": "SMAA Medio", - "SmaaHigh": "SMAA Alto", - "SmaaUltra": "SMAA Ultra", - "UserEditorTitle": "Editar usuario", - "UserEditorTitleCreate": "Crear Usuario", - "SettingsTabNetworkInterface": "Interfaz de Red", - "NetworkInterfaceTooltip": "Interfaz de red usada para características LAN/LDN.\n\njunto con una VPN o XLink Kai y un juego con soporte LAN, puede usarse para suplantar una conexión de la misma red a través de Internet.\n\nDeje en DEFAULT si no está seguro.", - "NetworkInterfaceDefault": "Predeterminado", - "PackagingShaders": "Empaquetando sombreadores", - "AboutChangelogButton": "Ver registro de cambios en GitHub", - "AboutChangelogButtonTooltipMessage": "Haga clic para abrir el registro de cambios para esta versión en su navegador predeterminado.", - "SettingsTabNetworkMultiplayer": "Multijugador", - "MultiplayerMode": "Modo:", - "MultiplayerModeTooltip": "Cambiar modo LDN multijugador.\n\nLdnMitm modificará la funcionalidad local de juego inalámbrico para funcionar como si fuera LAN, permitiendo locales conexiones de la misma red con otras instancias de Ryujinx y consolas hackeadas de Nintendo Switch que tienen instalado el módulo ldn_mitm.\n\nMultijugador requiere que todos los jugadores estén en la misma versión del juego (por ejemplo, Super Smash Bros. Ultimate v13.0.1 no se puede conectar a v13.0.0).\n\nDejar DESACTIVADO si no está seguro.", - "MultiplayerModeDisabled": "Deshabilitar", - "MultiplayerModeLdnMitm": "ldn_mitm" -} diff --git a/src/Ryujinx/Assets/Locales/fr_FR.json b/src/Ryujinx/Assets/Locales/fr_FR.json deleted file mode 100644 index 99a060650..000000000 --- a/src/Ryujinx/Assets/Locales/fr_FR.json +++ /dev/null @@ -1,780 +0,0 @@ -{ - "Language": "Français", - "MenuBarFileOpenApplet": "Ouvrir un applet", - "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Ouvrir l'Applet Mii Editor en mode Standalone", - "SettingsTabInputDirectMouseAccess": "Accès direct à la souris", - "SettingsTabSystemMemoryManagerMode": "Mode de gestion de la mémoire :", - "SettingsTabSystemMemoryManagerModeSoftware": "Logiciel", - "SettingsTabSystemMemoryManagerModeHost": "Hôte (rapide)", - "SettingsTabSystemMemoryManagerModeHostUnchecked": "Hôte non vérifié (plus rapide, non sécurisé)", - "SettingsTabSystemUseHypervisor": "Utiliser l'Hyperviseur", - "MenuBarFile": "_Fichier", - "MenuBarFileOpenFromFile": "_Charger un jeu depuis un fichier", - "MenuBarFileOpenUnpacked": "Charger un jeu extrait", - "MenuBarFileOpenEmuFolder": "Ouvrir le dossier Ryujinx", - "MenuBarFileOpenLogsFolder": "Ouvrir le dossier des journaux", - "MenuBarFileExit": "_Quitter", - "MenuBarOptions": "_Options", - "MenuBarOptionsToggleFullscreen": "Basculer en plein écran", - "MenuBarOptionsStartGamesInFullscreen": "Démarrer le jeu en plein écran", - "MenuBarOptionsStopEmulation": "Arrêter l'émulation", - "MenuBarOptionsSettings": "_Paramètres", - "MenuBarOptionsManageUserProfiles": "_Gérer les profils d'utilisateurs", - "MenuBarActions": "_Actions", - "MenuBarOptionsSimulateWakeUpMessage": "Simuler un message de réveil", - "MenuBarActionsScanAmiibo": "Scanner un Amiibo", - "MenuBarTools": "_Outils", - "MenuBarToolsInstallFirmware": "Installer un firmware", - "MenuBarFileToolsInstallFirmwareFromFile": "Installer un firmware depuis un fichier XCI ou ZIP", - "MenuBarFileToolsInstallFirmwareFromDirectory": "Installer un firmware depuis un dossier", - "MenuBarToolsManageFileTypes": "Gérer les types de fichiers", - "MenuBarToolsInstallFileTypes": "Installer les types de fichiers", - "MenuBarToolsUninstallFileTypes": "Désinstaller les types de fichiers", - "MenuBarView": "_View", - "MenuBarViewWindow": "Window Size", - "MenuBarViewWindow720": "720p", - "MenuBarViewWindow1080": "1080p", - "MenuBarHelp": "_Aide", - "MenuBarHelpCheckForUpdates": "Vérifier les mises à jour", - "MenuBarHelpAbout": "À propos", - "MenuSearch": "Rechercher...", - "GameListHeaderFavorite": "Favoris", - "GameListHeaderIcon": "Icône", - "GameListHeaderApplication": "Nom", - "GameListHeaderDeveloper": "Développeur", - "GameListHeaderVersion": "Version", - "GameListHeaderTimePlayed": "Temps de jeu", - "GameListHeaderLastPlayed": "Dernière partie jouée", - "GameListHeaderFileExtension": "Extension du Fichier", - "GameListHeaderFileSize": "Taille du Fichier", - "GameListHeaderPath": "Chemin", - "GameListContextMenuOpenUserSaveDirectory": "Ouvrir le dossier de sauvegarde utilisateur", - "GameListContextMenuOpenUserSaveDirectoryToolTip": "Ouvre le dossier contenant la sauvegarde utilisateur du jeu", - "GameListContextMenuOpenDeviceSaveDirectory": "Ouvrir le dossier de sauvegarde console", - "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Ouvre le dossier contenant la sauvegarde console du jeu", - "GameListContextMenuOpenBcatSaveDirectory": "Ouvrir le dossier de sauvegarde BCAT", - "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Ouvre le dossier contenant la sauvegarde BCAT du jeu", - "GameListContextMenuManageTitleUpdates": "Gérer la mise à jour des titres", - "GameListContextMenuManageTitleUpdatesToolTip": "Ouvre la fenêtre de gestion des mises à jour du jeu", - "GameListContextMenuManageDlc": "Gérer les DLC", - "GameListContextMenuManageDlcToolTip": "Ouvre la fenêtre de gestion des DLC", - "GameListContextMenuCacheManagement": "Gestion des caches", - "GameListContextMenuCacheManagementPurgePptc": "Reconstruction du PPTC", - "GameListContextMenuCacheManagementPurgePptcToolTip": "Effectuer une reconstruction du PPTC au prochain démarrage du jeu", - "GameListContextMenuCacheManagementPurgeShaderCache": "Purger le cache des shaders", - "GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "Supprime le cache des shaders du jeu", - "GameListContextMenuCacheManagementOpenPptcDirectory": "Ouvrir le dossier du PPTC", - "GameListContextMenuCacheManagementOpenPptcDirectoryToolTip": "Ouvre le dossier contenant le PPTC du jeu", - "GameListContextMenuCacheManagementOpenShaderCacheDirectory": "Ouvrir le dossier du cache des shaders", - "GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip": "Ouvre le dossier contenant le cache des shaders du jeu", - "GameListContextMenuExtractData": "Extraire les données", - "GameListContextMenuExtractDataExeFS": "ExeFS", - "GameListContextMenuExtractDataExeFSToolTip": "Extrait la section ExeFS du jeu (mise à jour incluse)", - "GameListContextMenuExtractDataRomFS": "RomFS", - "GameListContextMenuExtractDataRomFSToolTip": "Extrait la section RomFS du jeu (mise à jour incluse)", - "GameListContextMenuExtractDataLogo": "Logo", - "GameListContextMenuExtractDataLogoToolTip": "Extrait la section Logo du jeu (mise à jour incluse)", - "GameListContextMenuCreateShortcut": "Créer un raccourci d'application", - "GameListContextMenuCreateShortcutToolTip": "Créer un raccourci sur le bureau qui lance l'application sélectionnée", - "GameListContextMenuCreateShortcutToolTipMacOS": "Créer un raccourci dans le dossier Applications de macOS qui lance l'application sélectionnée", - "GameListContextMenuOpenModsDirectory": "Ouvrir le dossier des mods", - "GameListContextMenuOpenModsDirectoryToolTip": "Ouvre le dossier contenant les mods de l'application", - "GameListContextMenuOpenSdModsDirectory": "Ouvrir le dossier des mods Atmosphère", - "GameListContextMenuOpenSdModsDirectoryToolTip": "Ouvre le dossier alternatif de la carte SD Atmosphère qui contient les mods de l'application. Utile pour les mods conçus pour du matériel réel.", - "StatusBarGamesLoaded": "{0}/{1} Jeux chargés", - "StatusBarSystemVersion": "Version du Firmware: {0}", - "LinuxVmMaxMapCountDialogTitle": "Limite basse pour les mappings mémoire détectée", - "LinuxVmMaxMapCountDialogTextPrimary": "Voulez-vous augmenter la valeur de vm.max_map_count à {0}", - "LinuxVmMaxMapCountDialogTextSecondary": "Certains jeux peuvent essayer de créer plus de mappings mémoire que ce qui est actuellement autorisé. Ryujinx plantera dès que cette limite sera dépassée.", - "LinuxVmMaxMapCountDialogButtonUntilRestart": "Oui, jusqu'au prochain redémarrage", - "LinuxVmMaxMapCountDialogButtonPersistent": "Oui, en permanence", - "LinuxVmMaxMapCountWarningTextPrimary": "La quantité maximale de mappings mémoire est inférieure à la valeur recommandée.", - "LinuxVmMaxMapCountWarningTextSecondary": "La valeur actuelle de vm.max_map_count ({0}) est inférieure à {1}. Certains jeux peuvent essayer de créer plus de mappings mémoire que ceux actuellement autorisés. Ryujinx plantera dès que cette limite sera dépassée.\n\nVous pouvez soit augmenter manuellement la limite, soit installer pkexec, ce qui permet à Ryujinx de l'aider.", - "Settings": "Paramètres", - "SettingsTabGeneral": "Interface Utilisateur", - "SettingsTabGeneralGeneral": "Général", - "SettingsTabGeneralEnableDiscordRichPresence": "Activer Discord Rich Presence", - "SettingsTabGeneralCheckUpdatesOnLaunch": "Vérifier les mises à jour au démarrage", - "SettingsTabGeneralShowConfirmExitDialog": "Afficher le message de \"Confirmation de sortie\"", - "SettingsTabGeneralRememberWindowState": "Remember Window Size/Position", - "SettingsTabGeneralHideCursor": "Masquer le Curseur :", - "SettingsTabGeneralHideCursorNever": "Jamais", - "SettingsTabGeneralHideCursorOnIdle": "Masquer le curseur si inactif", - "SettingsTabGeneralHideCursorAlways": "Toujours", - "SettingsTabGeneralGameDirectories": "Dossiers des jeux", - "SettingsTabGeneralAdd": "Ajouter", - "SettingsTabGeneralRemove": "Retirer", - "SettingsTabSystem": "Système", - "SettingsTabSystemCore": "Cœur", - "SettingsTabSystemSystemRegion": "Région du système:", - "SettingsTabSystemSystemRegionJapan": "Japon", - "SettingsTabSystemSystemRegionUSA": "USA", - "SettingsTabSystemSystemRegionEurope": "Europe", - "SettingsTabSystemSystemRegionAustralia": "Australie", - "SettingsTabSystemSystemRegionChina": "Chine", - "SettingsTabSystemSystemRegionKorea": "Corée", - "SettingsTabSystemSystemRegionTaiwan": "Taïwan", - "SettingsTabSystemSystemLanguage": "Langue du système:", - "SettingsTabSystemSystemLanguageJapanese": "Japonais", - "SettingsTabSystemSystemLanguageAmericanEnglish": "Anglais Américain", - "SettingsTabSystemSystemLanguageFrench": "Français", - "SettingsTabSystemSystemLanguageGerman": "Allemand", - "SettingsTabSystemSystemLanguageItalian": "Italien", - "SettingsTabSystemSystemLanguageSpanish": "Espagnol", - "SettingsTabSystemSystemLanguageChinese": "Chinois", - "SettingsTabSystemSystemLanguageKorean": "Coréen", - "SettingsTabSystemSystemLanguageDutch": "Néerlandais", - "SettingsTabSystemSystemLanguagePortuguese": "Portugais", - "SettingsTabSystemSystemLanguageRussian": "Russe", - "SettingsTabSystemSystemLanguageTaiwanese": "Taïwanais", - "SettingsTabSystemSystemLanguageBritishEnglish": "Anglais britannique ", - "SettingsTabSystemSystemLanguageCanadianFrench": "Français Canadien", - "SettingsTabSystemSystemLanguageLatinAmericanSpanish": "Espagnol latino-américain", - "SettingsTabSystemSystemLanguageSimplifiedChinese": "Chinois simplifié", - "SettingsTabSystemSystemLanguageTraditionalChinese": "Chinois traditionnel", - "SettingsTabSystemSystemTimeZone": "Fuseau horaire du système :", - "SettingsTabSystemSystemTime": "Heure du système:", - "SettingsTabSystemEnableVsync": "Synchronisation verticale (VSync)", - "SettingsTabSystemEnablePptc": "Activer le PPTC (Profiled Persistent Translation Cache)", - "SettingsTabSystemEnableFsIntegrityChecks": "Activer la vérification de l'intégrité du système de fichiers", - "SettingsTabSystemAudioBackend": "Bibliothèque Audio :", - "SettingsTabSystemAudioBackendDummy": "Factice", - "SettingsTabSystemAudioBackendOpenAL": "OpenAL", - "SettingsTabSystemAudioBackendSoundIO": "SoundIO", - "SettingsTabSystemAudioBackendSDL2": "SDL2", - "SettingsTabSystemHacks": "Hacks", - "SettingsTabSystemHacksNote": "Cela peut causer des instabilités", - "SettingsTabSystemExpandDramSize": "Utiliser disposition alternative de la mémoire (développeur)", - "SettingsTabSystemIgnoreMissingServices": "Ignorer les services manquants", - "SettingsTabGraphics": "Graphismes", - "SettingsTabGraphicsAPI": "API Graphique", - "SettingsTabGraphicsEnableShaderCache": "Activer le cache des shaders", - "SettingsTabGraphicsAnisotropicFiltering": "Filtrage anisotrope:", - "SettingsTabGraphicsAnisotropicFilteringAuto": "Auto", - "SettingsTabGraphicsAnisotropicFiltering2x": "x2", - "SettingsTabGraphicsAnisotropicFiltering4x": "x4", - "SettingsTabGraphicsAnisotropicFiltering8x": "x8", - "SettingsTabGraphicsAnisotropicFiltering16x": "x16", - "SettingsTabGraphicsResolutionScale": "Échelle de résolution:", - "SettingsTabGraphicsResolutionScaleCustom": "Personnalisée (Non recommandée)", - "SettingsTabGraphicsResolutionScaleNative": "Natif (720p/1080p)", - "SettingsTabGraphicsResolutionScale2x": "x2 (1440p/2160p)", - "SettingsTabGraphicsResolutionScale3x": "x3 (2160p/3240p)", - "SettingsTabGraphicsResolutionScale4x": "4x (2880p/4320p) (Non recommandé)", - "SettingsTabGraphicsAspectRatio": "Format d'affichage :", - "SettingsTabGraphicsAspectRatio4x3": "4:3", - "SettingsTabGraphicsAspectRatio16x9": "16:9", - "SettingsTabGraphicsAspectRatio16x10": "16:10", - "SettingsTabGraphicsAspectRatio21x9": "21:9", - "SettingsTabGraphicsAspectRatio32x9": "32:9", - "SettingsTabGraphicsAspectRatioStretch": "Écran étiré", - "SettingsTabGraphicsDeveloperOptions": "Options développeur", - "SettingsTabGraphicsShaderDumpPath": "Chemin du dossier de copie des shaders:", - "SettingsTabLogging": "Journaux", - "SettingsTabLoggingLogging": "Journaux", - "SettingsTabLoggingEnableLoggingToFile": "Activer la sauvegarde des journaux vers un fichier", - "SettingsTabLoggingEnableStubLogs": "Activer les journaux stub", - "SettingsTabLoggingEnableInfoLogs": "Activer les journaux d'informations", - "SettingsTabLoggingEnableWarningLogs": "Activer les journaux d'avertissements", - "SettingsTabLoggingEnableErrorLogs": "Activer les journaux d'erreurs", - "SettingsTabLoggingEnableTraceLogs": "Activer journaux d'erreurs Trace", - "SettingsTabLoggingEnableGuestLogs": "Activer les journaux du programme simulé", - "SettingsTabLoggingEnableFsAccessLogs": "Activer les journaux d'accès au système de fichiers", - "SettingsTabLoggingFsGlobalAccessLogMode": "Niveau des journaux d'accès au système de fichiers:", - "SettingsTabLoggingDeveloperOptions": "Options développeur", - "SettingsTabLoggingDeveloperOptionsNote": "ATTENTION : Réduira les performances", - "SettingsTabLoggingGraphicsBackendLogLevel": "Niveau du journal du backend graphique :", - "SettingsTabLoggingGraphicsBackendLogLevelNone": "Aucun", - "SettingsTabLoggingGraphicsBackendLogLevelError": "Erreur", - "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "Ralentissements", - "SettingsTabLoggingGraphicsBackendLogLevelAll": "Tout", - "SettingsTabLoggingEnableDebugLogs": "Activer les journaux de debug", - "SettingsTabInput": "Contrôles", - "SettingsTabInputEnableDockedMode": "Active le mode station d'accueil", - "SettingsTabInputDirectKeyboardAccess": "Accès direct au clavier", - "SettingsButtonSave": "Enregistrer", - "SettingsButtonClose": "Fermer", - "SettingsButtonOk": "OK", - "SettingsButtonCancel": "Annuler", - "SettingsButtonApply": "Appliquer", - "ControllerSettingsPlayer": "Joueur", - "ControllerSettingsPlayer1": "Joueur 1", - "ControllerSettingsPlayer2": "Joueur 2", - "ControllerSettingsPlayer3": "Joueur 3", - "ControllerSettingsPlayer4": "Joueur 4", - "ControllerSettingsPlayer5": "Joueur 5", - "ControllerSettingsPlayer6": "Joueur 6", - "ControllerSettingsPlayer7": "Joueur 7", - "ControllerSettingsPlayer8": "Joueur 8", - "ControllerSettingsHandheld": "Portable", - "ControllerSettingsInputDevice": "Périphériques", - "ControllerSettingsRefresh": "Actualiser", - "ControllerSettingsDeviceDisabled": "Désactivé", - "ControllerSettingsControllerType": "Type de contrôleur", - "ControllerSettingsControllerTypeHandheld": "Portable", - "ControllerSettingsControllerTypeProController": "Pro Controller", - "ControllerSettingsControllerTypeJoyConPair": "JoyCon Joints", - "ControllerSettingsControllerTypeJoyConLeft": "JoyCon Gauche", - "ControllerSettingsControllerTypeJoyConRight": "JoyCon Droite", - "ControllerSettingsProfile": "Profil", - "ControllerSettingsProfileDefault": "Défaut", - "ControllerSettingsLoad": "Charger", - "ControllerSettingsAdd": "Ajouter", - "ControllerSettingsRemove": "Supprimer", - "ControllerSettingsButtons": "Boutons", - "ControllerSettingsButtonA": "A", - "ControllerSettingsButtonB": "B", - "ControllerSettingsButtonX": "X", - "ControllerSettingsButtonY": "Y", - "ControllerSettingsButtonPlus": "+", - "ControllerSettingsButtonMinus": "-", - "ControllerSettingsDPad": "Croix directionnelle", - "ControllerSettingsDPadUp": "Haut", - "ControllerSettingsDPadDown": "Bas", - "ControllerSettingsDPadLeft": "Gauche", - "ControllerSettingsDPadRight": "Droite", - "ControllerSettingsStickButton": "Bouton", - "ControllerSettingsStickUp": "Haut", - "ControllerSettingsStickDown": "Bas", - "ControllerSettingsStickLeft": "Gauche", - "ControllerSettingsStickRight": "Droite", - "ControllerSettingsStickStick": "Joystick", - "ControllerSettingsStickInvertXAxis": "Inverser l'axe X", - "ControllerSettingsStickInvertYAxis": "Inverser l'axe Y", - "ControllerSettingsStickDeadzone": "Zone morte :", - "ControllerSettingsLStick": "Joystick Gauche", - "ControllerSettingsRStick": "Joystick Droit", - "ControllerSettingsTriggersLeft": "Gachettes Gauche", - "ControllerSettingsTriggersRight": "Gachettes Droite", - "ControllerSettingsTriggersButtonsLeft": "Boutons Gachettes Gauche", - "ControllerSettingsTriggersButtonsRight": "Boutons Gachettes Droite", - "ControllerSettingsTriggers": "Gachettes", - "ControllerSettingsTriggerL": "L", - "ControllerSettingsTriggerR": "R", - "ControllerSettingsTriggerZL": "ZL", - "ControllerSettingsTriggerZR": "ZR", - "ControllerSettingsLeftSL": "SL", - "ControllerSettingsLeftSR": "SR", - "ControllerSettingsRightSL": "SL", - "ControllerSettingsRightSR": "SR", - "ControllerSettingsExtraButtonsLeft": "Boutons Gauche", - "ControllerSettingsExtraButtonsRight": "Boutons Droite", - "ControllerSettingsMisc": "Divers", - "ControllerSettingsTriggerThreshold": "Seuil de gachettes:", - "ControllerSettingsMotion": "Mouvements", - "ControllerSettingsMotionUseCemuhookCompatibleMotion": "Utiliser un capteur de mouvements CemuHook", - "ControllerSettingsMotionControllerSlot": "Contrôleur ID:", - "ControllerSettingsMotionMirrorInput": "Inverser les contrôles", - "ControllerSettingsMotionRightJoyConSlot": "JoyCon Droit ID:", - "ControllerSettingsMotionServerHost": "Serveur d'hébergement :", - "ControllerSettingsMotionGyroSensitivity": "Sensibilitée du gyroscope:", - "ControllerSettingsMotionGyroDeadzone": "Zone morte du gyroscope:", - "ControllerSettingsSave": "Enregistrer", - "ControllerSettingsClose": "Fermer", - "KeyUnknown": "Unknown", - "KeyShiftLeft": "Shift Left", - "KeyShiftRight": "Shift Right", - "KeyControlLeft": "Ctrl Left", - "KeyMacControlLeft": "⌃ Left", - "KeyControlRight": "Ctrl Right", - "KeyMacControlRight": "⌃ Right", - "KeyAltLeft": "Alt Left", - "KeyMacAltLeft": "⌥ Left", - "KeyAltRight": "Alt Right", - "KeyMacAltRight": "⌥ Right", - "KeyWinLeft": "⊞ Left", - "KeyMacWinLeft": "⌘ Left", - "KeyWinRight": "⊞ Right", - "KeyMacWinRight": "⌘ Right", - "KeyMenu": "Menu", - "KeyUp": "Up", - "KeyDown": "Down", - "KeyLeft": "Left", - "KeyRight": "Right", - "KeyEnter": "Enter", - "KeyEscape": "Escape", - "KeySpace": "Space", - "KeyTab": "Tab", - "KeyBackSpace": "Backspace", - "KeyInsert": "Insert", - "KeyDelete": "Delete", - "KeyPageUp": "Page Up", - "KeyPageDown": "Page Down", - "KeyHome": "Home", - "KeyEnd": "End", - "KeyCapsLock": "Caps Lock", - "KeyScrollLock": "Scroll Lock", - "KeyPrintScreen": "Print Screen", - "KeyPause": "Pause", - "KeyNumLock": "Num Lock", - "KeyClear": "Clear", - "KeyKeypad0": "Keypad 0", - "KeyKeypad1": "Keypad 1", - "KeyKeypad2": "Keypad 2", - "KeyKeypad3": "Keypad 3", - "KeyKeypad4": "Keypad 4", - "KeyKeypad5": "Keypad 5", - "KeyKeypad6": "Keypad 6", - "KeyKeypad7": "Keypad 7", - "KeyKeypad8": "Keypad 8", - "KeyKeypad9": "Keypad 9", - "KeyKeypadDivide": "Keypad Divide", - "KeyKeypadMultiply": "Keypad Multiply", - "KeyKeypadSubtract": "Keypad Subtract", - "KeyKeypadAdd": "Keypad Add", - "KeyKeypadDecimal": "Keypad Decimal", - "KeyKeypadEnter": "Keypad Enter", - "KeyNumber0": "0", - "KeyNumber1": "1", - "KeyNumber2": "2", - "KeyNumber3": "3", - "KeyNumber4": "4", - "KeyNumber5": "5", - "KeyNumber6": "6", - "KeyNumber7": "7", - "KeyNumber8": "8", - "KeyNumber9": "9", - "KeyTilde": "~", - "KeyGrave": "`", - "KeyMinus": "-", - "KeyPlus": "+", - "KeyBracketLeft": "[", - "KeyBracketRight": "]", - "KeySemicolon": ";", - "KeyQuote": "\"", - "KeyComma": ",", - "KeyPeriod": ".", - "KeySlash": "/", - "KeyBackSlash": "\\", - "KeyUnbound": "Unbound", - "GamepadLeftStick": "L Stick Button", - "GamepadRightStick": "R Stick Button", - "GamepadLeftShoulder": "Left Shoulder", - "GamepadRightShoulder": "Right Shoulder", - "GamepadLeftTrigger": "Left Trigger", - "GamepadRightTrigger": "Right Trigger", - "GamepadDpadUp": "Up", - "GamepadDpadDown": "Down", - "GamepadDpadLeft": "Left", - "GamepadDpadRight": "Right", - "GamepadMinus": "-", - "GamepadPlus": "+", - "GamepadGuide": "Guide", - "GamepadMisc1": "Misc", - "GamepadPaddle1": "Paddle 1", - "GamepadPaddle2": "Paddle 2", - "GamepadPaddle3": "Paddle 3", - "GamepadPaddle4": "Paddle 4", - "GamepadTouchpad": "Touchpad", - "GamepadSingleLeftTrigger0": "Left Trigger 0", - "GamepadSingleRightTrigger0": "Right Trigger 0", - "GamepadSingleLeftTrigger1": "Left Trigger 1", - "GamepadSingleRightTrigger1": "Right Trigger 1", - "StickLeft": "Left Stick", - "StickRight": "Right Stick", - "UserProfilesSelectedUserProfile": "Profil utilisateur sélectionné :", - "UserProfilesSaveProfileName": "Enregistrer le nom du profil", - "UserProfilesChangeProfileImage": "Changer l'image du profil", - "UserProfilesAvailableUserProfiles": "Profils utilisateurs disponibles:", - "UserProfilesAddNewProfile": "Créer un profil", - "UserProfilesDelete": "Supprimer", - "UserProfilesClose": "Fermer", - "ProfileNameSelectionWatermark": "Choisir un pseudo", - "ProfileImageSelectionTitle": "Sélection de l'image du profil", - "ProfileImageSelectionHeader": "Choisir l'image du profil", - "ProfileImageSelectionNote": "Vous pouvez importer une image de profil personnalisée ou sélectionner un avatar à partir du firmware", - "ProfileImageSelectionImportImage": "Importer une image", - "ProfileImageSelectionSelectAvatar": "Choisir un avatar du firmware", - "InputDialogTitle": "Fenêtre d'entrée de texte", - "InputDialogOk": "OK", - "InputDialogCancel": "Annuler", - "InputDialogAddNewProfileTitle": "Choisir un nom de profil", - "InputDialogAddNewProfileHeader": "Merci d'entrer un nom de profil", - "InputDialogAddNewProfileSubtext": "(Longueur max.: {0})", - "AvatarChoose": "Choisir un avatar", - "AvatarSetBackgroundColor": "Choisir une couleur de fond", - "AvatarClose": "Fermer", - "ControllerSettingsLoadProfileToolTip": "Charger un profil", - "ControllerSettingsAddProfileToolTip": "Ajouter un profil", - "ControllerSettingsRemoveProfileToolTip": "Supprimer un profil", - "ControllerSettingsSaveProfileToolTip": "Enregistrer un profil", - "MenuBarFileToolsTakeScreenshot": "Prendre une capture d'écran", - "MenuBarFileToolsHideUi": "Masquer l'interface utilisateur", - "GameListContextMenuRunApplication": "Démarrer l'application", - "GameListContextMenuToggleFavorite": "Ajouter/Retirer des favoris", - "GameListContextMenuToggleFavoriteToolTip": "Activer/désactiver le statut favori du jeu", - "SettingsTabGeneralTheme": "Thème :", - "SettingsTabGeneralThemeDark": "Sombre", - "SettingsTabGeneralThemeLight": "Clair", - "ControllerSettingsConfigureGeneral": "Configurer", - "ControllerSettingsRumble": "Vibreur", - "ControllerSettingsRumbleStrongMultiplier": "Multiplicateur de vibrations fortes", - "ControllerSettingsRumbleWeakMultiplier": "Multiplicateur de vibrations faibles", - "DialogMessageSaveNotAvailableMessage": "Il n'y a aucune sauvegarde pour {0} [{1:x16}]", - "DialogMessageSaveNotAvailableCreateSaveMessage": "Voulez-vous créer une sauvegarde pour ce jeu ?", - "DialogConfirmationTitle": "Ryujinx - Confirmation", - "DialogUpdaterTitle": "Ryujinx - Mise à Jour", - "DialogErrorTitle": "Ryujinx - Erreur", - "DialogWarningTitle": "Ryujinx - Avertissement", - "DialogExitTitle": "Ryujinx - Quitter", - "DialogErrorMessage": "Ryujinx a rencontré une erreur", - "DialogExitMessage": "Êtes-vous sûr de vouloir fermer Ryujinx ?", - "DialogExitSubMessage": "Toutes les données non enregistrées seront perdues !", - "DialogMessageCreateSaveErrorMessage": "Une erreur s'est produite lors de la création de la sauvegarde spécifiée : {0}", - "DialogMessageFindSaveErrorMessage": "Une erreur s'est produite lors de la recherche de la sauvegarde spécifiée : {0}", - "FolderDialogExtractTitle": "Choisissez le dossier dans lequel extraire", - "DialogNcaExtractionMessage": "Extraction de la section {0} depuis {1}...", - "DialogNcaExtractionTitle": "Ryujinx - Extracteur de la section NCA", - "DialogNcaExtractionMainNcaNotFoundErrorMessage": "Échec de l'extraction. Le NCA principal n'était pas présent dans le fichier sélectionné.", - "DialogNcaExtractionCheckLogErrorMessage": "Échec de l'extraction. Lisez le fichier journal pour plus d'informations.", - "DialogNcaExtractionSuccessMessage": "Extraction terminée avec succès.", - "DialogUpdaterConvertFailedMessage": "Échec de la conversion de la version actuelle de Ryujinx.", - "DialogUpdaterCancelUpdateMessage": "Annuler la mise à jour !", - "DialogUpdaterAlreadyOnLatestVersionMessage": "Vous utilisez déjà la version la plus récente de Ryujinx !", - "DialogUpdaterFailedToGetVersionMessage": "Une erreur s'est produite lors de la tentative d'obtention des informations de publication de la version GitHub. Cela peut survenir lorsqu'une nouvelle version est en cours de compilation par GitHub Actions. Réessayez dans quelques minutes.", - "DialogUpdaterConvertFailedGithubMessage": "Impossible de convertir la version reçue de Ryujinx depuis Github Release.", - "DialogUpdaterDownloadingMessage": "Téléchargement de la mise à jour...", - "DialogUpdaterExtractionMessage": "Extraction de la mise à jour…", - "DialogUpdaterRenamingMessage": "Renommage de la mise à jour...", - "DialogUpdaterAddingFilesMessage": "Ajout d'une nouvelle mise à jour...", - "DialogUpdaterCompleteMessage": "Mise à jour terminée !", - "DialogUpdaterRestartMessage": "Voulez-vous redémarrer Ryujinx maintenant ?", - "DialogUpdaterNoInternetMessage": "Vous n'êtes pas connecté à Internet !", - "DialogUpdaterNoInternetSubMessage": "Veuillez vérifier que vous disposez d'une connexion Internet fonctionnelle !", - "DialogUpdaterDirtyBuildMessage": "Vous ne pouvez pas mettre à jour une version Dirty de Ryujinx !", - "DialogUpdaterDirtyBuildSubMessage": "Veuillez télécharger Ryujinx sur https://ryujinx.org/ si vous recherchez une version prise en charge.", - "DialogRestartRequiredMessage": "Redémarrage requis", - "DialogThemeRestartMessage": "Le thème a été enregistré. Un redémarrage est requis pour appliquer le thème.", - "DialogThemeRestartSubMessage": "Voulez-vous redémarrer", - "DialogFirmwareInstallEmbeddedMessage": "Voulez-vous installer le firmware intégré dans ce jeu ? (Firmware {0})", - "DialogFirmwareInstallEmbeddedSuccessMessage": "Aucun firmware installé n'a été trouvé mais Ryujinx a pu installer le firmware {0} à partir du jeu fourni.\nL'émulateur va maintenant démarrer.", - "DialogFirmwareNoFirmwareInstalledMessage": "Aucun Firmware installé", - "DialogFirmwareInstalledMessage": "Le firmware {0} a été installé", - "DialogInstallFileTypesSuccessMessage": "Types de fichiers installés avec succès!", - "DialogInstallFileTypesErrorMessage": "Échec de l'installation des types de fichiers.", - "DialogUninstallFileTypesSuccessMessage": "Types de fichiers désinstallés avec succès!", - "DialogUninstallFileTypesErrorMessage": "Échec de la désinstallation des types de fichiers.", - "DialogOpenSettingsWindowLabel": "Ouvrir la fenêtre de configuration", - "DialogControllerAppletTitle": "Controller Applet", - "DialogMessageDialogErrorExceptionMessage": "Erreur lors de l'affichage de la boîte de dialogue : {0}", - "DialogSoftwareKeyboardErrorExceptionMessage": "Erreur lors de l'affichage du clavier logiciel: {0}", - "DialogErrorAppletErrorExceptionMessage": "Erreur lors de l'affichage de la boîte de dialogue ErrorApplet: {0}", - "DialogUserErrorDialogMessage": "{0}: {1}", - "DialogUserErrorDialogInfoMessage": "\nPour plus d'informations sur la manière de corriger cette erreur, suivez notre Guide d'Installation.", - "DialogUserErrorDialogTitle": "Erreur Ryujinx ({0})", - "DialogAmiiboApiTitle": "Amiibo API", - "DialogAmiiboApiFailFetchMessage": "Une erreur est survenue lors de la récupération des informations de l'API.", - "DialogAmiiboApiConnectErrorMessage": "Impossible de se connecter au serveur API Amiibo. Le service est peut-être hors service ou vous devriez peut-être vérifier que votre connexion internet est connectée.", - "DialogProfileInvalidProfileErrorMessage": "Le profil {0} est incompatible avec le système de configuration de manette actuel.", - "DialogProfileDefaultProfileOverwriteErrorMessage": "Le profil par défaut ne peut pas être écrasé", - "DialogProfileDeleteProfileTitle": "Supprimer le profil", - "DialogProfileDeleteProfileMessage": "Cette action est irréversible, êtes-vous sûr de vouloir continuer ?", - "DialogWarning": "Avertissement", - "DialogPPTCDeletionMessage": "Vous êtes sur le point de mettre en file d'attente une reconstruction PPTC au prochain démarrage de :\n\n{0}\n\nÊtes-vous sûr de vouloir continuer ?", - "DialogPPTCDeletionErrorMessage": "Erreur lors de la purge du cache PPTC à {0}: {1}", - "DialogShaderDeletionMessage": "Vous êtes sur le point de supprimer le cache du Shader pour :\n\n{0}\n\nÊtes-vous sûr de vouloir continuer ?", - "DialogShaderDeletionErrorMessage": "Erreur lors de la purge du cache du Shader à {0}: {1}", - "DialogRyujinxErrorMessage": "Ryujinx a rencontré une erreur", - "DialogInvalidTitleIdErrorMessage": "Erreur d'UI : le jeu sélectionné n'a pas d'ID de titre valide", - "DialogFirmwareInstallerFirmwareNotFoundErrorMessage": "Un firmware valide n'a pas été trouvé dans {0}.", - "DialogFirmwareInstallerFirmwareInstallTitle": "Installer le Firmware {0}", - "DialogFirmwareInstallerFirmwareInstallMessage": "La version {0} du système sera installée.", - "DialogFirmwareInstallerFirmwareInstallSubMessage": "\n\nCela remplacera la version actuelle du système {0}.", - "DialogFirmwareInstallerFirmwareInstallConfirmMessage": "\n\nVoulez-vous continuer ?", - "DialogFirmwareInstallerFirmwareInstallWaitMessage": "Installation du firmware...", - "DialogFirmwareInstallerFirmwareInstallSuccessMessage": "Version du système {0} installée avec succès.", - "DialogUserProfileDeletionWarningMessage": "Il n'y aurait aucun autre profil à ouvrir si le profil sélectionné est supprimé", - "DialogUserProfileDeletionConfirmMessage": "Voulez-vous supprimer le profil sélectionné ?", - "DialogUserProfileUnsavedChangesTitle": "Avertissement - Modifications non enregistrées", - "DialogUserProfileUnsavedChangesMessage": "Vous avez effectué des modifications sur ce profil d'utilisateur qui n'ont pas été enregistrées.", - "DialogUserProfileUnsavedChangesSubMessage": "Voulez-vous annuler les modifications ?", - "DialogControllerSettingsModifiedConfirmMessage": "Les paramètres actuels du contrôleur ont été mis à jour.", - "DialogControllerSettingsModifiedConfirmSubMessage": "Voulez-vous sauvegarder ?", - "DialogLoadFileErrorMessage": "{0}. Fichier erroné : {1}", - "DialogModAlreadyExistsMessage": "Le mod existe déjà", - "DialogModInvalidMessage": "Le répertoire spécifié ne contient pas de mod !", - "DialogModDeleteNoParentMessage": "Impossible de supprimer : impossible de trouver le répertoire parent pour le mod \"{0} \" !", - "DialogDlcNoDlcErrorMessage": "Le fichier spécifié ne contient pas de DLC pour le titre sélectionné !", - "DialogPerformanceCheckLoggingEnabledMessage": "Vous avez activé la journalisation des traces, conçue pour être utilisée uniquement par les développeurs.", - "DialogPerformanceCheckLoggingEnabledConfirmMessage": "Pour des performances optimales, il est recommandé de désactiver la journalisation des traces. Souhaitez-vous désactiver la journalisation des traces maintenant ?", - "DialogPerformanceCheckShaderDumpEnabledMessage": "Vous avez activé l'extraction des shaders, qui est conçu pour être utilisé par les développeurs uniquement.", - "DialogPerformanceCheckShaderDumpEnabledConfirmMessage": "Pour des performances optimales, il est recommandé de désactiver l'extraction des shaders. Souhaitez-vous désactiver l'extraction des shaders maintenant ?", - "DialogLoadAppGameAlreadyLoadedMessage": "Un jeu a déjà été chargé", - "DialogLoadAppGameAlreadyLoadedSubMessage": "Veuillez arrêter l'émulation ou fermer l'émulateur avant de lancer un autre jeu.", - "DialogUpdateAddUpdateErrorMessage": "Le fichier spécifié ne contient pas de mise à jour pour le titre sélectionné !", - "DialogSettingsBackendThreadingWarningTitle": "Avertissement - Backend Threading ", - "DialogSettingsBackendThreadingWarningMessage": "Ryujinx doit être redémarré après avoir changé cette option pour qu'elle s'applique complètement. Selon votre plate-forme, vous devrez peut-être désactiver manuellement le multithreading de votre pilote lorsque vous utilisez Ryujinx.", - "DialogModManagerDeletionWarningMessage": "Vous êtes sur le point de supprimer le mod : {0}\n\nÊtes-vous sûr de vouloir continuer ?", - "DialogModManagerDeletionAllWarningMessage": "Vous êtes sur le point de supprimer tous les mods pour ce titre.\n\nÊtes-vous sûr de vouloir continuer ?", - "SettingsTabGraphicsFeaturesOptions": "Fonctionnalités", - "SettingsTabGraphicsBackendMultithreading": "Interface graphique multithread", - "CommonAuto": "Auto", - "CommonOff": "Désactivé", - "CommonOn": "Activé", - "InputDialogYes": "Oui", - "InputDialogNo": "Non", - "DialogProfileInvalidProfileNameErrorMessage": "Le nom du fichier contient des caractères invalides. Veuillez réessayer.", - "MenuBarOptionsPauseEmulation": "Suspendre", - "MenuBarOptionsResumeEmulation": "Reprendre", - "AboutUrlTooltipMessage": "Cliquez pour ouvrir le site de Ryujinx dans votre navigateur par défaut.", - "AboutDisclaimerMessage": "Ryujinx n'est pas affilié à Nintendo™,\nou à aucun de ses partenaires, de quelque manière que ce soit.", - "AboutAmiiboDisclaimerMessage": "AmiiboAPI (www.amiiboapi.com) est utilisé\ndans notre émulation Amiibo.", - "AboutPatreonUrlTooltipMessage": "Cliquez pour ouvrir la page Patreon de Ryujinx dans votre navigateur par défaut.", - "AboutGithubUrlTooltipMessage": "Cliquez pour ouvrir la page GitHub de Ryujinx dans votre navigateur par défaut.", - "AboutDiscordUrlTooltipMessage": "Cliquez pour ouvrir une invitation au serveur Discord de Ryujinx dans votre navigateur par défaut.", - "AboutTwitterUrlTooltipMessage": "Cliquez pour ouvrir la page Twitter de Ryujinx dans votre navigateur par défaut.", - "AboutRyujinxAboutTitle": "À propos :", - "AboutRyujinxAboutContent": "Ryujinx est un émulateur pour la Nintendo Switch™.\nMerci de nous soutenir sur Patreon.\nObtenez toutes les dernières actualités sur notre Twitter ou notre Discord.\nLes développeurs intéressés à contribuer peuvent en savoir plus sur notre GitHub ou notre Discord.", - "AboutRyujinxMaintainersTitle": "Maintenu par :", - "AboutRyujinxMaintainersContentTooltipMessage": "Cliquez pour ouvrir la page Contributeurs dans votre navigateur par défaut.", - "AboutRyujinxSupprtersTitle": "Supporté sur Patreon par :", - "AmiiboSeriesLabel": "Séries Amiibo", - "AmiiboCharacterLabel": "Personnage", - "AmiiboScanButtonLabel": "Scanner", - "AmiiboOptionsShowAllLabel": "Afficher tous les Amiibo", - "AmiiboOptionsUsRandomTagLabel": "Hack : Utiliser un tag Uuid aléatoire", - "DlcManagerTableHeadingEnabledLabel": "Activé", - "DlcManagerTableHeadingTitleIdLabel": "ID du titre", - "DlcManagerTableHeadingContainerPathLabel": "Chemin du conteneur", - "DlcManagerTableHeadingFullPathLabel": "Chemin complet", - "DlcManagerRemoveAllButton": "Tout supprimer", - "DlcManagerEnableAllButton": "Tout activer", - "DlcManagerDisableAllButton": "Tout désactiver", - "ModManagerDeleteAllButton": "Tout supprimer", - "MenuBarOptionsChangeLanguage": "Changer la langue", - "MenuBarShowFileTypes": "Afficher les types de fichiers", - "CommonSort": "Trier", - "CommonShowNames": "Afficher les noms", - "CommonFavorite": "Favoris", - "OrderAscending": "Croissant", - "OrderDescending": "Décroissant", - "SettingsTabGraphicsFeatures": "Fonctionnalités & Améliorations", - "ErrorWindowTitle": "Fenêtre d'erreur", - "ToggleDiscordTooltip": "Choisissez d'afficher ou non Ryujinx sur votre activité « en cours de jeu » Discord", - "AddGameDirBoxTooltip": "Entrez un répertoire de jeux à ajouter à la liste", - "AddGameDirTooltip": "Ajouter un répertoire de jeux à la liste", - "RemoveGameDirTooltip": "Supprimer le répertoire de jeu sélectionné", - "CustomThemeCheckTooltip": "Utilisez un thème personnalisé Avalonia pour modifier l'apparence des menus de l'émulateur", - "CustomThemePathTooltip": "Chemin vers le thème personnalisé de l'interface utilisateur", - "CustomThemeBrowseTooltip": "Parcourir vers un thème personnalisé pour l'interface utilisateur", - "DockModeToggleTooltip": "Le mode station d'accueil permet à la console émulée de se comporter comme une Nintendo Switch en mode station d'accueil, ce qui améliore la fidélité graphique dans la plupart des jeux. Inversement, la désactivation de cette option rendra la console émulée comme une console Nintendo Switch portable, réduisant la qualité graphique.\n\nConfigurer les controles du joueur 1 si vous prévoyez d'utiliser le mode station d'accueil; configurez les commandes portable si vous prévoyez d'utiliser le mode portable.\n\nLaissez ACTIVER si vous n'êtes pas sûr.", - "DirectKeyboardTooltip": "Prise en charge de l'accès direct au clavier (HID). Permet aux jeux d'accéder à votre clavier comme périphérique de saisie de texte.\n\nFonctionne uniquement avec les jeux prenant en charge nativement l'utilisation du clavier sur le matériel Switch.\n\nLaissez OFF si vous n'êtes pas sûr.", - "DirectMouseTooltip": "Prise en charge de l'accès direct à la souris (HID). Permet aux jeux d'accéder à votre souris en tant que dispositif de pointage.\n\nFonctionne uniquement avec les jeux qui prennent en charge nativement les contrôles de souris sur le matériel Switch, ce qui est rare.\n\nLorsqu'il est activé, la fonctionnalité de l'écran tactile peut ne pas fonctionner.\n\nLaissez sur OFF si vous n'êtes pas sûr.", - "RegionTooltip": "Changer la région du système", - "LanguageTooltip": "Changer la langue du système", - "TimezoneTooltip": "Changer le fuseau horaire du système", - "TimeTooltip": "Changer l'heure du système", - "VSyncToggleTooltip": "La synchronisation verticale de la console émulée. Essentiellement un limiteur de trame pour la majorité des jeux ; le désactiver peut entraîner un fonctionnement plus rapide des jeux ou prolonger ou bloquer les écrans de chargement.\n\nPeut être activé ou désactivé en jeu avec un raccourci clavier de votre choix (F1 par défaut). Nous recommandons de le faire si vous envisagez de le désactiver.\n\nLaissez activé si vous n'êtes pas sûr.", - "PptcToggleTooltip": "Sauvegarde les fonctions JIT afin qu'elles n'aient pas besoin d'être à chaque fois recompiler lorsque le jeu se charge.\n\nRéduit les lags et accélère considérablement le temps de chargement après le premier lancement d'un jeu.\n\nLaissez par défaut si vous n'êtes pas sûr.", - "FsIntegrityToggleTooltip": "Vérifie si des fichiers sont corrompus lors du lancement d'un jeu, et si des fichiers corrompus sont détectés, affiche une erreur de hachage dans la console.\n\nN'a aucun impact sur les performances et est destiné à aider le dépannage.\n\nLaissez activer en cas d'incertitude.", - "AudioBackendTooltip": "Modifie le backend utilisé pour donnée un rendu audio.\n\nSDL2 est préféré, tandis que OpenAL et SoundIO sont utilisés comme backend secondaire. Le backend Dummy (Factice) ne rends aucun son.\n\nLaissez sur SDL2 si vous n'êtes pas sûr.", - "MemoryManagerTooltip": "Change la façon dont la mémoire émulée est mappée et utiliser. Cela affecte grandement les performances du processeur.\n\nRéglez sur Host Uncheked en cas d'incertitude.", - "MemoryManagerSoftwareTooltip": "Utilisez une table logicielle pour la traduction d'adresses. La plus grande précision est fournie, mais les performances en seront impacter.", - "MemoryManagerHostTooltip": "Mappez directement la mémoire dans l'espace d'adresses de l'hôte. Compilation et exécution JIT beaucoup plus rapides.", - "MemoryManagerUnsafeTooltip": "Mapper directement la mémoire dans la carte, mais ne pas masquer l'adresse dans l'espace d'adressage du client avant l'accès. Plus rapide, mais la sécurité sera négliger. L'application peut accéder à la mémoire depuis n'importe où dans Ryujinx, donc exécutez uniquement les programmes en qui vous avez confiance avec ce mode.", - "UseHypervisorTooltip": "Utiliser l'Hyperviseur au lieu du JIT. Améliore considérablement les performances lorsqu'il est disponible, mais peut être instable dans son état actuel.", - "DRamTooltip": "Utilise une disposition alternative de la mémoire pour imiter le kit de développeur de la Switch.\n\nActiver cette option uniquement pour les packs de textures 4k ou les mods à résolution 4k.\nN'améliore pas les performances, cause des crashs dans certains jeux si activer.\n\nLaissez Désactiver en cas d'incertitude.", - "IgnoreMissingServicesTooltip": "Ignore les services Horizon OS non-intégré. Cela peut aider à contourner les plantages lors du démarrage de certains jeux.\n\nActivez-le en cas d'incertitude.", - "GraphicsBackendThreadingTooltip": "Exécute des commandes du backend graphiques sur un second thread.\n\nAccélère la compilation des shaders, réduit les crashs et les lags, améliore les performances sur les pilotes GPU sans support de multithreading. Légère augementation des performances sur les pilotes avec multithreading intégrer.\n\nRéglez sur Auto en cas d'incertitude.", - "GalThreadingTooltip": "Exécute des commandes du backend graphiques sur un second thread.\n\nAccélère la compilation des shaders, réduit les crashs et les lags, améliore les performances sur les pilotes GPU sans support de multithreading. Légère augementation des performances sur les pilotes avec multithreading intégrer.\n\nRéglez sur Auto en cas d'incertitude.", - "ShaderCacheToggleTooltip": "Enregistre un cache de shaders sur le disque dur, réduit le lag lors de multiples exécutions.\n\nLaissez Activer si vous n'êtes pas sûr.", - "ResolutionScaleTooltip": "Multiplie la résolution de rendu du jeu.\n\nQuelques jeux peuvent ne pas fonctionner avec cette fonctionnalité et sembler pixelisés même lorsque la résolution est augmentée ; pour ces jeux, vous devrez peut-être trouver des mods qui suppriment l'anti-aliasing ou qui augmentent leur résolution de rendu interne. Pour utiliser cette dernière option, vous voudrez probablement sélectionner \"Native\".\n\nCette option peut être modifiée pendant qu'un jeu est en cours d'exécution en cliquant sur \"Appliquer\" ci-dessous ; vous pouvez simplement déplacer la fenêtre des paramètres de côté et expérimenter jusqu'à ce que vous trouviez l'apparence souhaitée pour un jeu.\n\nGardez à l'esprit que 4x est excessif pour pratiquement n'importe quelle configuration.", - "ResolutionScaleEntryTooltip": "Échelle de résolution à virgule flottante, telle que : 1.5. Les échelles non intégrales sont plus susceptibles de causer des problèmes ou des crashs.", - "AnisotropyTooltip": "Niveau de filtrage anisotrope. Réglez sur Auto pour utiliser la valeur demandée par le jeu.", - "AspectRatioTooltip": "Rapport d'aspect appliqué à la fenêtre du moteur de rendu.\n\nChangez cela uniquement si vous utilisez un mod de rapport d'aspect pour votre jeu, sinon les graphismes seront étirés.\n\nLaissez sur 16:9 si vous n'êtes pas sûr.", - "ShaderDumpPathTooltip": "Chemin de copie des Shaders Graphiques", - "FileLogTooltip": "Sauver le journal de la console dans un fichier journal sur le disque. Cela n'affecte pas les performances.", - "StubLogTooltip": "Affiche les messages de log dans la console. N'affecte pas les performances.", - "InfoLogTooltip": "Affiche les messages de log d'informations dans la console. N'affecte pas les performances.", - "WarnLogTooltip": "Affiche les messages d'avertissement dans la console. N'affecte pas les performances.", - "ErrorLogTooltip": "Affiche les messages de log d'erreur dans la console. N'affecte pas les performances.", - "TraceLogTooltip": "Affiche la trace des messages de log dans la console. N'affecte pas les performances.", - "GuestLogTooltip": "Affiche les messages de log des invités dans la console. N'affecte pas les performances.", - "FileAccessLogTooltip": "Affiche les messages de log d'accès aux fichiers dans la console.", - "FSAccessLogModeTooltip": "Active la sortie du journal d'accès FS de la console. Les modes possibles sont 0-3", - "DeveloperOptionTooltip": "Utiliser avec précaution", - "OpenGlLogLevel": "Nécessite l'activation des niveaux de journalisation appropriés", - "DebugLogTooltip": "Affiche les messages de débogage dans la console.\n\nN'utilisez ceci que si un membre du personnel le demande, car cela rendra les logs difficiles à lire et réduit les performances de l'émulateur.", - "LoadApplicationFileTooltip": "Ouvrir un explorateur de fichiers pour choisir un fichier compatible Switch à charger", - "LoadApplicationFolderTooltip": "Ouvrir un explorateur de fichiers pour choisir une application Switch compatible et décompressée à charger", - "OpenRyujinxFolderTooltip": "Ouvrir le dossier du système de fichiers Ryujinx", - "OpenRyujinxLogsTooltip": "Ouvre le dossier dans lequel les journaux sont écrits", - "ExitTooltip": "Quitter Ryujinx", - "OpenSettingsTooltip": "Ouvrir la fenêtre de configuration", - "OpenProfileManagerTooltip": "Ouvrir la fenêtre du gestionnaire de profils d'utilisateurs", - "StopEmulationTooltip": "Arrêter l'émulation du jeu en cours et revenir à la sélection des jeux", - "CheckUpdatesTooltip": "Vérifier les mises à jour de Ryujinx", - "OpenAboutTooltip": "Ouvrir la fenêtre À Propos", - "GridSize": "Taille de la grille", - "GridSizeTooltip": "Modifier la taille des éléments de la grille", - "SettingsTabSystemSystemLanguageBrazilianPortuguese": "Portugais brésilien", - "AboutRyujinxContributorsButtonHeader": "Voir tous les contributeurs", - "SettingsTabSystemAudioVolume": "Volume :", - "AudioVolumeTooltip": "Modifier le volume audio", - "SettingsTabSystemEnableInternetAccess": "Accès Internet Invité/Mode LAN", - "EnableInternetAccessTooltip": "Permet à l'application émulée de se connecter à Internet.\n\nLes jeux avec un mode LAN peuvent se connecter les uns aux autres lorsque cette option est cochée et que les systèmes sont connectés au même point d'accès. Cela inclut également les vrais consoles.\n\nCette option n'autorise PAS la connexion aux serveurs Nintendo. Elle peut faire planter certains jeux qui essaient de se connecter à l'Internet.\n\nLaissez DÉSACTIVÉ si vous n'êtes pas sûr.", - "GameListContextMenuManageCheatToolTip": "Gérer la triche", - "GameListContextMenuManageCheat": "Gérer la triche", - "GameListContextMenuManageModToolTip": "Gérer les mods", - "GameListContextMenuManageMod": "Gérer les mods", - "ControllerSettingsStickRange": "Intervalle :", - "DialogStopEmulationTitle": "Ryujinx - Arrêt de l'émulation", - "DialogStopEmulationMessage": "Êtes-vous sûr de vouloir arrêter l'émulation ?", - "SettingsTabCpu": "CPU", - "SettingsTabAudio": "Audio", - "SettingsTabNetwork": "Réseau", - "SettingsTabNetworkConnection": "Connexion réseau", - "SettingsTabCpuCache": "Cache CPU", - "SettingsTabCpuMemory": "Mémoire CPU", - "DialogUpdaterFlatpakNotSupportedMessage": "Merci de mettre à jour Ryujinx via FlatHub.", - "UpdaterDisabledWarningTitle": "Mise à jour désactivée !", - "ControllerSettingsRotate90": "Faire pivoter de 90° à droite", - "IconSize": "Taille d'icône", - "IconSizeTooltip": "Changer la taille des icônes de jeu", - "MenuBarOptionsShowConsole": "Afficher la console", - "ShaderCachePurgeError": "Erreur lors de la purge du cache du Shader à {0}: {1}", - "UserErrorNoKeys": "Clés introuvables", - "UserErrorNoFirmware": "Firmware introuvable", - "UserErrorFirmwareParsingFailed": "Erreur d'analyse du firmware", - "UserErrorApplicationNotFound": " Application introuvable", - "UserErrorUnknown": "Erreur inconnue", - "UserErrorUndefined": "Erreur non définie", - "UserErrorNoKeysDescription": "Ryujinx n'a pas pu trouver votre fichier 'prod.keys'", - "UserErrorNoFirmwareDescription": "Ryujinx n'a pas trouvé de firmwares installés", - "UserErrorFirmwareParsingFailedDescription": "Ryujinx n'a pas pu analyser le firmware fourni. Cela est généralement dû à des clés obsolètes.", - "UserErrorApplicationNotFoundDescription": "Ryujinx n'a pas pu trouver une application valide dans le chemin indiqué.", - "UserErrorUnknownDescription": "Une erreur inconnue est survenue !", - "UserErrorUndefinedDescription": "Une erreur inconnue est survenue ! Cela ne devrait pas se produire, merci de contacter un développeur !", - "OpenSetupGuideMessage": "Ouvrir le guide d'installation", - "NoUpdate": "Aucune mise à jour", - "TitleUpdateVersionLabel": "Version {0}", - "RyujinxInfo": "Ryujinx - Info", - "RyujinxConfirm": "Ryujinx - Confirmation", - "FileDialogAllTypes": "Tous les types", - "Never": "Jamais", - "SwkbdMinCharacters": "Doit comporter au moins {0} caractères", - "SwkbdMinRangeCharacters": "Doit comporter entre {0} et {1} caractères", - "SoftwareKeyboard": "Clavier logiciel", - "SoftwareKeyboardModeNumeric": "Doit être 0-9 ou '.' uniquement", - "SoftwareKeyboardModeAlphabet": "Doit être uniquement des caractères non CJK", - "SoftwareKeyboardModeASCII": "Doit être uniquement du texte ASCII", - "ControllerAppletControllers": "Contrôleurs pris en charge :", - "ControllerAppletPlayers": "Joueurs :", - "ControllerAppletDescription": "Votre configuration actuelle n'est pas valide. Ouvrez les paramètres et reconfigurez vos contrôles.", - "ControllerAppletDocked": "Mode station d'accueil défini. Le mode contrôle portable doit être désactivé.", - "UpdaterRenaming": "Renommage des anciens fichiers...", - "UpdaterRenameFailed": "Impossible de renommer le fichier : {0}", - "UpdaterAddingFiles": "Ajout des nouveaux fichiers...", - "UpdaterExtracting": "Extraction de la mise à jour…", - "UpdaterDownloading": "Téléchargement de la mise à jour...", - "Game": "Jeu", - "Docked": "Mode station d'accueil", - "Handheld": "Mode Portable", - "ConnectionError": "Erreur de connexion.", - "AboutPageDeveloperListMore": "{0} et plus...", - "ApiError": "Erreur API.", - "LoadingHeading": "Chargement {0}", - "CompilingPPTC": "Compilation PTC", - "CompilingShaders": "Compilation des shaders", - "AllKeyboards": "Tous les claviers", - "OpenFileDialogTitle": "Sélectionnez un fichier supporté à ouvrir", - "OpenFolderDialogTitle": "Sélectionnez un dossier avec un jeu décompressé", - "AllSupportedFormats": "Tous les formats supportés", - "RyujinxUpdater": "Mise à jour de Ryujinx", - "SettingsTabHotkeys": "Raccourcis clavier", - "SettingsTabHotkeysHotkeys": "Raccourcis clavier", - "SettingsTabHotkeysToggleVsyncHotkey": "Activer/désactiver la VSync :", - "SettingsTabHotkeysScreenshotHotkey": "Capture d'écran :", - "SettingsTabHotkeysShowUiHotkey": "Afficher UI :", - "SettingsTabHotkeysPauseHotkey": "Suspendre :", - "SettingsTabHotkeysToggleMuteHotkey": "Muet : ", - "ControllerMotionTitle": "Réglages du contrôle par mouvement", - "ControllerRumbleTitle": "Paramètres de vibration", - "SettingsSelectThemeFileDialogTitle": "Sélectionner un fichier de thème", - "SettingsXamlThemeFile": "Fichier thème Xaml", - "AvatarWindowTitle": "Gérer les Comptes - Avatar", - "Amiibo": "Amiibo", - "Unknown": "Inconnu", - "Usage": "Utilisation", - "Writable": "Ecriture possible", - "SelectDlcDialogTitle": "Sélectionner les fichiers DLC", - "SelectUpdateDialogTitle": "Sélectionner les fichiers de mise à jour", - "SelectModDialogTitle": "Sélectionner le répertoire du mod", - "UserProfileWindowTitle": "Gestionnaire de profils utilisateur", - "CheatWindowTitle": "Gestionnaire de triches", - "DlcWindowTitle": "Gérer le contenu téléchargeable pour {0} ({1})", - "ModWindowTitle": "Gérer les mods pour {0} ({1})", - "UpdateWindowTitle": "Gestionnaire de mises à jour", - "CheatWindowHeading": "Cheats disponibles pour {0} [{1}]", - "BuildId": "BuildId:", - "DlcWindowHeading": "{0} Contenu(s) téléchargeable(s)", - "ModWindowHeading": "{0} Mod(s)", - "UserProfilesEditProfile": "Éditer la sélection", - "Cancel": "Annuler", - "Save": "Enregistrer", - "Discard": "Abandonner", - "Paused": "Suspendu", - "UserProfilesSetProfileImage": "Définir l'image de profil", - "UserProfileEmptyNameError": "Le nom est requis", - "UserProfileNoImageError": "L'image du profil doit être définie", - "GameUpdateWindowHeading": "Gérer les mises à jour pour {0} ({1})", - "SettingsTabHotkeysResScaleUpHotkey": "Augmenter la résolution :", - "SettingsTabHotkeysResScaleDownHotkey": "Diminuer la résolution :", - "UserProfilesName": "Nom :", - "UserProfilesUserId": "Identifiant de l'utilisateur :", - "SettingsTabGraphicsBackend": "API de Rendu", - "SettingsTabGraphicsBackendTooltip": "Sélectionnez le moteur graphique qui sera utilisé dans l'émulateur.\n\nVulkan est globalement meilleur pour toutes les cartes graphiques modernes, tant que leurs pilotes sont à jour. Vulkan offre également une compilation de shaders plus rapide (moins de saccades) sur tous les fournisseurs de GPU.\n\nOpenGL peut obtenir de meilleurs résultats sur d'anciennes cartes graphiques Nvidia, sur d'anciennes cartes graphiques AMD sous Linux, ou sur des GPU avec moins de VRAM, bien que les saccades dues à la compilation des shaders soient plus importantes.\n\nRéglez sur Vulkan si vous n'êtes pas sûr. Réglez sur OpenGL si votre GPU ne prend pas en charge Vulkan même avec les derniers pilotes graphiques.", - "SettingsEnableTextureRecompression": "Activer la recompression des textures", - "SettingsEnableTextureRecompressionTooltip": "Les jeux utilisant ce format de texture incluent Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder et The Legend of Zelda: Tears of the Kingdom.\n\nLes cartes graphiques avec 4 Go ou moins de VRAM risquent probablement de planter à un moment donné lors de l'exécution de ces jeux.\n\nActivez uniquement si vous manquez de VRAM sur les jeux mentionnés ci-dessus. Laissez DÉSACTIVÉ si vous n'êtes pas sûr.", - "SettingsTabGraphicsPreferredGpu": "GPU préféré", - "SettingsTabGraphicsPreferredGpuTooltip": "Sélectionnez la carte graphique qui sera utilisée avec l'interface graphique Vulkan.\n\nCela ne change pas le GPU qu'OpenGL utilisera.\n\nChoisissez le GPU noté \"dGPU\" si vous n'êtes pas sûr. S'il n'y en a pas, ne pas modifier.", - "SettingsAppRequiredRestartMessage": "Redémarrage de Ryujinx requis", - "SettingsGpuBackendRestartMessage": "Les paramètres de l'interface graphique ou du GPU ont été modifiés. Cela nécessitera un redémarrage pour être appliqué", - "SettingsGpuBackendRestartSubMessage": "\n\nVoulez-vous redémarrer maintenant ?", - "RyujinxUpdaterMessage": "Voulez-vous mettre à jour Ryujinx vers la dernière version ?", - "SettingsTabHotkeysVolumeUpHotkey": "Augmenter le volume :", - "SettingsTabHotkeysVolumeDownHotkey": "Diminuer le volume :", - "SettingsEnableMacroHLE": "Activer les macros HLE", - "SettingsEnableMacroHLETooltip": "Émulation de haut niveau du code de Macro GPU.\n\nAméliore les performances, mais peut causer des artefacts graphiques dans certains jeux.\n\nLaissez ACTIVER si vous n'êtes pas sûr.", - "SettingsEnableColorSpacePassthrough": "Traversée de l'espace colorimétrique", - "SettingsEnableColorSpacePassthroughTooltip": "Dirige l'interface graphique Vulkan pour qu'il transmette les informations de couleur sans spécifier d'espace colorimétrique. Pour les utilisateurs possédant des écrans Wide Color Gamut, cela peut entraîner des couleurs plus vives, au détriment de l'exactitude des couleurs.", - "VolumeShort": "Vol", - "UserProfilesManageSaves": "Gérer les sauvegardes", - "DeleteUserSave": "Voulez-vous supprimer la sauvegarde de l'utilisateur pour ce jeu ?", - "IrreversibleActionNote": "Cette action n'est pas réversible.", - "SaveManagerHeading": "Gérer les sauvegardes pour {0} ({1})", - "SaveManagerTitle": "Gestionnaire de sauvegarde", - "Name": "Nom ", - "Size": "Taille", - "Search": "Rechercher", - "UserProfilesRecoverLostAccounts": "Récupérer les comptes perdus", - "Recover": "Récupérer", - "UserProfilesRecoverHeading": "Des sauvegardes ont été trouvées pour les comptes suivants", - "UserProfilesRecoverEmptyList": "Aucun profil à restaurer", - "GraphicsAATooltip": "FXAA floute la plupart de l'image, tandis que SMAA tente de détecter les contours dentelés et de les lisser.\n\nIl n'est pas recommandé de l'utiliser en conjonction avec le filtre de mise à l'échelle FSR.\n\nCette option peut être modifiée pendant qu'un jeu est en cours d'exécution en cliquant sur \"Appliquer\" ci-dessous ; vous pouvez simplement déplacer la fenêtre des paramètres de côté et expérimenter jusqu'à ce que vous trouviez l'apparence souhaitée pour un jeu.\n\nLaissez sur NONE si vous n'êtes pas sûr.", - "GraphicsAALabel": "Anticrénelage :", - "GraphicsScalingFilterLabel": "Filtre de mise à l'échelle :", - "GraphicsScalingFilterTooltip": "Choisissez le filtre de mise à l'échelle qui sera appliqué lors de l'utilisation de la mise à l'échelle de la résolution.\n\nLe filtre bilinéaire fonctionne bien pour les jeux en 3D et constitue une option par défaut sûre.\n\nLe filtre le plus proche est recommandé pour les jeux de pixel art.\n\nFSR 1.0 est simplement un filtre de netteté, non recommandé pour une utilisation avec FXAA ou SMAA.\n\nCette option peut être modifiée pendant qu'un jeu est en cours d'exécution en cliquant sur \"Appliquer\" ci-dessous ; vous pouvez simplement déplacer la fenêtre des paramètres de côté et expérimenter jusqu'à ce que vous trouviez l'aspect souhaité pour un jeu.\n\nLaissez sur BILINEAR si vous n'êtes pas sûr.", - "GraphicsScalingFilterBilinear": "Bilinéaire", - "GraphicsScalingFilterNearest": "Le plus proche", - "GraphicsScalingFilterFsr": "FSR", - "GraphicsScalingFilterLevelLabel": "Niveau ", - "GraphicsScalingFilterLevelTooltip": "Définissez le niveau de netteté FSR 1.0. Plus élevé signifie plus net.", - "SmaaLow": "SMAA Faible", - "SmaaMedium": "SMAA moyen", - "SmaaHigh": "SMAA Élevé", - "SmaaUltra": "SMAA Ultra", - "UserEditorTitle": "Modifier Utilisateur", - "UserEditorTitleCreate": "Créer Utilisateur", - "SettingsTabNetworkInterface": "Interface Réseau :", - "NetworkInterfaceTooltip": "L'interface réseau utilisée pour les fonctionnalités LAN/LDN.\n\nEn conjonction avec un VPN ou XLink Kai et un jeu prenant en charge le LAN, peut être utilisée pour simuler une connexion sur le même réseau via Internet.\n\nLaissez sur DEFAULT si vous n'êtes pas sûr.", - "NetworkInterfaceDefault": "Par défaut", - "PackagingShaders": "Empaquetage des Shaders", - "AboutChangelogButton": "Voir le Changelog sur GitHub", - "AboutChangelogButtonTooltipMessage": "Cliquez pour ouvrir le changelog de cette version dans votre navigateur par défaut.", - "SettingsTabNetworkMultiplayer": "Multijoueur", - "MultiplayerMode": "Mode :", - "MultiplayerModeTooltip": "Changer le mode multijoueur LDN.\n\nLdnMitm modifiera la fonctionnalité de jeu sans fil local/jeu local dans les jeux pour fonctionner comme s'il s'agissait d'un LAN, permettant des connexions locales sur le même réseau avec d'autres instances de Ryujinx et des consoles Nintendo Switch piratées ayant le module ldn_mitm installé.\n\nLe multijoueur nécessite que tous les joueurs soient sur la même version du jeu (par exemple, Super Smash Bros. Ultimate v13.0.1 ne peut pas se connecter à v13.0.0).\n\nLaissez DÉSACTIVÉ si vous n'êtes pas sûr.", - "MultiplayerModeDisabled": "Désactivé", - "MultiplayerModeLdnMitm": "ldn_mitm" -} diff --git a/src/Ryujinx/Assets/Locales/he_IL.json b/src/Ryujinx/Assets/Locales/he_IL.json deleted file mode 100644 index 848f78080..000000000 --- a/src/Ryujinx/Assets/Locales/he_IL.json +++ /dev/null @@ -1,780 +0,0 @@ -{ - "Language": "עִברִית", - "MenuBarFileOpenApplet": "פתח יישומון", - "MenuBarFileOpenAppletOpenMiiAppletToolTip": "פתח את יישומון עורך ה- Mii במצב עצמאי", - "SettingsTabInputDirectMouseAccess": "גישה ישירה לעכבר", - "SettingsTabSystemMemoryManagerMode": "מצב מנהל זיכרון:", - "SettingsTabSystemMemoryManagerModeSoftware": "תוכנה", - "SettingsTabSystemMemoryManagerModeHost": "מארח (מהיר)", - "SettingsTabSystemMemoryManagerModeHostUnchecked": "מארח לא מבוקר (המהיר ביותר, לא בטוח)", - "SettingsTabSystemUseHypervisor": "השתמש ב Hypervisor", - "MenuBarFile": "_קובץ", - "MenuBarFileOpenFromFile": "_טען יישום מקובץ", - "MenuBarFileOpenUnpacked": "טען משחק _שאינו ארוז", - "MenuBarFileOpenEmuFolder": "פתח את תיקיית ריוג'ינקס", - "MenuBarFileOpenLogsFolder": "פתח את תיקיית קבצי הלוג", - "MenuBarFileExit": "_יציאה", - "MenuBarOptions": "_אפשרויות", - "MenuBarOptionsToggleFullscreen": "שנה מצב- מסך מלא", - "MenuBarOptionsStartGamesInFullscreen": "התחל משחקים במסך מלא", - "MenuBarOptionsStopEmulation": "עצור אמולציה", - "MenuBarOptionsSettings": "_הגדרות", - "MenuBarOptionsManageUserProfiles": "_נהל פרופילי משתמש", - "MenuBarActions": "_פעולות", - "MenuBarOptionsSimulateWakeUpMessage": "דמה הודעת השכמה", - "MenuBarActionsScanAmiibo": "סרוק אמיבו", - "MenuBarTools": "_כלים", - "MenuBarToolsInstallFirmware": "התקן קושחה", - "MenuBarFileToolsInstallFirmwareFromFile": "התקן קושחה מקובץ- ZIP/XCI", - "MenuBarFileToolsInstallFirmwareFromDirectory": "התקן קושחה מתוך תקייה", - "MenuBarToolsManageFileTypes": "ניהול סוגי קבצים", - "MenuBarToolsInstallFileTypes": "סוגי קבצי התקנה", - "MenuBarToolsUninstallFileTypes": "סוגי קבצי הסרה", - "MenuBarView": "_View", - "MenuBarViewWindow": "Window Size", - "MenuBarViewWindow720": "720p", - "MenuBarViewWindow1080": "1080p", - "MenuBarHelp": "_עזרה", - "MenuBarHelpCheckForUpdates": "חפש עדכונים", - "MenuBarHelpAbout": "אודות", - "MenuSearch": "חפש...", - "GameListHeaderFavorite": "אהוב", - "GameListHeaderIcon": "סמל", - "GameListHeaderApplication": "שם", - "GameListHeaderDeveloper": "מפתח", - "GameListHeaderVersion": "גרסה", - "GameListHeaderTimePlayed": "זמן משחק", - "GameListHeaderLastPlayed": "שוחק לאחרונה", - "GameListHeaderFileExtension": "סיומת קובץ", - "GameListHeaderFileSize": "גודל הקובץ", - "GameListHeaderPath": "נתיב", - "GameListContextMenuOpenUserSaveDirectory": "פתח את תקיית השמור של המשתמש", - "GameListContextMenuOpenUserSaveDirectoryToolTip": "פותח את תקיית השמור של המשתמש ביישום הנוכחי", - "GameListContextMenuOpenDeviceSaveDirectory": "פתח את תקיית השמור של המכשיר", - "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "פותח את הספרייה המכילה את שמור המכשיר של היישום", - "GameListContextMenuOpenBcatSaveDirectory": "פתח את תקיית השמור של ה-BCAT", - "GameListContextMenuOpenBcatSaveDirectoryToolTip": "פותח את תקיית שמור ה-BCAT של היישום", - "GameListContextMenuManageTitleUpdates": "מנהל עדכוני משחקים", - "GameListContextMenuManageTitleUpdatesToolTip": "פותח את חלון מנהל עדכוני המשחקים", - "GameListContextMenuManageDlc": "מנהל הרחבות", - "GameListContextMenuManageDlcToolTip": "פותח את חלון מנהל הרחבות המשחקים", - "GameListContextMenuCacheManagement": "ניהול מטמון", - "GameListContextMenuCacheManagementPurgePptc": "הוסף PPTC לתור בנייה מחדש", - "GameListContextMenuCacheManagementPurgePptcToolTip": "גרום ל-PPTC להבנות מחדש בפתיחה הבאה של המשחק", - "GameListContextMenuCacheManagementPurgeShaderCache": "ניקוי מטמון הצללות", - "GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "מוחק את מטמון ההצללות של היישום", - "GameListContextMenuCacheManagementOpenPptcDirectory": "פתח את תקיית PPTC", - "GameListContextMenuCacheManagementOpenPptcDirectoryToolTip": "פותח את התקייה של מטמון ה-PPTC של האפליקציה", - "GameListContextMenuCacheManagementOpenShaderCacheDirectory": "פתח את תקיית המטמון של ההצללות", - "GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip": "פותח את תקיית מטמון ההצללות של היישום", - "GameListContextMenuExtractData": "חילוץ נתונים", - "GameListContextMenuExtractDataExeFS": "ExeFS", - "GameListContextMenuExtractDataExeFSToolTip": "חלץ את קטע ה-ExeFS מתצורת היישום הנוכחית (כולל עדכונים)", - "GameListContextMenuExtractDataRomFS": "RomFS", - "GameListContextMenuExtractDataRomFSToolTip": "חלץ את קטע ה-RomFS מתצורת היישום הנוכחית (כולל עדכונים)", - "GameListContextMenuExtractDataLogo": "Logo", - "GameListContextMenuExtractDataLogoToolTip": "חלץ את קטע ה-Logo מתצורת היישום הנוכחית (כולל עדכונים)", - "GameListContextMenuCreateShortcut": "ליצור קיצור דרך לאפליקציה", - "GameListContextMenuCreateShortcutToolTip": "ליצור קיצור דרך בשולחן העבודה שיפתח את אפליקציה זו", - "GameListContextMenuCreateShortcutToolTipMacOS": "ליצור קיצור דרך בתיקיית האפליקציות של macOS שיפתח את אפליקציה זו", - "GameListContextMenuOpenModsDirectory": "פתח תיקיית מודים", - "GameListContextMenuOpenModsDirectoryToolTip": "פותח את התיקייה שמכילה מודים של האפליקציה", - "GameListContextMenuOpenSdModsDirectory": "פתח תיקיית מודים של Atmosphere", - "GameListContextMenuOpenSdModsDirectoryToolTip": "פותח את תיקיית כרטיס ה-SD החלופית של Atmosphere המכילה את המודים של האפליקציה. שימושי עבור מודים שארוזים עבור חומרה אמיתית.", - "StatusBarGamesLoaded": "{1}/{0} משחקים נטענו", - "StatusBarSystemVersion": "גרסת מערכת: {0}", - "LinuxVmMaxMapCountDialogTitle": "זוהתה מגבלה נמוכה עבור מיפויי זיכרון", - "LinuxVmMaxMapCountDialogTextPrimary": "האם תרצה להגביר את הערך של vm.max_map_count ל{0}", - "LinuxVmMaxMapCountDialogTextSecondary": "משחקים מסוימים עלולים לייצר עוד מיפויי זיכרון ממה שמתאפשר. Ryujinx יקרוס ברגע שהמגבלה תחרוג.", - "LinuxVmMaxMapCountDialogButtonUntilRestart": "כן, עד האתחול הבא", - "LinuxVmMaxMapCountDialogButtonPersistent": "כן, לצמיתות", - "LinuxVmMaxMapCountWarningTextPrimary": "הכמות המירבית של מיפויי הזיכרון נמוכה מהמומלץ.", - "LinuxVmMaxMapCountWarningTextSecondary": "הערך הנוכחי של vm.max_map_count {0} נמוך מ{1}. משחקים מסוימים עלולים לייצר עוד מיפוי זיכרון ממה שמתאפשר.Ryujinx יקרוס ברגע שהמגבלה תחרוג.\n\nיתכן ותרצה להעלות את המגבלה הנוכחית או להתקין את pkexec, אשר יאפשר לRyujinx לסייע בכך.", - "Settings": "הגדרות", - "SettingsTabGeneral": "ממשק משתמש", - "SettingsTabGeneralGeneral": "כללי", - "SettingsTabGeneralEnableDiscordRichPresence": "הפעלת תצוגה עשירה בדיסקורד", - "SettingsTabGeneralCheckUpdatesOnLaunch": "בדוק אם קיימים עדכונים בהפעלה", - "SettingsTabGeneralShowConfirmExitDialog": "הראה דיאלוג \"אשר יציאה\"", - "SettingsTabGeneralRememberWindowState": "Remember Window Size/Position", - "SettingsTabGeneralHideCursor": "הסתר את הסמן", - "SettingsTabGeneralHideCursorNever": "אף פעם", - "SettingsTabGeneralHideCursorOnIdle": "במצב סרק", - "SettingsTabGeneralHideCursorAlways": "תמיד", - "SettingsTabGeneralGameDirectories": "תקיות משחקים", - "SettingsTabGeneralAdd": "הוסף", - "SettingsTabGeneralRemove": "הסר", - "SettingsTabSystem": "מערכת", - "SettingsTabSystemCore": "ליבה", - "SettingsTabSystemSystemRegion": "אזור מערכת:", - "SettingsTabSystemSystemRegionJapan": "יפן", - "SettingsTabSystemSystemRegionUSA": "ארה\"ב", - "SettingsTabSystemSystemRegionEurope": "אירופה", - "SettingsTabSystemSystemRegionAustralia": "אוסטרליה", - "SettingsTabSystemSystemRegionChina": "סין", - "SettingsTabSystemSystemRegionKorea": "קוריאה", - "SettingsTabSystemSystemRegionTaiwan": "טייוואן", - "SettingsTabSystemSystemLanguage": "שפת המערכת:", - "SettingsTabSystemSystemLanguageJapanese": "יפנית", - "SettingsTabSystemSystemLanguageAmericanEnglish": "אנגלית אמריקאית", - "SettingsTabSystemSystemLanguageFrench": "צרפתית", - "SettingsTabSystemSystemLanguageGerman": "גרמנית", - "SettingsTabSystemSystemLanguageItalian": "איטלקית", - "SettingsTabSystemSystemLanguageSpanish": "ספרדית", - "SettingsTabSystemSystemLanguageChinese": "סינית", - "SettingsTabSystemSystemLanguageKorean": "קוריאנית", - "SettingsTabSystemSystemLanguageDutch": "הולנדית", - "SettingsTabSystemSystemLanguagePortuguese": "פורטוגזית", - "SettingsTabSystemSystemLanguageRussian": "רוסית", - "SettingsTabSystemSystemLanguageTaiwanese": "טייוואנית", - "SettingsTabSystemSystemLanguageBritishEnglish": "אנגלית בריטית", - "SettingsTabSystemSystemLanguageCanadianFrench": "צרפתית קנדית", - "SettingsTabSystemSystemLanguageLatinAmericanSpanish": "ספרדית אמריקה הלטינית", - "SettingsTabSystemSystemLanguageSimplifiedChinese": "סינית פשוטה", - "SettingsTabSystemSystemLanguageTraditionalChinese": "סינית מסורתית", - "SettingsTabSystemSystemTimeZone": "אזור זמן מערכת:", - "SettingsTabSystemSystemTime": "זמן מערכת:", - "SettingsTabSystemEnableVsync": "VSync", - "SettingsTabSystemEnablePptc": "PPTC (Profiled Persistent Translation Cache)", - "SettingsTabSystemEnableFsIntegrityChecks": "FS בדיקות תקינות", - "SettingsTabSystemAudioBackend": "אחראי שמע:", - "SettingsTabSystemAudioBackendDummy": "גולם", - "SettingsTabSystemAudioBackendOpenAL": "OpenAL", - "SettingsTabSystemAudioBackendSoundIO": "SoundIO", - "SettingsTabSystemAudioBackendSDL2": "SDL2", - "SettingsTabSystemHacks": "האצות", - "SettingsTabSystemHacksNote": "עלול לגרום לאי יציבות", - "SettingsTabSystemExpandDramSize": "השתמש בפריסת זיכרון חלופית (נועד למפתחים)", - "SettingsTabSystemIgnoreMissingServices": "התעלם משירותים חסרים", - "SettingsTabGraphics": "גרפיקה", - "SettingsTabGraphicsAPI": "ממשק גראפי", - "SettingsTabGraphicsEnableShaderCache": "הפעל מטמון הצללות", - "SettingsTabGraphicsAnisotropicFiltering": "סינון אניסוטרופי:", - "SettingsTabGraphicsAnisotropicFilteringAuto": "אוטומטי", - "SettingsTabGraphicsAnisotropicFiltering2x": "2x", - "SettingsTabGraphicsAnisotropicFiltering4x": "4x", - "SettingsTabGraphicsAnisotropicFiltering8x": "8x", - "SettingsTabGraphicsAnisotropicFiltering16x": "16x", - "SettingsTabGraphicsResolutionScale": "קנה מידה של רזולוציה:", - "SettingsTabGraphicsResolutionScaleCustom": "מותאם אישית (לא מומלץ)", - "SettingsTabGraphicsResolutionScaleNative": "מקורי (720p/1080p)", - "SettingsTabGraphicsResolutionScale2x": "2x (1440p/2160p)", - "SettingsTabGraphicsResolutionScale3x": "3x (2160p/3240p)", - "SettingsTabGraphicsResolutionScale4x": "4x (2880p/4320p) (לא מומלץ)", - "SettingsTabGraphicsAspectRatio": "יחס גובה-רוחב:", - "SettingsTabGraphicsAspectRatio4x3": "4:3", - "SettingsTabGraphicsAspectRatio16x9": "16:9", - "SettingsTabGraphicsAspectRatio16x10": "16:10", - "SettingsTabGraphicsAspectRatio21x9": "21:9", - "SettingsTabGraphicsAspectRatio32x9": "32:9", - "SettingsTabGraphicsAspectRatioStretch": "מתח לגודל חלון", - "SettingsTabGraphicsDeveloperOptions": "אפשרויות מפתח", - "SettingsTabGraphicsShaderDumpPath": "Graphics Shader Dump Path:", - "SettingsTabLogging": "רישום", - "SettingsTabLoggingLogging": "רישום", - "SettingsTabLoggingEnableLoggingToFile": "אפשר רישום לקובץ", - "SettingsTabLoggingEnableStubLogs": "אפשר רישום בדל", - "SettingsTabLoggingEnableInfoLogs": "אפשר רישום מידע", - "SettingsTabLoggingEnableWarningLogs": "אפשר רישום אזהרות", - "SettingsTabLoggingEnableErrorLogs": "אפשר רישום שגיאות", - "SettingsTabLoggingEnableTraceLogs": "הפעל רישום מעקבי", - "SettingsTabLoggingEnableGuestLogs": "הפעל רישום מארח", - "SettingsTabLoggingEnableFsAccessLogs": "אפשר רישום גישת קבצי מערכת", - "SettingsTabLoggingFsGlobalAccessLogMode": "מצב רישום גלובלי של גישת קבצי מערכת", - "SettingsTabLoggingDeveloperOptions": "אפשרויות מפתח", - "SettingsTabLoggingDeveloperOptionsNote": "אזהרה: יפחית ביצועים", - "SettingsTabLoggingGraphicsBackendLogLevel": "רישום גרפיקת קצה אחורי:", - "SettingsTabLoggingGraphicsBackendLogLevelNone": "כלום", - "SettingsTabLoggingGraphicsBackendLogLevelError": "שגיאה", - "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "האטות", - "SettingsTabLoggingGraphicsBackendLogLevelAll": "הכל", - "SettingsTabLoggingEnableDebugLogs": "אפשר רישום ניפוי באגים", - "SettingsTabInput": "קלט", - "SettingsTabInputEnableDockedMode": "מצב עגינה", - "SettingsTabInputDirectKeyboardAccess": "גישה ישירה למקלדת", - "SettingsButtonSave": "שמירה", - "SettingsButtonClose": "סגירה", - "SettingsButtonOk": "אישור", - "SettingsButtonCancel": "ביטול", - "SettingsButtonApply": "החל", - "ControllerSettingsPlayer": "שחקן/ית", - "ControllerSettingsPlayer1": "שחקן/ית 1", - "ControllerSettingsPlayer2": "שחקן/ית 2", - "ControllerSettingsPlayer3": "שחקן/ית 3", - "ControllerSettingsPlayer4": "שחקן/ית 4", - "ControllerSettingsPlayer5": "שחקן/ית 5", - "ControllerSettingsPlayer6": "שחקן/ית 6", - "ControllerSettingsPlayer7": "שחקן/ית 7", - "ControllerSettingsPlayer8": "שחקן/ית 8", - "ControllerSettingsHandheld": "נייד", - "ControllerSettingsInputDevice": "מכשיר קלט", - "ControllerSettingsRefresh": "רענון", - "ControllerSettingsDeviceDisabled": "מושבת", - "ControllerSettingsControllerType": "סוג שלט", - "ControllerSettingsControllerTypeHandheld": "נייד", - "ControllerSettingsControllerTypeProController": "שלט פרו ", - "ControllerSettingsControllerTypeJoyConPair": "ג'ויקון הותאם", - "ControllerSettingsControllerTypeJoyConLeft": "ג'ויקון שמאלי ", - "ControllerSettingsControllerTypeJoyConRight": "ג'ויקון ימני", - "ControllerSettingsProfile": "פרופיל", - "ControllerSettingsProfileDefault": "ברירת המחדל", - "ControllerSettingsLoad": "טעינה", - "ControllerSettingsAdd": "הוספה", - "ControllerSettingsRemove": "הסר", - "ControllerSettingsButtons": "כפתורים", - "ControllerSettingsButtonA": "A", - "ControllerSettingsButtonB": "B", - "ControllerSettingsButtonX": "X", - "ControllerSettingsButtonY": "Y", - "ControllerSettingsButtonPlus": "+", - "ControllerSettingsButtonMinus": "-", - "ControllerSettingsDPad": "כפתורי כיוונים", - "ControllerSettingsDPadUp": "מעלה", - "ControllerSettingsDPadDown": "מטה", - "ControllerSettingsDPadLeft": "שמאלה", - "ControllerSettingsDPadRight": "ימינה", - "ControllerSettingsStickButton": "כפתור", - "ControllerSettingsStickUp": "למעלה", - "ControllerSettingsStickDown": "למטה", - "ControllerSettingsStickLeft": "שמאלה", - "ControllerSettingsStickRight": "ימינה", - "ControllerSettingsStickStick": "סטיק", - "ControllerSettingsStickInvertXAxis": "הפיכת הX של הסטיק", - "ControllerSettingsStickInvertYAxis": "הפיכת הY של הסטיק", - "ControllerSettingsStickDeadzone": "שטח מת:", - "ControllerSettingsLStick": "מקל שמאלי", - "ControllerSettingsRStick": "מקל ימני", - "ControllerSettingsTriggersLeft": "הדק שמאלי", - "ControllerSettingsTriggersRight": "הדק ימני", - "ControllerSettingsTriggersButtonsLeft": "כפתור הדק שמאלי", - "ControllerSettingsTriggersButtonsRight": "כפתור הדק ימני", - "ControllerSettingsTriggers": "הדקים", - "ControllerSettingsTriggerL": "L", - "ControllerSettingsTriggerR": "R", - "ControllerSettingsTriggerZL": "ZL", - "ControllerSettingsTriggerZR": "ZR", - "ControllerSettingsLeftSL": "SL", - "ControllerSettingsLeftSR": "SR", - "ControllerSettingsRightSL": "SL", - "ControllerSettingsRightSR": "SR", - "ControllerSettingsExtraButtonsLeft": "כפתורים משמאל", - "ControllerSettingsExtraButtonsRight": "כפתורים מימין", - "ControllerSettingsMisc": "שונות", - "ControllerSettingsTriggerThreshold": "סף הדק:", - "ControllerSettingsMotion": "תנועה", - "ControllerSettingsMotionUseCemuhookCompatibleMotion": "השתמש בתנועת CemuHook תואמת ", - "ControllerSettingsMotionControllerSlot": "מיקום שלט", - "ControllerSettingsMotionMirrorInput": "קלט מראה", - "ControllerSettingsMotionRightJoyConSlot": "מיקום ג'ויקון ימני", - "ControllerSettingsMotionServerHost": "מארח השרת:", - "ControllerSettingsMotionGyroSensitivity": "רגישות ג'ירוסקופ:", - "ControllerSettingsMotionGyroDeadzone": "שטח מת של הג'ירוסקופ:", - "ControllerSettingsSave": "שמירה", - "ControllerSettingsClose": "סגירה", - "KeyUnknown": "Unknown", - "KeyShiftLeft": "Shift Left", - "KeyShiftRight": "Shift Right", - "KeyControlLeft": "Ctrl Left", - "KeyMacControlLeft": "⌃ Left", - "KeyControlRight": "Ctrl Right", - "KeyMacControlRight": "⌃ Right", - "KeyAltLeft": "Alt Left", - "KeyMacAltLeft": "⌥ Left", - "KeyAltRight": "Alt Right", - "KeyMacAltRight": "⌥ Right", - "KeyWinLeft": "⊞ Left", - "KeyMacWinLeft": "⌘ Left", - "KeyWinRight": "⊞ Right", - "KeyMacWinRight": "⌘ Right", - "KeyMenu": "Menu", - "KeyUp": "Up", - "KeyDown": "Down", - "KeyLeft": "Left", - "KeyRight": "Right", - "KeyEnter": "Enter", - "KeyEscape": "Escape", - "KeySpace": "Space", - "KeyTab": "Tab", - "KeyBackSpace": "Backspace", - "KeyInsert": "Insert", - "KeyDelete": "Delete", - "KeyPageUp": "Page Up", - "KeyPageDown": "Page Down", - "KeyHome": "Home", - "KeyEnd": "End", - "KeyCapsLock": "Caps Lock", - "KeyScrollLock": "Scroll Lock", - "KeyPrintScreen": "Print Screen", - "KeyPause": "Pause", - "KeyNumLock": "Num Lock", - "KeyClear": "Clear", - "KeyKeypad0": "Keypad 0", - "KeyKeypad1": "Keypad 1", - "KeyKeypad2": "Keypad 2", - "KeyKeypad3": "Keypad 3", - "KeyKeypad4": "Keypad 4", - "KeyKeypad5": "Keypad 5", - "KeyKeypad6": "Keypad 6", - "KeyKeypad7": "Keypad 7", - "KeyKeypad8": "Keypad 8", - "KeyKeypad9": "Keypad 9", - "KeyKeypadDivide": "Keypad Divide", - "KeyKeypadMultiply": "Keypad Multiply", - "KeyKeypadSubtract": "Keypad Subtract", - "KeyKeypadAdd": "Keypad Add", - "KeyKeypadDecimal": "Keypad Decimal", - "KeyKeypadEnter": "Keypad Enter", - "KeyNumber0": "0", - "KeyNumber1": "1", - "KeyNumber2": "2", - "KeyNumber3": "3", - "KeyNumber4": "4", - "KeyNumber5": "5", - "KeyNumber6": "6", - "KeyNumber7": "7", - "KeyNumber8": "8", - "KeyNumber9": "9", - "KeyTilde": "~", - "KeyGrave": "`", - "KeyMinus": "-", - "KeyPlus": "+", - "KeyBracketLeft": "[", - "KeyBracketRight": "]", - "KeySemicolon": ";", - "KeyQuote": "\"", - "KeyComma": ",", - "KeyPeriod": ".", - "KeySlash": "/", - "KeyBackSlash": "\\", - "KeyUnbound": "Unbound", - "GamepadLeftStick": "L Stick Button", - "GamepadRightStick": "R Stick Button", - "GamepadLeftShoulder": "Left Shoulder", - "GamepadRightShoulder": "Right Shoulder", - "GamepadLeftTrigger": "Left Trigger", - "GamepadRightTrigger": "Right Trigger", - "GamepadDpadUp": "Up", - "GamepadDpadDown": "Down", - "GamepadDpadLeft": "Left", - "GamepadDpadRight": "Right", - "GamepadMinus": "-", - "GamepadPlus": "+", - "GamepadGuide": "Guide", - "GamepadMisc1": "Misc", - "GamepadPaddle1": "Paddle 1", - "GamepadPaddle2": "Paddle 2", - "GamepadPaddle3": "Paddle 3", - "GamepadPaddle4": "Paddle 4", - "GamepadTouchpad": "Touchpad", - "GamepadSingleLeftTrigger0": "Left Trigger 0", - "GamepadSingleRightTrigger0": "Right Trigger 0", - "GamepadSingleLeftTrigger1": "Left Trigger 1", - "GamepadSingleRightTrigger1": "Right Trigger 1", - "StickLeft": "Left Stick", - "StickRight": "Right Stick", - "UserProfilesSelectedUserProfile": "פרופיל המשתמש הנבחר:", - "UserProfilesSaveProfileName": "שמור שם פרופיל", - "UserProfilesChangeProfileImage": "שנה תמונת פרופיל", - "UserProfilesAvailableUserProfiles": "פרופילי משתמש זמינים:", - "UserProfilesAddNewProfile": "צור פרופיל", - "UserProfilesDelete": "מחיקה", - "UserProfilesClose": "סגור", - "ProfileNameSelectionWatermark": "בחרו כינוי", - "ProfileImageSelectionTitle": "בחירת תמונת פרופיל", - "ProfileImageSelectionHeader": "בחרו תמונת פרופיל", - "ProfileImageSelectionNote": "אתם יכולים לייבא תמונת פרופיל מותאמת אישית, או לבחור אווטאר מקושחת המערכת", - "ProfileImageSelectionImportImage": "ייבוא קובץ תמונה", - "ProfileImageSelectionSelectAvatar": "בחרו אוואטר קושחה", - "InputDialogTitle": "דיאלוג קלט", - "InputDialogOk": "בסדר", - "InputDialogCancel": "ביטול", - "InputDialogAddNewProfileTitle": "בחרו את שם הפרופיל", - "InputDialogAddNewProfileHeader": "אנא הזינו שם לפרופיל", - "InputDialogAddNewProfileSubtext": "(אורך מרבי: {0})", - "AvatarChoose": "בחרו דמות", - "AvatarSetBackgroundColor": "הגדר צבע רקע", - "AvatarClose": "סגור", - "ControllerSettingsLoadProfileToolTip": "טען פרופיל", - "ControllerSettingsAddProfileToolTip": "הוסף פרופיל", - "ControllerSettingsRemoveProfileToolTip": "הסר פרופיל", - "ControllerSettingsSaveProfileToolTip": "שמור פרופיל", - "MenuBarFileToolsTakeScreenshot": "צלם מסך", - "MenuBarFileToolsHideUi": "הסתר ממשק משתמש ", - "GameListContextMenuRunApplication": "הרץ יישום", - "GameListContextMenuToggleFavorite": "למתג העדפה", - "GameListContextMenuToggleFavoriteToolTip": "למתג סטטוס העדפה של משחק", - "SettingsTabGeneralTheme": "ערכת נושא:", - "SettingsTabGeneralThemeDark": "כהה", - "SettingsTabGeneralThemeLight": "בהיר", - "ControllerSettingsConfigureGeneral": "הגדר", - "ControllerSettingsRumble": "רטט", - "ControllerSettingsRumbleStrongMultiplier": "העצמת רטט חזק", - "ControllerSettingsRumbleWeakMultiplier": "מכפיל רטט חלש", - "DialogMessageSaveNotAvailableMessage": "אין שמור משחק עבור [{1:x16}] {0}", - "DialogMessageSaveNotAvailableCreateSaveMessage": "האם תרצה ליצור שמור משחק עבור המשחק הזה?", - "DialogConfirmationTitle": "ריוג'ינקס - אישור", - "DialogUpdaterTitle": "ריוג'ינקס - מעדכן", - "DialogErrorTitle": "ריוג'ינקס - שגיאה", - "DialogWarningTitle": "ריוג'ינקס - אזהרה", - "DialogExitTitle": "ריוג'ינקס - יציאה", - "DialogErrorMessage": "ריוג'ינקס נתקל בשגיאה", - "DialogExitMessage": "האם אתם בטוחים שאתם רוצים לסגור את ריוג'ינקס?", - "DialogExitSubMessage": "כל הנתונים שלא נשמרו יאבדו!", - "DialogMessageCreateSaveErrorMessage": "אירעה שגיאה ביצירת שמור המשחק שצויין: {0}", - "DialogMessageFindSaveErrorMessage": "אירעה שגיאה במציאת שמור המשחק שצויין: {0}", - "FolderDialogExtractTitle": "בחרו את התיקייה לחילוץ", - "DialogNcaExtractionMessage": "מלחץ {0} ממקטע {1}...", - "DialogNcaExtractionTitle": "ריוג'ינקס - מחלץ מקטע NCA", - "DialogNcaExtractionMainNcaNotFoundErrorMessage": "כשל בחילוץ. ה-NCA הראשי לא היה קיים בקובץ שנבחר.", - "DialogNcaExtractionCheckLogErrorMessage": "כשל בחילוץ. קרא את קובץ הרישום למידע נוסף.", - "DialogNcaExtractionSuccessMessage": "החילוץ הושלם בהצלחה.", - "DialogUpdaterConvertFailedMessage": "המרת הגרסה הנוכחית של ריוג'ינקס נכשלה.", - "DialogUpdaterCancelUpdateMessage": "מבטל עדכון!", - "DialogUpdaterAlreadyOnLatestVersionMessage": "אתם כבר משתמשים בגרסה המעודכנת ביותר של ריוג'ינקס!", - "DialogUpdaterFailedToGetVersionMessage": "אירעה שגיאה בעת ניסיון לקבל עדכונים מ-גיטהב. זה יכול להיגרם אם הגרסה המעודכנת האחרונה נוצרה על ידי פעולות של גיטהב. נסה שוב בעוד מספר דקות.", - "DialogUpdaterConvertFailedGithubMessage": "המרת גרסת ריוג'ינקס שהתקבלה מ-עדכון הגרסאות של גיטהב נכשלה.", - "DialogUpdaterDownloadingMessage": "מוריד עדכון...", - "DialogUpdaterExtractionMessage": "מחלץ עדכון...", - "DialogUpdaterRenamingMessage": "משנה את שם העדכון...", - "DialogUpdaterAddingFilesMessage": "מוסיף עדכון חדש...", - "DialogUpdaterCompleteMessage": "העדכון הושלם!", - "DialogUpdaterRestartMessage": "האם אתם רוצים להפעיל מחדש את ריוג'ינקס עכשיו?", - "DialogUpdaterNoInternetMessage": "אתם לא מחוברים לאינטרנט!", - "DialogUpdaterNoInternetSubMessage": "אנא ודא שיש לך חיבור אינטרנט תקין!", - "DialogUpdaterDirtyBuildMessage": "אתם לא יכולים לעדכן מבנה מלוכלך של ריוג'ינקס!", - "DialogUpdaterDirtyBuildSubMessage": "אם אתם מחפשים גרסא נתמכת, אנא הורידו את ריוג'ינקס בכתובת https://ryujinx.org", - "DialogRestartRequiredMessage": "אתחול נדרש", - "DialogThemeRestartMessage": "ערכת הנושא נשמרה. יש צורך בהפעלה מחדש כדי להחיל את ערכת הנושא.", - "DialogThemeRestartSubMessage": "האם ברצונך להפעיל מחדש?", - "DialogFirmwareInstallEmbeddedMessage": "האם תרצו להתקין את הקושחה המוטמעת במשחק הזה? (קושחה {0})", - "DialogFirmwareInstallEmbeddedSuccessMessage": "לא נמצאה קושחה מותקנת אבל ריוג'ינקס הצליח להתקין קושחה {0} מהמשחק שסופק. \nהאמולטור יופעל כעת.", - "DialogFirmwareNoFirmwareInstalledMessage": "לא מותקנת קושחה", - "DialogFirmwareInstalledMessage": "הקושחה {0} הותקנה", - "DialogInstallFileTypesSuccessMessage": "סוגי קבצים הותקנו בהצלחה!", - "DialogInstallFileTypesErrorMessage": "נכשל בהתקנת סוגי קבצים.", - "DialogUninstallFileTypesSuccessMessage": "סוגי קבצים הוסרו בהצלחה!", - "DialogUninstallFileTypesErrorMessage": "נכשל בהסרת סוגי קבצים.", - "DialogOpenSettingsWindowLabel": "פתח את חלון ההגדרות", - "DialogControllerAppletTitle": "יישומון בקר", - "DialogMessageDialogErrorExceptionMessage": "שגיאה בהצגת דיאלוג ההודעה: {0}", - "DialogSoftwareKeyboardErrorExceptionMessage": "שגיאה בהצגת תוכנת המקלדת: {0}", - "DialogErrorAppletErrorExceptionMessage": "שגיאה בהצגת דיאלוג ErrorApplet: {0}", - "DialogUserErrorDialogMessage": "{0}: {1}", - "DialogUserErrorDialogInfoMessage": "\nלמידע נוסף על איך לתקן שגיאה זו, עקוב אחר מדריך ההתקנה שלנו.", - "DialogUserErrorDialogTitle": "שגיאת Ryujinx ({0})", - "DialogAmiiboApiTitle": "ממשק תכנות אמיבו", - "DialogAmiiboApiFailFetchMessage": "אירעה שגיאה בעת שליפת מידע מהממשק.", - "DialogAmiiboApiConnectErrorMessage": "לא ניתן להתחבר לממשק שרת האמיבו. ייתכן שהשירות מושבת או שתצטרך לוודא שהחיבור לאינטרנט שלך מקוון.", - "DialogProfileInvalidProfileErrorMessage": "הפרופיל {0} אינו תואם למערכת תצורת הקלט הנוכחית.", - "DialogProfileDefaultProfileOverwriteErrorMessage": "לא ניתן להחליף את פרופיל ברירת המחדל", - "DialogProfileDeleteProfileTitle": "מוחק פרופיל", - "DialogProfileDeleteProfileMessage": "פעולה זו היא בלתי הפיכה, האם אתם בטוחים שברצונכם להמשיך?", - "DialogWarning": "אזהרה", - "DialogPPTCDeletionMessage": "אם תמשיכו אתם עומדים לגרום לבנייה מחדש של מטמון ה-PPTC עבור:\n\n{0}", - "DialogPPTCDeletionErrorMessage": "שגיאה בטיהור מטמון PPTC ב-{0}: {1}", - "DialogShaderDeletionMessage": "אם תמשיכו אתם עומדים למחוק את מטמון ההצללות עבור:\n\n{0}", - "DialogShaderDeletionErrorMessage": "שגיאה בניקוי מטמון ההצללות ב-{0}: {1}", - "DialogRyujinxErrorMessage": "ריוג'ינקס נתקלה בשגיאה", - "DialogInvalidTitleIdErrorMessage": "שגיאת ממשק משתמש: למשחק שנבחר לא קיים מזהה משחק", - "DialogFirmwareInstallerFirmwareNotFoundErrorMessage": "לא נמצאה קושחת מערכת תקפה ב-{0}.", - "DialogFirmwareInstallerFirmwareInstallTitle": "התקן קושחה {0}", - "DialogFirmwareInstallerFirmwareInstallMessage": "גירסת המערכת {0} תותקן.", - "DialogFirmwareInstallerFirmwareInstallSubMessage": "\n\nזה יחליף את גרסת המערכת הנוכחית {0}.", - "DialogFirmwareInstallerFirmwareInstallConfirmMessage": "\n\nהאם ברצונך להמשיך?", - "DialogFirmwareInstallerFirmwareInstallWaitMessage": "מתקין קושחה...", - "DialogFirmwareInstallerFirmwareInstallSuccessMessage": "גרסת המערכת {0} הותקנה בהצלחה.", - "DialogUserProfileDeletionWarningMessage": "לא יהיו פרופילים אחרים שייפתחו אם הפרופיל שנבחר יימחק", - "DialogUserProfileDeletionConfirmMessage": "האם ברצונך למחוק את הפרופיל שנבחר", - "DialogUserProfileUnsavedChangesTitle": "אזהרה - שינויים לא שמורים", - "DialogUserProfileUnsavedChangesMessage": "ביצעת שינויים בפרופיל משתמש זה שלא נשמרו.", - "DialogUserProfileUnsavedChangesSubMessage": "האם ברצונך למחוק את השינויים האחרונים?", - "DialogControllerSettingsModifiedConfirmMessage": "הגדרות השלט הנוכחי עודכנו.", - "DialogControllerSettingsModifiedConfirmSubMessage": "האם ברצונך לשמור?", - "DialogLoadFileErrorMessage": "{0}. קובץ שגוי: {1}", - "DialogModAlreadyExistsMessage": "מוד כבר קיים", - "DialogModInvalidMessage": "התיקייה שצוינה אינה מכילה מוד", - "DialogModDeleteNoParentMessage": "נכשל למחוק: לא היה ניתן למצוא את תיקיית האב למוד \"{0}\"!\n", - "DialogDlcNoDlcErrorMessage": "הקובץ שצוין אינו מכיל DLC עבור המשחק שנבחר!", - "DialogPerformanceCheckLoggingEnabledMessage": "הפעלת רישום מעקב, אשר נועד לשמש מפתחים בלבד.", - "DialogPerformanceCheckLoggingEnabledConfirmMessage": "לביצועים מיטביים, מומלץ להשבית את רישום המעקב. האם ברצונך להשבית את רישום המעקב כעת?", - "DialogPerformanceCheckShaderDumpEnabledMessage": "הפעלת השלכת הצללה, שנועדה לשמש מפתחים בלבד.", - "DialogPerformanceCheckShaderDumpEnabledConfirmMessage": "לביצועים מיטביים, מומלץ להשבית את השלכת הצללה. האם ברצונך להשבית את השלכת הצללה עכשיו?", - "DialogLoadAppGameAlreadyLoadedMessage": "ישנו משחק שכבר רץ כעת", - "DialogLoadAppGameAlreadyLoadedSubMessage": "אנא הפסק את האמולציה או סגור את האמולטור לפני הפעלת משחק אחר.", - "DialogUpdateAddUpdateErrorMessage": "הקובץ שצוין אינו מכיל עדכון עבור המשחק שנבחר!", - "DialogSettingsBackendThreadingWarningTitle": "אזהרה - ריבוי תהליכי רקע", - "DialogSettingsBackendThreadingWarningMessage": "יש להפעיל מחדש את ריוג'ינקס לאחר שינוי אפשרות זו כדי שהיא תחול במלואה. בהתאם לפלטפורמה שלך, ייתכן שיהיה עליך להשבית ידנית את ריבוי ההליכים של ההתקן שלך בעת השימוש ב-ריוג'ינקס.", - "DialogModManagerDeletionWarningMessage": "אתה עומד למחוק את המוד: {0}\nהאם אתה בטוח שאתה רוצה להמשיך?", - "DialogModManagerDeletionAllWarningMessage": "אתה עומד למחוק את כל המודים בשביל משחק זה.\n\nהאם אתה בטוח שאתה רוצה להמשיך?", - "SettingsTabGraphicsFeaturesOptions": "אפשרויות", - "SettingsTabGraphicsBackendMultithreading": "אחראי גרפיקה רב-תהליכי:", - "CommonAuto": "אוטומטי", - "CommonOff": "כבוי", - "CommonOn": "דלוק", - "InputDialogYes": "כן", - "InputDialogNo": "לא", - "DialogProfileInvalidProfileNameErrorMessage": "שם הקובץ מכיל תווים לא חוקיים. אנא נסה שוב.", - "MenuBarOptionsPauseEmulation": "הפסק", - "MenuBarOptionsResumeEmulation": "המשך", - "AboutUrlTooltipMessage": "לחץ כדי לפתוח את אתר ריוג'ינקס בדפדפן ברירת המחדל שלך.", - "AboutDisclaimerMessage": "ריוג'ינקס אינה מזוהת עם נינטנדו,\nאו שוטפייה בכל דרך שהיא.", - "AboutAmiiboDisclaimerMessage": "ממשק אמיבו (www.amiiboapi.com) משומש בהדמיית האמיבו שלנו.", - "AboutPatreonUrlTooltipMessage": "לחץ כדי לפתוח את דף הפטראון של ריוג'ינקס בדפדפן ברירת המחדל שלך.", - "AboutGithubUrlTooltipMessage": "לחץ כדי לפתוח את דף הגיטהב של ריוג'ינקס בדפדפן ברירת המחדל שלך.", - "AboutDiscordUrlTooltipMessage": "לחץ כדי לפתוח הזמנה לשרת הדיסקורד של ריוג'ינקס בדפדפן ברירת המחדל שלך.", - "AboutTwitterUrlTooltipMessage": "לחץ כדי לפתוח את דף הטוויטר של Ryujinx בדפדפן ברירת המחדל שלך.", - "AboutRyujinxAboutTitle": "אודות:", - "AboutRyujinxAboutContent": "ריוג'ינקס הוא אמולטור עבור הנינטנדו סוויץ' (כל הזכויות שמורות).\nבבקשה תתמכו בנו בפטראון.\nקבל את כל החדשות האחרונות בטוויטר או בדיסקורד שלנו.\nמפתחים המעוניינים לתרום יכולים לקבל מידע נוסף ב-גיטהאב או ב-דיסקורד שלנו.", - "AboutRyujinxMaintainersTitle": "מתוחזק על ידי:", - "AboutRyujinxMaintainersContentTooltipMessage": "לחץ כדי לפתוח את דף התורמים בדפדפן ברירת המחדל שלך.", - "AboutRyujinxSupprtersTitle": "תמוך באמצעות Patreon", - "AmiiboSeriesLabel": "סדרת אמיבו", - "AmiiboCharacterLabel": "דמות", - "AmiiboScanButtonLabel": "סרוק את זה", - "AmiiboOptionsShowAllLabel": "הצג את כל האמיבואים", - "AmiiboOptionsUsRandomTagLabel": "האצה: השתמש בתג Uuid אקראי", - "DlcManagerTableHeadingEnabledLabel": "מאופשר", - "DlcManagerTableHeadingTitleIdLabel": "מזהה משחק", - "DlcManagerTableHeadingContainerPathLabel": "נתיב מכיל", - "DlcManagerTableHeadingFullPathLabel": "נתיב מלא", - "DlcManagerRemoveAllButton": "מחק הכל", - "DlcManagerEnableAllButton": "אפשר הכל", - "DlcManagerDisableAllButton": "השבת הכל", - "ModManagerDeleteAllButton": "מחק הכל", - "MenuBarOptionsChangeLanguage": "החלף שפה", - "MenuBarShowFileTypes": "הצג מזהה סוג קובץ", - "CommonSort": "מיין", - "CommonShowNames": "הצג שמות", - "CommonFavorite": "מועדף", - "OrderAscending": "סדר עולה", - "OrderDescending": "סדר יורד", - "SettingsTabGraphicsFeatures": "תכונות ושיפורים", - "ErrorWindowTitle": "חלון שגיאה", - "ToggleDiscordTooltip": "בחרו להציג את ריוג'ינקס או לא בפעילות הדיסקורד שלכם \"משוחק כרגע\".", - "AddGameDirBoxTooltip": "הזן תקיית משחקים כדי להוסיף לרשימה", - "AddGameDirTooltip": "הוסף תקיית משחקים לרשימה", - "RemoveGameDirTooltip": "הסר את תקיית המשחקים שנבחרה", - "CustomThemeCheckTooltip": "השתמש בעיצוב מותאם אישית של אבלוניה עבור ה-ממשק הגראפי כדי לשנות את המראה של תפריטי האמולטור", - "CustomThemePathTooltip": "נתיב לערכת נושא לממשק גראפי מותאם אישית", - "CustomThemeBrowseTooltip": "חפש עיצוב ממשק גראפי מותאם אישית", - "DockModeToggleTooltip": "מצב עגינה גורם למערכת המדומה להתנהג כ-נינטנדו סוויץ' בתחנת עגינתו. זה משפר את הנאמנות הגרפית ברוב המשחקים.\n לעומת זאת, השבתה של תכונה זו תגרום למערכת המדומה להתנהג כ- נינטנדו סוויץ' נייד, ולהפחית את איכות הגרפיקה.\n\nהגדירו את שלט שחקן 1 אם אתם מתכננים להשתמש במצב עגינה; הגדירו את פקדי כף היד אם אתם מתכננים להשתמש במצב נייד.\n\nמוטב להשאיר דלוק אם אתם לא בטוחים.", - "DirectKeyboardTooltip": "Direct keyboard access (HID) support. Provides games access to your keyboard as a text entry device.\n\nOnly works with games that natively support keyboard usage on Switch hardware.\n\nLeave OFF if unsure.", - "DirectMouseTooltip": "Direct mouse access (HID) support. Provides games access to your mouse as a pointing device.\n\nOnly works with games that natively support mouse controls on Switch hardware, which are few and far between.\n\nWhen enabled, touch screen functionality may not work.\n\nLeave OFF if unsure.", - "RegionTooltip": "שנה אזור מערכת", - "LanguageTooltip": "שנה שפת מערכת", - "TimezoneTooltip": "שנה את אזור הזמן של המערכת", - "TimeTooltip": "שנה זמן מערכת", - "VSyncToggleTooltip": "Emulated console's Vertical Sync. Essentially a frame-limiter for the majority of games; disabling it may cause games to run at higher speed or make loading screens take longer or get stuck.\n\nCan be toggled in-game with a hotkey of your preference (F1 by default). We recommend doing this if you plan on disabling it.\n\nLeave ON if unsure.", - "PptcToggleTooltip": "שומר את פונקציות ה-JIT המתורגמות כך שלא יצטרכו לעבור תרגום שוב כאשר משחק עולה.\n\nמפחית תקיעות ומשפר מהירות עלייה של המערכת אחרי הפתיחה הראשונה של המשחק.\n\nמוטב להשאיר דלוק אם לא בטוחים.", - "FsIntegrityToggleTooltip": "בודק לקבצים שגויים כאשר משחק עולה, ואם מתגלים כאלו, מציג את מזהה השגיאה שלהם לקובץ הלוג.\n\nאין לכך השפעה על הביצועים ונועד לעזור לבדיקה וניפוי שגיאות של האמולטור.\n\nמוטב להשאיר דלוק אם לא בטוחים.", - "AudioBackendTooltip": "משנה את אחראי השמע.\n\nSDL2 הוא הנבחר, למראת שOpenAL וגם SoundIO משומשים כאפשרויות חלופיות. אפשרות הDummy לא תשמיע קול כלל.\n\nמוטב להשאיר על SDL2 אם לא בטוחים.", - "MemoryManagerTooltip": "שנה איך שזיכרון מארח מיוחד ומונגד. משפיע מאוד על ביצועי המעבד המדומה.\n\nמוטב להשאיר על מארח לא מבוקר אם לא בטוחים.", - "MemoryManagerSoftwareTooltip": "השתמש בתוכנת ה-page table בכדי להתייחס לתרגומים. דיוק מרבי לקונסולה אך המימוש הכי איטי.", - "MemoryManagerHostTooltip": "ממפה זיכרון ישירות לכתובת המארח. מהיר בהרבה ביכולות קימפול ה-JIT והריצה.", - "MemoryManagerUnsafeTooltip": "ממפה זיכרון ישירות, אך לא ממסך את הכתובת בתוך כתובת המארח לפני הגישה. מהיר, אך במחיר של הגנה. יישום המארח בעל גישה לזיכרון מכל מקום בריוג'ינקס, לכן הריצו איתו רק קבצים שאתם סומכים עליהם.", - "UseHypervisorTooltip": "השתמש ב- Hypervisor במקום JIT. משפר מאוד ביצועים כשניתן, אבל יכול להיות לא יציב במצבו הנוכחי.", - "DRamTooltip": "מנצל תצורת מצב-זיכרון חלופית לחכות את מכשיר הפיתוח של הסוויץ'.\n\nזה שימושי להחלפת חבילות מרקמים באיכותיים יותר או כאלו ברזולוציית 4k. לא משפר ביצועים.\n\nמוטב להשאיר כבוי אם לא בטוחים.", - "IgnoreMissingServicesTooltip": "מתעלם מפעולות שלא קיבלו מימוש במערכת ההפעלה Horizon OS. זה עלול לעזור לעקוף קריסות של היישום במשחקים מסויימים.\n\nמוטב להשאיר כבוי אם לא בטוחים.", - "GraphicsBackendThreadingTooltip": "מריץ פקודות גראפיקה בתהליך שני נפרד.\n\nמאיץ עיבוד הצללות, מפחית תקיעות ומשפר ביצועים של דרייבר כרטיסי מסך אשר לא תומכים בהרצה רב-תהליכית.\n\nמוטב להשאיר על אוטומטי אם לא בטוחים.", - "GalThreadingTooltip": "מריץ פקודות גראפיקה בתהליך שני נפרד.\n\nמאיץ עיבוד הצללות, מפחית תקיעות ומשפר ביצועים של דרייבר כרטיסי מסך אשר לא תומכים בהרצה רב-תהליכית.\n\nמוטב להשאיר על אוטומטי אם לא בטוחים.", - "ShaderCacheToggleTooltip": "שומר זכרון מטמון של הצללות, דבר שמפחית תקיעות בריצות מסוימות.\n\nמוטב להשאיר דלוק אם לא בטוחים.", - "ResolutionScaleTooltip": "Multiplies the game's rendering resolution.\n\nA few games may not work with this and look pixelated even when the resolution is increased; for those games, you may need to find mods that remove anti-aliasing or that increase their internal rendering resolution. For using the latter, you'll likely want to select Native.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nKeep in mind 4x is overkill for virtually any setup.", - "ResolutionScaleEntryTooltip": "שיפור רזולוציית נקודה צפה, כגון 1.5. הוא שיפור לא אינטגרלי הנוטה לגרום יותר בעיות או להקריס.", - "AnisotropyTooltip": "Level of Anisotropic Filtering. Set to Auto to use the value requested by the game.", - "AspectRatioTooltip": "Aspect Ratio applied to the renderer window.\n\nOnly change this if you're using an aspect ratio mod for your game, otherwise the graphics will be stretched.\n\nLeave on 16:9 if unsure.", - "ShaderDumpPathTooltip": "נתיב השלכת הצללות גראפיות", - "FileLogTooltip": "שומר את רישומי שורת הפקודות לזיכרון, לא משפיע על ביצועי היישום.", - "StubLogTooltip": "מדפיס רישומים כושלים לשורת הפקודות. לא משפיע על ביצועי היישום.", - "InfoLogTooltip": "מדפיק רישומי מידע לשורת הפקודות. לא משפיע על ביצועי היישום.", - "WarnLogTooltip": "מדפיק רישומי הערות לשורת הפקודות. לא משפיע על ביצועי היישום.", - "ErrorLogTooltip": "מדפיס רישומי שגיאות לשורת הפקודות. לא משפיע על ביצועי היישום.", - "TraceLogTooltip": "מדפיק רישומי זיכרון לשורת הפקודות. לא משפיע על ביצועי היישום.", - "GuestLogTooltip": "מדפיס רישומי אורח לשורת הפקודות. לא משפיע על ביצועי היישום.", - "FileAccessLogTooltip": "מדפיס גישות לקבצי רישום לשורת הפקודות.", - "FSAccessLogModeTooltip": "מאפשר גישה לרישומי FS ליציאת שורת הפקודות. האפשרויות הינן 0-3.", - "DeveloperOptionTooltip": "השתמש בזהירות", - "OpenGlLogLevel": "דורש הפעלת רמות רישום מתאימות", - "DebugLogTooltip": "מדפיס הודעות יומן ניפוי באגים בשורת הפקודות.", - "LoadApplicationFileTooltip": "פתח סייר קבצים כדי לבחור קובץ תואם סוויץ' לטעינה", - "LoadApplicationFolderTooltip": "פתח סייר קבצים כדי לבחור יישום תואם סוויץ', לא ארוז לטעינה.", - "OpenRyujinxFolderTooltip": "פתח את תיקיית מערכת הקבצים ריוג'ינקס", - "OpenRyujinxLogsTooltip": "פותח את התיקיה שאליה נכתבים רישומים", - "ExitTooltip": "צא מריוג'ינקס", - "OpenSettingsTooltip": "פתח את חלון ההגדרות", - "OpenProfileManagerTooltip": "פתח את חלון מנהל פרופילי המשתמש", - "StopEmulationTooltip": "הפסק את הדמייה של המשחק הנוכחי וחזור למסך בחירת המשחק", - "CheckUpdatesTooltip": "בדוק אם קיימים עדכונים לריוג'ינקס", - "OpenAboutTooltip": "פתח את חלון אודות היישום", - "GridSize": "גודל רשת", - "GridSizeTooltip": "שנה את גודל המוצרים על הרשת.", - "SettingsTabSystemSystemLanguageBrazilianPortuguese": "פורטוגלית ברזילאית", - "AboutRyujinxContributorsButtonHeader": "צפה בכל התורמים", - "SettingsTabSystemAudioVolume": "עוצמת קול: ", - "AudioVolumeTooltip": "שנה עוצמת קול", - "SettingsTabSystemEnableInternetAccess": "אפשר גישה לאינטרנט בתור אורח/חיבור לאן", - "EnableInternetAccessTooltip": "מאפשר ליישומים באמולצייה להתחבר לאינטרנט.\n\nמשחקים עם חיבור לאן יכולים להתחבר אחד לשני כשאופצייה זו מופעלת והמערכות מתחברות לאותה נקודת גישה. כמו כן זה כולל שורות פקודות אמיתיות גם.\n\nאופצייה זו לא מאפשרת חיבור לשרתי נינטנדו. כשהאופצייה דלוקה היא עלולה לגרום לקריסת היישום במשחקים מסויימים שמנסים להתחבר לאינטרנט.\n\nמוטב להשאיר כבוי אם לא בטוחים.", - "GameListContextMenuManageCheatToolTip": "נהל צ'יטים", - "GameListContextMenuManageCheat": "נהל צ'יטים", - "GameListContextMenuManageModToolTip": "נהל מודים", - "GameListContextMenuManageMod": "נהל מודים", - "ControllerSettingsStickRange": "טווח:", - "DialogStopEmulationTitle": "ריוג'ינקס - עצור אמולציה", - "DialogStopEmulationMessage": "האם אתם בטוחים שאתם רוצים לסגור את האמולצייה?", - "SettingsTabCpu": "מעבד", - "SettingsTabAudio": "שמע", - "SettingsTabNetwork": "רשת", - "SettingsTabNetworkConnection": "חיבור רשת", - "SettingsTabCpuCache": "מטמון מעבד", - "SettingsTabCpuMemory": "מצב מעבד", - "DialogUpdaterFlatpakNotSupportedMessage": "בבקשה עדכן את ריוג'ינקס דרך פלאטהב.", - "UpdaterDisabledWarningTitle": "מעדכן מושבת!", - "ControllerSettingsRotate90": "סובב 90° עם בכיוון השעון", - "IconSize": "גודל הסמל", - "IconSizeTooltip": "שנה את גודל הסמלים של משחקים", - "MenuBarOptionsShowConsole": "הצג שורת פקודות", - "ShaderCachePurgeError": "שגיאה בניקוי מטמון ההצללות ב-{0}: {1}", - "UserErrorNoKeys": "המפתחות לא נמצאו", - "UserErrorNoFirmware": "קושחה לא נמצאה", - "UserErrorFirmwareParsingFailed": "שגיאת ניתוח קושחה", - "UserErrorApplicationNotFound": "יישום לא נמצא", - "UserErrorUnknown": "שגיאה לא ידועה", - "UserErrorUndefined": "שגיאה לא מוגדרת", - "UserErrorNoKeysDescription": "ריוג'ינקס לא הצליח למצוא את קובץ ה-'prod.keys' שלך", - "UserErrorNoFirmwareDescription": "ריוג'ינקס לא הצליחה למצוא קושחה מותקנת", - "UserErrorFirmwareParsingFailedDescription": "ריוג'ינקס לא הצליחה לנתח את הקושחה שסופקה. זה נגרם בדרך כלל על ידי מפתחות לא עדכניים.", - "UserErrorApplicationNotFoundDescription": "ריוג'ינקס לא מצאה יישום תקין בנתיב הנתון", - "UserErrorUnknownDescription": "קרתה שגיאה לא ידועה!", - "UserErrorUndefinedDescription": "קרתה שגיאה לא מוגדרת! זה לא אמור לקרות, אנא צרו קשר עם מפתח!", - "OpenSetupGuideMessage": "פתח מדריך התקנה", - "NoUpdate": "אין עדכון", - "TitleUpdateVersionLabel": "גרסה {0}", - "RyujinxInfo": "ריוג'ינקס - מידע", - "RyujinxConfirm": "ריוג'ינקס - אישור", - "FileDialogAllTypes": "כל הסוגים", - "Never": "אף פעם", - "SwkbdMinCharacters": "לפחות {0} תווים", - "SwkbdMinRangeCharacters": "באורך {0}-{1} תווים", - "SoftwareKeyboard": "מקלדת וירטואלית", - "SoftwareKeyboardModeNumeric": "חייב להיות בין 0-9 או '.' בלבד", - "SoftwareKeyboardModeAlphabet": "מחויב להיות ללא אותיות CJK", - "SoftwareKeyboardModeASCII": "מחויב להיות טקסט אסקיי", - "ControllerAppletControllers": "בקרים נתמכים:", - "ControllerAppletPlayers": "שחקנים:", - "ControllerAppletDescription": "התצורה הנוכחית אינה תקינה. פתח הגדרות והגדר מחדש את הקלטים שלך.", - "ControllerAppletDocked": "מצב עגינה מוגדר. כדאי ששליטה ניידת תהיה מושבתת.", - "UpdaterRenaming": "משנה שמות של קבצים ישנים...", - "UpdaterRenameFailed": "המעדכן לא הצליח לשנות את שם הקובץ: {0}", - "UpdaterAddingFiles": "מוסיף קבצים חדשים...", - "UpdaterExtracting": "מחלץ עדכון...", - "UpdaterDownloading": "מוריד עדכון...", - "Game": "משחק", - "Docked": "בתחנת עגינה", - "Handheld": "נייד", - "ConnectionError": "שגיאת חיבור", - "AboutPageDeveloperListMore": "{0} ועוד...", - "ApiError": "שגיאת ממשק.", - "LoadingHeading": "טוען {0}", - "CompilingPPTC": "קימפול PTC", - "CompilingShaders": "קימפול הצללות", - "AllKeyboards": "כל המקלדות", - "OpenFileDialogTitle": "בחר קובץ נתמך לפתיחה", - "OpenFolderDialogTitle": "בחר תיקיה עם משחק לא ארוז", - "AllSupportedFormats": "כל הפורמטים הנתמכים", - "RyujinxUpdater": "מעדכן ריוג'ינקס", - "SettingsTabHotkeys": "מקשי קיצור במקלדת", - "SettingsTabHotkeysHotkeys": "מקשי קיצור במקלדת", - "SettingsTabHotkeysToggleVsyncHotkey": "שנה סינכרון אנכי:", - "SettingsTabHotkeysScreenshotHotkey": "צילום מסך:", - "SettingsTabHotkeysShowUiHotkey": "הצג ממשק משתמש:", - "SettingsTabHotkeysPauseHotkey": "הפסק:", - "SettingsTabHotkeysToggleMuteHotkey": "השתק:", - "ControllerMotionTitle": "הגדרות שליטת תנועות ג'ירוסקופ", - "ControllerRumbleTitle": "הגדרות רטט", - "SettingsSelectThemeFileDialogTitle": "בחרו קובץ ערכת נושא", - "SettingsXamlThemeFile": "קובץ ערכת נושא Xaml", - "AvatarWindowTitle": "ניהול חשבונות - אוואטר", - "Amiibo": "אמיבו", - "Unknown": "לא ידוע", - "Usage": "שימוש", - "Writable": "ניתן לכתיבה", - "SelectDlcDialogTitle": "בחרו קבצי הרחבות משחק", - "SelectUpdateDialogTitle": "בחרו קבצי עדכון", - "SelectModDialogTitle": "בחר תיקיית מודים", - "UserProfileWindowTitle": "ניהול פרופילי משתמש", - "CheatWindowTitle": "נהל צ'יטים למשחק", - "DlcWindowTitle": "נהל הרחבות משחק עבור {0} ({1})", - "ModWindowTitle": "Manage Mods for {0} ({1})", - "UpdateWindowTitle": "נהל עדכוני משחקים", - "CheatWindowHeading": "צ'יטים זמינים עבור {0} [{1}]", - "BuildId": "מזהה בניה:", - "DlcWindowHeading": "{0} הרחבות משחק", - "ModWindowHeading": "{0} מוד(ים)", - "UserProfilesEditProfile": "ערוך נבחר/ים", - "Cancel": "בטל", - "Save": "שמור", - "Discard": "השלך", - "Paused": "מושהה", - "UserProfilesSetProfileImage": "הגדר תמונת פרופיל", - "UserProfileEmptyNameError": "נדרש שם", - "UserProfileNoImageError": "נדרשת תמונת פרופיל", - "GameUpdateWindowHeading": "נהל עדכונים עבור {0} ({1})", - "SettingsTabHotkeysResScaleUpHotkey": "שפר רזולוציה:", - "SettingsTabHotkeysResScaleDownHotkey": "הפחת רזולוציה:", - "UserProfilesName": "שם:", - "UserProfilesUserId": "מזהה משתמש:", - "SettingsTabGraphicsBackend": "אחראי גראפיקה", - "SettingsTabGraphicsBackendTooltip": "Select the graphics backend that will be used in the emulator.\n\nVulkan is overall better for all modern graphics cards, as long as their drivers are up to date. Vulkan also features faster shader compilation (less stuttering) on all GPU vendors.\n\nOpenGL may achieve better results on old Nvidia GPUs, on old AMD GPUs on Linux, or on GPUs with lower VRAM, though shader compilation stutters will be greater.\n\nSet to Vulkan if unsure. Set to OpenGL if your GPU does not support Vulkan even with the latest graphics drivers.", - "SettingsEnableTextureRecompression": "אפשר דחיסה מחדש של המרקם", - "SettingsEnableTextureRecompressionTooltip": "Compresses ASTC textures in order to reduce VRAM usage.\n\nGames using this texture format include Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder and The Legend of Zelda: Tears of the Kingdom.\n\nGraphics cards with 4GiB VRAM or less will likely crash at some point while running these games.\n\nEnable only if you're running out of VRAM on the aforementioned games. Leave OFF if unsure.", - "SettingsTabGraphicsPreferredGpu": "כרטיס גראפי מועדף", - "SettingsTabGraphicsPreferredGpuTooltip": "בחר את הכרטיס הגראפי שישומש עם הגראפיקה של וולקאן.\n\nדבר זה לא משפיע על הכרטיס הגראפי שישומש עם OpenGL.\n\nמוטב לבחור את ה-GPU המסומן כ-\"dGPU\" אם אינכם בטוחים, אם זו לא אופצייה, אל תשנו דבר.", - "SettingsAppRequiredRestartMessage": "ריוג'ינקס דורש אתחול מחדש", - "SettingsGpuBackendRestartMessage": "הגדרות אחראי גרפיקה או כרטיס גראפי שונו. זה ידרוש הפעלה מחדש כדי להחיל שינויים", - "SettingsGpuBackendRestartSubMessage": "האם ברצונך להפעיל מחדש כעט?", - "RyujinxUpdaterMessage": "האם ברצונך לעדכן את ריוג'ינקס לגרסא האחרונה?", - "SettingsTabHotkeysVolumeUpHotkey": "הגבר את עוצמת הקול:", - "SettingsTabHotkeysVolumeDownHotkey": "הנמך את עוצמת הקול:", - "SettingsEnableMacroHLE": "Enable Macro HLE", - "SettingsEnableMacroHLETooltip": "אמולצייה ברמה גבוהה של כרטיס גראפי עם קוד מקרו.\n\nמשפר את ביצועי היישום אך עלול לגרום לגליצ'ים חזותיים במשחקים מסויימים.\n\nמוטב להשאיר דלוק אם אינך בטוח.", - "SettingsEnableColorSpacePassthrough": "שקיפות מרחב צבע", - "SettingsEnableColorSpacePassthroughTooltip": "מנחה את המנוע Vulkan להעביר שקיפות בצבעים מבלי לציין מרחב צבע. עבור משתמשים עם מסכים רחבים, הדבר עשוי לגרום לצבעים מרהיבים יותר, בחוסר דיוק בצבעים האמתיים.", - "VolumeShort": "שמע", - "UserProfilesManageSaves": "נהל שמורים", - "DeleteUserSave": "האם ברצונך למחוק את תקיית השמור למשחק זה?", - "IrreversibleActionNote": "הפעולה הזו בלתי הפיכה.", - "SaveManagerHeading": "נהל שמורי משחק עבור {0} ({1})", - "SaveManagerTitle": "מנהל שמירות", - "Name": "שם", - "Size": "גודל", - "Search": "חפש", - "UserProfilesRecoverLostAccounts": "שחזר חשבון שאבד", - "Recover": "שחזר", - "UserProfilesRecoverHeading": "שמורים נמצאו לחשבונות הבאים", - "UserProfilesRecoverEmptyList": "אין פרופילים לשחזור", - "GraphicsAATooltip": "Applies anti-aliasing to the game render.\n\nFXAA will blur most of the image, while SMAA will attempt to find jagged edges and smooth them out.\n\nNot recommended to use in conjunction with the FSR scaling filter.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on NONE if unsure.", - "GraphicsAALabel": "החלקת-עקומות:", - "GraphicsScalingFilterLabel": "מסנן מידת איכות:", - "GraphicsScalingFilterTooltip": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.", - "GraphicsScalingFilterBilinear": "Bilinear", - "GraphicsScalingFilterNearest": "Nearest", - "GraphicsScalingFilterFsr": "FSR", - "GraphicsScalingFilterLevelLabel": "רמה", - "GraphicsScalingFilterLevelTooltip": "Set FSR 1.0 sharpening level. Higher is sharper.", - "SmaaLow": "SMAA נמוך", - "SmaaMedium": "SMAA בינוני", - "SmaaHigh": "SMAA גבוהה", - "SmaaUltra": "SMAA אולטרה", - "UserEditorTitle": "ערוך משתמש", - "UserEditorTitleCreate": "צור משתמש", - "SettingsTabNetworkInterface": "ממשק רשת", - "NetworkInterfaceTooltip": "The network interface used for LAN/LDN features.\n\nIn conjunction with a VPN or XLink Kai and a game with LAN support, can be used to spoof a same-network connection over the Internet.\n\nLeave on DEFAULT if unsure.", - "NetworkInterfaceDefault": "ברירת המחדל", - "PackagingShaders": "אורז הצללות", - "AboutChangelogButton": "צפה במידע אודות שינויים בגיטהב", - "AboutChangelogButtonTooltipMessage": "לחץ כדי לפתוח את יומן השינויים עבור גרסה זו בדפדפן ברירת המחדל שלך.", - "SettingsTabNetworkMultiplayer": "רב משתתפים", - "MultiplayerMode": "מצב:", - "MultiplayerModeTooltip": "Change LDN multiplayer mode.\n\nLdnMitm will modify local wireless/local play functionality in games to function as if it were LAN, allowing for local, same-network connections with other Ryujinx instances and hacked Nintendo Switch consoles that have the ldn_mitm module installed.\n\nMultiplayer requires all players to be on the same game version (i.e. Super Smash Bros. Ultimate v13.0.1 can't connect to v13.0.0).\n\nLeave DISABLED if unsure.", - "MultiplayerModeDisabled": "Disabled", - "MultiplayerModeLdnMitm": "ldn_mitm" -} diff --git a/src/Ryujinx/Assets/Locales/it_IT.json b/src/Ryujinx/Assets/Locales/it_IT.json deleted file mode 100644 index 280ebd880..000000000 --- a/src/Ryujinx/Assets/Locales/it_IT.json +++ /dev/null @@ -1,780 +0,0 @@ -{ - "Language": "Italiano", - "MenuBarFileOpenApplet": "Apri applet", - "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Apri l'applet Mii Editor in modalità Standalone", - "SettingsTabInputDirectMouseAccess": "Accesso diretto al mouse", - "SettingsTabSystemMemoryManagerMode": "Modalità di gestione della memoria:", - "SettingsTabSystemMemoryManagerModeSoftware": "Software", - "SettingsTabSystemMemoryManagerModeHost": "Host (veloce)", - "SettingsTabSystemMemoryManagerModeHostUnchecked": "Host Unchecked (più veloce, non sicura)", - "SettingsTabSystemUseHypervisor": "Usa Hypervisor", - "MenuBarFile": "_File", - "MenuBarFileOpenFromFile": "_Carica applicazione da un file", - "MenuBarFileOpenUnpacked": "Carica _gioco estratto", - "MenuBarFileOpenEmuFolder": "Apri cartella di Ryujinx", - "MenuBarFileOpenLogsFolder": "Apri cartella dei log", - "MenuBarFileExit": "_Esci", - "MenuBarOptions": "_Opzioni", - "MenuBarOptionsToggleFullscreen": "Schermo intero", - "MenuBarOptionsStartGamesInFullscreen": "Avvia i giochi a schermo intero", - "MenuBarOptionsStopEmulation": "Ferma emulazione", - "MenuBarOptionsSettings": "_Impostazioni", - "MenuBarOptionsManageUserProfiles": "_Gestisci i profili utente", - "MenuBarActions": "_Azioni", - "MenuBarOptionsSimulateWakeUpMessage": "Simula messaggio Wake-up", - "MenuBarActionsScanAmiibo": "Scansiona un Amiibo", - "MenuBarTools": "_Strumenti", - "MenuBarToolsInstallFirmware": "Installa firmware", - "MenuBarFileToolsInstallFirmwareFromFile": "Installa un firmware da file XCI o ZIP", - "MenuBarFileToolsInstallFirmwareFromDirectory": "Installa un firmare da una cartella", - "MenuBarToolsManageFileTypes": "Gestisci i tipi di file", - "MenuBarToolsInstallFileTypes": "Installa i tipi di file", - "MenuBarToolsUninstallFileTypes": "Disinstalla i tipi di file", - "MenuBarView": "_View", - "MenuBarViewWindow": "Window Size", - "MenuBarViewWindow720": "720p", - "MenuBarViewWindow1080": "1080p", - "MenuBarHelp": "_Aiuto", - "MenuBarHelpCheckForUpdates": "Controlla aggiornamenti", - "MenuBarHelpAbout": "Informazioni", - "MenuSearch": "Cerca...", - "GameListHeaderFavorite": "Preferito", - "GameListHeaderIcon": "Icona", - "GameListHeaderApplication": "Nome", - "GameListHeaderDeveloper": "Sviluppatore", - "GameListHeaderVersion": "Versione", - "GameListHeaderTimePlayed": "Tempo di gioco", - "GameListHeaderLastPlayed": "Ultima partita", - "GameListHeaderFileExtension": "Estensione", - "GameListHeaderFileSize": "Dimensione file", - "GameListHeaderPath": "Percorso", - "GameListContextMenuOpenUserSaveDirectory": "Apri la cartella dei salvataggi dell'utente", - "GameListContextMenuOpenUserSaveDirectoryToolTip": "Apre la cartella che contiene i dati di salvataggio dell'utente", - "GameListContextMenuOpenDeviceSaveDirectory": "Apri la cartella dei salvataggi del dispositivo", - "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Apre la cartella che contiene i dati di salvataggio del dispositivo", - "GameListContextMenuOpenBcatSaveDirectory": "Apri la cartella del salvataggio BCAT", - "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Apre la cartella che contiene il salvataggio BCAT dell'applicazione", - "GameListContextMenuManageTitleUpdates": "Gestisci aggiornamenti del gioco", - "GameListContextMenuManageTitleUpdatesToolTip": "Apre la finestra di gestione aggiornamenti del gioco", - "GameListContextMenuManageDlc": "Gestisci DLC", - "GameListContextMenuManageDlcToolTip": "Apre la finestra di gestione dei DLC", - "GameListContextMenuCacheManagement": "Gestione della cache", - "GameListContextMenuCacheManagementPurgePptc": "Accoda rigenerazione della cache PPTC", - "GameListContextMenuCacheManagementPurgePptcToolTip": "Esegue la rigenerazione della cache PPTC al prossimo avvio del gioco", - "GameListContextMenuCacheManagementPurgeShaderCache": "Elimina la cache degli shader", - "GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "Elimina la cache degli shader dell'applicazione", - "GameListContextMenuCacheManagementOpenPptcDirectory": "Apri la cartella della cache PPTC", - "GameListContextMenuCacheManagementOpenPptcDirectoryToolTip": "Apre la cartella che contiene la cache PPTC dell'applicazione", - "GameListContextMenuCacheManagementOpenShaderCacheDirectory": "Apri la cartella della cache degli shader", - "GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip": "Apre la cartella che contiene la cache degli shader dell'applicazione", - "GameListContextMenuExtractData": "Estrai dati", - "GameListContextMenuExtractDataExeFS": "ExeFS", - "GameListContextMenuExtractDataExeFSToolTip": "Estrae la sezione ExeFS dall'attuale configurazione dell'applicazione (includendo aggiornamenti)", - "GameListContextMenuExtractDataRomFS": "RomFS", - "GameListContextMenuExtractDataRomFSToolTip": "Estrae la sezione RomFS dall'attuale configurazione dell'applicazione (includendo aggiornamenti)", - "GameListContextMenuExtractDataLogo": "Logo", - "GameListContextMenuExtractDataLogoToolTip": "Estrae la sezione Logo dall'attuale configurazione dell'applicazione (includendo aggiornamenti)", - "GameListContextMenuCreateShortcut": "Crea collegamento", - "GameListContextMenuCreateShortcutToolTip": "Crea un collegamento sul desktop che avvia l'applicazione selezionata", - "GameListContextMenuCreateShortcutToolTipMacOS": "Crea un collegamento nella cartella Applicazioni di macOS che avvia l'applicazione selezionata", - "GameListContextMenuOpenModsDirectory": "Apri la cartella delle mod", - "GameListContextMenuOpenModsDirectoryToolTip": "Apre la cartella che contiene le mod dell'applicazione", - "GameListContextMenuOpenSdModsDirectory": "Apri la cartella delle mod Atmosphere", - "GameListContextMenuOpenSdModsDirectoryToolTip": "Apre la cartella alternativa di Atmosphere sulla scheda SD che contiene le mod dell'applicazione. Utile per le mod create per funzionare sull'hardware reale.", - "StatusBarGamesLoaded": "{0}/{1} giochi caricati", - "StatusBarSystemVersion": "Versione di sistema: {0}", - "LinuxVmMaxMapCountDialogTitle": "Rilevato limite basso per le mappature di memoria", - "LinuxVmMaxMapCountDialogTextPrimary": "Vuoi aumentare il valore di vm.max_map_count a {0}?", - "LinuxVmMaxMapCountDialogTextSecondary": "Alcuni giochi potrebbero provare a creare più mappature di memoria di quanto sia attualmente consentito. Ryujinx si bloccherà non appena questo limite viene superato.", - "LinuxVmMaxMapCountDialogButtonUntilRestart": "Sì, fino al prossimo riavvio", - "LinuxVmMaxMapCountDialogButtonPersistent": "Sì, in modo permanente", - "LinuxVmMaxMapCountWarningTextPrimary": "La quantità massima di mappature di memoria è inferiore a quella consigliata.", - "LinuxVmMaxMapCountWarningTextSecondary": "Il valore corrente di vm.max_map_count ({0}) è inferiore a {1}. Alcuni giochi potrebbero provare a creare più mappature di memoria di quanto sia attualmente consentito. Ryujinx si bloccherà non appena questo limite viene superato.\n\nPotresti voler aumentare manualmente il limite o installare pkexec, il che permette a Ryujinx di assisterlo.", - "Settings": "Impostazioni", - "SettingsTabGeneral": "Interfaccia utente", - "SettingsTabGeneralGeneral": "Generali", - "SettingsTabGeneralEnableDiscordRichPresence": "Attiva Discord Rich Presence", - "SettingsTabGeneralCheckUpdatesOnLaunch": "Controlla aggiornamenti all'avvio", - "SettingsTabGeneralShowConfirmExitDialog": "Mostra dialogo \"Conferma Uscita\"", - "SettingsTabGeneralRememberWindowState": "Remember Window Size/Position", - "SettingsTabGeneralHideCursor": "Nascondi il cursore:", - "SettingsTabGeneralHideCursorNever": "Mai", - "SettingsTabGeneralHideCursorOnIdle": "Quando è inattivo", - "SettingsTabGeneralHideCursorAlways": "Sempre", - "SettingsTabGeneralGameDirectories": "Cartelle dei giochi", - "SettingsTabGeneralAdd": "Aggiungi", - "SettingsTabGeneralRemove": "Rimuovi", - "SettingsTabSystem": "Sistema", - "SettingsTabSystemCore": "Principale", - "SettingsTabSystemSystemRegion": "Regione del sistema:", - "SettingsTabSystemSystemRegionJapan": "Giappone", - "SettingsTabSystemSystemRegionUSA": "Stati Uniti d'America", - "SettingsTabSystemSystemRegionEurope": "Europa", - "SettingsTabSystemSystemRegionAustralia": "Australia", - "SettingsTabSystemSystemRegionChina": "Cina", - "SettingsTabSystemSystemRegionKorea": "Corea", - "SettingsTabSystemSystemRegionTaiwan": "Taiwan", - "SettingsTabSystemSystemLanguage": "Lingua del sistema:", - "SettingsTabSystemSystemLanguageJapanese": "Giapponese", - "SettingsTabSystemSystemLanguageAmericanEnglish": "Inglese americano", - "SettingsTabSystemSystemLanguageFrench": "Francese", - "SettingsTabSystemSystemLanguageGerman": "Tedesco", - "SettingsTabSystemSystemLanguageItalian": "Italiano", - "SettingsTabSystemSystemLanguageSpanish": "Spagnolo", - "SettingsTabSystemSystemLanguageChinese": "Cinese", - "SettingsTabSystemSystemLanguageKorean": "Coreano", - "SettingsTabSystemSystemLanguageDutch": "Olandese", - "SettingsTabSystemSystemLanguagePortuguese": "Portoghese", - "SettingsTabSystemSystemLanguageRussian": "Russo", - "SettingsTabSystemSystemLanguageTaiwanese": "Taiwanese", - "SettingsTabSystemSystemLanguageBritishEnglish": "Inglese britannico", - "SettingsTabSystemSystemLanguageCanadianFrench": "Francese canadese", - "SettingsTabSystemSystemLanguageLatinAmericanSpanish": "Spagnolo latino americano", - "SettingsTabSystemSystemLanguageSimplifiedChinese": "Cinese semplificato", - "SettingsTabSystemSystemLanguageTraditionalChinese": "Cinese tradizionale", - "SettingsTabSystemSystemTimeZone": "Fuso orario del sistema:", - "SettingsTabSystemSystemTime": "Data e ora del sistema:", - "SettingsTabSystemEnableVsync": "Attiva VSync", - "SettingsTabSystemEnablePptc": "Attiva PPTC (Profiled Persistent Translation Cache)", - "SettingsTabSystemEnableFsIntegrityChecks": "Attiva controlli d'integrità FS", - "SettingsTabSystemAudioBackend": "Backend audio:", - "SettingsTabSystemAudioBackendDummy": "Dummy", - "SettingsTabSystemAudioBackendOpenAL": "OpenAL", - "SettingsTabSystemAudioBackendSoundIO": "SoundIO", - "SettingsTabSystemAudioBackendSDL2": "SDL2", - "SettingsTabSystemHacks": "Espedienti", - "SettingsTabSystemHacksNote": "Possono causare instabilità", - "SettingsTabSystemExpandDramSize": "Usa layout di memoria alternativo (per sviluppatori)", - "SettingsTabSystemIgnoreMissingServices": "Ignora servizi mancanti", - "SettingsTabGraphics": "Grafica", - "SettingsTabGraphicsAPI": "API grafica", - "SettingsTabGraphicsEnableShaderCache": "Attiva la cache degli shader", - "SettingsTabGraphicsAnisotropicFiltering": "Filtro anisotropico:", - "SettingsTabGraphicsAnisotropicFilteringAuto": "Auto", - "SettingsTabGraphicsAnisotropicFiltering2x": "2x", - "SettingsTabGraphicsAnisotropicFiltering4x": "4x", - "SettingsTabGraphicsAnisotropicFiltering8x": "8x", - "SettingsTabGraphicsAnisotropicFiltering16x": "16x", - "SettingsTabGraphicsResolutionScale": "Scala della risoluzione:", - "SettingsTabGraphicsResolutionScaleCustom": "Personalizzata (Non raccomandata)", - "SettingsTabGraphicsResolutionScaleNative": "Nativa (720p/1080p)", - "SettingsTabGraphicsResolutionScale2x": "2x (1440p/2160p)", - "SettingsTabGraphicsResolutionScale3x": "3x (2160p/3240p)", - "SettingsTabGraphicsResolutionScale4x": "4x (2880p/4320p) (Non consigliato)", - "SettingsTabGraphicsAspectRatio": "Rapporto d'aspetto:", - "SettingsTabGraphicsAspectRatio4x3": "4:3", - "SettingsTabGraphicsAspectRatio16x9": "16:9", - "SettingsTabGraphicsAspectRatio16x10": "16:10", - "SettingsTabGraphicsAspectRatio21x9": "21:9", - "SettingsTabGraphicsAspectRatio32x9": "32:9", - "SettingsTabGraphicsAspectRatioStretch": "Adatta alla finestra", - "SettingsTabGraphicsDeveloperOptions": "Opzioni per sviluppatori", - "SettingsTabGraphicsShaderDumpPath": "Percorso di dump degli shader:", - "SettingsTabLogging": "Log", - "SettingsTabLoggingLogging": "Log", - "SettingsTabLoggingEnableLoggingToFile": "Salva i log su file", - "SettingsTabLoggingEnableStubLogs": "Attiva log di stub", - "SettingsTabLoggingEnableInfoLogs": "Attiva log di informazioni", - "SettingsTabLoggingEnableWarningLogs": "Attiva log di avviso", - "SettingsTabLoggingEnableErrorLogs": "Attiva log di errore", - "SettingsTabLoggingEnableTraceLogs": "Attiva log di trace", - "SettingsTabLoggingEnableGuestLogs": "Attiva log del guest", - "SettingsTabLoggingEnableFsAccessLogs": "Attiva log di accesso FS", - "SettingsTabLoggingFsGlobalAccessLogMode": "Modalità log di accesso globale FS:", - "SettingsTabLoggingDeveloperOptions": "Opzioni per sviluppatori", - "SettingsTabLoggingDeveloperOptionsNote": "ATTENZIONE: ridurrà le prestazioni", - "SettingsTabLoggingGraphicsBackendLogLevel": "Livello di log del backend grafico:", - "SettingsTabLoggingGraphicsBackendLogLevelNone": "Nessuno", - "SettingsTabLoggingGraphicsBackendLogLevelError": "Errore", - "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "Rallentamenti", - "SettingsTabLoggingGraphicsBackendLogLevelAll": "Tutto", - "SettingsTabLoggingEnableDebugLogs": "Attiva log di debug", - "SettingsTabInput": "Input", - "SettingsTabInputEnableDockedMode": "Attiva modalità TV", - "SettingsTabInputDirectKeyboardAccess": "Accesso diretto alla tastiera", - "SettingsButtonSave": "Salva", - "SettingsButtonClose": "Chiudi", - "SettingsButtonOk": "OK", - "SettingsButtonCancel": "Annulla", - "SettingsButtonApply": "Applica", - "ControllerSettingsPlayer": "Giocatore", - "ControllerSettingsPlayer1": "Giocatore 1", - "ControllerSettingsPlayer2": "Giocatore 2", - "ControllerSettingsPlayer3": "Giocatore 3", - "ControllerSettingsPlayer4": "Giocatore 4", - "ControllerSettingsPlayer5": "Giocatore 5", - "ControllerSettingsPlayer6": "Giocatore 6", - "ControllerSettingsPlayer7": "Giocatore 7", - "ControllerSettingsPlayer8": "Giocatore 8", - "ControllerSettingsHandheld": "Portatile", - "ControllerSettingsInputDevice": "Dispositivo di input", - "ControllerSettingsRefresh": "Ricarica", - "ControllerSettingsDeviceDisabled": "Disabilitato", - "ControllerSettingsControllerType": "Tipo di controller", - "ControllerSettingsControllerTypeHandheld": "Portatile", - "ControllerSettingsControllerTypeProController": "Pro Controller", - "ControllerSettingsControllerTypeJoyConPair": "Coppia di JoyCon", - "ControllerSettingsControllerTypeJoyConLeft": "JoyCon sinistro", - "ControllerSettingsControllerTypeJoyConRight": "JoyCon destro", - "ControllerSettingsProfile": "Profilo", - "ControllerSettingsProfileDefault": "Predefinito", - "ControllerSettingsLoad": "Carica", - "ControllerSettingsAdd": "Aggiungi", - "ControllerSettingsRemove": "Rimuovi", - "ControllerSettingsButtons": "Pulsanti", - "ControllerSettingsButtonA": "A", - "ControllerSettingsButtonB": "B", - "ControllerSettingsButtonX": "X", - "ControllerSettingsButtonY": "Y", - "ControllerSettingsButtonPlus": "+", - "ControllerSettingsButtonMinus": "-", - "ControllerSettingsDPad": "Croce direzionale", - "ControllerSettingsDPadUp": "Su", - "ControllerSettingsDPadDown": "Giù", - "ControllerSettingsDPadLeft": "Sinistra", - "ControllerSettingsDPadRight": "Destra", - "ControllerSettingsStickButton": "Pulsante", - "ControllerSettingsStickUp": "Su", - "ControllerSettingsStickDown": "Giù", - "ControllerSettingsStickLeft": "Sinistra", - "ControllerSettingsStickRight": "Destra", - "ControllerSettingsStickStick": "Levetta", - "ControllerSettingsStickInvertXAxis": "Inverti levetta X", - "ControllerSettingsStickInvertYAxis": "Inverti levetta Y", - "ControllerSettingsStickDeadzone": "Zona morta:", - "ControllerSettingsLStick": "Levetta sinistra", - "ControllerSettingsRStick": "Levetta destra", - "ControllerSettingsTriggersLeft": "Grilletto sinistro", - "ControllerSettingsTriggersRight": "Grilletto destro", - "ControllerSettingsTriggersButtonsLeft": "Pulsante dorsale sinistro", - "ControllerSettingsTriggersButtonsRight": "Pulsante dorsale destro", - "ControllerSettingsTriggers": "Grilletti", - "ControllerSettingsTriggerL": "L", - "ControllerSettingsTriggerR": "R", - "ControllerSettingsTriggerZL": "ZL", - "ControllerSettingsTriggerZR": "ZR", - "ControllerSettingsLeftSL": "SL", - "ControllerSettingsLeftSR": "SR", - "ControllerSettingsRightSL": "SL", - "ControllerSettingsRightSR": "SR", - "ControllerSettingsExtraButtonsLeft": "Tasto sinistro", - "ControllerSettingsExtraButtonsRight": "Tasto destro", - "ControllerSettingsMisc": "Varie", - "ControllerSettingsTriggerThreshold": "Sensibilità dei grilletti:", - "ControllerSettingsMotion": "Movimento", - "ControllerSettingsMotionUseCemuhookCompatibleMotion": "Usa sensore compatibile con CemuHook", - "ControllerSettingsMotionControllerSlot": "Slot del controller:", - "ControllerSettingsMotionMirrorInput": "Input specchiato", - "ControllerSettingsMotionRightJoyConSlot": "Slot JoyCon destro:", - "ControllerSettingsMotionServerHost": "Server:", - "ControllerSettingsMotionGyroSensitivity": "Sensibilità del giroscopio:", - "ControllerSettingsMotionGyroDeadzone": "Zona morta del giroscopio:", - "ControllerSettingsSave": "Salva", - "ControllerSettingsClose": "Chiudi", - "KeyUnknown": "Sconosciuto", - "KeyShiftLeft": "Maiusc sinistro", - "KeyShiftRight": "Maiusc destro", - "KeyControlLeft": "Ctrl sinistro", - "KeyMacControlLeft": "⌃ sinistro", - "KeyControlRight": "Ctrl destro", - "KeyMacControlRight": "⌃ destro", - "KeyAltLeft": "Alt sinistro", - "KeyMacAltLeft": "⌥ sinistro", - "KeyAltRight": "Alt destro", - "KeyMacAltRight": "⌥ destro", - "KeyWinLeft": "⊞ sinistro", - "KeyMacWinLeft": "⌘ sinistro", - "KeyWinRight": "⊞ destro", - "KeyMacWinRight": "⌘ destro", - "KeyMenu": "Menù", - "KeyUp": "Su", - "KeyDown": "Giù", - "KeyLeft": "Sinistra", - "KeyRight": "Destra", - "KeyEnter": "Invio", - "KeyEscape": "Esc", - "KeySpace": "Spazio", - "KeyTab": "Tab", - "KeyBackSpace": "Backspace", - "KeyInsert": "Ins", - "KeyDelete": "Canc", - "KeyPageUp": "Pag. Su", - "KeyPageDown": "Pag. Giù", - "KeyHome": "Inizio", - "KeyEnd": "Fine", - "KeyCapsLock": "Bloc Maiusc", - "KeyScrollLock": "Bloc Scorr", - "KeyPrintScreen": "Stamp", - "KeyPause": "Pausa", - "KeyNumLock": "Bloc Num", - "KeyClear": "Clear", - "KeyKeypad0": "Tast. num. 0", - "KeyKeypad1": "Tast. num. 1", - "KeyKeypad2": "Tast. num. 2", - "KeyKeypad3": "Tast. num. 3", - "KeyKeypad4": "Tast. num. 4", - "KeyKeypad5": "Tast. num. 5", - "KeyKeypad6": "Tast. num. 6", - "KeyKeypad7": "Tast. num. 7", - "KeyKeypad8": "Tast. num. 8", - "KeyKeypad9": "Tast. num. 9", - "KeyKeypadDivide": "Tast. num. /", - "KeyKeypadMultiply": "Tast. num. *", - "KeyKeypadSubtract": "Tast. num. -", - "KeyKeypadAdd": "Tast. num. +", - "KeyKeypadDecimal": "Tast. num. sep. decimale", - "KeyKeypadEnter": "Tast. num. Invio", - "KeyNumber0": "0", - "KeyNumber1": "1", - "KeyNumber2": "2", - "KeyNumber3": "3", - "KeyNumber4": "4", - "KeyNumber5": "5", - "KeyNumber6": "6", - "KeyNumber7": "7", - "KeyNumber8": "8", - "KeyNumber9": "9", - "KeyTilde": "ò", - "KeyGrave": "`", - "KeyMinus": "-", - "KeyPlus": "+", - "KeyBracketLeft": "'", - "KeyBracketRight": "ì", - "KeySemicolon": "è", - "KeyQuote": "à", - "KeyComma": ",", - "KeyPeriod": ".", - "KeySlash": "ù", - "KeyBackSlash": "<", - "KeyUnbound": "Non assegnato", - "GamepadLeftStick": "Pulsante levetta sinistra", - "GamepadRightStick": "Pulsante levetta destra", - "GamepadLeftShoulder": "Pulsante dorsale sinistro", - "GamepadRightShoulder": "Pulsante dorsale destro", - "GamepadLeftTrigger": "Grilletto sinistro", - "GamepadRightTrigger": "Grilletto destro", - "GamepadDpadUp": "Su", - "GamepadDpadDown": "Giù", - "GamepadDpadLeft": "Sinistra", - "GamepadDpadRight": "Destra", - "GamepadMinus": "-", - "GamepadPlus": "+", - "GamepadGuide": "Guide", - "GamepadMisc1": "Misc", - "GamepadPaddle1": "Paddle 1", - "GamepadPaddle2": "Paddle 2", - "GamepadPaddle3": "Paddle 3", - "GamepadPaddle4": "Paddle 4", - "GamepadTouchpad": "Touchpad", - "GamepadSingleLeftTrigger0": "Grilletto sinistro 0", - "GamepadSingleRightTrigger0": "Grilletto destro 0", - "GamepadSingleLeftTrigger1": "Grilletto sinistro 1", - "GamepadSingleRightTrigger1": "Grilletto destro 1", - "StickLeft": "Levetta sinistra", - "StickRight": "Levetta destra", - "UserProfilesSelectedUserProfile": "Profilo utente selezionato:", - "UserProfilesSaveProfileName": "Salva nome del profilo", - "UserProfilesChangeProfileImage": "Cambia immagine profilo", - "UserProfilesAvailableUserProfiles": "Profili utente disponibili:", - "UserProfilesAddNewProfile": "Aggiungi nuovo profilo", - "UserProfilesDelete": "Elimina", - "UserProfilesClose": "Chiudi", - "ProfileNameSelectionWatermark": "Scegli un soprannome", - "ProfileImageSelectionTitle": "Selezione dell'immagine profilo", - "ProfileImageSelectionHeader": "Scegli un'immagine profilo", - "ProfileImageSelectionNote": "Puoi importare un'immagine profilo personalizzata o selezionare un avatar dal firmware del sistema", - "ProfileImageSelectionImportImage": "Importa file immagine", - "ProfileImageSelectionSelectAvatar": "Seleziona avatar dal firmware", - "InputDialogTitle": "Finestra di input", - "InputDialogOk": "OK", - "InputDialogCancel": "Annulla", - "InputDialogAddNewProfileTitle": "Scegli il nome del profilo", - "InputDialogAddNewProfileHeader": "Digita un nome profilo", - "InputDialogAddNewProfileSubtext": "(Lunghezza massima: {0})", - "AvatarChoose": "Scegli", - "AvatarSetBackgroundColor": "Imposta colore di sfondo", - "AvatarClose": "Chiudi", - "ControllerSettingsLoadProfileToolTip": "Carica profilo", - "ControllerSettingsAddProfileToolTip": "Aggiungi profilo", - "ControllerSettingsRemoveProfileToolTip": "Rimuovi profilo", - "ControllerSettingsSaveProfileToolTip": "Salva profilo", - "MenuBarFileToolsTakeScreenshot": "Cattura uno screenshot", - "MenuBarFileToolsHideUi": "Nascondi l'interfaccia", - "GameListContextMenuRunApplication": "Esegui applicazione", - "GameListContextMenuToggleFavorite": "Preferito", - "GameListContextMenuToggleFavoriteToolTip": "Segna il gioco come preferito", - "SettingsTabGeneralTheme": "Tema:", - "SettingsTabGeneralThemeDark": "Scuro", - "SettingsTabGeneralThemeLight": "Chiaro", - "ControllerSettingsConfigureGeneral": "Configura", - "ControllerSettingsRumble": "Vibrazione", - "ControllerSettingsRumbleStrongMultiplier": "Moltiplicatore vibrazione forte", - "ControllerSettingsRumbleWeakMultiplier": "Moltiplicatore vibrazione debole", - "DialogMessageSaveNotAvailableMessage": "Non ci sono dati di salvataggio per {0} [{1:x16}]", - "DialogMessageSaveNotAvailableCreateSaveMessage": "Vuoi creare dei dati di salvataggio per questo gioco?", - "DialogConfirmationTitle": "Ryujinx - Conferma", - "DialogUpdaterTitle": "Ryujinx - Aggiornamento", - "DialogErrorTitle": "Ryujinx - Errore", - "DialogWarningTitle": "Ryujinx - Avviso", - "DialogExitTitle": "Ryujinx - Esci", - "DialogErrorMessage": "Ryujinx ha riscontrato un problema", - "DialogExitMessage": "Sei sicuro di voler chiudere Ryujinx?", - "DialogExitSubMessage": "Tutti i dati non salvati andranno persi!", - "DialogMessageCreateSaveErrorMessage": "C'è stato un errore durante la creazione dei dati di salvataggio: {0}", - "DialogMessageFindSaveErrorMessage": "C'è stato un errore durante la ricerca dei dati di salvataggio: {0}", - "FolderDialogExtractTitle": "Scegli una cartella in cui estrarre", - "DialogNcaExtractionMessage": "Estrazione della sezione {0} da {1}...", - "DialogNcaExtractionTitle": "Ryujinx - Estrazione sezione NCA", - "DialogNcaExtractionMainNcaNotFoundErrorMessage": "L'estrazione è fallita. L'NCA principale non era presente nel file selezionato.", - "DialogNcaExtractionCheckLogErrorMessage": "L'estrazione è fallita. Consulta il file di log per maggiori informazioni.", - "DialogNcaExtractionSuccessMessage": "Estrazione completata con successo.", - "DialogUpdaterConvertFailedMessage": "La conversione dell'attuale versione di Ryujinx è fallita.", - "DialogUpdaterCancelUpdateMessage": "Annullamento dell'aggiornamento in corso!", - "DialogUpdaterAlreadyOnLatestVersionMessage": "Stai già usando la versione più recente di Ryujinx!", - "DialogUpdaterFailedToGetVersionMessage": "Si è verificato un errore durante il tentativo di recuperare le informazioni sulla versione da GitHub Release. Ciò può verificarsi se una nuova versione è in fase di compilazione da GitHub Actions. Riprova tra qualche minuto.", - "DialogUpdaterConvertFailedGithubMessage": "La conversione della versione di Ryujinx ricevuta da Github Release è fallita.", - "DialogUpdaterDownloadingMessage": "Download dell'aggiornamento...", - "DialogUpdaterExtractionMessage": "Estrazione dell'aggiornamento...", - "DialogUpdaterRenamingMessage": "Rinominazione dell'aggiornamento...", - "DialogUpdaterAddingFilesMessage": "Aggiunta del nuovo aggiornamento...", - "DialogUpdaterCompleteMessage": "Aggiornamento completato!", - "DialogUpdaterRestartMessage": "Vuoi riavviare Ryujinx adesso?", - "DialogUpdaterNoInternetMessage": "Non sei connesso ad Internet!", - "DialogUpdaterNoInternetSubMessage": "Verifica di avere una connessione ad Internet funzionante!", - "DialogUpdaterDirtyBuildMessage": "Non puoi aggiornare una Dirty build di Ryujinx!", - "DialogUpdaterDirtyBuildSubMessage": "Scarica Ryujinx da https://ryujinx.org/ se stai cercando una versione supportata.", - "DialogRestartRequiredMessage": "Riavvio richiesto", - "DialogThemeRestartMessage": "Il tema è stato salvato. È richiesto un riavvio per applicare il tema.", - "DialogThemeRestartSubMessage": "Vuoi riavviare?", - "DialogFirmwareInstallEmbeddedMessage": "Vuoi installare il firmware incorporato in questo gioco? (Firmware {0})", - "DialogFirmwareInstallEmbeddedSuccessMessage": "Non è stato trovato alcun firmware installato, ma Ryujinx è riuscito ad installare il firmware {0} dal gioco fornito.\nL'emulatore si avvierà adesso.", - "DialogFirmwareNoFirmwareInstalledMessage": "Nessun firmware installato", - "DialogFirmwareInstalledMessage": "Il firmware {0} è stato installato", - "DialogInstallFileTypesSuccessMessage": "Tipi di file installati con successo!", - "DialogInstallFileTypesErrorMessage": "Impossibile installare i tipi di file.", - "DialogUninstallFileTypesSuccessMessage": "Tipi di file disinstallati con successo!", - "DialogUninstallFileTypesErrorMessage": "Disinstallazione dei tipi di file non riuscita.", - "DialogOpenSettingsWindowLabel": "Apri finestra delle impostazioni", - "DialogControllerAppletTitle": "Applet del controller", - "DialogMessageDialogErrorExceptionMessage": "Errore nella visualizzazione del Message Dialog: {0}", - "DialogSoftwareKeyboardErrorExceptionMessage": "Errore nella visualizzazione della tastiera software: {0}", - "DialogErrorAppletErrorExceptionMessage": "Errore nella visualizzazione dell'ErrorApplet Dialog: {0}", - "DialogUserErrorDialogMessage": "{0}: {1}", - "DialogUserErrorDialogInfoMessage": "\nPer maggiori informazioni su come risolvere questo errore, segui la nostra guida all'installazione.", - "DialogUserErrorDialogTitle": "Errore di Ryujinx ({0})", - "DialogAmiiboApiTitle": "Amiibo API", - "DialogAmiiboApiFailFetchMessage": "Si è verificato un errore durante il recupero delle informazioni dall'API.", - "DialogAmiiboApiConnectErrorMessage": "Impossibile connettersi al server Amiibo API. Il servizio potrebbe essere fuori uso o potresti dover verificare che la tua connessione internet sia online.", - "DialogProfileInvalidProfileErrorMessage": "Il profilo {0} è incompatibile con l'attuale sistema di configurazione input.", - "DialogProfileDefaultProfileOverwriteErrorMessage": "Il profilo predefinito non può essere sovrascritto", - "DialogProfileDeleteProfileTitle": "Eliminazione profilo", - "DialogProfileDeleteProfileMessage": "Quest'azione è irreversibile, sei sicuro di voler continuare?", - "DialogWarning": "Avviso", - "DialogPPTCDeletionMessage": "Stai per accodare la rigenerazione della cache PPTC al prossimo avvio per:\n\n{0}\n\nSei sicuro di voler proseguire?", - "DialogPPTCDeletionErrorMessage": "Errore nell'eliminazione della cache PPTC a {0}: {1}", - "DialogShaderDeletionMessage": "Stai per eliminare la cache degli shader per:\n\n{0}\n\nSei sicuro di voler proseguire?", - "DialogShaderDeletionErrorMessage": "Errore nell'eliminazione della cache degli shader a {0}: {1}", - "DialogRyujinxErrorMessage": "Ryujinx ha incontrato un errore", - "DialogInvalidTitleIdErrorMessage": "Errore UI: Il gioco selezionato non ha un ID titolo valido", - "DialogFirmwareInstallerFirmwareNotFoundErrorMessage": "Un firmware del sistema valido non è stato trovato in {0}.", - "DialogFirmwareInstallerFirmwareInstallTitle": "Installa firmware {0}", - "DialogFirmwareInstallerFirmwareInstallMessage": "La versione del sistema {0} sarà installata.", - "DialogFirmwareInstallerFirmwareInstallSubMessage": "\n\nQuesta sostituirà l'attuale versione di sistema {0}.", - "DialogFirmwareInstallerFirmwareInstallConfirmMessage": "\n\nVuoi continuare?", - "DialogFirmwareInstallerFirmwareInstallWaitMessage": "Installazione del firmware...", - "DialogFirmwareInstallerFirmwareInstallSuccessMessage": "La versione del sistema {0} è stata installata.", - "DialogUserProfileDeletionWarningMessage": "Non ci sarebbero altri profili da aprire se il profilo selezionato viene cancellato", - "DialogUserProfileDeletionConfirmMessage": "Vuoi eliminare il profilo selezionato?", - "DialogUserProfileUnsavedChangesTitle": "Attenzione - Modifiche Non Salvate", - "DialogUserProfileUnsavedChangesMessage": "Hai apportato modifiche a questo profilo utente che non sono state salvate.", - "DialogUserProfileUnsavedChangesSubMessage": "Vuoi scartare le modifiche?", - "DialogControllerSettingsModifiedConfirmMessage": "Le attuali impostazioni del controller sono state aggiornate.", - "DialogControllerSettingsModifiedConfirmSubMessage": "Vuoi salvare?", - "DialogLoadFileErrorMessage": "{0}. Errore File: {1}", - "DialogModAlreadyExistsMessage": "La mod risulta già installata", - "DialogModInvalidMessage": "La cartella specificata non contiene nessuna mod!", - "DialogModDeleteNoParentMessage": "Eliminazione non riuscita: impossibile trovare la directory superiore per la mod \"{0}\"!", - "DialogDlcNoDlcErrorMessage": "Il file specificato non contiene un DLC per il titolo selezionato!", - "DialogPerformanceCheckLoggingEnabledMessage": "Hai abilitato il trace logging, che è progettato per essere usato solo dagli sviluppatori.", - "DialogPerformanceCheckLoggingEnabledConfirmMessage": "Per prestazioni ottimali, si raccomanda di disabilitare il trace logging. Vuoi disabilitarlo adesso?", - "DialogPerformanceCheckShaderDumpEnabledMessage": "Hai abilitato il dump degli shader, che è progettato per essere usato solo dagli sviluppatori.", - "DialogPerformanceCheckShaderDumpEnabledConfirmMessage": "Per prestazioni ottimali, si raccomanda di disabilitare il dump degli shader. Vuoi disabilitarlo adesso?", - "DialogLoadAppGameAlreadyLoadedMessage": "Un gioco è già stato caricato", - "DialogLoadAppGameAlreadyLoadedSubMessage": "Ferma l'emulazione o chiudi l'emulatore prima di avviare un altro gioco.", - "DialogUpdateAddUpdateErrorMessage": "Il file specificato non contiene un aggiornamento per il titolo selezionato!", - "DialogSettingsBackendThreadingWarningTitle": "Avviso - Backend Threading", - "DialogSettingsBackendThreadingWarningMessage": "Ryujinx deve essere riavviato dopo aver cambiato questa opzione per applicarla completamente. A seconda della tua piattaforma, potrebbe essere necessario disabilitare manualmente il multithreading del driver quando usi quello di Ryujinx.", - "DialogModManagerDeletionWarningMessage": "Stai per eliminare la mod: {0}\n\nConfermi di voler procedere?", - "DialogModManagerDeletionAllWarningMessage": "Stai per eliminare tutte le mod per questo titolo.\n\nVuoi davvero procedere?", - "SettingsTabGraphicsFeaturesOptions": "Funzionalità", - "SettingsTabGraphicsBackendMultithreading": "Multithreading del backend grafico:", - "CommonAuto": "Auto", - "CommonOff": "Disattivato", - "CommonOn": "Attivo", - "InputDialogYes": "Sì", - "InputDialogNo": "No", - "DialogProfileInvalidProfileNameErrorMessage": "Il nome del file contiene dei caratteri non validi. Riprova.", - "MenuBarOptionsPauseEmulation": "Metti in pausa", - "MenuBarOptionsResumeEmulation": "Riprendi", - "AboutUrlTooltipMessage": "Clicca per aprire il sito web di Ryujinx nel tuo browser predefinito.", - "AboutDisclaimerMessage": "Ryujinx non è affiliato con Nintendo™,\no i suoi partner, in alcun modo.", - "AboutAmiiboDisclaimerMessage": "AmiiboAPI (www.amiiboapi.com) è usata\nnella nostra emulazione Amiibo.", - "AboutPatreonUrlTooltipMessage": "Clicca per aprire la pagina Patreon di Ryujinx nel tuo browser predefinito.", - "AboutGithubUrlTooltipMessage": "Clicca per aprire la pagina GitHub di Ryujinx nel tuo browser predefinito.", - "AboutDiscordUrlTooltipMessage": "Clicca per aprire un invito al server Discord di Ryujinx nel tuo browser predefinito.", - "AboutTwitterUrlTooltipMessage": "Clicca per aprire la pagina Twitter di Ryujinx nel tuo browser predefinito.", - "AboutRyujinxAboutTitle": "Informazioni:", - "AboutRyujinxAboutContent": "Ryujinx è un emulatore per la console Nintendo Switch™.\nSostienici su Patreon.\nRicevi tutte le ultime notizie sul nostro Twitter o su Discord.\nGli sviluppatori interessati a contribuire possono trovare più informazioni sul nostro GitHub o Discord.", - "AboutRyujinxMaintainersTitle": "Mantenuto da:", - "AboutRyujinxMaintainersContentTooltipMessage": "Clicca per aprire la pagina dei contributori nel tuo browser predefinito.", - "AboutRyujinxSupprtersTitle": "Supportato su Patreon da:", - "AmiiboSeriesLabel": "Serie Amiibo", - "AmiiboCharacterLabel": "Personaggio", - "AmiiboScanButtonLabel": "Scansiona", - "AmiiboOptionsShowAllLabel": "Mostra tutti gli amiibo", - "AmiiboOptionsUsRandomTagLabel": "Espediente: Usa un UUID del tag casuale", - "DlcManagerTableHeadingEnabledLabel": "Abilitato", - "DlcManagerTableHeadingTitleIdLabel": "ID Titolo", - "DlcManagerTableHeadingContainerPathLabel": "Percorso del contenitore", - "DlcManagerTableHeadingFullPathLabel": "Percorso completo", - "DlcManagerRemoveAllButton": "Rimuovi tutti", - "DlcManagerEnableAllButton": "Abilita tutto", - "DlcManagerDisableAllButton": "Disabilita tutto", - "ModManagerDeleteAllButton": "Elimina tutto", - "MenuBarOptionsChangeLanguage": "Cambia lingua", - "MenuBarShowFileTypes": "Mostra tipi di file", - "CommonSort": "Ordina", - "CommonShowNames": "Mostra nomi", - "CommonFavorite": "Preferito", - "OrderAscending": "Crescente", - "OrderDescending": "Decrescente", - "SettingsTabGraphicsFeatures": "Funzionalità e miglioramenti", - "ErrorWindowTitle": "Finestra di errore", - "ToggleDiscordTooltip": "Scegli se mostrare o meno Ryujinx nella tua attività su Discord", - "AddGameDirBoxTooltip": "Inserisci una cartella dei giochi per aggiungerla alla lista", - "AddGameDirTooltip": "Aggiungi una cartella dei giochi alla lista", - "RemoveGameDirTooltip": "Rimuovi la cartella dei giochi selezionata", - "CustomThemeCheckTooltip": "Attiva o disattiva temi personalizzati nella GUI", - "CustomThemePathTooltip": "Percorso al tema GUI personalizzato", - "CustomThemeBrowseTooltip": "Sfoglia per cercare un tema GUI personalizzato", - "DockModeToggleTooltip": "La modalità TV fa sì che il sistema emulato si comporti come una Nintendo Switch posizionata nella sua base. Ciò migliora la qualità grafica nella maggior parte dei giochi. Al contrario, disabilitandola il sistema emulato si comporterà come una Nintendo Switch in modalità portatile, riducendo la qualità grafica.\n\nConfigura i controlli del giocatore 1 se intendi usare la modalità TV; configura i controlli della modalità portatile se intendi usare quest'ultima.\n\nNel dubbio, lascia l'opzione attiva.", - "DirectKeyboardTooltip": "Supporto per l'accesso diretto alla tastiera (HID). Fornisce ai giochi l'accesso alla tastiera come dispositivo di inserimento del testo.\n\nFunziona solo con i giochi che supportano nativamente l'utilizzo della tastiera su hardware Switch.\n\nNel dubbio, lascia l'opzione disattivata.", - "DirectMouseTooltip": "Supporto per l'accesso diretto al mouse (HID). Fornisce ai giochi l'accesso al mouse come dispositivo di puntamento.\n\nFunziona solo con i rari giochi che supportano nativamente l'utilizzo del mouse su hardware Switch.\n\nQuando questa opzione è attivata, il touchscreen potrebbe non funzionare.\n\nNel dubbio, lascia l'opzione disattivata.", - "RegionTooltip": "Cambia regione di sistema", - "LanguageTooltip": "Cambia lingua di sistema", - "TimezoneTooltip": "Cambia fuso orario di sistema", - "TimeTooltip": "Cambia data e ora di sistema", - "VSyncToggleTooltip": "Sincronizzazione verticale della console Emulata. Essenzialmente un limitatore di frame per la maggior parte dei giochi; disabilitarlo può far girare giochi a velocità più alta, allungare le schermate di caricamento o farle bloccare.\n\nPuò essere attivata in gioco con un tasto di scelta rapida (F1 per impostazione predefinita). Ti consigliamo di farlo se hai intenzione di disabilitarlo.\n\nLascia ON se non sei sicuro.", - "PptcToggleTooltip": "Salva le funzioni JIT tradotte in modo che non debbano essere tradotte tutte le volte che si avvia un determinato gioco.\n\nRiduce i fenomeni di stuttering e velocizza sensibilmente gli avvii successivi del gioco.\n\nNel dubbio, lascia l'opzione attiva.", - "FsIntegrityToggleTooltip": "Controlla la presenza di file corrotti quando si avvia un gioco. Se vengono rilevati dei file corrotti, verrà mostrato un errore di hash nel log.\n\nQuesta opzione non influisce sulle prestazioni ed è pensata per facilitare la risoluzione dei problemi.\n\nNel dubbio, lascia l'opzione attiva.", - "AudioBackendTooltip": "Cambia il backend usato per riprodurre l'audio.\n\nSDL2 è quello preferito, mentre OpenAL e SoundIO sono usati come ripiego. Dummy non riprodurrà alcun suono.\n\nNel dubbio, imposta l'opzione su SDL2.", - "MemoryManagerTooltip": "Cambia il modo in cui la memoria guest è mappata e vi si accede. Influisce notevolmente sulle prestazioni della CPU emulata.\n\nNel dubbio, imposta l'opzione su Host Unchecked.", - "MemoryManagerSoftwareTooltip": "Usa una software page table per la traduzione degli indirizzi. Massima precisione ma prestazioni più lente.", - "MemoryManagerHostTooltip": "Mappa direttamente la memoria nello spazio degli indirizzi dell'host. Compilazione ed esecuzione JIT molto più veloce.", - "MemoryManagerUnsafeTooltip": "Mappa direttamente la memoria, ma non maschera l'indirizzo all'interno dello spazio degli indirizzi guest prima dell'accesso. Più veloce, ma a costo della sicurezza. L'applicazione guest può accedere alla memoria da qualsiasi punto di Ryujinx, quindi esegui solo programmi di cui ti fidi con questa modalità.", - "UseHypervisorTooltip": "Usa Hypervisor invece di JIT. Migliora notevolmente le prestazioni quando disponibile, ma può essere instabile nel suo stato attuale.", - "DRamTooltip": "Utilizza un layout di memoria alternativo per imitare un'unità di sviluppo di Switch.\n\nQuesta opzione è utile soltanto per i pacchetti di texture ad alta risoluzione o per le mod che aumentano la risoluzione a 4K. NON migliora le prestazioni.\n\nNel dubbio, lascia l'opzione disattivata.", - "IgnoreMissingServicesTooltip": "Ignora i servizi non implementati del sistema operativo Horizon. Può aiutare ad aggirare gli arresti anomali che si verificano avviando alcuni giochi.\n\nNel dubbio, lascia l'opzione disattivata.", - "GraphicsBackendThreadingTooltip": "Esegue i comandi del backend grafico su un secondo thread.\n\nVelocizza la compilazione degli shader, riduce lo stuttering e migliora le prestazioni sui driver grafici senza il supporto integrato al multithreading. Migliora leggermente le prestazioni sui driver che supportano il multithreading.\n\nNel dubbio, imposta l'opzione su Auto.", - "GalThreadingTooltip": "Esegue i comandi del backend grafico su un secondo thread.\n\nVelocizza la compilazione degli shader, riduce lo stuttering e migliora le prestazioni sui driver grafici senza il supporto integrato al multithreading. Migliora leggermente le prestazioni sui driver che supportano il multithreading.\n\nNel dubbio, imposta l'opzione su Auto.", - "ShaderCacheToggleTooltip": "Salva una cache degli shader su disco che riduce i fenomeni di stuttering nelle esecuzioni successive.\n\nNel dubbio, lascia l'opzione attiva.", - "ResolutionScaleTooltip": "Moltiplica la risoluzione di rendering del gioco.\n\nAlcuni giochi potrebbero non funzionare con questa opzione e sembrare pixelati anche quando la risoluzione è aumentata; per quei giochi, potrebbe essere necessario trovare mod che rimuovono l'anti-aliasing o che aumentano la risoluzione di rendering interna. Per quest'ultimo caso, probabilmente dovrai selezionare Nativo (1x).\n\nQuesta opzione può essere modificata mentre un gioco è in esecuzione facendo clic su \"Applica\" qui sotto; puoi semplicemente spostare la finestra delle impostazioni da parte e sperimentare fino a quando non trovi il tuo look preferito per un gioco.\n\nTenete a mente che 4x è troppo per praticamente qualsiasi configurazione.", - "ResolutionScaleEntryTooltip": "Scala della risoluzione in virgola mobile, come 1,5. Le scale non integrali hanno maggiori probabilità di causare problemi o crash.", - "AnisotropyTooltip": "Livello del filtro anisotropico. Imposta su Auto per usare il valore richiesto dal gioco.", - "AspectRatioTooltip": "Proporzioni dello schermo applicate alla finestra di renderizzazione.\n\nCambialo solo se stai usando una mod di proporzioni per il tuo gioco, altrimenti la grafica verrà allungata.\n\nLasciare il 16:9 se incerto.", - "ShaderDumpPathTooltip": "Percorso di dump degli shader", - "FileLogTooltip": "Salva il log della console in un file su disco. Non influisce sulle prestazioni.", - "StubLogTooltip": "Stampa i messaggi di log relativi alle stub nella console. Non influisce sulle prestazioni.", - "InfoLogTooltip": "Stampa i messaggi di log informativi nella console. Non influisce sulle prestazioni.", - "WarnLogTooltip": "Stampa i messaggi di log relativi agli avvisi nella console. Non influisce sulle prestazioni.", - "ErrorLogTooltip": "Stampa i messaggi di log relativi agli errori nella console. Non influisce sulle prestazioni.", - "TraceLogTooltip": "Stampa i messaggi di log relativi al trace nella console. Non influisce sulle prestazioni.", - "GuestLogTooltip": "Stampa i messaggi di log del guest nella console. Non influisce sulle prestazioni.", - "FileAccessLogTooltip": "Stampa i messaggi di log relativi all'accesso ai file nella console.", - "FSAccessLogModeTooltip": "Attiva l'output dei log di accesso FS nella console. Le modalità possibili vanno da 0 a 3", - "DeveloperOptionTooltip": "Usa con attenzione", - "OpenGlLogLevel": "Richiede che i livelli di log appropriati siano abilitati", - "DebugLogTooltip": "Stampa i messaggi di log per il debug nella console.\n\nUsa questa opzione solo se specificatamente richiesto da un membro del team, dal momento che rende i log difficili da leggere e riduce le prestazioni dell'emulatore.", - "LoadApplicationFileTooltip": "Apri un file explorer per scegliere un file compatibile Switch da caricare", - "LoadApplicationFolderTooltip": "Apri un file explorer per scegliere un file compatibile Switch, applicazione sfusa da caricare", - "OpenRyujinxFolderTooltip": "Apri la cartella del filesystem di Ryujinx", - "OpenRyujinxLogsTooltip": "Apre la cartella dove vengono salvati i log", - "ExitTooltip": "Esci da Ryujinx", - "OpenSettingsTooltip": "Apri la finestra delle impostazioni", - "OpenProfileManagerTooltip": "Apri la finestra di gestione dei profili utente", - "StopEmulationTooltip": "Ferma l'emulazione del gioco attuale e torna alla selezione dei giochi", - "CheckUpdatesTooltip": "Controlla la presenza di aggiornamenti di Ryujinx", - "OpenAboutTooltip": "Apri la finestra delle informazioni", - "GridSize": "Dimensione griglia", - "GridSizeTooltip": "Cambia la dimensione dei riquadri della griglia", - "SettingsTabSystemSystemLanguageBrazilianPortuguese": "Portoghese brasiliano", - "AboutRyujinxContributorsButtonHeader": "Mostra tutti i contributori", - "SettingsTabSystemAudioVolume": "Volume: ", - "AudioVolumeTooltip": "Cambia volume audio", - "SettingsTabSystemEnableInternetAccess": "Attiva l'accesso a Internet da parte del guest/Modalità LAN", - "EnableInternetAccessTooltip": "Consente all'applicazione emulata di connettersi a Internet.\n\nI giochi che dispongono di una modalità LAN possono connettersi tra di loro quando questa opzione è abilitata e sono connessi alla stessa rete, comprese le console reali.\n\nQuesta opzione NON consente la connessione ai server di Nintendo. Potrebbe causare arresti anomali in alcuni giochi che provano a connettersi a Internet.\n\nNel dubbio, lascia l'opzione disattivata.", - "GameListContextMenuManageCheatToolTip": "Gestisci trucchi", - "GameListContextMenuManageCheat": "Gestisci trucchi", - "GameListContextMenuManageModToolTip": "Gestisci mod", - "GameListContextMenuManageMod": "Gestisci mod", - "ControllerSettingsStickRange": "Raggio:", - "DialogStopEmulationTitle": "Ryujinx - Ferma emulazione", - "DialogStopEmulationMessage": "Sei sicuro di voler fermare l'emulazione?", - "SettingsTabCpu": "CPU", - "SettingsTabAudio": "Audio", - "SettingsTabNetwork": "Rete", - "SettingsTabNetworkConnection": "Connessione di rete", - "SettingsTabCpuCache": "Cache CPU", - "SettingsTabCpuMemory": "Modalità CPU", - "DialogUpdaterFlatpakNotSupportedMessage": "Aggiorna Ryujinx tramite FlatHub.", - "UpdaterDisabledWarningTitle": "Updater disabilitato!", - "ControllerSettingsRotate90": "Ruota in senso orario di 90°", - "IconSize": "Dimensioni icone", - "IconSizeTooltip": "Cambia le dimensioni delle icone dei giochi", - "MenuBarOptionsShowConsole": "Mostra console", - "ShaderCachePurgeError": "Errore nell'eliminazione della cache degli shader a {0}: {1}", - "UserErrorNoKeys": "Chiavi non trovate", - "UserErrorNoFirmware": "Firmware non trovato", - "UserErrorFirmwareParsingFailed": "Errori di analisi del firmware", - "UserErrorApplicationNotFound": "Applicazione non trovata", - "UserErrorUnknown": "Errore sconosciuto", - "UserErrorUndefined": "Errore non definito", - "UserErrorNoKeysDescription": "Ryujinx non è riuscito a trovare il file 'prod.keys'", - "UserErrorNoFirmwareDescription": "Ryujinx non è riuscito a trovare alcun firmware installato", - "UserErrorFirmwareParsingFailedDescription": "Ryujinx non è riuscito ad analizzare il firmware. Questo di solito è causato da chiavi non aggiornate.", - "UserErrorApplicationNotFoundDescription": "Ryujinx non è riuscito a trovare un'applicazione valida nel percorso specificato.", - "UserErrorUnknownDescription": "Si è verificato un errore sconosciuto!", - "UserErrorUndefinedDescription": "Si è verificato un errore sconosciuto! Ciò non dovrebbe accadere, contatta uno sviluppatore!", - "OpenSetupGuideMessage": "Apri la guida all'installazione", - "NoUpdate": "Nessun aggiornamento", - "TitleUpdateVersionLabel": "Versione {0}", - "RyujinxInfo": "Ryujinx - Info", - "RyujinxConfirm": "Ryujinx - Conferma", - "FileDialogAllTypes": "Tutti i tipi", - "Never": "Mai", - "SwkbdMinCharacters": "Non può avere meno di {0} caratteri", - "SwkbdMinRangeCharacters": "Può avere da {0} a {1} caratteri", - "SoftwareKeyboard": "Tastiera software", - "SoftwareKeyboardModeNumeric": "Deve essere solo 0-9 o '.'", - "SoftwareKeyboardModeAlphabet": "Deve essere solo caratteri non CJK", - "SoftwareKeyboardModeASCII": "Deve essere solo testo ASCII", - "ControllerAppletControllers": "Controller supportati:", - "ControllerAppletPlayers": "Giocatori:", - "ControllerAppletDescription": "La configurazione corrente non è valida. Aprire le impostazioni e riconfigurare gli input.", - "ControllerAppletDocked": "Modalità TV attivata. Gli input della modalità portatile dovrebbero essere disabilitati.", - "UpdaterRenaming": "Rinominazione dei vecchi files...", - "UpdaterRenameFailed": "Non è stato possibile rinominare il file: {0}", - "UpdaterAddingFiles": "Aggiunta dei nuovi file...", - "UpdaterExtracting": "Estrazione dell'aggiornamento...", - "UpdaterDownloading": "Download dell'aggiornamento...", - "Game": "Gioco", - "Docked": "TV", - "Handheld": "Portatile", - "ConnectionError": "Errore di connessione.", - "AboutPageDeveloperListMore": "{0} e altri ancora...", - "ApiError": "Errore dell'API.", - "LoadingHeading": "Caricamento di {0}", - "CompilingPPTC": "Compilazione PPTC", - "CompilingShaders": "Compilazione degli shader", - "AllKeyboards": "Tutte le tastiere", - "OpenFileDialogTitle": "Seleziona un file supportato da aprire", - "OpenFolderDialogTitle": "Seleziona una cartella con un gioco estratto", - "AllSupportedFormats": "Tutti i formati supportati", - "RyujinxUpdater": "Aggiornamento di Ryujinx", - "SettingsTabHotkeys": "Tasti di scelta rapida", - "SettingsTabHotkeysHotkeys": "Tasti di scelta rapida", - "SettingsTabHotkeysToggleVsyncHotkey": "Attiva/disattiva VSync:", - "SettingsTabHotkeysScreenshotHotkey": "Cattura uno screenshot:", - "SettingsTabHotkeysShowUiHotkey": "Mostra l'interfaccia:", - "SettingsTabHotkeysPauseHotkey": "Metti in pausa:", - "SettingsTabHotkeysToggleMuteHotkey": "Disattiva l'audio:", - "ControllerMotionTitle": "Impostazioni dei sensori di movimento", - "ControllerRumbleTitle": "Impostazioni di vibrazione", - "SettingsSelectThemeFileDialogTitle": "Seleziona file del tema", - "SettingsXamlThemeFile": "File del tema xaml", - "AvatarWindowTitle": "Gestisci account - Avatar", - "Amiibo": "Amiibo", - "Unknown": "Sconosciuto", - "Usage": "Utilizzo", - "Writable": "Scrivibile", - "SelectDlcDialogTitle": "Seleziona file dei DLC", - "SelectUpdateDialogTitle": "Seleziona file di aggiornamento", - "SelectModDialogTitle": "Seleziona cartella delle mod", - "UserProfileWindowTitle": "Gestione profili utente", - "CheatWindowTitle": "Gestione trucchi", - "DlcWindowTitle": "Gestisci DLC per {0} ({1})", - "ModWindowTitle": "Gestisci mod per {0} ({1})", - "UpdateWindowTitle": "Gestione aggiornamenti", - "CheatWindowHeading": "Trucchi disponibili per {0} [{1}]", - "BuildId": "ID Build", - "DlcWindowHeading": "DLC disponibili per {0} [{1}]", - "ModWindowHeading": "{0} mod", - "UserProfilesEditProfile": "Modifica selezionati", - "Cancel": "Annulla", - "Save": "Salva", - "Discard": "Scarta", - "Paused": "In pausa", - "UserProfilesSetProfileImage": "Imposta immagine profilo", - "UserProfileEmptyNameError": "Il nome è obbligatorio", - "UserProfileNoImageError": "Dev'essere impostata un'immagine profilo", - "GameUpdateWindowHeading": "Gestisci aggiornamenti per {0} ({1})", - "SettingsTabHotkeysResScaleUpHotkey": "Aumenta la risoluzione:", - "SettingsTabHotkeysResScaleDownHotkey": "Riduci la risoluzione:", - "UserProfilesName": "Nome:", - "UserProfilesUserId": "ID utente:", - "SettingsTabGraphicsBackend": "Backend grafico", - "SettingsTabGraphicsBackendTooltip": "Seleziona il backend grafico che verrà utilizzato nell'emulatore.\n\nVulkan è nel complesso migliore per tutte le schede grafiche moderne, a condizione che i relativi driver siano aggiornati. Vulkan dispone anche di una compilazione degli shader più veloce (con minore stuttering) su tutte le marche di GPU.\n\nOpenGL può ottenere risultati migliori su vecchie GPU Nvidia, su vecchie GPU AMD su Linux, o su GPU con poca VRAM, anche se lo stuttering dovuto alla compilazione degli shader sarà maggiore.\n\nNel dubbio, scegli Vulkan. Seleziona OpenGL se la GPU non supporta Vulkan nemmeno con i driver grafici più recenti.", - "SettingsEnableTextureRecompression": "Attiva la ricompressione delle texture", - "SettingsEnableTextureRecompressionTooltip": "Comprime le texture ASTC per ridurre l'utilizzo di VRAM.\n\nI giochi che utilizzano questo formato di texture includono Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder e The Legend of Zelda: Tears of the Kingdom.\n\nLe schede grafiche con 4GiB o meno di VRAM probabilmente si bloccheranno ad un certo punto durante l'esecuzione di questi giochi.\n\nAttiva questa opzione solo se sei a corto di VRAM nei giochi sopra menzionati. Nel dubbio, lascia l'opzione disattivata.", - "SettingsTabGraphicsPreferredGpu": "GPU preferita", - "SettingsTabGraphicsPreferredGpuTooltip": "Seleziona la scheda grafica che verrà usata con la backend grafica Vulkan.\n\nNon influenza la GPU che userà OpenGL.\n\nImposta la GPU contrassegnata come \"dGPU\" se non sei sicuro. Se non ce n'è una, lascia intatta quest'impostazione.", - "SettingsAppRequiredRestartMessage": "È richiesto un riavvio di Ryujinx", - "SettingsGpuBackendRestartMessage": "Le impostazioni della backend grafica o della GPU sono state modificate. Questo richiederà un riavvio perché le modifiche siano applicate", - "SettingsGpuBackendRestartSubMessage": "Vuoi riavviare ora?", - "RyujinxUpdaterMessage": "Vuoi aggiornare Ryujinx all'ultima versione?", - "SettingsTabHotkeysVolumeUpHotkey": "Alza il volume:", - "SettingsTabHotkeysVolumeDownHotkey": "Abbassa il volume:", - "SettingsEnableMacroHLE": "Attiva HLE macro", - "SettingsEnableMacroHLETooltip": "Emulazione di alto livello del codice macro della GPU.\n\nMigliora le prestazioni, ma può causare anomalie grafiche in alcuni giochi.\n\nNel dubbio, lascia l'opzione attiva.", - "SettingsEnableColorSpacePassthrough": "Passthrough dello spazio dei colori", - "SettingsEnableColorSpacePassthroughTooltip": "Indica al backend Vulkan di passare le informazioni sul colore senza specificare uno spazio dei colori. Per gli utenti con schermi ad ampia gamma, ciò può rendere i colori più vivaci, sacrificando la correttezza del colore.", - "VolumeShort": "Vol", - "UserProfilesManageSaves": "Gestisci i salvataggi", - "DeleteUserSave": "Vuoi eliminare il salvataggio utente per questo gioco?", - "IrreversibleActionNote": "Questa azione non è reversibile.", - "SaveManagerHeading": "Gestisci salvataggi per {0} ({1})", - "SaveManagerTitle": "Gestione salvataggi", - "Name": "Nome", - "Size": "Dimensione", - "Search": "Cerca", - "UserProfilesRecoverLostAccounts": "Recupera account persi", - "Recover": "Recupera", - "UserProfilesRecoverHeading": "Sono stati trovati dei salvataggi per i seguenti account", - "UserProfilesRecoverEmptyList": "Nessun profilo da recuperare", - "GraphicsAATooltip": "Applica anti-aliasing al rendering del gioco.\n\nFXAA sfocerà la maggior parte dell'immagine, mentre SMAA tenterà di trovare bordi frastagliati e lisciarli.\n\nNon si consiglia di usarlo in combinazione con il filtro di scala FSR.\n\nQuesta opzione può essere modificata mentre un gioco è in esecuzione facendo clic su \"Applica\" qui sotto; puoi semplicemente spostare la finestra delle impostazioni da parte e sperimentare fino a quando non trovi il tuo look preferito per un gioco.\n\nLasciare su Nessuno se incerto.", - "GraphicsAALabel": "Anti-Aliasing:", - "GraphicsScalingFilterLabel": "Filtro di scala:", - "GraphicsScalingFilterTooltip": "Scegli il filtro di scaling che verrà applicato quando si utilizza o scaling di risoluzione.\n\nBilineare funziona bene per i giochi 3D ed è un'opzione predefinita affidabile.\n\nNearest è consigliato per i giochi in pixel art.\n\nFSR 1.0 è solo un filtro di nitidezza, non raccomandato per l'uso con FXAA o SMAA.\n\nQuesta opzione può essere modificata mentre un gioco è in esecuzione facendo clic su \"Applica\" qui sotto; puoi semplicemente spostare la finestra delle impostazioni da parte e sperimentare fino a quando non trovi il tuo look preferito per un gioco.\n\nLasciare su Bilineare se incerto.", - "GraphicsScalingFilterBilinear": "Bilineare", - "GraphicsScalingFilterNearest": "Nearest", - "GraphicsScalingFilterFsr": "FSR", - "GraphicsScalingFilterLevelLabel": "Livello", - "GraphicsScalingFilterLevelTooltip": "Imposta il livello di nitidezza di FSR 1.0. Valori più alti comportano una maggiore nitidezza.", - "SmaaLow": "SMAA Basso", - "SmaaMedium": "SMAA Medio", - "SmaaHigh": "SMAA Alto", - "SmaaUltra": "SMAA Ultra", - "UserEditorTitle": "Modificare L'Utente", - "UserEditorTitleCreate": "Crea Un Utente", - "SettingsTabNetworkInterface": "Interfaccia di rete:", - "NetworkInterfaceTooltip": "L'interfaccia di rete utilizzata per le funzionalità LAN/LDN.\n\nIn combinazione con una VPN o XLink Kai e un gioco che supporta la modalità LAN, questa opzione può essere usata per simulare la connessione alla stessa rete attraverso Internet.\n\nNel dubbio, lascia l'opzione su Predefinito.", - "NetworkInterfaceDefault": "Predefinito", - "PackagingShaders": "Salvataggio degli shader", - "AboutChangelogButton": "Visualizza changelog su GitHub", - "AboutChangelogButtonTooltipMessage": "Clicca per aprire il changelog per questa versione nel tuo browser predefinito.", - "SettingsTabNetworkMultiplayer": "Multigiocatore", - "MultiplayerMode": "Modalità:", - "MultiplayerModeTooltip": "Cambia la modalità multigiocatore LDN.\n\nLdnMitm modificherà la funzionalità locale wireless/local play nei giochi per funzionare come se fosse in modalità LAN, consentendo connessioni locali sulla stessa rete con altre istanze di Ryujinx e console Nintendo Switch modificate che hanno il modulo ldn_mitm installato.\n\nLa modalità multigiocatore richiede che tutti i giocatori usino la stessa versione del gioco (es. Super Smash Bros. Ultimate v13.0.1 non può connettersi con la v13.0.0).\n\nNel dubbio, lascia l'opzione su Disabilitato.", - "MultiplayerModeDisabled": "Disabilitato", - "MultiplayerModeLdnMitm": "ldn_mitm" -} diff --git a/src/Ryujinx/Assets/Locales/ja_JP.json b/src/Ryujinx/Assets/Locales/ja_JP.json deleted file mode 100644 index 61e963258..000000000 --- a/src/Ryujinx/Assets/Locales/ja_JP.json +++ /dev/null @@ -1,780 +0,0 @@ -{ - "Language": "日本語", - "MenuBarFileOpenApplet": "アプレットを開く", - "MenuBarFileOpenAppletOpenMiiAppletToolTip": "スタンドアロンモードで Mii エディタアプレットを開きます", - "SettingsTabInputDirectMouseAccess": "マウス直接アクセス", - "SettingsTabSystemMemoryManagerMode": "メモリ管理モード:", - "SettingsTabSystemMemoryManagerModeSoftware": "ソフトウェア", - "SettingsTabSystemMemoryManagerModeHost": "ホスト (高速)", - "SettingsTabSystemMemoryManagerModeHostUnchecked": "ホスト, チェックなし (最高速, 安全でない)", - "SettingsTabSystemUseHypervisor": "ハイパーバイザーを使用", - "MenuBarFile": "ファイル(_F)", - "MenuBarFileOpenFromFile": "ファイルからアプリケーションをロード(_L)", - "MenuBarFileOpenUnpacked": "展開されたゲームをロード", - "MenuBarFileOpenEmuFolder": "Ryujinx フォルダを開く", - "MenuBarFileOpenLogsFolder": "ログフォルダを開く", - "MenuBarFileExit": "終了(_E)", - "MenuBarOptions": "オプション(_O)", - "MenuBarOptionsToggleFullscreen": "全画面切り替え", - "MenuBarOptionsStartGamesInFullscreen": "全画面モードでゲームを開始", - "MenuBarOptionsStopEmulation": "エミュレーションを中止", - "MenuBarOptionsSettings": "設定(_S)", - "MenuBarOptionsManageUserProfiles": "ユーザプロファイルを管理(_M)", - "MenuBarActions": "アクション(_A)", - "MenuBarOptionsSimulateWakeUpMessage": "スリープ復帰メッセージをシミュレート", - "MenuBarActionsScanAmiibo": "Amiibo をスキャン", - "MenuBarTools": "ツール(_T)", - "MenuBarToolsInstallFirmware": "ファームウェアをインストール", - "MenuBarFileToolsInstallFirmwareFromFile": "XCI または ZIP からファームウェアをインストール", - "MenuBarFileToolsInstallFirmwareFromDirectory": "ディレクトリからファームウェアをインストール", - "MenuBarToolsManageFileTypes": "ファイル形式を管理", - "MenuBarToolsInstallFileTypes": "ファイル形式をインストール", - "MenuBarToolsUninstallFileTypes": "ファイル形式をアンインストール", - "MenuBarView": "_View", - "MenuBarViewWindow": "Window Size", - "MenuBarViewWindow720": "720p", - "MenuBarViewWindow1080": "1080p", - "MenuBarHelp": "ヘルプ(_H)", - "MenuBarHelpCheckForUpdates": "アップデートを確認", - "MenuBarHelpAbout": "Ryujinx について", - "MenuSearch": "検索...", - "GameListHeaderFavorite": "お気に入り", - "GameListHeaderIcon": "アイコン", - "GameListHeaderApplication": "名称", - "GameListHeaderDeveloper": "開発元", - "GameListHeaderVersion": "バージョン", - "GameListHeaderTimePlayed": "プレイ時間", - "GameListHeaderLastPlayed": "最終プレイ日時", - "GameListHeaderFileExtension": "ファイル拡張子", - "GameListHeaderFileSize": "ファイルサイズ", - "GameListHeaderPath": "パス", - "GameListContextMenuOpenUserSaveDirectory": "セーブディレクトリを開く", - "GameListContextMenuOpenUserSaveDirectoryToolTip": "アプリケーションのユーザセーブデータを格納するディレクトリを開きます", - "GameListContextMenuOpenDeviceSaveDirectory": "デバイスディレクトリを開く", - "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "アプリケーションのデバイスセーブデータを格納するディレクトリを開きます", - "GameListContextMenuOpenBcatSaveDirectory": "BCATディレクトリを開く", - "GameListContextMenuOpenBcatSaveDirectoryToolTip": "アプリケーションの BCAT セーブデータを格納するディレクトリを開きます", - "GameListContextMenuManageTitleUpdates": "アップデートを管理", - "GameListContextMenuManageTitleUpdatesToolTip": "タイトルのアップデート管理ウインドウを開きます", - "GameListContextMenuManageDlc": "DLCを管理", - "GameListContextMenuManageDlcToolTip": "DLC管理ウインドウを開きます", - "GameListContextMenuCacheManagement": "キャッシュ管理", - "GameListContextMenuCacheManagementPurgePptc": "PPTC を再構築", - "GameListContextMenuCacheManagementPurgePptcToolTip": "次回のゲーム起動時に PPTC を再構築します", - "GameListContextMenuCacheManagementPurgeShaderCache": "シェーダーキャッシュを破棄", - "GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "アプリケーションのシェーダーキャッシュを破棄します", - "GameListContextMenuCacheManagementOpenPptcDirectory": "PPTC ディレクトリを開く", - "GameListContextMenuCacheManagementOpenPptcDirectoryToolTip": "アプリケーションの PPTC キャッシュを格納するディレクトリを開きます", - "GameListContextMenuCacheManagementOpenShaderCacheDirectory": "シェーダーキャッシュディレクトリを開く", - "GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip": "アプリケーションのシェーダーキャッシュを格納するディレクトリを開きます", - "GameListContextMenuExtractData": "データを展開", - "GameListContextMenuExtractDataExeFS": "ExeFS", - "GameListContextMenuExtractDataExeFSToolTip": "現在のアプリケーション設定(アップデート含む)から ExeFS セクションを展開します", - "GameListContextMenuExtractDataRomFS": "RomFS", - "GameListContextMenuExtractDataRomFSToolTip": "現在のアプリケーション設定(アップデート含む)から RomFS セクションを展開します", - "GameListContextMenuExtractDataLogo": "ロゴ", - "GameListContextMenuExtractDataLogoToolTip": "現在のアプリケーション設定(アップデート含む)からロゴセクションを展開します", - "GameListContextMenuCreateShortcut": "アプリケーションのショートカットを作成", - "GameListContextMenuCreateShortcutToolTip": "選択したアプリケーションを起動するデスクトップショートカットを作成します", - "GameListContextMenuCreateShortcutToolTipMacOS": "選択したアプリケーションを起動する ショートカットを macOS の Applications フォルダに作成します", - "GameListContextMenuOpenModsDirectory": "Modディレクトリを開く", - "GameListContextMenuOpenModsDirectoryToolTip": "アプリケーションの Mod データを格納するディレクトリを開きます", - "GameListContextMenuOpenSdModsDirectory": "Atmosphere Mods ディレクトリを開く", - "GameListContextMenuOpenSdModsDirectoryToolTip": "アプリケーションの Mod データを格納する SD カードの Atmosphere ディレクトリを開きます. 実際のハードウェア用に作成された Mod データに有用です.", - "StatusBarGamesLoaded": "{0}/{1} ゲーム", - "StatusBarSystemVersion": "システムバージョン: {0}", - "LinuxVmMaxMapCountDialogTitle": "メモリマッピング上限値が小さすぎます", - "LinuxVmMaxMapCountDialogTextPrimary": "vm.max_map_count の値を {0}に増やしますか?", - "LinuxVmMaxMapCountDialogTextSecondary": "ゲームによっては, 現在許可されているサイズより大きなメモリマッピングを作成しようとすることがあります. この制限を超えると, Ryjinx はすぐにクラッシュします.", - "LinuxVmMaxMapCountDialogButtonUntilRestart": "はい, 次回再起動まで", - "LinuxVmMaxMapCountDialogButtonPersistent": "はい, 恒久的に", - "LinuxVmMaxMapCountWarningTextPrimary": "メモリマッピングの最大量が推奨値よりも小さいです.", - "LinuxVmMaxMapCountWarningTextSecondary": "vm.max_map_count の現在値 {0} は {1} よりも小さいです. ゲームによっては現在許可されている値よりも大きなメモリマッピングを作成しようとする場合があります. 上限を越えた場合, Ryujinx はクラッシュします.", - "Settings": "設定", - "SettingsTabGeneral": "ユーザインタフェース", - "SettingsTabGeneralGeneral": "一般", - "SettingsTabGeneralEnableDiscordRichPresence": "Discord リッチプレゼンスを有効にする", - "SettingsTabGeneralCheckUpdatesOnLaunch": "起動時にアップデートを確認する", - "SettingsTabGeneralShowConfirmExitDialog": "\"終了を確認\" ダイアログを表示する", - "SettingsTabGeneralRememberWindowState": "Remember Window Size/Position", - "SettingsTabGeneralHideCursor": "マウスカーソルを非表示", - "SettingsTabGeneralHideCursorNever": "決して", - "SettingsTabGeneralHideCursorOnIdle": "アイドル時", - "SettingsTabGeneralHideCursorAlways": "常時", - "SettingsTabGeneralGameDirectories": "ゲームディレクトリ", - "SettingsTabGeneralAdd": "追加", - "SettingsTabGeneralRemove": "削除", - "SettingsTabSystem": "システム", - "SettingsTabSystemCore": "コア", - "SettingsTabSystemSystemRegion": "地域:", - "SettingsTabSystemSystemRegionJapan": "日本", - "SettingsTabSystemSystemRegionUSA": "アメリカ", - "SettingsTabSystemSystemRegionEurope": "ヨーロッパ", - "SettingsTabSystemSystemRegionAustralia": "オーストラリア", - "SettingsTabSystemSystemRegionChina": "中国", - "SettingsTabSystemSystemRegionKorea": "韓国", - "SettingsTabSystemSystemRegionTaiwan": "台湾", - "SettingsTabSystemSystemLanguage": "言語:", - "SettingsTabSystemSystemLanguageJapanese": "日本語", - "SettingsTabSystemSystemLanguageAmericanEnglish": "英語(アメリカ)", - "SettingsTabSystemSystemLanguageFrench": "フランス語", - "SettingsTabSystemSystemLanguageGerman": "ドイツ語", - "SettingsTabSystemSystemLanguageItalian": "イタリア語", - "SettingsTabSystemSystemLanguageSpanish": "スペイン語", - "SettingsTabSystemSystemLanguageChinese": "中国語", - "SettingsTabSystemSystemLanguageKorean": "韓国語", - "SettingsTabSystemSystemLanguageDutch": "オランダ語", - "SettingsTabSystemSystemLanguagePortuguese": "ポルトガル語", - "SettingsTabSystemSystemLanguageRussian": "ロシア語", - "SettingsTabSystemSystemLanguageTaiwanese": "台湾語", - "SettingsTabSystemSystemLanguageBritishEnglish": "英語(イギリス)", - "SettingsTabSystemSystemLanguageCanadianFrench": "フランス語(カナダ)", - "SettingsTabSystemSystemLanguageLatinAmericanSpanish": "スペイン語(ラテンアメリカ)", - "SettingsTabSystemSystemLanguageSimplifiedChinese": "中国語", - "SettingsTabSystemSystemLanguageTraditionalChinese": "台湾語", - "SettingsTabSystemSystemTimeZone": "タイムゾーン:", - "SettingsTabSystemSystemTime": "時刻:", - "SettingsTabSystemEnableVsync": "VSync", - "SettingsTabSystemEnablePptc": "PPTC (Profiled Persistent Translation Cache)", - "SettingsTabSystemEnableFsIntegrityChecks": "ファイルシステム整合性チェック", - "SettingsTabSystemAudioBackend": "音声バックエンド:", - "SettingsTabSystemAudioBackendDummy": "ダミー", - "SettingsTabSystemAudioBackendOpenAL": "OpenAL", - "SettingsTabSystemAudioBackendSoundIO": "SoundIO", - "SettingsTabSystemAudioBackendSDL2": "SDL2", - "SettingsTabSystemHacks": "ハック", - "SettingsTabSystemHacksNote": " (挙動が不安定になる可能性があります)", - "SettingsTabSystemExpandDramSize": "DRAMサイズを6GiBに拡大する", - "SettingsTabSystemIgnoreMissingServices": "未実装サービスを無視する", - "SettingsTabGraphics": "グラフィックス", - "SettingsTabGraphicsAPI": "グラフィックスAPI", - "SettingsTabGraphicsEnableShaderCache": "シェーダーキャッシュを有効にする", - "SettingsTabGraphicsAnisotropicFiltering": "異方性フィルタリング:", - "SettingsTabGraphicsAnisotropicFilteringAuto": "自動", - "SettingsTabGraphicsAnisotropicFiltering2x": "2x", - "SettingsTabGraphicsAnisotropicFiltering4x": "4x", - "SettingsTabGraphicsAnisotropicFiltering8x": "8x", - "SettingsTabGraphicsAnisotropicFiltering16x": "16x", - "SettingsTabGraphicsResolutionScale": "解像度:", - "SettingsTabGraphicsResolutionScaleCustom": "カスタム (非推奨)", - "SettingsTabGraphicsResolutionScaleNative": "ネイティブ (720p/1080p)", - "SettingsTabGraphicsResolutionScale2x": "2x (1440p/2160p)", - "SettingsTabGraphicsResolutionScale3x": "3x (2160p/3240p)", - "SettingsTabGraphicsResolutionScale4x": "4x (2880p/4320p) (非推奨)", - "SettingsTabGraphicsAspectRatio": "アスペクト比:", - "SettingsTabGraphicsAspectRatio4x3": "4:3", - "SettingsTabGraphicsAspectRatio16x9": "16:9", - "SettingsTabGraphicsAspectRatio16x10": "16:10", - "SettingsTabGraphicsAspectRatio21x9": "21:9", - "SettingsTabGraphicsAspectRatio32x9": "32:9", - "SettingsTabGraphicsAspectRatioStretch": "ウインドウサイズに合わせる", - "SettingsTabGraphicsDeveloperOptions": "開発者向けオプション", - "SettingsTabGraphicsShaderDumpPath": "グラフィックス シェーダー ダンプパス:", - "SettingsTabLogging": "ロギング", - "SettingsTabLoggingLogging": "ロギング", - "SettingsTabLoggingEnableLoggingToFile": "ファイルへのロギングを有効にする", - "SettingsTabLoggingEnableStubLogs": "Stub ログを有効にする", - "SettingsTabLoggingEnableInfoLogs": "Info ログを有効にする", - "SettingsTabLoggingEnableWarningLogs": "Warning ログを有効にする", - "SettingsTabLoggingEnableErrorLogs": "Error ログを有効にする", - "SettingsTabLoggingEnableTraceLogs": "Trace ログを有効にする", - "SettingsTabLoggingEnableGuestLogs": "Guest ログを有効にする", - "SettingsTabLoggingEnableFsAccessLogs": "Fs アクセスログを有効にする", - "SettingsTabLoggingFsGlobalAccessLogMode": "Fs グローバルアクセスログモード:", - "SettingsTabLoggingDeveloperOptions": "開発者オプション", - "SettingsTabLoggingDeveloperOptionsNote": "警告: パフォーマンスを低下させます", - "SettingsTabLoggingGraphicsBackendLogLevel": "グラフィックスバックエンド ログレベル:", - "SettingsTabLoggingGraphicsBackendLogLevelNone": "なし", - "SettingsTabLoggingGraphicsBackendLogLevelError": "エラー", - "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "パフォーマンス低下", - "SettingsTabLoggingGraphicsBackendLogLevelAll": "すべて", - "SettingsTabLoggingEnableDebugLogs": "デバッグログを有効にする", - "SettingsTabInput": "入力", - "SettingsTabInputEnableDockedMode": "ドッキングモード", - "SettingsTabInputDirectKeyboardAccess": "キーボード直接アクセス", - "SettingsButtonSave": "セーブ", - "SettingsButtonClose": "閉じる", - "SettingsButtonOk": "OK", - "SettingsButtonCancel": "キャンセル", - "SettingsButtonApply": "適用", - "ControllerSettingsPlayer": "プレイヤー", - "ControllerSettingsPlayer1": "プレイヤー 1", - "ControllerSettingsPlayer2": "プレイヤー 2", - "ControllerSettingsPlayer3": "プレイヤー 3", - "ControllerSettingsPlayer4": "プレイヤー 4", - "ControllerSettingsPlayer5": "プレイヤー 5", - "ControllerSettingsPlayer6": "プレイヤー 6", - "ControllerSettingsPlayer7": "プレイヤー 7", - "ControllerSettingsPlayer8": "プレイヤー 8", - "ControllerSettingsHandheld": "携帯", - "ControllerSettingsInputDevice": "入力デバイス", - "ControllerSettingsRefresh": "更新", - "ControllerSettingsDeviceDisabled": "無効", - "ControllerSettingsControllerType": "コントローラ種別", - "ControllerSettingsControllerTypeHandheld": "携帯", - "ControllerSettingsControllerTypeProController": "Pro コントローラ", - "ControllerSettingsControllerTypeJoyConPair": "JoyCon ペア", - "ControllerSettingsControllerTypeJoyConLeft": "JoyCon 左", - "ControllerSettingsControllerTypeJoyConRight": "JoyCon 右", - "ControllerSettingsProfile": "プロファイル", - "ControllerSettingsProfileDefault": "デフォルト", - "ControllerSettingsLoad": "ロード", - "ControllerSettingsAdd": "追加", - "ControllerSettingsRemove": "削除", - "ControllerSettingsButtons": "ボタン", - "ControllerSettingsButtonA": "A", - "ControllerSettingsButtonB": "B", - "ControllerSettingsButtonX": "X", - "ControllerSettingsButtonY": "Y", - "ControllerSettingsButtonPlus": "+", - "ControllerSettingsButtonMinus": "-", - "ControllerSettingsDPad": "十字キー", - "ControllerSettingsDPadUp": "上", - "ControllerSettingsDPadDown": "下", - "ControllerSettingsDPadLeft": "左", - "ControllerSettingsDPadRight": "右", - "ControllerSettingsStickButton": "ボタン", - "ControllerSettingsStickUp": "上", - "ControllerSettingsStickDown": "下", - "ControllerSettingsStickLeft": "左", - "ControllerSettingsStickRight": "右", - "ControllerSettingsStickStick": "スティック", - "ControllerSettingsStickInvertXAxis": "X軸を反転", - "ControllerSettingsStickInvertYAxis": "Y軸を反転", - "ControllerSettingsStickDeadzone": "遊び:", - "ControllerSettingsLStick": "左スティック", - "ControllerSettingsRStick": "右スティック", - "ControllerSettingsTriggersLeft": "左トリガー", - "ControllerSettingsTriggersRight": "右トリガー", - "ControllerSettingsTriggersButtonsLeft": "左トリガーボタン", - "ControllerSettingsTriggersButtonsRight": "右トリガーボタン", - "ControllerSettingsTriggers": "トリガー", - "ControllerSettingsTriggerL": "L", - "ControllerSettingsTriggerR": "R", - "ControllerSettingsTriggerZL": "ZL", - "ControllerSettingsTriggerZR": "ZR", - "ControllerSettingsLeftSL": "SL", - "ControllerSettingsLeftSR": "SR", - "ControllerSettingsRightSL": "SL", - "ControllerSettingsRightSR": "SR", - "ControllerSettingsExtraButtonsLeft": "左ボタン", - "ControllerSettingsExtraButtonsRight": "右ボタン", - "ControllerSettingsMisc": "その他", - "ControllerSettingsTriggerThreshold": "トリガーしきい値:", - "ControllerSettingsMotion": "モーション", - "ControllerSettingsMotionUseCemuhookCompatibleMotion": "CemuHook 互換モーションを使用", - "ControllerSettingsMotionControllerSlot": "コントローラ スロット:", - "ControllerSettingsMotionMirrorInput": "入力反転", - "ControllerSettingsMotionRightJoyConSlot": "JoyCon 右 スロット:", - "ControllerSettingsMotionServerHost": "サーバ:", - "ControllerSettingsMotionGyroSensitivity": "ジャイロ感度:", - "ControllerSettingsMotionGyroDeadzone": "ジャイロ遊び:", - "ControllerSettingsSave": "セーブ", - "ControllerSettingsClose": "閉じる", - "KeyUnknown": "Unknown", - "KeyShiftLeft": "Shift Left", - "KeyShiftRight": "Shift Right", - "KeyControlLeft": "Ctrl Left", - "KeyMacControlLeft": "⌃ Left", - "KeyControlRight": "Ctrl Right", - "KeyMacControlRight": "⌃ Right", - "KeyAltLeft": "Alt Left", - "KeyMacAltLeft": "⌥ Left", - "KeyAltRight": "Alt Right", - "KeyMacAltRight": "⌥ Right", - "KeyWinLeft": "⊞ Left", - "KeyMacWinLeft": "⌘ Left", - "KeyWinRight": "⊞ Right", - "KeyMacWinRight": "⌘ Right", - "KeyMenu": "Menu", - "KeyUp": "Up", - "KeyDown": "Down", - "KeyLeft": "Left", - "KeyRight": "Right", - "KeyEnter": "Enter", - "KeyEscape": "Escape", - "KeySpace": "Space", - "KeyTab": "Tab", - "KeyBackSpace": "Backspace", - "KeyInsert": "Insert", - "KeyDelete": "Delete", - "KeyPageUp": "Page Up", - "KeyPageDown": "Page Down", - "KeyHome": "Home", - "KeyEnd": "End", - "KeyCapsLock": "Caps Lock", - "KeyScrollLock": "Scroll Lock", - "KeyPrintScreen": "Print Screen", - "KeyPause": "Pause", - "KeyNumLock": "Num Lock", - "KeyClear": "Clear", - "KeyKeypad0": "Keypad 0", - "KeyKeypad1": "Keypad 1", - "KeyKeypad2": "Keypad 2", - "KeyKeypad3": "Keypad 3", - "KeyKeypad4": "Keypad 4", - "KeyKeypad5": "Keypad 5", - "KeyKeypad6": "Keypad 6", - "KeyKeypad7": "Keypad 7", - "KeyKeypad8": "Keypad 8", - "KeyKeypad9": "Keypad 9", - "KeyKeypadDivide": "Keypad Divide", - "KeyKeypadMultiply": "Keypad Multiply", - "KeyKeypadSubtract": "Keypad Subtract", - "KeyKeypadAdd": "Keypad Add", - "KeyKeypadDecimal": "Keypad Decimal", - "KeyKeypadEnter": "Keypad Enter", - "KeyNumber0": "0", - "KeyNumber1": "1", - "KeyNumber2": "2", - "KeyNumber3": "3", - "KeyNumber4": "4", - "KeyNumber5": "5", - "KeyNumber6": "6", - "KeyNumber7": "7", - "KeyNumber8": "8", - "KeyNumber9": "9", - "KeyTilde": "~", - "KeyGrave": "`", - "KeyMinus": "-", - "KeyPlus": "+", - "KeyBracketLeft": "[", - "KeyBracketRight": "]", - "KeySemicolon": ";", - "KeyQuote": "\"", - "KeyComma": ",", - "KeyPeriod": ".", - "KeySlash": "/", - "KeyBackSlash": "\\", - "KeyUnbound": "Unbound", - "GamepadLeftStick": "L Stick Button", - "GamepadRightStick": "R Stick Button", - "GamepadLeftShoulder": "Left Shoulder", - "GamepadRightShoulder": "Right Shoulder", - "GamepadLeftTrigger": "Left Trigger", - "GamepadRightTrigger": "Right Trigger", - "GamepadDpadUp": "Up", - "GamepadDpadDown": "Down", - "GamepadDpadLeft": "Left", - "GamepadDpadRight": "Right", - "GamepadMinus": "-", - "GamepadPlus": "+", - "GamepadGuide": "Guide", - "GamepadMisc1": "Misc", - "GamepadPaddle1": "Paddle 1", - "GamepadPaddle2": "Paddle 2", - "GamepadPaddle3": "Paddle 3", - "GamepadPaddle4": "Paddle 4", - "GamepadTouchpad": "Touchpad", - "GamepadSingleLeftTrigger0": "Left Trigger 0", - "GamepadSingleRightTrigger0": "Right Trigger 0", - "GamepadSingleLeftTrigger1": "Left Trigger 1", - "GamepadSingleRightTrigger1": "Right Trigger 1", - "StickLeft": "Left Stick", - "StickRight": "Right Stick", - "UserProfilesSelectedUserProfile": "選択されたユーザプロファイル:", - "UserProfilesSaveProfileName": "プロファイル名をセーブ", - "UserProfilesChangeProfileImage": "プロファイル画像を変更", - "UserProfilesAvailableUserProfiles": "利用可能なユーザプロファイル:", - "UserProfilesAddNewProfile": "プロファイルを作成", - "UserProfilesDelete": "削除", - "UserProfilesClose": "閉じる", - "ProfileNameSelectionWatermark": "ニックネームを選択", - "ProfileImageSelectionTitle": "プロファイル画像選択", - "ProfileImageSelectionHeader": "プロファイル画像を選択", - "ProfileImageSelectionNote": "カスタム画像をインポート, またはファームウェア内のアバターを選択できます", - "ProfileImageSelectionImportImage": "画像ファイルをインポート", - "ProfileImageSelectionSelectAvatar": "ファームウェア内のアバターを選択", - "InputDialogTitle": "入力ダイアログ", - "InputDialogOk": "OK", - "InputDialogCancel": "キャンセル", - "InputDialogAddNewProfileTitle": "プロファイル名を選択", - "InputDialogAddNewProfileHeader": "プロファイル名を入力してください", - "InputDialogAddNewProfileSubtext": "(最大長: {0})", - "AvatarChoose": "選択", - "AvatarSetBackgroundColor": "背景色を指定", - "AvatarClose": "閉じる", - "ControllerSettingsLoadProfileToolTip": "プロファイルをロード", - "ControllerSettingsAddProfileToolTip": "プロファイルを追加", - "ControllerSettingsRemoveProfileToolTip": "プロファイルを削除", - "ControllerSettingsSaveProfileToolTip": "プロファイルをセーブ", - "MenuBarFileToolsTakeScreenshot": "スクリーンショットを撮影", - "MenuBarFileToolsHideUi": "UIを隠す", - "GameListContextMenuRunApplication": "アプリケーションを実行", - "GameListContextMenuToggleFavorite": "お気に入りを切り替え", - "GameListContextMenuToggleFavoriteToolTip": "ゲームをお気に入りに含めるかどうかを切り替えます", - "SettingsTabGeneralTheme": "テーマ:", - "SettingsTabGeneralThemeDark": "ダーク", - "SettingsTabGeneralThemeLight": "ライト", - "ControllerSettingsConfigureGeneral": "設定", - "ControllerSettingsRumble": "振動", - "ControllerSettingsRumbleStrongMultiplier": "強振動の補正値", - "ControllerSettingsRumbleWeakMultiplier": "弱振動の補正値", - "DialogMessageSaveNotAvailableMessage": "{0} [{1:x16}] のセーブデータはありません", - "DialogMessageSaveNotAvailableCreateSaveMessage": "このゲームのセーブデータを作成してよろしいですか?", - "DialogConfirmationTitle": "Ryujinx - 確認", - "DialogUpdaterTitle": "Ryujinx - アップデータ", - "DialogErrorTitle": "Ryujinx - エラー", - "DialogWarningTitle": "Ryujinx - 警告", - "DialogExitTitle": "Ryujinx - 終了", - "DialogErrorMessage": "エラーが発生しました", - "DialogExitMessage": "Ryujinx を閉じてよろしいですか?", - "DialogExitSubMessage": "セーブされていないデータはすべて失われます!", - "DialogMessageCreateSaveErrorMessage": "セーブデータ: {0} の作成中にエラーが発生しました", - "DialogMessageFindSaveErrorMessage": "セーブデータ: {0} の検索中にエラーが発生しました", - "FolderDialogExtractTitle": "展開フォルダを選択", - "DialogNcaExtractionMessage": "{1} から {0} セクションを展開中...", - "DialogNcaExtractionTitle": "Ryujinx - NCA セクション展開", - "DialogNcaExtractionMainNcaNotFoundErrorMessage": "展開に失敗しました. 選択されたファイルにはメイン NCA が存在しません.", - "DialogNcaExtractionCheckLogErrorMessage": "展開に失敗しました. 詳細はログを確認してください.", - "DialogNcaExtractionSuccessMessage": "展開が正常終了しました", - "DialogUpdaterConvertFailedMessage": "現在の Ryujinx バージョンの変換に失敗しました.", - "DialogUpdaterCancelUpdateMessage": "アップデータをキャンセル中!", - "DialogUpdaterAlreadyOnLatestVersionMessage": "最新バージョンの Ryujinx を使用中です!", - "DialogUpdaterFailedToGetVersionMessage": "Github からのリリース情報取得時にエラーが発生しました. Github Actions でリリースファイルを作成中かもしれません. 後ほどもう一度試してみてください.", - "DialogUpdaterConvertFailedGithubMessage": "Github から取得した Ryujinx バージョンの変換に失敗しました.", - "DialogUpdaterDownloadingMessage": "アップデートをダウンロード中...", - "DialogUpdaterExtractionMessage": "アップデートを展開中...", - "DialogUpdaterRenamingMessage": "アップデートをリネーム中...", - "DialogUpdaterAddingFilesMessage": "新規アップデートを追加中...", - "DialogUpdaterCompleteMessage": "アップデート完了!", - "DialogUpdaterRestartMessage": "すぐに Ryujinx を再起動しますか?", - "DialogUpdaterNoInternetMessage": "インターネットに接続されていません!", - "DialogUpdaterNoInternetSubMessage": "インターネット接続が正常動作しているか確認してください!", - "DialogUpdaterDirtyBuildMessage": "Dirty ビルドの Ryujinx はアップデートできません!", - "DialogUpdaterDirtyBuildSubMessage": "サポートされているバージョンをお探しなら, https://ryujinx.org/ で Ryujinx をダウンロードしてください.", - "DialogRestartRequiredMessage": "再起動が必要", - "DialogThemeRestartMessage": "テーマがセーブされました. テーマを適用するには再起動が必要です.", - "DialogThemeRestartSubMessage": "再起動しますか", - "DialogFirmwareInstallEmbeddedMessage": "このゲームに含まれるファームウェアをインストールしてよろしいですか? (ファームウェア {0})", - "DialogFirmwareInstallEmbeddedSuccessMessage": "ファームウェアがインストールされていませんが, ゲームに含まれるファームウェア {0} をインストールできます.\nエミュレータが開始します.", - "DialogFirmwareNoFirmwareInstalledMessage": "ファームウェアがインストールされていません", - "DialogFirmwareInstalledMessage": "ファームウェア {0} がインストールされました", - "DialogInstallFileTypesSuccessMessage": "ファイル形式のインストールに成功しました!", - "DialogInstallFileTypesErrorMessage": "ファイル形式のインストールに失敗しました.", - "DialogUninstallFileTypesSuccessMessage": "ファイル形式のアンインストールに成功しました!", - "DialogUninstallFileTypesErrorMessage": "ファイル形式のアンインストールに失敗しました.", - "DialogOpenSettingsWindowLabel": "設定ウインドウを開く", - "DialogControllerAppletTitle": "コントローラアプレット", - "DialogMessageDialogErrorExceptionMessage": "メッセージダイアログ表示エラー: {0}", - "DialogSoftwareKeyboardErrorExceptionMessage": "ソフトウェアキーボード表示エラー: {0}", - "DialogErrorAppletErrorExceptionMessage": "エラーアプレットダイアログ表示エラー: {0}", - "DialogUserErrorDialogMessage": "{0}: {1}", - "DialogUserErrorDialogInfoMessage": "\nこのエラーへの対処方法については, セットアップガイドを参照してください.", - "DialogUserErrorDialogTitle": "Ryujinx エラー ({0})", - "DialogAmiiboApiTitle": "Amiibo API", - "DialogAmiiboApiFailFetchMessage": "API からの情報取得中にエラーが発生しました.", - "DialogAmiiboApiConnectErrorMessage": "Amiibo API サーバに接続できませんでした. サーバがダウンしているか, インターネット接続に問題があるかもしれません.", - "DialogProfileInvalidProfileErrorMessage": "プロファイル {0} は現在の入力設定システムと互換性がありません.", - "DialogProfileDefaultProfileOverwriteErrorMessage": "デフォルトのプロファイルは上書きできません", - "DialogProfileDeleteProfileTitle": "プロファイルを削除中", - "DialogProfileDeleteProfileMessage": "このアクションは元に戻せません. 本当に続けてよろしいですか?", - "DialogWarning": "警告", - "DialogPPTCDeletionMessage": "次回起動時に PPTC を再構築します:\n\n{0}\n\n実行してよろしいですか?", - "DialogPPTCDeletionErrorMessage": "PPTC キャッシュ破棄エラー {0}: {1}", - "DialogShaderDeletionMessage": "シェーダーキャッシュを破棄しようとしています:\n\n{0}\n\n実行してよろしいですか?", - "DialogShaderDeletionErrorMessage": "シェーダーキャッシュ破棄エラー {0}: {1}", - "DialogRyujinxErrorMessage": "エラーが発生しました", - "DialogInvalidTitleIdErrorMessage": "UI エラー: 選択されたゲームは有効なタイトル ID を保持していません", - "DialogFirmwareInstallerFirmwareNotFoundErrorMessage": "{0} には有効なシステムファームウェアがありません.", - "DialogFirmwareInstallerFirmwareInstallTitle": "ファームウェア {0} をインストール", - "DialogFirmwareInstallerFirmwareInstallMessage": "システムバージョン {0} がインストールされます.", - "DialogFirmwareInstallerFirmwareInstallSubMessage": "\n\n現在のシステムバージョン {0} を置き換えます.", - "DialogFirmwareInstallerFirmwareInstallConfirmMessage": "\n\n続けてよろしいですか?", - "DialogFirmwareInstallerFirmwareInstallWaitMessage": "ファームウェアをインストール中...", - "DialogFirmwareInstallerFirmwareInstallSuccessMessage": "システムバージョン {0} が正常にインストールされました.", - "DialogUserProfileDeletionWarningMessage": "選択されたプロファイルを削除すると,プロファイルがひとつも存在しなくなります", - "DialogUserProfileDeletionConfirmMessage": "選択されたプロファイルを削除しますか", - "DialogUserProfileUnsavedChangesTitle": "警告 - 保存されていない変更", - "DialogUserProfileUnsavedChangesMessage": "保存されていないユーザプロファイルを変更しました.", - "DialogUserProfileUnsavedChangesSubMessage": "変更を破棄しますか?", - "DialogControllerSettingsModifiedConfirmMessage": "現在のコントローラ設定が更新されました.", - "DialogControllerSettingsModifiedConfirmSubMessage": "セーブしますか?", - "DialogLoadFileErrorMessage": "{0}. エラー発生ファイル: {1}", - "DialogModAlreadyExistsMessage": "Modはすでに存在します", - "DialogModInvalidMessage": "指定したディレクトリにはmodが含まれていません!", - "DialogModDeleteNoParentMessage": "削除に失敗しました: Mod \"{0}\" の親ディレクトリが見つかりませんでした!", - "DialogDlcNoDlcErrorMessage": "選択されたファイルはこのタイトル用の DLC ではありません!", - "DialogPerformanceCheckLoggingEnabledMessage": "トレースロギングを有効にします. これは開発者のみに有用な機能です.", - "DialogPerformanceCheckLoggingEnabledConfirmMessage": "パフォーマンス最適化のためには,トレースロギングを無効にすることを推奨します. トレースロギングを無効にしてよろしいですか?", - "DialogPerformanceCheckShaderDumpEnabledMessage": "シェーダーダンプを有効にします. これは開発者のみに有用な機能です.", - "DialogPerformanceCheckShaderDumpEnabledConfirmMessage": "パフォーマンス最適化のためには, シェーダーダンプを無効にすることを推奨します. シェーダーダンプを無効にしてよろしいですか?", - "DialogLoadAppGameAlreadyLoadedMessage": "ゲームはすでにロード済みです", - "DialogLoadAppGameAlreadyLoadedSubMessage": "別のゲームを起動する前に, エミュレーションを中止またはエミュレータを閉じてください.", - "DialogUpdateAddUpdateErrorMessage": "選択されたファイルはこのタイトル用のアップデートではありません!", - "DialogSettingsBackendThreadingWarningTitle": "警告 - バックエンドスレッディング", - "DialogSettingsBackendThreadingWarningMessage": "このオプションの変更を完全に適用するには Ryujinx の再起動が必要です. プラットフォームによっては, Ryujinx のものを使用する前に手動でドライバ自身のマルチスレッディングを無効にする必要があるかもしれません.", - "DialogModManagerDeletionWarningMessage": "以下のModを削除しようとしています: {0}\n\n続行してもよろしいですか?", - "DialogModManagerDeletionAllWarningMessage": "このタイトルの Mod をすべて削除しようとしています.\n\n続行してもよろしいですか?", - "SettingsTabGraphicsFeaturesOptions": "機能", - "SettingsTabGraphicsBackendMultithreading": "グラフィックスバックエンドのマルチスレッド実行:", - "CommonAuto": "自動", - "CommonOff": "オフ", - "CommonOn": "オン", - "InputDialogYes": "はい", - "InputDialogNo": "いいえ", - "DialogProfileInvalidProfileNameErrorMessage": "プロファイル名に無効な文字が含まれています. 再度試してみてください.", - "MenuBarOptionsPauseEmulation": "一時停止", - "MenuBarOptionsResumeEmulation": "再開", - "AboutUrlTooltipMessage": "クリックするとデフォルトのブラウザで Ryujinx のウェブサイトを開きます.", - "AboutDisclaimerMessage": "Ryujinx は Nintendo™ および\nそのパートナー企業とは一切関係ありません.", - "AboutAmiiboDisclaimerMessage": "AmiiboAPI (www.amiiboapi.com) は\nAmiibo エミュレーションに使用されています.", - "AboutPatreonUrlTooltipMessage": "クリックするとデフォルトのブラウザで Ryujinx の Patreon ページを開きます.", - "AboutGithubUrlTooltipMessage": "クリックするとデフォルトのブラウザで Ryujinx の Github ページを開きます.", - "AboutDiscordUrlTooltipMessage": "クリックするとデフォルトのブラウザで Ryujinx の Discord サーバを開きます.", - "AboutTwitterUrlTooltipMessage": "クリックするとデフォルトのブラウザで Ryujinx の Twitter ページを開きます.", - "AboutRyujinxAboutTitle": "Ryujinx について:", - "AboutRyujinxAboutContent": "Ryujinx は Nintendo Switch™ のエミュレータです.\nPatreon で私達の活動を支援してください.\n最新の情報は Twitter または Discord から取得できます.\n貢献したい開発者の方は GitHub または Discord で詳細をご確認ください.", - "AboutRyujinxMaintainersTitle": "開発者:", - "AboutRyujinxMaintainersContentTooltipMessage": "クリックするとデフォルトのブラウザで 貢献者のページを開きます.", - "AboutRyujinxSupprtersTitle": "Patreon での支援者:", - "AmiiboSeriesLabel": "Amiibo シリーズ", - "AmiiboCharacterLabel": "キャラクタ", - "AmiiboScanButtonLabel": "スキャン", - "AmiiboOptionsShowAllLabel": "すべての Amiibo を表示", - "AmiiboOptionsUsRandomTagLabel": "ハック: ランダムな Uuid を使用", - "DlcManagerTableHeadingEnabledLabel": "有効", - "DlcManagerTableHeadingTitleIdLabel": "タイトルID", - "DlcManagerTableHeadingContainerPathLabel": "コンテナパス", - "DlcManagerTableHeadingFullPathLabel": "フルパス", - "DlcManagerRemoveAllButton": "すべて削除", - "DlcManagerEnableAllButton": "すべて有効", - "DlcManagerDisableAllButton": "すべて無効", - "ModManagerDeleteAllButton": "すべて削除", - "MenuBarOptionsChangeLanguage": "言語を変更", - "MenuBarShowFileTypes": "ファイル形式を表示", - "CommonSort": "並べ替え", - "CommonShowNames": "名称を表示", - "CommonFavorite": "お気に入り", - "OrderAscending": "昇順", - "OrderDescending": "降順", - "SettingsTabGraphicsFeatures": "機能", - "ErrorWindowTitle": "エラーウインドウ", - "ToggleDiscordTooltip": "Discord の \"現在プレイ中\" アクティビティに Ryujinx を表示するかどうかを選択します", - "AddGameDirBoxTooltip": "リストに追加するゲームディレクトリを入力します", - "AddGameDirTooltip": "リストにゲームディレクトリを追加します", - "RemoveGameDirTooltip": "選択したゲームディレクトリを削除します", - "CustomThemeCheckTooltip": "エミュレータのメニュー外観を変更するためカスタム Avalonia テーマを使用します", - "CustomThemePathTooltip": "カスタム GUI テーマのパスです", - "CustomThemeBrowseTooltip": "カスタム GUI テーマを参照します", - "DockModeToggleTooltip": "有効にすると,ドッキングされた Nintendo Switch をエミュレートします.多くのゲームではグラフィックス品質が向上します.\n無効にすると,携帯モードの Nintendo Switch をエミュレートします.グラフィックスの品質は低下します.\n\nドッキングモード有効ならプレイヤー1の,無効なら携帯の入力を設定してください.\n\nよくわからない場合はオンのままにしてください.", - "DirectKeyboardTooltip": "直接キーボード アクセス (HID) のサポートです. テキスト入力デバイスとしてキーボードへのゲームアクセスを提供します.\n\nSwitchハードウェアでキーボードの使用をネイティブにサポートしているゲームでのみ動作します.\n\nわからない場合はオフのままにしてください.", - "DirectMouseTooltip": "直接マウスアクセス (HID) のサポートです. ポインティングデバイスとしてマウスへのゲームアクセスを提供します.\n\nSwitchハードウェアでマウスの使用をネイティブにサポートしているゲームでのみ動作します.\n\n有効にしている場合, タッチスクリーン機能は動作しない場合があります.\n\nわからない場合はオフのままにしてください.", - "RegionTooltip": "システムの地域を変更します", - "LanguageTooltip": "システムの言語を変更します", - "TimezoneTooltip": "システムのタイムゾーンを変更します", - "TimeTooltip": "システムの時刻を変更します", - "VSyncToggleTooltip": "エミュレートされたゲーム機の垂直同期です. 多くのゲームにおいて, フレームリミッタとして機能します. 無効にすると, ゲームが高速で実行されたり, ロード中に時間がかかったり, 止まったりすることがあります.\n\n設定したホットキー(デフォルトではF1)で, ゲーム内で切り替え可能です. 無効にする場合は, この操作を行うことをおすすめします.\n\nよくわからない場合はオンのままにしてください.", - "PptcToggleTooltip": "翻訳されたJIT関数をセーブすることで, ゲームをロードするたびに毎回翻訳する処理を不要とします.\n\n一度ゲームを起動すれば,二度目以降の起動時遅延を大きく軽減できます.\n\nよくわからない場合はオンのままにしてください.", - "FsIntegrityToggleTooltip": "ゲーム起動時にファイル破損をチェックし,破損が検出されたらログにハッシュエラーを表示します..\n\nパフォーマンスには影響なく, トラブルシューティングに役立ちます.\n\nよくわからない場合はオンのままにしてください.", - "AudioBackendTooltip": "音声レンダリングに使用するバックエンドを変更します.\n\nSDL2 が優先され, OpenAL と SoundIO はフォールバックとして使用されます. ダミーは音声出力しません.\n\nよくわからない場合は SDL2 を設定してください.", - "MemoryManagerTooltip": "ゲストメモリのマップ/アクセス方式を変更します. エミュレートされるCPUのパフォーマンスに大きな影響を与えます.\n\nよくわからない場合は「ホスト,チェックなし」を設定してください.", - "MemoryManagerSoftwareTooltip": "アドレス変換にソフトウェアページテーブルを使用します. 非常に正確ですがパフォーマンスが大きく低下します.", - "MemoryManagerHostTooltip": "ホストのアドレス空間にメモリを直接マップします.JITのコンパイルと実行速度が大きく向上します.", - "MemoryManagerUnsafeTooltip": "メモリを直接マップしますが, アクセス前にゲストのアドレス空間内のアドレスをマスクしません. より高速になりますが, 安全性が犠牲になります. ゲストアプリケーションは Ryujinx のどこからでもメモリにアクセスできるので,このモードでは信頼できるプログラムだけを実行するようにしてください.", - "UseHypervisorTooltip": "JIT の代わりにハイパーバイザーを使用します. 利用可能な場合, パフォーマンスが大幅に向上しますが, 現在の状態では不安定になる可能性があります.", - "DRamTooltip": "エミュレートされたシステムのメモリ容量を 4GiB から 6GiB に増加します.\n\n高解像度のテクスチャパックや 4K解像度の mod を使用する場合に有用です. パフォーマンスを改善するものではありません.\n\nよくわからない場合はオフのままにしてください.", - "IgnoreMissingServicesTooltip": "未実装の Horizon OS サービスを無視します. 特定のゲームにおいて起動時のクラッシュを回避できる場合があります.\n\nよくわからない場合はオフのままにしてください.", - "GraphicsBackendThreadingTooltip": "グラフィックスバックエンドのコマンドを別スレッドで実行します.\n\nシェーダのコンパイルを高速化し, 遅延を軽減し, マルチスレッド非対応の GPU ドライバにおいてパフォーマンスを改善します. マルチスレッド対応のドライバでも若干パフォーマンス改善が見られます.\n\nよくわからない場合は自動に設定してください.", - "GalThreadingTooltip": "グラフィックスバックエンドのコマンドを別スレッドで実行します.\n\nシェーダのコンパイルを高速化し, 遅延を軽減し, マルチスレッド非対応の GPU ドライバにおいてパフォーマンスを改善します. マルチスレッド対応のドライバでも若干パフォーマンス改善が見られます.\n\nよくわからない場合は自動に設定してください.", - "ShaderCacheToggleTooltip": "ディスクシェーダーキャッシュをセーブし,次回以降の実行時遅延を軽減します.\n\nよくわからない場合はオンのままにしてください.", - "ResolutionScaleTooltip": "ゲームのレンダリング解像度倍率を設定します.\n\n解像度を上げてもピクセルのように見えるゲームもあります. そのようなゲームでは, アンチエイリアスを削除するか, 内部レンダリング解像度を上げる mod を見つける必要があるかもしれません. その場合, ようなゲームでは、ネイティブを選択してください.\n\nこのオプションはゲーム実行中に下の「適用」をクリックすることで変更できます. 設定ウィンドウを脇に移動して, ゲームが好みの表示になるよう試してみてください.\n\nどのような設定でも, \"4x\" はやり過ぎであることを覚えておいてください.", - "ResolutionScaleEntryTooltip": "1.5 のような整数でない倍率を指定すると,問題が発生したりクラッシュしたりする場合があります.", - "AnisotropyTooltip": "異方性フィルタリングのレベルです. ゲームが要求する値を使用する場合は「自動」を設定してください.", - "AspectRatioTooltip": "レンダリングウインドウに適用するアスペクト比です.\n\nゲームにアスペクト比を変更する mod を使用している場合のみ変更してください.\n\nわからない場合は16:9のままにしておいてください.\n", - "ShaderDumpPathTooltip": "グラフィックス シェーダー ダンプのパスです", - "FileLogTooltip": "コンソール出力されるログをディスク上のログファイルにセーブします. パフォーマンスには影響を与えません.", - "StubLogTooltip": "stub ログメッセージをコンソールに出力します. パフォーマンスには影響を与えません.", - "InfoLogTooltip": "info ログメッセージをコンソールに出力します. パフォーマンスには影響を与えません.", - "WarnLogTooltip": "warning ログメッセージをコンソールに出力します. パフォーマンスには影響を与えません.", - "ErrorLogTooltip": "error ログメッセージをコンソールに出力します. パフォーマンスには影響を与えません.", - "TraceLogTooltip": "trace ログメッセージをコンソールに出力します. パフォーマンスには影響を与えません.", - "GuestLogTooltip": "guest ログメッセージをコンソールに出力します. パフォーマンスには影響を与えません.", - "FileAccessLogTooltip": "ファイルアクセスログメッセージをコンソールに出力します.", - "FSAccessLogModeTooltip": "コンソールへのファイルシステムアクセスログ出力を有効にします.0-3 のモードが有効です", - "DeveloperOptionTooltip": "使用上の注意", - "OpenGlLogLevel": "適切なログレベルを有効にする必要があります", - "DebugLogTooltip": "デバッグログメッセージをコンソールに出力します.\n\nログが読みづらくなり,エミュレータのパフォーマンスが低下するため,開発者から特別な指示がある場合のみ使用してください.", - "LoadApplicationFileTooltip": "ロードする Switch 互換のファイルを選択するためファイルエクスプローラを開きます", - "LoadApplicationFolderTooltip": "ロードする Switch 互換の展開済みアプリケーションを選択するためファイルエクスプローラを開きます", - "OpenRyujinxFolderTooltip": "Ryujinx ファイルシステムフォルダを開きます", - "OpenRyujinxLogsTooltip": "ログが格納されるフォルダを開きます", - "ExitTooltip": "Ryujinx を終了します", - "OpenSettingsTooltip": "設定ウインドウを開きます", - "OpenProfileManagerTooltip": "ユーザプロファイル管理ウインドウを開きます", - "StopEmulationTooltip": "ゲームのエミュレーションを中止してゲーム選択画面に戻ります", - "CheckUpdatesTooltip": "Ryujinx のアップデートを確認します", - "OpenAboutTooltip": "Ryujinx についてのウインドウを開きます", - "GridSize": "グリッドサイズ", - "GridSizeTooltip": "グリッドサイズを変更します", - "SettingsTabSystemSystemLanguageBrazilianPortuguese": "ポルトガル語(ブラジル)", - "AboutRyujinxContributorsButtonHeader": "すべての貢献者を確認", - "SettingsTabSystemAudioVolume": "音量: ", - "AudioVolumeTooltip": "音量を変更します", - "SettingsTabSystemEnableInternetAccess": "ゲストインターネットアクセス / LAN モード", - "EnableInternetAccessTooltip": "エミュレートしたアプリケーションをインターネットに接続できるようにします.\n\nLAN モードを持つゲーム同士は,この機能を有効にして同じアクセスポイントに接続すると接続できます. 実機も含まれます.\n\n任天堂のサーバーには接続できません. インターネットに接続しようとすると,特定のゲームでクラッシュすることがあります.\n\nよくわからない場合はオフのままにしてください.", - "GameListContextMenuManageCheatToolTip": "チートを管理します", - "GameListContextMenuManageCheat": "チートを管理", - "GameListContextMenuManageModToolTip": "Modを管理します", - "GameListContextMenuManageMod": "Manage Mods", - "ControllerSettingsStickRange": "範囲:", - "DialogStopEmulationTitle": "Ryujinx - エミュレーションを中止", - "DialogStopEmulationMessage": "エミュレーションを中止してよろしいですか?", - "SettingsTabCpu": "CPU", - "SettingsTabAudio": "音声", - "SettingsTabNetwork": "ネットワーク", - "SettingsTabNetworkConnection": "ネットワーク接続", - "SettingsTabCpuCache": "CPU キャッシュ", - "SettingsTabCpuMemory": "CPU メモリ", - "DialogUpdaterFlatpakNotSupportedMessage": "FlatHub を使用して Ryujinx をアップデートしてください.", - "UpdaterDisabledWarningTitle": "アップデータは無効です!", - "ControllerSettingsRotate90": "時計回りに 90° 回転", - "IconSize": "アイコンサイズ", - "IconSizeTooltip": "ゲームアイコンのサイズを変更します", - "MenuBarOptionsShowConsole": "コンソールを表示", - "ShaderCachePurgeError": "シェーダーキャッシュの破棄エラー {0}: {1}", - "UserErrorNoKeys": "Keys がありません", - "UserErrorNoFirmware": "ファームウェアがありません", - "UserErrorFirmwareParsingFailed": "ファームウェアのパーズエラー", - "UserErrorApplicationNotFound": "アプリケーションがありません", - "UserErrorUnknown": "不明なエラー", - "UserErrorUndefined": "未定義エラー", - "UserErrorNoKeysDescription": "'prod.keys' が見つかりませんでした", - "UserErrorNoFirmwareDescription": "インストールされたファームウェアが見つかりませんでした", - "UserErrorFirmwareParsingFailedDescription": "ファームウェアをパーズできませんでした.通常,古いキーが原因です.", - "UserErrorApplicationNotFoundDescription": "指定されたパスに有効なアプリケーションがありませんでした.", - "UserErrorUnknownDescription": "不明なエラーが発生しました!", - "UserErrorUndefinedDescription": "未定義のエラーが発生しました! 発生すべきものではないので,開発者にご連絡ください!", - "OpenSetupGuideMessage": "セットアップガイドを開く", - "NoUpdate": "アップデートなし", - "TitleUpdateVersionLabel": "バージョン {0} - {1}", - "RyujinxInfo": "Ryujinx - 情報", - "RyujinxConfirm": "Ryujinx - 確認", - "FileDialogAllTypes": "すべての種別", - "Never": "決して", - "SwkbdMinCharacters": "最低 {0} 文字必要です", - "SwkbdMinRangeCharacters": "{0}-{1} 文字にしてください", - "SoftwareKeyboard": "ソフトウェアキーボード", - "SoftwareKeyboardModeNumeric": "0-9 または '.' のみでなければなりません", - "SoftwareKeyboardModeAlphabet": "CJK文字以外のみ", - "SoftwareKeyboardModeASCII": "ASCII文字列のみ", - "ControllerAppletControllers": "サポートされているコントローラ:", - "ControllerAppletPlayers": "プレイヤー:", - "ControllerAppletDescription": "現在の設定は無効です. 設定を開いて入力を再設定してください.", - "ControllerAppletDocked": "ドッキングモードが設定されています. 携帯コントロールは無効にする必要があります.", - "UpdaterRenaming": "古いファイルをリネーム中...", - "UpdaterRenameFailed": "ファイルをリネームできませんでした: {0}", - "UpdaterAddingFiles": "新規ファイルを追加中...", - "UpdaterExtracting": "アップデートを展開中...", - "UpdaterDownloading": "アップデートをダウンロード中...", - "Game": "ゲーム", - "Docked": "ドッキング", - "Handheld": "携帯", - "ConnectionError": "接続エラー.", - "AboutPageDeveloperListMore": "{0}, その他大勢...", - "ApiError": "API エラー.", - "LoadingHeading": "ロード中: {0}", - "CompilingPPTC": "PTC をコンパイル中", - "CompilingShaders": "シェーダーをコンパイル中", - "AllKeyboards": "すべてのキーボード", - "OpenFileDialogTitle": "開くファイルを選択", - "OpenFolderDialogTitle": "展開されたゲームフォルダを選択", - "AllSupportedFormats": "すべての対応フォーマット", - "RyujinxUpdater": "Ryujinx アップデータ", - "SettingsTabHotkeys": "キーボード ホットキー", - "SettingsTabHotkeysHotkeys": "キーボード ホットキー", - "SettingsTabHotkeysToggleVsyncHotkey": "VSync 切り替え:", - "SettingsTabHotkeysScreenshotHotkey": "スクリーンショット:", - "SettingsTabHotkeysShowUiHotkey": "UI表示:", - "SettingsTabHotkeysPauseHotkey": "一時停止:", - "SettingsTabHotkeysToggleMuteHotkey": "ミュート:", - "ControllerMotionTitle": "モーションコントロール設定", - "ControllerRumbleTitle": "振動設定", - "SettingsSelectThemeFileDialogTitle": "テーマファイルを選択", - "SettingsXamlThemeFile": "Xaml テーマファイル", - "AvatarWindowTitle": "アカウント - アバター管理", - "Amiibo": "Amiibo", - "Unknown": "不明", - "Usage": "使用法", - "Writable": "書き込み可能", - "SelectDlcDialogTitle": "DLC ファイルを選択", - "SelectUpdateDialogTitle": "アップデートファイルを選択", - "SelectModDialogTitle": "modディレクトリを選択", - "UserProfileWindowTitle": "ユーザプロファイルを管理", - "CheatWindowTitle": "チート管理", - "DlcWindowTitle": "DLC 管理", - "ModWindowTitle": "Manage Mods for {0} ({1})", - "UpdateWindowTitle": "アップデート管理", - "CheatWindowHeading": "利用可能なチート {0} [{1}]", - "BuildId": "ビルドID:", - "DlcWindowHeading": "利用可能な DLC {0} [{1}]", - "ModWindowHeading": "{0} Mod(s)", - "UserProfilesEditProfile": "編集", - "Cancel": "キャンセル", - "Save": "セーブ", - "Discard": "破棄", - "Paused": "一時停止中", - "UserProfilesSetProfileImage": "プロファイル画像を設定", - "UserProfileEmptyNameError": "名称が必要です", - "UserProfileNoImageError": "プロファイル画像が必要です", - "GameUpdateWindowHeading": "利用可能なアップデート {0} [{1}]", - "SettingsTabHotkeysResScaleUpHotkey": "解像度を上げる:", - "SettingsTabHotkeysResScaleDownHotkey": "解像度を下げる:", - "UserProfilesName": "名称:", - "UserProfilesUserId": "ユーザID:", - "SettingsTabGraphicsBackend": "グラフィックスバックエンド", - "SettingsTabGraphicsBackendTooltip": "エミュレーションに使用するグラフィックスバックエンドを選択します.\n\nVulkanは, 最近のグラフィックカードでドライバが最新であれば, 全体的に優れています. すべてのGPUベンダーで, シェーダーコンパイルがより高速で, スタッタリングが少ないのが特徴です.\n\n古いNvidia GPU, Linuxでの古いAMD GPU, VRAMの少ないGPUなどでは, OpenGLの方が良い結果が得られるかもしれません. ですが, シェーダーコンパイルのスタッターは大きくなります.\n\n不明な場合はVulkanに設定してください。最新のグラフィックドライバでもVulkanをサポートしていないGPUの場合は, OpenGLに設定してください.", - "SettingsEnableTextureRecompression": "テクスチャの再圧縮を有効にする", - "SettingsEnableTextureRecompressionTooltip": "VRAM使用量を減らすためにASTCテクスチャを圧縮します.\n\nこのテクスチャフォーマットを使用するゲームには, Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder, The Legend of Zelda: Tears of the Kingdomが含まれます.\n\nVRAMが4GB以下のグラフィックカードでは, これらのゲームを実行中にクラッシュする可能性があります.\n\n前述のゲームでVRAMが不足している場合のみ有効にしてください. 不明な場合はオフにしてください.", - "SettingsTabGraphicsPreferredGpu": "優先使用するGPU", - "SettingsTabGraphicsPreferredGpuTooltip": "Vulkanグラフィックスバックエンドで使用されるグラフィックスカードを選択します.\n\nOpenGLが使用するGPUには影響しません.\n\n不明な場合は, \"dGPU\" としてフラグが立っているGPUに設定します. ない場合はそのままにします.", - "SettingsAppRequiredRestartMessage": "Ryujinx の再起動が必要です", - "SettingsGpuBackendRestartMessage": "グラフィックスバックエンドまたはGPUの設定が変更されました. 変更を適用するには再起動する必要があります", - "SettingsGpuBackendRestartSubMessage": "今すぐ再起動しますか?", - "RyujinxUpdaterMessage": "Ryujinx を最新版にアップデートしますか?", - "SettingsTabHotkeysVolumeUpHotkey": "音量を上げる:", - "SettingsTabHotkeysVolumeDownHotkey": "音量を下げる:", - "SettingsEnableMacroHLE": "マクロの高レベルエミュレーション (HLE) を有効にする", - "SettingsEnableMacroHLETooltip": "GPU マクロコードの高レベルエミュレーションです.\n\nパフォーマンスを向上させますが, 一部のゲームでグラフィックに不具合が発生する可能性があります.\n\nよくわからない場合はオンのままにしてください.", - "SettingsEnableColorSpacePassthrough": "色空間をパススルー", - "SettingsEnableColorSpacePassthroughTooltip": "Vulkan バックエンドに対して, 色空間を指定せずに色情報を渡します. 高色域ディスプレイを使用する場合, 正確ではないですがより鮮やかな色になる可能性があります.", - "VolumeShort": "音量", - "UserProfilesManageSaves": "セーブデータの管理", - "DeleteUserSave": "このゲームのユーザセーブデータを削除しますか?", - "IrreversibleActionNote": "この操作は元に戻せません.", - "SaveManagerHeading": "{0} のセーブデータを管理", - "SaveManagerTitle": "セーブデータマネージャ", - "Name": "名称", - "Size": "サイズ", - "Search": "検索", - "UserProfilesRecoverLostAccounts": "アカウントの復旧", - "Recover": "復旧", - "UserProfilesRecoverHeading": "以下のアカウントのセーブデータが見つかりました", - "UserProfilesRecoverEmptyList": "復元するプロファイルはありません", - "GraphicsAATooltip": "ゲームレンダリングにアンチエイリアスを適用します.\n\nFXAAは画像の大部分をぼかし, SMAAはギザギザのエッジを見つけて滑らかにします.\n\nFSRスケーリングフィルタとの併用は推奨しません.\n\nこのオプションは, ゲーム実行中に下の「適用」をクリックして変更できます. 設定ウィンドウを脇に移動し, ゲームが好みの表示になるように試してみてください.\n\n不明な場合は「なし」のままにしておいてください.", - "GraphicsAALabel": "アンチエイリアス:", - "GraphicsScalingFilterLabel": "スケーリングフィルタ:", - "GraphicsScalingFilterTooltip": "解像度変更時に適用されるスケーリングフィルタを選択します.\n\nBilinearは3Dゲームに適しており, 安全なデフォルトオプションです.\n\nピクセルアートゲームにはNearestを推奨します.\n\nFSR 1.0は単なるシャープニングフィルタであり, FXAAやSMAAとの併用は推奨されません.\n\nこのオプションは, ゲーム実行中に下の「適用」をクリックすることで変更できます. 設定ウィンドウを脇に移動し, ゲームが好みの表示になるように試してみてください.\n\n不明な場合はBilinearのままにしておいてください.", - "GraphicsScalingFilterBilinear": "Bilinear", - "GraphicsScalingFilterNearest": "Nearest", - "GraphicsScalingFilterFsr": "FSR", - "GraphicsScalingFilterLevelLabel": "レベル", - "GraphicsScalingFilterLevelTooltip": "FSR 1.0のシャープ化レベルを設定します. 高い値ほどシャープになります.", - "SmaaLow": "SMAA Low", - "SmaaMedium": "SMAA Medium", - "SmaaHigh": "SMAA High", - "SmaaUltra": "SMAA Ultra", - "UserEditorTitle": "ユーザを編集", - "UserEditorTitleCreate": "ユーザを作成", - "SettingsTabNetworkInterface": "ネットワークインタフェース:", - "NetworkInterfaceTooltip": "LAN/LDN機能に使用されるネットワークインタフェースです.\n\nVPNやXLink Kai、LAN対応のゲームと併用することで, インターネット上の同一ネットワーク接続になりすますことができます.\n\n不明な場合はデフォルトのままにしてください.", - "NetworkInterfaceDefault": "デフォルト", - "PackagingShaders": "シェーダーを構築中", - "AboutChangelogButton": "GitHub で更新履歴を表示", - "AboutChangelogButtonTooltipMessage": "クリックして, このバージョンの更新履歴をデフォルトのブラウザで開きます.", - "SettingsTabNetworkMultiplayer": "マルチプレイヤー", - "MultiplayerMode": "モード:", - "MultiplayerModeTooltip": "LDNマルチプレイヤーモードを変更します.\n\nldn_mitmモジュールがインストールされた, 他のRyujinxインスタンスや,ハックされたNintendo Switchコンソールとのローカル/同一ネットワーク接続を可能にします.\n\nマルチプレイでは, すべてのプレイヤーが同じゲームバージョンである必要があります(例:Super Smash Bros. Ultimate v13.0.1はv13.0.0に接続できません).\n\n不明な場合は「無効」のままにしてください.", - "MultiplayerModeDisabled": "無効", - "MultiplayerModeLdnMitm": "ldn_mitm" -} diff --git a/src/Ryujinx/Assets/Locales/ko_KR.json b/src/Ryujinx/Assets/Locales/ko_KR.json deleted file mode 100644 index a92d084e0..000000000 --- a/src/Ryujinx/Assets/Locales/ko_KR.json +++ /dev/null @@ -1,780 +0,0 @@ -{ - "Language": "한국어", - "MenuBarFileOpenApplet": "애플릿 열기", - "MenuBarFileOpenAppletOpenMiiAppletToolTip": "독립 실행형 모드로 Mii 편집기 애플릿 열기", - "SettingsTabInputDirectMouseAccess": "다이렉트 마우스 접근", - "SettingsTabSystemMemoryManagerMode": "메모리 관리자 모드:", - "SettingsTabSystemMemoryManagerModeSoftware": "소프트웨어", - "SettingsTabSystemMemoryManagerModeHost": "호스트 (빠름)", - "SettingsTabSystemMemoryManagerModeHostUnchecked": "호스트 확인 안함 (가장 빠르나 안전하지 않음)", - "SettingsTabSystemUseHypervisor": "하이퍼바이저 사용하기", - "MenuBarFile": "_파일", - "MenuBarFileOpenFromFile": "_파일에서 응용 프로그램 불러오기", - "MenuBarFileOpenUnpacked": "_압축을 푼 게임 불러오기", - "MenuBarFileOpenEmuFolder": "Ryujinx 폴더 열기", - "MenuBarFileOpenLogsFolder": "로그 폴더 열기", - "MenuBarFileExit": "_종료", - "MenuBarOptions": "옵션(_O)", - "MenuBarOptionsToggleFullscreen": "전체화면 전환", - "MenuBarOptionsStartGamesInFullscreen": "전체 화면 모드에서 게임 시작", - "MenuBarOptionsStopEmulation": "에뮬레이션 중지", - "MenuBarOptionsSettings": "_설정", - "MenuBarOptionsManageUserProfiles": "_사용자 프로파일 관리", - "MenuBarActions": "_동작", - "MenuBarOptionsSimulateWakeUpMessage": "깨우기 메시지 시뮬레이션", - "MenuBarActionsScanAmiibo": "Amiibo 스캔", - "MenuBarTools": "_도구", - "MenuBarToolsInstallFirmware": "펌웨어 설치", - "MenuBarFileToolsInstallFirmwareFromFile": "XCI 또는 ZIP에서 펌웨어 설치", - "MenuBarFileToolsInstallFirmwareFromDirectory": "디렉터리에서 펌웨어 설치", - "MenuBarToolsManageFileTypes": "파일 형식 관리", - "MenuBarToolsInstallFileTypes": "파일 형식 설치", - "MenuBarToolsUninstallFileTypes": "파일 형식 설치 제거", - "MenuBarView": "_보기", - "MenuBarViewWindow": "창 크기", - "MenuBarViewWindow720": "720p", - "MenuBarViewWindow1080": "1080p", - "MenuBarHelp": "도움말(_H)", - "MenuBarHelpCheckForUpdates": "업데이트 확인", - "MenuBarHelpAbout": "정보", - "MenuSearch": "검색...", - "GameListHeaderFavorite": "즐겨찾기", - "GameListHeaderIcon": "아이콘", - "GameListHeaderApplication": "이름", - "GameListHeaderDeveloper": "개발자", - "GameListHeaderVersion": "버전", - "GameListHeaderTimePlayed": "플레이 시간", - "GameListHeaderLastPlayed": "마지막 플레이", - "GameListHeaderFileExtension": "파일 확장자", - "GameListHeaderFileSize": "파일 크기", - "GameListHeaderPath": "경로", - "GameListContextMenuOpenUserSaveDirectory": "사용자 저장 디렉터리 열기", - "GameListContextMenuOpenUserSaveDirectoryToolTip": "응용프로그램의 사용자 저장이 포함된 디렉터리 열기", - "GameListContextMenuOpenDeviceSaveDirectory": "사용자 장치 디렉토리 열기", - "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "응용프로그램의 장치 저장이 포함된 디렉터리 열기", - "GameListContextMenuOpenBcatSaveDirectory": "BCAT 저장 디렉터리 열기", - "GameListContextMenuOpenBcatSaveDirectoryToolTip": "응용프로그램의 BCAT 저장이 포함된 디렉터리 열기", - "GameListContextMenuManageTitleUpdates": "타이틀 업데이트 관리", - "GameListContextMenuManageTitleUpdatesToolTip": "타이틀 업데이트 관리 창 열기", - "GameListContextMenuManageDlc": "DLC 관리", - "GameListContextMenuManageDlcToolTip": "DLC 관리 창 열기", - "GameListContextMenuCacheManagement": "캐시 관리", - "GameListContextMenuCacheManagementPurgePptc": "대기열 PPTC 재구성", - "GameListContextMenuCacheManagementPurgePptcToolTip": "다음 게임 시작에서 부팅 시 PPTC가 다시 빌드하도록 트리거", - "GameListContextMenuCacheManagementPurgeShaderCache": "셰이더 캐시 제거", - "GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "응용프로그램 셰이더 캐시 삭제\n", - "GameListContextMenuCacheManagementOpenPptcDirectory": "PPTC 디렉터리 열기", - "GameListContextMenuCacheManagementOpenPptcDirectoryToolTip": "응용프로그램 PPTC 캐시가 포함된 디렉터리 열기", - "GameListContextMenuCacheManagementOpenShaderCacheDirectory": "셰이더 캐시 디렉터리 열기", - "GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip": "응용프로그램 셰이더 캐시가 포함된 디렉터리 열기", - "GameListContextMenuExtractData": "데이터 추출", - "GameListContextMenuExtractDataExeFS": "ExeFS", - "GameListContextMenuExtractDataExeFSToolTip": "응용프로그램의 현재 구성에서 ExeFS 추출 (업데이트 포함)", - "GameListContextMenuExtractDataRomFS": "RomFS", - "GameListContextMenuExtractDataRomFSToolTip": "응용 프로그램의 현재 구성에서 RomFS 추출 (업데이트 포함)", - "GameListContextMenuExtractDataLogo": "로고", - "GameListContextMenuExtractDataLogoToolTip": "응용프로그램의 현재 구성에서 로고 섹션 추출 (업데이트 포함)", - "GameListContextMenuCreateShortcut": "애플리케이션 바로 가기 만들기", - "GameListContextMenuCreateShortcutToolTip": "선택한 애플리케이션을 실행하는 바탕 화면 바로 가기를 만듭니다.", - "GameListContextMenuCreateShortcutToolTipMacOS": "해당 게임을 실행할 수 있는 바로가기를 macOS의 응용 프로그램 폴더에 추가합니다.", - "GameListContextMenuOpenModsDirectory": "Mod 디렉터리 열기", - "GameListContextMenuOpenModsDirectoryToolTip": "해당 게임의 Mod가 저장된 디렉터리 열기", - "GameListContextMenuOpenSdModsDirectory": "Atmosphere Mod 디렉터리 열기", - "GameListContextMenuOpenSdModsDirectoryToolTip": "해당 게임의 Mod가 포함된 대체 SD 카드 Atmosphere 디렉터리를 엽니다. 실제 하드웨어용으로 패키징된 Mod에 유용합니다.", - "StatusBarGamesLoaded": "{0}/{1}개의 게임 불러옴", - "StatusBarSystemVersion": "시스템 버전 : {0}", - "LinuxVmMaxMapCountDialogTitle": "감지된 메모리 매핑의 하한선", - "LinuxVmMaxMapCountDialogTextPrimary": "vm.max_map_count의 값을 {0}으로 늘리시겠습니까?", - "LinuxVmMaxMapCountDialogTextSecondary": "일부 게임은 현재 허용된 것보다 더 많은 메모리 매핑을 생성하려고 시도할 수 있습니다. 이 제한을 초과하는 즉시 Ryujinx에 문제가 발생합니다.", - "LinuxVmMaxMapCountDialogButtonUntilRestart": "예, 다음에 다시 시작할 때까지", - "LinuxVmMaxMapCountDialogButtonPersistent": "예, 영구적으로", - "LinuxVmMaxMapCountWarningTextPrimary": "메모리 매핑의 최대 용량이 권장 용량보다 적습니다.", - "LinuxVmMaxMapCountWarningTextSecondary": "vm.max_map_count({0})의 현재 값이 {1}보다 낮습니다. 일부 게임은 현재 허용된 것보다 더 많은 메모리 매핑을 생성하려고 시도할 수 있습니다. 이 제한을 초과하는 즉시 Ryujinx에 문제가 발생합니다.\n\n수동으로 제한을 늘리거나 Ryujinx의 도움을 받을 수 있는 pkexec을 설치하는 것이 좋습니다.", - "Settings": "설정", - "SettingsTabGeneral": "사용자 인터페이스", - "SettingsTabGeneralGeneral": "일반", - "SettingsTabGeneralEnableDiscordRichPresence": "디스코드 활동 상태 활성화", - "SettingsTabGeneralCheckUpdatesOnLaunch": "시작 시, 업데이트 확인", - "SettingsTabGeneralShowConfirmExitDialog": "\"종료 확인\" 대화 상자 표시", - "SettingsTabGeneralRememberWindowState": "창 크기/위치 기억", - "SettingsTabGeneralHideCursor": "마우스 커서 숨기기", - "SettingsTabGeneralHideCursorNever": "절대 안 함", - "SettingsTabGeneralHideCursorOnIdle": "유휴 상태", - "SettingsTabGeneralHideCursorAlways": "언제나", - "SettingsTabGeneralGameDirectories": "게임 디렉터리", - "SettingsTabGeneralAdd": "추가", - "SettingsTabGeneralRemove": "제거", - "SettingsTabSystem": "시스템", - "SettingsTabSystemCore": "코어", - "SettingsTabSystemSystemRegion": "시스템 지역:", - "SettingsTabSystemSystemRegionJapan": "일본", - "SettingsTabSystemSystemRegionUSA": "미국", - "SettingsTabSystemSystemRegionEurope": "유럽", - "SettingsTabSystemSystemRegionAustralia": "호주", - "SettingsTabSystemSystemRegionChina": "중국", - "SettingsTabSystemSystemRegionKorea": "한국", - "SettingsTabSystemSystemRegionTaiwan": "대만", - "SettingsTabSystemSystemLanguage": "시스템 언어 :", - "SettingsTabSystemSystemLanguageJapanese": "일본어", - "SettingsTabSystemSystemLanguageAmericanEnglish": "영어(미국)", - "SettingsTabSystemSystemLanguageFrench": "프랑스어", - "SettingsTabSystemSystemLanguageGerman": "독일어", - "SettingsTabSystemSystemLanguageItalian": "이탈리아어", - "SettingsTabSystemSystemLanguageSpanish": "스페인어", - "SettingsTabSystemSystemLanguageChinese": "중국어", - "SettingsTabSystemSystemLanguageKorean": "한국어", - "SettingsTabSystemSystemLanguageDutch": "네덜란드어", - "SettingsTabSystemSystemLanguagePortuguese": "포르투갈어", - "SettingsTabSystemSystemLanguageRussian": "러시아어", - "SettingsTabSystemSystemLanguageTaiwanese": "대만어", - "SettingsTabSystemSystemLanguageBritishEnglish": "영어(영국)", - "SettingsTabSystemSystemLanguageCanadianFrench": "프랑스어(캐나다)", - "SettingsTabSystemSystemLanguageLatinAmericanSpanish": "스페인어(라틴 아메리카)", - "SettingsTabSystemSystemLanguageSimplifiedChinese": "중국어 간체", - "SettingsTabSystemSystemLanguageTraditionalChinese": "중국어 번체", - "SettingsTabSystemSystemTimeZone": "시스템 시간대:", - "SettingsTabSystemSystemTime": "시스템 시간:", - "SettingsTabSystemEnableVsync": "수직 동기화", - "SettingsTabSystemEnablePptc": "PPTC(프로파일된 영구 번역 캐시)", - "SettingsTabSystemEnableFsIntegrityChecks": "파일 시스템 무결성 검사", - "SettingsTabSystemAudioBackend": "음향 후단부 :", - "SettingsTabSystemAudioBackendDummy": "더미", - "SettingsTabSystemAudioBackendOpenAL": "OpenAL", - "SettingsTabSystemAudioBackendSoundIO": "사운드IO", - "SettingsTabSystemAudioBackendSDL2": "SDL2", - "SettingsTabSystemHacks": "해킹", - "SettingsTabSystemHacksNote": "불안정성을 유발할 수 있음", - "SettingsTabSystemExpandDramSize": "대체 메모리 레이아웃 사용(개발자)", - "SettingsTabSystemIgnoreMissingServices": "누락된 서비스 무시", - "SettingsTabGraphics": "그래픽", - "SettingsTabGraphicsAPI": "그래픽 API", - "SettingsTabGraphicsEnableShaderCache": "셰이더 캐시 활성화", - "SettingsTabGraphicsAnisotropicFiltering": "이방성 필터링 :", - "SettingsTabGraphicsAnisotropicFilteringAuto": "자동", - "SettingsTabGraphicsAnisotropicFiltering2x": "2배", - "SettingsTabGraphicsAnisotropicFiltering4x": "4배", - "SettingsTabGraphicsAnisotropicFiltering8x": "8배", - "SettingsTabGraphicsAnisotropicFiltering16x": "16배", - "SettingsTabGraphicsResolutionScale": "해상도 배율 :", - "SettingsTabGraphicsResolutionScaleCustom": "사용자 정의(권장하지 않음)", - "SettingsTabGraphicsResolutionScaleNative": "원본(720p/1080p)", - "SettingsTabGraphicsResolutionScale2x": "2배(1440p/2160p)", - "SettingsTabGraphicsResolutionScale3x": "3배(2160p/3240p)", - "SettingsTabGraphicsResolutionScale4x": "4x (2880p/4320p) (권장하지 않음)", - "SettingsTabGraphicsAspectRatio": "종횡비 :", - "SettingsTabGraphicsAspectRatio4x3": "4:3", - "SettingsTabGraphicsAspectRatio16x9": "16:9", - "SettingsTabGraphicsAspectRatio16x10": "16:10", - "SettingsTabGraphicsAspectRatio21x9": "21:9", - "SettingsTabGraphicsAspectRatio32x9": "32:9", - "SettingsTabGraphicsAspectRatioStretch": "창에 맞게 늘리기", - "SettingsTabGraphicsDeveloperOptions": "개발자 옵션", - "SettingsTabGraphicsShaderDumpPath": "그래픽 셰이더 덤프 경로 :", - "SettingsTabLogging": "로그 기록", - "SettingsTabLoggingLogging": "로그 기록", - "SettingsTabLoggingEnableLoggingToFile": "파일에 로그 기록 활성화", - "SettingsTabLoggingEnableStubLogs": "스텁 로그 활성화", - "SettingsTabLoggingEnableInfoLogs": "정보 로그 활성화", - "SettingsTabLoggingEnableWarningLogs": "경고 로그 활성화", - "SettingsTabLoggingEnableErrorLogs": "오류 로그 활성화", - "SettingsTabLoggingEnableTraceLogs": "추적 로그 활성화", - "SettingsTabLoggingEnableGuestLogs": "게스트 로그 활성화", - "SettingsTabLoggingEnableFsAccessLogs": "Fs 접속 로그 활성화", - "SettingsTabLoggingFsGlobalAccessLogMode": "Fs 전역 접속 로그 모드 :", - "SettingsTabLoggingDeveloperOptions": "개발자 옵션", - "SettingsTabLoggingDeveloperOptionsNote": "경고: 성능이 저하됨", - "SettingsTabLoggingGraphicsBackendLogLevel": "그래픽 후단부 로그 수준 :", - "SettingsTabLoggingGraphicsBackendLogLevelNone": "없음", - "SettingsTabLoggingGraphicsBackendLogLevelError": "오류", - "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "느려짐", - "SettingsTabLoggingGraphicsBackendLogLevelAll": "모두", - "SettingsTabLoggingEnableDebugLogs": "디버그 로그 활성화", - "SettingsTabInput": "입력", - "SettingsTabInputEnableDockedMode": "도킹 모드", - "SettingsTabInputDirectKeyboardAccess": "직접 키보드 접속", - "SettingsButtonSave": "저장", - "SettingsButtonClose": "닫기", - "SettingsButtonOk": "확인", - "SettingsButtonCancel": "취소", - "SettingsButtonApply": "적용", - "ControllerSettingsPlayer": "플레이어", - "ControllerSettingsPlayer1": "플레이어 1", - "ControllerSettingsPlayer2": "플레이어 2", - "ControllerSettingsPlayer3": "플레이어 3", - "ControllerSettingsPlayer4": "플레이어 4", - "ControllerSettingsPlayer5": "플레이어 5", - "ControllerSettingsPlayer6": "플레이어 6", - "ControllerSettingsPlayer7": "플레이어 7", - "ControllerSettingsPlayer8": "플레이어 8", - "ControllerSettingsHandheld": "휴대 모드", - "ControllerSettingsInputDevice": "입력 장치", - "ControllerSettingsRefresh": "새로 고침", - "ControllerSettingsDeviceDisabled": "비활성화됨", - "ControllerSettingsControllerType": "컨트롤러 유형", - "ControllerSettingsControllerTypeHandheld": "휴대 모드", - "ControllerSettingsControllerTypeProController": "프로 컨트롤러", - "ControllerSettingsControllerTypeJoyConPair": "조이콘 페어링", - "ControllerSettingsControllerTypeJoyConLeft": "좌측 조이콘", - "ControllerSettingsControllerTypeJoyConRight": "우측 조이콘", - "ControllerSettingsProfile": "프로필", - "ControllerSettingsProfileDefault": "기본", - "ControllerSettingsLoad": "불러오기", - "ControllerSettingsAdd": "추가", - "ControllerSettingsRemove": "제거", - "ControllerSettingsButtons": "버튼", - "ControllerSettingsButtonA": "A", - "ControllerSettingsButtonB": "B", - "ControllerSettingsButtonX": "X", - "ControllerSettingsButtonY": "Y", - "ControllerSettingsButtonPlus": "+", - "ControllerSettingsButtonMinus": "-", - "ControllerSettingsDPad": "방향 패드", - "ControllerSettingsDPadUp": "↑", - "ControllerSettingsDPadDown": "↓", - "ControllerSettingsDPadLeft": "←", - "ControllerSettingsDPadRight": "→", - "ControllerSettingsStickButton": "버튼", - "ControllerSettingsStickUp": "↑", - "ControllerSettingsStickDown": "↓", - "ControllerSettingsStickLeft": "←", - "ControllerSettingsStickRight": "→", - "ControllerSettingsStickStick": "스틱", - "ControllerSettingsStickInvertXAxis": "스틱 X 축 반전", - "ControllerSettingsStickInvertYAxis": "스틱 Y 축 반전", - "ControllerSettingsStickDeadzone": "사각지대 :", - "ControllerSettingsLStick": "좌측 스틱", - "ControllerSettingsRStick": "우측 스틱", - "ControllerSettingsTriggersLeft": "좌측 트리거", - "ControllerSettingsTriggersRight": "우측 트리거", - "ControllerSettingsTriggersButtonsLeft": "좌측 트리거 버튼", - "ControllerSettingsTriggersButtonsRight": "우측 트리거 버튼", - "ControllerSettingsTriggers": "트리거 버튼", - "ControllerSettingsTriggerL": "L", - "ControllerSettingsTriggerR": "R", - "ControllerSettingsTriggerZL": "ZL", - "ControllerSettingsTriggerZR": "ZR", - "ControllerSettingsLeftSL": "SL", - "ControllerSettingsLeftSR": "SR", - "ControllerSettingsRightSL": "SL", - "ControllerSettingsRightSR": "SR", - "ControllerSettingsExtraButtonsLeft": "좌측 버튼", - "ControllerSettingsExtraButtonsRight": "우측 버튼", - "ControllerSettingsMisc": "기타", - "ControllerSettingsTriggerThreshold": "트리거 임계값 :", - "ControllerSettingsMotion": "동작", - "ControllerSettingsMotionUseCemuhookCompatibleMotion": "CemuHook 호환 모션 사용", - "ControllerSettingsMotionControllerSlot": "컨트롤러 슬롯 :", - "ControllerSettingsMotionMirrorInput": "미러 입력", - "ControllerSettingsMotionRightJoyConSlot": "우측 조이콘 슬롯 :", - "ControllerSettingsMotionServerHost": "서버 호스트 :", - "ControllerSettingsMotionGyroSensitivity": "자이로 감도 :", - "ControllerSettingsMotionGyroDeadzone": "자이로 사각지대 :", - "ControllerSettingsSave": "저장", - "ControllerSettingsClose": "닫기", - "KeyUnknown": "알 수 없음", - "KeyShiftLeft": "왼쪽 Shift", - "KeyShiftRight": "오른쪽 Shift", - "KeyControlLeft": "왼쪽 Ctrl", - "KeyMacControlLeft": "왼쪽 ^", - "KeyControlRight": "오른쪽 Ctrl", - "KeyMacControlRight": "오른쪽 ^", - "KeyAltLeft": "왼쪽 Alt", - "KeyMacAltLeft": "왼쪽 ⌥", - "KeyAltRight": "오른쪽 Alt", - "KeyMacAltRight": "오른쪽 ⌥", - "KeyWinLeft": "왼쪽 ⊞", - "KeyMacWinLeft": "왼쪽 ⌘", - "KeyWinRight": "오른쪽 ⊞", - "KeyMacWinRight": "오른쪽 ⌘", - "KeyMenu": "메뉴", - "KeyUp": "↑", - "KeyDown": "↓", - "KeyLeft": "←", - "KeyRight": "→", - "KeyEnter": "엔터", - "KeyEscape": "이스케이프", - "KeySpace": "스페이스", - "KeyTab": "탭", - "KeyBackSpace": "백스페이스", - "KeyInsert": "Ins", - "KeyDelete": "Del", - "KeyPageUp": "Page Up", - "KeyPageDown": "Page Down", - "KeyHome": "Home", - "KeyEnd": "End", - "KeyCapsLock": "Caps Lock", - "KeyScrollLock": "Scroll Lock", - "KeyPrintScreen": "프린트 스크린", - "KeyPause": "Pause", - "KeyNumLock": "Num Lock", - "KeyClear": "지우기", - "KeyKeypad0": "키패드 0", - "KeyKeypad1": "키패드 1", - "KeyKeypad2": "키패드 2", - "KeyKeypad3": "키패드 3", - "KeyKeypad4": "키패드 4", - "KeyKeypad5": "키패드 5", - "KeyKeypad6": "키패드 6", - "KeyKeypad7": "키패드 7", - "KeyKeypad8": "키패드 8", - "KeyKeypad9": "키패드 9", - "KeyKeypadDivide": "키패드 분할", - "KeyKeypadMultiply": "키패드 멀티플", - "KeyKeypadSubtract": "키패드 빼기", - "KeyKeypadAdd": "키패드 추가", - "KeyKeypadDecimal": "숫자 키패드", - "KeyKeypadEnter": "키패드 엔터", - "KeyNumber0": "0", - "KeyNumber1": "1", - "KeyNumber2": "2", - "KeyNumber3": "3", - "KeyNumber4": "4", - "KeyNumber5": "5", - "KeyNumber6": "6", - "KeyNumber7": "7", - "KeyNumber8": "8", - "KeyNumber9": "9", - "KeyTilde": "~", - "KeyGrave": "`", - "KeyMinus": "-", - "KeyPlus": "+", - "KeyBracketLeft": "[", - "KeyBracketRight": "]", - "KeySemicolon": ";", - "KeyQuote": "\"", - "KeyComma": ",", - "KeyPeriod": ".", - "KeySlash": "/", - "KeyBackSlash": "\\", - "KeyUnbound": "바인딩 해제", - "GamepadLeftStick": "L 스틱 버튼", - "GamepadRightStick": "R 스틱 버튼", - "GamepadLeftShoulder": "좌측 숄더", - "GamepadRightShoulder": "우측 숄더", - "GamepadLeftTrigger": "좌측 트리거", - "GamepadRightTrigger": "우측 트리거", - "GamepadDpadUp": "↑", - "GamepadDpadDown": "↓", - "GamepadDpadLeft": "←", - "GamepadDpadRight": "→", - "GamepadMinus": "-", - "GamepadPlus": "+", - "GamepadGuide": "안내", - "GamepadMisc1": "기타", - "GamepadPaddle1": "패들 1", - "GamepadPaddle2": "패들 2", - "GamepadPaddle3": "패들 3", - "GamepadPaddle4": "패들 4", - "GamepadTouchpad": "터치패드", - "GamepadSingleLeftTrigger0": "왼쪽 트리거 0", - "GamepadSingleRightTrigger0": "오른쪽 트리거 0", - "GamepadSingleLeftTrigger1": "왼쪽 트리거 1", - "GamepadSingleRightTrigger1": "오른쪽 트리거 1", - "StickLeft": "좌측 스틱", - "StickRight": "우측 스틱", - "UserProfilesSelectedUserProfile": "선택한 사용자 프로필 :", - "UserProfilesSaveProfileName": "프로필 이름 저장", - "UserProfilesChangeProfileImage": "프로필 이미지 변경", - "UserProfilesAvailableUserProfiles": "사용 가능한 사용자 프로필 :", - "UserProfilesAddNewProfile": "프로필 생성", - "UserProfilesDelete": "삭제", - "UserProfilesClose": "닫기", - "ProfileNameSelectionWatermark": "닉네임을 입력하세요", - "ProfileImageSelectionTitle": "프로필 이미지 선택", - "ProfileImageSelectionHeader": "프로필 이미지 선택", - "ProfileImageSelectionNote": "사용자 지정 프로필 이미지를 가져오거나 시스템 펌웨어에서 아바타 선택 가능", - "ProfileImageSelectionImportImage": "이미지 파일 가져오기", - "ProfileImageSelectionSelectAvatar": "펌웨어 아바타 선택", - "InputDialogTitle": "입력 대화상자", - "InputDialogOk": "확인", - "InputDialogCancel": "취소", - "InputDialogAddNewProfileTitle": "프로필 이름 선택", - "InputDialogAddNewProfileHeader": "프로필 이름 입력", - "InputDialogAddNewProfileSubtext": "(최대 길이 : {0})", - "AvatarChoose": "선택", - "AvatarSetBackgroundColor": "배경색 설정", - "AvatarClose": "닫기", - "ControllerSettingsLoadProfileToolTip": "프로필 불러오기", - "ControllerSettingsAddProfileToolTip": "프로필 추가", - "ControllerSettingsRemoveProfileToolTip": "프로필 제거", - "ControllerSettingsSaveProfileToolTip": "프로필 저장", - "MenuBarFileToolsTakeScreenshot": "스크린 샷 찍기", - "MenuBarFileToolsHideUi": "UI 숨기기", - "GameListContextMenuRunApplication": "응용프로그램 실행", - "GameListContextMenuToggleFavorite": "즐겨찾기 전환", - "GameListContextMenuToggleFavoriteToolTip": "게임 즐겨찾기 상태 전환", - "SettingsTabGeneralTheme": "테마:", - "SettingsTabGeneralThemeDark": "어두운 테마", - "SettingsTabGeneralThemeLight": "밝은 테마", - "ControllerSettingsConfigureGeneral": "구성", - "ControllerSettingsRumble": "진동", - "ControllerSettingsRumbleStrongMultiplier": "강력한 진동 증폭기", - "ControllerSettingsRumbleWeakMultiplier": "약한 진동 증폭기", - "DialogMessageSaveNotAvailableMessage": "{0} [{1:x16}]에 대한 저장 데이터가 없음", - "DialogMessageSaveNotAvailableCreateSaveMessage": "이 게임에 대한 저장 데이터를 생성하겠습니까?", - "DialogConfirmationTitle": "Ryujinx - 확인", - "DialogUpdaterTitle": "Ryujinx - 업데이터", - "DialogErrorTitle": "Ryujinx - 오류", - "DialogWarningTitle": "Ryujinx - 경고", - "DialogExitTitle": "Ryujinx - 종료", - "DialogErrorMessage": "Ryujinx 오류 발생", - "DialogExitMessage": "Ryujinx를 종료하겠습니까?", - "DialogExitSubMessage": "저장하지 않은 모든 데이터는 손실됩니다!", - "DialogMessageCreateSaveErrorMessage": "지정된 저장 데이터를 작성하는 중에 오류 발생: {0}", - "DialogMessageFindSaveErrorMessage": "지정된 저장 데이터를 찾는 중에 오류 발생: {0}", - "FolderDialogExtractTitle": "추출할 폴더 선택", - "DialogNcaExtractionMessage": "{1}에서 {0} 섹션을 추출하는 중...", - "DialogNcaExtractionTitle": "Ryujinx - NCA 섹션 추출기", - "DialogNcaExtractionMainNcaNotFoundErrorMessage": "추출 실패하였습니다. 선택한 파일에 기본 NCA가 없습니다.", - "DialogNcaExtractionCheckLogErrorMessage": "추출 실패하였습니다. 자세한 내용은 로그 파일을 읽으세요.", - "DialogNcaExtractionSuccessMessage": "추출이 성공적으로 완료되었습니다.", - "DialogUpdaterConvertFailedMessage": "현재 Ryujinx 버전을 변환하지 못했습니다.", - "DialogUpdaterCancelUpdateMessage": "업데이트 취소 중 입니다!", - "DialogUpdaterAlreadyOnLatestVersionMessage": "이미 최신 버전의 Ryujinx를 사용하고 있습니다!", - "DialogUpdaterFailedToGetVersionMessage": "GitHub 릴리스에서 릴리스 정보를 가져오는 중에 오류가 발생했습니다. 이는 GitHub Actions에서 새 릴리스를 컴파일하는 경우 발생할 수 있습니다. 몇 분 후에 다시 시도하세요.", - "DialogUpdaterConvertFailedGithubMessage": "Github 개정에서 받은 Ryujinx 버전을 변환하지 못했습니다.", - "DialogUpdaterDownloadingMessage": "업데이트 다운로드 중...", - "DialogUpdaterExtractionMessage": "업데이트 추출 중...", - "DialogUpdaterRenamingMessage": "업데이트 이름 바꾸는 중...", - "DialogUpdaterAddingFilesMessage": "새 업데이트 추가 중...", - "DialogUpdaterCompleteMessage": "업데이트를 완료했습니다!", - "DialogUpdaterRestartMessage": "지금 Ryujinx를 다시 시작하겠습니까?", - "DialogUpdaterNoInternetMessage": "인터넷에 연결되어 있지 않습니다!", - "DialogUpdaterNoInternetSubMessage": "인터넷 연결이 작동하는지 확인하세요!", - "DialogUpdaterDirtyBuildMessage": "Ryujinx의 나쁜 빌드는 업데이트할 수 없습니다!\n", - "DialogUpdaterDirtyBuildSubMessage": "지원되는 버전을 찾고 있다면 https://ryujinx.org/에서 Ryujinx를 다운로드하세요.", - "DialogRestartRequiredMessage": "재시작 필요", - "DialogThemeRestartMessage": "테마가 저장되었습니다. 테마를 적용하려면 다시 시작해야 합니다.", - "DialogThemeRestartSubMessage": "다시 시작하겠습니까?", - "DialogFirmwareInstallEmbeddedMessage": "이 게임에 내장된 펌웨어를 설치하겠습니까? (펌웨어 {0})", - "DialogFirmwareInstallEmbeddedSuccessMessage": "설치된 펌웨어가 없지만 Ryujinx가 제공된 게임에서 펌웨어 {0}을(를) 설치할 수 있었습니다.\n이제 에뮬레이터가 시작됩니다.", - "DialogFirmwareNoFirmwareInstalledMessage": "설치된 펌웨어 없음", - "DialogFirmwareInstalledMessage": "펌웨어 {0}이(가) 설치됨", - "DialogInstallFileTypesSuccessMessage": "파일 형식을 성공적으로 설치했습니다!", - "DialogInstallFileTypesErrorMessage": "파일 형식을 설치하지 못했습니다.", - "DialogUninstallFileTypesSuccessMessage": "파일 형식을 성공적으로 제거했습니다!", - "DialogUninstallFileTypesErrorMessage": "파일 형식을 제거하지 못했습니다.", - "DialogOpenSettingsWindowLabel": "설정 창 열기", - "DialogControllerAppletTitle": "컨트롤러 애플릿", - "DialogMessageDialogErrorExceptionMessage": "메시지 대화상자를 표시하는 동안 오류 발생 : {0}", - "DialogSoftwareKeyboardErrorExceptionMessage": "소프트웨어 키보드를 표시하는 동안 오류 발생 : {0}", - "DialogErrorAppletErrorExceptionMessage": "오류에플릿 대화상자를 표시하는 동안 오류 발생 : {0}", - "DialogUserErrorDialogMessage": "{0}: {1}", - "DialogUserErrorDialogInfoMessage": "\n이 오류를 수정하는 방법에 대한 자세한 내용은 설정 가이드를 따르세요.", - "DialogUserErrorDialogTitle": "Ryuijnx 오류 ({0})", - "DialogAmiiboApiTitle": "Amiibo API", - "DialogAmiiboApiFailFetchMessage": "API에서 정보를 가져오는 동안 오류가 발생했습니다.", - "DialogAmiiboApiConnectErrorMessage": "Amiibo API 서버에 연결할 수 없습니다. 서비스가 다운되었거나 인터넷 연결이 온라인 상태인지 확인해야 할 수 있습니다.", - "DialogProfileInvalidProfileErrorMessage": "{0} 프로필은 현재 입력 구성 시스템과 호환되지 않습니다.", - "DialogProfileDefaultProfileOverwriteErrorMessage": "기본 프로필을 덮어쓸 수 없음", - "DialogProfileDeleteProfileTitle": "프로필 삭제", - "DialogProfileDeleteProfileMessage": "이 작업은 되돌릴 수 없습니다. 계속하겠습니까?", - "DialogWarning": "경고", - "DialogPPTCDeletionMessage": "다음 부팅 시, PPTC 재구축을 대기열에 추가 :\n\n{0}\n\n계속하겠습니까?", - "DialogPPTCDeletionErrorMessage": "{0}에서 PPTC 캐시 삭제 오류 : {1}", - "DialogShaderDeletionMessage": "다음에 대한 셰이더 캐시 삭제 :\n\n{0}\n\n계속하겠습니까?", - "DialogShaderDeletionErrorMessage": "{0}에서 셰이더 캐시 제거 오류 : {1}", - "DialogRyujinxErrorMessage": "Ryujinx에 오류 발생", - "DialogInvalidTitleIdErrorMessage": "UI 오류 : 선택한 게임에 유효한 타이틀 ID가 없음", - "DialogFirmwareInstallerFirmwareNotFoundErrorMessage": "{0}에서 유효한 시스템 펌웨어를 찾을 수 없습니다.", - "DialogFirmwareInstallerFirmwareInstallTitle": "펌웨어 {0} 설치", - "DialogFirmwareInstallerFirmwareInstallMessage": "시스템 버전 {0}이(가) 설치됩니다.", - "DialogFirmwareInstallerFirmwareInstallSubMessage": "\n\n이것은 현재 시스템 버전 {0}을(를) 대체합니다.", - "DialogFirmwareInstallerFirmwareInstallConfirmMessage": "\n\n계속하겠습니까?", - "DialogFirmwareInstallerFirmwareInstallWaitMessage": "펌웨어 설치 중...", - "DialogFirmwareInstallerFirmwareInstallSuccessMessage": "시스템 버전 {0}이(가) 성공적으로 설치되었습니다.", - "DialogUserProfileDeletionWarningMessage": "선택한 프로파일이 삭제되면 사용 가능한 다른 프로파일이 없음", - "DialogUserProfileDeletionConfirmMessage": "선택한 프로파일을 삭제하겠습니까?", - "DialogUserProfileUnsavedChangesTitle": "경고 - 변경사항 저장되지 않음", - "DialogUserProfileUnsavedChangesMessage": "저장되지 않은 사용자 프로파일을 수정했습니다.", - "DialogUserProfileUnsavedChangesSubMessage": "변경사항을 저장하지 않으시겠습니까?", - "DialogControllerSettingsModifiedConfirmMessage": "현재 컨트롤러 설정이 업데이트되었습니다.", - "DialogControllerSettingsModifiedConfirmSubMessage": "저장하겠습니까?", - "DialogLoadFileErrorMessage": "{0}. 오류 발생 파일 : {1}", - "DialogModAlreadyExistsMessage": "Mod가 이미 존재합니다.", - "DialogModInvalidMessage": "지정된 디렉터리에 Mod가 없습니다!", - "DialogModDeleteNoParentMessage": "삭제 실패: \"{0}\" Mod의 상위 디렉터리를 찾을 수 없습니다!", - "DialogDlcNoDlcErrorMessage": "지정된 파일에 선택한 타이틀에 대한 DLC가 포함되어 있지 않습니다!", - "DialogPerformanceCheckLoggingEnabledMessage": "개발자만 사용하도록 설계된 추적 로그 기록이 활성화되어 있습니다.", - "DialogPerformanceCheckLoggingEnabledConfirmMessage": "최적의 성능을 위해 추적 로그 생성을 비활성화하는 것이 좋습니다. 지금 추적 로그 기록을 비활성화하겠습니까?", - "DialogPerformanceCheckShaderDumpEnabledMessage": "개발자만 사용하도록 설계된 셰이더 덤프를 활성화했습니다.", - "DialogPerformanceCheckShaderDumpEnabledConfirmMessage": "최적의 성능을 위해 세이더 덤핑을 비활성화하는 것이 좋습니다. 지금 세이더 덤핑을 비활성화하겠습니까?", - "DialogLoadAppGameAlreadyLoadedMessage": "이미 게임 불러옴", - "DialogLoadAppGameAlreadyLoadedSubMessage": "다른 게임을 시작하기 전에 에뮬레이션을 중지하거나 에뮬레이터를 닫으세요.", - "DialogUpdateAddUpdateErrorMessage": "지정된 파일에 선택한 제목에 대한 업데이트가 포함되어 있지 않습니다!", - "DialogSettingsBackendThreadingWarningTitle": "경고 - 후단부 스레딩", - "DialogSettingsBackendThreadingWarningMessage": "변경 사항을 완전히 적용하려면 이 옵션을 변경한 후, Ryujinx를 다시 시작해야 합니다. 플랫폼에 따라 Ryujinx를 사용할 때 드라이버 자체의 멀티스레딩을 수동으로 비활성화해야 할 수도 있습니다.", - "DialogModManagerDeletionWarningMessage": "해당 Mod를 삭제하려고 합니다: {0}\n\n정말로 삭제하시겠습니까?", - "DialogModManagerDeletionAllWarningMessage": "해당 타이틀에 대한 모든 Mod들을 삭제하려고 합니다.\n\n정말로 삭제하시겠습니까?", - "SettingsTabGraphicsFeaturesOptions": "기능", - "SettingsTabGraphicsBackendMultithreading": "그래픽 후단부 멀티스레딩 :", - "CommonAuto": "자동", - "CommonOff": "끔", - "CommonOn": "켬", - "InputDialogYes": "예", - "InputDialogNo": "아니오", - "DialogProfileInvalidProfileNameErrorMessage": "파일 이름에 잘못된 문자가 포함되어 있습니다. 다시 시도하세요.", - "MenuBarOptionsPauseEmulation": "일시 정지", - "MenuBarOptionsResumeEmulation": "다시 시작", - "AboutUrlTooltipMessage": "기본 브라우저에서 Ryujinx 웹사이트를 열려면 클릭하세요.", - "AboutDisclaimerMessage": "Ryujinx는 닌텐도™,\n또는 그 파트너와 제휴한 바가 없습니다.", - "AboutAmiiboDisclaimerMessage": "AmiiboAPI (www.amiiboapi.com)는\nAmiibo 에뮬레이션에 사용됩니다.", - "AboutPatreonUrlTooltipMessage": "기본 브라우저에서 Ryujinx Patreon 페이지를 열려면 클릭하세요.", - "AboutGithubUrlTooltipMessage": "기본 브라우저에서 Ryujinx GitHub 페이지를 열려면 클릭하세요.", - "AboutDiscordUrlTooltipMessage": "기본 브라우저에서 Ryujinx 디스코드 서버에 대한 초대를 열려면 클릭하세요.", - "AboutTwitterUrlTooltipMessage": "기본 브라우저에서 Ryujinx 트위터 페이지를 열려면 클릭하세요.", - "AboutRyujinxAboutTitle": "정보 :", - "AboutRyujinxAboutContent": "Ryujinx는 닌텐도 스위치™용 에뮬레이터입니다.\nPatreon에서 지원해 주세요.\n트위터나 디스코드에서 최신 소식을 받아보세요.\n기여에 참여하고자 하는 개발자는 GitHub 또는 디스코드에서 자세한 내용을 확인할 수 있습니다.", - "AboutRyujinxMaintainersTitle": "유지 관리 :", - "AboutRyujinxMaintainersContentTooltipMessage": "기본 브라우저에서 기여자 페이지를 열려면 클릭하세요.", - "AboutRyujinxSupprtersTitle": "Patreon에서 후원:", - "AmiiboSeriesLabel": "Amiibo 시리즈", - "AmiiboCharacterLabel": "캐릭터", - "AmiiboScanButtonLabel": "스캔", - "AmiiboOptionsShowAllLabel": "모든 Amiibo 표시", - "AmiiboOptionsUsRandomTagLabel": "해킹: 임의의 태그 UUID 사용", - "DlcManagerTableHeadingEnabledLabel": "활성화됨", - "DlcManagerTableHeadingTitleIdLabel": "타이틀 ID", - "DlcManagerTableHeadingContainerPathLabel": "컨테이너 경로", - "DlcManagerTableHeadingFullPathLabel": "전체 경로", - "DlcManagerRemoveAllButton": "모두 제거", - "DlcManagerEnableAllButton": "모두 활성화", - "DlcManagerDisableAllButton": "모두 비활성화", - "ModManagerDeleteAllButton": "모두 삭제", - "MenuBarOptionsChangeLanguage": "언어 변경", - "MenuBarShowFileTypes": "파일 유형 표시", - "CommonSort": "정렬", - "CommonShowNames": "이름 표시", - "CommonFavorite": "즐겨찾기", - "OrderAscending": "오름차순", - "OrderDescending": "내림차순", - "SettingsTabGraphicsFeatures": "기능ㆍ개선 사항", - "ErrorWindowTitle": "오류 창", - "ToggleDiscordTooltip": "\"현재 재생 중인\" 디스코드 활동에 Ryujinx를 표시할지 여부 선택", - "AddGameDirBoxTooltip": "목록에 추가할 게임 디렉터리 입력", - "AddGameDirTooltip": "목록에 게임 디렉터리 추가", - "RemoveGameDirTooltip": "선택한 게임 디렉터리 제거", - "CustomThemeCheckTooltip": "GUI에 사용자 지정 Avalonia 테마를 사용하여 에뮬레이터 메뉴의 모양 변경", - "CustomThemePathTooltip": "사용자 정의 GUI 테마 경로", - "CustomThemeBrowseTooltip": "사용자 정의 GUI 테마 찾아보기", - "DockModeToggleTooltip": "독 모드에서는 에뮬레이트된 시스템이 도킹된 닌텐도 스위치처럼 작동합니다. 이것은 대부분의 게임에서 그래픽 품질을 향상시킵니다. 반대로 이 기능을 비활성화하면 에뮬레이트된 시스템이 휴대용 닌텐도 스위치처럼 작동하여 그래픽 품질이 저하됩니다.\n\n독 모드를 사용하려는 경우 플레이어 1의 컨트롤을 구성하세요. 휴대 모드를 사용하려는 경우 휴대용 컨트롤을 구성하세요.\n\n확실하지 않으면 켜 두세요.", - "DirectKeyboardTooltip": "다이렉트 키보드 접근(HID)은 게임에서 사용자의 키보드를 텍스트 입력 장치로 사용할 수 있게끔 제공합니다.\n\n스위치 하드웨어에서 키보드 사용을 네이티브로 지원하는 게임에서만 작동합니다.\n\n이 옵션에 대해 잘 모른다면 끄기를 권장합니다.", - "DirectMouseTooltip": "다이렉트 마우스 접근(HID)은 게임에서 사용자의 마우스를 포인터 장치로 사용할 수 있게끔 제공합니다.\n\n스위치 하드웨어에서 마우스 사용을 네이티브로 지원하는 극히 일부 게임에서만 작동합니다.\n\n이 옵션이 활성화된 경우, 터치 스크린 기능이 작동하지 않을 수 있습니다.\n\n이 옵션에 대해 잘 모른다면 끄기를 권장합니다.", - "RegionTooltip": "시스템 지역 변경", - "LanguageTooltip": "시스템 언어 변경", - "TimezoneTooltip": "시스템 시간대 변경", - "TimeTooltip": "시스템 시간 변경", - "VSyncToggleTooltip": "에뮬레이트된 콘솔의 수직 동기화. 기본적으로 대부분의 게임에 대한 프레임 제한 장치로, 비활성화시 게임이 더 빠른 속도로 실행되거나 로딩 화면이 더 오래 걸리거나 멈출 수 있습니다.\n\n게임 내에서 선호하는 핫키로 전환할 수 있습니다(기본값 F1). 핫키를 비활성화할 계획이라면 이 작업을 수행하는 것이 좋습니다.\n\n이 옵션에 대해 잘 모른다면 켜기를 권장드립니다.", - "PptcToggleTooltip": "게임이 불러올 때마다 번역할 필요가 없도록 번역된 JIT 기능을 저장합니다.\n\n게임을 처음 부팅한 후 끊김 현상을 줄이고 부팅 시간을 크게 단축합니다.\n\n확실하지 않으면 켜 두세요.", - "FsIntegrityToggleTooltip": "게임을 부팅할 때 손상된 파일을 확인하고 손상된 파일이 감지되면 로그에 해시 오류를 표시합니다.\n\n성능에 영향을 미치지 않으며 문제 해결에 도움이 됩니다.\n\n확실하지 않으면 켜 두세요.", - "AudioBackendTooltip": "오디오를 렌더링하는 데 사용되는 백엔드를 변경합니다.\n\nSDL2가 선호되는 반면 OpenAL 및 사운드IO는 폴백으로 사용됩니다. 더미는 소리가 나지 않습니다.\n\n확실하지 않으면 SDL2로 설정하세요.", - "MemoryManagerTooltip": "게스트 메모리가 매핑되고 접속되는 방식을 변경합니다. 에뮬레이트된 CPU 성능에 크게 영향을 미칩니다.\n\n확실하지 않은 경우 호스트 확인 안함으로 설정하세요.", - "MemoryManagerSoftwareTooltip": "주소 변환을 위해 소프트웨어 페이지 테이블을 사용하세요. 정확도는 가장 높지만 성능은 가장 느립니다.", - "MemoryManagerHostTooltip": "호스트 주소 공간의 메모리를 직접 매핑합니다. 훨씬 빠른 JIT 컴파일 및 실행합니다.", - "MemoryManagerUnsafeTooltip": "메모리를 직접 매핑하지만 접속하기 전에 게스트 주소 공간 내의 주소를 마스킹하지 마십시오. 더 빠르지만 안전을 희생해야 합니다. 게스트 응용 프로그램은 Ryujinx의 어디에서나 메모리에 접속할 수 있으므로 이 모드에서는 신뢰할 수 있는 프로그램만 실행하세요.", - "UseHypervisorTooltip": "JIT 대신 하이퍼바이저를 사용합니다. 하이퍼바이저를 사용할 수 있을 때 성능을 향상시키지만, 현재 상태에서는 불안정할 수 있습니다.", - "DRamTooltip": "대체 메모리모드 레이아웃을 활용하여 스위치 개발 모델을 모방합니다.\n\n고해상도 텍스처 팩 또는 4k 해상도 모드에만 유용합니다. 성능을 향상시키지 않습니다.\n\n확실하지 않으면 꺼 두세요.", - "IgnoreMissingServicesTooltip": "구현되지 않은 호라이즌 OS 서비스를 무시합니다. 이것은 특정 게임을 부팅할 때 충돌을 우회하는 데 도움이 될 수 있습니다.\n\n확실하지 않으면 꺼 두세요.", - "GraphicsBackendThreadingTooltip": "두 번째 스레드에서 그래픽 백엔드 명령을 실행합니다.\n\n세이더 컴파일 속도를 높이고 끊김 현상을 줄이며 자체 멀티스레딩 지원 없이 GPU 드라이버의 성능을 향상시킵니다. 멀티스레딩이 있는 드라이버에서 성능이 약간 향상되었습니다.\n\n잘 모르겠으면 자동으로 설정하세요.", - "GalThreadingTooltip": "두 번째 스레드에서 그래픽 백엔드 명령을 실행합니다.\n\n세이더 컴파일 속도를 높이고 끊김 현상을 줄이며 자체 멀티스레딩 지원 없이 GPU 드라이버의 성능을 향상시킵니다. 멀티스레딩이 있는 드라이버에서 성능이 약간 향상되었습니다.\n\n잘 모르겠으면 자동으로 설정하세요.", - "ShaderCacheToggleTooltip": "후속 실행에서 끊김 현상을 줄이는 디스크 세이더 캐시를 저장합니다.\n\n확실하지 않으면 켜 두세요.", - "ResolutionScaleTooltip": "게임의 렌더링 해상도를 늘립니다.\n\n일부 게임에서는 해당 기능을 지원하지 않거나 해상도가 늘어났음에도 픽셀이 자글자글해 보일 수 있습니다; 이러한 게임들의 경우 사용자가 직접 안티 앨리어싱 기능을 끄는 Mod나 내부 렌더링 해상도를 증가시키는 Mod 등을 찾아보아야 합니다. 후자의 Mod를 사용 시에는 해당 옵션을 네이티브로 두시는 것이 좋습니다.\n\n이 옵션은 게임이 구동중일 때에도 아래 Apply 버튼을 눌러서 변경할 수 있습니다; 설정 창을 게임 창 옆에 두고 사용자가 선호하는 해상도를 실험하여 고를 수 있습니다.\n\n4x 설정은 어떤 셋업에서도 무리인 점을 유의하세요.", - "ResolutionScaleEntryTooltip": "1.5와 같은 부동 소수점 분해능 스케일입니다. 비통합 척도는 문제나 충돌을 일으킬 가능성이 더 큽니다.", - "AnisotropyTooltip": "비등방성 필터링 레벨. 게임에서 요청한 값을 사용하려면 자동으로 설정하세요.", - "AspectRatioTooltip": "렌더러 창에 적용될 화면비.\n\n화면비를 변경하는 Mod를 사용할 때에만 이 옵션을 바꾸세요, 그렇지 않을 경우 그래픽이 늘어나 보일 수 있습니다.\n\n이 옵션에 대해 잘 모른다면 16:9로 설정하세요.", - "ShaderDumpPathTooltip": "그래픽 셰이더 덤프 경로", - "FileLogTooltip": "디스크의 로그 파일에 콘솔 로깅을 저장합니다. 성능에 영향을 미치지 않습니다.", - "StubLogTooltip": "콘솔에 스텁 로그 메시지를 인쇄합니다. 성능에 영향을 미치지 않습니다.", - "InfoLogTooltip": "콘솔에 정보 로그 메시지를 인쇄합니다. 성능에 영향을 미치지 않습니다.", - "WarnLogTooltip": "콘솔에 경고 로그 메시지를 인쇄합니다. 성능에 영향을 미치지 않습니다.", - "ErrorLogTooltip": "콘솔에 오류 로그 메시지를 인쇄합니다. 성능에 영향을 미치지 않습니다.", - "TraceLogTooltip": "콘솔에 추적 로그 메시지를 인쇄합니다. 성능에 영향을 미치지 않습니다.", - "GuestLogTooltip": "콘솔에 게스트 로그 메시지를 인쇄합니다. 성능에 영향을 미치지 않습니다.", - "FileAccessLogTooltip": "콘솔에 파일 액세스 로그 메시지를 인쇄합니다.", - "FSAccessLogModeTooltip": "콘솔에 대한 FS 접속 로그 출력을 활성화합니다. 가능한 모드는 0-3\t\t\t\t", - "DeveloperOptionTooltip": "주의해서 사용", - "OpenGlLogLevel": "적절한 로그 수준을 활성화해야 함", - "DebugLogTooltip": "콘솔에 디버그 로그 메시지를 인쇄합니다.\n\n로그를 읽기 어렵게 만들고 에뮬레이터 성능을 악화시키므로 직원이 구체적으로 지시한 경우에만 사용하세요.", - "LoadApplicationFileTooltip": "파일 탐색기를 열어 불러올 스위치 호환 파일 선택", - "LoadApplicationFolderTooltip": "파일 탐색기를 열어 불러올 스위치 호환 압축 해제 응용 프로그램 선택", - "OpenRyujinxFolderTooltip": "Ryujinx 파일 시스템 폴더 열기", - "OpenRyujinxLogsTooltip": "로그가 기록된 폴더 열기", - "ExitTooltip": "Ryujinx 종료", - "OpenSettingsTooltip": "설정 창 열기", - "OpenProfileManagerTooltip": "사용자 프로파일 관리자 창 열기", - "StopEmulationTooltip": "현재 게임의 에뮬레이션을 중지하고 게임 선택으로 돌아감", - "CheckUpdatesTooltip": "Ryujinx 업데이트 확인", - "OpenAboutTooltip": "정보 창 열기", - "GridSize": "격자 크기", - "GridSizeTooltip": "격자 항목의 크기 변경", - "SettingsTabSystemSystemLanguageBrazilianPortuguese": "포르투갈어(브라질)", - "AboutRyujinxContributorsButtonHeader": "모든 기여자 보기", - "SettingsTabSystemAudioVolume": "음량 : ", - "AudioVolumeTooltip": "음향 음량 변경", - "SettingsTabSystemEnableInternetAccess": "게스트 인터넷 접속/LAN 모드", - "EnableInternetAccessTooltip": "에뮬레이션된 응용프로그램이 인터넷에 연결되도록 허용합니다.\n\nLAN 모드가 있는 게임은 이 모드가 활성화되고 시스템이 동일한 접속 포인트에 연결된 경우 서로 연결할 수 있습니다. 여기에는 실제 콘솔도 포함됩니다.\n\n닌텐도 서버에 연결할 수 없습니다. 인터넷에 연결을 시도하는 특정 게임에서 충돌이 발생할 수 있습니다.\n\n확실하지 않으면 꺼두세요.", - "GameListContextMenuManageCheatToolTip": "치트 관리", - "GameListContextMenuManageCheat": "치트 관리", - "GameListContextMenuManageModToolTip": "Mod 관리", - "GameListContextMenuManageMod": "Mod 관리", - "ControllerSettingsStickRange": "범위 :", - "DialogStopEmulationTitle": "Ryujinx - 에뮬레이션 중지", - "DialogStopEmulationMessage": "에뮬레이션을 중지하겠습니까?", - "SettingsTabCpu": "CPU", - "SettingsTabAudio": "오디오", - "SettingsTabNetwork": "네트워크", - "SettingsTabNetworkConnection": "네트워크 연결", - "SettingsTabCpuCache": "CPU 캐시", - "SettingsTabCpuMemory": "CPU 모드", - "DialogUpdaterFlatpakNotSupportedMessage": "FlatHub를 통해 Ryujinx를 업데이트하세요.", - "UpdaterDisabledWarningTitle": "업데이터 비활성화입니다!", - "ControllerSettingsRotate90": "시계 방향으로 90° 회전", - "IconSize": "아이콘 크기", - "IconSizeTooltip": "게임 아이콘 크기 변경", - "MenuBarOptionsShowConsole": "콘솔 표시", - "ShaderCachePurgeError": "{0}에서 셰이더 캐시를 제거하는 중 오류 발생: {1}", - "UserErrorNoKeys": "키를 찾을 수 없음", - "UserErrorNoFirmware": "펌웨어를 찾을 수 없음", - "UserErrorFirmwareParsingFailed": "펌웨어 구문 분석 오류", - "UserErrorApplicationNotFound": "응용 프로그램을 찾을 수 없음", - "UserErrorUnknown": "알 수 없는 오류", - "UserErrorUndefined": "정의되지 않은 오류", - "UserErrorNoKeysDescription": "Ryujinx가 'prod.keys' 파일을 찾을 수 없음", - "UserErrorNoFirmwareDescription": "Ryujinx가 설치된 펌웨어를 찾을 수 없음", - "UserErrorFirmwareParsingFailedDescription": "Ryujinx가 제공된 펌웨어를 구문 분석할 수 없습니다. 일반적으로 오래된 키가 원인입니다.", - "UserErrorApplicationNotFoundDescription": "Ryujinx가 지정된 경로에서 유효한 응용 프로그램을 찾을 수 없습니다.", - "UserErrorUnknownDescription": "알 수 없는 오류가 발생했습니다!", - "UserErrorUndefinedDescription": "정의되지 않은 오류가 발생했습니다! 이런 일이 발생하면 안 되므로, 개발자에게 문의하세요!", - "OpenSetupGuideMessage": "설정 가이드 열기", - "NoUpdate": "업데이트 없음", - "TitleUpdateVersionLabel": "버전 {0}", - "RyujinxInfo": "Ryujinx - 정보", - "RyujinxConfirm": "Ryujinx - 확인", - "FileDialogAllTypes": "모든 유형", - "Never": "절대 안 함", - "SwkbdMinCharacters": "{0}자 이상이어야 함", - "SwkbdMinRangeCharacters": "{0}-{1}자여야 함", - "SoftwareKeyboard": "소프트웨어 키보드", - "SoftwareKeyboardModeNumeric": "'0~9' 또는 '.'만 가능", - "SoftwareKeyboardModeAlphabet": "한중일 문자가 아닌 문자만 가능", - "SoftwareKeyboardModeASCII": "ASCII 텍스트만 가능", - "ControllerAppletControllers": "지원하는 컨트롤러:", - "ControllerAppletPlayers": "플레이어:", - "ControllerAppletDescription": "현재 설정은 유효하지 않습니다. 설정을 열어 입력 장치를 다시 설정하세요.", - "ControllerAppletDocked": "독 모드가 설정되었습니다. 핸드헬드 컨트롤은 비활성화됩니다.", - "UpdaterRenaming": "이전 파일 이름 바꾸는 중...", - "UpdaterRenameFailed": "업데이터가 파일 이름을 바꿀 수 없음: {0}", - "UpdaterAddingFiles": "새로운 파일을 추가하는 중...", - "UpdaterExtracting": "업데이트를 추출하는 중...", - "UpdaterDownloading": "업데이트 다운로드 중...", - "Game": "게임", - "Docked": "도킹됨", - "Handheld": "휴대용", - "ConnectionError": "연결 오류입니다.", - "AboutPageDeveloperListMore": "{0} 등...", - "ApiError": "API 오류입니다.", - "LoadingHeading": "{0} 로딩 중", - "CompilingPPTC": "PTC 컴파일 중", - "CompilingShaders": "셰이더 컴파일 중", - "AllKeyboards": "모든 키보드", - "OpenFileDialogTitle": "지원되는 파일을 선택", - "OpenFolderDialogTitle": "압축을 푼 게임이 있는 폴더 선택", - "AllSupportedFormats": "지원되는 모든 형식", - "RyujinxUpdater": "Ryujinx 업데이터", - "SettingsTabHotkeys": "키보드 단축키", - "SettingsTabHotkeysHotkeys": "키보드 단축키", - "SettingsTabHotkeysToggleVsyncHotkey": "수직 동기화 전환 :", - "SettingsTabHotkeysScreenshotHotkey": "스크린샷 :", - "SettingsTabHotkeysShowUiHotkey": "UI 표시 :", - "SettingsTabHotkeysPauseHotkey": "일시 중지 :", - "SettingsTabHotkeysToggleMuteHotkey": "음 소거 :", - "ControllerMotionTitle": "동작 제어 설정", - "ControllerRumbleTitle": "진동 설정", - "SettingsSelectThemeFileDialogTitle": "테마 파일 선택", - "SettingsXamlThemeFile": "Xaml 테마 파일", - "AvatarWindowTitle": "계정 관리 - 아바타", - "Amiibo": "Amiibo", - "Unknown": "알 수 없음", - "Usage": "사용법", - "Writable": "쓰기 가능", - "SelectDlcDialogTitle": "DLC 파일 선택", - "SelectUpdateDialogTitle": "업데이트 파일 선택", - "SelectModDialogTitle": "Mod 디렉터리 선택", - "UserProfileWindowTitle": "사용자 프로파일 관리자", - "CheatWindowTitle": "치트 관리자", - "DlcWindowTitle": "{0} ({1})의 다운로드 가능한 콘텐츠 관리", - "ModWindowTitle": "{0} ({1})의 Mod 관리", - "UpdateWindowTitle": "타이틀 업데이트 관리자", - "CheatWindowHeading": "{0} [{1}]에 사용할 수 있는 치트", - "BuildId": "빌드ID :", - "DlcWindowHeading": "{0} 내려받기 가능한 콘텐츠", - "ModWindowHeading": "{0} Mod(s)", - "UserProfilesEditProfile": "선택된 항목 편집", - "Cancel": "취소", - "Save": "저장", - "Discard": "삭제", - "Paused": "일시 중지", - "UserProfilesSetProfileImage": "프로파일 이미지 설정", - "UserProfileEmptyNameError": "이름 필요", - "UserProfileNoImageError": "프로파일 이미지를 설정해야 함", - "GameUpdateWindowHeading": "{0} ({1})에 대한 업데이트 관리", - "SettingsTabHotkeysResScaleUpHotkey": "해상도 증가 :", - "SettingsTabHotkeysResScaleDownHotkey": "해상도 감소 :", - "UserProfilesName": "이름 :", - "UserProfilesUserId": "사용자 ID :", - "SettingsTabGraphicsBackend": "그래픽 후단부", - "SettingsTabGraphicsBackendTooltip": "에뮬레이터에 사용될 그래픽 백엔드를 선택합니다.\n\nVulkan이 드라이버가 최신이기 때문에 모든 현대 그래픽 카드들에서 더 좋은 성능을 발휘합니다. 또한 Vulkan은 모든 벤더사의 GPU에서 더 빠른 쉐이더 컴파일을 지원하여 스터터링이 적습니다.\n\nOpenGL의 경우 오래된 Nvidia GPU나 오래된 AMD GPU(리눅스 한정), 혹은 VRAM이 적은 GPU에서 더 나은 성능을 발휘할 수는 있으나 쉐이더 컴파일로 인한 스터터링이 Vulkan보다 심할 수 있습니다.\n\n이 옵션에 대해 잘 모른다면 Vulkan으로 설정하세요. 사용하는 GPU가 최신 그래픽 드라이버에서도 Vulkan을 지원하지 않는다면 그 땐 OpenGL로 설정하세요.", - "SettingsEnableTextureRecompression": "텍스처 재압축 활성화", - "SettingsEnableTextureRecompressionTooltip": "ASTC 텍스처를 압축하여 VRAM 사용량을 줄입니다.\n\n애스트럴 체인, 바요네타 3, 파이어 엠블렘 인게이지, 메트로이드 프라임 리마스터, 슈퍼 마리오브라더스 원더, 젤다의 전설: 티어스 오브 더 킹덤 등이 이러한 텍스처 포맷을 사용합니다.\n\nVRAM이 4GiB 이하인 그래픽 카드로 위와 같은 게임들을 구동할시 특정 지점에서 크래시가 발생할 수 있습니다.\n\n위에 서술된 게임들에서 VRAM이 부족한 경우에만 해당 옵션을 켜고, 그 외의 경우에는 끄기를 권장드립니다.", - "SettingsTabGraphicsPreferredGpu": "선호하는 GPU", - "SettingsTabGraphicsPreferredGpuTooltip": "Vulkan 그래픽 후단부와 함께 사용할 그래픽 카드를 선택하세요.\n\nOpenGL이 사용할 GPU에는 영향을 미치지 않습니다.\n\n확실하지 않은 경우 \"dGPU\" 플래그가 지정된 GPU로 설정하세요. 없는 경우, 그대로 두세요.", - "SettingsAppRequiredRestartMessage": "Ryujinx 다시 시작 필요", - "SettingsGpuBackendRestartMessage": "그래픽 후단부 또는 GPU 설정이 수정되었습니다. 적용하려면 다시 시작해야 합니다.", - "SettingsGpuBackendRestartSubMessage": "지금 다시 시작하겠습니까?", - "RyujinxUpdaterMessage": "Ryujinx를 최신 버전으로 업데이트하겠습니까?", - "SettingsTabHotkeysVolumeUpHotkey": "음량 증가 :", - "SettingsTabHotkeysVolumeDownHotkey": "음량 감소 :", - "SettingsEnableMacroHLE": "매크로 HLE 활성화", - "SettingsEnableMacroHLETooltip": "GPU 매크로 코드의 높은 수준 에뮬레이션입니다.\n\n성능이 향상되지만 일부 게임에서 그래픽 결함이 발생할 수 있습니다.\n\n확실하지 않으면 켜 두세요.", - "SettingsEnableColorSpacePassthrough": "색 공간 통과", - "SettingsEnableColorSpacePassthroughTooltip": "색 공간을 지정하지 않고 색상 정보를 전달하도록 Vulkan 후단에 지시합니다. 와이드 가멋 디스플레이를 사용하는 사용자의 경우 색 정확도가 저하되지만 더 생생한 색상을 얻을 수 있습니다.", - "VolumeShort": "음량", - "UserProfilesManageSaves": "저장 관리", - "DeleteUserSave": "이 게임에 대한 사용자 저장을 삭제하겠습니까?", - "IrreversibleActionNote": "이 작업은 되돌릴 수 없습니다.", - "SaveManagerHeading": "{0} ({1})의 저장 관리", - "SaveManagerTitle": "저장 관리자", - "Name": "이름", - "Size": "크기", - "Search": "검색", - "UserProfilesRecoverLostAccounts": "잃어버린 계정 복구", - "Recover": "복구", - "UserProfilesRecoverHeading": "다음 계정에 대한 저장 발견", - "UserProfilesRecoverEmptyList": "복구할 프로파일이 없습니다", - "GraphicsAATooltip": "게임 렌더에 안티 앨리어싱을 적용합니다.\n\nFXAA는 대부분의 이미지를 뿌옇게 만들지만, SMAA는 들쭉날쭉한 모서리 부분들을 찾아 부드럽게 만듭니다.\n\nFSR 스케일링 필터와 같이 사용하는 것은 권장하지 않습니다.\n\n이 옵션은 게임이 구동중일 때에도 아래 Apply 버튼을 눌러서 변경할 수 있습니다; 설정 창을 게임 창 옆에 두고 사용자가 선호하는 옵션을 실험하여 고를 수 있습니다.\n\n이 옵션에 대해 잘 모른다면 끄기를 권장드립니다.", - "GraphicsAALabel": "안티 앨리어싱:", - "GraphicsScalingFilterLabel": "스케일링 필터:", - "GraphicsScalingFilterTooltip": "해상도 스케일에 사용될 스케일링 필터를 선택하세요.\n\nBilinear는 3D 게임에서 잘 작동하며 안전한 기본값입니다.\n\nNearest는 픽셀 아트 게임에 추천합니다.\n\nFSR 1.0은 그저 샤프닝 필터임으로, FXAA나 SMAA와 같이 사용하는 것은 권장하지 않습니다.\n\n이 옵션은 게임이 구동중일 때에도 아래 Apply 버튼을 눌러서 변경할 수 있습니다; 설정 창을 게임 창 옆에 두고 사용자가 선호하는 옵션을 실험하여 고를 수 있습니다.\n\n이 옵션에 대해 잘 모른다면 BILINEAR로 두세요.", - "GraphicsScalingFilterBilinear": "Bilinear", - "GraphicsScalingFilterNearest": "Nearest", - "GraphicsScalingFilterFsr": "FSR", - "GraphicsScalingFilterLevelLabel": "수준", - "GraphicsScalingFilterLevelTooltip": "FSR 1.0의 샤프닝 레벨을 설정하세요. 높을수록 더 또렷해집니다.", - "SmaaLow": "SMAA 낮음", - "SmaaMedium": "SMAA 중간", - "SmaaHigh": "SMAA 높음", - "SmaaUltra": "SMAA 울트라", - "UserEditorTitle": "사용자 수정", - "UserEditorTitleCreate": "사용자 생성", - "SettingsTabNetworkInterface": "네트워크 인터페이스:", - "NetworkInterfaceTooltip": "LAN/LDN 기능에 사용될 네트워크 인터페이스입니다.\n\nLAN 기능을 지원하는 게임에서 VPN이나 XLink Kai 등을 동시에 사용하면, 인터넷을 통해 동일 네트워크 연결인 것을 속일 수 있습니다.\n\n이 옵션에 대해 잘 모른다면 기본값으로 설정하세요.", - "NetworkInterfaceDefault": "기본", - "PackagingShaders": "셰이더 패키징 중", - "AboutChangelogButton": "GitHub에서 변경 로그 보기", - "AboutChangelogButtonTooltipMessage": "기본 브라우저에서 이 버전의 변경 로그를 열려면 클릭합니다.", - "SettingsTabNetworkMultiplayer": "멀티 플레이어", - "MultiplayerMode": "모드 :", - "MultiplayerModeTooltip": "LDN 멀티플레이어 모드를 변경합니다.\n\nLdnMitm은 로컬 무선/로컬 플레이 기능을 수정하여 LAN 모드에 있는 것처럼 만들어 로컬이나 동일한 네트워크 상에 있는 다른 Ryujinx 인스턴스나 커펌된 닌텐도 스위치 콘솔(ldn_mitm 모듈 설치 필요)과 연결할 수 있습니다.\n\n멀티플레이어 모드는 모든 플레이어들이 동일한 게임 버전을 요구합니다. 예를 들어 슈퍼 스매시브라더스 얼티밋 v13.0.1 사용자는 v13.0.0 사용자와 연결할 수 없습니다.\n\n해당 옵션에 대해 잘 모른다면 비활성화해두세요.", - "MultiplayerModeDisabled": "비활성화됨", - "MultiplayerModeLdnMitm": "ldn_mitm" -} diff --git a/src/Ryujinx/Assets/Locales/pl_PL.json b/src/Ryujinx/Assets/Locales/pl_PL.json deleted file mode 100644 index 9d1bd7b44..000000000 --- a/src/Ryujinx/Assets/Locales/pl_PL.json +++ /dev/null @@ -1,780 +0,0 @@ -{ - "Language": "Polski", - "MenuBarFileOpenApplet": "Otwórz Aplet", - "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Otwórz aplet Mii Editor w trybie indywidualnym", - "SettingsTabInputDirectMouseAccess": "Bezpośredni dostęp do myszy", - "SettingsTabSystemMemoryManagerMode": "Tryb menedżera pamięci:", - "SettingsTabSystemMemoryManagerModeSoftware": "Oprogramowanie", - "SettingsTabSystemMemoryManagerModeHost": "Gospodarz (szybki)", - "SettingsTabSystemMemoryManagerModeHostUnchecked": "Gospodarza (NIESPRAWDZONY, najszybszy, niebezpieczne)", - "SettingsTabSystemUseHypervisor": "Użyj Hipernadzorcy", - "MenuBarFile": "_Plik", - "MenuBarFileOpenFromFile": "_Załaduj aplikację z pliku", - "MenuBarFileOpenUnpacked": "Załaduj _rozpakowaną grę", - "MenuBarFileOpenEmuFolder": "Otwórz folder Ryujinx", - "MenuBarFileOpenLogsFolder": "Otwórz folder plików dziennika zdarzeń", - "MenuBarFileExit": "_Wyjdź", - "MenuBarOptions": "_Opcje", - "MenuBarOptionsToggleFullscreen": "Przełącz na tryb pełnoekranowy", - "MenuBarOptionsStartGamesInFullscreen": "Uruchamiaj gry w trybie pełnoekranowym", - "MenuBarOptionsStopEmulation": "Zatrzymaj emulację", - "MenuBarOptionsSettings": "_Ustawienia", - "MenuBarOptionsManageUserProfiles": "_Zarządzaj profilami użytkowników", - "MenuBarActions": "_Akcje", - "MenuBarOptionsSimulateWakeUpMessage": "Symuluj wiadomość wybudzania", - "MenuBarActionsScanAmiibo": "Skanuj Amiibo", - "MenuBarTools": "_Narzędzia", - "MenuBarToolsInstallFirmware": "Zainstaluj oprogramowanie", - "MenuBarFileToolsInstallFirmwareFromFile": "Zainstaluj oprogramowanie z XCI lub ZIP", - "MenuBarFileToolsInstallFirmwareFromDirectory": "Zainstaluj oprogramowanie z katalogu", - "MenuBarToolsManageFileTypes": "Zarządzaj rodzajami plików", - "MenuBarToolsInstallFileTypes": "Typy plików instalacyjnych", - "MenuBarToolsUninstallFileTypes": "Typy plików dezinstalacyjnych", - "MenuBarView": "_View", - "MenuBarViewWindow": "Window Size", - "MenuBarViewWindow720": "720p", - "MenuBarViewWindow1080": "1080p", - "MenuBarHelp": "_Pomoc", - "MenuBarHelpCheckForUpdates": "Sprawdź aktualizacje", - "MenuBarHelpAbout": "O programie", - "MenuSearch": "Wyszukaj...", - "GameListHeaderFavorite": "Ulubione", - "GameListHeaderIcon": "Ikona", - "GameListHeaderApplication": "Nazwa", - "GameListHeaderDeveloper": "Twórca", - "GameListHeaderVersion": "Wersja", - "GameListHeaderTimePlayed": "Czas w grze:", - "GameListHeaderLastPlayed": "Ostatnio grane", - "GameListHeaderFileExtension": "Rozszerzenie pliku", - "GameListHeaderFileSize": "Rozmiar pliku", - "GameListHeaderPath": "Ścieżka", - "GameListContextMenuOpenUserSaveDirectory": "Otwórz katalog zapisów użytkownika", - "GameListContextMenuOpenUserSaveDirectoryToolTip": "Otwiera katalog, który zawiera zapis użytkownika dla tej aplikacji", - "GameListContextMenuOpenDeviceSaveDirectory": "Otwórz katalog zapisów urządzenia", - "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Otwiera katalog, który zawiera zapis urządzenia dla tej aplikacji", - "GameListContextMenuOpenBcatSaveDirectory": "Otwórz katalog zapisu BCAT obecnego użytkownika", - "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Otwiera katalog, który zawiera zapis BCAT dla tej aplikacji", - "GameListContextMenuManageTitleUpdates": "Zarządzaj aktualizacjami", - "GameListContextMenuManageTitleUpdatesToolTip": "Otwiera okno zarządzania aktualizacjami danej aplikacji", - "GameListContextMenuManageDlc": "Zarządzaj dodatkową zawartością (DLC)", - "GameListContextMenuManageDlcToolTip": "Otwiera okno zarządzania dodatkową zawartością", - "GameListContextMenuCacheManagement": "Zarządzanie Cache", - "GameListContextMenuCacheManagementPurgePptc": "Zakolejkuj rekompilację PPTC", - "GameListContextMenuCacheManagementPurgePptcToolTip": "Zainicjuj Rekompilację PPTC przy następnym uruchomieniu gry", - "GameListContextMenuCacheManagementPurgeShaderCache": "Wyczyść pamięć podręczną cieni", - "GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "Usuwa pamięć podręczną cieni danej aplikacji", - "GameListContextMenuCacheManagementOpenPptcDirectory": "Otwórz katalog PPTC", - "GameListContextMenuCacheManagementOpenPptcDirectoryToolTip": "Otwiera katalog, który zawiera pamięć podręczną PPTC aplikacji", - "GameListContextMenuCacheManagementOpenShaderCacheDirectory": "Otwórz katalog pamięci podręcznej cieni", - "GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip": "Otwiera katalog, który zawiera pamięć podręczną cieni aplikacji", - "GameListContextMenuExtractData": "Wypakuj dane", - "GameListContextMenuExtractDataExeFS": "ExeFS", - "GameListContextMenuExtractDataExeFSToolTip": "Wyodrębnij sekcję ExeFS z bieżącej konfiguracji aplikacji (w tym aktualizacje)", - "GameListContextMenuExtractDataRomFS": "RomFS", - "GameListContextMenuExtractDataRomFSToolTip": "Wyodrębnij sekcję RomFS z bieżącej konfiguracji aplikacji (w tym aktualizacje)", - "GameListContextMenuExtractDataLogo": "Logo", - "GameListContextMenuExtractDataLogoToolTip": "Wyodrębnij sekcję z logiem z bieżącej konfiguracji aplikacji (w tym aktualizacje)", - "GameListContextMenuCreateShortcut": "Utwórz skrót aplikacji", - "GameListContextMenuCreateShortcutToolTip": "Utwórz skrót na pulpicie, który uruchamia wybraną aplikację", - "GameListContextMenuCreateShortcutToolTipMacOS": "Utwórz skrót w folderze 'Aplikacje' w systemie macOS, który uruchamia wybraną aplikację", - "GameListContextMenuOpenModsDirectory": "Otwórz katalog modów", - "GameListContextMenuOpenModsDirectoryToolTip": "Otwiera katalog zawierający mody dla danej aplikacji", - "GameListContextMenuOpenSdModsDirectory": "Otwórz katalog modów Atmosphere", - "GameListContextMenuOpenSdModsDirectoryToolTip": "Otwiera alternatywny katalog Atmosphere na karcie SD, który zawiera mody danej aplikacji. Przydatne dla modów przygotowanych pod prawdziwy sprzęt.", - "StatusBarGamesLoaded": "{0}/{1} Załadowane gry", - "StatusBarSystemVersion": "Wersja systemu: {0}", - "LinuxVmMaxMapCountDialogTitle": "Wykryto niski limit dla przypisań pamięci", - "LinuxVmMaxMapCountDialogTextPrimary": "Czy chcesz zwiększyć wartość vm.max_map_count do {0}", - "LinuxVmMaxMapCountDialogTextSecondary": "Niektóre gry mogą próbować przypisać sobie więcej pamięci niż obecnie, jest to dozwolone. Ryujinx ulegnie awarii, gdy limit zostanie przekroczony.", - "LinuxVmMaxMapCountDialogButtonUntilRestart": "Tak, do następnego ponownego uruchomienia", - "LinuxVmMaxMapCountDialogButtonPersistent": "Tak, permanentnie ", - "LinuxVmMaxMapCountWarningTextPrimary": "Maksymalna ilość przypisanej pamięci jest mniejsza niż zalecana.", - "LinuxVmMaxMapCountWarningTextSecondary": "Obecna wartość vm.max_map_count ({0}) jest mniejsza niż {1}. Niektóre gry mogą próbować stworzyć więcej mapowań pamięci niż obecnie jest to dozwolone. Ryujinx napotka crash, gdy dojdzie do takiej sytuacji.\n\nMożesz chcieć ręcznie zwiększyć limit lub zainstalować pkexec, co pozwala Ryujinx na pomoc w tym zakresie.", - "Settings": "Ustawienia", - "SettingsTabGeneral": "Interfejs użytkownika", - "SettingsTabGeneralGeneral": "Ogólne", - "SettingsTabGeneralEnableDiscordRichPresence": "Włącz Bogatą Obecność Discord", - "SettingsTabGeneralCheckUpdatesOnLaunch": "Sprawdzaj aktualizacje przy uruchomieniu", - "SettingsTabGeneralShowConfirmExitDialog": "Pokazuj okno dialogowe \"Potwierdź wyjście\"", - "SettingsTabGeneralRememberWindowState": "Remember Window Size/Position", - "SettingsTabGeneralHideCursor": "Ukryj kursor:", - "SettingsTabGeneralHideCursorNever": "Nigdy", - "SettingsTabGeneralHideCursorOnIdle": "Gdy bezczynny", - "SettingsTabGeneralHideCursorAlways": "Zawsze", - "SettingsTabGeneralGameDirectories": "Katalogi gier", - "SettingsTabGeneralAdd": "Dodaj", - "SettingsTabGeneralRemove": "Usuń", - "SettingsTabSystem": "System", - "SettingsTabSystemCore": "Główne", - "SettingsTabSystemSystemRegion": "Region systemu:", - "SettingsTabSystemSystemRegionJapan": "Japonia", - "SettingsTabSystemSystemRegionUSA": "Stany Zjednoczone", - "SettingsTabSystemSystemRegionEurope": "Europa", - "SettingsTabSystemSystemRegionAustralia": "Australia", - "SettingsTabSystemSystemRegionChina": "Chiny", - "SettingsTabSystemSystemRegionKorea": "Korea", - "SettingsTabSystemSystemRegionTaiwan": "Tajwan", - "SettingsTabSystemSystemLanguage": "Język systemu:", - "SettingsTabSystemSystemLanguageJapanese": "Japoński", - "SettingsTabSystemSystemLanguageAmericanEnglish": "Angielski (Stany Zjednoczone)", - "SettingsTabSystemSystemLanguageFrench": "Francuski", - "SettingsTabSystemSystemLanguageGerman": "Niemiecki", - "SettingsTabSystemSystemLanguageItalian": "Włoski", - "SettingsTabSystemSystemLanguageSpanish": "Hiszpański", - "SettingsTabSystemSystemLanguageChinese": "Chiński", - "SettingsTabSystemSystemLanguageKorean": "Koreański", - "SettingsTabSystemSystemLanguageDutch": "Holenderski", - "SettingsTabSystemSystemLanguagePortuguese": "Portugalski", - "SettingsTabSystemSystemLanguageRussian": "Rosyjski", - "SettingsTabSystemSystemLanguageTaiwanese": "Tajwański", - "SettingsTabSystemSystemLanguageBritishEnglish": "Angielski (Wielka Brytania)", - "SettingsTabSystemSystemLanguageCanadianFrench": "Kanadyjski Francuski", - "SettingsTabSystemSystemLanguageLatinAmericanSpanish": "Hiszpański (Ameryka Łacińska)", - "SettingsTabSystemSystemLanguageSimplifiedChinese": "Chiński (Uproszczony)", - "SettingsTabSystemSystemLanguageTraditionalChinese": "Chiński (Tradycyjny)", - "SettingsTabSystemSystemTimeZone": "Strefa czasowa systemu:", - "SettingsTabSystemSystemTime": "Czas systemu:", - "SettingsTabSystemEnableVsync": "Synchronizacja pionowa", - "SettingsTabSystemEnablePptc": "PPTC (Profilowana pamięć podręczna trwałych łłumaczeń)", - "SettingsTabSystemEnableFsIntegrityChecks": "Sprawdzanie integralności systemu plików", - "SettingsTabSystemAudioBackend": "Backend Dżwięku:", - "SettingsTabSystemAudioBackendDummy": "Atrapa", - "SettingsTabSystemAudioBackendOpenAL": "OpenAL", - "SettingsTabSystemAudioBackendSoundIO": "SoundIO", - "SettingsTabSystemAudioBackendSDL2": "SDL2", - "SettingsTabSystemHacks": "Hacki", - "SettingsTabSystemHacksNote": " (mogą powodować niestabilność)", - "SettingsTabSystemExpandDramSize": "Użyj alternatywnego układu pamięci (Deweloperzy)", - "SettingsTabSystemIgnoreMissingServices": "Ignoruj Brakujące Usługi", - "SettingsTabGraphics": "Grafika", - "SettingsTabGraphicsAPI": "Graficzne API", - "SettingsTabGraphicsEnableShaderCache": "Włącz pamięć podręczną cieni", - "SettingsTabGraphicsAnisotropicFiltering": "Filtrowanie anizotropowe:", - "SettingsTabGraphicsAnisotropicFilteringAuto": "Automatyczne", - "SettingsTabGraphicsAnisotropicFiltering2x": "2x", - "SettingsTabGraphicsAnisotropicFiltering4x": "4x", - "SettingsTabGraphicsAnisotropicFiltering8x": "8x", - "SettingsTabGraphicsAnisotropicFiltering16x": "16x", - "SettingsTabGraphicsResolutionScale": "Skalowanie rozdzielczości:", - "SettingsTabGraphicsResolutionScaleCustom": "Niestandardowa (Niezalecane)", - "SettingsTabGraphicsResolutionScaleNative": "Natywna (720p/1080p)", - "SettingsTabGraphicsResolutionScale2x": "2x (1440p/2160p)", - "SettingsTabGraphicsResolutionScale3x": "3x (2160p/3240p)", - "SettingsTabGraphicsResolutionScale4x": "4x (2880p/4320p) (niezalecane)", - "SettingsTabGraphicsAspectRatio": "Format obrazu:", - "SettingsTabGraphicsAspectRatio4x3": "4:3", - "SettingsTabGraphicsAspectRatio16x9": "16:9", - "SettingsTabGraphicsAspectRatio16x10": "16:10", - "SettingsTabGraphicsAspectRatio21x9": "21:9", - "SettingsTabGraphicsAspectRatio32x9": "32:9", - "SettingsTabGraphicsAspectRatioStretch": "Rozciągnij do Okna", - "SettingsTabGraphicsDeveloperOptions": "Opcje programisty", - "SettingsTabGraphicsShaderDumpPath": "Ścieżka do zgranych cieni graficznych:", - "SettingsTabLogging": "Dziennik zdarzeń", - "SettingsTabLoggingLogging": "Dziennik zdarzeń", - "SettingsTabLoggingEnableLoggingToFile": "Włącz rejestrowanie zdarzeń do pliku", - "SettingsTabLoggingEnableStubLogs": "Wlącz Skróty Logów", - "SettingsTabLoggingEnableInfoLogs": "Włącz Logi Informacyjne", - "SettingsTabLoggingEnableWarningLogs": "Włącz Logi Ostrzeżeń", - "SettingsTabLoggingEnableErrorLogs": "Włącz Logi Błędów", - "SettingsTabLoggingEnableTraceLogs": "Włącz Logi Śledzenia", - "SettingsTabLoggingEnableGuestLogs": "Włącz Logi Gości", - "SettingsTabLoggingEnableFsAccessLogs": "Włącz Logi Dostępu do Systemu Plików", - "SettingsTabLoggingFsGlobalAccessLogMode": "Tryb globalnego dziennika zdarzeń systemu plików:", - "SettingsTabLoggingDeveloperOptions": "Opcje programisty (UWAGA: wpływa na wydajność)", - "SettingsTabLoggingDeveloperOptionsNote": "UWAGA: Pogrorszy wydajność", - "SettingsTabLoggingGraphicsBackendLogLevel": "Poziom rejestrowania do dziennika zdarzeń Backendu Graficznego:", - "SettingsTabLoggingGraphicsBackendLogLevelNone": "Nic", - "SettingsTabLoggingGraphicsBackendLogLevelError": "Błędy", - "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "Spowolnienia", - "SettingsTabLoggingGraphicsBackendLogLevelAll": "Wszystko", - "SettingsTabLoggingEnableDebugLogs": "Włącz dzienniki zdarzeń do debugowania", - "SettingsTabInput": "Sterowanie", - "SettingsTabInputEnableDockedMode": "Tryb zadokowany", - "SettingsTabInputDirectKeyboardAccess": "Bezpośredni dostęp do klawiatury", - "SettingsButtonSave": "Zapisz", - "SettingsButtonClose": "Zamknij", - "SettingsButtonOk": "OK", - "SettingsButtonCancel": "Anuluj", - "SettingsButtonApply": "Zastosuj", - "ControllerSettingsPlayer": "Gracz", - "ControllerSettingsPlayer1": "Gracz 1", - "ControllerSettingsPlayer2": "Gracz 2", - "ControllerSettingsPlayer3": "Gracz 3", - "ControllerSettingsPlayer4": "Gracz 4", - "ControllerSettingsPlayer5": "Gracz 5", - "ControllerSettingsPlayer6": "Gracz 6", - "ControllerSettingsPlayer7": "Gracz 7", - "ControllerSettingsPlayer8": "Gracz 8", - "ControllerSettingsHandheld": "Przenośny", - "ControllerSettingsInputDevice": "Urządzenie wejściowe", - "ControllerSettingsRefresh": "Odśwież", - "ControllerSettingsDeviceDisabled": "Wyłączone", - "ControllerSettingsControllerType": "Typ kontrolera", - "ControllerSettingsControllerTypeHandheld": "Przenośny", - "ControllerSettingsControllerTypeProController": "Pro Kontroler", - "ControllerSettingsControllerTypeJoyConPair": "Para JoyCon-ów", - "ControllerSettingsControllerTypeJoyConLeft": "Lewy JoyCon", - "ControllerSettingsControllerTypeJoyConRight": "Prawy JoyCon", - "ControllerSettingsProfile": "Profil", - "ControllerSettingsProfileDefault": "Domyślny", - "ControllerSettingsLoad": "Wczytaj", - "ControllerSettingsAdd": "Dodaj", - "ControllerSettingsRemove": "Usuń", - "ControllerSettingsButtons": "Przyciski", - "ControllerSettingsButtonA": "A", - "ControllerSettingsButtonB": "B", - "ControllerSettingsButtonX": "X", - "ControllerSettingsButtonY": "Y", - "ControllerSettingsButtonPlus": "+", - "ControllerSettingsButtonMinus": "-", - "ControllerSettingsDPad": "Krzyżak (D-Pad)", - "ControllerSettingsDPadUp": "Góra", - "ControllerSettingsDPadDown": "Dół", - "ControllerSettingsDPadLeft": "Lewo", - "ControllerSettingsDPadRight": "Prawo", - "ControllerSettingsStickButton": "Przycisk", - "ControllerSettingsStickUp": "Góra ", - "ControllerSettingsStickDown": "Dół ", - "ControllerSettingsStickLeft": "Lewo", - "ControllerSettingsStickRight": "Prawo", - "ControllerSettingsStickStick": "Gałka", - "ControllerSettingsStickInvertXAxis": "Odwróć gałkę X", - "ControllerSettingsStickInvertYAxis": "Odwróć gałkę Y", - "ControllerSettingsStickDeadzone": "Martwa strefa:", - "ControllerSettingsLStick": "Lewa Gałka", - "ControllerSettingsRStick": "Prawa Gałka", - "ControllerSettingsTriggersLeft": "Lewe Triggery", - "ControllerSettingsTriggersRight": "Prawe Triggery", - "ControllerSettingsTriggersButtonsLeft": "Lewe Przyciski Triggerów", - "ControllerSettingsTriggersButtonsRight": "Prawe Przyciski Triggerów", - "ControllerSettingsTriggers": "Triggery", - "ControllerSettingsTriggerL": "L", - "ControllerSettingsTriggerR": "R", - "ControllerSettingsTriggerZL": "ZL", - "ControllerSettingsTriggerZR": "ZR", - "ControllerSettingsLeftSL": "SL", - "ControllerSettingsLeftSR": "SR", - "ControllerSettingsRightSL": "SL", - "ControllerSettingsRightSR": "SR", - "ControllerSettingsExtraButtonsLeft": "Lewe Przyciski", - "ControllerSettingsExtraButtonsRight": "Prawe Przyciski", - "ControllerSettingsMisc": "Różne", - "ControllerSettingsTriggerThreshold": "Próg Triggerów:", - "ControllerSettingsMotion": "Ruch", - "ControllerSettingsMotionUseCemuhookCompatibleMotion": "Użyj ruchu zgodnego z CemuHook", - "ControllerSettingsMotionControllerSlot": "Slot Kontrolera:", - "ControllerSettingsMotionMirrorInput": "Odzwierciedlaj Sterowanie", - "ControllerSettingsMotionRightJoyConSlot": "Prawy Slot JoyCon:", - "ControllerSettingsMotionServerHost": "Host Serwera:", - "ControllerSettingsMotionGyroSensitivity": "Czułość Żyroskopu:", - "ControllerSettingsMotionGyroDeadzone": "Deadzone Żyroskopu:", - "ControllerSettingsSave": "Zapisz", - "ControllerSettingsClose": "Zamknij", - "KeyUnknown": "Unknown", - "KeyShiftLeft": "Shift Left", - "KeyShiftRight": "Shift Right", - "KeyControlLeft": "Ctrl Left", - "KeyMacControlLeft": "⌃ Left", - "KeyControlRight": "Ctrl Right", - "KeyMacControlRight": "⌃ Right", - "KeyAltLeft": "Alt Left", - "KeyMacAltLeft": "⌥ Left", - "KeyAltRight": "Alt Right", - "KeyMacAltRight": "⌥ Right", - "KeyWinLeft": "⊞ Left", - "KeyMacWinLeft": "⌘ Left", - "KeyWinRight": "⊞ Right", - "KeyMacWinRight": "⌘ Right", - "KeyMenu": "Menu", - "KeyUp": "Up", - "KeyDown": "Down", - "KeyLeft": "Left", - "KeyRight": "Right", - "KeyEnter": "Enter", - "KeyEscape": "Escape", - "KeySpace": "Space", - "KeyTab": "Tab", - "KeyBackSpace": "Backspace", - "KeyInsert": "Insert", - "KeyDelete": "Delete", - "KeyPageUp": "Page Up", - "KeyPageDown": "Page Down", - "KeyHome": "Home", - "KeyEnd": "End", - "KeyCapsLock": "Caps Lock", - "KeyScrollLock": "Scroll Lock", - "KeyPrintScreen": "Print Screen", - "KeyPause": "Pause", - "KeyNumLock": "Num Lock", - "KeyClear": "Clear", - "KeyKeypad0": "Keypad 0", - "KeyKeypad1": "Keypad 1", - "KeyKeypad2": "Keypad 2", - "KeyKeypad3": "Keypad 3", - "KeyKeypad4": "Keypad 4", - "KeyKeypad5": "Keypad 5", - "KeyKeypad6": "Keypad 6", - "KeyKeypad7": "Keypad 7", - "KeyKeypad8": "Keypad 8", - "KeyKeypad9": "Keypad 9", - "KeyKeypadDivide": "Keypad Divide", - "KeyKeypadMultiply": "Keypad Multiply", - "KeyKeypadSubtract": "Keypad Subtract", - "KeyKeypadAdd": "Keypad Add", - "KeyKeypadDecimal": "Keypad Decimal", - "KeyKeypadEnter": "Keypad Enter", - "KeyNumber0": "0", - "KeyNumber1": "1", - "KeyNumber2": "2", - "KeyNumber3": "3", - "KeyNumber4": "4", - "KeyNumber5": "5", - "KeyNumber6": "6", - "KeyNumber7": "7", - "KeyNumber8": "8", - "KeyNumber9": "9", - "KeyTilde": "~", - "KeyGrave": "`", - "KeyMinus": "-", - "KeyPlus": "+", - "KeyBracketLeft": "[", - "KeyBracketRight": "]", - "KeySemicolon": ";", - "KeyQuote": "\"", - "KeyComma": ",", - "KeyPeriod": ".", - "KeySlash": "/", - "KeyBackSlash": "\\", - "KeyUnbound": "Unbound", - "GamepadLeftStick": "L Stick Button", - "GamepadRightStick": "R Stick Button", - "GamepadLeftShoulder": "Left Shoulder", - "GamepadRightShoulder": "Right Shoulder", - "GamepadLeftTrigger": "Left Trigger", - "GamepadRightTrigger": "Right Trigger", - "GamepadDpadUp": "Up", - "GamepadDpadDown": "Down", - "GamepadDpadLeft": "Left", - "GamepadDpadRight": "Right", - "GamepadMinus": "-", - "GamepadPlus": "+", - "GamepadGuide": "Guide", - "GamepadMisc1": "Misc", - "GamepadPaddle1": "Paddle 1", - "GamepadPaddle2": "Paddle 2", - "GamepadPaddle3": "Paddle 3", - "GamepadPaddle4": "Paddle 4", - "GamepadTouchpad": "Touchpad", - "GamepadSingleLeftTrigger0": "Left Trigger 0", - "GamepadSingleRightTrigger0": "Right Trigger 0", - "GamepadSingleLeftTrigger1": "Left Trigger 1", - "GamepadSingleRightTrigger1": "Right Trigger 1", - "StickLeft": "Left Stick", - "StickRight": "Right Stick", - "UserProfilesSelectedUserProfile": "Wybrany profil użytkownika:", - "UserProfilesSaveProfileName": "Zapisz nazwę profilu", - "UserProfilesChangeProfileImage": "Zmień obrazek profilu", - "UserProfilesAvailableUserProfiles": "Dostępne profile użytkownika:", - "UserProfilesAddNewProfile": "Utwórz profil", - "UserProfilesDelete": "Usuń", - "UserProfilesClose": "Zamknij", - "ProfileNameSelectionWatermark": "Wybierz pseudonim", - "ProfileImageSelectionTitle": "Wybór Obrazu Profilu", - "ProfileImageSelectionHeader": "Wybierz zdjęcie profilowe", - "ProfileImageSelectionNote": "Możesz zaimportować niestandardowy obraz profilu lub wybrać awatar z firmware'u systemowego", - "ProfileImageSelectionImportImage": "Importuj Plik Obrazu", - "ProfileImageSelectionSelectAvatar": "Wybierz domyślny awatar z oprogramowania konsoli", - "InputDialogTitle": "Okno Dialogowe Wprowadzania", - "InputDialogOk": "OK", - "InputDialogCancel": "Anuluj", - "InputDialogAddNewProfileTitle": "Wybierz nazwę profilu", - "InputDialogAddNewProfileHeader": "Wprowadź nazwę profilu", - "InputDialogAddNewProfileSubtext": "(Maksymalna długość: {0})", - "AvatarChoose": "Wybierz awatar", - "AvatarSetBackgroundColor": "Ustaw kolor tła", - "AvatarClose": "Zamknij", - "ControllerSettingsLoadProfileToolTip": "Wczytaj profil", - "ControllerSettingsAddProfileToolTip": "Dodaj profil", - "ControllerSettingsRemoveProfileToolTip": "Usuń profil", - "ControllerSettingsSaveProfileToolTip": "Zapisz profil", - "MenuBarFileToolsTakeScreenshot": "Zrób zrzut ekranu", - "MenuBarFileToolsHideUi": "Ukryj interfejs użytkownika", - "GameListContextMenuRunApplication": "Uruchom aplikację ", - "GameListContextMenuToggleFavorite": "Przełącz na ulubione", - "GameListContextMenuToggleFavoriteToolTip": "Przełącz status Ulubionej Gry", - "SettingsTabGeneralTheme": "Motyw:", - "SettingsTabGeneralThemeDark": "Ciemny", - "SettingsTabGeneralThemeLight": "Jasny", - "ControllerSettingsConfigureGeneral": "Konfiguruj", - "ControllerSettingsRumble": "Wibracje", - "ControllerSettingsRumbleStrongMultiplier": "Mnożnik mocnych wibracji", - "ControllerSettingsRumbleWeakMultiplier": "Mnożnik słabych wibracji", - "DialogMessageSaveNotAvailableMessage": "Nie ma zapisanych danych dla {0} [{1:x16}]", - "DialogMessageSaveNotAvailableCreateSaveMessage": "Czy chcesz utworzyć zapis danych dla tej gry?", - "DialogConfirmationTitle": "Ryujinx - Potwierdzenie", - "DialogUpdaterTitle": "Ryujinx - Asystent aktualizacji", - "DialogErrorTitle": "Ryujinx - Błąd", - "DialogWarningTitle": "Ryujinx - Ostrzeżenie", - "DialogExitTitle": "Ryujinx - Wyjdź", - "DialogErrorMessage": "Ryujinx napotkał błąd", - "DialogExitMessage": "Czy na pewno chcesz zamknąć Ryujinx?", - "DialogExitSubMessage": "Wszystkie niezapisane dane zostaną utracone!", - "DialogMessageCreateSaveErrorMessage": "Wystąpił błąd podczas tworzenia określonych zapisanych danych: {0}", - "DialogMessageFindSaveErrorMessage": "Wystąpił błąd podczas próby znalezienia określonych zapisanych danych: {0}", - "FolderDialogExtractTitle": "Wybierz folder, do którego chcesz rozpakować", - "DialogNcaExtractionMessage": "Wypakowywanie sekcji {0} z {1}...", - "DialogNcaExtractionTitle": "Ryujinx - Asystent wypakowania sekcji NCA", - "DialogNcaExtractionMainNcaNotFoundErrorMessage": "Niepowodzenie podczas wypakowywania. W wybranym pliku nie było głównego NCA.", - "DialogNcaExtractionCheckLogErrorMessage": "Niepowodzenie podczas wypakowywania. Przeczytaj plik dziennika, aby uzyskać więcej informacji.", - "DialogNcaExtractionSuccessMessage": "Wypakowywanie zakończone pomyślnie.", - "DialogUpdaterConvertFailedMessage": "Nie udało się przekonwertować obecnej wersji Ryujinx.", - "DialogUpdaterCancelUpdateMessage": "Anulowanie aktualizacji!", - "DialogUpdaterAlreadyOnLatestVersionMessage": "Używasz już najnowszej wersji Ryujinx!", - "DialogUpdaterFailedToGetVersionMessage": "Wystąpił błąd podczas próby uzyskania informacji o obecnej wersji z GitHub Release. Może to być spowodowane nową wersją kompilowaną przez GitHub Actions. Spróbuj ponownie za kilka minut.", - "DialogUpdaterConvertFailedGithubMessage": "Nie udało się przekonwertować otrzymanej wersji Ryujinx z Github Release.", - "DialogUpdaterDownloadingMessage": "Pobieranie aktualizacji...", - "DialogUpdaterExtractionMessage": "Wypakowywanie Aktualizacji...", - "DialogUpdaterRenamingMessage": "Zmiana Nazwy Aktualizacji...", - "DialogUpdaterAddingFilesMessage": "Dodawanie Nowej Aktualizacji...", - "DialogUpdaterCompleteMessage": "Aktualizacja Zakończona!", - "DialogUpdaterRestartMessage": "Czy chcesz teraz zrestartować Ryujinx?", - "DialogUpdaterNoInternetMessage": "Nie masz połączenia z Internetem!", - "DialogUpdaterNoInternetSubMessage": "Sprawdź, czy masz działające połączenie internetowe!", - "DialogUpdaterDirtyBuildMessage": "Nie możesz zaktualizować Dirty wersji Ryujinx!", - "DialogUpdaterDirtyBuildSubMessage": "Pobierz Ryujinx ze strony https://ryujinx.org/, jeśli szukasz obsługiwanej wersji.", - "DialogRestartRequiredMessage": "Wymagane Ponowne Uruchomienie", - "DialogThemeRestartMessage": "Motyw został zapisany. Aby zastosować motyw, konieczne jest ponowne uruchomienie.", - "DialogThemeRestartSubMessage": "Czy chcesz uruchomić ponownie?", - "DialogFirmwareInstallEmbeddedMessage": "Czy chcesz zainstalować firmware wbudowany w tę grę? (Firmware {0})", - "DialogFirmwareInstallEmbeddedSuccessMessage": "Nie znaleziono zainstalowanego oprogramowania, ale Ryujinx był w stanie zainstalować oprogramowanie {0} z dostarczonej gry.\n\nEmulator uruchomi się teraz.", - "DialogFirmwareNoFirmwareInstalledMessage": "Brak Zainstalowanego Firmware'u", - "DialogFirmwareInstalledMessage": "Firmware {0} został zainstalowany", - "DialogInstallFileTypesSuccessMessage": "Pomyślnie zainstalowano typy plików!", - "DialogInstallFileTypesErrorMessage": "Nie udało się zainstalować typów plików.", - "DialogUninstallFileTypesSuccessMessage": "Pomyślnie odinstalowano typy plików!", - "DialogUninstallFileTypesErrorMessage": "Nie udało się odinstalować typów plików.", - "DialogOpenSettingsWindowLabel": "Otwórz Okno Ustawień", - "DialogControllerAppletTitle": "Aplet Kontrolera", - "DialogMessageDialogErrorExceptionMessage": "Błąd wyświetlania okna Dialogowego Wiadomości: {0}", - "DialogSoftwareKeyboardErrorExceptionMessage": "Błąd wyświetlania Klawiatury Oprogramowania: {0}", - "DialogErrorAppletErrorExceptionMessage": "Błąd wyświetlania okna Dialogowego ErrorApplet: {0}", - "DialogUserErrorDialogMessage": "{0}: {1}", - "DialogUserErrorDialogInfoMessage": "\nAby uzyskać więcej informacji o tym, jak naprawić ten błąd, zapoznaj się z naszym Przewodnikiem instalacji.", - "DialogUserErrorDialogTitle": "Błąd Ryujinxa ({0})", - "DialogAmiiboApiTitle": "API Amiibo", - "DialogAmiiboApiFailFetchMessage": "Wystąpił błąd podczas pobierania informacji z API.", - "DialogAmiiboApiConnectErrorMessage": "Nie można połączyć się z serwerem API Amiibo. Usługa może nie działać lub może być konieczne sprawdzenie, czy połączenie internetowe jest online.", - "DialogProfileInvalidProfileErrorMessage": "Profil {0} jest niezgodny z bieżącym systemem konfiguracji sterowania.", - "DialogProfileDefaultProfileOverwriteErrorMessage": "Profil Domyślny nie może zostać nadpisany", - "DialogProfileDeleteProfileTitle": "Usuwanie Profilu", - "DialogProfileDeleteProfileMessage": "Ta czynność jest nieodwracalna, czy na pewno chcesz kontynuować?", - "DialogWarning": "Uwaga", - "DialogPPTCDeletionMessage": "Masz zamiar umieścić w kolejce rekompilację PPTC przy następnym uruchomieniu:\n\n{0}\n\nCzy na pewno chcesz kontynuować?", - "DialogPPTCDeletionErrorMessage": "Błąd czyszczenia cache PPTC w {0}: {1}", - "DialogShaderDeletionMessage": "Zamierzasz usunąć cache Shaderów dla :\n\n{0}\n\nNa pewno chcesz kontynuować?", - "DialogShaderDeletionErrorMessage": "Błąd czyszczenia cache Shaderów w {0}: {1}", - "DialogRyujinxErrorMessage": "Ryujinx napotkał błąd", - "DialogInvalidTitleIdErrorMessage": "Błąd UI: Wybrana gra nie miała prawidłowego ID tytułu", - "DialogFirmwareInstallerFirmwareNotFoundErrorMessage": "Nie znaleziono prawidłowego firmware'u systemowego w {0}.", - "DialogFirmwareInstallerFirmwareInstallTitle": "Zainstaluj Firmware {0}", - "DialogFirmwareInstallerFirmwareInstallMessage": "Wersja systemu {0} zostanie zainstalowana.", - "DialogFirmwareInstallerFirmwareInstallSubMessage": "\n\nZastąpi to obecną wersję systemu {0}.", - "DialogFirmwareInstallerFirmwareInstallConfirmMessage": "\n\nCzy chcesz kontynuować?", - "DialogFirmwareInstallerFirmwareInstallWaitMessage": "Instalowanie firmware'u...", - "DialogFirmwareInstallerFirmwareInstallSuccessMessage": "Wersja systemu {0} została pomyślnie zainstalowana.", - "DialogUserProfileDeletionWarningMessage": "Nie będzie innych profili do otwarcia, jeśli wybrany profil zostanie usunięty", - "DialogUserProfileDeletionConfirmMessage": "Czy chcesz usunąć wybrany profil", - "DialogUserProfileUnsavedChangesTitle": "Uwaga - Niezapisane zmiany", - "DialogUserProfileUnsavedChangesMessage": "Wprowadziłeś zmiany dla tego profilu użytkownika, które nie zostały zapisane.", - "DialogUserProfileUnsavedChangesSubMessage": "Czy chcesz odrzucić zmiany?", - "DialogControllerSettingsModifiedConfirmMessage": "Aktualne ustawienia kontrolera zostały zaktualizowane.", - "DialogControllerSettingsModifiedConfirmSubMessage": "Czy chcesz zapisać?", - "DialogLoadFileErrorMessage": "{0}. Błędny plik: {1}", - "DialogModAlreadyExistsMessage": "Modyfikacja już istnieje", - "DialogModInvalidMessage": "Podany katalog nie zawiera modyfikacji!", - "DialogModDeleteNoParentMessage": "Nie udało się usunąć: Nie można odnaleźć katalogu nadrzędnego dla modyfikacji \"{0}\"!", - "DialogDlcNoDlcErrorMessage": "Określony plik nie zawiera DLC dla wybranego tytułu!", - "DialogPerformanceCheckLoggingEnabledMessage": "Masz włączone rejestrowanie śledzenia, które jest przeznaczone tylko dla programistów.", - "DialogPerformanceCheckLoggingEnabledConfirmMessage": "Aby uzyskać optymalną wydajność, zaleca się wyłączenie rejestrowania śledzenia. Czy chcesz teraz wyłączyć rejestrowanie śledzenia?", - "DialogPerformanceCheckShaderDumpEnabledMessage": "Masz włączone zrzucanie shaderów, które jest przeznaczone tylko dla programistów.", - "DialogPerformanceCheckShaderDumpEnabledConfirmMessage": "Aby uzyskać optymalną wydajność, zaleca się wyłączenie zrzucania shaderów. Czy chcesz teraz wyłączyć zrzucanie shaderów?", - "DialogLoadAppGameAlreadyLoadedMessage": "Gra została już załadowana", - "DialogLoadAppGameAlreadyLoadedSubMessage": "Zatrzymaj emulację lub zamknij emulator przed uruchomieniem innej gry.", - "DialogUpdateAddUpdateErrorMessage": "Określony plik nie zawiera aktualizacji dla wybranego tytułu!", - "DialogSettingsBackendThreadingWarningTitle": "Ostrzeżenie — Wątki Backend", - "DialogSettingsBackendThreadingWarningMessage": "Ryujinx musi zostać ponownie uruchomiony po zmianie tej opcji, aby działał w pełni. W zależności od platformy może być konieczne ręczne wyłączenie sterownika wielowątkowości podczas korzystania z Ryujinx.", - "DialogModManagerDeletionWarningMessage": "Zamierzasz usunąć modyfikacje: {0}\n\nCzy na pewno chcesz kontynuować?", - "DialogModManagerDeletionAllWarningMessage": "Zamierzasz usunąć wszystkie modyfikacje dla wybranego tytułu: {0}\n\nCzy na pewno chcesz kontynuować?", - "SettingsTabGraphicsFeaturesOptions": "Funkcje", - "SettingsTabGraphicsBackendMultithreading": "Wielowątkowość Backendu Graficznego:", - "CommonAuto": "Auto", - "CommonOff": "Wyłączone", - "CommonOn": "Włączone", - "InputDialogYes": "Tak", - "InputDialogNo": "Nie", - "DialogProfileInvalidProfileNameErrorMessage": "Nazwa pliku zawiera nieprawidłowe znaki. Proszę spróbuj ponownie.", - "MenuBarOptionsPauseEmulation": "Pauza", - "MenuBarOptionsResumeEmulation": "Wznów", - "AboutUrlTooltipMessage": "Kliknij, aby otworzyć stronę Ryujinx w domyślnej przeglądarce.", - "AboutDisclaimerMessage": "Ryujinx nie jest w żaden sposób powiązany z Nintendo™,\nani z żadnym z jej partnerów.", - "AboutAmiiboDisclaimerMessage": "AmiiboAPI (www.amiiboapi.com) jest używane\nw naszej emulacji Amiibo.", - "AboutPatreonUrlTooltipMessage": "Kliknij, aby otworzyć stronę Patreon Ryujinx w domyślnej przeglądarce.", - "AboutGithubUrlTooltipMessage": "Kliknij, aby otworzyć stronę GitHub Ryujinx w domyślnej przeglądarce.", - "AboutDiscordUrlTooltipMessage": "Kliknij, aby otworzyć zaproszenie na serwer Discord Ryujinx w domyślnej przeglądarce.", - "AboutTwitterUrlTooltipMessage": "Kliknij, aby otworzyć stronę Twitter Ryujinx w domyślnej przeglądarce.", - "AboutRyujinxAboutTitle": "O Aplikacji:", - "AboutRyujinxAboutContent": "Ryujinx to emulator Nintendo Switch™.\nWspieraj nas na Patreonie.\nOtrzymuj najnowsze wiadomości na naszym Twitterze lub Discordzie.\nDeweloperzy zainteresowani współpracą mogą dowiedzieć się więcej na naszym GitHubie lub Discordzie.", - "AboutRyujinxMaintainersTitle": "Utrzymywany Przez:", - "AboutRyujinxMaintainersContentTooltipMessage": "Kliknij, aby otworzyć stronę Współtwórcy w domyślnej przeglądarce.", - "AboutRyujinxSupprtersTitle": "Wspierani na Patreonie Przez:", - "AmiiboSeriesLabel": "Seria Amiibo", - "AmiiboCharacterLabel": "Postać", - "AmiiboScanButtonLabel": "Zeskanuj", - "AmiiboOptionsShowAllLabel": "Pokaż Wszystkie Amiibo", - "AmiiboOptionsUsRandomTagLabel": "Hack: Użyj losowego UUID tagu", - "DlcManagerTableHeadingEnabledLabel": "Włączone", - "DlcManagerTableHeadingTitleIdLabel": "ID Tytułu", - "DlcManagerTableHeadingContainerPathLabel": "Ścieżka Kontenera", - "DlcManagerTableHeadingFullPathLabel": "Pełna Ścieżka", - "DlcManagerRemoveAllButton": "Usuń Wszystkie", - "DlcManagerEnableAllButton": "Włącz Wszystkie", - "DlcManagerDisableAllButton": "Wyłącz Wszystkie", - "ModManagerDeleteAllButton": "Usuń wszystko", - "MenuBarOptionsChangeLanguage": "Zmień język", - "MenuBarShowFileTypes": "Pokaż typy plików", - "CommonSort": "Sortuj", - "CommonShowNames": "Pokaż Nazwy", - "CommonFavorite": "Ulubione", - "OrderAscending": "Rosnąco", - "OrderDescending": "Malejąco", - "SettingsTabGraphicsFeatures": "Funkcje i Ulepszenia", - "ErrorWindowTitle": "Okno Błędu", - "ToggleDiscordTooltip": "Wybierz, czy chcesz wyświetlać Ryujinx w swojej \"aktualnie grane\" aktywności Discord", - "AddGameDirBoxTooltip": "Wprowadź katalog gier aby dodać go do listy", - "AddGameDirTooltip": "Dodaj katalog gier do listy", - "RemoveGameDirTooltip": "Usuń wybrany katalog gier", - "CustomThemeCheckTooltip": "Użyj niestandardowego motywu Avalonia dla GUI, aby zmienić wygląd menu emulatora", - "CustomThemePathTooltip": "Ścieżka do niestandardowego motywu GUI", - "CustomThemeBrowseTooltip": "Wyszukaj niestandardowy motyw GUI", - "DockModeToggleTooltip": "Tryb Zadokowany sprawia, że emulowany system zachowuje się jak zadokowany Nintendo Switch. Poprawia to jakość grafiki w większości gier. I odwrotnie, wyłączenie tej opcji sprawi, że emulowany system będzie zachowywał się jak przenośny Nintendo Switch, zmniejszając jakość grafiki.\n\nSkonfiguruj sterowanie gracza 1, jeśli planujesz używać trybu Zadokowanego; Skonfiguruj sterowanie przenośne, jeśli planujesz używać trybu przenośnego.\n\nPozostaw WŁĄCZONY, jeśli nie masz pewności.", - "DirectKeyboardTooltip": "Obsługa bezpośredniego dostępu do klawiatury (HID). Zapewnia dostęp gier do klawiatury jako urządzenia do wprowadzania tekstu.\n\nDziała tylko z grami, które natywnie wspierają użycie klawiatury w urządzeniu Switch hardware.\n\nPozostaw wyłączone w razie braku pewności.", - "DirectMouseTooltip": "Obsługa bezpośredniego dostępu do myszy (HID). Zapewnia dostęp gier do myszy jako urządzenia wskazującego.\n\nDziała tylko z grami, które natywnie obsługują przyciski myszy w urządzeniu Switch, które są nieliczne i daleko między nimi.\n\nPo włączeniu funkcja ekranu dotykowego może nie działać.\n\nPozostaw wyłączone w razie wątpliwości.", - "RegionTooltip": "Zmień Region Systemu", - "LanguageTooltip": "Zmień język systemu", - "TimezoneTooltip": "Zmień Strefę Czasową Systemu", - "TimeTooltip": "Zmień czas systemowy", - "VSyncToggleTooltip": "Synchronizacja pionowa emulowanej konsoli. Zasadniczo ogranicznik klatek dla większości gier; wyłączenie jej może spowodować, że gry będą działać z większą szybkością, ekrany wczytywania wydłużą się lub nawet utkną.\n\nMoże być przełączana w grze za pomocą preferowanego skrótu klawiszowego. Zalecamy to zrobić, jeśli planujesz ją wyłączyć.\n\nW razie wątpliwości pozostaw WŁĄCZONĄ.", - "PptcToggleTooltip": "Zapisuje przetłumaczone funkcje JIT, dzięki czemu nie muszą być tłumaczone za każdym razem, gdy gra się ładuje.\n\nZmniejsza zacinanie się i znacznie przyspiesza uruchamianie po pierwszym uruchomieniu gry.\n\nJeśli nie masz pewności, pozostaw WŁĄCZONE", - "FsIntegrityToggleTooltip": "Sprawdza pliki podczas uruchamiania gry i jeśli zostaną wykryte uszkodzone pliki, wyświetla w dzienniku błąd hash.\n\nNie ma wpływu na wydajność i ma pomóc w rozwiązywaniu problemów.\n\nPozostaw WŁĄCZONE, jeśli nie masz pewności.", - "AudioBackendTooltip": "Zmienia backend używany do renderowania dźwięku.\n\nSDL2 jest preferowany, podczas gdy OpenAL i SoundIO są używane jako rezerwy. Dummy nie będzie odtwarzać dźwięku.\n\nW razie wątpliwości ustaw SDL2.", - "MemoryManagerTooltip": "Zmień sposób mapowania i uzyskiwania dostępu do pamięci gości. Znacznie wpływa na wydajność emulowanego procesora.\n\nUstaw na HOST UNCHECKED, jeśli nie masz pewności.", - "MemoryManagerSoftwareTooltip": "Użyj tabeli stron oprogramowania do translacji adresów. Najwyższa celność, ale najwolniejsza wydajność.", - "MemoryManagerHostTooltip": "Bezpośrednio mapuj pamięć w przestrzeni adresowej hosta. Znacznie szybsza kompilacja i wykonanie JIT.", - "MemoryManagerUnsafeTooltip": "Bezpośrednio mapuj pamięć, ale nie maskuj adresu w przestrzeni adresowej gościa przed uzyskaniem dostępu. Szybciej, ale kosztem bezpieczeństwa. Aplikacja gościa może uzyskać dostęp do pamięci z dowolnego miejsca w Ryujinx, więc w tym trybie uruchamiaj tylko programy, którym ufasz.", - "UseHypervisorTooltip": "Użyj Hiperwizora zamiast JIT. Znacznie poprawia wydajność, gdy jest dostępny, ale może być niestabilny w swoim obecnym stanie ", - "DRamTooltip": "Wykorzystuje alternatywny układ MemoryMode, aby naśladować model rozwojowy Switcha.\n\nJest to przydatne tylko w przypadku pakietów tekstur o wyższej rozdzielczości lub modów w rozdzielczości 4k. NIE poprawia wydajności.\n\nW razie wątpliwości pozostaw WYŁĄCZONE.", - "IgnoreMissingServicesTooltip": "Ignoruje niezaimplementowane usługi Horizon OS. Może to pomóc w ominięciu awarii podczas uruchamiania niektórych gier.\n\nW razie wątpliwości pozostaw WYŁĄCZONE.", - "GraphicsBackendThreadingTooltip": "Wykonuje polecenia backend'u graficznego w drugim wątku.\n\nPrzyspiesza kompilację shaderów, zmniejsza zacinanie się i poprawia wydajność sterowników GPU bez własnej obsługi wielowątkowości. Nieco lepsza wydajność w sterownikach z wielowątkowością.\n\nUstaw na AUTO, jeśli nie masz pewności.", - "GalThreadingTooltip": "Wykonuje polecenia backend'u graficznego w drugim wątku.\n\nPrzyspiesza kompilację shaderów, zmniejsza zacinanie się i poprawia wydajność sterowników GPU bez własnej obsługi wielowątkowości. Nieco lepsza wydajność w sterownikach z wielowątkowością.\n\nUstaw na AUTO, jeśli nie masz pewności.", - "ShaderCacheToggleTooltip": "Zapisuje pamięć podręczną shaderów na dysku, co zmniejsza zacinanie się w kolejnych uruchomieniach.\n\nPozostaw WŁĄCZONE, jeśli nie masz pewności.", - "ResolutionScaleTooltip": "Multiplies the game's rendering resolution.\n\nA few games may not work with this and look pixelated even when the resolution is increased; for those games, you may need to find mods that remove anti-aliasing or that increase their internal rendering resolution. For using the latter, you'll likely want to select Native.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nKeep in mind 4x is overkill for virtually any setup.", - "ResolutionScaleEntryTooltip": "Skala rozdzielczości zmiennoprzecinkowej, np. 1,5. Skale niecałkowite częściej powodują problemy lub awarie.", - "AnisotropyTooltip": "Level of Anisotropic Filtering. Set to Auto to use the value requested by the game.", - "AspectRatioTooltip": "Aspect Ratio applied to the renderer window.\n\nOnly change this if you're using an aspect ratio mod for your game, otherwise the graphics will be stretched.\n\nLeave on 16:9 if unsure.", - "ShaderDumpPathTooltip": "Ścieżka Zrzutu Shaderów Grafiki", - "FileLogTooltip": "Zapisuje logowanie konsoli w pliku dziennika na dysku. Nie wpływa na wydajność.", - "StubLogTooltip": "Wyświetla w konsoli skrótowe komunikaty dziennika. Nie wpływa na wydajność.", - "InfoLogTooltip": "Wyświetla komunikaty dziennika informacyjnego w konsoli. Nie wpływa na wydajność.", - "WarnLogTooltip": "Wyświetla komunikaty dziennika ostrzeżeń w konsoli. Nie wpływa na wydajność.", - "ErrorLogTooltip": "Wyświetla w konsoli komunikaty dziennika błędów. Nie wpływa na wydajność.", - "TraceLogTooltip": "Wyświetla komunikaty dziennika śledzenia w konsoli. Nie wpływa na wydajność.", - "GuestLogTooltip": "Wyświetla komunikaty dziennika gości w konsoli. Nie wpływa na wydajność.", - "FileAccessLogTooltip": "Wyświetla w konsoli komunikaty dziennika dostępu do plików.", - "FSAccessLogModeTooltip": "Włącza wyjście dziennika dostępu FS do konsoli. Możliwe tryby to 0-3", - "DeveloperOptionTooltip": "Używaj ostrożnie", - "OpenGlLogLevel": "Wymaga włączonych odpowiednich poziomów logów", - "DebugLogTooltip": "Wyświetla komunikaty dziennika debugowania w konsoli.\n\nUżywaj tego tylko na wyraźne polecenie członka załogi, ponieważ utrudni to odczytanie dzienników i pogorszy wydajność emulatora.", - "LoadApplicationFileTooltip": "Otwórz eksplorator plików, aby wybrać plik kompatybilny z Switch do wczytania", - "LoadApplicationFolderTooltip": "Otwórz eksplorator plików, aby wybrać zgodną z Switch, rozpakowaną aplikację do załadowania", - "OpenRyujinxFolderTooltip": "Otwórz folder systemu plików Ryujinx", - "OpenRyujinxLogsTooltip": "Otwiera folder, w którym zapisywane są logi", - "ExitTooltip": "Wyjdź z Ryujinx", - "OpenSettingsTooltip": "Otwórz okno ustawień", - "OpenProfileManagerTooltip": "Otwórz okno Menedżera Profili Użytkownika", - "StopEmulationTooltip": "Zatrzymaj emulację bieżącej gry i wróć do wyboru gier", - "CheckUpdatesTooltip": "Sprawdź aktualizacje Ryujinx", - "OpenAboutTooltip": "Otwórz Okno Informacje", - "GridSize": "Wielkość siatki", - "GridSizeTooltip": "Zmień rozmiar elementów siatki", - "SettingsTabSystemSystemLanguageBrazilianPortuguese": "Brazylijski Portugalski", - "AboutRyujinxContributorsButtonHeader": "Zobacz Wszystkich Współtwórców", - "SettingsTabSystemAudioVolume": "Głośność: ", - "AudioVolumeTooltip": "Zmień Głośność Dźwięku", - "SettingsTabSystemEnableInternetAccess": "Dostęp do Internetu Gościa/Tryb LAN", - "EnableInternetAccessTooltip": "Pozwala emulowanej aplikacji na łączenie się z Internetem.\n\nGry w trybie LAN mogą łączyć się ze sobą, gdy ta opcja jest włączona, a systemy są połączone z tym samym punktem dostępu. Dotyczy to również prawdziwych konsol.\n\nNie pozwala na łączenie się z serwerami Nintendo. Może powodować awarie niektórych gier, które próbują połączyć się z Internetem.\n\nPozostaw WYŁĄCZONE, jeśli nie masz pewności.", - "GameListContextMenuManageCheatToolTip": "Zarządzaj Kodami", - "GameListContextMenuManageCheat": "Zarządzaj Kodami", - "GameListContextMenuManageModToolTip": "Zarządzaj modyfikacjami", - "GameListContextMenuManageMod": "Zarządzaj modyfikacjami", - "ControllerSettingsStickRange": "Zasięg:", - "DialogStopEmulationTitle": "Ryujinx - Zatrzymaj Emulację", - "DialogStopEmulationMessage": "Czy na pewno chcesz zatrzymać emulację?", - "SettingsTabCpu": "CPU", - "SettingsTabAudio": "Dżwięk", - "SettingsTabNetwork": "Sieć", - "SettingsTabNetworkConnection": "Połączenie Sieciowe", - "SettingsTabCpuCache": "Cache CPU", - "SettingsTabCpuMemory": "Pamięć CPU", - "DialogUpdaterFlatpakNotSupportedMessage": "Zaktualizuj Ryujinx przez FlatHub.", - "UpdaterDisabledWarningTitle": "Aktualizator Wyłączony!", - "ControllerSettingsRotate90": "Obróć o 90° w Prawo", - "IconSize": "Rozmiar ikon", - "IconSizeTooltip": "Zmień rozmiar ikon gry", - "MenuBarOptionsShowConsole": "Pokaż Konsolę", - "ShaderCachePurgeError": "Błąd podczas czyszczenia cache shaderów w {0}: {1}", - "UserErrorNoKeys": "Nie znaleziono kluczy", - "UserErrorNoFirmware": "Nie znaleziono firmware'u", - "UserErrorFirmwareParsingFailed": "Błąd parsowania firmware'u", - "UserErrorApplicationNotFound": "Aplikacja nie znaleziona", - "UserErrorUnknown": "Nieznany błąd", - "UserErrorUndefined": "Niezdefiniowany błąd", - "UserErrorNoKeysDescription": "Ryujinx nie mógł znaleźć twojego pliku 'prod.keys'", - "UserErrorNoFirmwareDescription": "Ryujinx nie mógł znaleźć żadnego zainstalowanego firmware'u", - "UserErrorFirmwareParsingFailedDescription": "Ryujinx nie był w stanie zparsować dostarczonego firmware'u. Jest to zwykle spowodowane nieaktualnymi kluczami.", - "UserErrorApplicationNotFoundDescription": "Ryujinx nie mógł znaleźć prawidłowej aplikacji na podanej ścieżce.", - "UserErrorUnknownDescription": "Wystąpił nieznany błąd!", - "UserErrorUndefinedDescription": "Wystąpił niezdefiniowany błąd! To nie powinno się zdarzyć, skontaktuj się z deweloperem!", - "OpenSetupGuideMessage": "Otwórz Podręcznik Konfiguracji", - "NoUpdate": "Brak Aktualizacji", - "TitleUpdateVersionLabel": "Wersja {0} - {1}", - "RyujinxInfo": "Ryujinx - Info", - "RyujinxConfirm": "Ryujinx - Potwierdzenie", - "FileDialogAllTypes": "Wszystkie typy", - "Never": "Nigdy", - "SwkbdMinCharacters": "Musi mieć co najmniej {0} znaków", - "SwkbdMinRangeCharacters": "Musi mieć długość od {0}-{1} znaków", - "SoftwareKeyboard": "Klawiatura Oprogramowania", - "SoftwareKeyboardModeNumeric": "Może składać się jedynie z 0-9 lub '.'", - "SoftwareKeyboardModeAlphabet": "Nie może zawierać znaków CJK", - "SoftwareKeyboardModeASCII": "Musi zawierać tylko tekst ASCII", - "ControllerAppletControllers": "Obsługiwane Kontrolery:", - "ControllerAppletPlayers": "Gracze:", - "ControllerAppletDescription": "Twoja aktualna konfiguracja jest nieprawidłowa. Otwórz ustawienia i skonfiguruj swoje wejścia.", - "ControllerAppletDocked": "Ustawiony tryb zadokowany. Sterowanie przenośne powinno być wyłączone.", - "UpdaterRenaming": "Zmienianie Nazw Starych Plików...", - "UpdaterRenameFailed": "Aktualizator nie mógł zmienić nazwy pliku: {0}", - "UpdaterAddingFiles": "Dodawanie Nowych Plików...", - "UpdaterExtracting": "Wypakowywanie Aktualizacji...", - "UpdaterDownloading": "Pobieranie Aktualizacji...", - "Game": "Gra", - "Docked": "Zadokowany", - "Handheld": "Przenośny", - "ConnectionError": "Błąd Połączenia.", - "AboutPageDeveloperListMore": "{0} i więcej...", - "ApiError": "Błąd API.", - "LoadingHeading": "Wczytywanie {0}", - "CompilingPPTC": "Kompilowanie PTC", - "CompilingShaders": "Kompilowanie Shaderów", - "AllKeyboards": "Wszystkie klawiatury", - "OpenFileDialogTitle": "Wybierz obsługiwany plik do otwarcia", - "OpenFolderDialogTitle": "Wybierz folder z rozpakowaną grą", - "AllSupportedFormats": "Wszystkie Obsługiwane Formaty", - "RyujinxUpdater": "Aktualizator Ryujinx", - "SettingsTabHotkeys": "Skróty Klawiszowe Klawiatury", - "SettingsTabHotkeysHotkeys": "Skróty Klawiszowe Klawiatury", - "SettingsTabHotkeysToggleVsyncHotkey": "Przełącz VSync:", - "SettingsTabHotkeysScreenshotHotkey": "Zrzut Ekranu:", - "SettingsTabHotkeysShowUiHotkey": "Pokaż UI:", - "SettingsTabHotkeysPauseHotkey": "Pauza:", - "SettingsTabHotkeysToggleMuteHotkey": "Wycisz:", - "ControllerMotionTitle": "Ustawienia Sterowania Ruchowego", - "ControllerRumbleTitle": "Ustawienia Wibracji", - "SettingsSelectThemeFileDialogTitle": "Wybierz Plik Motywu", - "SettingsXamlThemeFile": "Plik Motywu Xaml", - "AvatarWindowTitle": "Zarządzaj Kontami — Avatar", - "Amiibo": "Amiibo", - "Unknown": "Nieznane", - "Usage": "Użycie", - "Writable": "Zapisywalne", - "SelectDlcDialogTitle": "Wybierz pliki DLC", - "SelectUpdateDialogTitle": "Wybierz pliki aktualizacji", - "SelectModDialogTitle": "Wybierz katalog modów", - "UserProfileWindowTitle": "Menedżer Profili Użytkowników", - "CheatWindowTitle": "Menedżer Kodów", - "DlcWindowTitle": "Menedżer Zawartości do Pobrania", - "ModWindowTitle": "Zarządzaj modami dla {0} ({1})", - "UpdateWindowTitle": "Menedżer Aktualizacji Tytułu", - "CheatWindowHeading": "Kody Dostępne dla {0} [{1}]", - "BuildId": "Identyfikator wersji:", - "DlcWindowHeading": "{0} Zawartości do Pobrania dostępna dla {1} ({2})", - "ModWindowHeading": "{0} Mod(y/ów)", - "UserProfilesEditProfile": "Edytuj Zaznaczone", - "Cancel": "Anuluj", - "Save": "Zapisz", - "Discard": "Odrzuć", - "Paused": "Wstrzymano", - "UserProfilesSetProfileImage": "Ustaw Obraz Profilu", - "UserProfileEmptyNameError": "Nazwa jest wymagana", - "UserProfileNoImageError": "Należy ustawić obraz profilowy", - "GameUpdateWindowHeading": "{0} Aktualizacje dostępne dla {1} ({2})", - "SettingsTabHotkeysResScaleUpHotkey": "Zwiększ Rozdzielczość:", - "SettingsTabHotkeysResScaleDownHotkey": "Zmniejsz Rozdzielczość:", - "UserProfilesName": "Nazwa:", - "UserProfilesUserId": "ID Użytkownika:", - "SettingsTabGraphicsBackend": "Backend Graficzny", - "SettingsTabGraphicsBackendTooltip": "Select the graphics backend that will be used in the emulator.\n\nVulkan is overall better for all modern graphics cards, as long as their drivers are up to date. Vulkan also features faster shader compilation (less stuttering) on all GPU vendors.\n\nOpenGL may achieve better results on old Nvidia GPUs, on old AMD GPUs on Linux, or on GPUs with lower VRAM, though shader compilation stutters will be greater.\n\nSet to Vulkan if unsure. Set to OpenGL if your GPU does not support Vulkan even with the latest graphics drivers.", - "SettingsEnableTextureRecompression": "Włącz Rekompresję Tekstur", - "SettingsEnableTextureRecompressionTooltip": "Compresses ASTC textures in order to reduce VRAM usage.\n\nGames using this texture format include Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder and The Legend of Zelda: Tears of the Kingdom.\n\nGraphics cards with 4GiB VRAM or less will likely crash at some point while running these games.\n\nEnable only if you're running out of VRAM on the aforementioned games. Leave OFF if unsure.", - "SettingsTabGraphicsPreferredGpu": "Preferowane GPU", - "SettingsTabGraphicsPreferredGpuTooltip": "Wybierz kartę graficzną, która będzie używana z backendem graficznym Vulkan.\n\nNie wpływa na GPU używane przez OpenGL.\n\nW razie wątpliwości ustaw flagę GPU jako \"dGPU\". Jeśli żadnej nie ma, pozostaw nietknięte.", - "SettingsAppRequiredRestartMessage": "Wymagane Zrestartowanie Ryujinx", - "SettingsGpuBackendRestartMessage": "Zmieniono ustawienia Backendu Graficznego lub GPU. Będzie to wymagało ponownego uruchomienia", - "SettingsGpuBackendRestartSubMessage": "Czy chcesz zrestartować teraz?", - "RyujinxUpdaterMessage": "Czy chcesz zaktualizować Ryujinx do najnowszej wersji?", - "SettingsTabHotkeysVolumeUpHotkey": "Zwiększ Głośność:", - "SettingsTabHotkeysVolumeDownHotkey": "Zmniejsz Głośność:", - "SettingsEnableMacroHLE": "Włącz Macro HLE", - "SettingsEnableMacroHLETooltip": "Wysokopoziomowa emulacja kodu GPU Macro.\n\nPoprawia wydajność, ale może powodować błędy graficzne w niektórych grach.\n\nW razie wątpliwości pozostaw WŁĄCZONE.", - "SettingsEnableColorSpacePassthrough": "Przekazywanie przestrzeni kolorów", - "SettingsEnableColorSpacePassthroughTooltip": "Nakazuje API Vulkan przekazywać informacje o kolorze bez określania przestrzeni kolorów. Dla użytkowników z wyświetlaczami o szerokim zakresie kolorów może to skutkować bardziej żywymi kolorami, kosztem ich poprawności.", - "VolumeShort": "Głoś", - "UserProfilesManageSaves": "Zarządzaj Zapisami", - "DeleteUserSave": "Czy chcesz usunąć zapis użytkownika dla tej gry?", - "IrreversibleActionNote": "Ta czynność nie jest odwracalna.", - "SaveManagerHeading": "Zarządzaj Zapisami dla {0}", - "SaveManagerTitle": "Menedżer Zapisów", - "Name": "Nazwa", - "Size": "Rozmiar", - "Search": "Wyszukaj", - "UserProfilesRecoverLostAccounts": "Odzyskaj Utracone Konta", - "Recover": "Odzyskaj", - "UserProfilesRecoverHeading": "Znaleziono zapisy dla następujących kont", - "UserProfilesRecoverEmptyList": "Brak profili do odzyskania", - "GraphicsAATooltip": "Applies anti-aliasing to the game render.\n\nFXAA will blur most of the image, while SMAA will attempt to find jagged edges and smooth them out.\n\nNot recommended to use in conjunction with the FSR scaling filter.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on NONE if unsure.", - "GraphicsAALabel": "Antyaliasing:", - "GraphicsScalingFilterLabel": "Filtr skalowania:", - "GraphicsScalingFilterTooltip": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.", - "GraphicsScalingFilterBilinear": "Dwuliniowe", - "GraphicsScalingFilterNearest": "Najbliższe", - "GraphicsScalingFilterFsr": "FSR", - "GraphicsScalingFilterLevelLabel": "Poziom", - "GraphicsScalingFilterLevelTooltip": "Ustaw poziom ostrzeżenia FSR 1.0. Wyższy jest ostrzejszy.", - "SmaaLow": "SMAA Niskie", - "SmaaMedium": "SMAA Średnie", - "SmaaHigh": "SMAA Wysokie", - "SmaaUltra": "SMAA Ultra", - "UserEditorTitle": "Edytuj użytkownika", - "UserEditorTitleCreate": "Utwórz użytkownika", - "SettingsTabNetworkInterface": "Interfejs sieci:", - "NetworkInterfaceTooltip": "Interfejs sieciowy używany dla funkcji LAN/LDN.\n\nw połączeniu z VPN lub XLink Kai i grą z obsługą sieci LAN, może być użyty do spoofowania połączenia z tą samą siecią przez Internet.\n\nZostaw DOMYŚLNE, jeśli nie ma pewności.", - "NetworkInterfaceDefault": "Domyślny", - "PackagingShaders": "Pakuje Shadery ", - "AboutChangelogButton": "Zobacz listę zmian na GitHubie", - "AboutChangelogButtonTooltipMessage": "Kliknij, aby otworzyć listę zmian dla tej wersji w domyślnej przeglądarce.", - "SettingsTabNetworkMultiplayer": "Gra Wieloosobowa", - "MultiplayerMode": "Tryb:", - "MultiplayerModeTooltip": "Change LDN multiplayer mode.\n\nLdnMitm will modify local wireless/local play functionality in games to function as if it were LAN, allowing for local, same-network connections with other Ryujinx instances and hacked Nintendo Switch consoles that have the ldn_mitm module installed.\n\nMultiplayer requires all players to be on the same game version (i.e. Super Smash Bros. Ultimate v13.0.1 can't connect to v13.0.0).\n\nLeave DISABLED if unsure.", - "MultiplayerModeDisabled": "Wyłączone", - "MultiplayerModeLdnMitm": "ldn_mitm" -} diff --git a/src/Ryujinx/Assets/Locales/pt_BR.json b/src/Ryujinx/Assets/Locales/pt_BR.json deleted file mode 100644 index a8c244b65..000000000 --- a/src/Ryujinx/Assets/Locales/pt_BR.json +++ /dev/null @@ -1,780 +0,0 @@ -{ - "Language": "Português (BR)", - "MenuBarFileOpenApplet": "Abrir Applet", - "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Abrir editor Mii em modo avulso", - "SettingsTabInputDirectMouseAccess": "Acesso direto ao mouse", - "SettingsTabSystemMemoryManagerMode": "Modo de gerenciamento de memória:", - "SettingsTabSystemMemoryManagerModeSoftware": "Software", - "SettingsTabSystemMemoryManagerModeHost": "Hóspede (rápido)", - "SettingsTabSystemMemoryManagerModeHostUnchecked": "Hóspede sem verificação (mais rápido, inseguro)", - "SettingsTabSystemUseHypervisor": "Usar Hipervisor", - "MenuBarFile": "_Arquivo", - "MenuBarFileOpenFromFile": "_Abrir ROM do jogo...", - "MenuBarFileOpenUnpacked": "Abrir jogo _extraído...", - "MenuBarFileOpenEmuFolder": "Abrir diretório do e_mulador...", - "MenuBarFileOpenLogsFolder": "Abrir diretório de _logs...", - "MenuBarFileExit": "_Sair", - "MenuBarOptions": "_Opções", - "MenuBarOptionsToggleFullscreen": "_Mudar para tela cheia", - "MenuBarOptionsStartGamesInFullscreen": "Iniciar jogos em tela cheia", - "MenuBarOptionsStopEmulation": "_Encerrar emulação", - "MenuBarOptionsSettings": "_Configurações", - "MenuBarOptionsManageUserProfiles": "_Gerenciar perfis de usuário", - "MenuBarActions": "_Ações", - "MenuBarOptionsSimulateWakeUpMessage": "_Simular mensagem de acordar console", - "MenuBarActionsScanAmiibo": "Escanear um Amiibo", - "MenuBarTools": "_Ferramentas", - "MenuBarToolsInstallFirmware": "_Instalar firmware", - "MenuBarFileToolsInstallFirmwareFromFile": "Instalar firmware a partir de um arquivo ZIP/XCI", - "MenuBarFileToolsInstallFirmwareFromDirectory": "Instalar firmware a partir de um diretório", - "MenuBarToolsManageFileTypes": "Gerenciar tipos de arquivo", - "MenuBarToolsInstallFileTypes": "Instalar tipos de arquivo", - "MenuBarToolsUninstallFileTypes": "Desinstalar tipos de arquivos", - "MenuBarView": "_View", - "MenuBarViewWindow": "Window Size", - "MenuBarViewWindow720": "720p", - "MenuBarViewWindow1080": "1080p", - "MenuBarHelp": "_Ajuda", - "MenuBarHelpCheckForUpdates": "_Verificar se há atualizações", - "MenuBarHelpAbout": "_Sobre", - "MenuSearch": "Buscar...", - "GameListHeaderFavorite": "Favorito", - "GameListHeaderIcon": "Ícone", - "GameListHeaderApplication": "Nome", - "GameListHeaderDeveloper": "Desenvolvedor", - "GameListHeaderVersion": "Versão", - "GameListHeaderTimePlayed": "Tempo de jogo", - "GameListHeaderLastPlayed": "Último jogo", - "GameListHeaderFileExtension": "Extensão", - "GameListHeaderFileSize": "Tamanho", - "GameListHeaderPath": "Caminho", - "GameListContextMenuOpenUserSaveDirectory": "Abrir diretório de saves do usuário", - "GameListContextMenuOpenUserSaveDirectoryToolTip": "Abre o diretório que contém jogos salvos para o usuário atual", - "GameListContextMenuOpenDeviceSaveDirectory": "Abrir diretório de saves de dispositivo do usuário", - "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Abre o diretório que contém saves do dispositivo para o usuário atual", - "GameListContextMenuOpenBcatSaveDirectory": "Abrir diretório de saves BCAT do usuário", - "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Abre o diretório que contém saves BCAT para o usuário atual", - "GameListContextMenuManageTitleUpdates": "Gerenciar atualizações do jogo", - "GameListContextMenuManageTitleUpdatesToolTip": "Abre a janela de gerenciamento de atualizações", - "GameListContextMenuManageDlc": "Gerenciar DLCs", - "GameListContextMenuManageDlcToolTip": "Abre a janela de gerenciamento de DLCs", - "GameListContextMenuCacheManagement": "Gerenciamento de cache", - "GameListContextMenuCacheManagementPurgePptc": "Limpar cache PPTC", - "GameListContextMenuCacheManagementPurgePptcToolTip": "Deleta o cache PPTC armazenado em disco do jogo", - "GameListContextMenuCacheManagementPurgeShaderCache": "Limpar cache de Shader", - "GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "Deleta o cache de Shader armazenado em disco do jogo", - "GameListContextMenuCacheManagementOpenPptcDirectory": "Abrir diretório do cache PPTC", - "GameListContextMenuCacheManagementOpenPptcDirectoryToolTip": "Abre o diretório contendo os arquivos do cache PPTC", - "GameListContextMenuCacheManagementOpenShaderCacheDirectory": "Abrir diretório do cache de Shader", - "GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip": "Abre o diretório contendo os arquivos do cache de Shader", - "GameListContextMenuExtractData": "Extrair dados", - "GameListContextMenuExtractDataExeFS": "ExeFS", - "GameListContextMenuExtractDataExeFSToolTip": "Extrai a seção ExeFS do jogo (incluindo atualizações)", - "GameListContextMenuExtractDataRomFS": "RomFS", - "GameListContextMenuExtractDataRomFSToolTip": "Extrai a seção RomFS do jogo (incluindo atualizações)", - "GameListContextMenuExtractDataLogo": "Logo", - "GameListContextMenuExtractDataLogoToolTip": "Extrai a seção Logo do jogo (incluindo atualizações)", - "GameListContextMenuCreateShortcut": "Criar atalho da aplicação", - "GameListContextMenuCreateShortcutToolTip": "Criar um atalho de área de trabalho que inicia o aplicativo selecionado", - "GameListContextMenuCreateShortcutToolTipMacOS": "Crie um atalho na pasta Aplicativos do macOS que abre o Aplicativo selecionado", - "GameListContextMenuOpenModsDirectory": "Abrir pasta de Mods", - "GameListContextMenuOpenModsDirectoryToolTip": "Abre a pasta que contém os mods da aplicação ", - "GameListContextMenuOpenSdModsDirectory": "Abrir diretório de mods Atmosphere", - "GameListContextMenuOpenSdModsDirectoryToolTip": "Opens the alternative SD card Atmosphere directory which contains Application's Mods. Useful for mods that are packaged for real hardware.", - "StatusBarGamesLoaded": "{0}/{1} jogos carregados", - "StatusBarSystemVersion": "Versão do firmware: {0}", - "LinuxVmMaxMapCountDialogTitle": "Limite baixo para mapeamentos de memória detectado", - "LinuxVmMaxMapCountDialogTextPrimary": "Você gostaria de aumentar o valor de vm.max_map_count para {0}", - "LinuxVmMaxMapCountDialogTextSecondary": "Alguns jogos podem tentar criar mais mapeamentos de memória do que o atualmente permitido. Ryujinx irá falhar assim que este limite for excedido.", - "LinuxVmMaxMapCountDialogButtonUntilRestart": "Sim, até a próxima reinicialização", - "LinuxVmMaxMapCountDialogButtonPersistent": "Sim, permanentemente", - "LinuxVmMaxMapCountWarningTextPrimary": "A quantidade máxima de mapeamentos de memória é menor que a recomendada.", - "LinuxVmMaxMapCountWarningTextSecondary": "O valor atual de vm.max_map_count ({0}) é menor que {1}. Alguns jogos podem tentar criar mais mapeamentos de memória do que o permitido no momento. Ryujinx vai falhar assim que este limite for excedido.\n\nTalvez você queira aumentar o limite manualmente ou instalar pkexec, o que permite que Ryujinx ajude com isso.", - "Settings": "Configurações", - "SettingsTabGeneral": "Geral", - "SettingsTabGeneralGeneral": "Geral", - "SettingsTabGeneralEnableDiscordRichPresence": "Habilitar Rich Presence do Discord", - "SettingsTabGeneralCheckUpdatesOnLaunch": "Verificar se há atualizações ao iniciar", - "SettingsTabGeneralShowConfirmExitDialog": "Exibir diálogo de confirmação ao sair", - "SettingsTabGeneralRememberWindowState": "Lembrar tamanho/posição da Janela", - "SettingsTabGeneralHideCursor": "Esconder o cursor do mouse:", - "SettingsTabGeneralHideCursorNever": "Nunca", - "SettingsTabGeneralHideCursorOnIdle": "Esconder o cursor quando ocioso", - "SettingsTabGeneralHideCursorAlways": "Sempre", - "SettingsTabGeneralGameDirectories": "Diretórios de jogo", - "SettingsTabGeneralAdd": "Adicionar", - "SettingsTabGeneralRemove": "Remover", - "SettingsTabSystem": "Sistema", - "SettingsTabSystemCore": "Principal", - "SettingsTabSystemSystemRegion": "Região do sistema:", - "SettingsTabSystemSystemRegionJapan": "Japão", - "SettingsTabSystemSystemRegionUSA": "EUA", - "SettingsTabSystemSystemRegionEurope": "Europa", - "SettingsTabSystemSystemRegionAustralia": "Austrália", - "SettingsTabSystemSystemRegionChina": "China", - "SettingsTabSystemSystemRegionKorea": "Coreia", - "SettingsTabSystemSystemRegionTaiwan": "Taiwan", - "SettingsTabSystemSystemLanguage": "Idioma do sistema:", - "SettingsTabSystemSystemLanguageJapanese": "Japonês", - "SettingsTabSystemSystemLanguageAmericanEnglish": "Inglês americano", - "SettingsTabSystemSystemLanguageFrench": "Francês", - "SettingsTabSystemSystemLanguageGerman": "Alemão", - "SettingsTabSystemSystemLanguageItalian": "Italiano", - "SettingsTabSystemSystemLanguageSpanish": "Espanhol", - "SettingsTabSystemSystemLanguageChinese": "Chinês", - "SettingsTabSystemSystemLanguageKorean": "Coreano", - "SettingsTabSystemSystemLanguageDutch": "Holandês", - "SettingsTabSystemSystemLanguagePortuguese": "Português", - "SettingsTabSystemSystemLanguageRussian": "Russo", - "SettingsTabSystemSystemLanguageTaiwanese": "Taiwanês", - "SettingsTabSystemSystemLanguageBritishEnglish": "Inglês britânico", - "SettingsTabSystemSystemLanguageCanadianFrench": "Francês canadense", - "SettingsTabSystemSystemLanguageLatinAmericanSpanish": "Espanhol latino", - "SettingsTabSystemSystemLanguageSimplifiedChinese": "Chinês simplificado", - "SettingsTabSystemSystemLanguageTraditionalChinese": "Chinês tradicional", - "SettingsTabSystemSystemTimeZone": "Fuso horário do sistema:", - "SettingsTabSystemSystemTime": "Hora do sistema:", - "SettingsTabSystemEnableVsync": "Habilitar sincronia vertical", - "SettingsTabSystemEnablePptc": "Habilitar PPTC (Profiled Persistent Translation Cache)", - "SettingsTabSystemEnableFsIntegrityChecks": "Habilitar verificação de integridade do sistema de arquivos", - "SettingsTabSystemAudioBackend": "Biblioteca de saída de áudio:", - "SettingsTabSystemAudioBackendDummy": "Nenhuma", - "SettingsTabSystemAudioBackendOpenAL": "OpenAL", - "SettingsTabSystemAudioBackendSoundIO": "SoundIO", - "SettingsTabSystemAudioBackendSDL2": "SDL2", - "SettingsTabSystemHacks": "Hacks", - "SettingsTabSystemHacksNote": " (Pode causar instabilidade)", - "SettingsTabSystemExpandDramSize": "Expandir memória para 6GiB", - "SettingsTabSystemIgnoreMissingServices": "Ignorar serviços não implementados", - "SettingsTabGraphics": "Gráficos", - "SettingsTabGraphicsAPI": "API gráfica", - "SettingsTabGraphicsEnableShaderCache": "Habilitar cache de shader", - "SettingsTabGraphicsAnisotropicFiltering": "Filtragem anisotrópica:", - "SettingsTabGraphicsAnisotropicFilteringAuto": "Automático", - "SettingsTabGraphicsAnisotropicFiltering2x": "2x", - "SettingsTabGraphicsAnisotropicFiltering4x": "4x", - "SettingsTabGraphicsAnisotropicFiltering8x": "8x", - "SettingsTabGraphicsAnisotropicFiltering16x": "16x", - "SettingsTabGraphicsResolutionScale": "Escala de resolução:", - "SettingsTabGraphicsResolutionScaleCustom": "Customizada (não recomendado)", - "SettingsTabGraphicsResolutionScaleNative": "Nativa (720p/1080p)", - "SettingsTabGraphicsResolutionScale2x": "2x (1440p/2160p)", - "SettingsTabGraphicsResolutionScale3x": "3x (2160p/3240p)", - "SettingsTabGraphicsResolutionScale4x": "4x (2880p/4320p) (não recomendado)", - "SettingsTabGraphicsAspectRatio": "Proporção:", - "SettingsTabGraphicsAspectRatio4x3": "4:3", - "SettingsTabGraphicsAspectRatio16x9": "16:9", - "SettingsTabGraphicsAspectRatio16x10": "16:10", - "SettingsTabGraphicsAspectRatio21x9": "21:9", - "SettingsTabGraphicsAspectRatio32x9": "32:9", - "SettingsTabGraphicsAspectRatioStretch": "Esticar até caber", - "SettingsTabGraphicsDeveloperOptions": "Opções do desenvolvedor", - "SettingsTabGraphicsShaderDumpPath": "Diretório para despejo de shaders:", - "SettingsTabLogging": "Log", - "SettingsTabLoggingLogging": "Log", - "SettingsTabLoggingEnableLoggingToFile": "Salvar logs em arquivo", - "SettingsTabLoggingEnableStubLogs": "Habilitar logs de stub", - "SettingsTabLoggingEnableInfoLogs": "Habilitar logs de informação", - "SettingsTabLoggingEnableWarningLogs": "Habilitar logs de alerta", - "SettingsTabLoggingEnableErrorLogs": "Habilitar logs de erro", - "SettingsTabLoggingEnableTraceLogs": "Habilitar logs de rastreamento", - "SettingsTabLoggingEnableGuestLogs": "Habilitar logs do programa convidado", - "SettingsTabLoggingEnableFsAccessLogs": "Habilitar logs de acesso ao sistema de arquivos", - "SettingsTabLoggingFsGlobalAccessLogMode": "Modo global de logs do sistema de arquivos:", - "SettingsTabLoggingDeveloperOptions": "Opções do desenvolvedor (AVISO: Vai reduzir a performance)", - "SettingsTabLoggingDeveloperOptionsNote": "AVISO: Reduzirá o desempenho", - "SettingsTabLoggingGraphicsBackendLogLevel": "Nível de log do backend gráfico:", - "SettingsTabLoggingGraphicsBackendLogLevelNone": "Nenhum", - "SettingsTabLoggingGraphicsBackendLogLevelError": "Erro", - "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "Lentidão", - "SettingsTabLoggingGraphicsBackendLogLevelAll": "Todos", - "SettingsTabLoggingEnableDebugLogs": "Habilitar logs de depuração", - "SettingsTabInput": "Controle", - "SettingsTabInputEnableDockedMode": "Habilitar modo TV", - "SettingsTabInputDirectKeyboardAccess": "Acesso direto ao teclado", - "SettingsButtonSave": "Salvar", - "SettingsButtonClose": "Fechar", - "SettingsButtonOk": "OK", - "SettingsButtonCancel": "Cancelar", - "SettingsButtonApply": "Aplicar", - "ControllerSettingsPlayer": "Jogador", - "ControllerSettingsPlayer1": "Jogador 1", - "ControllerSettingsPlayer2": "Jogador 2", - "ControllerSettingsPlayer3": "Jogador 3", - "ControllerSettingsPlayer4": "Jogador 4", - "ControllerSettingsPlayer5": "Jogador 5", - "ControllerSettingsPlayer6": "Jogador 6", - "ControllerSettingsPlayer7": "Jogador 7", - "ControllerSettingsPlayer8": "Jogador 8", - "ControllerSettingsHandheld": "Portátil", - "ControllerSettingsInputDevice": "Dispositivo de entrada", - "ControllerSettingsRefresh": "Atualizar", - "ControllerSettingsDeviceDisabled": "Desabilitado", - "ControllerSettingsControllerType": "Tipo do controle", - "ControllerSettingsControllerTypeHandheld": "Portátil", - "ControllerSettingsControllerTypeProController": "Pro Controller", - "ControllerSettingsControllerTypeJoyConPair": "Par de JoyCon", - "ControllerSettingsControllerTypeJoyConLeft": "JoyCon esquerdo", - "ControllerSettingsControllerTypeJoyConRight": "JoyCon direito", - "ControllerSettingsProfile": "Perfil", - "ControllerSettingsProfileDefault": "Padrão", - "ControllerSettingsLoad": "Carregar", - "ControllerSettingsAdd": "Adicionar", - "ControllerSettingsRemove": "Remover", - "ControllerSettingsButtons": "Botões", - "ControllerSettingsButtonA": "A", - "ControllerSettingsButtonB": "B", - "ControllerSettingsButtonX": "X", - "ControllerSettingsButtonY": "Y", - "ControllerSettingsButtonPlus": "+", - "ControllerSettingsButtonMinus": "-", - "ControllerSettingsDPad": "Direcional", - "ControllerSettingsDPadUp": "Cima", - "ControllerSettingsDPadDown": "Baixo", - "ControllerSettingsDPadLeft": "Esquerda", - "ControllerSettingsDPadRight": "Direita", - "ControllerSettingsStickButton": "Botão", - "ControllerSettingsStickUp": "Cima", - "ControllerSettingsStickDown": "Baixo", - "ControllerSettingsStickLeft": "Esquerda", - "ControllerSettingsStickRight": "Direita", - "ControllerSettingsStickStick": "Analógico", - "ControllerSettingsStickInvertXAxis": "Inverter eixo X", - "ControllerSettingsStickInvertYAxis": "Inverter eixo Y", - "ControllerSettingsStickDeadzone": "Zona morta:", - "ControllerSettingsLStick": "Analógico esquerdo", - "ControllerSettingsRStick": "Analógico direito", - "ControllerSettingsTriggersLeft": "Gatilhos esquerda", - "ControllerSettingsTriggersRight": "Gatilhos direita", - "ControllerSettingsTriggersButtonsLeft": "Botões de gatilho esquerda", - "ControllerSettingsTriggersButtonsRight": "Botões de gatilho direita", - "ControllerSettingsTriggers": "Gatilhos", - "ControllerSettingsTriggerL": "L", - "ControllerSettingsTriggerR": "R", - "ControllerSettingsTriggerZL": "ZL", - "ControllerSettingsTriggerZR": "ZR", - "ControllerSettingsLeftSL": "SL", - "ControllerSettingsLeftSR": "SR", - "ControllerSettingsRightSL": "SL", - "ControllerSettingsRightSR": "SR", - "ControllerSettingsExtraButtonsLeft": "Botões esquerda", - "ControllerSettingsExtraButtonsRight": "Botões direita", - "ControllerSettingsMisc": "Miscelâneas", - "ControllerSettingsTriggerThreshold": "Sensibilidade do gatilho:", - "ControllerSettingsMotion": "Sensor de movimento", - "ControllerSettingsMotionUseCemuhookCompatibleMotion": "Usar sensor compatível com CemuHook", - "ControllerSettingsMotionControllerSlot": "Slot do controle:", - "ControllerSettingsMotionMirrorInput": "Espelhar movimento", - "ControllerSettingsMotionRightJoyConSlot": "Slot do JoyCon direito:", - "ControllerSettingsMotionServerHost": "Endereço do servidor:", - "ControllerSettingsMotionGyroSensitivity": "Sensibilidade do giroscópio:", - "ControllerSettingsMotionGyroDeadzone": "Zona morta do giroscópio:", - "ControllerSettingsSave": "Salvar", - "ControllerSettingsClose": "Fechar", - "KeyUnknown": "Unknown", - "KeyShiftLeft": "Shift Left", - "KeyShiftRight": "Shift Right", - "KeyControlLeft": "Ctrl Left", - "KeyMacControlLeft": "⌃ Left", - "KeyControlRight": "Ctrl Right", - "KeyMacControlRight": "⌃ Right", - "KeyAltLeft": "Alt Left", - "KeyMacAltLeft": "⌥ Left", - "KeyAltRight": "Alt Right", - "KeyMacAltRight": "⌥ Right", - "KeyWinLeft": "⊞ Left", - "KeyMacWinLeft": "⌘ Left", - "KeyWinRight": "⊞ Right", - "KeyMacWinRight": "⌘ Right", - "KeyMenu": "Menu", - "KeyUp": "Up", - "KeyDown": "Down", - "KeyLeft": "Left", - "KeyRight": "Right", - "KeyEnter": "Enter", - "KeyEscape": "Escape", - "KeySpace": "Space", - "KeyTab": "Tab", - "KeyBackSpace": "Backspace", - "KeyInsert": "Insert", - "KeyDelete": "Delete", - "KeyPageUp": "Page Up", - "KeyPageDown": "Page Down", - "KeyHome": "Home", - "KeyEnd": "End", - "KeyCapsLock": "Caps Lock", - "KeyScrollLock": "Scroll Lock", - "KeyPrintScreen": "Print Screen", - "KeyPause": "Pause", - "KeyNumLock": "Num Lock", - "KeyClear": "Clear", - "KeyKeypad0": "Keypad 0", - "KeyKeypad1": "Keypad 1", - "KeyKeypad2": "Keypad 2", - "KeyKeypad3": "Keypad 3", - "KeyKeypad4": "Keypad 4", - "KeyKeypad5": "Keypad 5", - "KeyKeypad6": "Keypad 6", - "KeyKeypad7": "Keypad 7", - "KeyKeypad8": "Keypad 8", - "KeyKeypad9": "Keypad 9", - "KeyKeypadDivide": "Keypad Divide", - "KeyKeypadMultiply": "Keypad Multiply", - "KeyKeypadSubtract": "Keypad Subtract", - "KeyKeypadAdd": "Keypad Add", - "KeyKeypadDecimal": "Keypad Decimal", - "KeyKeypadEnter": "Keypad Enter", - "KeyNumber0": "0", - "KeyNumber1": "1", - "KeyNumber2": "2", - "KeyNumber3": "3", - "KeyNumber4": "4", - "KeyNumber5": "5", - "KeyNumber6": "6", - "KeyNumber7": "7", - "KeyNumber8": "8", - "KeyNumber9": "9", - "KeyTilde": "~", - "KeyGrave": "`", - "KeyMinus": "-", - "KeyPlus": "+", - "KeyBracketLeft": "[", - "KeyBracketRight": "]", - "KeySemicolon": ";", - "KeyQuote": "\"", - "KeyComma": ",", - "KeyPeriod": ".", - "KeySlash": "/", - "KeyBackSlash": "\\", - "KeyUnbound": "Unbound", - "GamepadLeftStick": "L Stick Button", - "GamepadRightStick": "R Stick Button", - "GamepadLeftShoulder": "Left Shoulder", - "GamepadRightShoulder": "Right Shoulder", - "GamepadLeftTrigger": "Left Trigger", - "GamepadRightTrigger": "Right Trigger", - "GamepadDpadUp": "Up", - "GamepadDpadDown": "Down", - "GamepadDpadLeft": "Left", - "GamepadDpadRight": "Right", - "GamepadMinus": "-", - "GamepadPlus": "+", - "GamepadGuide": "Guide", - "GamepadMisc1": "Misc", - "GamepadPaddle1": "Paddle 1", - "GamepadPaddle2": "Paddle 2", - "GamepadPaddle3": "Paddle 3", - "GamepadPaddle4": "Paddle 4", - "GamepadTouchpad": "Touchpad", - "GamepadSingleLeftTrigger0": "Left Trigger 0", - "GamepadSingleRightTrigger0": "Right Trigger 0", - "GamepadSingleLeftTrigger1": "Left Trigger 1", - "GamepadSingleRightTrigger1": "Right Trigger 1", - "StickLeft": "Left Stick", - "StickRight": "Right Stick", - "UserProfilesSelectedUserProfile": "Perfil de usuário selecionado:", - "UserProfilesSaveProfileName": "Salvar nome de perfil", - "UserProfilesChangeProfileImage": "Mudar imagem de perfil", - "UserProfilesAvailableUserProfiles": "Perfis de usuário disponíveis:", - "UserProfilesAddNewProfile": "Adicionar novo perfil", - "UserProfilesDelete": "Apagar", - "UserProfilesClose": "Fechar", - "ProfileNameSelectionWatermark": "Escolha um apelido", - "ProfileImageSelectionTitle": "Seleção da imagem de perfil", - "ProfileImageSelectionHeader": "Escolha uma imagem de perfil", - "ProfileImageSelectionNote": "Você pode importar uma imagem customizada, ou selecionar um avatar do firmware", - "ProfileImageSelectionImportImage": "Importar arquivo de imagem", - "ProfileImageSelectionSelectAvatar": "Selecionar avatar do firmware", - "InputDialogTitle": "Diálogo de texto", - "InputDialogOk": "OK", - "InputDialogCancel": "Cancelar", - "InputDialogAddNewProfileTitle": "Escolha o nome de perfil", - "InputDialogAddNewProfileHeader": "Escreva o nome do perfil", - "InputDialogAddNewProfileSubtext": "(Máximo de caracteres: {0})", - "AvatarChoose": "Escolher", - "AvatarSetBackgroundColor": "Definir cor de fundo", - "AvatarClose": "Fechar", - "ControllerSettingsLoadProfileToolTip": "Carregar perfil", - "ControllerSettingsAddProfileToolTip": "Adicionar perfil", - "ControllerSettingsRemoveProfileToolTip": "Remover perfil", - "ControllerSettingsSaveProfileToolTip": "Salvar perfil", - "MenuBarFileToolsTakeScreenshot": "Salvar captura de tela", - "MenuBarFileToolsHideUi": "Esconder Interface", - "GameListContextMenuRunApplication": "Executar Aplicativo", - "GameListContextMenuToggleFavorite": "Alternar favorito", - "GameListContextMenuToggleFavoriteToolTip": "Marca ou desmarca jogo como favorito", - "SettingsTabGeneralTheme": "Tema:", - "SettingsTabGeneralThemeDark": "Escuro", - "SettingsTabGeneralThemeLight": "Claro", - "ControllerSettingsConfigureGeneral": "Configurar", - "ControllerSettingsRumble": "Vibração", - "ControllerSettingsRumbleStrongMultiplier": "Multiplicador de vibração forte", - "ControllerSettingsRumbleWeakMultiplier": "Multiplicador de vibração fraca", - "DialogMessageSaveNotAvailableMessage": "Não há jogos salvos para {0} [{1:x16}]", - "DialogMessageSaveNotAvailableCreateSaveMessage": "Gostaria de criar o diretório de salvamento para esse jogo?", - "DialogConfirmationTitle": "Ryujinx - Confirmação", - "DialogUpdaterTitle": "Ryujinx - Atualizador", - "DialogErrorTitle": "Ryujinx - Erro", - "DialogWarningTitle": "Ryujinx - Alerta", - "DialogExitTitle": "Ryujinx - Sair", - "DialogErrorMessage": "Ryujinx encontrou um erro", - "DialogExitMessage": "Tem certeza que deseja fechar o Ryujinx?", - "DialogExitSubMessage": "Todos os dados que não foram salvos serão perdidos!", - "DialogMessageCreateSaveErrorMessage": "Ocorreu um erro ao criar o diretório de salvamento: {0}", - "DialogMessageFindSaveErrorMessage": "Ocorreu um erro ao tentar encontrar o diretório de salvamento: {0}", - "FolderDialogExtractTitle": "Escolha o diretório onde os arquivos serão extraídos", - "DialogNcaExtractionMessage": "Extraindo seção {0} de {1}...", - "DialogNcaExtractionTitle": "Ryujinx - Extrator de seções NCA", - "DialogNcaExtractionMainNcaNotFoundErrorMessage": "Falha na extração. O NCA principal não foi encontrado no arquivo selecionado.", - "DialogNcaExtractionCheckLogErrorMessage": "Falha na extração. Leia o arquivo de log para mais informações.", - "DialogNcaExtractionSuccessMessage": "Extração concluída com êxito.", - "DialogUpdaterConvertFailedMessage": "Falha ao converter a versão atual do Ryujinx.", - "DialogUpdaterCancelUpdateMessage": "Cancelando atualização!", - "DialogUpdaterAlreadyOnLatestVersionMessage": "Você já está usando a versão mais recente do Ryujinx!", - "DialogUpdaterFailedToGetVersionMessage": "Ocorreu um erro ao tentar obter as informações de atualização do GitHub Release. Isso pode ser causado se uma nova versão estiver sendo compilado pelas Ações do GitHub. Tente novamente em alguns minutos.", - "DialogUpdaterConvertFailedGithubMessage": "Falha ao converter a versão do Ryujinx recebida do AppVeyor.", - "DialogUpdaterDownloadingMessage": "Baixando atualização...", - "DialogUpdaterExtractionMessage": "Extraindo atualização...", - "DialogUpdaterRenamingMessage": "Renomeando atualização...", - "DialogUpdaterAddingFilesMessage": "Adicionando nova atualização...", - "DialogUpdaterCompleteMessage": "Atualização concluída!", - "DialogUpdaterRestartMessage": "Deseja reiniciar o Ryujinx agora?", - "DialogUpdaterNoInternetMessage": "Você não está conectado à Internet!", - "DialogUpdaterNoInternetSubMessage": "Por favor, certifique-se de que você tem uma conexão funcional à Internet!", - "DialogUpdaterDirtyBuildMessage": "Você não pode atualizar uma compilação Dirty do Ryujinx!", - "DialogUpdaterDirtyBuildSubMessage": "Por favor, baixe o Ryujinx em https://ryujinx.org/ se está procurando por uma versão suportada.", - "DialogRestartRequiredMessage": "Reinicialização necessária", - "DialogThemeRestartMessage": "O tema foi salvo. Uma reinicialização é necessária para aplicar o tema.", - "DialogThemeRestartSubMessage": "Deseja reiniciar?", - "DialogFirmwareInstallEmbeddedMessage": "Gostaria de instalar o firmware incluso neste jogo? (Firmware {0})", - "DialogFirmwareInstallEmbeddedSuccessMessage": "No installed firmware was found but Ryujinx was able to install firmware {0} from the provided game.\nThe emulator will now start.", - "DialogFirmwareNoFirmwareInstalledMessage": "Firmware não foi instalado", - "DialogFirmwareInstalledMessage": "Firmware {0} foi instalado", - "DialogInstallFileTypesSuccessMessage": "Tipos de arquivo instalados com sucesso!", - "DialogInstallFileTypesErrorMessage": "Falha ao instalar tipos de arquivo.", - "DialogUninstallFileTypesSuccessMessage": "Tipos de arquivo desinstalados com sucesso!", - "DialogUninstallFileTypesErrorMessage": "Falha ao desinstalar tipos de arquivo.", - "DialogOpenSettingsWindowLabel": "Abrir janela de configurações", - "DialogControllerAppletTitle": "Applet de controle", - "DialogMessageDialogErrorExceptionMessage": "Erro ao exibir diálogo de mensagem: {0}", - "DialogSoftwareKeyboardErrorExceptionMessage": "Erro ao exibir teclado virtual: {0}", - "DialogErrorAppletErrorExceptionMessage": "Erro ao exibir applet ErrorApplet: {0}", - "DialogUserErrorDialogMessage": "{0}: {1}", - "DialogUserErrorDialogInfoMessage": "\nPara mais informações sobre como corrigir esse erro, siga nosso Guia de Configuração.", - "DialogUserErrorDialogTitle": "Erro do Ryujinx ({0})", - "DialogAmiiboApiTitle": "API Amiibo", - "DialogAmiiboApiFailFetchMessage": "Um erro ocorreu ao tentar obter informações da API.", - "DialogAmiiboApiConnectErrorMessage": "Não foi possível conectar ao servidor da API Amiibo. O serviço pode estar fora do ar ou você precisa verificar sua conexão com a Internet.", - "DialogProfileInvalidProfileErrorMessage": "Perfil {0} é incompatível com o sistema de configuração de controle atual.", - "DialogProfileDefaultProfileOverwriteErrorMessage": "O perfil Padrão não pode ser substituído", - "DialogProfileDeleteProfileTitle": "Apagando perfil", - "DialogProfileDeleteProfileMessage": "Essa ação é irreversível, tem certeza que deseja continuar?", - "DialogWarning": "Alerta", - "DialogPPTCDeletionMessage": "Você está prestes a apagar o cache PPTC para :\n\n{0}\n\nTem certeza que deseja continuar?", - "DialogPPTCDeletionErrorMessage": "Erro apagando cache PPTC em {0}: {1}", - "DialogShaderDeletionMessage": "Você está prestes a apagar o cache de Shader para :\n\n{0}\n\nTem certeza que deseja continuar?", - "DialogShaderDeletionErrorMessage": "Erro apagando o cache de Shader em {0}: {1}", - "DialogRyujinxErrorMessage": "Ryujinx encontrou um erro", - "DialogInvalidTitleIdErrorMessage": "Erro de interface: O jogo selecionado não tem um ID de título válido", - "DialogFirmwareInstallerFirmwareNotFoundErrorMessage": "Um firmware de sistema válido não foi encontrado em {0}.", - "DialogFirmwareInstallerFirmwareInstallTitle": "Instalar firmware {0}", - "DialogFirmwareInstallerFirmwareInstallMessage": "A versão do sistema {0} será instalada.", - "DialogFirmwareInstallerFirmwareInstallSubMessage": "\n\nIsso substituirá a versão do sistema atual {0}.", - "DialogFirmwareInstallerFirmwareInstallConfirmMessage": "\n\nDeseja continuar?", - "DialogFirmwareInstallerFirmwareInstallWaitMessage": "Instalando firmware...", - "DialogFirmwareInstallerFirmwareInstallSuccessMessage": "Versão do sistema {0} instalada com sucesso.", - "DialogUserProfileDeletionWarningMessage": "Não haveria nenhum perfil selecionado se o perfil atual fosse deletado", - "DialogUserProfileDeletionConfirmMessage": "Deseja deletar o perfil selecionado", - "DialogUserProfileUnsavedChangesTitle": "Alerta - Alterações não salvas", - "DialogUserProfileUnsavedChangesMessage": "Você fez alterações para este perfil de usuário que não foram salvas.", - "DialogUserProfileUnsavedChangesSubMessage": "Deseja descartar as alterações?", - "DialogControllerSettingsModifiedConfirmMessage": "As configurações de controle atuais foram atualizadas.", - "DialogControllerSettingsModifiedConfirmSubMessage": "Deseja salvar?", - "DialogLoadFileErrorMessage": "{0}. Errored File: {1}", - "DialogModAlreadyExistsMessage": "Mod already exists", - "DialogModInvalidMessage": "The specified directory does not contain a mod!", - "DialogModDeleteNoParentMessage": "Failed to Delete: Could not find the parent directory for mod \"{0}\"!", - "DialogDlcNoDlcErrorMessage": "O arquivo especificado não contém DLCs para o título selecionado!", - "DialogPerformanceCheckLoggingEnabledMessage": "Os logs de depuração estão ativos, esse recurso é feito para ser usado apenas por desenvolvedores.", - "DialogPerformanceCheckLoggingEnabledConfirmMessage": "Para melhor performance, é recomendável desabilitar os logs de depuração. Gostaria de desabilitar os logs de depuração agora?", - "DialogPerformanceCheckShaderDumpEnabledMessage": "O despejo de shaders está ativo, esse recurso é feito para ser usado apenas por desenvolvedores.", - "DialogPerformanceCheckShaderDumpEnabledConfirmMessage": "Para melhor performance, é recomendável desabilitar o despejo de shaders. Gostaria de desabilitar o despejo de shaders agora?", - "DialogLoadAppGameAlreadyLoadedMessage": "Um jogo já foi carregado", - "DialogLoadAppGameAlreadyLoadedSubMessage": "Por favor, pare a emulação ou feche o emulador antes de abrir outro jogo.", - "DialogUpdateAddUpdateErrorMessage": "O arquivo especificado não contém atualizações para o título selecionado!", - "DialogSettingsBackendThreadingWarningTitle": "Alerta - Threading da API gráfica", - "DialogSettingsBackendThreadingWarningMessage": "Ryujinx precisa ser reiniciado após mudar essa opção para que ela tenha efeito. Dependendo da sua plataforma, pode ser preciso desabilitar o multithreading do driver de vídeo quando usar o Ryujinx.", - "DialogModManagerDeletionWarningMessage": "You are about to delete the mod: {0}\n\nAre you sure you want to proceed?", - "DialogModManagerDeletionAllWarningMessage": "You are about to delete all mods for this title.\n\nAre you sure you want to proceed?", - "SettingsTabGraphicsFeaturesOptions": "Recursos", - "SettingsTabGraphicsBackendMultithreading": "Multithreading da API gráfica:", - "CommonAuto": "Automático", - "CommonOff": "Desligado", - "CommonOn": "Ligado", - "InputDialogYes": "Sim", - "InputDialogNo": "Não", - "DialogProfileInvalidProfileNameErrorMessage": "O nome do arquivo contém caracteres inválidos. Por favor, tente novamente.", - "MenuBarOptionsPauseEmulation": "Pausar", - "MenuBarOptionsResumeEmulation": "Resumir", - "AboutUrlTooltipMessage": "Clique para abrir o site do Ryujinx no seu navegador padrão.", - "AboutDisclaimerMessage": "Ryujinx não é afiliado com a Nintendo™,\nou qualquer um de seus parceiros, de nenhum modo.", - "AboutAmiiboDisclaimerMessage": "AmiiboAPI (www.amiiboapi.com) é usado\nem nossa emulação de Amiibo.", - "AboutPatreonUrlTooltipMessage": "Clique para abrir a página do Patreon do Ryujinx no seu navegador padrão.", - "AboutGithubUrlTooltipMessage": "Clique para abrir a página do GitHub do Ryujinx no seu navegador padrão.", - "AboutDiscordUrlTooltipMessage": "Clique para abrir um convite ao servidor do Discord do Ryujinx no seu navegador padrão.", - "AboutTwitterUrlTooltipMessage": "Clique para abrir a página do Twitter do Ryujinx no seu navegador padrão.", - "AboutRyujinxAboutTitle": "Sobre:", - "AboutRyujinxAboutContent": "Ryujinx é um emulador de Nintendo Switch™.\nPor favor, nos dê apoio no Patreon.\nFique por dentro de todas as novidades no Twitter ou Discord.\nDesenvolvedores com interesse em contribuir podem conseguir mais informações no GitHub ou Discord.", - "AboutRyujinxMaintainersTitle": "Mantido por:", - "AboutRyujinxMaintainersContentTooltipMessage": "Clique para abrir a página de contribuidores no seu navegador padrão.", - "AboutRyujinxSupprtersTitle": "Apoiado no Patreon por:", - "AmiiboSeriesLabel": "Franquia Amiibo", - "AmiiboCharacterLabel": "Personagem", - "AmiiboScanButtonLabel": "Escanear", - "AmiiboOptionsShowAllLabel": "Exibir todos os Amiibos", - "AmiiboOptionsUsRandomTagLabel": "Hack: Usar Uuid de tag aleatório", - "DlcManagerTableHeadingEnabledLabel": "Habilitado", - "DlcManagerTableHeadingTitleIdLabel": "ID do título", - "DlcManagerTableHeadingContainerPathLabel": "Caminho do container", - "DlcManagerTableHeadingFullPathLabel": "Caminho completo", - "DlcManagerRemoveAllButton": "Remover todos", - "DlcManagerEnableAllButton": "Habilitar todos", - "DlcManagerDisableAllButton": "Desabilitar todos", - "ModManagerDeleteAllButton": "Delete All", - "MenuBarOptionsChangeLanguage": "Mudar idioma", - "MenuBarShowFileTypes": "Mostrar tipos de arquivo", - "CommonSort": "Ordenar", - "CommonShowNames": "Exibir nomes", - "CommonFavorite": "Favorito", - "OrderAscending": "Ascendente", - "OrderDescending": "Descendente", - "SettingsTabGraphicsFeatures": "Recursos & Melhorias", - "ErrorWindowTitle": "Janela de erro", - "ToggleDiscordTooltip": "Habilita ou desabilita Discord Rich Presence", - "AddGameDirBoxTooltip": "Escreva um diretório de jogo para adicionar à lista", - "AddGameDirTooltip": "Adicionar um diretório de jogo à lista", - "RemoveGameDirTooltip": "Remover diretório de jogo selecionado", - "CustomThemeCheckTooltip": "Habilita ou desabilita temas customizados na interface gráfica", - "CustomThemePathTooltip": "Diretório do tema customizado", - "CustomThemeBrowseTooltip": "Navegar até um tema customizado", - "DockModeToggleTooltip": "Habilita ou desabilita modo TV", - "DirectKeyboardTooltip": "Direct keyboard access (HID) support. Provides games access to your keyboard as a text entry device.\n\nOnly works with games that natively support keyboard usage on Switch hardware.\n\nLeave OFF if unsure.", - "DirectMouseTooltip": "Direct mouse access (HID) support. Provides games access to your mouse as a pointing device.\n\nOnly works with games that natively support mouse controls on Switch hardware, which are few and far between.\n\nWhen enabled, touch screen functionality may not work.\n\nLeave OFF if unsure.", - "RegionTooltip": "Mudar a região do sistema", - "LanguageTooltip": "Mudar o idioma do sistema", - "TimezoneTooltip": "Mudar o fuso-horário do sistema", - "TimeTooltip": "Mudar a hora do sistema", - "VSyncToggleTooltip": "Emulated console's Vertical Sync. Essentially a frame-limiter for the majority of games; disabling it may cause games to run at higher speed or make loading screens take longer or get stuck.\n\nCan be toggled in-game with a hotkey of your preference (F1 by default). We recommend doing this if you plan on disabling it.\n\nLeave ON if unsure.", - "PptcToggleTooltip": "Habilita ou desabilita PPTC", - "FsIntegrityToggleTooltip": "Habilita ou desabilita verificação de integridade dos arquivos do jogo", - "AudioBackendTooltip": "Mudar biblioteca de áudio", - "MemoryManagerTooltip": "Muda como a memória do sistema convidado é acessada. Tem um grande impacto na performance da CPU emulada.", - "MemoryManagerSoftwareTooltip": "Usar uma tabela de página via software para tradução de endereços. Maior precisão, porém performance mais baixa.", - "MemoryManagerHostTooltip": "Mapeia memória no espaço de endereço hóspede diretamente. Compilação e execução do JIT muito mais rápida.", - "MemoryManagerUnsafeTooltip": "Mapeia memória diretamente, mas sem limitar o acesso ao espaço de endereçamento do sistema convidado. Mais rápido, porém menos seguro. O aplicativo convidado pode acessar memória de qualquer parte do Ryujinx, então apenas rode programas em que você confia nesse modo.", - "UseHypervisorTooltip": "Usa o Hypervisor em vez de JIT (recompilador dinâmico). Melhora significativamente o desempenho quando disponível, mas pode ser instável no seu estado atual.", - "DRamTooltip": "Expande a memória do sistema emulado de 4GiB para 6GiB", - "IgnoreMissingServicesTooltip": "Habilita ou desabilita a opção de ignorar serviços não implementados", - "GraphicsBackendThreadingTooltip": "Habilita multithreading do backend gráfico", - "GalThreadingTooltip": "Executa comandos do backend gráfico em uma segunda thread. Permite multithreading em tempo de execução da compilação de shader, diminui os travamentos, e melhora performance em drivers sem suporte embutido a multithreading. Pequena variação na performance máxima em drivers com suporte a multithreading. Ryujinx pode precisar ser reiniciado para desabilitar adequadamente o multithreading embutido do driver, ou você pode precisar fazer isso manualmente para ter a melhor performance.", - "ShaderCacheToggleTooltip": "Habilita ou desabilita o cache de shader", - "ResolutionScaleTooltip": "Multiplies the game's rendering resolution.\n\nA few games may not work with this and look pixelated even when the resolution is increased; for those games, you may need to find mods that remove anti-aliasing or that increase their internal rendering resolution. For using the latter, you'll likely want to select Native.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nKeep in mind 4x is overkill for virtually any setup.", - "ResolutionScaleEntryTooltip": "Escala de resolução de ponto flutuante, como 1.5. Valores não inteiros tem probabilidade maior de causar problemas ou quebras.", - "AnisotropyTooltip": "Level of Anisotropic Filtering. Set to Auto to use the value requested by the game.", - "AspectRatioTooltip": "Aspect Ratio applied to the renderer window.\n\nOnly change this if you're using an aspect ratio mod for your game, otherwise the graphics will be stretched.\n\nLeave on 16:9 if unsure.", - "ShaderDumpPathTooltip": "Diretòrio de despejo de shaders", - "FileLogTooltip": "Habilita ou desabilita log para um arquivo no disco", - "StubLogTooltip": "Habilita ou desabilita exibição de mensagens de stub", - "InfoLogTooltip": "Habilita ou desabilita exibição de mensagens informativas", - "WarnLogTooltip": "Habilita ou desabilita exibição de mensagens de alerta", - "ErrorLogTooltip": "Habilita ou desabilita exibição de mensagens de erro", - "TraceLogTooltip": "Habilita ou desabilita exibição de mensagens de rastreamento", - "GuestLogTooltip": "Habilita ou desabilita exibição de mensagens do programa convidado", - "FileAccessLogTooltip": "Habilita ou desabilita exibição de mensagens do acesso de arquivos", - "FSAccessLogModeTooltip": "Habilita exibição de mensagens de acesso ao sistema de arquivos no console. Modos permitidos são 0-3", - "DeveloperOptionTooltip": "Use com cuidado", - "OpenGlLogLevel": "Requer que os níveis de log apropriados estejaam habilitados", - "DebugLogTooltip": "Habilita exibição de mensagens de depuração", - "LoadApplicationFileTooltip": "Abre o navegador de arquivos para seleção de um arquivo do Switch compatível a ser carregado", - "LoadApplicationFolderTooltip": "Abre o navegador de pastas para seleção de pasta extraída do Switch compatível a ser carregada", - "OpenRyujinxFolderTooltip": "Abre o diretório do sistema de arquivos do Ryujinx", - "OpenRyujinxLogsTooltip": "Abre o diretório onde os logs são salvos", - "ExitTooltip": "Sair do Ryujinx", - "OpenSettingsTooltip": "Abrir janela de configurações", - "OpenProfileManagerTooltip": "Abrir janela de gerenciamento de perfis", - "StopEmulationTooltip": "Parar emulação do jogo atual e voltar a seleção de jogos", - "CheckUpdatesTooltip": "Verificar por atualizações para o Ryujinx", - "OpenAboutTooltip": "Abrir janela sobre", - "GridSize": "Tamanho da grade", - "GridSizeTooltip": "Mudar tamanho dos items da grade", - "SettingsTabSystemSystemLanguageBrazilianPortuguese": "Português do Brasil", - "AboutRyujinxContributorsButtonHeader": "Ver todos os contribuidores", - "SettingsTabSystemAudioVolume": "Volume:", - "AudioVolumeTooltip": "Mudar volume do áudio", - "SettingsTabSystemEnableInternetAccess": "Habilitar acesso à internet do programa convidado", - "EnableInternetAccessTooltip": "Habilita acesso à internet do programa convidado. Se habilitado, o aplicativo vai se comportar como se o sistema Switch emulado estivesse conectado a Internet. Note que em alguns casos, aplicativos podem acessar a Internet mesmo com essa opção desabilitada", - "GameListContextMenuManageCheatToolTip": "Gerenciar Cheats", - "GameListContextMenuManageCheat": "Gerenciar Cheats", - "GameListContextMenuManageModToolTip": "Manage Mods", - "GameListContextMenuManageMod": "Manage Mods", - "ControllerSettingsStickRange": "Intervalo:", - "DialogStopEmulationTitle": "Ryujinx - Parar emulação", - "DialogStopEmulationMessage": "Tem certeza que deseja parar a emulação?", - "SettingsTabCpu": "CPU", - "SettingsTabAudio": "Áudio", - "SettingsTabNetwork": "Rede", - "SettingsTabNetworkConnection": "Conexão de rede", - "SettingsTabCpuCache": "Cache da CPU", - "SettingsTabCpuMemory": "Memória da CPU", - "DialogUpdaterFlatpakNotSupportedMessage": "Por favor, atualize o Ryujinx pelo FlatHub.", - "UpdaterDisabledWarningTitle": "Atualizador desabilitado!", - "ControllerSettingsRotate90": "Rodar 90° sentido horário", - "IconSize": "Tamanho do ícone", - "IconSizeTooltip": "Muda o tamanho do ícone do jogo", - "MenuBarOptionsShowConsole": "Exibir console", - "ShaderCachePurgeError": "Erro ao deletar o shader em {0}: {1}", - "UserErrorNoKeys": "Chaves não encontradas", - "UserErrorNoFirmware": "Firmware não encontrado", - "UserErrorFirmwareParsingFailed": "Erro na leitura do Firmware", - "UserErrorApplicationNotFound": "Aplicativo não encontrado", - "UserErrorUnknown": "Erro desconhecido", - "UserErrorUndefined": "Erro indefinido", - "UserErrorNoKeysDescription": "Ryujinx não conseguiu encontrar o seu arquivo 'prod.keys'", - "UserErrorNoFirmwareDescription": "Ryujinx não conseguiu encontrar nenhum Firmware instalado", - "UserErrorFirmwareParsingFailedDescription": "Ryujinx não conseguiu ler o Firmware fornecido. Geralmente isso é causado por chaves desatualizadas.", - "UserErrorApplicationNotFoundDescription": "Ryujinx não conseguiu encontrar um aplicativo válido no caminho fornecido.", - "UserErrorUnknownDescription": "Um erro desconhecido foi encontrado!", - "UserErrorUndefinedDescription": "Um erro indefinido occoreu! Isso não deveria acontecer, por favor contate um desenvolvedor!", - "OpenSetupGuideMessage": "Abrir o guia de configuração", - "NoUpdate": "Sem atualizações", - "TitleUpdateVersionLabel": "Versão {0} - {1}", - "RyujinxInfo": "Ryujinx - Informação", - "RyujinxConfirm": "Ryujinx - Confirmação", - "FileDialogAllTypes": "Todos os tipos", - "Never": "Nunca", - "SwkbdMinCharacters": "Deve ter pelo menos {0} caracteres", - "SwkbdMinRangeCharacters": "Deve ter entre {0}-{1} caracteres", - "SoftwareKeyboard": "Teclado por Software", - "SoftwareKeyboardModeNumeric": "Deve ser somente 0-9 ou '.'", - "SoftwareKeyboardModeAlphabet": "Apenas devem ser caracteres não CJK.", - "SoftwareKeyboardModeASCII": "Deve ser apenas texto ASCII", - "ControllerAppletControllers": "Supported Controllers:", - "ControllerAppletPlayers": "Players:", - "ControllerAppletDescription": "Your current configuration is invalid. Open settings and reconfigure your inputs.", - "ControllerAppletDocked": "Docked mode set. Handheld control should be disabled.", - "UpdaterRenaming": "Renomeando arquivos antigos...", - "UpdaterRenameFailed": "O atualizador não conseguiu renomear o arquivo: {0}", - "UpdaterAddingFiles": "Adicionando novos arquivos...", - "UpdaterExtracting": "Extraíndo atualização...", - "UpdaterDownloading": "Baixando atualização...", - "Game": "Jogo", - "Docked": "TV", - "Handheld": "Portátil", - "ConnectionError": "Erro de conexão.", - "AboutPageDeveloperListMore": "{0} e mais...", - "ApiError": "Erro de API.", - "LoadingHeading": "Carregando {0}", - "CompilingPPTC": "Compilando PTC", - "CompilingShaders": "Compilando Shaders", - "AllKeyboards": "Todos os teclados", - "OpenFileDialogTitle": "Selecione um arquivo suportado para abrir", - "OpenFolderDialogTitle": "Selecione um diretório com um jogo extraído", - "AllSupportedFormats": "Todos os formatos suportados", - "RyujinxUpdater": "Atualizador do Ryujinx", - "SettingsTabHotkeys": "Atalhos do teclado", - "SettingsTabHotkeysHotkeys": "Atalhos do teclado", - "SettingsTabHotkeysToggleVsyncHotkey": "Mudar VSync:", - "SettingsTabHotkeysScreenshotHotkey": "Captura de tela:", - "SettingsTabHotkeysShowUiHotkey": "Exibir UI:", - "SettingsTabHotkeysPauseHotkey": "Pausar:", - "SettingsTabHotkeysToggleMuteHotkey": "Mudo:", - "ControllerMotionTitle": "Configurações do controle de movimento", - "ControllerRumbleTitle": "Configurações de vibração", - "SettingsSelectThemeFileDialogTitle": "Selecionar arquivo do tema", - "SettingsXamlThemeFile": "Arquivo de tema Xaml", - "AvatarWindowTitle": "Gerenciar contas - Avatar", - "Amiibo": "Amiibo", - "Unknown": "Desconhecido", - "Usage": "Uso", - "Writable": "Gravável", - "SelectDlcDialogTitle": "Selecionar arquivos de DLC", - "SelectUpdateDialogTitle": "Selecionar arquivos de atualização", - "SelectModDialogTitle": "Select mod directory", - "UserProfileWindowTitle": "Gerenciador de perfis de usuário", - "CheatWindowTitle": "Gerenciador de Cheats", - "DlcWindowTitle": "Gerenciador de DLC", - "ModWindowTitle": "Manage Mods for {0} ({1})", - "UpdateWindowTitle": "Gerenciador de atualizações", - "CheatWindowHeading": "Cheats disponíveis para {0} [{1}]", - "BuildId": "ID da Build", - "DlcWindowHeading": "{0} DLCs disponíveis para {1} ({2})", - "ModWindowHeading": "{0} Mod(s)", - "UserProfilesEditProfile": "Editar selecionado", - "Cancel": "Cancelar", - "Save": "Salvar", - "Discard": "Descartar", - "Paused": "Paused", - "UserProfilesSetProfileImage": "Definir imagem de perfil", - "UserProfileEmptyNameError": "É necessário um nome", - "UserProfileNoImageError": "A imagem de perfil deve ser definida", - "GameUpdateWindowHeading": "{0} atualizações disponíveis para {1} ({2})", - "SettingsTabHotkeysResScaleUpHotkey": "Aumentar a resolução:", - "SettingsTabHotkeysResScaleDownHotkey": "Diminuir a resolução:", - "UserProfilesName": "Nome:", - "UserProfilesUserId": "ID de usuário:", - "SettingsTabGraphicsBackend": "Backend gráfico", - "SettingsTabGraphicsBackendTooltip": "Select the graphics backend that will be used in the emulator.\n\nVulkan is overall better for all modern graphics cards, as long as their drivers are up to date. Vulkan also features faster shader compilation (less stuttering) on all GPU vendors.\n\nOpenGL may achieve better results on old Nvidia GPUs, on old AMD GPUs on Linux, or on GPUs with lower VRAM, though shader compilation stutters will be greater.\n\nSet to Vulkan if unsure. Set to OpenGL if your GPU does not support Vulkan even with the latest graphics drivers.", - "SettingsEnableTextureRecompression": "Habilitar recompressão de texturas", - "SettingsEnableTextureRecompressionTooltip": "Compresses ASTC textures in order to reduce VRAM usage.\n\nGames using this texture format include Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder and The Legend of Zelda: Tears of the Kingdom.\n\nGraphics cards with 4GiB VRAM or less will likely crash at some point while running these games.\n\nEnable only if you're running out of VRAM on the aforementioned games. Leave OFF if unsure.", - "SettingsTabGraphicsPreferredGpu": "GPU preferencial", - "SettingsTabGraphicsPreferredGpuTooltip": "Selecione a placa de vídeo que será usada com o backend gráfico Vulkan.\n\nNão afeta a GPU que OpenGL usará.\n\nSelecione \"dGPU\" em caso de dúvida. Se não houver nenhuma, não mexa.", - "SettingsAppRequiredRestartMessage": "Reinicialização do Ryujinx necessária", - "SettingsGpuBackendRestartMessage": "Configurações do backend gráfico ou da GPU foram alteradas. Uma reinicialização é necessária para que as mudanças tenham efeito.", - "SettingsGpuBackendRestartSubMessage": "Deseja reiniciar agora?", - "RyujinxUpdaterMessage": "Você quer atualizar o Ryujinx para a última versão?", - "SettingsTabHotkeysVolumeUpHotkey": "Aumentar volume:", - "SettingsTabHotkeysVolumeDownHotkey": "Diminuir volume:", - "SettingsEnableMacroHLE": "Habilitar emulação de alto nível para Macros", - "SettingsEnableMacroHLETooltip": "Habilita emulação de alto nível de códigos Macro da GPU.\n\nMelhora a performance, mas pode causar problemas gráficos em alguns jogos.\n\nEm caso de dúvida, deixe ATIVADO.", - "SettingsEnableColorSpacePassthrough": "Passagem de Espaço Cor", - "SettingsEnableColorSpacePassthroughTooltip": "Direciona o backend Vulkan para passar informações de cores sem especificar um espaço de cores. Para usuários com telas de ampla gama, isso pode resultar em cores mais vibrantes, ao custo da correção de cores.", - "VolumeShort": "Vol", - "UserProfilesManageSaves": "Gerenciar jogos salvos", - "DeleteUserSave": "Deseja apagar o jogo salvo do usuário para este jogo?", - "IrreversibleActionNote": "Esta ação não é reversível.", - "SaveManagerHeading": "Gerenciar jogos salvos para {0}", - "SaveManagerTitle": "Gerenciador de jogos salvos", - "Name": "Nome", - "Size": "Tamanho", - "Search": "Buscar", - "UserProfilesRecoverLostAccounts": "Recuperar contas perdidas", - "Recover": "Recuperar", - "UserProfilesRecoverHeading": "Jogos salvos foram encontrados para as seguintes contas", - "UserProfilesRecoverEmptyList": "Nenhum perfil para recuperar", - "GraphicsAATooltip": "Applies anti-aliasing to the game render.\n\nFXAA will blur most of the image, while SMAA will attempt to find jagged edges and smooth them out.\n\nNot recommended to use in conjunction with the FSR scaling filter.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on NONE if unsure.", - "GraphicsAALabel": "Anti-serrilhado:", - "GraphicsScalingFilterLabel": "Filtro de escala:", - "GraphicsScalingFilterTooltip": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.", - "GraphicsScalingFilterBilinear": "Bilinear", - "GraphicsScalingFilterNearest": "Nearest", - "GraphicsScalingFilterFsr": "FSR", - "GraphicsScalingFilterLevelLabel": "Nível", - "GraphicsScalingFilterLevelTooltip": "Set FSR 1.0 sharpening level. Higher is sharper.", - "SmaaLow": "SMAA Baixo", - "SmaaMedium": "SMAA Médio", - "SmaaHigh": "SMAA Alto", - "SmaaUltra": "SMAA Ultra", - "UserEditorTitle": "Editar usuário", - "UserEditorTitleCreate": "Criar usuário", - "SettingsTabNetworkInterface": "Interface de rede:", - "NetworkInterfaceTooltip": "The network interface used for LAN/LDN features.\n\nIn conjunction with a VPN or XLink Kai and a game with LAN support, can be used to spoof a same-network connection over the Internet.\n\nLeave on DEFAULT if unsure.", - "NetworkInterfaceDefault": "Padrão", - "PackagingShaders": "Empacotamento de Shaders", - "AboutChangelogButton": "Ver mudanças no GitHub", - "AboutChangelogButtonTooltipMessage": "Clique para abrir o relatório de alterações para esta versão no seu navegador padrão.", - "SettingsTabNetworkMultiplayer": "Multiplayer", - "MultiplayerMode": "Modo:", - "MultiplayerModeTooltip": "Change LDN multiplayer mode.\n\nLdnMitm will modify local wireless/local play functionality in games to function as if it were LAN, allowing for local, same-network connections with other Ryujinx instances and hacked Nintendo Switch consoles that have the ldn_mitm module installed.\n\nMultiplayer requires all players to be on the same game version (i.e. Super Smash Bros. Ultimate v13.0.1 can't connect to v13.0.0).\n\nLeave DISABLED if unsure.", - "MultiplayerModeDisabled": "Disabled", - "MultiplayerModeLdnMitm": "ldn_mitm" -} diff --git a/src/Ryujinx/Assets/Locales/ru_RU.json b/src/Ryujinx/Assets/Locales/ru_RU.json deleted file mode 100644 index 75fd4fe12..000000000 --- a/src/Ryujinx/Assets/Locales/ru_RU.json +++ /dev/null @@ -1,780 +0,0 @@ -{ - "Language": "Русский (RU)", - "MenuBarFileOpenApplet": "Открыть апплет", - "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Открывает апплет Mii Editor в автономном режиме", - "SettingsTabInputDirectMouseAccess": "Прямой ввод мыши", - "SettingsTabSystemMemoryManagerMode": "Режим менеджера памяти:", - "SettingsTabSystemMemoryManagerModeSoftware": "Программное обеспечение", - "SettingsTabSystemMemoryManagerModeHost": "Хост (быстро)", - "SettingsTabSystemMemoryManagerModeHostUnchecked": "Хост не установлен (самый быстрый, небезопасный)", - "SettingsTabSystemUseHypervisor": "Использовать Hypervisor", - "MenuBarFile": "_Файл", - "MenuBarFileOpenFromFile": "_Добавить приложение из файла", - "MenuBarFileOpenUnpacked": "Добавить _распакованную игру", - "MenuBarFileOpenEmuFolder": "Открыть папку Ryujinx", - "MenuBarFileOpenLogsFolder": "Открыть папку с логами", - "MenuBarFileExit": "_Выход", - "MenuBarOptions": "_Настройки", - "MenuBarOptionsToggleFullscreen": "Включить полноэкранный режим", - "MenuBarOptionsStartGamesInFullscreen": "Запускать игры в полноэкранном режиме", - "MenuBarOptionsStopEmulation": "Остановить эмуляцию", - "MenuBarOptionsSettings": "_Параметры", - "MenuBarOptionsManageUserProfiles": "_Менеджер учетных записей", - "MenuBarActions": "_Действия", - "MenuBarOptionsSimulateWakeUpMessage": "Имитировать сообщение пробуждения", - "MenuBarActionsScanAmiibo": "Сканировать Amiibo", - "MenuBarTools": "_Инструменты", - "MenuBarToolsInstallFirmware": "Установка прошивки", - "MenuBarFileToolsInstallFirmwareFromFile": "Установить прошивку из XCI или ZIP", - "MenuBarFileToolsInstallFirmwareFromDirectory": "Установить прошивку из папки", - "MenuBarToolsManageFileTypes": "Управление типами файлов", - "MenuBarToolsInstallFileTypes": "Установить типы файлов", - "MenuBarToolsUninstallFileTypes": "Удалить типы файлов", - "MenuBarView": "_Вид", - "MenuBarViewWindow": "Размер окна", - "MenuBarViewWindow720": "720p", - "MenuBarViewWindow1080": "1080p", - "MenuBarHelp": "_Помощь", - "MenuBarHelpCheckForUpdates": "Проверить наличие обновлений", - "MenuBarHelpAbout": "О программе", - "MenuSearch": "Поиск...", - "GameListHeaderFavorite": "Избранное", - "GameListHeaderIcon": "Значок", - "GameListHeaderApplication": "Название", - "GameListHeaderDeveloper": "Разработчик", - "GameListHeaderVersion": "Версия", - "GameListHeaderTimePlayed": "Время в игре", - "GameListHeaderLastPlayed": "Последний запуск", - "GameListHeaderFileExtension": "Расширение файла", - "GameListHeaderFileSize": "Размер файла", - "GameListHeaderPath": "Путь", - "GameListContextMenuOpenUserSaveDirectory": "Открыть папку с сохранениями", - "GameListContextMenuOpenUserSaveDirectoryToolTip": "Открывает папку с пользовательскими сохранениями", - "GameListContextMenuOpenDeviceSaveDirectory": "Открыть папку сохраненных устройств", - "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Открывает папку, содержащую сохраненные устройства", - "GameListContextMenuOpenBcatSaveDirectory": "Открыть папку сохраненных BCAT", - "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Открывает папку, содержащую сохраненные BCAT", - "GameListContextMenuManageTitleUpdates": "Управление обновлениями", - "GameListContextMenuManageTitleUpdatesToolTip": "Открывает окно управления обновлениями приложения", - "GameListContextMenuManageDlc": "Управление DLC", - "GameListContextMenuManageDlcToolTip": "Открывает окно управления DLC", - "GameListContextMenuCacheManagement": "Управление кэшем", - "GameListContextMenuCacheManagementPurgePptc": "Перестроить очередь PPTC", - "GameListContextMenuCacheManagementPurgePptcToolTip": "Запускает перестройку PPTC во время следующего запуска игры.", - "GameListContextMenuCacheManagementPurgeShaderCache": "Очистить кэш шейдеров", - "GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "Удаляет кеш шейдеров приложения", - "GameListContextMenuCacheManagementOpenPptcDirectory": "Открыть папку PPTC", - "GameListContextMenuCacheManagementOpenPptcDirectoryToolTip": "Открывает папку, содержащую PPTC кэш приложений и игр", - "GameListContextMenuCacheManagementOpenShaderCacheDirectory": "Открыть папку с кэшем шейдеров", - "GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip": "Открывает папку, содержащую кэш шейдеров приложений и игр", - "GameListContextMenuExtractData": "Извлечь данные", - "GameListContextMenuExtractDataExeFS": "ExeFS", - "GameListContextMenuExtractDataExeFSToolTip": "Извлечение раздела ExeFS из текущих настроек приложения (включая обновления)", - "GameListContextMenuExtractDataRomFS": "RomFS", - "GameListContextMenuExtractDataRomFSToolTip": "Извлечение раздела RomFS из текущих настроек приложения (включая обновления)", - "GameListContextMenuExtractDataLogo": "Логотип", - "GameListContextMenuExtractDataLogoToolTip": "Извлечение раздела с логотипом из текущих настроек приложения (включая обновления)", - "GameListContextMenuCreateShortcut": "Создать ярлык приложения", - "GameListContextMenuCreateShortcutToolTip": "Создает ярлык на рабочем столе, с помощью которого можно запустить игру или приложение", - "GameListContextMenuCreateShortcutToolTipMacOS": "Создает ярлык игры или приложения в папке Программы macOS", - "GameListContextMenuOpenModsDirectory": "Открыть папку с модами", - "GameListContextMenuOpenModsDirectoryToolTip": "Открывает папку, содержащую моды для приложений и игр", - "GameListContextMenuOpenSdModsDirectory": "Открыть папку с модами Atmosphere", - "GameListContextMenuOpenSdModsDirectoryToolTip": "Открывает папку Atmosphere на альтернативной SD-карте, которая содержит моды для приложений и игр. Полезно для модов, сделанных для реальной консоли.", - "StatusBarGamesLoaded": "{0}/{1} игр загружено", - "StatusBarSystemVersion": "Версия прошивки: {0}", - "LinuxVmMaxMapCountDialogTitle": "Обнаружен низкий лимит разметки памяти", - "LinuxVmMaxMapCountDialogTextPrimary": "Хотите увеличить значение vm.max_map_count до {0}", - "LinuxVmMaxMapCountDialogTextSecondary": "Некоторые игры могут создавать большую разметку памяти, чем разрешено на данный момент по умолчанию. Ryujinx вылетит при превышении этого лимита.", - "LinuxVmMaxMapCountDialogButtonUntilRestart": "Да, до следующего перезапуска", - "LinuxVmMaxMapCountDialogButtonPersistent": "Да, постоянно", - "LinuxVmMaxMapCountWarningTextPrimary": "Максимальная разметка памяти меньше, чем рекомендуется.", - "LinuxVmMaxMapCountWarningTextSecondary": "Текущее значение vm.max_map_count ({0}) меньше, чем {1}. Некоторые игры могут попытаться создать большую разметку памяти, чем разрешено в данный момент. Ryujinx вылетит как только этот лимит будет превышен.\n\nВозможно, вам потребуется вручную увеличить лимит или установить pkexec, что позволит Ryujinx помочь справиться с превышением лимита.", - "Settings": "Параметры", - "SettingsTabGeneral": "Интерфейс", - "SettingsTabGeneralGeneral": "Общее", - "SettingsTabGeneralEnableDiscordRichPresence": "Статус активности в Discord", - "SettingsTabGeneralCheckUpdatesOnLaunch": "Проверять наличие обновлений при запуске", - "SettingsTabGeneralShowConfirmExitDialog": "Подтверждать выход из приложения", - "SettingsTabGeneralRememberWindowState": "Запомнить размер/положение окна", - "SettingsTabGeneralHideCursor": "Скрывать курсор", - "SettingsTabGeneralHideCursorNever": "Никогда", - "SettingsTabGeneralHideCursorOnIdle": "В простое", - "SettingsTabGeneralHideCursorAlways": "Всегда", - "SettingsTabGeneralGameDirectories": "Папки с играми", - "SettingsTabGeneralAdd": "Добавить", - "SettingsTabGeneralRemove": "Удалить", - "SettingsTabSystem": "Система", - "SettingsTabSystemCore": "Основные настройки", - "SettingsTabSystemSystemRegion": "Регион прошивки:", - "SettingsTabSystemSystemRegionJapan": "Япония", - "SettingsTabSystemSystemRegionUSA": "США", - "SettingsTabSystemSystemRegionEurope": "Европа", - "SettingsTabSystemSystemRegionAustralia": "Австралия", - "SettingsTabSystemSystemRegionChina": "Китай", - "SettingsTabSystemSystemRegionKorea": "Корея", - "SettingsTabSystemSystemRegionTaiwan": "Тайвань", - "SettingsTabSystemSystemLanguage": "Язык прошивки:", - "SettingsTabSystemSystemLanguageJapanese": "Японский", - "SettingsTabSystemSystemLanguageAmericanEnglish": "Английский (США)", - "SettingsTabSystemSystemLanguageFrench": "Французский", - "SettingsTabSystemSystemLanguageGerman": "Германский", - "SettingsTabSystemSystemLanguageItalian": "Итальянский", - "SettingsTabSystemSystemLanguageSpanish": "Испанский", - "SettingsTabSystemSystemLanguageChinese": "Китайский", - "SettingsTabSystemSystemLanguageKorean": "Корейский", - "SettingsTabSystemSystemLanguageDutch": "Нидерландский", - "SettingsTabSystemSystemLanguagePortuguese": "Португальский", - "SettingsTabSystemSystemLanguageRussian": "Русский", - "SettingsTabSystemSystemLanguageTaiwanese": "Тайванский", - "SettingsTabSystemSystemLanguageBritishEnglish": "Английский (Британия)", - "SettingsTabSystemSystemLanguageCanadianFrench": "Французский (Канада)", - "SettingsTabSystemSystemLanguageLatinAmericanSpanish": "Испанский (Латинская Америка)", - "SettingsTabSystemSystemLanguageSimplifiedChinese": "Китайский (упрощённый)", - "SettingsTabSystemSystemLanguageTraditionalChinese": "Китайский (традиционный)", - "SettingsTabSystemSystemTimeZone": "Часовой пояс прошивки:", - "SettingsTabSystemSystemTime": "Системное время в прошивке:", - "SettingsTabSystemEnableVsync": "Вертикальная синхронизация", - "SettingsTabSystemEnablePptc": "Использовать PPTC (Profiled Persistent Translation Cache)", - "SettingsTabSystemEnableFsIntegrityChecks": "Проверка целостности файловой системы", - "SettingsTabSystemAudioBackend": "Аудио бэкенд:", - "SettingsTabSystemAudioBackendDummy": "Без звука", - "SettingsTabSystemAudioBackendOpenAL": "OpenAL", - "SettingsTabSystemAudioBackendSoundIO": "SoundIO", - "SettingsTabSystemAudioBackendSDL2": "SDL2", - "SettingsTabSystemHacks": "Хаки", - "SettingsTabSystemHacksNote": "Возможна нестабильная работа", - "SettingsTabSystemExpandDramSize": "Использовать альтернативный макет памяти (для разработчиков)", - "SettingsTabSystemIgnoreMissingServices": "Игнорировать отсутствующие службы", - "SettingsTabGraphics": "Графика", - "SettingsTabGraphicsAPI": "Графические API", - "SettingsTabGraphicsEnableShaderCache": "Кэшировать шейдеры", - "SettingsTabGraphicsAnisotropicFiltering": "Анизотропная фильтрация:", - "SettingsTabGraphicsAnisotropicFilteringAuto": "Автоматически", - "SettingsTabGraphicsAnisotropicFiltering2x": "2x", - "SettingsTabGraphicsAnisotropicFiltering4x": "4x", - "SettingsTabGraphicsAnisotropicFiltering8x": "8x", - "SettingsTabGraphicsAnisotropicFiltering16x": "16x", - "SettingsTabGraphicsResolutionScale": "Масштабирование:", - "SettingsTabGraphicsResolutionScaleCustom": "Пользовательское (не рекомендуется)", - "SettingsTabGraphicsResolutionScaleNative": "Нативное (720p/1080p)", - "SettingsTabGraphicsResolutionScale2x": "2x (1440p/2160p)", - "SettingsTabGraphicsResolutionScale3x": "3x (2160p/3240p)", - "SettingsTabGraphicsResolutionScale4x": "4x (2880p/4320p) (не рекомендуется)", - "SettingsTabGraphicsAspectRatio": "Соотношение сторон:", - "SettingsTabGraphicsAspectRatio4x3": "4:3", - "SettingsTabGraphicsAspectRatio16x9": "16:9", - "SettingsTabGraphicsAspectRatio16x10": "16:10", - "SettingsTabGraphicsAspectRatio21x9": "21:9", - "SettingsTabGraphicsAspectRatio32x9": "32:9", - "SettingsTabGraphicsAspectRatioStretch": "Растянуть до размеров окна", - "SettingsTabGraphicsDeveloperOptions": "Параметры разработчика", - "SettingsTabGraphicsShaderDumpPath": "Путь дампа графических шейдеров", - "SettingsTabLogging": "Журналирование", - "SettingsTabLoggingLogging": "Журналирование", - "SettingsTabLoggingEnableLoggingToFile": "Включить запись в файл", - "SettingsTabLoggingEnableStubLogs": "Включить журнал-заглушку", - "SettingsTabLoggingEnableInfoLogs": "Включить информационный журнал", - "SettingsTabLoggingEnableWarningLogs": "Включить журнал предупреждений", - "SettingsTabLoggingEnableErrorLogs": "Включить журнал ошибок", - "SettingsTabLoggingEnableTraceLogs": "Включить журнал трассировки", - "SettingsTabLoggingEnableGuestLogs": "Включить гостевые журналы", - "SettingsTabLoggingEnableFsAccessLogs": "Включить журналы доступа файловой системы", - "SettingsTabLoggingFsGlobalAccessLogMode": "Режим журнала глобального доступа файловой системы:", - "SettingsTabLoggingDeveloperOptions": "Параметры разработчика", - "SettingsTabLoggingDeveloperOptionsNote": "ВНИМАНИЕ: эти настройки снижают производительность", - "SettingsTabLoggingGraphicsBackendLogLevel": "Уровень журнала бэкенда графики:", - "SettingsTabLoggingGraphicsBackendLogLevelNone": "Нет", - "SettingsTabLoggingGraphicsBackendLogLevelError": "Ошибка", - "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "Замедления", - "SettingsTabLoggingGraphicsBackendLogLevelAll": "Всё", - "SettingsTabLoggingEnableDebugLogs": "Включить журнал отладки", - "SettingsTabInput": "Управление", - "SettingsTabInputEnableDockedMode": "Стационарный режим", - "SettingsTabInputDirectKeyboardAccess": "Прямой ввод клавиатуры", - "SettingsButtonSave": "Сохранить", - "SettingsButtonClose": "Закрыть", - "SettingsButtonOk": "Ок", - "SettingsButtonCancel": "Отмена", - "SettingsButtonApply": "Применить", - "ControllerSettingsPlayer": "Игрок", - "ControllerSettingsPlayer1": "Игрок 1", - "ControllerSettingsPlayer2": "Игрок 2", - "ControllerSettingsPlayer3": "Игрок 3", - "ControllerSettingsPlayer4": "Игрок 4", - "ControllerSettingsPlayer5": "Игрок 5", - "ControllerSettingsPlayer6": "Игрок 6", - "ControllerSettingsPlayer7": "Игрок 7", - "ControllerSettingsPlayer8": "Игрок 8", - "ControllerSettingsHandheld": "Портативный", - "ControllerSettingsInputDevice": "Устройство ввода", - "ControllerSettingsRefresh": "Обновить", - "ControllerSettingsDeviceDisabled": "Отключить", - "ControllerSettingsControllerType": "Тип контроллера", - "ControllerSettingsControllerTypeHandheld": "Портативный", - "ControllerSettingsControllerTypeProController": "Pro Controller", - "ControllerSettingsControllerTypeJoyConPair": "JoyCon (пара)", - "ControllerSettingsControllerTypeJoyConLeft": "JoyCon (левый)", - "ControllerSettingsControllerTypeJoyConRight": "JoyCon (правый)", - "ControllerSettingsProfile": "Профиль", - "ControllerSettingsProfileDefault": "По умолчанию", - "ControllerSettingsLoad": "Загрузить", - "ControllerSettingsAdd": "Добавить", - "ControllerSettingsRemove": "Удалить", - "ControllerSettingsButtons": "Кнопки", - "ControllerSettingsButtonA": "A", - "ControllerSettingsButtonB": "B", - "ControllerSettingsButtonX": "X", - "ControllerSettingsButtonY": "Y", - "ControllerSettingsButtonPlus": "+", - "ControllerSettingsButtonMinus": "-", - "ControllerSettingsDPad": "Кнопки направления", - "ControllerSettingsDPadUp": "Вверх", - "ControllerSettingsDPadDown": "Вниз", - "ControllerSettingsDPadLeft": "Влево", - "ControllerSettingsDPadRight": "Вправо", - "ControllerSettingsStickButton": "Нажатие на стик", - "ControllerSettingsStickUp": "Вверх", - "ControllerSettingsStickDown": "Вниз", - "ControllerSettingsStickLeft": "Влево", - "ControllerSettingsStickRight": "Вправо", - "ControllerSettingsStickStick": "Стик", - "ControllerSettingsStickInvertXAxis": "Инвертировать ось X", - "ControllerSettingsStickInvertYAxis": "Инвертировать ось Y", - "ControllerSettingsStickDeadzone": "Мёртвая зона:", - "ControllerSettingsLStick": "Левый стик", - "ControllerSettingsRStick": "Правый стик", - "ControllerSettingsTriggersLeft": "Триггеры слева", - "ControllerSettingsTriggersRight": "Триггеры справа", - "ControllerSettingsTriggersButtonsLeft": "Триггерные кнопки слева", - "ControllerSettingsTriggersButtonsRight": "Триггерные кнопки справа", - "ControllerSettingsTriggers": "Триггеры", - "ControllerSettingsTriggerL": "L", - "ControllerSettingsTriggerR": "R", - "ControllerSettingsTriggerZL": "ZL", - "ControllerSettingsTriggerZR": "ZR", - "ControllerSettingsLeftSL": "SL", - "ControllerSettingsLeftSR": "SR", - "ControllerSettingsRightSL": "SL", - "ControllerSettingsRightSR": "SR", - "ControllerSettingsExtraButtonsLeft": "Левые кнопки", - "ControllerSettingsExtraButtonsRight": "Правые кнопки", - "ControllerSettingsMisc": "Разное", - "ControllerSettingsTriggerThreshold": "Порог срабатывания:", - "ControllerSettingsMotion": "Движение", - "ControllerSettingsMotionUseCemuhookCompatibleMotion": "Включить совместимость с CemuHook", - "ControllerSettingsMotionControllerSlot": "Слот контроллера:", - "ControllerSettingsMotionMirrorInput": "Зеркальный ввод", - "ControllerSettingsMotionRightJoyConSlot": "Слот правого JoyCon:", - "ControllerSettingsMotionServerHost": "Хост сервера:", - "ControllerSettingsMotionGyroSensitivity": "Чувствительность гироскопа:", - "ControllerSettingsMotionGyroDeadzone": "Мертвая зона гироскопа:", - "ControllerSettingsSave": "Сохранить", - "ControllerSettingsClose": "Закрыть", - "KeyUnknown": "Неизвестно", - "KeyShiftLeft": "Левый Shift", - "KeyShiftRight": "Правый Shift", - "KeyControlLeft": "Левый Ctrl", - "KeyMacControlLeft": "Левый ⌃", - "KeyControlRight": "Правый Ctrl", - "KeyMacControlRight": "Правый ⌃", - "KeyAltLeft": "Левый Alt", - "KeyMacAltLeft": "Левый ⌥", - "KeyAltRight": "Правый Alt", - "KeyMacAltRight": "Правый ⌥", - "KeyWinLeft": "Левый ⊞", - "KeyMacWinLeft": "Левый ⌘", - "KeyWinRight": "Правый ⊞", - "KeyMacWinRight": "Правый ⌘", - "KeyMenu": "Меню", - "KeyUp": "Вверх", - "KeyDown": "Вниз", - "KeyLeft": "Влево", - "KeyRight": "Вправо", - "KeyEnter": "Enter", - "KeyEscape": "Escape", - "KeySpace": "Пробел", - "KeyTab": "Tab", - "KeyBackSpace": "Backspace", - "KeyInsert": "Insert", - "KeyDelete": "Delete", - "KeyPageUp": "Page Up", - "KeyPageDown": "Page Down", - "KeyHome": "Home", - "KeyEnd": "End", - "KeyCapsLock": "Caps Lock", - "KeyScrollLock": "Scroll Lock", - "KeyPrintScreen": "Print Screen", - "KeyPause": "Pause", - "KeyNumLock": "Num Lock", - "KeyClear": "Очистить", - "KeyKeypad0": "Блок цифр 0", - "KeyKeypad1": "Блок цифр 1", - "KeyKeypad2": "Блок цифр 2", - "KeyKeypad3": "Блок цифр 3", - "KeyKeypad4": "Блок цифр 4", - "KeyKeypad5": "Блок цифр 5", - "KeyKeypad6": "Блок цифр 6", - "KeyKeypad7": "Блок цифр 7", - "KeyKeypad8": "Блок цифр 8", - "KeyKeypad9": "Блок цифр 9", - "KeyKeypadDivide": "/ (блок цифр)", - "KeyKeypadMultiply": "* (блок цифр)", - "KeyKeypadSubtract": "- (блок цифр)", - "KeyKeypadAdd": "+ (блок цифр)", - "KeyKeypadDecimal": ". (блок цифр)", - "KeyKeypadEnter": "Enter (блок цифр)", - "KeyNumber0": "0", - "KeyNumber1": "1", - "KeyNumber2": "2", - "KeyNumber3": "3", - "KeyNumber4": "4", - "KeyNumber5": "5", - "KeyNumber6": "6", - "KeyNumber7": "7", - "KeyNumber8": "8", - "KeyNumber9": "9", - "KeyTilde": "~", - "KeyGrave": "`", - "KeyMinus": "-", - "KeyPlus": "+", - "KeyBracketLeft": "[", - "KeyBracketRight": "]", - "KeySemicolon": ";", - "KeyQuote": "\"", - "KeyComma": ",", - "KeyPeriod": ".", - "KeySlash": "/", - "KeyBackSlash": "\\", - "KeyUnbound": "Не привязано", - "GamepadLeftStick": "Кнопка лев. стика", - "GamepadRightStick": "Кнопка пр. стика", - "GamepadLeftShoulder": "Левый бампер", - "GamepadRightShoulder": "Правый бампер", - "GamepadLeftTrigger": "Левый триггер", - "GamepadRightTrigger": "Правый триггер", - "GamepadDpadUp": "Вверх", - "GamepadDpadDown": "Вниз", - "GamepadDpadLeft": "Влево", - "GamepadDpadRight": "Вправо", - "GamepadMinus": "-", - "GamepadPlus": "+", - "GamepadGuide": "Кнопка Xbox", - "GamepadMisc1": "Прочее", - "GamepadPaddle1": "Доп.кнопка 1", - "GamepadPaddle2": "Доп.кнопка 2", - "GamepadPaddle3": "Доп.кнопка 3", - "GamepadPaddle4": "Доп.кнопка 4", - "GamepadTouchpad": "Тачпад", - "GamepadSingleLeftTrigger0": "Левый триггер 0", - "GamepadSingleRightTrigger0": "Правый триггер 0", - "GamepadSingleLeftTrigger1": "Левый триггер 1", - "GamepadSingleRightTrigger1": "Правый триггер 1", - "StickLeft": "Левый стик", - "StickRight": "Правый стик", - "UserProfilesSelectedUserProfile": "Выбранный пользовательский профиль:", - "UserProfilesSaveProfileName": "Сохранить пользовательский профиль", - "UserProfilesChangeProfileImage": "Изменить аватар", - "UserProfilesAvailableUserProfiles": "Доступные профили пользователей:", - "UserProfilesAddNewProfile": "Добавить новый профиль", - "UserProfilesDelete": "Удалить", - "UserProfilesClose": "Закрыть", - "ProfileNameSelectionWatermark": "Укажите никнейм", - "ProfileImageSelectionTitle": "Выбор изображения профиля", - "ProfileImageSelectionHeader": "Выбор аватара", - "ProfileImageSelectionNote": "Вы можете импортировать собственное изображение или выбрать аватар из системной прошивки.", - "ProfileImageSelectionImportImage": "Импорт изображения", - "ProfileImageSelectionSelectAvatar": "Встроенные аватары", - "InputDialogTitle": "Диалоговое окно ввода", - "InputDialogOk": "ОК", - "InputDialogCancel": "Отмена", - "InputDialogAddNewProfileTitle": "Выберите никнейм", - "InputDialogAddNewProfileHeader": "Пожалуйста, введите никнейм", - "InputDialogAddNewProfileSubtext": "(Максимальная длина: {0})", - "AvatarChoose": "Выбрать аватар", - "AvatarSetBackgroundColor": "Установить цвет фона", - "AvatarClose": "Закрыть", - "ControllerSettingsLoadProfileToolTip": "Загрузить профиль", - "ControllerSettingsAddProfileToolTip": "Добавить профиль", - "ControllerSettingsRemoveProfileToolTip": "Удалить профиль", - "ControllerSettingsSaveProfileToolTip": "Сохранить профиль", - "MenuBarFileToolsTakeScreenshot": "Сделать снимок экрана", - "MenuBarFileToolsHideUi": "Скрыть интерфейс", - "GameListContextMenuRunApplication": "Запуск приложения", - "GameListContextMenuToggleFavorite": "Добавить в избранное", - "GameListContextMenuToggleFavoriteToolTip": "Добавляет игру в избранное и помечает звездочкой", - "SettingsTabGeneralTheme": "Тема:", - "SettingsTabGeneralThemeDark": "Темная", - "SettingsTabGeneralThemeLight": "Светлая", - "ControllerSettingsConfigureGeneral": "Настройка", - "ControllerSettingsRumble": "Вибрация", - "ControllerSettingsRumbleStrongMultiplier": "Множитель сильной вибрации", - "ControllerSettingsRumbleWeakMultiplier": "Множитель слабой вибрации", - "DialogMessageSaveNotAvailableMessage": "Нет сохранений для {0} [{1:x16}]", - "DialogMessageSaveNotAvailableCreateSaveMessage": "Создать сохранение для этой игры?", - "DialogConfirmationTitle": "Ryujinx - Подтверждение", - "DialogUpdaterTitle": "Ryujinx - Обновление", - "DialogErrorTitle": "Ryujinx - Ошибка", - "DialogWarningTitle": "Ryujinx - Предупреждение", - "DialogExitTitle": "Ryujinx - Выход", - "DialogErrorMessage": "Ryujinx обнаружил ошибку", - "DialogExitMessage": "Вы уверены, что хотите выйти из Ryujinx?", - "DialogExitSubMessage": "Все несохраненные данные будут потеряны", - "DialogMessageCreateSaveErrorMessage": "Произошла ошибка при создании указанных данных сохранения: {0}", - "DialogMessageFindSaveErrorMessage": "Произошла ошибка при поиске указанных данных сохранения: {0}", - "FolderDialogExtractTitle": "Выберите папку для извлечения", - "DialogNcaExtractionMessage": "Извлечение {0} раздела из {1}...", - "DialogNcaExtractionTitle": "Ryujinx - Извлечение разделов NCA", - "DialogNcaExtractionMainNcaNotFoundErrorMessage": "Ошибка извлечения. Основной NCA не присутствовал в выбранном файле.", - "DialogNcaExtractionCheckLogErrorMessage": "Ошибка извлечения. Прочтите файл журнала для получения дополнительной информации.", - "DialogNcaExtractionSuccessMessage": "Извлечение завершено успешно.", - "DialogUpdaterConvertFailedMessage": "Не удалось преобразовать текущую версию Ryujinx.", - "DialogUpdaterCancelUpdateMessage": "Отмена обновления...", - "DialogUpdaterAlreadyOnLatestVersionMessage": "Вы используете самую последнюю версию Ryujinx", - "DialogUpdaterFailedToGetVersionMessage": "Произошла ошибка при попытке получить информацию о выпуске от GitHub Release. Это может быть вызвано тем, что в данный момент в GitHub Actions компилируется новый релиз. Повторите попытку позже.", - "DialogUpdaterConvertFailedGithubMessage": "Не удалось преобразовать полученную версию Ryujinx из Github Release.", - "DialogUpdaterDownloadingMessage": "Загрузка обновления...", - "DialogUpdaterExtractionMessage": "Извлечение обновления...", - "DialogUpdaterRenamingMessage": "Переименование обновления...", - "DialogUpdaterAddingFilesMessage": "Добавление нового обновления...", - "DialogUpdaterCompleteMessage": "Обновление завершено", - "DialogUpdaterRestartMessage": "Перезапустить Ryujinx?", - "DialogUpdaterNoInternetMessage": "Вы не подключены к интернету", - "DialogUpdaterNoInternetSubMessage": "Убедитесь, что у вас работает подключение к интернету", - "DialogUpdaterDirtyBuildMessage": "Вы не можете обновлять Dirty Build", - "DialogUpdaterDirtyBuildSubMessage": "Загрузите Ryujinx по адресу https://ryujinx.org/ если вам нужна поддерживаемая версия.", - "DialogRestartRequiredMessage": "Требуется перезагрузка", - "DialogThemeRestartMessage": "Тема сохранена. Для применения темы требуется перезапуск.", - "DialogThemeRestartSubMessage": "Хотите перезапустить", - "DialogFirmwareInstallEmbeddedMessage": "Хотите установить прошивку, встроенную в эту игру? (Прошивка {0})", - "DialogFirmwareInstallEmbeddedSuccessMessage": "Установленная прошивка не была найдена, но Ryujinx удалось установить прошивку {0} из предоставленной игры.\nТеперь эмулятор запустится.", - "DialogFirmwareNoFirmwareInstalledMessage": "Прошивка не установлена", - "DialogFirmwareInstalledMessage": "Прошивка {0} была установлена", - "DialogInstallFileTypesSuccessMessage": "Типы файлов успешно установлены", - "DialogInstallFileTypesErrorMessage": "Не удалось установить типы файлов.", - "DialogUninstallFileTypesSuccessMessage": "Типы файлов успешно удалены", - "DialogUninstallFileTypesErrorMessage": "Не удалось удалить типы файлов.", - "DialogOpenSettingsWindowLabel": "Открывает окно параметров", - "DialogControllerAppletTitle": "Апплет контроллера", - "DialogMessageDialogErrorExceptionMessage": "Ошибка отображения сообщения: {0}", - "DialogSoftwareKeyboardErrorExceptionMessage": "Ошибка отображения программной клавиатуры: {0}", - "DialogErrorAppletErrorExceptionMessage": "Ошибка отображения диалогового окна ErrorApplet: {0}", - "DialogUserErrorDialogMessage": "{0}: {1}", - "DialogUserErrorDialogInfoMessage": "\nДля получения дополнительной информации о том, как исправить эту ошибку, следуйте нашему Руководству по установке.", - "DialogUserErrorDialogTitle": "Ошибка Ryujinx ({0})", - "DialogAmiiboApiTitle": "Amiibo API", - "DialogAmiiboApiFailFetchMessage": "Произошла ошибка при получении информации из API.", - "DialogAmiiboApiConnectErrorMessage": "Не удалось подключиться к серверу Amiibo API. Служба может быть недоступна или вам может потребоваться проверить ваше интернет-соединение.", - "DialogProfileInvalidProfileErrorMessage": "Профиль {0} несовместим с текущей системой конфигурации ввода.", - "DialogProfileDefaultProfileOverwriteErrorMessage": "Профиль по умолчанию не может быть перезаписан", - "DialogProfileDeleteProfileTitle": "Удаление профиля", - "DialogProfileDeleteProfileMessage": "Это действие необратимо. Вы уверены, что хотите продолжить?", - "DialogWarning": "Внимание", - "DialogPPTCDeletionMessage": "Вы собираетесь перестроить кэш PPTC при следующем запуске для:\n\n{0}\n\nВы уверены, что хотите продолжить?", - "DialogPPTCDeletionErrorMessage": "Ошибка очистки кэша PPTC в {0}: {1}", - "DialogShaderDeletionMessage": "Вы собираетесь удалить кэш шейдеров для:\n\n{0}\n\nВы уверены, что хотите продолжить?", - "DialogShaderDeletionErrorMessage": "Ошибка очистки кэша шейдеров в {0}: {1}", - "DialogRyujinxErrorMessage": "Ryujinx обнаружил ошибку", - "DialogInvalidTitleIdErrorMessage": "Ошибка пользовательского интерфейса: выбранная игра не имеет действительного ID.", - "DialogFirmwareInstallerFirmwareNotFoundErrorMessage": "Валидная системная прошивка не найдена в {0}.", - "DialogFirmwareInstallerFirmwareInstallTitle": "Установить прошивку {0}", - "DialogFirmwareInstallerFirmwareInstallMessage": "Будет установлена версия прошивки {0}.", - "DialogFirmwareInstallerFirmwareInstallSubMessage": "\n\nЭто заменит текущую версию прошивки {0}.", - "DialogFirmwareInstallerFirmwareInstallConfirmMessage": "\n\nПродолжить?", - "DialogFirmwareInstallerFirmwareInstallWaitMessage": "Установка прошивки...", - "DialogFirmwareInstallerFirmwareInstallSuccessMessage": "Прошивка версии {0} успешно установлена.", - "DialogUserProfileDeletionWarningMessage": "Если выбранный профиль будет удален, другие профили не будут открываться.", - "DialogUserProfileDeletionConfirmMessage": "Удалить выбранный профиль?", - "DialogUserProfileUnsavedChangesTitle": "Внимание - Несохраненные изменения", - "DialogUserProfileUnsavedChangesMessage": "В эту учетную запись внесены изменения, которые не были сохранены.", - "DialogUserProfileUnsavedChangesSubMessage": "Отменить изменения?", - "DialogControllerSettingsModifiedConfirmMessage": "Текущие настройки управления обновлены.", - "DialogControllerSettingsModifiedConfirmSubMessage": "Сохранить?", - "DialogLoadFileErrorMessage": "{0}. Файл с ошибкой: {1}", - "DialogModAlreadyExistsMessage": "Мод уже существует", - "DialogModInvalidMessage": "Выбранная папка не содержит модов", - "DialogModDeleteNoParentMessage": "Невозможно удалить: не удалось найти папку мода \"{0}\"", - "DialogDlcNoDlcErrorMessage": "Указанный файл не содержит DLC для выбранной игры", - "DialogPerformanceCheckLoggingEnabledMessage": "У вас включено ведение журнала отладки, предназначенное только для разработчиков.", - "DialogPerformanceCheckLoggingEnabledConfirmMessage": "Для оптимальной производительности рекомендуется отключить ведение журнала отладки. Хотите отключить ведение журнала отладки?", - "DialogPerformanceCheckShaderDumpEnabledMessage": "У вас включен дамп шейдеров, который предназначен только для разработчиков.", - "DialogPerformanceCheckShaderDumpEnabledConfirmMessage": "Для оптимальной производительности рекомендуется отключить дамп шейдеров. Хотите отключить дамп шейдеров?", - "DialogLoadAppGameAlreadyLoadedMessage": "Игра уже загружена", - "DialogLoadAppGameAlreadyLoadedSubMessage": "Пожалуйста, остановите эмуляцию или закройте эмулятор перед запуском другой игры.", - "DialogUpdateAddUpdateErrorMessage": "Указанный файл не содержит обновлений для выбранного приложения", - "DialogSettingsBackendThreadingWarningTitle": "Предупреждение: многопоточность в бэкенде", - "DialogSettingsBackendThreadingWarningMessage": "Для применения этой настройки необходимо перезапустить Ryujinx. В зависимости от используемой вами операционной системы вам может потребоваться вручную отключить многопоточность драйвера при использовании Ryujinx.", - "DialogModManagerDeletionWarningMessage": "Вы сейчас удалите мод: {0}\n\nВы уверены, что хотите продолжить?", - "DialogModManagerDeletionAllWarningMessage": "Вы сейчас удалите все выбранные моды для этой игры.\n\nВы уверены, что хотите продолжить?", - "SettingsTabGraphicsFeaturesOptions": "Функции & Улучшения", - "SettingsTabGraphicsBackendMultithreading": "Многопоточность графического бэкенда:", - "CommonAuto": "Автоматически", - "CommonOff": "Выключено", - "CommonOn": "Включено", - "InputDialogYes": "Да", - "InputDialogNo": "Нет", - "DialogProfileInvalidProfileNameErrorMessage": "Имя файла содержит недопустимые символы. Пожалуйста, попробуйте еще раз.", - "MenuBarOptionsPauseEmulation": "Пауза эмуляции", - "MenuBarOptionsResumeEmulation": "Продолжить", - "AboutUrlTooltipMessage": "Нажмите, чтобы открыть веб-сайт Ryujinx", - "AboutDisclaimerMessage": "Ryujinx никоим образом не связан ни с Nintendo™, ни с кем-либо из ее партнеров.", - "AboutAmiiboDisclaimerMessage": "Amiibo API (www.amiiboapi.com) используется для эмуляции Amiibo.", - "AboutPatreonUrlTooltipMessage": "Нажмите, чтобы открыть страницу Ryujinx на Patreon", - "AboutGithubUrlTooltipMessage": "Нажмите, чтобы открыть страницу Ryujinx на GitHub", - "AboutDiscordUrlTooltipMessage": "Нажмите, чтобы открыть приглашение на сервер Ryujinx в Discord", - "AboutTwitterUrlTooltipMessage": "Нажмите, чтобы открыть страницу Ryujinx в X (бывший Twitter)", - "AboutRyujinxAboutTitle": "О программе:", - "AboutRyujinxAboutContent": "Ryujinx — это эмулятор Nintendo Switch™.\nПожалуйста, поддержите нас на Patreon.\nЧитайте последние новости в наших X (Twitter) или Discord.\nРазработчики, заинтересованные в участии, могут ознакомиться с проектом на GitHub или в Discord.", - "AboutRyujinxMaintainersTitle": "Разработка:", - "AboutRyujinxMaintainersContentTooltipMessage": "Нажмите, чтобы открыть страницу с участниками", - "AboutRyujinxSupprtersTitle": "Поддержка на Patreon:", - "AmiiboSeriesLabel": "Серия Amiibo", - "AmiiboCharacterLabel": "Персонаж", - "AmiiboScanButtonLabel": "Сканировать", - "AmiiboOptionsShowAllLabel": "Показать все Amiibo", - "AmiiboOptionsUsRandomTagLabel": "Хак: Использовать случайный тег Uuid", - "DlcManagerTableHeadingEnabledLabel": "Включено", - "DlcManagerTableHeadingTitleIdLabel": "ID приложения", - "DlcManagerTableHeadingContainerPathLabel": "Путь к контейнеру", - "DlcManagerTableHeadingFullPathLabel": "Полный путь", - "DlcManagerRemoveAllButton": "Удалить все", - "DlcManagerEnableAllButton": "Включить все", - "DlcManagerDisableAllButton": "Отключить все", - "ModManagerDeleteAllButton": "Удалить все", - "MenuBarOptionsChangeLanguage": "Сменить язык", - "MenuBarShowFileTypes": "Показывать форматы файлов", - "CommonSort": "Сортировка", - "CommonShowNames": "Показывать названия", - "CommonFavorite": "Избранное", - "OrderAscending": "По возрастанию", - "OrderDescending": "По убыванию", - "SettingsTabGraphicsFeatures": "Функции", - "ErrorWindowTitle": "Окно ошибки", - "ToggleDiscordTooltip": "Включает или отключает отображение статуса \"Играет в игру\" в Discord", - "AddGameDirBoxTooltip": "Введите путь к папке с играми для добавления ее в список выше", - "AddGameDirTooltip": "Добавить папку с играми в список", - "RemoveGameDirTooltip": "Удалить выбранную папку игры", - "CustomThemeCheckTooltip": "Включить или отключить пользовательские темы", - "CustomThemePathTooltip": "Путь к пользовательской теме для интерфейса", - "CustomThemeBrowseTooltip": "Просмотр пользовательской темы интерфейса", - "DockModeToggleTooltip": "\"Стационарный\" режим запускает эмулятор, как если бы Nintendo Switch находилась в доке, что улучшает графику и разрешение в большинстве игр. И наоборот, при отключении этого режима эмулятор будет запускать игры в \"Портативном\" режиме, снижая качество графики.\n\nНастройте управление для Игрока 1 если планируете использовать в \"Стационарном\" режиме; настройте портативное управление если планируете использовать эмулятор в \"Портативном\" режиме.\n\nРекомендуется оставить включенным.", - "DirectKeyboardTooltip": "Поддержка прямого ввода с клавиатуры (HID). Предоставляет игре прямой доступ к клавиатуре в качестве устройства ввода текста.\nРаботает только с играми, которые изначально поддерживают использование клавиатуры с Switch.\nРекомендуется оставить выключенным.", - "DirectMouseTooltip": "Поддержка прямого ввода мыши (HID). Предоставляет игре прямой доступ к мыши в качестве указывающего устройства.\nРаботает только с играми, которые изначально поддерживают использование мыши совместно с железом Switch.\nРекомендуется оставить выключенным.", - "RegionTooltip": "Сменяет регион прошивки", - "LanguageTooltip": "Меняет язык прошивки", - "TimezoneTooltip": "Меняет часовой пояс прошивки", - "TimeTooltip": "Меняет системное время прошивки", - "VSyncToggleTooltip": "Эмуляция вертикальной синхронизации консоли, которая ограничивает количество кадров в секунду в большинстве игр; отключение может привести к тому, что игры будут запущены с более высокой частотой кадров, но загрузка игры может занять больше времени, либо игра не запустится вообще.\n\nМожно включать и выключать эту настройку непосредственно в игре с помощью горячих клавиш (F1 по умолчанию). Если планируете отключить вертикальную синхронизацию, рекомендуем настроить горячие клавиши.\n\nРекомендуется оставить включенным.", - "PptcToggleTooltip": "Сохраняет скомпилированные JIT-функции для того, чтобы не преобразовывать их по новой каждый раз при запуске игры.\n\nУменьшает статтеры и значительно ускоряет последующую загрузку игр.\n\nРекомендуется оставить включенным.", - "FsIntegrityToggleTooltip": "Проверяет файлы при загрузке игры и если обнаружены поврежденные файлы, выводит сообщение о поврежденном хэше в журнале.\n\nНе влияет на производительность и необходим для помощи в устранении неполадок.\n\nРекомендуется оставить включенным.", - "AudioBackendTooltip": "Изменяет используемый аудио бэкенд для рендера звука.\n\nSDL2 является предпочтительным вариантом, в то время как OpenAL и SoundIO используются в качестве резервных.\n\nРекомендуется использование SDL2.", - "MemoryManagerTooltip": "Меняет разметку и доступ к гостевой памяти. Значительно влияет на производительность процессора.\n\nРекомендуется оставить \"Хост не установлен\"", - "MemoryManagerSoftwareTooltip": "Использует таблицу страниц для преобразования адресов. \nСамая высокая точность, но самая низкая производительность.", - "MemoryManagerHostTooltip": "Прямая разметка памяти в адресном пространстве хоста. \nЗначительно более быстрые запуск и компиляция JIT.", - "MemoryManagerUnsafeTooltip": "Производит прямую разметку памяти, но не маскирует адрес в гостевом адресном пространстве перед получением доступа. \nБыстро, но небезопасно. Гостевое приложение может получить доступ к памяти из Ryujinx, поэтому в этом режиме рекомендуется запускать только те программы, которым вы доверяете.", - "UseHypervisorTooltip": "Использует Hypervisor вместо JIT. Значительно увеличивает производительность, но может работать нестабильно.", - "DRamTooltip": "Использует альтернативный макет MemoryMode для имитации использования Nintendo Switch в режиме разработчика.\n\nПолезно только для пакетов текстур с высоким разрешением или модов добавляющих разрешение 4К. Не улучшает производительность.\n\nРекомендуется оставить выключенным.", - "IgnoreMissingServicesTooltip": "Игнорирует нереализованные сервисы Horizon в новых прошивках. Эта настройка поможет избежать вылеты при запуске определенных игр.\n\nРекомендуется оставить выключенным.", - "GraphicsBackendThreadingTooltip": "Выполняет команды графического бэкенда на втором потоке.\n\nУскоряет компиляцию шейдеров, уменьшает статтеры и повышает производительность на драйверах видеоадаптера без поддержки многопоточности. Производительность на драйверах с многопоточностью немного выше.\n\nРекомендуется оставить Автоматически.", - "GalThreadingTooltip": "Выполняет команды графического бэкенда на втором потоке.\n\nУскоряет компиляцию шейдеров, уменьшает статтеры и повышает производительность на драйверах видеоадаптера без поддержки многопоточности. Производительность на драйверах с многопоточностью немного выше.\n\nРекомендуется оставить Автоматически.", - "ShaderCacheToggleTooltip": "Сохраняет кэш шейдеров на диске, для уменьшения статтеров при последующих запусках.\n\nРекомендуется оставить включенным.", - "ResolutionScaleTooltip": "Увеличивает разрешение рендера игры.\n\nНекоторые игры могут не работать с этой настройкой и выглядеть смазано даже когда разрешение увеличено. Для таких игр может потребоваться установка модов, которые убирают сглаживание или увеличивают разрешение рендеринга. \nДля использования последнего, вам нужно будет выбрать опцию \"Нативное\".\n\nЭта опция может быть изменена во время игры по нажатию кнопки \"Применить\" ниже. Вы можете просто переместить окно настроек в сторону и поэкспериментировать, пока не подберете подходящие настройки для конкретной игры.\n\nИмейте в виду, что \"4x\" является излишеством.", - "ResolutionScaleEntryTooltip": "Масштабирование разрешения с плавающей запятой, например 1,5. Неинтегральное масштабирование с большой вероятностью вызовет сбои в работе.", - "AnisotropyTooltip": "Уровень анизотропной фильтрации. \n\nУстановите значение Автоматически, чтобы использовать значение по умолчанию игры.", - "AspectRatioTooltip": "Соотношение сторон окна рендерера.\n\nИзмените эту настройку только если вы используете мод для соотношения сторон, иначе изображение будет растянуто.\n\nРекомендуется настройка 16:9.", - "ShaderDumpPathTooltip": "Путь с дампами графических шейдеров", - "FileLogTooltip": "Включает ведение журнала в файл на диске. Не влияет на производительность.", - "StubLogTooltip": "Включает ведение журнала-заглушки. Не влияет на производительность.", - "InfoLogTooltip": "Включает вывод сообщений информационного журнала в консоль. Не влияет на производительность.", - "WarnLogTooltip": "Включает вывод сообщений журнала предупреждений в консоль. Не влияет на производительность.", - "ErrorLogTooltip": "Включает вывод сообщений журнала ошибок. Не влияет на производительность.", - "TraceLogTooltip": "Выводит сообщения журнала трассировки в консоли. Не влияет на производительность.", - "GuestLogTooltip": "Включает вывод сообщений гостевого журнала. Не влияет на производительность.", - "FileAccessLogTooltip": "Включает вывод сообщений журнала доступа к файлам.", - "FSAccessLogModeTooltip": "Включает вывод журнала доступа к файловой системе. Возможные режимы: 0-3", - "DeveloperOptionTooltip": "Используйте с осторожностью", - "OpenGlLogLevel": "Требует включения соответствующих уровней ведения журнала", - "DebugLogTooltip": "Выводит журнал сообщений отладки в консоли.\n\nИспользуйте только в случае просьбы разработчика, так как включение этой функции затруднит чтение журналов и ухудшит работу эмулятора.", - "LoadApplicationFileTooltip": "Открывает файловый менеджер для выбора файла, совместимого с Nintendo Switch.", - "LoadApplicationFolderTooltip": "Открывает файловый менеджер для выбора распакованного приложения, совместимого с Nintendo Switch.", - "OpenRyujinxFolderTooltip": "Открывает папку с файлами Ryujinx. ", - "OpenRyujinxLogsTooltip": "Открывает папку в которую записываются логи", - "ExitTooltip": "Выйти из Ryujinx", - "OpenSettingsTooltip": "Открывает окно параметров", - "OpenProfileManagerTooltip": "Открыть менеджер учетных записей", - "StopEmulationTooltip": "Остановка эмуляции текущей игры и возврат к списку игр", - "CheckUpdatesTooltip": "Проверяет наличие обновлений для Ryujinx", - "OpenAboutTooltip": "Открывает окно «О программе»", - "GridSize": "Размер сетки", - "GridSizeTooltip": "Меняет размер сетки элементов", - "SettingsTabSystemSystemLanguageBrazilianPortuguese": "Португальский язык (Бразилия)", - "AboutRyujinxContributorsButtonHeader": "Посмотреть всех участников", - "SettingsTabSystemAudioVolume": "Громкость: ", - "AudioVolumeTooltip": "Изменяет громкость звука", - "SettingsTabSystemEnableInternetAccess": "Гостевой доступ в интернет/сетевой режим", - "EnableInternetAccessTooltip": "Позволяет эмулированному приложению подключаться к Интернету.\n\nПри включении этой функции игры с возможностью сетевой игры могут подключаться друг к другу, если все эмуляторы (или реальные консоли) подключены к одной и той же точке доступа.\n\nНЕ разрешает подключение к серверам Nintendo. Может вызвать сбой в некоторых играх, которые пытаются подключиться к Интернету.\n\nРекомендутеся оставить выключенным.", - "GameListContextMenuManageCheatToolTip": "Открывает окно управления читами", - "GameListContextMenuManageCheat": "Управление читами", - "GameListContextMenuManageModToolTip": "Открывает окно управления модами", - "GameListContextMenuManageMod": "Управление модами", - "ControllerSettingsStickRange": "Диапазон:", - "DialogStopEmulationTitle": "Ryujinx - Остановка эмуляции", - "DialogStopEmulationMessage": "Вы уверены, что хотите остановить эмуляцию?", - "SettingsTabCpu": "Процессор", - "SettingsTabAudio": "Аудио", - "SettingsTabNetwork": "Сеть", - "SettingsTabNetworkConnection": "Подключение к сети", - "SettingsTabCpuCache": "Кэш процессора", - "SettingsTabCpuMemory": "Режим процессора", - "DialogUpdaterFlatpakNotSupportedMessage": "Пожалуйста, обновите Ryujinx через FlatHub.", - "UpdaterDisabledWarningTitle": "Средство обновления отключено", - "ControllerSettingsRotate90": "Повернуть на 90° по часовой стрелке", - "IconSize": "Размер обложек", - "IconSizeTooltip": "Меняет размер обложек", - "MenuBarOptionsShowConsole": "Показать консоль", - "ShaderCachePurgeError": "Ошибка очистки кэша шейдеров в {0}: {1}", - "UserErrorNoKeys": "Ключи не найдены", - "UserErrorNoFirmware": "Прошивка не найдена", - "UserErrorFirmwareParsingFailed": "Ошибка извлечения прошивки", - "UserErrorApplicationNotFound": "Приложение не найдено", - "UserErrorUnknown": "Неизвестная ошибка", - "UserErrorUndefined": "Неопределенная ошибка", - "UserErrorNoKeysDescription": "Ryujinx не удалось найти ваш 'prod.keys' файл", - "UserErrorNoFirmwareDescription": "Ryujinx не удалось найти ни одной установленной прошивки", - "UserErrorFirmwareParsingFailedDescription": "Ryujinx не удалось распаковать выбранную прошивку. Обычно это вызвано устаревшими ключами.", - "UserErrorApplicationNotFoundDescription": "Ryujinx не удалось найти валидное приложение по указанному пути.", - "UserErrorUnknownDescription": "Произошла неизвестная ошибка", - "UserErrorUndefinedDescription": "Произошла неизвестная ошибка. Этого не должно происходить, пожалуйста, свяжитесь с разработчиками.", - "OpenSetupGuideMessage": "Открыть руководство по установке", - "NoUpdate": "Без обновлений", - "TitleUpdateVersionLabel": "Version {0} - {1}", - "RyujinxInfo": "Ryujinx - Информация", - "RyujinxConfirm": "Ryujinx - Подтверждение", - "FileDialogAllTypes": "Все типы", - "Never": "Никогда", - "SwkbdMinCharacters": "Должно быть не менее {0} символов.", - "SwkbdMinRangeCharacters": "Должно быть {0}-{1} символов", - "SoftwareKeyboard": "Программная клавиатура", - "SoftwareKeyboardModeNumeric": "Должно быть в диапазоне 0-9 или '.'", - "SoftwareKeyboardModeAlphabet": "Не должно быть CJK-символов", - "SoftwareKeyboardModeASCII": "Текст должен быть только в ASCII кодировке", - "ControllerAppletControllers": "Поддерживаемые геймпады:", - "ControllerAppletPlayers": "Игроки:", - "ControllerAppletDescription": "Текущая конфигурация некорректна. Откройте параметры и перенастройте управление.", - "ControllerAppletDocked": "Используется стационарный режим. Управление в портативном режиме должно быть отключено.", - "UpdaterRenaming": "Переименование старых файлов...", - "UpdaterRenameFailed": "Программе обновления не удалось переименовать файл: {0}", - "UpdaterAddingFiles": "Добавление новых файлов...", - "UpdaterExtracting": "Извлечение обновления...", - "UpdaterDownloading": "Загрузка обновления...", - "Game": "Игра", - "Docked": "Стационарный режим", - "Handheld": "Портативный режим", - "ConnectionError": "Ошибка соединения", - "AboutPageDeveloperListMore": "{0} и другие...", - "ApiError": "Ошибка API.", - "LoadingHeading": "Загрузка {0}", - "CompilingPPTC": "Компиляция PTC", - "CompilingShaders": "Компиляция шейдеров", - "AllKeyboards": "Все клавиатуры", - "OpenFileDialogTitle": "Выберите совместимый файл для открытия", - "OpenFolderDialogTitle": "Выберите папку с распакованной игрой", - "AllSupportedFormats": "Все поддерживаемые форматы", - "RyujinxUpdater": "Ryujinx - Обновление", - "SettingsTabHotkeys": "Горячие клавиши", - "SettingsTabHotkeysHotkeys": "Горячие клавиши", - "SettingsTabHotkeysToggleVsyncHotkey": "Вертикальная синхронизация:", - "SettingsTabHotkeysScreenshotHotkey": "Сделать скриншот:", - "SettingsTabHotkeysShowUiHotkey": "Показать интерфейс:", - "SettingsTabHotkeysPauseHotkey": "Пауза эмуляции:", - "SettingsTabHotkeysToggleMuteHotkey": "Выключить звук:", - "ControllerMotionTitle": "Настройки управления движением", - "ControllerRumbleTitle": "Настройки вибрации", - "SettingsSelectThemeFileDialogTitle": "Выбрать файл темы", - "SettingsXamlThemeFile": "Файл темы Xaml", - "AvatarWindowTitle": "Управление аккаунтами - Аватар", - "Amiibo": "Amiibo", - "Unknown": "Неизвестно", - "Usage": "Применение", - "Writable": "Доступно для записи", - "SelectDlcDialogTitle": "Выберите файлы DLC", - "SelectUpdateDialogTitle": "Выберите файлы обновлений", - "SelectModDialogTitle": "Выбрать папку с модами", - "UserProfileWindowTitle": "Менеджер учетных записей", - "CheatWindowTitle": "Менеджер читов", - "DlcWindowTitle": "Управление DLC для {0} ({1})", - "ModWindowTitle": "Управление модами для {0} ({1})", - "UpdateWindowTitle": "Менеджер обновлений игр", - "CheatWindowHeading": "Доступные читы для {0} [{1}]", - "BuildId": "ID версии:", - "DlcWindowHeading": "{0} DLC", - "ModWindowHeading": "Моды для {0} ", - "UserProfilesEditProfile": "Изменить выбранные", - "Cancel": "Отмена", - "Save": "Сохранить", - "Discard": "Отменить", - "Paused": "Приостановлено", - "UserProfilesSetProfileImage": "Установить аватар", - "UserProfileEmptyNameError": "Необходимо ввести никнейм", - "UserProfileNoImageError": "Необходимо установить аватар", - "GameUpdateWindowHeading": "Доступные обновления для {0} ({1})", - "SettingsTabHotkeysResScaleUpHotkey": "Увеличить разрешение:", - "SettingsTabHotkeysResScaleDownHotkey": "Уменьшить разрешение:", - "UserProfilesName": "Никнейм:", - "UserProfilesUserId": "ID пользователя:", - "SettingsTabGraphicsBackend": "Графический бэкенд", - "SettingsTabGraphicsBackendTooltip": "Выберает бэкенд, который будет использован в эмуляторе.\n\nVulkan является лучшим выбором для всех современных графических карт с актуальными драйверами. В Vulkan также включена более быстрая компиляция шейдеров (меньше статтеров) для всех видеоадаптеров.\n\nПри использовании OpenGL можно достичь лучших результатов на старых видеоадаптерах Nvidia и AMD в Linux или на видеоадаптерах с небольшим количеством видеопамяти, хотя статтеров при компиляции шейдеров будет больше.\n\nРекомендуется использовать Vulkan. Используйте OpenGL, если ваш видеоадаптер не поддерживает Vulkan даже с актуальными драйверами.", - "SettingsEnableTextureRecompression": "Пережимать текстуры", - "SettingsEnableTextureRecompressionTooltip": "Сжатие ASTC текстур для уменьшения использования VRAM. \n\nИгры, использующие этот формат текстур: Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder и The Legend of Zelda: Tears of the Kingdom. \nНа видеоадаптерах с 4GiB видеопамяти или менее возможны вылеты при запуске этих игр. \n\nВключите, только если у вас заканчивается видеопамять в вышеупомянутых играх. \n\nРекомендуется оставить выключенным.", - "SettingsTabGraphicsPreferredGpu": "Предпочтительный видеоадаптер", - "SettingsTabGraphicsPreferredGpuTooltip": "Выберает видеоадаптер, который будет использоваться графическим бэкендом Vulkan.\n\nЭта настройка не влияет на видеоадаптер, который будет использоваться с OpenGL.\n\nЕсли вы не уверены что нужно выбрать, используйте графический процессор, помеченный как \"dGPU\". Если его нет, оставьте выбор по умолчанию.", - "SettingsAppRequiredRestartMessage": "Требуется перезапуск Ryujinx", - "SettingsGpuBackendRestartMessage": "Графический бэкенд или настройки графического процессора были изменены. Требуется перезапуск для вступления в силу изменений.", - "SettingsGpuBackendRestartSubMessage": "Перезапустить сейчас?", - "RyujinxUpdaterMessage": "Обновить Ryujinx до последней версии?", - "SettingsTabHotkeysVolumeUpHotkey": "Увеличить громкость:", - "SettingsTabHotkeysVolumeDownHotkey": "Уменьшить громкость:", - "SettingsEnableMacroHLE": "Использовать макрос высокоуровневой эмуляции видеоадаптера", - "SettingsEnableMacroHLETooltip": "Высокоуровневая эмуляции макрокода видеоадаптера.\n\nПовышает производительность, но может вызывать графические артефакты в некоторых играх.\n\nРекомендуется оставить включенным.", - "SettingsEnableColorSpacePassthrough": "Пропускать цветовое пространство", - "SettingsEnableColorSpacePassthroughTooltip": "Направляет бэкенд Vulkan на передачу информации о цвете без указания цветового пространства. Для пользователей с экранами с расширенной гаммой данная настройка приводит к получению более ярких цветов за счет снижения корректности цветопередачи.", - "VolumeShort": "Громкость", - "UserProfilesManageSaves": "Управление сохранениями", - "DeleteUserSave": "Удалить сохранения для этой игры?", - "IrreversibleActionNote": "Данное действие является необратимым.", - "SaveManagerHeading": "Редактирование сохранений для {0} ({1})", - "SaveManagerTitle": "Менеджер сохранений", - "Name": "Название", - "Size": "Размер", - "Search": "Поиск", - "UserProfilesRecoverLostAccounts": "Восстановить учетные записи", - "Recover": "Восстановление", - "UserProfilesRecoverHeading": "Были найдены сохранения для следующих аккаунтов", - "UserProfilesRecoverEmptyList": "Нет учетных записей для восстановления", - "GraphicsAATooltip": "Применимое сглаживание для рендера.\n\nFXAA размывает большую часть изображения, SMAA попытается найти \"зазубренные\" края и сгладить их.\n\nНе рекомендуется использовать вместе с масштабирующим фильтром FSR.\n\nЭта опция может быть изменена во время игры по нажатию \"Применить\" ниже; \nВы можете просто переместить окно настроек в сторону и поэкспериментировать, пока не найдёте подходящую настройку игры.\n\nРекомендуется использовать \"Нет\".", - "GraphicsAALabel": "Сглаживание:", - "GraphicsScalingFilterLabel": "Интерполяция:", - "GraphicsScalingFilterTooltip": "Фильтрация текстур, которая будет применяться при масштабировании.\n\nБилинейная хорошо работает для 3D-игр и является настройкой по умолчанию.\n\nСтупенчатая рекомендуется для пиксельных игр.\n\nFSR это фильтр резкости, который не рекомендуется использовать с FXAA или SMAA.\n\nЭта опция может быть изменена во время игры по нажатию кнопки \"Применить\" ниже; \nВы можете просто переместить окно настроек в сторону и поэкспериментировать, пока не подберете подходящие настройки для конкретной игры.\n\nРекомендуется использовать \"Билинейная\".", - "GraphicsScalingFilterBilinear": "Билинейная", - "GraphicsScalingFilterNearest": "Ступенчатая", - "GraphicsScalingFilterFsr": "FSR", - "GraphicsScalingFilterLevelLabel": "Уровень", - "GraphicsScalingFilterLevelTooltip": "Выбор режима работы FSR 1.0. Выше - четче.", - "SmaaLow": "SMAA Низкое", - "SmaaMedium": "SMAA Среднее", - "SmaaHigh": "SMAA Высокое", - "SmaaUltra": "SMAA Ультра", - "UserEditorTitle": "Редактирование пользователя", - "UserEditorTitleCreate": "Создание пользователя", - "SettingsTabNetworkInterface": "Сетевой интерфейс:", - "NetworkInterfaceTooltip": "Сетевой интерфейс, используемый для функций LAN/LDN.\n\nМожет использоваться для игры через интернет в сочетании с VPN или XLink Kai и игрой с поддержкой LAN.\n\nРекомендуется использовать \"По умолчанию\".", - "NetworkInterfaceDefault": "По умолчанию", - "PackagingShaders": "Упаковка шейдеров", - "AboutChangelogButton": "Список изменений на GitHub", - "AboutChangelogButtonTooltipMessage": "Нажмите, чтобы открыть список изменений для этой версии", - "SettingsTabNetworkMultiplayer": "Мультиплеер", - "MultiplayerMode": "Режим:", - "MultiplayerModeTooltip": "Меняет многопользовательский режим LDN.\n\nLdnMitm модифицирует функциональность локальной беспроводной/игры на одном устройстве в играх, позволяя играть с другими пользователями Ryujinx или взломанными консолями Nintendo Switch с установленным модулем ldn_mitm, находящимися в одной локальной сети друг с другом.\n\nМногопользовательская игра требует наличия у всех игроков одной и той же версии игры (т.е. Super Smash Bros. Ultimate v13.0.1 не может подключиться к v13.0.0).\n\nРекомендуется оставить отключенным.", - "MultiplayerModeDisabled": "Отключено", - "MultiplayerModeLdnMitm": "ldn_mitm" -} diff --git a/src/Ryujinx/Assets/Locales/th_TH.json b/src/Ryujinx/Assets/Locales/th_TH.json deleted file mode 100644 index 629442269..000000000 --- a/src/Ryujinx/Assets/Locales/th_TH.json +++ /dev/null @@ -1,780 +0,0 @@ -{ - "Language": "ภาษาไทย", - "MenuBarFileOpenApplet": "เปิด Applet", - "MenuBarFileOpenAppletOpenMiiAppletToolTip": "เปิด Mii Editor Applet ในโหมดสแตนด์อโลน", - "SettingsTabInputDirectMouseAccess": "เข้าถึงเมาส์ได้โดยตรง", - "SettingsTabSystemMemoryManagerMode": "โหมดจัดการหน่วยความจำ:", - "SettingsTabSystemMemoryManagerModeSoftware": "ซอฟต์แวร์", - "SettingsTabSystemMemoryManagerModeHost": "โฮสต์ (เร็ว)", - "SettingsTabSystemMemoryManagerModeHostUnchecked": "ไม่ได้ตรวจสอบโฮสต์ (เร็วที่สุด, แต่ไม่ปลอดภัย)", - "SettingsTabSystemUseHypervisor": "ใช้งาน Hypervisor", - "MenuBarFile": "ไฟล์", - "MenuBarFileOpenFromFile": "โหลดแอปพลิเคชั่นจากไฟล์", - "MenuBarFileOpenUnpacked": "โหลดเกมที่คลายแพ็กแล้ว", - "MenuBarFileOpenEmuFolder": "เปิดโฟลเดอร์ Ryujinx", - "MenuBarFileOpenLogsFolder": "เปิดโฟลเดอร์ Logs", - "MenuBarFileExit": "_ออก", - "MenuBarOptions": "_ตัวเลือก", - "MenuBarOptionsToggleFullscreen": "สลับการแสดงผลแบบเต็มหน้าจอ", - "MenuBarOptionsStartGamesInFullscreen": "เริ่มเกมในโหมดเต็มหน้าจอ", - "MenuBarOptionsStopEmulation": "หยุดการจำลอง", - "MenuBarOptionsSettings": "_ตั้งค่า", - "MenuBarOptionsManageUserProfiles": "_จัดการโปรไฟล์ผู้ใช้งาน", - "MenuBarActions": "การดำเนินการ", - "MenuBarOptionsSimulateWakeUpMessage": "จำลองข้อความปลุก", - "MenuBarActionsScanAmiibo": "สแกนหา Amiibo", - "MenuBarTools": "_เครื่องมือ", - "MenuBarToolsInstallFirmware": "ติดตั้งเฟิร์มแวร์", - "MenuBarFileToolsInstallFirmwareFromFile": "ติดตั้งเฟิร์มแวร์จาก ไฟล์ XCI หรือ ไฟล์ ZIP", - "MenuBarFileToolsInstallFirmwareFromDirectory": "ติดตั้งเฟิร์มแวร์จากไดเร็กทอรี", - "MenuBarToolsManageFileTypes": "จัดการประเภทไฟล์", - "MenuBarToolsInstallFileTypes": "ติดตั้งตามประเภทของไฟล์", - "MenuBarToolsUninstallFileTypes": "ถอนการติดตั้งตามประเภทของไฟล์", - "MenuBarView": "_View", - "MenuBarViewWindow": "Window Size", - "MenuBarViewWindow720": "720p", - "MenuBarViewWindow1080": "1080p", - "MenuBarHelp": "_ช่วยเหลือ", - "MenuBarHelpCheckForUpdates": "ตรวจสอบอัปเดต", - "MenuBarHelpAbout": "เกี่ยวกับ", - "MenuSearch": "กำลังค้นหา...", - "GameListHeaderFavorite": "ชื่นชอบ", - "GameListHeaderIcon": "ไอคอน", - "GameListHeaderApplication": "ชื่อ", - "GameListHeaderDeveloper": "ผู้พัฒนา", - "GameListHeaderVersion": "เวอร์ชั่น", - "GameListHeaderTimePlayed": "เล่นไปแล้ว", - "GameListHeaderLastPlayed": "เล่นล่าสุด", - "GameListHeaderFileExtension": "นามสกุลไฟล์", - "GameListHeaderFileSize": "ขนาดไฟล์", - "GameListHeaderPath": "ที่อยู่ไฟล์", - "GameListContextMenuOpenUserSaveDirectory": "เปิดไดเร็กทอรี่บันทึกของผู้ใช้", - "GameListContextMenuOpenUserSaveDirectoryToolTip": "เปิดไดเร็กทอรี่ซึ่งมีการบันทึกผู้ใช้ของแอปพลิเคชัน", - "GameListContextMenuOpenDeviceSaveDirectory": "เปิดไดเร็กทอรี่บันทึกของอุปกรณ์", - "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "เปิดไดเรกทอรี่ซึ่งมีบันทึกอุปกรณ์ของแอปพลิเคชัน", - "GameListContextMenuOpenBcatSaveDirectory": "เปิดไดเรกทอรี่บันทึก BCAT", - "GameListContextMenuOpenBcatSaveDirectoryToolTip": "เปิดไดเรกทอรี่ซึ่งมีการบันทึก BCAT ของแอปพลิเคชัน", - "GameListContextMenuManageTitleUpdates": "จัดการอัปเดตตามหัวข้อ", - "GameListContextMenuManageTitleUpdatesToolTip": "เปิดหน้าต่างการจัดการการอัพเดตหัวข้อ", - "GameListContextMenuManageDlc": "จัดการ DLC", - "GameListContextMenuManageDlcToolTip": "เปิดหน้าต่างจัดการ DLC", - "GameListContextMenuCacheManagement": "จัดการ แคช", - "GameListContextMenuCacheManagementPurgePptc": "เพิ่มเข้าคิวงาน PPTC ที่สร้างใหม่", - "GameListContextMenuCacheManagementPurgePptcToolTip": "ทริกเกอร์ PPTC ให้สร้างใหม่ในเวลาบูตเมื่อเปิดตัวเกมครั้งถัดไป", - "GameListContextMenuCacheManagementPurgeShaderCache": "ล้างแคช พื้นผิวและแสงเงา", - "GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "ลบแคช พื้นผิวและแสงเงา ของแอปพลิเคชัน", - "GameListContextMenuCacheManagementOpenPptcDirectory": "เปิดไดเรกทอรี่ PPTC", - "GameListContextMenuCacheManagementOpenPptcDirectoryToolTip": "เปิดไดเร็กทอรี่ PPTC แคช ของแอปพลิเคชัน", - "GameListContextMenuCacheManagementOpenShaderCacheDirectory": "เปิดไดเรกทอรี่ แคช พื้นผิวและแสงเงา", - "GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip": "เปิดไดเรกทอรี่ แคช พื้นผิวและแสงเงา ของแอปพลิเคชัน", - "GameListContextMenuExtractData": "แยกส่วนข้อมูล", - "GameListContextMenuExtractDataExeFS": "ExeFS", - "GameListContextMenuExtractDataExeFSToolTip": "แยกส่วน ExeFS ออกจากการกำหนดค่าปัจจุบันของแอปพลิเคชัน (รวมถึงการอัปเดต)", - "GameListContextMenuExtractDataRomFS": "RomFS", - "GameListContextMenuExtractDataRomFSToolTip": "แยกส่วน RomFS ออกจากการกำหนดค่าปัจจุบันของแอปพลิเคชัน (รวมถึงการอัพเดต)", - "GameListContextMenuExtractDataLogo": "โลโก้", - "GameListContextMenuExtractDataLogoToolTip": "แยกส่วน โลโก้ ออกจากการกำหนดค่าปัจจุบันของแอปพลิเคชัน (รวมถึงการอัปเดต)", - "GameListContextMenuCreateShortcut": "สร้างทางลัดของแอปพลิเคชัน", - "GameListContextMenuCreateShortcutToolTip": "สร้างทางลัดบนเดสก์ท็อปที่เรียกใช้แอปพลิเคชันที่เลือก", - "GameListContextMenuCreateShortcutToolTipMacOS": "สร้างทางลัดในโฟลเดอร์ Applications ของ macOS ที่เรียกใช้ Application ที่เลือก", - "GameListContextMenuOpenModsDirectory": "เปิดไดเร็กทอรี่ Mods", - "GameListContextMenuOpenModsDirectoryToolTip": "เปิดไดเร็กทอรี่ Mods ของแอปพลิเคชัน", - "GameListContextMenuOpenSdModsDirectory": "เปิดไดเร็กทอรี่ Mods Atmosphere", - "GameListContextMenuOpenSdModsDirectoryToolTip": "เปิดไดเร็กทอรี่ Atmosphere ของการ์ด SD สำรองซึ่งมี Mods ของแอปพลิเคชัน มีประโยชน์สำหรับ Mods ที่บรรจุมากับฮาร์ดแวร์จริง", - "StatusBarGamesLoaded": "เกมส์โหลดแล้ว {0}/{1}", - "StatusBarSystemVersion": "เวอร์ชั่นของระบบ: {0}", - "LinuxVmMaxMapCountDialogTitle": "ตรวจพบขีดจำกัดต่ำสุด สำหรับการแมปหน่วยความจำ", - "LinuxVmMaxMapCountDialogTextPrimary": "คุณต้องการที่จะเพิ่มค่า vm.max_map_count ไปยัง {0}", - "LinuxVmMaxMapCountDialogTextSecondary": "บางเกมอาจพยายามสร้างการแมปหน่วยความจำมากกว่าที่ได้รับอนุญาตในปัจจุบัน รียูจินซ์ จะปิดตัวลงเมื่อเกินขีดจำกัดนี้", - "LinuxVmMaxMapCountDialogButtonUntilRestart": "ใช่, จนกว่าจะรีสตาร์ทครั้งถัดไป", - "LinuxVmMaxMapCountDialogButtonPersistent": "ใช่, อย่างถาวร", - "LinuxVmMaxMapCountWarningTextPrimary": "จำนวนสูงสุดของการแม็ปหน่วยความจำ ต่ำกว่าที่แนะนำ", - "LinuxVmMaxMapCountWarningTextSecondary": "ค่าปัจจุบันของ vm.max_map_count ({0}) มีค่าต่ำกว่า {1} บางเกมอาจพยายามสร้างการแมปหน่วยความจำมากกว่าที่ได้รับอนุญาตในปัจจุบัน รียูจินซ์ จะปิดตัวลงเมื่อเกินขีดจำกัดนี้\n\nคุณอาจต้องการเพิ่มขีดจำกัดด้วยตนเองหรือติดตั้ง pkexec ซึ่งอนุญาตให้ ริวจินซ์ เพื่อช่วยเหลือคุณได้", - "Settings": "ตั้งค่า", - "SettingsTabGeneral": "หน้าจอผู้ใช้", - "SettingsTabGeneralGeneral": "ทั่วไป", - "SettingsTabGeneralEnableDiscordRichPresence": "เปิดใช้งาน Discord Rich Presence", - "SettingsTabGeneralCheckUpdatesOnLaunch": "ตรวจหาการอัปเดตเมื่อเปิดโปรแกรม", - "SettingsTabGeneralShowConfirmExitDialog": "แสดง \"ยืนยันการออก\" กล่องข้อความโต้ตอบ", - "SettingsTabGeneralRememberWindowState": "Remember Window Size/Position", - "SettingsTabGeneralHideCursor": "ซ่อน เคอร์เซอร์:", - "SettingsTabGeneralHideCursorNever": "ไม่มี", - "SettingsTabGeneralHideCursorOnIdle": "เมื่อไม่ได้ใช้", - "SettingsTabGeneralHideCursorAlways": "ตลอดเวลา", - "SettingsTabGeneralGameDirectories": "ไดเรกทอรี่ของเกม", - "SettingsTabGeneralAdd": "เพิ่ม", - "SettingsTabGeneralRemove": "เอาออก", - "SettingsTabSystem": "ระบบ", - "SettingsTabSystemCore": "แกนกลาง", - "SettingsTabSystemSystemRegion": "ภูมิภาคของระบบ:", - "SettingsTabSystemSystemRegionJapan": "ญี่ปุ่น", - "SettingsTabSystemSystemRegionUSA": "สหรัฐอเมริกา", - "SettingsTabSystemSystemRegionEurope": "ยุโรป", - "SettingsTabSystemSystemRegionAustralia": "ออสเตรเลีย", - "SettingsTabSystemSystemRegionChina": "จีน", - "SettingsTabSystemSystemRegionKorea": "เกาหลี", - "SettingsTabSystemSystemRegionTaiwan": "ไต้หวัน", - "SettingsTabSystemSystemLanguage": "ภาษาของระบบ:", - "SettingsTabSystemSystemLanguageJapanese": "ญี่ปุ่น", - "SettingsTabSystemSystemLanguageAmericanEnglish": "อังกฤษ (อเมริกัน)", - "SettingsTabSystemSystemLanguageFrench": "ฝรั่งเศส", - "SettingsTabSystemSystemLanguageGerman": "เยอรมัน", - "SettingsTabSystemSystemLanguageItalian": "อิตาลี", - "SettingsTabSystemSystemLanguageSpanish": "สเปน", - "SettingsTabSystemSystemLanguageChinese": "จีน", - "SettingsTabSystemSystemLanguageKorean": "เกาหลี", - "SettingsTabSystemSystemLanguageDutch": "ดัตช์", - "SettingsTabSystemSystemLanguagePortuguese": "โปรตุเกส", - "SettingsTabSystemSystemLanguageRussian": "รัสเซีย", - "SettingsTabSystemSystemLanguageTaiwanese": "จีนตัวเต็ม (ไต้หวัน)", - "SettingsTabSystemSystemLanguageBritishEnglish": "อังกฤษ (บริติช)", - "SettingsTabSystemSystemLanguageCanadianFrench": "ฝรั่งเศส (แคนาดา)", - "SettingsTabSystemSystemLanguageLatinAmericanSpanish": "สเปน (ลาตินอเมริกา)", - "SettingsTabSystemSystemLanguageSimplifiedChinese": "จีน (ตัวย่อ)", - "SettingsTabSystemSystemLanguageTraditionalChinese": "จีน (ดั้งเดิม)", - "SettingsTabSystemSystemTimeZone": "เขตเวลาของระบบ:", - "SettingsTabSystemSystemTime": "เวลาของระบบ:", - "SettingsTabSystemEnableVsync": "VSync", - "SettingsTabSystemEnablePptc": "PPTC (แคชโปรไฟล์การแปลแบบถาวร)", - "SettingsTabSystemEnableFsIntegrityChecks": "ตรวจสอบความถูกต้องของ FS", - "SettingsTabSystemAudioBackend": "ระบบเสียงเบื้องหลัง:", - "SettingsTabSystemAudioBackendDummy": "Dummy", - "SettingsTabSystemAudioBackendOpenAL": "OpenAL", - "SettingsTabSystemAudioBackendSoundIO": "SoundIO", - "SettingsTabSystemAudioBackendSDL2": "SDL2", - "SettingsTabSystemHacks": "แฮ็ก", - "SettingsTabSystemHacksNote": "อาจทำให้เกิดข้อผิดพลาดได้", - "SettingsTabSystemExpandDramSize": "ใช้รูปแบบหน่วยความจำสำรอง (โหมดนักพัฒนา)", - "SettingsTabSystemIgnoreMissingServices": "ไม่สนใจบริการที่ขาดหายไป", - "SettingsTabGraphics": "กราฟิก", - "SettingsTabGraphicsAPI": "กราฟฟิก API", - "SettingsTabGraphicsEnableShaderCache": "เปิดใช้งาน แคชพื้นผิวและแสงเงา", - "SettingsTabGraphicsAnisotropicFiltering": "ตัวกรองแบบ Anisotropic:", - "SettingsTabGraphicsAnisotropicFilteringAuto": "อัตโนมัติ", - "SettingsTabGraphicsAnisotropicFiltering2x": "2x", - "SettingsTabGraphicsAnisotropicFiltering4x": "4x", - "SettingsTabGraphicsAnisotropicFiltering8x": "8x", - "SettingsTabGraphicsAnisotropicFiltering16x": "16x", - "SettingsTabGraphicsResolutionScale": "อัตราส่วนความละเอียด:", - "SettingsTabGraphicsResolutionScaleCustom": "กำหนดเอง (ไม่แนะนำ)", - "SettingsTabGraphicsResolutionScaleNative": "พื้นฐานของระบบ (720p/1080p)", - "SettingsTabGraphicsResolutionScale2x": "2x (1440p/2160p)", - "SettingsTabGraphicsResolutionScale3x": "3x (2160p/3240p)", - "SettingsTabGraphicsResolutionScale4x": "4x (2880p/4320p) (ไม่แนะนำ)", - "SettingsTabGraphicsAspectRatio": "อัตราส่วนภาพ:", - "SettingsTabGraphicsAspectRatio4x3": "4:3", - "SettingsTabGraphicsAspectRatio16x9": "16:9", - "SettingsTabGraphicsAspectRatio16x10": "16:10", - "SettingsTabGraphicsAspectRatio21x9": "21:9", - "SettingsTabGraphicsAspectRatio32x9": "32:9", - "SettingsTabGraphicsAspectRatioStretch": "ยืดภาพเพื่อให้พอดีกับหน้าต่าง", - "SettingsTabGraphicsDeveloperOptions": "ตัวเลือกนักพัฒนา", - "SettingsTabGraphicsShaderDumpPath": "ที่เก็บ ดัมพ์ไฟล์ พื้นผิวและแสงเงา:", - "SettingsTabLogging": "ประวัติ", - "SettingsTabLoggingLogging": "ประวัติ", - "SettingsTabLoggingEnableLoggingToFile": "เปิดใช้งาน ประวัติ ไปยังไฟล์", - "SettingsTabLoggingEnableStubLogs": "เปิดใช้งาน ประวัติ", - "SettingsTabLoggingEnableInfoLogs": "เปิดใช้งาน ประวัติการใช้งาน", - "SettingsTabLoggingEnableWarningLogs": "เปิดใช้งาน ประวัติคำเตือน", - "SettingsTabLoggingEnableErrorLogs": "เปิดใช้งาน ประวัติข้อผิดพลาด", - "SettingsTabLoggingEnableTraceLogs": "เปิดใช้งาน ประวัติการติดตาม", - "SettingsTabLoggingEnableGuestLogs": "เปิดใช้งาน บันทึกของผู้เยี่ยมชม", - "SettingsTabLoggingEnableFsAccessLogs": "เปิดใช้งาน ประวัติการเข้าถึง Fs", - "SettingsTabLoggingFsGlobalAccessLogMode": "โหมด ประวัติการเข้าถึงส่วนกลาง:", - "SettingsTabLoggingDeveloperOptions": "ตัวเลือกนักพัฒนา", - "SettingsTabLoggingDeveloperOptionsNote": "คำเตือน: จะทำให้ประสิทธิภาพลดลง", - "SettingsTabLoggingGraphicsBackendLogLevel": "ระดับการบันทึกประวัติ กราฟิกเบื้องหลัง:", - "SettingsTabLoggingGraphicsBackendLogLevelNone": "ไม่มี", - "SettingsTabLoggingGraphicsBackendLogLevelError": "ผิดพลาด", - "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "ช้าลง", - "SettingsTabLoggingGraphicsBackendLogLevelAll": "ทั้งหมด", - "SettingsTabLoggingEnableDebugLogs": "เปิดใช้งาน ประวัติแก้ไขข้อบกพร่อง", - "SettingsTabInput": "ป้อนข้อมูล", - "SettingsTabInputEnableDockedMode": "ด็อกโหมด", - "SettingsTabInputDirectKeyboardAccess": "เข้าถึงคีย์บอร์ดโดยตรง", - "SettingsButtonSave": "บันทึก", - "SettingsButtonClose": "ปิด", - "SettingsButtonOk": "ตกลง", - "SettingsButtonCancel": "ยกเลิก", - "SettingsButtonApply": "นำไปใช้", - "ControllerSettingsPlayer": "ผู้เล่น", - "ControllerSettingsPlayer1": "ผู้เล่นคนที่ 1", - "ControllerSettingsPlayer2": "ผู้เล่นคนที่ 2", - "ControllerSettingsPlayer3": "ผู้เล่นคนที่ 3", - "ControllerSettingsPlayer4": "ผู้เล่นคนที่ 4", - "ControllerSettingsPlayer5": "ผู้เล่นคนที่ 5", - "ControllerSettingsPlayer6": "ผู้เล่นคนที่ 6", - "ControllerSettingsPlayer7": "ผู้เล่นคนที่ 7", - "ControllerSettingsPlayer8": "ผู้เล่นคนที่ 8", - "ControllerSettingsHandheld": "แฮนด์เฮลด์โหมด", - "ControllerSettingsInputDevice": "อุปกรณ์ป้อนข้อมูล", - "ControllerSettingsRefresh": "รีเฟรช", - "ControllerSettingsDeviceDisabled": "ปิดการใช้งาน", - "ControllerSettingsControllerType": "ประเภทของคอนโทรลเลอร์", - "ControllerSettingsControllerTypeHandheld": "แฮนด์เฮลด์", - "ControllerSettingsControllerTypeProController": "โปรคอนโทรลเลอร์", - "ControllerSettingsControllerTypeJoyConPair": "จับคู่ จอยคอน", - "ControllerSettingsControllerTypeJoyConLeft": "จอยคอน ด้านซ้าย", - "ControllerSettingsControllerTypeJoyConRight": "จอยคอน ด้านขวา", - "ControllerSettingsProfile": "โปรไฟล์", - "ControllerSettingsProfileDefault": "ค่าเริ่มต้น", - "ControllerSettingsLoad": "โหลด", - "ControllerSettingsAdd": "เพิ่ม", - "ControllerSettingsRemove": "เอาออก", - "ControllerSettingsButtons": "ปุ่มกด", - "ControllerSettingsButtonA": "A", - "ControllerSettingsButtonB": "B", - "ControllerSettingsButtonX": "X", - "ControllerSettingsButtonY": "Y", - "ControllerSettingsButtonPlus": "+", - "ControllerSettingsButtonMinus": "-", - "ControllerSettingsDPad": "ปุ่มลูกศร", - "ControllerSettingsDPadUp": "ขึ้น", - "ControllerSettingsDPadDown": "ลง", - "ControllerSettingsDPadLeft": "ซ้าย", - "ControllerSettingsDPadRight": "ขวา", - "ControllerSettingsStickButton": "ปุ่ม", - "ControllerSettingsStickUp": "ขึ้น", - "ControllerSettingsStickDown": "ลง", - "ControllerSettingsStickLeft": "ซ้าย", - "ControllerSettingsStickRight": "ขวา", - "ControllerSettingsStickStick": "จอยสติ๊ก", - "ControllerSettingsStickInvertXAxis": "กลับทิศทางของแกน X", - "ControllerSettingsStickInvertYAxis": "กลับทิศทางของแกน Y", - "ControllerSettingsStickDeadzone": "โซนที่ไม่ทำงานของ จอยสติ๊ก:", - "ControllerSettingsLStick": "จอยสติ๊ก ด้านซ้าย", - "ControllerSettingsRStick": "จอยสติ๊ก ด้านขวา", - "ControllerSettingsTriggersLeft": "ทริกเกอร์ ด้านซ้าย", - "ControllerSettingsTriggersRight": "ทริกเกอร์ ด้านขวา", - "ControllerSettingsTriggersButtonsLeft": "ปุ่มทริกเกอร์ ด้านซ้าย", - "ControllerSettingsTriggersButtonsRight": "ปุ่มทริกเกอร์ ด้านขวา", - "ControllerSettingsTriggers": "ทริกเกอร์", - "ControllerSettingsTriggerL": "L", - "ControllerSettingsTriggerR": "R", - "ControllerSettingsTriggerZL": "ZL", - "ControllerSettingsTriggerZR": "ZR", - "ControllerSettingsLeftSL": "SL", - "ControllerSettingsLeftSR": "SR", - "ControllerSettingsRightSL": "SL", - "ControllerSettingsRightSR": "SR", - "ControllerSettingsExtraButtonsLeft": "ปุ่มกดเสริม ด้านซ้าย", - "ControllerSettingsExtraButtonsRight": "ปุ่มกดเสริม ด้านขวา", - "ControllerSettingsMisc": "การควบคุมเพิ่มเติม", - "ControllerSettingsTriggerThreshold": "ตั้งค่าขีดจำกัดของ ทริกเกอร์:", - "ControllerSettingsMotion": "การเคลื่อนไหว", - "ControllerSettingsMotionUseCemuhookCompatibleMotion": "ใช้การเคลื่อนไหวที่เข้ากันได้กับ CemuHook", - "ControllerSettingsMotionControllerSlot": "ช่องเสียบ คอนโทรลเลอร์:", - "ControllerSettingsMotionMirrorInput": "นำเข้าการสะท้อน การควบคุม", - "ControllerSettingsMotionRightJoyConSlot": "ช่องเสียบ จอยคอน ด้านขวา:", - "ControllerSettingsMotionServerHost": "เจ้าของเซิร์ฟเวอร์:", - "ControllerSettingsMotionGyroSensitivity": "ความไวของไจโร:", - "ControllerSettingsMotionGyroDeadzone": "ส่วนไม่ทำงานของไจโร:", - "ControllerSettingsSave": "บันทึก", - "ControllerSettingsClose": "ปิด", - "KeyUnknown": "Unknown", - "KeyShiftLeft": "Shift Left", - "KeyShiftRight": "Shift Right", - "KeyControlLeft": "Ctrl Left", - "KeyMacControlLeft": "⌃ Left", - "KeyControlRight": "Ctrl Right", - "KeyMacControlRight": "⌃ Right", - "KeyAltLeft": "Alt Left", - "KeyMacAltLeft": "⌥ Left", - "KeyAltRight": "Alt Right", - "KeyMacAltRight": "⌥ Right", - "KeyWinLeft": "⊞ Left", - "KeyMacWinLeft": "⌘ Left", - "KeyWinRight": "⊞ Right", - "KeyMacWinRight": "⌘ Right", - "KeyMenu": "Menu", - "KeyUp": "Up", - "KeyDown": "Down", - "KeyLeft": "Left", - "KeyRight": "Right", - "KeyEnter": "Enter", - "KeyEscape": "Escape", - "KeySpace": "Space", - "KeyTab": "Tab", - "KeyBackSpace": "Backspace", - "KeyInsert": "Insert", - "KeyDelete": "Delete", - "KeyPageUp": "Page Up", - "KeyPageDown": "Page Down", - "KeyHome": "Home", - "KeyEnd": "End", - "KeyCapsLock": "Caps Lock", - "KeyScrollLock": "Scroll Lock", - "KeyPrintScreen": "Print Screen", - "KeyPause": "Pause", - "KeyNumLock": "Num Lock", - "KeyClear": "Clear", - "KeyKeypad0": "Keypad 0", - "KeyKeypad1": "Keypad 1", - "KeyKeypad2": "Keypad 2", - "KeyKeypad3": "Keypad 3", - "KeyKeypad4": "Keypad 4", - "KeyKeypad5": "Keypad 5", - "KeyKeypad6": "Keypad 6", - "KeyKeypad7": "Keypad 7", - "KeyKeypad8": "Keypad 8", - "KeyKeypad9": "Keypad 9", - "KeyKeypadDivide": "Keypad Divide", - "KeyKeypadMultiply": "Keypad Multiply", - "KeyKeypadSubtract": "Keypad Subtract", - "KeyKeypadAdd": "Keypad Add", - "KeyKeypadDecimal": "Keypad Decimal", - "KeyKeypadEnter": "Keypad Enter", - "KeyNumber0": "0", - "KeyNumber1": "1", - "KeyNumber2": "2", - "KeyNumber3": "3", - "KeyNumber4": "4", - "KeyNumber5": "5", - "KeyNumber6": "6", - "KeyNumber7": "7", - "KeyNumber8": "8", - "KeyNumber9": "9", - "KeyTilde": "~", - "KeyGrave": "`", - "KeyMinus": "-", - "KeyPlus": "+", - "KeyBracketLeft": "[", - "KeyBracketRight": "]", - "KeySemicolon": ";", - "KeyQuote": "\"", - "KeyComma": ",", - "KeyPeriod": ".", - "KeySlash": "/", - "KeyBackSlash": "\\", - "KeyUnbound": "Unbound", - "GamepadLeftStick": "L Stick Button", - "GamepadRightStick": "R Stick Button", - "GamepadLeftShoulder": "Left Shoulder", - "GamepadRightShoulder": "Right Shoulder", - "GamepadLeftTrigger": "Left Trigger", - "GamepadRightTrigger": "Right Trigger", - "GamepadDpadUp": "Up", - "GamepadDpadDown": "Down", - "GamepadDpadLeft": "Left", - "GamepadDpadRight": "Right", - "GamepadMinus": "-", - "GamepadPlus": "+", - "GamepadGuide": "Guide", - "GamepadMisc1": "Misc", - "GamepadPaddle1": "Paddle 1", - "GamepadPaddle2": "Paddle 2", - "GamepadPaddle3": "Paddle 3", - "GamepadPaddle4": "Paddle 4", - "GamepadTouchpad": "Touchpad", - "GamepadSingleLeftTrigger0": "Left Trigger 0", - "GamepadSingleRightTrigger0": "Right Trigger 0", - "GamepadSingleLeftTrigger1": "Left Trigger 1", - "GamepadSingleRightTrigger1": "Right Trigger 1", - "StickLeft": "Left Stick", - "StickRight": "Right Stick", - "UserProfilesSelectedUserProfile": "โปรไฟล์ผู้ใช้งานที่เลือก:", - "UserProfilesSaveProfileName": "บันทึกชื่อโปรไฟล์", - "UserProfilesChangeProfileImage": "เปลี่ยนรูปโปรไฟล์", - "UserProfilesAvailableUserProfiles": "โปรไฟล์ผู้ใช้ที่ใช้งานได้:", - "UserProfilesAddNewProfile": "สร้างโปรไฟล์ใหม่", - "UserProfilesDelete": "ลบ", - "UserProfilesClose": "ปิด", - "ProfileNameSelectionWatermark": "เลือก ชื่อเล่น", - "ProfileImageSelectionTitle": "เลือก รูปโปรไฟล์ ของคุณ", - "ProfileImageSelectionHeader": "เลือก รูปโปรไฟล์", - "ProfileImageSelectionNote": "คุณสามารถนำเข้ารูปโปรไฟล์ที่กำหนดเอง หรือ เลือกอวาต้าจากเฟิร์มแวร์ระบบได้", - "ProfileImageSelectionImportImage": "นำเข้า ไฟล์รูปภาพ", - "ProfileImageSelectionSelectAvatar": "เลือก รูปอวาต้า เฟิร์มแวร์", - "InputDialogTitle": "กล่องโต้ตอบการป้อนข้อมูล", - "InputDialogOk": "ตกลง", - "InputDialogCancel": "ยกเลิก", - "InputDialogAddNewProfileTitle": "เลือก ชื่อโปรไฟล์", - "InputDialogAddNewProfileHeader": "กรุณาใส่ชื่อโปรไฟล์", - "InputDialogAddNewProfileSubtext": "(ความยาวสูงสุด: {0})", - "AvatarChoose": "เลือก รูปอวาต้า ของคุณ", - "AvatarSetBackgroundColor": "ตั้งค่าสีพื้นหลัง", - "AvatarClose": "ปิด", - "ControllerSettingsLoadProfileToolTip": "โหลด โปรไฟล์", - "ControllerSettingsAddProfileToolTip": "เพิ่ม โปรไฟล์", - "ControllerSettingsRemoveProfileToolTip": "ลบ โปรไฟล์", - "ControllerSettingsSaveProfileToolTip": "บันทึก โปรไฟล์", - "MenuBarFileToolsTakeScreenshot": "ถ่ายภาพหน้าจอ", - "MenuBarFileToolsHideUi": "ซ่อน UI", - "GameListContextMenuRunApplication": "เรียกใช้แอปพลิเคชัน", - "GameListContextMenuToggleFavorite": "สลับรายการโปรด", - "GameListContextMenuToggleFavoriteToolTip": "สลับสถานะเกมที่ชื่นชอบ", - "SettingsTabGeneralTheme": "ธีม:", - "SettingsTabGeneralThemeDark": "มืด", - "SettingsTabGeneralThemeLight": "สว่าง", - "ControllerSettingsConfigureGeneral": "กำหนดค่า", - "ControllerSettingsRumble": "การสั่นไหว", - "ControllerSettingsRumbleStrongMultiplier": "เพิ่มความแรงการสั่นไหว", - "ControllerSettingsRumbleWeakMultiplier": "ลดความแรงการสั่นไหว", - "DialogMessageSaveNotAvailableMessage": "ไม่มีข้อมูลบันทึกไว้สำหรับ {0} [{1:x16}]", - "DialogMessageSaveNotAvailableCreateSaveMessage": "คุณต้องการสร้างข้อมูลบันทึกสำหรับเกมนี้หรือไม่?", - "DialogConfirmationTitle": "ริวจินซ์ - ยืนยัน", - "DialogUpdaterTitle": "รียูจินซ์ - อัพเดต", - "DialogErrorTitle": "รียูจินซ์ - ผิดพลาด", - "DialogWarningTitle": "รียูจินซ์ - คำเตือน", - "DialogExitTitle": "รียูจินซ์ - ออก", - "DialogErrorMessage": "รียูจินซ์ พบข้อผิดพลาด", - "DialogExitMessage": "คุณแน่ใจหรือไม่ว่าต้องการปิด ริวจินซ์ หรือไม่?", - "DialogExitSubMessage": "ข้อมูลที่ไม่ได้บันทึกทั้งหมดจะสูญหาย!", - "DialogMessageCreateSaveErrorMessage": "มีข้อผิดพลาดในการสร้างข้อมูลการบันทึกที่ระบุ: {0}", - "DialogMessageFindSaveErrorMessage": "มีข้อผิดพลาดในการค้นหาข้อมูลที่บันทึกไว้ที่ระบุ: {0}", - "FolderDialogExtractTitle": "เลือกโฟลเดอร์ที่จะแตกไฟล์เข้าไป", - "DialogNcaExtractionMessage": "กำลังแตกไฟล์ {0} จากส่วน {1}...", - "DialogNcaExtractionTitle": "รียูจินซ์ - เครื่องมือแตกไฟล์ของ NCA", - "DialogNcaExtractionMainNcaNotFoundErrorMessage": "เกิดความล้มเหลวในการแตกไฟล์เนื่องจากไม่พบ NCA หลักในไฟล์ที่เลือก", - "DialogNcaExtractionCheckLogErrorMessage": "เกิดความล้มเหลวในการแตกไฟล์ โปรดอ่านไฟล์บันทึกเพื่อดูข้อมูลเพิ่มเติม", - "DialogNcaExtractionSuccessMessage": "การแตกไฟล์เสร็จสมบูรณ์แล้ว", - "DialogUpdaterConvertFailedMessage": "ไม่สามารถแปลงเวอร์ชั่น รียูจินซ์ ปัจจุบันได้", - "DialogUpdaterCancelUpdateMessage": "ยกเลิกการอัพเดต!", - "DialogUpdaterAlreadyOnLatestVersionMessage": "คุณกำลังใช้ รียูจินซ์ เวอร์ชั่นที่อัปเดตล่าสุด!", - "DialogUpdaterFailedToGetVersionMessage": "เกิดข้อผิดพลาดขณะพยายามรับข้อมูลเวอร์ชั่นจาก GitHub Release ปัญหานี้อาจเกิดขึ้นได้หากมีการรวบรวมเวอร์ชั่นใหม่โดย GitHub Actions โปรดลองอีกครั้งในอีกไม่กี่นาทีข้างหน้า", - "DialogUpdaterConvertFailedGithubMessage": "ไม่สามารถแปลงเวอร์ชั่น รียูจินซ์ ที่ได้รับจาก Github Release", - "DialogUpdaterDownloadingMessage": "กำลังดาวน์โหลดอัปเดต...", - "DialogUpdaterExtractionMessage": "กำลังแตกไฟล์อัปเดต...", - "DialogUpdaterRenamingMessage": "กำลังลบไฟล์เก่า...", - "DialogUpdaterAddingFilesMessage": "กำลังเพิ่มไฟล์อัปเดตใหม่...", - "DialogUpdaterCompleteMessage": "อัปเดตเสร็จสมบูรณ์แล้ว!", - "DialogUpdaterRestartMessage": "คุณต้องการรีสตาร์ท รียูจินซ์ ตอนนี้หรือไม่?", - "DialogUpdaterNoInternetMessage": "คุณไม่ได้เชื่อมต่อกับอินเทอร์เน็ต!", - "DialogUpdaterNoInternetSubMessage": "โปรดตรวจสอบว่าคุณมีการเชื่อมต่ออินเทอร์เน็ตว่ามีการใช้งานได้หรือไม่!", - "DialogUpdaterDirtyBuildMessage": "คุณไม่สามารถอัปเดต Dirty build ของ รียูจินซ์ ได้!", - "DialogUpdaterDirtyBuildSubMessage": "โปรดดาวน์โหลด รียูจินซ์ ได้ที่ https://ryujinx.org/ หากคุณกำลังมองหาเวอร์ชั่นที่รองรับ", - "DialogRestartRequiredMessage": "จำเป็นต้องรีสตาร์ทเพื่อให้การอัพเดตสามารถให้งานได้", - "DialogThemeRestartMessage": "บันทึกธีมแล้ว จำเป็นต้องรีสตาร์ทเพื่อใช้ธีม", - "DialogThemeRestartSubMessage": "คุณต้องการรีสตาร์ทหรือไม่?", - "DialogFirmwareInstallEmbeddedMessage": "คุณต้องการติดตั้งเฟิร์มแวร์ที่ฝังอยู่ในเกมนี้หรือไม่? (เฟิร์มแวร์ {0})", - "DialogFirmwareInstallEmbeddedSuccessMessage": "ไม่พบเฟิร์มแวร์ที่ติดตั้งไว้ แต่ รียูจินซ์ สามารถติดตั้งเฟิร์มแวร์ได้ {0} จากเกมที่ให้มา\nตอนนี้โปรแกรมจำลองจะเริ่มทำงาน", - "DialogFirmwareNoFirmwareInstalledMessage": "ไม่มีการติดตั้งเฟิร์มแวร์", - "DialogFirmwareInstalledMessage": "เฟิร์มแวร์ติดตั้งแล้ว {0}", - "DialogInstallFileTypesSuccessMessage": "ติดตั้งตามประเภทของไฟล์สำเร็จแล้ว!", - "DialogInstallFileTypesErrorMessage": "ติดตั้งตามประเภทของไฟล์ไม่สำเร็จ", - "DialogUninstallFileTypesSuccessMessage": "ถอนการติดตั้งตามประเภทของไฟล์สำเร็จแล้ว!", - "DialogUninstallFileTypesErrorMessage": "ไม่สามารถถอนการติดตั้งตามประเภทของไฟล์ได้", - "DialogOpenSettingsWindowLabel": "เปิดหน้าต่างการตั้งค่า", - "DialogControllerAppletTitle": "แอพเพล็ตคอนโทรลเลอร์", - "DialogMessageDialogErrorExceptionMessage": "เกิดข้อผิดพลาดในการแสดงกล่องโต้ตอบข้อความ: {0}", - "DialogSoftwareKeyboardErrorExceptionMessage": "เกิดข้อผิดพลาดในการแสดงซอฟต์แวร์แป้นพิมพ์: {0}", - "DialogErrorAppletErrorExceptionMessage": "เกิดข้อผิดพลาดในการแสดงกล่องโต้ตอบ ข้อผิดพลาด แอปเพล็ต: {0}", - "DialogUserErrorDialogMessage": "{0}: {1}", - "DialogUserErrorDialogInfoMessage": "\nสำหรับข้อมูลเพิ่มเติมเกี่ยวกับวิธีแก้ไขข้อผิดพลาดนี้ โปรดทำตามคำแนะนำในการตั้งค่าของเรา", - "DialogUserErrorDialogTitle": "ข้อผิดพลาด รียูจินซ์ ({0})", - "DialogAmiiboApiTitle": "อะมิโบ API", - "DialogAmiiboApiFailFetchMessage": "เกิดข้อผิดพลาดขณะเรียกข้อมูลจาก API", - "DialogAmiiboApiConnectErrorMessage": "ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์ อะมิโบ API บ้างบริการอาจหยุดทำงาน หรือไม่คุณต้องทำการตรวจสอบว่าการเชื่อมต่ออินเทอร์เน็ตของคุณอยู่ในสถานะเชื่อมต่ออยู่หรือไม่", - "DialogProfileInvalidProfileErrorMessage": "โปรไฟล์ {0} เข้ากันไม่ได้กับระบบการกำหนดค่าอินพุตปัจจุบัน", - "DialogProfileDefaultProfileOverwriteErrorMessage": "ไม่สามารถเขียนทับโปรไฟล์เริ่มต้นได้", - "DialogProfileDeleteProfileTitle": "กำลังลบโปรไฟล์", - "DialogProfileDeleteProfileMessage": "การดำเนินการนี้ไม่สามารถย้อนกลับได้ คุณแน่ใจหรือไม่ว่าต้องการดำเนินการต่อหรือไม่?", - "DialogWarning": "คำเตือน", - "DialogPPTCDeletionMessage": "คุณกำลังจะจัดคิวการสร้าง PPTC ใหม่ในการบูตครั้งถัดไป:\n\n{0}\n\nคุณแน่ใจหรือไม่ว่าต้องการดำเนินการต่อหรือไม่?", - "DialogPPTCDeletionErrorMessage": "มีข้อผิดพลาดในการล้างแคช PPTC {0}: {1}", - "DialogShaderDeletionMessage": "คุณกำลังจะลบ เชเดอร์แคช:\n\n{0}\n\nคุณแน่ใจหรือไม่ว่าต้องการดำเนินการต่อหรือไม่?", - "DialogShaderDeletionErrorMessage": "เกิดข้อผิดพลาดในการล้าง เชเดอร์แคช {0}: {1}", - "DialogRyujinxErrorMessage": "รียูจินซ์ พบข้อผิดพลาด", - "DialogInvalidTitleIdErrorMessage": "ข้อผิดพลาดของ UI: เกมที่เลือกไม่มีชื่อ ID ที่ถูกต้อง", - "DialogFirmwareInstallerFirmwareNotFoundErrorMessage": "ไม่พบเฟิร์มแวร์ของระบบที่ถูกต้อง {0}.", - "DialogFirmwareInstallerFirmwareInstallTitle": "ติดตั้งเฟิร์มแวร์ {0}", - "DialogFirmwareInstallerFirmwareInstallMessage": "นี่คื่อเวอร์ชั่นของระบบ {0} ที่ได้รับการติดตั้งเมื่อเร็วๆ นี้", - "DialogFirmwareInstallerFirmwareInstallSubMessage": "\n\nสิ่งนี้จะแทนที่เวอร์ชั่นของระบบปัจจุบัน {0}.", - "DialogFirmwareInstallerFirmwareInstallConfirmMessage": "\n\nคุณต้องการดำเนินการต่อหรือไม่?", - "DialogFirmwareInstallerFirmwareInstallWaitMessage": "กำลังติดตั้งเฟิร์มแวร์...", - "DialogFirmwareInstallerFirmwareInstallSuccessMessage": "การติดตั้งเวอร์ชั่นระบบ {0} เรียบร้อยแล้ว", - "DialogUserProfileDeletionWarningMessage": "จะไม่มีโปรไฟล์อื่นให้เปิดหากโปรไฟล์ที่เลือกถูกลบ", - "DialogUserProfileDeletionConfirmMessage": "คุณต้องการลบโปรไฟล์ที่เลือกหรือไม่?", - "DialogUserProfileUnsavedChangesTitle": "คำเตือน - มีการเปลี่ยนแปลงที่ไม่ได้บันทึก", - "DialogUserProfileUnsavedChangesMessage": "คุณได้ทำการเปลี่ยนแปลงโปรไฟล์ผู้ใช้นี้โดยไม่ได้รับการบันทึก", - "DialogUserProfileUnsavedChangesSubMessage": "คุณต้องการยกเลิกการเปลี่ยนแปลงของคุณหรือไม่?", - "DialogControllerSettingsModifiedConfirmMessage": "การตั้งค่าคอนโทรลเลอร์ปัจจุบันได้รับการอัปเดตแล้ว", - "DialogControllerSettingsModifiedConfirmSubMessage": "คุณต้องการบันทึกหรือไม่?", - "DialogLoadFileErrorMessage": "{0} ไฟล์เกิดข้อผิดพลาด: {1}", - "DialogModAlreadyExistsMessage": "มีม็อดอยู่แล้ว", - "DialogModInvalidMessage": "ไดเร็กทอรีที่ระบุไม่มี ม็อดอยู่!", - "DialogModDeleteNoParentMessage": "ไม่สามารถลบ: ไม่พบไดเร็กทอรีหลักสำหรับ ม็อด \"{0}\"!", - "DialogDlcNoDlcErrorMessage": "ไฟล์ที่ระบุไม่มี DLC สำหรับชื่อที่เลือก!", - "DialogPerformanceCheckLoggingEnabledMessage": "คุณได้เปิดใช้งานการบันทึกการติดตาม ซึ่งออกแบบมาเพื่อให้นักพัฒนาใช้เท่านั้น", - "DialogPerformanceCheckLoggingEnabledConfirmMessage": "เพื่อประสิทธิภาพสูงสุด ขอแนะนำให้ปิดใช้งานการบันทึกการติดตาม คุณต้องการปิดใช้การบันทึกการติดตามตอนนี้หรือไม่?", - "DialogPerformanceCheckShaderDumpEnabledMessage": "คุณได้เปิดใช้งาน การดัมพ์เชเดอร์ ซึ่งออกแบบมาเพื่อให้นักพัฒนาใช้งานเท่านั้น", - "DialogPerformanceCheckShaderDumpEnabledConfirmMessage": "เพื่อประสิทธิภาพสูงสุด ขอแนะนำให้ปิดใช้การดัมพ์เชเดอร์ คุณต้องการปิดการใช้งานการ ดัมพ์เชเดอร์ ตอนนี้หรือไม่?", - "DialogLoadAppGameAlreadyLoadedMessage": "ทำการโหลดเกมเรียบร้อยแล้ว", - "DialogLoadAppGameAlreadyLoadedSubMessage": "โปรดหยุดการจำลอง หรือปิดโปรแกรมจำลองก่อนที่จะเปิดเกมอื่น", - "DialogUpdateAddUpdateErrorMessage": "ไฟล์ที่ระบุไม่มีการอัพเดตสำหรับชื่อเรื่องที่เลือก!", - "DialogSettingsBackendThreadingWarningTitle": "คำเตือน - การทำเธรดแบ็กเอนด์", - "DialogSettingsBackendThreadingWarningMessage": "รียูจินซ์ ต้องรีสตาร์ทหลังจากเปลี่ยนตัวเลือกนี้จึงจะใช้งานได้อย่างสมบูรณ์ คุณอาจต้องปิดการใช้งาน มัลติเธรด ของไดรเวอร์ของคุณด้วยตนเองเมื่อใช้ รียูจินซ์ ทั้งนี้ขึ้นอยู่กับแพลตฟอร์มของคุณ", - "DialogModManagerDeletionWarningMessage": "คุณกำลังจะลบ ม็อด: {0}\n\nคุณแน่ใจหรือไม่ว่าต้องการดำเนินการต่อ?", - "DialogModManagerDeletionAllWarningMessage": "คุณกำลังจะลบม็อดทั้งหมดสำหรับชื่อนี้\n\nคุณแน่ใจหรือไม่ว่าต้องการดำเนินการต่อ?", - "SettingsTabGraphicsFeaturesOptions": "คุณสมบัติ", - "SettingsTabGraphicsBackendMultithreading": "มัลติเธรด กราฟิกเบื้องหลัง:", - "CommonAuto": "อัตโนมัติ", - "CommonOff": "ปิดการใช้งาน", - "CommonOn": "เปิดใช้งาน", - "InputDialogYes": "ใช่", - "InputDialogNo": "ไม่ใช่", - "DialogProfileInvalidProfileNameErrorMessage": "ชื่อไฟล์ประกอบด้วยอักขระที่ไม่ถูกต้อง กรุณาลองอีกครั้ง", - "MenuBarOptionsPauseEmulation": "หยุดชั่วคราว", - "MenuBarOptionsResumeEmulation": "ดำเนินการต่อ", - "AboutUrlTooltipMessage": "คลิกเพื่อเปิดเว็บไซต์ รียูจินซ์ บนเบราว์เซอร์เริ่มต้นของคุณ", - "AboutDisclaimerMessage": "ทางผู้พัฒนาโปรแกรม รียูจินซ์ ไม่มีส่วนเกี่ยวข้องกับทางบริษัท Nintendo™\nหรือพันธมิตรใดๆ ทั้งสิ้น!", - "AboutAmiiboDisclaimerMessage": "AmiiboAPI (www.amiiboapi.com) ถูกใช้\nในการจำลอง อะมิโบ ของเรา", - "AboutPatreonUrlTooltipMessage": "คลิกเพื่อเปิดหน้า เพทรีออน ของ รียูจินซ์ บนเบราว์เซอร์เริ่มต้นของคุณ", - "AboutGithubUrlTooltipMessage": "คลิกเพื่อเปิดหน้า กิตฮับ ของ ริวจินซ์ บนเบราว์เซอร์เริ่มต้นของคุณ", - "AboutDiscordUrlTooltipMessage": "คลิกเพื่อเปิดคำเชิญเข้าสู่เซิร์ฟเวอร์ ดิสคอร์ด ของ รียูจินซ์ บนเบราว์เซอร์เริ่มต้นของคุณ", - "AboutTwitterUrlTooltipMessage": "คลิกเพื่อเปิดหน้าเพจ ทวิตเตอร์ ของ รียูจินซ์ บนเบราว์เซอร์เริ่มต้นของคุณ", - "AboutRyujinxAboutTitle": "เกี่ยวกับ:", - "AboutRyujinxAboutContent": "รียูจินซ์ เป็นอีมูเลเตอร์สำหรับ Nintendo Switch™\nโปรดสนับสนุนเราบน เพทรีออน\nรับข่าวสารล่าสุดทั้งหมดบน ทวิตเตอร์ หรือ ดิสคอร์ด ของเรา\nนักพัฒนาที่สนใจจะมีส่วนร่วมสามารถดูข้อมูลเพิ่มเติมได้ที่ กิตฮับ หรือ ดิสคอร์ด ของเรา", - "AboutRyujinxMaintainersTitle": "ได้รับการดูแลรักษาโดย:", - "AboutRyujinxMaintainersContentTooltipMessage": "คลิกเพื่อเปิดหน้าผู้ร่วมให้ข้อมูลในเบราว์เซอร์เริ่มต้นของคุณ", - "AboutRyujinxSupprtersTitle": "ลายนามผู้สนับสนุนบน เพทรีออน:", - "AmiiboSeriesLabel": "อะมิโบซีรีส์", - "AmiiboCharacterLabel": "ตัวละคร", - "AmiiboScanButtonLabel": "สแกนเลย", - "AmiiboOptionsShowAllLabel": "แสดง อะมิโบ ทั้งหมด", - "AmiiboOptionsUsRandomTagLabel": "แฮ็ค: ใช้แท็กสุ่ม Uuid", - "DlcManagerTableHeadingEnabledLabel": "เปิดใช้งานแล้ว", - "DlcManagerTableHeadingTitleIdLabel": "ชื่อไอดี", - "DlcManagerTableHeadingContainerPathLabel": "ที่เก็บไฟล์ คอนเทนเนอร์", - "DlcManagerTableHeadingFullPathLabel": "ที่เก็บไฟล์แบบเต็ม", - "DlcManagerRemoveAllButton": "ลบทั้งหมด", - "DlcManagerEnableAllButton": "เปิดใช้งานทั้งหมด", - "DlcManagerDisableAllButton": "ปิดใช้งานทั้งหมด", - "ModManagerDeleteAllButton": "ลบทั้งหมด", - "MenuBarOptionsChangeLanguage": "เปลี่ยนภาษา", - "MenuBarShowFileTypes": "แสดงประเภทของไฟล์", - "CommonSort": "เรียงลำดับ", - "CommonShowNames": "แสดงชื่อ", - "CommonFavorite": "สิ่งที่ชื่นชอบ", - "OrderAscending": "จากน้อยไปมาก", - "OrderDescending": "จากมากไปน้อย", - "SettingsTabGraphicsFeatures": "คุณสมบัติ และ การเพิ่มประสิทธิภาพ", - "ErrorWindowTitle": "หน้าต่างแสดงข้อผิดพลาด", - "ToggleDiscordTooltip": "เลือกว่าจะแสดง รียูจินซ์ ในกิจกรรม ดิสคอร์ด \"ที่กำลังเล่นอยู่\" ของคุณหรือไม่?", - "AddGameDirBoxTooltip": "ป้อนไดเรกทอรี่เกมที่จะทำการเพิ่มลงในรายการ", - "AddGameDirTooltip": "เพิ่มไดเรกทอรี่เกมลงในรายการ", - "RemoveGameDirTooltip": "ลบไดเรกทอรี่เกมที่เลือก", - "CustomThemeCheckTooltip": "ใช้ธีม Avalonia แบบกำหนดเองสำหรับ GUI เพื่อเปลี่ยนรูปลักษณ์ของเมนูโปรแกรมจำลอง", - "CustomThemePathTooltip": "ไปยังที่เก็บไฟล์ธีม GUI แบบกำหนดเอง", - "CustomThemeBrowseTooltip": "เรียกดูธีม GUI ที่กำหนดเอง", - "DockModeToggleTooltip": "ด็อกโหมด ทำให้ระบบจำลองการทำงานเสมือน Nintendo ที่กำลังเชื่อมต่ออยู่ด็อก สิ่งนี้จะปรับปรุงความเสถียรภาพของกราฟิกในเกมส่วนใหญ่ ในทางกลับกัน การปิดใช้จะทำให้ระบบจำลองทำงานเหมือนกับ Nintendo Switch แบบพกพา ส่งผลให้คุณภาพกราฟิกลดลง\n\nกำหนดค่าส่วนควบคุมของผู้เล่น 1 หากวางแผนที่จะใช้ด็อกโหมด กำหนดค่าการควบคุมแบบ แฮนด์เฮลด์ หากวางแผนที่จะใช้โหมดแฮนด์เฮลด์\n\nเปิดทิ้งไว้หากคุณไม่แน่ใจ", - "DirectKeyboardTooltip": "รองรับการเข้าถึงแป้นพิมพ์โดยตรง (HID) ให้เกมเข้าถึงคีย์บอร์ดของคุณเป็นอุปกรณ์ป้อนข้อความ\n\nใช้งานได้กับเกมที่รองรับการใช้งานคีย์บอร์ดบนฮาร์ดแวร์ของ Switch เท่านั้น\n\nหากคุณไม่แน่ใจปล่อยให้ปิดอย่างนั้น", - "DirectMouseTooltip": "รองรับการเข้าถึงเมาส์โดยตรง (HID) ให้เกมเข้าถึงเมาส์ของคุณเป็นอุปกรณ์ชี้ตำแหน่ง\n\nใช้งานได้เฉพาะกับเกมที่รองรับการควบคุมเมาส์บนฮาร์ดแวร์ของ Switch เท่านั้น ซึ่งมีอยู่ไม่มากนัก\n\nเมื่อเปิดใช้งาน ฟังก์ชั่นหน้าจอสัมผัสอาจไม่ทำงาน\n\nหากคุณไม่แน่ใจปล่อยให้ปิดอย่างนั้น", - "RegionTooltip": "เปลี่ยนภูมิภาคของระบบ", - "LanguageTooltip": "เปลี่ยนภาษาของระบบ", - "TimezoneTooltip": "เปลี่ยนโซนเวลาของระบบ", - "TimeTooltip": "เปลี่ยนเวลาของระบบ", - "VSyncToggleTooltip": "Vertical Sync ของคอนโซลจำลอง โดยพื้นฐานแล้วเป็นตัวจำกัดเฟรมสำหรับเกมส่วนใหญ่ การปิดใช้งานอาจทำให้เกมทำงานด้วยความเร็วสูงขึ้น หรือทำให้หน้าจอการโหลดใช้เวลานานขึ้นหรือค้าง\n\nสามารถสลับได้ในเกมด้วยปุ่มลัดตามที่คุณต้องการ (F1 เป็นค่าเริ่มต้น) เราขอแนะนำให้ทำเช่นนี้หากคุณวางแผนที่จะปิดการใช้งาน\n\nหากคุณไม่แน่ใจให้ปล่อยไว้อย่างนั้น", - "PptcToggleTooltip": "บันทึกฟังก์ชั่น JIT ที่แปลแล้ว ดังนั้นจึงไม่จำเป็นต้องแปลทุกครั้งที่โหลดเกม\n\nลดอาการกระตุกและเร่งความเร็วการบูตได้อย่างมากหลังจากการบูตครั้งแรกของเกม\n\nปล่อยไว้หากคุณไม่แน่ใจ", - "FsIntegrityToggleTooltip": "ตรวจสอบไฟล์ที่เสียหายเมื่อบูตเกม และหากตรวจพบไฟล์ที่เสียหาย จะแสดงข้อผิดพลาดของแฮชในบันทึก\n\nไม่มีผลกระทบต่อประสิทธิภาพการทำงานและมีไว้เพื่อช่วยในการแก้ไขปัญหา\n\nปล่อยไว้หากคุณไม่แน่ใจ", - "AudioBackendTooltip": "เปลี่ยนแบ็กเอนด์ที่ใช้ในการเรนเดอร์เสียง\n\nSDL2 เป็นที่ต้องการ ในขณะที่ OpenAL และ SoundIO ถูกใช้เป็นทางเลือกสำรอง ดัมมี่จะไม่มีเสียง\n\nปล่อยไว้หากคุณไม่แน่ใจ", - "MemoryManagerTooltip": "เปลี่ยนวิธีการแมปและเข้าถึงหน่วยความจำของผู้เยี่ยมชม ส่งผลอย่างมากต่อประสิทธิภาพการทำงานของ CPU ที่จำลอง\n\nตั้งค่าเป็น ไม่ทำการตรวจสอบ โฮสต์ หากคุณไม่แน่ใจ", - "MemoryManagerSoftwareTooltip": "ใช้ตารางหน้าซอฟต์แวร์สำหรับการแปลที่อยู่ ความแม่นยำสูงสุดแต่ประสิทธิภาพช้าที่สุด", - "MemoryManagerHostTooltip": "แมปหน่วยความจำในพื้นที่ที่อยู่โฮสต์โดยตรง การคอมไพล์และดำเนินการ JIT เร็วขึ้นมาก", - "MemoryManagerUnsafeTooltip": "แมปหน่วยความจำโดยตรง แต่อย่าปิดบังที่อยู่ภายในพื้นที่ที่อยู่ของผู้เยี่ยมชมก่อนที่จะเข้าถึง เร็วกว่า แต่ต้องแลกกับความปลอดภัย แอปพลิเคชั่นผู้เยี่ยมชมสามารถเข้าถึงหน่วยความจำได้จากทุกที่ใน รียูจินซ์ ดังนั้นให้รันเฉพาะโปรแกรมที่คุณเชื่อถือในโหมดนี้", - "UseHypervisorTooltip": "ใช้ Hypervisor แทน JIT ปรับปรุงประสิทธิภาพอย่างมากเมื่อพร้อมใช้งาน แต่อาจไม่เสถียรในสถานะปัจจุบัน", - "DRamTooltip": "ใช้เค้าโครง MemoryMode ทางเลือกเพื่อเลียนแบบโมเดลการพัฒนาสวิตช์\n\nสิ่งนี้มีประโยชน์สำหรับแพ็กพื้นผิวที่มีความละเอียดสูงกว่าหรือม็อดที่มีความละเอียด 4k เท่านั้น ไม่ปรับปรุงประสิทธิภาพ\n\nปล่อยให้ปิดหากคุณไม่แน่ใจ", - "IgnoreMissingServicesTooltip": "ละเว้นบริการ Horizon OS ที่ยังไม่ได้ใช้งาน วิธีนี้อาจช่วยในการหลีกเลี่ยงข้อผิดพลาดเมื่อบู๊ตเกมบางเกม\n\nปล่อยให้ปิดหากคุณไม่แน่ใจ", - "GraphicsBackendThreadingTooltip": "ดำเนินการคำสั่งแบ็กเอนด์กราฟิกบนเธรดที่สอง\n\nเร่งความเร็วการคอมไพล์เชเดอร์ ลดการกระตุก และปรับปรุงประสิทธิภาพการทำงานของไดรเวอร์ GPU โดยไม่ต้องรองรับมัลติเธรดในตัว ประสิทธิภาพที่ดีขึ้นเล็กน้อยสำหรับไดรเวอร์ที่มีมัลติเธรด\n\nตั้งเป็น อัตโนมัติ หากคุณไม่แน่ใจ", - "GalThreadingTooltip": "ดำเนินการคำสั่งแบ็กเอนด์กราฟิกบนเธรดที่สอง\n\nเร่งความเร็วการคอมไพล์เชเดอร์ ลดการกระตุก และปรับปรุงประสิทธิภาพการทำงานของไดรเวอร์ GPU โดยไม่ต้องรองรับมัลติเธรดในตัว ประสิทธิภาพที่ดีขึ้นเล็กน้อยสำหรับไดรเวอร์ที่มีมัลติเธรด\n\nตั้งเป็น อัตโนมัติ หากคุณไม่แน่ใจ", - "ShaderCacheToggleTooltip": "บันทึกแคชเชเดอร์ของดิสก์ซึ่งช่วยลดการกระตุกในการรันครั้งต่อๆ ไป\n\nปล่อยไว้หากคุณไม่แน่ใจ", - "ResolutionScaleTooltip": "คูณความละเอียดการเรนเดอร์ของเกม\n\nเกมบางเกมอาจไม่สามารถใช้งานได้และดูเป็นพิกเซลแม้ว่าความละเอียดจะเพิ่มขึ้นก็ตาม สำหรับเกมเหล่านั้น คุณอาจต้องค้นหาม็อดที่ลบรอยหยักของภาพหรือเพิ่มความละเอียดในการเรนเดอร์ภายใน หากต้องการใช้อย่างหลัง คุณอาจต้องเลือก Native\n\nตัวเลือกนี้สามารถเปลี่ยนแปลงได้ในขณะที่เกมกำลังทำงานอยู่โดยคลิก \"นำมาใช้\" ด้านล่าง คุณสามารถย้ายหน้าต่างการตั้งค่าไปด้านข้างและทดลองจนกว่าคุณจะพบรูปลักษณ์ที่คุณต้องการสำหรับเกม\n\nโปรดทราบว่า 4x นั้นเกินความจำเป็นสำหรับการตั้งค่าแทบทุกประเภท", - "ResolutionScaleEntryTooltip": "สเกลความละเอียดจุดทศนิยม เช่น 1.5 ไม่ใช่จำนวนเต็มของสเกล มีแนวโน้มที่จะก่อให้เกิดปัญหาหรือความผิดพลาดได้", - "AnisotropyTooltip": "ระดับของการกรองแบบ Anisotropic ตั้งค่าเป็นอัตโนมัติเพื่อใช้ค่าที่เกมร้องขอ", - "AspectRatioTooltip": "อัตราส่วนภาพที่ใช้กับหน้าต่างตัวแสดงภาพ\n\nเปลี่ยนสิ่งนี้หากคุณใช้ตัวดัดแปลงอัตราส่วนกว้างยาวสำหรับเกมของคุณ ไม่เช่นนั้นกราฟิกจะถูกยืดออก\n\nทิ้งไว้ที่ 16:9 หากไม่แน่ใจ", - "ShaderDumpPathTooltip": "ที่เก็บ ดัมพ์ไฟล์ พื้นผิวและแสงเงา", - "FileLogTooltip": "บันทึก ประวัติคอนโซลลงในไฟล์บันทึกบนดิสก์ จะไม่ส่งผลกระทบต่อประสิทธิภาพการทำงาน", - "StubLogTooltip": "พิมพ์ข้อความประวัติในคอนโซล จะไม่ส่งผลกระทบต่อประสิทธิภาพการทำงาน", - "InfoLogTooltip": "พิมพ์ข้อความบันทึกข้อมูลในคอนโซล จะไม่ส่งผลกระทบต่อประสิทธิภาพการทำงาน", - "WarnLogTooltip": "พิมพ์ข้อความประวัติแจ้งตือนในคอนโซล จะไม่ส่งผลกระทบต่อประสิทธิภาพการทำงาน", - "ErrorLogTooltip": "พิมพ์ข้อความบันทึกข้อผิดพลาดในคอนโซล จะไม่ส่งผลกระทบต่อประสิทธิภาพการทำงาน", - "TraceLogTooltip": "พิมพ์ข้อความประวัติการติดตามในคอนโซล ไม่ส่งผลกระทบต่อประสิทธิภาพการทำงาน", - "GuestLogTooltip": "พิมพ์ข้อความประวัติของผู้เยี่ยมชมในคอนโซล ไม่ส่งผลกระทบต่อประสิทธิภาพการทำงาน", - "FileAccessLogTooltip": "พิมพ์ข้อความบันทึกการเข้าถึงไฟล์ในคอนโซล", - "FSAccessLogModeTooltip": "เปิดใช้งาน เอาต์พุตประวัติการเข้าถึง FS ไปยังคอนโซล โหมดที่เป็นไปได้คือ 0-3", - "DeveloperOptionTooltip": "โปรดใช้ด้วยความระมัดระวัง", - "OpenGlLogLevel": "จำเป็นต้องเปิดใช้งานระดับบันทึกที่เหมาะสม", - "DebugLogTooltip": "พิมพ์ข้อความประวัติการแก้ไขข้อบกพร่องในคอนโซล\n\nใช้สิ่งนี้เฉพาะเมื่อได้รับคำแนะนำจากเจ้าหน้าที่โดยเฉพาะเท่านั้น เนื่องจากจะทำให้บันทึกอ่านยากและทำให้ประสิทธิภาพของโปรแกรมจำลองแย่ลง", - "LoadApplicationFileTooltip": "เปิด File Explorer เพื่อเลือกไฟล์ที่เข้ากันได้กับ Switch ที่จะโหลด", - "LoadApplicationFolderTooltip": "เปิดตัวสำรวจไฟล์เพื่อเลือกไฟล์ที่เข้ากันได้กับ Switch ที่จะโหลด", - "OpenRyujinxFolderTooltip": "เปิดโฟลเดอร์ระบบไฟล์ Ryujinx", - "OpenRyujinxLogsTooltip": "เปิดโฟลเดอร์ ที่เก็บไฟล์ประวัติ", - "ExitTooltip": "ออกจากโปรแกรม รียูจินซ์", - "OpenSettingsTooltip": "เปิดหน้าต่างการตั้งค่า", - "OpenProfileManagerTooltip": "เปิดหน้าต่างตัวจัดการโปรไฟล์ผู้ใช้", - "StopEmulationTooltip": "หยุดการจำลองของเกมที่เปิดอยู่ในปัจจุบันและกลับไปยังการเลือกเกม", - "CheckUpdatesTooltip": "ตรวจสอบการอัปเดตของ รียูจินซ์", - "OpenAboutTooltip": "เปิดหน้าต่าง เกี่ยวกับ", - "GridSize": "ขนาดตาราง", - "GridSizeTooltip": "เปลี่ยนขนาด ของตาราง", - "SettingsTabSystemSystemLanguageBrazilianPortuguese": "บราซิล โปรตุเกส", - "AboutRyujinxContributorsButtonHeader": "ดูผู้มีส่วนร่วมทั้งหมด", - "SettingsTabSystemAudioVolume": "ระดับเสียง: ", - "AudioVolumeTooltip": "ปรับระดับเสียง", - "SettingsTabSystemEnableInternetAccess": "การเข้าถึงอินเทอร์เน็ตของผู้เยี่ยมชม/โหมด LAN", - "EnableInternetAccessTooltip": "อนุญาตให้แอปพลิเคชันจำลองเชื่อมต่ออินเทอร์เน็ต\n\nเกมที่มีโหมด LAN สามารถเชื่อมต่อระหว่างกันได้เมื่อเปิดใช้งานและระบบเชื่อมต่อกับจุดเชื่อมต่อเดียวกัน รวมถึงคอนโซลจริงด้วย\n\nไม่อนุญาตให้มีการเชื่อมต่อกับเซิร์ฟเวอร์ Nintendo อาจทำให้เกิดการหยุดทำงานในบางเกมที่พยายามเชื่อมต่ออินเทอร์เน็ต\n\nปล่อยให้ปิดหากคุณไม่แน่ใจ", - "GameListContextMenuManageCheatToolTip": "ฟังชั่นจัดการสูตรโกง", - "GameListContextMenuManageCheat": "จัดการสูตรโกง", - "GameListContextMenuManageModToolTip": "จัดการ ม็อด", - "GameListContextMenuManageMod": "จัดการ ม็อด", - "ControllerSettingsStickRange": "ขอบเขต:", - "DialogStopEmulationTitle": "รียูจินซ์ - หยุดการจำลอง", - "DialogStopEmulationMessage": "คุณแน่ใจหรือไม่ว่าต้องการหยุดการจำลองหรือไม่?", - "SettingsTabCpu": "หน่วยประมวลผลกลาง", - "SettingsTabAudio": "เสียง", - "SettingsTabNetwork": "เครือข่าย", - "SettingsTabNetworkConnection": "การเชื่อมต่อเครือข่าย", - "SettingsTabCpuCache": "ซีพียู แคช", - "SettingsTabCpuMemory": "โหมดซีพียู", - "DialogUpdaterFlatpakNotSupportedMessage": "โปรดอัปเดต รียูจินซ์ ผ่านช่องทาง FlatHub", - "UpdaterDisabledWarningTitle": "ปิดใช้งานการอัปเดตแล้ว!", - "ControllerSettingsRotate90": "หมุน 90 องศา ตามเข็มนาฬิกา", - "IconSize": "ขนาดไอคอน", - "IconSizeTooltip": "เปลี่ยนขนาดของไอคอนเกม", - "MenuBarOptionsShowConsole": "แสดง คอนโซล", - "ShaderCachePurgeError": "เกิดข้อผิดพลาดในการล้างแคชเชเดอร์ {0}: {1}", - "UserErrorNoKeys": "ไม่พบ คีย์", - "UserErrorNoFirmware": "ไม่พบ เฟิร์มแวร์", - "UserErrorFirmwareParsingFailed": "เกิดข้อผิดพลาดในการวิเคราะห์เฟิร์มแวร์", - "UserErrorApplicationNotFound": "ไม่พบ แอปพลิเคชัน", - "UserErrorUnknown": "ข้อผิดพลาดที่ไม่รู้จัก", - "UserErrorUndefined": "ข้อผิดพลาดที่ไม่ได้ระบุ", - "UserErrorNoKeysDescription": "รียูจินซ์ ไม่พบไฟล์ 'prod.keys' ในเครื่องของคุณ", - "UserErrorNoFirmwareDescription": "รียูจินซ์ ไม่พบ เฟิร์มแวร์ที่ติดตั้งไว้ในเครื่องของคุณ", - "UserErrorFirmwareParsingFailedDescription": "รียูจินซ์ ไม่สามารถวิเคราะห์เฟิร์มแวร์ที่ให้มาได้ ซึ่งมักมีสาเหตุมาจากคีย์ที่ล้าสมัย", - "UserErrorApplicationNotFoundDescription": "รียูจินซ์ ไม่พบแอปพลิเคชันที่ถูกต้องในที่เก็บไฟล์ที่กำหนด", - "UserErrorUnknownDescription": "เกิดข้อผิดพลาดที่ไม่รู้จัก!", - "UserErrorUndefinedDescription": "เกิดข้อผิดพลาดที่ไม่สามารถระบุได้! สิ่งนี้ไม่ควรเกิดขึ้น โปรดติดต่อผู้พัฒนา!", - "OpenSetupGuideMessage": "เปิดคู่มือการตั้งค่า", - "NoUpdate": "ไม่มีการอัปเดต", - "TitleUpdateVersionLabel": "เวอร์ชั่น {0}", - "RyujinxInfo": "รียูจินซ์ – ข้อมูล", - "RyujinxConfirm": "รียูจินซ์ - ยืนยัน", - "FileDialogAllTypes": "ทุกประเภท", - "Never": "ไม่มี", - "SwkbdMinCharacters": "ต้องมีความยาวของตัวอักษรอย่างน้อย {0} ตัว", - "SwkbdMinRangeCharacters": "ต้องมีความยาวของตัวอักษร {0}-{1} ตัว", - "SoftwareKeyboard": "ซอฟต์แวร์ ของคีย์บอร์ด", - "SoftwareKeyboardModeNumeric": "ต้องเป็น 0-9 หรือ '.' เท่านั้น", - "SoftwareKeyboardModeAlphabet": "ต้องเป็นตัวอักษรที่ไม่ใช่ CJK เท่านั้น", - "SoftwareKeyboardModeASCII": "ต้องเป็นตัวอักษร ASCII เท่านั้น", - "ControllerAppletControllers": "คอนโทรลเลอร์ที่รองรับ:", - "ControllerAppletPlayers": "ผู้เล่น:", - "ControllerAppletDescription": "การกำหนดค่าปัจจุบันของคุณไม่ถูกต้อง เปิดการตั้งค่าและกำหนดค่าอินพุตของคุณใหม่", - "ControllerAppletDocked": "ตั้งค่าด็อกโหมด ควรปิดใช้งานการควบคุมแบบแฮนด์เฮลด์", - "UpdaterRenaming": "กำลังเปลี่ยนชื่อไฟล์เก่า...", - "UpdaterRenameFailed": "โปรแกรมอัปเดตไม่สามารถเปลี่ยนชื่อไฟล์ได้: {0}", - "UpdaterAddingFiles": "กำลังเพิ่มไฟล์ใหม่...", - "UpdaterExtracting": "กำลังแยกการอัปเดต...", - "UpdaterDownloading": "กำลังดาวน์โหลดอัปเดต...", - "Game": "เกมส์", - "Docked": "ด็อก", - "Handheld": "แฮนด์เฮลด์", - "ConnectionError": "การเชื่อมต่อล้มเหลว", - "AboutPageDeveloperListMore": "{0} และอื่นๆ ...", - "ApiError": "ข้อผิดพลาดของ API", - "LoadingHeading": "กำลังโหลด {0}", - "CompilingPPTC": "กำลังคอมไพล์ PTC", - "CompilingShaders": "กำลังคอมไพล์ พื้นผิวและแสงเงา", - "AllKeyboards": "คีย์บอร์ดทั้งหมด", - "OpenFileDialogTitle": "เลือกไฟล์ที่สนับสนุนเพื่อเปิด", - "OpenFolderDialogTitle": "เลือกโฟลเดอร์ที่มีเกมที่แตกไฟล์แล้ว", - "AllSupportedFormats": "รูปแบบที่รองรับทั้งหมด", - "RyujinxUpdater": "อัปเดต รียูจินซ์", - "SettingsTabHotkeys": "ปุ่มลัดของคีย์บอร์ด", - "SettingsTabHotkeysHotkeys": "ปุ่มลัดของคีย์บอร์ด", - "SettingsTabHotkeysToggleVsyncHotkey": "สลับเป็น VSync:", - "SettingsTabHotkeysScreenshotHotkey": "ภาพหน้าจอ:", - "SettingsTabHotkeysShowUiHotkey": "แสดง UI:", - "SettingsTabHotkeysPauseHotkey": "หยุดชั่วคราว:", - "SettingsTabHotkeysToggleMuteHotkey": "ปิดเสียง:", - "ControllerMotionTitle": "ตั้งค่าควบคุมการเคลื่อนไหว", - "ControllerRumbleTitle": "ตั้งค่าการสั่นไหว", - "SettingsSelectThemeFileDialogTitle": "เลือกไฟล์ธีม", - "SettingsXamlThemeFile": "ไฟล์ธีมรูปแบบ XAML", - "AvatarWindowTitle": "จัดการบัญชี - อวาต้า", - "Amiibo": "อะมิโบ", - "Unknown": "ไม่รู้จัก", - "Usage": "การใช้งาน", - "Writable": "สามารถเขียนได้", - "SelectDlcDialogTitle": "เลือกไฟล์ DLC", - "SelectUpdateDialogTitle": "เลือกไฟล์อัพเดต", - "SelectModDialogTitle": "เลือกไดเรกทอรี Mods", - "UserProfileWindowTitle": "จัดการโปรไฟล์ผู้ใช้", - "CheatWindowTitle": "จัดการสูตรโกง", - "DlcWindowTitle": "จัดการเนื้อหาที่ดาวน์โหลดได้สำหรับ {0} ({1})", - "ModWindowTitle": "Manage Mods for {0} ({1})", - "UpdateWindowTitle": "จัดการอัปเดตหัวข้อ", - "CheatWindowHeading": "สูตรโกงมีให้สำหรับ {0} [{1}]", - "BuildId": "รหัสบิวด์:", - "DlcWindowHeading": "{0} เนื้อหาที่สามารถดาวน์โหลดได้", - "ModWindowHeading": "{0} ม็อด", - "UserProfilesEditProfile": "แก้ไขที่เลือกแล้ว", - "Cancel": "ยกเลิก", - "Save": "บันทึก", - "Discard": "ละทิ้ง", - "Paused": "หยุดชั่วคราว", - "UserProfilesSetProfileImage": "ตั้งค่ารูปโปรไฟล์", - "UserProfileEmptyNameError": "จำเป็นต้องระบุชื่อ", - "UserProfileNoImageError": "จำเป็นต้องตั้งค่ารูปโปรไฟล์", - "GameUpdateWindowHeading": "จัดการอัพเดตสำหรับ {0} ({1})", - "SettingsTabHotkeysResScaleUpHotkey": "เพิ่มความละเอียด:", - "SettingsTabHotkeysResScaleDownHotkey": "ลดความละเอียด:", - "UserProfilesName": "ชื่อ:", - "UserProfilesUserId": "รหัสผู้ใช้:", - "SettingsTabGraphicsBackend": "กราฟิกเบื้องหลัง", - "SettingsTabGraphicsBackendTooltip": "เลือกกราฟิกเบื้องหลังที่จะใช้ในโปรแกรมจำลอง\n\nโดยรวมแล้ว Vulkan นั้นดีกว่าสำหรับกราฟิกการ์ดรุ่นใหม่ทั้งหมด ตราบใดที่ไดรเวอร์ยังอัพเดทอยู่เสมอ Vulkan ยังมีคุณสมบัติการคอมไพล์เชเดอร์ที่เร็วขึ้น (ลดอาการกระตุก) ของผู้จำหน่าย GPU ทุกราย\n\nOpenGL อาจได้รับผลลัพธ์ที่ดีกว่าบน Nvidia GPU รุ่นเก่า, AMD GPU รุ่นเก่าบน Linux หรือบน GPU ที่มี VRAM ต่ำกว่า แม้ว่าการคอมไพล์เชเดอร์ จะทำให้อาการกระตุกมากขึ้นก็ตาม\n\nตั้งค่าเป็น Vulkan หากไม่แน่ใจ ตั้งค่าเป็น OpenGL หาก GPU ของคุณไม่รองรับ Vulkan แม้จะมีไดรเวอร์กราฟิกล่าสุดก็ตาม", - "SettingsEnableTextureRecompression": "เปิดใช้งาน การบีบอัดพื้นผิวอีกครั้ง", - "SettingsEnableTextureRecompressionTooltip": "บีบอัดพื้นผิว ASTC เพื่อลดการใช้งาน VRAM\n\nเกมที่ใช้รูปแบบพื้นผิวนี้ ได้แก่ Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder และ The Legend of Zelda: Tears of the Kingdom\n\nกราฟิกการ์ดที่มี 4 กิกะไบต์ VRAM หรือน้อยกว่ามีแนวโน้มที่จะให้แคชในบางจุดขณะเล่นเกมเหล่านี้\n\nเปิดใช้งานเฉพาะในกรณีที่ VRAM ของคุณใกล้หมดในเกมที่กล่าวมาข้างต้น ปล่อยให้ปิดหากไม่แน่ใจ", - "SettingsTabGraphicsPreferredGpu": "GPU ที่ต้องการ", - "SettingsTabGraphicsPreferredGpuTooltip": "เลือกกราฟิกการ์ดที่จะใช้กับแบ็กเอนด์กราฟิก Vulkan\n\nไม่ส่งผลต่อ GPU ที่ OpenGL จะใช้\n\nตั้งค่าเป็น GPU ที่ถูกตั้งค่าสถานะเป็น \"dGPU\" หากคุณไม่แน่ใจ หากไม่มีก็ปล่อยทิ้งไว้โดยไม่มีใครแตะต้องมัน", - "SettingsAppRequiredRestartMessage": "จำเป็นต้องรีสตาร์ท รียูจินซ์", - "SettingsGpuBackendRestartMessage": "การตั้งค่ากราฟิกเบื้องหลังหรือ GPU ได้รับการแก้ไขแล้ว สิ่งนี้จะต้องมีการรีสตาร์ทจึงจะสามารถใช้งานได้", - "SettingsGpuBackendRestartSubMessage": "คุณต้องการรีสตาร์ทตอนนี้หรือไม่?", - "RyujinxUpdaterMessage": "คุณต้องการอัพเดต รียูจินซ์ เป็นเวอร์ชั่นล่าสุดหรือไม่?", - "SettingsTabHotkeysVolumeUpHotkey": "เพิ่มระดับเสียง:", - "SettingsTabHotkeysVolumeDownHotkey": "ลดระดับเสียง:", - "SettingsEnableMacroHLE": "เปิดใช้งาน มาโคร HLE", - "SettingsEnableMacroHLETooltip": "การจำลองระดับสูงของโค้ดมาโคร GPU\n\nปรับปรุงประสิทธิภาพ แต่อาจทำให้เกิดข้อผิดพลาดด้านกราฟิกในบางเกม\n\nปล่อยไว้หากคุณไม่แน่ใจ", - "SettingsEnableColorSpacePassthrough": "ทะลุผ่านพื้นที่สี", - "SettingsEnableColorSpacePassthroughTooltip": "สั่งให้แบ็กเอนด์ Vulkan ส่งผ่านข้อมูลสีโดยไม่ต้องระบุค่าของสี สำหรับผู้ใช้ที่มีการแสดงกระจายตัวของสี อาจส่งผลให้สีสดใสมากขึ้น โดยต้องแลกกับความถูกต้องของสี", - "VolumeShort": "ระดับเสียง", - "UserProfilesManageSaves": "จัดการบันทึก", - "DeleteUserSave": "คุณต้องการลบบันทึกผู้ใช้สำหรับเกมนี้หรือไม่?", - "IrreversibleActionNote": "การดำเนินการนี้ไม่สามารถย้อนกลับได้", - "SaveManagerHeading": "จัดการบันทึกสำหรับ {0} ({1})", - "SaveManagerTitle": "จัดการบันทึก", - "Name": "ชื่อ", - "Size": "ขนาด", - "Search": "ค้นหา", - "UserProfilesRecoverLostAccounts": "กู้คืนบัญชีที่สูญหาย", - "Recover": "กู้คืน", - "UserProfilesRecoverHeading": "พบบันทึกสำหรับบัญชีดังต่อไปนี้", - "UserProfilesRecoverEmptyList": "ไม่มีโปรไฟล์ที่สามารถกู้คืนได้", - "GraphicsAATooltip": "ใช้การลดรอยหยักกับการเรนเดอร์เกม\n\nFXAA จะเบลอภาพส่วนใหญ่ ในขณะที่ SMAA จะพยายามค้นหาขอบหยักและปรับให้เรียบ\n\nไม่แนะนำให้ใช้ร่วมกับตัวกรองสเกล FSR\n\nตัวเลือกนี้สามารถเปลี่ยนแปลงได้ในขณะที่เกมกำลังทำงานอยู่โดยคลิก \"นำไปใช้\" ด้านล่าง คุณสามารถย้ายหน้าต่างการตั้งค่าไปด้านข้างและทดลองจนกว่าคุณจะพบรูปลักษณ์ที่คุณต้องการสำหรับเกม\n\nปล่อยไว้ที่ NONE หากไม่แน่ใจ", - "GraphicsAALabel": "ลดการฉีกขาดของภาพ:", - "GraphicsScalingFilterLabel": "ปรับขนาดตัวกรอง:", - "GraphicsScalingFilterTooltip": "เลือกตัวกรองสเกลที่จะใช้เมื่อใช้สเกลความละเอียด\n\nBilinear ทำงานได้ดีกับเกม 3D และเป็นตัวเลือกเริ่มต้นที่ปลอดภัย\n\nแนะนำให้ใช้เกมภาพพิกเซลที่ใกล้เคียงที่สุด\n\nFSR 1.0 เป็นเพียงตัวกรองความคมชัด ไม่แนะนำให้ใช้กับ FXAA หรือ SMAA\n\nตัวเลือกนี้สามารถเปลี่ยนแปลงได้ในขณะที่เกมกำลังทำงานอยู่โดยคลิก \"นำไปใช้\" ด้านล่าง คุณสามารถย้ายหน้าต่างการตั้งค่าไปด้านข้างและทดลองจนกว่าคุณจะพบรูปลักษณ์ที่คุณต้องการสำหรับเกม", - "GraphicsScalingFilterBilinear": "Bilinear", - "GraphicsScalingFilterNearest": "Nearest", - "GraphicsScalingFilterFsr": "FSR", - "GraphicsScalingFilterLevelLabel": "ระดับ", - "GraphicsScalingFilterLevelTooltip": "ตั้งค่าระดับความคมชัด FSR 1.0 สูงกว่าจะคมชัดกว่า", - "SmaaLow": "SMAA ต่ำ", - "SmaaMedium": "SMAA ปานกลาง", - "SmaaHigh": "SMAA สูง", - "SmaaUltra": "SMAA สูงมาก", - "UserEditorTitle": "แก้ไขผู้ใช้", - "UserEditorTitleCreate": "สร้างผู้ใช้", - "SettingsTabNetworkInterface": "เชื่อมต่อเครือข่าย:", - "NetworkInterfaceTooltip": "อินเทอร์เฟซเครือข่ายที่ใช้สำหรับคุณสมบัติ LAN/LDN\n\nเมื่อใช้ร่วมกับ VPN หรือ XLink Kai และเกมที่รองรับ LAN สามารถใช้เพื่อปลอมการเชื่อมต่อเครือข่ายเดียวกันผ่านทางอินเทอร์เน็ต\n\nปล่อยให้เป็น ค่าเริ่มต้น หากคุณไม่แน่ใจ", - "NetworkInterfaceDefault": "ค่าเริ่มต้น", - "PackagingShaders": "รวม Shaders เข้าด้วยกัน", - "AboutChangelogButton": "ดูประวัติการเปลี่ยนแปลงบน GitHub", - "AboutChangelogButtonTooltipMessage": "คลิกเพื่อเปิดประวัติการเปลี่ยนแปลงสำหรับเวอร์ชั่นนี้ บนเบราว์เซอร์เริ่มต้นของคุณ", - "SettingsTabNetworkMultiplayer": "ผู้เล่นหลายคน", - "MultiplayerMode": "โหมด:", - "MultiplayerModeTooltip": "เปลี่ยนโหมดผู้เล่นหลายคนของ LDN\n\nLdnMitm จะปรับเปลี่ยนฟังก์ชันการเล่นแบบไร้สาย/ภายใน จะให้เกมทำงานเหมือนกับว่าเป็น LAN ช่วยให้สามารถเชื่อมต่อภายในเครือข่ายเดียวกันกับอินสแตนซ์ Ryujinx อื่น ๆ และคอนโซล Nintendo Switch ที่ถูกแฮ็กซึ่งมีโมดูล ldn_mitm ติดตั้งอยู่\n\nผู้เล่นหลายคนต้องการให้ผู้เล่นทุกคนอยู่ในเกมเวอร์ชันเดียวกัน (เช่น Super Smash Bros. Ultimate v13.0.1 ไม่สามารถเชื่อมต่อกับ v13.0.0)\n\nปล่อยให้ปิดการใช้งานหากไม่แน่ใจ", - "MultiplayerModeDisabled": "Disabled", - "MultiplayerModeLdnMitm": "ldn_mitm" -} diff --git a/src/Ryujinx/Assets/Locales/tr_TR.json b/src/Ryujinx/Assets/Locales/tr_TR.json deleted file mode 100644 index f74baaa18..000000000 --- a/src/Ryujinx/Assets/Locales/tr_TR.json +++ /dev/null @@ -1,780 +0,0 @@ -{ - "Language": "Türkçe", - "MenuBarFileOpenApplet": "Applet'i Aç", - "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Mii Editör Applet'ini Bağımsız Mod'da Aç", - "SettingsTabInputDirectMouseAccess": "Doğrudan Mouse Erişimi", - "SettingsTabSystemMemoryManagerMode": "Hafıza Yönetim Modu:", - "SettingsTabSystemMemoryManagerModeSoftware": "Yazılım", - "SettingsTabSystemMemoryManagerModeHost": "Host (hızlı)", - "SettingsTabSystemMemoryManagerModeHostUnchecked": "Host Unchecked (en hızlısı, tehlikeli)", - "SettingsTabSystemUseHypervisor": "Hypervisor Kullan", - "MenuBarFile": "_Dosya", - "MenuBarFileOpenFromFile": "_Dosyadan Uygulama Aç", - "MenuBarFileOpenUnpacked": "_Sıkıştırılmamış Oyun Aç", - "MenuBarFileOpenEmuFolder": "Ryujinx Klasörünü aç", - "MenuBarFileOpenLogsFolder": "Logs Klasörünü aç", - "MenuBarFileExit": "_Çıkış", - "MenuBarOptions": "_Seçenekler", - "MenuBarOptionsToggleFullscreen": "Tam Ekran Modunu Aç", - "MenuBarOptionsStartGamesInFullscreen": "Oyunları Tam Ekran Modunda Başlat", - "MenuBarOptionsStopEmulation": "Emülasyonu Durdur", - "MenuBarOptionsSettings": "_Seçenekler", - "MenuBarOptionsManageUserProfiles": "_Kullanıcı Profillerini Yönet", - "MenuBarActions": "_Eylemler", - "MenuBarOptionsSimulateWakeUpMessage": "Uyandırma Mesajı Simüle Et", - "MenuBarActionsScanAmiibo": "Bir Amiibo Tara", - "MenuBarTools": "_Araçlar", - "MenuBarToolsInstallFirmware": "Yazılım Yükle", - "MenuBarFileToolsInstallFirmwareFromFile": "XCI veya ZIP'ten Yazılım Yükle", - "MenuBarFileToolsInstallFirmwareFromDirectory": "Bir Dizin Üzerinden Yazılım Yükle", - "MenuBarToolsManageFileTypes": "Dosya uzantılarını yönet", - "MenuBarToolsInstallFileTypes": "Dosya uzantılarını yükle", - "MenuBarToolsUninstallFileTypes": "Dosya uzantılarını kaldır", - "MenuBarView": "_Görüntüle", - "MenuBarViewWindow": "Pencere Boyutu", - "MenuBarViewWindow720": "720p", - "MenuBarViewWindow1080": "1080p", - "MenuBarHelp": "_Yardım", - "MenuBarHelpCheckForUpdates": "Güncellemeleri Denetle", - "MenuBarHelpAbout": "Hakkında", - "MenuSearch": "Ara...", - "GameListHeaderFavorite": "Favori", - "GameListHeaderIcon": "Simge", - "GameListHeaderApplication": "Oyun Adı", - "GameListHeaderDeveloper": "Geliştirici", - "GameListHeaderVersion": "Sürüm", - "GameListHeaderTimePlayed": "Oynama Süresi", - "GameListHeaderLastPlayed": "Son Oynama Tarihi", - "GameListHeaderFileExtension": "Dosya Uzantısı", - "GameListHeaderFileSize": "Dosya Boyutu", - "GameListHeaderPath": "Yol", - "GameListContextMenuOpenUserSaveDirectory": "Kullanıcı Kayıt Dosyası Dizinini Aç", - "GameListContextMenuOpenUserSaveDirectoryToolTip": "Uygulamanın Kullanıcı Kaydı'nın bulunduğu dizini açar", - "GameListContextMenuOpenDeviceSaveDirectory": "Kullanıcı Cihaz Dizinini Aç", - "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Uygulamanın Kullanıcı Cihaz Kaydı'nın bulunduğu dizini açar", - "GameListContextMenuOpenBcatSaveDirectory": "Kullanıcı BCAT Dizinini Aç", - "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Uygulamanın Kullanıcı BCAT Kaydı'nın bulunduğu dizini açar", - "GameListContextMenuManageTitleUpdates": "Oyun Güncellemelerini Yönet", - "GameListContextMenuManageTitleUpdatesToolTip": "Oyun Güncelleme Yönetim Penceresini Açar", - "GameListContextMenuManageDlc": "DLC'leri Yönet", - "GameListContextMenuManageDlcToolTip": "DLC yönetim penceresini açar", - "GameListContextMenuCacheManagement": "Önbellek Yönetimi", - "GameListContextMenuCacheManagementPurgePptc": "PPTC Yeniden Yapılandırmasını Başlat", - "GameListContextMenuCacheManagementPurgePptcToolTip": "Oyunun bir sonraki açılışında PPTC'yi yeniden yapılandır", - "GameListContextMenuCacheManagementPurgeShaderCache": "Shader Önbelleğini Temizle", - "GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "Uygulamanın shader önbelleğini temizler", - "GameListContextMenuCacheManagementOpenPptcDirectory": "PPTC Dizinini Aç", - "GameListContextMenuCacheManagementOpenPptcDirectoryToolTip": "Uygulamanın PPTC Önbelleğinin bulunduğu dizini açar", - "GameListContextMenuCacheManagementOpenShaderCacheDirectory": "Shader Önbelleği Dizinini Aç", - "GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip": "Uygulamanın shader önbelleğinin bulunduğu dizini açar", - "GameListContextMenuExtractData": "Veriyi Ayıkla", - "GameListContextMenuExtractDataExeFS": "ExeFS", - "GameListContextMenuExtractDataExeFSToolTip": "Uygulamanın geçerli yapılandırmasından ExeFS kısmını ayıkla (Güncellemeler dahil)", - "GameListContextMenuExtractDataRomFS": "RomFS", - "GameListContextMenuExtractDataRomFSToolTip": "Uygulamanın geçerli yapılandırmasından RomFS kısmını ayıkla (Güncellemeler dahil)", - "GameListContextMenuExtractDataLogo": "Simge", - "GameListContextMenuExtractDataLogoToolTip": "Uygulamanın geçerli yapılandırmasından Logo kısmını ayıkla (Güncellemeler dahil)", - "GameListContextMenuCreateShortcut": "Uygulama Kısayolu Oluştur", - "GameListContextMenuCreateShortcutToolTip": "Seçilmiş uygulamayı çalıştıracak bir masaüstü kısayolu oluştur", - "GameListContextMenuCreateShortcutToolTipMacOS": "Create a shortcut in macOS's Applications folder that launches the selected Application", - "GameListContextMenuOpenModsDirectory": "Mod Dizinini Aç", - "GameListContextMenuOpenModsDirectoryToolTip": "Opens the directory which contains Application's Mods", - "GameListContextMenuOpenSdModsDirectory": "Open Atmosphere Mods Directory", - "GameListContextMenuOpenSdModsDirectoryToolTip": "Opens the alternative SD card Atmosphere directory which contains Application's Mods. Useful for mods that are packaged for real hardware.", - "StatusBarGamesLoaded": "{0}/{1} Oyun Yüklendi", - "StatusBarSystemVersion": "Sistem Sürümü: {0}", - "LinuxVmMaxMapCountDialogTitle": "Bellek Haritaları İçin Düşük Limit Tespit Edildi ", - "LinuxVmMaxMapCountDialogTextPrimary": "vm.max_map_count değerini {0} sayısına yükseltmek ister misiniz", - "LinuxVmMaxMapCountDialogTextSecondary": "Bazı oyunlar şu an izin verilen bellek haritası limitinden daha fazlasını yaratmaya çalışabilir. Ryujinx bu limitin geçildiği takdirde kendini kapatıcaktır.", - "LinuxVmMaxMapCountDialogButtonUntilRestart": "Evet, bir sonraki yeniden başlatmaya kadar", - "LinuxVmMaxMapCountDialogButtonPersistent": "Evet, kalıcı olarak", - "LinuxVmMaxMapCountWarningTextPrimary": "İzin verilen maksimum bellek haritası değeri tavsiye edildiğinden daha düşük. ", - "LinuxVmMaxMapCountWarningTextSecondary": "Şu anki vm.max_map_count değeri {0}, bu {1} değerinden daha az. Bazı oyunlar şu an izin verilen bellek haritası limitinden daha fazlasını yaratmaya çalışabilir. Ryujinx bu limitin geçildiği takdirde kendini kapatıcaktır.\n\nManuel olarak bu limiti arttırmayı deneyebilir ya da pkexec'i yükleyebilirsiniz, bu da Ryujinx'in yardımcı olmasına izin verir.", - "Settings": "Ayarlar", - "SettingsTabGeneral": "Kullancı Arayüzü", - "SettingsTabGeneralGeneral": "Genel", - "SettingsTabGeneralEnableDiscordRichPresence": "Discord Zengin İçerik'i Etkinleştir", - "SettingsTabGeneralCheckUpdatesOnLaunch": "Her Açılışta Güncellemeleri Denetle", - "SettingsTabGeneralShowConfirmExitDialog": "\"Çıkışı Onayla\" Diyaloğunu Göster", - "SettingsTabGeneralRememberWindowState": "Remember Window Size/Position", - "SettingsTabGeneralHideCursor": "İşaretçiyi Gizle:", - "SettingsTabGeneralHideCursorNever": "Hiçbir Zaman", - "SettingsTabGeneralHideCursorOnIdle": "Hareketsiz Durumda", - "SettingsTabGeneralHideCursorAlways": "Her Zaman", - "SettingsTabGeneralGameDirectories": "Oyun Dizinleri", - "SettingsTabGeneralAdd": "Ekle", - "SettingsTabGeneralRemove": "Kaldır", - "SettingsTabSystem": "Sistem", - "SettingsTabSystemCore": "Çekirdek", - "SettingsTabSystemSystemRegion": "Sistem Bölgesi:", - "SettingsTabSystemSystemRegionJapan": "Japonya", - "SettingsTabSystemSystemRegionUSA": "ABD", - "SettingsTabSystemSystemRegionEurope": "Avrupa", - "SettingsTabSystemSystemRegionAustralia": "Avustralya", - "SettingsTabSystemSystemRegionChina": "Çin", - "SettingsTabSystemSystemRegionKorea": "Kore", - "SettingsTabSystemSystemRegionTaiwan": "Tayvan", - "SettingsTabSystemSystemLanguage": "Sistem Dili:", - "SettingsTabSystemSystemLanguageJapanese": "Japonca", - "SettingsTabSystemSystemLanguageAmericanEnglish": "Amerikan İngilizcesi", - "SettingsTabSystemSystemLanguageFrench": "Fransızca", - "SettingsTabSystemSystemLanguageGerman": "Almanca", - "SettingsTabSystemSystemLanguageItalian": "İtalyanca", - "SettingsTabSystemSystemLanguageSpanish": "İspanyolca", - "SettingsTabSystemSystemLanguageChinese": "Çince", - "SettingsTabSystemSystemLanguageKorean": "Korece", - "SettingsTabSystemSystemLanguageDutch": "Flemenkçe", - "SettingsTabSystemSystemLanguagePortuguese": "Portekizce", - "SettingsTabSystemSystemLanguageRussian": "Rusça", - "SettingsTabSystemSystemLanguageTaiwanese": "Tayvanca", - "SettingsTabSystemSystemLanguageBritishEnglish": "İngiliz İngilizcesi", - "SettingsTabSystemSystemLanguageCanadianFrench": "Kanada Fransızcası", - "SettingsTabSystemSystemLanguageLatinAmericanSpanish": "Latin Amerika İspanyolcası", - "SettingsTabSystemSystemLanguageSimplifiedChinese": "Basitleştirilmiş Çince", - "SettingsTabSystemSystemLanguageTraditionalChinese": "Geleneksel Çince", - "SettingsTabSystemSystemTimeZone": "Sistem Saat Dilimi:", - "SettingsTabSystemSystemTime": "Sistem Saati:", - "SettingsTabSystemEnableVsync": "Dikey Eşitleme", - "SettingsTabSystemEnablePptc": "PPTC (Profilli Sürekli Çeviri Önbelleği)", - "SettingsTabSystemEnableFsIntegrityChecks": "FS Bütünlük Kontrolleri", - "SettingsTabSystemAudioBackend": "Ses Motoru:", - "SettingsTabSystemAudioBackendDummy": "Yapay", - "SettingsTabSystemAudioBackendOpenAL": "OpenAL", - "SettingsTabSystemAudioBackendSoundIO": "SoundIO", - "SettingsTabSystemAudioBackendSDL2": "SDL2", - "SettingsTabSystemHacks": "Hack'ler", - "SettingsTabSystemHacksNote": " (dengesizlik oluşturabilir)", - "SettingsTabSystemExpandDramSize": "Alternatif bellek düzeni kullan (Geliştirici)", - "SettingsTabSystemIgnoreMissingServices": "Eksik Servisleri Görmezden Gel", - "SettingsTabGraphics": "Grafikler", - "SettingsTabGraphicsAPI": "Grafikler API", - "SettingsTabGraphicsEnableShaderCache": "Shader Önbelleğini Etkinleştir", - "SettingsTabGraphicsAnisotropicFiltering": "Eşyönsüz Doku Süzmesi:", - "SettingsTabGraphicsAnisotropicFilteringAuto": "Otomatik", - "SettingsTabGraphicsAnisotropicFiltering2x": "2x", - "SettingsTabGraphicsAnisotropicFiltering4x": "4x", - "SettingsTabGraphicsAnisotropicFiltering8x": "8x", - "SettingsTabGraphicsAnisotropicFiltering16x": "16x", - "SettingsTabGraphicsResolutionScale": "Çözünürlük Ölçeği:", - "SettingsTabGraphicsResolutionScaleCustom": "Özel (Tavsiye Edilmez)", - "SettingsTabGraphicsResolutionScaleNative": "Yerel (720p/1080p)", - "SettingsTabGraphicsResolutionScale2x": "2x (1440p/2160p)", - "SettingsTabGraphicsResolutionScale3x": "3x (2160p/3240p)", - "SettingsTabGraphicsResolutionScale4x": "4x (2880p/4320p) (Tavsiye Edilmez)", - "SettingsTabGraphicsAspectRatio": "En-Boy Oranı:", - "SettingsTabGraphicsAspectRatio4x3": "4:3", - "SettingsTabGraphicsAspectRatio16x9": "16:9", - "SettingsTabGraphicsAspectRatio16x10": "16:10", - "SettingsTabGraphicsAspectRatio21x9": "21:9", - "SettingsTabGraphicsAspectRatio32x9": "32:9", - "SettingsTabGraphicsAspectRatioStretch": "Pencereye Sığdırmak İçin Genişlet", - "SettingsTabGraphicsDeveloperOptions": "Geliştirici Seçenekleri", - "SettingsTabGraphicsShaderDumpPath": "Grafik Shader Döküm Yolu:", - "SettingsTabLogging": "Loglama", - "SettingsTabLoggingLogging": "Loglama", - "SettingsTabLoggingEnableLoggingToFile": "Logları Dosyaya Kaydetmeyi Etkinleştir", - "SettingsTabLoggingEnableStubLogs": "Stub Loglarını Etkinleştir", - "SettingsTabLoggingEnableInfoLogs": "Bilgi Loglarını Etkinleştir", - "SettingsTabLoggingEnableWarningLogs": "Uyarı Loglarını Etkinleştir", - "SettingsTabLoggingEnableErrorLogs": "Hata Loglarını Etkinleştir", - "SettingsTabLoggingEnableTraceLogs": "Trace Loglarını Etkinleştir", - "SettingsTabLoggingEnableGuestLogs": "Guest Loglarını Etkinleştir", - "SettingsTabLoggingEnableFsAccessLogs": "Fs Erişim Loglarını Etkinleştir", - "SettingsTabLoggingFsGlobalAccessLogMode": "Fs Evrensel Erişim Log Modu:", - "SettingsTabLoggingDeveloperOptions": "Geliştirici Seçenekleri (UYARI: Performansı düşürecektir)", - "SettingsTabLoggingDeveloperOptionsNote": "UYARI: Oyun performansı azalacak", - "SettingsTabLoggingGraphicsBackendLogLevel": "Grafik Arka Uç Günlük Düzeyi", - "SettingsTabLoggingGraphicsBackendLogLevelNone": "Hiçbiri", - "SettingsTabLoggingGraphicsBackendLogLevelError": "Hata", - "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "Yavaşlamalar", - "SettingsTabLoggingGraphicsBackendLogLevelAll": "Hepsi", - "SettingsTabLoggingEnableDebugLogs": "Hata Ayıklama Loglarını Etkinleştir", - "SettingsTabInput": "Giriş Yöntemi", - "SettingsTabInputEnableDockedMode": "Docked Modu Etkinleştir", - "SettingsTabInputDirectKeyboardAccess": "Doğrudan Klavye Erişimi", - "SettingsButtonSave": "Kaydet", - "SettingsButtonClose": "Kapat", - "SettingsButtonOk": "Tamam", - "SettingsButtonCancel": "İptal", - "SettingsButtonApply": "Uygula", - "ControllerSettingsPlayer": "Oyuncu", - "ControllerSettingsPlayer1": "Oyuncu 1", - "ControllerSettingsPlayer2": "Oyuncu 2", - "ControllerSettingsPlayer3": "Oyuncu 3", - "ControllerSettingsPlayer4": "Oyuncu 4", - "ControllerSettingsPlayer5": "Oyuncu 5", - "ControllerSettingsPlayer6": "Oyuncu 6", - "ControllerSettingsPlayer7": "Oyuncu 7", - "ControllerSettingsPlayer8": "Oyuncu 8", - "ControllerSettingsHandheld": "Portatif Mod", - "ControllerSettingsInputDevice": "Giriş Cihazı", - "ControllerSettingsRefresh": "Yenile", - "ControllerSettingsDeviceDisabled": "Devre Dışı", - "ControllerSettingsControllerType": "Kumanda Tipi", - "ControllerSettingsControllerTypeHandheld": "Portatif Mod", - "ControllerSettingsControllerTypeProController": "Profesyonel Kumanda", - "ControllerSettingsControllerTypeJoyConPair": "JoyCon Çifti", - "ControllerSettingsControllerTypeJoyConLeft": "JoyCon Sol", - "ControllerSettingsControllerTypeJoyConRight": "JoyCon Sağ", - "ControllerSettingsProfile": "Profil", - "ControllerSettingsProfileDefault": "Varsayılan", - "ControllerSettingsLoad": "Yükle", - "ControllerSettingsAdd": "Ekle", - "ControllerSettingsRemove": "Kaldır", - "ControllerSettingsButtons": "Tuşlar", - "ControllerSettingsButtonA": "A", - "ControllerSettingsButtonB": "B", - "ControllerSettingsButtonX": "X", - "ControllerSettingsButtonY": "Y", - "ControllerSettingsButtonPlus": "+", - "ControllerSettingsButtonMinus": "-", - "ControllerSettingsDPad": "Yön Tuşları", - "ControllerSettingsDPadUp": "Yukarı", - "ControllerSettingsDPadDown": "Aşağı", - "ControllerSettingsDPadLeft": "Sol", - "ControllerSettingsDPadRight": "Sağ", - "ControllerSettingsStickButton": "Tuş", - "ControllerSettingsStickUp": "Yukarı", - "ControllerSettingsStickDown": "Aşağı", - "ControllerSettingsStickLeft": "Sol", - "ControllerSettingsStickRight": "Sağ", - "ControllerSettingsStickStick": "Analog", - "ControllerSettingsStickInvertXAxis": "X Eksenini Tersine Çevir", - "ControllerSettingsStickInvertYAxis": "Y Eksenini Tersine Çevir", - "ControllerSettingsStickDeadzone": "Ölü Bölge", - "ControllerSettingsLStick": "Sol Analog", - "ControllerSettingsRStick": "Sağ Analog", - "ControllerSettingsTriggersLeft": "Tetikler Sol", - "ControllerSettingsTriggersRight": "Tetikler Sağ", - "ControllerSettingsTriggersButtonsLeft": "Tetik Tuşları Sol", - "ControllerSettingsTriggersButtonsRight": "Tetik Tuşları Sağ", - "ControllerSettingsTriggers": "Tetikler", - "ControllerSettingsTriggerL": "L", - "ControllerSettingsTriggerR": "R", - "ControllerSettingsTriggerZL": "ZL", - "ControllerSettingsTriggerZR": "ZR", - "ControllerSettingsLeftSL": "SL", - "ControllerSettingsLeftSR": "SR", - "ControllerSettingsRightSL": "SL", - "ControllerSettingsRightSR": "SR", - "ControllerSettingsExtraButtonsLeft": "Tuşlar Sol", - "ControllerSettingsExtraButtonsRight": "Tuşlar Sağ", - "ControllerSettingsMisc": "Diğer", - "ControllerSettingsTriggerThreshold": "Tetik Eşiği:", - "ControllerSettingsMotion": "Hareket", - "ControllerSettingsMotionUseCemuhookCompatibleMotion": "CemuHook uyumlu hareket kullan", - "ControllerSettingsMotionControllerSlot": "Kumanda Yuvası:", - "ControllerSettingsMotionMirrorInput": "Girişi Aynala", - "ControllerSettingsMotionRightJoyConSlot": "Sağ JoyCon Yuvası:", - "ControllerSettingsMotionServerHost": "Sunucu Sahibi:", - "ControllerSettingsMotionGyroSensitivity": "Gyro Hassasiyeti:", - "ControllerSettingsMotionGyroDeadzone": "Gyro Ölü Bölgesi:", - "ControllerSettingsSave": "Kaydet", - "ControllerSettingsClose": "Kapat", - "KeyUnknown": "Unknown", - "KeyShiftLeft": "Sol Shift", - "KeyShiftRight": "Sağ Shift", - "KeyControlLeft": "Sol Ctrl", - "KeyMacControlLeft": "⌃ Sol", - "KeyControlRight": "Sağ Control", - "KeyMacControlRight": "⌃ Sağ", - "KeyAltLeft": "Sol Alt", - "KeyMacAltLeft": "⌥ Sol", - "KeyAltRight": "Sağ Alt", - "KeyMacAltRight": "⌥ Sağ", - "KeyWinLeft": "⊞ Sol", - "KeyMacWinLeft": "⌘ Sol", - "KeyWinRight": "⊞ Sağ", - "KeyMacWinRight": "⌘ Sağ", - "KeyMenu": "Menü", - "KeyUp": "Yukarı", - "KeyDown": "Aşağı", - "KeyLeft": "Sol", - "KeyRight": "Sağ", - "KeyEnter": "Enter", - "KeyEscape": "Esc", - "KeySpace": "Space", - "KeyTab": "Tab", - "KeyBackSpace": "Geri tuşu", - "KeyInsert": "Insert", - "KeyDelete": "Delete", - "KeyPageUp": "Page Up", - "KeyPageDown": "Page Down", - "KeyHome": "Home", - "KeyEnd": "End", - "KeyCapsLock": "Caps Lock", - "KeyScrollLock": "Scroll Lock", - "KeyPrintScreen": "Print Screen", - "KeyPause": "Pause", - "KeyNumLock": "Num Lock", - "KeyClear": "Clear", - "KeyKeypad0": "Keypad 0", - "KeyKeypad1": "Keypad 1", - "KeyKeypad2": "Keypad 2", - "KeyKeypad3": "Keypad 3", - "KeyKeypad4": "Keypad 4", - "KeyKeypad5": "Keypad 5", - "KeyKeypad6": "Keypad 6", - "KeyKeypad7": "Keypad 7", - "KeyKeypad8": "Keypad 8", - "KeyKeypad9": "Keypad 9", - "KeyKeypadDivide": "Keypad Divide", - "KeyKeypadMultiply": "Keypad Multiply", - "KeyKeypadSubtract": "Keypad Subtract", - "KeyKeypadAdd": "Keypad Add", - "KeyKeypadDecimal": "Keypad Decimal", - "KeyKeypadEnter": "Keypad Enter", - "KeyNumber0": "0", - "KeyNumber1": "1", - "KeyNumber2": "2", - "KeyNumber3": "3", - "KeyNumber4": "4", - "KeyNumber5": "5", - "KeyNumber6": "6", - "KeyNumber7": "7", - "KeyNumber8": "8", - "KeyNumber9": "9", - "KeyTilde": "~", - "KeyGrave": "`", - "KeyMinus": "-", - "KeyPlus": "+", - "KeyBracketLeft": "[", - "KeyBracketRight": "]", - "KeySemicolon": ";", - "KeyQuote": "\"", - "KeyComma": ",", - "KeyPeriod": ".", - "KeySlash": "/", - "KeyBackSlash": "\\", - "KeyUnbound": "Unbound", - "GamepadLeftStick": "L Stick Button", - "GamepadRightStick": "R Stick Button", - "GamepadLeftShoulder": "Left Shoulder", - "GamepadRightShoulder": "Right Shoulder", - "GamepadLeftTrigger": "Left Trigger", - "GamepadRightTrigger": "Right Trigger", - "GamepadDpadUp": "Up", - "GamepadDpadDown": "Down", - "GamepadDpadLeft": "Left", - "GamepadDpadRight": "Sağ", - "GamepadMinus": "-", - "GamepadPlus": "4", - "GamepadGuide": "Rehber", - "GamepadMisc1": "Diğer", - "GamepadPaddle1": "Pedal 1", - "GamepadPaddle2": "Pedal 2", - "GamepadPaddle3": "Pedal 3", - "GamepadPaddle4": "Pedal 4", - "GamepadTouchpad": "Touchpad", - "GamepadSingleLeftTrigger0": "Sol Tetik 0", - "GamepadSingleRightTrigger0": "Sağ Tetik 0", - "GamepadSingleLeftTrigger1": "Sol Tetik 1", - "GamepadSingleRightTrigger1": "Sağ Tetik 1", - "StickLeft": "Sol Çubuk", - "StickRight": "Sağ çubuk", - "UserProfilesSelectedUserProfile": "Seçili Kullanıcı Profili:", - "UserProfilesSaveProfileName": "Profil İsmini Kaydet", - "UserProfilesChangeProfileImage": "Profil Resmini Değiştir", - "UserProfilesAvailableUserProfiles": "Mevcut Kullanıcı Profilleri:", - "UserProfilesAddNewProfile": "Yeni Profil Ekle", - "UserProfilesDelete": "Sil", - "UserProfilesClose": "Kapat", - "ProfileNameSelectionWatermark": "Kullanıcı Adı Seç", - "ProfileImageSelectionTitle": "Profil Resmi Seçimi", - "ProfileImageSelectionHeader": "Profil Resmi Seç", - "ProfileImageSelectionNote": "Özel bir profil resmi içeri aktarabilir veya sistem avatarlarından birini seçebilirsiniz", - "ProfileImageSelectionImportImage": "Resim İçeri Aktar", - "ProfileImageSelectionSelectAvatar": "Yazılım Avatarı Seç", - "InputDialogTitle": "Giriş Yöntemi Diyaloğu", - "InputDialogOk": "Tamam", - "InputDialogCancel": "İptal", - "InputDialogAddNewProfileTitle": "Profil İsmini Seç", - "InputDialogAddNewProfileHeader": "Lütfen Bir Profil İsmi Girin", - "InputDialogAddNewProfileSubtext": "(Maksimum Uzunluk: {0})", - "AvatarChoose": "Seç", - "AvatarSetBackgroundColor": "Arka Plan Rengi Ayarla", - "AvatarClose": "Kapat", - "ControllerSettingsLoadProfileToolTip": "Profil Yükle", - "ControllerSettingsAddProfileToolTip": "Profil Ekle", - "ControllerSettingsRemoveProfileToolTip": "Profili Kaldır", - "ControllerSettingsSaveProfileToolTip": "Profili Kaydet", - "MenuBarFileToolsTakeScreenshot": "Ekran Görüntüsü Al", - "MenuBarFileToolsHideUi": "Arayüzü Gizle", - "GameListContextMenuRunApplication": "Uygulamayı Çalıştır", - "GameListContextMenuToggleFavorite": "Favori Ayarla", - "GameListContextMenuToggleFavoriteToolTip": "Oyunu Favorilere Ekle/Çıkar", - "SettingsTabGeneralTheme": "Tema:", - "SettingsTabGeneralThemeDark": "Karanlık", - "SettingsTabGeneralThemeLight": "Aydınlık", - "ControllerSettingsConfigureGeneral": "Ayarla", - "ControllerSettingsRumble": "Titreşim", - "ControllerSettingsRumbleStrongMultiplier": "Güçlü Titreşim Çoklayıcı", - "ControllerSettingsRumbleWeakMultiplier": "Zayıf Titreşim Seviyesi", - "DialogMessageSaveNotAvailableMessage": "{0} [{1:x16}] için kayıt verisi bulunamadı", - "DialogMessageSaveNotAvailableCreateSaveMessage": "Bu oyun için kayıt verisi oluşturmak ister misiniz?", - "DialogConfirmationTitle": "Ryujinx - Onay", - "DialogUpdaterTitle": "Ryujinx - Güncelleyici", - "DialogErrorTitle": "Ryujinx - Hata", - "DialogWarningTitle": "Ryujinx - Uyarı", - "DialogExitTitle": "Ryujinx - Çıkış", - "DialogErrorMessage": "Ryujinx bir hata ile karşılaştı", - "DialogExitMessage": "Ryujinx'i kapatmak istediğinizden emin misiniz?", - "DialogExitSubMessage": "Kaydedilmeyen bütün veriler kaybedilecek!", - "DialogMessageCreateSaveErrorMessage": "Belirtilen kayıt verisi oluşturulurken bir hata oluştu: {0}", - "DialogMessageFindSaveErrorMessage": "Belirtilen kayıt verisi bulunmaya çalışırken hata: {0}", - "FolderDialogExtractTitle": "İçine ayıklanacak klasörü seç", - "DialogNcaExtractionMessage": "{1} den {0} kısmı ayıklanıyor...", - "DialogNcaExtractionTitle": "Ryujinx - NCA Kısmı Ayıklayıcısı", - "DialogNcaExtractionMainNcaNotFoundErrorMessage": "Ayıklama hatası. Ana NCA seçilen dosyada bulunamadı.", - "DialogNcaExtractionCheckLogErrorMessage": "Ayıklama hatası. Ek bilgi için kayıt dosyasını okuyun.", - "DialogNcaExtractionSuccessMessage": "Ayıklama başarıyla tamamlandı.", - "DialogUpdaterConvertFailedMessage": "Güncel Ryujinx sürümü dönüştürülemedi.", - "DialogUpdaterCancelUpdateMessage": "Güncelleme iptal ediliyor!", - "DialogUpdaterAlreadyOnLatestVersionMessage": "Zaten Ryujinx'in en güncel sürümünü kullanıyorsunuz!", - "DialogUpdaterFailedToGetVersionMessage": "GitHub tarafından sürüm bilgileri alınırken bir hata oluştu. Eğer yeni sürüm için hazırlıklar yapılıyorsa bu hatayı almanız olasıdır. Lütfen birkaç dakika sonra tekrar deneyiniz.", - "DialogUpdaterConvertFailedGithubMessage": "Github Release'den alınan Ryujinx sürümü dönüştürülemedi.", - "DialogUpdaterDownloadingMessage": "Güncelleme İndiriliyor...", - "DialogUpdaterExtractionMessage": "Güncelleme Ayıklanıyor...", - "DialogUpdaterRenamingMessage": "Güncelleme Yeniden Adlandırılıyor...", - "DialogUpdaterAddingFilesMessage": "Yeni Güncelleme Ekleniyor...", - "DialogUpdaterCompleteMessage": "Güncelleme Tamamlandı!", - "DialogUpdaterRestartMessage": "Ryujinx'i şimdi yeniden başlatmak istiyor musunuz?", - "DialogUpdaterNoInternetMessage": "İnternete bağlı değilsiniz!", - "DialogUpdaterNoInternetSubMessage": "Lütfen aktif bir internet bağlantınız olduğunu kontrol edin!", - "DialogUpdaterDirtyBuildMessage": "Ryujinx'in Dirty build'lerini güncelleyemezsiniz!", - "DialogUpdaterDirtyBuildSubMessage": "Desteklenen bir sürüm için lütfen Ryujinx'i https://ryujinx.org/ sitesinden indirin.", - "DialogRestartRequiredMessage": "Yeniden Başlatma Gerekli", - "DialogThemeRestartMessage": "Tema kaydedildi. Temayı uygulamak için yeniden başlatma gerekiyor.", - "DialogThemeRestartSubMessage": "Yeniden başlatmak ister misiniz", - "DialogFirmwareInstallEmbeddedMessage": "Bu oyunun içine gömülü olan yazılımı yüklemek ister misiniz? (Firmware {0})", - "DialogFirmwareInstallEmbeddedSuccessMessage": "No installed firmware was found but Ryujinx was able to install firmware {0} from the provided game.\nThe emulator will now start.", - "DialogFirmwareNoFirmwareInstalledMessage": "Yazılım Yüklü Değil", - "DialogFirmwareInstalledMessage": "Yazılım {0} yüklendi", - "DialogInstallFileTypesSuccessMessage": "Dosya uzantıları başarıyla yüklendi!", - "DialogInstallFileTypesErrorMessage": "Dosya uzantıları yükleme işlemi başarısız oldu.", - "DialogUninstallFileTypesSuccessMessage": "Dosya uzantıları başarıyla kaldırıldı!", - "DialogUninstallFileTypesErrorMessage": "Dosya uzantıları kaldırma işlemi başarısız oldu.", - "DialogOpenSettingsWindowLabel": "Seçenekler Penceresini Aç", - "DialogControllerAppletTitle": "Kumanda Applet'i", - "DialogMessageDialogErrorExceptionMessage": "Mesaj diyaloğu gösterilirken hata: {0}", - "DialogSoftwareKeyboardErrorExceptionMessage": "Mesaj diyaloğu gösterilirken hata: {0}", - "DialogErrorAppletErrorExceptionMessage": "Applet diyaloğu gösterilirken hata: {0}", - "DialogUserErrorDialogMessage": "{0}: {1}", - "DialogUserErrorDialogInfoMessage": "\nBu hatayı düzeltmek adına daha fazla bilgi için kurulum kılavuzumuzu takip edin.", - "DialogUserErrorDialogTitle": "Ryujinx Hatası ({0})", - "DialogAmiiboApiTitle": "Amiibo API", - "DialogAmiiboApiFailFetchMessage": "API'dan bilgi alırken bir hata oluştu.", - "DialogAmiiboApiConnectErrorMessage": "Amiibo API sunucusuna bağlanılamadı. Sunucu çevrimdışı olabilir veya uygun bir internet bağlantınızın olduğunu kontrol etmeniz gerekebilir.", - "DialogProfileInvalidProfileErrorMessage": "Profil {0} güncel giriş konfigürasyon sistemi ile uyumlu değil.", - "DialogProfileDefaultProfileOverwriteErrorMessage": "Varsayılan Profil'in üstüne yazılamaz", - "DialogProfileDeleteProfileTitle": "Profil Siliniyor", - "DialogProfileDeleteProfileMessage": "Bu eylem geri döndürülemez, devam etmek istediğinizden emin misiniz?", - "DialogWarning": "Uyarı", - "DialogPPTCDeletionMessage": "Belirtilen PPTC cache silinecek :\n\n{0}\n\nDevam etmek istediğinizden emin misiniz?", - "DialogPPTCDeletionErrorMessage": "Belirtilen PPTC cache temizlenirken hata {0}: {1}", - "DialogShaderDeletionMessage": "Belirtilen Shader cache silinecek :\n\n{0}\n\nDevam etmek istediğinizden emin misiniz?", - "DialogShaderDeletionErrorMessage": "Belirtilen Shader cache temizlenirken hata {0}: {1}", - "DialogRyujinxErrorMessage": "Ryujinx bir hata ile karşılaştı", - "DialogInvalidTitleIdErrorMessage": "Arayüz hatası: Seçilen oyun geçerli bir title ID'ye sahip değil", - "DialogFirmwareInstallerFirmwareNotFoundErrorMessage": "{0} da geçerli bir sistem firmware'i bulunamadı.", - "DialogFirmwareInstallerFirmwareInstallTitle": "Firmware {0} Yükle", - "DialogFirmwareInstallerFirmwareInstallMessage": "Sistem sürümü {0} yüklenecek.", - "DialogFirmwareInstallerFirmwareInstallSubMessage": "\n\nBu şimdiki sistem sürümünün yerini alacak {0}.", - "DialogFirmwareInstallerFirmwareInstallConfirmMessage": "\n\nDevam etmek istiyor musunuz?", - "DialogFirmwareInstallerFirmwareInstallWaitMessage": "Firmware yükleniyor...", - "DialogFirmwareInstallerFirmwareInstallSuccessMessage": "Sistem sürümü {0} başarıyla yüklendi.", - "DialogUserProfileDeletionWarningMessage": "Seçilen profil silinirse kullanılabilen başka profil kalmayacak", - "DialogUserProfileDeletionConfirmMessage": "Seçilen profili silmek istiyor musunuz", - "DialogUserProfileUnsavedChangesTitle": "Uyarı - Kaydedilmemiş Değişiklikler", - "DialogUserProfileUnsavedChangesMessage": "Kullanıcı profilinizde kaydedilmemiş değişiklikler var.", - "DialogUserProfileUnsavedChangesSubMessage": "Yaptığınız değişiklikleri iptal etmek istediğinize emin misiniz?", - "DialogControllerSettingsModifiedConfirmMessage": "Geçerli kumanda seçenekleri güncellendi.", - "DialogControllerSettingsModifiedConfirmSubMessage": "Kaydetmek istiyor musunuz?", - "DialogLoadFileErrorMessage": "{0}. Hatalı Dosya: {1}", - "DialogModAlreadyExistsMessage": "Mod zaten var", - "DialogModInvalidMessage": "The specified directory does not contain a mod!", - "DialogModDeleteNoParentMessage": "Silme Başarısız: \"{0}\" Modu için üst dizin bulunamadı! ", - "DialogDlcNoDlcErrorMessage": "Belirtilen dosya seçilen oyun için DLC içermiyor!", - "DialogPerformanceCheckLoggingEnabledMessage": "Sadece geliştiriler için dizayn edilen Trace Loglama seçeneği etkin.", - "DialogPerformanceCheckLoggingEnabledConfirmMessage": "En iyi performans için trace loglama'nın devre dışı bırakılması tavsiye edilir. Trace loglama seçeneğini şimdi devre dışı bırakmak ister misiniz?", - "DialogPerformanceCheckShaderDumpEnabledMessage": "Sadece geliştiriler için dizayn edilen Shader Dumping seçeneği etkin.", - "DialogPerformanceCheckShaderDumpEnabledConfirmMessage": "En iyi performans için Shader Dumping'in devre dışı bırakılması tavsiye edilir. Shader Dumping seçeneğini şimdi devre dışı bırakmak ister misiniz?", - "DialogLoadAppGameAlreadyLoadedMessage": "Bir oyun zaten yüklendi", - "DialogLoadAppGameAlreadyLoadedSubMessage": "Lütfen yeni bir oyun açmadan önce emülasyonu durdurun veya emülatörü kapatın.", - "DialogUpdateAddUpdateErrorMessage": "Belirtilen dosya seçilen oyun için güncelleme içermiyor!", - "DialogSettingsBackendThreadingWarningTitle": "Uyarı - Backend Threading", - "DialogSettingsBackendThreadingWarningMessage": "Bu seçeneğin tamamen uygulanması için Ryujinx'in kapatıp açılması gerekir. Kullandığınız işletim sistemine bağlı olarak, Ryujinx'in multithreading'ini kullanırken driver'ınızın multithreading seçeneğini kapatmanız gerekebilir.", - "DialogModManagerDeletionWarningMessage": "You are about to delete the mod: {0}\n\nAre you sure you want to proceed?", - "DialogModManagerDeletionAllWarningMessage": "You are about to delete all mods for this title.\n\nAre you sure you want to proceed?", - "SettingsTabGraphicsFeaturesOptions": "Özellikler", - "SettingsTabGraphicsBackendMultithreading": "Grafik Backend Multithreading:", - "CommonAuto": "Otomatik", - "CommonOff": "Kapalı", - "CommonOn": "Açık", - "InputDialogYes": "Evet", - "InputDialogNo": "Hayır", - "DialogProfileInvalidProfileNameErrorMessage": "Dosya adı geçersiz karakter içeriyor. Lütfen tekrar deneyin.", - "MenuBarOptionsPauseEmulation": "Durdur", - "MenuBarOptionsResumeEmulation": "Devam Et", - "AboutUrlTooltipMessage": "Ryujinx'in websitesini varsayılan tarayıcınızda açmak için tıklayın.", - "AboutDisclaimerMessage": "Ryujinx, Nintendo™ veya ortaklarıyla herhangi bir şekilde bağlantılı değildir.", - "AboutAmiiboDisclaimerMessage": "Amiibo emülasyonumuzda \nAmiiboAPI (www.amiiboapi.com) kullanılmaktadır.", - "AboutPatreonUrlTooltipMessage": "Ryujinx'in Patreon sayfasını varsayılan tarayıcınızda açmak için tıklayın.", - "AboutGithubUrlTooltipMessage": "Ryujinx'in GitHub sayfasını varsayılan tarayıcınızda açmak için tıklayın.", - "AboutDiscordUrlTooltipMessage": "Varsayılan tarayıcınızda Ryujinx'in Discord'una bir davet açmak için tıklayın.", - "AboutTwitterUrlTooltipMessage": "Ryujinx'in Twitter sayfasını varsayılan tarayıcınızda açmak için tıklayın.", - "AboutRyujinxAboutTitle": "Hakkında:", - "AboutRyujinxAboutContent": "Ryujinx bir Nintendo Switch™ emülatörüdür.\nLütfen bizi Patreon'da destekleyin.\nEn son haberleri Twitter veya Discord'umuzdan alın.\nKatkıda bulunmak isteyen geliştiriciler GitHub veya Discord üzerinden daha fazla bilgi edinebilir.", - "AboutRyujinxMaintainersTitle": "Geliştiriciler:", - "AboutRyujinxMaintainersContentTooltipMessage": "Katkıda bulunanlar sayfasını varsayılan tarayıcınızda açmak için tıklayın.", - "AboutRyujinxSupprtersTitle": "Patreon Destekleyicileri:", - "AmiiboSeriesLabel": "Amiibo Serisi", - "AmiiboCharacterLabel": "Karakter", - "AmiiboScanButtonLabel": "Tarat", - "AmiiboOptionsShowAllLabel": "Tüm Amiibo'ları Göster", - "AmiiboOptionsUsRandomTagLabel": "Hack: Rastgele bir Uuid kullan", - "DlcManagerTableHeadingEnabledLabel": "Etkin", - "DlcManagerTableHeadingTitleIdLabel": "Başlık ID", - "DlcManagerTableHeadingContainerPathLabel": "Container Yol", - "DlcManagerTableHeadingFullPathLabel": "Tam Yol", - "DlcManagerRemoveAllButton": "Tümünü kaldır", - "DlcManagerEnableAllButton": "Tümünü Aktif Et", - "DlcManagerDisableAllButton": "Tümünü Devre Dışı Bırak", - "ModManagerDeleteAllButton": "Hepsini Sil", - "MenuBarOptionsChangeLanguage": "Dili Değiştir", - "MenuBarShowFileTypes": "Dosya Uzantılarını Göster", - "CommonSort": "Sırala", - "CommonShowNames": "İsimleri Göster", - "CommonFavorite": "Favori", - "OrderAscending": "Artan", - "OrderDescending": "Azalan", - "SettingsTabGraphicsFeatures": "Özellikler & İyileştirmeler", - "ErrorWindowTitle": "Hata Penceresi", - "ToggleDiscordTooltip": "Ryujinx'i \"şimdi oynanıyor\" Discord aktivitesinde göstermeyi veya göstermemeyi seçin", - "AddGameDirBoxTooltip": "Listeye eklemek için oyun dizini seçin", - "AddGameDirTooltip": "Listeye oyun dizini ekle", - "RemoveGameDirTooltip": "Seçili oyun dizinini kaldır", - "CustomThemeCheckTooltip": "Emülatör pencerelerinin görünümünü değiştirmek için özel bir Avalonia teması kullan", - "CustomThemePathTooltip": "Özel arayüz temasının yolu", - "CustomThemeBrowseTooltip": "Özel arayüz teması için göz at", - "DockModeToggleTooltip": "Docked modu emüle edilen sistemin yerleşik Nintendo Switch gibi davranmasını sağlar. Bu çoğu oyunda grafik kalitesini arttırır. Diğer yandan, bu seçeneği devre dışı bırakmak emüle edilen sistemin portatif Ninendo Switch gibi davranmasını sağlayıp grafik kalitesini düşürür.\n\nDocked modu kullanmayı düşünüyorsanız 1. Oyuncu kontrollerini; Handheld modunu kullanmak istiyorsanız portatif kontrollerini konfigüre edin.\n\nEmin değilseniz aktif halde bırakın.", - "DirectKeyboardTooltip": "Direct keyboard access (HID) support. Provides games access to your keyboard as a text entry device.\n\nOnly works with games that natively support keyboard usage on Switch hardware.\n\nLeave OFF if unsure.", - "DirectMouseTooltip": "Direct mouse access (HID) support. Provides games access to your mouse as a pointing device.\n\nOnly works with games that natively support mouse controls on Switch hardware, which are few and far between.\n\nWhen enabled, touch screen functionality may not work.\n\nLeave OFF if unsure.", - "RegionTooltip": "Sistem Bölgesini Değiştir", - "LanguageTooltip": "Sistem Dilini Değiştir", - "TimezoneTooltip": "Sistem Saat Dilimini Değiştir", - "TimeTooltip": "Sistem Saatini Değiştir", - "VSyncToggleTooltip": "Emulated console's Vertical Sync. Essentially a frame-limiter for the majority of games; disabling it may cause games to run at higher speed or make loading screens take longer or get stuck.\n\nCan be toggled in-game with a hotkey of your preference (F1 by default). We recommend doing this if you plan on disabling it.\n\nLeave ON if unsure.", - "PptcToggleTooltip": "Çevrilen JIT fonksiyonlarını oyun her açıldığında çevrilmek zorunda kalmaması için kaydeder.\n\nTeklemeyi azaltır ve ilk açılıştan sonra oyunların ilk açılış süresini ciddi biçimde hızlandırır.\n\nEmin değilseniz aktif halde bırakın.", - "FsIntegrityToggleTooltip": "Oyun açarken hatalı dosyaların olup olmadığını kontrol eder, ve hatalı dosya bulursa log dosyasında hash hatası görüntüler.\n\nPerformansa herhangi bir etkisi yoktur ve sorun gidermeye yardımcı olur.\n\nEmin değilseniz aktif halde bırakın.", - "AudioBackendTooltip": "Ses çıkış motorunu değiştirir.\n\nSDL2 tercih edilen seçenektir, OpenAL ve SoundIO ise alternatif olarak kullanılabilir. Dummy seçeneğinde ses çıkışı olmayacaktır.\n\nEmin değilseniz SDL2 seçeneğine ayarlayın.", - "MemoryManagerTooltip": "Guest hafızasının nasıl tahsis edilip erişildiğini değiştirir. Emüle edilen CPU performansını ciddi biçimde etkiler.\n\nEmin değilseniz HOST UNCHECKED seçeneğine ayarlayın.", - "MemoryManagerSoftwareTooltip": "Adres çevirisi için bir işlemci sayfası kullanır. En yüksek doğruluğu ve en yavaş performansı sunar.", - "MemoryManagerHostTooltip": "Hafızayı doğrudan host adres aralığında tahsis eder. Çok daha hızlı JIT derleme ve işletimi sunar.", - "MemoryManagerUnsafeTooltip": "Hafızayı doğrudan tahsis eder, ancak host aralığına erişimden önce adresi maskelemez. Daha iyi performansa karşılık emniyetten ödün verir. Misafir uygulama Ryujinx içerisinden istediği hafızaya erişebilir, bu sebeple bu seçenek ile sadece güvendiğiniz uygulamaları çalıştırın.", - "UseHypervisorTooltip": "JIT yerine Hypervisor kullan. Uygun durumlarda performansı büyük oranda arttırır. Ancak şu anki halinde stabil durumda çalışmayabilir.", - "DRamTooltip": "Emüle edilen sistem hafızasını 4GiB'dan 6GiB'a yükseltir.\n\nBu seçenek yalnızca yüksek çözünürlük doku paketleri veya 4k çözünürlük modları için kullanılır. Performansı artırMAZ!\n\nEmin değilseniz devre dışı bırakın.", - "IgnoreMissingServicesTooltip": "Henüz programlanmamış Horizon işletim sistemi servislerini görmezden gelir. Bu seçenek belirli oyunların açılırken çökmesinin önüne geçmeye yardımcı olabilir.\n\nEmin değilseniz devre dışı bırakın.", - "GraphicsBackendThreadingTooltip": "Grafik arka uç komutlarını ikinci bir iş parçacığında işletir.\n\nKendi multithreading desteği olmayan sürücülerde shader derlemeyi hızlandırıp performansı artırır. Multithreading desteği olan sürücülerde çok az daha iyi performans sağlar.\n\nEmin değilseniz Otomatik seçeneğine ayarlayın.", - "GalThreadingTooltip": "Grafik arka uç komutlarını ikinci bir iş parçacığında işletir.\n\nKendi multithreading desteği olmayan sürücülerde shader derlemeyi hızlandırıp performansı artırır. Multithreading desteği olan sürücülerde çok az daha iyi performans sağlar.\n\nEmin değilseniz Otomatik seçeneğine ayarlayın.", - "ShaderCacheToggleTooltip": "Sonraki çalışmalarda takılmaları engelleyen bir gölgelendirici disk önbelleğine kaydeder.", - "ResolutionScaleTooltip": "Multiplies the game's rendering resolution.\n\nA few games may not work with this and look pixelated even when the resolution is increased; for those games, you may need to find mods that remove anti-aliasing or that increase their internal rendering resolution. For using the latter, you'll likely want to select Native.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nKeep in mind 4x is overkill for virtually any setup.", - "ResolutionScaleEntryTooltip": "Küsüratlı çözünürlük ölçeği, 1.5 gibi. Küsüratlı ölçekler hata oluşturmaya ve çökmeye daha yatkındır.", - "AnisotropyTooltip": "Level of Anisotropic Filtering. Set to Auto to use the value requested by the game.", - "AspectRatioTooltip": "Aspect Ratio applied to the renderer window.\n\nOnly change this if you're using an aspect ratio mod for your game, otherwise the graphics will be stretched.\n\nLeave on 16:9 if unsure.", - "ShaderDumpPathTooltip": "Grafik Shader Döküm Yolu", - "FileLogTooltip": "Konsol loglarını diskte bir log dosyasına kaydeder. Performansı etkilemez.", - "StubLogTooltip": "Stub log mesajlarını konsola yazdırır. Performansı etkilemez.", - "InfoLogTooltip": "Bilgi log mesajlarını konsola yazdırır. Performansı etkilemez.", - "WarnLogTooltip": "Uyarı log mesajlarını konsola yazdırır. Performansı etkilemez.", - "ErrorLogTooltip": "Hata log mesajlarını konsola yazdırır. Performansı etkilemez.", - "TraceLogTooltip": "Trace log mesajlarını konsola yazdırır. Performansı etkilemez.", - "GuestLogTooltip": "Guest log mesajlarını konsola yazdırır. Performansı etkilemez.", - "FileAccessLogTooltip": "Dosya sistemi erişim log mesajlarını konsola yazdırır.", - "FSAccessLogModeTooltip": "Konsola FS erişim loglarının yazılmasını etkinleştirir. Kullanılabilir modlar 0-3'tür", - "DeveloperOptionTooltip": "Dikkatli kullanın", - "OpenGlLogLevel": "Uygun log seviyesinin aktif olmasını gerektirir", - "DebugLogTooltip": "Debug log mesajlarını konsola yazdırır.\n\nBu seçeneği yalnızca geliştirici üyemiz belirtirse aktifleştirin, çünkü bu seçenek log dosyasını okumayı zorlaştırır ve emülatörün performansını düşürür.", - "LoadApplicationFileTooltip": "Switch ile uyumlu bir dosya yüklemek için dosya tarayıcısını açar", - "LoadApplicationFolderTooltip": "Switch ile uyumlu ayrıştırılmamış bir uygulama yüklemek için dosya tarayıcısını açar", - "OpenRyujinxFolderTooltip": "Ryujinx dosya sistem klasörünü açar", - "OpenRyujinxLogsTooltip": "Log dosyalarının bulunduğu klasörü açar", - "ExitTooltip": "Ryujinx'ten çıkış yapmayı sağlar", - "OpenSettingsTooltip": "Seçenekler penceresini açar", - "OpenProfileManagerTooltip": "Kullanıcı profil yöneticisi penceresini açar", - "StopEmulationTooltip": "Oynanmakta olan oyunun emülasyonunu durdurup oyun seçimine geri döndürür", - "CheckUpdatesTooltip": "Ryujinx güncellemelerini denetlemeyi sağlar", - "OpenAboutTooltip": "Hakkında penceresini açar", - "GridSize": "Öge Boyutu", - "GridSizeTooltip": "Grid ögelerinin boyutunu değiştirmeyi sağlar", - "SettingsTabSystemSystemLanguageBrazilianPortuguese": "Brezilya Portekizcesi", - "AboutRyujinxContributorsButtonHeader": "Tüm katkıda bulunanları gör", - "SettingsTabSystemAudioVolume": "Ses Seviyesi: ", - "AudioVolumeTooltip": "Ses seviyesini değiştirir", - "SettingsTabSystemEnableInternetAccess": "Guest Internet Erişimi/LAN Modu", - "EnableInternetAccessTooltip": "Emüle edilen uygulamanın internete bağlanmasını sağlar.\n\nLAN modu bulunan oyunlar bu seçenek ile birbirine bağlanabilir ve sistemler aynı access point'e bağlanır. Bu gerçek konsolları da kapsar.\n\nNintendo sunucularına bağlanmayı sağlaMAZ. Internete bağlanmaya çalışan baz oyunların çökmesine sebep olabilr.\n\nEmin değilseniz devre dışı bırakın.", - "GameListContextMenuManageCheatToolTip": "Hileleri yönetmeyi sağlar", - "GameListContextMenuManageCheat": "Hileleri Yönet", - "GameListContextMenuManageModToolTip": "Modları Yönet", - "GameListContextMenuManageMod": "Modları Yönet", - "ControllerSettingsStickRange": "Menzil:", - "DialogStopEmulationTitle": "Ryujinx - Emülasyonu Durdur", - "DialogStopEmulationMessage": "Emülasyonu durdurmak istediğinizden emin misiniz?", - "SettingsTabCpu": "İşlemci", - "SettingsTabAudio": "Ses", - "SettingsTabNetwork": "Ağ", - "SettingsTabNetworkConnection": "Ağ Bağlantısı", - "SettingsTabCpuCache": "İşlemci Belleği", - "SettingsTabCpuMemory": "CPU Hafızası", - "DialogUpdaterFlatpakNotSupportedMessage": "Lütfen Ryujinx'i FlatHub aracılığıyla güncelleyin.", - "UpdaterDisabledWarningTitle": "Güncelleyici Devre Dışı!", - "ControllerSettingsRotate90": "Saat yönünde 90° Döndür", - "IconSize": "Ikon Boyutu", - "IconSizeTooltip": "Oyun ikonlarının boyutunu değiştirmeyi sağlar", - "MenuBarOptionsShowConsole": "Konsol'u Göster", - "ShaderCachePurgeError": "Belirtilen shader cache temizlenirken hata {0}: {1}", - "UserErrorNoKeys": "Keys bulunamadı", - "UserErrorNoFirmware": "Firmware bulunamadı", - "UserErrorFirmwareParsingFailed": "Firmware çözümleme hatası", - "UserErrorApplicationNotFound": "Uygulama bulunamadı", - "UserErrorUnknown": "Bilinmeyen hata", - "UserErrorUndefined": "Tanımlanmayan hata", - "UserErrorNoKeysDescription": "Ryujinx 'prod.keys' dosyasını bulamadı", - "UserErrorNoFirmwareDescription": "Ryujinx yüklü herhangi firmware bulamadı", - "UserErrorFirmwareParsingFailedDescription": "Ryujinx temin edilen firmware'i çözümleyemedi. Bu durum genellikle güncel olmayan keys'den kaynaklanır.", - "UserErrorApplicationNotFoundDescription": "Ryujinx belirtilen yolda geçerli bir uygulama bulamadı.", - "UserErrorUnknownDescription": "Bilinmeyen bir hata oluştu!", - "UserErrorUndefinedDescription": "Tanımlanmayan bir hata oluştu! Bu durum ile karşılaşılmamalıydı, lütfen bir geliştirici ile iletişime geçin!", - "OpenSetupGuideMessage": "Kurulum Kılavuzunu Aç", - "NoUpdate": "Güncelleme Yok", - "TitleUpdateVersionLabel": "Sürüm {0} - {1}", - "RyujinxInfo": "Ryujinx - Bilgi", - "RyujinxConfirm": "Ryujinx - Doğrulama", - "FileDialogAllTypes": "Tüm türler", - "Never": "Hiçbir Zaman", - "SwkbdMinCharacters": "En az {0} karakter uzunluğunda olmalı", - "SwkbdMinRangeCharacters": "{0}-{1} karakter uzunluğunda olmalı", - "SoftwareKeyboard": "Yazılım Klavyesi", - "SoftwareKeyboardModeNumeric": "Sadece 0-9 veya '.' olabilir", - "SoftwareKeyboardModeAlphabet": "Sadece CJK-characters olmayan karakterler olabilir", - "SoftwareKeyboardModeASCII": "Sadece ASCII karakterler olabilir", - "ControllerAppletControllers": "Desteklenen Kumandalar:", - "ControllerAppletPlayers": "Oyuncular:", - "ControllerAppletDescription": "Halihazırdaki konfigürasyonunuz geçersiz. Ayarları açın ve girişlerinizi yeniden konfigüre edin.", - "ControllerAppletDocked": "Docked mod ayarlandı. Portatif denetim devre dışı bırakılmalı.", - "UpdaterRenaming": "Eski dosyalar yeniden adlandırılıyor...", - "UpdaterRenameFailed": "Güncelleyici belirtilen dosyayı yeniden adlandıramadı: {0}", - "UpdaterAddingFiles": "Yeni Dosyalar Ekleniyor...", - "UpdaterExtracting": "Güncelleme Ayrıştırılıyor...", - "UpdaterDownloading": "Güncelleme İndiriliyor...", - "Game": "Oyun", - "Docked": "Docked", - "Handheld": "El tipi", - "ConnectionError": "Bağlantı Hatası.", - "AboutPageDeveloperListMore": "{0} ve daha fazla...", - "ApiError": "API Hatası.", - "LoadingHeading": "{0} Yükleniyor", - "CompilingPPTC": "PTC Derleniyor", - "CompilingShaders": "Shaderlar Derleniyor", - "AllKeyboards": "Tüm Klavyeler", - "OpenFileDialogTitle": "Açmak için desteklenen bir dosya seçin", - "OpenFolderDialogTitle": "Ayrıştırılmamış oyun içeren bir klasör seçin", - "AllSupportedFormats": "Tüm Desteklenen Formatlar", - "RyujinxUpdater": "Ryujinx Güncelleyicisi", - "SettingsTabHotkeys": "Klavye Kısayolları", - "SettingsTabHotkeysHotkeys": "Klavye Kısayolları", - "SettingsTabHotkeysToggleVsyncHotkey": "VSync'i Etkinleştir/Devre Dışı Bırak:", - "SettingsTabHotkeysScreenshotHotkey": "Ekran Görüntüsü Al:", - "SettingsTabHotkeysShowUiHotkey": "Arayüzü Göster:", - "SettingsTabHotkeysPauseHotkey": "Durdur:", - "SettingsTabHotkeysToggleMuteHotkey": "Sustur:", - "ControllerMotionTitle": "Hareket Kontrol Seçenekleri", - "ControllerRumbleTitle": "Titreşim Seçenekleri", - "SettingsSelectThemeFileDialogTitle": "Tema Dosyası Seç", - "SettingsXamlThemeFile": "Xaml Tema Dosyası", - "AvatarWindowTitle": "Hesapları Yönet - Avatar", - "Amiibo": "Amiibo", - "Unknown": "Bilinmeyen", - "Usage": "Kullanım", - "Writable": "Yazılabilir", - "SelectDlcDialogTitle": "DLC dosyalarını seç", - "SelectUpdateDialogTitle": "Güncelleme dosyalarını seç", - "SelectModDialogTitle": "Mod Dizinini Seç", - "UserProfileWindowTitle": "Kullanıcı Profillerini Yönet", - "CheatWindowTitle": "Oyun Hilelerini Yönet", - "DlcWindowTitle": "Oyun DLC'lerini Yönet", - "ModWindowTitle": "Manage Mods for {0} ({1})", - "UpdateWindowTitle": "Oyun Güncellemelerini Yönet", - "CheatWindowHeading": "{0} için Hile mevcut [{1}]", - "BuildId": "BuildId:", - "DlcWindowHeading": "{0} için DLC mevcut [{1}]", - "ModWindowHeading": "{0} Mod(lar)", - "UserProfilesEditProfile": "Seçiliyi Düzenle", - "Cancel": "İptal", - "Save": "Kaydet", - "Discard": "Iskarta", - "Paused": "Durduruldu", - "UserProfilesSetProfileImage": "Profil Resmi Ayarla", - "UserProfileEmptyNameError": "İsim gerekli", - "UserProfileNoImageError": "Profil resmi ayarlanmalıdır", - "GameUpdateWindowHeading": "{0} için güncellemeler mevcut [{1}]", - "SettingsTabHotkeysResScaleUpHotkey": "Çözünürlüğü artır:", - "SettingsTabHotkeysResScaleDownHotkey": "Çözünürlüğü azalt:", - "UserProfilesName": "İsim:", - "UserProfilesUserId": "Kullanıcı Adı:", - "SettingsTabGraphicsBackend": "Grafik Arka Ucu", - "SettingsTabGraphicsBackendTooltip": "Select the graphics backend that will be used in the emulator.\n\nVulkan is overall better for all modern graphics cards, as long as their drivers are up to date. Vulkan also features faster shader compilation (less stuttering) on all GPU vendors.\n\nOpenGL may achieve better results on old Nvidia GPUs, on old AMD GPUs on Linux, or on GPUs with lower VRAM, though shader compilation stutters will be greater.\n\nSet to Vulkan if unsure. Set to OpenGL if your GPU does not support Vulkan even with the latest graphics drivers.", - "SettingsEnableTextureRecompression": "Yeniden Doku Sıkıştırılmasını Aktif Et", - "SettingsEnableTextureRecompressionTooltip": "Compresses ASTC textures in order to reduce VRAM usage.\n\nGames using this texture format include Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder and The Legend of Zelda: Tears of the Kingdom.\n\nGraphics cards with 4GiB VRAM or less will likely crash at some point while running these games.\n\nEnable only if you're running out of VRAM on the aforementioned games. Leave OFF if unsure.", - "SettingsTabGraphicsPreferredGpu": "Kullanılan GPU", - "SettingsTabGraphicsPreferredGpuTooltip": "Vulkan Grafik Arka Ucu ile kullanılacak Ekran Kartını Seçin.\n\nOpenGL'nin kullanacağı GPU'yu etkilemez.\n\n Emin değilseniz \"dGPU\" olarak işaretlenmiş GPU'ya ayarlayın. Eğer yoksa, dokunmadan bırakın.\n", - "SettingsAppRequiredRestartMessage": "Ryujinx'i Yeniden Başlatma Gerekli", - "SettingsGpuBackendRestartMessage": "Grafik Motoru ya da GPU ayarları değiştirildi. Bu işlemin uygulanması için yeniden başlatma gerekli.", - "SettingsGpuBackendRestartSubMessage": "Şimdi yeniden başlatmak istiyor musunuz?", - "RyujinxUpdaterMessage": "Ryujinx'i en son sürüme güncellemek ister misiniz?", - "SettingsTabHotkeysVolumeUpHotkey": "Sesi Arttır:", - "SettingsTabHotkeysVolumeDownHotkey": "Sesi Azalt:", - "SettingsEnableMacroHLE": "Macro HLE'yi Aktifleştir", - "SettingsEnableMacroHLETooltip": "GPU Macro kodunun yüksek seviye emülasyonu.\n\nPerformansı arttırır, ama bazı oyunlarda grafik hatalarına yol açabilir.\n\nEmin değilseniz AÇIK bırakın.", - "SettingsEnableColorSpacePassthrough": "Renk Alanı Geçişi", - "SettingsEnableColorSpacePassthroughTooltip": "Vulkan Backend'ini renk alanı belirtmeden renk bilgisinden geçmeye yönlendirir. Geniş gam ekranlı kullanıcılar için bu, renk doğruluğu pahasına daha canlı renklerle sonuçlanabilir.", - "VolumeShort": "Ses", - "UserProfilesManageSaves": "Kayıtları Yönet", - "DeleteUserSave": "Bu oyun için kullanıcı kaydını silmek istiyor musunuz?", - "IrreversibleActionNote": "Bu eylem geri alınamaz.", - "SaveManagerHeading": "{0} için Kayıt Dosyalarını Yönet", - "SaveManagerTitle": "Kayıt Yöneticisi", - "Name": "İsim", - "Size": "Boyut", - "Search": "Ara", - "UserProfilesRecoverLostAccounts": "Kayıp Hesapları Kurtar", - "Recover": "Kurtar", - "UserProfilesRecoverHeading": "Aşağıdaki hesaplar için kayıtlar bulundu", - "UserProfilesRecoverEmptyList": "Kurtarılacak profil bulunamadı", - "GraphicsAATooltip": "Applies anti-aliasing to the game render.\n\nFXAA will blur most of the image, while SMAA will attempt to find jagged edges and smooth them out.\n\nNot recommended to use in conjunction with the FSR scaling filter.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on NONE if unsure.", - "GraphicsAALabel": "Kenar Yumuşatma:", - "GraphicsScalingFilterLabel": "Ölçekleme Filtresi:", - "GraphicsScalingFilterTooltip": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.", - "GraphicsScalingFilterBilinear": "Bilinear", - "GraphicsScalingFilterNearest": "Nearest", - "GraphicsScalingFilterFsr": "FSR", - "GraphicsScalingFilterLevelLabel": "Seviye", - "GraphicsScalingFilterLevelTooltip": "Set FSR 1.0 sharpening level. Higher is sharper.", - "SmaaLow": "Düşük SMAA", - "SmaaMedium": "Orta SMAA", - "SmaaHigh": "Yüksek SMAA", - "SmaaUltra": "En Yüksek SMAA", - "UserEditorTitle": "Kullanıcıyı Düzenle", - "UserEditorTitleCreate": "Kullanıcı Oluştur", - "SettingsTabNetworkInterface": "Ağ Bağlantısı:", - "NetworkInterfaceTooltip": "The network interface used for LAN/LDN features.\n\nIn conjunction with a VPN or XLink Kai and a game with LAN support, can be used to spoof a same-network connection over the Internet.\n\nLeave on DEFAULT if unsure.", - "NetworkInterfaceDefault": "Varsayılan", - "PackagingShaders": "Gölgeler Paketleniyor", - "AboutChangelogButton": "GitHub'da Değişiklikleri Görüntüle", - "AboutChangelogButtonTooltipMessage": "Kullandığınız versiyon için olan değişiklikleri varsayılan tarayıcınızda görmek için tıklayın", - "SettingsTabNetworkMultiplayer": "Çok Oyunculu", - "MultiplayerMode": "Mod:", - "MultiplayerModeTooltip": "Change LDN multiplayer mode.\n\nLdnMitm will modify local wireless/local play functionality in games to function as if it were LAN, allowing for local, same-network connections with other Ryujinx instances and hacked Nintendo Switch consoles that have the ldn_mitm module installed.\n\nMultiplayer requires all players to be on the same game version (i.e. Super Smash Bros. Ultimate v13.0.1 can't connect to v13.0.0).\n\nLeave DISABLED if unsure.", - "MultiplayerModeDisabled": "Devre Dışı", - "MultiplayerModeLdnMitm": "ldn_mitm" -} diff --git a/src/Ryujinx/Assets/Locales/uk_UA.json b/src/Ryujinx/Assets/Locales/uk_UA.json deleted file mode 100644 index 976edfb1b..000000000 --- a/src/Ryujinx/Assets/Locales/uk_UA.json +++ /dev/null @@ -1,780 +0,0 @@ -{ - "Language": "Українська", - "MenuBarFileOpenApplet": "Відкрити аплет", - "MenuBarFileOpenAppletOpenMiiAppletToolTip": "Відкрити аплет Mii Editor в автономному режимі", - "SettingsTabInputDirectMouseAccess": "Прямий доступ мишею", - "SettingsTabSystemMemoryManagerMode": "Режим диспетчера пам’яті:", - "SettingsTabSystemMemoryManagerModeSoftware": "Програмне забезпечення", - "SettingsTabSystemMemoryManagerModeHost": "Хост (швидко)", - "SettingsTabSystemMemoryManagerModeHostUnchecked": "Неперевірений хост (найшвидший, небезпечний)", - "SettingsTabSystemUseHypervisor": "Використовувати гіпервізор", - "MenuBarFile": "_Файл", - "MenuBarFileOpenFromFile": "_Завантажити програму з файлу", - "MenuBarFileOpenUnpacked": "Завантажити _розпаковану гру", - "MenuBarFileOpenEmuFolder": "Відкрити теку Ryujinx", - "MenuBarFileOpenLogsFolder": "Відкрити теку журналів змін", - "MenuBarFileExit": "_Вихід", - "MenuBarOptions": "_Параметри", - "MenuBarOptionsToggleFullscreen": "На весь екран", - "MenuBarOptionsStartGamesInFullscreen": "Запускати ігри на весь екран", - "MenuBarOptionsStopEmulation": "Зупинити емуляцію", - "MenuBarOptionsSettings": "_Налаштування", - "MenuBarOptionsManageUserProfiles": "_Керувати профілями користувачів", - "MenuBarActions": "_Дії", - "MenuBarOptionsSimulateWakeUpMessage": "Симулювати повідомлення про пробудження", - "MenuBarActionsScanAmiibo": "Сканувати Amiibo", - "MenuBarTools": "_Інструменти", - "MenuBarToolsInstallFirmware": "Установити прошивку", - "MenuBarFileToolsInstallFirmwareFromFile": "Установити прошивку з XCI або ZIP", - "MenuBarFileToolsInstallFirmwareFromDirectory": "Установити прошивку з теки", - "MenuBarToolsManageFileTypes": "Керувати типами файлів", - "MenuBarToolsInstallFileTypes": "Установити типи файлів", - "MenuBarToolsUninstallFileTypes": "Видалити типи файлів", - "MenuBarView": "_View", - "MenuBarViewWindow": "Window Size", - "MenuBarViewWindow720": "720p", - "MenuBarViewWindow1080": "1080p", - "MenuBarHelp": "_Допомога", - "MenuBarHelpCheckForUpdates": "Перевірити оновлення", - "MenuBarHelpAbout": "Про застосунок", - "MenuSearch": "Пошук...", - "GameListHeaderFavorite": "Обране", - "GameListHeaderIcon": "Значок", - "GameListHeaderApplication": "Назва", - "GameListHeaderDeveloper": "Розробник", - "GameListHeaderVersion": "Версія", - "GameListHeaderTimePlayed": "Зіграно часу", - "GameListHeaderLastPlayed": "Востаннє зіграно", - "GameListHeaderFileExtension": "Розширення файлу", - "GameListHeaderFileSize": "Розмір файлу", - "GameListHeaderPath": "Шлях", - "GameListContextMenuOpenUserSaveDirectory": "Відкрити теку збереження користувача", - "GameListContextMenuOpenUserSaveDirectoryToolTip": "Відкриває каталог, який містить збереження користувача програми", - "GameListContextMenuOpenDeviceSaveDirectory": "Відкрити каталог пристроїв користувача", - "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Відкриває каталог, який містить збереження пристрою програми", - "GameListContextMenuOpenBcatSaveDirectory": "Відкрити каталог користувача BCAT", - "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Відкриває каталог, який містить BCAT-збереження програми", - "GameListContextMenuManageTitleUpdates": "Керування оновленнями заголовків", - "GameListContextMenuManageTitleUpdatesToolTip": "Відкриває вікно керування оновленням заголовка", - "GameListContextMenuManageDlc": "Керування DLC", - "GameListContextMenuManageDlcToolTip": "Відкриває вікно керування DLC", - "GameListContextMenuCacheManagement": "Керування кешем", - "GameListContextMenuCacheManagementPurgePptc": "Очистити кеш PPTC", - "GameListContextMenuCacheManagementPurgePptcToolTip": "Видаляє кеш PPTC програми", - "GameListContextMenuCacheManagementPurgeShaderCache": "Очистити кеш шейдерів", - "GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "Видаляє кеш шейдерів програми", - "GameListContextMenuCacheManagementOpenPptcDirectory": "Відкрити каталог PPTC", - "GameListContextMenuCacheManagementOpenPptcDirectoryToolTip": "Відкриває каталог, який містить кеш PPTC програми", - "GameListContextMenuCacheManagementOpenShaderCacheDirectory": "Відкрити каталог кешу шейдерів", - "GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip": "Відкриває каталог, який містить кеш шейдерів програми", - "GameListContextMenuExtractData": "Видобути дані", - "GameListContextMenuExtractDataExeFS": "ExeFS", - "GameListContextMenuExtractDataExeFSToolTip": "Видобуває розділ ExeFS із поточної конфігурації програми (включаючи оновлення)", - "GameListContextMenuExtractDataRomFS": "RomFS", - "GameListContextMenuExtractDataRomFSToolTip": "Видобуває розділ RomFS із поточної конфігурації програми (включаючи оновлення)", - "GameListContextMenuExtractDataLogo": "Логотип", - "GameListContextMenuExtractDataLogoToolTip": "Видобуває розділ логотипу з поточної конфігурації програми (включаючи оновлення)", - "GameListContextMenuCreateShortcut": "Створити ярлик застосунку", - "GameListContextMenuCreateShortcutToolTip": "Створити ярлик на робочому столі, який запускає вибраний застосунок", - "GameListContextMenuCreateShortcutToolTipMacOS": "Створити ярлик у каталозі macOS програм, що запускає обраний Додаток", - "GameListContextMenuOpenModsDirectory": "Відкрити теку з модами", - "GameListContextMenuOpenModsDirectoryToolTip": "Відкриває каталог, який містить модифікації Додатків", - "GameListContextMenuOpenSdModsDirectory": "Відкрити каталог модифікацій Atmosphere", - "GameListContextMenuOpenSdModsDirectoryToolTip": "Відкриває альтернативний каталог SD-карти Atmosphere, що містить модифікації Додатків. Корисно для модифікацій, зроблених для реального обладнання.", - "StatusBarGamesLoaded": "{0}/{1} ігор завантажено", - "StatusBarSystemVersion": "Версія системи: {0}", - "LinuxVmMaxMapCountDialogTitle": "Виявлено низьку межу для відображення памʼяті", - "LinuxVmMaxMapCountDialogTextPrimary": "Бажаєте збільшити значення vm.max_map_count на {0}", - "LinuxVmMaxMapCountDialogTextSecondary": "Деякі ігри можуть спробувати створити більше відображень памʼяті, ніж дозволено наразі. Ryujinx завершить роботу, щойно цей ліміт буде перевищено.", - "LinuxVmMaxMapCountDialogButtonUntilRestart": "Так, до наст. перезапуску", - "LinuxVmMaxMapCountDialogButtonPersistent": "Так, назавжди", - "LinuxVmMaxMapCountWarningTextPrimary": "Максимальна кількість відображення памʼяті менша, ніж рекомендовано.", - "LinuxVmMaxMapCountWarningTextSecondary": "Поточне значення vm.max_map_count ({0}) менше за {1}. Деякі ігри можуть спробувати створити більше відображень пам’яті, ніж дозволено наразі. Ryujinx завершить роботу, щойно цей ліміт буде перевищено.\n\nВи можете збільшити ліміт вручну або встановити pkexec, який дозволяє Ryujinx допомогти з цим.", - "Settings": "Налаштування", - "SettingsTabGeneral": "Інтерфейс користувача", - "SettingsTabGeneralGeneral": "Загальні", - "SettingsTabGeneralEnableDiscordRichPresence": "Увімкнути розширену присутність Discord", - "SettingsTabGeneralCheckUpdatesOnLaunch": "Перевіряти наявність оновлень під час запуску", - "SettingsTabGeneralShowConfirmExitDialog": "Показати діалогове вікно «Підтвердити вихід».", - "SettingsTabGeneralRememberWindowState": "Remember Window Size/Position", - "SettingsTabGeneralHideCursor": "Сховати вказівник:", - "SettingsTabGeneralHideCursorNever": "Ніколи", - "SettingsTabGeneralHideCursorOnIdle": "Сховати у режимі очікування", - "SettingsTabGeneralHideCursorAlways": "Завжди", - "SettingsTabGeneralGameDirectories": "Тека ігор", - "SettingsTabGeneralAdd": "Додати", - "SettingsTabGeneralRemove": "Видалити", - "SettingsTabSystem": "Система", - "SettingsTabSystemCore": "Ядро", - "SettingsTabSystemSystemRegion": "Регіон системи:", - "SettingsTabSystemSystemRegionJapan": "Японія", - "SettingsTabSystemSystemRegionUSA": "США", - "SettingsTabSystemSystemRegionEurope": "Європа", - "SettingsTabSystemSystemRegionAustralia": "Австралія", - "SettingsTabSystemSystemRegionChina": "Китай", - "SettingsTabSystemSystemRegionKorea": "Корея", - "SettingsTabSystemSystemRegionTaiwan": "Тайвань", - "SettingsTabSystemSystemLanguage": "Мова системи:", - "SettingsTabSystemSystemLanguageJapanese": "Японська", - "SettingsTabSystemSystemLanguageAmericanEnglish": "Англійська (США)", - "SettingsTabSystemSystemLanguageFrench": "Французька", - "SettingsTabSystemSystemLanguageGerman": "Німецька", - "SettingsTabSystemSystemLanguageItalian": "Італійська", - "SettingsTabSystemSystemLanguageSpanish": "Іспанська", - "SettingsTabSystemSystemLanguageChinese": "Китайська", - "SettingsTabSystemSystemLanguageKorean": "Корейська", - "SettingsTabSystemSystemLanguageDutch": "Нідерландська", - "SettingsTabSystemSystemLanguagePortuguese": "Португальська", - "SettingsTabSystemSystemLanguageRussian": "Російська", - "SettingsTabSystemSystemLanguageTaiwanese": "Тайванська", - "SettingsTabSystemSystemLanguageBritishEnglish": "Англійська (Великобританія)", - "SettingsTabSystemSystemLanguageCanadianFrench": "Французька (Канада)", - "SettingsTabSystemSystemLanguageLatinAmericanSpanish": "Іспанська (Латиноамериканська)", - "SettingsTabSystemSystemLanguageSimplifiedChinese": "Спрощена китайська", - "SettingsTabSystemSystemLanguageTraditionalChinese": "Традиційна китайська", - "SettingsTabSystemSystemTimeZone": "Часовий пояс системи:", - "SettingsTabSystemSystemTime": "Час системи:", - "SettingsTabSystemEnableVsync": "Вертикальна синхронізація", - "SettingsTabSystemEnablePptc": "PPTC (профільований постійний кеш перекладу)", - "SettingsTabSystemEnableFsIntegrityChecks": "Перевірка цілісності FS", - "SettingsTabSystemAudioBackend": "Аудіосистема:", - "SettingsTabSystemAudioBackendDummy": "Dummy", - "SettingsTabSystemAudioBackendOpenAL": "OpenAL", - "SettingsTabSystemAudioBackendSoundIO": "SoundIO", - "SettingsTabSystemAudioBackendSDL2": "SDL2", - "SettingsTabSystemHacks": "Хитрощі", - "SettingsTabSystemHacksNote": " (може викликати нестабільність)", - "SettingsTabSystemExpandDramSize": "Використовувати альтернативне розташування пам'яті (розробники)", - "SettingsTabSystemIgnoreMissingServices": "Ігнорувати відсутні служби", - "SettingsTabGraphics": "Графіка", - "SettingsTabGraphicsAPI": "Графічний API", - "SettingsTabGraphicsEnableShaderCache": "Увімкнути кеш шейдерів", - "SettingsTabGraphicsAnisotropicFiltering": "Анізотропна фільтрація:", - "SettingsTabGraphicsAnisotropicFilteringAuto": "Авто", - "SettingsTabGraphicsAnisotropicFiltering2x": "2x", - "SettingsTabGraphicsAnisotropicFiltering4x": "4x", - "SettingsTabGraphicsAnisotropicFiltering8x": "8x", - "SettingsTabGraphicsAnisotropicFiltering16x": "16x", - "SettingsTabGraphicsResolutionScale": "Роздільна здатність:", - "SettingsTabGraphicsResolutionScaleCustom": "Користувацька (не рекомендовано)", - "SettingsTabGraphicsResolutionScaleNative": "Стандартний (720p/1080p)", - "SettingsTabGraphicsResolutionScale2x": "2x (1440p/2160p)", - "SettingsTabGraphicsResolutionScale3x": "3x (2160p/3240p)", - "SettingsTabGraphicsResolutionScale4x": "4x (2880p/4320p) (Не рекомендується)", - "SettingsTabGraphicsAspectRatio": "Співвідношення сторін:", - "SettingsTabGraphicsAspectRatio4x3": "4:3", - "SettingsTabGraphicsAspectRatio16x9": "16:9", - "SettingsTabGraphicsAspectRatio16x10": "16:10", - "SettingsTabGraphicsAspectRatio21x9": "21:9", - "SettingsTabGraphicsAspectRatio32x9": "32:9", - "SettingsTabGraphicsAspectRatioStretch": "Розтягнути до розміру вікна", - "SettingsTabGraphicsDeveloperOptions": "Параметри розробника", - "SettingsTabGraphicsShaderDumpPath": "Шлях скидання графічного шейдера:", - "SettingsTabLogging": "Налагодження", - "SettingsTabLoggingLogging": "Налагодження", - "SettingsTabLoggingEnableLoggingToFile": "Увімкнути налагодження у файл", - "SettingsTabLoggingEnableStubLogs": "Увімкнути журнали заглушки", - "SettingsTabLoggingEnableInfoLogs": "Увімкнути інформаційні журнали", - "SettingsTabLoggingEnableWarningLogs": "Увімкнути журнали попереджень", - "SettingsTabLoggingEnableErrorLogs": "Увімкнути журнали помилок", - "SettingsTabLoggingEnableTraceLogs": "Увімкнути журнали трасування", - "SettingsTabLoggingEnableGuestLogs": "Увімкнути журнали гостей", - "SettingsTabLoggingEnableFsAccessLogs": "Увімкнути журнали доступу Fs", - "SettingsTabLoggingFsGlobalAccessLogMode": "Режим журналу глобального доступу Fs:", - "SettingsTabLoggingDeveloperOptions": "Параметри розробника (УВАГА: знизиться продуктивність)", - "SettingsTabLoggingDeveloperOptionsNote": "УВАГА: Знижує продуктивність", - "SettingsTabLoggingGraphicsBackendLogLevel": "Рівень журналу графічного сервера:", - "SettingsTabLoggingGraphicsBackendLogLevelNone": "Немає", - "SettingsTabLoggingGraphicsBackendLogLevelError": "Помилка", - "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "Уповільнення", - "SettingsTabLoggingGraphicsBackendLogLevelAll": "Все", - "SettingsTabLoggingEnableDebugLogs": "Увімкнути журнали налагодження", - "SettingsTabInput": "Введення", - "SettingsTabInputEnableDockedMode": "Режим док-станції", - "SettingsTabInputDirectKeyboardAccess": "Прямий доступ з клавіатури", - "SettingsButtonSave": "Зберегти", - "SettingsButtonClose": "Закрити", - "SettingsButtonOk": "Гаразд", - "SettingsButtonCancel": "Скасувати", - "SettingsButtonApply": "Застосувати", - "ControllerSettingsPlayer": "Гравець", - "ControllerSettingsPlayer1": "Гравець 1", - "ControllerSettingsPlayer2": "Гравець 2", - "ControllerSettingsPlayer3": "Гравець 3", - "ControllerSettingsPlayer4": "Гравець 4", - "ControllerSettingsPlayer5": "Гравець 5", - "ControllerSettingsPlayer6": "Гравець 6", - "ControllerSettingsPlayer7": "Гравець 7", - "ControllerSettingsPlayer8": "Гравець 8", - "ControllerSettingsHandheld": "Портативний", - "ControllerSettingsInputDevice": "Пристрій введення", - "ControllerSettingsRefresh": "Оновити", - "ControllerSettingsDeviceDisabled": "Вимкнено", - "ControllerSettingsControllerType": "Тип контролера", - "ControllerSettingsControllerTypeHandheld": "Портативний", - "ControllerSettingsControllerTypeProController": "Контролер Pro", - "ControllerSettingsControllerTypeJoyConPair": "Обидва JoyCon", - "ControllerSettingsControllerTypeJoyConLeft": "Лівий JoyCon", - "ControllerSettingsControllerTypeJoyConRight": "Правий JoyCon", - "ControllerSettingsProfile": "Профіль", - "ControllerSettingsProfileDefault": "Типовий", - "ControllerSettingsLoad": "Завантажити", - "ControllerSettingsAdd": "Додати", - "ControllerSettingsRemove": "Видалити", - "ControllerSettingsButtons": "Кнопки", - "ControllerSettingsButtonA": "A", - "ControllerSettingsButtonB": "B", - "ControllerSettingsButtonX": "X", - "ControllerSettingsButtonY": "Y", - "ControllerSettingsButtonPlus": "+", - "ControllerSettingsButtonMinus": "-", - "ControllerSettingsDPad": "Панель направлення", - "ControllerSettingsDPadUp": "Вгору", - "ControllerSettingsDPadDown": "Вниз", - "ControllerSettingsDPadLeft": "Вліво", - "ControllerSettingsDPadRight": "Вправо", - "ControllerSettingsStickButton": "Кнопка", - "ControllerSettingsStickUp": "Уверх", - "ControllerSettingsStickDown": "Униз", - "ControllerSettingsStickLeft": "Ліворуч", - "ControllerSettingsStickRight": "Праворуч", - "ControllerSettingsStickStick": "Стик", - "ControllerSettingsStickInvertXAxis": "Обернути вісь стику X", - "ControllerSettingsStickInvertYAxis": "Обернути вісь стику Y", - "ControllerSettingsStickDeadzone": "Мертва зона:", - "ControllerSettingsLStick": "Лівий джойстик", - "ControllerSettingsRStick": "Правий джойстик", - "ControllerSettingsTriggersLeft": "Тригери ліворуч", - "ControllerSettingsTriggersRight": "Тригери праворуч", - "ControllerSettingsTriggersButtonsLeft": "Кнопки тригерів ліворуч", - "ControllerSettingsTriggersButtonsRight": "Кнопки тригерів праворуч", - "ControllerSettingsTriggers": "Тригери", - "ControllerSettingsTriggerL": "L", - "ControllerSettingsTriggerR": "R", - "ControllerSettingsTriggerZL": "ZL", - "ControllerSettingsTriggerZR": "ZR", - "ControllerSettingsLeftSL": "SL", - "ControllerSettingsLeftSR": "SR", - "ControllerSettingsRightSL": "SL", - "ControllerSettingsRightSR": "SR", - "ControllerSettingsExtraButtonsLeft": "Кнопки ліворуч", - "ControllerSettingsExtraButtonsRight": "Кнопки праворуч", - "ControllerSettingsMisc": "Різне", - "ControllerSettingsTriggerThreshold": "Поріг спрацьовування:", - "ControllerSettingsMotion": "Рух", - "ControllerSettingsMotionUseCemuhookCompatibleMotion": "Використовувати рух, сумісний з CemuHook", - "ControllerSettingsMotionControllerSlot": "Слот контролера:", - "ControllerSettingsMotionMirrorInput": "Дзеркальний вхід", - "ControllerSettingsMotionRightJoyConSlot": "Правий слот JoyCon:", - "ControllerSettingsMotionServerHost": "Хост сервера:", - "ControllerSettingsMotionGyroSensitivity": "Чутливість гіроскопа:", - "ControllerSettingsMotionGyroDeadzone": "Мертва зона гіроскопа:", - "ControllerSettingsSave": "Зберегти", - "ControllerSettingsClose": "Закрити", - "KeyUnknown": "Unknown", - "KeyShiftLeft": "Shift Left", - "KeyShiftRight": "Shift Right", - "KeyControlLeft": "Ctrl Left", - "KeyMacControlLeft": "⌃ Left", - "KeyControlRight": "Ctrl Right", - "KeyMacControlRight": "⌃ Right", - "KeyAltLeft": "Alt Left", - "KeyMacAltLeft": "⌥ Left", - "KeyAltRight": "Alt Right", - "KeyMacAltRight": "⌥ Right", - "KeyWinLeft": "⊞ Left", - "KeyMacWinLeft": "⌘ Left", - "KeyWinRight": "⊞ Right", - "KeyMacWinRight": "⌘ Right", - "KeyMenu": "Menu", - "KeyUp": "Up", - "KeyDown": "Down", - "KeyLeft": "Left", - "KeyRight": "Right", - "KeyEnter": "Enter", - "KeyEscape": "Escape", - "KeySpace": "Space", - "KeyTab": "Tab", - "KeyBackSpace": "Backspace", - "KeyInsert": "Insert", - "KeyDelete": "Delete", - "KeyPageUp": "Page Up", - "KeyPageDown": "Page Down", - "KeyHome": "Home", - "KeyEnd": "End", - "KeyCapsLock": "Caps Lock", - "KeyScrollLock": "Scroll Lock", - "KeyPrintScreen": "Print Screen", - "KeyPause": "Pause", - "KeyNumLock": "Num Lock", - "KeyClear": "Clear", - "KeyKeypad0": "Keypad 0", - "KeyKeypad1": "Keypad 1", - "KeyKeypad2": "Keypad 2", - "KeyKeypad3": "Keypad 3", - "KeyKeypad4": "Keypad 4", - "KeyKeypad5": "Keypad 5", - "KeyKeypad6": "Keypad 6", - "KeyKeypad7": "Keypad 7", - "KeyKeypad8": "Keypad 8", - "KeyKeypad9": "Keypad 9", - "KeyKeypadDivide": "Keypad Divide", - "KeyKeypadMultiply": "Keypad Multiply", - "KeyKeypadSubtract": "Keypad Subtract", - "KeyKeypadAdd": "Keypad Add", - "KeyKeypadDecimal": "Keypad Decimal", - "KeyKeypadEnter": "Keypad Enter", - "KeyNumber0": "0", - "KeyNumber1": "1", - "KeyNumber2": "2", - "KeyNumber3": "3", - "KeyNumber4": "4", - "KeyNumber5": "5", - "KeyNumber6": "6", - "KeyNumber7": "7", - "KeyNumber8": "8", - "KeyNumber9": "9", - "KeyTilde": "~", - "KeyGrave": "`", - "KeyMinus": "-", - "KeyPlus": "+", - "KeyBracketLeft": "[", - "KeyBracketRight": "]", - "KeySemicolon": ";", - "KeyQuote": "\"", - "KeyComma": ",", - "KeyPeriod": ".", - "KeySlash": "/", - "KeyBackSlash": "\\", - "KeyUnbound": "Unbound", - "GamepadLeftStick": "L Stick Button", - "GamepadRightStick": "R Stick Button", - "GamepadLeftShoulder": "Left Shoulder", - "GamepadRightShoulder": "Right Shoulder", - "GamepadLeftTrigger": "Left Trigger", - "GamepadRightTrigger": "Right Trigger", - "GamepadDpadUp": "Up", - "GamepadDpadDown": "Down", - "GamepadDpadLeft": "Left", - "GamepadDpadRight": "Right", - "GamepadMinus": "-", - "GamepadPlus": "+", - "GamepadGuide": "Guide", - "GamepadMisc1": "Misc", - "GamepadPaddle1": "Paddle 1", - "GamepadPaddle2": "Paddle 2", - "GamepadPaddle3": "Paddle 3", - "GamepadPaddle4": "Paddle 4", - "GamepadTouchpad": "Touchpad", - "GamepadSingleLeftTrigger0": "Left Trigger 0", - "GamepadSingleRightTrigger0": "Right Trigger 0", - "GamepadSingleLeftTrigger1": "Left Trigger 1", - "GamepadSingleRightTrigger1": "Right Trigger 1", - "StickLeft": "Left Stick", - "StickRight": "Right Stick", - "UserProfilesSelectedUserProfile": "Вибраний профіль користувача:", - "UserProfilesSaveProfileName": "Зберегти ім'я профілю", - "UserProfilesChangeProfileImage": "Змінити зображення профілю", - "UserProfilesAvailableUserProfiles": "Доступні профілі користувачів:", - "UserProfilesAddNewProfile": "Створити профіль", - "UserProfilesDelete": "Видалити", - "UserProfilesClose": "Закрити", - "ProfileNameSelectionWatermark": "Оберіть псевдонім", - "ProfileImageSelectionTitle": "Вибір зображення профілю", - "ProfileImageSelectionHeader": "Виберіть зображення профілю", - "ProfileImageSelectionNote": "Ви можете імпортувати власне зображення профілю або вибрати аватар із мікропрограми системи", - "ProfileImageSelectionImportImage": "Імпорт файлу зображення", - "ProfileImageSelectionSelectAvatar": "Виберіть аватар прошивки ", - "InputDialogTitle": "Діалог введення", - "InputDialogOk": "Гаразд", - "InputDialogCancel": "Скасувати", - "InputDialogAddNewProfileTitle": "Виберіть ім'я профілю", - "InputDialogAddNewProfileHeader": "Будь ласка, введіть ім'я профілю", - "InputDialogAddNewProfileSubtext": "(Макс. довжина: {0})", - "AvatarChoose": "Вибрати", - "AvatarSetBackgroundColor": "Встановити колір фону", - "AvatarClose": "Закрити", - "ControllerSettingsLoadProfileToolTip": "Завантажити профіль", - "ControllerSettingsAddProfileToolTip": "Додати профіль", - "ControllerSettingsRemoveProfileToolTip": "Видалити профіль", - "ControllerSettingsSaveProfileToolTip": "Зберегти профіль", - "MenuBarFileToolsTakeScreenshot": "Зробити знімок екрана", - "MenuBarFileToolsHideUi": "Сховати інтерфейс", - "GameListContextMenuRunApplication": "Запустити додаток", - "GameListContextMenuToggleFavorite": "Перемкнути вибране", - "GameListContextMenuToggleFavoriteToolTip": "Перемкнути улюблений статус гри", - "SettingsTabGeneralTheme": "Тема:", - "SettingsTabGeneralThemeDark": "Темна", - "SettingsTabGeneralThemeLight": "Світла", - "ControllerSettingsConfigureGeneral": "Налаштування", - "ControllerSettingsRumble": "Вібрація", - "ControllerSettingsRumbleStrongMultiplier": "Множник сильної вібрації", - "ControllerSettingsRumbleWeakMultiplier": "Множник слабкої вібрації", - "DialogMessageSaveNotAvailableMessage": "Немає збережених даних для {0} [{1:x16}]", - "DialogMessageSaveNotAvailableCreateSaveMessage": "Хочете створити дані збереження для цієї гри?", - "DialogConfirmationTitle": "Ryujinx - Підтвердження", - "DialogUpdaterTitle": "Ryujinx - Програма оновлення", - "DialogErrorTitle": "Ryujinx - Помилка", - "DialogWarningTitle": "Ryujinx - Попередження", - "DialogExitTitle": "Ryujinx - Вихід", - "DialogErrorMessage": "У Ryujinx сталася помилка", - "DialogExitMessage": "Ви впевнені, що бажаєте закрити Ryujinx?", - "DialogExitSubMessage": "Усі незбережені дані буде втрачено!", - "DialogMessageCreateSaveErrorMessage": "Під час створення вказаних даних збереження сталася помилка: {0}", - "DialogMessageFindSaveErrorMessage": "Під час пошуку вказаних даних збереження сталася помилка: {0}", - "FolderDialogExtractTitle": "Виберіть папку для видобування", - "DialogNcaExtractionMessage": "Видобування розділу {0} з {1}...", - "DialogNcaExtractionTitle": "Ryujinx - Екстрактор розділів NCA", - "DialogNcaExtractionMainNcaNotFoundErrorMessage": "Помилка видобування. Основний NCA не був присутній у вибраному файлі.", - "DialogNcaExtractionCheckLogErrorMessage": "Помилка видобування. Прочитайте файл журналу для отримання додаткової інформації.", - "DialogNcaExtractionSuccessMessage": "Видобування успішно завершено.", - "DialogUpdaterConvertFailedMessage": "Не вдалося конвертувати поточну версію Ryujinx.", - "DialogUpdaterCancelUpdateMessage": "Скасування оновлення!", - "DialogUpdaterAlreadyOnLatestVersionMessage": "Ви вже використовуєте останню версію Ryujinx!", - "DialogUpdaterFailedToGetVersionMessage": "Під час спроби отримати інформацію про випуск із GitHub Release сталася помилка. Це може бути спричинено, якщо новий випуск компілюється GitHub Actions. Повторіть спробу через кілька хвилин.", - "DialogUpdaterConvertFailedGithubMessage": "Не вдалося конвертувати отриману версію Ryujinx із випуску Github.", - "DialogUpdaterDownloadingMessage": "Завантаження оновлення...", - "DialogUpdaterExtractionMessage": "Видобування оновлення...", - "DialogUpdaterRenamingMessage": "Перейменування оновлення...", - "DialogUpdaterAddingFilesMessage": "Додавання нового оновлення...", - "DialogUpdaterCompleteMessage": "Оновлення завершено!", - "DialogUpdaterRestartMessage": "Перезапустити Ryujinx зараз?", - "DialogUpdaterNoInternetMessage": "Ви не підключені до Інтернету!", - "DialogUpdaterNoInternetSubMessage": "Будь ласка, переконайтеся, що у вас є робоче підключення до Інтернету!", - "DialogUpdaterDirtyBuildMessage": "Ви не можете оновити брудну збірку Ryujinx!", - "DialogUpdaterDirtyBuildSubMessage": "Будь ласка, завантажте Ryujinx на https://ryujinx.org/, якщо ви шукаєте підтримувану версію.", - "DialogRestartRequiredMessage": "Потрібен перезапуск", - "DialogThemeRestartMessage": "Тему збережено. Щоб застосувати тему, потрібен перезапуск.", - "DialogThemeRestartSubMessage": "Ви хочете перезапустити", - "DialogFirmwareInstallEmbeddedMessage": "Бажаєте встановити прошивку, вбудовану в цю гру? (Прошивка {0})", - "DialogFirmwareInstallEmbeddedSuccessMessage": "Встановлену прошивку не знайдено, але Ryujinx вдалося встановити прошивку {0} з наданої гри.\nТепер запуститься емулятор.", - "DialogFirmwareNoFirmwareInstalledMessage": "Прошивка не встановлена", - "DialogFirmwareInstalledMessage": "Встановлено прошивку {0}", - "DialogInstallFileTypesSuccessMessage": "Успішно встановлено типи файлів!", - "DialogInstallFileTypesErrorMessage": "Не вдалося встановити типи файлів.", - "DialogUninstallFileTypesSuccessMessage": "Успішно видалено типи файлів!", - "DialogUninstallFileTypesErrorMessage": "Не вдалося видалити типи файлів.", - "DialogOpenSettingsWindowLabel": "Відкрити вікно налаштувань", - "DialogControllerAppletTitle": "Аплет контролера", - "DialogMessageDialogErrorExceptionMessage": "Помилка показу діалогового вікна повідомлення: {0}", - "DialogSoftwareKeyboardErrorExceptionMessage": "Помилка показу програмної клавіатури: {0}", - "DialogErrorAppletErrorExceptionMessage": "Помилка показу діалогового вікна ErrorApplet: {0}", - "DialogUserErrorDialogMessage": "{0}: {1}", - "DialogUserErrorDialogInfoMessage": "\nДля отримання додаткової інформації про те, як виправити цю помилку, дотримуйтесь нашого посібника з налаштування.", - "DialogUserErrorDialogTitle": "Помилка Ryujinx ({0})", - "DialogAmiiboApiTitle": "Amiibo API", - "DialogAmiiboApiFailFetchMessage": "Під час отримання інформації з API сталася помилка.", - "DialogAmiiboApiConnectErrorMessage": "Неможливо підключитися до сервера Amiibo API. Можливо, служба не працює або вам потрібно перевірити, чи є підключення до Інтернету.", - "DialogProfileInvalidProfileErrorMessage": "Профіль {0} несумісний із поточною системою конфігурації вводу.", - "DialogProfileDefaultProfileOverwriteErrorMessage": "Стандартний профіль не можна перезаписати", - "DialogProfileDeleteProfileTitle": "Видалення профілю", - "DialogProfileDeleteProfileMessage": "Цю дію неможливо скасувати. Ви впевнені, що бажаєте продовжити?", - "DialogWarning": "Увага", - "DialogPPTCDeletionMessage": "Ви збираєтеся видалити кеш PPTC для:\n\n{0}\n\nВи впевнені, що бажаєте продовжити?", - "DialogPPTCDeletionErrorMessage": "Помилка очищення кешу PPTC на {0}: {1}", - "DialogShaderDeletionMessage": "Ви збираєтеся видалити кеш шейдерів для:\n\n{0}\n\nВи впевнені, що бажаєте продовжити?", - "DialogShaderDeletionErrorMessage": "Помилка очищення кешу шейдерів на {0}: {1}", - "DialogRyujinxErrorMessage": "У Ryujinx сталася помилка", - "DialogInvalidTitleIdErrorMessage": "Помилка інтерфейсу: вибрана гра не мала дійсного ідентифікатора назви", - "DialogFirmwareInstallerFirmwareNotFoundErrorMessage": "Дійсна прошивка системи не знайдена в {0}.", - "DialogFirmwareInstallerFirmwareInstallTitle": "Встановити прошивку {0}", - "DialogFirmwareInstallerFirmwareInstallMessage": "Буде встановлено версію системи {0}.", - "DialogFirmwareInstallerFirmwareInstallSubMessage": "\n\nЦе замінить поточну версію системи {0}.", - "DialogFirmwareInstallerFirmwareInstallConfirmMessage": "\n\nВи хочете продовжити?", - "DialogFirmwareInstallerFirmwareInstallWaitMessage": "Встановлення прошивки...", - "DialogFirmwareInstallerFirmwareInstallSuccessMessage": "Версію системи {0} успішно встановлено.", - "DialogUserProfileDeletionWarningMessage": "Якщо вибраний профіль буде видалено, інші профілі не відкриватимуться", - "DialogUserProfileDeletionConfirmMessage": "Ви хочете видалити вибраний профіль", - "DialogUserProfileUnsavedChangesTitle": "Увага — Незбережені зміни", - "DialogUserProfileUnsavedChangesMessage": "Ви зробили зміни у цьому профілю користувача які не було збережено.", - "DialogUserProfileUnsavedChangesSubMessage": "Бажаєте скасувати зміни?", - "DialogControllerSettingsModifiedConfirmMessage": "Поточні налаштування контролера оновлено.", - "DialogControllerSettingsModifiedConfirmSubMessage": "Ви хочете зберегти?", - "DialogLoadFileErrorMessage": "{0}. Файл з помилкою: {1}", - "DialogModAlreadyExistsMessage": "Модифікація вже існує", - "DialogModInvalidMessage": "Вказаний каталог не містить модифікації!", - "DialogModDeleteNoParentMessage": "Не видалено: Не знайдено батьківський каталог для модифікації \"{0}\"!", - "DialogDlcNoDlcErrorMessage": "Зазначений файл не містить DLC для вибраного заголовку!", - "DialogPerformanceCheckLoggingEnabledMessage": "Ви увімкнули журнал налагодження, призначений лише для розробників.", - "DialogPerformanceCheckLoggingEnabledConfirmMessage": "Для оптимальної продуктивності рекомендується вимкнути ведення журналу налагодження. Ви хочете вимкнути ведення журналу налагодження зараз?", - "DialogPerformanceCheckShaderDumpEnabledMessage": "Ви увімкнули скидання шейдерів, призначений лише для розробників.", - "DialogPerformanceCheckShaderDumpEnabledConfirmMessage": "Для оптимальної продуктивності рекомендується вимкнути скидання шейдерів. Ви хочете вимкнути скидання шейдерів зараз?", - "DialogLoadAppGameAlreadyLoadedMessage": "Гру вже завантажено", - "DialogLoadAppGameAlreadyLoadedSubMessage": "Зупиніть емуляцію або закрийте емулятор перед запуском іншої гри.", - "DialogUpdateAddUpdateErrorMessage": "Зазначений файл не містить оновлення для вибраного заголовка!", - "DialogSettingsBackendThreadingWarningTitle": "Попередження - потокове керування сервером", - "DialogSettingsBackendThreadingWarningMessage": "Ryujinx потрібно перезапустити після зміни цього параметра, щоб він застосовувався повністю. Залежно від вашої платформи вам може знадобитися вручну вимкнути власну багатопотоковість драйвера під час використання Ryujinx.", - "DialogModManagerDeletionWarningMessage": "Ви збираєтесь видалити модифікацію: {0}\n\nВи дійсно бажаєте продовжити?", - "DialogModManagerDeletionAllWarningMessage": "Ви збираєтесь видалити всі модифікації для цього Додатка.\n\nВи дійсно бажаєте продовжити?", - "SettingsTabGraphicsFeaturesOptions": "Особливості", - "SettingsTabGraphicsBackendMultithreading": "Багатопотоковість графічного сервера:", - "CommonAuto": "Авто", - "CommonOff": "Вимкнути", - "CommonOn": "Увімкнути", - "InputDialogYes": "Так", - "InputDialogNo": "Ні", - "DialogProfileInvalidProfileNameErrorMessage": "Ім'я файлу містить неприпустимі символи. Будь ласка, спробуйте ще раз.", - "MenuBarOptionsPauseEmulation": "Пауза", - "MenuBarOptionsResumeEmulation": "Продовжити", - "AboutUrlTooltipMessage": "Натисніть, щоб відкрити сайт Ryujinx у браузері за замовчування.", - "AboutDisclaimerMessage": "Ryujinx жодним чином не пов’язано з Nintendo™,\nчи будь-яким із їхніх партнерів.", - "AboutAmiiboDisclaimerMessage": "AmiiboAPI (www.amiiboapi.com) використовується в нашій емуляції Amiibo.", - "AboutPatreonUrlTooltipMessage": "Натисніть, щоб відкрити сторінку Patreon Ryujinx у вашому браузері за замовчування.", - "AboutGithubUrlTooltipMessage": "Натисніть, щоб відкрити сторінку GitHub Ryujinx у браузері за замовчуванням.", - "AboutDiscordUrlTooltipMessage": "Натисніть, щоб відкрити запрошення на сервер Discord Ryujinx у браузері за замовчуванням.", - "AboutTwitterUrlTooltipMessage": "Натисніть, щоб відкрити сторінку Twitter Ryujinx у браузері за замовчуванням.", - "AboutRyujinxAboutTitle": "Про програму:", - "AboutRyujinxAboutContent": "Ryujinx — це емулятор для Nintendo Switch™.\nБудь ласка, підтримайте нас на Patreon.\nОтримуйте всі останні новини в нашому Twitter або Discord.\nРозробники, які хочуть зробити внесок, можуть дізнатися більше на нашому GitHub або в Discord.", - "AboutRyujinxMaintainersTitle": "Підтримується:", - "AboutRyujinxMaintainersContentTooltipMessage": "Натисніть, щоб відкрити сторінку співавторів у вашому браузері за замовчування.", - "AboutRyujinxSupprtersTitle": "Підтримується на Patreon:", - "AmiiboSeriesLabel": "Серія Amiibo", - "AmiiboCharacterLabel": "Персонаж", - "AmiiboScanButtonLabel": "Сканувати", - "AmiiboOptionsShowAllLabel": "Показати всі Amiibo", - "AmiiboOptionsUsRandomTagLabel": "Хитрість: Використовувати випадковий тег Uuid", - "DlcManagerTableHeadingEnabledLabel": "Увімкнено", - "DlcManagerTableHeadingTitleIdLabel": "ID заголовка", - "DlcManagerTableHeadingContainerPathLabel": "Шлях до контейнеру", - "DlcManagerTableHeadingFullPathLabel": "Повний шлях", - "DlcManagerRemoveAllButton": "Видалити все", - "DlcManagerEnableAllButton": "Увімкнути всі", - "DlcManagerDisableAllButton": "Вимкнути всі", - "ModManagerDeleteAllButton": "Видалити все", - "MenuBarOptionsChangeLanguage": "Змінити мову", - "MenuBarShowFileTypes": "Показати типи файлів", - "CommonSort": "Сортувати", - "CommonShowNames": "Показати назви", - "CommonFavorite": "Вибрані", - "OrderAscending": "За зростанням", - "OrderDescending": "За спаданням", - "SettingsTabGraphicsFeatures": "Функції та вдосконалення", - "ErrorWindowTitle": "Вікно помилок", - "ToggleDiscordTooltip": "Виберіть, чи відображати Ryujinx у вашій «поточній грі» в Discord", - "AddGameDirBoxTooltip": "Введіть каталог ігор, щоб додати до списку", - "AddGameDirTooltip": "Додати каталог гри до списку", - "RemoveGameDirTooltip": "Видалити вибраний каталог гри", - "CustomThemeCheckTooltip": "Використовуйте користувацьку тему Avalonia для графічного інтерфейсу, щоб змінити вигляд меню емулятора", - "CustomThemePathTooltip": "Шлях до користувацької теми графічного інтерфейсу", - "CustomThemeBrowseTooltip": "Огляд користувацької теми графічного інтерфейсу", - "DockModeToggleTooltip": "У режимі док-станції емульована система веде себе як приєднаний Nintendo Switch. Це покращує точність графіки в більшості ігор. І навпаки, вимкнення цього призведе до того, що емульована система поводитиметься як портативний комутатор Nintendo, погіршуючи якість графіки.\n\nНалаштуйте елементи керування для гравця 1, якщо плануєте використовувати режим док-станції; налаштуйте ручні елементи керування, якщо плануєте використовувати портативний режим.\n\nЗалиште увімкненим, якщо не впевнені.", - "DirectKeyboardTooltip": "Підтримка прямого доступу до клавіатури (HID). Надає іграм доступ до клавіатури для вводу тексту.\n\nПрацює тільки з іграми, які підтримують клавіатуру на обладнанні Switch.\n\nЗалиште вимкненим, якщо не впевнені.", - "DirectMouseTooltip": "Підтримка прямого доступу до миші (HID). Надає іграм доступ до миші, як пристрій вказування.\n\nПрацює тільки з іграми, які підтримують мишу на обладнанні Switch, їх небагато.\n\nФункціонал сенсорного екрана може не працювати, якщо функція ввімкнена.\n\nЗалиште вимкненим, якщо не впевнені.", - "RegionTooltip": "Змінити регіон системи", - "LanguageTooltip": "Змінити мову системи", - "TimezoneTooltip": "Змінити часовий пояс системи", - "TimeTooltip": "Змінити час системи", - "VSyncToggleTooltip": "Емульована вертикальна синхронізація консолі. По суті, обмежувач кадрів для більшості ігор; його вимкнення може призвести до того, що ігри працюватимуть на вищій швидкості, екрани завантаження триватимуть довше чи зупинятимуться.\n\nМожна перемикати в грі гарячою клавішею (За умовчанням F1). Якщо ви плануєте вимкнути функцію, рекомендуємо зробити це через гарячу клавішу.\n\nЗалиште увімкненим, якщо не впевнені.", - "PptcToggleTooltip": "Зберігає перекладені функції JIT, щоб їх не потрібно було перекладати кожного разу, коли гра завантажується.\n\nЗменшує заїкання та значно прискорює час завантаження після першого завантаження гри.\n\nЗалиште увімкненим, якщо не впевнені.", - "FsIntegrityToggleTooltip": "Перевіряє наявність пошкоджених файлів під час завантаження гри, і якщо виявлено пошкоджені файли, показує помилку хешу в журналі.\n\nНе впливає на продуктивність і призначений для усунення несправностей.\n\nЗалиште увімкненим, якщо не впевнені.", - "AudioBackendTooltip": "Змінює серверну частину, яка використовується для відтворення аудіо.\n\nSDL2 є кращим, тоді як OpenAL і SoundIO використовуються як резервні варіанти. Dummy не матиме звуку.\n\nВстановіть SDL2, якщо не впевнені.", - "MemoryManagerTooltip": "Змінює спосіб відображення та доступу до гостьової пам’яті. Значно впливає на продуктивність емульованого ЦП.\n\nВстановіть «Неперевірений хост», якщо не впевнені.", - "MemoryManagerSoftwareTooltip": "Використовує програмну таблицю сторінок для перекладу адрес. Найвища точність, але найповільніша продуктивність.", - "MemoryManagerHostTooltip": "Пряме відображення пам'яті в адресному просторі хосту. Набагато швидша компіляція та виконання JIT.", - "MemoryManagerUnsafeTooltip": "Пряме відображення пам’яті, але не маскує адресу в гостьовому адресному просторі перед доступом. Швидше, але ціною безпеки. Гостьова програма може отримати доступ до пам’яті з будь-якого місця в Ryujinx, тому запускайте в цьому режимі лише програми, яким ви довіряєте.", - "UseHypervisorTooltip": "Використання гіпервізор замість JIT. Значно покращує продуктивність, коли доступний, але може бути нестабільним у поточному стані.", - "DRamTooltip": "Використовує альтернативний макет MemoryMode для імітації моделі розробки Switch.\n\nЦе корисно лише для пакетів текстур з вищою роздільною здатністю або модифікацій із роздільною здатністю 4K. НЕ покращує продуктивність.\n\nЗалиште вимкненим, якщо не впевнені.", - "IgnoreMissingServicesTooltip": "Ігнорує нереалізовані служби Horizon OS. Це може допомогти в обході збоїв під час завантаження певних ігор.\n\nЗалиште вимкненим, якщо не впевнені.", - "GraphicsBackendThreadingTooltip": "Виконує команди графічного сервера в другому потоці.\n\nПрискорює компіляцію шейдерів, зменшує затримки та покращує продуктивність драйверів GPU без власної підтримки багатопоточності. Трохи краща продуктивність на драйверах з багатопотоковістю.\nВстановіть значення «Авто», якщо не впевнені", - "GalThreadingTooltip": "Виконує команди графічного сервера в другому потоці.\n\nПрискорює компіляцію шейдерів, зменшує затримки та покращує продуктивність драйверів GPU без власної підтримки багатопоточності. Трохи краща продуктивність на драйверах з багатопотоковістю.\n\nВстановіть значення «Авто», якщо не впевнені.", - "ShaderCacheToggleTooltip": "Зберігає кеш дискового шейдера, що зменшує затримки під час наступних запусків.\n\nЗалиште увімкненим, якщо не впевнені.", - "ResolutionScaleTooltip": "Множить роздільну здатність гри.\n\nДеякі ігри можуть не працювати з цією функцією, і виглядатимуть піксельними; для цих ігор треба знайти модифікації, що зупиняють згладжування або підвищують роздільну здатність. Для останніх модифікацій, вибирайте \"Native\".\n\nЦей параметр можна міняти коли гра запущена кліком на \"Застосувати\"; ви можете перемістити вікно налаштувань і поекспериментувати з видом гри.\n\nМайте на увазі, що 4x це занадто для будь-якого комп'ютера.", - "ResolutionScaleEntryTooltip": "Масштаб роздільної здатності з плаваючою комою, наприклад 1,5. Не інтегральні масштаби, швидше за все, спричинять проблеми або збій.", - "AnisotropyTooltip": "Рівень анізотропної фільтрації. Встановіть на «Авто», щоб використовувати значення, яке вимагає гра.", - "AspectRatioTooltip": "Співвідношення сторін застосовано до вікна рендера.\n\nМіняйте тільки, якщо використовуєте модифікацію співвідношення сторін для гри, інакше графіка буде розтягнута.\n\nЗалиште на \"16:9\", якщо не впевнені.", - "ShaderDumpPathTooltip": "Шлях скидання графічних шейдерів", - "FileLogTooltip": "Зберігає журнал консолі у файл журналу на диску. Не впливає на продуктивність.", - "StubLogTooltip": "Друкує повідомлення журналу-заглушки на консолі. Не впливає на продуктивність.", - "InfoLogTooltip": "Друкує повідомлення інформаційного журналу на консолі. Не впливає на продуктивність.", - "WarnLogTooltip": "Друкує повідомлення журналу попереджень у консолі. Не впливає на продуктивність.", - "ErrorLogTooltip": "Друкує повідомлення журналу помилок у консолі. Не впливає на продуктивність.", - "TraceLogTooltip": "Друкує повідомлення журналу трасування на консолі. Не впливає на продуктивність.", - "GuestLogTooltip": "Друкує повідомлення журналу гостей у консолі. Не впливає на продуктивність.", - "FileAccessLogTooltip": "Друкує повідомлення журналу доступу до файлів у консолі.", - "FSAccessLogModeTooltip": "Вмикає виведення журналу доступу до FS на консоль. Можливі режими 0-3", - "DeveloperOptionTooltip": "Використовуйте з обережністю", - "OpenGlLogLevel": "Потрібно увімкнути відповідні рівні журналу", - "DebugLogTooltip": "Друкує повідомлення журналу налагодження на консолі.\n\nВикористовуйте це лише за спеціальною вказівкою співробітника, оскільки це ускладнить читання журналів і погіршить роботу емулятора.", - "LoadApplicationFileTooltip": "Відкриває файловий провідник, щоб вибрати для завантаження сумісний файл Switch", - "LoadApplicationFolderTooltip": "Відкриває файловий провідник, щоб вибрати сумісну з комутатором розпаковану програму для завантаження", - "OpenRyujinxFolderTooltip": "Відкриває папку файлової системи Ryujinx", - "OpenRyujinxLogsTooltip": "Відкриває папку, куди записуються журнали", - "ExitTooltip": "Виходить з Ryujinx", - "OpenSettingsTooltip": "Відкриває вікно налаштувань", - "OpenProfileManagerTooltip": "Відкриває вікно диспетчера профілів користувачів", - "StopEmulationTooltip": "Зупиняє емуляцію поточної гри та повертається до вибору гри", - "CheckUpdatesTooltip": "Перевіряє наявність оновлень для Ryujinx", - "OpenAboutTooltip": "Відкриває вікно «Про програму».", - "GridSize": "Розмір сітки", - "GridSizeTooltip": "Змінити розмір елементів сітки", - "SettingsTabSystemSystemLanguageBrazilianPortuguese": "Португальська (Бразилія)", - "AboutRyujinxContributorsButtonHeader": "Переглянути всіх співавторів", - "SettingsTabSystemAudioVolume": "Гучність: ", - "AudioVolumeTooltip": "Змінити гучність звуку", - "SettingsTabSystemEnableInternetAccess": "Гостьовий доступ до Інтернету/режим LAN", - "EnableInternetAccessTooltip": "Дозволяє емульованій програмі підключатися до Інтернету.\n\nІгри з режимом локальної мережі можуть підключатися одна до одної, якщо це увімкнено, і системи підключені до однієї точки доступу. Сюди входять і справжні консолі.\n\nНЕ дозволяє підключатися до серверів Nintendo. Може призвести до збою в деяких іграх, які намагаються підключитися до Інтернету.\n\nЗалиште вимкненим, якщо не впевнені.", - "GameListContextMenuManageCheatToolTip": "Керування читами", - "GameListContextMenuManageCheat": "Керування читами", - "GameListContextMenuManageModToolTip": "Керування модами", - "GameListContextMenuManageMod": "Керування модами", - "ControllerSettingsStickRange": "Діапазон:", - "DialogStopEmulationTitle": "Ryujinx - Зупинити емуляцію", - "DialogStopEmulationMessage": "Ви впевнені, що хочете зупинити емуляцію?", - "SettingsTabCpu": "ЦП", - "SettingsTabAudio": "Аудіо", - "SettingsTabNetwork": "Мережа", - "SettingsTabNetworkConnection": "Підключення до мережі", - "SettingsTabCpuCache": "Кеш ЦП", - "SettingsTabCpuMemory": "Пам'ять ЦП", - "DialogUpdaterFlatpakNotSupportedMessage": "Будь ласка, оновіть Ryujinx через FlatHub.", - "UpdaterDisabledWarningTitle": "Програму оновлення вимкнено!", - "ControllerSettingsRotate90": "Повернути на 90° за годинниковою стрілкою", - "IconSize": "Розмір значка", - "IconSizeTooltip": "Змінити розмір значків гри", - "MenuBarOptionsShowConsole": "Показати консоль", - "ShaderCachePurgeError": "Помилка очищення кешу шейдера {0}: {1}", - "UserErrorNoKeys": "Ключі не знайдено", - "UserErrorNoFirmware": "Прошивка не знайдена", - "UserErrorFirmwareParsingFailed": "Помилка аналізу прошивки", - "UserErrorApplicationNotFound": "Додаток не знайдено", - "UserErrorUnknown": "Невідома помилка", - "UserErrorUndefined": "Невизначена помилка", - "UserErrorNoKeysDescription": "Ryujinx не вдалося знайти ваш файл «prod.keys».", - "UserErrorNoFirmwareDescription": "Ryujinx не вдалося знайти встановлену прошивку", - "UserErrorFirmwareParsingFailedDescription": "Ryujinx не вдалося проаналізувати прошивку. Зазвичай це спричинено застарілими ключами.", - "UserErrorApplicationNotFoundDescription": "Ryujinx не вдалося знайти дійсний додаток за вказаним шляхом", - "UserErrorUnknownDescription": "Сталася невідома помилка!", - "UserErrorUndefinedDescription": "Сталася невизначена помилка! Цього не повинно статися, зверніться до розробника!", - "OpenSetupGuideMessage": "Відкрити посібник із налаштування", - "NoUpdate": "Немає оновлень", - "TitleUpdateVersionLabel": "Версія {0} - {1}", - "RyujinxInfo": "Ryujin x - Інформація", - "RyujinxConfirm": "Ryujinx - Підтвердження", - "FileDialogAllTypes": "Всі типи", - "Never": "Ніколи", - "SwkbdMinCharacters": "Мінімальна кількість символів: {0}", - "SwkbdMinRangeCharacters": "Має бути {0}-{1} символів", - "SoftwareKeyboard": "Програмна клавіатура", - "SoftwareKeyboardModeNumeric": "Повинно бути лише 0-9 або “.”", - "SoftwareKeyboardModeAlphabet": "Повинно бути лише не CJK-символи", - "SoftwareKeyboardModeASCII": "Повинно бути лише ASCII текст", - "ControllerAppletControllers": "Підтримувані контролери:", - "ControllerAppletPlayers": "Гравці:", - "ControllerAppletDescription": "Поточна конфігурація невірна. Відкрийте налаштування та переналаштуйте Ваші дані.", - "ControllerAppletDocked": "Встановлений режим в док-станції. Вимкніть портативні контролери.", - "UpdaterRenaming": "Перейменування старих файлів...", - "UpdaterRenameFailed": "Програмі оновлення не вдалося перейменувати файл: {0}", - "UpdaterAddingFiles": "Додавання нових файлів...", - "UpdaterExtracting": "Видобування оновлення...", - "UpdaterDownloading": "Завантаження оновлення...", - "Game": "Гра", - "Docked": "Док-станція", - "Handheld": "Портативний", - "ConnectionError": "Помилка з'єднання.", - "AboutPageDeveloperListMore": "{0} та інші...", - "ApiError": "Помилка API.", - "LoadingHeading": "Завантаження {0}", - "CompilingPPTC": "Компіляція PTC", - "CompilingShaders": "Компіляція шейдерів", - "AllKeyboards": "Всі клавіатури", - "OpenFileDialogTitle": "Виберіть підтримуваний файл для відкриття", - "OpenFolderDialogTitle": "Виберіть теку з розпакованою грою", - "AllSupportedFormats": "Усі підтримувані формати", - "RyujinxUpdater": "Програма оновлення Ryujinx", - "SettingsTabHotkeys": "Гарячі клавіші клавіатури", - "SettingsTabHotkeysHotkeys": "Гарячі клавіші клавіатури", - "SettingsTabHotkeysToggleVsyncHotkey": "Увімк/вимк вертикальну синхронізацію:", - "SettingsTabHotkeysScreenshotHotkey": "Знімок екрана:", - "SettingsTabHotkeysShowUiHotkey": "Показати інтерфейс:", - "SettingsTabHotkeysPauseHotkey": "Пауза:", - "SettingsTabHotkeysToggleMuteHotkey": "Вимкнути звук:", - "ControllerMotionTitle": "Налаштування керування рухом", - "ControllerRumbleTitle": "Налаштування вібрації", - "SettingsSelectThemeFileDialogTitle": "Виберіть файл теми", - "SettingsXamlThemeFile": "Файл теми Xaml", - "AvatarWindowTitle": "Керування обліковими записами - Аватар", - "Amiibo": "Amiibo", - "Unknown": "Невідомо", - "Usage": "Використання", - "Writable": "Можливість запису", - "SelectDlcDialogTitle": "Виберіть файли DLC", - "SelectUpdateDialogTitle": "Виберіть файли оновлення", - "SelectModDialogTitle": "Виберіть теку з модами", - "UserProfileWindowTitle": "Менеджер профілів користувачів", - "CheatWindowTitle": "Менеджер читів", - "DlcWindowTitle": "Менеджер вмісту для завантаження", - "ModWindowTitle": "Керувати модами для {0} ({1})", - "UpdateWindowTitle": "Менеджер оновлення назв", - "CheatWindowHeading": "Коди доступні для {0} [{1}]", - "BuildId": "ID збірки:", - "DlcWindowHeading": "Вміст для завантаження, доступний для {1} ({2}): {0}", - "ModWindowHeading": "{0} мод(ів)", - "UserProfilesEditProfile": "Редагувати вибране", - "Cancel": "Скасувати", - "Save": "Зберегти", - "Discard": "Скасувати", - "Paused": "Призупинено", - "UserProfilesSetProfileImage": "Встановити зображення профілю", - "UserProfileEmptyNameError": "Імʼя обовʼязкове", - "UserProfileNoImageError": "Зображення профілю обовʼязкове", - "GameUpdateWindowHeading": "{0} Доступні оновлення для {1} ({2})", - "SettingsTabHotkeysResScaleUpHotkey": "Збільшити роздільність:", - "SettingsTabHotkeysResScaleDownHotkey": "Зменшити роздільність:", - "UserProfilesName": "Імʼя", - "UserProfilesUserId": "ID користувача:", - "SettingsTabGraphicsBackend": "Графічний сервер", - "SettingsTabGraphicsBackendTooltip": "Виберіть backend графіки, що буде використовуватись в емуляторі.\n\n\"Vulkan\" краще для всіх сучасних відеокарт, якщо драйвери вчасно оновлюються. У Vulkan також швидше компілюються шейдери (менше \"заїкання\" зображення) на відеокартах всіх компаній.\n\n\"OpenGL\" може дати кращі результати на старих відеокартах Nvidia, старих відеокартах AMD на Linux, або на відеокартах з маленькою кількістю VRAM, але \"заїкання\" через компіляцію шейдерів будуть частіші.\n\nЯкщо не впевнені, встановіть на \"Vulkan\". Встановіть на \"OpenGL\", якщо Ваша відеокарта не підтримує Vulkan навіть на останніх драйверах.", - "SettingsEnableTextureRecompression": "Увімкнути рекомпресію текстури", - "SettingsEnableTextureRecompressionTooltip": "Стискає текстури ASTC, щоб зменшити використання VRAM.\n\nЦим форматом текстур користуються такі ігри, як Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder і The Legend of Zelda: Tears of the Kingdom.\n\nЦі ігри, скоріше всього крашнуться на відеокартах з розміром VRAM в 4 Гб і менше.\n\nВмикайте тільки якщо у Вас закінчується VRAM на цих іграх. Залиште на \"Вимкнути\", якщо не впевнені.", - "SettingsTabGraphicsPreferredGpu": "Бажаний GPU", - "SettingsTabGraphicsPreferredGpuTooltip": "Виберіть відеокарту, яка використовуватиметься з графічним сервером Vulkan.\n\nНе впливає на графічний процесор, який використовуватиме OpenGL.\n\nВстановіть графічний процесор, позначений як «dGPU», якщо не впевнені. Якщо такого немає, не чіпайте.", - "SettingsAppRequiredRestartMessage": "Необхідно перезапустити Ryujinx", - "SettingsGpuBackendRestartMessage": "Налаштування графічного сервера або GPU було змінено. Для цього знадобиться перезапуск", - "SettingsGpuBackendRestartSubMessage": "Бажаєте перезапустити зараз?", - "RyujinxUpdaterMessage": "Бажаєте оновити Ryujinx до останньої версії?", - "SettingsTabHotkeysVolumeUpHotkey": "Збільшити гучність:", - "SettingsTabHotkeysVolumeDownHotkey": "Зменшити гучність:", - "SettingsEnableMacroHLE": "Увімкнути макрос HLE", - "SettingsEnableMacroHLETooltip": "Високорівнева емуляція коду макросу GPU.\n\nПокращує продуктивність, але може викликати графічні збої в деяких іграх.\n\nЗалиште увімкненим, якщо не впевнені.", - "SettingsEnableColorSpacePassthrough": "Наскрізний колірний простір", - "SettingsEnableColorSpacePassthroughTooltip": "Дозволяє серверу Vulkan передавати інформацію про колір без вказівки колірного простору. Для користувачів з екранами з широкою гамою це може призвести до більш яскравих кольорів, але шляхом втрати коректності передачі кольору.", - "VolumeShort": "Гуч.", - "UserProfilesManageSaves": "Керувати збереженнями", - "DeleteUserSave": "Ви хочете видалити збереження користувача для цієї гри?", - "IrreversibleActionNote": "Цю дію не можна скасувати.", - "SaveManagerHeading": "Керувати збереженнями для {0}", - "SaveManagerTitle": "Менеджер збереження", - "Name": "Назва", - "Size": "Розмір", - "Search": "Пошук", - "UserProfilesRecoverLostAccounts": "Відновлення втрачених облікових записів", - "Recover": "Відновити", - "UserProfilesRecoverHeading": "Знайдено збереження для наступних облікових записів", - "UserProfilesRecoverEmptyList": "Немає профілів для відновлення", - "GraphicsAATooltip": "Застосовує згладження до рендера гри.\n\nFXAA розмиє більшість зображення, а SMAA спробує знайти нерівні краї та згладити їх.\n\nНе рекомендується використовувати разом з фільтром масштабування FSR.\n\nЦю опцію можна міняти коли гра запущена кліком на \"Застосувати; ви можете відсунути вікно налаштувань і поекспериментувати з видом гри.\n\nЗалиште на \"Немає\", якщо не впевнені.", - "GraphicsAALabel": "Згладжування:", - "GraphicsScalingFilterLabel": "Фільтр масштабування:", - "GraphicsScalingFilterTooltip": "Виберіть фільтр масштабування, що використається при збільшенні роздільної здатності.\n\n\"Білінійний\" добре виглядає в 3D іграх, і хороше налаштування за умовчуванням.\n\n\"Найближчий\" рекомендується для ігор з піксель-артом.\n\n\"FSR 1.0\" - це просто фільтр різкості, не рекомендується використовувати разом з FXAA або SMAA.\n\nЦю опцію можна міняти коли гра запущена кліком на \"Застосувати; ви можете відсунути вікно налаштувань і поекспериментувати з видом гри.\n\nЗалиште на \"Білінійний\", якщо не впевнені.", - "GraphicsScalingFilterBilinear": "Білінійний", - "GraphicsScalingFilterNearest": "Найближчий", - "GraphicsScalingFilterFsr": "FSR", - "GraphicsScalingFilterLevelLabel": "Рівень", - "GraphicsScalingFilterLevelTooltip": "Встановити рівень різкості в FSR 1.0. Чим вище - тим різкіше.", - "SmaaLow": "SMAA Низький", - "SmaaMedium": "SMAA Середній", - "SmaaHigh": "SMAA Високий", - "SmaaUltra": "SMAA Ультра", - "UserEditorTitle": "Редагувати користувача", - "UserEditorTitleCreate": "Створити користувача", - "SettingsTabNetworkInterface": "Мережевий інтерфейс:", - "NetworkInterfaceTooltip": "Мережевий інтерфейс, що використовується для LAN/LDN.\n\nРазом з VPN або XLink Kai, і грою що підтримує LAN, може імітувати з'єднання в однаковій мережі через Інтернет.", - "NetworkInterfaceDefault": "Стандартний", - "PackagingShaders": "Пакування шейдерів", - "AboutChangelogButton": "Переглянути журнал змін на GitHub", - "AboutChangelogButtonTooltipMessage": "Клацніть, щоб відкрити журнал змін для цієї версії у стандартному браузері.", - "SettingsTabNetworkMultiplayer": "Мережева гра", - "MultiplayerMode": "Режим:", - "MultiplayerModeTooltip": "Змінити LDN мультиплеєру.\n\nLdnMitm змінить функціонал бездротової/локальної гри в іграх, щоб вони працювали так, ніби це LAN, що дозволяє локальні підключення в тій самій мережі з іншими екземплярами Ryujinx та хакнутими консолями Nintendo Switch, які мають встановлений модуль ldn_mitm.\n\nМультиплеєр вимагає, щоб усі гравці були на одній і тій же версії гри (наприклад Super Smash Bros. Ultimate v13.0.1 не зможе під'єднатися до v13.0.0).\n\nЗалиште на \"Вимкнено\", якщо не впевнені, ", - "MultiplayerModeDisabled": "Вимкнено", - "MultiplayerModeLdnMitm": "ldn_mitm" -} diff --git a/src/Ryujinx/Assets/Locales/zh_CN.json b/src/Ryujinx/Assets/Locales/zh_CN.json deleted file mode 100644 index 66f59ecd0..000000000 --- a/src/Ryujinx/Assets/Locales/zh_CN.json +++ /dev/null @@ -1,780 +0,0 @@ -{ - "Language": "简体中文", - "MenuBarFileOpenApplet": "打开小程序", - "MenuBarFileOpenAppletOpenMiiAppletToolTip": "打开独立的 Mii 小程序", - "SettingsTabInputDirectMouseAccess": "直通鼠标操作", - "SettingsTabSystemMemoryManagerMode": "内存管理模式:", - "SettingsTabSystemMemoryManagerModeSoftware": "软件管理", - "SettingsTabSystemMemoryManagerModeHost": "本机映射 (较快)", - "SettingsTabSystemMemoryManagerModeHostUnchecked": "跳过检查的本机映射 (最快,但不安全)", - "SettingsTabSystemUseHypervisor": "使用 Hypervisor 虚拟化", - "MenuBarFile": "文件(_F)", - "MenuBarFileOpenFromFile": "加载游戏文件(_L)", - "MenuBarFileOpenUnpacked": "加载解包后的游戏(_U)", - "MenuBarFileOpenEmuFolder": "打开 Ryujinx 系统目录", - "MenuBarFileOpenLogsFolder": "打开日志目录", - "MenuBarFileExit": "退出(_E)", - "MenuBarOptions": "选项(_O)", - "MenuBarOptionsToggleFullscreen": "切换全屏", - "MenuBarOptionsStartGamesInFullscreen": "全屏模式启动游戏", - "MenuBarOptionsStopEmulation": "停止模拟", - "MenuBarOptionsSettings": "设置(_S)", - "MenuBarOptionsManageUserProfiles": "管理用户账户(_M)", - "MenuBarActions": "操作(_A)", - "MenuBarOptionsSimulateWakeUpMessage": "模拟唤醒消息", - "MenuBarActionsScanAmiibo": "扫描 Amiibo", - "MenuBarTools": "工具(_T)", - "MenuBarToolsInstallFirmware": "安装系统固件", - "MenuBarFileToolsInstallFirmwareFromFile": "从 XCI 或 ZIP 文件中安装系统固件", - "MenuBarFileToolsInstallFirmwareFromDirectory": "从文件夹中安装系统固件", - "MenuBarToolsManageFileTypes": "管理文件扩展名", - "MenuBarToolsInstallFileTypes": "关联文件扩展名", - "MenuBarToolsUninstallFileTypes": "取消关联扩展名", - "MenuBarView": "视图(_V)", - "MenuBarViewWindow": "窗口大小", - "MenuBarViewWindow720": "720p", - "MenuBarViewWindow1080": "1080p", - "MenuBarHelp": "帮助(_H)", - "MenuBarHelpCheckForUpdates": "检查更新", - "MenuBarHelpAbout": "关于", - "MenuSearch": "搜索…", - "GameListHeaderFavorite": "收藏", - "GameListHeaderIcon": "图标", - "GameListHeaderApplication": "名称", - "GameListHeaderDeveloper": "制作商", - "GameListHeaderVersion": "版本", - "GameListHeaderTimePlayed": "游玩时长", - "GameListHeaderLastPlayed": "最近游玩", - "GameListHeaderFileExtension": "扩展名", - "GameListHeaderFileSize": "大小", - "GameListHeaderPath": "路径", - "GameListContextMenuOpenUserSaveDirectory": "打开用户存档目录", - "GameListContextMenuOpenUserSaveDirectoryToolTip": "打开储存游戏用户存档的目录", - "GameListContextMenuOpenDeviceSaveDirectory": "打开系统数据目录", - "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "打开储存游戏系统数据的目录", - "GameListContextMenuOpenBcatSaveDirectory": "打开 BCAT 数据目录", - "GameListContextMenuOpenBcatSaveDirectoryToolTip": "打开储存游戏 BCAT 数据的目录", - "GameListContextMenuManageTitleUpdates": "管理游戏更新", - "GameListContextMenuManageTitleUpdatesToolTip": "打开游戏更新管理窗口", - "GameListContextMenuManageDlc": "管理 DLC", - "GameListContextMenuManageDlcToolTip": "打开 DLC 管理窗口", - "GameListContextMenuCacheManagement": "缓存管理", - "GameListContextMenuCacheManagementPurgePptc": "清除 PPTC 缓存文件", - "GameListContextMenuCacheManagementPurgePptcToolTip": "删除游戏的 PPTC 缓存文件,下次启动游戏时重新编译生成 PPTC 缓存文件", - "GameListContextMenuCacheManagementPurgeShaderCache": "清除着色器缓存文件", - "GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "删除游戏的着色器缓存文件,下次启动游戏时重新生成着色器缓存文件", - "GameListContextMenuCacheManagementOpenPptcDirectory": "打开 PPTC 缓存目录", - "GameListContextMenuCacheManagementOpenPptcDirectoryToolTip": "打开储存游戏 PPTC 缓存文件的目录", - "GameListContextMenuCacheManagementOpenShaderCacheDirectory": "打开着色器缓存目录", - "GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip": "打开储存游戏着色器缓存文件的目录", - "GameListContextMenuExtractData": "提取数据", - "GameListContextMenuExtractDataExeFS": "ExeFS", - "GameListContextMenuExtractDataExeFSToolTip": "从游戏的当前状态中提取 ExeFS 分区 (包括更新)", - "GameListContextMenuExtractDataRomFS": "RomFS", - "GameListContextMenuExtractDataRomFSToolTip": "从游戏的当前状态中提取 RomFS 分区 (包括更新)", - "GameListContextMenuExtractDataLogo": "图标", - "GameListContextMenuExtractDataLogoToolTip": "从游戏的当前状态中提取图标 (包括更新)", - "GameListContextMenuCreateShortcut": "创建游戏快捷方式", - "GameListContextMenuCreateShortcutToolTip": "创建一个直接启动此游戏的桌面快捷方式", - "GameListContextMenuCreateShortcutToolTipMacOS": "在 macOS 的应用程序目录中创建一个直接启动此游戏的快捷方式", - "GameListContextMenuOpenModsDirectory": "打开 MOD 目录", - "GameListContextMenuOpenModsDirectoryToolTip": "打开存放游戏 MOD 的目录", - "GameListContextMenuOpenSdModsDirectory": "打开大气层系统 MOD 目录", - "GameListContextMenuOpenSdModsDirectoryToolTip": "打开存放适用于大气层系统的游戏 MOD 的目录,对于为真实硬件打包的 MOD 非常有用", - "StatusBarGamesLoaded": "{0}/{1} 游戏加载完成", - "StatusBarSystemVersion": "系统固件版本:{0}", - "LinuxVmMaxMapCountDialogTitle": "检测到操作系统内存映射最大数量被设置的过低", - "LinuxVmMaxMapCountDialogTextPrimary": "你想要将操作系统 vm.max_map_count 的值增加到 {0} 吗", - "LinuxVmMaxMapCountDialogTextSecondary": "有些游戏可能会尝试创建超过当前系统允许的内存映射最大数量,若超过当前最大数量,Ryujinx 模拟器将会闪退。", - "LinuxVmMaxMapCountDialogButtonUntilRestart": "确定,临时保存(重启后失效)", - "LinuxVmMaxMapCountDialogButtonPersistent": "确定,永久保存", - "LinuxVmMaxMapCountWarningTextPrimary": "内存映射的最大数量低于推荐值。", - "LinuxVmMaxMapCountWarningTextSecondary": "vm.max_map_count ({0}) 的当前值小于 {1}。 有些游戏可能会尝试创建超过当前系统允许的内存映射最大数量,若超过当前最大数量,Ryujinx 模拟器将会闪退。\n\n你可以手动增加内存映射最大数量,或者安装 pkexec,它可以辅助 Ryujinx 完成内存映射最大数量的修改操作。", - "Settings": "设置", - "SettingsTabGeneral": "用户界面", - "SettingsTabGeneralGeneral": "常规", - "SettingsTabGeneralEnableDiscordRichPresence": "启用 Discord 在线状态展示", - "SettingsTabGeneralCheckUpdatesOnLaunch": "启动时检查更新", - "SettingsTabGeneralShowConfirmExitDialog": "退出游戏时需要确认", - "SettingsTabGeneralRememberWindowState": "记住窗口大小和位置", - "SettingsTabGeneralHideCursor": "隐藏鼠标指针:", - "SettingsTabGeneralHideCursorNever": "从不隐藏", - "SettingsTabGeneralHideCursorOnIdle": "自动隐藏", - "SettingsTabGeneralHideCursorAlways": "始终隐藏", - "SettingsTabGeneralGameDirectories": "游戏目录", - "SettingsTabGeneralAdd": "添加", - "SettingsTabGeneralRemove": "删除", - "SettingsTabSystem": "系统", - "SettingsTabSystemCore": "核心", - "SettingsTabSystemSystemRegion": "系统区域:", - "SettingsTabSystemSystemRegionJapan": "日本", - "SettingsTabSystemSystemRegionUSA": "美国", - "SettingsTabSystemSystemRegionEurope": "欧洲", - "SettingsTabSystemSystemRegionAustralia": "澳大利亚", - "SettingsTabSystemSystemRegionChina": "中国", - "SettingsTabSystemSystemRegionKorea": "韩国", - "SettingsTabSystemSystemRegionTaiwan": "台湾地区", - "SettingsTabSystemSystemLanguage": "系统语言:", - "SettingsTabSystemSystemLanguageJapanese": "日语", - "SettingsTabSystemSystemLanguageAmericanEnglish": "英语(美国)", - "SettingsTabSystemSystemLanguageFrench": "法语", - "SettingsTabSystemSystemLanguageGerman": "德语", - "SettingsTabSystemSystemLanguageItalian": "意大利语", - "SettingsTabSystemSystemLanguageSpanish": "西班牙语", - "SettingsTabSystemSystemLanguageChinese": "中文(简体)——无效", - "SettingsTabSystemSystemLanguageKorean": "韩语", - "SettingsTabSystemSystemLanguageDutch": "荷兰语", - "SettingsTabSystemSystemLanguagePortuguese": "葡萄牙语", - "SettingsTabSystemSystemLanguageRussian": "俄语", - "SettingsTabSystemSystemLanguageTaiwanese": "中文(繁体)——无效", - "SettingsTabSystemSystemLanguageBritishEnglish": "英语(英国)", - "SettingsTabSystemSystemLanguageCanadianFrench": "加拿大法语", - "SettingsTabSystemSystemLanguageLatinAmericanSpanish": "拉美西班牙语", - "SettingsTabSystemSystemLanguageSimplifiedChinese": "简体中文(推荐)", - "SettingsTabSystemSystemLanguageTraditionalChinese": "繁体中文(推荐)", - "SettingsTabSystemSystemTimeZone": "系统时区:", - "SettingsTabSystemSystemTime": "系统时钟:", - "SettingsTabSystemEnableVsync": "启用垂直同步", - "SettingsTabSystemEnablePptc": "开启 PPTC 缓存", - "SettingsTabSystemEnableFsIntegrityChecks": "启用文件系统完整性检查", - "SettingsTabSystemAudioBackend": "音频处理引擎:", - "SettingsTabSystemAudioBackendDummy": "无", - "SettingsTabSystemAudioBackendOpenAL": "OpenAL", - "SettingsTabSystemAudioBackendSoundIO": "SoundIO", - "SettingsTabSystemAudioBackendSDL2": "SDL2", - "SettingsTabSystemHacks": "修改", - "SettingsTabSystemHacksNote": "会导致模拟器不稳定", - "SettingsTabSystemExpandDramSize": "使用开发机的内存布局(开发人员使用)", - "SettingsTabSystemIgnoreMissingServices": "忽略缺失的服务", - "SettingsTabGraphics": "图形", - "SettingsTabGraphicsAPI": "图形 API", - "SettingsTabGraphicsEnableShaderCache": "启用着色器缓存", - "SettingsTabGraphicsAnisotropicFiltering": "各向异性过滤:", - "SettingsTabGraphicsAnisotropicFilteringAuto": "自动", - "SettingsTabGraphicsAnisotropicFiltering2x": "2x", - "SettingsTabGraphicsAnisotropicFiltering4x": "4x", - "SettingsTabGraphicsAnisotropicFiltering8x": "8x", - "SettingsTabGraphicsAnisotropicFiltering16x": "16x", - "SettingsTabGraphicsResolutionScale": "分辨率缩放:", - "SettingsTabGraphicsResolutionScaleCustom": "自定义(不推荐)", - "SettingsTabGraphicsResolutionScaleNative": "原生 (720p/1080p)", - "SettingsTabGraphicsResolutionScale2x": "2 倍 (1440p/2160p)", - "SettingsTabGraphicsResolutionScale3x": "3 倍 (2160p/3240p)", - "SettingsTabGraphicsResolutionScale4x": "4 倍 (2880p/4320p) (不推荐)", - "SettingsTabGraphicsAspectRatio": "宽高比:", - "SettingsTabGraphicsAspectRatio4x3": "4:3", - "SettingsTabGraphicsAspectRatio16x9": "16:9", - "SettingsTabGraphicsAspectRatio16x10": "16:10", - "SettingsTabGraphicsAspectRatio21x9": "21:9", - "SettingsTabGraphicsAspectRatio32x9": "32:9", - "SettingsTabGraphicsAspectRatioStretch": "拉伸以适应窗口", - "SettingsTabGraphicsDeveloperOptions": "开发者选项", - "SettingsTabGraphicsShaderDumpPath": "图形着色器转储路径:", - "SettingsTabLogging": "日志", - "SettingsTabLoggingLogging": "日志", - "SettingsTabLoggingEnableLoggingToFile": "将日志写入文件", - "SettingsTabLoggingEnableStubLogs": "启用存根日志", - "SettingsTabLoggingEnableInfoLogs": "启用信息日志", - "SettingsTabLoggingEnableWarningLogs": "启用警告日志", - "SettingsTabLoggingEnableErrorLogs": "启用错误日志", - "SettingsTabLoggingEnableTraceLogs": "启用跟踪日志", - "SettingsTabLoggingEnableGuestLogs": "启用访客日志", - "SettingsTabLoggingEnableFsAccessLogs": "启用文件访问日志", - "SettingsTabLoggingFsGlobalAccessLogMode": "文件系统全局访问日志模式:", - "SettingsTabLoggingDeveloperOptions": "开发者选项", - "SettingsTabLoggingDeveloperOptionsNote": "警告:会降低模拟器性能", - "SettingsTabLoggingGraphicsBackendLogLevel": "图形引擎日志级别:", - "SettingsTabLoggingGraphicsBackendLogLevelNone": "无", - "SettingsTabLoggingGraphicsBackendLogLevelError": "错误", - "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "减速", - "SettingsTabLoggingGraphicsBackendLogLevelAll": "全部", - "SettingsTabLoggingEnableDebugLogs": "启用调试日志", - "SettingsTabInput": "输入", - "SettingsTabInputEnableDockedMode": "主机模式", - "SettingsTabInputDirectKeyboardAccess": "直通键盘控制", - "SettingsButtonSave": "保存", - "SettingsButtonClose": "关闭", - "SettingsButtonOk": "确定", - "SettingsButtonCancel": "取消", - "SettingsButtonApply": "应用", - "ControllerSettingsPlayer": "玩家", - "ControllerSettingsPlayer1": "玩家 1", - "ControllerSettingsPlayer2": "玩家 2", - "ControllerSettingsPlayer3": "玩家 3", - "ControllerSettingsPlayer4": "玩家 4", - "ControllerSettingsPlayer5": "玩家 5", - "ControllerSettingsPlayer6": "玩家 6", - "ControllerSettingsPlayer7": "玩家 7", - "ControllerSettingsPlayer8": "玩家 8", - "ControllerSettingsHandheld": "掌机模式", - "ControllerSettingsInputDevice": "输入设备", - "ControllerSettingsRefresh": "刷新", - "ControllerSettingsDeviceDisabled": "禁用", - "ControllerSettingsControllerType": "手柄类型", - "ControllerSettingsControllerTypeHandheld": "掌机", - "ControllerSettingsControllerTypeProController": "Pro 手柄", - "ControllerSettingsControllerTypeJoyConPair": "双 JoyCon 手柄", - "ControllerSettingsControllerTypeJoyConLeft": "左 JoyCon 手柄", - "ControllerSettingsControllerTypeJoyConRight": "右 JoyCon 手柄", - "ControllerSettingsProfile": "配置文件", - "ControllerSettingsProfileDefault": "默认设置", - "ControllerSettingsLoad": "加载", - "ControllerSettingsAdd": "新建", - "ControllerSettingsRemove": "删除", - "ControllerSettingsButtons": "基础按键", - "ControllerSettingsButtonA": "A", - "ControllerSettingsButtonB": "B", - "ControllerSettingsButtonX": "X", - "ControllerSettingsButtonY": "Y", - "ControllerSettingsButtonPlus": "+", - "ControllerSettingsButtonMinus": "-", - "ControllerSettingsDPad": "方向键", - "ControllerSettingsDPadUp": "上", - "ControllerSettingsDPadDown": "下", - "ControllerSettingsDPadLeft": "左", - "ControllerSettingsDPadRight": "右", - "ControllerSettingsStickButton": "按下摇杆", - "ControllerSettingsStickUp": "上", - "ControllerSettingsStickDown": "下", - "ControllerSettingsStickLeft": "左", - "ControllerSettingsStickRight": "右", - "ControllerSettingsStickStick": "摇杆", - "ControllerSettingsStickInvertXAxis": "摇杆左右反转", - "ControllerSettingsStickInvertYAxis": "摇杆上下反转", - "ControllerSettingsStickDeadzone": "死区:", - "ControllerSettingsLStick": "左摇杆", - "ControllerSettingsRStick": "右摇杆", - "ControllerSettingsTriggersLeft": "左扳机", - "ControllerSettingsTriggersRight": "右扳机", - "ControllerSettingsTriggersButtonsLeft": "左扳机键", - "ControllerSettingsTriggersButtonsRight": "右扳机键", - "ControllerSettingsTriggers": "扳机", - "ControllerSettingsTriggerL": "L", - "ControllerSettingsTriggerR": "R", - "ControllerSettingsTriggerZL": "ZL", - "ControllerSettingsTriggerZR": "ZR", - "ControllerSettingsLeftSL": "SL", - "ControllerSettingsLeftSR": "SR", - "ControllerSettingsRightSL": "SL", - "ControllerSettingsRightSR": "SR", - "ControllerSettingsExtraButtonsLeft": "左背键", - "ControllerSettingsExtraButtonsRight": "右背键", - "ControllerSettingsMisc": "其他", - "ControllerSettingsTriggerThreshold": "扳机阈值:", - "ControllerSettingsMotion": "体感", - "ControllerSettingsMotionUseCemuhookCompatibleMotion": "使用 CemuHook 兼容的体感协议", - "ControllerSettingsMotionControllerSlot": "手柄槽位:", - "ControllerSettingsMotionMirrorInput": "镜像操作", - "ControllerSettingsMotionRightJoyConSlot": "右 JoyCon 槽位:", - "ControllerSettingsMotionServerHost": "服务器地址:", - "ControllerSettingsMotionGyroSensitivity": "陀螺仪敏感度:", - "ControllerSettingsMotionGyroDeadzone": "陀螺仪死区:", - "ControllerSettingsSave": "保存", - "ControllerSettingsClose": "关闭", - "KeyUnknown": "未知", - "KeyShiftLeft": "左侧Shift", - "KeyShiftRight": "右侧Shift", - "KeyControlLeft": "左侧Ctrl", - "KeyMacControlLeft": "左侧⌃", - "KeyControlRight": "右侧Ctrl", - "KeyMacControlRight": "右侧⌃", - "KeyAltLeft": "左侧Alt", - "KeyMacAltLeft": "左侧⌥", - "KeyAltRight": "右侧Alt", - "KeyMacAltRight": "右侧⌥", - "KeyWinLeft": "左侧⊞", - "KeyMacWinLeft": "左侧⌘", - "KeyWinRight": "右侧⊞", - "KeyMacWinRight": "右侧⌘", - "KeyMenu": "菜单键", - "KeyUp": "上", - "KeyDown": "下", - "KeyLeft": "左", - "KeyRight": "右", - "KeyEnter": "回车键", - "KeyEscape": "Esc", - "KeySpace": "空格键", - "KeyTab": "Tab", - "KeyBackSpace": "退格键", - "KeyInsert": "Insert", - "KeyDelete": "Delete", - "KeyPageUp": "Page Up", - "KeyPageDown": "Page Down", - "KeyHome": "Home", - "KeyEnd": "End", - "KeyCapsLock": "Caps Lock", - "KeyScrollLock": "Scroll Lock", - "KeyPrintScreen": "Print Screen", - "KeyPause": "Pause", - "KeyNumLock": "Num Lock", - "KeyClear": "清除键", - "KeyKeypad0": "小键盘0", - "KeyKeypad1": "小键盘1", - "KeyKeypad2": "小键盘2", - "KeyKeypad3": "小键盘3", - "KeyKeypad4": "小键盘4", - "KeyKeypad5": "小键盘5", - "KeyKeypad6": "小键盘6", - "KeyKeypad7": "小键盘7", - "KeyKeypad8": "小键盘8", - "KeyKeypad9": "小键盘9", - "KeyKeypadDivide": "小键盘/", - "KeyKeypadMultiply": "小键盘*", - "KeyKeypadSubtract": "小键盘-", - "KeyKeypadAdd": "小键盘+", - "KeyKeypadDecimal": "小键盘.", - "KeyKeypadEnter": "小键盘回车键", - "KeyNumber0": "0", - "KeyNumber1": "1", - "KeyNumber2": "2", - "KeyNumber3": "3", - "KeyNumber4": "4", - "KeyNumber5": "5", - "KeyNumber6": "6", - "KeyNumber7": "7", - "KeyNumber8": "8", - "KeyNumber9": "9", - "KeyTilde": "~", - "KeyGrave": "`", - "KeyMinus": "-", - "KeyPlus": "+", - "KeyBracketLeft": "[", - "KeyBracketRight": "]", - "KeySemicolon": ";", - "KeyQuote": "\"", - "KeyComma": ",", - "KeyPeriod": ".", - "KeySlash": "/", - "KeyBackSlash": "\\", - "KeyUnbound": "未分配", - "GamepadLeftStick": "左摇杆按键", - "GamepadRightStick": "右摇杆按键", - "GamepadLeftShoulder": "左肩键L", - "GamepadRightShoulder": "右肩键R", - "GamepadLeftTrigger": "左扳机键ZL", - "GamepadRightTrigger": "右扳机键ZR", - "GamepadDpadUp": "上键", - "GamepadDpadDown": "下键", - "GamepadDpadLeft": "左键", - "GamepadDpadRight": "右键", - "GamepadMinus": "-键", - "GamepadPlus": "+键", - "GamepadGuide": "主页键", - "GamepadMisc1": "截图键", - "GamepadPaddle1": "其他按键1", - "GamepadPaddle2": "其他按键2", - "GamepadPaddle3": "其他按键3", - "GamepadPaddle4": "其他按键4", - "GamepadTouchpad": "触摸板", - "GamepadSingleLeftTrigger0": "左扳机0", - "GamepadSingleRightTrigger0": "右扳机0", - "GamepadSingleLeftTrigger1": "左扳机1", - "GamepadSingleRightTrigger1": "右扳机1", - "StickLeft": "左摇杆", - "StickRight": "右摇杆", - "UserProfilesSelectedUserProfile": "选定的用户账户:", - "UserProfilesSaveProfileName": "保存名称", - "UserProfilesChangeProfileImage": "更换头像", - "UserProfilesAvailableUserProfiles": "现有用户账户:", - "UserProfilesAddNewProfile": "新建账户", - "UserProfilesDelete": "删除", - "UserProfilesClose": "关闭", - "ProfileNameSelectionWatermark": "输入昵称", - "ProfileImageSelectionTitle": "选择头像", - "ProfileImageSelectionHeader": "选择合适的头像图片", - "ProfileImageSelectionNote": "您可以导入自定义头像,或从模拟器系统固件中选择预设头像", - "ProfileImageSelectionImportImage": "导入图像文件", - "ProfileImageSelectionSelectAvatar": "选择预设头像", - "InputDialogTitle": "输入对话框", - "InputDialogOk": "完成", - "InputDialogCancel": "取消", - "InputDialogAddNewProfileTitle": "选择用户名称", - "InputDialogAddNewProfileHeader": "请输入账户名称", - "InputDialogAddNewProfileSubtext": "(最大长度:{0})", - "AvatarChoose": "保存选定头像", - "AvatarSetBackgroundColor": "设置背景色", - "AvatarClose": "关闭", - "ControllerSettingsLoadProfileToolTip": "加载配置文件", - "ControllerSettingsAddProfileToolTip": "新增配置文件", - "ControllerSettingsRemoveProfileToolTip": "删除配置文件", - "ControllerSettingsSaveProfileToolTip": "保存配置文件", - "MenuBarFileToolsTakeScreenshot": "保存截屏", - "MenuBarFileToolsHideUi": "隐藏菜单栏和状态栏", - "GameListContextMenuRunApplication": "启动游戏", - "GameListContextMenuToggleFavorite": "收藏", - "GameListContextMenuToggleFavoriteToolTip": "切换游戏的收藏状态", - "SettingsTabGeneralTheme": "主题:", - "SettingsTabGeneralThemeDark": "深色(暗黑)", - "SettingsTabGeneralThemeLight": "浅色(亮色)", - "ControllerSettingsConfigureGeneral": "配置", - "ControllerSettingsRumble": "震动", - "ControllerSettingsRumbleStrongMultiplier": "强震动幅度", - "ControllerSettingsRumbleWeakMultiplier": "弱震动幅度", - "DialogMessageSaveNotAvailableMessage": "没有{0} [{1:x16}]的游戏存档", - "DialogMessageSaveNotAvailableCreateSaveMessage": "是否创建该游戏的存档?", - "DialogConfirmationTitle": "Ryujinx - 确认", - "DialogUpdaterTitle": "Ryujinx - 更新", - "DialogErrorTitle": "Ryujinx - 错误", - "DialogWarningTitle": "Ryujinx - 警告", - "DialogExitTitle": "Ryujinx - 退出", - "DialogErrorMessage": "Ryujinx 模拟器发生错误", - "DialogExitMessage": "是否关闭 Ryujinx 模拟器?", - "DialogExitSubMessage": "未保存的进度将会丢失!", - "DialogMessageCreateSaveErrorMessage": "创建指定存档时出错:{0}", - "DialogMessageFindSaveErrorMessage": "查找指定存档时出错:{0}", - "FolderDialogExtractTitle": "选择要提取到的文件夹", - "DialogNcaExtractionMessage": "提取 {1} 的 {0} 分区...", - "DialogNcaExtractionTitle": "Ryujinx - NCA 分区提取", - "DialogNcaExtractionMainNcaNotFoundErrorMessage": "提取失败,所选文件中没有 NCA 文件", - "DialogNcaExtractionCheckLogErrorMessage": "提取失败,请查看日志文件获取详情", - "DialogNcaExtractionSuccessMessage": "提取成功!", - "DialogUpdaterConvertFailedMessage": "无法切换当前 Ryujinx 版本。", - "DialogUpdaterCancelUpdateMessage": "取消更新!", - "DialogUpdaterAlreadyOnLatestVersionMessage": "您使用的 Ryujinx 模拟器是最新版本。", - "DialogUpdaterFailedToGetVersionMessage": "尝试从 Github 获取版本信息时无效,可能由于 GitHub Actions 正在编译新版本。\n请过一会再试。", - "DialogUpdaterConvertFailedGithubMessage": "无法切换至从 Github 接收到的新版 Ryujinx 模拟器。", - "DialogUpdaterDownloadingMessage": "下载更新中...", - "DialogUpdaterExtractionMessage": "正在提取更新...", - "DialogUpdaterRenamingMessage": "正在重命名更新...", - "DialogUpdaterAddingFilesMessage": "安装更新中...", - "DialogUpdaterCompleteMessage": "更新成功!", - "DialogUpdaterRestartMessage": "是否立即重启 Ryujinx 模拟器?", - "DialogUpdaterNoInternetMessage": "没有连接到网络", - "DialogUpdaterNoInternetSubMessage": "请确保互联网连接正常。", - "DialogUpdaterDirtyBuildMessage": "无法更新非官方版本的 Ryujinx 模拟器!", - "DialogUpdaterDirtyBuildSubMessage": "如果想使用受支持的版本,请您在 https://ryujinx.org/ 下载官方版本。", - "DialogRestartRequiredMessage": "需要重启模拟器", - "DialogThemeRestartMessage": "主题设置已保存,需要重启模拟器才能生效。", - "DialogThemeRestartSubMessage": "是否要重启模拟器?", - "DialogFirmwareInstallEmbeddedMessage": "要安装游戏文件中内嵌的系统固件吗?(固件版本 {0})", - "DialogFirmwareInstallEmbeddedSuccessMessage": "Ryujinx 模拟器已经从当前游戏文件中安装了系统固件 {0} 。\n模拟器现在可以正常运行了。", - "DialogFirmwareNoFirmwareInstalledMessage": "未安装系统固件", - "DialogFirmwareInstalledMessage": "已安装系统固件 {0}", - "DialogInstallFileTypesSuccessMessage": "关联文件类型成功!", - "DialogInstallFileTypesErrorMessage": "关联文件类型失败!", - "DialogUninstallFileTypesSuccessMessage": "成功解除文件类型关联!", - "DialogUninstallFileTypesErrorMessage": "解除文件类型关联失败!", - "DialogOpenSettingsWindowLabel": "打开设置窗口", - "DialogControllerAppletTitle": "控制器小窗口", - "DialogMessageDialogErrorExceptionMessage": "显示消息对话框时出错:{0}", - "DialogSoftwareKeyboardErrorExceptionMessage": "显示软件键盘时出错:{0}", - "DialogErrorAppletErrorExceptionMessage": "显示错误对话框时出错:{0}", - "DialogUserErrorDialogMessage": "{0}: {1}", - "DialogUserErrorDialogInfoMessage": "\n有关修复此错误的更多信息,可以查看我们的安装指南。", - "DialogUserErrorDialogTitle": "Ryujinx 错误 ({0})", - "DialogAmiiboApiTitle": "Amiibo API", - "DialogAmiiboApiFailFetchMessage": "从 API 获取信息时出错。", - "DialogAmiiboApiConnectErrorMessage": "无法连接到 Amiibo API 服务器,服务可能已关闭,或者没有连接互联网。", - "DialogProfileInvalidProfileErrorMessage": "配置文件 {0} 与当前输入配置系统不兼容。", - "DialogProfileDefaultProfileOverwriteErrorMessage": "不允许覆盖默认配置文件", - "DialogProfileDeleteProfileTitle": "删除配置文件", - "DialogProfileDeleteProfileMessage": "删除后不可恢复,确认删除吗?", - "DialogWarning": "警告", - "DialogPPTCDeletionMessage": "您即将删除:\n\n{0} 的 PPTC 缓存文件\n\n确定吗?", - "DialogPPTCDeletionErrorMessage": "清除 {0} 的 PPTC 缓存文件时出错:{1}", - "DialogShaderDeletionMessage": "您即将删除:\n\n{0} 的着色器缓存文件\n\n确定吗?", - "DialogShaderDeletionErrorMessage": "清除 {0} 的着色器缓存文件时出错:{1}", - "DialogRyujinxErrorMessage": "Ryujinx 模拟器发生错误", - "DialogInvalidTitleIdErrorMessage": "用户界面错误:所选游戏没有有效的游戏 ID", - "DialogFirmwareInstallerFirmwareNotFoundErrorMessage": "在路径 {0} 中找不到有效的 Switch 系统固件。", - "DialogFirmwareInstallerFirmwareInstallTitle": "安装系统固件 {0}", - "DialogFirmwareInstallerFirmwareInstallMessage": "即将安装系统固件版本 {0} 。", - "DialogFirmwareInstallerFirmwareInstallSubMessage": "\n\n替换当前系统固件版本 {0} 。", - "DialogFirmwareInstallerFirmwareInstallConfirmMessage": "\n\n是否继续?", - "DialogFirmwareInstallerFirmwareInstallWaitMessage": "安装系统固件中...", - "DialogFirmwareInstallerFirmwareInstallSuccessMessage": "成功安装系统固件版本 {0} 。", - "DialogUserProfileDeletionWarningMessage": "删除后将没有可用的账户", - "DialogUserProfileDeletionConfirmMessage": "是否删除所选账户", - "DialogUserProfileUnsavedChangesTitle": "警告 - 有未保存的更改", - "DialogUserProfileUnsavedChangesMessage": "您对该账户的更改尚未保存。", - "DialogUserProfileUnsavedChangesSubMessage": "确定要放弃更改吗?", - "DialogControllerSettingsModifiedConfirmMessage": "当前的输入设置已更新", - "DialogControllerSettingsModifiedConfirmSubMessage": "是否保存?", - "DialogLoadFileErrorMessage": "{0}. 错误的文件:{1}", - "DialogModAlreadyExistsMessage": "MOD 已存在", - "DialogModInvalidMessage": "指定的目录找不到 MOD 文件!", - "DialogModDeleteNoParentMessage": "删除失败:找不到 MOD 的父目录“{0}”!", - "DialogDlcNoDlcErrorMessage": "选择的文件不是当前游戏的 DLC!", - "DialogPerformanceCheckLoggingEnabledMessage": "您启用了跟踪日志,该功能仅供开发人员使用。", - "DialogPerformanceCheckLoggingEnabledConfirmMessage": "为了获得最佳性能,建议禁用跟踪日志记录。您是否要立即禁用?", - "DialogPerformanceCheckShaderDumpEnabledMessage": "您启用了着色器转储,该功能仅供开发人员使用。", - "DialogPerformanceCheckShaderDumpEnabledConfirmMessage": "为了获得最佳性能,建议禁用着色器转储。您是否要立即禁用?", - "DialogLoadAppGameAlreadyLoadedMessage": "游戏已经启动", - "DialogLoadAppGameAlreadyLoadedSubMessage": "请停止模拟或关闭模拟器,再启动另一个游戏。", - "DialogUpdateAddUpdateErrorMessage": "选择的文件不是当前游戏的更新!", - "DialogSettingsBackendThreadingWarningTitle": "警告 - 图形引擎多线程", - "DialogSettingsBackendThreadingWarningMessage": "更改此选项后,必须重启 Ryujinx 模拟器才能生效。\n\n当启用图形引擎多线程时,根据显卡不同,您可能需要手动禁用显卡驱动程序自身的多线程(线程优化)。", - "DialogModManagerDeletionWarningMessage": "您即将删除 MOD:{0} \n\n确定吗?", - "DialogModManagerDeletionAllWarningMessage": "您即将删除该游戏的所有 MOD,\n\n确定吗?", - "SettingsTabGraphicsFeaturesOptions": "功能", - "SettingsTabGraphicsBackendMultithreading": "图形引擎多线程:", - "CommonAuto": "自动(推荐)", - "CommonOff": "关闭", - "CommonOn": "打开", - "InputDialogYes": "是", - "InputDialogNo": "否", - "DialogProfileInvalidProfileNameErrorMessage": "文件名包含无效字符,请重试。", - "MenuBarOptionsPauseEmulation": "暂停", - "MenuBarOptionsResumeEmulation": "继续", - "AboutUrlTooltipMessage": "在浏览器中打开 Ryujinx 模拟器官网。", - "AboutDisclaimerMessage": "Ryujinx 与 Nintendo™ 以及其合作伙伴没有任何关联。", - "AboutAmiiboDisclaimerMessage": "我们的 Amiibo 模拟使用了\nAmiiboAPI (www.amiiboapi.com)。", - "AboutPatreonUrlTooltipMessage": "在浏览器中打开 Ryujinx 的 Patreon 赞助页。", - "AboutGithubUrlTooltipMessage": "在浏览器中打开 Ryujinx 的 GitHub 代码库。", - "AboutDiscordUrlTooltipMessage": "在浏览器中打开 Ryujinx 的 Discord 邀请链接。", - "AboutTwitterUrlTooltipMessage": "在浏览器中打开 Ryujinx 的 Twitter 主页。", - "AboutRyujinxAboutTitle": "关于:", - "AboutRyujinxAboutContent": "Ryujinx 是一款 Nintendo Switch™ 模拟器。\n您可以在 Patreon 上赞助 Ryujinx。\n关注 Twitter 或 Discord 可以获取模拟器最新动态。\n如果您对开发感兴趣,欢迎来 GitHub 或 Discord 加入我们!", - "AboutRyujinxMaintainersTitle": "开发维护人员名单:", - "AboutRyujinxMaintainersContentTooltipMessage": "在浏览器中打开贡献者页面", - "AboutRyujinxSupprtersTitle": "感谢 Patreon 上的赞助者:", - "AmiiboSeriesLabel": "Amiibo 系列", - "AmiiboCharacterLabel": "角色", - "AmiiboScanButtonLabel": "扫描", - "AmiiboOptionsShowAllLabel": "显示所有 Amiibo", - "AmiiboOptionsUsRandomTagLabel": "修改:使用随机生成的Amiibo ID", - "DlcManagerTableHeadingEnabledLabel": "已启用", - "DlcManagerTableHeadingTitleIdLabel": "游戏 ID", - "DlcManagerTableHeadingContainerPathLabel": "容器路径", - "DlcManagerTableHeadingFullPathLabel": "完整路径", - "DlcManagerRemoveAllButton": "全部删除", - "DlcManagerEnableAllButton": "全部启用", - "DlcManagerDisableAllButton": "全部停用", - "ModManagerDeleteAllButton": "全部刪除", - "MenuBarOptionsChangeLanguage": "更改界面语言", - "MenuBarShowFileTypes": "主页显示的文件类型", - "CommonSort": "排序", - "CommonShowNames": "显示名称", - "CommonFavorite": "收藏", - "OrderAscending": "升序", - "OrderDescending": "降序", - "SettingsTabGraphicsFeatures": "功能与优化", - "ErrorWindowTitle": "错误窗口", - "ToggleDiscordTooltip": "选择是否在 Discord 中显示您的游玩状态", - "AddGameDirBoxTooltip": "输入要添加的游戏目录", - "AddGameDirTooltip": "添加游戏目录到列表中", - "RemoveGameDirTooltip": "移除选中的目录", - "CustomThemeCheckTooltip": "使用自定义的 Avalonia 主题作为模拟器菜单的外观", - "CustomThemePathTooltip": "自定义主题的目录", - "CustomThemeBrowseTooltip": "查找自定义主题", - "DockModeToggleTooltip": "启用 Switch 的主机模式(电视模式、底座模式),就是模拟 Switch 连接底座的情况;若禁用主机模式,则使用 Switch 的掌机模式,就是模拟手持 Switch 运行游戏的情况。\n对于绝大多数游戏而言,主机模式会比掌机模式,画质更高,同时性能消耗也更高。\n\n简而言之,想要更好画质则启用主机模式;电脑硬件性能不足则禁用主机模式。\n\n如果使用主机模式,请选择“玩家 1”的手柄设置;如果使用掌机模式,请选择“掌机模式”的手柄设置。\n\n如果不确定,请保持开启状态。", - "DirectKeyboardTooltip": "直接键盘访问(HID)支持,游戏可以直接访问键盘作为文本输入设备。\n\n仅适用于在 Switch 硬件上原生支持键盘的游戏。\n\n如果不确定,请保持关闭状态。", - "DirectMouseTooltip": "直接鼠标访问(HID)支持,游戏可以直接访问鼠标作为指针输入设备。\n\n只适用于在 Switch 硬件上原生支持鼠标控制的游戏,这种游戏很少。\n\n启用后,触屏功能可能无法正常工作。\n\n如果不确定,请保持关闭状态。", - "RegionTooltip": "更改系统区域", - "LanguageTooltip": "更改系统语言", - "TimezoneTooltip": "更改系统时区", - "TimeTooltip": "更改系统时间", - "VSyncToggleTooltip": "模拟控制台的垂直同步,开启后会降低大部分游戏的帧率。关闭后,可以获得更高的帧率,但也可能导致游戏画面加载耗时更长或卡住。\n\n在游戏中可以使用热键进行切换(默认为 F1 键)。\n\n如果不确定,请保持开启状态。", - "PptcToggleTooltip": "缓存已编译的游戏指令,这样每次游戏加载时就无需重新编译。\n\n可以减少卡顿和启动时间,提高游戏响应速度。\n\n如果不确定,请保持开启状态。", - "FsIntegrityToggleTooltip": "启动游戏时检查游戏文件的完整性,并在日志中记录损坏的文件。\n\n对性能没有影响,用于排查故障。\n\n如果不确定,请保持开启状态。", - "AudioBackendTooltip": "更改音频处理引擎。\n\n推荐选择“SDL2”,另外“OpenAL”和“SoundIO”可以作为备选,选择“无”将没有声音。\n\n如果不确定,请设置为“SDL2”。", - "MemoryManagerTooltip": "更改模拟器内存映射和访问的方式,对模拟器 CPU 的性能影响很大。\n\n如果不确定,请设置为“跳过检查的本机映射”。", - "MemoryManagerSoftwareTooltip": "使用软件内存页进行内存地址映射,最准确但是速度最慢。", - "MemoryManagerHostTooltip": "直接映射内存页到电脑内存,使得即时编译和执行的效率更高。", - "MemoryManagerUnsafeTooltip": "直接映射内存页到电脑内存,并且不检查内存溢出,使得效率更高,但牺牲了安全。\n游戏程序可以访问模拟器内存的任意地址,所以不安全。\n建议此模式下只运行您信任的游戏程序。", - "UseHypervisorTooltip": "使用 Hypervisor 虚拟机代替即时编译,在可用的情况下能大幅提高性能,但目前可能还不稳定。", - "DRamTooltip": "模拟 Switch 开发机的内存布局。\n\n不会提高性能,某些高清纹理包或 4k 分辨率 MOD 可能需要使用此选项。\n\n如果不确定,请保持关闭状态。", - "IgnoreMissingServicesTooltip": "开启后,游戏会忽略未实现的系统服务,从而继续运行。\n少部分新发布的游戏由于使用了新的未知系统服务,可能需要此选项来避免闪退。\n模拟器更新完善系统服务之后,则无需开启此选项。\n\n如果不确定,请保持关闭状态。", - "GraphicsBackendThreadingTooltip": "在第二个线程上执行图形引擎指令。\n\n可以加速着色器编译,减少卡顿,提高 GPU 的性能。\n\n如果不确定,请设置为“自动”。", - "GalThreadingTooltip": "在第二个线程上执行图形引擎指令。\n\n可以加速着色器编译,减少卡顿,提高 GPU 的性能。\n\n如果不确定,请设置为“自动”。", - "ShaderCacheToggleTooltip": "模拟器将已编译的着色器保存到硬盘,可以减少游戏再次渲染相同图形导致的卡顿。\n\n如果不确定,请保持开启状态。", - "ResolutionScaleTooltip": "将游戏的渲染分辨率乘以一个倍数。\n\n有些游戏可能不适用这项设置,而且即使提高了分辨率仍然看起来像素化;对于这些游戏,您可能需要找到移除抗锯齿或提高内部渲染分辨率的 MOD。当使用这些 MOD 时,建议设置为“原生”。\n\n在游戏运行时,通过点击下面的“应用”按钮可以使设置生效;你可以将设置窗口移开,并试验找到您喜欢的游戏画面效果。\n\n请记住,对于几乎所有人而言,4倍分辨率都是过度的。", - "ResolutionScaleEntryTooltip": "建议设置为整数倍,带小数的分辨率缩放倍数(例如1.5),非整数倍的缩放容易导致问题或闪退。", - "AnisotropyTooltip": "各向异性过滤等级,可以提高倾斜视角纹理的清晰度。\n当设置为“自动”时,使用游戏自身设定的等级。", - "AspectRatioTooltip": "游戏渲染窗口的宽高比。\n\n只有当游戏使用了修改宽高比的 MOD 时才需要修改这个设置,否则图像会被拉伸。\n\n如果不确定,请保持为“16:9”。", - "ShaderDumpPathTooltip": "转储图形着色器的路径", - "FileLogTooltip": "将控制台日志保存到硬盘文件,不影响性能。", - "StubLogTooltip": "在控制台中显示存根日志,不影响性能。", - "InfoLogTooltip": "在控制台中显示信息日志,不影响性能。", - "WarnLogTooltip": "在控制台中显示警告日志,不影响性能。", - "ErrorLogTooltip": "在控制台中显示错误日志,不影响性能。", - "TraceLogTooltip": "在控制台中显示跟踪日志。", - "GuestLogTooltip": "在控制台中显示访客日志,不影响性能。", - "FileAccessLogTooltip": "在控制台中显示文件访问日志。", - "FSAccessLogModeTooltip": "在控制台中显示文件系统访问日志,可选模式为 0-3。", - "DeveloperOptionTooltip": "请谨慎使用", - "OpenGlLogLevel": "需要启用适当的日志级别", - "DebugLogTooltip": "在控制台中显示调试日志。\n\n仅在特别需要时使用此功能,因为它会导致日志信息难以阅读,并降低模拟器性能。", - "LoadApplicationFileTooltip": "选择 Switch 游戏文件并加载", - "LoadApplicationFolderTooltip": "选择解包后的 Switch 游戏目录并加载", - "OpenRyujinxFolderTooltip": "打开 Ryujinx 模拟器系统目录", - "OpenRyujinxLogsTooltip": "打开日志存放的目录", - "ExitTooltip": "退出 Ryujinx 模拟器", - "OpenSettingsTooltip": "打开设置窗口", - "OpenProfileManagerTooltip": "打开用户账户管理窗口", - "StopEmulationTooltip": "停止运行当前游戏,并回到主界面", - "CheckUpdatesTooltip": "检查 Ryujinx 新版本", - "OpenAboutTooltip": "打开关于窗口", - "GridSize": "网格尺寸", - "GridSizeTooltip": "调整网格项目的大小", - "SettingsTabSystemSystemLanguageBrazilianPortuguese": "巴西葡萄牙语", - "AboutRyujinxContributorsButtonHeader": "查看所有贡献者", - "SettingsTabSystemAudioVolume": "音量:", - "AudioVolumeTooltip": "调节音量", - "SettingsTabSystemEnableInternetAccess": "启用网络连接(局域网)", - "EnableInternetAccessTooltip": "允许模拟的游戏程序访问网络。\n\n当多个模拟器或实体 Switch 连接到同一个网络时,带有局域网模式的游戏便可以相互通信。\n\n即使开启此选项也无法访问 Nintendo 服务器,有可能导致某些尝试联网的游戏闪退。\n\n如果不确定,请保持关闭状态。", - "GameListContextMenuManageCheatToolTip": "管理当前游戏的金手指", - "GameListContextMenuManageCheat": "管理金手指", - "GameListContextMenuManageModToolTip": "管理当前游戏的 MOD", - "GameListContextMenuManageMod": "管理 MOD", - "ControllerSettingsStickRange": "范围:", - "DialogStopEmulationTitle": "Ryujinx - 停止模拟", - "DialogStopEmulationMessage": "确定要停止模拟?", - "SettingsTabCpu": "CPU", - "SettingsTabAudio": "音频", - "SettingsTabNetwork": "网络", - "SettingsTabNetworkConnection": "网络连接", - "SettingsTabCpuCache": "CPU 缓存", - "SettingsTabCpuMemory": "CPU 模式", - "DialogUpdaterFlatpakNotSupportedMessage": "请通过 FlatHub 更新 Ryujinx 模拟器。", - "UpdaterDisabledWarningTitle": "已禁用更新!", - "ControllerSettingsRotate90": "顺时针旋转 90°", - "IconSize": "图标尺寸", - "IconSizeTooltip": "更改游戏图标的显示尺寸", - "MenuBarOptionsShowConsole": "显示控制台", - "ShaderCachePurgeError": "清除 {0} 的着色器缓存文件时出错:{1}", - "UserErrorNoKeys": "找不到密钥Keys", - "UserErrorNoFirmware": "未安装系统固件", - "UserErrorFirmwareParsingFailed": "固件文件解析出错", - "UserErrorApplicationNotFound": "找不到游戏程序", - "UserErrorUnknown": "未知错误", - "UserErrorUndefined": "未定义错误", - "UserErrorNoKeysDescription": "Ryujinx 模拟器找不到“prod.keys”密钥文件", - "UserErrorNoFirmwareDescription": "Ryujinx 模拟器未安装 Switch 系统固件", - "UserErrorFirmwareParsingFailedDescription": "Ryujinx 模拟器无法解密当前固件,一般是由于使用了旧版的密钥导致的。", - "UserErrorApplicationNotFoundDescription": "Ryujinx 模拟器在所选路径中找不到有效的游戏程序。", - "UserErrorUnknownDescription": "出现未知错误!", - "UserErrorUndefinedDescription": "出现未定义错误!此类错误不应出现,请联系开发者!", - "OpenSetupGuideMessage": "打开安装指南", - "NoUpdate": "无更新(或不加载游戏更新)", - "TitleUpdateVersionLabel": "游戏更新的版本 {0}", - "RyujinxInfo": "Ryujinx - 信息", - "RyujinxConfirm": "Ryujinx - 确认", - "FileDialogAllTypes": "全部类型", - "Never": "从不", - "SwkbdMinCharacters": "不少于 {0} 个字符", - "SwkbdMinRangeCharacters": "必须为 {0}-{1} 个字符", - "SoftwareKeyboard": "软键盘", - "SoftwareKeyboardModeNumeric": "只能输入 0-9 或 \".\"", - "SoftwareKeyboardModeAlphabet": "仅支持非中文字符", - "SoftwareKeyboardModeASCII": "仅支持 ASCII 字符", - "ControllerAppletControllers": "支持的手柄:", - "ControllerAppletPlayers": "玩家:", - "ControllerAppletDescription": "您当前的输入配置无效。打开设置并重新设置您的输入选项。", - "ControllerAppletDocked": "已经设置为主机模式,应禁用掌机手柄操控。", - "UpdaterRenaming": "正在重命名旧文件...", - "UpdaterRenameFailed": "更新过程中无法重命名文件:{0}", - "UpdaterAddingFiles": "安装更新中...", - "UpdaterExtracting": "正在提取更新...", - "UpdaterDownloading": "下载更新中...", - "Game": "游戏", - "Docked": "主机模式", - "Handheld": "掌机模式", - "ConnectionError": "连接错误。", - "AboutPageDeveloperListMore": "{0} 等开发者...", - "ApiError": "API 错误。", - "LoadingHeading": "正在启动 {0}", - "CompilingPPTC": "编译 PPTC 缓存中", - "CompilingShaders": "编译着色器中", - "AllKeyboards": "所有键盘", - "OpenFileDialogTitle": "选择支持的游戏文件并加载", - "OpenFolderDialogTitle": "选择包含解包游戏的目录并加载", - "AllSupportedFormats": "所有支持的格式", - "RyujinxUpdater": "Ryujinx 更新", - "SettingsTabHotkeys": "快捷键", - "SettingsTabHotkeysHotkeys": "键盘快捷键", - "SettingsTabHotkeysToggleVsyncHotkey": "开启或关闭垂直同步:", - "SettingsTabHotkeysScreenshotHotkey": "保存截屏:", - "SettingsTabHotkeysShowUiHotkey": "隐藏菜单栏和状态栏:", - "SettingsTabHotkeysPauseHotkey": "暂停:", - "SettingsTabHotkeysToggleMuteHotkey": "静音:", - "ControllerMotionTitle": "体感设置", - "ControllerRumbleTitle": "震动设置", - "SettingsSelectThemeFileDialogTitle": "选择主题文件", - "SettingsXamlThemeFile": "Xaml 主题文件", - "AvatarWindowTitle": "管理账户 - 头像", - "Amiibo": "Amiibo", - "Unknown": "未知", - "Usage": "用法", - "Writable": "可写入", - "SelectDlcDialogTitle": "选择 DLC 文件", - "SelectUpdateDialogTitle": "选择更新文件", - "SelectModDialogTitle": "选择 MOD 目录", - "UserProfileWindowTitle": "管理用户账户", - "CheatWindowTitle": "金手指管理器", - "DlcWindowTitle": "管理 {0} ({1}) 的 DLC", - "ModWindowTitle": "管理 {0} ({1}) 的 MOD", - "UpdateWindowTitle": "游戏更新管理器", - "CheatWindowHeading": "适用于 {0} [{1}] 的金手指", - "BuildId": "游戏版本 ID:", - "DlcWindowHeading": "{0} 个 DLC", - "ModWindowHeading": "{0} 个 MOD", - "UserProfilesEditProfile": "编辑所选", - "Cancel": "取消", - "Save": "保存", - "Discard": "放弃", - "Paused": "已暂停", - "UserProfilesSetProfileImage": "选择头像", - "UserProfileEmptyNameError": "必须输入名称", - "UserProfileNoImageError": "必须设置头像", - "GameUpdateWindowHeading": "管理 {0} ({1}) 的更新", - "SettingsTabHotkeysResScaleUpHotkey": "提高分辨率:", - "SettingsTabHotkeysResScaleDownHotkey": "降低分辨率:", - "UserProfilesName": "名称:", - "UserProfilesUserId": "用户 ID:", - "SettingsTabGraphicsBackend": "图形渲染引擎:", - "SettingsTabGraphicsBackendTooltip": "选择模拟器中使用的图像渲染引擎。\n\n安装了最新显卡驱动程序的所有现代显卡基本都支持 Vulkan,Vulkan 能够提供更快的着色器编译(较少的卡顿)。\n\n在旧版 Nvidia 显卡上、Linux 上的旧版 AMD 显卡,或者显存较低的显卡上,OpenGL 可能会取得更好的效果,但着色器编译更慢(更多的卡顿)。\n\n如果不确定,请设置为“Vulkan”。如果您的 GPU 已安装了最新显卡驱动程序也不支持 Vulkan,那请设置为“OpenGL”。", - "SettingsEnableTextureRecompression": "启用纹理压缩", - "SettingsEnableTextureRecompressionTooltip": "压缩 ASTC 纹理以减少 VRAM (显存)的占用。\n\n使用此纹理格式的游戏包括:异界锁链(Astral Chain),蓓优妮塔3(Bayonetta 3),火焰纹章Engage(Fire Emblem Engage),密特罗德 究极(Metroid Prime Remased),超级马力欧兄弟 惊奇(Super Mario Bros. Wonder)以及塞尔达传说 王国之泪(The Legend of Zelda: Tears of the Kingdom)。\n\n显存小于4GB的显卡在运行这些游戏时可能会偶发闪退。\n\n只有当您在上述游戏中的显存不足时才需要启用此选项。\n\n如果不确定,请保持关闭状态。", - "SettingsTabGraphicsPreferredGpu": "首选 GPU:", - "SettingsTabGraphicsPreferredGpuTooltip": "选择 Vulkan 图形引擎使用的 GPU。\n\n此选项不会影响 OpenGL 使用的 GPU。\n\n如果不确定,建议选择\"独立显卡(dGPU)\"。如果没有独立显卡,则无需改动此选项。", - "SettingsAppRequiredRestartMessage": "Ryujinx 模拟器需要重启", - "SettingsGpuBackendRestartMessage": "您修改了图形引擎或 GPU 设置,需要重启模拟器才能生效", - "SettingsGpuBackendRestartSubMessage": "是否要立即重启模拟器?", - "RyujinxUpdaterMessage": "是否更新 Ryujinx 到最新的版本?", - "SettingsTabHotkeysVolumeUpHotkey": "音量加:", - "SettingsTabHotkeysVolumeDownHotkey": "音量减:", - "SettingsEnableMacroHLE": "启用 HLE 宏加速", - "SettingsEnableMacroHLETooltip": "GPU 宏指令的高级模拟。\n\n提高性能表现,但一些游戏可能会出现图形错误。\n\n如果不确定,请保持开启状态。", - "SettingsEnableColorSpacePassthrough": "色彩空间直通", - "SettingsEnableColorSpacePassthroughTooltip": "使 Vulkan 图形引擎直接传输原始色彩信息。对于宽色域 (例如 DCI-P3) 显示器的用户来说,可以产生更鲜艳的颜色,代价是会损失部分色彩准确度。", - "VolumeShort": "音量", - "UserProfilesManageSaves": "管理存档", - "DeleteUserSave": "确定删除此游戏的用户存档吗?", - "IrreversibleActionNote": "删除后不可恢复。", - "SaveManagerHeading": "管理 {0} ({1}) 的存档", - "SaveManagerTitle": "存档管理器", - "Name": "名称", - "Size": "大小", - "Search": "搜索", - "UserProfilesRecoverLostAccounts": "恢复丢失的账户", - "Recover": "恢复", - "UserProfilesRecoverHeading": "找到了这些用户的存档数据", - "UserProfilesRecoverEmptyList": "没有可以恢复的用户数据", - "GraphicsAATooltip": "抗锯齿是一种图形处理技术,用于减少图像边缘的锯齿状现象,使图像更加平滑。\n\nFXAA(快速近似抗锯齿)是一种性能开销相对较小的抗锯齿方法,但可能会使得整体图像看起来有些模糊。\n\nSMAA(增强型子像素抗锯齿)则更加精细,它会尝试找到锯齿边缘并平滑它们,相比 FXAA 有更好的图像质量,但性能开销可能会稍大一些。\n\n如果开启了 FSR(FidelityFX Super Resolution,超级分辨率锐画技术)来提高性能或图像质量,不建议再启用抗锯齿,因为它们会产生不必要的图形处理开销,或者相互之间效果不协调。\n\n在游戏运行时,通过点击下面的“应用”按钮可以使设置生效;你可以将设置窗口移开,并试验找到您喜欢的游戏画面效果。\n\n如果不确定,请保持为“无”。", - "GraphicsAALabel": "抗锯齿:", - "GraphicsScalingFilterLabel": "缩放过滤:", - "GraphicsScalingFilterTooltip": "选择在分辨率缩放时将使用的缩放过滤器。\n\nBilinear(双线性过滤)对于3D游戏效果较好,是一个安全的默认选项。\n\nNearest(最近邻过滤)推荐用于像素艺术游戏。\n\nFSR(超级分辨率锐画)只是一个锐化过滤器,不推荐与 FXAA 或 SMAA 抗锯齿一起使用。\n\n在游戏运行时,通过点击下面的“应用”按钮可以使设置生效;你可以将设置窗口移开,并试验找到您喜欢的游戏画面效果。\n\n如果不确定,请保持为“Bilinear(双线性过滤)”。", - "GraphicsScalingFilterBilinear": "Bilinear(双线性过滤)", - "GraphicsScalingFilterNearest": "Nearest(最近邻过滤)", - "GraphicsScalingFilterFsr": "FSR(超级分辨率锐画技术)", - "GraphicsScalingFilterLevelLabel": "等级", - "GraphicsScalingFilterLevelTooltip": "设置 FSR 1.0 的锐化等级,数值越高,图像越锐利。", - "SmaaLow": "SMAA 低质量", - "SmaaMedium": "SMAA 中质量", - "SmaaHigh": "SMAA 高质量", - "SmaaUltra": "SMAA 超高质量", - "UserEditorTitle": "编辑用户", - "UserEditorTitleCreate": "创建用户", - "SettingsTabNetworkInterface": "网络接口:", - "NetworkInterfaceTooltip": "用于局域网(LAN)/本地网络发现(LDN)功能的网络接口。\n\n结合 VPN 或 XLink Kai 以及支持局域网功能的游戏,可以在互联网上伪造为同一网络连接。\n\n如果不确定,请保持为“默认”。", - "NetworkInterfaceDefault": "默认", - "PackagingShaders": "整合着色器中", - "AboutChangelogButton": "在 Github 上查看更新日志", - "AboutChangelogButtonTooltipMessage": "点击这里在浏览器中打开此版本的更新日志。", - "SettingsTabNetworkMultiplayer": "多人联机游玩", - "MultiplayerMode": "联机模式:", - "MultiplayerModeTooltip": "修改 LDN 多人联机游玩模式。\n\nldn_mitm 联机插件将修改游戏中的本地无线和本地游玩功能,使其表现得像局域网一样,允许和其他安装了 ldn_mitm 插件的 Ryujinx 模拟器和破解的任天堂 Switch 主机在同一网络下进行本地连接,实现多人联机游玩。\n\n多人联机游玩要求所有玩家必须运行相同的游戏版本(例如,任天堂明星大乱斗特别版 v13.0.1 无法与 v13.0.0 版本联机)。\n\n如果不确定,请保持为“禁用”。", - "MultiplayerModeDisabled": "禁用", - "MultiplayerModeLdnMitm": "ldn_mitm" -} diff --git a/src/Ryujinx/Assets/Locales/zh_TW.json b/src/Ryujinx/Assets/Locales/zh_TW.json deleted file mode 100644 index fc838d251..000000000 --- a/src/Ryujinx/Assets/Locales/zh_TW.json +++ /dev/null @@ -1,780 +0,0 @@ -{ - "Language": "繁體中文 (台灣)", - "MenuBarFileOpenApplet": "開啟小程式", - "MenuBarFileOpenAppletOpenMiiAppletToolTip": "在獨立模式下開啟 Mii 編輯器小程式", - "SettingsTabInputDirectMouseAccess": "滑鼠直接存取", - "SettingsTabSystemMemoryManagerMode": "記憶體管理員模式:", - "SettingsTabSystemMemoryManagerModeSoftware": "軟體模式", - "SettingsTabSystemMemoryManagerModeHost": "主體模式 (快速)", - "SettingsTabSystemMemoryManagerModeHostUnchecked": "主體略過檢查模式 (最快,不安全)", - "SettingsTabSystemUseHypervisor": "使用 Hypervisor", - "MenuBarFile": "檔案(_F)", - "MenuBarFileOpenFromFile": "從檔案載入應用程式(_L)", - "MenuBarFileOpenUnpacked": "載入未封裝的遊戲(_U)", - "MenuBarFileOpenEmuFolder": "開啟 Ryujinx 資料夾", - "MenuBarFileOpenLogsFolder": "開啟日誌資料夾", - "MenuBarFileExit": "結束(_E)", - "MenuBarOptions": "選項(_O)", - "MenuBarOptionsToggleFullscreen": "切換全螢幕模式", - "MenuBarOptionsStartGamesInFullscreen": "使用全螢幕模式啟動遊戲", - "MenuBarOptionsStopEmulation": "停止模擬", - "MenuBarOptionsSettings": "設定(_S)", - "MenuBarOptionsManageUserProfiles": "管理使用者設定檔(_M)", - "MenuBarActions": "動作(_A)", - "MenuBarOptionsSimulateWakeUpMessage": "模擬喚醒訊息", - "MenuBarActionsScanAmiibo": "掃描 Amiibo", - "MenuBarTools": "工具(_T)", - "MenuBarToolsInstallFirmware": "安裝韌體", - "MenuBarFileToolsInstallFirmwareFromFile": "從 XCI 或 ZIP 安裝韌體", - "MenuBarFileToolsInstallFirmwareFromDirectory": "從資料夾安裝韌體", - "MenuBarToolsManageFileTypes": "管理檔案類型", - "MenuBarToolsInstallFileTypes": "安裝檔案類型", - "MenuBarToolsUninstallFileTypes": "移除檔案類型", - "MenuBarView": "檢視(_V)", - "MenuBarViewWindow": "視窗大小", - "MenuBarViewWindow720": "720p", - "MenuBarViewWindow1080": "1080p", - "MenuBarHelp": "說明(_H)", - "MenuBarHelpCheckForUpdates": "檢查更新", - "MenuBarHelpAbout": "關於", - "MenuSearch": "搜尋...", - "GameListHeaderFavorite": "我的最愛", - "GameListHeaderIcon": "圖示", - "GameListHeaderApplication": "名稱", - "GameListHeaderDeveloper": "開發者", - "GameListHeaderVersion": "版本", - "GameListHeaderTimePlayed": "遊玩時數", - "GameListHeaderLastPlayed": "最近遊玩", - "GameListHeaderFileExtension": "副檔名", - "GameListHeaderFileSize": "檔案大小", - "GameListHeaderPath": "路徑", - "GameListContextMenuOpenUserSaveDirectory": "開啟使用者存檔資料夾", - "GameListContextMenuOpenUserSaveDirectoryToolTip": "開啟此應用程式的使用者存檔資料夾", - "GameListContextMenuOpenDeviceSaveDirectory": "開啟裝置存檔資料夾", - "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "開啟此應用程式的裝置存檔資料夾", - "GameListContextMenuOpenBcatSaveDirectory": "開啟 BCAT 存檔資料夾", - "GameListContextMenuOpenBcatSaveDirectoryToolTip": "開啟此應用程式的 BCAT 存檔資料夾", - "GameListContextMenuManageTitleUpdates": "管理遊戲更新", - "GameListContextMenuManageTitleUpdatesToolTip": "開啟遊戲更新管理視窗", - "GameListContextMenuManageDlc": "管理 DLC", - "GameListContextMenuManageDlcToolTip": "開啟 DLC 管理視窗", - "GameListContextMenuCacheManagement": "快取管理", - "GameListContextMenuCacheManagementPurgePptc": "佇列 PPTC 重建", - "GameListContextMenuCacheManagementPurgePptcToolTip": "下一次啟動遊戲時,觸發 PPTC 進行重建", - "GameListContextMenuCacheManagementPurgeShaderCache": "清除著色器快取", - "GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "刪除應用程式的著色器快取", - "GameListContextMenuCacheManagementOpenPptcDirectory": "開啟 PPTC 資料夾", - "GameListContextMenuCacheManagementOpenPptcDirectoryToolTip": "開啟此應用程式的 PPTC 快取資料夾", - "GameListContextMenuCacheManagementOpenShaderCacheDirectory": "開啟著色器快取資料夾", - "GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip": "開啟此應用程式的著色器快取資料夾", - "GameListContextMenuExtractData": "提取資料", - "GameListContextMenuExtractDataExeFS": "ExeFS", - "GameListContextMenuExtractDataExeFSToolTip": "從應用程式的目前配置中提取 ExeFS 分區 (包含更新)", - "GameListContextMenuExtractDataRomFS": "RomFS", - "GameListContextMenuExtractDataRomFSToolTip": "從應用程式的目前配置中提取 RomFS 分區 (包含更新)", - "GameListContextMenuExtractDataLogo": "Logo", - "GameListContextMenuExtractDataLogoToolTip": "從應用程式的目前配置中提取 Logo 分區 (包含更新)", - "GameListContextMenuCreateShortcut": "建立應用程式捷徑", - "GameListContextMenuCreateShortcutToolTip": "建立桌面捷徑,啟動選取的應用程式", - "GameListContextMenuCreateShortcutToolTipMacOS": "在 macOS 的應用程式資料夾中建立捷徑,啟動選取的應用程式", - "GameListContextMenuOpenModsDirectory": "開啟模組資料夾", - "GameListContextMenuOpenModsDirectoryToolTip": "開啟此應用程式模組的資料夾", - "GameListContextMenuOpenSdModsDirectory": "開啟 Atmosphere 模組資料夾", - "GameListContextMenuOpenSdModsDirectoryToolTip": "開啟此應用程式模組的另一個 SD 卡 Atmosphere 資料夾。適用於為真實硬體封裝的模組。", - "StatusBarGamesLoaded": "{0}/{1} 遊戲已載入", - "StatusBarSystemVersion": "系統版本: {0}", - "LinuxVmMaxMapCountDialogTitle": "檢測到記憶體映射的低限值", - "LinuxVmMaxMapCountDialogTextPrimary": "您是否要將 vm.max_map_count 的數值增至 {0}?", - "LinuxVmMaxMapCountDialogTextSecondary": "某些遊戲可能會嘗試建立超過目前允許的記憶體映射。一旦超過此限制,Ryujinx 就會崩潰。", - "LinuxVmMaxMapCountDialogButtonUntilRestart": "是的,直到下次重新啟動", - "LinuxVmMaxMapCountDialogButtonPersistent": "是的,永久設定", - "LinuxVmMaxMapCountWarningTextPrimary": "記憶體映射的最大值低於建議值。", - "LinuxVmMaxMapCountWarningTextSecondary": "目前 vm.max_map_count ({0}) 的數值小於 {1}。某些遊戲可能會嘗試建立比目前允許值更多的記憶體映射。一旦超過此限制,Ryujinx 就會崩潰。\n\n您可能需要手動提高上限,或者安裝 pkexec,讓 Ryujinx 協助提高上限。", - "Settings": "設定", - "SettingsTabGeneral": "使用者介面", - "SettingsTabGeneralGeneral": "一般", - "SettingsTabGeneralEnableDiscordRichPresence": "啟用 Discord 動態狀態展示", - "SettingsTabGeneralCheckUpdatesOnLaunch": "啟動時檢查更新", - "SettingsTabGeneralShowConfirmExitDialog": "顯示「確認結束」對話方塊", - "SettingsTabGeneralRememberWindowState": "記住視窗大小/位置", - "SettingsTabGeneralHideCursor": "隱藏滑鼠游標:", - "SettingsTabGeneralHideCursorNever": "從不", - "SettingsTabGeneralHideCursorOnIdle": "閒置時", - "SettingsTabGeneralHideCursorAlways": "總是", - "SettingsTabGeneralGameDirectories": "遊戲資料夾", - "SettingsTabGeneralAdd": "新增", - "SettingsTabGeneralRemove": "刪除", - "SettingsTabSystem": "系統", - "SettingsTabSystemCore": "核心", - "SettingsTabSystemSystemRegion": "系統區域:", - "SettingsTabSystemSystemRegionJapan": "日本", - "SettingsTabSystemSystemRegionUSA": "美國", - "SettingsTabSystemSystemRegionEurope": "歐洲", - "SettingsTabSystemSystemRegionAustralia": "澳洲", - "SettingsTabSystemSystemRegionChina": "中國", - "SettingsTabSystemSystemRegionKorea": "韓國", - "SettingsTabSystemSystemRegionTaiwan": "台灣 (中華民國)", - "SettingsTabSystemSystemLanguage": "系統語言:", - "SettingsTabSystemSystemLanguageJapanese": "日文", - "SettingsTabSystemSystemLanguageAmericanEnglish": "英文 (美國)", - "SettingsTabSystemSystemLanguageFrench": "法文", - "SettingsTabSystemSystemLanguageGerman": "德文", - "SettingsTabSystemSystemLanguageItalian": "義大利文", - "SettingsTabSystemSystemLanguageSpanish": "西班牙文", - "SettingsTabSystemSystemLanguageChinese": "中文 (中國)", - "SettingsTabSystemSystemLanguageKorean": "韓文", - "SettingsTabSystemSystemLanguageDutch": "荷蘭文", - "SettingsTabSystemSystemLanguagePortuguese": "葡萄牙文", - "SettingsTabSystemSystemLanguageRussian": "俄文", - "SettingsTabSystemSystemLanguageTaiwanese": "中文 (台灣)", - "SettingsTabSystemSystemLanguageBritishEnglish": "英文 (英國)", - "SettingsTabSystemSystemLanguageCanadianFrench": "加拿大法文", - "SettingsTabSystemSystemLanguageLatinAmericanSpanish": "美洲西班牙文", - "SettingsTabSystemSystemLanguageSimplifiedChinese": "簡體中文", - "SettingsTabSystemSystemLanguageTraditionalChinese": "正體中文 (建議)", - "SettingsTabSystemSystemTimeZone": "系統時區:", - "SettingsTabSystemSystemTime": "系統時鐘:", - "SettingsTabSystemEnableVsync": "垂直同步", - "SettingsTabSystemEnablePptc": "PPTC (剖析式持久轉譯快取, Profiled Persistent Translation Cache)", - "SettingsTabSystemEnableFsIntegrityChecks": "檔案系統完整性檢查", - "SettingsTabSystemAudioBackend": "音效後端:", - "SettingsTabSystemAudioBackendDummy": "虛設 (Dummy)", - "SettingsTabSystemAudioBackendOpenAL": "OpenAL", - "SettingsTabSystemAudioBackendSoundIO": "SoundIO", - "SettingsTabSystemAudioBackendSDL2": "SDL2", - "SettingsTabSystemHacks": "補釘修正", - "SettingsTabSystemHacksNote": "可能導致模擬器不穩定", - "SettingsTabSystemExpandDramSize": "使用替代的記憶體配置 (開發者專用)", - "SettingsTabSystemIgnoreMissingServices": "忽略缺少的模擬器功能", - "SettingsTabGraphics": "圖形", - "SettingsTabGraphicsAPI": "圖形 API", - "SettingsTabGraphicsEnableShaderCache": "啟用著色器快取", - "SettingsTabGraphicsAnisotropicFiltering": "各向異性過濾:", - "SettingsTabGraphicsAnisotropicFilteringAuto": "自動", - "SettingsTabGraphicsAnisotropicFiltering2x": "2 倍", - "SettingsTabGraphicsAnisotropicFiltering4x": "4 倍", - "SettingsTabGraphicsAnisotropicFiltering8x": "8 倍", - "SettingsTabGraphicsAnisotropicFiltering16x": "16 倍", - "SettingsTabGraphicsResolutionScale": "解析度比例:", - "SettingsTabGraphicsResolutionScaleCustom": "自訂 (不建議使用)", - "SettingsTabGraphicsResolutionScaleNative": "原生 (720p/1080p)", - "SettingsTabGraphicsResolutionScale2x": "2 倍 (1440p/2160p)", - "SettingsTabGraphicsResolutionScale3x": "3 倍 (2160p/3240p)", - "SettingsTabGraphicsResolutionScale4x": "4 倍 (2880p/4320p) (不建議使用)", - "SettingsTabGraphicsAspectRatio": "顯示長寬比例:", - "SettingsTabGraphicsAspectRatio4x3": "4:3", - "SettingsTabGraphicsAspectRatio16x9": "16:9", - "SettingsTabGraphicsAspectRatio16x10": "16:10", - "SettingsTabGraphicsAspectRatio21x9": "21:9", - "SettingsTabGraphicsAspectRatio32x9": "32:9", - "SettingsTabGraphicsAspectRatioStretch": "拉伸以適應視窗", - "SettingsTabGraphicsDeveloperOptions": "開發者選項", - "SettingsTabGraphicsShaderDumpPath": "圖形著色器傾印路徑:", - "SettingsTabLogging": "日誌", - "SettingsTabLoggingLogging": "日誌", - "SettingsTabLoggingEnableLoggingToFile": "啟用日誌到檔案", - "SettingsTabLoggingEnableStubLogs": "啟用 Stub 日誌", - "SettingsTabLoggingEnableInfoLogs": "啟用資訊日誌", - "SettingsTabLoggingEnableWarningLogs": "啟用警告日誌", - "SettingsTabLoggingEnableErrorLogs": "啟用錯誤日誌", - "SettingsTabLoggingEnableTraceLogs": "啟用追蹤日誌", - "SettingsTabLoggingEnableGuestLogs": "啟用客體日誌", - "SettingsTabLoggingEnableFsAccessLogs": "啟用檔案系統存取日誌", - "SettingsTabLoggingFsGlobalAccessLogMode": "檔案系統全域存取日誌模式:", - "SettingsTabLoggingDeveloperOptions": "開發者選項", - "SettingsTabLoggingDeveloperOptionsNote": "警告: 會降低效能", - "SettingsTabLoggingGraphicsBackendLogLevel": "圖形後端日誌等級:", - "SettingsTabLoggingGraphicsBackendLogLevelNone": "無", - "SettingsTabLoggingGraphicsBackendLogLevelError": "錯誤", - "SettingsTabLoggingGraphicsBackendLogLevelPerformance": "減速", - "SettingsTabLoggingGraphicsBackendLogLevelAll": "全部", - "SettingsTabLoggingEnableDebugLogs": "啟用偵錯日誌", - "SettingsTabInput": "輸入", - "SettingsTabInputEnableDockedMode": "底座模式", - "SettingsTabInputDirectKeyboardAccess": "鍵盤直接存取", - "SettingsButtonSave": "儲存", - "SettingsButtonClose": "關閉", - "SettingsButtonOk": "確定", - "SettingsButtonCancel": "取消", - "SettingsButtonApply": "套用", - "ControllerSettingsPlayer": "玩家", - "ControllerSettingsPlayer1": "玩家 1", - "ControllerSettingsPlayer2": "玩家 2", - "ControllerSettingsPlayer3": "玩家 3", - "ControllerSettingsPlayer4": "玩家 4", - "ControllerSettingsPlayer5": "玩家 5", - "ControllerSettingsPlayer6": "玩家 6", - "ControllerSettingsPlayer7": "玩家 7", - "ControllerSettingsPlayer8": "玩家 8", - "ControllerSettingsHandheld": "手提模式", - "ControllerSettingsInputDevice": "輸入裝置", - "ControllerSettingsRefresh": "重新整理", - "ControllerSettingsDeviceDisabled": "已停用", - "ControllerSettingsControllerType": "控制器類型", - "ControllerSettingsControllerTypeHandheld": "手提模式", - "ControllerSettingsControllerTypeProController": "Pro 控制器", - "ControllerSettingsControllerTypeJoyConPair": "雙 JoyCon", - "ControllerSettingsControllerTypeJoyConLeft": "左 JoyCon", - "ControllerSettingsControllerTypeJoyConRight": "右 JoyCon", - "ControllerSettingsProfile": "設定檔", - "ControllerSettingsProfileDefault": "預設", - "ControllerSettingsLoad": "載入", - "ControllerSettingsAdd": "新增", - "ControllerSettingsRemove": "刪除", - "ControllerSettingsButtons": "按鍵", - "ControllerSettingsButtonA": "A", - "ControllerSettingsButtonB": "B", - "ControllerSettingsButtonX": "X", - "ControllerSettingsButtonY": "Y", - "ControllerSettingsButtonPlus": "+", - "ControllerSettingsButtonMinus": "-", - "ControllerSettingsDPad": "方向鍵", - "ControllerSettingsDPadUp": "上", - "ControllerSettingsDPadDown": "下", - "ControllerSettingsDPadLeft": "左", - "ControllerSettingsDPadRight": "右", - "ControllerSettingsStickButton": "按鍵", - "ControllerSettingsStickUp": "上", - "ControllerSettingsStickDown": "下", - "ControllerSettingsStickLeft": "左", - "ControllerSettingsStickRight": "右", - "ControllerSettingsStickStick": "搖桿", - "ControllerSettingsStickInvertXAxis": "搖桿左右反向", - "ControllerSettingsStickInvertYAxis": "搖桿上下反向", - "ControllerSettingsStickDeadzone": "無感帶:", - "ControllerSettingsLStick": "左搖桿", - "ControllerSettingsRStick": "右搖桿", - "ControllerSettingsTriggersLeft": "左扳機", - "ControllerSettingsTriggersRight": "右扳機", - "ControllerSettingsTriggersButtonsLeft": "左扳機鍵", - "ControllerSettingsTriggersButtonsRight": "右扳機鍵", - "ControllerSettingsTriggers": "板機", - "ControllerSettingsTriggerL": "L", - "ControllerSettingsTriggerR": "R", - "ControllerSettingsTriggerZL": "ZL", - "ControllerSettingsTriggerZR": "ZR", - "ControllerSettingsLeftSL": "SL", - "ControllerSettingsLeftSR": "SR", - "ControllerSettingsRightSL": "SL", - "ControllerSettingsRightSR": "SR", - "ControllerSettingsExtraButtonsLeft": "左背鍵", - "ControllerSettingsExtraButtonsRight": "右背鍵", - "ControllerSettingsMisc": "其他", - "ControllerSettingsTriggerThreshold": "扳機閾值:", - "ControllerSettingsMotion": "體感", - "ControllerSettingsMotionUseCemuhookCompatibleMotion": "使用與 CemuHook 相容的體感", - "ControllerSettingsMotionControllerSlot": "控制器插槽:", - "ControllerSettingsMotionMirrorInput": "鏡像輸入", - "ControllerSettingsMotionRightJoyConSlot": "右 JoyCon 插槽:", - "ControllerSettingsMotionServerHost": "伺服器主機位址:", - "ControllerSettingsMotionGyroSensitivity": "陀螺儀靈敏度:", - "ControllerSettingsMotionGyroDeadzone": "陀螺儀無感帶:", - "ControllerSettingsSave": "儲存", - "ControllerSettingsClose": "關閉", - "KeyUnknown": "未知", - "KeyShiftLeft": "左 Shift", - "KeyShiftRight": "右 Shift", - "KeyControlLeft": "左 Ctrl", - "KeyMacControlLeft": "左 ⌃", - "KeyControlRight": "右 Ctrl", - "KeyMacControlRight": "右 ⌃", - "KeyAltLeft": "左 Alt", - "KeyMacAltLeft": "左 ⌥", - "KeyAltRight": "右 Alt", - "KeyMacAltRight": "右 ⌥", - "KeyWinLeft": "左 ⊞", - "KeyMacWinLeft": "左 ⌘", - "KeyWinRight": "右 ⊞", - "KeyMacWinRight": "右 ⌘", - "KeyMenu": "功能表", - "KeyUp": "上", - "KeyDown": "下", - "KeyLeft": "左", - "KeyRight": "右", - "KeyEnter": "Enter 鍵", - "KeyEscape": "Esc 鍵", - "KeySpace": "空白鍵", - "KeyTab": "Tab 鍵", - "KeyBackSpace": "Backspace 鍵", - "KeyInsert": "Insert 鍵", - "KeyDelete": "Delete 鍵", - "KeyPageUp": "向上捲頁鍵", - "KeyPageDown": "向下捲頁鍵", - "KeyHome": "Home 鍵", - "KeyEnd": "End 鍵", - "KeyCapsLock": "Caps Lock 鍵", - "KeyScrollLock": "Scroll Lock 鍵", - "KeyPrintScreen": "Print Screen 鍵", - "KeyPause": "Pause 鍵", - "KeyNumLock": "Num Lock 鍵", - "KeyClear": "清除", - "KeyKeypad0": "數字鍵 0", - "KeyKeypad1": "數字鍵 1", - "KeyKeypad2": "數字鍵 2", - "KeyKeypad3": "數字鍵 3", - "KeyKeypad4": "數字鍵 4", - "KeyKeypad5": "數字鍵 5", - "KeyKeypad6": "數字鍵 6", - "KeyKeypad7": "數字鍵 7", - "KeyKeypad8": "數字鍵 8", - "KeyKeypad9": "數字鍵 9", - "KeyKeypadDivide": "數字鍵除號", - "KeyKeypadMultiply": "數字鍵乘號", - "KeyKeypadSubtract": "數字鍵減號", - "KeyKeypadAdd": "數字鍵加號", - "KeyKeypadDecimal": "數字鍵小數點", - "KeyKeypadEnter": "數字鍵 Enter", - "KeyNumber0": "0", - "KeyNumber1": "1", - "KeyNumber2": "2", - "KeyNumber3": "3", - "KeyNumber4": "4", - "KeyNumber5": "5", - "KeyNumber6": "6", - "KeyNumber7": "7", - "KeyNumber8": "8", - "KeyNumber9": "9", - "KeyTilde": "~", - "KeyGrave": "`", - "KeyMinus": "-", - "KeyPlus": "+", - "KeyBracketLeft": "[", - "KeyBracketRight": "]", - "KeySemicolon": ";", - "KeyQuote": "\"", - "KeyComma": ",", - "KeyPeriod": ".", - "KeySlash": "/", - "KeyBackSlash": "\\", - "KeyUnbound": "未分配", - "GamepadLeftStick": "左搖桿按鍵", - "GamepadRightStick": "右搖桿按鍵", - "GamepadLeftShoulder": "左肩鍵", - "GamepadRightShoulder": "右肩鍵", - "GamepadLeftTrigger": "左扳機", - "GamepadRightTrigger": "右扳機", - "GamepadDpadUp": "上", - "GamepadDpadDown": "下", - "GamepadDpadLeft": "左", - "GamepadDpadRight": "右", - "GamepadMinus": "-", - "GamepadPlus": "+", - "GamepadGuide": "快顯功能表鍵", - "GamepadMisc1": "其他按鍵", - "GamepadPaddle1": "其他按鍵 1", - "GamepadPaddle2": "其他按鍵 2", - "GamepadPaddle3": "其他按鍵 3", - "GamepadPaddle4": "其他按鍵 4", - "GamepadTouchpad": "觸控板", - "GamepadSingleLeftTrigger0": "左扳機 0", - "GamepadSingleRightTrigger0": "右扳機 0", - "GamepadSingleLeftTrigger1": "左扳機 1", - "GamepadSingleRightTrigger1": "右扳機 1", - "StickLeft": "左搖桿", - "StickRight": "右搖桿", - "UserProfilesSelectedUserProfile": "選取的使用者設定檔:", - "UserProfilesSaveProfileName": "儲存設定檔名稱", - "UserProfilesChangeProfileImage": "變更設定檔圖像", - "UserProfilesAvailableUserProfiles": "可用的使用者設定檔:", - "UserProfilesAddNewProfile": "建立設定檔", - "UserProfilesDelete": "刪除", - "UserProfilesClose": "關閉", - "ProfileNameSelectionWatermark": "選擇暱稱", - "ProfileImageSelectionTitle": "設定檔圖像選取", - "ProfileImageSelectionHeader": "選擇設定檔圖像", - "ProfileImageSelectionNote": "您可以匯入自訂的設定檔圖像,或從系統韌體中選取大頭貼。", - "ProfileImageSelectionImportImage": "匯入圖像檔案", - "ProfileImageSelectionSelectAvatar": "選取韌體大頭貼", - "InputDialogTitle": "輸入對話方塊", - "InputDialogOk": "確定", - "InputDialogCancel": "取消", - "InputDialogAddNewProfileTitle": "選擇設定檔名稱", - "InputDialogAddNewProfileHeader": "請輸入設定檔名稱", - "InputDialogAddNewProfileSubtext": "(最大長度: {0})", - "AvatarChoose": "選擇大頭貼", - "AvatarSetBackgroundColor": "設定背景顏色", - "AvatarClose": "關閉", - "ControllerSettingsLoadProfileToolTip": "載入設定檔", - "ControllerSettingsAddProfileToolTip": "新增設定檔", - "ControllerSettingsRemoveProfileToolTip": "刪除設定檔", - "ControllerSettingsSaveProfileToolTip": "儲存設定檔", - "MenuBarFileToolsTakeScreenshot": "儲存擷取畫面", - "MenuBarFileToolsHideUi": "隱藏 UI", - "GameListContextMenuRunApplication": "執行應用程式", - "GameListContextMenuToggleFavorite": "加入/移除為我的最愛", - "GameListContextMenuToggleFavoriteToolTip": "切換遊戲的我的最愛狀態", - "SettingsTabGeneralTheme": "佈景主題:", - "SettingsTabGeneralThemeDark": "深色", - "SettingsTabGeneralThemeLight": "淺色", - "ControllerSettingsConfigureGeneral": "配置", - "ControllerSettingsRumble": "震動", - "ControllerSettingsRumbleStrongMultiplier": "強震動調節", - "ControllerSettingsRumbleWeakMultiplier": "弱震動調節", - "DialogMessageSaveNotAvailableMessage": "沒有 {0} [{1:x16}] 的存檔", - "DialogMessageSaveNotAvailableCreateSaveMessage": "您想為這款遊戲建立存檔嗎?", - "DialogConfirmationTitle": "Ryujinx - 確認", - "DialogUpdaterTitle": "Ryujinx - 更新程式", - "DialogErrorTitle": "Ryujinx - 錯誤", - "DialogWarningTitle": "Ryujinx - 警告", - "DialogExitTitle": "Ryujinx - 結束", - "DialogErrorMessage": "Ryujinx 遇到了錯誤", - "DialogExitMessage": "您確定要關閉 Ryujinx 嗎?", - "DialogExitSubMessage": "所有未儲存的資料將會遺失!", - "DialogMessageCreateSaveErrorMessage": "建立指定的存檔時出現錯誤: {0}", - "DialogMessageFindSaveErrorMessage": "尋找指定的存檔時出現錯誤: {0}", - "FolderDialogExtractTitle": "選擇要解壓到的資料夾", - "DialogNcaExtractionMessage": "從 {1} 提取 {0} 分區...", - "DialogNcaExtractionTitle": "Ryujinx - NCA 分區提取器", - "DialogNcaExtractionMainNcaNotFoundErrorMessage": "提取失敗。所選檔案中不存在主 NCA 檔案。", - "DialogNcaExtractionCheckLogErrorMessage": "提取失敗。請閱讀日誌檔案了解更多資訊。", - "DialogNcaExtractionSuccessMessage": "提取成功。", - "DialogUpdaterConvertFailedMessage": "無法轉換目前的 Ryujinx 版本。", - "DialogUpdaterCancelUpdateMessage": "取消更新!", - "DialogUpdaterAlreadyOnLatestVersionMessage": "您已經在使用最新版本的 Ryujinx!", - "DialogUpdaterFailedToGetVersionMessage": "嘗試從 GitHub Release 取得發布資訊時發生錯誤。如果 GitHub Actions 正在編譯新版本,則可能會出現這種情況。請幾分鐘後再試一次。", - "DialogUpdaterConvertFailedGithubMessage": "無法轉換從 Github Release 接收到的 Ryujinx 版本。", - "DialogUpdaterDownloadingMessage": "正在下載更新...", - "DialogUpdaterExtractionMessage": "正在提取更新...", - "DialogUpdaterRenamingMessage": "重新命名更新...", - "DialogUpdaterAddingFilesMessage": "加入新更新...", - "DialogUpdaterCompleteMessage": "更新成功!", - "DialogUpdaterRestartMessage": "您現在要重新啟動 Ryujinx 嗎?", - "DialogUpdaterNoInternetMessage": "您沒有連線到網際網路!", - "DialogUpdaterNoInternetSubMessage": "請確認您的網際網路連線正常!", - "DialogUpdaterDirtyBuildMessage": "您無法更新非官方版本的 Ryujinx!", - "DialogUpdaterDirtyBuildSubMessage": "如果您正在尋找受官方支援的版本,請從 https://ryujinx.org/ 下載 Ryujinx。", - "DialogRestartRequiredMessage": "需要重新啟動", - "DialogThemeRestartMessage": "佈景主題設定已儲存。需要重新啟動才能套用主題。", - "DialogThemeRestartSubMessage": "您要重新啟動嗎", - "DialogFirmwareInstallEmbeddedMessage": "您想安裝遊戲內建的韌體嗎? (韌體 {0})", - "DialogFirmwareInstallEmbeddedSuccessMessage": "未找到已安裝的韌體,但 Ryujinx 可以從現有的遊戲安裝韌體{0}。\n模擬器現在可以執行。", - "DialogFirmwareNoFirmwareInstalledMessage": "未安裝韌體", - "DialogFirmwareInstalledMessage": "已安裝韌體{0}", - "DialogInstallFileTypesSuccessMessage": "成功安裝檔案類型!", - "DialogInstallFileTypesErrorMessage": "無法安裝檔案類型。", - "DialogUninstallFileTypesSuccessMessage": "成功移除檔案類型!", - "DialogUninstallFileTypesErrorMessage": "無法移除檔案類型。", - "DialogOpenSettingsWindowLabel": "開啟設定視窗", - "DialogControllerAppletTitle": "控制器小程式", - "DialogMessageDialogErrorExceptionMessage": "顯示訊息對話方塊時出現錯誤: {0}", - "DialogSoftwareKeyboardErrorExceptionMessage": "顯示軟體鍵盤時出現錯誤: {0}", - "DialogErrorAppletErrorExceptionMessage": "顯示錯誤對話方塊時出現錯誤: {0}", - "DialogUserErrorDialogMessage": "{0}: {1}", - "DialogUserErrorDialogInfoMessage": "\n有關如何修復此錯誤的更多資訊,請參閱我們的設定指南。", - "DialogUserErrorDialogTitle": "Ryujinx 錯誤 ({0})", - "DialogAmiiboApiTitle": "Amiibo API", - "DialogAmiiboApiFailFetchMessage": "從 API 取得資訊時出現錯誤。", - "DialogAmiiboApiConnectErrorMessage": "無法連接 Amiibo API 伺服器。服務可能已停機,或者您可能需要確認網際網路連線是否在線上。", - "DialogProfileInvalidProfileErrorMessage": "設定檔 {0} 與目前輸入配置系統不相容。", - "DialogProfileDefaultProfileOverwriteErrorMessage": "無法覆蓋預設設定檔", - "DialogProfileDeleteProfileTitle": "刪除設定檔", - "DialogProfileDeleteProfileMessage": "此動作不可復原,您確定要繼續嗎?", - "DialogWarning": "警告", - "DialogPPTCDeletionMessage": "您將在下一次啟動時佇列重建以下遊戲的 PPTC:\n\n{0}\n\n您確定要繼續嗎?", - "DialogPPTCDeletionErrorMessage": "在 {0} 清除 PPTC 快取時出錯: {1}", - "DialogShaderDeletionMessage": "您將刪除以下遊戲的著色器快取:\n\n{0}\n\n您確定要繼續嗎?", - "DialogShaderDeletionErrorMessage": "在 {0} 清除著色器快取時出錯: {1}", - "DialogRyujinxErrorMessage": "Ryujinx 遇到錯誤", - "DialogInvalidTitleIdErrorMessage": "UI 錯誤: 所選遊戲沒有有效的遊戲 ID", - "DialogFirmwareInstallerFirmwareNotFoundErrorMessage": "在 {0} 中未發現有效的系統韌體。", - "DialogFirmwareInstallerFirmwareInstallTitle": "安裝韌體 {0}", - "DialogFirmwareInstallerFirmwareInstallMessage": "將安裝系統版本 {0}。", - "DialogFirmwareInstallerFirmwareInstallSubMessage": "\n\n這將取代目前的系統版本 {0}。", - "DialogFirmwareInstallerFirmwareInstallConfirmMessage": "\n\n您確定要繼續嗎?", - "DialogFirmwareInstallerFirmwareInstallWaitMessage": "正在安裝韌體...", - "DialogFirmwareInstallerFirmwareInstallSuccessMessage": "成功安裝系統版本 {0}。", - "DialogUserProfileDeletionWarningMessage": "如果刪除選取的設定檔,將無法開啟其他設定檔", - "DialogUserProfileDeletionConfirmMessage": "您是否要刪除所選設定檔", - "DialogUserProfileUnsavedChangesTitle": "警告 - 未儲存的變更", - "DialogUserProfileUnsavedChangesMessage": "您對該使用者設定檔所做的變更尚未儲存。", - "DialogUserProfileUnsavedChangesSubMessage": "您確定要放棄變更嗎?", - "DialogControllerSettingsModifiedConfirmMessage": "目前控制器設定已更新。", - "DialogControllerSettingsModifiedConfirmSubMessage": "您想要儲存嗎?", - "DialogLoadFileErrorMessage": "{0}。出錯檔案: {1}", - "DialogModAlreadyExistsMessage": "模組已經存在", - "DialogModInvalidMessage": "指定資料夾不包含模組!", - "DialogModDeleteNoParentMessage": "刪除失敗: 無法找到模組「{0}」的父資料夾!", - "DialogDlcNoDlcErrorMessage": "指定檔案不包含所選遊戲的 DLC!", - "DialogPerformanceCheckLoggingEnabledMessage": "您已啟用追蹤日誌,該功能僅供開發者使用。", - "DialogPerformanceCheckLoggingEnabledConfirmMessage": "為獲得最佳效能,建議停用追蹤日誌。您是否要立即停用追蹤日誌嗎?", - "DialogPerformanceCheckShaderDumpEnabledMessage": "您已啟用著色器傾印,該功能僅供開發者使用。", - "DialogPerformanceCheckShaderDumpEnabledConfirmMessage": "為獲得最佳效能,建議停用著色器傾印。您是否要立即停用著色器傾印嗎?", - "DialogLoadAppGameAlreadyLoadedMessage": "已載入此遊戲", - "DialogLoadAppGameAlreadyLoadedSubMessage": "請停止模擬或關閉模擬器,然後再啟動另一款遊戲。", - "DialogUpdateAddUpdateErrorMessage": "指定檔案不包含所選遊戲的更新!", - "DialogSettingsBackendThreadingWarningTitle": "警告 - 後端執行緒處理中", - "DialogSettingsBackendThreadingWarningMessage": "變更此選項後,必須重新啟動 Ryujinx 才能完全生效。使用 Ryujinx 的多執行緒功能時,可能需要手動停用驅動程式本身的多執行緒功能,這取決於您的平台。", - "DialogModManagerDeletionWarningMessage": "您將刪除模組: {0}\n\n您確定要繼續嗎?", - "DialogModManagerDeletionAllWarningMessage": "您即將刪除此遊戲的所有模組。\n\n您確定要繼續嗎?", - "SettingsTabGraphicsFeaturesOptions": "功能", - "SettingsTabGraphicsBackendMultithreading": "圖形後端多執行緒:", - "CommonAuto": "自動", - "CommonOff": "關閉", - "CommonOn": "開啟", - "InputDialogYes": "是", - "InputDialogNo": "否", - "DialogProfileInvalidProfileNameErrorMessage": "檔案名稱包含無效字元。請重試。", - "MenuBarOptionsPauseEmulation": "暫停", - "MenuBarOptionsResumeEmulation": "繼續", - "AboutUrlTooltipMessage": "在預設瀏覽器中開啟 Ryujinx 網站。", - "AboutDisclaimerMessage": "Ryujinx 和 Nintendo™\n或其任何合作夥伴完全沒有關聯。", - "AboutAmiiboDisclaimerMessage": "我們在 Amiibo 模擬中\n使用了 AmiiboAPI (www.amiiboapi.com)。", - "AboutPatreonUrlTooltipMessage": "在預設瀏覽器中開啟 Ryujinx 的 Patreon 網頁。", - "AboutGithubUrlTooltipMessage": "在預設瀏覽器中開啟 Ryujinx 的 GitHub 網頁。", - "AboutDiscordUrlTooltipMessage": "在預設瀏覽器中開啟 Ryujinx 的 Discord 邀請連結。", - "AboutTwitterUrlTooltipMessage": "在預設瀏覽器中開啟 Ryujinx 的 Twitter 網頁。", - "AboutRyujinxAboutTitle": "關於:", - "AboutRyujinxAboutContent": "Ryujinx 是一款 Nintendo Switch™ 模擬器。\n請在 Patreon 上支持我們。\n關注我們的 Twitter 或 Discord 取得所有最新消息。\n對於有興趣貢獻的開發者,可以在我們的 GitHub 或 Discord 上了解更多資訊。", - "AboutRyujinxMaintainersTitle": "維護者:", - "AboutRyujinxMaintainersContentTooltipMessage": "在預設瀏覽器中開啟貢獻者的網頁", - "AboutRyujinxSupprtersTitle": "Patreon 支持者:", - "AmiiboSeriesLabel": "Amiibo 系列", - "AmiiboCharacterLabel": "角色", - "AmiiboScanButtonLabel": "掃描", - "AmiiboOptionsShowAllLabel": "顯示所有 Amiibo", - "AmiiboOptionsUsRandomTagLabel": "補釘修正:使用隨機標記的 Uuid", - "DlcManagerTableHeadingEnabledLabel": "已啟用", - "DlcManagerTableHeadingTitleIdLabel": "遊戲 ID", - "DlcManagerTableHeadingContainerPathLabel": "容器路徑", - "DlcManagerTableHeadingFullPathLabel": "完整路徑", - "DlcManagerRemoveAllButton": "全部刪除", - "DlcManagerEnableAllButton": "全部啟用", - "DlcManagerDisableAllButton": "全部停用", - "ModManagerDeleteAllButton": "全部刪除", - "MenuBarOptionsChangeLanguage": "變更語言", - "MenuBarShowFileTypes": "顯示檔案類型", - "CommonSort": "排序", - "CommonShowNames": "顯示名稱", - "CommonFavorite": "我的最愛", - "OrderAscending": "從小到大", - "OrderDescending": "從大到小", - "SettingsTabGraphicsFeatures": "功能與改進", - "ErrorWindowTitle": "錯誤視窗", - "ToggleDiscordTooltip": "啟用或關閉 Discord 動態狀態展示", - "AddGameDirBoxTooltip": "輸入要新增到清單中的遊戲資料夾", - "AddGameDirTooltip": "新增遊戲資料夾到清單中", - "RemoveGameDirTooltip": "移除選取的遊戲資料夾", - "CustomThemeCheckTooltip": "為圖形使用者介面使用自訂 Avalonia 佈景主題,變更模擬器功能表的外觀", - "CustomThemePathTooltip": "自訂 GUI 佈景主題的路徑", - "CustomThemeBrowseTooltip": "瀏覽自訂 GUI 佈景主題", - "DockModeToggleTooltip": "底座模式可使模擬系統表現為底座的 Nintendo Switch。這可以提高大多數遊戲的圖形保真度。反之,停用該模式將使模擬系統表現為手提模式的 Nintendo Switch,從而降低圖形品質。\n\n如果計劃使用底座模式,請配置玩家 1 控制;如果計劃使用手提模式,請配置手提控制。\n\n如果不確定,請保持開啟狀態。", - "DirectKeyboardTooltip": "支援直接鍵盤存取 (HID)。遊戲可將鍵盤作為文字輸入裝置。\n\n僅適用於在 Switch 硬體上原生支援使用鍵盤的遊戲。\n\n如果不確定,請保持關閉狀態。", - "DirectMouseTooltip": "支援滑鼠直接存取 (HID)。遊戲可將滑鼠作為指向裝置使用。\n\n僅適用於在 Switch 硬體上原生支援滑鼠控制的遊戲,這類遊戲很少。\n\n啟用後,觸控螢幕功能可能無法使用。\n\n如果不確定,請保持關閉狀態。", - "RegionTooltip": "變更系統區域", - "LanguageTooltip": "變更系統語言", - "TimezoneTooltip": "變更系統時區", - "TimeTooltip": "變更系統時鐘", - "VSyncToggleTooltip": "模擬遊戲機的垂直同步。對大多數遊戲來說,它本質上是一個幀率限制器;停用它可能會導致遊戲以更高的速度執行,或使載入畫面耗時更長或卡住。\n\n可以在遊戲中使用快速鍵進行切換 (預設為 F1)。如果您打算停用,我們建議您這樣做。\n\n如果不確定,請保持開啟狀態。", - "PptcToggleTooltip": "儲存已轉譯的 JIT 函數,這樣每次載入遊戲時就無需再轉譯這些函數。\n\n減少遊戲首次啟動後的卡頓現象,並大大加快啟動時間。\n\n如果不確定,請保持開啟狀態。", - "FsIntegrityToggleTooltip": "在啟動遊戲時檢查損壞的檔案,如果檢測到損壞的檔案,則在日誌中顯示雜湊值錯誤。\n\n對效能沒有影響,旨在幫助排除故障。\n\n如果不確定,請保持開啟狀態。", - "AudioBackendTooltip": "變更用於繪製音訊的後端。\n\nSDL2 是首選,而 OpenAL 和 SoundIO 則作為備用。虛設 (Dummy) 將沒有聲音。\n\n如果不確定,請設定為 SDL2。", - "MemoryManagerTooltip": "變更客體記憶體的映射和存取方式。這會極大地影響模擬 CPU 效能。\n\n如果不確定,請設定為主體略過檢查模式。", - "MemoryManagerSoftwareTooltip": "使用軟體分頁表進行位址轉換。精度最高,但效能最差。", - "MemoryManagerHostTooltip": "直接映射主體位址空間中的記憶體。更快的 JIT 編譯和執行速度。", - "MemoryManagerUnsafeTooltip": "直接映射記憶體,但在存取前不封鎖客體位址空間內的位址。速度更快,但相對不安全。訪客應用程式可以從 Ryujinx 中的任何地方存取記憶體,因此只能使用該模式執行您信任的程式。", - "UseHypervisorTooltip": "使用 Hypervisor 取代 JIT。使用時可大幅提高效能,但在目前狀態下可能不穩定。", - "DRamTooltip": "利用另一種 MemoryMode 配置來模仿 Switch 開發模式。\n\n這僅對高解析度紋理套件或 4K 解析度模組有用。不會提高效能。\n\n如果不確定,請保持關閉狀態。", - "IgnoreMissingServicesTooltip": "忽略未實現的 Horizon OS 服務。這可能有助於在啟動某些遊戲時避免崩潰。\n\n如果不確定,請保持關閉狀態。", - "GraphicsBackendThreadingTooltip": "在第二個執行緒上執行圖形後端指令。\n\n在本身不支援多執行緒的 GPU 驅動程式上,可加快著色器編譯、減少卡頓並提高效能。在支援多執行緒的驅動程式上效能略有提升。\n\n如果不確定,請設定為自動。", - "GalThreadingTooltip": "在第二個執行緒上執行圖形後端指令。\n\n在本身不支援多執行緒的 GPU 驅動程式上,可加快著色器編譯、減少卡頓並提高效能。在支援多執行緒的驅動程式上效能略有提升。\n\n如果不確定,請設定為自動。", - "ShaderCacheToggleTooltip": "儲存磁碟著色器快取,減少後續執行時的卡頓。\n\n如果不確定,請保持開啟狀態。", - "ResolutionScaleTooltip": "使用倍數提升遊戲的繪製解析度。\n\n少數遊戲可能無法使用此功能,即使提高解析度也會顯得像素化;對於這些遊戲,您可能需要找到去除反鋸齒或提高內部繪製解析度的模組。對於後者,您可能需要選擇原生。\n\n此選項可在遊戲執行時透過點選下方的「套用」進行變更;您只需將設定視窗移到一旁,然後進行試驗,直到找到您喜歡的遊戲效果。\n\n請記住,4 倍幾乎對任何設定都是過度的。", - "ResolutionScaleEntryTooltip": "浮點解析度刻度,如 1.5。非整數刻度更容易出現問題或崩潰。", - "AnisotropyTooltip": "各向異性過濾等級。設定為自動可使用遊戲要求的值。", - "AspectRatioTooltip": "套用於繪製器視窗的長寬比。\n\n只有在遊戲中使用長寬比模組時才可變更,否則圖形會被拉伸。\n\n如果不確定,請保持 16:9 狀態。", - "ShaderDumpPathTooltip": "圖形著色器傾印路徑", - "FileLogTooltip": "將控制台日誌儲存到磁碟上的日誌檔案中。不會影響效能。", - "StubLogTooltip": "在控制台中輸出日誌訊息。不會影響效能。", - "InfoLogTooltip": "在控制台中輸出資訊日誌訊息。不會影響效能。", - "WarnLogTooltip": "在控制台中輸出警告日誌訊息。不會影響效能。", - "ErrorLogTooltip": "在控制台中輸出錯誤日誌訊息。不會影響效能。", - "TraceLogTooltip": "在控制台中輸出追蹤日誌訊息。不會影響效能。", - "GuestLogTooltip": "在控制台中輸出客體日誌訊息。不會影響效能。", - "FileAccessLogTooltip": "在控制台中輸出檔案存取日誌訊息。", - "FSAccessLogModeTooltip": "啟用檔案系統存取日誌輸出到控制台中。可能的模式為 0 到 3", - "DeveloperOptionTooltip": "謹慎使用", - "OpenGlLogLevel": "需要啟用適當的日誌等級", - "DebugLogTooltip": "在控制台中輸出偵錯日誌訊息。\n\n只有在人員特別指示的情況下才能使用,因為這會導致日誌難以閱讀,並降低模擬器效能。", - "LoadApplicationFileTooltip": "開啟檔案總管,選擇與 Switch 相容的檔案來載入", - "LoadApplicationFolderTooltip": "開啟檔案總管,選擇與 Switch 相容且未封裝的應用程式來載入", - "OpenRyujinxFolderTooltip": "開啟 Ryujinx 檔案系統資料夾", - "OpenRyujinxLogsTooltip": "開啟日誌被寫入的資料夾", - "ExitTooltip": "結束 Ryujinx", - "OpenSettingsTooltip": "開啟設定視窗", - "OpenProfileManagerTooltip": "開啟使用者設定檔管理員視窗", - "StopEmulationTooltip": "停止模擬目前遊戲,返回遊戲選擇介面", - "CheckUpdatesTooltip": "檢查 Ryujinx 的更新", - "OpenAboutTooltip": "開啟關於視窗", - "GridSize": "網格尺寸", - "GridSizeTooltip": "調整網格的大小", - "SettingsTabSystemSystemLanguageBrazilianPortuguese": "巴西葡萄牙文", - "AboutRyujinxContributorsButtonHeader": "查看所有貢獻者", - "SettingsTabSystemAudioVolume": "音量:", - "AudioVolumeTooltip": "調節音量", - "SettingsTabSystemEnableInternetAccess": "訪客網際網路存取/區域網路模式", - "EnableInternetAccessTooltip": "允許模擬應用程式連線網際網路。\n\n當啟用此功能且系統連線到同一接入點時,具有區域網路模式的遊戲可相互連線。這也包括真正的遊戲機。\n\n不允許連接 Nintendo 伺服器。可能會導致某些嘗試連線網際網路的遊戲崩潰。\n\n如果不確定,請保持關閉狀態。", - "GameListContextMenuManageCheatToolTip": "管理密技", - "GameListContextMenuManageCheat": "管理密技", - "GameListContextMenuManageModToolTip": "管理模組", - "GameListContextMenuManageMod": "管理模組", - "ControllerSettingsStickRange": "範圍:", - "DialogStopEmulationTitle": "Ryujinx - 停止模擬", - "DialogStopEmulationMessage": "您確定要停止模擬嗎?", - "SettingsTabCpu": "CPU", - "SettingsTabAudio": "音訊", - "SettingsTabNetwork": "網路", - "SettingsTabNetworkConnection": "網路連線", - "SettingsTabCpuCache": "CPU 快取", - "SettingsTabCpuMemory": "CPU 模式", - "DialogUpdaterFlatpakNotSupportedMessage": "請透過 Flathub 更新 Ryujinx。", - "UpdaterDisabledWarningTitle": "更新已停用!", - "ControllerSettingsRotate90": "順時針旋轉 90°", - "IconSize": "圖示大小", - "IconSizeTooltip": "變更遊戲圖示的大小", - "MenuBarOptionsShowConsole": "顯示控制台", - "ShaderCachePurgeError": "在 {0} 清除著色器快取時出錯: {1}", - "UserErrorNoKeys": "找不到金鑰", - "UserErrorNoFirmware": "找不到韌體", - "UserErrorFirmwareParsingFailed": "韌體解析錯誤", - "UserErrorApplicationNotFound": "找不到應用程式", - "UserErrorUnknown": "未知錯誤", - "UserErrorUndefined": "未定義錯誤", - "UserErrorNoKeysDescription": "Ryujinx 無法找到您的「prod.keys」檔案", - "UserErrorNoFirmwareDescription": "Ryujinx 無法找到已安裝的任何韌體", - "UserErrorFirmwareParsingFailedDescription": "Ryujinx 無法解析所提供的韌體。這通常是由於金鑰過時造成的。", - "UserErrorApplicationNotFoundDescription": "Ryujinx 無法在指定路徑下找到有效的應用程式。", - "UserErrorUnknownDescription": "發生未知錯誤!", - "UserErrorUndefinedDescription": "發生未定義錯誤! 這種情況不應該發生,請聯絡開發人員!", - "OpenSetupGuideMessage": "開啟設定指南", - "NoUpdate": "沒有更新", - "TitleUpdateVersionLabel": "版本 {0}", - "RyujinxInfo": "Ryujinx - 資訊", - "RyujinxConfirm": "Ryujinx - 確認", - "FileDialogAllTypes": "全部類型", - "Never": "從不", - "SwkbdMinCharacters": "長度必須至少為 {0} 個字元", - "SwkbdMinRangeCharacters": "長度必須為 {0} 到 {1} 個字元", - "SoftwareKeyboard": "軟體鍵盤", - "SoftwareKeyboardModeNumeric": "必須是 0 到 9 或「.」", - "SoftwareKeyboardModeAlphabet": "必須是「非中日韓字元」 (non CJK)", - "SoftwareKeyboardModeASCII": "必須是 ASCII 文字", - "ControllerAppletControllers": "支援的控制器:", - "ControllerAppletPlayers": "玩家:", - "ControllerAppletDescription": "您目前的配置無效。開啟設定並重新配置輸入。", - "ControllerAppletDocked": "已設定底座模式。手提控制應該停用。", - "UpdaterRenaming": "正在重新命名舊檔案...", - "UpdaterRenameFailed": "更新程式無法重新命名檔案: {0}", - "UpdaterAddingFiles": "正在加入新檔案...", - "UpdaterExtracting": "正在提取更新...", - "UpdaterDownloading": "正在下載更新...", - "Game": "遊戲", - "Docked": "底座模式", - "Handheld": "手提模式", - "ConnectionError": "連線錯誤。", - "AboutPageDeveloperListMore": "{0} 等人...", - "ApiError": "API 錯誤。", - "LoadingHeading": "正在載入 {0}", - "CompilingPPTC": "正在編譯 PTC", - "CompilingShaders": "正在編譯著色器", - "AllKeyboards": "所有鍵盤", - "OpenFileDialogTitle": "選取支援的檔案格式", - "OpenFolderDialogTitle": "選取未封裝遊戲的資料夾", - "AllSupportedFormats": "所有支援的格式", - "RyujinxUpdater": "Ryujinx 更新程式", - "SettingsTabHotkeys": "鍵盤快速鍵", - "SettingsTabHotkeysHotkeys": "鍵盤快捷鍵", - "SettingsTabHotkeysToggleVsyncHotkey": "切換垂直同步:", - "SettingsTabHotkeysScreenshotHotkey": "擷取畫面:", - "SettingsTabHotkeysShowUiHotkey": "顯示 UI:", - "SettingsTabHotkeysPauseHotkey": "暫停:", - "SettingsTabHotkeysToggleMuteHotkey": "靜音:", - "ControllerMotionTitle": "體感控制設定", - "ControllerRumbleTitle": "震動設定", - "SettingsSelectThemeFileDialogTitle": "選取佈景主題檔案", - "SettingsXamlThemeFile": "Xaml 佈景主題檔案", - "AvatarWindowTitle": "管理帳戶 - 大頭貼", - "Amiibo": "Amiibo", - "Unknown": "未知", - "Usage": "用途", - "Writable": "可寫入", - "SelectDlcDialogTitle": "選取 DLC 檔案", - "SelectUpdateDialogTitle": "選取更新檔", - "SelectModDialogTitle": "選取模組資料夾", - "UserProfileWindowTitle": "使用者設定檔管理員", - "CheatWindowTitle": "密技管理員", - "DlcWindowTitle": "管理 {0} 的可下載內容 ({1})", - "ModWindowTitle": "管理 {0} 的模組 ({1})", - "UpdateWindowTitle": "遊戲更新管理員", - "CheatWindowHeading": "可用於 {0} [{1}] 的密技", - "BuildId": "組建識別碼:", - "DlcWindowHeading": "{0} 個可下載內容", - "ModWindowHeading": "{0} 模組", - "UserProfilesEditProfile": "編輯所選", - "Cancel": "取消", - "Save": "儲存", - "Discard": "放棄變更", - "Paused": "暫停", - "UserProfilesSetProfileImage": "設定設定檔圖像", - "UserProfileEmptyNameError": "名稱為必填", - "UserProfileNoImageError": "必須設定設定檔圖像", - "GameUpdateWindowHeading": "管理 {0} 的更新 ({1})", - "SettingsTabHotkeysResScaleUpHotkey": "提高解析度:", - "SettingsTabHotkeysResScaleDownHotkey": "降低解析度:", - "UserProfilesName": "名稱:", - "UserProfilesUserId": "使用者 ID:", - "SettingsTabGraphicsBackend": "圖形後端", - "SettingsTabGraphicsBackendTooltip": "選擇模擬器將使用的圖形後端。\n\n只要驅動程式是最新的,Vulkan 對所有現代顯示卡來說都更好用。Vulkan 還能在所有 GPU 廠商上實現更快的著色器編譯 (減少卡頓)。\n\nOpenGL 在舊式 Nvidia GPU、Linux 上的舊式 AMD GPU 或 VRAM 較低的 GPU 上可能會取得更好的效果,不過著色器編譯的卡頓會更嚴重。\n\n如果不確定,請設定為 Vulkan。如果您的 GPU 使用最新的圖形驅動程式也不支援 Vulkan,請設定為 OpenGL。", - "SettingsEnableTextureRecompression": "開啟材質重新壓縮", - "SettingsEnableTextureRecompressionTooltip": "壓縮 ASTC 紋理,以減少 VRAM 占用。\n\n使用這種紋理格式的遊戲包括 Astral Chain、Bayonetta 3、Fire Emblem Engage、Metroid Prime Remastered、Super Mario Bros. Wonder 和 The Legend of Zelda: Tears of the Kingdom。\n\n使用 4GB 或更低 VRAM 的顯示卡在執行這些遊戲時可能會崩潰。\n\n只有在上述遊戲的 VRAM 即將耗盡時才啟用。如果不確定,請保持關閉狀態。", - "SettingsTabGraphicsPreferredGpu": "優先選取的 GPU", - "SettingsTabGraphicsPreferredGpuTooltip": "選擇將與 Vulkan 圖形後端一起使用的顯示卡。\n\n不會影響 OpenGL 將使用的 GPU。\n\n如果不確定,請設定為標記為「dGPU」的 GPU。如果沒有,則保持原狀。", - "SettingsAppRequiredRestartMessage": "需要重新啟動 Ryujinx", - "SettingsGpuBackendRestartMessage": "圖形後端或 GPU 設定已修改。這需要重新啟動才能套用。", - "SettingsGpuBackendRestartSubMessage": "您現在要重新啟動嗎?", - "RyujinxUpdaterMessage": "您想將 Ryujinx 升級到最新版本嗎?", - "SettingsTabHotkeysVolumeUpHotkey": "提高音量:", - "SettingsTabHotkeysVolumeDownHotkey": "降低音量:", - "SettingsEnableMacroHLE": "啟用 Macro HLE", - "SettingsEnableMacroHLETooltip": "GPU 巨集程式碼的進階模擬。\n\n可提高效能,但在某些遊戲中可能會導致圖形閃爍。\n\n如果不確定,請保持開啟狀態。", - "SettingsEnableColorSpacePassthrough": "色彩空間直通", - "SettingsEnableColorSpacePassthroughTooltip": "指示 Vulkan 後端在不指定色彩空間的情況下傳遞色彩資訊。對於使用廣色域顯示器的使用者來說,這可能會帶來更鮮艷的色彩,但代價是犧牲色彩的正確性。", - "VolumeShort": "音量", - "UserProfilesManageSaves": "管理存檔", - "DeleteUserSave": "您想刪除此遊戲的使用者存檔嗎?", - "IrreversibleActionNote": "此動作將無法復原。", - "SaveManagerHeading": "管理 {0} 的存檔 ({1})", - "SaveManagerTitle": "存檔管理員", - "Name": "名稱", - "Size": "大小", - "Search": "搜尋", - "UserProfilesRecoverLostAccounts": "復原遺失的帳戶", - "Recover": "復原", - "UserProfilesRecoverHeading": "發現下列帳戶有一些存檔", - "UserProfilesRecoverEmptyList": "無設定檔可復原", - "GraphicsAATooltip": "對遊戲繪製進行反鋸齒處理。\n\nFXAA 會模糊大部分圖像,而 SMAA 則會嘗試找出鋸齒邊緣並將其平滑化。\n\n不建議與 FSR 縮放濾鏡一起使用。\n\n此選項可在遊戲執行時透過點選下方的「套用」進行變更;您只需將設定視窗移到一旁,然後進行試驗,直到找到您喜歡的遊戲效果。\n\n如果不確定,請選擇無狀態。", - "GraphicsAALabel": "反鋸齒:", - "GraphicsScalingFilterLabel": "縮放過濾器:", - "GraphicsScalingFilterTooltip": "選擇使用解析度縮放時套用的縮放過濾器。\n\n雙線性 (Bilinear) 濾鏡適用於 3D 遊戲,是一個安全的預設選項。\n\n建議像素美術遊戲使用近鄰性 (Nearest) 濾鏡。\n\nFSR 1.0 只是一個銳化濾鏡,不建議與 FXAA 或 SMAA 一起使用。\n\n此選項可在遊戲執行時透過點選下方的「套用」進行變更;您只需將設定視窗移到一旁,然後進行試驗,直到找到您喜歡的遊戲效果。\n\n如果不確定,請保持雙線性 (Bilinear) 狀態。", - "GraphicsScalingFilterBilinear": "雙線性 (Bilinear)", - "GraphicsScalingFilterNearest": "近鄰性 (Nearest)", - "GraphicsScalingFilterFsr": "FSR", - "GraphicsScalingFilterLevelLabel": "日誌等級", - "GraphicsScalingFilterLevelTooltip": "設定 FSR 1.0 銳化等級。越高越清晰。", - "SmaaLow": "低階 SMAA", - "SmaaMedium": "中階 SMAA", - "SmaaHigh": "高階 SMAA", - "SmaaUltra": "超高階 SMAA", - "UserEditorTitle": "編輯使用者", - "UserEditorTitleCreate": "建立使用者", - "SettingsTabNetworkInterface": "網路介面:", - "NetworkInterfaceTooltip": "用於 LAN/LDN 功能的網路介面。\n\n與 VPN 或 XLink Kai 以及支援區域網路的遊戲配合使用,可用於在網路上偽造同網際網路連線。\n\n如果不確定,請保持預設狀態。", - "NetworkInterfaceDefault": "預設", - "PackagingShaders": "封裝著色器", - "AboutChangelogButton": "在 GitHub 上檢視更新日誌", - "AboutChangelogButtonTooltipMessage": "在預設瀏覽器中開啟此版本的更新日誌。", - "SettingsTabNetworkMultiplayer": "多人遊戲", - "MultiplayerMode": "模式:", - "MultiplayerModeTooltip": "變更 LDN 多人遊戲模式。\n\nLdnMitm 將修改遊戲中的本機無線/本機遊戲功能,使其如同區域網路一樣執行,允許與其他安裝了 ldn_mitm 模組的 Ryujinx 實例和已破解的 Nintendo Switch 遊戲機進行本機同網路連線。\n\n多人遊戲要求所有玩家使用相同的遊戲版本 (例如,Super Smash Bros. Ultimate v13.0.1 無法連接 v13.0.0)。\n\n如果不確定,請保持 Disabled (停用) 狀態。", - "MultiplayerModeDisabled": "已停用", - "MultiplayerModeLdnMitm": "ldn_mitm" -} diff --git a/src/Ryujinx/Assets/Styles/Styles.xaml b/src/Ryujinx/Assets/Styles/Styles.xaml index b3a6f59c8..878b5e7f1 100644 --- a/src/Ryujinx/Assets/Styles/Styles.xaml +++ b/src/Ryujinx/Assets/Styles/Styles.xaml @@ -1,7 +1,8 @@  + xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" + xmlns:windowing="clr-namespace:FluentAvalonia.UI.Windowing;assembly=FluentAvalonia"> @@ -43,6 +44,10 @@ + - + + + + + + Header="{ext:Locale MenuBarOptionsStartGamesInFullscreen}"> + Header="{ext:Locale MenuBarOptionsShowConsole}"> - - + + Padding="0" + Header="{ext:Locale MenuBarOptionsChangeLanguage}" + Icon="{ext:Icon fa-solid fa-language}"> + + + + + + Header="{ext:Locale MenuBarShowFileTypes}" /> + Padding="0" + Header="{ext:Locale MenuBarOptionsSettings}" + Icon="{ext:Icon fa-solid fa-gear}" + ToolTip.Tip="{ext:Locale OpenSettingsTooltip}"> + + + + + + ToolTip.Tip="{ext:Locale OpenProfileManagerTooltip}"> + + + + + - + ToolTip.Tip="{ext:Locale StopEmulationTooltip}" /> + + - - - - + + + + - - - + + + + + + + + + + + + + + + + + - - - - - - - + + + Header="{ext:Locale MenuBarHelpCheckForUpdates}" + Icon="{ext:Icon mdi-update}" + ToolTip.Tip="{ext:Locale CheckUpdatesTooltip}" /> - + + + + + diff --git a/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs b/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs index 73ae0df14..fa900be81 100644 --- a/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs +++ b/src/Ryujinx/UI/Views/Main/MainMenuBarView.axaml.cs @@ -2,6 +2,7 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Interactivity; using Avalonia.Threading; +using Gommon; using LibHac.Ncm; using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Ava.Common.Locale; @@ -10,14 +11,14 @@ using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.Windows; using Ryujinx.Common; using Ryujinx.Common.Utilities; -using Ryujinx.Modules; +using Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption; +using Ryujinx.HLE; using Ryujinx.UI.App.Common; using Ryujinx.UI.Common; using Ryujinx.UI.Common.Configuration; using Ryujinx.UI.Common.Helper; using System; using System.Collections.Generic; -using System.IO; using System.Linq; namespace Ryujinx.Ava.UI.Views.Main @@ -31,57 +32,53 @@ namespace Ryujinx.Ava.UI.Views.Main { InitializeComponent(); + RyuLogo.IsVisible = !ConfigurationState.Instance.ShowTitleBar; + ToggleFileTypesMenuItem.ItemsSource = GenerateToggleFileTypeItems(); ChangeLanguageMenuItem.ItemsSource = GenerateLanguageMenuItems(); } - private CheckBox[] GenerateToggleFileTypeItems() - { - List checkBoxes = new(); - - foreach (var item in Enum.GetValues(typeof(FileTypes))) - { - string fileName = Enum.GetName(typeof(FileTypes), item); - checkBoxes.Add(new CheckBox - { - Content = $".{fileName}", - IsChecked = ((FileTypes)item).GetConfigValue(ConfigurationState.Instance.UI.ShownFileTypes), - Command = MiniCommand.Create(() => Window.ToggleFileType(fileName)), - }); - } - - return checkBoxes.ToArray(); - } + private CheckBox[] GenerateToggleFileTypeItems() => + Enum.GetValues() + .Select(it => (FileName: Enum.GetName(it)!, FileType: it)) + .Select(it => + new CheckBox + { + Content = $".{it.FileName}", + IsChecked = it.FileType.GetConfigValue(ConfigurationState.Instance.UI.ShownFileTypes), + Command = MiniCommand.Create(() => Window.ToggleFileType(it.FileName)) + } + ).ToArray(); private static MenuItem[] GenerateLanguageMenuItems() { List menuItems = new(); - string localePath = "Ryujinx/Assets/Locales"; - string localeExt = ".json"; + string localePath = "Ryujinx/Assets/locales.json"; - string[] localesPath = EmbeddedResources.GetAllAvailableResources(localePath, localeExt); + string languageJson = EmbeddedResources.ReadAllText(localePath); - Array.Sort(localesPath); + LocalesJson locales = JsonHelper.Deserialize(languageJson, LocalesJsonContext.Default.LocalesJson); - foreach (string locale in localesPath) + foreach (string language in locales.Languages) { - string languageCode = Path.GetFileNameWithoutExtension(locale).Split('.').Last(); - string languageJson = EmbeddedResources.ReadAllText($"{localePath}/{languageCode}{localeExt}"); - var strings = JsonHelper.Deserialize(languageJson, CommonJsonContext.Default.StringDictionary); + int index = locales.Locales.FindIndex(x => x.ID == "Language"); + string languageName; - if (!strings.TryGetValue("Language", out string languageName)) + if (index == -1) { - languageName = languageCode; + languageName = language; + } + else + { + languageName = locales.Locales[index].Translations[language] == "" ? language : locales.Locales[index].Translations[language]; } MenuItem menuItem = new() { - Header = languageName, - Command = MiniCommand.Create(() => - { - MainWindowViewModel.ChangeLanguage(languageCode); - }), + Padding = new Thickness(10, 0, 0, 0), + Header = " " + languageName, + Command = MiniCommand.Create(() => MainWindowViewModel.ChangeLanguage(language)) }; menuItems.Add(menuItem); @@ -97,25 +94,23 @@ namespace Ryujinx.Ava.UI.Views.Main if (VisualRoot is MainWindow window) { Window = window; + DataContext = ViewModel = window.ViewModel; } - - ViewModel = Window.ViewModel; - DataContext = ViewModel; } private async void StopEmulation_Click(object sender, RoutedEventArgs e) { - await Window.ViewModel.AppHost?.ShowExitPrompt(); + await ViewModel.AppHost?.ShowExitPrompt().OrCompleted()!; } private void PauseEmulation_Click(object sender, RoutedEventArgs e) { - Window.ViewModel.AppHost?.Pause(); + ViewModel.AppHost?.Pause(); } private void ResumeEmulation_Click(object sender, RoutedEventArgs e) { - Window.ViewModel.AppHost?.Resume(); + ViewModel.AppHost?.Resume(); } public async void OpenSettings(object sender, RoutedEventArgs e) @@ -131,51 +126,37 @@ namespace Ryujinx.Ava.UI.Views.Main public async void OpenMiiApplet(object sender, RoutedEventArgs e) { - string contentPath = ViewModel.ContentManager.GetInstalledContentPath(0x0100000000001009, StorageId.BuiltInSystem, NcaContentType.Program); + const string AppletName = "miiEdit"; + const ulong AppletProgramId = 0x0100000000001009; + const string AppletVersion = "1.0.0"; + + string contentPath = ViewModel.ContentManager.GetInstalledContentPath(AppletProgramId, StorageId.BuiltInSystem, NcaContentType.Program); if (!string.IsNullOrEmpty(contentPath)) { ApplicationData applicationData = new() { - Name = "miiEdit", - Id = 0x0100000000001009, - Path = contentPath, + Name = AppletName, + Id = AppletProgramId, + Path = contentPath }; + + var nacpData = StructHelpers.CreateCustomNacpData(AppletName, AppletVersion); - await ViewModel.LoadApplication(applicationData, ViewModel.IsFullScreen || ViewModel.StartGamesInFullscreen); + await ViewModel.LoadApplication(applicationData, ViewModel.IsFullScreen || ViewModel.StartGamesInFullscreen, nacpData); } } public async void OpenAmiiboWindow(object sender, RoutedEventArgs e) - { - if (!ViewModel.IsAmiiboRequested) - { - return; - } + => await ViewModel.OpenAmiiboWindow(); - if (ViewModel.AppHost.Device.System.SearchingForAmiibo(out int deviceId)) - { - string titleId = ViewModel.AppHost.Device.Processes.ActiveApplication.ProgramIdText.ToUpper(); - AmiiboWindow window = new(ViewModel.ShowAll, ViewModel.LastScannedAmiiboId, titleId); - - await window.ShowDialog(Window); - - if (window.IsScanned) - { - ViewModel.ShowAll = window.ViewModel.ShowAllAmiibo; - ViewModel.LastScannedAmiiboId = window.ScannedAmiibo.GetId(); - - ViewModel.AppHost.Device.System.ScanAmiibo(deviceId, ViewModel.LastScannedAmiiboId, window.ViewModel.UseRandomUuid); - } - } - } + public async void OpenBinFile(object sender, RoutedEventArgs e) + => await ViewModel.OpenBinFile(); public async void OpenCheatManagerForCurrentApp(object sender, RoutedEventArgs e) { if (!ViewModel.IsGameRunning) - { return; - } string name = ViewModel.AppHost.Device.Processes.ActiveApplication.ApplicationControlProperties.Title[(int)ViewModel.AppHost.Device.System.State.DesiredTitleLanguage].NameString.ToString(); @@ -183,7 +164,7 @@ namespace Ryujinx.Ava.UI.Views.Main Window.VirtualFileSystem, ViewModel.AppHost.Device.Processes.ActiveApplication.ProgramIdText, name, - Window.ViewModel.SelectedApplication.Path).ShowDialog(Window); + ViewModel.SelectedApplication.Path).ShowDialog(Window); ViewModel.AppHost.Device.EnableCheats(); } @@ -191,85 +172,74 @@ namespace Ryujinx.Ava.UI.Views.Main private void ScanAmiiboMenuItem_AttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e) { if (sender is MenuItem) - { - ViewModel.IsAmiiboRequested = Window.ViewModel.AppHost.Device.System.SearchingForAmiibo(out _); - } + ViewModel.IsAmiiboRequested = ViewModel.AppHost.Device.System.SearchingForAmiibo(out _); + } + + private void ScanBinAmiiboMenuItem_AttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e) + { + if (sender is MenuItem) + ViewModel.IsAmiiboBinRequested = ViewModel.IsAmiiboRequested && AmiiboBinReader.HasAmiiboKeyFile; } private async void InstallFileTypes_Click(object sender, RoutedEventArgs e) { - if (FileAssociationHelper.Install()) - { + ViewModel.AreMimeTypesRegistered = FileAssociationHelper.Install(); + if (ViewModel.AreMimeTypesRegistered) await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogInstallFileTypesSuccessMessage], string.Empty, LocaleManager.Instance[LocaleKeys.InputDialogOk], string.Empty, string.Empty); - } else - { await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogInstallFileTypesErrorMessage]); - } } private async void UninstallFileTypes_Click(object sender, RoutedEventArgs e) { - if (FileAssociationHelper.Uninstall()) - { + ViewModel.AreMimeTypesRegistered = !FileAssociationHelper.Uninstall(); + if (!ViewModel.AreMimeTypesRegistered) await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUninstallFileTypesSuccessMessage], string.Empty, LocaleManager.Instance[LocaleKeys.InputDialogOk], string.Empty, string.Empty); - } else - { await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUninstallFileTypesErrorMessage]); - } } private async void ChangeWindowSize_Click(object sender, RoutedEventArgs e) { - if (sender is MenuItem item) + if (sender is not MenuItem { Tag: string resolution }) + return; + + (int resolutionWidth, int resolutionHeight) = resolution.Split(' ', 2) + .Into(parts => + (int.Parse(parts[0]), int.Parse(parts[1])) + ); + + // Correctly size window when 'TitleBar' is enabled (Nov. 14, 2024) + double barsHeight = ((Window.StatusBarHeight + Window.MenuBarHeight) + + (ConfigurationState.Instance.ShowTitleBar ? (int)Window.TitleBar.Height : 0)); + + double windowWidthScaled = (resolutionWidth * Program.WindowScaleFactor); + double windowHeightScaled = ((resolutionHeight + barsHeight) * Program.WindowScaleFactor); + + await Dispatcher.UIThread.InvokeAsync(() => { - int height; - int width; + ViewModel.WindowState = WindowState.Normal; - switch (item.Tag) - { - case "720": - height = 720; - width = 1280; - break; - - case "1080": - height = 1080; - width = 1920; - break; - - default: - throw new ArgumentNullException($"Invalid Tag for {item}"); - } - - await Dispatcher.UIThread.InvokeAsync(() => - { - ViewModel.WindowState = WindowState.Normal; - - height += (int)Window.StatusBarHeight + (int)Window.MenuBarHeight; - - Window.Arrange(new Rect(Window.Position.X, Window.Position.Y, width, height)); - }); - } + Window.Arrange(new Rect(Window.Position.X, Window.Position.Y, windowWidthScaled, windowHeightScaled)); + }); } public async void CheckForUpdates(object sender, RoutedEventArgs e) { if (Updater.CanUpdate(true)) - { - await Updater.BeginParse(Window, true); - } + await Updater.BeginUpdateAsync(true); } - public async void OpenAboutWindow(object sender, RoutedEventArgs e) + private void MenuItem_OnClick(object sender, RoutedEventArgs e) { - await AboutWindow.Show(); + if (sender is MenuItem { Tag: string url }) + OpenHelper.OpenUrl(url); } - public void CloseWindow(object sender, RoutedEventArgs e) - { - Window.Close(); - } + public async void OpenXCITrimmerWindow(object sender, RoutedEventArgs e) => await XCITrimmerWindow.Show(ViewModel); + + public async void OpenAboutWindow(object sender, RoutedEventArgs e) => await AboutWindow.Show(); + + public void CloseWindow(object sender, RoutedEventArgs e) => Window.Close(); } } diff --git a/src/Ryujinx/UI/Views/Main/MainStatusBarView.axaml b/src/Ryujinx/UI/Views/Main/MainStatusBarView.axaml index f9e192e62..6e72a8b4b 100644 --- a/src/Ryujinx/UI/Views/Main/MainStatusBarView.axaml +++ b/src/Ryujinx/UI/Views/Main/MainStatusBarView.axaml @@ -1,10 +1,10 @@ - - - - - - - + IsVisible="{Binding ShowMenuAndStatusBar}" + ColumnDefinitions="Auto,Auto,*,Auto,Auto"> - - - - - - + + ToolTip.Tip="{ext:Locale AspectRatioTooltip}"> + + + + + + + + + + + diff --git a/src/Ryujinx/UI/Views/Settings/SettingsUIView.axaml.cs b/src/Ryujinx/UI/Views/Settings/SettingsUIView.axaml.cs index 996d15cdb..3532e1855 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsUIView.axaml.cs +++ b/src/Ryujinx/UI/Views/Settings/SettingsUIView.axaml.cs @@ -2,8 +2,8 @@ using Avalonia.Controls; using Avalonia.Interactivity; using Avalonia.Platform.Storage; using Avalonia.VisualTree; -using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.ViewModels; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -17,16 +17,17 @@ namespace Ryujinx.Ava.UI.Views.Settings public SettingsUiView() { InitializeComponent(); + ShowTitleBarBox.IsVisible = OperatingSystem.IsWindows(); } - private async void AddButton_OnClick(object sender, RoutedEventArgs e) + private async void AddGameDirButton_OnClick(object sender, RoutedEventArgs e) { - string path = PathBox.Text; + string path = GameDirPathBox.Text; if (!string.IsNullOrWhiteSpace(path) && Directory.Exists(path) && !ViewModel.GameDirectories.Contains(path)) { ViewModel.GameDirectories.Add(path); - ViewModel.DirectoryChanged = true; + ViewModel.GameDirectoryChanged = true; } else { @@ -40,25 +41,68 @@ namespace Ryujinx.Ava.UI.Views.Settings if (result.Count > 0) { ViewModel.GameDirectories.Add(result[0].Path.LocalPath); - ViewModel.DirectoryChanged = true; + ViewModel.GameDirectoryChanged = true; } } } } - private void RemoveButton_OnClick(object sender, RoutedEventArgs e) + private void RemoveGameDirButton_OnClick(object sender, RoutedEventArgs e) { - int oldIndex = GameList.SelectedIndex; + int oldIndex = GameDirsList.SelectedIndex; - foreach (string path in new List(GameList.SelectedItems.Cast())) + foreach (string path in new List(GameDirsList.SelectedItems.Cast())) { ViewModel.GameDirectories.Remove(path); - ViewModel.DirectoryChanged = true; + ViewModel.GameDirectoryChanged = true; } - if (GameList.ItemCount > 0) + if (GameDirsList.ItemCount > 0) { - GameList.SelectedIndex = oldIndex < GameList.ItemCount ? oldIndex : 0; + GameDirsList.SelectedIndex = oldIndex < GameDirsList.ItemCount ? oldIndex : 0; + } + } + + private async void AddAutoloadDirButton_OnClick(object sender, RoutedEventArgs e) + { + string path = AutoloadDirPathBox.Text; + + if (!string.IsNullOrWhiteSpace(path) && Directory.Exists(path) && !ViewModel.AutoloadDirectories.Contains(path)) + { + ViewModel.AutoloadDirectories.Add(path); + ViewModel.AutoloadDirectoryChanged = true; + } + else + { + if (this.GetVisualRoot() is Window window) + { + var result = await window.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions + { + AllowMultiple = false, + }); + + if (result.Count > 0) + { + ViewModel.AutoloadDirectories.Add(result[0].Path.LocalPath); + ViewModel.AutoloadDirectoryChanged = true; + } + } + } + } + + private void RemoveAutoloadDirButton_OnClick(object sender, RoutedEventArgs e) + { + int oldIndex = AutoloadDirsList.SelectedIndex; + + foreach (string path in new List(AutoloadDirsList.SelectedItems.Cast())) + { + ViewModel.AutoloadDirectories.Remove(path); + ViewModel.AutoloadDirectoryChanged = true; + } + + if (AutoloadDirsList.ItemCount > 0) + { + AutoloadDirsList.SelectedIndex = oldIndex < AutoloadDirsList.ItemCount ? oldIndex : 0; } } } diff --git a/src/Ryujinx/UI/Views/User/UserEditorView.axaml b/src/Ryujinx/UI/Views/User/UserEditorView.axaml index ab83c2cdd..7a4af4823 100644 --- a/src/Ryujinx/UI/Views/User/UserEditorView.axaml +++ b/src/Ryujinx/UI/Views/User/UserEditorView.axaml @@ -2,7 +2,7 @@ x:Class="Ryujinx.Ava.UI.Views.User.UserEditorView" xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" + xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" @@ -32,15 +32,15 @@ HorizontalAlignment="Stretch" Orientation="Vertical" Spacing="10"> - + - + + Content="{ext:Locale UserProfilesDelete}" /> diff --git a/src/Ryujinx/UI/Views/User/UserProfileImageSelectorView.axaml.cs b/src/Ryujinx/UI/Views/User/UserProfileImageSelectorView.axaml.cs index b4f23b5b8..dba762972 100644 --- a/src/Ryujinx/UI/Views/User/UserProfileImageSelectorView.axaml.cs +++ b/src/Ryujinx/UI/Views/User/UserProfileImageSelectorView.axaml.cs @@ -63,8 +63,7 @@ namespace Ryujinx.Ava.UI.Views.User private async void Import_OnClick(object sender, RoutedEventArgs e) { - var window = this.GetVisualRoot() as Window; - var result = await window.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions + var result = await ((Window)this.GetVisualRoot()!).StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions { AllowMultiple = false, FileTypeFilter = new List diff --git a/src/Ryujinx/UI/Views/User/UserRecovererView.axaml b/src/Ryujinx/UI/Views/User/UserRecovererView.axaml index 3fdb4ab92..f49444642 100644 --- a/src/Ryujinx/UI/Views/User/UserRecovererView.axaml +++ b/src/Ryujinx/UI/Views/User/UserRecovererView.axaml @@ -8,7 +8,7 @@ d:DesignHeight="450" Width="500" Height="400" - xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" + xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup" xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" x:Class="Ryujinx.Ava.UI.Views.User.UserRecovererView" @@ -55,7 +55,7 @@ HorizontalAlignment="Right" Click="Recover" CommandParameter="{Binding}" - Content="{locale:Locale Recover}"/> + Content="{ext:Locale Recover}"/> @@ -64,7 +64,7 @@ + Text="{ext:Locale UserProfilesRecoverEmptyList}"/> - - - - - - + - - - - + Grid.Column="0" ColumnDefinitions="*,Auto"> + TextTrimming="CharacterEllipsis"> + + + + + + + - + - + diff --git a/src/Ryujinx/UI/Windows/DownloadableContentManagerWindow.axaml.cs b/src/Ryujinx/UI/Windows/DownloadableContentManagerWindow.axaml.cs index ab04fd68f..2afa8b529 100644 --- a/src/Ryujinx/UI/Windows/DownloadableContentManagerWindow.axaml.cs +++ b/src/Ryujinx/UI/Windows/DownloadableContentManagerWindow.axaml.cs @@ -3,11 +3,10 @@ using Avalonia.Interactivity; using Avalonia.Styling; using FluentAvalonia.UI.Controls; using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.ViewModels; -using Ryujinx.HLE.FileSystem; using Ryujinx.UI.App.Common; using Ryujinx.UI.Common.Helper; +using Ryujinx.UI.Common.Models; using System.Threading.Tasks; namespace Ryujinx.Ava.UI.Windows @@ -23,21 +22,21 @@ namespace Ryujinx.Ava.UI.Windows InitializeComponent(); } - public DownloadableContentManagerWindow(VirtualFileSystem virtualFileSystem, ApplicationData applicationData) + public DownloadableContentManagerWindow(ApplicationLibrary applicationLibrary, ApplicationData applicationData) { - DataContext = ViewModel = new DownloadableContentManagerViewModel(virtualFileSystem, applicationData); + DataContext = ViewModel = new DownloadableContentManagerViewModel(applicationLibrary, applicationData); InitializeComponent(); } - public static async Task Show(VirtualFileSystem virtualFileSystem, ApplicationData applicationData) + public static async Task Show(ApplicationLibrary applicationLibrary, ApplicationData applicationData) { ContentDialog contentDialog = new() { - PrimaryButtonText = "", - SecondaryButtonText = "", - CloseButtonText = "", - Content = new DownloadableContentManagerWindow(virtualFileSystem, applicationData), + PrimaryButtonText = string.Empty, + SecondaryButtonText = string.Empty, + CloseButtonText = string.Empty, + Content = new DownloadableContentManagerWindow(applicationLibrary, applicationData), Title = string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowTitle], applicationData.Name, applicationData.IdBaseString), }; @@ -62,23 +61,17 @@ namespace Ryujinx.Ava.UI.Windows private void RemoveDLC(object sender, RoutedEventArgs e) { - if (sender is Button button) + if (sender is Button { DataContext: DownloadableContentModel dlc }) { - if (button.DataContext is DownloadableContentModel model) - { - ViewModel.Remove(model); - } + ViewModel.Remove(dlc); } } private void OpenLocation(object sender, RoutedEventArgs e) { - if (sender is Button button) + if (sender is Button { DataContext: DownloadableContentModel dlc }) { - if (button.DataContext is DownloadableContentModel model) - { - OpenHelper.LocateFile(model.ContainerPath); - } + OpenHelper.LocateFile(dlc.ContainerPath); } } @@ -88,12 +81,7 @@ namespace Ryujinx.Ava.UI.Windows { if (content is DownloadableContentModel model) { - var index = ViewModel.DownloadableContents.IndexOf(model); - - if (index != -1) - { - ViewModel.DownloadableContents[index].Enabled = true; - } + ViewModel.Enable(model); } } @@ -101,12 +89,7 @@ namespace Ryujinx.Ava.UI.Windows { if (content is DownloadableContentModel model) { - var index = ViewModel.DownloadableContents.IndexOf(model); - - if (index != -1) - { - ViewModel.DownloadableContents[index].Enabled = false; - } + ViewModel.Disable(model); } } } diff --git a/src/Ryujinx/UI/Windows/IconColorPicker.cs b/src/Ryujinx/UI/Windows/IconColorPicker.cs index dd6a55d4d..bfa33eb43 100644 --- a/src/Ryujinx/UI/Windows/IconColorPicker.cs +++ b/src/Ryujinx/UI/Windows/IconColorPicker.cs @@ -19,20 +19,12 @@ namespace Ryujinx.Ava.UI.Windows private const int CutOffLuminosity = 64; - private readonly struct PaletteColor + private readonly struct PaletteColor(int qck, byte r, byte g, byte b) { - public int Qck { get; } - public byte R { get; } - public byte G { get; } - public byte B { get; } - - public PaletteColor(int qck, byte r, byte g, byte b) - { - Qck = qck; - R = r; - G = g; - B = b; - } + public int Qck => qck; + public byte R => r; + public byte G => g; + public byte B => b; } public static SKColor GetFilteredColor(SKBitmap image) @@ -62,15 +54,6 @@ namespace Ryujinx.Ava.UI.Windows var buffer = GetBuffer(image); - int w = image.Width; - int w8 = w << 8; - int h8 = image.Height << 8; - -#pragma warning disable IDE0059 // Unnecessary assignment - int xStep = w8 / ColorsPerLine; - int yStep = h8 / ColorsPerLine; -#pragma warning restore IDE0059 - int i = 0; int maxHitCount = 0; @@ -147,7 +130,7 @@ namespace Ryujinx.Ava.UI.Windows var value = GetColorValue(color); // If the color is rarely used on the image, - // then chances are that theres a better candidate, even if the saturation value + // then chances are that there's a better candidate, even if the saturation value // is high. By multiplying the saturation value with a weight, we can lower // it if the color is almost never used (hit count is low). var satWeighted = quantSat; @@ -164,16 +147,6 @@ namespace Ryujinx.Ava.UI.Windows return score; } - private static int BalanceHitCount(int hitCount, int maxHitCount) - { - return (hitCount << 8) / maxHitCount; - } - - private static int GetColorApproximateLuminosity(byte r, byte g, byte b) - { - return (r + g + b) / 3; - } - private static int GetColorSaturation(PaletteColor color) { int cMax = Math.Max(Math.Max(color.R, color.G), color.B); @@ -188,10 +161,11 @@ namespace Ryujinx.Ava.UI.Windows return (delta << 8) / cMax; } - private static int GetColorValue(PaletteColor color) - { - return Math.Max(Math.Max(color.R, color.G), color.B); - } + private static int GetColorValue(PaletteColor color) => Math.Max(Math.Max(color.R, color.G), color.B); + + private static int BalanceHitCount(int hitCount, int maxHitCount) => (hitCount << 8) / maxHitCount; + + private static int GetColorApproximateLuminosity(byte r, byte g, byte b) => (r + g + b) / 3; private static int GetQuantizedColorKey(byte r, byte g, byte b) { diff --git a/src/Ryujinx/UI/Windows/MainWindow.axaml b/src/Ryujinx/UI/Windows/MainWindow.axaml index 3a2e02c26..cb2e5936d 100644 --- a/src/Ryujinx/UI/Windows/MainWindow.axaml +++ b/src/Ryujinx/UI/Windows/MainWindow.axaml @@ -1,4 +1,4 @@ - + - - - - + - - - - - - - - + VerticalAlignment="Stretch" ColumnDefinitions="*" RowDefinitions="Auto,*,Auto"> - - - - - + @@ -116,11 +103,7 @@ Margin="40" HorizontalAlignment="Center" VerticalAlignment="Center" - IsVisible="{Binding ShowLoadProgress}"> - - - - + IsVisible="{Binding ShowLoadProgress}" ColumnDefinitions="Auto,*"> - - - - - + IsVisible="{Binding ShowLoadProgress}" RowDefinitions="Auto,Auto,Auto"> - + diff --git a/src/Ryujinx/UI/Windows/MainWindow.axaml.cs b/src/Ryujinx/UI/Windows/MainWindow.axaml.cs index 348412e78..c433d7fdb 100644 --- a/src/Ryujinx/UI/Windows/MainWindow.axaml.cs +++ b/src/Ryujinx/UI/Windows/MainWindow.axaml.cs @@ -4,7 +4,10 @@ using Avalonia.Controls.Primitives; using Avalonia.Interactivity; using Avalonia.Platform; using Avalonia.Threading; +using DynamicData; using FluentAvalonia.UI.Controls; +using FluentAvalonia.UI.Windowing; +using Gommon; using LibHac.Tools.FsSystem; using Ryujinx.Ava.Common; using Ryujinx.Ava.Common.Locale; @@ -12,6 +15,7 @@ using Ryujinx.Ava.Input; using Ryujinx.Ava.UI.Applet; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.ViewModels; +using Ryujinx.Common; using Ryujinx.Common.Logging; using Ryujinx.Graphics.Gpu; using Ryujinx.HLE.FileSystem; @@ -19,22 +23,27 @@ using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.Input.HLE; using Ryujinx.Input.SDL2; -using Ryujinx.Modules; using Ryujinx.UI.App.Common; using Ryujinx.UI.Common; using Ryujinx.UI.Common.Configuration; using Ryujinx.UI.Common.Helper; using System; using System.Collections.Generic; +using System.Linq; +using System.Reactive.Linq; using System.Runtime.Versioning; using System.Threading; using System.Threading.Tasks; namespace Ryujinx.Ava.UI.Windows { - public partial class MainWindow : StyleableWindow + public partial class MainWindow : StyleableAppWindow { internal static MainWindowViewModel MainWindowViewModel { get; private set; } + + public MainWindowViewModel ViewModel { get; } + + internal readonly AvaHostUIHandler UiHandler; private bool _isLoading; private bool _applicationsLoadedOnce; @@ -44,7 +53,7 @@ namespace Ryujinx.Ava.UI.Windows private static string _launchPath; private static string _launchApplicationId; private static bool _startFullscreen; - internal readonly AvaHostUIHandler UiHandler; + private IDisposable _appLibraryAppsSubscription; public VirtualFileSystem VirtualFileSystem { get; private set; } public ContentManager ContentManager { get; private set; } @@ -54,36 +63,40 @@ namespace Ryujinx.Ava.UI.Windows public InputManager InputManager { get; private set; } - internal MainWindowViewModel ViewModel { get; private set; } public SettingsWindow SettingsWindow { get; set; } public static bool ShowKeyErrorOnLoad { get; set; } public ApplicationLibrary ApplicationLibrary { get; set; } + // Correctly size window when 'TitleBar' is enabled (Nov. 14, 2024) + public readonly double TitleBarHeight; + public readonly double StatusBarHeight; public readonly double MenuBarHeight; public MainWindow() { - ViewModel = new MainWindowViewModel(); - - MainWindowViewModel = ViewModel; - - DataContext = ViewModel; + DataContext = ViewModel = MainWindowViewModel = new MainWindowViewModel + { + Window = this + }; InitializeComponent(); Load(); UiHandler = new AvaHostUIHandler(this); - ViewModel.Title = $"Ryujinx {Program.Version}"; + ViewModel.Title = App.FormatTitle(); + + TitleBar.ExtendsContentIntoTitleBar = !ConfigurationState.Instance.ShowTitleBar; + TitleBar.TitleBarHitTestType = (ConfigurationState.Instance.ShowTitleBar) ? TitleBarHitTestType.Simple : TitleBarHitTestType.Complex; + + // Correctly size window when 'TitleBar' is enabled (Nov. 14, 2024) + TitleBarHeight = (ConfigurationState.Instance.ShowTitleBar ? TitleBar.Height : 0); // NOTE: Height of MenuBar and StatusBar is not usable here, since it would still be 0 at this point. StatusBarHeight = StatusBarView.StatusBar.MinHeight; MenuBarHeight = MenuBar.MinHeight; - double barHeight = MenuBarHeight + StatusBarHeight; - Height = ((Height - barHeight) / Program.WindowScaleFactor) + barHeight; - Width /= Program.WindowScaleFactor; SetWindowSizePosition(); @@ -91,7 +104,7 @@ namespace Ryujinx.Ava.UI.Windows { InputManager = new InputManager(new AvaloniaKeyboardDriver(this), new SDL2GamepadDriver()); - this.GetObservable(IsActiveProperty).Subscribe(IsActiveChanged); + _ = this.GetObservable(IsActiveProperty).Subscribe(it => ViewModel.IsActive = it); this.ScalingChanged += OnScalingChanged; } } @@ -99,12 +112,10 @@ namespace Ryujinx.Ava.UI.Windows /// /// Event handler for detecting OS theme change when using "Follow OS theme" option /// - private void OnPlatformColorValuesChanged(object sender, PlatformColorValues e) + private static void OnPlatformColorValuesChanged(object sender, PlatformColorValues e) { if (Application.Current is App app) - { - app.ApplyConfiguredTheme(); - } + app.ApplyConfiguredTheme(ConfigurationState.Instance.UI.BaseStyle); } protected override void OnClosed(EventArgs e) @@ -112,9 +123,6 @@ namespace Ryujinx.Ava.UI.Windows base.OnClosed(e); if (PlatformSettings != null) { - /// - /// Unsubscribe to the ColorValuesChanged event - /// PlatformSettings.ColorValuesChanged -= OnPlatformColorValuesChanged; } } @@ -126,24 +134,11 @@ namespace Ryujinx.Ava.UI.Windows NotificationHelper.SetNotificationManager(this); } - private void IsActiveChanged(bool obj) - { - ViewModel.IsActive = obj; - } - private void OnScalingChanged(object sender, EventArgs e) { Program.DesktopScaleFactor = this.RenderScaling; } - private void ApplicationLibrary_ApplicationAdded(object sender, ApplicationAddedEventArgs e) - { - Dispatcher.UIThread.Post(() => - { - ViewModel.Applications.Add(e.AppData); - }); - } - private void ApplicationLibrary_ApplicationCountUpdated(object sender, ApplicationCountUpdatedEventArgs e) { LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarGamesLoaded, e.NumAppsLoaded, e.NumAppsFound); @@ -165,6 +160,36 @@ namespace Ryujinx.Ava.UI.Windows }); } + private void ApplicationLibrary_LdnGameDataReceived(object sender, LdnGameDataReceivedEventArgs e) + { + Dispatcher.UIThread.Post(() => + { + var ldnGameDataArray = e.LdnData; + ViewModel.LastLdnGameData = ldnGameDataArray; + foreach (var application in ViewModel.Applications) + { + UpdateApplicationWithLdnData(application); + } + ViewModel.RefreshView(); + }); + } + + private void UpdateApplicationWithLdnData(ApplicationData application) + { + if (application.ControlHolder.ByteSpan.Length > 0 && ViewModel.LastLdnGameData != null) + { + IEnumerable ldnGameData = ViewModel.LastLdnGameData.Where(game => application.ControlHolder.Value.LocalCommunicationId.Items.Contains(Convert.ToUInt64(game.TitleId, 16))); + + application.PlayerCount = ldnGameData.Sum(game => game.PlayerCount); + application.GameCount = ldnGameData.Count(); + } + else + { + application.PlayerCount = 0; + application.GameCount = 0; + } + } + public void Application_Opened(object sender, ApplicationOpenedEventArgs args) { if (args.Application != null) @@ -191,7 +216,7 @@ namespace Ryujinx.Ava.UI.Windows ViewModel.ShowContent = true; ViewModel.IsLoadingIndeterminate = false; - if (startFullscreen && ViewModel.WindowState != WindowState.FullScreen) + if (startFullscreen && ViewModel.WindowState is not WindowState.FullScreen) { ViewModel.ToggleFullscreen(); } @@ -203,7 +228,7 @@ namespace Ryujinx.Ava.UI.Windows ViewModel.ShowLoadProgress = true; ViewModel.IsLoadingIndeterminate = true; - if (startFullscreen && ViewModel.WindowState != WindowState.FullScreen) + if (startFullscreen && ViewModel.WindowState is not WindowState.FullScreen) { ViewModel.ToggleFullscreen(); } @@ -329,7 +354,7 @@ namespace Ryujinx.Ava.UI.Windows if (_launchApplicationId != null) { - applicationData = applications.Find(application => application.IdString == _launchApplicationId); + applicationData = applications.FirstOrDefault(application => application.IdString == _launchApplicationId); if (applicationData != null) { @@ -361,12 +386,10 @@ namespace Ryujinx.Ava.UI.Windows await Dispatcher.UIThread.InvokeAsync(async () => await UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys)); } - if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false)) + if (ConfigurationState.Instance.CheckUpdatesOnStart && !CommandLineState.HideAvailableUpdates && Updater.CanUpdate()) { - await Updater.BeginParse(this, false).ContinueWith(task => - { - Logger.Error?.Print(LogClass.Application, $"Updater Error: {task.Exception}"); - }, TaskContinuationOptions.OnlyOnFaulted); + await Updater.BeginUpdateAsync() + .Catch(task => Logger.Error?.Print(LogClass.Application, $"Updater Error: {task.Exception}")); } } @@ -387,7 +410,8 @@ namespace Ryujinx.Ava.UI.Windows { if (!ConfigurationState.Instance.RememberWindowState) { - ViewModel.WindowHeight = (720 + StatusBarHeight + MenuBarHeight) * Program.WindowScaleFactor; + // Correctly size window when 'TitleBar' is enabled (Nov. 14, 2024) + ViewModel.WindowHeight = (720 + StatusBarHeight + MenuBarHeight + TitleBarHeight) * Program.WindowScaleFactor; ViewModel.WindowWidth = 1280 * Program.WindowScaleFactor; WindowState = WindowState.Normal; @@ -404,30 +428,17 @@ namespace Ryujinx.Ava.UI.Windows ViewModel.WindowState = ConfigurationState.Instance.UI.WindowStartup.WindowMaximized.Value ? WindowState.Maximized : WindowState.Normal; - if (CheckScreenBounds(savedPoint)) + if (Screens.All.Any(screen => screen.Bounds.Contains(savedPoint))) { Position = savedPoint; } else { + Logger.Warning?.Print(LogClass.Application, "Failed to find valid start-up coordinates. Defaulting to primary monitor center."); WindowStartupLocation = WindowStartupLocation.CenterScreen; } } - private bool CheckScreenBounds(PixelPoint configPoint) - { - for (int i = 0; i < Screens.ScreenCount; i++) - { - if (Screens.All[i].Bounds.Contains(configPoint)) - { - return true; - } - } - - Logger.Warning?.Print(LogClass.Application, "Failed to find valid start-up coordinates. Defaulting to primary monitor center."); - return false; - } - private void SaveWindowSizePosition() { ConfigurationState.Instance.UI.WindowStartup.WindowMaximized.Value = WindowState == WindowState.Maximized; @@ -435,8 +446,10 @@ namespace Ryujinx.Ava.UI.Windows // Only save rectangle properties if the window is not in a maximized state. if (WindowState != WindowState.Maximized) { - ConfigurationState.Instance.UI.WindowStartup.WindowSizeHeight.Value = (int)Height; - ConfigurationState.Instance.UI.WindowStartup.WindowSizeWidth.Value = (int)Width; + // Since scaling is being applied to the loaded settings from disk (see SetWindowSizePosition() above), scaling should be removed from width/height before saving out to disk + // as well - otherwise anyone not using a 1.0 scale factor their window will increase in size with every subsequent launch of the program when scaling is applied (Nov. 14, 2024) + ConfigurationState.Instance.UI.WindowStartup.WindowSizeHeight.Value = (int)(Height / Program.WindowScaleFactor); + ConfigurationState.Instance.UI.WindowStartup.WindowSizeWidth.Value = (int)(Width / Program.WindowScaleFactor); ConfigurationState.Instance.UI.WindowStartup.WindowPositionX.Value = Position.X; ConfigurationState.Instance.UI.WindowStartup.WindowPositionY.Value = Position.Y; @@ -451,10 +464,7 @@ namespace Ryujinx.Ava.UI.Windows Initialize(); - /// - /// Subscribe to the ColorValuesChanged event - /// - PlatformSettings.ColorValuesChanged += OnPlatformColorValuesChanged; + PlatformSettings!.ColorValuesChanged += OnPlatformColorValuesChanged; ViewModel.Initialize( ContentManager, @@ -472,7 +482,25 @@ namespace Ryujinx.Ava.UI.Windows this); ApplicationLibrary.ApplicationCountUpdated += ApplicationLibrary_ApplicationCountUpdated; - ApplicationLibrary.ApplicationAdded += ApplicationLibrary_ApplicationAdded; + _appLibraryAppsSubscription?.Dispose(); + _appLibraryAppsSubscription = ApplicationLibrary.Applications + .Connect() + .ObserveOn(SynchronizationContext.Current!) + .Bind(ViewModel.Applications) + .OnItemAdded(UpdateApplicationWithLdnData) + .Subscribe(); + ApplicationLibrary.LdnGameDataReceived += ApplicationLibrary_LdnGameDataReceived; + + ConfigurationState.Instance.Multiplayer.Mode.Event += (sender, evt) => + { + _ = Task.Run(ViewModel.ApplicationLibrary.RefreshLdn); + }; + + ConfigurationState.Instance.Multiplayer.LdnServer.Event += (sender, evt) => + { + _ = Task.Run(ViewModel.ApplicationLibrary.RefreshLdn); + }; + _ = Task.Run(ViewModel.ApplicationLibrary.RefreshLdn); ViewModel.RefreshFirmwareStatus(); @@ -482,9 +510,7 @@ namespace Ryujinx.Ava.UI.Windows LoadApplications(); } -#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - CheckLaunchState(); -#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed + _ = CheckLaunchState(); } private void SetMainContent(Control content = null) @@ -506,7 +532,9 @@ namespace Ryujinx.Ava.UI.Windows public static void UpdateGraphicsConfig() { #pragma warning disable IDE0055 // Disable formatting - GraphicsConfig.ResScale = ConfigurationState.Instance.Graphics.ResScale == -1 ? ConfigurationState.Instance.Graphics.ResScaleCustom : ConfigurationState.Instance.Graphics.ResScale; + GraphicsConfig.ResScale = ConfigurationState.Instance.Graphics.ResScale == -1 + ? ConfigurationState.Instance.Graphics.ResScaleCustom + : ConfigurationState.Instance.Graphics.ResScale; GraphicsConfig.MaxAnisotropy = ConfigurationState.Instance.Graphics.MaxAnisotropy; GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath; GraphicsConfig.EnableShaderCache = ConfigurationState.Instance.Graphics.EnableShaderCache; @@ -517,8 +545,7 @@ namespace Ryujinx.Ava.UI.Windows private void VolumeStatus_CheckedChanged(object sender, RoutedEventArgs e) { - var volumeSplitButton = sender as ToggleSplitButton; - if (ViewModel.IsGameRunning) + if (ViewModel.IsGameRunning && sender is ToggleSplitButton volumeSplitButton) { if (!volumeSplitButton.IsChecked) { @@ -550,7 +577,7 @@ namespace Ryujinx.Ava.UI.Windows if (ViewModel.AppHost != null) { ViewModel.AppHost.AppExit -= ViewModel.AppHost_AppExit; - ViewModel.AppHost.AppExit += (sender, e) => + ViewModel.AppHost.AppExit += (_, _) => { ViewModel.AppHost = null; @@ -575,6 +602,7 @@ namespace Ryujinx.Ava.UI.Windows ApplicationLibrary.CancelLoading(); InputManager.Dispose(); + _appLibraryAppsSubscription?.Dispose(); Program.Exit(); base.OnClosing(e); @@ -596,7 +624,6 @@ namespace Ryujinx.Ava.UI.Windows public void LoadApplications() { _applicationsLoadedOnce = true; - ViewModel.Applications.Clear(); StatusBarView.LoadProgressBar.IsVisible = true; ViewModel.StatusBarProgressMaximum = 0; @@ -609,18 +636,29 @@ namespace Ryujinx.Ava.UI.Windows public void ToggleFileType(string fileType) { - _ = fileType switch + switch (fileType) { -#pragma warning disable IDE0055 // Disable formatting - "NSP" => ConfigurationState.Instance.UI.ShownFileTypes.NSP.Value = !ConfigurationState.Instance.UI.ShownFileTypes.NSP, - "PFS0" => ConfigurationState.Instance.UI.ShownFileTypes.PFS0.Value = !ConfigurationState.Instance.UI.ShownFileTypes.PFS0, - "XCI" => ConfigurationState.Instance.UI.ShownFileTypes.XCI.Value = !ConfigurationState.Instance.UI.ShownFileTypes.XCI, - "NCA" => ConfigurationState.Instance.UI.ShownFileTypes.NCA.Value = !ConfigurationState.Instance.UI.ShownFileTypes.NCA, - "NRO" => ConfigurationState.Instance.UI.ShownFileTypes.NRO.Value = !ConfigurationState.Instance.UI.ShownFileTypes.NRO, - "NSO" => ConfigurationState.Instance.UI.ShownFileTypes.NSO.Value = !ConfigurationState.Instance.UI.ShownFileTypes.NSO, - _ => throw new ArgumentOutOfRangeException(fileType), -#pragma warning restore IDE0055 - }; + case "NSP": + ConfigurationState.Instance.UI.ShownFileTypes.NSP.Toggle(); + break; + case "PFS0": + ConfigurationState.Instance.UI.ShownFileTypes.PFS0.Toggle(); + break; + case "XCI": + ConfigurationState.Instance.UI.ShownFileTypes.XCI.Toggle(); + break; + case "NCA": + ConfigurationState.Instance.UI.ShownFileTypes.NCA.Toggle(); + break; + case "NRO": + ConfigurationState.Instance.UI.ShownFileTypes.NRO.Toggle(); + break; + case "NSO": + ConfigurationState.Instance.UI.ShownFileTypes.NSO.Toggle(); + break; + default: + throw new ArgumentOutOfRangeException(fileType); + } ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); LoadApplications(); @@ -638,8 +676,18 @@ namespace Ryujinx.Ava.UI.Windows Thread applicationLibraryThread = new(() => { ApplicationLibrary.DesiredLanguage = ConfigurationState.Instance.System.Language; + ApplicationLibrary.LoadApplications(ConfigurationState.Instance.UI.GameDirs); + var autoloadDirs = ConfigurationState.Instance.UI.AutoloadDirs.Value; + if (autoloadDirs.Count > 0) + { + var updatesLoaded = ApplicationLibrary.AutoLoadTitleUpdates(autoloadDirs, out int updatesRemoved); + var dlcLoaded = ApplicationLibrary.AutoLoadDownloadableContents(autoloadDirs, out int dlcRemoved); + + ShowNewContentAddedDialog(dlcLoaded, dlcRemoved, updatesLoaded, updatesRemoved); + } + _isLoading = false; }) { @@ -648,5 +696,32 @@ namespace Ryujinx.Ava.UI.Windows }; applicationLibraryThread.Start(); } + + private void ShowNewContentAddedDialog(int numDlcAdded, int numDlcRemoved, int numUpdatesAdded, int numUpdatesRemoved) + { + string[] messages = { + numDlcRemoved > 0 ? string.Format(LocaleManager.Instance[LocaleKeys.AutoloadDlcRemovedMessage], numDlcRemoved): null, + numDlcAdded > 0 ? string.Format(LocaleManager.Instance[LocaleKeys.AutoloadDlcAddedMessage], numDlcAdded): null, + numUpdatesRemoved > 0 ? string.Format(LocaleManager.Instance[LocaleKeys.AutoloadUpdateRemovedMessage], numUpdatesRemoved): null, + numUpdatesAdded > 0 ? string.Format(LocaleManager.Instance[LocaleKeys.AutoloadUpdateAddedMessage], numUpdatesAdded) : null + }; + + string msg = String.Join("\r\n", messages); + + if (String.IsNullOrWhiteSpace(msg)) + return; + + Dispatcher.UIThread.InvokeAsync(async () => + { + await ContentDialogHelper.ShowTextDialog( + LocaleManager.Instance[LocaleKeys.DialogConfirmationTitle], + msg, + string.Empty, + string.Empty, + string.Empty, + LocaleManager.Instance[LocaleKeys.InputDialogOk], + (int)Symbol.Checkmark); + }); + } } } diff --git a/src/Ryujinx/UI/Windows/ModManagerWindow.axaml b/src/Ryujinx/UI/Windows/ModManagerWindow.axaml index 0ed05ce3f..3a1c4e6dd 100644 --- a/src/Ryujinx/UI/Windows/ModManagerWindow.axaml +++ b/src/Ryujinx/UI/Windows/ModManagerWindow.axaml @@ -2,7 +2,7 @@ xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" + xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" xmlns:models="clr-namespace:Ryujinx.Ava.UI.Models" @@ -14,21 +14,11 @@ x:CompileBindings="True" x:DataType="viewModels:ModManagerViewModel" Focusable="True"> - - - - - - + - - - - - - + @@ -41,14 +31,14 @@ MinWidth="90" Margin="5" Command="{Binding EnableAll}"> - + @@ -145,14 +135,14 @@ MinWidth="90" Margin="5" Command="{Binding Add}"> - + - + diff --git a/src/Ryujinx/UI/Windows/ModManagerWindow.axaml.cs b/src/Ryujinx/UI/Windows/ModManagerWindow.axaml.cs index 98f694db8..774446cf1 100644 --- a/src/Ryujinx/UI/Windows/ModManagerWindow.axaml.cs +++ b/src/Ryujinx/UI/Windows/ModManagerWindow.axaml.cs @@ -14,7 +14,7 @@ namespace Ryujinx.Ava.UI.Windows { public partial class ModManagerWindow : UserControl { - public ModManagerViewModel ViewModel; + public readonly ModManagerViewModel ViewModel; public ModManagerWindow() { @@ -34,9 +34,9 @@ namespace Ryujinx.Ava.UI.Windows { ContentDialog contentDialog = new() { - PrimaryButtonText = "", - SecondaryButtonText = "", - CloseButtonText = "", + PrimaryButtonText = string.Empty, + SecondaryButtonText = string.Empty, + CloseButtonText = string.Empty, Content = new ModManagerWindow(titleId), Title = string.Format(LocaleManager.Instance[LocaleKeys.ModWindowTitle], titleName, titleId.ToString("X16")), }; diff --git a/src/Ryujinx/UI/Windows/SettingsWindow.axaml b/src/Ryujinx/UI/Windows/SettingsWindow.axaml index de3c2291a..2bf5b55e7 100644 --- a/src/Ryujinx/UI/Windows/SettingsWindow.axaml +++ b/src/Ryujinx/UI/Windows/SettingsWindow.axaml @@ -1,9 +1,9 @@ - - - - - - - + @@ -114,17 +109,16 @@ HorizontalAlignment="Right" ReverseOrder="{Binding IsMacOS}"> - + diff --git a/src/Ryujinx/UI/Windows/TitleUpdateWindow.axaml.cs b/src/Ryujinx/UI/Windows/TitleUpdateWindow.axaml.cs index af917e7f3..a13ad4012 100644 --- a/src/Ryujinx/UI/Windows/TitleUpdateWindow.axaml.cs +++ b/src/Ryujinx/UI/Windows/TitleUpdateWindow.axaml.cs @@ -1,15 +1,12 @@ -using Avalonia; using Avalonia.Controls; -using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Interactivity; using Avalonia.Styling; using FluentAvalonia.UI.Controls; using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.ViewModels; -using Ryujinx.HLE.FileSystem; using Ryujinx.UI.App.Common; using Ryujinx.UI.Common.Helper; +using Ryujinx.UI.Common.Models; using System.Threading.Tasks; namespace Ryujinx.Ava.UI.Windows @@ -25,21 +22,21 @@ namespace Ryujinx.Ava.UI.Windows InitializeComponent(); } - public TitleUpdateWindow(VirtualFileSystem virtualFileSystem, ApplicationData applicationData) + public TitleUpdateWindow(ApplicationLibrary applicationLibrary, ApplicationData applicationData) { - DataContext = ViewModel = new TitleUpdateViewModel(virtualFileSystem, applicationData); + DataContext = ViewModel = new TitleUpdateViewModel(applicationLibrary, applicationData); InitializeComponent(); } - public static async Task Show(VirtualFileSystem virtualFileSystem, ApplicationData applicationData) + public static async Task Show(ApplicationLibrary applicationLibrary, ApplicationData applicationData) { ContentDialog contentDialog = new() { - PrimaryButtonText = "", - SecondaryButtonText = "", - CloseButtonText = "", - Content = new TitleUpdateWindow(virtualFileSystem, applicationData), + PrimaryButtonText = string.Empty, + SecondaryButtonText = string.Empty, + CloseButtonText = string.Empty, + Content = new TitleUpdateWindow(applicationLibrary, applicationData), Title = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.GameUpdateWindowHeading, applicationData.Name, applicationData.IdBaseString), }; @@ -60,37 +57,19 @@ namespace Ryujinx.Ava.UI.Windows { ViewModel.Save(); - if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime al) - { - foreach (Window window in al.Windows) - { - if (window is MainWindow mainWindow) - { - mainWindow.LoadApplications(); - } - } - } - ((ContentDialog)Parent).Hide(); } private void OpenLocation(object sender, RoutedEventArgs e) { - if (sender is Button button) - { - if (button.DataContext is TitleUpdateModel model) - { - OpenHelper.LocateFile(model.Path); - } - } + if (sender is Button { DataContext: TitleUpdateModel model }) + OpenHelper.LocateFile(model.Path); } private void RemoveUpdate(object sender, RoutedEventArgs e) { - if (sender is Button button) - { - ViewModel.RemoveUpdate((TitleUpdateModel)button.DataContext); - } + if (sender is Button { DataContext: TitleUpdateModel model }) + ViewModel.RemoveUpdate(model); } private void RemoveAll(object sender, RoutedEventArgs e) diff --git a/src/Ryujinx/UI/Windows/XCITrimmerWindow.axaml b/src/Ryujinx/UI/Windows/XCITrimmerWindow.axaml new file mode 100644 index 000000000..d726f8099 --- /dev/null +++ b/src/Ryujinx/UI/Windows/XCITrimmerWindow.axaml @@ -0,0 +1,354 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Ryujinx/UI/Windows/XCITrimmerWindow.axaml.cs b/src/Ryujinx/UI/Windows/XCITrimmerWindow.axaml.cs new file mode 100644 index 000000000..6df862283 --- /dev/null +++ b/src/Ryujinx/UI/Windows/XCITrimmerWindow.axaml.cs @@ -0,0 +1,101 @@ +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Styling; +using FluentAvalonia.UI.Controls; +using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.UI.ViewModels; +using Ryujinx.UI.Common.Models; +using System; +using System.Threading.Tasks; + +namespace Ryujinx.Ava.UI.Windows +{ + public partial class XCITrimmerWindow : UserControl + { + public XCITrimmerViewModel ViewModel; + + public XCITrimmerWindow() + { + DataContext = this; + + InitializeComponent(); + } + + public XCITrimmerWindow(MainWindowViewModel mainWindowViewModel) + { + DataContext = ViewModel = new XCITrimmerViewModel(mainWindowViewModel); + + InitializeComponent(); + } + + public static async Task Show(MainWindowViewModel mainWindowViewModel) + { + ContentDialog contentDialog = new() + { + PrimaryButtonText = string.Empty, + SecondaryButtonText = string.Empty, + CloseButtonText = string.Empty, + Content = new XCITrimmerWindow(mainWindowViewModel), + Title = string.Format(LocaleManager.Instance[LocaleKeys.XCITrimmerWindowTitle]), + }; + + Style bottomBorder = new(x => x.OfType().Name("DialogSpace").Child().OfType()); + bottomBorder.Setters.Add(new Setter(IsVisibleProperty, false)); + + contentDialog.Styles.Add(bottomBorder); + + await contentDialog.ShowAsync(); + } + + private void Trim(object sender, RoutedEventArgs e) + { + ViewModel.TrimSelected(); + } + + private void Untrim(object sender, RoutedEventArgs e) + { + ViewModel.UntrimSelected(); + } + + private void Close(object sender, RoutedEventArgs e) + { + ((ContentDialog)Parent).Hide(); + } + + private void Cancel(Object sender, RoutedEventArgs e) + { + ViewModel.Cancel = true; + } + + public void Sort_Checked(object sender, RoutedEventArgs args) + { + if (sender is RadioButton { Tag: string sortField }) + ViewModel.SortingField = Enum.Parse(sortField); + } + + public void Order_Checked(object sender, RoutedEventArgs args) + { + if (sender is RadioButton { Tag: string sortOrder }) + ViewModel.SortingAscending = sortOrder is "Ascending"; + } + + private void OnSelectionChanged(object sender, SelectionChangedEventArgs e) + { + foreach (var content in e.AddedItems) + { + if (content is XCITrimmerFileModel applicationData) + { + ViewModel.Select(applicationData); + } + } + + foreach (var content in e.RemovedItems) + { + if (content is XCITrimmerFileModel applicationData) + { + ViewModel.Deselect(applicationData); + } + } + } + } +} diff --git a/src/Ryujinx/Modules/Updater/Updater.cs b/src/Ryujinx/Updater.cs similarity index 84% rename from src/Ryujinx/Modules/Updater/Updater.cs rename to src/Ryujinx/Updater.cs index 9f186f2b3..21d991d97 100644 --- a/src/Ryujinx/Modules/Updater/Updater.cs +++ b/src/Ryujinx/Updater.cs @@ -1,10 +1,9 @@ -using Avalonia.Controls; using Avalonia.Threading; using FluentAvalonia.UI.Controls; +using Gommon; using ICSharpCode.SharpZipLib.GZip; using ICSharpCode.SharpZipLib.Tar; using ICSharpCode.SharpZipLib.Zip; -using Ryujinx.Ava; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Common; @@ -27,11 +26,14 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -namespace Ryujinx.Modules +namespace Ryujinx.Ava { internal static class Updater { private const string GitHubApiUrl = "https://api.github.com"; + private const string LatestReleaseUrl = + $"{GitHubApiUrl}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest"; + private static readonly GithubReleasesJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); private static readonly string _homeDir = AppDomain.CurrentDomain.BaseDirectory; @@ -46,9 +48,9 @@ namespace Ryujinx.Modules private static bool _updateSuccessful; private static bool _running; - private static readonly string[] _windowsDependencyDirs = Array.Empty(); + private static readonly string[] _windowsDependencyDirs = []; - public static async Task BeginParse(Window mainWindow, bool showVersionUpToDate) + public static async Task BeginUpdateAsync(bool showVersionUpToDate = false) { if (_running) { @@ -72,16 +74,9 @@ namespace Ryujinx.Modules _platformExt = $"linux_{arch}.tar.gz"; } - Version newVersion; - Version currentVersion; - - try + if (!Version.TryParse(Program.Version, out Version currentVersion)) { - currentVersion = Version.Parse(Program.Version); - } - catch - { - Logger.Error?.Print(LogClass.Application, "Failed to convert the current Ryujinx version!"); + Logger.Error?.Print(LogClass.Application, $"Failed to convert the current {App.FullAppName} version!"); await ContentDialogHelper.CreateWarningDialog( LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedMessage], @@ -96,11 +91,10 @@ namespace Ryujinx.Modules try { using HttpClient jsonClient = ConstructHttpClient(); - - string buildInfoUrl = $"{GitHubApiUrl}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest"; - string fetchedJson = await jsonClient.GetStringAsync(buildInfoUrl); + + string fetchedJson = await jsonClient.GetStringAsync(LatestReleaseUrl); var fetched = JsonHelper.Deserialize(fetchedJson, _serializerContext.GithubReleasesJsonResponse); - _buildVer = fetched.Name; + _buildVer = fetched.TagName; foreach (var asset in fetched.Assets) { @@ -112,9 +106,14 @@ namespace Ryujinx.Modules { if (showVersionUpToDate) { - await ContentDialogHelper.CreateUpdaterInfoDialog( + UserResult userResult = await ContentDialogHelper.CreateUpdaterUpToDateInfoDialog( LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage], - ""); + string.Empty); + + if (userResult is UserResult.Ok) + { + OpenHelper.OpenUrl(ReleaseInformation.GetChangelogForVersion(currentVersion)); + } } _running = false; @@ -131,9 +130,14 @@ namespace Ryujinx.Modules { if (showVersionUpToDate) { - await ContentDialogHelper.CreateUpdaterInfoDialog( + UserResult userResult = await ContentDialogHelper.CreateUpdaterUpToDateInfoDialog( LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage], - ""); + string.Empty); + + if (userResult is UserResult.Ok) + { + OpenHelper.OpenUrl(ReleaseInformation.GetChangelogForVersion(currentVersion)); + } } _running = false; @@ -153,13 +157,9 @@ namespace Ryujinx.Modules return; } - try + if (!Version.TryParse(_buildVer, out Version newVersion)) { - newVersion = Version.Parse(_buildVer); - } - catch - { - Logger.Error?.Print(LogClass.Application, "Failed to convert the received Ryujinx version from Github!"); + Logger.Error?.Print(LogClass.Application, $"Failed to convert the received {App.FullAppName} version from GitHub!"); await ContentDialogHelper.CreateWarningDialog( LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedGithubMessage], @@ -174,9 +174,14 @@ namespace Ryujinx.Modules { if (showVersionUpToDate) { - await ContentDialogHelper.CreateUpdaterInfoDialog( + UserResult userResult = await ContentDialogHelper.CreateUpdaterUpToDateInfoDialog( LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage], - ""); + string.Empty); + + if (userResult is UserResult.Ok) + { + OpenHelper.OpenUrl(ReleaseInformation.GetChangelogForVersion(currentVersion)); + } } _running = false; @@ -204,19 +209,29 @@ namespace Ryujinx.Modules await Dispatcher.UIThread.InvokeAsync(async () => { + string newVersionString = ReleaseInformation.IsCanaryBuild + ? $"Canary {currentVersion} -> Canary {newVersion}" + : $"{currentVersion} -> {newVersion}"; + + RequestUserToUpdate: // Show a message asking the user if they want to update - var shouldUpdate = await ContentDialogHelper.CreateChoiceDialog( + UserResult shouldUpdate = await ContentDialogHelper.CreateUpdaterChoiceDialog( LocaleManager.Instance[LocaleKeys.RyujinxUpdater], LocaleManager.Instance[LocaleKeys.RyujinxUpdaterMessage], - $"{Program.Version} -> {newVersion}"); + newVersionString); - if (shouldUpdate) + switch (shouldUpdate) { - await UpdateRyujinx(mainWindow, _buildUrl); - } - else - { - _running = false; + case UserResult.Yes: + await UpdateRyujinx(_buildUrl); + break; + // Secondary button maps to no, which in this case is the show changelog button. + case UserResult.No: + OpenHelper.OpenUrl(ReleaseInformation.GetChangelogUrl(currentVersion, newVersion)); + goto RequestUserToUpdate; + default: + _running = false; + break; } }); } @@ -231,7 +246,7 @@ namespace Ryujinx.Modules return result; } - private static async Task UpdateRyujinx(Window parent, string downloadUrl) + private static async Task UpdateRyujinx(string downloadUrl) { _updateSuccessful = false; @@ -251,7 +266,7 @@ namespace Ryujinx.Modules SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterDownloading], IconSource = new SymbolIconSource { Symbol = Symbol.Download }, ShowProgressBar = true, - XamlRoot = parent, + XamlRoot = App.MainWindow, }; taskDialog.Opened += (s, e) => @@ -300,13 +315,6 @@ namespace Ryujinx.Modules // Find the process name. string ryuName = Path.GetFileName(Environment.ProcessPath) ?? string.Empty; - // Migration: Start the updated binary. - // TODO: Remove this in a future update. - if (ryuName.StartsWith("Ryujinx.Ava")) - { - ryuName = ryuName.Replace(".Ava", ""); - } - // Some operating systems can see the renamed executable, so strip off the .ryuold if found. if (ryuName.EndsWith(".ryuold")) { @@ -466,7 +474,7 @@ namespace Ryujinx.Modules using Stream updateFileStream = File.Open(updateFile, FileMode.Create); long totalBytes = response.Content.Headers.ContentLength.Value; - long byteWritten = 0; + long bytesWritten = 0; byte[] buffer = new byte[32 * 1024]; @@ -479,9 +487,10 @@ namespace Ryujinx.Modules break; } - byteWritten += readSize; + bytesWritten += readSize; - taskDialog.SetProgressBarState(GetPercentage(byteWritten, totalBytes), TaskDialogProgressState.Normal); + taskDialog.SetProgressBarState(GetPercentage(bytesWritten, totalBytes), TaskDialogProgressState.Normal); + App.SetTaskbarProgressValue(bytesWritten, totalBytes); updateFileStream.Write(buffer, 0, readSize); } @@ -642,7 +651,7 @@ namespace Ryujinx.Modules taskDialog.Hide(); } - public static bool CanUpdate(bool showWarnings) + public static bool CanUpdate(bool showWarnings = false) { #if !DISABLE_UPDATER if (!NetworkInterface.GetIsNetworkAvailable()) @@ -677,22 +686,11 @@ namespace Ryujinx.Modules #else if (showWarnings) { - if (ReleaseInformation.IsFlatHubBuild) - { - Dispatcher.UIThread.InvokeAsync(() => - ContentDialogHelper.CreateWarningDialog( - LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle], - LocaleManager.Instance[LocaleKeys.DialogUpdaterFlatpakNotSupportedMessage]) + Dispatcher.UIThread.InvokeAsync(() => + ContentDialogHelper.CreateWarningDialog( + LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle], + LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage]) ); - } - else - { - Dispatcher.UIThread.InvokeAsync(() => - ContentDialogHelper.CreateWarningDialog( - LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle], - LocaleManager.Instance[LocaleKeys.DialogUpdaterDirtyBuildSubMessage]) - ); - } } return false; @@ -760,49 +758,8 @@ namespace Ryujinx.Modules } } - public static void CleanupUpdate() - { - foreach (string file in Directory.GetFiles(_homeDir, "*.ryuold", SearchOption.AllDirectories)) - { - File.Delete(file); - } - - // Migration: Delete old Ryujinx binary. - // TODO: Remove this in a future update. - if (!OperatingSystem.IsMacOS()) - { - string[] oldRyuFiles = Directory.GetFiles(_homeDir, "Ryujinx.Ava*", SearchOption.TopDirectoryOnly); - // Assume we are running the new one if the process path is not available. - // This helps to prevent an infinite loop of restarts. - string currentRyuName = Path.GetFileName(Environment.ProcessPath) ?? (OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx"); - - string newRyuName = Path.Combine(_homeDir, currentRyuName.Replace(".Ava", "")); - if (!currentRyuName.Contains("Ryujinx.Ava")) - { - foreach (string oldRyuFile in oldRyuFiles) - { - File.Delete(oldRyuFile); - } - } - // Should we be running the old binary, start the new one if possible. - else if (File.Exists(newRyuName)) - { - ProcessStartInfo processStart = new(newRyuName) - { - UseShellExecute = true, - WorkingDirectory = _homeDir, - }; - - foreach (string argument in CommandLineState.Arguments) - { - processStart.ArgumentList.Add(argument); - } - - Process.Start(processStart); - - Environment.Exit(0); - } - } - } + public static void CleanupUpdate() => + Directory.GetFiles(_homeDir, "*.ryuold", SearchOption.AllDirectories) + .ForEach(File.Delete); } } diff --git a/src/Spv.Generator/Autogenerated/CoreGrammar.cs b/src/Spv.Generator/Autogenerated/CoreGrammar.cs index 37936b8ef..9982fdfa8 100644 --- a/src/Spv.Generator/Autogenerated/CoreGrammar.cs +++ b/src/Spv.Generator/Autogenerated/CoreGrammar.cs @@ -26,6 +26,7 @@ // IN THE MATERIALS. #endregion +using System; using static Spv.Specification; namespace Spv.Generator @@ -192,7 +193,7 @@ namespace Spv.Generator return result; } - public Instruction Decorate(Instruction target, Decoration decoration, params IOperand[] parameters) + public Instruction Decorate(Instruction target, Decoration decoration, params ReadOnlySpan parameters) { Instruction result = NewInstruction(Op.OpDecorate); @@ -229,7 +230,7 @@ namespace Spv.Generator return result; } - public Instruction MemberDecorate(Instruction structureType, LiteralInteger member, Decoration decoration, params IOperand[] parameters) + public Instruction MemberDecorate(Instruction structureType, LiteralInteger member, Decoration decoration, params ReadOnlySpan parameters) { Instruction result = NewInstruction(Op.OpMemberDecorate); @@ -251,7 +252,7 @@ namespace Spv.Generator return result; } - public Instruction GroupDecorate(Instruction decorationGroup, params Instruction[] targets) + public Instruction GroupDecorate(Instruction decorationGroup, params ReadOnlySpan targets) { Instruction result = NewInstruction(Op.OpGroupDecorate); @@ -262,7 +263,7 @@ namespace Spv.Generator return result; } - public Instruction GroupMemberDecorate(Instruction decorationGroup, params IOperand[] targets) + public Instruction GroupMemberDecorate(Instruction decorationGroup, params ReadOnlySpan targets) { Instruction result = NewInstruction(Op.OpGroupMemberDecorate); @@ -273,7 +274,7 @@ namespace Spv.Generator return result; } - public Instruction DecorateId(Instruction target, Decoration decoration, params IOperand[] parameters) + public Instruction DecorateId(Instruction target, Decoration decoration, params ReadOnlySpan parameters) { Instruction result = NewInstruction(Op.OpDecorateId); @@ -285,7 +286,7 @@ namespace Spv.Generator return result; } - public Instruction DecorateString(Instruction target, Decoration decoration, params IOperand[] parameters) + public Instruction DecorateString(Instruction target, Decoration decoration, params ReadOnlySpan parameters) { Instruction result = NewInstruction(Op.OpDecorateString); @@ -297,7 +298,7 @@ namespace Spv.Generator return result; } - public Instruction DecorateStringGOOGLE(Instruction target, Decoration decoration, params IOperand[] parameters) + public Instruction DecorateStringGOOGLE(Instruction target, Decoration decoration, params ReadOnlySpan parameters) { Instruction result = NewInstruction(Op.OpDecorateStringGOOGLE); @@ -309,7 +310,7 @@ namespace Spv.Generator return result; } - public Instruction MemberDecorateString(Instruction structType, LiteralInteger member, Decoration decoration, params IOperand[] parameters) + public Instruction MemberDecorateString(Instruction structType, LiteralInteger member, Decoration decoration, params ReadOnlySpan parameters) { Instruction result = NewInstruction(Op.OpMemberDecorateString); @@ -322,7 +323,7 @@ namespace Spv.Generator return result; } - public Instruction MemberDecorateStringGOOGLE(Instruction structType, LiteralInteger member, Decoration decoration, params IOperand[] parameters) + public Instruction MemberDecorateStringGOOGLE(Instruction structType, LiteralInteger member, Decoration decoration, params ReadOnlySpan parameters) { Instruction result = NewInstruction(Op.OpMemberDecorateStringGOOGLE); @@ -458,7 +459,7 @@ namespace Spv.Generator return result; } - public Instruction TypeStruct(bool forceIdAllocation, params Instruction[] parameters) + public Instruction TypeStruct(bool forceIdAllocation, params ReadOnlySpan parameters) { Instruction result = NewInstruction(Op.OpTypeStruct); @@ -489,7 +490,7 @@ namespace Spv.Generator return result; } - public Instruction TypeFunction(Instruction returnType, bool forceIdAllocation, params Instruction[] parameters) + public Instruction TypeFunction(Instruction returnType, bool forceIdAllocation, params ReadOnlySpan parameters) { Instruction result = NewInstruction(Op.OpTypeFunction); @@ -605,7 +606,7 @@ namespace Spv.Generator return result; } - public Instruction ConstantComposite(Instruction resultType, params Instruction[] constituents) + public Instruction ConstantComposite(Instruction resultType, params ReadOnlySpan constituents) { Instruction result = NewInstruction(Op.OpConstantComposite, Instruction.InvalidId, resultType); @@ -664,7 +665,7 @@ namespace Spv.Generator return result; } - public Instruction SpecConstantComposite(Instruction resultType, params Instruction[] constituents) + public Instruction SpecConstantComposite(Instruction resultType, params ReadOnlySpan constituents) { Instruction result = NewInstruction(Op.OpSpecConstantComposite, GetNewId(), resultType); @@ -814,7 +815,7 @@ namespace Spv.Generator return result; } - public Instruction AccessChain(Instruction resultType, Instruction baseObj, params Instruction[] indexes) + public Instruction AccessChain(Instruction resultType, Instruction baseObj, params ReadOnlySpan indexes) { Instruction result = NewInstruction(Op.OpAccessChain, GetNewId(), resultType); @@ -825,7 +826,7 @@ namespace Spv.Generator return result; } - public Instruction InBoundsAccessChain(Instruction resultType, Instruction baseObj, params Instruction[] indexes) + public Instruction InBoundsAccessChain(Instruction resultType, Instruction baseObj, params ReadOnlySpan indexes) { Instruction result = NewInstruction(Op.OpInBoundsAccessChain, GetNewId(), resultType); @@ -836,7 +837,7 @@ namespace Spv.Generator return result; } - public Instruction PtrAccessChain(Instruction resultType, Instruction baseObj, Instruction element, params Instruction[] indexes) + public Instruction PtrAccessChain(Instruction resultType, Instruction baseObj, Instruction element, params ReadOnlySpan indexes) { Instruction result = NewInstruction(Op.OpPtrAccessChain, GetNewId(), resultType); @@ -869,7 +870,7 @@ namespace Spv.Generator return result; } - public Instruction InBoundsPtrAccessChain(Instruction resultType, Instruction baseObj, Instruction element, params Instruction[] indexes) + public Instruction InBoundsPtrAccessChain(Instruction resultType, Instruction baseObj, Instruction element, params ReadOnlySpan indexes) { Instruction result = NewInstruction(Op.OpInBoundsPtrAccessChain, GetNewId(), resultType); @@ -949,7 +950,7 @@ namespace Spv.Generator return result; } - public Instruction FunctionCall(Instruction resultType, Instruction function, params Instruction[] parameters) + public Instruction FunctionCall(Instruction resultType, Instruction function, params ReadOnlySpan parameters) { Instruction result = NewInstruction(Op.OpFunctionCall, GetNewId(), resultType); @@ -973,7 +974,7 @@ namespace Spv.Generator return result; } - public Instruction ImageSampleImplicitLod(Instruction resultType, Instruction sampledImage, Instruction coordinate, ImageOperandsMask imageOperands, params Instruction[] imageOperandIds) + public Instruction ImageSampleImplicitLod(Instruction resultType, Instruction sampledImage, Instruction coordinate, ImageOperandsMask imageOperands, params ReadOnlySpan imageOperandIds) { Instruction result = NewInstruction(Op.OpImageSampleImplicitLod, GetNewId(), resultType); @@ -992,7 +993,7 @@ namespace Spv.Generator return result; } - public Instruction ImageSampleExplicitLod(Instruction resultType, Instruction sampledImage, Instruction coordinate, ImageOperandsMask imageOperands, params Instruction[] imageOperandIds) + public Instruction ImageSampleExplicitLod(Instruction resultType, Instruction sampledImage, Instruction coordinate, ImageOperandsMask imageOperands, params ReadOnlySpan imageOperandIds) { Instruction result = NewInstruction(Op.OpImageSampleExplicitLod, GetNewId(), resultType); @@ -1008,7 +1009,7 @@ namespace Spv.Generator return result; } - public Instruction ImageSampleDrefImplicitLod(Instruction resultType, Instruction sampledImage, Instruction coordinate, Instruction dRef, ImageOperandsMask imageOperands, params Instruction[] imageOperandIds) + public Instruction ImageSampleDrefImplicitLod(Instruction resultType, Instruction sampledImage, Instruction coordinate, Instruction dRef, ImageOperandsMask imageOperands, params ReadOnlySpan imageOperandIds) { Instruction result = NewInstruction(Op.OpImageSampleDrefImplicitLod, GetNewId(), resultType); @@ -1028,7 +1029,7 @@ namespace Spv.Generator return result; } - public Instruction ImageSampleDrefExplicitLod(Instruction resultType, Instruction sampledImage, Instruction coordinate, Instruction dRef, ImageOperandsMask imageOperands, params Instruction[] imageOperandIds) + public Instruction ImageSampleDrefExplicitLod(Instruction resultType, Instruction sampledImage, Instruction coordinate, Instruction dRef, ImageOperandsMask imageOperands, params ReadOnlySpan imageOperandIds) { Instruction result = NewInstruction(Op.OpImageSampleDrefExplicitLod, GetNewId(), resultType); @@ -1045,7 +1046,7 @@ namespace Spv.Generator return result; } - public Instruction ImageSampleProjImplicitLod(Instruction resultType, Instruction sampledImage, Instruction coordinate, ImageOperandsMask imageOperands, params Instruction[] imageOperandIds) + public Instruction ImageSampleProjImplicitLod(Instruction resultType, Instruction sampledImage, Instruction coordinate, ImageOperandsMask imageOperands, params ReadOnlySpan imageOperandIds) { Instruction result = NewInstruction(Op.OpImageSampleProjImplicitLod, GetNewId(), resultType); @@ -1064,7 +1065,7 @@ namespace Spv.Generator return result; } - public Instruction ImageSampleProjExplicitLod(Instruction resultType, Instruction sampledImage, Instruction coordinate, ImageOperandsMask imageOperands, params Instruction[] imageOperandIds) + public Instruction ImageSampleProjExplicitLod(Instruction resultType, Instruction sampledImage, Instruction coordinate, ImageOperandsMask imageOperands, params ReadOnlySpan imageOperandIds) { Instruction result = NewInstruction(Op.OpImageSampleProjExplicitLod, GetNewId(), resultType); @@ -1080,7 +1081,7 @@ namespace Spv.Generator return result; } - public Instruction ImageSampleProjDrefImplicitLod(Instruction resultType, Instruction sampledImage, Instruction coordinate, Instruction dRef, ImageOperandsMask imageOperands, params Instruction[] imageOperandIds) + public Instruction ImageSampleProjDrefImplicitLod(Instruction resultType, Instruction sampledImage, Instruction coordinate, Instruction dRef, ImageOperandsMask imageOperands, params ReadOnlySpan imageOperandIds) { Instruction result = NewInstruction(Op.OpImageSampleProjDrefImplicitLod, GetNewId(), resultType); @@ -1100,7 +1101,7 @@ namespace Spv.Generator return result; } - public Instruction ImageSampleProjDrefExplicitLod(Instruction resultType, Instruction sampledImage, Instruction coordinate, Instruction dRef, ImageOperandsMask imageOperands, params Instruction[] imageOperandIds) + public Instruction ImageSampleProjDrefExplicitLod(Instruction resultType, Instruction sampledImage, Instruction coordinate, Instruction dRef, ImageOperandsMask imageOperands, params ReadOnlySpan imageOperandIds) { Instruction result = NewInstruction(Op.OpImageSampleProjDrefExplicitLod, GetNewId(), resultType); @@ -1117,7 +1118,7 @@ namespace Spv.Generator return result; } - public Instruction ImageFetch(Instruction resultType, Instruction image, Instruction coordinate, ImageOperandsMask imageOperands, params Instruction[] imageOperandIds) + public Instruction ImageFetch(Instruction resultType, Instruction image, Instruction coordinate, ImageOperandsMask imageOperands, params ReadOnlySpan imageOperandIds) { Instruction result = NewInstruction(Op.OpImageFetch, GetNewId(), resultType); @@ -1136,7 +1137,7 @@ namespace Spv.Generator return result; } - public Instruction ImageGather(Instruction resultType, Instruction sampledImage, Instruction coordinate, Instruction component, ImageOperandsMask imageOperands, params Instruction[] imageOperandIds) + public Instruction ImageGather(Instruction resultType, Instruction sampledImage, Instruction coordinate, Instruction component, ImageOperandsMask imageOperands, params ReadOnlySpan imageOperandIds) { Instruction result = NewInstruction(Op.OpImageGather, GetNewId(), resultType); @@ -1156,7 +1157,7 @@ namespace Spv.Generator return result; } - public Instruction ImageDrefGather(Instruction resultType, Instruction sampledImage, Instruction coordinate, Instruction dRef, ImageOperandsMask imageOperands, params Instruction[] imageOperandIds) + public Instruction ImageDrefGather(Instruction resultType, Instruction sampledImage, Instruction coordinate, Instruction dRef, ImageOperandsMask imageOperands, params ReadOnlySpan imageOperandIds) { Instruction result = NewInstruction(Op.OpImageDrefGather, GetNewId(), resultType); @@ -1176,7 +1177,7 @@ namespace Spv.Generator return result; } - public Instruction ImageRead(Instruction resultType, Instruction image, Instruction coordinate, ImageOperandsMask imageOperands, params Instruction[] imageOperandIds) + public Instruction ImageRead(Instruction resultType, Instruction image, Instruction coordinate, ImageOperandsMask imageOperands, params ReadOnlySpan imageOperandIds) { Instruction result = NewInstruction(Op.OpImageRead, GetNewId(), resultType); @@ -1195,7 +1196,7 @@ namespace Spv.Generator return result; } - public Instruction ImageWrite(Instruction image, Instruction coordinate, Instruction texel, ImageOperandsMask imageOperands, params Instruction[] imageOperandIds) + public Instruction ImageWrite(Instruction image, Instruction coordinate, Instruction texel, ImageOperandsMask imageOperands, params ReadOnlySpan imageOperandIds) { Instruction result = NewInstruction(Op.OpImageWrite); @@ -1297,7 +1298,7 @@ namespace Spv.Generator return result; } - public Instruction ImageSparseSampleImplicitLod(Instruction resultType, Instruction sampledImage, Instruction coordinate, ImageOperandsMask imageOperands, params Instruction[] imageOperandIds) + public Instruction ImageSparseSampleImplicitLod(Instruction resultType, Instruction sampledImage, Instruction coordinate, ImageOperandsMask imageOperands, params ReadOnlySpan imageOperandIds) { Instruction result = NewInstruction(Op.OpImageSparseSampleImplicitLod, GetNewId(), resultType); @@ -1316,7 +1317,7 @@ namespace Spv.Generator return result; } - public Instruction ImageSparseSampleExplicitLod(Instruction resultType, Instruction sampledImage, Instruction coordinate, ImageOperandsMask imageOperands, params Instruction[] imageOperandIds) + public Instruction ImageSparseSampleExplicitLod(Instruction resultType, Instruction sampledImage, Instruction coordinate, ImageOperandsMask imageOperands, params ReadOnlySpan imageOperandIds) { Instruction result = NewInstruction(Op.OpImageSparseSampleExplicitLod, GetNewId(), resultType); @@ -1332,7 +1333,7 @@ namespace Spv.Generator return result; } - public Instruction ImageSparseSampleDrefImplicitLod(Instruction resultType, Instruction sampledImage, Instruction coordinate, Instruction dRef, ImageOperandsMask imageOperands, params Instruction[] imageOperandIds) + public Instruction ImageSparseSampleDrefImplicitLod(Instruction resultType, Instruction sampledImage, Instruction coordinate, Instruction dRef, ImageOperandsMask imageOperands, params ReadOnlySpan imageOperandIds) { Instruction result = NewInstruction(Op.OpImageSparseSampleDrefImplicitLod, GetNewId(), resultType); @@ -1352,7 +1353,7 @@ namespace Spv.Generator return result; } - public Instruction ImageSparseSampleDrefExplicitLod(Instruction resultType, Instruction sampledImage, Instruction coordinate, Instruction dRef, ImageOperandsMask imageOperands, params Instruction[] imageOperandIds) + public Instruction ImageSparseSampleDrefExplicitLod(Instruction resultType, Instruction sampledImage, Instruction coordinate, Instruction dRef, ImageOperandsMask imageOperands, params ReadOnlySpan imageOperandIds) { Instruction result = NewInstruction(Op.OpImageSparseSampleDrefExplicitLod, GetNewId(), resultType); @@ -1369,7 +1370,7 @@ namespace Spv.Generator return result; } - public Instruction ImageSparseSampleProjImplicitLod(Instruction resultType, Instruction sampledImage, Instruction coordinate, ImageOperandsMask imageOperands, params Instruction[] imageOperandIds) + public Instruction ImageSparseSampleProjImplicitLod(Instruction resultType, Instruction sampledImage, Instruction coordinate, ImageOperandsMask imageOperands, params ReadOnlySpan imageOperandIds) { Instruction result = NewInstruction(Op.OpImageSparseSampleProjImplicitLod, GetNewId(), resultType); @@ -1388,7 +1389,7 @@ namespace Spv.Generator return result; } - public Instruction ImageSparseSampleProjExplicitLod(Instruction resultType, Instruction sampledImage, Instruction coordinate, ImageOperandsMask imageOperands, params Instruction[] imageOperandIds) + public Instruction ImageSparseSampleProjExplicitLod(Instruction resultType, Instruction sampledImage, Instruction coordinate, ImageOperandsMask imageOperands, params ReadOnlySpan imageOperandIds) { Instruction result = NewInstruction(Op.OpImageSparseSampleProjExplicitLod, GetNewId(), resultType); @@ -1404,7 +1405,7 @@ namespace Spv.Generator return result; } - public Instruction ImageSparseSampleProjDrefImplicitLod(Instruction resultType, Instruction sampledImage, Instruction coordinate, Instruction dRef, ImageOperandsMask imageOperands, params Instruction[] imageOperandIds) + public Instruction ImageSparseSampleProjDrefImplicitLod(Instruction resultType, Instruction sampledImage, Instruction coordinate, Instruction dRef, ImageOperandsMask imageOperands, params ReadOnlySpan imageOperandIds) { Instruction result = NewInstruction(Op.OpImageSparseSampleProjDrefImplicitLod, GetNewId(), resultType); @@ -1424,7 +1425,7 @@ namespace Spv.Generator return result; } - public Instruction ImageSparseSampleProjDrefExplicitLod(Instruction resultType, Instruction sampledImage, Instruction coordinate, Instruction dRef, ImageOperandsMask imageOperands, params Instruction[] imageOperandIds) + public Instruction ImageSparseSampleProjDrefExplicitLod(Instruction resultType, Instruction sampledImage, Instruction coordinate, Instruction dRef, ImageOperandsMask imageOperands, params ReadOnlySpan imageOperandIds) { Instruction result = NewInstruction(Op.OpImageSparseSampleProjDrefExplicitLod, GetNewId(), resultType); @@ -1441,7 +1442,7 @@ namespace Spv.Generator return result; } - public Instruction ImageSparseFetch(Instruction resultType, Instruction image, Instruction coordinate, ImageOperandsMask imageOperands, params Instruction[] imageOperandIds) + public Instruction ImageSparseFetch(Instruction resultType, Instruction image, Instruction coordinate, ImageOperandsMask imageOperands, params ReadOnlySpan imageOperandIds) { Instruction result = NewInstruction(Op.OpImageSparseFetch, GetNewId(), resultType); @@ -1460,7 +1461,7 @@ namespace Spv.Generator return result; } - public Instruction ImageSparseGather(Instruction resultType, Instruction sampledImage, Instruction coordinate, Instruction component, ImageOperandsMask imageOperands, params Instruction[] imageOperandIds) + public Instruction ImageSparseGather(Instruction resultType, Instruction sampledImage, Instruction coordinate, Instruction component, ImageOperandsMask imageOperands, params ReadOnlySpan imageOperandIds) { Instruction result = NewInstruction(Op.OpImageSparseGather, GetNewId(), resultType); @@ -1480,7 +1481,7 @@ namespace Spv.Generator return result; } - public Instruction ImageSparseDrefGather(Instruction resultType, Instruction sampledImage, Instruction coordinate, Instruction dRef, ImageOperandsMask imageOperands, params Instruction[] imageOperandIds) + public Instruction ImageSparseDrefGather(Instruction resultType, Instruction sampledImage, Instruction coordinate, Instruction dRef, ImageOperandsMask imageOperands, params ReadOnlySpan imageOperandIds) { Instruction result = NewInstruction(Op.OpImageSparseDrefGather, GetNewId(), resultType); @@ -1510,7 +1511,7 @@ namespace Spv.Generator return result; } - public Instruction ImageSparseRead(Instruction resultType, Instruction image, Instruction coordinate, ImageOperandsMask imageOperands, params Instruction[] imageOperandIds) + public Instruction ImageSparseRead(Instruction resultType, Instruction image, Instruction coordinate, ImageOperandsMask imageOperands, params ReadOnlySpan imageOperandIds) { Instruction result = NewInstruction(Op.OpImageSparseRead, GetNewId(), resultType); @@ -1529,7 +1530,7 @@ namespace Spv.Generator return result; } - public Instruction ImageSampleFootprintNV(Instruction resultType, Instruction sampledImage, Instruction coordinate, Instruction granularity, Instruction coarse, ImageOperandsMask imageOperands, params Instruction[] imageOperandIds) + public Instruction ImageSampleFootprintNV(Instruction resultType, Instruction sampledImage, Instruction coordinate, Instruction granularity, Instruction coarse, ImageOperandsMask imageOperands, params ReadOnlySpan imageOperandIds) { Instruction result = NewInstruction(Op.OpImageSampleFootprintNV, GetNewId(), resultType); @@ -1738,7 +1739,7 @@ namespace Spv.Generator return result; } - public Instruction VectorShuffle(Instruction resultType, Instruction vector1, Instruction vector2, params LiteralInteger[] components) + public Instruction VectorShuffle(Instruction resultType, Instruction vector1, Instruction vector2, params ReadOnlySpan components) { Instruction result = NewInstruction(Op.OpVectorShuffle, GetNewId(), resultType); @@ -1750,7 +1751,7 @@ namespace Spv.Generator return result; } - public Instruction CompositeConstruct(Instruction resultType, params Instruction[] constituents) + public Instruction CompositeConstruct(Instruction resultType, params ReadOnlySpan constituents) { Instruction result = NewInstruction(Op.OpCompositeConstruct, GetNewId(), resultType); @@ -1760,7 +1761,7 @@ namespace Spv.Generator return result; } - public Instruction CompositeExtract(Instruction resultType, Instruction composite, params LiteralInteger[] indexes) + public Instruction CompositeExtract(Instruction resultType, Instruction composite, params ReadOnlySpan indexes) { Instruction result = NewInstruction(Op.OpCompositeExtract, GetNewId(), resultType); @@ -1771,7 +1772,7 @@ namespace Spv.Generator return result; } - public Instruction CompositeInsert(Instruction resultType, Instruction obj, Instruction composite, params LiteralInteger[] indexes) + public Instruction CompositeInsert(Instruction resultType, Instruction obj, Instruction composite, params ReadOnlySpan indexes) { Instruction result = NewInstruction(Op.OpCompositeInsert, GetNewId(), resultType); @@ -2752,7 +2753,7 @@ namespace Spv.Generator // Control-Flow - public Instruction Phi(Instruction resultType, params Instruction[] parameters) + public Instruction Phi(Instruction resultType, params ReadOnlySpan parameters) { Instruction result = NewInstruction(Op.OpPhi, GetNewId(), resultType); @@ -2802,7 +2803,7 @@ namespace Spv.Generator return result; } - public Instruction BranchConditional(Instruction condition, Instruction trueLabel, Instruction falseLabel, params LiteralInteger[] branchweights) + public Instruction BranchConditional(Instruction condition, Instruction trueLabel, Instruction falseLabel, params ReadOnlySpan branchweights) { Instruction result = NewInstruction(Op.OpBranchConditional); @@ -2815,7 +2816,7 @@ namespace Spv.Generator return result; } - public Instruction Switch(Instruction selector, Instruction defaultObj, params IOperand[] target) + public Instruction Switch(Instruction selector, Instruction defaultObj, params ReadOnlySpan target) { Instruction result = NewInstruction(Op.OpSwitch); @@ -3678,7 +3679,7 @@ namespace Spv.Generator return result; } - public Instruction EnqueueKernel(Instruction resultType, Instruction queue, Instruction flags, Instruction nDRange, Instruction numEvents, Instruction waitEvents, Instruction retEvent, Instruction invoke, Instruction param, Instruction paramSize, Instruction paramAlign, params Instruction[] localSize) + public Instruction EnqueueKernel(Instruction resultType, Instruction queue, Instruction flags, Instruction nDRange, Instruction numEvents, Instruction waitEvents, Instruction retEvent, Instruction invoke, Instruction param, Instruction paramSize, Instruction paramAlign, params ReadOnlySpan localSize) { Instruction result = NewInstruction(Op.OpEnqueueKernel, GetNewId(), resultType); @@ -5108,7 +5109,7 @@ namespace Spv.Generator return result; } - public Instruction LoopControlINTEL(params LiteralInteger[] loopControlParameters) + public Instruction LoopControlINTEL(params ReadOnlySpan loopControlParameters) { Instruction result = NewInstruction(Op.OpLoopControlINTEL); diff --git a/src/Spv.Generator/Instruction.cs b/src/Spv.Generator/Instruction.cs index 45492033f..741b30d6d 100644 --- a/src/Spv.Generator/Instruction.cs +++ b/src/Spv.Generator/Instruction.cs @@ -64,7 +64,7 @@ namespace Spv.Generator _operands.Add(value); } - public void AddOperand(IOperand[] value) + public void AddOperand(ReadOnlySpan value) { foreach (IOperand instruction in value) { @@ -72,7 +72,7 @@ namespace Spv.Generator } } - public void AddOperand(LiteralInteger[] value) + public void AddOperand(ReadOnlySpan value) { foreach (LiteralInteger instruction in value) { @@ -85,7 +85,7 @@ namespace Spv.Generator AddOperand((IOperand)value); } - public void AddOperand(Instruction[] value) + public void AddOperand(ReadOnlySpan value) { foreach (Instruction instruction in value) { diff --git a/src/Spv.Generator/Module.cs b/src/Spv.Generator/Module.cs index fb02cc966..fdcd62752 100644 --- a/src/Spv.Generator/Module.cs +++ b/src/Spv.Generator/Module.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -132,7 +133,7 @@ namespace Spv.Generator _typeDeclarationsList.Add(instruction); } - public void AddEntryPoint(ExecutionModel executionModel, Instruction function, string name, params Instruction[] interfaces) + public void AddEntryPoint(ExecutionModel executionModel, Instruction function, string name, params ReadOnlySpan interfaces) { Debug.Assert(function.Opcode == Op.OpFunction); @@ -146,7 +147,7 @@ namespace Spv.Generator _entrypoints.Add(entryPoint); } - public void AddExecutionMode(Instruction function, ExecutionMode mode, params IOperand[] parameters) + public void AddExecutionMode(Instruction function, ExecutionMode mode, params ReadOnlySpan parameters) { Debug.Assert(function.Opcode == Op.OpFunction); @@ -228,7 +229,7 @@ namespace Spv.Generator _constants.Add(key, constant); } - public Instruction ExtInst(Instruction resultType, Instruction set, LiteralInteger instruction, params IOperand[] parameters) + public Instruction ExtInst(Instruction resultType, Instruction set, LiteralInteger instruction, params ReadOnlySpan parameters) { Instruction result = NewInstruction(Op.OpExtInst, GetNewId(), resultType); @@ -247,7 +248,7 @@ namespace Spv.Generator } // TODO: Find a way to make the auto generate one used. - public Instruction OpenClPrintf(Instruction resultType, Instruction format, params Instruction[] additionalarguments) + public Instruction OpenClPrintf(Instruction resultType, Instruction format, params ReadOnlySpan additionalarguments) { Instruction result = NewInstruction(Op.OpExtInst, GetNewId(), resultType); diff --git a/src/Spv.Generator/Spv.Generator.csproj b/src/Spv.Generator/Spv.Generator.csproj index ae2821edb..3642709d8 100644 --- a/src/Spv.Generator/Spv.Generator.csproj +++ b/src/Spv.Generator/Spv.Generator.csproj @@ -1,7 +1,7 @@  - net8.0 + $(DefaultItemExcludes);._*