mirror of
https://git.naxdy.org/Mirror/Ryujinx.git
synced 2026-04-20 22:22:03 +00:00
Compare commits
46 commits
Canary-1.3
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9905ee17c2 | ||
|
|
7f0e82fe48 | ||
|
|
69c22a744e | ||
|
|
35b1531102 | ||
|
|
ea1185e96a | ||
|
|
31e0430b06 | ||
|
|
efb8658194 | ||
|
|
e909ea3210 | ||
|
|
2a688abeec | ||
|
|
9f88d68268 | ||
|
|
fe685c3f8f | ||
|
|
2edc165e26 | ||
|
|
fa7cb22240 | ||
|
|
c5b4add186 | ||
|
|
0079e0bfa5 | ||
|
|
25b70e05d7 | ||
|
|
78eb95733a | ||
|
|
17a768a5df | ||
|
|
339cddd0e2 | ||
|
|
66d9bc5a35 | ||
|
|
3e393449aa | ||
|
|
744c41937b | ||
|
|
ecd1c1240c | ||
|
|
3ad4d4a692 | ||
|
|
6fe7fb8dcb | ||
|
|
fc357d3ba4 | ||
|
|
32ee806070 | ||
|
|
4e81a4c2f4 | ||
|
|
9cae62096a | ||
|
|
648b609ebb | ||
|
|
5ae86fc493 | ||
|
|
6f90e47a73 | ||
|
|
ac5f9857e2 | ||
|
|
4b42087bd4 | ||
|
|
80cbf5d1fc | ||
|
|
cc6d2dc162 | ||
|
|
4ebc318da5 | ||
|
|
00dad0a5e2 | ||
|
|
b70e2e44cb | ||
|
|
012d1d6886 | ||
|
|
d1205dc95d | ||
|
|
6f95172bb6 | ||
|
|
8208d43d9e | ||
|
|
1260f93aaf | ||
|
|
1b3bf1473d | ||
|
|
081cdcab0c |
89 changed files with 1962 additions and 852 deletions
|
|
@ -10,6 +10,10 @@ 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/**'
|
||||||
|
|
@ -18,17 +22,17 @@ gpu:
|
||||||
- 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.Common/**', 'src/Ryujinx.UI.LocaleGenerator/**']
|
- any-glob-to-any-file: ['src/Ryujinx/**', 'src/Ryujinx.UI.LocaleGenerator/**']
|
||||||
|
|
||||||
horizon:
|
'horizon/hle':
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: ['src/Ryujinx.HLE/**', 'src/Ryujinx.Horizon/**']
|
- any-glob-to-any-file: ['src/Ryujinx.HLE/**', 'src/Ryujinx.HLE.Generators/**', 'src/Ryujinx.Horizon/**']
|
||||||
|
|
||||||
|
i18n:
|
||||||
|
- changed-files:
|
||||||
|
- any-glob-to-any-file: ['assets/**/*.json', 'src/Ryujinx.UI.LocaleGenerator/**']
|
||||||
|
|
||||||
kernel:
|
kernel:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
|
|
@ -36,7 +40,7 @@ kernel:
|
||||||
|
|
||||||
infra:
|
infra:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: ['.github/**', 'distribution/**', 'Directory.Packages.props', 'src/Ryujinx.BuildValidationTasks/**']
|
- any-glob-to-any-file: ['.forgejo/**', 'distribution/**', 'Directory.Packages.props', 'src/Ryujinx.BuildValidationTasks/**']
|
||||||
|
|
||||||
documentation:
|
documentation:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
|
|
@ -44,4 +48,4 @@ documentation:
|
||||||
|
|
||||||
ldn:
|
ldn:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: 'src/Ryujinx.HLE/HOS/Services/Ldn/**'
|
- any-glob-to-any-file: ['src/Ryujinx.HLE/HOS/Services/Ldn/**', 'src/Ryujinx/UI/Windows/LdnGamesListWindow.*', 'src/Ryujinx/UI/ViewModels/LdnGamesListViewModel.cs']
|
||||||
197
.forgejo/workflows/build.yml
Normal file
197
.forgejo/workflows/build.yml
Normal file
|
|
@ -0,0 +1,197 @@
|
||||||
|
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@v4
|
||||||
|
|
||||||
|
- uses: actions/setup-dotnet@v4
|
||||||
|
with:
|
||||||
|
global-json-file: global.json
|
||||||
|
|
||||||
|
- name: Install GLI
|
||||||
|
uses: actions/setup-gli@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.SETUP_GLI_TOKEN }}
|
||||||
|
|
||||||
|
- 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 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: 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: Set executable bit
|
||||||
|
run: |
|
||||||
|
chmod +x ./publish/Ryujinx ./publish/Ryujinx.sh
|
||||||
|
if: forgejo.event_name == 'pull_request' && contains(matrix.platform.name, 'linux')
|
||||||
|
|
||||||
|
- name: Build AppImage
|
||||||
|
if: forgejo.event_name == 'pull_request' && contains(matrix.platform.name, 'linux')
|
||||||
|
run: |
|
||||||
|
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 artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ryujinx-${{ matrix.configuration }}-${{ steps.version_info.outputs.result }}+${{ steps.version_info.outputs.git_short_hash }}-${{ matrix.platform.zip_os_name }}
|
||||||
|
path: publish
|
||||||
|
if: forgejo.event_name == 'pull_request'
|
||||||
|
|
||||||
|
- name: Upload Ryujinx (AppImage) artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
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@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 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@v4
|
||||||
|
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'
|
||||||
|
|
@ -6,7 +6,7 @@ on:
|
||||||
push:
|
push:
|
||||||
branches: [ master ]
|
branches: [ master ]
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- '.github/**'
|
- '.forgejo/**'
|
||||||
- 'docs/**'
|
- 'docs/**'
|
||||||
- 'assets/**'
|
- 'assets/**'
|
||||||
- '*.yml'
|
- '*.yml'
|
||||||
|
|
@ -25,44 +25,41 @@ env:
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
name: Release for ${{ matrix.platform.name }}
|
name: Release for ${{ matrix.platform.name }}
|
||||||
runs-on: ${{ matrix.platform.os }}
|
runs-on: docker
|
||||||
|
container:
|
||||||
|
image: ${{ matrix.platform.os }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
platform:
|
platform:
|
||||||
- { name: win-x64, os: ubuntu-latest, zip_os_name: win_x64 }
|
- { name: win-x64, os: ghcr.io/catthehacker/ubuntu:act-latest, zip_os_name: win_x64 }
|
||||||
#- { name: win-arm64, os: ubuntu-latest, zip_os_name: win_arm64 }
|
#- { name: win-arm64, os: ghcr.io/catthehacker/ubuntu:act-latest, zip_os_name: win_arm64 }
|
||||||
- { name: linux-x64, os: ubuntu-latest, zip_os_name: linux_x64 }
|
- { name: linux-x64, os: ghcr.io/catthehacker/ubuntu:act-latest, zip_os_name: linux_x64 }
|
||||||
- { name: linux-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 }
|
- { name: linux-arm64, os: ghcr.io/catthehacker/ubuntu:act-latest, zip_os_name: linux_arm64 }
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- uses: actions/setup-dotnet@v4
|
- uses: actions/setup-dotnet@v5
|
||||||
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::.github/csc.json"
|
run: echo "::add-matcher::.forgejo/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 install -y 7zip
|
sudo apt update && 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)" >> $GITHUB_OUTPUT
|
echo "build_version=$(gli get-next-version -c Canary -R)" >> $FORGEJO_OUTPUT
|
||||||
echo "prev_build_version=$(gli get-current-version -c Canary -R)" >> $GITHUB_OUTPUT
|
echo "prev_build_version=$(gli get-current-version -c Canary -R)" >> $FORGEJO_OUTPUT
|
||||||
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
echo "git_short_hash=$(git rev-parse --short "${{ forgejo.sha }}")" >> $FORGEJO_OUTPUT
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Configure for release
|
- name: Configure for release
|
||||||
|
|
@ -88,11 +85,7 @@ jobs:
|
||||||
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
|
||||||
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')
|
||||||
|
|
@ -102,8 +95,6 @@ jobs:
|
||||||
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
|
||||||
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)
|
||||||
|
|
@ -112,7 +103,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 install -y zsync desktop-file-utils appstream
|
sudo apt update && sudo apt install -y zsync desktop-file-utils appstream libfuse2t64
|
||||||
|
|
||||||
mkdir -p tools
|
mkdir -p tools
|
||||||
export PATH="$PATH:$(readlink -f tools)"
|
export PATH="$PATH:$(readlink -f tools)"
|
||||||
|
|
@ -139,17 +130,28 @@ 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: ubuntu-24.04
|
runs-on: docker
|
||||||
|
container:
|
||||||
|
image: ghcr.io/catthehacker/ubuntu:act-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- uses: actions/setup-dotnet@v4
|
- uses: actions/setup-dotnet@v5
|
||||||
with:
|
with:
|
||||||
global-json-file: global.json
|
global-json-file: global.json
|
||||||
|
|
||||||
|
|
@ -159,33 +161,24 @@ jobs:
|
||||||
chmod +x llvm.sh
|
chmod +x llvm.sh
|
||||||
sudo ./llvm.sh 17
|
sudo ./llvm.sh 17
|
||||||
|
|
||||||
- name: Install gli
|
- name: Install GLI
|
||||||
run: |
|
uses: actions/setup-gli@v1
|
||||||
mkdir -p $HOME/.bin
|
with:
|
||||||
gh release download -R GreemDev/GLI -O gli -p 'gli-linux-x64'
|
token: ${{ secrets.SETUP_GLI_TOKEN }}
|
||||||
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: |
|
||||||
mkdir -p $HOME/.bin
|
gli ghr -R indygreg/apple-platform-rs -p apple-codesign-*-x86_64-unknown-linux-musl.tar.gz -O apple-codesign.tar.gz
|
||||||
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 $HOME/.bin/
|
mv rcodesign /usr/bin/rcodesign
|
||||||
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)" >> $GITHUB_OUTPUT
|
echo "build_version=$(gli get-next-version -c Canary -R)" >> $FORGEJO_OUTPUT
|
||||||
echo "prev_build_version=$(gli get-current-version -c Canary -R)" >> $GITHUB_OUTPUT
|
echo "prev_build_version=$(gli get-current-version -c Canary -R)" >> $FORGEJO_OUTPUT
|
||||||
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
echo "git_short_hash=$(git rev-parse --short "${{ forgejo.sha }}")" >> $FORGEJO_OUTPUT
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Configure for release
|
- name: Configure for release
|
||||||
|
|
@ -201,46 +194,53 @@ 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
|
|
||||||
|
|
||||||
create_gitlab_release:
|
- name: Create release
|
||||||
name: Create GitLab Release
|
uses: actions/create-release@v1
|
||||||
runs-on: ubuntu-24.04
|
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: |-
|
||||||
|
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@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install gli
|
- name: Install GLI
|
||||||
run: |
|
uses: actions/setup-gli@v1
|
||||||
mkdir -p $HOME/.bin
|
with:
|
||||||
gh release download -R GreemDev/GLI -O gli -p 'gli-linux-x64'
|
token: ${{ secrets.SETUP_GLI_TOKEN }}
|
||||||
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)" >> $GITHUB_OUTPUT
|
echo "build_version=$(gli get-next-version -c Canary -R)" >> $FORGEJO_OUTPUT
|
||||||
echo "prev_build_version=$(gli get-current-version -c Canary -R)" >> $GITHUB_OUTPUT
|
echo "prev_build_version=$(gli get-current-version -c Canary -R)" >> $FORGEJO_OUTPUT
|
||||||
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
echo "git_short_hash=$(git rev-parse --short "${{ forgejo.sha }}")" >> $FORGEJO_OUTPUT
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Create tag
|
- name: Create tag
|
||||||
run: |
|
run: |
|
||||||
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 }}
|
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 }}
|
||||||
|
|
||||||
- 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: 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: Send notification webhook
|
- name: Send notification webhook
|
||||||
run: |
|
run: |
|
||||||
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
|
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
|
||||||
|
|
||||||
- name: Notify update server of new builds
|
- name: Notify update server of new builds
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -5,10 +5,6 @@ on:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
triage:
|
triage:
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|
@ -18,11 +14,13 @@ jobs:
|
||||||
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: GreemDev/Ryujinx
|
repository: projects/Ryubing
|
||||||
ref: master
|
ref: master
|
||||||
|
|
||||||
- name: Update labels based on changes
|
- name: Update labels based on changes
|
||||||
uses: actions/labeler@v5
|
uses: actions/labeler@v6
|
||||||
with:
|
with:
|
||||||
|
repo-token: ${{ secrets.LABELER_TOKEN }}
|
||||||
|
configuration-path: .forgejo/labeler.yml
|
||||||
sync-labels: true
|
sync-labels: true
|
||||||
dot: true
|
dot: true
|
||||||
|
|
@ -19,18 +19,20 @@ env:
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
name: Release for ${{ matrix.platform.name }}
|
name: Release for ${{ matrix.platform.name }}
|
||||||
runs-on: ${{ matrix.platform.os }}
|
runs-on: docker
|
||||||
|
container:
|
||||||
|
image: ${{ matrix.platform.os }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
platform:
|
platform:
|
||||||
- { name: win-x64, os: ubuntu-latest, zip_os_name: win_x64 }
|
- { name: win-x64, os: ghcr.io/catthehacker/ubuntu:act-latest, zip_os_name: win_x64 }
|
||||||
#- { name: win-arm64, os: ubuntu-latest, zip_os_name: win_arm64 }
|
#- { name: win-arm64, os: ghcr.io/catthehacker/ubuntu:act-latest, zip_os_name: win_arm64 }
|
||||||
- { name: linux-x64, os: ubuntu-latest, zip_os_name: linux_x64 }
|
- { name: linux-x64, os: ghcr.io/catthehacker/ubuntu:act-latest, zip_os_name: linux_x64 }
|
||||||
- { name: linux-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 }
|
- { name: linux-arm64, os: ghcr.io/catthehacker/ubuntu:act-latest, zip_os_name: linux_arm64 }
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- uses: actions/setup-dotnet@v4
|
- uses: actions/setup-dotnet@v5
|
||||||
with:
|
with:
|
||||||
global-json-file: global.json
|
global-json-file: global.json
|
||||||
|
|
||||||
|
|
@ -41,26 +43,21 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
sudo apt install -y 7zip
|
sudo apt install -y 7zip
|
||||||
|
|
||||||
- name: Install gli
|
- name: Install GLI
|
||||||
run: |
|
uses: actions/setup-gli@v1
|
||||||
mkdir -p $HOME/.bin
|
with:
|
||||||
gh release download -R GreemDev/GLI -O gli -p 'gli-linux-x64'
|
token: ${{ secrets.SETUP_GLI_TOKEN }}
|
||||||
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)" >> $GITHUB_OUTPUT
|
echo "build_version=$(gli get-next-version -m -c Stable -R)" >> $FORGEJO_OUTPUT
|
||||||
else
|
else
|
||||||
echo "build_version=$(gli get-next-version -c Stable -R)" >> $GITHUB_OUTPUT
|
echo "build_version=$(gli get-next-version -c Stable -R)" >> $FORGEJO_OUTPUT
|
||||||
fi
|
fi
|
||||||
echo "prev_build_version=$(gli get-current-version -c Stable -R)" >> $GITHUB_OUTPUT
|
echo "prev_build_version=$(gli get-current-version -c Stable -R)" >> $FORGEJO_OUTPUT
|
||||||
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
echo "git_short_hash=$(git rev-parse --short "${{ forgejo.sha }}")" >> $FORGEJO_OUTPUT
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Configure for release
|
- name: Configure for release
|
||||||
|
|
@ -85,11 +82,7 @@ jobs:
|
||||||
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
|
||||||
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')
|
||||||
|
|
@ -99,11 +92,7 @@ jobs:
|
||||||
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
|
||||||
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')
|
||||||
|
|
@ -138,17 +127,27 @@ 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
|
||||||
|
|
||||||
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
|
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
|
- name: Create release
|
||||||
|
uses: actions/create-release@v1
|
||||||
|
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-24.04
|
runs-on: docker
|
||||||
|
container:
|
||||||
|
image: ghcr.io/catthehacker/ubuntu:act-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- uses: actions/setup-dotnet@v4
|
- uses: actions/setup-dotnet@v5
|
||||||
with:
|
with:
|
||||||
global-json-file: global.json
|
global-json-file: global.json
|
||||||
|
|
||||||
|
|
@ -158,37 +157,28 @@ jobs:
|
||||||
chmod +x llvm.sh
|
chmod +x llvm.sh
|
||||||
sudo ./llvm.sh 17
|
sudo ./llvm.sh 17
|
||||||
|
|
||||||
- name: Install gli
|
- name: Install GLI
|
||||||
run: |
|
uses: actions/setup-gli@v1
|
||||||
mkdir -p $HOME/.bin
|
with:
|
||||||
gh release download -R GreemDev/GLI -O gli -p 'gli-linux-x64'
|
token: ${{ secrets.SETUP_GLI_TOKEN }}
|
||||||
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: |
|
||||||
mkdir -p $HOME/.bin
|
gli ghr -R indygreg/apple-platform-rs -p apple-codesign-*-x86_64-unknown-linux-musl.tar.gz -O apple-codesign.tar.gz
|
||||||
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 $HOME/.bin/
|
mv rcodesign /usr/bin/rcodesign
|
||||||
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)" >> $GITHUB_OUTPUT
|
echo "build_version=$(gli get-next-version -m -c Stable -R)" >> $FORGEJO_OUTPUT
|
||||||
else
|
else
|
||||||
echo "build_version=$(gli get-next-version -c Stable -R)" >> $GITHUB_OUTPUT
|
echo "build_version=$(gli get-next-version -c Stable -R)" >> $FORGEJO_OUTPUT
|
||||||
fi
|
fi
|
||||||
echo "prev_build_version=$(gli get-current-version -c Stable -R)" >> $GITHUB_OUTPUT
|
echo "prev_build_version=$(gli get-current-version -c Stable -R)" >> $FORGEJO_OUTPUT
|
||||||
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
echo "git_short_hash=$(git rev-parse --short "${{ forgejo.sha }}")" >> $FORGEJO_OUTPUT
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Configure for release
|
- name: Configure for release
|
||||||
|
|
@ -201,12 +191,20 @@ jobs:
|
||||||
|
|
||||||
- name: Publish macOS Ryujinx
|
- name: Publish macOS Ryujinx
|
||||||
run: |
|
run: |
|
||||||
./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
|
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
|
||||||
|
|
||||||
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
|
- name: Create release
|
||||||
|
uses: actions/create-release@v1
|
||||||
create_gitlab_release:
|
with:
|
||||||
name: Create GitLab Release
|
name: "${{ steps.version_info.outputs.build_version }}"
|
||||||
|
repository: "projects/Ryubing"
|
||||||
|
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-24.04
|
runs-on: ubuntu-24.04
|
||||||
needs:
|
needs:
|
||||||
- macos_release
|
- macos_release
|
||||||
|
|
@ -214,36 +212,26 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install gli
|
- name: Install GLI
|
||||||
run: |
|
uses: actions/setup-gli@v1
|
||||||
mkdir -p $HOME/.bin
|
with:
|
||||||
gh release download -R GreemDev/GLI -O gli -p 'gli-linux-x64'
|
token: ${{ secrets.SETUP_GLI_TOKEN }}
|
||||||
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)" >> $GITHUB_OUTPUT
|
echo "build_version=$(gli get-next-version -m -c Stable -R)" >> $FORGEJO_OUTPUT
|
||||||
else
|
else
|
||||||
echo "build_version=$(gli get-next-version -c Stable -R)" >> $GITHUB_OUTPUT
|
echo "build_version=$(gli get-next-version -c Stable -R)" >> $FORGEJO_OUTPUT
|
||||||
fi
|
fi
|
||||||
echo "prev_build_version=$(gli get-current-version -c Stable -R)" >> $GITHUB_OUTPUT
|
echo "prev_build_version=$(gli get-current-version -c Stable -R)" >> $FORGEJO_OUTPUT
|
||||||
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
echo "git_short_hash=$(git rev-parse --short "${{ forgejo.sha }}")" >> $FORGEJO_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.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
|
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
|
||||||
|
|
||||||
- name: Notify update server of new builds
|
- name: Notify update server of new builds
|
||||||
run: |
|
run: |
|
||||||
86
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
86
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
|
@ -1,86 +0,0 @@
|
||||||
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
5
.github/ISSUE_TEMPLATE/config.yml
vendored
|
|
@ -1,5 +0,0 @@
|
||||||
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.
|
|
||||||
31
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
31
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
|
|
@ -1,31 +0,0 @@
|
||||||
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
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
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
|
|
||||||
25
.github/ISSUE_TEMPLATE/missing_service_call.yml
vendored
25
.github/ISSUE_TEMPLATE/missing_service_call.yml
vendored
|
|
@ -1,25 +0,0 @@
|
||||||
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
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
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
|
|
||||||
168
.github/workflows/build.yml
vendored
168
.github/workflows/build.yml
vendored
|
|
@ -1,168 +0,0 @@
|
||||||
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'
|
|
||||||
25
.github/workflows/checks.yml
vendored
25
.github/workflows/checks.yml
vendored
|
|
@ -1,25 +0,0 @@
|
||||||
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
|
|
||||||
|
|
@ -3,26 +3,26 @@
|
||||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageVersion Include="Avalonia" Version="11.3.6" />
|
<PackageVersion Include="Avalonia" Version="11.3.12" />
|
||||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.6" />
|
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.12" />
|
||||||
<PackageVersion Include="Avalonia.Desktop" Version="11.3.6" />
|
<PackageVersion Include="Avalonia.Desktop" Version="11.3.12" />
|
||||||
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.6" />
|
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.12" />
|
||||||
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.3.6" />
|
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.3.12" />
|
||||||
<PackageVersion Include="Svg.Controls.Avalonia" Version="11.3.6.2" />
|
<PackageVersion Include="Svg.Controls.Avalonia" Version="11.3.9.4" />
|
||||||
<PackageVersion Include="Svg.Controls.Skia.Avalonia" Version="11.3.6.2" />
|
<PackageVersion Include="Svg.Controls.Skia.Avalonia" Version="11.3.9.4" />
|
||||||
<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.6" />
|
<PackageVersion Include="Microsoft.Build.Utilities.Core" Version="17.12.6" />
|
||||||
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
|
<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="ppy.SDL3-CS" Version="2025.920.0" />
|
<PackageVersion Include="ppy.SDL3-CS" Version="2026.320.0" />
|
||||||
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
||||||
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
<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.1" />
|
<PackageVersion Include="DynamicData" Version="9.4.1" />
|
||||||
<PackageVersion Include="FluentAvaloniaUI.NoAnim" Version="2.4.0-build3" />
|
<PackageVersion Include="FluentAvaloniaUI" Version="2.5.0" />
|
||||||
<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" />
|
||||||
|
|
@ -41,7 +41,7 @@
|
||||||
<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.Graphics.Nvdec.Dependencies.AllArch" Version="6.1.2-build3" />
|
||||||
<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.126" />
|
<PackageVersion Include="Ryujinx.LibHac" Version="0.21.0-alpha.129" />
|
||||||
<PackageVersion Include="Ryujinx.UpdateClient" Version="1.0.44" />
|
<PackageVersion Include="Ryujinx.UpdateClient" Version="1.0.44" />
|
||||||
<PackageVersion Include="Ryujinx.Systems.Update.Common" Version="1.0.44" />
|
<PackageVersion Include="Ryujinx.Systems.Update.Common" Version="1.0.44" />
|
||||||
<PackageVersion Include="Gommon" Version="2.8.0.1" />
|
<PackageVersion Include="Gommon" Version="2.8.0.1" />
|
||||||
|
|
@ -56,7 +56,6 @@
|
||||||
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.9" />
|
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" 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.2" />
|
<PackageVersion Include="System.IO.Hashing" Version="9.0.2" />
|
||||||
<PackageVersion Include="System.Management" Version="9.0.2" />
|
|
||||||
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
24
README.md
24
README.md
|
|
@ -7,8 +7,8 @@
|
||||||
|
|
||||||
# Ryujinx
|
# Ryujinx
|
||||||
|
|
||||||
[](https://update.ryujinx.app/latest/stable)
|
[](https://update.ryujinx.app/latest/stable)
|
||||||
[](https://update.ryujinx.app/latest/canary)
|
[](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 GitLab instance under the <a href="https://git.ryujinx.app/ryubing/ryujinx/-/blob/master/LICENSE.txt?ref_type=heads" target="_blank">MIT license</a>.
|
Ryujinx is available on a self-managed <a href="https://github.com/Ryubing/forgejo" target="_blank">modified</a> <a href="https://forgejo.org/" target="_blank">Forgejo</a> instance under the <a href="https://git.ryujinx.app/projects/Ryubing/src/branch/master/LICENSE.txt" 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/groups/ryubing/-/wikis/home">Wiki tab</a>.
|
Guides and documentation can be found on the <a href="https://git.ryujinx.app/projects/Ryubing/wiki/Home">Wiki tab</a>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="https://git.ryujinx.app/ryubing/ryujinx/-/raw/master/docs/shell.png?ref_type=heads&inline=false" alt="Ryujinx example">
|
<img src="https://git.ryujinx.app/projects/Ryubing/raw/branch/master/docs/shell.png" 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/ryubing/ryujinx/-/releases).
|
You can find the stable releases [here](https://git.ryujinx.app/projects/Ryubing/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](docs/README.md).
|
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).
|
||||||
|
|
||||||
## 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](LICENSE.txt).
|
This software is licensed under the terms of the [MIT license](https://git.ryujinx.app/projects/Ryubing/src/branch/master/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](LICENSE.txt) and [THIRDPARTY.md](distribution/legal/THIRDPARTY.md) for more details.
|
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.
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
- [LibHac](https://git.ryujinx.app/ryubing/libhac) is used for our file-system.
|
- [LibHac](https://git.ryujinx.app/projects/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.
|
||||||
|
|
|
||||||
16
Ryujinx.sln
16
Ryujinx.sln
|
|
@ -86,11 +86,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
|
||||||
.github\workflows\build.yml = .github\workflows\build.yml
|
.forgejo\workflows\build.yml = .forgejo\workflows\build.yml
|
||||||
.github\workflows\canary.yml = .github\workflows\canary.yml
|
.forgejo\workflows\canary.yml = .forgejo\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
|
||||||
.github\workflows\release.yml = .github\workflows\release.yml
|
.forgejo\workflows\release.yml = .forgejo\workflows\release.yml
|
||||||
nuget.config = nuget.config
|
nuget.config = nuget.config
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
|
|
@ -573,6 +573,16 @@ Global
|
||||||
{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.ActiveCfg = Debug|Any CPU
|
||||||
{AC26EFF0-8593-4184-9A09-98E37EFFB32E}.Debug|Any CPU.Build.0 = 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
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,8 @@
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "",
|
"zh_CN": "启动 RenderDoc 帧捕获",
|
||||||
"zh_TW": ""
|
"zh_TW": "啟動 RenderDoc 畫格擷取"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -46,8 +46,8 @@
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "",
|
"zh_CN": "结束 RenderDoc 帧捕获",
|
||||||
"zh_TW": ""
|
"zh_TW": "停止 RenderDoc 畫格擷取"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -71,8 +71,8 @@
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "",
|
"zh_CN": "丢弃 RenderDoc 帧捕获",
|
||||||
"zh_TW": ""
|
"zh_TW": "捨棄 RenderDoc 畫格擷取"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -96,8 +96,8 @@
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "",
|
"zh_CN": "结束当前正在进行的 RenderDoc 帧捕获,并立即丢弃其结果。",
|
||||||
"zh_TW": ""
|
"zh_TW": "停止正在執行的 RenderDoc 畫格擷取,且立即捨棄其結果。"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -575,6 +575,31 @@
|
||||||
"zh_TW": "停止模擬"
|
"zh_TW": "停止模擬"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ID": "MenuBarOptionsRestartEmulation",
|
||||||
|
"Translations": {
|
||||||
|
"ar_SA": "",
|
||||||
|
"de_DE": "",
|
||||||
|
"el_GR": "",
|
||||||
|
"en_US": "Restart Emulation",
|
||||||
|
"es_ES": "Reiniciar Emulación",
|
||||||
|
"fr_FR": "Redémarrer l'Émulation",
|
||||||
|
"he_IL": "",
|
||||||
|
"it_IT": "",
|
||||||
|
"ja_JP": "",
|
||||||
|
"ko_KR": "",
|
||||||
|
"no_NO": "",
|
||||||
|
"pl_PL": "",
|
||||||
|
"pt_BR": "",
|
||||||
|
"ru_RU": "",
|
||||||
|
"sv_SE": "Starta om emulering",
|
||||||
|
"th_TH": "",
|
||||||
|
"tr_TR": "",
|
||||||
|
"uk_UA": "",
|
||||||
|
"zh_CN": "",
|
||||||
|
"zh_TW": "重新啟動模擬"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ID": "MenuBarOptionsSettings",
|
"ID": "MenuBarOptionsSettings",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
|
|
@ -700,6 +725,56 @@
|
||||||
"zh_TW": "掃描 Amiibo"
|
"zh_TW": "掃描 Amiibo"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ID": "MenuBarActionsScanSkylander",
|
||||||
|
"Translations": {
|
||||||
|
"ar_SA": "فحص Skylander",
|
||||||
|
"de_DE": "Skylander scannen",
|
||||||
|
"el_GR": "Σάρωση Skylander",
|
||||||
|
"en_US": "Scan A Skylander",
|
||||||
|
"es_ES": "Escanear Skylander",
|
||||||
|
"fr_FR": "Scanner un Skylander",
|
||||||
|
"he_IL": "סרוק אמיבו",
|
||||||
|
"it_IT": "Scansiona un Skylander",
|
||||||
|
"ja_JP": "Skylander をスキャン",
|
||||||
|
"ko_KR": "Skylander 스캔",
|
||||||
|
"no_NO": "Skann en Skylander",
|
||||||
|
"pl_PL": "Skanuj Skylander",
|
||||||
|
"pt_BR": "Escanear um Skylander",
|
||||||
|
"ru_RU": "Сканировать Skylander",
|
||||||
|
"sv_SE": "Skanna en Skylander",
|
||||||
|
"th_TH": "สแกนหา Skylander",
|
||||||
|
"tr_TR": "Bir Skylander Tara",
|
||||||
|
"uk_UA": "Сканувати Skylander",
|
||||||
|
"zh_CN": "扫描 Skylander",
|
||||||
|
"zh_TW": "掃描 Skylander"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ID": "MenuBarActionsRemoveSkylander",
|
||||||
|
"Translations": {
|
||||||
|
"ar_SA": "إزالة Skylander",
|
||||||
|
"de_DE": "Skylander entfernen",
|
||||||
|
"el_GR": "Αφαίρεση Skylander",
|
||||||
|
"en_US": "Remove Skylander",
|
||||||
|
"es_ES": "Eliminar Skylander",
|
||||||
|
"fr_FR": "Supprimer un Skylander",
|
||||||
|
"he_IL": "הסר Skylander",
|
||||||
|
"it_IT": "Rimuovi Skylander",
|
||||||
|
"ja_JP": "Skylander を削除",
|
||||||
|
"ko_KR": "Skylander 제거",
|
||||||
|
"no_NO": "Fjern Skylander",
|
||||||
|
"pl_PL": "Usuń Skylander",
|
||||||
|
"pt_BR": "Remover um Skylander",
|
||||||
|
"ru_RU": "Удалить Skylander",
|
||||||
|
"sv_SE": "Ta bort Skylander",
|
||||||
|
"th_TH": "ลบ Skylander",
|
||||||
|
"tr_TR": "Skylander'ı Kaldır",
|
||||||
|
"uk_UA": "Видалити Skylander",
|
||||||
|
"zh_CN": "移除 Skylander",
|
||||||
|
"zh_TW": "移除 Skylander"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ID": "MenuBarActionsScanAmiiboBin",
|
"ID": "MenuBarActionsScanAmiiboBin",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
|
|
@ -11250,6 +11325,31 @@
|
||||||
"zh_TW": "刪除"
|
"zh_TW": "刪除"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ID": "UserProfilesSave",
|
||||||
|
"Translations": {
|
||||||
|
"ar_SA": "",
|
||||||
|
"de_DE": "Speichern",
|
||||||
|
"el_GR": "",
|
||||||
|
"en_US": "Save",
|
||||||
|
"es_ES": "Guardar",
|
||||||
|
"fr_FR": "Sauvegarder",
|
||||||
|
"he_IL": "",
|
||||||
|
"it_IT": "",
|
||||||
|
"ja_JP": "",
|
||||||
|
"ko_KR": "",
|
||||||
|
"no_NO": "",
|
||||||
|
"pl_PL": "",
|
||||||
|
"pt_BR": "",
|
||||||
|
"ru_RU": "",
|
||||||
|
"sv_SE": "Spara",
|
||||||
|
"th_TH": "",
|
||||||
|
"tr_TR": "",
|
||||||
|
"uk_UA": "",
|
||||||
|
"zh_CN": "",
|
||||||
|
"zh_TW": "儲存"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ID": "UserProfilesClose",
|
"ID": "UserProfilesClose",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
|
|
@ -15251,28 +15351,28 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ID": "AboutGitLabUrlTooltipMessage",
|
"ID": "AboutForgejoUrlTooltipMessage",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "انقر لفتح صفحة ريوجينكس في غيت هاب في متصفحك الافتراضي.",
|
"ar_SA": "انقر لفتح صفحة ريوجينكس في غيت هاب في متصفحك الافتراضي.",
|
||||||
"de_DE": "Klicke hier, um die Ryujinx GitLab Seite im Standardbrowser zu öffnen.",
|
"de_DE": "Klicke hier, um die Ryujinx Forgejo Seite im Standardbrowser zu öffnen.",
|
||||||
"el_GR": "Κάντε κλικ για να ανοίξετε τη σελίδα Ryujinx GitLab στο προεπιλεγμένο πρόγραμμα περιήγησης.",
|
"el_GR": "Κάντε κλικ για να ανοίξετε τη σελίδα Ryujinx Forgejo στο προεπιλεγμένο πρόγραμμα περιήγησης.",
|
||||||
"en_US": "Click to open the Ryujinx GitLab page in your default browser.",
|
"en_US": "Click to open the Ryujinx Forgejo page in your default browser.",
|
||||||
"es_ES": "Haz clic para abrir el GitLab de Ryujinx en tu navegador predeterminado.",
|
"es_ES": "Haz clic para abrir el Forgejo de Ryujinx en tu navegador predeterminado.",
|
||||||
"fr_FR": "Cliquez pour ouvrir la page GitLab de Ryujinx dans votre navigateur par défaut.",
|
"fr_FR": "Cliquez pour ouvrir la page Forgejo de Ryujinx dans votre navigateur par défaut.",
|
||||||
"he_IL": "לחץ כדי לפתוח את דף הגיטהב של ריוג'ינקס בדפדפן ברירת המחדל שלך.",
|
"he_IL": "לחץ כדי לפתוח את דף הגיטהב של ריוג'ינקס בדפדפן ברירת המחדל שלך.",
|
||||||
"it_IT": "Clicca per aprire la pagina GitLab di Ryujinx nel tuo browser predefinito.",
|
"it_IT": "Clicca per aprire la pagina Forgejo di Ryujinx nel tuo browser predefinito.",
|
||||||
"ja_JP": "クリックするとデフォルトのブラウザで Ryujinx の GitLab ページを開きます.",
|
"ja_JP": "クリックするとデフォルトのブラウザで Ryujinx の Forgejo ページを開きます.",
|
||||||
"ko_KR": "클릭하면 기본 브라우저에서 Ryujinx GitLab 페이지가 열립니다.",
|
"ko_KR": "클릭하면 기본 브라우저에서 Ryujinx Forgejo 페이지가 열립니다.",
|
||||||
"no_NO": "Klikk for å åpne Ryujinx GitLab siden i din standardnettleser.",
|
"no_NO": "Klikk for å åpne Ryujinx Forgejo siden i din standardnettleser.",
|
||||||
"pl_PL": "Kliknij, aby otworzyć stronę GitLab Ryujinx w domyślnej przeglądarce.",
|
"pl_PL": "Kliknij, aby otworzyć stronę Forgejo Ryujinx w domyślnej przeglądarce.",
|
||||||
"pt_BR": "Clique para abrir a página do GitLab do Ryujinx no seu navegador padrão.",
|
"pt_BR": "Clique para abrir a página do Forgejo do Ryujinx no seu navegador padrão.",
|
||||||
"ru_RU": "Нажмите, чтобы открыть страницу Ryujinx на GitLab",
|
"ru_RU": "Нажмите, чтобы открыть страницу Ryujinx на Forgejo",
|
||||||
"sv_SE": "Klicka för att öppna Ryujinx GitLab-sida i din webbläsare.",
|
"sv_SE": "Klicka för att öppna Ryujinx Forgejo-sida i din webbläsare.",
|
||||||
"th_TH": "คลิกเพื่อเปิดหน้า GitLab ของ Ryujinx บนเบราว์เซอร์เริ่มต้นของคุณ",
|
"th_TH": "คลิกเพื่อเปิดหน้า Forgejo ของ Ryujinx บนเบราว์เซอร์เริ่มต้นของคุณ",
|
||||||
"tr_TR": "Ryujinx'in GitLab sayfasını varsayılan tarayıcınızda açmak için tıklayın.",
|
"tr_TR": "Ryujinx'in Forgejo sayfasını varsayılan tarayıcınızda açmak için tıklayın.",
|
||||||
"uk_UA": "Натисніть, щоб відкрити сторінку GitLab Ryujinx у браузері.",
|
"uk_UA": "Натисніть, щоб відкрити сторінку Forgejo Ryujinx у браузері.",
|
||||||
"zh_CN": "在浏览器中打开 Ryujinx 的 GitLab 代码库。",
|
"zh_CN": "在浏览器中打开 Ryujinx 的 Forgejo 代码库。",
|
||||||
"zh_TW": "在預設瀏覽器中開啟 Ryujinx 的 GitLab 網頁。"
|
"zh_TW": "在預設瀏覽器中開啟 Ryujinx 的 Forgejo 網頁。"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -15331,23 +15431,23 @@
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Ryujinx is an emulator for the Nintendo Switch™ 1.\nGet all the latest news in our Discord.\nDevelopers interested in contributing can find out more on our GitLab or Discord.",
|
"en_US": "Ryujinx is an emulator for the Nintendo Switch™ 1.\nGet all the latest news in our Discord.\nDevelopers interested in contributing can find out more on our Forgejo or Discord.",
|
||||||
"es_ES": "Ryujinx es un emulador para Nintendo Switch™ 1.\nObtén todas las novedades en nuestro Discord.\nLos desarrolladores interesados en contribuir pueden obtener más información en nuestro GitLab o Discord.",
|
"es_ES": "Ryujinx es un emulador para Nintendo Switch™ 1.\nObtén todas las novedades en nuestro Discord.\nLos desarrolladores interesados en contribuir pueden obtener más información en nuestro Forgejo o Discord.",
|
||||||
"fr_FR": "Ryujinx est un émulateur pour la Nintendo Switch™ 1.\nObtenez les dernières nouvelles sur notre Discord.\nLes développeurs souhaitant contribuer peuvent en savoir plus sur notre GitLab ou Discord.",
|
"fr_FR": "Ryujinx est un émulateur pour la Nintendo Switch™ 1.\nObtenez les dernières nouvelles sur notre Discord.\nLes développeurs souhaitant contribuer peuvent en savoir plus sur notre Forgejo ou Discord.",
|
||||||
"he_IL": "",
|
"he_IL": "",
|
||||||
"it_IT": "Ryujinx è un emulatore della console Nintendo Switch™ 1.\nRimani aggiornato sulle ultime novità nel nostro server Discord.\nGli sviluppatori interessati a contribuire possono trovare maggiori informazioni su Discord o sulla nostra pagina GitLab.",
|
"it_IT": "Ryujinx è un emulatore della console Nintendo Switch™ 1.\nRimani aggiornato sulle ultime novità nel nostro server Discord.\nGli sviluppatori interessati a contribuire possono trovare maggiori informazioni su Discord o sulla nostra pagina Forgejo.",
|
||||||
"ja_JP": "",
|
"ja_JP": "",
|
||||||
"ko_KR": "Ryujinx는 Nintendo Switch™ 1용 에뮬레이터입니다.\n모든 최신 소식을 Discord에서 확인하세요.\n기여에 관심이 있는 개발자는 GitLab 또는 Discord에서 자세한 내용을 확인할 수 있습니다.",
|
"ko_KR": "Ryujinx는 Nintendo Switch™ 1용 에뮬레이터입니다.\n모든 최신 소식을 Discord에서 확인하세요.\n기여에 관심이 있는 개발자는 Forgejo 또는 Discord에서 자세한 내용을 확인할 수 있습니다.",
|
||||||
"no_NO": "Ryujinx er en emulator for Nintendo Switch™ 1\nVennligst støtt oss på Patreon.\nFå alle de siste nyhetene på vår Twitter eller Discord.\nUtviklere som er interessert i å bidra kan finne ut mer på GitLab eller Discord.",
|
"no_NO": "Ryujinx er en emulator for Nintendo Switch™ 1\nVennligst støtt oss på Patreon.\nFå alle de siste nyhetene på vår Twitter eller Discord.\nUtviklere som er interessert i å bidra kan finne ut mer på Forgejo eller Discord.",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Ryujinx é um emulador de Nintendo Switch™ 1.\nReceba todas as últimas notícias em nosso Discord.\nDesenvolvedores interessados em contribuir podem descobrir mais em nosso GitLab ou Discord.",
|
"pt_BR": "Ryujinx é um emulador de Nintendo Switch™ 1.\nReceba todas as últimas notícias em nosso Discord.\nDesenvolvedores interessados em contribuir podem descobrir mais em nosso Forgejo ou Discord.",
|
||||||
"ru_RU": "Ryujinx - это эмулятор для Nintendo Switch™ 1.\nПолучайте все последние новости разработки в нашем Discord.\nРазработчики, заинтересованные в участии, могут узнать больше на нашем GitLab или Discord.",
|
"ru_RU": "Ryujinx - это эмулятор для Nintendo Switch™ 1.\nПолучайте все последние новости разработки в нашем Discord.\nРазработчики, заинтересованные в участии, могут узнать больше на нашем Forgejo или Discord.",
|
||||||
"sv_SE": "Ryujinx är en emulator för Nintendo Switch™ 1.\nFå de senaste nyheterna via vår Discord.\nUtvecklare som är intresserade att bidra kan hitta mer info på vår GitLab eller Discord.",
|
"sv_SE": "Ryujinx är en emulator för Nintendo Switch™ 1.\nFå de senaste nyheterna via vår Discord.\nUtvecklare som är intresserade att bidra kan hitta mer info på vår Forgejo eller Discord.",
|
||||||
"th_TH": "Ryujinx เป็นโปรแกรมจำลองสำหรับเครื่อง Nintendo Switch™ 1\nติดตามข่าวสารล่าสุดได้ที่ Discord ของเรา\nนักพัฒนาที่สนใจร่วมพัฒนา สามารถดูข้อมูลเพิ่มเติมได้ทาง GitLab หรือ Discord",
|
"th_TH": "Ryujinx เป็นโปรแกรมจำลองสำหรับเครื่อง Nintendo Switch™ 1\nติดตามข่าวสารล่าสุดได้ที่ Discord ของเรา\nนักพัฒนาที่สนใจร่วมพัฒนา สามารถดูข้อมูลเพิ่มเติมได้ทาง Forgejo หรือ Discord",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "Ryujinx — це емулятор для Nintendo Switch™ 1.\nОстанні новини можна отримати в нашому Discord.\nРозробники, що бажають долучитись до розробки та зробити свій внесок, можуть отримати більше інформації на нашому GitLab або в Discord.",
|
"uk_UA": "Ryujinx — це емулятор для Nintendo Switch™ 1.\nОстанні новини можна отримати в нашому Discord.\nРозробники, що бажають долучитись до розробки та зробити свій внесок, можуть отримати більше інформації на нашому Forgejo або в Discord.",
|
||||||
"zh_CN": "Ryujinx 是一个 Nintendo Switch™ 1 模拟器。\n有兴趣做出贡献的开发者可以在我们的 GitLab 或 Discord 上了解更多信息。\n",
|
"zh_CN": "Ryujinx 是一个 Nintendo Switch™ 1 模拟器。\n有兴趣做出贡献的开发者可以在我们的 Forgejo 或 Discord 上了解更多信息。\n",
|
||||||
"zh_TW": "Ryujinx 是一款 Nintendo Switch™ 1 模擬器。\n關注我們的 Discord 取得所有最新消息。\n對於有興趣貢獻的開發者,可以在我們的 GitLab 或 Discord 上了解更多資訊。"
|
"zh_TW": "Ryujinx 是一款 Nintendo Switch™ 1 模擬器。\n關注我們的 Discord 取得所有最新消息。\n對於有興趣貢獻的開發者,可以在我們的 Forgejo 或 Discord 上了解更多資訊。"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -22,5 +22,5 @@ chmod +x AppDir/AppRun AppDir/usr/bin/Ryujinx*
|
||||||
|
|
||||||
mkdir -p "$OUTDIR"
|
mkdir -p "$OUTDIR"
|
||||||
|
|
||||||
appimagetool -n --comp zstd --mksquashfs-opt -Xcompression-level --mksquashfs-opt 21 \
|
appimagetool --appimage-extract-and-run -n --comp zstd --mksquashfs-opt -Xcompression-level --mksquashfs-opt 21 \
|
||||||
AppDir "$OUTDIR"/Ryujinx.AppImage
|
AppDir "$OUTDIR"/Ryujinx.AppImage
|
||||||
120
distribution/macos/create_macos_pr_build_ava.sh
Normal file
120
distribution/macos/create_macos_pr_build_ava.sh
Normal file
|
|
@ -0,0 +1,120 @@
|
||||||
|
#!/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"
|
||||||
|
|
@ -2050,7 +2050,9 @@
|
||||||
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
|
||||||
|
|
@ -2638,6 +2640,7 @@
|
||||||
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
|
||||||
|
|
@ -3307,6 +3310,7 @@
|
||||||
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
|
||||||
|
|
|
||||||
|
|
|
@ -5,8 +5,7 @@
|
||||||
<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/v4/projects/17/packages/nuget/index.json" />
|
<add key="LibHacAlpha" value="https://git.ryujinx.app/api/packages/projects/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 -->
|
||||||
|
|
@ -14,10 +13,6 @@
|
||||||
<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>
|
||||||
|
|
|
||||||
|
|
@ -107,12 +107,12 @@ namespace Ryujinx.BuildValidationTasks
|
||||||
{
|
{
|
||||||
locale.Translations[langCode] = string.Empty;
|
locale.Translations[langCode] = string.Empty;
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
$"Lanugage '{langCode}' is a duplicate of en_US in Locale '{locale.ID}'! Resetting it...");
|
$"Language '{langCode}' is a duplicate of en_US in Locale '{locale.ID}'! Resetting it...");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
$"Lanugage '{langCode}' is a duplicate of en_US in Locale '{locale.ID}'!");
|
$"Language '{langCode}' is a duplicate of en_US in Locale '{locale.ID}'!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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/ryubing/ryujinx/-/compare/Canary-{currentVersion}...Canary-{newVersion}"
|
? $"https://git.ryujinx.app/projects/Ryubing/compare/Canary-{currentVersion}...Canary-{newVersion}"
|
||||||
: $"https://git.ryujinx.app/ryubing/ryujinx/-/releases/{newVersion}";
|
: $"https://git.ryujinx.app/projects/Ryubing/releases/tag/{newVersion}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@
|
||||||
<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>
|
||||||
|
|
|
||||||
|
|
@ -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/ryubing/ryujinx/-/wikis/FAQ-&-Troubleshooting";
|
public const string FaqWikiUrl = "https://git.ryujinx.app/projects/Ryubing/wiki/FAQ-%26-Troubleshooting";
|
||||||
|
|
||||||
public const string SetupGuideWikiUrl =
|
public const string SetupGuideWikiUrl =
|
||||||
"https://git.ryujinx.app/ryubing/ryujinx/-/wikis/Setup-&-Configuration-Guide";
|
"https://git.ryujinx.app/projects/Ryubing/wiki/Setup-%26-Configuration-Guide";
|
||||||
|
|
||||||
public const string MultiplayerWikiUrl =
|
public const string MultiplayerWikiUrl =
|
||||||
"https://git.ryujinx.app/ryubing/ryujinx/-/wikis/Multiplayer-(LDN-Local-Wireless)-Guide";
|
"https://git.ryujinx.app/projects/Ryubing/wiki/Multiplayer-(LDN-Local-Wireless)-Guide";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -184,6 +184,7 @@ 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
|
||||||
"01001cc01b2d4000", // Goat Simulator 3
|
"01001cc01b2d4000", // Goat Simulator 3
|
||||||
"01003620068ea000", // Hand of Fate 2
|
"01003620068ea000", // Hand of Fate 2
|
||||||
"0100f7e00c70e000", // Hogwarts Legacy
|
"0100f7e00c70e000", // Hogwarts Legacy
|
||||||
|
|
@ -193,9 +194,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
|
||||||
|
"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
|
||||||
"0100e8300a67a000", // Risk
|
"0100e8300a67a000", // Risk
|
||||||
"01002f7013224000", // Rune Factory 5
|
"01002f7013224000", // Rune Factory 5
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,11 @@ 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;
|
||||||
|
|
||||||
[DllImport("libc", SetLastError = true)]
|
[LibraryImport("libc", SetLastError = true)]
|
||||||
private static extern int prctl(int option, int arg2);
|
private static partial int prctl(int option, int arg2);
|
||||||
|
|
||||||
public static void SetCoreDumpable(bool dumpable)
|
public static void SetCoreDumpable(bool dumpable)
|
||||||
{
|
{
|
||||||
|
|
@ -36,5 +37,13 @@ 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -488,6 +488,8 @@ 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
|
||||||
|
|
@ -497,17 +499,20 @@ 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));
|
||||||
|
|
||||||
if (File.Exists(destPath))
|
|
||||||
File.Delete(destPath);
|
|
||||||
|
|
||||||
File.Copy(filePath, destPath, true);
|
File.Copy(filePath, destPath, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (failedFiles.Count > 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Failed to install the following key files: {string.Join(", ", failedFiles)}");
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -518,8 +523,6 @@ 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");
|
||||||
|
|
||||||
|
|
@ -534,10 +537,6 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1059,7 +1058,7 @@ namespace Ryujinx.HLE.FileSystem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool AreKeysAlredyPresent(string pathToCheck)
|
public static bool AreKeysAlreadyPresent(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)
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ 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;
|
||||||
|
|
@ -66,6 +67,8 @@ 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; }
|
||||||
|
|
@ -132,6 +135,7 @@ 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.
|
||||||
|
|
@ -242,21 +246,21 @@ namespace Ryujinx.HLE.HOS
|
||||||
public void InitializeServices()
|
public void InitializeServices()
|
||||||
{
|
{
|
||||||
SmRegistry = new SmRegistry();
|
SmRegistry = new SmRegistry();
|
||||||
SmServer = new ServerBase(KernelContext, "SmServer", () => new IUserInterface(KernelContext, SmRegistry));
|
SmServer = new ServerBase(KernelContext, "Sm", () => 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, "BsdServer");
|
BsdServer = new ServerBase(KernelContext, "Bsd");
|
||||||
FsServer = new ServerBase(KernelContext, "FsServer");
|
FsServer = new ServerBase(KernelContext, "Fs");
|
||||||
HidServer = new ServerBase(KernelContext, "HidServer");
|
HidServer = new ServerBase(KernelContext, "Hid");
|
||||||
NvDrvServer = new ServerBase(KernelContext, "NvservicesServer");
|
NvDrvServer = new ServerBase(KernelContext, "Nv");
|
||||||
TimeServer = new ServerBase(KernelContext, "TimeServer");
|
TimeServer = new ServerBase(KernelContext, "Time");
|
||||||
ViServer = new ServerBase(KernelContext, "ViServerU");
|
ViServer = new ServerBase(KernelContext, "Vi:u");
|
||||||
ViServerM = new ServerBase(KernelContext, "ViServerM");
|
ViServerM = new ServerBase(KernelContext, "Vi:m");
|
||||||
ViServerS = new ServerBase(KernelContext, "ViServerS");
|
ViServerS = new ServerBase(KernelContext, "Vi:s");
|
||||||
LdnServer = new ServerBase(KernelContext, "LdnServer");
|
LdnServer = new ServerBase(KernelContext, "Ldn");
|
||||||
|
|
||||||
StartNewServices();
|
StartNewServices();
|
||||||
}
|
}
|
||||||
|
|
@ -282,7 +286,7 @@ namespace Ryujinx.HLE.HOS
|
||||||
ProcessCreationFlags.Is64Bit |
|
ProcessCreationFlags.Is64Bit |
|
||||||
ProcessCreationFlags.PoolPartitionSystem;
|
ProcessCreationFlags.PoolPartitionSystem;
|
||||||
|
|
||||||
ProcessCreationInfo creationInfo = new("Service", 1, 0, 0x8000000, 1, Flags, 0, 0);
|
ProcessCreationInfo creationInfo = new(service.Name, 1, 0, 0x8000000, 1, Flags, 0, 0);
|
||||||
|
|
||||||
uint[] defaultCapabilities =
|
uint[] defaultCapabilities =
|
||||||
[
|
[
|
||||||
|
|
@ -372,6 +376,15 @@ 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;
|
||||||
|
|
@ -389,6 +402,53 @@ 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();
|
||||||
|
|
|
||||||
|
|
@ -118,8 +118,11 @@ namespace Ryujinx.HLE.HOS.Services.Caps
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: The saved JPEG file doesn't have the limitation in the extra EXIF data.
|
// 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));
|
using SKBitmap bitmap = new(new SKImageInfo(1280, 720, SKColorType.Rgba8888, SKAlphaType.Premul));
|
||||||
Marshal.Copy(screenshotData, 0, bitmap.GetPixels(), screenshotData.Length);
|
int dataLen = screenshotData.Length > bitmap.ByteCount ? bitmap.ByteCount : screenshotData.Length;
|
||||||
|
|
||||||
|
Marshal.Copy(screenshotData, 0, bitmap.GetPixels(), dataLen);
|
||||||
|
|
||||||
using SKData data = bitmap.Encode(SKEncodedImageFormat.Jpeg, 80);
|
using SKData data = bitmap.Encode(SKEncodedImageFormat.Jpeg, 80);
|
||||||
using FileStream file = File.OpenWrite(filePath);
|
using FileStream file = File.OpenWrite(filePath);
|
||||||
data.SaveTo(file);
|
data.SaveTo(file);
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ 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)
|
||||||
|
|
@ -580,6 +581,29 @@ 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)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -602,19 +602,33 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||||
}
|
}
|
||||||
|
|
||||||
[CommandCmif(82)]
|
[CommandCmif(82)]
|
||||||
// IsSixAxisSensorAtRest(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) -> bool IsAsRest
|
// IsSixAxisSensorAtRest(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) -> bool IsAtRest
|
||||||
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();
|
||||||
|
|
||||||
bool isAtRest = true;
|
// TODO: link to context.Device.Hid.Npads.SixAxisActive when properly implemented
|
||||||
|
// 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -629,7 +643,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,19 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
477
src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/IMifare.cs
Normal file
477
src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/MifareManager/IMifare.cs
Normal file
|
|
@ -0,0 +1,477 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x18)]
|
||||||
|
struct NfcMifareReadBlockParameter
|
||||||
|
{
|
||||||
|
public byte SectorNumber;
|
||||||
|
public Array7<byte> Reserved;
|
||||||
|
public NfcSectorKey SectorKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x28)]
|
||||||
|
struct NfcMifareWriteBlockParameter
|
||||||
|
{
|
||||||
|
public Array16<byte> Data;
|
||||||
|
public byte SectorNumber;
|
||||||
|
public Array7<byte> Reserved;
|
||||||
|
public NfcSectorKey SectorKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
|
||||||
|
{
|
||||||
|
enum NfcProtocol : byte
|
||||||
|
{
|
||||||
|
NfcProtocol_None = 0b_0000_0000,
|
||||||
|
NfcProtocol_TypeA = 0b_0000_0001, ///< ISO14443A
|
||||||
|
NfcProtocol_TypeB = 0b_0000_0010, ///< ISO14443B
|
||||||
|
NfcProtocol_TypeF = 0b_0000_0100, ///< Sony FeliCa
|
||||||
|
NfcProtocol_All = 0xFF,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
|
||||||
|
struct NfcSectorKey
|
||||||
|
{
|
||||||
|
public NfcMifareCommand MifareCommand;
|
||||||
|
public byte Unknown;
|
||||||
|
public Array6<byte> Reserved1;
|
||||||
|
public Array6<byte> SectorKey;
|
||||||
|
public Array2<byte> Reserved2;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
|
||||||
|
{
|
||||||
|
enum NfcTagType : byte
|
||||||
|
{
|
||||||
|
NfcTagType_None = 0b_0000_0000,
|
||||||
|
NfcTagType_Type1 = 0b_0000_0001, ///< ISO14443A RW. Topaz
|
||||||
|
NfcTagType_Type2 = 0b_0000_0010, ///< ISO14443A RW. Ultralight, NTAGX, ST25TN
|
||||||
|
NfcTagType_Type3 = 0b_0000_0100, ///< ISO14443A RW/RO. Sony FeliCa
|
||||||
|
NfcTagType_Type4A = 0b_0000_1000, ///< ISO14443A RW/RO. DESFire
|
||||||
|
NfcTagType_Type4B = 0b_0001_0000, ///< ISO14443B RW/RO. DESFire
|
||||||
|
NfcTagType_Type5 = 0b_0010_0000, ///< ISO15693 RW/RO. SLI, SLIX, ST25TV
|
||||||
|
NfcTagType_Mifare = 0b_0100_0000, ///< Mifare clasic. Skylanders
|
||||||
|
NfcTagType_All = 0xFF,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
|
||||||
|
{
|
||||||
|
enum NfcDeviceState : byte
|
||||||
|
{
|
||||||
|
Initialized = 0,
|
||||||
|
SearchingForTag = 1,
|
||||||
|
TagFound = 2,
|
||||||
|
TagRemoved = 3,
|
||||||
|
TagMounted = 4,
|
||||||
|
Unavailable = 5,
|
||||||
|
Finalized = 6,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
|
||||||
|
{
|
||||||
|
enum State
|
||||||
|
{
|
||||||
|
NonInitialized,
|
||||||
|
Initialized,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare.MifareManager
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x58)]
|
||||||
|
struct TagInfo
|
||||||
|
{
|
||||||
|
public Array10<byte> Uuid;
|
||||||
|
public byte UuidLength;
|
||||||
|
public Array21<byte> Reserved1;
|
||||||
|
public uint Protocol;
|
||||||
|
public uint TagType;
|
||||||
|
public Array6<byte> Reserved2;
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/ResultCode.cs
Normal file
17
src/Ryujinx.HLE/HOS/Services/Nfc/Mifare/ResultCode.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Nfc.Mifare
|
||||||
|
{
|
||||||
|
public enum ResultCode
|
||||||
|
{
|
||||||
|
ModuleId = 161,
|
||||||
|
ErrorCodeShift = 9,
|
||||||
|
|
||||||
|
Success = 0,
|
||||||
|
|
||||||
|
DeviceNotFound = (64 << ErrorCodeShift) | ModuleId, // 0x80A1
|
||||||
|
WrongArgument = (65 << ErrorCodeShift) | ModuleId, // 0x82A1
|
||||||
|
WrongDeviceState = (73 << ErrorCodeShift) | ModuleId, // 0x92A1
|
||||||
|
NfcDisabled = (80 << ErrorCodeShift) | ModuleId, // 0xA0A1
|
||||||
|
TagNotFound = (97 << ErrorCodeShift) | ModuleId, // 0xC2A1
|
||||||
|
MifareAccessError = (288 << ErrorCodeShift) | ModuleId, // 0x240a1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -79,9 +79,15 @@ namespace Ryujinx.HLE.HOS.Services
|
||||||
ProcessCreationFlags.Is64Bit |
|
ProcessCreationFlags.Is64Bit |
|
||||||
ProcessCreationFlags.PoolPartitionSystem;
|
ProcessCreationFlags.PoolPartitionSystem;
|
||||||
|
|
||||||
ProcessCreationInfo creationInfo = new("Service", 1, 0, 0x8000000, 1, Flags, 0, 0);
|
ProcessCreationInfo creationInfo = new(Name, 1, 0, 0x8000000, 1, Flags, 0, 0);
|
||||||
|
|
||||||
KernelStatic.StartInitialProcess(context, creationInfo, DefaultCapabilities, 44, Main);
|
KernelStatic.StartInitialProcess(context, creationInfo, DefaultCapabilities, 44, () =>
|
||||||
|
{
|
||||||
|
var currentThread = KernelStatic.GetCurrentThread();
|
||||||
|
currentThread.HostThread.Name = $"{{{Name}}}";
|
||||||
|
|
||||||
|
Main();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddPort(int serverPortHandle, Func<IpcService> objectFactory)
|
private void AddPort(int serverPortHandle, Func<IpcService> objectFactory)
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,12 @@ namespace Ryujinx.HLE.HOS.Services.Sm
|
||||||
private static readonly Dictionary<string, Type> _services;
|
private static readonly Dictionary<string, Type> _services;
|
||||||
|
|
||||||
private readonly SmRegistry _registry;
|
private readonly SmRegistry _registry;
|
||||||
private readonly ServerBase _commonServer;
|
private ServerBase _commonServer;
|
||||||
|
|
||||||
private bool _isInitialized;
|
private bool _isInitialized;
|
||||||
|
|
||||||
public IUserInterface(KernelContext context, SmRegistry registry) : base(registerTipc: true)
|
public IUserInterface(KernelContext context, SmRegistry registry) : base(registerTipc: true)
|
||||||
{
|
{
|
||||||
_commonServer = new ServerBase(context, "CommonServer");
|
|
||||||
_registry = registry;
|
_registry = registry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -97,6 +96,11 @@ namespace Ryujinx.HLE.HOS.Services.Sm
|
||||||
|
|
||||||
IpcService service = GetServiceInstance(type, context, serviceAttribute.Parameter);
|
IpcService service = GetServiceInstance(type, context, serviceAttribute.Parameter);
|
||||||
|
|
||||||
|
if (_commonServer is null)
|
||||||
|
{
|
||||||
|
_commonServer = new ServerBase(context.Device.System.KernelContext, "Common");
|
||||||
|
}
|
||||||
|
|
||||||
service.TrySetServer(_commonServer);
|
service.TrySetServer(_commonServer);
|
||||||
service.Server.AddSessionObj(session.ServerSession, service);
|
service.Server.AddSessionObj(session.ServerSession, service);
|
||||||
}
|
}
|
||||||
|
|
@ -253,7 +257,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm
|
||||||
|
|
||||||
public override void DestroyAtExit()
|
public override void DestroyAtExit()
|
||||||
{
|
{
|
||||||
_commonServer.Dispose();
|
_commonServer?.Dispose();
|
||||||
|
|
||||||
base.DestroyAtExit();
|
base.DestroyAtExit();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl
|
||||||
public ISslService(ServiceCtx context) { }
|
public ISslService(ServiceCtx context) { }
|
||||||
|
|
||||||
[CommandCmif(0)]
|
[CommandCmif(0)]
|
||||||
// CreateContext(nn::ssl::sf::SslVersion, u64, pid) -> object<nn::ssl::sf::ISslContext>
|
// CreateContext(nn::ssl::sf::SslVersion, u64 pid_placeholder, pid) -> object<nn::ssl::sf::ISslContext>
|
||||||
public ResultCode CreateContext(ServiceCtx context)
|
public ResultCode CreateContext(ServiceCtx context)
|
||||||
{
|
{
|
||||||
SslVersion sslVersion = (SslVersion)context.RequestData.ReadUInt32();
|
SslVersion sslVersion = (SslVersion)context.RequestData.ReadUInt32();
|
||||||
|
|
@ -126,14 +126,18 @@ namespace Ryujinx.HLE.HOS.Services.Ssl
|
||||||
}
|
}
|
||||||
|
|
||||||
[CommandCmif(100)]
|
[CommandCmif(100)]
|
||||||
// CreateContextForSystem(u64 pid, nn::ssl::sf::SslVersion, u64)
|
// CreateContextForSystem(nn::ssl::sf::SslVersion, u64 pid_placeholder, pid) -> object<nn::ssl::sf::ISslContextForSystem>
|
||||||
public ResultCode CreateContextForSystem(ServiceCtx context)
|
public ResultCode CreateContextForSystem(ServiceCtx context)
|
||||||
{
|
{
|
||||||
ulong pid = context.RequestData.ReadUInt64();
|
|
||||||
SslVersion sslVersion = (SslVersion)context.RequestData.ReadUInt32();
|
SslVersion sslVersion = (SslVersion)context.RequestData.ReadUInt32();
|
||||||
|
#pragma warning disable IDE0059 // Remove unnecessary value assignment
|
||||||
ulong pidPlaceholder = context.RequestData.ReadUInt64();
|
ulong pidPlaceholder = context.RequestData.ReadUInt64();
|
||||||
|
#pragma warning restore IDE0059
|
||||||
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceSsl, new { pid, sslVersion, pidPlaceholder });
|
// Note: We use ISslContext here instead of ISslContextForSystem class because Ryujinx implements both in one class.
|
||||||
|
MakeObject(context, new ISslContext(context.Request.HandleDesc.PId, sslVersion));
|
||||||
|
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceSsl, new { sslVersion });
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,5 +20,7 @@ namespace Ryujinx.HLE.HOS.SystemState
|
||||||
SimplifiedChinese,
|
SimplifiedChinese,
|
||||||
TraditionalChinese,
|
TraditionalChinese,
|
||||||
BrazilianPortuguese,
|
BrazilianPortuguese,
|
||||||
|
Polish,
|
||||||
|
Thai,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,9 @@ namespace Ryujinx.HLE.HOS.SystemState
|
||||||
"es-419",
|
"es-419",
|
||||||
"zh-Hans",
|
"zh-Hans",
|
||||||
"zh-Hant",
|
"zh-Hant",
|
||||||
"pt-BR"
|
"pt-BR",
|
||||||
|
"pl",
|
||||||
|
"th"
|
||||||
];
|
];
|
||||||
|
|
||||||
internal long DesiredKeyboardLayout { get; private set; }
|
internal long DesiredKeyboardLayout { get; private set; }
|
||||||
|
|
|
||||||
|
|
@ -18,5 +18,7 @@ namespace Ryujinx.HLE.HOS.SystemState
|
||||||
TraditionalChinese,
|
TraditionalChinese,
|
||||||
SimplifiedChinese,
|
SimplifiedChinese,
|
||||||
BrazilianPortuguese,
|
BrazilianPortuguese,
|
||||||
|
Polish,
|
||||||
|
Thai,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,19 @@
|
||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers.Binary;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Ryujinx.Horizon.Sdk.Ns
|
namespace Ryujinx.Horizon.Sdk.Ns
|
||||||
{
|
{
|
||||||
public struct ApplicationControlProperty
|
public struct ApplicationControlProperty
|
||||||
{
|
{
|
||||||
public Array16<ApplicationTitle> Title;
|
/// <summary>
|
||||||
|
/// Use <see cref="Title"/> to access titles instead of accessing them directly.
|
||||||
|
/// </summary>
|
||||||
|
public Array16<ApplicationTitle> TitleBlock;
|
||||||
public Array37<byte> Isbn;
|
public Array37<byte> Isbn;
|
||||||
public StartupUserAccountValue StartupUserAccount;
|
public StartupUserAccountValue StartupUserAccount;
|
||||||
public UserAccountSwitchLockValue UserAccountSwitchLock;
|
public UserAccountSwitchLockValue UserAccountSwitchLock;
|
||||||
|
|
@ -58,7 +65,10 @@ namespace Ryujinx.Horizon.Sdk.Ns
|
||||||
public RepairFlagValue RepairFlag;
|
public RepairFlagValue RepairFlag;
|
||||||
public byte ProgramIndex;
|
public byte ProgramIndex;
|
||||||
public RequiredNetworkServiceLicenseOnLaunchValue RequiredNetworkServiceLicenseOnLaunchFlag;
|
public RequiredNetworkServiceLicenseOnLaunchValue RequiredNetworkServiceLicenseOnLaunchFlag;
|
||||||
public Array4<byte> Reserved3214;
|
public byte ApplicationErrorCodePrefix;
|
||||||
|
public TitleCompressionValue TitleCompression;
|
||||||
|
public byte AcdIndex;
|
||||||
|
public byte ApparentPlatform;
|
||||||
public ApplicationNeighborDetectionClientConfiguration NeighborDetectionClientConfiguration;
|
public ApplicationNeighborDetectionClientConfiguration NeighborDetectionClientConfiguration;
|
||||||
public ApplicationJitConfiguration JitConfiguration;
|
public ApplicationJitConfiguration JitConfiguration;
|
||||||
public RequiredAddOnContentsSetBinaryDescriptor RequiredAddOnContentsSetBinaryDescriptors;
|
public RequiredAddOnContentsSetBinaryDescriptor RequiredAddOnContentsSetBinaryDescriptors;
|
||||||
|
|
@ -74,6 +84,47 @@ namespace Ryujinx.Horizon.Sdk.Ns
|
||||||
public readonly string DisplayVersionString => Encoding.UTF8.GetString(DisplayVersion.AsSpan()).TrimEnd('\0');
|
public readonly string DisplayVersionString => Encoding.UTF8.GetString(DisplayVersion.AsSpan()).TrimEnd('\0');
|
||||||
public readonly string ApplicationErrorCodeCategoryString => Encoding.UTF8.GetString(ApplicationErrorCodeCategory.AsSpan()).TrimEnd('\0');
|
public readonly string ApplicationErrorCodeCategoryString => Encoding.UTF8.GetString(ApplicationErrorCodeCategory.AsSpan()).TrimEnd('\0');
|
||||||
public readonly string BcatPassphraseString => Encoding.UTF8.GetString(BcatPassphrase.AsSpan()).TrimEnd('\0');
|
public readonly string BcatPassphraseString => Encoding.UTF8.GetString(BcatPassphrase.AsSpan()).TrimEnd('\0');
|
||||||
|
|
||||||
|
private const int TitleCount = 32;
|
||||||
|
private const int TitleEntrySize = 0x300;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the resolved title entries. When <see cref="TitleCompression"/> is
|
||||||
|
/// <see cref="TitleCompressionValue.Enable"/>, the raw <see cref="TitleBlock"/> bytes are
|
||||||
|
/// decompressed (raw deflate) from 0x3000 into 0x6000 bytes yielding up to 32 entries.
|
||||||
|
/// Otherwise the 16 uncompressed entries from <see cref="TitleBlock"/> are returned directly.
|
||||||
|
/// </summary>
|
||||||
|
public readonly ApplicationTitle[] Title
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var titles = new ApplicationTitle[TitleCount];
|
||||||
|
|
||||||
|
if (TitleCompression != TitleCompressionValue.Enable)
|
||||||
|
{
|
||||||
|
TitleBlock.AsSpan().CopyTo(titles);
|
||||||
|
|
||||||
|
return titles;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadOnlySpan<byte> titleBytes = MemoryMarshal.AsBytes(TitleBlock.AsSpan());
|
||||||
|
ushort compressedBlobSize = BinaryPrimitives.ReadUInt16LittleEndian(titleBytes);
|
||||||
|
ReadOnlySpan<byte> compressedBlob = titleBytes.Slice(2, compressedBlobSize);
|
||||||
|
|
||||||
|
byte[] decompressed = new byte[TitleCount * TitleEntrySize];
|
||||||
|
|
||||||
|
using (var compressedStream = new MemoryStream(compressedBlob.ToArray()))
|
||||||
|
using (var deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress))
|
||||||
|
{
|
||||||
|
deflateStream.ReadExactly(decompressed, 0, decompressed.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryMarshal.Cast<byte, ApplicationTitle>(decompressed).CopyTo(titles);
|
||||||
|
|
||||||
|
return titles;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public struct ApplicationTitle
|
public struct ApplicationTitle
|
||||||
{
|
{
|
||||||
|
|
@ -130,6 +181,8 @@ namespace Ryujinx.Horizon.Sdk.Ns
|
||||||
TraditionalChinese = 13,
|
TraditionalChinese = 13,
|
||||||
SimplifiedChinese = 14,
|
SimplifiedChinese = 14,
|
||||||
BrazilianPortuguese = 15,
|
BrazilianPortuguese = 15,
|
||||||
|
Polish = 16,
|
||||||
|
Thai = 17,
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Organization
|
public enum Organization
|
||||||
|
|
@ -302,5 +355,11 @@ namespace Ryujinx.Horizon.Sdk.Ns
|
||||||
Deny = 0,
|
Deny = 0,
|
||||||
Allow = 1,
|
Allow = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum TitleCompressionValue : byte
|
||||||
|
{
|
||||||
|
Disable = 0,
|
||||||
|
Enable = 1,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,14 @@ namespace Ryujinx.Horizon
|
||||||
private readonly Action<ServiceTable> _entrypoint;
|
private readonly Action<ServiceTable> _entrypoint;
|
||||||
private readonly ServiceTable _serviceTable;
|
private readonly ServiceTable _serviceTable;
|
||||||
private readonly HorizonOptions _options;
|
private readonly HorizonOptions _options;
|
||||||
|
public readonly string Name;
|
||||||
|
|
||||||
internal ServiceEntry(Action<ServiceTable> entrypoint, ServiceTable serviceTable, HorizonOptions options)
|
internal ServiceEntry(Action<ServiceTable> entrypoint, ServiceTable serviceTable, HorizonOptions options, string name)
|
||||||
{
|
{
|
||||||
_entrypoint = entrypoint;
|
_entrypoint = entrypoint;
|
||||||
_serviceTable = serviceTable;
|
_serviceTable = serviceTable;
|
||||||
_options = options;
|
_options = options;
|
||||||
|
Name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Start(ISyscallApi syscallApi, IVirtualMemoryManager addressSpace, IThreadContext threadContext)
|
public void Start(ISyscallApi syscallApi, IVirtualMemoryManager addressSpace, IThreadContext threadContext)
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ namespace Ryujinx.Horizon
|
||||||
|
|
||||||
void RegisterService<T>() where T : IService
|
void RegisterService<T>() where T : IService
|
||||||
{
|
{
|
||||||
entries.Add(new ServiceEntry(T.Main, this, options));
|
entries.Add(new ServiceEntry(T.Main, this, options, typeof(T).Name));
|
||||||
}
|
}
|
||||||
|
|
||||||
RegisterService<ArpMain>();
|
RegisterService<ArpMain>();
|
||||||
|
|
|
||||||
|
|
@ -151,7 +151,9 @@ namespace Ryujinx.Input.SDL3
|
||||||
result |= GamepadFeaturesFlag.Led;
|
result |= GamepadFeaturesFlag.Led;
|
||||||
}
|
}
|
||||||
SDL_UnlockProperties(propID);
|
SDL_UnlockProperties(propID);
|
||||||
SDL_DestroyProperties(propID);
|
|
||||||
|
// NOTE: Do not call SDL_DestroyProperties here. These properties are owned
|
||||||
|
// internally by SDL and are freed when SDL_CloseGamepad is called (in Dispose).
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,20 @@ using static SDL.SDL3;
|
||||||
|
|
||||||
namespace Ryujinx.Input.SDL3
|
namespace Ryujinx.Input.SDL3
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
public unsafe class SDL3GamepadDriver : IGamepadDriver
|
public unsafe class SDL3GamepadDriver : IGamepadDriver
|
||||||
{
|
{
|
||||||
private readonly Dictionary<SDL_JoystickID, string> _gamepadsInstanceIdsMapping;
|
private readonly Dictionary<SDL_JoystickID, string> _gamepadsInstanceIdsMapping;
|
||||||
private readonly Dictionary<SDL_JoystickID, string> _gamepadsIds;
|
private readonly Dictionary<SDL_JoystickID, string> _gamepadsIds;
|
||||||
|
/// <summary>
|
||||||
|
/// Unlinked joy-cons
|
||||||
|
/// </summary>
|
||||||
|
private readonly Dictionary<SDL_JoystickID, string> _joyConsIds;
|
||||||
|
/// <summary>
|
||||||
|
/// Linked joy-cons, remove dual joy-con from <c>_gamepadsIds</c> when a linked joy-con is removed
|
||||||
|
/// </summary>
|
||||||
|
private readonly Dictionary<SDL_JoystickID,string> _linkedJoyConsIds;
|
||||||
private readonly Lock _lock = new();
|
private readonly Lock _lock = new();
|
||||||
|
|
||||||
public ReadOnlySpan<string> GamepadsIds
|
public ReadOnlySpan<string> GamepadsIds
|
||||||
|
|
@ -21,7 +31,11 @@ namespace Ryujinx.Input.SDL3
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
return _gamepadsIds.Values.ToArray();
|
List<string> temp = [];
|
||||||
|
temp.AddRange(_gamepadsIds.Values);
|
||||||
|
temp.AddRange(_joyConsIds.Values);
|
||||||
|
temp.AddRange(_linkedJoyConsIds.Values);
|
||||||
|
return temp.ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -35,6 +49,8 @@ namespace Ryujinx.Input.SDL3
|
||||||
{
|
{
|
||||||
_gamepadsInstanceIdsMapping = new Dictionary<SDL_JoystickID, string>();
|
_gamepadsInstanceIdsMapping = new Dictionary<SDL_JoystickID, string>();
|
||||||
_gamepadsIds = [];
|
_gamepadsIds = [];
|
||||||
|
_joyConsIds = [];
|
||||||
|
_linkedJoyConsIds = [];
|
||||||
|
|
||||||
SDL3Driver.Instance.Initialize();
|
SDL3Driver.Instance.Initialize();
|
||||||
SDL3Driver.Instance.OnJoyStickConnected += HandleJoyStickConnected;
|
SDL3Driver.Instance.OnJoyStickConnected += HandleJoyStickConnected;
|
||||||
|
|
@ -92,7 +108,7 @@ namespace Ryujinx.Input.SDL3
|
||||||
int guidIndex = 0;
|
int guidIndex = 0;
|
||||||
id = guidIndex + "-" + guidString;
|
id = guidIndex + "-" + guidString;
|
||||||
|
|
||||||
while (_gamepadsIds.ContainsValue(id))
|
while (_gamepadsIds.ContainsValue(id) || _joyConsIds.ContainsValue(id) || _linkedJoyConsIds.ContainsValue(id))
|
||||||
{
|
{
|
||||||
id = (++guidIndex) + "-" + guidString;
|
id = (++guidIndex) + "-" + guidString;
|
||||||
}
|
}
|
||||||
|
|
@ -104,16 +120,47 @@ namespace Ryujinx.Input.SDL3
|
||||||
private void HandleJoyStickDisconnected(SDL_JoystickID joystickInstanceId)
|
private void HandleJoyStickDisconnected(SDL_JoystickID joystickInstanceId)
|
||||||
{
|
{
|
||||||
bool joyConPairDisconnected = false;
|
bool joyConPairDisconnected = false;
|
||||||
|
string fakeId = null;
|
||||||
|
|
||||||
if (!_gamepadsInstanceIdsMapping.Remove(joystickInstanceId, out string id))
|
if (!_gamepadsInstanceIdsMapping.Remove(joystickInstanceId, out string id))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
_gamepadsIds.Remove(joystickInstanceId);
|
if (!_linkedJoyConsIds.ContainsKey(joystickInstanceId))
|
||||||
if (!SDL3JoyConPair.IsCombinable(_gamepadsIds))
|
|
||||||
{
|
{
|
||||||
_gamepadsIds.Remove(GetInstanceIdFromId(SDL3JoyConPair.Id));
|
if (!_joyConsIds.Remove(joystickInstanceId))
|
||||||
|
{
|
||||||
|
_gamepadsIds.Remove(joystickInstanceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (string matchId in _gamepadsIds.Values)
|
||||||
|
{
|
||||||
|
if (matchId.Contains(id))
|
||||||
|
{
|
||||||
|
fakeId = matchId;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string leftId = fakeId!.Split('_')[0];
|
||||||
|
string rightId = fakeId!.Split('_')[1];
|
||||||
|
|
||||||
|
if (leftId == id)
|
||||||
|
{
|
||||||
|
_linkedJoyConsIds.Remove(GetInstanceIdFromId(rightId));
|
||||||
|
_joyConsIds.Add(GetInstanceIdFromId(rightId), rightId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_linkedJoyConsIds.Remove(GetInstanceIdFromId(leftId));
|
||||||
|
_joyConsIds.Add(GetInstanceIdFromId(leftId), leftId);
|
||||||
|
}
|
||||||
|
|
||||||
|
_linkedJoyConsIds.Remove(joystickInstanceId);
|
||||||
|
_gamepadsIds.Remove(GetInstanceIdFromId(fakeId));
|
||||||
joyConPairDisconnected = true;
|
joyConPairDisconnected = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -121,13 +168,14 @@ namespace Ryujinx.Input.SDL3
|
||||||
OnGamepadDisconnected?.Invoke(id);
|
OnGamepadDisconnected?.Invoke(id);
|
||||||
if (joyConPairDisconnected)
|
if (joyConPairDisconnected)
|
||||||
{
|
{
|
||||||
OnGamepadDisconnected?.Invoke(SDL3JoyConPair.Id);
|
OnGamepadDisconnected?.Invoke(fakeId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleJoyStickConnected(SDL_JoystickID joystickInstanceId)
|
private void HandleJoyStickConnected(SDL_JoystickID joystickInstanceId)
|
||||||
{
|
{
|
||||||
bool joyConPairConnected = false;
|
bool joyConPairConnected = false;
|
||||||
|
string fakeId = null;
|
||||||
|
|
||||||
if (SDL_IsGamepad(joystickInstanceId))
|
if (SDL_IsGamepad(joystickInstanceId))
|
||||||
{
|
{
|
||||||
|
|
@ -149,27 +197,40 @@ namespace Ryujinx.Input.SDL3
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
|
if (!SDL3JoyCon.IsJoyCon(joystickInstanceId))
|
||||||
_gamepadsIds.Add(joystickInstanceId, id);
|
|
||||||
|
|
||||||
if (SDL3JoyConPair.IsCombinable(_gamepadsIds))
|
|
||||||
{
|
{
|
||||||
// TODO - It appears that you can only have one joy con pair connected at a time?
|
_gamepadsIds.Add(joystickInstanceId, id);
|
||||||
// This was also the behavior before SDL3
|
}
|
||||||
_gamepadsIds.Remove(GetInstanceIdFromId(SDL3JoyConPair.Id));
|
else
|
||||||
uint fakeInstanceID = uint.MaxValue;
|
{
|
||||||
while (!_gamepadsIds.TryAdd((SDL_JoystickID)fakeInstanceID, SDL3JoyConPair.Id))
|
if (SDL3JoyConPair.IsCombinable(joystickInstanceId, _joyConsIds, out SDL_JoystickID match))
|
||||||
{
|
{
|
||||||
fakeInstanceID--;
|
_joyConsIds.Remove(match, out string matchId);
|
||||||
|
_linkedJoyConsIds.Add(joystickInstanceId, id);
|
||||||
|
_linkedJoyConsIds.Add(match, matchId);
|
||||||
|
|
||||||
|
uint fakeInstanceId = uint.MaxValue;
|
||||||
|
fakeId = SDL3JoyCon.IsLeftJoyCon(joystickInstanceId)
|
||||||
|
? $"{id}_{matchId}"
|
||||||
|
: $"{matchId}_{id}";
|
||||||
|
while (!_gamepadsIds.TryAdd((SDL_JoystickID)fakeInstanceId, fakeId))
|
||||||
|
{
|
||||||
|
fakeInstanceId--;
|
||||||
|
}
|
||||||
|
_gamepadsInstanceIdsMapping.Add((SDL_JoystickID)fakeInstanceId, fakeId);
|
||||||
|
joyConPairConnected = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_joyConsIds.Add(joystickInstanceId, id);
|
||||||
}
|
}
|
||||||
joyConPairConnected = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
OnGamepadConnected?.Invoke(id);
|
OnGamepadConnected?.Invoke(id);
|
||||||
if (joyConPairConnected)
|
if (joyConPairConnected)
|
||||||
{
|
{
|
||||||
OnGamepadConnected?.Invoke(SDL3JoyConPair.Id);
|
OnGamepadConnected?.Invoke(fakeId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -193,10 +254,22 @@ namespace Ryujinx.Input.SDL3
|
||||||
{
|
{
|
||||||
OnGamepadDisconnected?.Invoke(gamepad.Value);
|
OnGamepadDisconnected?.Invoke(gamepad.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (var gamepad in _joyConsIds)
|
||||||
|
{
|
||||||
|
OnGamepadDisconnected?.Invoke(gamepad.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var gamepad in _linkedJoyConsIds)
|
||||||
|
{
|
||||||
|
OnGamepadDisconnected?.Invoke(gamepad.Value);
|
||||||
|
}
|
||||||
|
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
_gamepadsIds.Clear();
|
_gamepadsIds.Clear();
|
||||||
|
_joyConsIds.Clear();
|
||||||
|
_linkedJoyConsIds.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL3Driver.Instance.Dispose();
|
SDL3Driver.Instance.Dispose();
|
||||||
|
|
@ -215,11 +288,27 @@ namespace Ryujinx.Input.SDL3
|
||||||
|
|
||||||
public IGamepad GetGamepad(string id)
|
public IGamepad GetGamepad(string id)
|
||||||
{
|
{
|
||||||
if (id == SDL3JoyConPair.Id)
|
// joy-con pair ids is the combined ids of its parts which are split using a '_'
|
||||||
|
if (id.Contains('_'))
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
return SDL3JoyConPair.GetGamepad(_gamepadsIds);
|
string leftId = id.Split('_')[0];
|
||||||
|
string rightId = id.Split('_')[1];
|
||||||
|
|
||||||
|
SDL_JoystickID leftInstanceId = GetInstanceIdFromId(leftId);
|
||||||
|
SDL_JoystickID rightInstanceId = GetInstanceIdFromId(rightId);
|
||||||
|
|
||||||
|
SDL_Gamepad* leftGamepadHandle = SDL_OpenGamepad(leftInstanceId);
|
||||||
|
SDL_Gamepad* rightGamepadHandle = SDL_OpenGamepad(rightInstanceId);
|
||||||
|
|
||||||
|
if (leftGamepadHandle == null || rightGamepadHandle == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SDL3JoyConPair(new SDL3JoyCon(leftGamepadHandle, leftId),
|
||||||
|
new SDL3JoyCon(rightGamepadHandle, rightId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -232,7 +321,7 @@ namespace Ryujinx.Input.SDL3
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SDL_GetGamepadName(gamepadHandle).StartsWith(SDL3JoyCon.Prefix))
|
if (SDL3JoyCon.IsJoyCon(instanceId))
|
||||||
{
|
{
|
||||||
return new SDL3JoyCon(gamepadHandle, id);
|
return new SDL3JoyCon(gamepadHandle, id);
|
||||||
}
|
}
|
||||||
|
|
@ -242,12 +331,18 @@ namespace Ryujinx.Input.SDL3
|
||||||
|
|
||||||
public IEnumerable<IGamepad> GetGamepads()
|
public IEnumerable<IGamepad> GetGamepads()
|
||||||
{
|
{
|
||||||
lock (_gamepadsIds)
|
string[] ids;
|
||||||
|
lock (_lock)
|
||||||
{
|
{
|
||||||
foreach (var gamepad in _gamepadsIds)
|
ids = _gamepadsIds.Values
|
||||||
{
|
.Concat(_joyConsIds.Values)
|
||||||
yield return GetGamepad(gamepad.Value);
|
.Concat(_linkedJoyConsIds.Values)
|
||||||
}
|
.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (string id in ids)
|
||||||
|
{
|
||||||
|
yield return GetGamepad(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,10 +24,10 @@ namespace Ryujinx.Input.SDL3
|
||||||
private readonly Dictionary<GamepadButtonInputId, SDL_GamepadButton> _leftButtonsDriverMapping = new()
|
private readonly Dictionary<GamepadButtonInputId, SDL_GamepadButton> _leftButtonsDriverMapping = new()
|
||||||
{
|
{
|
||||||
{GamepadButtonInputId.LeftStick, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_LEFT_STICK},
|
{GamepadButtonInputId.LeftStick, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_LEFT_STICK},
|
||||||
{GamepadButtonInputId.DpadUp, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_NORTH},
|
{GamepadButtonInputId.DpadUp, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_WEST},
|
||||||
{GamepadButtonInputId.DpadDown, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_SOUTH},
|
{GamepadButtonInputId.DpadDown, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_EAST},
|
||||||
{GamepadButtonInputId.DpadLeft, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_EAST},
|
{GamepadButtonInputId.DpadLeft, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_SOUTH},
|
||||||
{GamepadButtonInputId.DpadRight, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_WEST},
|
{GamepadButtonInputId.DpadRight, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_NORTH},
|
||||||
{GamepadButtonInputId.Minus, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_START},
|
{GamepadButtonInputId.Minus, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_START},
|
||||||
{GamepadButtonInputId.LeftShoulder, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_LEFT_PADDLE1},
|
{GamepadButtonInputId.LeftShoulder, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_LEFT_PADDLE1},
|
||||||
{GamepadButtonInputId.LeftTrigger, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_LEFT_PADDLE2},
|
{GamepadButtonInputId.LeftTrigger, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_LEFT_PADDLE2},
|
||||||
|
|
@ -37,10 +37,10 @@ namespace Ryujinx.Input.SDL3
|
||||||
private readonly Dictionary<GamepadButtonInputId, SDL_GamepadButton> _rightButtonsDriverMapping = new()
|
private readonly Dictionary<GamepadButtonInputId, SDL_GamepadButton> _rightButtonsDriverMapping = new()
|
||||||
{
|
{
|
||||||
{GamepadButtonInputId.RightStick, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_LEFT_STICK},
|
{GamepadButtonInputId.RightStick, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_LEFT_STICK},
|
||||||
{GamepadButtonInputId.A, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_EAST},
|
{GamepadButtonInputId.A, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_SOUTH},
|
||||||
{GamepadButtonInputId.B, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_NORTH},
|
{GamepadButtonInputId.B, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_WEST},
|
||||||
{GamepadButtonInputId.X, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_SOUTH},
|
{GamepadButtonInputId.X, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_EAST},
|
||||||
{GamepadButtonInputId.Y, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_WEST},
|
{GamepadButtonInputId.Y, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_NORTH},
|
||||||
{GamepadButtonInputId.Plus, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_START},
|
{GamepadButtonInputId.Plus, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_START},
|
||||||
{GamepadButtonInputId.RightShoulder, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1},
|
{GamepadButtonInputId.RightShoulder, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1},
|
||||||
{GamepadButtonInputId.RightTrigger, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2},
|
{GamepadButtonInputId.RightTrigger, SDL_GamepadButton.SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2},
|
||||||
|
|
@ -398,5 +398,15 @@ namespace Ryujinx.Input.SDL3
|
||||||
|
|
||||||
return SDL_GetGamepadButton(_gamepadHandle, button);
|
return SDL_GetGamepadButton(_gamepadHandle, button);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsJoyCon(SDL_JoystickID gamepadsId)
|
||||||
|
{
|
||||||
|
return SDL_GetGamepadNameForID(gamepadsId) is LeftName or RightName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsLeftJoyCon(SDL_JoystickID gamepadsId)
|
||||||
|
{
|
||||||
|
return SDL_GetGamepadNameForID(gamepadsId) is LeftName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ namespace Ryujinx.Input.SDL3
|
||||||
public const string Id = "JoyConPair";
|
public const string Id = "JoyConPair";
|
||||||
string IGamepad.Id => Id;
|
string IGamepad.Id => Id;
|
||||||
|
|
||||||
public string Name => "* Nintendo Switch Joy-Con (L/R)";
|
public string Name => "Nintendo Switch Dual Joy-Con (L/R)";
|
||||||
public bool IsConnected => left is { IsConnected: true } && right is { IsConnected: true };
|
public bool IsConnected => left is { IsConnected: true } && right is { IsConnected: true };
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|
@ -96,44 +96,23 @@ namespace Ryujinx.Input.SDL3
|
||||||
right.SetTriggerThreshold(triggerThreshold);
|
right.SetTriggerThreshold(triggerThreshold);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsCombinable(Dictionary<SDL_JoystickID, string> gamepadsIds)
|
public static bool IsCombinable(SDL_JoystickID joyCon1, Dictionary<SDL_JoystickID, string> joyConIds, out SDL_JoystickID match)
|
||||||
{
|
{
|
||||||
(int leftIndex, int rightIndex) = DetectJoyConPair(gamepadsIds);
|
bool isLeft = SDL3JoyCon.IsLeftJoyCon(joyCon1);
|
||||||
return leftIndex >= 0 && rightIndex >= 0;
|
string matchName = isLeft ? SDL3JoyCon.RightName : SDL3JoyCon.LeftName;
|
||||||
}
|
match = 0;
|
||||||
|
|
||||||
private static (int leftIndex, int rightIndex) DetectJoyConPair(Dictionary<SDL_JoystickID, string> gamepadsIds)
|
foreach (var joyConId in joyConIds.Keys)
|
||||||
{
|
|
||||||
Dictionary<string, SDL_JoystickID> gamepadNames = gamepadsIds
|
|
||||||
.Where(gamepadId => gamepadId.Value != Id && SDL_GetGamepadNameForID(gamepadId.Key) is SDL3JoyCon.LeftName or SDL3JoyCon.RightName)
|
|
||||||
.Select(gamepad => (SDL_GetGamepadNameForID(gamepad.Key), gamepad.Key))
|
|
||||||
.ToDictionary();
|
|
||||||
SDL_JoystickID idx;
|
|
||||||
int leftIndex = gamepadNames.TryGetValue(SDL3JoyCon.LeftName, out idx) ? (int)idx : -1;
|
|
||||||
int rightIndex = gamepadNames.TryGetValue(SDL3JoyCon.RightName, out idx) ? (int)idx : -1;
|
|
||||||
|
|
||||||
return (leftIndex, rightIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe static IGamepad GetGamepad(Dictionary<SDL_JoystickID, string> gamepadsIds)
|
|
||||||
{
|
|
||||||
(int leftIndex, int rightIndex) = DetectJoyConPair(gamepadsIds);
|
|
||||||
|
|
||||||
if (leftIndex <= 0 || rightIndex <= 0)
|
|
||||||
{
|
{
|
||||||
return null;
|
if (SDL_GetGamepadNameForID(joyConId) == matchName)
|
||||||
|
{
|
||||||
|
match = joyConId;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_Gamepad* leftGamepadHandle = SDL_OpenGamepad((SDL_JoystickID)leftIndex);
|
return false;
|
||||||
SDL_Gamepad* rightGamepadHandle = SDL_OpenGamepad((SDL_JoystickID)rightIndex);
|
|
||||||
|
|
||||||
if (leftGamepadHandle == null || rightGamepadHandle == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new SDL3JoyConPair(new SDL3JoyCon(leftGamepadHandle, gamepadsIds[(SDL_JoystickID)leftIndex]),
|
|
||||||
new SDL3JoyCon(rightGamepadHandle, gamepadsIds[(SDL_JoystickID)rightIndex]));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -563,7 +563,7 @@ namespace Ryujinx.Input.HLE
|
||||||
float low = Math.Min(1f, (float)((rightVibrationValue.AmplitudeLow * 0.85 + rightVibrationValue.AmplitudeHigh * 0.15) * controllerConfig.Rumble.StrongRumble));
|
float low = Math.Min(1f, (float)((rightVibrationValue.AmplitudeLow * 0.85 + rightVibrationValue.AmplitudeHigh * 0.15) * controllerConfig.Rumble.StrongRumble));
|
||||||
float high = Math.Min(1f, (float)((leftVibrationValue.AmplitudeLow * 0.15 + leftVibrationValue.AmplitudeHigh * 0.85) * controllerConfig.Rumble.WeakRumble));
|
float high = Math.Min(1f, (float)((leftVibrationValue.AmplitudeLow * 0.15 + leftVibrationValue.AmplitudeHigh * 0.85) * controllerConfig.Rumble.WeakRumble));
|
||||||
|
|
||||||
_gamepad.Rumble(low, high, uint.MaxValue);
|
_gamepad?.Rumble(low, high, uint.MaxValue);
|
||||||
|
|
||||||
Logger.Debug?.Print(LogClass.Hid, $"Effect for {controllerConfig.PlayerIndex} " +
|
Logger.Debug?.Print(LogClass.Hid, $"Effect for {controllerConfig.PlayerIndex} " +
|
||||||
$"L.low.amp={leftVibrationValue.AmplitudeLow}, " +
|
$"L.low.amp={leftVibrationValue.AmplitudeLow}, " +
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,14 @@ namespace Ryujinx.SDL3.Common
|
||||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1");
|
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1");
|
||||||
SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, "1");
|
SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, "1");
|
||||||
|
|
||||||
|
// When hid_nintendo is loaded, it creates separate evdev devices for the gamepad
|
||||||
|
// and IMU which SDL3's evdev backend combines via UNIQ matching. Using HIDAPI
|
||||||
|
// instead conflicts with the kernel driver and breaks motion and hotplug.
|
||||||
|
if (OperatingSystem.IsLinux() && Directory.Exists("/sys/module/hid_nintendo"))
|
||||||
|
{
|
||||||
|
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "0");
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE: As of SDL3 2.24.0, joycons are combined by default but the motion source only come from one of them.
|
// NOTE: As of SDL3 2.24.0, joycons are combined by default but the motion source only come from one of them.
|
||||||
// We disable this behavior for now.
|
// We disable this behavior for now.
|
||||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS, "0");
|
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS, "0");
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Parameter.Effect
|
||||||
public void EnsureTypeSize()
|
public void EnsureTypeSize()
|
||||||
{
|
{
|
||||||
Assert.AreEqual(0x18, Unsafe.SizeOf<BiquadFilterEffectParameter1>());
|
Assert.AreEqual(0x18, Unsafe.SizeOf<BiquadFilterEffectParameter1>());
|
||||||
Assert.AreEqual(0x24, Unsafe.SizeOf<BiquadFilterEffectParameter2>());
|
Assert.AreEqual(0x28, Unsafe.SizeOf<BiquadFilterEffectParameter2>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
BIN
src/Ryujinx/Assets/UIImages/Logo_Forgejo.png
Normal file
BIN
src/Ryujinx/Assets/UIImages/Logo_Forgejo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.9 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 5.1 KiB |
|
|
@ -141,7 +141,7 @@ namespace Ryujinx.Ava.Input
|
||||||
AvaKey.OemComma,
|
AvaKey.OemComma,
|
||||||
AvaKey.OemPeriod,
|
AvaKey.OemPeriod,
|
||||||
AvaKey.OemQuestion,
|
AvaKey.OemQuestion,
|
||||||
AvaKey.OemBackslash,
|
AvaKey.OemPipe,
|
||||||
|
|
||||||
// NOTE: invalid
|
// NOTE: invalid
|
||||||
AvaKey.None
|
AvaKey.None
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ namespace Ryujinx.Ava
|
||||||
public static bool PreviewerDetached { get; private set; }
|
public static bool PreviewerDetached { get; private set; }
|
||||||
public static bool UseHardwareAcceleration { get; private set; }
|
public static bool UseHardwareAcceleration { get; private set; }
|
||||||
public static string BackendThreadingArg { get; private set; }
|
public static string BackendThreadingArg { get; private set; }
|
||||||
|
public static bool CoreDumpArg { get; private set; }
|
||||||
|
|
||||||
private const uint MbIconwarning = 0x30;
|
private const uint MbIconwarning = 0x30;
|
||||||
|
|
||||||
|
|
@ -81,6 +82,8 @@ namespace Ryujinx.Ava
|
||||||
bool noGuiArg = ConsumeCommandLineArgument(ref args, "--no-gui") || ConsumeCommandLineArgument(ref args, "nogui");
|
bool noGuiArg = ConsumeCommandLineArgument(ref args, "--no-gui") || ConsumeCommandLineArgument(ref args, "nogui");
|
||||||
bool coreDumpArg = ConsumeCommandLineArgument(ref args, "--core-dumps");
|
bool coreDumpArg = ConsumeCommandLineArgument(ref args, "--core-dumps");
|
||||||
|
|
||||||
|
CoreDumpArg = coreDumpArg;
|
||||||
|
|
||||||
// TODO: Ryujinx causes core dumps on Linux when it exits "uncleanly", eg. through an unhandled exception.
|
// TODO: Ryujinx causes core dumps on Linux when it exits "uncleanly", eg. through an unhandled exception.
|
||||||
// This is undesirable and causes very odd behavior during development (the process stops responding,
|
// This is undesirable and causes very odd behavior during development (the process stops responding,
|
||||||
// the .NET debugger freezes or suddenly detaches, /tmp/ gets filled etc.), unless explicitly requested by the user.
|
// the .NET debugger freezes or suddenly detaches, /tmp/ gets filled etc.), unless explicitly requested by the user.
|
||||||
|
|
|
||||||
|
|
@ -28,11 +28,6 @@
|
||||||
<PublishTrimmed>true</PublishTrimmed>
|
<PublishTrimmed>true</PublishTrimmed>
|
||||||
<TrimMode>partial</TrimMode>
|
<TrimMode>partial</TrimMode>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(RuntimeIdentifier)' == 'win-arm64'">
|
|
||||||
<PublishSingleFile>true</PublishSingleFile>
|
|
||||||
<PublishTrimmed>false</PublishTrimmed>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
FluentAvalonia, used in the Avalonia UI, requires a workaround for the json serializer used internally when using .NET 8+ System.Text.Json.
|
FluentAvalonia, used in the Avalonia UI, requires a workaround for the json serializer used internally when using .NET 8+ System.Text.Json.
|
||||||
|
|
@ -54,7 +49,7 @@
|
||||||
<PackageReference Include="Svg.Controls.Avalonia" />
|
<PackageReference Include="Svg.Controls.Avalonia" />
|
||||||
<PackageReference Include="Svg.Controls.Skia.Avalonia" />
|
<PackageReference Include="Svg.Controls.Skia.Avalonia" />
|
||||||
<PackageReference Include="DynamicData" />
|
<PackageReference Include="DynamicData" />
|
||||||
<PackageReference Include="FluentAvaloniaUI.NoAnim" />
|
<PackageReference Include="FluentAvaloniaUI" />
|
||||||
<PackageReference Include="CommandLineParser" />
|
<PackageReference Include="CommandLineParser" />
|
||||||
<PackageReference Include="CommunityToolkit.Mvvm" />
|
<PackageReference Include="CommunityToolkit.Mvvm" />
|
||||||
<PackageReference Include="DiscordRichPresence" />
|
<PackageReference Include="DiscordRichPresence" />
|
||||||
|
|
@ -174,9 +169,8 @@
|
||||||
<EmbeddedResource Include="Assets\UIImages\Logo_Amiibo.png" />
|
<EmbeddedResource Include="Assets\UIImages\Logo_Amiibo.png" />
|
||||||
<EmbeddedResource Include="Assets\UIImages\Logo_Discord_Dark.png" />
|
<EmbeddedResource Include="Assets\UIImages\Logo_Discord_Dark.png" />
|
||||||
<EmbeddedResource Include="Assets\UIImages\Logo_Discord_Light.png" />
|
<EmbeddedResource Include="Assets\UIImages\Logo_Discord_Light.png" />
|
||||||
<EmbeddedResource Include="Assets\UIImages\Logo_GitLab_Dark.png" />
|
|
||||||
<EmbeddedResource Include="Assets\UIImages\Logo_GitLab_Light.png" />
|
|
||||||
<EmbeddedResource Include="Assets\UIImages\Logo_Ryujinx.png" />
|
<EmbeddedResource Include="Assets\UIImages\Logo_Ryujinx.png" />
|
||||||
|
<EmbeddedResource Include="Assets\UIImages\Logo_Forgejo.png" />
|
||||||
<EmbeddedResource Include="Assets\UIImages\Logo_Ryujinx_AntiAlias.png" />
|
<EmbeddedResource Include="Assets\UIImages\Logo_Ryujinx_AntiAlias.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -1404,7 +1404,7 @@ namespace Ryujinx.Ava.Systems.AppLibrary
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(data.Name))
|
if (string.IsNullOrWhiteSpace(data.Name))
|
||||||
{
|
{
|
||||||
foreach (ref readonly ApplicationControlProperty.ApplicationTitle controlTitle in controlData.Title)
|
foreach (ApplicationControlProperty.ApplicationTitle controlTitle in controlData.Title)
|
||||||
{
|
{
|
||||||
if (!controlTitle.NameString.IsEmpty())
|
if (!controlTitle.NameString.IsEmpty())
|
||||||
{
|
{
|
||||||
|
|
@ -1417,7 +1417,7 @@ namespace Ryujinx.Ava.Systems.AppLibrary
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(data.Developer))
|
if (string.IsNullOrWhiteSpace(data.Developer))
|
||||||
{
|
{
|
||||||
foreach (ref readonly ApplicationControlProperty.ApplicationTitle controlTitle in controlData.Title)
|
foreach (ApplicationControlProperty.ApplicationTitle controlTitle in controlData.Title)
|
||||||
{
|
{
|
||||||
if (!controlTitle.PublisherString.IsEmpty())
|
if (!controlTitle.PublisherString.IsEmpty())
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@ namespace Ryujinx.Ava.Systems.Configuration.System
|
||||||
SimplifiedChinese,
|
SimplifiedChinese,
|
||||||
TraditionalChinese,
|
TraditionalChinese,
|
||||||
BrazilianPortuguese,
|
BrazilianPortuguese,
|
||||||
|
Polish,
|
||||||
|
Thai,
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class LanguageEnumHelper
|
public static class LanguageEnumHelper
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ namespace Ryujinx.Ava.Systems
|
||||||
{
|
{
|
||||||
buildSizeClient.DefaultRequestHeaders.Add("Range", "bytes=0-0");
|
buildSizeClient.DefaultRequestHeaders.Add("Range", "bytes=0-0");
|
||||||
|
|
||||||
// GitLab instance is located in Ukraine. Connection times will vary across the world.
|
// Forgejo instance is located in Ukraine. Connection times will vary across the world.
|
||||||
buildSizeClient.Timeout = TimeSpan.FromSeconds(10);
|
buildSizeClient.Timeout = TimeSpan.FromSeconds(10);
|
||||||
|
|
||||||
HttpResponseMessage message = await buildSizeClient.GetAsync(new Uri(_versionResponse.ArtifactUrl), HttpCompletionOption.ResponseHeadersRead);
|
HttpResponseMessage message = await buildSizeClient.GetAsync(new Uri(_versionResponse.ArtifactUrl), HttpCompletionOption.ResponseHeadersRead);
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using Avalonia.Markup.Xaml;
|
||||||
using Avalonia.Platform;
|
using Avalonia.Platform;
|
||||||
using Avalonia.Styling;
|
using Avalonia.Styling;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
|
using FluentAvalonia.Core;
|
||||||
using FluentAvalonia.UI.Windowing;
|
using FluentAvalonia.UI.Windowing;
|
||||||
using Gommon;
|
using Gommon;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
|
|
@ -53,6 +54,9 @@ namespace Ryujinx.Ava
|
||||||
{
|
{
|
||||||
Name = FormatTitle();
|
Name = FormatTitle();
|
||||||
|
|
||||||
|
// Disable menu animations
|
||||||
|
FAUISettings.SetAnimationsEnabledAtAppLevel(false);
|
||||||
|
|
||||||
AvaloniaXamlLoader.Load(this);
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
|
||||||
if (OperatingSystem.IsMacOS())
|
if (OperatingSystem.IsMacOS())
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
{
|
{
|
||||||
public partial class AboutWindowViewModel : BaseModel, IDisposable
|
public partial class AboutWindowViewModel : BaseModel, IDisposable
|
||||||
{
|
{
|
||||||
[ObservableProperty] public partial Bitmap GitLabLogo { get; set; }
|
[ObservableProperty] public partial Bitmap ForgejoLogo { get; set; }
|
||||||
|
|
||||||
[ObservableProperty] public partial Bitmap DiscordLogo { get; set; }
|
[ObservableProperty] public partial Bitmap DiscordLogo { get; set; }
|
||||||
|
|
||||||
|
|
@ -37,6 +37,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
}
|
}
|
||||||
|
|
||||||
private const string LogoPathFormat = "resm:Ryujinx.Assets.UIImages.Logo_{0}_{1}.png?assembly=Ryujinx";
|
private const string LogoPathFormat = "resm:Ryujinx.Assets.UIImages.Logo_{0}_{1}.png?assembly=Ryujinx";
|
||||||
|
private const string UnthemedLogoPathFormat = "resm:Ryujinx.Assets.UIImages.Logo_{0}.png?assembly=Ryujinx";
|
||||||
|
|
||||||
private void UpdateLogoTheme(string theme)
|
private void UpdateLogoTheme(string theme)
|
||||||
{
|
{
|
||||||
|
|
@ -46,7 +47,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
string themeName = isDarkTheme ? "Dark" : "Light";
|
string themeName = isDarkTheme ? "Dark" : "Light";
|
||||||
|
|
||||||
DiscordLogo = LoadBitmap(LogoPathFormat.Format("Discord", themeName));
|
DiscordLogo = LoadBitmap(LogoPathFormat.Format("Discord", themeName));
|
||||||
GitLabLogo = LoadBitmap(LogoPathFormat.Format("GitLab", themeName));
|
ForgejoLogo = LoadBitmap(UnthemedLogoPathFormat.Format("Forgejo"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Bitmap LoadBitmap(string uri) => new(Avalonia.Platform.AssetLoader.Open(new Uri(uri)));
|
private static Bitmap LoadBitmap(string uri) => new(Avalonia.Platform.AssetLoader.Open(new Uri(uri)));
|
||||||
|
|
@ -55,7 +56,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
{
|
{
|
||||||
RyujinxApp.ThemeChanged -= Ryujinx_ThemeChanged;
|
RyujinxApp.ThemeChanged -= Ryujinx_ThemeChanged;
|
||||||
|
|
||||||
GitLabLogo.Dispose();
|
ForgejoLogo.Dispose();
|
||||||
DiscordLogo.Dispose();
|
DiscordLogo.Dispose();
|
||||||
|
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
|
|
|
||||||
|
|
@ -184,7 +184,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||||
_controller = 0;
|
_controller = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Controllers.Count > 0 && value < Controllers.Count && _controller > -1)
|
if (Controllers.Count > 0 && _controller < Controllers.Count && _controller > -1)
|
||||||
{
|
{
|
||||||
ControllerType controller = Controllers[_controller].Type;
|
ControllerType controller = Controllers[_controller].Type;
|
||||||
|
|
||||||
|
|
@ -467,7 +467,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||||
IsModified = true;
|
IsModified = true;
|
||||||
RevertChanges();
|
RevertChanges();
|
||||||
FindPairedDeviceInConfigFile();
|
FindPairedDeviceInConfigFile();
|
||||||
|
|
||||||
_isChangeTrackingActive = true; // Enable configuration change tracking
|
_isChangeTrackingActive = true; // Enable configuration change tracking
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -521,7 +521,17 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||||
|
|
||||||
if (Config != null && Controllers.ToList().FindIndex(x => x.Type == Config.ControllerType) != -1)
|
if (Config != null && Controllers.ToList().FindIndex(x => x.Type == Config.ControllerType) != -1)
|
||||||
{
|
{
|
||||||
Controller = Controllers.ToList().FindIndex(x => x.Type == Config.ControllerType);
|
int controllerIndex = Controllers.ToList().FindIndex(x => x.Type == Config.ControllerType);
|
||||||
|
|
||||||
|
// Avalonia bug: setting a newly instanced ComboBox to 0
|
||||||
|
// causes the selected item to show up blank
|
||||||
|
// Workaround: set the box to 1 and then 0
|
||||||
|
if (controllerIndex == 0)
|
||||||
|
{
|
||||||
|
Controller = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Controller = controllerIndex;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -576,7 +586,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||||
DeviceList.Clear();
|
DeviceList.Clear();
|
||||||
Devices.Add((DeviceType.None, Disabled, LocaleManager.Instance[LocaleKeys.ControllerSettingsDeviceDisabled]));
|
Devices.Add((DeviceType.None, Disabled, LocaleManager.Instance[LocaleKeys.ControllerSettingsDeviceDisabled]));
|
||||||
|
|
||||||
int controllerNumber = 0;
|
|
||||||
foreach (string id in _mainWindow.InputManager.KeyboardDriver.GamepadsIds)
|
foreach (string id in _mainWindow.InputManager.KeyboardDriver.GamepadsIds)
|
||||||
{
|
{
|
||||||
using IGamepad gamepad = _mainWindow.InputManager.KeyboardDriver.GetGamepad(id);
|
using IGamepad gamepad = _mainWindow.InputManager.KeyboardDriver.GetGamepad(id);
|
||||||
|
|
@ -593,6 +603,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||||
|
|
||||||
if (gamepad != null)
|
if (gamepad != null)
|
||||||
{
|
{
|
||||||
|
int controllerNumber = 0;
|
||||||
string name = GetUniqueGamepadName(gamepad, ref controllerNumber);
|
string name = GetUniqueGamepadName(gamepad, ref controllerNumber);
|
||||||
Devices.Add((DeviceType.Controller, id, name));
|
Devices.Add((DeviceType.Controller, id, name));
|
||||||
}
|
}
|
||||||
|
|
@ -950,8 +961,10 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||||
LoadConfiguration(); // configuration preload is required if the paired gamepad was disconnected but was changed to another gamepad
|
LoadConfiguration(); // configuration preload is required if the paired gamepad was disconnected but was changed to another gamepad
|
||||||
Device = Devices.ToList().FindIndex(d => d.Id == RevertDeviceId);
|
Device = Devices.ToList().FindIndex(d => d.Id == RevertDeviceId);
|
||||||
|
|
||||||
LoadDevice();
|
_isLoaded = false;
|
||||||
LoadConfiguration();
|
LoadConfiguration();
|
||||||
|
LoadDevice();
|
||||||
|
_isLoaded = true;
|
||||||
|
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
IsModified = false;
|
IsModified = false;
|
||||||
|
|
|
||||||
|
|
@ -174,6 +174,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
private string _screenshotKey = "F8";
|
private string _screenshotKey = "F8";
|
||||||
private float _volume;
|
private float _volume;
|
||||||
private ApplicationData _currentApplicationData;
|
private ApplicationData _currentApplicationData;
|
||||||
|
private bool _pendingRestart;
|
||||||
private readonly AutoResetEvent _rendererWaitEvent;
|
private readonly AutoResetEvent _rendererWaitEvent;
|
||||||
private int _customVSyncInterval;
|
private int _customVSyncInterval;
|
||||||
private int _customVSyncIntervalPercentageProxy;
|
private int _customVSyncIntervalPercentageProxy;
|
||||||
|
|
@ -370,6 +371,39 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
|
|
||||||
public bool CanScanAmiiboBinaries => AmiiboBinReader.HasAmiiboKeyFile;
|
public bool CanScanAmiiboBinaries => AmiiboBinReader.HasAmiiboKeyFile;
|
||||||
|
|
||||||
|
public bool IsSkylanderRequested
|
||||||
|
{
|
||||||
|
get => field && _isGameRunning;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
field = value;
|
||||||
|
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasSkylander
|
||||||
|
{
|
||||||
|
get => field && _isGameRunning;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
field = value;
|
||||||
|
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ShowSkylanderActions
|
||||||
|
{
|
||||||
|
get => field && _isGameRunning;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
field = value;
|
||||||
|
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool ShowLoadProgress
|
public bool ShowLoadProgress
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
|
|
@ -1029,7 +1063,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
string dialogMessage =
|
string dialogMessage =
|
||||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogKeysInstallerKeysInstallMessage);
|
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogKeysInstallerKeysInstallMessage);
|
||||||
|
|
||||||
if (ContentManager.AreKeysAlredyPresent(systemDirectory))
|
if (ContentManager.AreKeysAlreadyPresent(systemDirectory))
|
||||||
{
|
{
|
||||||
dialogMessage +=
|
dialogMessage +=
|
||||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys
|
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys
|
||||||
|
|
@ -1217,6 +1251,14 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
|
|
||||||
await LoadApplication(_currentApplicationData);
|
await LoadApplication(_currentApplicationData);
|
||||||
}
|
}
|
||||||
|
else if (_pendingRestart)
|
||||||
|
{
|
||||||
|
_pendingRestart = false;
|
||||||
|
|
||||||
|
Logger.Info?.Print(LogClass.Application, $"Restarting emulation for '{_currentApplicationData.Name}'");
|
||||||
|
|
||||||
|
await LoadApplication(_currentApplicationData);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Otherwise, clear state.
|
// Otherwise, clear state.
|
||||||
|
|
@ -1225,6 +1267,21 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void RestartEmulation()
|
||||||
|
{
|
||||||
|
if (AppHost is null || _currentApplicationData is null)
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.Application, "RestartEmulation called but no application is running.");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.Info?.Print(LogClass.Application, $"Restart requested for '{_currentApplicationData.Name}'");
|
||||||
|
|
||||||
|
_pendingRestart = true;
|
||||||
|
AppHost.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
private void Update_StatusBar(object sender, StatusUpdatedEventArgs args)
|
private void Update_StatusBar(object sender, StatusUpdatedEventArgs args)
|
||||||
{
|
{
|
||||||
if (ShowMenuAndStatusBar && !ShowLoadProgress)
|
if (ShowMenuAndStatusBar && !ShowLoadProgress)
|
||||||
|
|
@ -1864,6 +1921,46 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public async Task OpenSkylanderWindow()
|
||||||
|
{
|
||||||
|
if (AppHost.Device.System.SearchingForSkylander(out int deviceId))
|
||||||
|
{
|
||||||
|
Optional<IStorageFile> result = await StorageProvider.OpenSingleFilePickerAsync(
|
||||||
|
new FilePickerOpenOptions
|
||||||
|
{
|
||||||
|
Title = LocaleManager.Instance[LocaleKeys.OpenFileDialogTitle],
|
||||||
|
FileTypeFilter = new List<FilePickerFileType>
|
||||||
|
{
|
||||||
|
new(LocaleManager.Instance[LocaleKeys.AllSupportedFormats])
|
||||||
|
{
|
||||||
|
Patterns = ["*.sky", "*.bin", "*.dmp", "*.dump"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (result.HasValue)
|
||||||
|
{
|
||||||
|
// Open reading stream from the first file.
|
||||||
|
await using var stream = await result.Value.OpenReadAsync();
|
||||||
|
using var streamReader = new BinaryReader(stream);
|
||||||
|
// Reads all the content of file as a text.
|
||||||
|
byte[] data = new byte[1024];
|
||||||
|
var count = streamReader.Read(data, 0, 1024);
|
||||||
|
if (count < 1024)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AppHost.Device.System.ScanSkylander(deviceId, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task RemoveSkylander()
|
||||||
|
{
|
||||||
|
AppHost.Device.System.RemoveSkylander();
|
||||||
|
}
|
||||||
|
|
||||||
public void ReloadRenderDocApi()
|
public void ReloadRenderDocApi()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -122,8 +122,8 @@
|
||||||
Click="Button_OnClick"
|
Click="Button_OnClick"
|
||||||
CornerRadius="15"
|
CornerRadius="15"
|
||||||
Tag="https://src.ryujinx.app"
|
Tag="https://src.ryujinx.app"
|
||||||
ToolTip.Tip="{ext:Locale AboutGitLabUrlTooltipMessage}">
|
ToolTip.Tip="{ext:Locale AboutForgejoUrlTooltipMessage}">
|
||||||
<Image Source="{Binding GitLabLogo}" />
|
<Image Source="{Binding ForgejoLogo}" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
MinWidth="30"
|
MinWidth="30"
|
||||||
|
|
|
||||||
|
|
@ -47,18 +47,13 @@
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Orientation="Horizontal" IsEnabled="{Binding ShowLedColorPicker}">
|
<StackPanel Orientation="Horizontal" IsEnabled="{Binding ShowLedColorPicker}">
|
||||||
<TextBlock MinWidth="75" MaxWidth="200" Text="{ext:Locale ControllerSettingsLedColor}" />
|
<TextBlock MinWidth="75" MaxWidth="200" Text="{ext:Locale ControllerSettingsLedColor}" />
|
||||||
<ui:ColorPickerButton
|
<ColorPicker
|
||||||
Margin="5"
|
Margin="5"
|
||||||
IsMoreButtonVisible="False"
|
|
||||||
UseColorPalette="False"
|
|
||||||
UseColorTriangle="False"
|
|
||||||
UseColorWheel="False"
|
|
||||||
ShowAcceptDismissButtons="False"
|
|
||||||
IsAlphaEnabled="False"
|
IsAlphaEnabled="False"
|
||||||
AttachedToVisualTree="ColorPickerButton_OnAttachedToVisualTree"
|
AttachedToVisualTree="ColorPicker_OnAttachedToVisualTree"
|
||||||
ColorChanged="ColorPickerButton_OnColorChanged"
|
ColorChanged="ColorPicker_OnColorChanged"
|
||||||
Color="{Binding LedColor, Mode=TwoWay}">
|
Color="{Binding LedColor, Mode=TwoWay}">
|
||||||
</ui:ColorPickerButton>
|
</ColorPicker>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
using FluentAvalonia.UI.Controls;
|
using FluentAvalonia.UI.Controls;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
using Ryujinx.Ava.UI.Controls;
|
using Ryujinx.Ava.UI.Controls;
|
||||||
|
|
@ -30,19 +31,17 @@ namespace Ryujinx.UI.Views.Input
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ColorPickerButton_OnColorChanged(ColorPickerButton sender, ColorButtonColorChangedEventArgs args)
|
private void ColorPicker_OnColorChanged(object sender, ColorChangedEventArgs args)
|
||||||
{
|
{
|
||||||
if (!args.NewColor.HasValue)
|
|
||||||
return;
|
|
||||||
if (!ViewModel.EnableLedChanging)
|
if (!ViewModel.EnableLedChanging)
|
||||||
return;
|
return;
|
||||||
if (ViewModel.TurnOffLed)
|
if (ViewModel.TurnOffLed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ViewModel.ParentModel.SelectedGamepad.SetLed(args.NewColor.Value.ToUInt32());
|
ViewModel.ParentModel.SelectedGamepad.SetLed(args.NewColor.ToUInt32());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ColorPickerButton_OnAttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e)
|
private void ColorPicker_OnAttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e)
|
||||||
{
|
{
|
||||||
if (!ViewModel.EnableLedChanging)
|
if (!ViewModel.EnableLedChanging)
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -167,6 +167,12 @@
|
||||||
Icon="{ext:Icon fa-solid fa-stop}"
|
Icon="{ext:Icon fa-solid fa-stop}"
|
||||||
InputGesture="Escape"
|
InputGesture="Escape"
|
||||||
IsEnabled="{Binding IsGameRunning}" />
|
IsEnabled="{Binding IsGameRunning}" />
|
||||||
|
<MenuItem
|
||||||
|
Name="RestartEmulationMenuItem"
|
||||||
|
Header="{ext:Locale MenuBarOptionsRestartEmulation}"
|
||||||
|
Icon="{ext:Icon fa-solid fa-rotate-right}"
|
||||||
|
InputGesture="Ctrl + R"
|
||||||
|
IsEnabled="{Binding IsGameRunning}" />
|
||||||
<MenuItem Command="{Binding SimulateWakeUpMessage}" Header="{ext:Locale MenuBarOptionsSimulateWakeUpMessage}" Icon="{ext:Icon fa-solid fa-sun}" />
|
<MenuItem Command="{Binding SimulateWakeUpMessage}" Header="{ext:Locale MenuBarOptionsSimulateWakeUpMessage}" Icon="{ext:Icon fa-solid fa-sun}" />
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
|
|
@ -184,6 +190,22 @@
|
||||||
IsVisible="{Binding CanScanAmiiboBinaries}"
|
IsVisible="{Binding CanScanAmiiboBinaries}"
|
||||||
InputGesture="Ctrl + B"
|
InputGesture="Ctrl + B"
|
||||||
IsEnabled="{Binding IsAmiiboBinRequested}" />
|
IsEnabled="{Binding IsAmiiboBinRequested}" />
|
||||||
|
<MenuItem
|
||||||
|
Command="{Binding OpenSkylanderWindow}"
|
||||||
|
AttachedToVisualTree="ScanSkylanderMenuItem_AttachedToVisualTree"
|
||||||
|
Header="{ext:Locale MenuBarActionsScanSkylander}"
|
||||||
|
Icon="{ext:Icon fa-solid fa-cube}"
|
||||||
|
IsVisible="{Binding ShowSkylanderActions}"
|
||||||
|
InputGesture="Ctrl + S"
|
||||||
|
IsEnabled="{Binding IsSkylanderRequested}" />
|
||||||
|
<MenuItem
|
||||||
|
Command="{Binding RemoveSkylander}"
|
||||||
|
AttachedToVisualTree="RemoveSkylanderMenuItem_AttachedToVisualTree"
|
||||||
|
Header="{ext:Locale MenuBarActionsRemoveSkylander}"
|
||||||
|
Icon="{ext:Icon fa-solid fa-cube}"
|
||||||
|
IsVisible="{Binding ShowSkylanderActions}"
|
||||||
|
InputGesture="Ctrl + D"
|
||||||
|
IsEnabled="{Binding HasSkylander}" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Command="{Binding TakeScreenshot}"
|
Command="{Binding TakeScreenshot}"
|
||||||
Header="{ext:Locale MenuBarFileToolsTakeScreenshot}"
|
Header="{ext:Locale MenuBarFileToolsTakeScreenshot}"
|
||||||
|
|
@ -273,17 +295,17 @@
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Name="SetupGuideMenuItem"
|
Name="SetupGuideMenuItem"
|
||||||
Header="{ext:Locale MenuBarHelpSetup}"
|
Header="{ext:Locale MenuBarHelpSetup}"
|
||||||
Icon="{ext:Icon fa-brands fa-gitlab}"
|
Icon="{ext:Icon fa-solid fa-circle-info}"
|
||||||
CommandParameter="{x:Static common:SharedConstants.SetupGuideWikiUrl}" />
|
CommandParameter="{x:Static common:SharedConstants.SetupGuideWikiUrl}" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Name="LdnGuideMenuItem"
|
Name="LdnGuideMenuItem"
|
||||||
Header="{ext:Locale MenuBarHelpMultiplayer}"
|
Header="{ext:Locale MenuBarHelpMultiplayer}"
|
||||||
Icon="{ext:Icon fa-brands fa-gitlab}"
|
Icon="{ext:Icon fa-solid fa-circle-info}"
|
||||||
CommandParameter="{x:Static common:SharedConstants.MultiplayerWikiUrl}" />
|
CommandParameter="{x:Static common:SharedConstants.MultiplayerWikiUrl}" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Name="FaqMenuItem"
|
Name="FaqMenuItem"
|
||||||
Header="{ext:Locale MenuBarHelpFaq}"
|
Header="{ext:Locale MenuBarHelpFaq}"
|
||||||
Icon="{ext:Icon fa-brands fa-gitlab}"
|
Icon="{ext:Icon fa-solid fa-circle-info}"
|
||||||
CommandParameter="{x:Static common:SharedConstants.FaqWikiUrl}" />
|
CommandParameter="{x:Static common:SharedConstants.FaqWikiUrl}" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<Separator />
|
<Separator />
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||||
PauseEmulationMenuItem.Command = Commands.Create(() => ViewModel.AppHost?.Pause());
|
PauseEmulationMenuItem.Command = Commands.Create(() => ViewModel.AppHost?.Pause());
|
||||||
ResumeEmulationMenuItem.Command = Commands.Create(() => ViewModel.AppHost?.Resume());
|
ResumeEmulationMenuItem.Command = Commands.Create(() => ViewModel.AppHost?.Resume());
|
||||||
StopEmulationMenuItem.Command = Commands.Create(() => ViewModel.AppHost?.ShowExitPrompt().OrCompleted());
|
StopEmulationMenuItem.Command = Commands.Create(() => ViewModel.AppHost?.ShowExitPrompt().OrCompleted());
|
||||||
|
RestartEmulationMenuItem.Command = Commands.Create(() => ViewModel.RestartEmulation());
|
||||||
CheatManagerMenuItem.Command = Commands.CreateSilentFail(OpenCheatManagerForCurrentApp);
|
CheatManagerMenuItem.Command = Commands.CreateSilentFail(OpenCheatManagerForCurrentApp);
|
||||||
InstallFileTypesMenuItem.Command = Commands.Create(InstallFileTypes);
|
InstallFileTypesMenuItem.Command = Commands.Create(InstallFileTypes);
|
||||||
UninstallFileTypesMenuItem.Command = Commands.Create(UninstallFileTypes);
|
UninstallFileTypesMenuItem.Command = Commands.Create(UninstallFileTypes);
|
||||||
|
|
@ -193,6 +194,20 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||||
ViewModel.IsAmiiboBinRequested = ViewModel.IsAmiiboRequested && AmiiboBinReader.HasAmiiboKeyFile;
|
ViewModel.IsAmiiboBinRequested = ViewModel.IsAmiiboRequested && AmiiboBinReader.HasAmiiboKeyFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ScanSkylanderMenuItem_AttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is MenuItem)
|
||||||
|
ViewModel.IsSkylanderRequested = ViewModel.AppHost.Device.System.SearchingForSkylander(out _);
|
||||||
|
ViewModel.ShowSkylanderActions = string.Equals(ViewModel.AppHost.Device.Processes.ActiveApplication.ProgramIdText.ToUpper(), "0100CCC0002E6000");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveSkylanderMenuItem_AttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is MenuItem)
|
||||||
|
ViewModel.HasSkylander = ViewModel.AppHost.Device.System.HasSkylander(out _);
|
||||||
|
ViewModel.ShowSkylanderActions = string.Equals(ViewModel.AppHost.Device.Processes.ActiveApplication.ProgramIdText.ToUpper(), "0100CCC0002E6000");
|
||||||
|
}
|
||||||
|
|
||||||
private async Task InstallFileTypes()
|
private async Task InstallFileTypes()
|
||||||
{
|
{
|
||||||
ViewModel.AreMimeTypesRegistered = FileAssociationHelper.Install();
|
ViewModel.AreMimeTypesRegistered = FileAssociationHelper.Install();
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,7 @@
|
||||||
<Button
|
<Button
|
||||||
Name="SaveButton"
|
Name="SaveButton"
|
||||||
Click="SaveButton_Click">
|
Click="SaveButton_Click">
|
||||||
<TextBlock Text="{ext:Locale UserProfilesSetProfileImage}" />
|
<TextBlock Text="{ext:Locale UserProfilesSave}" />
|
||||||
</Button>
|
</Button>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
|
||||||
|
|
@ -78,22 +78,16 @@
|
||||||
Spacing="10"
|
Spacing="10"
|
||||||
Margin="0 24 0 0"
|
Margin="0 24 0 0"
|
||||||
HorizontalAlignment="Right">
|
HorizontalAlignment="Right">
|
||||||
<ui:ColorPickerButton
|
<ColorPicker
|
||||||
FlyoutPlacement="Top"
|
|
||||||
IsMoreButtonVisible="False"
|
|
||||||
UseColorPalette="False"
|
|
||||||
UseColorTriangle="False"
|
|
||||||
UseColorWheel="False"
|
|
||||||
ShowAcceptDismissButtons="False"
|
|
||||||
IsAlphaEnabled="False"
|
IsAlphaEnabled="False"
|
||||||
Color="{Binding BackgroundColor, Mode=TwoWay}"
|
Color="{Binding BackgroundColor, Mode=TwoWay}"
|
||||||
Name="ColorButton">
|
Name="ColorButton">
|
||||||
<ui:ColorPickerButton.Styles>
|
<ColorPicker.Styles>
|
||||||
<Style Selector="Grid#Root > DockPanel > Grid">
|
<Style Selector="Grid#Root > DockPanel > Grid">
|
||||||
<Setter Property="IsVisible" Value="False" />
|
<Setter Property="IsVisible" Value="False" />
|
||||||
</Style>
|
</Style>
|
||||||
</ui:ColorPickerButton.Styles>
|
</ColorPicker.Styles>
|
||||||
</ui:ColorPickerButton>
|
</ColorPicker>
|
||||||
<Button
|
<Button
|
||||||
Content="{ext:Locale AvatarChoose}"
|
Content="{ext:Locale AvatarChoose}"
|
||||||
Height="35"
|
Height="35"
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@
|
||||||
<KeyBinding Gesture="Escape" Command="{Binding ExitCurrentState}" />
|
<KeyBinding Gesture="Escape" Command="{Binding ExitCurrentState}" />
|
||||||
<KeyBinding Gesture="Ctrl+A" Command="{Binding OpenAmiiboWindow}" />
|
<KeyBinding Gesture="Ctrl+A" Command="{Binding OpenAmiiboWindow}" />
|
||||||
<KeyBinding Gesture="Ctrl+B" Command="{Binding OpenBinFile}" />
|
<KeyBinding Gesture="Ctrl+B" Command="{Binding OpenBinFile}" />
|
||||||
|
<KeyBinding Gesture="Ctrl+R" Command="{Binding RestartEmulation}" />
|
||||||
<KeyBinding Gesture="Ctrl+Shift+R" Command="{Binding ReloadRenderDocApi}" />
|
<KeyBinding Gesture="Ctrl+Shift+R" Command="{Binding ReloadRenderDocApi}" />
|
||||||
<KeyBinding Gesture="Ctrl+Shift+C" Command="{Binding ToggleCapture}" />
|
<KeyBinding Gesture="Ctrl+Shift+C" Command="{Binding ToggleCapture}" />
|
||||||
</Window.KeyBindings>
|
</Window.KeyBindings>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
using Avalonia.Platform.Storage;
|
using Avalonia.Platform.Storage;
|
||||||
using Gommon;
|
using Gommon;
|
||||||
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -11,29 +13,42 @@ namespace Ryujinx.Ava.Utilities
|
||||||
extension(IStorageProvider storageProvider)
|
extension(IStorageProvider storageProvider)
|
||||||
{
|
{
|
||||||
public Task<Optional<IStorageFolder>> OpenSingleFolderPickerAsync(FolderPickerOpenOptions openOptions = null) =>
|
public Task<Optional<IStorageFolder>> OpenSingleFolderPickerAsync(FolderPickerOpenOptions openOptions = null) =>
|
||||||
storageProvider.OpenFolderPickerAsync(FixOpenOptions(openOptions, false))
|
CoreDumpable(() => storageProvider.OpenFolderPickerAsync(FixOpenOptions(openOptions, false)))
|
||||||
.Then(folders => folders.FindFirst());
|
.Then(folders => folders.FindFirst());
|
||||||
|
|
||||||
public Task<Optional<IStorageFile>> OpenSingleFilePickerAsync(FilePickerOpenOptions openOptions = null) =>
|
public Task<Optional<IStorageFile>> OpenSingleFilePickerAsync(FilePickerOpenOptions openOptions = null) =>
|
||||||
storageProvider.OpenFilePickerAsync(FixOpenOptions(openOptions, false))
|
CoreDumpable(() => storageProvider.OpenFilePickerAsync(FixOpenOptions(openOptions, false)))
|
||||||
.Then(files => files.FindFirst());
|
.Then(files => files.FindFirst());
|
||||||
|
|
||||||
public Task<Optional<IReadOnlyList<IStorageFolder>>> OpenMultiFolderPickerAsync(FolderPickerOpenOptions openOptions = null) =>
|
public Task<Optional<IReadOnlyList<IStorageFolder>>> OpenMultiFolderPickerAsync(FolderPickerOpenOptions openOptions = null) =>
|
||||||
storageProvider.OpenFolderPickerAsync(FixOpenOptions(openOptions, true))
|
CoreDumpable(() => storageProvider.OpenFolderPickerAsync(FixOpenOptions(openOptions, true)))
|
||||||
.Then(folders => folders.Count > 0 ? Optional.Of(folders) : default);
|
.Then(folders => folders.Count > 0 ? Optional.Of(folders) : default);
|
||||||
|
|
||||||
public Task<Optional<IReadOnlyList<IStorageFile>>> OpenMultiFilePickerAsync(FilePickerOpenOptions openOptions = null) =>
|
public Task<Optional<IReadOnlyList<IStorageFile>>> OpenMultiFilePickerAsync(FilePickerOpenOptions openOptions = null) =>
|
||||||
storageProvider.OpenFilePickerAsync(FixOpenOptions(openOptions, true))
|
CoreDumpable(() => storageProvider.OpenFilePickerAsync(FixOpenOptions(openOptions, true)))
|
||||||
.Then(files => files.Count > 0 ? Optional.Of(files) : default);
|
.Then(files => files.Count > 0 ? Optional.Of(files) : default);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static async Task<T> CoreDumpable<T>(Func<Task<T>> picker)
|
||||||
|
{
|
||||||
|
OsUtils.SetCoreDumpable(true);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await picker();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (!Program.CoreDumpArg)
|
||||||
|
OsUtils.SetCoreDumpable(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static FilePickerOpenOptions FixOpenOptions(this FilePickerOpenOptions openOptions, bool allowMultiple)
|
private static FilePickerOpenOptions FixOpenOptions(this FilePickerOpenOptions openOptions, bool allowMultiple)
|
||||||
{
|
{
|
||||||
if (openOptions is null)
|
if (openOptions is null)
|
||||||
return new FilePickerOpenOptions { AllowMultiple = allowMultiple };
|
return new FilePickerOpenOptions { AllowMultiple = allowMultiple };
|
||||||
|
|
||||||
openOptions.AllowMultiple = allowMultiple;
|
openOptions.AllowMultiple = allowMultiple;
|
||||||
|
|
||||||
return openOptions;
|
return openOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,7 +58,6 @@ namespace Ryujinx.Ava.Utilities
|
||||||
return new FolderPickerOpenOptions { AllowMultiple = allowMultiple };
|
return new FolderPickerOpenOptions { AllowMultiple = allowMultiple };
|
||||||
|
|
||||||
openOptions.AllowMultiple = allowMultiple;
|
openOptions.AllowMultiple = allowMultiple;
|
||||||
|
|
||||||
return openOptions;
|
return openOptions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using System;
|
using System;
|
||||||
using System.Management;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
|
|
@ -11,7 +10,7 @@ namespace Ryujinx.Ava.Utilities.SystemInfo
|
||||||
{
|
{
|
||||||
internal WindowsSystemInfo()
|
internal WindowsSystemInfo()
|
||||||
{
|
{
|
||||||
CpuName = $"{GetCpuidCpuName() ?? GetCpuNameWMI()} ; {LogicalCoreCount} logical"; // WMI is very slow
|
CpuName = $"{GetCpuidCpuName() ?? GetCpuNameFromRegistry()} ; {LogicalCoreCount} logical";
|
||||||
(RamTotal, RamAvailable) = GetMemoryStats();
|
(RamTotal, RamAvailable) = GetMemoryStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -28,25 +27,26 @@ namespace Ryujinx.Ava.Utilities.SystemInfo
|
||||||
return (0, 0);
|
return (0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetCpuNameWMI()
|
private static string GetCpuNameFromRegistry()
|
||||||
{
|
{
|
||||||
ManagementObjectCollection cpuObjs = GetWMIObjects("root\\CIMV2", "SELECT * FROM Win32_Processor");
|
try
|
||||||
|
|
||||||
if (cpuObjs != null)
|
|
||||||
{
|
{
|
||||||
foreach (ManagementBaseObject cpuObj in cpuObjs)
|
using var key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"HARDWARE\DESCRIPTION\System\CentralProcessor\0");
|
||||||
{
|
|
||||||
return cpuObj["Name"].ToString().Trim();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Environment.GetEnvironmentVariable("PROCESSOR_IDENTIFIER")?.Trim();
|
return key?.GetValue("ProcessorNameString")?.ToString()?.Trim();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Application, $"Registry CPU name lookup failed: {ex.Message}");
|
||||||
|
|
||||||
|
return Environment.GetEnvironmentVariable("PROCESSOR_IDENTIFIER")?.Trim();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
private struct MemoryStatusEx
|
private struct MemoryStatusEx()
|
||||||
{
|
{
|
||||||
public uint Length;
|
public uint Length = (uint)Marshal.SizeOf<MemoryStatusEx>();
|
||||||
public uint MemoryLoad;
|
public uint MemoryLoad;
|
||||||
public ulong TotalPhys;
|
public ulong TotalPhys;
|
||||||
public ulong AvailPhys;
|
public ulong AvailPhys;
|
||||||
|
|
@ -55,33 +55,10 @@ namespace Ryujinx.Ava.Utilities.SystemInfo
|
||||||
public ulong TotalVirtual;
|
public ulong TotalVirtual;
|
||||||
public ulong AvailVirtual;
|
public ulong AvailVirtual;
|
||||||
public ulong AvailExtendedVirtual;
|
public ulong AvailExtendedVirtual;
|
||||||
|
|
||||||
public MemoryStatusEx()
|
|
||||||
{
|
|
||||||
Length = (uint)Marshal.SizeOf<MemoryStatusEx>();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[LibraryImport("kernel32.dll", SetLastError = true)]
|
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
private static partial bool GlobalMemoryStatusEx(ref MemoryStatusEx lpBuffer);
|
private static partial bool GlobalMemoryStatusEx(ref MemoryStatusEx lpBuffer);
|
||||||
|
|
||||||
private static ManagementObjectCollection GetWMIObjects(string scope, string query)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return new ManagementObjectSearcher(scope, query).Get();
|
|
||||||
}
|
|
||||||
catch (PlatformNotSupportedException ex)
|
|
||||||
{
|
|
||||||
Logger.Error?.Print(LogClass.Application, $"WMI isn't available : {ex.Message}");
|
|
||||||
}
|
|
||||||
catch (COMException ex)
|
|
||||||
{
|
|
||||||
Logger.Error?.Print(LogClass.Application, $"WMI isn't available : {ex.Message}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue