Compare commits

..

2 Commits

Author SHA1 Message Date
GreemDev
e4abc3a960 cleanup 2026-01-04 19:34:32 -06:00
GreemDev
8ccbf33327 Replace CommandLineState with a more user-friendly CLI experience. 2026-01-04 05:18:32 -06:00
190 changed files with 1816 additions and 5436 deletions

View File

@@ -1,5 +0,0 @@
blank_issues_enabled: true
contact_links:
- name: Ryubing Issue Tracker
url: https://github.com/Ryubing/Issues/issues/
about: "Please use this GitHub repository instead of creating issues on this Forgejo repository. Blank issues should only be used by maintainers and authorized bots. Issues made on this repository can and will be deleted."

View File

@@ -1,35 +0,0 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"renovate/config"
],
"enabledManagers": ["nuget", "github-actions"],
"packageRules": [
{
// require approval for *all* NuGet package updates, not just major versions.
"matchDepTypes": "nuget",
"dependencyDashboardApproval": true
},
{
// Ignore Gommon for automatic updates. I make breaking changes on minor updates not infrequently.
"matchDepNames": "Gommon",
"matchDepTypes": "nuget",
"enabled": false
},
{
"description": "group Silk.NET packages",
"extends": ["renovate/config//groups/silkdotnet.json"],
"groupName": "Silk.NET"
},
{
"description": "group OpenTK packages",
"extends": ["renovate/config//groups/opentk.json"],
"groupName": "OpenTK"
},
{
"description": "group Svg.Controls packages",
"extends": ["renovate/config//groups/svgcontrols.json"],
"groupName": "Svg.Controls"
}
]
}

View File

@@ -1,204 +0,0 @@
name: Build PR
on:
pull_request:
branches: [ master ]
paths:
- '**'
- '!.forgejo/**'
- '!*.yml'
- '!*.config'
- '!*.md'
- '.forgejo/workflows/*.yml'
env:
POWERSHELL_TELEMETRY_OPTOUT: 1
DOTNET_CLI_TELEMETRY_OPTOUT: 1
RELEASE: 0
jobs:
build:
name: ${{ matrix.platform.name }} (${{ matrix.configuration }})
runs-on: docker
container:
image: ghcr.io/catthehacker/ubuntu:act-latest
timeout-minutes: 45
strategy:
matrix:
configuration: [Release]
platform:
- { name: win-x64, zip_os_name: win_x64 }
#- { name: win-arm64, zip_os_name: win_arm64 }
- { name: linux-x64, zip_os_name: linux_x64 }
- { name: linux-arm64, zip_os_name: linux_arm64 }
#- { name: osx-x64, zip_os_name: osx_x64 }
fail-fast: false
steps:
- uses: actions/checkout@v6
- uses: actions/setup-dotnet@v5
with:
global-json-file: global.json
- name: Install GLI
uses: actions/setup-gli@v1
with:
token: ${{ secrets.SETUP_GLI_TOKEN }}
- name: Install 7zip
run: |
sudo apt update && sudo apt install -y 7zip
- name: Overwrite csc problem matcher
run: echo "::add-matcher::.forgejo/csc.json"
- name: Get version info
id: version_info
run: |
echo "result=$(gli get-next-version -c Canary -R)" >> $FORGEJO_OUTPUT
echo "git_short_hash=$(git rev-parse --short "${{ forgejo.sha }}")" >> $FORGEJO_OUTPUT
shell: bash
- name: Change config filename
run: sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/PRConfig\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
shell: bash
if: forgejo.event_name == 'pull_request'
- name: 'Cache: ~/.nuget/packages'
uses: actions/cache@v5
with:
path: |
~/.nuget/packages
key: ${{ runner.os }}-${{ hashFiles('**/global.json', '**/*.csproj', '**/Directory.Packages.props') }}
- name: Build
run: dotnet build -c "${{ matrix.configuration }}" -p:Version="${{ steps.version_info.outputs.result }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:ExtraDefineConstants=DISABLE_UPDATER
- name: Test
uses: actions/unstable-commands@v1
with:
commands: dotnet test --no-build -c "${{ matrix.configuration }}"
timeout-minutes: 10
retry-codes: 139
if: matrix.platform.name != 'linux-arm64'
- name: Publish Ryujinx
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ steps.version_info.outputs.result }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx --self-contained
if: forgejo.event_name == 'pull_request'
- name: Packing Windows builds
if: contains(matrix.platform.name, 'win')
run: |
7z a artifact/ryujinx-${{ matrix.configuration }}-${{ steps.version_info.outputs.result }}+${{ steps.version_info.outputs.git_short_hash }}-${{ matrix.platform.zip_os_name }}.7z publish
shell: bash
- name: Upload Ryujinx Windows artifact
uses: actions/upload-artifact@v5
with:
name: ryujinx-${{ matrix.configuration }}-${{ steps.version_info.outputs.result }}+${{ steps.version_info.outputs.git_short_hash }}-${{ matrix.platform.zip_os_name }}
path: artifact
if: forgejo.event_name == 'pull_request' && contains(matrix.platform.name, 'win')
- name: Build AppImage
if: forgejo.event_name == 'pull_request' && contains(matrix.platform.name, 'linux')
run: |
chmod +x ./publish/Ryujinx ./publish/Ryujinx.sh
PLATFORM_NAME="${{ matrix.platform.name }}"
sudo apt update && sudo apt install -y zsync desktop-file-utils appstream libfuse2t64
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
BUILDDIR=publish OUTDIR=publish_appimage distribution/linux/appimage/build-appimage.sh
shell: bash
- name: Upload Ryujinx AppImage artifact
uses: actions/upload-artifact@v5
if: forgejo.event_name == 'pull_request' && contains(matrix.platform.name, 'linux')
with:
name: ryujinx-${{ matrix.configuration }}-${{ steps.version_info.outputs.result }}+${{ steps.version_info.outputs.git_short_hash }}-${{ matrix.platform.zip_os_name }}-AppImage
path: publish_appimage
build_macos:
name: macOS Universal (${{ matrix.configuration }})
runs-on: ubuntu-latest
timeout-minutes: 45
strategy:
matrix:
configuration: [ Release ]
steps:
- uses: actions/checkout@v6
- uses: actions/setup-dotnet@v5
with:
global-json-file: global.json
- name: Setup LLVM 17
run: |
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 17
- name: Install GLI
uses: actions/setup-gli@v1
with:
token: ${{ secrets.SETUP_GLI_TOKEN }}
- name: Install rcodesign
run: |
gli ghr -R indygreg/apple-platform-rs -p apple-codesign-*-x86_64-unknown-linux-musl.tar.gz -O apple-codesign.tar.gz
tar -xzvf apple-codesign.tar.gz --wildcards '*/rcodesign' --strip-components=1
rm apple-codesign.tar.gz
sudo mv rcodesign /usr/bin/rcodesign
- name: Get version info
id: version_info
run: |
echo "result=$(gli get-next-version -c Stable -R)" >> $FORGEJO_OUTPUT
echo "git_short_hash=$(git rev-parse --short "${{ forgejo.sha }}")" >> $FORGEJO_OUTPUT
shell: bash
- name: Change config filename
run: sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/PRConfig\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
shell: bash
if: forgejo.event_name == 'pull_request'
- name: 'Cache: ~/.nuget/packages'
uses: actions/cache@v5
with:
path: |
~/.nuget/packages
key: ${{ runner.os }}-${{ hashFiles('**/global.json', '**/*.csproj', '**/Directory.Packages.props') }}
- name: Publish macOS Ryujinx
run: |
bash distribution/macos/create_macos_pr_build_ava.sh . publish_tmp publish ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.result }}" "${{ steps.version_info.outputs.git_short_hash }}" "${{ matrix.configuration }}" "-p:ExtraDefineConstants=DISABLE_UPDATER"
shell: bash
- name: Upload Ryujinx artifact
uses: actions/upload-artifact@v5
with:
name: ryujinx-${{ matrix.configuration }}-${{ steps.version_info.outputs.result }}+${{ steps.version_info.outputs.git_short_hash }}-macos_universal
path: "publish/*.tar.gz"
if: forgejo.event_name == 'pull_request'

86
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,86 @@
name: Bug Report
description: File a bug report
title: "[Bug]"
labels: bug
body:
- type: textarea
id: issue
attributes:
label: Description of the issue
description: What's the issue you encountered?
validations:
required: true
- type: textarea
id: repro
attributes:
label: Reproduction steps
description: How can the issue be reproduced?
placeholder: Describe each step as precisely as possible
validations:
required: true
- type: textarea
id: log
attributes:
label: Log file
description: "A log file will help our developers to better diagnose and fix the issue. UPLOAD THE FILE. DO NOT COPY AND PASTE THE FILE'S CONTENT."
placeholder: Logs files can be found under "Logs" folder in Ryujinx program folder. They can also be accessed by opening Ryujinx, then going to File > Open Logs Folder. You can drag and drop the log on to the text area (do not copy paste).
validations:
required: true
- type: input
id: os
attributes:
label: OS
placeholder: "e.g. Windows 10"
validations:
required: true
- type: input
id: ryujinx-version
attributes:
label: Ryujinx version
placeholder: "e.g. 1.0.470"
validations:
required: true
- type: input
id: game-version
attributes:
label: Game version
placeholder: "e.g. 1.1.1"
validations:
required: false
- type: input
id: cpu
attributes:
label: CPU
placeholder: "e.g. i7-6700"
validations:
required: false
- type: input
id: gpu
attributes:
label: GPU
placeholder: "e.g. NVIDIA RTX 2070"
validations:
required: false
- type: input
id: ram
attributes:
label: RAM
placeholder: "e.g. 16GB"
validations:
required: false
- type: textarea
id: mods
attributes:
label: List of applied mods
placeholder: You can list applied mods here.
validations:
required: false
- type: textarea
id: additional-context
attributes:
label: Additional context?
description: |
- Additional info about your environment:
- Any other information relevant to your issue.
validations:
required: false

5
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Ryujinx Discord
url: https://discord.gg/N2FmfVc
about: This is for development related issues. For support and technical issues, please come to our Discord server.

View File

@@ -0,0 +1,31 @@
name: Feature Request
description: Suggest a new feature for Ryujinx.
title: "[Feature Request]"
labels: enhancement
body:
- type: textarea
id: overview
attributes:
label: Overview
description: Include the basic, high-level concepts for this feature here.
validations:
required: true
- type: textarea
id: details
attributes:
label: Smaller details
description: These may include specific methods of implementation etc.
validations:
required: true
- type: textarea
id: request
attributes:
label: Nature of request
validations:
required: true
- type: textarea
id: feature
attributes:
label: Why would this feature be useful?
validations:
required: true

View File

@@ -0,0 +1,26 @@
name: Missing CPU Instruction
description: CPU Instruction is missing in Ryujinx.
title: "[CPU]"
labels: [cpu, not-implemented]
body:
- type: textarea
id: instruction
attributes:
label: CPU instruction
description: What CPU instruction is missing?
validations:
required: true
- type: textarea
id: name
attributes:
label: Instruction name
description: Include the name from [armconverter.com](https://armconverter.com/?disasm) or [shell-storm.org](http://shell-storm.org/online/Online-Assembler-and-Disassembler/?arch=arm64&endianness=big&dis_with_raw=True&dis_with_ins=True) in the above code block
validations:
required: true
- type: textarea
id: required
attributes:
label: Required by
description: Add links to the [compatibility list page(s)](https://github.com/Ryujinx/Ryujinx-Games-List/issues) of the game(s) that require this instruction.
validations:
required: true

View File

@@ -0,0 +1,25 @@
name: Missing Service Call
description: Service call is missing in Ryujinx.
labels: not-implemented
body:
- type: textarea
id: instruction
attributes:
label: Service call
description: What service call is missing?
validations:
required: true
- type: textarea
id: name
attributes:
label: Service description
description: Include the description/explanation from [Switchbrew](https://switchbrew.org/w/index.php?title=Services_API) and/or [SwIPC](https://reswitched.github.io/SwIPC/) in the above code block
validations:
required: true
- type: textarea
id: required
attributes:
label: Required by
description: Add links to the [compatibility list page(s)](https://github.com/Ryujinx/Ryujinx-Games-List/issues) of the game(s) that require this service.
validations:
required: true

View File

@@ -0,0 +1,19 @@
name: Missing Shader Instruction
description: Shader Instruction is missing in Ryujinx.
title: "[GPU]"
labels: [gpu, not-implemented]
body:
- type: textarea
id: instruction
attributes:
label: Shader instruction
description: What shader instruction is missing?
validations:
required: true
- type: textarea
id: required
attributes:
label: Required by
description: Add links to the [compatibility list page(s)](https://github.com/Ryujinx/Ryujinx-Games-List/issues) of the game(s) that require this instruction.
validations:
required: true

View File

@@ -10,10 +10,6 @@ gpu:
- changed-files: - changed-files:
- any-glob-to-any-file: ['src/Ryujinx.Graphics.*/**', 'src/Spv.Generator/**', 'src/Ryujinx.ShaderTools/**'] - any-glob-to-any-file: ['src/Ryujinx.Graphics.*/**', 'src/Spv.Generator/**', 'src/Ryujinx.ShaderTools/**']
input:
- changed-files:
- any-glob-to-any-file: ['src/Ryujinx.Input*/**', 'src/Ryujinx/UI/Views/Input/**']
'graphics-backend:opengl': 'graphics-backend:opengl':
- changed-files: - changed-files:
- any-glob-to-any-file: 'src/Ryujinx.Graphics.OpenGL/**' - any-glob-to-any-file: 'src/Ryujinx.Graphics.OpenGL/**'
@@ -22,17 +18,17 @@ input:
- changed-files: - changed-files:
- any-glob-to-any-file: ['src/Ryujinx.Graphics.Vulkan/**', 'src/Spv.Generator/**'] - any-glob-to-any-file: ['src/Ryujinx.Graphics.Vulkan/**', 'src/Spv.Generator/**']
'graphics-backend:metal':
- changed-files:
- any-glob-to-any-file: ['src/Ryujinx.Graphics.Metal/**', 'src/Ryujinx.Graphics.Metal.SharpMetalExtensions/**']
gui: gui:
- changed-files: - changed-files:
- any-glob-to-any-file: ['src/Ryujinx/**', 'src/Ryujinx.UI.LocaleGenerator/**'] - any-glob-to-any-file: ['src/Ryujinx/**', 'src/Ryujinx.UI.Common/**', 'src/Ryujinx.UI.LocaleGenerator/**']
'horizon/hle': horizon:
- changed-files: - changed-files:
- any-glob-to-any-file: ['src/Ryujinx.HLE/**', 'src/Ryujinx.HLE.Generators/**', 'src/Ryujinx.Horizon/**'] - any-glob-to-any-file: ['src/Ryujinx.HLE/**', 'src/Ryujinx.Horizon/**']
i18n:
- changed-files:
- any-glob-to-any-file: ['assets/**/*.json', 'src/Ryujinx.UI.LocaleGenerator/**']
kernel: kernel:
- changed-files: - changed-files:
@@ -40,7 +36,7 @@ kernel:
infra: infra:
- changed-files: - changed-files:
- any-glob-to-any-file: ['.forgejo/**', 'distribution/**', 'Directory.Packages.props', 'src/Ryujinx.BuildValidationTasks/**'] - any-glob-to-any-file: ['.github/**', 'distribution/**', 'Directory.Packages.props', 'src/Ryujinx.BuildValidationTasks/**']
documentation: documentation:
- changed-files: - changed-files:
@@ -48,4 +44,4 @@ documentation:
ldn: ldn:
- changed-files: - changed-files:
- any-glob-to-any-file: ['src/Ryujinx.HLE/HOS/Services/Ldn/**', 'src/Ryujinx/UI/Windows/LdnGamesListWindow.*', 'src/Ryujinx/UI/ViewModels/LdnGamesListViewModel.cs'] - any-glob-to-any-file: 'src/Ryujinx.HLE/HOS/Services/Ldn/**'

168
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,168 @@
name: Build job
on:
workflow_call:
env:
POWERSHELL_TELEMETRY_OPTOUT: 1
DOTNET_CLI_TELEMETRY_OPTOUT: 1
RYUJINX_BASE_VERSION: "1.2.0"
RELEASE: 0
jobs:
build:
name: ${{ matrix.platform.name }} (${{ matrix.configuration }})
runs-on: ${{ matrix.platform.os }}
timeout-minutes: 45
strategy:
matrix:
configuration: [Debug, Release]
platform:
- { name: win-x64, os: windows-latest, zip_os_name: win_x64 }
- { name: win-arm64, os: windows-latest, zip_os_name: win_arm64 }
- { name: linux-x64, os: ubuntu-latest, zip_os_name: linux_x64 }
- { name: linux-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 }
- { name: osx-x64, os: macos-13, zip_os_name: osx_x64 }
fail-fast: false
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 git short hash
id: git_short_hash
run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
shell: bash
- name: Change config filename
run: sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/PRConfig\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
shell: bash
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
- name: Change config filename for macOS
run: sed -r -i '' 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/PRConfig\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
shell: bash
if: github.event_name == 'pull_request' && matrix.platform.os == 'macos-13'
- name: Build
run: dotnet build -c "${{ matrix.configuration }}" -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER
- name: Test
uses: TSRBerry/unstable-commands@v1
with:
commands: dotnet test --no-build -c "${{ matrix.configuration }}"
timeout-minutes: 10
retry-codes: 139
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
if: github.event_name == 'pull_request' && matrix.platform.os != 'macos-13'
- name: Set executable bit
run: |
chmod +x ./publish/Ryujinx ./publish/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:
name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.platform.zip_os_name }}
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
build_macos:
name: macOS Universal (${{ matrix.configuration }})
runs-on: ubuntu-latest
timeout-minutes: 45
strategy:
matrix:
configuration: [ Debug, Release ]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
global-json-file: global.json
- name: Setup LLVM 17
run: |
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 17
- 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 git short hash
id: git_short_hash
run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
- name: Change config filename
run: sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/PRConfig\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
shell: bash
if: github.event_name == 'pull_request'
- name: Publish macOS Ryujinx
run: |
./distribution/macos/create_macos_build_ava.sh . publish_tmp publish ./distribution/macos/entitlements.xml "${{ env.RYUJINX_BASE_VERSION }}" "${{ steps.git_short_hash.outputs.result }}" "${{ matrix.configuration }}" "-p:ExtraDefineConstants=DISABLE_UPDATER"
- name: Upload Ryujinx artifact
uses: actions/upload-artifact@v4
with:
name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal
path: "publish/*.tar.gz"
if: github.event_name == 'pull_request'

View File

@@ -6,7 +6,7 @@ on:
push: push:
branches: [ master ] branches: [ master ]
paths-ignore: paths-ignore:
- '.forgejo/**' - '.github/**'
- 'docs/**' - 'docs/**'
- 'assets/**' - 'assets/**'
- '*.yml' - '*.yml'
@@ -25,41 +25,44 @@ env:
jobs: jobs:
release: release:
name: Release for ${{ matrix.platform.name }} name: Release for ${{ matrix.platform.name }}
runs-on: docker runs-on: ${{ matrix.platform.os }}
container:
image: ${{ matrix.platform.os }}
strategy: strategy:
matrix: matrix:
platform: platform:
- { name: win-x64, os: ghcr.io/catthehacker/ubuntu:act-latest, zip_os_name: win_x64 } - { name: win-x64, os: ubuntu-latest, zip_os_name: win_x64 }
#- { name: win-arm64, os: ghcr.io/catthehacker/ubuntu:act-latest, zip_os_name: win_arm64 } #- { name: win-arm64, os: ubuntu-latest, zip_os_name: win_arm64 }
- { name: linux-x64, os: ghcr.io/catthehacker/ubuntu:act-latest, zip_os_name: linux_x64 } - { name: linux-x64, os: ubuntu-latest, zip_os_name: linux_x64 }
- { name: linux-arm64, os: ghcr.io/catthehacker/ubuntu:act-latest, zip_os_name: linux_arm64 } - { name: linux-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 }
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v4
- uses: actions/setup-dotnet@v5 - uses: actions/setup-dotnet@v4
with: with:
global-json-file: global.json global-json-file: global.json
- name: Overwrite csc problem matcher - name: Overwrite csc problem matcher
run: echo "::add-matcher::.forgejo/csc.json" run: echo "::add-matcher::.github/csc.json"
- name: Install GLI
uses: actions/setup-gli@v1
with:
token: ${{ secrets.SETUP_GLI_TOKEN }}
- name: Install 7zip - name: Install 7zip
run: | run: |
sudo apt update && sudo apt install -y 7zip sudo apt install -y 7zip
- name: Install gli
run: |
mkdir -p $HOME/.bin
gh release download -R GreemDev/GLI -O gli -p 'gli-linux-x64'
chmod +x gli
mv gli $HOME/.bin/
echo "$HOME/.bin" >> $GITHUB_PATH
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Get version info - name: Get version info
id: version_info id: version_info
run: | run: |
echo "build_version=$(gli get-next-version -c Canary -R)" >> $FORGEJO_OUTPUT echo "build_version=$(gli get-next-version -c Canary -R)" >> $GITHUB_OUTPUT
echo "prev_build_version=$(gli get-current-version -c Canary -R)" >> $FORGEJO_OUTPUT echo "prev_build_version=$(gli get-current-version -c Canary -R)" >> $GITHUB_OUTPUT
echo "git_short_hash=$(git rev-parse --short "${{ forgejo.sha }}")" >> $FORGEJO_OUTPUT echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
shell: bash shell: bash
- name: Configure for release - name: Configure for release
@@ -84,9 +87,12 @@ jobs:
pushd publish pushd publish
rm libarmeilleure-jitsupport.dylib rm libarmeilleure-jitsupport.dylib
7z a ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip ../publish 7z a ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip ../publish
7z a ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.7z ../publish
popd popd
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/canary -n Ryubing-Canary -v ${{ steps.version_info.outputs.build_version }} -r 5 -p release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip
shell: bash shell: bash
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Packing Linux builds - name: Packing Linux builds
if: contains(matrix.platform.name, 'linux') if: contains(matrix.platform.name, 'linux')
@@ -95,8 +101,9 @@ jobs:
rm libarmeilleure-jitsupport.dylib rm libarmeilleure-jitsupport.dylib
chmod +x Ryujinx.sh Ryujinx chmod +x Ryujinx.sh Ryujinx
tar -czvf ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz ../publish tar -czvf ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz ../publish
tar -cJvf ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.xz ../publish
popd popd
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/canary -n Ryubing-Canary -v ${{ steps.version_info.outputs.build_version }} -r 5 -p release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz
shell: bash shell: bash
- name: Build AppImage (Linux) - name: Build AppImage (Linux)
@@ -105,7 +112,7 @@ jobs:
BUILD_VERSION="${{ steps.version_info.outputs.build_version }}" BUILD_VERSION="${{ steps.version_info.outputs.build_version }}"
PLATFORM_NAME="${{ matrix.platform.name }}" PLATFORM_NAME="${{ matrix.platform.name }}"
sudo apt update && sudo apt install -y zsync desktop-file-utils appstream libfuse2t64 sudo apt install -y zsync desktop-file-utils appstream
mkdir -p tools mkdir -p tools
export PATH="$PATH:$(readlink -f tools)" export PATH="$PATH:$(readlink -f tools)"
@@ -132,28 +139,17 @@ jobs:
pushd publish_appimage pushd publish_appimage
mv Ryujinx.AppImage ../release_output/ryujinx-canary-$BUILD_VERSION-$ARCH_NAME.AppImage mv Ryujinx.AppImage ../release_output/ryujinx-canary-$BUILD_VERSION-$ARCH_NAME.AppImage
popd popd
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/canary -n Ryubing-Canary -v ${{ steps.version_info.outputs.build_version }} -r 5 -p release_output/ryujinx-canary-$BUILD_VERSION-$ARCH_NAME.AppImage
shell: bash shell: bash
- name: Create release
uses: actions/create-release@v1
with:
name: "Canary ${{ steps.version_info.outputs.build_version }}"
body: "**Full Changelog:** [`${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }}`](https://git.ryujinx.app/projects/Ryubing/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }})"
repository: "Ryubing/Canary"
token: ${{ secrets.RELEASER_TOKEN }}
tag_name: ${{ steps.version_info.outputs.build_version }}
files: |-
release_output/**
macos_release: macos_release:
name: Release MacOS universal name: Release MacOS universal
runs-on: docker runs-on: ubuntu-24.04
container:
image: ghcr.io/catthehacker/ubuntu:act-latest
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v4
- uses: actions/setup-dotnet@v5 - uses: actions/setup-dotnet@v4
with: with:
global-json-file: global.json global-json-file: global.json
@@ -163,24 +159,33 @@ jobs:
chmod +x llvm.sh chmod +x llvm.sh
sudo ./llvm.sh 17 sudo ./llvm.sh 17
- name: Install GLI - name: Install gli
uses: actions/setup-gli@v1 run: |
with: mkdir -p $HOME/.bin
token: ${{ secrets.SETUP_GLI_TOKEN }} gh release download -R GreemDev/GLI -O gli -p 'gli-linux-x64'
chmod +x gli
mv gli $HOME/.bin/
echo "$HOME/.bin" >> $GITHUB_PATH
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install rcodesign - name: Install rcodesign
run: | run: |
gli ghr -R indygreg/apple-platform-rs -p apple-codesign-*-x86_64-unknown-linux-musl.tar.gz -O apple-codesign.tar.gz 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 tar -xzvf apple-codesign.tar.gz --wildcards '*/rcodesign' --strip-components=1
rm apple-codesign.tar.gz rm apple-codesign.tar.gz
mv rcodesign /usr/bin/rcodesign mv rcodesign $HOME/.bin/
echo "$HOME/.bin" >> $GITHUB_PATH
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Get version info - name: Get version info
id: version_info id: version_info
run: | run: |
echo "build_version=$(gli get-next-version -c Canary -R)" >> $FORGEJO_OUTPUT echo "build_version=$(gli get-next-version -c Canary -R)" >> $GITHUB_OUTPUT
echo "prev_build_version=$(gli get-current-version -c Canary -R)" >> $FORGEJO_OUTPUT echo "prev_build_version=$(gli get-current-version -c Canary -R)" >> $GITHUB_OUTPUT
echo "git_short_hash=$(git rev-parse --short "${{ forgejo.sha }}")" >> $FORGEJO_OUTPUT echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
shell: bash shell: bash
- name: Configure for release - name: Configure for release
@@ -196,53 +201,46 @@ jobs:
- name: Publish macOS Ryujinx - name: Publish macOS Ryujinx
run: | 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 ./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
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/canary -n Ryubing-Canary -v ${{ steps.version_info.outputs.build_version }} -r 5 -p publish_ava/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz
- name: Create release create_gitlab_release:
uses: actions/create-release@v1 name: Create GitLab Release
with: runs-on: ubuntu-24.04
name: "Canary ${{ steps.version_info.outputs.build_version }}"
body: "**Full Changelog:** [`${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }}`](https://git.ryujinx.app/projects/Ryubing/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }})"
repository: "Ryubing/Canary"
token: ${{ secrets.RELEASER_TOKEN }}
tag_name: ${{ steps.version_info.outputs.build_version }}
files: |-
publish_ava/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz
post_ci:
name: Post CI Steps
runs-on: docker
container:
image: ghcr.io/catthehacker/ubuntu:act-latest
needs: needs:
- macos_release - macos_release
- release - release
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v4
- name: Install GLI - name: Install gli
uses: actions/setup-gli@v1 run: |
with: mkdir -p $HOME/.bin
token: ${{ secrets.SETUP_GLI_TOKEN }} gh release download -R GreemDev/GLI -O gli -p 'gli-linux-x64'
chmod +x gli
mv gli $HOME/.bin/
echo "$HOME/.bin" >> $GITHUB_PATH
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Get version info - name: Get version info
id: version_info id: version_info
run: | run: |
echo "build_version=$(gli get-next-version -c Canary -R)" >> $FORGEJO_OUTPUT echo "build_version=$(gli get-next-version -c Canary -R)" >> $GITHUB_OUTPUT
echo "prev_build_version=$(gli get-current-version -c Canary -R)" >> $FORGEJO_OUTPUT echo "prev_build_version=$(gli get-current-version -c Canary -R)" >> $GITHUB_OUTPUT
echo "git_short_hash=$(git rev-parse --short "${{ forgejo.sha }}")" >> $FORGEJO_OUTPUT echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
shell: bash shell: bash
- name: Create tag - name: Create tag
run: | run: |
gli create-tag -T ${{ secrets.RELEASER_TOKEN }} -P projects/Ryubing -n Canary-${{ steps.version_info.outputs.build_version }} -r ${{ steps.version_info.outputs.git_short_hash }} gli create-tag -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/ryujinx -n Canary-${{ steps.version_info.outputs.build_version }} -r ${{ steps.version_info.outputs.git_short_hash }}
- name: Link to actual source archives for Canary
run: |
gli canary-release -T ${{ secrets.RELEASER_TOKEN }} -P Ryubing/Canary -r ${{ steps.version_info.outputs.build_version }}
- name: Create release
run: |
gli create-release-from-generic-package-files -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/canary -n Ryubing-Canary -v ${{ steps.version_info.outputs.build_version }} -r main -t "Canary ${{ steps.version_info.outputs.build_version }}" -b "**Full Changelog:** [${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }}](https://git.ryujinx.app/ryubing/ryujinx/-/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }})"
- name: Send notification webhook - name: Send notification webhook
run: | run: |
gli send-update-message -T ${{ secrets.RELEASER_TOKEN }} -P Ryubing/Canary -t ${{ steps.version_info.outputs.build_version }} -c FF4500 -w ${{ secrets.CANARY_DISCORD_WEBHOOK }} -i https://avatars.githubusercontent.com/u/192939710?s=200&v=4 gli send-update-message -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/canary -t ${{ steps.version_info.outputs.build_version }} -c FF4500 -w ${{ secrets.CANARY_DISCORD_WEBHOOK }} -i https://avatars.githubusercontent.com/u/192939710?s=200&v=4
- name: Notify update server of new builds - name: Notify update server of new builds
run: | run: |

25
.github/workflows/checks.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
name: Build PR
on:
pull_request:
branches: [ master ]
paths:
- '**'
- '!.github/**'
- '!*.yml'
- '!*.config'
- '!*.md'
- '.github/workflows/*.yml'
permissions:
pull-requests: write
checks: write
concurrency:
group: pr-checks-${{ github.event.number }}
cancel-in-progress: true
jobs:
pr_build:
uses: ./.github/workflows/build.yml
secrets: inherit

View File

@@ -0,0 +1,61 @@
name: Comment PR artifacts links
on:
workflow_run:
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
steps:
- uses: actions/github-script@v6
with:
script: |
const {owner, repo} = context.repo;
const run_id = ${{github.event.workflow_run.id}};
const pull_head_sha = '${{github.event.workflow_run.head_sha}}';
const issue_number = await (async () => {
const pulls = await github.rest.pulls.list({owner, repo});
for await (const {data} of github.paginate.iterator(pulls)) {
for (const pull of data) {
if (pull.head.sha === pull_head_sha) {
return pull.number;
}
}
}
})();
if (issue_number) {
core.info(`Using pull request ${issue_number}`);
} else {
return core.error(`No matching pull request found`);
}
const {data: {artifacts}} = await github.rest.actions.listWorkflowRunArtifacts({owner, repo, run_id});
if (!artifacts.length) {
return core.error(`No artifacts found`);
}
let body = `Download the artifacts for this pull request:\n`;
let hidden_debug_artifacts = `\n\n <details><summary>Only for Developers</summary>\n`;
for (const art of artifacts) {
const url = `https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip`;
if (art.name.includes('Debug')) {
hidden_debug_artifacts += `\n* [${art.name}](${url})`;
} else {
body += `\n* [${art.name}](${url})`;
}
}
hidden_debug_artifacts += `\n</details>`;
body += hidden_debug_artifacts;
const {data: comments} = await github.rest.issues.listComments({repo, owner, issue_number});
const existing_comment = comments.find((c) => c.user.login === 'github-actions[bot]');
if (existing_comment) {
core.info(`Updating comment ${existing_comment.id}`);
await github.rest.issues.updateComment({repo, owner, comment_id: existing_comment.id, body});
} else {
core.info(`Creating a comment`);
await github.rest.issues.createComment({repo, owner, issue_number, body});
}

View File

@@ -5,22 +5,24 @@ on:
jobs: jobs:
triage: triage:
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
# Grab sources to get latest labeler.yml # Grab sources to get latest labeler.yml
- name: Fetch sources - name: Fetch sources
uses: actions/checkout@v6 uses: actions/checkout@v4
with: with:
# Ensure we pin the source origin as pull_request_target run under forks. # Ensure we pin the source origin as pull_request_target run under forks.
fetch-depth: 0 fetch-depth: 0
repository: projects/Ryubing repository: GreemDev/Ryujinx
ref: master ref: master
- name: Update labels based on changes - name: Update labels based on changes
uses: actions/labeler@v6 uses: actions/labeler@v5
with: with:
repo-token: ${{ secrets.LABELER_TOKEN }}
configuration-path: .forgejo/labeler.yml
sync-labels: true sync-labels: true
dot: true dot: true

View File

@@ -19,20 +19,18 @@ env:
jobs: jobs:
release: release:
name: Release for ${{ matrix.platform.name }} name: Release for ${{ matrix.platform.name }}
runs-on: docker runs-on: ${{ matrix.platform.os }}
container:
image: ${{ matrix.platform.os }}
strategy: strategy:
matrix: matrix:
platform: platform:
- { name: win-x64, os: ghcr.io/catthehacker/ubuntu:act-latest, zip_os_name: win_x64 } - { name: win-x64, os: ubuntu-latest, zip_os_name: win_x64 }
#- { name: win-arm64, os: ghcr.io/catthehacker/ubuntu:act-latest, zip_os_name: win_arm64 } #- { name: win-arm64, os: ubuntu-latest, zip_os_name: win_arm64 }
- { name: linux-x64, os: ghcr.io/catthehacker/ubuntu:act-latest, zip_os_name: linux_x64 } - { name: linux-x64, os: ubuntu-latest, zip_os_name: linux_x64 }
- { name: linux-arm64, os: ghcr.io/catthehacker/ubuntu:act-latest, zip_os_name: linux_arm64 } - { name: linux-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 }
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v4
- uses: actions/setup-dotnet@v5 - uses: actions/setup-dotnet@v4
with: with:
global-json-file: global.json global-json-file: global.json
@@ -43,21 +41,26 @@ jobs:
run: | run: |
sudo apt install -y 7zip sudo apt install -y 7zip
- name: Install GLI - name: Install gli
uses: actions/setup-gli@v1 run: |
with: mkdir -p $HOME/.bin
token: ${{ secrets.SETUP_GLI_TOKEN }} gh release download -R GreemDev/GLI -O gli -p 'gli-linux-x64'
chmod +x gli
mv gli $HOME/.bin/
echo "$HOME/.bin" >> $GITHUB_PATH
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Get version info - name: Get version info
id: version_info id: version_info
run: | run: |
if [ '${{ inputs.is_bugfix_release }}' == 'false' ]; then if [ '${{ inputs.is_bugfix_release }}' == 'false' ]; then
echo "build_version=$(gli get-next-version -m -c Stable -R)" >> $FORGEJO_OUTPUT echo "build_version=$(gli get-next-version -m -c Stable -R)" >> $GITHUB_OUTPUT
else else
echo "build_version=$(gli get-next-version -c Stable -R)" >> $FORGEJO_OUTPUT echo "build_version=$(gli get-next-version -c Stable -R)" >> $GITHUB_OUTPUT
fi fi
echo "prev_build_version=$(gli get-current-version -c Stable -R)" >> $FORGEJO_OUTPUT echo "prev_build_version=$(gli get-current-version -c Stable -R)" >> $GITHUB_OUTPUT
echo "git_short_hash=$(git rev-parse --short "${{ forgejo.sha }}")" >> $FORGEJO_OUTPUT echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
shell: bash shell: bash
- name: Configure for release - name: Configure for release
@@ -81,9 +84,12 @@ jobs:
pushd publish pushd publish
rm libarmeilleure-jitsupport.dylib rm libarmeilleure-jitsupport.dylib
7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip ../publish 7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip ../publish
7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.7z ../publish
popd popd
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/ryujinx -n Ryubing -v ${{ steps.version_info.outputs.build_version }} -r 5 -p release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip
shell: bash shell: bash
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Packing Linux builds - name: Packing Linux builds
if: contains(matrix.platform.name, 'linux') if: contains(matrix.platform.name, 'linux')
@@ -92,9 +98,12 @@ jobs:
rm libarmeilleure-jitsupport.dylib rm libarmeilleure-jitsupport.dylib
chmod +x Ryujinx.sh Ryujinx chmod +x Ryujinx.sh Ryujinx
tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz ../publish tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz ../publish
tar -cJvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.xz ../publish
popd popd
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/ryujinx -n Ryubing -v ${{ steps.version_info.outputs.build_version }} -r 5 -p release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz
shell: bash shell: bash
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build AppImage (Linux) - name: Build AppImage (Linux)
if: contains(matrix.platform.name, 'linux') if: contains(matrix.platform.name, 'linux')
@@ -129,25 +138,17 @@ jobs:
pushd publish_appimage pushd publish_appimage
mv Ryujinx.AppImage ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage mv Ryujinx.AppImage ../release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage
popd popd
shell: bash
- name: Create release gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/ryujinx -n Ryubing -v ${{ steps.version_info.outputs.build_version }} -r 5 -p release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage
uses: actions/create-release@v1 shell: bash
with:
name: "${{ steps.version_info.outputs.build_version }}"
repository: "projects/Ryubing"
token: ${{ secrets.RELEASER_TOKEN }}
tag_name: ${{ steps.version_info.outputs.build_version }}
files: |-
release_output/**
macos_release: macos_release:
name: Release MacOS universal name: Release MacOS universal
runs-on: ubuntu-latest runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v4
- uses: actions/setup-dotnet@v5 - uses: actions/setup-dotnet@v4
with: with:
global-json-file: global.json global-json-file: global.json
@@ -157,28 +158,37 @@ jobs:
chmod +x llvm.sh chmod +x llvm.sh
sudo ./llvm.sh 17 sudo ./llvm.sh 17
- name: Install GLI - name: Install gli
uses: actions/setup-gli@v1 run: |
with: mkdir -p $HOME/.bin
token: ${{ secrets.SETUP_GLI_TOKEN }} gh release download -R GreemDev/GLI -O gli -p 'gli-linux-x64'
chmod +x gli
mv gli $HOME/.bin/
echo "$HOME/.bin" >> $GITHUB_PATH
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install rcodesign - name: Install rcodesign
run: | run: |
gli ghr -R indygreg/apple-platform-rs -p apple-codesign-*-x86_64-unknown-linux-musl.tar.gz -O apple-codesign.tar.gz 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 tar -xzvf apple-codesign.tar.gz --wildcards '*/rcodesign' --strip-components=1
rm apple-codesign.tar.gz rm apple-codesign.tar.gz
mv rcodesign /usr/bin/rcodesign mv rcodesign $HOME/.bin/
echo "$HOME/.bin" >> $GITHUB_PATH
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Get version info - name: Get version info
id: version_info id: version_info
run: | run: |
if [ '${{ inputs.is_bugfix_release }}' == 'false' ]; then if [ '${{ inputs.is_bugfix_release }}' == 'false' ]; then
echo "build_version=$(gli get-next-version -m -c Stable -R)" >> $FORGEJO_OUTPUT echo "build_version=$(gli get-next-version -m -c Stable -R)" >> $GITHUB_OUTPUT
else else
echo "build_version=$(gli get-next-version -c Stable -R)" >> $FORGEJO_OUTPUT echo "build_version=$(gli get-next-version -c Stable -R)" >> $GITHUB_OUTPUT
fi fi
echo "prev_build_version=$(gli get-current-version -c Stable -R)" >> $FORGEJO_OUTPUT echo "prev_build_version=$(gli get-current-version -c Stable -R)" >> $GITHUB_OUTPUT
echo "git_short_hash=$(git rev-parse --short "${{ forgejo.sha }}")" >> $FORGEJO_OUTPUT echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
shell: bash shell: bash
- name: Configure for release - name: Configure for release
@@ -191,47 +201,49 @@ jobs:
- name: Publish macOS Ryujinx - name: Publish macOS Ryujinx
run: | run: |
bash 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 ./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: Create release gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/ryujinx -n Ryubing -v ${{ steps.version_info.outputs.build_version }} -r 5 -p publish/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz
uses: actions/create-release@v1
with: create_gitlab_release:
name: "${{ steps.version_info.outputs.build_version }}" name: Create GitLab Release
repository: "projects/Ryubing" runs-on: ubuntu-24.04
token: ${{ secrets.RELEASER_TOKEN }}
tag_name: ${{ steps.version_info.outputs.build_version }}
files: |-
publish_ava/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz
post_ci:
name: Post-CI Steps
runs-on: ubuntu-latest
needs: needs:
- macos_release - macos_release
- release - release
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v4
- name: Install GLI - name: Install gli
uses: actions/setup-gli@v1 run: |
with: mkdir -p $HOME/.bin
token: ${{ secrets.SETUP_GLI_TOKEN }} gh release download -R GreemDev/GLI -O gli -p 'gli-linux-x64'
chmod +x gli
mv gli $HOME/.bin/
echo "$HOME/.bin" >> $GITHUB_PATH
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Get version info - name: Get version info
id: version_info id: version_info
run: | run: |
if [ '${{ inputs.is_bugfix_release }}' == 'false' ]; then if [ '${{ inputs.is_bugfix_release }}' == 'false' ]; then
echo "build_version=$(gli get-next-version -m -c Stable -R)" >> $FORGEJO_OUTPUT echo "build_version=$(gli get-next-version -m -c Stable -R)" >> $GITHUB_OUTPUT
else else
echo "build_version=$(gli get-next-version -c Stable -R)" >> $FORGEJO_OUTPUT echo "build_version=$(gli get-next-version -c Stable -R)" >> $GITHUB_OUTPUT
fi fi
echo "prev_build_version=$(gli get-current-version -c Stable -R)" >> $FORGEJO_OUTPUT echo "prev_build_version=$(gli get-current-version -c Stable -R)" >> $GITHUB_OUTPUT
echo "git_short_hash=$(git rev-parse --short "${{ forgejo.sha }}")" >> $FORGEJO_OUTPUT echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
echo "commit_message=$(git log -1 --pretty=%B)" >> $GITHUB_OUTPUT
shell: bash shell: bash
- name: Create release
run: |
gli create-release-from-generic-package-files -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/ryujinx -n Ryubing -v ${{ steps.version_info.outputs.build_version }} -r ${{ steps.version_info.outputs.git_short_hash }} -t "${{ steps.version_info.outputs.build_version }}" -b "msd:${{ steps.version_info.outputs.build_version }}"
- name: Send notification webhook - name: Send notification webhook
run: | run: |
gli send-update-message -T ${{ secrets.RELEASER_TOKEN }} -P projects/Ryubing -t ${{ steps.version_info.outputs.build_version }} -c 32cd32 -w ${{ secrets.STABLE_DISCORD_WEBHOOK }} -i https://avatars.githubusercontent.com/u/192939710?s=200&v=4 gli send-update-message -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/ryujinx -t ${{ steps.version_info.outputs.build_version }} -c 32cd32 -w ${{ secrets.STABLE_DISCORD_WEBHOOK }} -i https://avatars.githubusercontent.com/u/192939710?s=200&v=4
- name: Notify update server of new builds - name: Notify update server of new builds
run: | run: |

View File

@@ -3,65 +3,60 @@
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageVersion Include="Avalonia" Version="11.3.15" /> <PackageVersion Include="Avalonia" Version="11.3.6" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.13" /> <PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.6" />
<PackageVersion Include="Avalonia.Desktop" Version="11.3.15" /> <PackageVersion Include="Avalonia.Desktop" Version="11.3.6" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.15" /> <PackageVersion Include="Avalonia.Diagnostics" Version="11.3.6" />
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.3.15" /> <PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.3.6" />
<PackageVersion Include="SharpCompress" Version="0.48.1" /> <PackageVersion Include="Svg.Controls.Avalonia" Version="11.3.6.2" />
<PackageVersion Include="Svg.Controls.Avalonia" Version="11.3.9.5" /> <PackageVersion Include="Svg.Controls.Skia.Avalonia" Version="11.3.6.2" />
<PackageVersion Include="Svg.Controls.Skia.Avalonia" Version="11.3.9.5" />
<PackageVersion Include="Microsoft.Build.Framework" Version="17.11.4" /> <PackageVersion Include="Microsoft.Build.Framework" Version="17.11.4" />
<PackageVersion Include="Microsoft.Build.Utilities.Core" Version="17.12.50" /> <PackageVersion Include="Microsoft.Build.Utilities.Core" Version="17.12.6" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" /> <PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="Projektanker.Icons.Avalonia" Version="9.6.2" /> <PackageVersion Include="Projektanker.Icons.Avalonia" Version="9.6.2" />
<PackageVersion Include="Projektanker.Icons.Avalonia.FontAwesome" Version="9.6.2" /> <PackageVersion Include="Projektanker.Icons.Avalonia.FontAwesome" Version="9.6.2" />
<PackageVersion Include="Projektanker.Icons.Avalonia.MaterialDesign" Version="9.6.2" /> <PackageVersion Include="Projektanker.Icons.Avalonia.MaterialDesign" Version="9.6.2" />
<PackageVersion Include="Ryujinx.SDL3-CS" Version="2026.501.0" /> <PackageVersion Include="ppy.SDL3-CS" Version="2025.920.0" />
<PackageVersion Include="CommandLineParser" Version="2.9.1" /> <PackageVersion Include="CommandLineParser" Version="2.9.1" />
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.2" /> <PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageVersion Include="Concentus" Version="2.2.2" /> <PackageVersion Include="Concentus" Version="2.2.2" />
<PackageVersion Include="DiscordRichPresence" Version="1.6.1.70" /> <PackageVersion Include="DiscordRichPresence" Version="1.6.1.70" />
<PackageVersion Include="DynamicData" Version="9.4.31" /> <PackageVersion Include="DynamicData" Version="9.4.1" />
<PackageVersion Include="FluentAvaloniaUI" Version="2.5.1" /> <PackageVersion Include="FluentAvaloniaUI.NoAnim" Version="2.4.0-build3" />
<PackageVersion Include="Humanizer" Version="2.14.1" /> <PackageVersion Include="Humanizer" Version="2.14.1" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" /> <PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" /> <PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.18.0" /> <PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.3.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" /> <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" /> <PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" /> <PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
<PackageVersion Include="NetCoreServer" Version="8.0.7" /> <PackageVersion Include="NetCoreServer" Version="8.0.7" />
<PackageVersion Include="NUnit" Version="3.14.0" /> <PackageVersion Include="NUnit" Version="3.13.3" />
<PackageVersion Include="NUnit3TestAdapter" Version="4.6.0" /> <PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" />
<PackageVersion Include="OpenTK.Core" Version="4.9.4" /> <PackageVersion Include="OpenTK.Core" Version="4.8.2" />
<PackageVersion Include="OpenTK.Graphics" Version="4.9.4" /> <PackageVersion Include="OpenTK.Graphics" Version="4.8.2" />
<!-- OpenTk.Audio.OpenAL has moved to OpenTk.Audio --> <PackageVersion Include="OpenTK.Audio.OpenAL" Version="4.8.2" />
<!--<PackageVersion Include="OpenTK.Audio" Version="5.0.0-pre.15" />--> <PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.8.2" />
<PackageVersion Include="OpenTK.Audio.OpenAL" Version="4.9.4" />
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.9.4" />
<PackageVersion Include="Open.NAT.Core" Version="2.1.0.5" /> <PackageVersion Include="Open.NAT.Core" Version="2.1.0.5" />
<!-- Ryujinx.Audio.OpenAL.Dependencies is from the original project, last updated 12/30/20 --> <PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />
<!--<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />--> <PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.AllArch" Version="6.1.2-build3" />
<PackageVersion Include="Ryujinx.Audio.OpenAL" Version="1.25.1" />
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.AllArch" Version="6.1.4-build6" />
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" /> <PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
<PackageVersion Include="Ryujinx.LibHac" Version="0.21.0-alpha.133" /> <PackageVersion Include="Ryujinx.LibHac" Version="0.21.0-alpha.126" />
<PackageVersion Include="Ryujinx.UpdateClient" Version="2.0.6" /> <PackageVersion Include="Ryujinx.UpdateClient" Version="1.0.44" />
<PackageVersion Include="Ryujinx.Systems.Update.Common" Version="2.0.6" /> <PackageVersion Include="Ryujinx.Systems.Update.Common" Version="1.0.44" />
<PackageVersion Include="Gommon" Version="2.8.1.2" /> <PackageVersion Include="Gommon" Version="2.8.0.4" />
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" /> <PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
<PackageVersion Include="Sep" Version="0.14.1" /> <PackageVersion Include="Sep" Version="0.11.1" />
<PackageVersion Include="shaderc.net" Version="0.1.0" /> <PackageVersion Include="shaderc.net" Version="0.1.0" />
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
<PackageVersion Include="Silk.NET.Vulkan" Version="2.22.0" /> <PackageVersion Include="Silk.NET.Vulkan" Version="2.22.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.22.0" /> <PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.22.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.22.0" /> <PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.22.0" />
<PackageVersion Include="SkiaSharp" Version="2.88.9" /> <PackageVersion Include="SkiaSharp" Version="2.88.9" />
<PackageVersion Include="SkiaSharp.NativeAssets.Win32" Version="2.88.9" /> <PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.9" />
<PackageVersion Include="SkiaSharp.NativeAssets.macOS" Version="2.88.9" />
<PackageVersion Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="2.88.9" />
<PackageVersion Include="SPB" Version="0.0.4-build32" /> <PackageVersion Include="SPB" Version="0.0.4-build32" />
<PackageVersion Include="System.IO.Hashing" Version="9.0.15" /> <PackageVersion Include="System.IO.Hashing" Version="9.0.2" />
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.1.3" /> <PackageVersion Include="System.Management" Version="9.0.2" />
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -5,10 +5,10 @@
</td> </td>
<td align="center" width="75%"> <td align="center" width="75%">
<h1 class="ryu-gradient-text">Ryujinx</h1> # Ryujinx
[![Latest release](https://git.ryujinx.app/projects/Ryubing/badges/release.svg?label=stable&color=32cd32)](https://update.ryujinx.app/latest/stable) [![Latest release](https://img.shields.io/gitlab/v/release/ryubing%2Fryujinx?gitlab_url=https%3A%2F%2Fgit.ryujinx.app&label=stable&color=32cd32)](https://update.ryujinx.app/latest/stable)
[![Latest canary release](https://git.ryujinx.app/Ryubing/Canary/badges/release.svg?label=canary&color=FF4500)](https://update.ryujinx.app/latest/canary) [![Latest canary release](https://img.shields.io/gitlab/v/release/ryubing%2Fcanary?gitlab_url=https%3A%2F%2Fgit.ryujinx.app&label=canary&color=FF4500)](https://update.ryujinx.app/latest/canary)
<br> <br>
<a href="https://discord.gg/PEuzjrFXUA"> <a href="https://discord.gg/PEuzjrFXUA">
<img src="https://img.shields.io/discord/1294443224030511104?color=5865F2&label=Ryubing&logo=discord&logoColor=white" alt="Discord"> <img src="https://img.shields.io/discord/1294443224030511104?color=5865F2&label=Ryubing&logo=discord&logoColor=white" alt="Discord">
@@ -21,7 +21,7 @@
Ryujinx is an open-source Nintendo Switch emulator, originally 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. 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. It was written from scratch and development on the project began in September 2017.
Ryujinx is available on a self-managed <a class="forgejo-gradient-text" href="https://github.com/Ryubing/forgejo" target="_blank">modified Forgejo</a> instance under the <a href="https://git.ryujinx.app/projects/Ryubing/src/branch/master/LICENSE.txt" target="_blank">MIT license</a>. Ryujinx is available on a self-managed GitLab instance under the <a href="https://git.ryujinx.app/ryubing/ryujinx/-/blob/master/LICENSE.txt?ref_type=heads" target="_blank">MIT license</a>.
<br /> <br />
</p> </p>
<p align="center"> <p align="center">
@@ -31,11 +31,11 @@
<br> <br>
This is not a Ryujinx revival project. This is not a Phoenix project. This is not a Ryujinx revival project. This is not a Phoenix project.
<br> <br>
Guides and documentation can be found on the <a href="https://git.ryujinx.app/projects/Ryubing/wiki/Home">Wiki tab</a>. Guides and documentation can be found on the <a href="https://git.ryujinx.app/groups/ryubing/-/wikis/home">Wiki tab</a>.
</p> </p>
<p align="center"> <p align="center">
<img src="https://git.ryujinx.app/projects/Ryubing/raw/branch/master/docs/shell.png" alt="Ryujinx example"> <img src="https://git.ryujinx.app/ryubing/ryujinx/-/raw/master/docs/shell.png?ref_type=heads&inline=false" alt="Ryujinx example">
</p> </p>
## Usage ## Usage
@@ -49,17 +49,17 @@ Stable builds are made every so often, based on the `master` branch, that then g
These stable builds exist so that the end user can get a more **enjoyable and stable experience**. These stable builds exist so that the end user can get a more **enjoyable and stable experience**.
They are released every month or so, to ensure consistent updates, while not being an annoying amount of individual updates to download over the course of that month. They are released every month or so, to ensure consistent updates, while not being an annoying amount of individual updates to download over the course of that month.
You can find the stable releases [here](https://git.ryujinx.app/projects/Ryubing/releases). You can find the stable releases [here](https://git.ryujinx.app/ryubing/ryujinx/-/releases).
Canary builds are compiled automatically for each commit on the `master` branch. 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**. 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. These canary builds are only recommended for experienced users.
You can find the canary releases [here](https://git.ryujinx.app/Ryubing/Canary/releases). You can find the canary releases [here](https://git.ryujinx.app/ryubing/canary/-/releases).
## Documentation ## Documentation
If you are planning to contribute or just want to learn more about this project please read through our [documentation](https://git.ryujinx.app/projects/Ryubing/src/branch/master/docs/README.md). If you are planning to contribute or just want to learn more about this project please read through our [documentation](docs/README.md).
## Features ## Features
@@ -105,13 +105,13 @@ If you are planning to contribute or just want to learn more about this project
## License ## License
This software is licensed under the terms of the [MIT license](https://git.ryujinx.app/projects/Ryubing/src/branch/master/LICENSE.txt). This software is licensed under the terms of the [MIT license](LICENSE.txt).
This project makes use of code authored by the libvpx project, licensed under BSD and the ffmpeg project, licensed under LGPLv3. This project makes use of code authored by the libvpx project, licensed under BSD and the ffmpeg project, licensed under LGPLv3.
See [LICENSE.txt](https://git.ryujinx.app/projects/Ryubing/src/branch/master/LICENSE.txt) and [THIRDPARTY.md](https://git.ryujinx.app/projects/Ryubing/src/branch/master/distribution/legal/THIRDPARTY.md) for more details. See [LICENSE.txt](LICENSE.txt) and [THIRDPARTY.md](distribution/legal/THIRDPARTY.md) for more details.
## Credits ## Credits
- [LibHac](https://git.ryujinx.app/projects/LibHac) is used for our file-system. - [LibHac](https://git.ryujinx.app/ryubing/libhac) is used for our file-system.
- [AmiiboAPI](https://www.amiiboapi.com) is used in our Amiibo emulation. - [AmiiboAPI](https://www.amiiboapi.com) is used in our Amiibo emulation.
- [ldn_mitm](https://github.com/spacemeowx2/ldn_mitm) is used for one of our available multiplayer modes. - [ldn_mitm](https://github.com/spacemeowx2/ldn_mitm) is used for one of our available multiplayer modes.
- [ShellLink](https://github.com/securifybv/ShellLink) is used for Windows shortcut generation. - [ShellLink](https://github.com/securifybv/ShellLink) is used for Windows shortcut generation.

View File

@@ -47,8 +47,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Vic", "src
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Video", "src\Ryujinx.Graphics.Video\Ryujinx.Graphics.Video.csproj", "{FD4A2C14-8E3D-4957-ABBE-3C38897B3E2D}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Video", "src\Ryujinx.Graphics.Video\Ryujinx.Graphics.Video.csproj", "{FD4A2C14-8E3D-4957-ABBE-3C38897B3E2D}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.Apple", "src\Ryujinx.Audio.Backends.Apple\Ryujinx.Audio.Backends.Apple.csproj", "{AC26EFF0-8593-4184-9A09-98E37EFFB32E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.SDL3", "src\Ryujinx.Audio.Backends.SDL3\Ryujinx.Audio.Backends.SDL3.csproj", "{988E6191-82E1-4E13-9DDB-CB9FA2FDAF29}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.SDL3", "src\Ryujinx.Audio.Backends.SDL3\Ryujinx.Audio.Backends.SDL3.csproj", "{988E6191-82E1-4E13-9DDB-CB9FA2FDAF29}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.OpenAL", "src\Ryujinx.Audio.Backends.OpenAL\Ryujinx.Audio.Backends.OpenAL.csproj", "{0BE11899-DF2D-4BDE-B9EE-2489E8D35E7D}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio.Backends.OpenAL", "src\Ryujinx.Audio.Backends.OpenAL\Ryujinx.Audio.Backends.OpenAL.csproj", "{0BE11899-DF2D-4BDE-B9EE-2489E8D35E7D}"
@@ -86,11 +84,11 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{36F870C1-3E5F-485F-B426-F0645AF78751}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{36F870C1-3E5F-485F-B426-F0645AF78751}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig .editorconfig = .editorconfig
.forgejo\workflows\build.yml = .forgejo\workflows\build.yml .github\workflows\build.yml = .github\workflows\build.yml
.forgejo\workflows\canary.yml = .forgejo\workflows\canary.yml .github\workflows\canary.yml = .github\workflows\canary.yml
Directory.Packages.props = Directory.Packages.props Directory.Packages.props = Directory.Packages.props
Directory.Build.props = Directory.Build.props Directory.Build.props = Directory.Build.props
.forgejo\workflows\release.yml = .forgejo\workflows\release.yml .github\workflows\release.yml = .github\workflows\release.yml
nuget.config = nuget.config nuget.config = nuget.config
EndProjectSection EndProjectSection
EndProject EndProject
@@ -571,18 +569,6 @@ Global
{D58FA894-27D5-4EAA-9042-AD422AD82931}.Release|x64.Build.0 = Release|Any CPU {D58FA894-27D5-4EAA-9042-AD422AD82931}.Release|x64.Build.0 = Release|Any CPU
{D58FA894-27D5-4EAA-9042-AD422AD82931}.Release|x86.ActiveCfg = Release|Any CPU {D58FA894-27D5-4EAA-9042-AD422AD82931}.Release|x86.ActiveCfg = Release|Any CPU
{D58FA894-27D5-4EAA-9042-AD422AD82931}.Release|x86.Build.0 = Release|Any CPU {D58FA894-27D5-4EAA-9042-AD422AD82931}.Release|x86.Build.0 = Release|Any CPU
{AC26EFF0-8593-4184-9A09-98E37EFFB32E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AC26EFF0-8593-4184-9A09-98E37EFFB32E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AC26EFF0-8593-4184-9A09-98E37EFFB32E}.Debug|x64.ActiveCfg = Debug|Any CPU
{AC26EFF0-8593-4184-9A09-98E37EFFB32E}.Debug|x64.Build.0 = Debug|Any CPU
{AC26EFF0-8593-4184-9A09-98E37EFFB32E}.Debug|x86.ActiveCfg = Debug|Any CPU
{AC26EFF0-8593-4184-9A09-98E37EFFB32E}.Debug|x86.Build.0 = Debug|Any CPU
{AC26EFF0-8593-4184-9A09-98E37EFFB32E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AC26EFF0-8593-4184-9A09-98E37EFFB32E}.Release|Any CPU.Build.0 = Release|Any CPU
{AC26EFF0-8593-4184-9A09-98E37EFFB32E}.Release|x64.ActiveCfg = Release|Any CPU
{AC26EFF0-8593-4184-9A09-98E37EFFB32E}.Release|x64.Build.0 = Release|Any CPU
{AC26EFF0-8593-4184-9A09-98E37EFFB32E}.Release|x86.ActiveCfg = Release|Any CPU
{AC26EFF0-8593-4184-9A09-98E37EFFB32E}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@@ -4,15 +4,15 @@
"ID": "MenuBarActions_StartCapture", "ID": "MenuBarActions_StartCapture",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "RenderDoc Frame-Aufnahme starten", "de_DE": "",
"el_GR": "", "el_GR": "",
"en_US": "Start RenderDoc Frame Capture", "en_US": "Start RenderDoc Frame Capture",
"es_ES": "Iniciar una captura de fotograma de RenderDoc", "es_ES": "",
"fr_FR": "Démarrer une capture de trame RenderDoc", "fr_FR": "",
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "RenderDoc 프레임 캡처 시작", "ko_KR": "",
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
@@ -21,23 +21,23 @@
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
"zh_CN": "启动 RenderDoc 帧捕获", "zh_CN": "",
"zh_TW": "啟動 RenderDoc 畫格擷取" "zh_TW": ""
} }
}, },
{ {
"ID": "MenuBarActions_EndCapture", "ID": "MenuBarActions_EndCapture",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "RenderDoc Frame-Aufnahme beenden", "de_DE": "",
"el_GR": "", "el_GR": "",
"en_US": "End RenderDoc Frame Capture", "en_US": "End RenderDoc Frame Capture",
"es_ES": "Detener la captura de fotograma de RenderDoc", "es_ES": "",
"fr_FR": "Arrêter la capture de trame RenderDoc", "fr_FR": "",
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "RenderDoc 프레임 캡처 종료", "ko_KR": "",
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
@@ -46,23 +46,23 @@
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
"zh_CN": "结束 RenderDoc 帧捕获", "zh_CN": "",
"zh_TW": "停止 RenderDoc 畫格擷取" "zh_TW": ""
} }
}, },
{ {
"ID": "MenuBarActions_DiscardCapture", "ID": "MenuBarActions_DiscardCapture",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "RenderDoc Frame-Aufnahme verwerfen", "de_DE": "",
"el_GR": "", "el_GR": "",
"en_US": "Discard RenderDoc Frame Capture", "en_US": "Discard RenderDoc Frame Capture",
"es_ES": "Descartar la captura de fotograma de RenderDoc", "es_ES": "",
"fr_FR": "Supprimer la capture de trame RenderDoc", "fr_FR": "",
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "RenderDoc 프레임 캡처 폐기", "ko_KR": "",
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
@@ -71,23 +71,23 @@
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
"zh_CN": "丢弃 RenderDoc 帧捕获", "zh_CN": "",
"zh_TW": "捨棄 RenderDoc 畫格擷取" "zh_TW": ""
} }
}, },
{ {
"ID": "MenuBarActions_DiscardCapture_ToolTip", "ID": "MenuBarActions_DiscardCapture_ToolTip",
"Translations": { "Translations": {
"ar_SA": "", "ar_SA": "",
"de_DE": "Beendet die jetzige RenderDoc Frame-Aufnahme, verwirft sofort das Ergebnis.", "de_DE": "",
"el_GR": "", "el_GR": "",
"en_US": "Ends the currently active RenderDoc Frame Capture, immediately discarding its result.", "en_US": "Ends the currently active RenderDoc Frame Capture, immediately discarding its result.",
"es_ES": "Finaliza la captura de fotograma de RenderDoc actualmente activa y descarta inmediatamente su resultado.", "es_ES": "",
"fr_FR": "Met fin à la capture de trame RenderDoc en cours, en supprimant immédiatement son résultat.", "fr_FR": "",
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",
"ko_KR": "현재 활성화된 RenderDoc 프레임 캡처를 종료하고 결과를 즉시 폐기합니다.", "ko_KR": "",
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
@@ -96,8 +96,8 @@
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
"uk_UA": "", "uk_UA": "",
"zh_CN": "结束当前正在进行的 RenderDoc 帧捕获,并立即丢弃其结果。", "zh_CN": "",
"zh_TW": "停止正在執行的 RenderDoc 畫格擷取,且立即捨棄其結果。" "zh_TW": ""
} }
} }
] ]

File diff suppressed because it is too large Load Diff

View File

@@ -22,5 +22,5 @@ chmod +x AppDir/AppRun AppDir/usr/bin/Ryujinx*
mkdir -p "$OUTDIR" mkdir -p "$OUTDIR"
appimagetool --appimage-extract-and-run -n --comp zstd --mksquashfs-opt -Xcompression-level --mksquashfs-opt 21 \ appimagetool -n --comp zstd --mksquashfs-opt -Xcompression-level --mksquashfs-opt 21 \
AppDir "$OUTDIR"/Ryujinx.AppImage AppDir "$OUTDIR"/Ryujinx.AppImage

View File

@@ -1,120 +0,0 @@
#!/bin/bash
set -e
if [ "$#" -lt 8 ]; then
echo "usage <BASE_DIR> <TEMP_DIRECTORY> <OUTPUT_DIRECTORY> <ENTITLEMENTS_FILE_PATH> <VERSION> <SOURCE_REVISION_ID> <CONFIGURATION>"
exit 1
fi
mkdir -p "$1"
mkdir -p "$2"
mkdir -p "$3"
BASE_DIR=$(readlink -f "$1")
TEMP_DIRECTORY=$(readlink -f "$2")
OUTPUT_DIRECTORY=$(readlink -f "$3")
ENTITLEMENTS_FILE_PATH=$(readlink -f "$4")
VERSION=$5
SOURCE_REVISION_ID=$6
CONFIGURATION=$7
if [[ "$(uname)" == "Darwin" ]]; then
echo "Clearing xattr on all dot undercsore files"
find "$BASE_DIR" -type f -name "._*" -exec sh -c '
for f; do
dir=$(dirname "$f")
base=$(basename "$f")
orig="$dir/${base#._}"
[ -f "$orig" ] && xattr -c "$orig" || true
done
' sh {} +
fi
RELEASE_TAR_FILE_NAME=ryujinx-$CONFIGURATION-$VERSION+$SOURCE_REVISION_ID-macos_universal.app.tar
ARM64_APP_BUNDLE="$TEMP_DIRECTORY/output_arm64/Ryujinx.app"
X64_APP_BUNDLE="$TEMP_DIRECTORY/output_x64/Ryujinx.app"
UNIVERSAL_APP_BUNDLE="$OUTPUT_DIRECTORY/Ryujinx.app"
EXECUTABLE_SUB_PATH=Contents/MacOS/Ryujinx
rm -rf "$TEMP_DIRECTORY"
mkdir -p "$TEMP_DIRECTORY"
DOTNET_COMMON_ARGS=(-p:DebugType=embedded -p:Version="$VERSION" -p:SourceRevisionId="$SOURCE_REVISION_ID" --self-contained true $EXTRA_ARGS)
dotnet restore
dotnet build -c "$CONFIGURATION" src/Ryujinx
dotnet publish -c "$CONFIGURATION" -r osx-arm64 -o "$TEMP_DIRECTORY/publish_arm64" "${DOTNET_COMMON_ARGS[@]}" src/Ryujinx
dotnet publish -c "$CONFIGURATION" -r osx-x64 -o "$TEMP_DIRECTORY/publish_x64" "${DOTNET_COMMON_ARGS[@]}" src/Ryujinx
# Get rid of the support library for ARMeilleure for x64 (that's only for arm64)
rm -rf "$TEMP_DIRECTORY/publish_x64/libarmeilleure-jitsupport.dylib"
# Get rid of libsoundio from arm64 builds as we don't have a arm64 variant
# TODO: remove this once done
rm -rf "$TEMP_DIRECTORY/publish_arm64/libsoundio.dylib"
pushd "$BASE_DIR/distribution/macos"
./create_app_bundle.sh "$TEMP_DIRECTORY/publish_x64" "$TEMP_DIRECTORY/output_x64" "$ENTITLEMENTS_FILE_PATH"
./create_app_bundle.sh "$TEMP_DIRECTORY/publish_arm64" "$TEMP_DIRECTORY/output_arm64" "$ENTITLEMENTS_FILE_PATH"
popd
rm -rf "$UNIVERSAL_APP_BUNDLE"
mkdir -p "$OUTPUT_DIRECTORY"
# Let's copy one of the two different app bundle and remove the executable
cp -R "$ARM64_APP_BUNDLE" "$UNIVERSAL_APP_BUNDLE"
rm "$UNIVERSAL_APP_BUNDLE/$EXECUTABLE_SUB_PATH"
# 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)" ];
then
if ! [ -x "$(command -v llvm-lipo-17)" ];
then
LIPO=llvm-lipo
else
LIPO=llvm-lipo-17
fi
else
LIPO=lipo
fi
# Make the executable universal
$LIPO "$ARM64_APP_BUNDLE/$EXECUTABLE_SUB_PATH" "$X64_APP_BUNDLE/$EXECUTABLE_SUB_PATH" -output "$UNIVERSAL_APP_BUNDLE/$EXECUTABLE_SUB_PATH" -create
# Patch up the Info.plist to have appropriate version
sed -r -i.bck "s/\%\%RYUJINX_BUILD_VERSION\%\%/$VERSION/g;" "$UNIVERSAL_APP_BUNDLE/Contents/Info.plist"
sed -r -i.bck "s/\%\%RYUJINX_BUILD_GIT_HASH\%\%/$SOURCE_REVISION_ID/g;" "$UNIVERSAL_APP_BUNDLE/Contents/Info.plist"
rm "$UNIVERSAL_APP_BUNDLE/Contents/Info.plist.bck"
# Now sign it
if ! [ -x "$(command -v codesign)" ];
then
if ! [ -x "$(command -v rcodesign)" ];
then
echo "Cannot find rcodesign on your system, please install rcodesign."
exit 1
fi
# NOTE: Currently require https://github.com/indygreg/apple-platform-rs/pull/44 to work on other OSes.
# cargo install --git "https://github.com/marysaka/apple-platform-rs" --branch "fix/adhoc-app-bundle" apple-codesign --bin "rcodesign"
echo "Using rcodesign for ad-hoc signing"
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 -s - "$UNIVERSAL_APP_BUNDLE"
fi
echo "Creating archive"
pushd "$OUTPUT_DIRECTORY"
tar --exclude "Ryujinx.app/Contents/MacOS/Ryujinx" -cvf "$RELEASE_TAR_FILE_NAME" Ryujinx.app 1> /dev/null
python3 "$BASE_DIR/distribution/misc/add_tar_exec.py" "$RELEASE_TAR_FILE_NAME" "Ryujinx.app/Contents/MacOS/Ryujinx" "Ryujinx.app/Contents/MacOS/Ryujinx"
gzip -9 < "$RELEASE_TAR_FILE_NAME" > "$RELEASE_TAR_FILE_NAME.gz"
rm "$RELEASE_TAR_FILE_NAME"
popd
echo "Done"

View File

@@ -1061,7 +1061,6 @@
0100BCA016636000,"eBaseball Powerful Pro Yakyuu 2022",gpu;services-horizon;crash,nothing,2024-05-26 23:07:19 0100BCA016636000,"eBaseball Powerful Pro Yakyuu 2022",gpu;services-horizon;crash,nothing,2024-05-26 23:07:19
01001F20100B8000,"Eclipse: Edge of Light",,playable,2020-08-11 23:06:29 01001F20100B8000,"Eclipse: Edge of Light",,playable,2020-08-11 23:06:29
0100E0A0110F4000,"eCrossminton",,playable,2020-07-11 18:24:27 0100E0A0110F4000,"eCrossminton",,playable,2020-07-11 18:24:27
010054601D54C000,"Emio The Smiling Man: Famicom Detective Club (DEMO)",demo,playable,2026-05-13 18:32:12
0100ABE00DB4E000,"Edna & Harvey: Harvey's New Eyes",nvdec,playable,2021-01-26 14:36:08 0100ABE00DB4E000,"Edna & Harvey: Harvey's New Eyes",nvdec,playable,2021-01-26 14:36:08
01004F000B716000,"Edna & Harvey: The Breakout Anniversary Edition",crash;nvdec,ingame,2022-08-01 16:59:56 01004F000B716000,"Edna & Harvey: The Breakout Anniversary Edition",crash;nvdec,ingame,2022-08-01 16:59:56
01002550129F0000,"Effie",,playable,2022-10-27 14:36:39 01002550129F0000,"Effie",,playable,2022-10-27 14:36:39
@@ -1205,7 +1204,7 @@
01003B200E440000,"Five Nights at Freddy's: Sister Location",,playable,2023-10-06 09:00:58 01003B200E440000,"Five Nights at Freddy's: Sister Location",,playable,2023-10-06 09:00:58
010038200E088000,"Flan",crash;regression,ingame,2021-11-17 07:39:28 010038200E088000,"Flan",crash;regression,ingame,2021-11-17 07:39:28
01000A0004C50000,"FLASHBACK™",nvdec,playable,2020-05-14 13:57:29 01000A0004C50000,"FLASHBACK™",nvdec,playable,2020-05-14 13:57:29
0100C53004C52000,"Flat Heroes",,playable,2026-02-27 17:00:00 0100C53004C52000,"Flat Heroes",gpu,ingame,2022-07-26 19:37:37
0100B54012798000,"Flatland: Prologue",,playable,2020-12-11 20:41:12 0100B54012798000,"Flatland: Prologue",,playable,2020-12-11 20:41:12
0100307004B4C000,"Flinthook",online,playable,2021-03-25 20:42:29 0100307004B4C000,"Flinthook",online,playable,2021-03-25 20:42:29
010095A004040000,"Flip Wars",services;ldn-untested,ingame,2022-05-02 15:39:18 010095A004040000,"Flip Wars",services;ldn-untested,ingame,2022-05-02 15:39:18
@@ -1395,7 +1394,6 @@
0100c3c012718000,"Grand Theft Auto: III The Definitive Edition",gpu;UE4,ingame,2022-10-31 20:13:52 0100c3c012718000,"Grand Theft Auto: III The Definitive Edition",gpu;UE4,ingame,2022-10-31 20:13:52
0100182014022000,"Grand Theft Auto: Vice City The Definitive Edition",gpu;UE4,ingame,2022-10-31 20:13:52 0100182014022000,"Grand Theft Auto: Vice City The Definitive Edition",gpu;UE4,ingame,2022-10-31 20:13:52
010065a014024000,"Grand Theft Auto: San Andreas The Definitive Edition",gpu;UE4,ingame,2022-10-31 20:13:52 010065a014024000,"Grand Theft Auto: San Andreas The Definitive Edition",gpu;UE4,ingame,2022-10-31 20:13:52
0100EB500D92E000,"GROOVE COASTER WAI WAI PARTY!!!!",gpu,ingame,2026-05-13 18:32:12
0100822012D76000,"HAAK",gpu,ingame,2023-02-19 14:31:05 0100822012D76000,"HAAK",gpu,ingame,2023-02-19 14:31:05
01007E100EFA8000,"Habroxia",,playable,2020-06-16 23:04:42 01007E100EFA8000,"Habroxia",,playable,2020-06-16 23:04:42
0100535012974000,"Hades",vulkan,playable,2022-10-05 10:45:21 0100535012974000,"Hades",vulkan,playable,2022-10-05 10:45:21
@@ -1834,7 +1832,6 @@
010055200E87E000,"Metamorphosis",UE4;audout;gpu;nvdec,ingame,2021-06-16 16:18:11 010055200E87E000,"Metamorphosis",UE4;audout;gpu;nvdec,ingame,2021-06-16 16:18:11
0100D4900E82C000,"Metro 2033 Redux",gpu,ingame,2022-11-09 10:53:13 0100D4900E82C000,"Metro 2033 Redux",gpu,ingame,2022-11-09 10:53:13
0100F0400E850000,"Metro: Last Light Redux",slow;nvdec;vulkan-backend-bug,ingame,2023-11-01 11:53:52 0100F0400E850000,"Metro: Last Light Redux",slow;nvdec;vulkan-backend-bug,ingame,2023-11-01 11:53:52
010019A01E2F2000,"Metroid Prime 4: Beyond",,ingame,2026-05-13 18:32:12
010012101468C000,"Metroid Prime™ Remastered",gpu;Needs Update;vulkan-backend-bug;opengl-backend-bug,ingame,2024-05-07 22:48:15 010012101468C000,"Metroid Prime™ Remastered",gpu;Needs Update;vulkan-backend-bug;opengl-backend-bug,ingame,2024-05-07 22:48:15
010093801237C000,"Metroid™ Dread",,playable,2023-11-13 04:02:36 010093801237C000,"Metroid™ Dread",,playable,2023-11-13 04:02:36
0100A1200F20C000,"Midnight Evil",,playable,2022-10-18 22:55:19 0100A1200F20C000,"Midnight Evil",,playable,2022-10-18 22:55:19
@@ -1948,7 +1945,6 @@
0100C3E00ACAA000,"Mutant Football League: Dynasty Edition",online-broken,playable,2022-08-05 17:01:51 0100C3E00ACAA000,"Mutant Football League: Dynasty Edition",online-broken,playable,2022-08-05 17:01:51
01004BE004A86000,"Mutant Mudds Collection",,playable,2022-08-05 17:11:38 01004BE004A86000,"Mutant Mudds Collection",,playable,2022-08-05 17:11:38
0100E6B00DEA4000,"Mutant Year Zero: Road to Eden - Deluxe Edition",nvdec;UE4,playable,2022-09-10 13:31:10 0100E6B00DEA4000,"Mutant Year Zero: Road to Eden - Deluxe Edition",nvdec;UE4,playable,2022-09-10 13:31:10
010037501F864000,"Mute Crimson DX",,ingame,2026-05-13 18:32:12
0100161009E5C000,"MX Nitro: Unleashed",,playable,2022-09-27 22:34:33 0100161009E5C000,"MX Nitro: Unleashed",,playable,2022-09-27 22:34:33
0100218011E7E000,"MX vs ATV All Out",nvdec;UE4;vulkan-backend-bug,playable,2022-10-25 19:51:46 0100218011E7E000,"MX vs ATV All Out",nvdec;UE4;vulkan-backend-bug,playable,2022-10-25 19:51:46
0100D940063A0000,"MXGP3 - The Official Motocross Videogame",UE4;gpu;nvdec,ingame,2020-12-16 14:00:20 0100D940063A0000,"MXGP3 - The Official Motocross Videogame",UE4;gpu;nvdec,ingame,2020-12-16 14:00:20
@@ -2054,9 +2050,7 @@
010003C00B868000,"Ninjin: Clash of Carrots",online-broken,playable,2024-07-10 05:12:26 010003C00B868000,"Ninjin: Clash of Carrots",online-broken,playable,2024-07-10 05:12:26
0100746010E4C000,"NinNinDays",,playable,2022-11-20 15:17:29 0100746010E4C000,"NinNinDays",,playable,2022-11-20 15:17:29
0100C9A00ECE6000,"Nintendo 64™ Nintendo Switch Online",gpu;vulkan,ingame,2024-04-23 20:21:07 0100C9A00ECE6000,"Nintendo 64™ Nintendo Switch Online",gpu;vulkan,ingame,2024-04-23 20:21:07
010057D00ECE4000,"Nintendo 64™ Nintendo Switch Online",gpu;vulkan,ingame,2024-04-23 20:21:07
0100e0601c632000,"Nintendo 64™ Nintendo Switch Online: MATURE 17+",,ingame,2025-02-03 22:27:00 0100e0601c632000,"Nintendo 64™ Nintendo Switch Online: MATURE 17+",,ingame,2025-02-03 22:27:00
010037A0170D2000,"NINTENDO 64™ Nintendo Switch Online 18+",,ingame,2025-02-03 22:27:00
0100D870045B6000,"Nintendo Entertainment System™ - Nintendo Switch Online",online,playable,2022-07-01 15:45:06 0100D870045B6000,"Nintendo Entertainment System™ - Nintendo Switch Online",online,playable,2022-07-01 15:45:06
0100C4B0034B2000,"Nintendo Labo Toy-Con 01 Variety Kit",gpu,ingame,2022-08-07 12:56:07 0100C4B0034B2000,"Nintendo Labo Toy-Con 01 Variety Kit",gpu,ingame,2022-08-07 12:56:07
01001E9003502000,"Nintendo Labo Toy-Con 03 Vehicle Kit",services;crash,menus,2022-08-03 17:20:11 01001E9003502000,"Nintendo Labo Toy-Con 03 Vehicle Kit",services;crash,menus,2022-08-03 17:20:11
@@ -2272,7 +2266,6 @@
010086F0064CE000,"Poi: Explorer Edition",nvdec,playable,2021-01-21 19:32:00 010086F0064CE000,"Poi: Explorer Edition",nvdec,playable,2021-01-21 19:32:00
0100EB6012FD2000,"Poison Control",,playable,2021-05-16 14:01:54 0100EB6012FD2000,"Poison Control",,playable,2021-05-16 14:01:54
010072400E04A000,"Pokémon Café ReMix",,playable,2021-08-17 20:00:04 010072400E04A000,"Pokémon Café ReMix",,playable,2021-08-17 20:00:04
01005B7008C52800,"Pokémon Champions",Needs Update;services;online-broke,menus,2026-05-13 18:32:12
010008c01e742000,"Pokémon Friends",crash;services,menus,2025-07-24 13:32:00 010008c01e742000,"Pokémon Friends",crash;services,menus,2025-07-24 13:32:00
01003D200BAA2000,"Pokémon Mystery Dungeon™: Rescue Team DX",mac-bug,playable,2024-01-21 00:16:32 01003D200BAA2000,"Pokémon Mystery Dungeon™: Rescue Team DX",mac-bug,playable,2024-01-21 00:16:32
01008DB008C2C000,"Pokémon Shield + Pokémon Shield Expansion Pass",deadlock;crash;online-broken;ldn-works;LAN,ingame,2024-08-12 07:20:22 01008DB008C2C000,"Pokémon Shield + Pokémon Shield Expansion Pass",deadlock;crash;online-broken;ldn-works;LAN,ingame,2024-08-12 07:20:22
@@ -2280,8 +2273,6 @@
01009AD008C4C000,"Pokémon: Let's Go, Pikachu! demo",slow;demo,playable,2023-11-26 11:23:20 01009AD008C4C000,"Pokémon: Let's Go, Pikachu! demo",slow;demo,playable,2023-11-26 11:23:20
0100000011D90000,"Pokémon™ Brilliant Diamond",gpu;ldn-works,ingame,2024-08-28 13:26:35 0100000011D90000,"Pokémon™ Brilliant Diamond",gpu;ldn-works,ingame,2024-08-28 13:26:35
010018E011D92000,"Pokémon™ Shining Pearl",gpu;ldn-works,ingame,2024-08-28 13:26:35 010018E011D92000,"Pokémon™ Shining Pearl",gpu;ldn-works,ingame,2024-08-28 13:26:35
100554023408000,"Pokémon FireRed Version",crashes,nothing,2026-05-13 18:32:12
010034D02340E000,"Pokémon LeafGreen Version",crashes,nothing,2026-05-13 18:32:12
010015F008C54000,"Pokémon™ HOME",Needs Update;crash;services,menus,2020-12-06 06:01:51 010015F008C54000,"Pokémon™ HOME",Needs Update;crash;services,menus,2020-12-06 06:01:51
01001F5010DFA000,"Pokémon™ Legends: Arceus",gpu;Needs Update;ldn-works,ingame,2024-09-19 10:02:02 01001F5010DFA000,"Pokémon™ Legends: Arceus",gpu;Needs Update;ldn-works,ingame,2024-09-19 10:02:02
0100F43008C44000,"Pokémon™ Legends: Z-A",gpu;crash;ldn-works,ingame,2025-11-16 00:30:00 0100F43008C44000,"Pokémon™ Legends: Z-A",gpu;crash;ldn-works,ingame,2025-11-16 00:30:00
@@ -2647,7 +2638,6 @@
0100B16009C10000,"SINNER: Sacrifice for Redemption",nvdec;UE4;vulkan-backend-bug,playable,2022-08-12 20:37:33 0100B16009C10000,"SINNER: Sacrifice for Redemption",nvdec;UE4;vulkan-backend-bug,playable,2022-08-12 20:37:33
0100E9201410E000,"Sir Lovelot",,playable,2021-04-05 16:21:46 0100E9201410E000,"Sir Lovelot",,playable,2021-04-05 16:21:46
0100134011E32000,"Skate City",,playable,2022-11-04 11:37:39 0100134011E32000,"Skate City",,playable,2022-11-04 11:37:39
0100a8501b66e000,"Skateboard Drifting with Maxwell Cat: The Game Simulator",,playable,2026-02-17 19:05:00
0100B2F008BD8000,"Skee-Ball",,playable,2020-11-16 04:44:07 0100B2F008BD8000,"Skee-Ball",,playable,2020-11-16 04:44:07
01001A900F862000,"Skelattack",,playable,2021-06-09 15:26:26 01001A900F862000,"Skelattack",,playable,2021-06-09 15:26:26
01008E700F952000,"Skelittle: A Giant Party!",,playable,2021-06-09 19:08:34 01008E700F952000,"Skelittle: A Giant Party!",,playable,2021-06-09 19:08:34
@@ -2873,7 +2863,7 @@
0100277011F1A000,"Super Mario Bros.™ 35",online-broken,menus,2022-08-07 16:27:25 0100277011F1A000,"Super Mario Bros.™ 35",online-broken,menus,2022-08-07 16:27:25
010015100B514000,"Super Mario Bros.™ Wonder",amd-vendor-bug,playable,2024-09-06 13:21:21 010015100B514000,"Super Mario Bros.™ Wonder",amd-vendor-bug,playable,2024-09-06 13:21:21
01009B90006DC000,"Super Mario Maker™ 2",online-broken;ldn-broken,playable,2024-08-25 11:05:19 01009B90006DC000,"Super Mario Maker™ 2",online-broken;ldn-broken,playable,2024-08-25 11:05:19
0100000000010000,"Super Mario Odyssey™",nvdec;intel-vendor-bug;mac-bug;amd-vendor-bug,playable,2026-05-13 18:32:12 0100000000010000,"Super Mario Odyssey™",nvdec;intel-vendor-bug;mac-bug,playable,2024-08-25 01:32:34
010036B0034E4000,"Super Mario Party™",gpu;Needs Update;ldn-works,ingame,2024-06-21 05:10:16 010036B0034E4000,"Super Mario Party™",gpu;Needs Update;ldn-works,ingame,2024-06-21 05:10:16
0100965017338000,"Super Mario Party Jamboree",mac-bug;gpu,ingame,2025-02-17 02:09:20 0100965017338000,"Super Mario Party Jamboree",mac-bug;gpu,ingame,2025-02-17 02:09:20
0100BC0018138000,"Super Mario RPG™",gpu;audio;nvdec,ingame,2024-06-19 17:43:42 0100BC0018138000,"Super Mario RPG™",gpu;audio;nvdec,ingame,2024-06-19 17:43:42
@@ -3170,8 +3160,6 @@
0100E2E00CB14000,"Tokyo School Life",,playable,2022-09-16 20:25:54 0100E2E00CB14000,"Tokyo School Life",,playable,2022-09-16 20:25:54
010024601BB16000,"Tomb Raider I-III Remastered Starring Lara Croft",gpu;opengl,ingame,2024-09-27 12:32:04 010024601BB16000,"Tomb Raider I-III Remastered Starring Lara Croft",gpu;opengl,ingame,2024-09-27 12:32:04
0100D7F01E49C000,"Tomba! Special Edition",services-horizon,nothing,2024-09-15 21:59:54 0100D7F01E49C000,"Tomba! Special Edition",services-horizon,nothing,2024-09-15 21:59:54
010051F0207B2000,"Tomodachi Life: Living the Dream",amd-vendor-bug;gpu;intel-vendor-bug;ldn-broken,ingame,2026-05-13 18:32:12
0100CA502552A000,"Tomodachi Life: Living the Dream Welcome Edtion",amd-vendor-bug;demo,playable,2026-05-13 18:32:12
0100D400100F8000,"Tonight We Riot",,playable,2021-02-26 15:55:09 0100D400100F8000,"Tonight We Riot",,playable,2021-02-26 15:55:09
0100CC00102B4000,"Tony Hawk's™ Pro Skater™ 1 + 2",gpu;Needs Update,ingame,2024-09-24 08:18:14 0100CC00102B4000,"Tony Hawk's™ Pro Skater™ 1 + 2",gpu;Needs Update,ingame,2024-09-24 08:18:14
010093F00E818000,"Tools Up!",crash,ingame,2020-07-21 12:58:17 010093F00E818000,"Tools Up!",crash,ingame,2020-07-21 12:58:17
@@ -3319,7 +3307,6 @@
0100AFA011068000,"Voxel Pirates",,playable,2022-09-28 22:55:02 0100AFA011068000,"Voxel Pirates",,playable,2022-09-28 22:55:02
0100BFB00D1F4000,"Voxel Sword",,playable,2022-08-30 14:57:27 0100BFB00D1F4000,"Voxel Sword",,playable,2022-08-30 14:57:27
01004E90028A2000,"Vroom in the night sky",Needs Update;vulkan-backend-bug,playable,2023-02-20 02:32:29 01004E90028A2000,"Vroom in the night sky",Needs Update;vulkan-backend-bug,playable,2023-02-20 02:32:29
0100BFC01D976000,"Virtual Boy Nintendo Classics",services,nothing,2026-02-17 11:26:59
0100C7C00AE6C000,"VSR: Void Space Racing",,playable,2021-01-27 14:08:59 0100C7C00AE6C000,"VSR: Void Space Racing",,playable,2021-01-27 14:08:59
0100B130119D0000,"Waifu Uncovered",crash,ingame,2023-02-27 01:17:46 0100B130119D0000,"Waifu Uncovered",crash,ingame,2023-02-27 01:17:46
0100E29010A4A000,"Wanba Warriors",,playable,2020-10-04 17:56:22 0100E29010A4A000,"Wanba Warriors",,playable,2020-10-04 17:56:22
1 title_id game_name labels status last_updated
1061 0100BCA016636000 eBaseball Powerful Pro Yakyuu 2022 gpu;services-horizon;crash nothing 2024-05-26 23:07:19
1062 01001F20100B8000 Eclipse: Edge of Light playable 2020-08-11 23:06:29
1063 0100E0A0110F4000 eCrossminton playable 2020-07-11 18:24:27
010054601D54C000 Emio – The Smiling Man: Famicom Detective Club (DEMO) demo playable 2026-05-13 18:32:12
1064 0100ABE00DB4E000 Edna & Harvey: Harvey's New Eyes nvdec playable 2021-01-26 14:36:08
1065 01004F000B716000 Edna & Harvey: The Breakout – Anniversary Edition crash;nvdec ingame 2022-08-01 16:59:56
1066 01002550129F0000 Effie playable 2022-10-27 14:36:39
1204 01003B200E440000 Five Nights at Freddy's: Sister Location playable 2023-10-06 09:00:58
1205 010038200E088000 Flan crash;regression ingame 2021-11-17 07:39:28
1206 01000A0004C50000 FLASHBACK™ nvdec playable 2020-05-14 13:57:29
1207 0100C53004C52000 Flat Heroes gpu playable ingame 2026-02-27 17:00:00 2022-07-26 19:37:37
1208 0100B54012798000 Flatland: Prologue playable 2020-12-11 20:41:12
1209 0100307004B4C000 Flinthook online playable 2021-03-25 20:42:29
1210 010095A004040000 Flip Wars services;ldn-untested ingame 2022-05-02 15:39:18
1394 0100c3c012718000 Grand Theft Auto: III – The Definitive Edition gpu;UE4 ingame 2022-10-31 20:13:52
1395 0100182014022000 Grand Theft Auto: Vice City – The Definitive Edition gpu;UE4 ingame 2022-10-31 20:13:52
1396 010065a014024000 Grand Theft Auto: San Andreas – The Definitive Edition gpu;UE4 ingame 2022-10-31 20:13:52
0100EB500D92E000 GROOVE COASTER WAI WAI PARTY!!!! gpu ingame 2026-05-13 18:32:12
1397 0100822012D76000 HAAK gpu ingame 2023-02-19 14:31:05
1398 01007E100EFA8000 Habroxia playable 2020-06-16 23:04:42
1399 0100535012974000 Hades vulkan playable 2022-10-05 10:45:21
1832 010055200E87E000 Metamorphosis UE4;audout;gpu;nvdec ingame 2021-06-16 16:18:11
1833 0100D4900E82C000 Metro 2033 Redux gpu ingame 2022-11-09 10:53:13
1834 0100F0400E850000 Metro: Last Light Redux slow;nvdec;vulkan-backend-bug ingame 2023-11-01 11:53:52
010019A01E2F2000 Metroid Prime 4: Beyond ingame 2026-05-13 18:32:12
1835 010012101468C000 Metroid Prime™ Remastered gpu;Needs Update;vulkan-backend-bug;opengl-backend-bug ingame 2024-05-07 22:48:15
1836 010093801237C000 Metroid™ Dread playable 2023-11-13 04:02:36
1837 0100A1200F20C000 Midnight Evil playable 2022-10-18 22:55:19
1945 0100C3E00ACAA000 Mutant Football League: Dynasty Edition online-broken playable 2022-08-05 17:01:51
1946 01004BE004A86000 Mutant Mudds Collection playable 2022-08-05 17:11:38
1947 0100E6B00DEA4000 Mutant Year Zero: Road to Eden - Deluxe Edition nvdec;UE4 playable 2022-09-10 13:31:10
010037501F864000 Mute Crimson DX ingame 2026-05-13 18:32:12
1948 0100161009E5C000 MX Nitro: Unleashed playable 2022-09-27 22:34:33
1949 0100218011E7E000 MX vs ATV All Out nvdec;UE4;vulkan-backend-bug playable 2022-10-25 19:51:46
1950 0100D940063A0000 MXGP3 - The Official Motocross Videogame UE4;gpu;nvdec ingame 2020-12-16 14:00:20
2050 010003C00B868000 Ninjin: Clash of Carrots online-broken playable 2024-07-10 05:12:26
2051 0100746010E4C000 NinNinDays playable 2022-11-20 15:17:29
2052 0100C9A00ECE6000 Nintendo 64™ – Nintendo Switch Online gpu;vulkan ingame 2024-04-23 20:21:07
010057D00ECE4000 Nintendo 64™ – Nintendo Switch Online gpu;vulkan ingame 2024-04-23 20:21:07
2053 0100e0601c632000 Nintendo 64™ – Nintendo Switch Online: MATURE 17+ ingame 2025-02-03 22:27:00
010037A0170D2000 NINTENDO 64™ – Nintendo Switch Online 18+ ingame 2025-02-03 22:27:00
2054 0100D870045B6000 Nintendo Entertainment System™ - Nintendo Switch Online online playable 2022-07-01 15:45:06
2055 0100C4B0034B2000 Nintendo Labo Toy-Con 01 Variety Kit gpu ingame 2022-08-07 12:56:07
2056 01001E9003502000 Nintendo Labo Toy-Con 03 Vehicle Kit services;crash menus 2022-08-03 17:20:11
2266 010086F0064CE000 Poi: Explorer Edition nvdec playable 2021-01-21 19:32:00
2267 0100EB6012FD2000 Poison Control playable 2021-05-16 14:01:54
2268 010072400E04A000 Pokémon Café ReMix playable 2021-08-17 20:00:04
01005B7008C52800 Pokémon Champions Needs Update;services;online-broke menus 2026-05-13 18:32:12
2269 010008c01e742000 Pokémon Friends crash;services menus 2025-07-24 13:32:00
2270 01003D200BAA2000 Pokémon Mystery Dungeon™: Rescue Team DX mac-bug playable 2024-01-21 00:16:32
2271 01008DB008C2C000 Pokémon Shield + Pokémon Shield Expansion Pass deadlock;crash;online-broken;ldn-works;LAN ingame 2024-08-12 07:20:22
2273 01009AD008C4C000 Pokémon: Let's Go, Pikachu! demo slow;demo playable 2023-11-26 11:23:20
2274 0100000011D90000 Pokémon™ Brilliant Diamond gpu;ldn-works ingame 2024-08-28 13:26:35
2275 010018E011D92000 Pokémon™ Shining Pearl gpu;ldn-works ingame 2024-08-28 13:26:35
100554023408000 Pokémon FireRed Version crashes nothing 2026-05-13 18:32:12
010034D02340E000 Pokémon LeafGreen Version crashes nothing 2026-05-13 18:32:12
2276 010015F008C54000 Pokémon™ HOME Needs Update;crash;services menus 2020-12-06 06:01:51
2277 01001F5010DFA000 Pokémon™ Legends: Arceus gpu;Needs Update;ldn-works ingame 2024-09-19 10:02:02
2278 0100F43008C44000 Pokémon™ Legends: Z-A gpu;crash;ldn-works ingame 2025-11-16 00:30:00
2638 0100B16009C10000 SINNER: Sacrifice for Redemption nvdec;UE4;vulkan-backend-bug playable 2022-08-12 20:37:33
2639 0100E9201410E000 Sir Lovelot playable 2021-04-05 16:21:46
2640 0100134011E32000 Skate City playable 2022-11-04 11:37:39
0100a8501b66e000 Skateboard Drifting with Maxwell Cat: The Game Simulator playable 2026-02-17 19:05:00
2641 0100B2F008BD8000 Skee-Ball playable 2020-11-16 04:44:07
2642 01001A900F862000 Skelattack playable 2021-06-09 15:26:26
2643 01008E700F952000 Skelittle: A Giant Party! playable 2021-06-09 19:08:34
2863 0100277011F1A000 Super Mario Bros.™ 35 online-broken menus 2022-08-07 16:27:25
2864 010015100B514000 Super Mario Bros.™ Wonder amd-vendor-bug playable 2024-09-06 13:21:21
2865 01009B90006DC000 Super Mario Maker™ 2 online-broken;ldn-broken playable 2024-08-25 11:05:19
2866 0100000000010000 Super Mario Odyssey™ nvdec;intel-vendor-bug;mac-bug;amd-vendor-bug nvdec;intel-vendor-bug;mac-bug playable 2026-05-13 18:32:12 2024-08-25 01:32:34
2867 010036B0034E4000 Super Mario Party™ gpu;Needs Update;ldn-works ingame 2024-06-21 05:10:16
2868 0100965017338000 Super Mario Party Jamboree mac-bug;gpu ingame 2025-02-17 02:09:20
2869 0100BC0018138000 Super Mario RPG™ gpu;audio;nvdec ingame 2024-06-19 17:43:42
3160 0100E2E00CB14000 Tokyo School Life playable 2022-09-16 20:25:54
3161 010024601BB16000 Tomb Raider I-III Remastered Starring Lara Croft gpu;opengl ingame 2024-09-27 12:32:04
3162 0100D7F01E49C000 Tomba! Special Edition services-horizon nothing 2024-09-15 21:59:54
010051F0207B2000 Tomodachi Life: Living the Dream amd-vendor-bug;gpu;intel-vendor-bug;ldn-broken ingame 2026-05-13 18:32:12
0100CA502552A000 Tomodachi Life: Living the Dream – Welcome Edtion amd-vendor-bug;demo playable 2026-05-13 18:32:12
3163 0100D400100F8000 Tonight We Riot playable 2021-02-26 15:55:09
3164 0100CC00102B4000 Tony Hawk's™ Pro Skater™ 1 + 2 gpu;Needs Update ingame 2024-09-24 08:18:14
3165 010093F00E818000 Tools Up! crash ingame 2020-07-21 12:58:17
3307 0100AFA011068000 Voxel Pirates playable 2022-09-28 22:55:02
3308 0100BFB00D1F4000 Voxel Sword playable 2022-08-30 14:57:27
3309 01004E90028A2000 Vroom in the night sky Needs Update;vulkan-backend-bug playable 2023-02-20 02:32:29
0100BFC01D976000 Virtual Boy – Nintendo Classics services nothing 2026-02-17 11:26:59
3310 0100C7C00AE6C000 VSR: Void Space Racing playable 2021-01-27 14:08:59
3311 0100B130119D0000 Waifu Uncovered crash ingame 2023-02-27 01:17:46
3312 0100E29010A4A000 Wanba Warriors playable 2020-10-04 17:56:22

View File

@@ -5,7 +5,8 @@
<clear /> <clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" /> <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<!-- Only needed when using pre-release versions of Ryujinx.LibHac. --> <!-- Only needed when using pre-release versions of Ryujinx.LibHac. -->
<add key="LibHacAlpha" value="https://git.ryujinx.app/api/packages/projects/nuget/index.json" /> <add key="LibHacAlpha" value="https://git.ryujinx.app/api/v4/projects/17/packages/nuget/index.json" />
<add key="Ryujinx.UpdateClient" value="https://git.ryujinx.app/api/v4/projects/71/packages/nuget/index.json" />
</packageSources> </packageSources>
<packageSourceMapping> <packageSourceMapping>
<!-- key value for <packageSource> should match key values from <packageSources> element --> <!-- key value for <packageSource> should match key values from <packageSources> element -->
@@ -13,6 +14,10 @@
<packageSource key="nuget.org"> <packageSource key="nuget.org">
<package pattern="*" /> <package pattern="*" />
</packageSource> </packageSource>
<packageSource key="Ryujinx.UpdateClient">
<package pattern="Ryujinx.UpdateClient" />
<package pattern="Ryujinx.Systems.Update.Common" />
</packageSource>
<packageSource key="LibHacAlpha"> <packageSource key="LibHacAlpha">
<package pattern="Ryujinx.LibHac" /> <package pattern="Ryujinx.LibHac" />
</packageSource> </packageSource>

View File

@@ -168,7 +168,7 @@ namespace ARMeilleure.Common
{ {
_allocated.Dispose(); _allocated.Dispose();
foreach (nint page in _pages.Values) foreach (IntPtr page in _pages.Values)
{ {
NativeAllocator.Instance.Free((void*)page); NativeAllocator.Instance.Free((void*)page);
} }

View File

@@ -1,16 +0,0 @@
namespace Ryujinx.Audio.Backends.Apple
{
class AppleAudioBuffer
{
public readonly ulong DriverIdentifier;
public readonly ulong SampleCount;
public ulong SamplePlayed;
public AppleAudioBuffer(ulong driverIdentifier, ulong sampleCount)
{
DriverIdentifier = driverIdentifier;
SampleCount = sampleCount;
SamplePlayed = 0;
}
}
}

View File

@@ -1,196 +0,0 @@
using Ryujinx.Audio.Common;
using Ryujinx.Audio.Integration;
using Ryujinx.Common.Logging;
using Ryujinx.Memory;
using System;
using System.Collections.Concurrent;
using System.Runtime.InteropServices;
using System.Threading;
using System.Runtime.Versioning;
using Ryujinx.Audio.Backends.Apple.Native;
using static Ryujinx.Audio.Backends.Apple.Native.AudioToolbox;
using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
namespace Ryujinx.Audio.Backends.Apple
{
[SupportedOSPlatform("macos")]
[SupportedOSPlatform("ios")]
public sealed class AppleHardwareDeviceDriver : IHardwareDeviceDriver
{
private readonly ManualResetEvent _updateRequiredEvent;
private readonly ManualResetEvent _pauseEvent;
private readonly ConcurrentDictionary<AppleHardwareDeviceSession, byte> _sessions;
private readonly bool _supportSurroundConfiguration;
public float Volume { get; set; }
public AppleHardwareDeviceDriver()
{
_updateRequiredEvent = new ManualResetEvent(false);
_pauseEvent = new ManualResetEvent(true);
_sessions = new ConcurrentDictionary<AppleHardwareDeviceSession, byte>();
_supportSurroundConfiguration = TestSurroundSupport();
Volume = 1f;
}
private bool TestSurroundSupport()
{
try
{
AudioStreamBasicDescription format =
GetAudioFormat(SampleFormat.PcmFloat, Constants.TargetSampleRate, 6);
int result = AudioQueueNewOutput(
ref format,
nint.Zero,
nint.Zero,
nint.Zero,
nint.Zero,
0,
out nint testQueue);
if (result == 0)
{
AudioChannelLayout layout = new AudioChannelLayout
{
AudioChannelLayoutTag = kAudioChannelLayoutTag_MPEG_5_1_A,
AudioChannelBitmap = 0,
NumberChannelDescriptions = 0
};
int layoutResult = AudioQueueSetProperty(
testQueue,
kAudioQueueProperty_ChannelLayout,
ref layout,
(uint)Marshal.SizeOf<AudioChannelLayout>());
if (layoutResult == 0)
{
AudioQueueDispose(testQueue, true);
return true;
}
AudioQueueDispose(testQueue, true);
}
return false;
}
catch
{
return false;
}
}
public static bool IsSupported => OperatingSystem.IsMacOSVersionAtLeast(10, 5);
public ManualResetEvent GetUpdateRequiredEvent()
=> _updateRequiredEvent;
public ManualResetEvent GetPauseEvent()
=> _pauseEvent;
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager,
SampleFormat sampleFormat, uint sampleRate, uint channelCount)
{
if (channelCount == 0)
{
channelCount = 2;
}
if (sampleRate == 0)
{
sampleRate = Constants.TargetSampleRate;
}
if (direction != Direction.Output)
{
throw new NotImplementedException("Input direction is currently not implemented on Apple backend!");
}
AppleHardwareDeviceSession session = new(this, memoryManager, sampleFormat, sampleRate, channelCount);
_sessions.TryAdd(session, 0);
return session;
}
internal bool Unregister(AppleHardwareDeviceSession session)
=> _sessions.TryRemove(session, out _);
internal static AudioStreamBasicDescription GetAudioFormat(SampleFormat sampleFormat, uint sampleRate,
uint channelCount)
{
uint formatFlags;
uint bitsPerChannel;
switch (sampleFormat)
{
case SampleFormat.PcmInt8:
formatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
bitsPerChannel = 8;
break;
case SampleFormat.PcmInt16:
formatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
bitsPerChannel = 16;
break;
case SampleFormat.PcmInt32:
formatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
bitsPerChannel = 32;
break;
case SampleFormat.PcmFloat:
formatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked;
bitsPerChannel = 32;
break;
default:
throw new ArgumentException($"Unsupported sample format {sampleFormat}");
}
uint bytesPerFrame = (bitsPerChannel / 8) * channelCount;
return new AudioStreamBasicDescription
{
SampleRate = sampleRate,
FormatID = kAudioFormatLinearPCM,
FormatFlags = formatFlags,
BytesPerPacket = bytesPerFrame,
FramesPerPacket = 1,
BytesPerFrame = bytesPerFrame,
ChannelsPerFrame = channelCount,
BitsPerChannel = bitsPerChannel,
Reserved = 0
};
}
public void Dispose()
{
GC.SuppressFinalize(this);
Dispose(true);
}
private void Dispose(bool disposing)
{
if (disposing)
{
foreach (AppleHardwareDeviceSession session in _sessions.Keys)
{
session.Dispose();
}
_pauseEvent.Dispose();
}
}
public bool SupportsDirection(Direction direction)
=> direction != Direction.Input;
public bool SupportsSampleRate(uint sampleRate) => true;
public bool SupportsSampleFormat(SampleFormat sampleFormat)
=> sampleFormat != SampleFormat.PcmInt24;
public bool SupportsChannelCount(uint channelCount)
=> channelCount != 6 || _supportSurroundConfiguration;
}
}

View File

@@ -1,285 +0,0 @@
using Ryujinx.Audio.Backends.Common;
using Ryujinx.Audio.Common;
using Ryujinx.Memory;
using System;
using System.Collections.Concurrent;
using System.Runtime.InteropServices;
using System.Threading;
using System.Runtime.Versioning;
using static Ryujinx.Audio.Backends.Apple.Native.AudioToolbox;
namespace Ryujinx.Audio.Backends.Apple
{
[SupportedOSPlatform("macos")]
[SupportedOSPlatform("ios")]
class AppleHardwareDeviceSession : HardwareDeviceSessionOutputBase
{
private const int NumBuffers = 3;
private readonly AppleHardwareDeviceDriver _driver;
private readonly ConcurrentQueue<AppleAudioBuffer> _queuedBuffers = new();
private readonly DynamicRingBuffer _ringBuffer = new();
private readonly ManualResetEvent _updateRequiredEvent;
private readonly AudioQueueOutputCallback _callbackDelegate;
private readonly GCHandle _gcHandle;
private nint _audioQueue;
private readonly nint[] _audioQueueBuffers = new nint[NumBuffers];
private readonly int[] _bufferBytesFilled = new int[NumBuffers];
private readonly int _bytesPerFrame;
private ulong _playedSampleCount;
private bool _started;
private float _volume = 1f;
private readonly object _lock = new();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void AudioQueueOutputCallback(
nint userData,
nint audioQueue,
nint buffer);
public AppleHardwareDeviceSession(
AppleHardwareDeviceDriver driver,
IVirtualMemoryManager memoryManager,
SampleFormat requestedSampleFormat,
uint requestedSampleRate,
uint requestedChannelCount)
: base(memoryManager, requestedSampleFormat, requestedSampleRate, requestedChannelCount)
{
_driver = driver;
_updateRequiredEvent = driver.GetUpdateRequiredEvent();
_callbackDelegate = OutputCallback;
_bytesPerFrame = BackendHelper.GetSampleSize(requestedSampleFormat) * (int)requestedChannelCount;
_gcHandle = GCHandle.Alloc(this, GCHandleType.Normal);
SetupAudioQueue();
}
private void SetupAudioQueue()
{
lock (_lock)
{
AudioStreamBasicDescription format = AppleHardwareDeviceDriver.GetAudioFormat(
RequestedSampleFormat,
RequestedSampleRate,
RequestedChannelCount);
nint callbackPtr = Marshal.GetFunctionPointerForDelegate(_callbackDelegate);
nint userData = GCHandle.ToIntPtr(_gcHandle);
int result = AudioQueueNewOutput(
ref format,
callbackPtr,
userData,
nint.Zero,
nint.Zero,
0,
out _audioQueue);
if (result != 0)
{
throw new InvalidOperationException($"AudioQueueNewOutput failed: {result}");
}
uint framesPerBuffer = RequestedSampleRate / 100;
uint bufferSize = framesPerBuffer * (uint)_bytesPerFrame;
for (int i = 0; i < NumBuffers; i++)
{
AudioQueueAllocateBuffer(_audioQueue, bufferSize, out _audioQueueBuffers[i]);
_bufferBytesFilled[i] = 0;
PrimeBuffer(_audioQueueBuffers[i], i);
}
}
}
private unsafe void PrimeBuffer(nint bufferPtr, int bufferIndex)
{
AudioQueueBuffer* buffer = (AudioQueueBuffer*)bufferPtr;
int capacityBytes = (int)buffer->AudioDataBytesCapacity;
int framesPerBuffer = capacityBytes / _bytesPerFrame;
int availableFrames = _ringBuffer.Length / _bytesPerFrame;
int framesToRead = Math.Min(availableFrames, framesPerBuffer);
int bytesToRead = framesToRead * _bytesPerFrame;
Span<byte> dst = new((void*)buffer->AudioData, capacityBytes);
dst.Clear();
if (bytesToRead > 0)
{
Span<byte> audio = dst.Slice(0, bytesToRead);
_ringBuffer.Read(audio, 0, bytesToRead);
ApplyVolume(buffer->AudioData, bytesToRead);
}
buffer->AudioDataByteSize = (uint)capacityBytes;
_bufferBytesFilled[bufferIndex] = bytesToRead;
AudioQueueEnqueueBuffer(_audioQueue, bufferPtr, 0, nint.Zero);
}
private void OutputCallback(nint userData, nint audioQueue, nint bufferPtr)
{
if (!_started || bufferPtr == nint.Zero)
return;
int bufferIndex = Array.IndexOf(_audioQueueBuffers, bufferPtr);
if (bufferIndex < 0)
return;
int bytesPlayed = _bufferBytesFilled[bufferIndex];
if (bytesPlayed > 0)
{
ProcessPlayedSamples(bytesPlayed);
}
PrimeBuffer(bufferPtr, bufferIndex);
}
private void ProcessPlayedSamples(int bytesPlayed)
{
ulong samplesPlayed = GetSampleCount(bytesPlayed);
ulong remaining = samplesPlayed;
bool needUpdate = false;
while (remaining > 0 && _queuedBuffers.TryPeek(out AppleAudioBuffer buffer))
{
ulong needed = buffer.SampleCount - Interlocked.Read(ref buffer.SamplePlayed);
ulong take = Math.Min(needed, remaining);
ulong played = Interlocked.Add(ref buffer.SamplePlayed, take);
remaining -= take;
if (played == buffer.SampleCount)
{
_queuedBuffers.TryDequeue(out _);
needUpdate = true;
}
Interlocked.Add(ref _playedSampleCount, take);
}
if (needUpdate)
{
_updateRequiredEvent.Set();
}
}
private unsafe void ApplyVolume(nint dataPtr, int byteSize)
{
float volume = Math.Clamp(_volume * _driver.Volume, 0f, 1f);
if (volume >= 0.999f)
return;
int sampleCount = byteSize / BackendHelper.GetSampleSize(RequestedSampleFormat);
switch (RequestedSampleFormat)
{
case SampleFormat.PcmInt16:
short* s16 = (short*)dataPtr;
for (int i = 0; i < sampleCount; i++)
s16[i] = (short)(s16[i] * volume);
break;
case SampleFormat.PcmFloat:
float* f32 = (float*)dataPtr;
for (int i = 0; i < sampleCount; i++)
f32[i] *= volume;
break;
case SampleFormat.PcmInt32:
int* s32 = (int*)dataPtr;
for (int i = 0; i < sampleCount; i++)
s32[i] = (int)(s32[i] * volume);
break;
case SampleFormat.PcmInt8:
sbyte* s8 = (sbyte*)dataPtr;
for (int i = 0; i < sampleCount; i++)
s8[i] = (sbyte)(s8[i] * volume);
break;
}
}
public override void QueueBuffer(AudioBuffer buffer)
{
_ringBuffer.Write(buffer.Data, 0, buffer.Data.Length);
_queuedBuffers.Enqueue(new AppleAudioBuffer(buffer.DataPointer, GetSampleCount(buffer)));
}
public override void Start()
{
lock (_lock)
{
if (_started)
return;
_started = true;
AudioQueueStart(_audioQueue, nint.Zero);
}
}
public override void Stop()
{
lock (_lock)
{
if (!_started)
return;
_started = false;
AudioQueuePause(_audioQueue);
}
}
public override ulong GetPlayedSampleCount()
=> Interlocked.Read(ref _playedSampleCount);
public override float GetVolume() => _volume;
public override void SetVolume(float volume) => _volume = volume;
public override bool WasBufferFullyConsumed(AudioBuffer buffer)
{
if (!_queuedBuffers.TryPeek(out AppleAudioBuffer driverBuffer))
return true;
return driverBuffer.DriverIdentifier != buffer.DataPointer;
}
public override void PrepareToClose() { }
public override void UnregisterBuffer(AudioBuffer buffer) { }
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
Stop();
if (_audioQueue != nint.Zero)
{
AudioQueueStop(_audioQueue, true);
AudioQueueDispose(_audioQueue, true);
_audioQueue = nint.Zero;
}
if (_gcHandle.IsAllocated)
{
_gcHandle.Free();
}
}
}
public override void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}

View File

@@ -1,102 +0,0 @@
using System.Runtime.InteropServices;
// ReSharper disable InconsistentNaming
namespace Ryujinx.Audio.Backends.Apple.Native
{
public static partial class AudioToolbox
{
[StructLayout(LayoutKind.Sequential)]
internal struct AudioStreamBasicDescription
{
public double SampleRate;
public uint FormatID;
public uint FormatFlags;
public uint BytesPerPacket;
public uint FramesPerPacket;
public uint BytesPerFrame;
public uint ChannelsPerFrame;
public uint BitsPerChannel;
public uint Reserved;
}
[StructLayout(LayoutKind.Sequential)]
internal struct AudioChannelLayout
{
public uint AudioChannelLayoutTag;
public uint AudioChannelBitmap;
public uint NumberChannelDescriptions;
}
internal const uint kAudioFormatLinearPCM = 0x6C70636D;
internal const uint kAudioQueueProperty_ChannelLayout = 0x6171636c;
internal const uint kAudioChannelLayoutTag_MPEG_5_1_A = 0x650006;
internal const uint kAudioFormatFlagIsFloat = (1 << 0);
internal const uint kAudioFormatFlagIsSignedInteger = (1 << 2);
internal const uint kAudioFormatFlagIsPacked = (1 << 3);
internal const uint kAudioFormatFlagIsBigEndian = (1 << 1);
internal const uint kAudioFormatFlagIsAlignedHigh = (1 << 4);
internal const uint kAudioFormatFlagIsNonInterleaved = (1 << 5);
[LibraryImport("/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox")]
internal static partial int AudioQueueNewOutput(
ref AudioStreamBasicDescription format,
nint callback,
nint userData,
nint callbackRunLoop,
nint callbackRunLoopMode,
uint flags,
out nint audioQueue);
[LibraryImport("/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox")]
internal static partial int AudioQueueSetProperty(
nint audioQueue,
uint propertyID,
ref AudioChannelLayout layout,
uint layoutSize);
[LibraryImport("/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox")]
internal static partial int AudioQueueDispose(nint audioQueue, [MarshalAs(UnmanagedType.I1)] bool immediate);
[LibraryImport("/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox")]
internal static partial int AudioQueueAllocateBuffer(
nint audioQueue,
uint bufferByteSize,
out nint buffer);
[LibraryImport("/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox")]
internal static partial int AudioQueueStart(nint audioQueue, nint startTime);
[LibraryImport("/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox")]
internal static partial int AudioQueuePause(nint audioQueue);
[LibraryImport("/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox")]
internal static partial int AudioQueueStop(nint audioQueue, [MarshalAs(UnmanagedType.I1)] bool immediate);
[LibraryImport("/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox")]
internal static partial int AudioQueueSetParameter(
nint audioQueue,
uint parameterID,
float value);
[LibraryImport("/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox")]
internal static partial int AudioQueueEnqueueBuffer(
nint audioQueue,
nint buffer,
uint numPacketDescs,
nint packetDescs);
[StructLayout(LayoutKind.Sequential)]
internal struct AudioQueueBuffer
{
public uint AudioDataBytesCapacity;
public nint AudioData;
public uint AudioDataByteSize;
public nint UserData;
public uint PacketDescriptionCapacity;
public nint PacketDescriptions;
public uint PacketDescriptionCount;
}
internal const uint kAudioQueueParam_Volume = 1;
}
}

View File

@@ -1,13 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Ryujinx.Audio\Ryujinx.Audio.csproj" />
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
</ItemGroup>
</Project>

View File

@@ -10,8 +10,7 @@ using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
namespace Ryujinx.Audio.Backends.OpenAL namespace Ryujinx.Audio.Backends.OpenAL
{ {
// ReSharper disable once InconsistentNaming public class OpenALHardwareDeviceDriver : IHardwareDeviceDriver
public sealed class OpenALHardwareDeviceDriver : IHardwareDeviceDriver
{ {
private readonly ALDevice _device; private readonly ALDevice _device;
private readonly ALContext _context; private readonly ALContext _context;
@@ -149,7 +148,7 @@ namespace Ryujinx.Audio.Backends.OpenAL
Dispose(true); Dispose(true);
} }
private void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (disposing) if (disposing)
{ {

View File

@@ -9,8 +9,7 @@ using System.Threading;
namespace Ryujinx.Audio.Backends.OpenAL namespace Ryujinx.Audio.Backends.OpenAL
{ {
// ReSharper disable once InconsistentNaming class OpenALHardwareDeviceSession : HardwareDeviceSessionOutputBase
sealed class OpenALHardwareDeviceSession : HardwareDeviceSessionOutputBase
{ {
private readonly OpenALHardwareDeviceDriver _driver; private readonly OpenALHardwareDeviceDriver _driver;
private readonly int _sourceId; private readonly int _sourceId;
@@ -191,7 +190,7 @@ namespace Ryujinx.Audio.Backends.OpenAL
} }
} }
private void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (disposing && _driver.Unregister(this)) if (disposing && _driver.Unregister(this))
{ {

View File

@@ -17,7 +17,7 @@ namespace Ryujinx.Audio.Backends.SDL3
using unsafe SDL_AudioStreamCallbackPointer = delegate* unmanaged[Cdecl]<nint, SDL_AudioStream*, int, int, void>; using unsafe SDL_AudioStreamCallbackPointer = delegate* unmanaged[Cdecl]<nint, SDL_AudioStream*, int, int, void>;
public sealed class SDL3HardwareDeviceDriver : IHardwareDeviceDriver public class SDL3HardwareDeviceDriver : IHardwareDeviceDriver
{ {
private readonly ManualResetEvent _updateRequiredEvent; private readonly ManualResetEvent _updateRequiredEvent;
private readonly ManualResetEvent _pauseEvent; private readonly ManualResetEvent _pauseEvent;
@@ -162,7 +162,7 @@ namespace Ryujinx.Audio.Backends.SDL3
Dispose(true); Dispose(true);
} }
private void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (disposing) if (disposing)
{ {

View File

@@ -12,7 +12,10 @@ using System.Runtime.InteropServices;
namespace Ryujinx.Audio.Backends.SDL3 namespace Ryujinx.Audio.Backends.SDL3
{ {
sealed unsafe class SDL3HardwareDeviceSession : HardwareDeviceSessionOutputBase
unsafe class SDL3HardwareDeviceSession : HardwareDeviceSessionOutputBase
{ {
private readonly SDL3HardwareDeviceDriver _driver; private readonly SDL3HardwareDeviceDriver _driver;
private readonly ConcurrentQueue<SDL3AudioBuffer> _queuedBuffers; private readonly ConcurrentQueue<SDL3AudioBuffer> _queuedBuffers;
@@ -223,7 +226,7 @@ namespace Ryujinx.Audio.Backends.SDL3
return driverBuffer.DriverIdentifier != buffer.DataPointer; return driverBuffer.DriverIdentifier != buffer.DataPointer;
} }
private void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (disposing && _driver.Unregister(this)) if (disposing && _driver.Unregister(this))
{ {

View File

@@ -130,7 +130,7 @@ namespace Ryujinx.Audio.Backends.SoundIo.Native
unsafe unsafe
{ {
int* frameCountPtr = &nativeFrameCount; int* frameCountPtr = &nativeFrameCount;
nint* arenasPtr = &arenas; IntPtr* arenasPtr = &arenas;
CheckError(soundio_outstream_begin_write(_context, (nint)arenasPtr, (nint)frameCountPtr)); CheckError(soundio_outstream_begin_write(_context, (nint)arenasPtr, (nint)frameCountPtr));
frameCount = *frameCountPtr; frameCount = *frameCountPtr;

View File

@@ -10,7 +10,7 @@ using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
namespace Ryujinx.Audio.Backends.SoundIo namespace Ryujinx.Audio.Backends.SoundIo
{ {
public sealed class SoundIoHardwareDeviceDriver : IHardwareDeviceDriver public class SoundIoHardwareDeviceDriver : IHardwareDeviceDriver
{ {
private readonly SoundIoContext _audioContext; private readonly SoundIoContext _audioContext;
private readonly SoundIoDeviceContext _audioDevice; private readonly SoundIoDeviceContext _audioDevice;
@@ -227,7 +227,7 @@ namespace Ryujinx.Audio.Backends.SoundIo
} }
} }
private void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (disposing) if (disposing)
{ {

View File

@@ -11,7 +11,7 @@ using static Ryujinx.Audio.Backends.SoundIo.Native.SoundIo;
namespace Ryujinx.Audio.Backends.SoundIo namespace Ryujinx.Audio.Backends.SoundIo
{ {
sealed class SoundIoHardwareDeviceSession : HardwareDeviceSessionOutputBase class SoundIoHardwareDeviceSession : HardwareDeviceSessionOutputBase
{ {
private readonly SoundIoHardwareDeviceDriver _driver; private readonly SoundIoHardwareDeviceDriver _driver;
private readonly ConcurrentQueue<SoundIoAudioBuffer> _queuedBuffers; private readonly ConcurrentQueue<SoundIoAudioBuffer> _queuedBuffers;
@@ -428,7 +428,7 @@ namespace Ryujinx.Audio.Backends.SoundIo
} }
} }
private void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (disposing && _driver.Unregister(this)) if (disposing && _driver.Unregister(this))
{ {

View File

@@ -17,7 +17,7 @@ namespace Ryujinx.Audio.Renderer.Common
public uint MixesSize; public uint MixesSize;
public uint SinksSize; public uint SinksSize;
public uint PerformanceBufferSize; public uint PerformanceBufferSize;
public uint SplitterSize; public uint Unknown24;
public uint RenderInfoSize; public uint RenderInfoSize;
#pragma warning disable IDE0051, CS0169 // Remove unused field #pragma warning disable IDE0051, CS0169 // Remove unused field

View File

@@ -433,12 +433,8 @@ namespace Ryujinx.Audio.Renderer.Server
public ResultCode UpdateSplitter(SplitterContext context) public ResultCode UpdateSplitter(SplitterContext context)
{ {
long initialInputConsumed = _inputReader.Consumed;
if (context.Update(ref _inputReader)) if (context.Update(ref _inputReader))
{ {
_inputReader.SetConsumed(initialInputConsumed + _inputHeader.SplitterSize);
return ResultCode.Success; return ResultCode.Success;
} }

View File

@@ -107,12 +107,12 @@ namespace Ryujinx.BuildValidationTasks
{ {
locale.Translations[langCode] = string.Empty; locale.Translations[langCode] = string.Empty;
Console.WriteLine( Console.WriteLine(
$"Language '{langCode}' is a duplicate of en_US in Locale '{locale.ID}'! Resetting it..."); $"Lanugage '{langCode}' is a duplicate of en_US in Locale '{locale.ID}'! Resetting it...");
} }
else else
{ {
Console.WriteLine( Console.WriteLine(
$"Language '{langCode}' is a duplicate of en_US in Locale '{locale.ID}'!"); $"Lanugage '{langCode}' is a duplicate of en_US in Locale '{locale.ID}'!");
} }
} }

View File

@@ -12,12 +12,20 @@ namespace Ryujinx.Common.Helper
private static partial nint GetConsoleWindow(); private static partial nint GetConsoleWindow();
[SupportedOSPlatform("windows")] [SupportedOSPlatform("windows")]
[LibraryImport("kernel32", SetLastError = true)] [LibraryImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)] [return: MarshalAs(UnmanagedType.Bool)]
private static partial bool FreeConsole(); 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 bool SetConsoleWindowStateSupported => OperatingSystem.IsWindows();
public static bool HasConsoleWindow => OperatingSystem.IsWindows() && GetConsoleWindow() != nint.Zero;
public static void SetConsoleWindowState(bool show) public static void SetConsoleWindowState(bool show)
{ {
@@ -34,31 +42,22 @@ namespace Ryujinx.Common.Helper
[SupportedOSPlatform("windows")] [SupportedOSPlatform("windows")]
private static void SetConsoleWindowStateWindows(bool show) private static void SetConsoleWindowStateWindows(bool show)
{ {
if (show) const int SW_HIDE = 0;
const int SW_SHOW = 5;
nint hWnd = GetConsoleWindow();
if (hWnd == nint.Zero)
{ {
if (GetConsoleWindow() != nint.Zero) Logger.Warning?.Print(LogClass.Application, "Attempted to show/hide console window but console window does not exist");
{
Logger.SetConsoleTargetEnabled(true);
}
return; return;
} }
Logger.SetConsoleTargetEnabled(false); SetForegroundWindow(hWnd);
DetachConsole();
}
[SupportedOSPlatform("windows")] hWnd = GetForegroundWindow();
private static void DetachConsole()
{
if (GetConsoleWindow() == nint.Zero)
{
return;
}
if (!FreeConsole()) ShowWindow(hWnd, show ? SW_SHOW : SW_HIDE);
{
Logger.Warning?.Print(LogClass.Application, "Attempted to detach console window but the operation failed");
}
} }
} }
} }

View File

@@ -51,7 +51,6 @@ namespace Ryujinx.Common.Logging
ServiceNgct, ServiceNgct,
ServiceNifm, ServiceNifm,
ServiceNim, ServiceNim,
ServiceNotification,
ServiceNs, ServiceNs,
ServiceNsd, ServiceNsd,
ServiceNtc, ServiceNtc,

View File

@@ -12,7 +12,6 @@ namespace Ryujinx.Common.Logging
Error, Error,
Guest, Guest,
AccessLog, AccessLog,
NetLog,
Notice, Notice,
Trace, Trace,
} }

View File

@@ -12,6 +12,8 @@ namespace Ryujinx.Common.Logging
{ {
public static class Logger public static class Logger
{ {
public static readonly TextWriter WriterProxy = new TextWriterProxy();
private static readonly Stopwatch _time; private static readonly Stopwatch _time;
private static readonly bool[] _enabledClasses; private static readonly bool[] _enabledClasses;
@@ -119,7 +121,6 @@ namespace Ryujinx.Common.Logging
public static Log? Error { get; private set; } public static Log? Error { get; private set; }
public static Log? Guest { get; private set; } public static Log? Guest { get; private set; }
public static Log? AccessLog { get; private set; } public static Log? AccessLog { get; private set; }
public static Log? NetLog { get; private set; }
public static Log? Stub { get; private set; } public static Log? Stub { get; private set; }
public static Log? Trace { get; private set; } public static Log? Trace { get; private set; }
public static Log Notice { get; } // Always enabled public static Log Notice { get; } // Always enabled
@@ -137,7 +138,11 @@ namespace Ryujinx.Common.Logging
_time = Stopwatch.StartNew(); _time = Stopwatch.StartNew();
SetConsoleTargetEnabled(true); // Logger should log to console by default
AddTarget(new AsyncLogTargetWrapper(
new ConsoleLogTarget("console"),
1000,
AsyncLogTargetOverflowAction.Discard));
Notice = new Log(LogLevel.Notice); Notice = new Log(LogLevel.Notice);
@@ -170,21 +175,6 @@ namespace Ryujinx.Common.Logging
Updated += target.Log; Updated += target.Log;
} }
public static void SetConsoleTargetEnabled(bool enabled)
{
if (enabled)
{
AddTarget(new AsyncLogTargetWrapper(
new ConsoleLogTarget("console"),
1000,
AsyncLogTargetOverflowAction.Discard));
}
else
{
RemoveTarget("console");
}
}
public static void RemoveTarget(string target) public static void RemoveTarget(string target)
{ {
ILogTarget logTarget = GetTarget(target); ILogTarget logTarget = GetTarget(target);
@@ -248,7 +238,6 @@ namespace Ryujinx.Common.Logging
case LogLevel.Error : Error = enabled ? new Log(LogLevel.Error) : null; break; case LogLevel.Error : Error = enabled ? new Log(LogLevel.Error) : null; break;
case LogLevel.Guest : Guest = enabled ? new Log(LogLevel.Guest) : null; break; case LogLevel.Guest : Guest = enabled ? new Log(LogLevel.Guest) : null; break;
case LogLevel.AccessLog : AccessLog = enabled ? new Log(LogLevel.AccessLog) : null; break; case LogLevel.AccessLog : AccessLog = enabled ? new Log(LogLevel.AccessLog) : null; break;
case LogLevel.NetLog : NetLog = enabled ? new Log(LogLevel.NetLog) : null; break;
case LogLevel.Stub : Stub = enabled ? new Log(LogLevel.Stub) : null; break; case LogLevel.Stub : Stub = enabled ? new Log(LogLevel.Stub) : null; break;
case LogLevel.Trace : Trace = enabled ? new Log(LogLevel.Trace) : null; break; case LogLevel.Trace : Trace = enabled ? new Log(LogLevel.Trace) : null; break;
case LogLevel.Notice : break; case LogLevel.Notice : break;

View File

@@ -0,0 +1,21 @@
using System;
using System.IO;
using System.Text;
namespace Ryujinx.Common.Logging
{
internal class TextWriterProxy : TextWriter
{
public override Encoding Encoding => Console.OutputEncoding;
public override void Write(string value)
{
if (value is null) return;
foreach (var line in value.Split(Console.Out.NewLine))
{
Logger.Info?.PrintMsg(LogClass.Application, line);
}
}
}
}

View File

@@ -29,8 +29,8 @@ namespace Ryujinx.Common
public static string GetChangelogUrl(Version currentVersion, Version newVersion) => public static string GetChangelogUrl(Version currentVersion, Version newVersion) =>
IsCanaryBuild IsCanaryBuild
? $"https://git.ryujinx.app/projects/Ryubing/compare/Canary-{currentVersion}...Canary-{newVersion}" ? $"https://git.ryujinx.app/ryubing/ryujinx/-/compare/Canary-{currentVersion}...Canary-{newVersion}"
: $"https://git.ryujinx.app/projects/Ryubing/releases/tag/{newVersion}"; : $"https://git.ryujinx.app/ryubing/ryujinx/-/releases/{newVersion}";
} }

View File

@@ -10,6 +10,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" /> <PackageReference Include="Microsoft.IO.RecyclableMemoryStream" />
<PackageReference Include="MsgPack.Cli" /> <PackageReference Include="MsgPack.Cli" />
<PackageReference Include="System.Management" />
<PackageReference Include="Humanizer" /> <PackageReference Include="Humanizer" />
<PackageReference Include="Gommon" /> <PackageReference Include="Gommon" />
</ItemGroup> </ItemGroup>

View File

@@ -8,12 +8,12 @@ namespace Ryujinx.Common
public const string AmiiboTagsUrl = "https://raw.githubusercontent.com/Ryubing/Nfc/refs/heads/main/tags.json"; public const string AmiiboTagsUrl = "https://raw.githubusercontent.com/Ryubing/Nfc/refs/heads/main/tags.json";
public const string FaqWikiUrl = "https://git.ryujinx.app/projects/Ryubing/wiki/FAQ-%26-Troubleshooting"; public const string FaqWikiUrl = "https://git.ryujinx.app/ryubing/ryujinx/-/wikis/FAQ-&-Troubleshooting";
public const string SetupGuideWikiUrl = public const string SetupGuideWikiUrl =
"https://git.ryujinx.app/projects/Ryubing/wiki/Setup-%26-Configuration-Guide"; "https://git.ryujinx.app/ryubing/ryujinx/-/wikis/Setup-&-Configuration-Guide";
public const string MultiplayerWikiUrl = public const string MultiplayerWikiUrl =
"https://git.ryujinx.app/projects/Ryubing/wiki/Multiplayer-(LDN-Local-Wireless)-Guide"; "https://git.ryujinx.app/ryubing/ryujinx/-/wikis/Multiplayer-(LDN-Local-Wireless)-Guide";
} }
} }

View File

@@ -59,7 +59,6 @@ namespace Ryujinx.Common
//Mario Franchise //Mario Franchise
"010021d00812a000", // Arcade Archives VS. SUPER MARIO BROS. "010021d00812a000", // Arcade Archives VS. SUPER MARIO BROS.
"01007fe0221d8000", // Hello, Mario!
"01006d0017f7a000", // Mario & Luigi: Brothership "01006d0017f7a000", // Mario & Luigi: Brothership
"010003000e146000", // Mario & Sonic at the Olympic Games Tokyo 2020 "010003000e146000", // Mario & Sonic at the Olympic Games Tokyo 2020
"010067300059a000", // Mario + Rabbids: Kingdom Battle "010067300059a000", // Mario + Rabbids: Kingdom Battle
@@ -71,9 +70,6 @@ namespace Ryujinx.Common
"0100bde00862a000", // Mario Tennis Aces "0100bde00862a000", // Mario Tennis Aces
"0100b99019412000", // Mario vs. Donkey Kong "0100b99019412000", // Mario vs. Donkey Kong
"010049900f546000", // Super Mario 3D All-Stars "010049900f546000", // Super Mario 3D All-Stars
"010049900f546001", // Super Mario 3D All-Stars | Super Mario 64
"010049900f546002", // Super Mario 3D All-Stars | Super Mario Sunshine
"010049900f546003", // Super Mario 3D All-Stars | Super Mario Galaxy
"010028600ebda000", // Super Mario 3D World + Bowser's Fury "010028600ebda000", // Super Mario 3D World + Bowser's Fury
"010049900F546001", // Super Mario 64 "010049900F546001", // Super Mario 64
"0100ea80032ea000", // Super Mario Bros. U Deluxe "0100ea80032ea000", // Super Mario Bros. U Deluxe
@@ -111,11 +107,6 @@ namespace Ryujinx.Common
"0100187003a36000", // Pokémon: Let's Go Eevee! "0100187003a36000", // Pokémon: Let's Go Eevee!
"010003f003a34000", // Pokémon: Let's Go Pikachu! "010003f003a34000", // Pokémon: Let's Go Pikachu!
"0100f43008c44000", // Pokémon Legends: Z-A "0100f43008c44000", // Pokémon Legends: Z-A
"0100554023408000", // Pokémon FireRed Version (EN)
"01006fa0233f8000", // Pokémon FireRed Version (JP)
"0100fd6023430000", // Pokémon LeafGreen Version (DE)
"0100f1e0233fa000", // Pokémon LeafGreen Version (JP)
"01005b7008c52000", // Pokémon Champions
//Splatoon Franchise //Splatoon Franchise
"0100f8f0000a2000", // Splatoon 2 (EU) "0100f8f0000a2000", // Splatoon 2 (EU)
@@ -125,14 +116,13 @@ namespace Ryujinx.Common
"0100ba0018500000", // Splatoon 3: Splatfest World Premiere "0100ba0018500000", // Splatoon 3: Splatfest World Premiere
//NSO Membership games //NSO Membership games
"0100d870045b6000", // NES - Nintendo Switch Online
"01008d300c50c000", // SNES - Nintendo Switch Online
"0100c62011050000", // GB - Nintendo Switch Online "0100c62011050000", // GB - Nintendo Switch Online
"010012f017576000", // GBA - Nintendo Switch Online "010012f017576000", // GBA - Nintendo Switch Online
"0100c9a00ece6000", // N64 - Nintendo Switch Online "0100c9a00ece6000", // N64 - Nintendo Switch Online
"0100e0601c632000", // N64 - Nintendo Switch Online 18+ "0100e0601c632000", // N64 - Nintendo Switch Online 18+
"0100d870045b6000", // NES - Nintendo Switch Online
"0100b3c014bda000", // SEGA Genesis - Nintendo Switch Online "0100b3c014bda000", // SEGA Genesis - Nintendo Switch Online
"0100bfc01d976000", // Virtual Boy - Nintendo Switch Online "01008d300c50c000", // SNES - Nintendo Switch Online
"0100ccf019c8c000", // F-ZERO 99 "0100ccf019c8c000", // F-ZERO 99
"0100ad9012510000", // PAC-MAN 99 "0100ad9012510000", // PAC-MAN 99
"010040600c5ce000", // Tetris 99 "010040600c5ce000", // Tetris 99
@@ -151,17 +141,12 @@ namespace Ryujinx.Common
"0100704000B3A000", // Snipperclips "0100704000B3A000", // Snipperclips
"01006a800016e000", // Super Smash Bros. Ultimate "01006a800016e000", // Super Smash Bros. Ultimate
"0100a9400c9c2000", // Tokyo Mirage Sessions #FE Encore "0100a9400c9c2000", // Tokyo Mirage Sessions #FE Encore
"0100ca502552a000", // Tomodachi Life: Living the Dream - Welcome Edition
"010051f0207b2000", // Tomodachi Life: Living the Dream
//Bayonetta Franchise //Bayonetta Franchise
"010076f0049a2000", // Bayonetta "010076f0049a2000", // Bayonetta
"01007960049a0000", // Bayonetta 2 "01007960049a0000", // Bayonetta 2
"01004a4010fea000", // Bayonetta 3 "01004a4010fea000", // Bayonetta 3
"0100cf5010fec000", // Bayonetta Origins: Cereza and the Lost Demon "0100cf5010fec000", // Bayonetta Origins: Cereza and the Lost Demon
// Famicom Detective Club Franchise
"010054601d54c000", // Emio - The Smiling Man: Famicom Detective Series (DEMO)
//Persona Franchise //Persona Franchise
"0100dcd01525a000", // Persona 3 Portable "0100dcd01525a000", // Persona 3 Portable
@@ -186,9 +171,7 @@ namespace Ryujinx.Common
"0100453019aa8000", // Xenoblade Chronicles: X Definitive Edition "0100453019aa8000", // Xenoblade Chronicles: X Definitive Edition
//Misc Games //Misc Games
"01003670066de000", // 36 Fragments of Midnight
"010056e00853a000", // A Hat in Time "010056e00853a000", // A Hat in Time
"0100c9f00aaee000", // Ascendence
"0100fd1014726000", // Baldurs Gate: Dark Alliance "0100fd1014726000", // Baldurs Gate: Dark Alliance
"01008c2019598000", // Bluey: The Video Game "01008c2019598000", // Bluey: The Video Game
"010096f00ff22000", // Borderlands 2: Game of the Year Edition "010096f00ff22000", // Borderlands 2: Game of the Year Edition
@@ -201,11 +184,8 @@ namespace Ryujinx.Common
"01001b300b9be000", // Diablo III: Eternal Collection "01001b300b9be000", // Diablo III: Eternal Collection
"010027400cdc6000", // Divinity Original 2 - Definitive Edition "010027400cdc6000", // Divinity Original 2 - Definitive Edition
"01008c8012920000", // Dying Light Platinum Edition "01008c8012920000", // Dying Light Platinum Edition
"0100d11013e6a000", // Eschatos
"01000490067ae000", // Frederic 2: Evil Strikes Back
"01001cc01b2d4000", // Goat Simulator 3 "01001cc01b2d4000", // Goat Simulator 3
"01003620068ea000", // Hand of Fate 2 "01003620068ea000", // Hand of Fate 2
"01007ac00e012000", // HEXAGRAVITY
"0100f7e00c70e000", // Hogwarts Legacy "0100f7e00c70e000", // Hogwarts Legacy
"010013c00e930000", // Hollow Knight: Silksong "010013c00e930000", // Hollow Knight: Silksong
"010085500130a000", // Lego City: Undercover "010085500130a000", // Lego City: Undercover
@@ -213,25 +193,15 @@ namespace Ryujinx.Common
"0100d71004694000", // Minecraft "0100d71004694000", // Minecraft
"01007430037f6000", // Monopoly "01007430037f6000", // Monopoly
"0100853015e86000", // No Man's Sky "0100853015e86000", // No Man's Sky
"0100f85014ed0000", // No More Heroes
"0100463014ed4000", // No More Heroes 2
"0100f7d00a1bc000", // NO THING
"0100e570094e8000", // Owlboy
"01007bb017812000", // Portal "01007bb017812000", // Portal
"0100abd01785c000", // Portal 2 "0100abd01785c000", // Portal 2
"01009f100bc52000", // Psikyo Collection 1
"01009d400c4a8000", // Psikyo Collection 2
"01008e200c5c2000", // Muse Dash "01008e200c5c2000", // Muse Dash
"01005ff002e2a000", // Rayman Legends
"01007820196a6000", // Red Dead Redemption "01007820196a6000", // Red Dead Redemption
"01007a800d520000", // REFUNCT
"0100e8300a67a000", // Risk "0100e8300a67a000", // Risk
"01002f7013224000", // Rune Factory 5 "01002f7013224000", // Rune Factory 5
"01008d100d43e000", // Saints Row IV "01008d100d43e000", // Saints Row IV
"0100de600beee000", // Saints Row: The Third - The Full Package "0100de600beee000", // Saints Row: The Third - The Full Package
"01001180021fa000", // Shovel Knight: Specter of Torment "01001180021fa000", // Shovel Knight: Specter of Torment
"010079f00671c000", // Sparkle 2: Evo
"010077b00e046000", // Spyro: Reignited Trilogy
"0100e1D01eb2e000", // Squeakross: Home Squeak Home "0100e1D01eb2e000", // Squeakross: Home Squeak Home
"0100e65002bb8000", // Stardew Valley "0100e65002bb8000", // Stardew Valley
"0100d7a01b7a2000", // Star Wars: Bounty Hunter "0100d7a01b7a2000", // Star Wars: Bounty Hunter

View File

@@ -22,11 +22,10 @@ namespace Ryujinx.Common.Utilities
} }
// "dumpable" attribute of the calling process // "dumpable" attribute of the calling process
private const int PR_GET_DUMPABLE = 3;
private const int PR_SET_DUMPABLE = 4; private const int PR_SET_DUMPABLE = 4;
[LibraryImport("libc", SetLastError = true)] [DllImport("libc", SetLastError = true)]
private static partial int prctl(int option, int arg2); private static extern int prctl(int option, int arg2);
public static void SetCoreDumpable(bool dumpable) public static void SetCoreDumpable(bool dumpable)
{ {
@@ -37,13 +36,5 @@ namespace Ryujinx.Common.Utilities
Debug.Assert(result == 0); Debug.Assert(result == 0);
} }
} }
// Use the below line to display dumpable status in the console:
// Console.WriteLine($"{OsUtils.IsCoreDumpable()}");
public static bool IsCoreDumpable()
{
int result = prctl(PR_GET_DUMPABLE, 0);
return result == 1;
}
} }
} }

View File

@@ -30,9 +30,9 @@ namespace ARMeilleure.Common
/// <summary> /// <summary>
/// Base address for the page. /// Base address for the page.
/// </summary> /// </summary>
public readonly nint Address; public readonly IntPtr Address;
public AddressTablePage(bool isSparse, nint address) public AddressTablePage(bool isSparse, IntPtr address)
{ {
IsSparse = isSparse; IsSparse = isSparse;
Address = address; Address = address;
@@ -47,20 +47,20 @@ namespace ARMeilleure.Common
public readonly SparseMemoryBlock Block; public readonly SparseMemoryBlock Block;
private readonly TrackingEventDelegate _trackingEvent; private readonly TrackingEventDelegate _trackingEvent;
public TableSparseBlock(ulong size, Action<nint> ensureMapped, PageInitDelegate pageInit) public TableSparseBlock(ulong size, Action<IntPtr> ensureMapped, PageInitDelegate pageInit)
{ {
SparseMemoryBlock block = new(size, pageInit, null); SparseMemoryBlock block = new(size, pageInit, null);
_trackingEvent = (address, size, write) => _trackingEvent = (address, size, write) =>
{ {
ulong pointer = (ulong)block.Block.Pointer + address; ulong pointer = (ulong)block.Block.Pointer + address;
ensureMapped((nint)pointer); ensureMapped((IntPtr)pointer);
return pointer; return pointer;
}; };
bool added = NativeSignalHandler.AddTrackedRegion( bool added = NativeSignalHandler.AddTrackedRegion(
(nuint)block.Block.Pointer, (nuint)block.Block.Pointer,
(nuint)(block.Block.Pointer + (nint)block.Block.Size), (nuint)(block.Block.Pointer + (IntPtr)block.Block.Size),
Marshal.GetFunctionPointerForDelegate(_trackingEvent)); Marshal.GetFunctionPointerForDelegate(_trackingEvent));
if (!added) if (!added)
@@ -116,7 +116,7 @@ namespace ARMeilleure.Common
} }
/// <inheritdoc/> /// <inheritdoc/>
public nint Base public IntPtr Base
{ {
get get
{ {
@@ -124,7 +124,7 @@ namespace ARMeilleure.Common
lock (_pages) lock (_pages)
{ {
return (nint)GetRootPage(); return (IntPtr)GetRootPage();
} }
} }
} }
@@ -240,7 +240,7 @@ namespace ARMeilleure.Common
long index = Levels[^1].GetValue(address); long index = Levels[^1].GetValue(address);
EnsureMapped((nint)(page + index)); EnsureMapped((IntPtr)(page + index));
return ref page[index]; return ref page[index];
} }
@@ -284,7 +284,7 @@ namespace ARMeilleure.Common
/// Ensure the given pointer is mapped in any overlapping sparse reservations. /// Ensure the given pointer is mapped in any overlapping sparse reservations.
/// </summary> /// </summary>
/// <param name="ptr">Pointer to be mapped</param> /// <param name="ptr">Pointer to be mapped</param>
private void EnsureMapped(nint ptr) private void EnsureMapped(IntPtr ptr)
{ {
if (Sparse) if (Sparse)
{ {
@@ -299,7 +299,7 @@ namespace ARMeilleure.Common
{ {
SparseMemoryBlock sparse = reserved.Block; SparseMemoryBlock sparse = reserved.Block;
if (ptr >= sparse.Block.Pointer && ptr < sparse.Block.Pointer + (nint)sparse.Block.Size) if (ptr >= sparse.Block.Pointer && ptr < sparse.Block.Pointer + (IntPtr)sparse.Block.Size)
{ {
sparse.EnsureMapped((ulong)(ptr - sparse.Block.Pointer)); sparse.EnsureMapped((ulong)(ptr - sparse.Block.Pointer));
@@ -319,15 +319,15 @@ namespace ARMeilleure.Common
/// </summary> /// </summary>
/// <param name="level">Level to get the fill value for</param> /// <param name="level">Level to get the fill value for</param>
/// <returns>The fill value</returns> /// <returns>The fill value</returns>
private nint GetFillValue(int level) private IntPtr GetFillValue(int level)
{ {
if (_fillBottomLevel != null && level == Levels.Length - 2) if (_fillBottomLevel != null && level == Levels.Length - 2)
{ {
return (nint)_fillBottomLevelPtr; return (IntPtr)_fillBottomLevelPtr;
} }
else else
{ {
return nint.Zero; return IntPtr.Zero;
} }
} }
@@ -379,7 +379,7 @@ namespace ARMeilleure.Common
/// <param name="fill">Fill value</param> /// <param name="fill">Fill value</param>
/// <param name="leaf"><see langword="true"/> if leaf; otherwise <see langword="false"/></param> /// <param name="leaf"><see langword="true"/> if leaf; otherwise <see langword="false"/></param>
/// <returns>Allocated block</returns> /// <returns>Allocated block</returns>
private nint Allocate<T>(int length, T fill, bool leaf) where T : unmanaged private IntPtr Allocate<T>(int length, T fill, bool leaf) where T : unmanaged
{ {
int size = sizeof(T) * length; int size = sizeof(T) * length;
@@ -405,7 +405,7 @@ namespace ARMeilleure.Common
} }
} }
page = new AddressTablePage(true, block.Block.Pointer + (nint)_sparseReservedOffset); page = new AddressTablePage(true, block.Block.Pointer + (IntPtr)_sparseReservedOffset);
_sparseReservedOffset += (ulong)size; _sparseReservedOffset += (ulong)size;
@@ -413,7 +413,7 @@ namespace ARMeilleure.Common
} }
else else
{ {
nint address = (nint)NativeAllocator.Instance.Allocate((uint)size); IntPtr address = (IntPtr)NativeAllocator.Instance.Allocate((uint)size);
page = new AddressTablePage(false, address); page = new AddressTablePage(false, address);
Span<T> span = new((void*)page.Address, length); Span<T> span = new((void*)page.Address, length);

View File

@@ -17,8 +17,7 @@ namespace Ryujinx.Cpu.LightningJit.Cache
private static readonly int _pageMask = _pageSize - 1; private static readonly int _pageMask = _pageSize - 1;
private const int CodeAlignment = 4; // Bytes. private const int CodeAlignment = 4; // Bytes.
// TODO: JIT Cache size should be application dependent, not global. private const int CacheSize = 256 * 1024 * 1024;
private const int CacheSize = 1024 * (1024 * 1024); // Megabytes * Size of Megabytes (since its in bytes).
private static JitCacheInvalidation _jitCacheInvalidator; private static JitCacheInvalidation _jitCacheInvalidator;
@@ -34,14 +33,6 @@ namespace Ryujinx.Cpu.LightningJit.Cache
[SupportedOSPlatform("windows")] [SupportedOSPlatform("windows")]
[LibraryImport("kernel32.dll", SetLastError = true)] [LibraryImport("kernel32.dll", SetLastError = true)]
public static partial nint FlushInstructionCache(nint hProcess, nint lpAddress, nuint dwSize); public static partial nint FlushInstructionCache(nint hProcess, nint lpAddress, nuint dwSize);
[SupportedOSPlatform("macos")]
[LibraryImport("libSystem.dylib", EntryPoint = "sys_icache_invalidate")]
internal static partial void SysICacheInvalidate(nint start, nuint len);
[SupportedOSPlatform("linux")]
[LibraryImport("libgcc_s.so.1", EntryPoint = "__clear_cache")]
internal static partial void ClearCache(nint begin, nint end);
public static void Initialize(IJitMemoryAllocator allocator) public static void Initialize(IJitMemoryAllocator allocator)
{ {

View File

@@ -42,7 +42,6 @@ namespace Ryujinx.Graphics.GAL
public readonly bool SupportsShaderBallot; public readonly bool SupportsShaderBallot;
public readonly bool SupportsShaderBarrierDivergence; public readonly bool SupportsShaderBarrierDivergence;
public readonly bool SupportsShaderFloat64; public readonly bool SupportsShaderFloat64;
public readonly bool SupportsShaderNonUniformIndexing;
public readonly bool SupportsTextureGatherOffsets; public readonly bool SupportsTextureGatherOffsets;
public readonly bool SupportsTextureShadowLod; public readonly bool SupportsTextureShadowLod;
public readonly bool SupportsVertexStoreAndAtomics; public readonly bool SupportsVertexStoreAndAtomics;
@@ -111,7 +110,6 @@ namespace Ryujinx.Graphics.GAL
bool supportsShaderBallot, bool supportsShaderBallot,
bool supportsShaderBarrierDivergence, bool supportsShaderBarrierDivergence,
bool supportsShaderFloat64, bool supportsShaderFloat64,
bool supportsShaderNonUniformIndexing,
bool supportsTextureGatherOffsets, bool supportsTextureGatherOffsets,
bool supportsTextureShadowLod, bool supportsTextureShadowLod,
bool supportsVertexStoreAndAtomics, bool supportsVertexStoreAndAtomics,
@@ -174,7 +172,6 @@ namespace Ryujinx.Graphics.GAL
SupportsShaderBallot = supportsShaderBallot; SupportsShaderBallot = supportsShaderBallot;
SupportsShaderBarrierDivergence = supportsShaderBarrierDivergence; SupportsShaderBarrierDivergence = supportsShaderBarrierDivergence;
SupportsShaderFloat64 = supportsShaderFloat64; SupportsShaderFloat64 = supportsShaderFloat64;
SupportsShaderNonUniformIndexing = supportsShaderNonUniformIndexing;
SupportsTextureGatherOffsets = supportsTextureGatherOffsets; SupportsTextureGatherOffsets = supportsTextureGatherOffsets;
SupportsTextureShadowLod = supportsTextureShadowLod; SupportsTextureShadowLod = supportsTextureShadowLod;
SupportsVertexStoreAndAtomics = supportsVertexStoreAndAtomics; SupportsVertexStoreAndAtomics = supportsVertexStoreAndAtomics;

View File

@@ -658,7 +658,7 @@ namespace Ryujinx.Graphics.Gpu.Image
bool canImport = Storage.Info.IsLinear && Storage.Info.Stride >= Storage.Info.Width * Storage.Info.FormatInfo.BytesPerPixel; bool canImport = Storage.Info.IsLinear && Storage.Info.Stride >= Storage.Info.Width * Storage.Info.FormatInfo.BytesPerPixel;
nint hostPointer = canImport ? _physicalMemory.GetHostPointer(Storage.Range) : 0; IntPtr hostPointer = canImport ? _physicalMemory.GetHostPointer(Storage.Range) : 0;
if (hostPointer != 0 && _context.Renderer.PrepareHostMapping(hostPointer, Storage.Size)) if (hostPointer != 0 && _context.Renderer.PrepareHostMapping(hostPointer, Storage.Size))
{ {

View File

@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2; private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
private const uint CodeGenVersion = 7354; private const uint CodeGenVersion = 7353;
private const string SharedTocFileName = "shared.toc"; private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data"; private const string SharedDataFileName = "shared.data";

View File

@@ -231,8 +231,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
public bool QueryHostSupportsShaderFloat64() => _context.Capabilities.SupportsShaderFloat64; public bool QueryHostSupportsShaderFloat64() => _context.Capabilities.SupportsShaderFloat64;
public bool QueryHostSupportsShaderNonUniformIndexing() => _context.Capabilities.SupportsShaderNonUniformIndexing;
public bool QueryHostSupportsSnormBufferTextureFormat() => _context.Capabilities.SupportsSnormBufferTextureFormat; public bool QueryHostSupportsSnormBufferTextureFormat() => _context.Capabilities.SupportsSnormBufferTextureFormat;
public bool QueryHostSupportsTextureGatherOffsets() => _context.Capabilities.SupportsTextureGatherOffsets; public bool QueryHostSupportsTextureGatherOffsets() => _context.Capabilities.SupportsTextureGatherOffsets;

View File

@@ -551,7 +551,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
level, level,
x, x,
width, width,
(InternalFormat) format.PixelFormat, format.PixelFormat,
mipSize, mipSize,
data); data);
} }
@@ -579,7 +579,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
layer, layer,
width, width,
1, 1,
(InternalFormat) format.PixelFormat, format.PixelFormat,
mipSize, mipSize,
data); data);
} }
@@ -609,7 +609,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
y, y,
width, width,
height, height,
(InternalFormat) format.PixelFormat, format.PixelFormat,
mipSize, mipSize,
data); data);
} }
@@ -643,7 +643,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
width, width,
height, height,
1, 1,
(InternalFormat) format.PixelFormat, format.PixelFormat,
mipSize, mipSize,
data); data);
} }
@@ -675,7 +675,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
y, y,
width, width,
height, height,
(InternalFormat) format.PixelFormat, format.PixelFormat,
mipSize, mipSize,
data); data);
} }
@@ -744,7 +744,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
level, level,
0, 0,
width, width,
(InternalFormat) format.PixelFormat, format.PixelFormat,
mipSize, mipSize,
data); data);
} }
@@ -773,7 +773,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
0, 0,
width, width,
height, height,
(InternalFormat) format.PixelFormat, format.PixelFormat,
mipSize, mipSize,
data); data);
} }
@@ -807,7 +807,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
width, width,
height, height,
depth, depth,
(InternalFormat) format.PixelFormat, format.PixelFormat,
mipSize, mipSize,
data); data);
} }
@@ -843,7 +843,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
0, 0,
width, width,
height, height,
(InternalFormat) format.PixelFormat, format.PixelFormat,
mipSize / 6, mipSize / 6,
data + faceOffset); data + faceOffset);
} }

View File

@@ -184,7 +184,6 @@ namespace Ryujinx.Graphics.OpenGL
supportsShaderBallot: HwCapabilities.SupportsShaderBallot, supportsShaderBallot: HwCapabilities.SupportsShaderBallot,
supportsShaderBarrierDivergence: !(intelWindows || intelUnix), supportsShaderBarrierDivergence: !(intelWindows || intelUnix),
supportsShaderFloat64: true, supportsShaderFloat64: true,
supportsShaderNonUniformIndexing: false,
supportsTextureGatherOffsets: true, supportsTextureGatherOffsets: true,
supportsTextureShadowLod: HwCapabilities.SupportsTextureShadowLod, supportsTextureShadowLod: HwCapabilities.SupportsTextureShadowLod,
supportsVertexStoreAndAtomics: true, supportsVertexStoreAndAtomics: true,

View File

@@ -27,7 +27,7 @@ namespace Ryujinx.Graphics.OpenGL
public void Map(BufferHandle handle, int size) public void Map(BufferHandle handle, int size)
{ {
GL.BindBuffer(BufferTarget.CopyWriteBuffer, handle); GL.BindBuffer(BufferTarget.CopyWriteBuffer, handle);
nint ptr = GL.MapBufferRange(BufferTarget.CopyWriteBuffer, nint.Zero, size, MapBufferAccessMask.MapReadBit | MapBufferAccessMask.MapPersistentBit); nint ptr = GL.MapBufferRange(BufferTarget.CopyWriteBuffer, nint.Zero, size, BufferAccessMask.MapReadBit | BufferAccessMask.MapPersistentBit);
_maps[handle] = ptr; _maps[handle] = ptr;
} }
@@ -75,7 +75,7 @@ namespace Ryujinx.Graphics.OpenGL
GL.BindBuffer(BufferTarget.CopyWriteBuffer, _copyBufferHandle); GL.BindBuffer(BufferTarget.CopyWriteBuffer, _copyBufferHandle);
GL.BufferStorage(BufferTarget.CopyWriteBuffer, requiredSize, nint.Zero, BufferStorageFlags.MapReadBit | BufferStorageFlags.MapPersistentBit); GL.BufferStorage(BufferTarget.CopyWriteBuffer, requiredSize, nint.Zero, BufferStorageFlags.MapReadBit | BufferStorageFlags.MapPersistentBit);
_bufferMap = GL.MapBufferRange(BufferTarget.CopyWriteBuffer, nint.Zero, requiredSize, MapBufferAccessMask.MapReadBit | MapBufferAccessMask.MapPersistentBit); _bufferMap = GL.MapBufferRange(BufferTarget.CopyWriteBuffer, nint.Zero, requiredSize, BufferAccessMask.MapReadBit | BufferAccessMask.MapPersistentBit);
} }
} }

View File

@@ -924,8 +924,8 @@ namespace Ryujinx.Graphics.OpenGL
GL.Disable(EnableCap.CullFace); GL.Disable(EnableCap.CullFace);
return; return;
} }
GL.CullFace((TriangleFace) face.Convert()); GL.CullFace(face.Convert());
GL.Enable(EnableCap.CullFace); GL.Enable(EnableCap.CullFace);
} }
@@ -1085,12 +1085,12 @@ namespace Ryujinx.Graphics.OpenGL
{ {
if (frontMode == backMode) if (frontMode == backMode)
{ {
GL.PolygonMode((TriangleFace) MaterialFace.FrontAndBack, frontMode.Convert()); GL.PolygonMode(MaterialFace.FrontAndBack, frontMode.Convert());
} }
else else
{ {
GL.PolygonMode((TriangleFace) MaterialFace.Front, frontMode.Convert()); GL.PolygonMode(MaterialFace.Front, frontMode.Convert());
GL.PolygonMode((TriangleFace) MaterialFace.Back, backMode.Convert()); GL.PolygonMode(MaterialFace.Back, backMode.Convert());
} }
} }

View File

@@ -59,7 +59,7 @@ namespace Ryujinx.Graphics.OpenGL
GL.CompileShader(shaderHandle); GL.CompileShader(shaderHandle);
break; break;
case TargetLanguage.Spirv: case TargetLanguage.Spirv:
GL.ShaderBinary(1, ref shaderHandle, ShaderBinaryFormat.ShaderBinaryFormatSpirV, shader.BinaryCode, shader.BinaryCode.Length); GL.ShaderBinary(1, ref shaderHandle, (BinaryFormat)All.ShaderBinaryFormatSpirVArb, shader.BinaryCode, shader.BinaryCode.Length);
GL.SpecializeShader(shaderHandle, "main", 0, (int[])null, (int[])null); GL.SpecializeShader(shaderHandle, "main", 0, (int[])null, (int[])null);
break; break;
} }

View File

@@ -32,7 +32,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries
GL.BufferStorage(BufferTarget.QueryBuffer, sizeof(long), (nint)(&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, nint.Zero, sizeof(long), MapBufferAccessMask.MapReadBit | MapBufferAccessMask.MapWriteBit | MapBufferAccessMask.MapPersistentBit); _bufferMap = GL.MapBufferRange(BufferTarget.QueryBuffer, nint.Zero, sizeof(long), BufferAccessMask.MapReadBit | BufferAccessMask.MapWriteBit | BufferAccessMask.MapPersistentBit);
} }
public void Reset() public void Reset()

View File

@@ -82,7 +82,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
public bool IsMainFunction { get; private set; } public bool IsMainFunction { get; private set; }
public bool MayHaveReturned { get; set; } public bool MayHaveReturned { get; set; }
public bool WasNonUniformAccessDeclared { get; set; }
public CodeGenContext( public CodeGenContext(
StructuredProgramInfo info, StructuredProgramInfo info,
@@ -90,8 +89,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
GeneratorPool<Instruction> instPool, GeneratorPool<Instruction> instPool,
GeneratorPool<LiteralInteger> integerPool) : base(SpirvVersionPacked, instPool, integerPool) GeneratorPool<LiteralInteger> integerPool) : base(SpirvVersionPacked, instPool, integerPool)
{ {
WasNonUniformAccessDeclared = false;
Info = info; Info = info;
AttributeUsage = parameters.AttributeUsage; AttributeUsage = parameters.AttributeUsage;
Definitions = parameters.Definitions; Definitions = parameters.Definitions;

View File

@@ -587,23 +587,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
return OperationResult.Invalid; return OperationResult.Invalid;
} }
private static void MarkNonUniform(CodeGenContext context, SpvInstruction inst)
{
if (context.HostCapabilities.SupportsShaderNonUniformIndexing)
{
if (!context.WasNonUniformAccessDeclared)
{
context.AddExtension("SPV_EXT_descriptor_indexing");
context.AddCapability(Capability.ShaderNonUniform);
context.AddCapability(Capability.SampledImageArrayNonUniformIndexing);
context.AddCapability(Capability.StorageImageArrayNonUniformIndexing);
}
context.Decorate(inst, Decoration.NonUniform);
context.WasNonUniformAccessDeclared = true;
}
}
private static OperationResult GenerateImageAtomic(CodeGenContext context, AstOperation operation) private static OperationResult GenerateImageAtomic(CodeGenContext context, AstOperation operation)
{ {
AstTextureOperation texOp = (AstTextureOperation)operation; AstTextureOperation texOp = (AstTextureOperation)operation;
@@ -630,7 +613,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
SpvInstruction textureIndex = Src(AggregateType.S32); SpvInstruction textureIndex = Src(AggregateType.S32);
image = context.AccessChain(imagePointerType, image, textureIndex); image = context.AccessChain(imagePointerType, image, textureIndex);
MarkNonUniform(context, image);
} }
int coordsCount = texOp.Type.Dimensions; int coordsCount = texOp.Type.Dimensions;
@@ -701,21 +683,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
ImageDeclaration declaration = context.Images[texOp.GetTextureSetAndBinding()]; ImageDeclaration declaration = context.Images[texOp.GetTextureSetAndBinding()];
SpvInstruction image = declaration.Image; SpvInstruction image = declaration.Image;
bool isIndexed = declaration.IsIndexed;
if (isIndexed) if (declaration.IsIndexed)
{ {
SpvInstruction textureIndex = Src(AggregateType.S32); SpvInstruction textureIndex = Src(AggregateType.S32);
image = context.AccessChain(declaration.ImagePointerType, image, textureIndex); image = context.AccessChain(declaration.ImagePointerType, image, textureIndex);
MarkNonUniform(context, image);
} }
image = context.Load(declaration.ImageType, image); image = context.Load(declaration.ImageType, image);
if (isIndexed)
{
MarkNonUniform(context, image);
}
int coordsCount = texOp.Type.Dimensions; int coordsCount = texOp.Type.Dimensions;
@@ -764,21 +740,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
ImageDeclaration declaration = context.Images[texOp.GetTextureSetAndBinding()]; ImageDeclaration declaration = context.Images[texOp.GetTextureSetAndBinding()];
SpvInstruction image = declaration.Image; SpvInstruction image = declaration.Image;
bool isIndexed = declaration.IsIndexed;
if (isIndexed) if (declaration.IsIndexed)
{ {
SpvInstruction textureIndex = Src(AggregateType.S32); SpvInstruction textureIndex = Src(AggregateType.S32);
image = context.AccessChain(declaration.ImagePointerType, image, textureIndex); image = context.AccessChain(declaration.ImagePointerType, image, textureIndex);
MarkNonUniform(context, image);
} }
image = context.Load(declaration.ImageType, image); image = context.Load(declaration.ImageType, image);
if (isIndexed)
{
MarkNonUniform(context, image);
}
int coordsCount = texOp.Type.Dimensions; int coordsCount = texOp.Type.Dimensions;
@@ -1908,56 +1878,35 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
private static SpvInstruction GenerateSampledImageLoad(CodeGenContext context, AstTextureOperation texOp, SamplerDeclaration declaration, ref int srcIndex) private static SpvInstruction GenerateSampledImageLoad(CodeGenContext context, AstTextureOperation texOp, SamplerDeclaration declaration, ref int srcIndex)
{ {
SpvInstruction image = declaration.Image; SpvInstruction image = declaration.Image;
bool imageIndexed = declaration.IsIndexed;
if (imageIndexed) if (declaration.IsIndexed)
{ {
SpvInstruction textureIndex = context.Get(AggregateType.S32, texOp.GetSource(srcIndex++)); SpvInstruction textureIndex = context.Get(AggregateType.S32, texOp.GetSource(srcIndex++));
image = context.AccessChain(declaration.SampledImagePointerType, image, textureIndex); image = context.AccessChain(declaration.SampledImagePointerType, image, textureIndex);
MarkNonUniform(context, image);
} }
if (texOp.IsSeparate) if (texOp.IsSeparate)
{ {
image = context.Load(declaration.ImageType, image); image = context.Load(declaration.ImageType, image);
if (imageIndexed)
{
MarkNonUniform(context, image);
}
SamplerDeclaration samplerDeclaration = context.Samplers[texOp.GetSamplerSetAndBinding()]; SamplerDeclaration samplerDeclaration = context.Samplers[texOp.GetSamplerSetAndBinding()];
SpvInstruction sampler = samplerDeclaration.Image; SpvInstruction sampler = samplerDeclaration.Image;
bool samplerIndexed = samplerDeclaration.IsIndexed;
if (samplerIndexed) if (samplerDeclaration.IsIndexed)
{ {
SpvInstruction samplerIndex = context.Get(AggregateType.S32, texOp.GetSource(srcIndex++)); SpvInstruction samplerIndex = context.Get(AggregateType.S32, texOp.GetSource(srcIndex++));
sampler = context.AccessChain(samplerDeclaration.SampledImagePointerType, sampler, samplerIndex); sampler = context.AccessChain(samplerDeclaration.SampledImagePointerType, sampler, samplerIndex);
MarkNonUniform(context, sampler);
} }
sampler = context.Load(samplerDeclaration.ImageType, sampler); sampler = context.Load(samplerDeclaration.ImageType, sampler);
if (samplerIndexed)
{
MarkNonUniform(context, sampler);
}
image = context.SampledImage(declaration.SampledImageType, image, sampler); image = context.SampledImage(declaration.SampledImageType, image, sampler);
if (imageIndexed || samplerIndexed)
{
MarkNonUniform(context, image);
}
} }
else else
{ {
image = context.Load(declaration.SampledImageType, image); image = context.Load(declaration.SampledImageType, image);
if (imageIndexed)
{
MarkNonUniform(context, image);
}
} }
return image; return image;

View File

@@ -336,10 +336,6 @@ namespace Ryujinx.Graphics.Shader
{ {
return true; return true;
} }
bool QueryHostSupportsShaderNonUniformIndexing()
{
return false;
}
/// <summary> /// <summary>
/// Queries host GPU support for signed normalized buffer texture formats. /// Queries host GPU support for signed normalized buffer texture formats.

View File

@@ -9,7 +9,6 @@ namespace Ryujinx.Graphics.Shader.Translation
public readonly bool SupportsShaderBallot; public readonly bool SupportsShaderBallot;
public readonly bool SupportsShaderBarrierDivergence; public readonly bool SupportsShaderBarrierDivergence;
public readonly bool SupportsShaderFloat64; public readonly bool SupportsShaderFloat64;
public readonly bool SupportsShaderNonUniformIndexing;
public readonly bool SupportsTextureShadowLod; public readonly bool SupportsTextureShadowLod;
public readonly bool SupportsViewportMask; public readonly bool SupportsViewportMask;
@@ -21,7 +20,6 @@ namespace Ryujinx.Graphics.Shader.Translation
bool supportsShaderBallot, bool supportsShaderBallot,
bool supportsShaderBarrierDivergence, bool supportsShaderBarrierDivergence,
bool supportsShaderFloat64, bool supportsShaderFloat64,
bool supportsShaderNonUniformIndexing,
bool supportsTextureShadowLod, bool supportsTextureShadowLod,
bool supportsViewportMask) bool supportsViewportMask)
{ {
@@ -32,7 +30,6 @@ namespace Ryujinx.Graphics.Shader.Translation
SupportsShaderBallot = supportsShaderBallot; SupportsShaderBallot = supportsShaderBallot;
SupportsShaderBarrierDivergence = supportsShaderBarrierDivergence; SupportsShaderBarrierDivergence = supportsShaderBarrierDivergence;
SupportsShaderFloat64 = supportsShaderFloat64; SupportsShaderFloat64 = supportsShaderFloat64;
SupportsShaderNonUniformIndexing = supportsShaderNonUniformIndexing;
SupportsTextureShadowLod = supportsTextureShadowLod; SupportsTextureShadowLod = supportsTextureShadowLod;
SupportsViewportMask = supportsViewportMask; SupportsViewportMask = supportsViewportMask;
} }

View File

@@ -364,7 +364,6 @@ namespace Ryujinx.Graphics.Shader.Translation
GpuAccessor.QueryHostSupportsShaderBallot(), GpuAccessor.QueryHostSupportsShaderBallot(),
GpuAccessor.QueryHostSupportsShaderBarrierDivergence(), GpuAccessor.QueryHostSupportsShaderBarrierDivergence(),
GpuAccessor.QueryHostSupportsShaderFloat64(), GpuAccessor.QueryHostSupportsShaderFloat64(),
GpuAccessor.QueryHostSupportsShaderNonUniformIndexing(),
GpuAccessor.QueryHostSupportsTextureShadowLod(), GpuAccessor.QueryHostSupportsTextureShadowLod(),
GpuAccessor.QueryHostSupportsViewportMask()); GpuAccessor.QueryHostSupportsViewportMask());

View File

@@ -1,4 +1,3 @@
using Ryujinx.Common.Logging;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Threading; using System.Threading;
@@ -115,7 +114,7 @@ namespace Ryujinx.Graphics.Vulkan
cbs.AddDependant(this); cbs.AddDependant(this);
// We need to add a dependency on the command buffer to all objects this object // We need to add a dependency on the command buffer to all objects this object
// references as well. // references aswell.
if (_referencedObjs != null) if (_referencedObjs != null)
{ {
for (int i = 0; i < _referencedObjs.Length; i++) for (int i = 0; i < _referencedObjs.Length; i++)
@@ -176,7 +175,7 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
} }
Debug.Assert(_referenceCount >= 0); Debug.Assert(_referenceCount >= 0);
} }

View File

@@ -46,14 +46,7 @@ namespace Ryujinx.Graphics.Vulkan
public static (AccessFlags Access, PipelineStageFlags Stages) GetSubpassAccessSuperset(VulkanRenderer gd) public static (AccessFlags Access, PipelineStageFlags Stages) GetSubpassAccessSuperset(VulkanRenderer gd)
{ {
AccessFlags access = BufferAccess | AccessFlags access = BufferAccess;
AccessFlags.ShaderReadBit |
AccessFlags.ShaderWriteBit |
AccessFlags.ColorAttachmentReadBit |
AccessFlags.ColorAttachmentWriteBit |
AccessFlags.DepthStencilAttachmentReadBit |
AccessFlags.DepthStencilAttachmentWriteBit;
PipelineStageFlags stages = PipelineStageFlags.AllGraphicsBit; PipelineStageFlags stages = PipelineStageFlags.AllGraphicsBit;
if (gd.TransformFeedbackApi != null) if (gd.TransformFeedbackApi != null)

View File

@@ -19,7 +19,7 @@ namespace Ryujinx.Graphics.Vulkan.MoltenVK
public static void Initialize() public static void Initialize()
{ {
nint configSize = (nint)Marshal.SizeOf<MVKConfiguration>(); IntPtr configSize = (nint)Marshal.SizeOf<MVKConfiguration>();
vkGetMoltenVKConfigurationMVK(nint.Zero, out MVKConfiguration config, configSize); vkGetMoltenVKConfigurationMVK(nint.Zero, out MVKConfiguration config, configSize);

View File

@@ -15,8 +15,6 @@ namespace Ryujinx.Graphics.Vulkan
Image dstImage, Image dstImage,
TextureCreateInfo srcInfo, TextureCreateInfo srcInfo,
TextureCreateInfo dstInfo, TextureCreateInfo dstInfo,
TextureCreateInfo srcStorageInfo,
TextureCreateInfo dstStorageInfo,
Extents2D srcRegion, Extents2D srcRegion,
Extents2D dstRegion, Extents2D dstRegion,
int srcLayer, int srcLayer,
@@ -42,13 +40,6 @@ namespace Ryujinx.Graphics.Vulkan
return (xy1, xy2); return (xy1, xy2);
} }
static (Offset3D, Offset3D) ClampOffsetsToMip(Offset3D xy1, Offset3D xy2, int mipW, int mipH)
{
return (
new Offset3D(Math.Min(xy1.X, mipW), Math.Min(xy1.Y, mipH), xy1.Z),
new Offset3D(Math.Min(xy2.X, mipW), Math.Min(xy2.Y, mipH), xy2.Z));
}
if (srcAspectFlags == 0) if (srcAspectFlags == 0)
{ {
srcAspectFlags = srcInfo.Format.ConvertAspectFlags(); srcAspectFlags = srcInfo.Format.ConvertAspectFlags();
@@ -89,14 +80,6 @@ namespace Ryujinx.Graphics.Vulkan
(srcOffsets.Element0, srcOffsets.Element1) = ExtentsToOffset3D(srcRegion, srcInfo.Width, srcInfo.Height, level); (srcOffsets.Element0, srcOffsets.Element1) = ExtentsToOffset3D(srcRegion, srcInfo.Width, srcInfo.Height, level);
(dstOffsets.Element0, dstOffsets.Element1) = ExtentsToOffset3D(dstRegion, dstInfo.Width, dstInfo.Height, level); (dstOffsets.Element0, dstOffsets.Element1) = ExtentsToOffset3D(dstRegion, dstInfo.Width, dstInfo.Height, level);
int srcMipW = Math.Max(1, srcStorageInfo.Width >> (int)copySrcLevel);
int srcMipH = Math.Max(1, srcStorageInfo.Height >> (int)copySrcLevel);
int dstMipW = Math.Max(1, dstStorageInfo.Width >> (int)copyDstLevel);
int dstMipH = Math.Max(1, dstStorageInfo.Height >> (int)copyDstLevel);
(srcOffsets.Element0, srcOffsets.Element1) = ClampOffsetsToMip(srcOffsets.Element0, srcOffsets.Element1, srcMipW, srcMipH);
(dstOffsets.Element0, dstOffsets.Element1) = ClampOffsetsToMip(dstOffsets.Element0, dstOffsets.Element1, dstMipW, dstMipH);
ImageBlit region = new() ImageBlit region = new()
{ {
SrcSubresource = srcSl, SrcSubresource = srcSl,
@@ -138,8 +121,6 @@ namespace Ryujinx.Graphics.Vulkan
Image dstImage, Image dstImage,
TextureCreateInfo srcInfo, TextureCreateInfo srcInfo,
TextureCreateInfo dstInfo, TextureCreateInfo dstInfo,
TextureCreateInfo srcStorageInfo,
TextureCreateInfo dstStorageInfo,
int srcViewLayer, int srcViewLayer,
int dstViewLayer, int dstViewLayer,
int srcViewLevel, int srcViewLevel,
@@ -170,8 +151,6 @@ namespace Ryujinx.Graphics.Vulkan
dstImage, dstImage,
srcInfo, srcInfo,
dstInfo, dstInfo,
srcStorageInfo,
dstStorageInfo,
srcViewLayer, srcViewLayer,
dstViewLayer, dstViewLayer,
srcViewLevel, srcViewLevel,
@@ -207,8 +186,6 @@ namespace Ryujinx.Graphics.Vulkan
Image dstImage, Image dstImage,
TextureCreateInfo srcInfo, TextureCreateInfo srcInfo,
TextureCreateInfo dstInfo, TextureCreateInfo dstInfo,
TextureCreateInfo srcStorageInfo,
TextureCreateInfo dstStorageInfo,
int srcViewLayer, int srcViewLayer,
int dstViewLayer, int dstViewLayer,
int srcViewLevel, int srcViewLevel,
@@ -337,14 +314,6 @@ namespace Ryujinx.Graphics.Vulkan
int copyWidth = sizeInBlocks ? BitUtils.DivRoundUp(width, blockWidth) : width; int copyWidth = sizeInBlocks ? BitUtils.DivRoundUp(width, blockWidth) : width;
int copyHeight = sizeInBlocks ? BitUtils.DivRoundUp(height, blockHeight) : height; int copyHeight = sizeInBlocks ? BitUtils.DivRoundUp(height, blockHeight) : height;
int srcMipW = Math.Max(1, srcStorageInfo.Width >> (srcViewLevel + srcLevel + level));
int srcMipH = Math.Max(1, srcStorageInfo.Height >> (srcViewLevel + srcLevel + level));
int dstMipW = Math.Max(1, dstStorageInfo.Width >> (dstViewLevel + dstLevel + level));
int dstMipH = Math.Max(1, dstStorageInfo.Height >> (dstViewLevel + dstLevel + level));
copyWidth = Math.Min(copyWidth, Math.Min(srcMipW, dstMipW));
copyHeight = Math.Min(copyHeight, Math.Min(srcMipH, dstMipH));
Extent3D extent = new((uint)copyWidth, (uint)copyHeight, (uint)srcDepth); Extent3D extent = new((uint)copyWidth, (uint)copyHeight, (uint)srcDepth);
if (srcInfo.Samples > 1 && srcInfo.Samples != dstInfo.Samples) if (srcInfo.Samples > 1 && srcInfo.Samples != dstInfo.Samples)

View File

@@ -67,8 +67,6 @@ namespace Ryujinx.Graphics.Vulkan
public VkFormat VkFormat { get; } public VkFormat VkFormat { get; }
public ImageUsageFlags UsageFlags { get; }
public unsafe TextureStorage( public unsafe TextureStorage(
VulkanRenderer gd, VulkanRenderer gd,
Device device, Device device,
@@ -95,8 +93,7 @@ namespace Ryujinx.Graphics.Vulkan
SampleCountFlags sampleCountFlags = ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)info.Samples); SampleCountFlags sampleCountFlags = ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)info.Samples);
ImageUsageFlags usage = GetImageUsage(info.Format, gd.Capabilities, isMsImageStorageSupported); ImageUsageFlags usage = GetImageUsage(info.Format, gd.Capabilities, isMsImageStorageSupported, true);
UsageFlags = usage;
ImageCreateFlags flags = ImageCreateFlags.CreateMutableFormatBit | ImageCreateFlags.CreateExtendedUsageBit; ImageCreateFlags flags = ImageCreateFlags.CreateMutableFormatBit | ImageCreateFlags.CreateExtendedUsageBit;
@@ -162,7 +159,7 @@ namespace Ryujinx.Graphics.Vulkan
_imageAuto = new Auto<DisposableImage>(new DisposableImage(_gd.Api, device, _image)); _imageAuto = new Auto<DisposableImage>(new DisposableImage(_gd.Api, device, _image));
InitialTransition(ImageLayout.Undefined, ImageLayout.General); InitialTransition(ImageLayout.Preinitialized, ImageLayout.General);
} }
_slices = new TextureSliceInfo[levels * _depthOrLayers]; _slices = new TextureSliceInfo[levels * _depthOrLayers];
@@ -310,7 +307,7 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
public static ImageUsageFlags GetImageUsage(Format format, in HardwareCapabilities capabilities, bool isMsImageStorageSupported) public static ImageUsageFlags GetImageUsage(Format format, in HardwareCapabilities capabilities, bool isMsImageStorageSupported, bool extendedUsage)
{ {
ImageUsageFlags usage = DefaultUsageFlags; ImageUsageFlags usage = DefaultUsageFlags;
@@ -323,7 +320,7 @@ namespace Ryujinx.Graphics.Vulkan
usage |= ImageUsageFlags.ColorAttachmentBit; usage |= ImageUsageFlags.ColorAttachmentBit;
} }
if (format.IsImageCompatible && isMsImageStorageSupported) if ((format.IsImageCompatible && isMsImageStorageSupported) || extendedUsage)
{ {
usage |= ImageUsageFlags.StorageBit; usage |= ImageUsageFlags.StorageBit;
} }

View File

@@ -64,7 +64,7 @@ namespace Ryujinx.Graphics.Vulkan
bool isMsImageStorageSupported = gd.Capabilities.SupportsShaderStorageImageMultisample || !info.Target.IsMultisample; bool isMsImageStorageSupported = gd.Capabilities.SupportsShaderStorageImageMultisample || !info.Target.IsMultisample;
VkFormat format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format, isMsImageStorageSupported); VkFormat format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format, isMsImageStorageSupported);
ImageUsageFlags usage = TextureStorage.GetImageUsage(info.Format, gd.Capabilities, isMsImageStorageSupported) & storage.UsageFlags; ImageUsageFlags usage = TextureStorage.GetImageUsage(info.Format, gd.Capabilities, isMsImageStorageSupported, false);
uint levels = (uint)info.Levels; uint levels = (uint)info.Levels;
uint layers = (uint)info.GetLayers(); uint layers = (uint)info.GetLayers();
@@ -133,8 +133,6 @@ namespace Ryujinx.Graphics.Vulkan
shaderUsage |= ImageUsageFlags.StorageBit; shaderUsage |= ImageUsageFlags.StorageBit;
} }
shaderUsage &= storage.UsageFlags;
_imageView = CreateImageView(componentMapping, subresourceRange, type, shaderUsage); _imageView = CreateImageView(componentMapping, subresourceRange, type, shaderUsage);
// Framebuffer attachments and storage images requires a identity component mapping. // Framebuffer attachments and storage images requires a identity component mapping.
@@ -259,8 +257,6 @@ namespace Ryujinx.Graphics.Vulkan
dstImage, dstImage,
src.Info, src.Info,
dst.Info, dst.Info,
src.Storage.Info,
dst.Storage.Info,
src.FirstLayer, src.FirstLayer,
dst.FirstLayer, dst.FirstLayer,
src.FirstLevel, src.FirstLevel,
@@ -314,8 +310,6 @@ namespace Ryujinx.Graphics.Vulkan
dstImage, dstImage,
src.Info, src.Info,
dst.Info, dst.Info,
src.Storage.Info,
dst.Storage.Info,
src.FirstLayer, src.FirstLayer,
dst.FirstLayer, dst.FirstLayer,
src.FirstLevel, src.FirstLevel,
@@ -391,8 +385,6 @@ namespace Ryujinx.Graphics.Vulkan
dst.GetImage().Get(cbs).Value, dst.GetImage().Get(cbs).Value,
src.Info, src.Info,
dst.Info, dst.Info,
src.Storage.Info,
dst.Storage.Info,
src.FirstLayer, src.FirstLayer,
dst.FirstLayer, dst.FirstLayer,
src.FirstLevel, src.FirstLevel,
@@ -418,8 +410,6 @@ namespace Ryujinx.Graphics.Vulkan
dst.GetImage().Get(cbs).Value, dst.GetImage().Get(cbs).Value,
src.Info, src.Info,
dst.Info, dst.Info,
src.Storage.Info,
dst.Storage.Info,
srcRegion, srcRegion,
dstRegion, dstRegion,
src.FirstLayer, src.FirstLayer,
@@ -473,8 +463,6 @@ namespace Ryujinx.Graphics.Vulkan
dstImage.Get(cbs).Value, dstImage.Get(cbs).Value,
src.Info, src.Info,
dst.Info, dst.Info,
src.Storage.Info,
dst.Storage.Info,
srcRegion, srcRegion,
dstRegion, dstRegion,
src.FirstLayer, src.FirstLayer,

View File

@@ -68,7 +68,9 @@ namespace Ryujinx.Graphics.Vulkan
int stride = (_stride + (alignment - 1)) & -alignment; int stride = (_stride + (alignment - 1)) & -alignment;
int newSize = (_size / _stride) * stride; int newSize = (_size / _stride) * stride;
updater.BindVertexBuffer(cbs, binding, autoBuffer, 0, newSize, (ulong)stride); Buffer buffer = autoBuffer.Get(cbs, 0, newSize).Value;
updater.BindVertexBuffer(cbs, binding, buffer, 0, (ulong)newSize, (ulong)stride);
_buffer = autoBuffer; _buffer = autoBuffer;
@@ -91,7 +93,11 @@ namespace Ryujinx.Graphics.Vulkan
if (autoBuffer != null) if (autoBuffer != null)
{ {
updater.BindVertexBuffer(cbs, binding, autoBuffer, _offset, _size, (ulong)_stride); int offset = _offset;
bool mirrorable = _size <= VertexBufferMaxMirrorable;
Buffer buffer = mirrorable ? autoBuffer.GetMirrorable(cbs, ref offset, _size, out _).Value : autoBuffer.Get(cbs, offset, _size).Value;
updater.BindVertexBuffer(cbs, binding, buffer, (ulong)offset, (ulong)_size, (ulong)_stride);
} }
} }

View File

@@ -15,10 +15,6 @@ namespace Ryujinx.Graphics.Vulkan
private readonly NativeArray<ulong> _sizes; private readonly NativeArray<ulong> _sizes;
private readonly NativeArray<ulong> _strides; private readonly NativeArray<ulong> _strides;
private readonly Auto<DisposableBuffer>[] _bufferAutos;
private readonly int[] _bufferOffsetsForGet;
private readonly int[] _bufferSizesForGet;
public VertexBufferUpdater(VulkanRenderer gd) public VertexBufferUpdater(VulkanRenderer gd)
{ {
_gd = gd; _gd = gd;
@@ -27,13 +23,9 @@ namespace Ryujinx.Graphics.Vulkan
_offsets = new NativeArray<ulong>(Constants.MaxVertexBuffers); _offsets = new NativeArray<ulong>(Constants.MaxVertexBuffers);
_sizes = new NativeArray<ulong>(Constants.MaxVertexBuffers); _sizes = new NativeArray<ulong>(Constants.MaxVertexBuffers);
_strides = new NativeArray<ulong>(Constants.MaxVertexBuffers); _strides = new NativeArray<ulong>(Constants.MaxVertexBuffers);
_bufferAutos = new Auto<DisposableBuffer>[Constants.MaxVertexBuffers];
_bufferOffsetsForGet = new int[Constants.MaxVertexBuffers];
_bufferSizesForGet = new int[Constants.MaxVertexBuffers];
} }
public void BindVertexBuffer(CommandBufferScoped cbs, uint binding, Auto<DisposableBuffer> autoBuffer, int offset, int size, ulong stride) public void BindVertexBuffer(CommandBufferScoped cbs, uint binding, VkBuffer buffer, ulong offset, ulong size, ulong stride)
{ {
if (_count == 0) if (_count == 0)
{ {
@@ -47,11 +39,9 @@ namespace Ryujinx.Graphics.Vulkan
int index = (int)_count; int index = (int)_count;
_bufferAutos[index] = autoBuffer; _buffers[index] = buffer;
_bufferOffsetsForGet[index] = offset; _offsets[index] = offset;
_bufferSizesForGet[index] = size; _sizes[index] = size;
_offsets[index] = (ulong)offset;
_sizes[index] = (ulong)size;
_strides[index] = stride; _strides[index] = stride;
_count++; _count++;
@@ -61,12 +51,6 @@ namespace Ryujinx.Graphics.Vulkan
{ {
if (_count != 0) if (_count != 0)
{ {
for (int i = 0; i < _count; i++)
{
_buffers[i] = _bufferAutos[i].Get(cbs, _bufferOffsetsForGet[i], _bufferSizesForGet[i]).Value;
_bufferAutos[i] = null;
}
if (_gd.Capabilities.SupportsExtendedDynamicState) if (_gd.Capabilities.SupportsExtendedDynamicState)
{ {
_gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2( _gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2(

View File

@@ -86,7 +86,7 @@ namespace Ryujinx.Graphics.Vulkan
enabledExtensions = enabledExtensions.Append(ExtDebugUtils.ExtensionName).ToArray(); enabledExtensions = enabledExtensions.Append(ExtDebugUtils.ExtensionName).ToArray();
} }
nint appName = Marshal.StringToHGlobalAnsi(AppName); IntPtr appName = Marshal.StringToHGlobalAnsi(AppName);
ApplicationInfo applicationInfo = new() ApplicationInfo applicationInfo = new()
{ {
@@ -166,7 +166,7 @@ namespace Ryujinx.Graphics.Vulkan
internal static DeviceInfo[] GetSuitablePhysicalDevices(Vk api) internal static DeviceInfo[] GetSuitablePhysicalDevices(Vk api)
{ {
nint appName = Marshal.StringToHGlobalAnsi(AppName); IntPtr appName = Marshal.StringToHGlobalAnsi(AppName);
ApplicationInfo applicationInfo = new() ApplicationInfo applicationInfo = new()
{ {
@@ -494,8 +494,6 @@ namespace Ryujinx.Graphics.Vulkan
UniformBufferStandardLayout = supportedPhysicalDeviceVulkan12Features.UniformBufferStandardLayout, UniformBufferStandardLayout = supportedPhysicalDeviceVulkan12Features.UniformBufferStandardLayout,
UniformAndStorageBuffer8BitAccess = supportedPhysicalDeviceVulkan12Features.UniformAndStorageBuffer8BitAccess, UniformAndStorageBuffer8BitAccess = supportedPhysicalDeviceVulkan12Features.UniformAndStorageBuffer8BitAccess,
StorageBuffer8BitAccess = supportedPhysicalDeviceVulkan12Features.StorageBuffer8BitAccess, StorageBuffer8BitAccess = supportedPhysicalDeviceVulkan12Features.StorageBuffer8BitAccess,
ShaderSampledImageArrayNonUniformIndexing = supportedPhysicalDeviceVulkan12Features.ShaderSampledImageArrayNonUniformIndexing,
ShaderStorageImageArrayNonUniformIndexing = supportedPhysicalDeviceVulkan12Features.ShaderStorageImageArrayNonUniformIndexing,
}; };
pExtendedFeatures = &featuresVk12; pExtendedFeatures = &featuresVk12;

View File

@@ -775,9 +775,6 @@ namespace Ryujinx.Graphics.Vulkan
supportsShaderBallot: false, supportsShaderBallot: false,
supportsShaderBarrierDivergence: Vendor != Vendor.Intel, supportsShaderBarrierDivergence: Vendor != Vendor.Intel,
supportsShaderFloat64: Capabilities.SupportsShaderFloat64, supportsShaderFloat64: Capabilities.SupportsShaderFloat64,
supportsShaderNonUniformIndexing:
featuresVk12.ShaderSampledImageArrayNonUniformIndexing &&
featuresVk12.ShaderStorageImageArrayNonUniformIndexing,
supportsTextureGatherOffsets: features2.Features.ShaderImageGatherExtended && !IsMoltenVk, supportsTextureGatherOffsets: features2.Features.ShaderImageGatherExtended && !IsMoltenVk,
supportsTextureShadowLod: false, supportsTextureShadowLod: false,
supportsVertexStoreAndAtomics: features2.Features.VertexPipelineStoresAndAtomics, supportsVertexStoreAndAtomics: features2.Features.VertexPipelineStoresAndAtomics,

View File

@@ -391,12 +391,12 @@ namespace Ryujinx.Graphics.Vulkan
{ {
if (_effect != null) if (_effect != null)
{ {
_gd.FlushAllCommands();
_gd.CommandBufferPool.Return( _gd.CommandBufferPool.Return(
cbs, cbs,
null, null,
[PipelineStageFlags.ColorAttachmentOutputBit], [PipelineStageFlags.ColorAttachmentOutputBit],
null); null);
_gd.FlushAllCommands();
cbs.GetFence().Wait(); cbs.GetFence().Wait();
cbs = _gd.CommandBufferPool.Rent(); cbs = _gd.CommandBufferPool.Rent();
} }
@@ -455,8 +455,6 @@ namespace Ryujinx.Graphics.Vulkan
ImageLayout.General, ImageLayout.General,
ImageLayout.PresentSrcKhr); ImageLayout.PresentSrcKhr);
_gd.FlushAllCommands();
_gd.CommandBufferPool.Return( _gd.CommandBufferPool.Return(
cbs, cbs,
[_imageAvailableSemaphores[semaphoreIndex]], [_imageAvailableSemaphores[semaphoreIndex]],

View File

@@ -488,8 +488,6 @@ namespace Ryujinx.HLE.FileSystem
if (keyPaths.Length is 0) if (keyPaths.Length is 0)
throw new FileNotFoundException($"Directory '{keysSource}' contained no '.keys' files."); throw new FileNotFoundException($"Directory '{keysSource}' contained no '.keys' files.");
List<string> failedFiles = new();
foreach (string filePath in keyPaths) foreach (string filePath in keyPaths)
{ {
try try
@@ -499,18 +497,15 @@ namespace Ryujinx.HLE.FileSystem
catch (Exception e) catch (Exception e)
{ {
Logger.Error?.Print(LogClass.Application, e.Message); Logger.Error?.Print(LogClass.Application, e.Message);
failedFiles.Add(Path.GetFileName(filePath));
continue; continue;
} }
string destPath = Path.Combine(installDirectory, Path.GetFileName(filePath)); string destPath = Path.Combine(installDirectory, Path.GetFileName(filePath));
File.Copy(filePath, destPath, true); if (File.Exists(destPath))
} File.Delete(destPath);
if (failedFiles.Count > 0) File.Copy(filePath, destPath, true);
{
throw new InvalidOperationException($"Failed to install the following key files: {string.Join(", ", failedFiles)}");
} }
return; return;
@@ -523,6 +518,8 @@ namespace Ryujinx.HLE.FileSystem
FileInfo info = new(keysSource); FileInfo info = new(keysSource);
using FileStream file = File.OpenRead(keysSource);
if (info.Extension is not ".keys") if (info.Extension is not ".keys")
throw new InvalidFirmwarePackageException("Input file extension is not .keys"); throw new InvalidFirmwarePackageException("Input file extension is not .keys");
@@ -537,6 +534,10 @@ namespace Ryujinx.HLE.FileSystem
string dest = Path.Combine(installDirectory, info.Name); string dest = Path.Combine(installDirectory, info.Name);
if (File.Exists(dest))
File.Delete(dest);
// overwrite: true seems to not work on its own? https://github.com/Ryubing/Issues/issues/189
File.Copy(keysSource, dest, true); File.Copy(keysSource, dest, true);
} }
@@ -1058,7 +1059,7 @@ namespace Ryujinx.HLE.FileSystem
} }
} }
public static bool AreKeysAlreadyPresent(string pathToCheck) public static bool AreKeysAlredyPresent(string pathToCheck)
{ {
string[] fileNames = ["prod.keys", "title.keys", "console.keys", "dev.keys"]; string[] fileNames = ["prod.keys", "title.keys", "console.keys", "dev.keys"];
foreach (string file in fileNames) foreach (string file in fileNames)

View File

@@ -20,7 +20,6 @@ using Ryujinx.HLE.HOS.Services.Mii;
using Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption; using Ryujinx.HLE.HOS.Services.Nfc.AmiiboDecryption;
using Ryujinx.HLE.HOS.Services.Nfc.Nfp; using Ryujinx.HLE.HOS.Services.Nfc.Nfp;
using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager; using Ryujinx.HLE.HOS.Services.Nfc.Nfp.NfpManager;
using Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager;
using Ryujinx.HLE.HOS.Services.Nv; using Ryujinx.HLE.HOS.Services.Nv;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl;
using Ryujinx.HLE.HOS.Services.Pcv.Bpc; using Ryujinx.HLE.HOS.Services.Pcv.Bpc;
@@ -67,8 +66,6 @@ namespace Ryujinx.HLE.HOS
internal List<NfpDevice> NfpDevices { get; private set; } internal List<NfpDevice> NfpDevices { get; private set; }
internal List<NfcDevice> NfcDevices { get; private set; }
internal SmRegistry SmRegistry { get; private set; } internal SmRegistry SmRegistry { get; private set; }
internal ServerBase SmServer { get; private set; } internal ServerBase SmServer { get; private set; }
@@ -135,7 +132,6 @@ namespace Ryujinx.HLE.HOS
PerformanceState = new PerformanceState(); PerformanceState = new PerformanceState();
NfpDevices = []; NfpDevices = [];
NfcDevices = [];
// Note: This is not really correct, but with HLE of services, the only memory // Note: This is not really correct, but with HLE of services, the only memory
// region used that is used is Application, so we can use the other ones for anything. // region used that is used is Application, so we can use the other ones for anything.
@@ -246,21 +242,21 @@ namespace Ryujinx.HLE.HOS
public void InitializeServices() public void InitializeServices()
{ {
SmRegistry = new SmRegistry(); SmRegistry = new SmRegistry();
SmServer = new ServerBase(KernelContext, "Sm", () => new IUserInterface(KernelContext, SmRegistry)); SmServer = new ServerBase(KernelContext, "SmServer", () => new IUserInterface(KernelContext, SmRegistry));
// Wait until SM server thread is done with initialization, // Wait until SM server thread is done with initialization,
// only then doing connections to SM is safe. // only then doing connections to SM is safe.
SmServer.InitDone.WaitOne(); SmServer.InitDone.WaitOne();
BsdServer = new ServerBase(KernelContext, "Bsd"); BsdServer = new ServerBase(KernelContext, "BsdServer");
FsServer = new ServerBase(KernelContext, "Fs"); FsServer = new ServerBase(KernelContext, "FsServer");
HidServer = new ServerBase(KernelContext, "Hid"); HidServer = new ServerBase(KernelContext, "HidServer");
NvDrvServer = new ServerBase(KernelContext, "Nv"); NvDrvServer = new ServerBase(KernelContext, "NvservicesServer");
TimeServer = new ServerBase(KernelContext, "Time"); TimeServer = new ServerBase(KernelContext, "TimeServer");
ViServer = new ServerBase(KernelContext, "Vi:u"); ViServer = new ServerBase(KernelContext, "ViServerU");
ViServerM = new ServerBase(KernelContext, "Vi:m"); ViServerM = new ServerBase(KernelContext, "ViServerM");
ViServerS = new ServerBase(KernelContext, "Vi:s"); ViServerS = new ServerBase(KernelContext, "ViServerS");
LdnServer = new ServerBase(KernelContext, "Ldn"); LdnServer = new ServerBase(KernelContext, "LdnServer");
StartNewServices(); StartNewServices();
} }
@@ -286,7 +282,7 @@ namespace Ryujinx.HLE.HOS
ProcessCreationFlags.Is64Bit | ProcessCreationFlags.Is64Bit |
ProcessCreationFlags.PoolPartitionSystem; ProcessCreationFlags.PoolPartitionSystem;
ProcessCreationInfo creationInfo = new(service.Name, 1, 0, 0x8000000, 1, Flags, 0, 0); ProcessCreationInfo creationInfo = new("Service", 1, 0, 0x8000000, 1, Flags, 0, 0);
uint[] defaultCapabilities = uint[] defaultCapabilities =
[ [
@@ -376,15 +372,6 @@ namespace Ryujinx.HLE.HOS
} }
} }
public void ScanSkylander(int nfcDeviceId, byte[] data)
{
if (NfcDevices[nfcDeviceId].State == NfcDeviceState.SearchingForTag)
{
NfcDevices[nfcDeviceId].State = NfcDeviceState.TagFound;
NfcDevices[nfcDeviceId].Data = data;
}
}
public bool SearchingForAmiibo(out int nfpDeviceId) public bool SearchingForAmiibo(out int nfpDeviceId)
{ {
nfpDeviceId = default; nfpDeviceId = default;
@@ -402,53 +389,6 @@ namespace Ryujinx.HLE.HOS
return false; return false;
} }
public bool SearchingForSkylander(out int nfcDeviceId)
{
nfcDeviceId = default;
for (int i = 0; i < NfcDevices.Count; i++)
{
if (NfcDevices[i].State == NfcDeviceState.SearchingForTag)
{
nfcDeviceId = i;
return true;
}
}
return false;
}
public bool HasSkylander(out int nfcDeviceId)
{
nfcDeviceId = default;
for (int i = 0; i < NfcDevices.Count; i++)
{
if (NfcDevices[i].State == NfcDeviceState.TagFound)
{
nfcDeviceId = i;
return true;
}
}
return false;
}
public void RemoveSkylander()
{
for (int i = 0; i < NfcDevices.Count; i++)
{
if (NfcDevices[i].State == NfcDeviceState.TagFound)
{
NfcDevices[i].State = NfcDeviceState.Initialized;
NfcDevices[i].SignalDeactivate();
Thread.Sleep(100); // NOTE: Simulate skylander scanning delay.
}
}
}
public void SignalDisplayResolutionChange() public void SignalDisplayResolutionChange()
{ {
DisplayResolutionChangeEvent.ReadableEvent.Signal(); DisplayResolutionChangeEvent.ReadableEvent.Signal();

View File

@@ -1,5 +1,4 @@
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Logging;
using System; using System;
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.LibraryAppletProxy namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.LibraryAppletProxy
@@ -45,35 +44,6 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib
return ResultCode.Success; return ResultCode.Success;
} }
[CommandCmif(1)]
// PushOutData(object<nn::am::service::IStorage>)
public ResultCode PushOutData(ServiceCtx context)
{
IStorage appletData = GetObject<IStorage>(context, 0);
if (appletData == null || appletData.Data.Length == 0) // is this necessary?
{
return ResultCode.NullObject;
}
_appletStandalone.InputData.Enqueue(appletData.Data);
return ResultCode.Success;
}
[CommandCmif(10)]
// ExitProcessAndReturn -> nn::am::service::LibraryAppletInfo
public ResultCode ExitProcessAndReturn(ServiceCtx context)
{
// Exits the LibraryApplet and returns to running the title which launched this LibraryApplet (qlaunch for example).
// On success, official sw will enter an infinite loop with sleep-thread value 86400000000000.
// Since we don't currently support qlaunch, it's fine to stub it.
Logger.Stub?.PrintStub(LogClass.Service);
return ResultCode.Success;
}
[CommandCmif(11)] [CommandCmif(11)]
// GetLibraryAppletInfo() -> nn::am::service::LibraryAppletInfo // GetLibraryAppletInfo() -> nn::am::service::LibraryAppletInfo
@@ -97,8 +67,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib
AppletIdentifyInfo appletIdentifyInfo = new() AppletIdentifyInfo appletIdentifyInfo = new()
{ {
AppletId = AppletId.QLaunch, AppletId = AppletId.QLaunch,
// 0x4 padding TitleId = 0x0100000000001000,
TitleId = 0x0100000000001000, // qlaunch systemAppletMenu title ID
}; };
context.ResponseData.WriteStruct(appletIdentifyInfo); context.ResponseData.WriteStruct(appletIdentifyInfo);

View File

@@ -1,9 +1,7 @@
using Ryujinx.Common.Logging;
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using Ryujinx.HLE.HOS.Services.Caps.Types; using Ryujinx.HLE.HOS.Services.Caps.Types;
using SkiaSharp; using SkiaSharp;
using System; using System;
using System.Globalization;
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@@ -11,20 +9,16 @@ using System.Security.Cryptography;
namespace Ryujinx.HLE.HOS.Services.Caps namespace Ryujinx.HLE.HOS.Services.Caps
{ {
internal class CaptureManager class CaptureManager
{ {
public CaptureManager(Switch device) private readonly string _sdCardPath;
{
_ = device;
}
private readonly string _sdCardPath = FileSystem.VirtualFileSystem.GetSdCardPath();
private uint _shimLibraryVersion; private uint _shimLibraryVersion;
private const int ScreenshotWidth = 1280; public CaptureManager(Switch device)
private const int ScreenshotHeight = 720; {
private const int ScreenshotBytesPerPixel = 4; _sdCardPath = FileSystem.VirtualFileSystem.GetSdCardPath();
private const int ScreenshotDataSize = ScreenshotWidth * ScreenshotHeight * ScreenshotBytesPerPixel; // 0x384000 }
public ResultCode SetShimLibraryVersion(ServiceCtx context) public ResultCode SetShimLibraryVersion(ServiceCtx context)
{ {
@@ -59,94 +53,81 @@ namespace Ryujinx.HLE.HOS.Services.Caps
return resultCode; return resultCode;
} }
public ResultCode SaveScreenShot( public ResultCode SaveScreenShot(byte[] screenshotData, ulong appletResourceUserId, ulong titleId, out ApplicationAlbumEntry applicationAlbumEntry)
byte[] screenshotData,
ulong appletResourceUserId,
ulong titleId,
out ApplicationAlbumEntry applicationAlbumEntry)
{ {
Logger.Stub?.PrintStub(LogClass.ServiceCaps, new
{
appletResourceUserId,
titleId,
screenshotDataLength = screenshotData?.Length ?? 0,
});
applicationAlbumEntry = default; applicationAlbumEntry = default;
if (screenshotData == null || screenshotData.Length == 0) if (screenshotData.Length == 0)
{ {
return ResultCode.NullInputBuffer; return ResultCode.NullInputBuffer;
} }
if (screenshotData.Length < ScreenshotDataSize) /*
{ // NOTE: On our current implementation, appletResourceUserId starts at 0, disable it for now.
Logger.Warning?.PrintMsg( if (appletResourceUserId == 0)
LogClass.ServiceCaps,
$"Invalid screenshot buffer size 0x{screenshotData.Length:X}; expected at least 0x{ScreenshotDataSize:X}.");
return ResultCode.NullInputBuffer;
}
DateTime currentDateTime = DateTime.Now;
applicationAlbumEntry = new ApplicationAlbumEntry()
{
Size = (ulong)Unsafe.SizeOf<ApplicationAlbumEntry>(),
TitleId = titleId,
AlbumFileDateTime = new AlbumFileDateTime()
{
Year = (ushort)currentDateTime.Year,
Month = (byte)currentDateTime.Month,
Day = (byte)currentDateTime.Day,
Hour = (byte)currentDateTime.Hour,
Minute = (byte)currentDateTime.Minute,
Second = (byte)currentDateTime.Second,
UniqueId = 0,
},
AlbumStorage = AlbumStorage.Sd,
ContentType = ContentType.Screenshot,
Padding = new Array5<byte>(),
Unknown0x1f = 1,
};
// NOTE: The hex hash is a HMAC-SHA256 (first 32 bytes) using a hardcoded secret key over the titleId, we can simulate it by hashing the titleId instead.
string hash = Convert.ToHexString(SHA256.HashData(BitConverter.GetBytes(titleId)))[..0x20];
string folderPath = Path.Combine(
_sdCardPath,
"Nintendo",
"Album",
currentDateTime.Year.ToString("0000", CultureInfo.InvariantCulture),
currentDateTime.Month.ToString("00", CultureInfo.InvariantCulture),
currentDateTime.Day.ToString("00", CultureInfo.InvariantCulture));
string filePath = GenerateFilePath(folderPath, applicationAlbumEntry, currentDateTime, hash);
_ = Directory.CreateDirectory(folderPath);
while (File.Exists(filePath))
{
applicationAlbumEntry.AlbumFileDateTime.UniqueId++;
filePath = GenerateFilePath(folderPath, applicationAlbumEntry, currentDateTime, hash);
}
using SKBitmap bitmap = new(new SKImageInfo(ScreenshotWidth, ScreenshotHeight, SKColorType.Rgba8888));
IntPtr pixels = bitmap.GetPixels();
if (pixels == IntPtr.Zero)
{ {
return ResultCode.InvalidArgument; return ResultCode.InvalidArgument;
} }
*/
Marshal.Copy(screenshotData, 0, pixels, ScreenshotDataSize); /*
// Doesn't occur in our case.
if (applicationAlbumEntry == null)
{
return ResultCode.NullOutputBuffer;
}
*/
using SKData data = bitmap.Encode(SKEncodedImageFormat.Jpeg, 80); if (screenshotData.Length >= 0x384000)
using FileStream file = File.OpenWrite(filePath); {
data.SaveTo(file); DateTime currentDateTime = DateTime.Now;
return ResultCode.Success; applicationAlbumEntry = new ApplicationAlbumEntry()
{
Size = (ulong)Unsafe.SizeOf<ApplicationAlbumEntry>(),
TitleId = titleId,
AlbumFileDateTime = new AlbumFileDateTime()
{
Year = (ushort)currentDateTime.Year,
Month = (byte)currentDateTime.Month,
Day = (byte)currentDateTime.Day,
Hour = (byte)currentDateTime.Hour,
Minute = (byte)currentDateTime.Minute,
Second = (byte)currentDateTime.Second,
UniqueId = 0,
},
AlbumStorage = AlbumStorage.Sd,
ContentType = ContentType.Screenshot,
Padding = new Array5<byte>(),
Unknown0x1f = 1,
};
// NOTE: The hex hash is a HMAC-SHA256 (first 32 bytes) using a hardcoded secret key over the titleId, we can simulate it by hashing the titleId instead.
string hash = Convert.ToHexString(SHA256.HashData(BitConverter.GetBytes(titleId)))[..0x20];
string folderPath = Path.Combine(_sdCardPath, "Nintendo", "Album", currentDateTime.Year.ToString("00"), currentDateTime.Month.ToString("00"), currentDateTime.Day.ToString("00"));
string filePath = GenerateFilePath(folderPath, applicationAlbumEntry, currentDateTime, hash);
// TODO: Handle that using the FS service implementation and return the right error code instead of throwing exceptions.
Directory.CreateDirectory(folderPath);
while (File.Exists(filePath))
{
applicationAlbumEntry.AlbumFileDateTime.UniqueId++;
filePath = GenerateFilePath(folderPath, applicationAlbumEntry, currentDateTime, hash);
}
// NOTE: The saved JPEG file doesn't have the limitation in the extra EXIF data.
using SKBitmap bitmap = new(new SKImageInfo(1280, 720, SKColorType.Rgba8888));
Marshal.Copy(screenshotData, 0, bitmap.GetPixels(), screenshotData.Length);
using SKData data = bitmap.Encode(SKEncodedImageFormat.Jpeg, 80);
using FileStream file = File.OpenWrite(filePath);
data.SaveTo(file);
return ResultCode.Success;
}
return ResultCode.NullInputBuffer;
} }
private string GenerateFilePath(string folderPath, ApplicationAlbumEntry applicationAlbumEntry, DateTime currentDateTime, string hash) private string GenerateFilePath(string folderPath, ApplicationAlbumEntry applicationAlbumEntry, DateTime currentDateTime, string hash)

View File

@@ -1,19 +1,13 @@
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Caps.Types; using Ryujinx.HLE.HOS.Services.Caps.Types;
namespace Ryujinx.HLE.HOS.Services.Caps namespace Ryujinx.HLE.HOS.Services.Caps
{ {
[Service("caps:su")] // 6.0.0+ [Service("caps:su")] // 6.0.0+
internal class IScreenShotApplicationService : IpcService class IScreenShotApplicationService : IpcService
{ {
private const ulong ScreenshotDataSize = 0x384000; public IScreenShotApplicationService(ServiceCtx context) { }
private const ulong ApplicationDataSize = 0x404;
public IScreenShotApplicationService(ServiceCtx context)
{
_ = context;
}
[CommandCmif(32)] // 7.0.0+ [CommandCmif(32)] // 7.0.0+
// SetShimLibraryVersion(pid, u64, nn::applet::AppletResourceUserId) // SetShimLibraryVersion(pid, u64, nn::applet::AppletResourceUserId)
public ResultCode SetShimLibraryVersion(ServiceCtx context) public ResultCode SetShimLibraryVersion(ServiceCtx context)
@@ -39,15 +33,6 @@ namespace Ryujinx.HLE.HOS.Services.Caps
ulong screenshotDataPosition = context.Request.SendBuff[0].Position; ulong screenshotDataPosition = context.Request.SendBuff[0].Position;
ulong screenshotDataSize = context.Request.SendBuff[0].Size; ulong screenshotDataSize = context.Request.SendBuff[0].Size;
if (screenshotDataSize < ScreenshotDataSize)
{
Logger.Warning?.PrintMsg(
LogClass.ServiceCaps,
$"Invalid screenshot buffer size 0x{screenshotDataSize:X}; expected at least 0x{ScreenshotDataSize:X}.");
return ResultCode.NullInputBuffer;
}
byte[] screenshotData = context.Memory.GetSpan(screenshotDataPosition, (int)screenshotDataSize, true).ToArray(); byte[] screenshotData = context.Memory.GetSpan(screenshotDataPosition, (int)screenshotDataSize, true).ToArray();
ResultCode resultCode = context.Device.System.CaptureManager.SaveScreenShot(screenshotData, appletResourceUserId, context.Device.Processes.ActiveApplication.ProgramId, out ApplicationAlbumEntry applicationAlbumEntry); ResultCode resultCode = context.Device.System.CaptureManager.SaveScreenShot(screenshotData, appletResourceUserId, context.Device.Processes.ActiveApplication.ProgramId, out ApplicationAlbumEntry applicationAlbumEntry);
@@ -75,24 +60,6 @@ namespace Ryujinx.HLE.HOS.Services.Caps
ulong screenshotDataPosition = context.Request.SendBuff[1].Position; ulong screenshotDataPosition = context.Request.SendBuff[1].Position;
ulong screenshotDataSize = context.Request.SendBuff[1].Size; ulong screenshotDataSize = context.Request.SendBuff[1].Size;
if (applicationDataSize != ApplicationDataSize)
{
Logger.Warning?.PrintMsg(
LogClass.ServiceCaps,
$"Invalid ApplicationData size 0x{applicationDataSize:X}; expected 0x{ApplicationDataSize:X}.");
return ResultCode.InvalidArgument;
}
if (screenshotDataSize < ScreenshotDataSize)
{
Logger.Warning?.PrintMsg(
LogClass.ServiceCaps,
$"Invalid screenshot buffer size 0x{screenshotDataSize:X}; expected at least 0x{ScreenshotDataSize:X}.");
return ResultCode.NullInputBuffer;
}
// TODO: Parse the application data: At 0x00 it's UserData (Size of 0x400), at 0x404 it's a uint UserDataSize (Always empty for now). // TODO: Parse the application data: At 0x00 it's UserData (Size of 0x400), at 0x404 it's a uint UserDataSize (Always empty for now).
_ = context.Memory.GetSpan(applicationDataPosition, (int)applicationDataSize).ToArray(); _ = context.Memory.GetSpan(applicationDataPosition, (int)applicationDataSize).ToArray();
@@ -121,23 +88,6 @@ namespace Ryujinx.HLE.HOS.Services.Caps
ulong screenshotDataPosition = context.Request.SendBuff[1].Position; ulong screenshotDataPosition = context.Request.SendBuff[1].Position;
ulong screenshotDataSize = context.Request.SendBuff[1].Size; ulong screenshotDataSize = context.Request.SendBuff[1].Size;
if (userIdListSize != 0x88)
{
Logger.Warning?.PrintMsg(
LogClass.ServiceCaps,
$"Invalid UserIdList size 0x{userIdListSize:X}; expected 0x88.");
return ResultCode.InvalidArgument;
}
if (screenshotDataSize < ScreenshotDataSize)
{
Logger.Warning?.PrintMsg(
LogClass.ServiceCaps,
$"Invalid screenshot buffer size 0x{screenshotDataSize:X}; expected at least 0x{ScreenshotDataSize:X}.");
return ResultCode.NullInputBuffer;
}
// TODO: Parse the UserIdList. // TODO: Parse the UserIdList.
_ = context.Memory.GetSpan(userIdListPosition, (int)userIdListSize).ToArray(); _ = context.Memory.GetSpan(userIdListPosition, (int)userIdListSize).ToArray();

View File

@@ -56,7 +56,6 @@ namespace Ryujinx.HLE.HOS.Services.Hid
_activeCount = 0; _activeCount = 0;
JoyHold = NpadJoyHoldType.Vertical; JoyHold = NpadJoyHoldType.Vertical;
SixAxisActive = false;
} }
internal ref KEvent GetStyleSetUpdateEvent(PlayerIndex player) internal ref KEvent GetStyleSetUpdateEvent(PlayerIndex player)
@@ -581,29 +580,6 @@ namespace Ryujinx.HLE.HOS.Services.Hid
return needUpdateRight; return needUpdateRight;
} }
public bool isAtRest(int playerNumber)
{
ref NpadInternalState currentNpad = ref _device.Hid.SharedMemory.Npads[playerNumber].InternalState;
if (currentNpad.StyleSet == NpadStyleTag.None)
{
return true; // it will always be at rest because it cannot move.
}
ref SixAxisSensorState storage = ref GetSixAxisSensorLifo(ref currentNpad, false).GetCurrentEntryRef();
float acceleration = Math.Abs(storage.Acceleration.X)
+ Math.Abs(storage.Acceleration.Y)
+ Math.Abs(storage.Acceleration.Z);
float angularVelocity = Math.Abs(storage.AngularVelocity.X)
+ Math.Abs(storage.AngularVelocity.Y)
+ Math.Abs(storage.AngularVelocity.Z);
// TODO: check against config deadzone and add sensitivity setting
return ((acceleration <= 1.0F) && (angularVelocity <= 1.0F));
}
private void UpdateDisconnectedInputSixAxis(PlayerIndex index) private void UpdateDisconnectedInputSixAxis(PlayerIndex index)
{ {

View File

@@ -602,33 +602,19 @@ namespace Ryujinx.HLE.HOS.Services.Hid
} }
[CommandCmif(82)] [CommandCmif(82)]
// IsSixAxisSensorAtRest(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) -> bool IsAtRest // IsSixAxisSensorAtRest(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) -> bool IsAsRest
public ResultCode IsSixAxisSensorAtRest(ServiceCtx context) public ResultCode IsSixAxisSensorAtRest(ServiceCtx context)
{ {
int sixAxisSensorHandle = context.RequestData.ReadInt32(); int sixAxisSensorHandle = context.RequestData.ReadInt32();
// 4 byte struct w/ 4-byte alignment
// uint typeValue = (uint) sixAxisSensorHandle; // 0x0 0x4 TypeValue
// uint npadStyleIndex = (uint) sixAxisSensorHandle & 0xff; // 0x0 0x1 NpadStyleIndex
int playerNumber = (sixAxisSensorHandle << 8) & 0xff; // 0x1 0x1 PlayerNumber
// uint deviceIdx= ((uint) sixAxisSensorHandle << 16) & 0xff; // 0x2 0x1 DeviceIdx
// uint unknown = ((uint) sixAxisSensorHandle << 24) & 0xff;
// 32bit sign extension padding -> if = 0, + offset, else - offset
// npadStyleIndex = ((npadStyleIndex & 0x8000) == 0) ? npadStyleIndex | 0xFFFF0000 : npadStyleIndex & 0xFFFF0000;
// playerNumber = ((playerNumber & 0x8000) == 0) ? playerNumber | 0xFFFF0000 : playerNumber & 0xFFFF0000;
// deviceIdx = ((deviceIdx & 0x8000) == 0) ? deviceIdx | 0xFFFF0000 : deviceIdx & 0xFFFF0000;
// unknown = ((unknown & 0x8000) == 0) ? unknown | 0xFFFF0000 : unknown & 0xFFFF0000;
context.RequestData.BaseStream.Position += 4; // Padding context.RequestData.BaseStream.Position += 4; // Padding
long appletResourceUserId = context.RequestData.ReadInt64(); long appletResourceUserId = context.RequestData.ReadInt64();
// TODO: link to context.Device.Hid.Npads.SixAxisActive when properly implemented bool isAtRest = true;
// We currently do not support stopping or starting SixAxisTracking.
context.ResponseData.Write(isAtRest);
context.ResponseData.Write(context.Device.Hid.Npads.isAtRest(playerNumber));
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, sixAxisSensorHandle, isAtRest });
return ResultCode.Success; return ResultCode.Success;
} }
@@ -643,7 +629,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
context.ResponseData.Write(_isFirmwareUpdateAvailableForSixAxisSensor); context.ResponseData.Write(_isFirmwareUpdateAvailableForSixAxisSensor);
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, sixAxisSensorHandle, _isFirmwareUpdateAvailableForSixAxisSensor }); Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, sixAxisSensorHandle, _isFirmwareUpdateAvailableForSixAxisSensor });
return ResultCode.Success; return ResultCode.Success;
} }

View File

@@ -66,11 +66,10 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{ {
if (localCommunicationId == localCommunicationIdChecked) if (localCommunicationId == localCommunicationIdChecked)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,"CheckLocalCommumicationIdPermission: Checked!");
return true; return true;
} }
} }
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,"CheckLocalCommumicationIdPermission: Check failed!");
return false; return false;
} }
@@ -81,7 +80,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
context.ResponseData.Write((int)NetworkState.Error); context.ResponseData.Write((int)NetworkState.Error);
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,$"GetState: _nifmResultCode = {_nifmResultCode.ToString()}");
return ResultCode.Success; return ResultCode.Success;
} }
@@ -113,14 +112,12 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,$"GetNetworkInfo: _nifmResultCode = {_nifmResultCode.ToString()}");
return _nifmResultCode; return _nifmResultCode;
} }
ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo); ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo);
if (resultCode != ResultCode.Success) if (resultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,$"GetState: resultCode = {resultCode.ToString()}");
return resultCode; return resultCode;
} }
@@ -136,22 +133,18 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
if (_state == NetworkState.StationConnected) if (_state == NetworkState.StationConnected)
{ {
networkInfo = _station.NetworkInfo; networkInfo = _station.NetworkInfo;
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,"GetNetworkInfoImpl: _station");
} }
else if (_state == NetworkState.AccessPointCreated) else if (_state == NetworkState.AccessPointCreated)
{ {
networkInfo = _accessPoint.NetworkInfo; networkInfo = _accessPoint.NetworkInfo;
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,"GetNetworkInfoImpl: _accessPoint");
} }
else else
{ {
networkInfo = new NetworkInfo(); networkInfo = new NetworkInfo();
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,"GetNetworkInfoImpl: Invalid state!");
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,$"GetNetworkInfoImpl: networkInfo = {networkInfo}");
return ResultCode.InvalidState; return ResultCode.InvalidState;
} }
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,$"GetNetworkInfoImpl: networkInfo = {networkInfo}");
return ResultCode.Success; return ResultCode.Success;
} }
@@ -203,7 +196,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
} }
else else
{ {
Logger.NetLog?.Print(LogClass.ServiceLdn, $"Console's LDN IP is \"{unicastAddress.Address}\"."); 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.Address));
context.ResponseData.Write(NetworkHelpers.ConvertIpv4Address(unicastAddress.IPv4Mask)); context.ResponseData.Write(NetworkHelpers.ConvertIpv4Address(unicastAddress.IPv4Mask));
@@ -211,7 +204,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
} }
else else
{ {
Logger.NetLog?.Print(LogClass.ServiceLdn, $"LDN obtained proxy IP."); Logger.Info?.Print(LogClass.ServiceLdn, $"LDN obtained proxy IP.");
context.ResponseData.Write(config.ProxyIp); context.ResponseData.Write(config.ProxyIp);
context.ResponseData.Write(config.ProxySubnetMask); context.ResponseData.Write(config.ProxySubnetMask);
@@ -232,7 +225,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
// NOTE: Returns ResultCode.InvalidArgument if _disconnectReason is null, doesn't occur in our case. // NOTE: Returns ResultCode.InvalidArgument if _disconnectReason is null, doesn't occur in our case.
context.ResponseData.Write((short)_disconnectReason); context.ResponseData.Write((short)_disconnectReason);
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetDisconnectReason: {_disconnectReason}");
return ResultCode.Success; return ResultCode.Success;
} }
@@ -252,14 +245,12 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{ {
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetSecurityParameter: _nifmResultCode = {_nifmResultCode}");
return _nifmResultCode; return _nifmResultCode;
} }
ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo); ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo);
if (resultCode != ResultCode.Success) if (resultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetSecurityParameter: resultCode = {resultCode}");
return resultCode; return resultCode;
} }
@@ -270,8 +261,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
}; };
context.ResponseData.WriteStruct(securityParameter); context.ResponseData.WriteStruct(securityParameter);
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetSecurityParameter: securityParameter = {securityParameter}");
return ResultCode.Success; return ResultCode.Success;
} }
@@ -281,14 +271,12 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{ {
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetNetworkConfig: _nifmResultCode = {_nifmResultCode}");
return _nifmResultCode; return _nifmResultCode;
} }
ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo); ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo);
if (resultCode != ResultCode.Success) if (resultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetNetworkConfig: resultCode = {resultCode}");
return resultCode; return resultCode;
} }
@@ -302,8 +290,6 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
}; };
context.ResponseData.WriteStruct(networkConfig); context.ResponseData.WriteStruct(networkConfig);
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetNetworkConfig: networkConfig = {networkConfig}");
return ResultCode.Success; return ResultCode.Success;
} }
@@ -334,14 +320,12 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetNetworkInfoLatestUpdate: _nifmResultCode = {_nifmResultCode}");
return _nifmResultCode; return _nifmResultCode;
} }
ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo); ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo);
if (resultCode != ResultCode.Success) if (resultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetNetworkInfoLatestUpdate: resultCode = {resultCode}");
return resultCode; return resultCode;
} }
@@ -392,7 +376,6 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ScanImpl: _nifmResultCode = {_nifmResultCode}");
return _nifmResultCode; return _nifmResultCode;
} }
@@ -415,7 +398,6 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{ {
if (scanFilter.Ssid.Length <= 31) if (scanFilter.Ssid.Length <= 31)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ScanImpl: resultCode = {resultCode}");
return resultCode; return resultCode;
} }
} }
@@ -424,13 +406,11 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{ {
if (scanFilterFlag > ScanFilterFlag.All) if (scanFilterFlag > ScanFilterFlag.All)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ScanImpl: resultCode = {resultCode}");
return resultCode; return resultCode;
} }
if (_state - 3 >= NetworkState.AccessPoint) if (_state - 3 >= NetworkState.AccessPoint)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "ScanImpl: Invalid state!");
resultCode = ResultCode.InvalidState; resultCode = ResultCode.InvalidState;
} }
else else
@@ -455,8 +435,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
} }
} }
} }
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ScanImpl: resultCode = {resultCode}");
return resultCode; return resultCode;
} }
@@ -481,7 +460,6 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
} }
} }
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ScanInternal: availableGames = {availableGames}");
return ResultCode.Success; return ResultCode.Success;
} }
@@ -509,37 +487,17 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
return ResultCode.Success; return ResultCode.Success;
} }
[CommandCmif(106)] // 20.0.0+
// SetProtocol
public ResultCode SetProtocol(ServiceCtx context)
{
uint protocolValue = context.RequestData.ReadUInt32();
// On NX only input value 1 or 3 is allowed, with an error being thrown otherwise.
if (protocolValue != 1 && protocolValue != 3)
{
throw new ArgumentException($"{GetType().FullName}: Protocol value is not 1 or 3!! Protocol value: {protocolValue}");
}
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"SetProtocol: protocolValue = {protocolValue}");
Logger.Stub?.PrintStub(LogClass.ServiceLdn, new { protocolValue});
return ResultCode.Success;
}
[CommandCmif(200)] [CommandCmif(200)]
// OpenAccessPoint() // OpenAccessPoint()
public ResultCode OpenAccessPoint(ServiceCtx context) public ResultCode OpenAccessPoint(ServiceCtx context)
{ {
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"OpenAccessPoint: _nifmResultCode = {_nifmResultCode}");
return _nifmResultCode; return _nifmResultCode;
} }
if (_state != NetworkState.Initialized) if (_state != NetworkState.Initialized)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "OpenAccessPoint: Invalid state!");
return ResultCode.InvalidState; return ResultCode.InvalidState;
} }
@@ -561,7 +519,6 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{ {
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"CloseAccessPoint: _nifmResultCode = {_nifmResultCode}");
return _nifmResultCode; return _nifmResultCode;
} }
@@ -571,7 +528,6 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
} }
else else
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "CloseAccessPoint: Invalid state!");
return ResultCode.InvalidState; return ResultCode.InvalidState;
} }
@@ -621,13 +577,11 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
bool isLocalCommunicationIdValid = CheckLocalCommunicationIdPermission(context, (ulong)networkConfig.IntentId.LocalCommunicationId); bool isLocalCommunicationIdValid = CheckLocalCommunicationIdPermission(context, (ulong)networkConfig.IntentId.LocalCommunicationId);
if (!isLocalCommunicationIdValid && NetworkClient.NeedsRealId) if (!isLocalCommunicationIdValid && NetworkClient.NeedsRealId)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "CreateNetworkImpl: Invalid object!");
return ResultCode.InvalidObject; return ResultCode.InvalidObject;
} }
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"CreateNetworkImpl: _nifmResultCode = {_nifmResultCode}");
return _nifmResultCode; return _nifmResultCode;
} }
@@ -656,22 +610,16 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
AddressList addressList = MemoryMarshal.Cast<byte, AddressList>(addressListBytes)[0]; AddressList addressList = MemoryMarshal.Cast<byte, AddressList>(addressListBytes)[0];
_accessPoint.CreateNetworkPrivate(securityConfig, securityParameter, userConfig, networkConfig, addressList); _accessPoint.CreateNetworkPrivate(securityConfig, securityParameter, userConfig, networkConfig, addressList);
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"CreateNetworkImpl: Created private network! " +
$"| securityConfig = {securityConfig} | securityParameter = {securityParameter} " +
$"| userConfig = {userConfig} | networkConfig = {networkConfig} | addressList = {addressList}");
} }
else else
{ {
_accessPoint.CreateNetwork(securityConfig, userConfig, networkConfig); _accessPoint.CreateNetwork(securityConfig, userConfig, networkConfig);
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"CreateNetworkImpl: Created network! " +
$"| securityConfig = {securityConfig} | userConfig = {userConfig} | networkConfig = {networkConfig}");
} }
return ResultCode.Success; return ResultCode.Success;
} }
else else
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "CreateNetworkImpl: Invalid state!");
return ResultCode.InvalidState; return ResultCode.InvalidState;
} }
} }
@@ -693,7 +641,6 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{ {
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"DestroyNetworkImpl: _nifmResultCode = {_nifmResultCode}");
return _nifmResultCode; return _nifmResultCode;
} }
@@ -710,11 +657,9 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
CloseAccessPoint(); CloseAccessPoint();
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "DestroyNetworkImpl: Invalid state!");
return ResultCode.InvalidState; return ResultCode.InvalidState;
} }
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "DestroyNetworkImpl: Invalid argument!");
return ResultCode.InvalidArgument; return ResultCode.InvalidArgument;
} }
@@ -731,17 +676,14 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{ {
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"RejectImpl: _nifmResultCode = {_nifmResultCode}");
return _nifmResultCode; return _nifmResultCode;
} }
if (_state != NetworkState.AccessPointCreated) if (_state != NetworkState.AccessPointCreated)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "RejectImpl: Invalid state!");
return ResultCode.InvalidState; // Must be network host to reject nodes. return ResultCode.InvalidState; // Must be network host to reject nodes.
} }
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"RejectImpl: disconnectReason = {disconnectReason} | nodeId = {nodeId}");
return NetworkClient.Reject(disconnectReason, nodeId); return NetworkClient.Reject(disconnectReason, nodeId);
} }
@@ -753,13 +695,11 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"SetAdvertiseData: _nifmResultCode = {_nifmResultCode}");
return _nifmResultCode; return _nifmResultCode;
} }
if (bufferSize is 0 or > LdnConst.AdvertiseDataSizeMax) if (bufferSize is 0 or > LdnConst.AdvertiseDataSizeMax)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "SetAdvertiseData: Invalid argument!");
return ResultCode.InvalidArgument; return ResultCode.InvalidArgument;
} }
@@ -768,12 +708,11 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
byte[] advertiseData = new byte[bufferSize]; byte[] advertiseData = new byte[bufferSize];
context.Memory.Read(bufferPosition, advertiseData); context.Memory.Read(bufferPosition, advertiseData);
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"SetAdvertiseData: advertiseData = {advertiseData}");
return _accessPoint.SetAdvertiseData(advertiseData); return _accessPoint.SetAdvertiseData(advertiseData);
} }
else else
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "SetAdvertiseData: Invalid state!");
return ResultCode.InvalidState; return ResultCode.InvalidState;
} }
} }
@@ -786,24 +725,20 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"SetStationAcceptPolicy: _nifmResultCode = {_nifmResultCode}");
return _nifmResultCode; return _nifmResultCode;
} }
if (acceptPolicy > AcceptPolicy.WhiteList) if (acceptPolicy > AcceptPolicy.WhiteList)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "SetStationAcceptPolicy: Invalid argument!");
return ResultCode.InvalidArgument; return ResultCode.InvalidArgument;
} }
if (_state is NetworkState.AccessPoint or NetworkState.AccessPointCreated) if (_state is NetworkState.AccessPoint or NetworkState.AccessPointCreated)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"SetStationAcceptPolicy: acceptPolicy = {acceptPolicy}");
return _accessPoint.SetStationAcceptPolicy(acceptPolicy); return _accessPoint.SetStationAcceptPolicy(acceptPolicy);
} }
else else
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "SetStationAcceptPolicy: Invalid state!");
return ResultCode.InvalidState; return ResultCode.InvalidState;
} }
} }
@@ -814,7 +749,6 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{ {
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"AddAcceptFilterEntry: _nifmResultCode = {_nifmResultCode}");
return _nifmResultCode; return _nifmResultCode;
} }
@@ -829,7 +763,6 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{ {
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ClearAcceptFilter: _nifmResultCode = {_nifmResultCode}");
return _nifmResultCode; return _nifmResultCode;
} }
@@ -844,13 +777,11 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{ {
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"OpenStation: _nifmResultCode = {_nifmResultCode}");
return _nifmResultCode; return _nifmResultCode;
} }
if (_state != NetworkState.Initialized) if (_state != NetworkState.Initialized)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "OpenStation: Invalid state!");
return ResultCode.InvalidState; return ResultCode.InvalidState;
} }
@@ -863,8 +794,6 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
// NOTE: Calls nifm service and returns related result codes. // NOTE: Calls nifm service and returns related result codes.
// Since we use our own implementation we can return ResultCode.Success. // Since we use our own implementation we can return ResultCode.Success.
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"OpenStation: _station = {_station}");
return ResultCode.Success; return ResultCode.Success;
} }
@@ -875,7 +804,6 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{ {
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"CloseStation: _nifmResultCode = {_nifmResultCode}");
return _nifmResultCode; return _nifmResultCode;
} }
@@ -885,13 +813,11 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
} }
else else
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "CloseStation: Invalid state!");
return ResultCode.InvalidState; return ResultCode.InvalidState;
} }
SetState(NetworkState.Initialized); SetState(NetworkState.Initialized);
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "CloseStation: Closed.");
return ResultCode.Success; return ResultCode.Success;
} }
@@ -956,13 +882,11 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
bool isLocalCommunicationIdValid = CheckLocalCommunicationIdPermission(context, (ulong)networkInfo.NetworkId.IntentId.LocalCommunicationId); bool isLocalCommunicationIdValid = CheckLocalCommunicationIdPermission(context, (ulong)networkInfo.NetworkId.IntentId.LocalCommunicationId);
if (!isLocalCommunicationIdValid && NetworkClient.NeedsRealId) if (!isLocalCommunicationIdValid && NetworkClient.NeedsRealId)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "ConnectImpl: Invalid object!");
return ResultCode.InvalidObject; return ResultCode.InvalidObject;
} }
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ConnectImpl: _nifmResultCode = {_nifmResultCode}");
return _nifmResultCode; return _nifmResultCode;
} }
@@ -982,7 +906,6 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{ {
if (_state != NetworkState.Station) if (_state != NetworkState.Station)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "ConnectImpl: Invalid state!");
resultCode = ResultCode.InvalidState; resultCode = ResultCode.InvalidState;
} }
else else
@@ -990,16 +913,10 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
if (isPrivate) if (isPrivate)
{ {
resultCode = _station.ConnectPrivate(securityConfig, securityParameter, userConfig, localCommunicationVersion, optionUnknown, networkConfig); resultCode = _station.ConnectPrivate(securityConfig, securityParameter, userConfig, localCommunicationVersion, optionUnknown, networkConfig);
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ConnectImpl: Private connection established! " +
$"| securityConfig = {securityConfig} | securityParameter = {securityParameter} | userConfig = {userConfig} " +
$"| localCommunicationVersion = {localCommunicationVersion} | optionUnknown = {optionUnknown} | networkConfig = {networkConfig}");
} }
else else
{ {
resultCode = _station.Connect(securityConfig, userConfig, localCommunicationVersion, optionUnknown, networkInfo); resultCode = _station.Connect(securityConfig, userConfig, localCommunicationVersion, optionUnknown, networkInfo);
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ConnectImpl: Connection established! " +
$"| securityConfig = {securityConfig} | userConfig = {userConfig} " +
$"| localCommunicationVersion = {localCommunicationVersion} | optionUnknown = {optionUnknown} | networkConfig = {networkConfig}");
} }
} }
} }
@@ -1007,8 +924,6 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
} }
} }
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ConnectImpl: resultCode = {resultCode}");
return resultCode; return resultCode;
} }
@@ -1023,7 +938,6 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{ {
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"DisconnectImpl: _nifmResultCode = {_nifmResultCode}");
return _nifmResultCode; return _nifmResultCode;
} }
@@ -1037,17 +951,14 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
_disconnectReason = disconnectReason; _disconnectReason = disconnectReason;
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"DisconnectImpl: _disconnectReason = {_disconnectReason}");
return ResultCode.Success; return ResultCode.Success;
} }
CloseStation(); CloseStation();
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "DisconnectImpl: Invalid state!");
return ResultCode.InvalidState; return ResultCode.InvalidState;
} }
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "DisconnectImpl: Invalid argument!");
return ResultCode.InvalidArgument; return ResultCode.InvalidArgument;
} }
@@ -1064,7 +975,6 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{ {
if (_nifmResultCode != ResultCode.Success) if (_nifmResultCode != ResultCode.Success)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"Finalize: _disconnectReason = {_disconnectReason}");
return _nifmResultCode; return _nifmResultCode;
} }
@@ -1081,13 +991,11 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
_stateChangeEventHandle = 0; _stateChangeEventHandle = 0;
} }
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"Finalize: resultCode = {resultCode}");
return resultCode; return resultCode;
} }
private ResultCode FinalizeImpl(bool isCausedBySystem) private ResultCode FinalizeImpl(bool isCausedBySystem)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "FinalizeImpl");
DisconnectReason disconnectReason; DisconnectReason disconnectReason;
switch (_state) switch (_state)
@@ -1211,6 +1119,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
NetworkClient.SetGameVersion(context.Device.Processes.ActiveApplication.ApplicationControlProperties.DisplayVersion); NetworkClient.SetGameVersion(context.Device.Processes.ActiveApplication.ApplicationControlProperties.DisplayVersion);
resultCode = ResultCode.Success; resultCode = ResultCode.Success;
_nifmResultCode = resultCode; _nifmResultCode = resultCode;
SetState(NetworkState.Initialized); SetState(NetworkState.Initialized);
@@ -1224,7 +1133,6 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
} }
} }
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"InitializeImpl: resultCode = {resultCode}");
return resultCode; return resultCode;
} }

View File

@@ -132,7 +132,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu
protected override void OnConnected() protected override void OnConnected()
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"LDN TCP client connected a new session with Id {Id}"); Logger.Info?.PrintMsg(LogClass.ServiceLdn, $"LDN TCP client connected a new session with Id {Id}");
UpdatePassphraseIfNeeded(); UpdatePassphraseIfNeeded();
@@ -141,7 +141,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu
protected override void OnDisconnected() protected override void OnDisconnected()
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"LDN TCP client disconnected a session with Id {Id}"); Logger.Info?.PrintMsg(LogClass.ServiceLdn, $"LDN TCP client disconnected a session with Id {Id}");
_passphrase = null; _passphrase = null;
@@ -174,7 +174,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu
protected override void OnError(SocketError error) protected override void OnError(SocketError error)
{ {
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"LDN TCP client caught an error with code {error}"); Logger.Info?.PrintMsg(LogClass.ServiceLdn, $"LDN TCP client caught an error with code {error}");
_error.Set(); _error.Set();
} }
@@ -428,7 +428,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu
} }
else else
{ {
Logger.NetLog?.Print(LogClass.ServiceLdn, $"Created a wireless P2P network on port {request.ExternalProxyPort}."); Logger.Info?.Print(LogClass.ServiceLdn, $"Created a wireless P2P network on port {request.ExternalProxyPort}.");
_hostedProxy.Start(); _hostedProxy.Start();
(_, UnicastIPAddressInformation unicastAddress) = NetworkHelpers.GetLocalInterface(); (_, UnicastIPAddressInformation unicastAddress) = NetworkHelpers.GetLocalInterface();

View File

@@ -1,4 +1,3 @@
using Ryujinx.Common.Logging;
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using Ryujinx.HLE.HOS.Services.Ldn.Types; using Ryujinx.HLE.HOS.Services.Ldn.Types;
using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types; using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types;
@@ -37,12 +36,10 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
if (Connected) if (Connected)
{ {
_parent.SetState(NetworkState.StationConnected); _parent.SetState(NetworkState.StationConnected);
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,$"NetworkChanged: {NetworkInfo}");
} }
else else
{ {
_parent.SetDisconnectReason(e.DisconnectReasonOrDefault(DisconnectReason.DestroyedByUser)); _parent.SetDisconnectReason(e.DisconnectReasonOrDefault(DisconnectReason.DestroyedByUser));
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,"NetworkChanged: Disconnected (DestroyedByUser)");
} }
} }
else else

View File

@@ -81,10 +81,8 @@ namespace Ryujinx.HLE.HOS.Services.Mii
return ResultCode.Success; return ResultCode.Success;
} }
public ResultCode UpdateLatest<T>(DatabaseSessionMetadata metadata, T oldMiiData, SourceFlag flag, out T newMiiData) where T : unmanaged, IStoredData<T> public ResultCode UpdateLatest<T>(DatabaseSessionMetadata metadata, IStoredData<T> oldMiiData, SourceFlag flag, IStoredData<T> newMiiData) where T : unmanaged
{ {
newMiiData = default;
if (!flag.HasFlag(SourceFlag.Database)) if (!flag.HasFlag(SourceFlag.Database))
{ {
return ResultCode.NotFound; return ResultCode.NotFound;
@@ -108,7 +106,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii
newMiiData.SetFromStoreData(storeData); newMiiData.SetFromStoreData(storeData);
if (oldMiiData.Equals(newMiiData)) if (oldMiiData == newMiiData)
{ {
return ResultCode.NotUpdated; return ResultCode.NotUpdated;
} }
@@ -288,18 +286,6 @@ namespace Ryujinx.HLE.HOS.Services.Mii
return result; return result;
} }
public ResultCode Append(DatabaseSessionMetadata metadata, CharInfo charInfo)
{
ResultCode result = _miiDatabase.Append(metadata, _utilityImpl, charInfo);
if (result == ResultCode.Success)
{
result = _miiDatabase.SaveDatabase();
}
return result;
}
public ResultCode ConvertCharInfoToCoreData(CharInfo charInfo, out CoreData coreData) public ResultCode ConvertCharInfoToCoreData(CharInfo charInfo, out CoreData coreData)
{ {
coreData = new CoreData(); coreData = new CoreData();

View File

@@ -449,32 +449,6 @@ namespace Ryujinx.HLE.HOS.Services.Mii
return ResultCode.Success; return ResultCode.Success;
} }
public ResultCode Append(DatabaseSessionMetadata metadata, UtilityImpl utilityImpl, CharInfo charInfo)
{
if (!charInfo.IsValid())
{
return ResultCode.InvalidCharInfo;
}
if (charInfo.Type == 1)
{
return ResultCode.InvalidOperationOnSpecialMii;
}
CoreData coreData = new();
coreData.SetFromCharInfo(charInfo);
StoreData storeData;
do
{
storeData = StoreData.BuildFromCoreData(utilityImpl, coreData);
}
while (_database.GetIndexByCreatorId(out _, storeData.CreateId));
return AddOrReplace(metadata, storeData);
}
public ResultCode Delete(DatabaseSessionMetadata metadata, CreateId createId) public ResultCode Delete(DatabaseSessionMetadata metadata, CreateId createId)
{ {
if (!_database.GetIndexByCreatorId(out int index, createId)) if (!_database.GetIndexByCreatorId(out int index, createId))

View File

@@ -54,7 +54,9 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService
protected override ResultCode UpdateLatest(CharInfo oldCharInfo, SourceFlag flag, out CharInfo newCharInfo) protected override ResultCode UpdateLatest(CharInfo oldCharInfo, SourceFlag flag, out CharInfo newCharInfo)
{ {
return _database.UpdateLatest(_metadata, oldCharInfo, flag, out newCharInfo); newCharInfo = default;
return _database.UpdateLatest(_metadata, oldCharInfo, flag, newCharInfo);
} }
protected override ResultCode BuildRandom(Age age, Gender gender, Race race, out CharInfo charInfo) protected override ResultCode BuildRandom(Age age, Gender gender, Race race, out CharInfo charInfo)
@@ -111,14 +113,14 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService
protected override ResultCode UpdateLatest1(StoreData oldStoreData, SourceFlag flag, out StoreData newStoreData) protected override ResultCode UpdateLatest1(StoreData oldStoreData, SourceFlag flag, out StoreData newStoreData)
{ {
newStoreData = default;
if (!_isSystem) if (!_isSystem)
{ {
newStoreData = default;
return ResultCode.PermissionDenied; return ResultCode.PermissionDenied;
} }
return _database.UpdateLatest(_metadata, oldStoreData, flag, out newStoreData); return _database.UpdateLatest(_metadata, oldStoreData, flag, newStoreData);
} }
protected override ResultCode FindIndex(CreateId createId, bool isSpecial, out int index) protected override ResultCode FindIndex(CreateId createId, bool isSpecial, out int index)
@@ -260,10 +262,5 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService
{ {
return _database.ConvertCharInfoToCoreData(charInfo, out coreData); return _database.ConvertCharInfoToCoreData(charInfo, out coreData);
} }
protected override ResultCode Append(CharInfo charInfo)
{
return _database.Append(_metadata, charInfo);
}
} }
} }

View File

@@ -340,15 +340,6 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService
return result; return result;
} }
[CommandCmif(26)] // 10.2.0+
// Append(nn::mii::CharInfo char_info)
public ResultCode Append(ServiceCtx context)
{
CharInfo charInfo = context.RequestData.ReadStruct<CharInfo>();
return Append(charInfo);
}
private Span<byte> CreateByteSpanFromBuffer(ServiceCtx context, IpcBuffDesc ipcBuff, bool isOutput) private Span<byte> CreateByteSpanFromBuffer(ServiceCtx context, IpcBuffDesc ipcBuff, bool isOutput)
{ {
byte[] rawData; byte[] rawData;
@@ -430,7 +421,5 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService
protected abstract ResultCode ConvertCoreDataToCharInfo(CoreData coreData, out CharInfo charInfo); protected abstract ResultCode ConvertCoreDataToCharInfo(CoreData coreData, out CharInfo charInfo);
protected abstract ResultCode ConvertCharInfoToCoreData(CharInfo charInfo, out CoreData coreData); protected abstract ResultCode ConvertCharInfoToCoreData(CharInfo charInfo, out CoreData coreData);
protected abstract ResultCode Append(CharInfo charInfo);
} }
} }

View File

@@ -1,19 +1,8 @@
using Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager;
namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare
{ {
[Service("nfc:mf:u")] [Service("nfc:mf:u")]
class IUserManager : IpcService class IUserManager : IpcService
{ {
public IUserManager(ServiceCtx context) { } public IUserManager(ServiceCtx context) { }
[CommandCmif(0)]
// CreateUserInterface() -> object<nn::nfc::mf::IUser>
public ResultCode CreateUserInterface(ServiceCtx context)
{
MakeObject(context, new IMifare());
return ResultCode.Success;
}
} }
} }

View File

@@ -1,477 +0,0 @@
using Ryujinx.Common.Memory;
using Ryujinx.Cpu;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.Hid;
using Ryujinx.HLE.HOS.Services.Hid.HidServer;
using Ryujinx.Horizon.Common;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
{
class IMifare : IpcService
{
private State _state;
private KEvent _availabilityChangeEvent;
private CancellationTokenSource _cancelTokenSource;
public IMifare()
{
_state = State.NonInitialized;
}
[CommandCmif(0)]
public ResultCode Initialize(ServiceCtx context)
{
_state = State.Initialized;
NfcDevice devicePlayer1 = new()
{
NpadIdType = NpadIdType.Player1,
Handle = HidUtils.GetIndexFromNpadIdType(NpadIdType.Player1),
State = NfcDeviceState.Initialized,
};
context.Device.System.NfcDevices.Add(devicePlayer1);
return ResultCode.Success;
}
[CommandCmif(1)]
public ResultCode Finalize(ServiceCtx context)
{
if (_state == State.Initialized)
{
_cancelTokenSource?.Cancel();
// NOTE: All events are destroyed here.
context.Device.System.NfcDevices.Clear();
_state = State.NonInitialized;
}
return ResultCode.Success;
}
[CommandCmif(2)]
public ResultCode GetListDevices(ServiceCtx context)
{
if (context.Request.RecvListBuff.Count == 0)
{
return ResultCode.WrongArgument;
}
ulong outputPosition = context.Request.RecvListBuff[0].Position;
ulong outputSize = context.Request.RecvListBuff[0].Size;
if (context.Device.System.NfcDevices.Count == 0)
{
return ResultCode.DeviceNotFound;
}
MemoryHelper.FillWithZeros(context.Memory, outputPosition, (int)outputSize);
for (int i = 0; i < context.Device.System.NfcDevices.Count; i++)
{
context.Memory.Write(outputPosition + ((uint)i * sizeof(long)), (uint)context.Device.System.NfcDevices[i].Handle);
}
context.ResponseData.Write(context.Device.System.NfcDevices.Count);
return ResultCode.Success;
}
[CommandCmif(3)]
public ResultCode StartDetection(ServiceCtx context)
{
uint deviceHandle = (uint)context.RequestData.ReadUInt64();
for (int i = 0; i < context.Device.System.NfcDevices.Count; i++)
{
if (context.Device.System.NfcDevices[i].Handle == (PlayerIndex)deviceHandle)
{
context.Device.System.NfcDevices[i].State = NfcDeviceState.SearchingForTag;
break;
}
}
_cancelTokenSource = new CancellationTokenSource();
Task.Run(() =>
{
while (true)
{
if (_cancelTokenSource.Token.IsCancellationRequested)
{
break;
}
for (int i = 0; i < context.Device.System.NfcDevices.Count; i++)
{
if (context.Device.System.NfcDevices[i].State == NfcDeviceState.TagFound)
{
context.Device.System.NfcDevices[i].SignalActivate();
Thread.Sleep(125); // NOTE: Simulate skylander scanning delay.
break;
}
}
}
}, _cancelTokenSource.Token);
return ResultCode.Success;
}
[CommandCmif(4)]
public ResultCode StopDetection(ServiceCtx context)
{
_cancelTokenSource?.Cancel();
uint deviceHandle = (uint)context.RequestData.ReadUInt64();
for (int i = 0; i < context.Device.System.NfcDevices.Count; i++)
{
if (context.Device.System.NfcDevices[i].Handle == (PlayerIndex)deviceHandle)
{
context.Device.System.NfcDevices[i].State = NfcDeviceState.Initialized;
Array.Clear(context.Device.System.NfcDevices[i].Data);
context.Device.System.NfcDevices[i].SignalDeactivate();
break;
}
}
return ResultCode.Success;
}
[CommandCmif(5)]
public ResultCode ReadMifare(ServiceCtx context)
{
if (context.Request.ReceiveBuff.Count == 0 || context.Request.SendBuff.Count == 0)
{
return ResultCode.WrongArgument;
}
uint deviceHandle = (uint)context.RequestData.ReadUInt64();
if (context.Device.System.NfcDevices.Count == 0)
{
return ResultCode.DeviceNotFound;
}
ulong outputPosition = context.Request.ReceiveBuff[0].Position;
ulong outputSize = context.Request.ReceiveBuff[0].Size;
MemoryHelper.FillWithZeros(context.Memory, outputPosition, (int)outputSize);
ulong inputPosition = context.Request.SendBuff[0].Position;
ulong inputSize = context.Request.SendBuff[0].Size;
byte[] readBlockParameter = new byte[inputSize];
context.Memory.Read(inputPosition, readBlockParameter);
var span = MemoryMarshal.Cast<byte, NfcMifareReadBlockParameter>(readBlockParameter);
var list = new List<NfcMifareReadBlockParameter>(span.Length);
foreach (var item in span)
list.Add(item);
Thread.Sleep(125 * list.Count); // NOTE: Simulate skylander scanning delay.
for (int i = 0; i < context.Device.System.NfcDevices.Count; i++)
{
if (context.Device.System.NfcDevices[i].Handle == (PlayerIndex)deviceHandle)
{
if (context.Device.System.NfcDevices[i].State == NfcDeviceState.TagRemoved)
{
return ResultCode.TagNotFound;
}
else
{
for (int p = 0; p < list.Count; p++)
{
NfcMifareReadBlockData blockData = new()
{
SectorNumber = list[p].SectorNumber,
Reserved = new Array7<byte>(),
};
byte[] data = new byte[16];
switch (list[p].SectorKey.MifareCommand)
{
case NfcMifareCommand.NfcMifareCommand_Read:
case NfcMifareCommand.NfcMifareCommand_AuthA:
if (IsCurrentBlockKeyBlock(list[p].SectorNumber))
{
Array.Copy(context.Device.System.NfcDevices[i].Data, (16 * list[p].SectorNumber) + 6, data, 6, 4);
}
else
{
Array.Copy(context.Device.System.NfcDevices[i].Data, 16 * list[p].SectorNumber, data, 0, 16);
}
data.CopyTo(blockData.Data.AsSpan());
context.Memory.Write(outputPosition + ((uint)(p * Unsafe.SizeOf<NfcMifareReadBlockData>())), blockData);
break;
}
}
}
}
}
return ResultCode.Success;
}
[CommandCmif(6)]
public ResultCode WriteMifare(ServiceCtx context)
{
if (context.Request.SendBuff.Count == 0)
{
return ResultCode.WrongArgument;
}
uint deviceHandle = (uint)context.RequestData.ReadUInt64();
if (context.Device.System.NfcDevices.Count == 0)
{
return ResultCode.DeviceNotFound;
}
ulong inputPosition = context.Request.SendBuff[0].Position;
ulong inputSize = context.Request.SendBuff[0].Size;
byte[] writeBlockParameter = new byte[inputSize];
context.Memory.Read(inputPosition, writeBlockParameter);
var span = MemoryMarshal.Cast<byte, NfcMifareWriteBlockParameter>(writeBlockParameter);
var list = new List<NfcMifareWriteBlockParameter>(span.Length);
foreach (var item in span)
list.Add(item);
Thread.Sleep(125 * list.Count); // NOTE: Simulate skylander scanning delay.
for (int i = 0; i < context.Device.System.NfcDevices.Count; i++)
{
if (context.Device.System.NfcDevices[i].Handle == (PlayerIndex)deviceHandle)
{
if (context.Device.System.NfcDevices[i].State == NfcDeviceState.TagRemoved)
{
return ResultCode.TagNotFound;
}
else
{
for (int p = 0; p < list.Count; p++)
{
switch (list[p].SectorKey.MifareCommand)
{
case NfcMifareCommand.NfcMifareCommand_Write:
case NfcMifareCommand.NfcMifareCommand_AuthA:
list[p].Data.AsSpan().CopyTo(context.Device.System.NfcDevices[i].Data.AsSpan(list[p].SectorNumber * 16, 16));
break;
}
}
}
}
}
return ResultCode.Success;
}
[CommandCmif(7)]
public ResultCode GetTagInfo(ServiceCtx context)
{
ResultCode resultCode = ResultCode.Success;
if (context.Request.RecvListBuff.Count == 0)
{
return ResultCode.WrongArgument;
}
ulong outputPosition = context.Request.RecvListBuff[0].Position;
context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Marshal.SizeOf<TagInfo>());
MemoryHelper.FillWithZeros(context.Memory, outputPosition, Marshal.SizeOf<TagInfo>());
uint deviceHandle = (uint)context.RequestData.ReadUInt64();
if (context.Device.System.NfcDevices.Count == 0)
{
return ResultCode.DeviceNotFound;
}
for (int i = 0; i < context.Device.System.NfcDevices.Count; i++)
{
if (context.Device.System.NfcDevices[i].Handle == (PlayerIndex)deviceHandle)
{
if (context.Device.System.NfcDevices[i].State == NfcDeviceState.TagRemoved)
{
resultCode = ResultCode.TagNotFound;
}
else
{
if (context.Device.System.NfcDevices[i].State == NfcDeviceState.TagMounted || context.Device.System.NfcDevices[i].State == NfcDeviceState.TagFound)
{
TagInfo tagInfo = new()
{
UuidLength = 4,
Reserved1 = new Array21<byte>(),
Protocol = (uint)NfcProtocol.NfcProtocol_TypeA, // Type A Protocol
TagType = (uint)NfcTagType.NfcTagType_Mifare, // Mifare Type
Reserved2 = new Array6<byte>(),
};
byte[] uuid = new byte[4];
Array.Copy(context.Device.System.NfcDevices[i].Data, 0, uuid, 0, 4);
uuid.CopyTo(tagInfo.Uuid.AsSpan());
context.Memory.Write(outputPosition, tagInfo);
resultCode = ResultCode.Success;
}
else
{
resultCode = ResultCode.WrongDeviceState;
}
}
break;
}
}
return resultCode;
}
[CommandCmif(8)]
public ResultCode AttachActivateEvent(ServiceCtx context)
{
uint deviceHandle = (uint)context.RequestData.ReadUInt64();
for (int i = 0; i < context.Device.System.NfcDevices.Count; i++)
{
if ((uint)context.Device.System.NfcDevices[i].Handle == deviceHandle)
{
context.Device.System.NfcDevices[i].ActivateEvent = new KEvent(context.Device.System.KernelContext);
if (context.Process.HandleTable.GenerateHandle(context.Device.System.NfcDevices[i].ActivateEvent.ReadableEvent, out int activateEventHandle) != Result.Success)
{
throw new InvalidOperationException("Out of handles!");
}
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(activateEventHandle);
return ResultCode.Success;
}
}
return ResultCode.DeviceNotFound;
}
[CommandCmif(9)]
public ResultCode AttachDeactivateEvent(ServiceCtx context)
{
uint deviceHandle = (uint)context.RequestData.ReadUInt64();
for (int i = 0; i < context.Device.System.NfcDevices.Count; i++)
{
if ((uint)context.Device.System.NfcDevices[i].Handle == deviceHandle)
{
context.Device.System.NfcDevices[i].DeactivateEvent = new KEvent(context.Device.System.KernelContext);
if (context.Process.HandleTable.GenerateHandle(context.Device.System.NfcDevices[i].DeactivateEvent.ReadableEvent, out int deactivateEventHandle) != Result.Success)
{
throw new InvalidOperationException("Out of handles!");
}
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(deactivateEventHandle);
return ResultCode.Success;
}
}
return ResultCode.DeviceNotFound;
}
[CommandCmif(10)]
public ResultCode GetState(ServiceCtx context)
{
context.ResponseData.Write((int)_state);
return ResultCode.Success;
}
[CommandCmif(11)]
public ResultCode GetDeviceState(ServiceCtx context)
{
uint deviceHandle = (uint)context.RequestData.ReadUInt64();
for (int i = 0; i < context.Device.System.NfcDevices.Count; i++)
{
if ((uint)context.Device.System.NfcDevices[i].Handle == deviceHandle)
{
if (context.Device.System.NfcDevices[i].State > NfcDeviceState.Finalized)
{
throw new InvalidOperationException($"{nameof(context.Device.System.NfcDevices)} contains an invalid state for device {i}: {context.Device.System.NfcDevices[i].State}");
}
context.ResponseData.Write((uint)context.Device.System.NfcDevices[i].State);
return ResultCode.Success;
}
}
context.ResponseData.Write((uint)NfcDeviceState.Unavailable);
return ResultCode.DeviceNotFound;
}
[CommandCmif(12)]
public ResultCode GetNpadId(ServiceCtx context)
{
uint deviceHandle = (uint)context.RequestData.ReadUInt64();
for (int i = 0; i < context.Device.System.NfcDevices.Count; i++)
{
if ((uint)context.Device.System.NfcDevices[i].Handle == deviceHandle)
{
context.ResponseData.Write((uint)HidUtils.GetNpadIdTypeFromIndex(context.Device.System.NfcDevices[i].Handle));
return ResultCode.Success;
}
}
return ResultCode.DeviceNotFound;
}
[CommandCmif(13)]
public ResultCode AttachAvailabilityChangeEvent(ServiceCtx context)
{
_availabilityChangeEvent = new KEvent(context.Device.System.KernelContext);
if (context.Process.HandleTable.GenerateHandle(_availabilityChangeEvent.ReadableEvent, out int availabilityChangeEventHandle) != Result.Success)
{
throw new InvalidOperationException("Out of handles!");
}
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(availabilityChangeEventHandle);
return ResultCode.Success;
}
private bool IsCurrentBlockKeyBlock(byte block)
{
return ((block + 1) % 4) == 0;
}
}
}

View File

@@ -1,21 +0,0 @@
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.Hid;
namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
{
class NfcDevice
{
public KEvent ActivateEvent;
public KEvent DeactivateEvent;
public void SignalActivate() => ActivateEvent.ReadableEvent.Signal();
public void SignalDeactivate() => DeactivateEvent.ReadableEvent.Signal();
public NfcDeviceState State = NfcDeviceState.Unavailable;
public PlayerIndex Handle;
public NpadIdType NpadIdType;
public byte[] Data;
}
}

View File

@@ -1,14 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
{
enum NfcMifareCommand : byte
{
NfcMifareCommand_Read = 0x30,
NfcMifareCommand_AuthA = 0x60,
NfcMifareCommand_AuthB = 0x61,
NfcMifareCommand_Write = 0xA0,
NfcMifareCommand_Transfer = 0xB0,
NfcMifareCommand_Decrement = 0xC0,
NfcMifareCommand_Increment = 0xC1,
NfcMifareCommand_Store = 0xC2,
}
}

View File

@@ -1,13 +0,0 @@
using Ryujinx.Common.Memory;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
{
[StructLayout(LayoutKind.Sequential, Size = 0x18)]
struct NfcMifareReadBlockData
{
public Array16<byte> Data;
public byte SectorNumber;
public Array7<byte> Reserved;
}
}

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