mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2026-05-20 20:25:48 +00:00
Compare commits
97 Commits
Canary-1.3
...
refactor/a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a6a95754c | ||
|
|
49891ba7af | ||
|
|
89ea41ef84 | ||
|
|
f8167eb625 | ||
|
|
bab160d650 | ||
|
|
e8cc252d9a | ||
|
|
8065dec744 | ||
|
|
e9c31bea3b | ||
|
|
5511ff5686 | ||
|
|
bf7f978f9d | ||
|
|
1f9bfab923 | ||
|
|
5d8cb3e378 | ||
|
|
708186d8d2 | ||
|
|
ad34237fc6 | ||
|
|
bf083a716c | ||
|
|
2d2661298c | ||
|
|
c4788154fd | ||
|
|
49dd56953c | ||
|
|
722eb93554 | ||
|
|
b0179e6433 | ||
|
|
1d3d4197b7 | ||
|
|
d2b2d65061 | ||
|
|
e1dcaef709 | ||
|
|
b222f671f3 | ||
|
|
4d0cd61b6a | ||
|
|
4e86159bce | ||
|
|
0d66cfa281 | ||
|
|
e656de5fff | ||
|
|
518dd65484 | ||
|
|
88421959a6 | ||
|
|
87ce5162be | ||
|
|
a3e10a1e5a | ||
|
|
1e06c86d47 | ||
|
|
3a3e5e5c5a | ||
|
|
c1c47d308d | ||
|
|
2b929c5537 | ||
|
|
ddfb56c424 | ||
|
|
c9c4ed67b9 | ||
|
|
44d77f8e59 | ||
|
|
bd11cbde08 | ||
|
|
f749cf90b6 | ||
|
|
1b1ceeaa11 | ||
|
|
3f9d37da83 | ||
|
|
0c0b6404b1 | ||
|
|
47c0180ba4 | ||
|
|
8705fabdb0 | ||
|
|
2a4eb8c529 | ||
|
|
51d0d888fb | ||
|
|
a69eab4619 | ||
|
|
93f23cde4f | ||
|
|
93c76a5839 | ||
|
|
0040f884d4 | ||
|
|
bd3b147002 | ||
|
|
b3ac7a2b94 | ||
|
|
96f8d519e6 | ||
|
|
433dd58f8c | ||
|
|
4347afff77 | ||
|
|
46f5d1d31a | ||
|
|
578b6bf00e | ||
|
|
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 |
5
.forgejo/issue_template/config.yml
Normal file
5
.forgejo/issue_template/config.yml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
blank_issues_enabled: true
|
||||||
|
contact_links:
|
||||||
|
- name: Ryubing Issue Tracker
|
||||||
|
url: https://github.com/Ryubing/Issues/issues/
|
||||||
|
about: "Please use this GitHub repository instead of creating issues on this Forgejo repository. Blank issues should only be used by maintainers and authorized bots. Issues made on this repository can and will be deleted."
|
||||||
@@ -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']
|
||||||
35
.forgejo/renovate.json
Normal file
35
.forgejo/renovate.json
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
|
"extends": [
|
||||||
|
"renovate/config"
|
||||||
|
],
|
||||||
|
"enabledManagers": ["nuget", "github-actions"],
|
||||||
|
"packageRules": [
|
||||||
|
{
|
||||||
|
// require approval for *all* NuGet package updates, not just major versions.
|
||||||
|
"matchDepTypes": "nuget",
|
||||||
|
"dependencyDashboardApproval": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Ignore Gommon for automatic updates. I make breaking changes on minor updates not infrequently.
|
||||||
|
"matchDepNames": "Gommon",
|
||||||
|
"matchDepTypes": "nuget",
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "group Silk.NET packages",
|
||||||
|
"extends": ["renovate/config//groups/silkdotnet.json"],
|
||||||
|
"groupName": "Silk.NET"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "group OpenTK packages",
|
||||||
|
"extends": ["renovate/config//groups/opentk.json"],
|
||||||
|
"groupName": "OpenTK"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "group Svg.Controls packages",
|
||||||
|
"extends": ["renovate/config//groups/svgcontrols.json"],
|
||||||
|
"groupName": "Svg.Controls"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
176
.forgejo/workflows/build.yml
Normal file
176
.forgejo/workflows/build.yml
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
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/gruke-build/ubuntu:dotnet-latest
|
||||||
|
timeout-minutes: 45
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
configuration: [Release]
|
||||||
|
platform:
|
||||||
|
- { name: win-x64, zip_os_name: win_x64 }
|
||||||
|
#- { name: win-arm64, zip_os_name: win_arm64 }
|
||||||
|
- { name: linux-x64, zip_os_name: linux_x64 }
|
||||||
|
- { name: linux-arm64, zip_os_name: linux_arm64 }
|
||||||
|
#- { name: osx-x64, zip_os_name: osx_x64 }
|
||||||
|
|
||||||
|
fail-fast: false
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: Overwrite csc problem matcher
|
||||||
|
run: echo "::add-matcher::.forgejo/csc.json"
|
||||||
|
|
||||||
|
- name: Get version info
|
||||||
|
id: version_info
|
||||||
|
run: |
|
||||||
|
echo "result=$(gli get-next-version -c Canary -R)" >> $FORGEJO_OUTPUT
|
||||||
|
echo "git_short_hash=$(git rev-parse --short "${{ forgejo.sha }}")" >> $FORGEJO_OUTPUT
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Change config filename
|
||||||
|
run: sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/PRConfig\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
shell: bash
|
||||||
|
if: forgejo.event_name == 'pull_request'
|
||||||
|
|
||||||
|
- name: 'Cache: ~/.nuget/packages'
|
||||||
|
uses: actions/cache@v5
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.nuget/packages
|
||||||
|
key: ${{ runner.os }}-${{ hashFiles('**/global.json', '**/*.csproj', '**/Directory.Packages.props') }}
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: dotnet build -c "${{ matrix.configuration }}" -p:Version="${{ steps.version_info.outputs.result }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:ExtraDefineConstants=DISABLE_UPDATER
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
uses: actions/unstable-commands@v1
|
||||||
|
with:
|
||||||
|
commands: dotnet test --no-build -c "${{ matrix.configuration }}"
|
||||||
|
timeout-minutes: 10
|
||||||
|
retry-codes: 139
|
||||||
|
if: matrix.platform.name != 'linux-arm64'
|
||||||
|
|
||||||
|
- name: Publish Ryujinx
|
||||||
|
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ steps.version_info.outputs.result }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx --self-contained
|
||||||
|
if: forgejo.event_name == 'pull_request'
|
||||||
|
|
||||||
|
- name: Packing Windows builds
|
||||||
|
if: contains(matrix.platform.name, 'win')
|
||||||
|
run: |
|
||||||
|
7z a artifact/ryujinx-${{ matrix.configuration }}-${{ steps.version_info.outputs.result }}+${{ steps.version_info.outputs.git_short_hash }}-${{ matrix.platform.zip_os_name }}.7z publish
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Upload Ryujinx Windows artifact
|
||||||
|
uses: actions/upload-artifact@v5
|
||||||
|
with:
|
||||||
|
name: ryujinx-${{ matrix.configuration }}-${{ steps.version_info.outputs.result }}+${{ steps.version_info.outputs.git_short_hash }}-${{ matrix.platform.zip_os_name }}
|
||||||
|
path: artifact
|
||||||
|
if: forgejo.event_name == 'pull_request' && contains(matrix.platform.name, 'win')
|
||||||
|
|
||||||
|
- name: Build AppImage
|
||||||
|
if: forgejo.event_name == 'pull_request' && contains(matrix.platform.name, 'linux')
|
||||||
|
run: |
|
||||||
|
chmod +x ./publish/Ryujinx ./publish/Ryujinx.sh
|
||||||
|
|
||||||
|
PLATFORM_NAME="${{ matrix.platform.name }}"
|
||||||
|
|
||||||
|
chmod +x distribution/linux/appimage/build-appimage.sh
|
||||||
|
|
||||||
|
# Explicitly set $ARCH for appimagetool ($ARCH_NAME is for the file name)
|
||||||
|
if [ "$PLATFORM_NAME" = "linux-x64" ]; then
|
||||||
|
ARCH_NAME=x64
|
||||||
|
export ARCH=x86_64
|
||||||
|
elif [ "$PLATFORM_NAME" = "linux-arm64" ]; then
|
||||||
|
ARCH_NAME=arm64
|
||||||
|
export ARCH=aarch64
|
||||||
|
else
|
||||||
|
echo "Unexpected PLATFORM_NAME "$PLATFORM_NAME""
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
BUILDDIR=publish OUTDIR=publish_appimage distribution/linux/appimage/build-appimage.sh
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Upload Ryujinx AppImage artifact
|
||||||
|
uses: actions/upload-artifact@v5
|
||||||
|
if: forgejo.event_name == 'pull_request' && contains(matrix.platform.name, 'linux')
|
||||||
|
with:
|
||||||
|
name: ryujinx-${{ matrix.configuration }}-${{ steps.version_info.outputs.result }}+${{ steps.version_info.outputs.git_short_hash }}-${{ matrix.platform.zip_os_name }}-AppImage
|
||||||
|
path: publish_appimage
|
||||||
|
|
||||||
|
build_macos:
|
||||||
|
name: macOS Universal (${{ matrix.configuration }})
|
||||||
|
runs-on: docker
|
||||||
|
container:
|
||||||
|
image: ghcr.io/gruke-build/ubuntu:dotnet-latest
|
||||||
|
timeout-minutes: 45
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
configuration: [ Release ]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: Setup LLVM 17
|
||||||
|
run: |
|
||||||
|
wget https://apt.llvm.org/llvm.sh
|
||||||
|
chmod +x llvm.sh
|
||||||
|
sudo ./llvm.sh 17
|
||||||
|
|
||||||
|
- name: Install rcodesign
|
||||||
|
run: |
|
||||||
|
gli ghr -R indygreg/apple-platform-rs -p apple-codesign-*-x86_64-unknown-linux-musl.tar.gz -O apple-codesign.tar.gz
|
||||||
|
tar -xzvf apple-codesign.tar.gz --wildcards '*/rcodesign' --strip-components=1
|
||||||
|
rm apple-codesign.tar.gz
|
||||||
|
sudo mv rcodesign /usr/bin/rcodesign
|
||||||
|
|
||||||
|
- name: Get version info
|
||||||
|
id: version_info
|
||||||
|
run: |
|
||||||
|
echo "result=$(gli get-next-version -c Stable -R)" >> $FORGEJO_OUTPUT
|
||||||
|
echo "git_short_hash=$(git rev-parse --short "${{ forgejo.sha }}")" >> $FORGEJO_OUTPUT
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Change config filename
|
||||||
|
run: sed -r --in-place 's/\%\%RYUJINX_CONFIG_FILE_NAME\%\%/PRConfig\.json/g;' src/Ryujinx.Common/ReleaseInformation.cs
|
||||||
|
shell: bash
|
||||||
|
if: forgejo.event_name == 'pull_request'
|
||||||
|
|
||||||
|
- name: 'Cache: ~/.nuget/packages'
|
||||||
|
uses: actions/cache@v5
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.nuget/packages
|
||||||
|
key: ${{ runner.os }}-${{ hashFiles('**/global.json', '**/*.csproj', '**/Directory.Packages.props') }}
|
||||||
|
|
||||||
|
- name: Publish macOS Ryujinx
|
||||||
|
run: |
|
||||||
|
bash distribution/macos/create_macos_pr_build_ava.sh . publish_tmp publish ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.result }}" "${{ steps.version_info.outputs.git_short_hash }}" "${{ matrix.configuration }}" "-p:ExtraDefineConstants=DISABLE_UPDATER"
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Upload Ryujinx artifact
|
||||||
|
uses: actions/upload-artifact@v5
|
||||||
|
with:
|
||||||
|
name: ryujinx-${{ matrix.configuration }}-${{ steps.version_info.outputs.result }}+${{ steps.version_info.outputs.git_short_hash }}-macos_universal
|
||||||
|
path: "publish/*.tar.gz"
|
||||||
|
if: forgejo.event_name == 'pull_request'
|
||||||
@@ -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,28 @@ 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/gruke-build/ubuntu:dotnet-latest, zip_os_name: win_x64 }
|
||||||
#- { name: win-arm64, os: ubuntu-latest, zip_os_name: win_arm64 }
|
#- { name: win-arm64, os: ghcr.io/gruke-build/ubuntu:dotnet-latest, zip_os_name: win_arm64 }
|
||||||
- { name: linux-x64, os: ubuntu-latest, zip_os_name: linux_x64 }
|
- { name: linux-x64, os: ghcr.io/gruke-build/ubuntu:dotnet-latest, zip_os_name: linux_x64 }
|
||||||
- { name: linux-arm64, os: ubuntu-latest, zip_os_name: linux_arm64 }
|
- { name: linux-arm64, os: ghcr.io/gruke-build/ubuntu:dotnet-latest, zip_os_name: linux_arm64 }
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- uses: actions/setup-dotnet@v4
|
|
||||||
with:
|
|
||||||
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 7zip
|
|
||||||
run: |
|
|
||||||
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
|
||||||
@@ -87,12 +71,9 @@ jobs:
|
|||||||
pushd publish
|
pushd publish
|
||||||
rm libarmeilleure-jitsupport.dylib
|
rm libarmeilleure-jitsupport.dylib
|
||||||
7z a ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip ../publish
|
7z a ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip ../publish
|
||||||
|
7z a ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.7z ../publish
|
||||||
popd
|
popd
|
||||||
|
|
||||||
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/canary -n Ryubing-Canary -v ${{ steps.version_info.outputs.build_version }} -r 5 -p release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip
|
|
||||||
shell: bash
|
shell: bash
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Packing Linux builds
|
- name: Packing Linux builds
|
||||||
if: contains(matrix.platform.name, 'linux')
|
if: contains(matrix.platform.name, 'linux')
|
||||||
@@ -101,9 +82,8 @@ jobs:
|
|||||||
rm libarmeilleure-jitsupport.dylib
|
rm libarmeilleure-jitsupport.dylib
|
||||||
chmod +x Ryujinx.sh Ryujinx
|
chmod +x Ryujinx.sh Ryujinx
|
||||||
tar -czvf ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz ../publish
|
tar -czvf ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz ../publish
|
||||||
|
tar -cJvf ../release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.xz ../publish
|
||||||
popd
|
popd
|
||||||
|
|
||||||
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/canary -n Ryubing-Canary -v ${{ steps.version_info.outputs.build_version }} -r 5 -p release_output/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz
|
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Build AppImage (Linux)
|
- name: Build AppImage (Linux)
|
||||||
@@ -112,14 +92,6 @@ 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
|
|
||||||
|
|
||||||
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
|
chmod +x distribution/linux/appimage/build-appimage.sh
|
||||||
|
|
||||||
# Explicitly set $ARCH for appimagetool ($ARCH_NAME is for the file name)
|
# Explicitly set $ARCH for appimagetool ($ARCH_NAME is for the file name)
|
||||||
@@ -139,19 +111,26 @@ 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/gruke-build/ubuntu:dotnet-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- uses: actions/setup-dotnet@v4
|
|
||||||
with:
|
|
||||||
global-json-file: global.json
|
|
||||||
|
|
||||||
- name: Setup LLVM 17
|
- name: Setup LLVM 17
|
||||||
run: |
|
run: |
|
||||||
@@ -159,33 +138,19 @@ jobs:
|
|||||||
chmod +x llvm.sh
|
chmod +x llvm.sh
|
||||||
sudo ./llvm.sh 17
|
sudo ./llvm.sh 17
|
||||||
|
|
||||||
- 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: 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 +166,46 @@ jobs:
|
|||||||
- name: Publish macOS Ryujinx
|
- name: Publish macOS Ryujinx
|
||||||
run: |
|
run: |
|
||||||
./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish_ava ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 1
|
./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish_ava ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 1
|
||||||
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/canary -n Ryubing-Canary -v ${{ steps.version_info.outputs.build_version }} -r 5 -p publish_ava/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz
|
|
||||||
|
|
||||||
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/gruke-build/ubuntu:act-latest
|
||||||
needs:
|
needs:
|
||||||
- macos_release
|
- macos_release
|
||||||
- release
|
- release
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- 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: 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
|
- name: Link to actual source archives for Canary
|
||||||
run: |
|
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 }})"
|
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,24 +5,22 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
triage:
|
triage:
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
# Grab sources to get latest labeler.yml
|
# Grab sources to get latest labeler.yml
|
||||||
- name: Fetch sources
|
- name: Fetch sources
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
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,48 +19,32 @@ 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/gruke-build/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/gruke-build/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/gruke-build/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/gruke-build/ubuntu:act-latest, zip_os_name: linux_arm64 }
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- uses: actions/setup-dotnet@v4
|
|
||||||
with:
|
|
||||||
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::.github/csc.json"
|
||||||
|
|
||||||
- name: Install 7zip
|
|
||||||
run: |
|
|
||||||
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: |
|
||||||
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
|
||||||
@@ -84,12 +68,9 @@ jobs:
|
|||||||
pushd publish
|
pushd publish
|
||||||
rm libarmeilleure-jitsupport.dylib
|
rm libarmeilleure-jitsupport.dylib
|
||||||
7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip ../publish
|
7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip ../publish
|
||||||
|
7z a ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.7z ../publish
|
||||||
popd
|
popd
|
||||||
|
|
||||||
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/ryujinx -n Ryubing -v ${{ steps.version_info.outputs.build_version }} -r 5 -p release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.zip
|
|
||||||
shell: bash
|
shell: bash
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Packing Linux builds
|
- name: Packing Linux builds
|
||||||
if: contains(matrix.platform.name, 'linux')
|
if: contains(matrix.platform.name, 'linux')
|
||||||
@@ -98,12 +79,9 @@ jobs:
|
|||||||
rm libarmeilleure-jitsupport.dylib
|
rm libarmeilleure-jitsupport.dylib
|
||||||
chmod +x Ryujinx.sh Ryujinx
|
chmod +x Ryujinx.sh Ryujinx
|
||||||
tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz ../publish
|
tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz ../publish
|
||||||
|
tar -cJvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.xz ../publish
|
||||||
popd
|
popd
|
||||||
|
|
||||||
gli upload-generic-package -T ${{ secrets.GITLAB_TOKEN }} -P ryubing/ryujinx -n Ryubing -v ${{ steps.version_info.outputs.build_version }} -r 5 -p release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-${{ matrix.platform.zip_os_name }}.tar.gz
|
|
||||||
shell: bash
|
shell: bash
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Build AppImage (Linux)
|
- name: Build AppImage (Linux)
|
||||||
if: contains(matrix.platform.name, 'linux')
|
if: contains(matrix.platform.name, 'linux')
|
||||||
@@ -111,14 +89,6 @@ 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
|
|
||||||
|
|
||||||
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
|
chmod +x distribution/linux/appimage/build-appimage.sh
|
||||||
|
|
||||||
# Explicitly set $ARCH for appimagetool ($ARCH_NAME is for the file name)
|
# Explicitly set $ARCH for appimagetool ($ARCH_NAME is for the file name)
|
||||||
@@ -138,17 +108,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/gruke-build/ubuntu:dotnet-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 +138,23 @@ jobs:
|
|||||||
chmod +x llvm.sh
|
chmod +x llvm.sh
|
||||||
sudo ./llvm.sh 17
|
sudo ./llvm.sh 17
|
||||||
|
|
||||||
- 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: 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,49 +167,42 @@ 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
|
||||||
|
with:
|
||||||
|
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
|
||||||
|
|
||||||
create_gitlab_release:
|
post_ci:
|
||||||
name: Create GitLab Release
|
name: Post-CI Steps
|
||||||
runs-on: ubuntu-24.04
|
runs-on: docker
|
||||||
|
container:
|
||||||
|
image: ghcr.io/gruke-build/ubuntu:act-latest
|
||||||
needs:
|
needs:
|
||||||
- macos_release
|
- macos_release
|
||||||
- release
|
- release
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- 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: |
|
||||||
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
|
|
||||||
61
.github/workflows/nightly_pr_comment.yml
vendored
61
.github/workflows/nightly_pr_comment.yml
vendored
@@ -1,61 +0,0 @@
|
|||||||
name: Comment PR artifacts links
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_run:
|
|
||||||
workflows: ['Build PR']
|
|
||||||
types: [completed]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
pr_comment:
|
|
||||||
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/github-script@v6
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
const {owner, repo} = context.repo;
|
|
||||||
const run_id = ${{github.event.workflow_run.id}};
|
|
||||||
const pull_head_sha = '${{github.event.workflow_run.head_sha}}';
|
|
||||||
|
|
||||||
const issue_number = await (async () => {
|
|
||||||
const pulls = await github.rest.pulls.list({owner, repo});
|
|
||||||
for await (const {data} of github.paginate.iterator(pulls)) {
|
|
||||||
for (const pull of data) {
|
|
||||||
if (pull.head.sha === pull_head_sha) {
|
|
||||||
return pull.number;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
if (issue_number) {
|
|
||||||
core.info(`Using pull request ${issue_number}`);
|
|
||||||
} else {
|
|
||||||
return core.error(`No matching pull request found`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const {data: {artifacts}} = await github.rest.actions.listWorkflowRunArtifacts({owner, repo, run_id});
|
|
||||||
if (!artifacts.length) {
|
|
||||||
return core.error(`No artifacts found`);
|
|
||||||
}
|
|
||||||
let body = `Download the artifacts for this pull request:\n`;
|
|
||||||
let hidden_debug_artifacts = `\n\n <details><summary>Only for Developers</summary>\n`;
|
|
||||||
for (const art of artifacts) {
|
|
||||||
const url = `https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip`;
|
|
||||||
if (art.name.includes('Debug')) {
|
|
||||||
hidden_debug_artifacts += `\n* [${art.name}](${url})`;
|
|
||||||
} else {
|
|
||||||
body += `\n* [${art.name}](${url})`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
hidden_debug_artifacts += `\n</details>`;
|
|
||||||
body += hidden_debug_artifacts;
|
|
||||||
|
|
||||||
const {data: comments} = await github.rest.issues.listComments({repo, owner, issue_number});
|
|
||||||
const existing_comment = comments.find((c) => c.user.login === 'github-actions[bot]');
|
|
||||||
if (existing_comment) {
|
|
||||||
core.info(`Updating comment ${existing_comment.id}`);
|
|
||||||
await github.rest.issues.updateComment({repo, owner, comment_id: existing_comment.id, body});
|
|
||||||
} else {
|
|
||||||
core.info(`Creating a comment`);
|
|
||||||
await github.rest.issues.createComment({repo, owner, issue_number, body});
|
|
||||||
}
|
|
||||||
@@ -3,60 +3,65 @@
|
|||||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageVersion Include="Avalonia" Version="11.3.6" />
|
<PackageVersion Include="Avalonia" Version="11.3.15" />
|
||||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.6" />
|
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.13" />
|
||||||
<PackageVersion Include="Avalonia.Desktop" Version="11.3.6" />
|
<PackageVersion Include="Avalonia.Desktop" Version="11.3.15" />
|
||||||
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.6" />
|
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.15" />
|
||||||
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.3.6" />
|
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.3.15" />
|
||||||
<PackageVersion Include="Svg.Controls.Avalonia" Version="11.3.6.2" />
|
<PackageVersion Include="SharpCompress" Version="0.48.0" />
|
||||||
<PackageVersion Include="Svg.Controls.Skia.Avalonia" Version="11.3.6.2" />
|
<PackageVersion Include="Svg.Controls.Avalonia" Version="11.3.9.5" />
|
||||||
|
<PackageVersion Include="Svg.Controls.Skia.Avalonia" Version="11.3.9.5" />
|
||||||
<PackageVersion Include="Microsoft.Build.Framework" Version="17.11.4" />
|
<PackageVersion Include="Microsoft.Build.Framework" Version="17.11.4" />
|
||||||
<PackageVersion Include="Microsoft.Build.Utilities.Core" Version="17.12.6" />
|
<PackageVersion Include="Microsoft.Build.Utilities.Core" Version="17.12.50" />
|
||||||
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
|
||||||
<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="Ryujinx.SDL3-CS" Version="2026.501.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.2" />
|
||||||
<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.31" />
|
||||||
<PackageVersion Include="FluentAvaloniaUI.NoAnim" Version="2.4.0-build3" />
|
<PackageVersion Include="FluentAvaloniaUI" Version="2.5.1" />
|
||||||
<PackageVersion Include="Humanizer" Version="2.14.1" />
|
<PackageVersion Include="Humanizer" Version="2.14.1" />
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
|
||||||
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.3.0" />
|
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.18.0" />
|
||||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||||
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
|
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
|
||||||
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
||||||
<PackageVersion Include="NetCoreServer" Version="8.0.7" />
|
<PackageVersion Include="NetCoreServer" Version="8.0.7" />
|
||||||
<PackageVersion Include="NUnit" Version="3.13.3" />
|
<PackageVersion Include="NUnit" Version="3.14.0" />
|
||||||
<PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" />
|
<PackageVersion Include="NUnit3TestAdapter" Version="4.6.0" />
|
||||||
<PackageVersion Include="OpenTK.Core" Version="4.8.2" />
|
<PackageVersion Include="OpenTK.Core" Version="4.9.4" />
|
||||||
<PackageVersion Include="OpenTK.Graphics" Version="4.8.2" />
|
<PackageVersion Include="OpenTK.Graphics" Version="4.9.4" />
|
||||||
<PackageVersion Include="OpenTK.Audio.OpenAL" Version="4.8.2" />
|
<!-- OpenTk.Audio.OpenAL has moved to OpenTk.Audio -->
|
||||||
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.8.2" />
|
<!--<PackageVersion Include="OpenTK.Audio" Version="5.0.0-pre.15" />-->
|
||||||
|
<PackageVersion Include="OpenTK.Audio.OpenAL" Version="4.9.4" />
|
||||||
|
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.9.4" />
|
||||||
<PackageVersion Include="Open.NAT.Core" Version="2.1.0.5" />
|
<PackageVersion Include="Open.NAT.Core" Version="2.1.0.5" />
|
||||||
<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />
|
<!-- Ryujinx.Audio.OpenAL.Dependencies is from the original project, last updated 12/30/20 -->
|
||||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.AllArch" Version="6.1.2-build3" />
|
<!--<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />-->
|
||||||
|
<PackageVersion Include="Ryujinx.Audio.OpenAL" Version="1.25.1" />
|
||||||
|
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.AllArch" Version="6.1.4-build6" />
|
||||||
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
||||||
<PackageVersion Include="Ryujinx.LibHac" Version="0.21.0-alpha.126" />
|
<PackageVersion Include="Ryujinx.LibHac" Version="0.21.0-alpha.133" />
|
||||||
<PackageVersion Include="Ryujinx.UpdateClient" Version="1.0.44" />
|
<PackageVersion Include="Ryujinx.UpdateClient" Version="2.0.6" />
|
||||||
<PackageVersion Include="Ryujinx.Systems.Update.Common" Version="1.0.44" />
|
<PackageVersion Include="Ryujinx.Systems.Update.Common" Version="2.0.6" />
|
||||||
<PackageVersion Include="Gommon" Version="2.8.0.1" />
|
<PackageVersion Include="Gommon" Version="2.8.1.2" />
|
||||||
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
||||||
<PackageVersion Include="Sep" Version="0.11.1" />
|
<PackageVersion Include="Sep" Version="0.13.0" />
|
||||||
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
||||||
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
|
||||||
<PackageVersion Include="Silk.NET.Vulkan" Version="2.22.0" />
|
<PackageVersion Include="Silk.NET.Vulkan" Version="2.22.0" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.22.0" />
|
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.22.0" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.22.0" />
|
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.22.0" />
|
||||||
<PackageVersion Include="SkiaSharp" Version="2.88.9" />
|
<PackageVersion Include="SkiaSharp" Version="2.88.9" />
|
||||||
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="2.88.9" />
|
<PackageVersion Include="SkiaSharp.NativeAssets.Win32" Version="2.88.9" />
|
||||||
|
<PackageVersion Include="SkiaSharp.NativeAssets.macOS" Version="2.88.9" />
|
||||||
|
<PackageVersion Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="2.88.9" />
|
||||||
<PackageVersion Include="SPB" Version="0.0.4-build32" />
|
<PackageVersion Include="SPB" Version="0.0.4-build32" />
|
||||||
<PackageVersion Include="System.IO.Hashing" Version="9.0.2" />
|
<PackageVersion Include="System.IO.Hashing" Version="9.0.15" />
|
||||||
<PackageVersion Include="System.Management" Version="9.0.2" />
|
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.1.3" />
|
||||||
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
24
README.md
24
README.md
@@ -5,10 +5,10 @@
|
|||||||
</td>
|
</td>
|
||||||
<td align="center" width="75%">
|
<td align="center" width="75%">
|
||||||
|
|
||||||
# Ryujinx
|
<h1 class="ryu-gradient-text">Ryujinx</h1>
|
||||||
|
|
||||||
[](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 class="forgejo-gradient-text" href="https://github.com/Ryubing/forgejo" target="_blank">modified Forgejo</a> instance under the <a href="https://git.ryujinx.app/projects/Ryubing/src/branch/master/LICENSE.txt" target="_blank">MIT license</a>.
|
||||||
<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
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"ID": "MenuBarActions_StartCapture",
|
"ID": "MenuBarActions_StartCapture",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "RenderDoc Frame-Aufnahme starten",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Start RenderDoc Frame Capture",
|
"en_US": "Start RenderDoc Frame Capture",
|
||||||
"es_ES": "Iniciar una captura de fotograma de RenderDoc",
|
"es_ES": "Iniciar una captura de fotograma de RenderDoc",
|
||||||
@@ -22,14 +22,14 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "启动 RenderDoc 帧捕获",
|
"zh_CN": "启动 RenderDoc 帧捕获",
|
||||||
"zh_TW": ""
|
"zh_TW": "啟動 RenderDoc 畫格擷取"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ID": "MenuBarActions_EndCapture",
|
"ID": "MenuBarActions_EndCapture",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "RenderDoc Frame-Aufnahme beenden",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "End RenderDoc Frame Capture",
|
"en_US": "End RenderDoc Frame Capture",
|
||||||
"es_ES": "Detener la captura de fotograma de RenderDoc",
|
"es_ES": "Detener la captura de fotograma de RenderDoc",
|
||||||
@@ -47,14 +47,14 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "结束 RenderDoc 帧捕获",
|
"zh_CN": "结束 RenderDoc 帧捕获",
|
||||||
"zh_TW": ""
|
"zh_TW": "停止 RenderDoc 畫格擷取"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ID": "MenuBarActions_DiscardCapture",
|
"ID": "MenuBarActions_DiscardCapture",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "RenderDoc Frame-Aufnahme verwerfen",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Discard RenderDoc Frame Capture",
|
"en_US": "Discard RenderDoc Frame Capture",
|
||||||
"es_ES": "Descartar la captura de fotograma de RenderDoc",
|
"es_ES": "Descartar la captura de fotograma de RenderDoc",
|
||||||
@@ -72,14 +72,14 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "丢弃 RenderDoc 帧捕获",
|
"zh_CN": "丢弃 RenderDoc 帧捕获",
|
||||||
"zh_TW": ""
|
"zh_TW": "捨棄 RenderDoc 畫格擷取"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ID": "MenuBarActions_DiscardCapture_ToolTip",
|
"ID": "MenuBarActions_DiscardCapture_ToolTip",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Beendet die jetzige RenderDoc Frame-Aufnahme, verwirft sofort das Ergebnis.",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Ends the currently active RenderDoc Frame Capture, immediately discarding its result.",
|
"en_US": "Ends the currently active RenderDoc Frame Capture, immediately discarding its result.",
|
||||||
"es_ES": "Finaliza la captura de fotograma de RenderDoc actualmente activa y descarta inmediatamente su resultado.",
|
"es_ES": "Finaliza la captura de fotograma de RenderDoc actualmente activa y descarta inmediatamente su resultado.",
|
||||||
@@ -97,7 +97,7 @@
|
|||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "结束当前正在进行的 RenderDoc 帧捕获,并立即丢弃其结果。",
|
"zh_CN": "结束当前正在进行的 RenderDoc 帧捕获,并立即丢弃其结果。",
|
||||||
"zh_TW": ""
|
"zh_TW": "停止正在執行的 RenderDoc 畫格擷取,且立即捨棄其結果。"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"ID": "MenuBarActionsOpenMiiEditor",
|
"ID": "MenuBarActionsOpenMiiEditor",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Mii-Editor",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Mii Editor",
|
"en_US": "Mii Editor",
|
||||||
"es_ES": "Editor de Mii",
|
"es_ES": "Editor de Mii",
|
||||||
@@ -254,7 +254,7 @@
|
|||||||
"ID": "MenuBarFileOpenFromFileError",
|
"ID": "MenuBarFileOpenFromFileError",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "Keine Anwendungen im ausgewählten Datei gefunden.",
|
"de_DE": "Keine Anwendungen in ausgewählter Datei gefunden.",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "No applications found in selected file.",
|
"en_US": "No applications found in selected file.",
|
||||||
"es_ES": "No se encontraron aplicaciones en el archivo seleccionado.",
|
"es_ES": "No se encontraron aplicaciones en el archivo seleccionado.",
|
||||||
@@ -379,7 +379,7 @@
|
|||||||
"ID": "MenuBarFileOpenScreenshotsFolder",
|
"ID": "MenuBarFileOpenScreenshotsFolder",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Screenshots-Ordner öffnen",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Open Screenshots Folder",
|
"en_US": "Open Screenshots Folder",
|
||||||
"es_ES": "Abrir Carpeta de Capturas de Pantalla",
|
"es_ES": "Abrir Carpeta de Capturas de Pantalla",
|
||||||
@@ -575,6 +575,31 @@
|
|||||||
"zh_TW": "停止模擬"
|
"zh_TW": "停止模擬"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ID": "MenuBarOptionsRestartEmulation",
|
||||||
|
"Translations": {
|
||||||
|
"ar_SA": "",
|
||||||
|
"de_DE": "Emulation neustarten",
|
||||||
|
"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": {
|
||||||
@@ -1029,7 +1054,7 @@
|
|||||||
"ID": "MenuBarActionsTools",
|
"ID": "MenuBarActionsTools",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Werkzeuge",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Tools",
|
"en_US": "Tools",
|
||||||
"es_ES": "Herramientas",
|
"es_ES": "Herramientas",
|
||||||
@@ -1329,7 +1354,7 @@
|
|||||||
"ID": "MenuBarHelpMultiplayer",
|
"ID": "MenuBarHelpMultiplayer",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "متعدد اللاعبين (LDN/LAN)",
|
"ar_SA": "متعدد اللاعبين (LDN/LAN)",
|
||||||
"de_DE": "",
|
"de_DE": "Mehrspieler (LDN/LAN)",
|
||||||
"el_GR": "Πολλαπλοί Παίκτες (LDN/LAN)",
|
"el_GR": "Πολλαπλοί Παίκτες (LDN/LAN)",
|
||||||
"en_US": "Multiplayer (LDN/LAN)",
|
"en_US": "Multiplayer (LDN/LAN)",
|
||||||
"es_ES": "Multijugador (LDN/LAN)",
|
"es_ES": "Multijugador (LDN/LAN)",
|
||||||
@@ -1779,7 +1804,7 @@
|
|||||||
"ID": "GameListSortStatusNameAscending",
|
"ID": "GameListSortStatusNameAscending",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Titel: A-Z",
|
||||||
"el_GR": "Όνομα: A-Z",
|
"el_GR": "Όνομα: A-Z",
|
||||||
"en_US": "Title: A-Z",
|
"en_US": "Title: A-Z",
|
||||||
"es_ES": "Título: A-Z",
|
"es_ES": "Título: A-Z",
|
||||||
@@ -1804,7 +1829,7 @@
|
|||||||
"ID": "GameListSortStatusNameDescending",
|
"ID": "GameListSortStatusNameDescending",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Titel: Z-A",
|
||||||
"el_GR": "Τίτλος: Z-A",
|
"el_GR": "Τίτλος: Z-A",
|
||||||
"en_US": "Title: Z-A",
|
"en_US": "Title: Z-A",
|
||||||
"es_ES": "Título: Z-A",
|
"es_ES": "Título: Z-A",
|
||||||
@@ -1929,7 +1954,7 @@
|
|||||||
"ID": "GameListHeaderTitleId",
|
"ID": "GameListHeaderTitleId",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Titel ID:",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Title ID:",
|
"en_US": "Title ID:",
|
||||||
"es_ES": "ID del Titulo:",
|
"es_ES": "ID del Titulo:",
|
||||||
@@ -2229,7 +2254,7 @@
|
|||||||
"ID": "GameListContextMenuCacheManagementNukePptc",
|
"ID": "GameListContextMenuCacheManagementNukePptc",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "PPTC Cache löschen",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Purge PPTC Cache",
|
"en_US": "Purge PPTC Cache",
|
||||||
"es_ES": "Purgar Caché PPTC",
|
"es_ES": "Purgar Caché PPTC",
|
||||||
@@ -2254,7 +2279,7 @@
|
|||||||
"ID": "GameListContextMenuCacheManagementPurgeShaderCache",
|
"ID": "GameListContextMenuCacheManagementPurgeShaderCache",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "تنظيف ذاكرة مرشحات الفيديو المؤقتة",
|
"ar_SA": "تنظيف ذاكرة مرشحات الفيديو المؤقتة",
|
||||||
"de_DE": "Shader Cache löschen",
|
"de_DE": "Shader-Cache löschen",
|
||||||
"el_GR": "Εκκαθάριση Προσωρινής Μνήμης Shader",
|
"el_GR": "Εκκαθάριση Προσωρινής Μνήμης Shader",
|
||||||
"en_US": "Purge Shader Cache",
|
"en_US": "Purge Shader Cache",
|
||||||
"es_ES": "Limpiar Caché de Sombreadores",
|
"es_ES": "Limpiar Caché de Sombreadores",
|
||||||
@@ -2579,7 +2604,7 @@
|
|||||||
"ID": "GameListContextMenuCreateShortcutToolTip",
|
"ID": "GameListContextMenuCreateShortcutToolTip",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "أنشئ اختصار سطح مكتب لتشغيل التطبيق المحدد.",
|
"ar_SA": "أنشئ اختصار سطح مكتب لتشغيل التطبيق المحدد.",
|
||||||
"de_DE": "Erstelle eine Desktop-Verknüpfung die die gewählte Anwendung startet.",
|
"de_DE": "Erstelle eine Desktop-Verknüpfung, die die gewählte Anwendung startet.",
|
||||||
"el_GR": "Δημιουργία συντόμευσης επιφάνειας εργασίας που ανοίγει την επιλεγμένη εφαρμογή.",
|
"el_GR": "Δημιουργία συντόμευσης επιφάνειας εργασίας που ανοίγει την επιλεγμένη εφαρμογή.",
|
||||||
"en_US": "Create a Desktop Shortcut that launches the selected Application.",
|
"en_US": "Create a Desktop Shortcut that launches the selected Application.",
|
||||||
"es_ES": "Crear un acceso directo en el escritorio que lance la aplicación seleccionada.",
|
"es_ES": "Crear un acceso directo en el escritorio que lance la aplicación seleccionada.",
|
||||||
@@ -2604,7 +2629,7 @@
|
|||||||
"ID": "GameListContextMenuCreateCustomConfiguration",
|
"ID": "GameListContextMenuCreateCustomConfiguration",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Eigene Konfiguration erstellen",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Create Custom Configuration",
|
"en_US": "Create Custom Configuration",
|
||||||
"es_ES": "Crear Configuración Personalizada",
|
"es_ES": "Crear Configuración Personalizada",
|
||||||
@@ -2629,7 +2654,7 @@
|
|||||||
"ID": "GameListContextMenuEditCustomConfiguration",
|
"ID": "GameListContextMenuEditCustomConfiguration",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Eigene Konfiguration bearbeiten",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Edit Custom Configuration",
|
"en_US": "Edit Custom Configuration",
|
||||||
"es_ES": "Editar Configuración Personalizada",
|
"es_ES": "Editar Configuración Personalizada",
|
||||||
@@ -2654,7 +2679,7 @@
|
|||||||
"ID": "GameListContextMenuCreateShortcutToolTipMacOS",
|
"ID": "GameListContextMenuCreateShortcutToolTipMacOS",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "أنشئ اختصار يُشغل التطبيق المحدد في مجلد تطبيقات macOS.",
|
"ar_SA": "أنشئ اختصار يُشغل التطبيق المحدد في مجلد تطبيقات macOS.",
|
||||||
"de_DE": "Erstellen Sie eine Verknüpfung im MacOS-Programme-Ordner, die die ausgewählte Anwendung startet.",
|
"de_DE": "Erstellt eine Verknüpfung im MacOS-Programme-Ordner, die die ausgewählte Anwendung startet.",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Create a shortcut in macOS's Applications folder that launches the selected Application.",
|
"en_US": "Create a shortcut in macOS's Applications folder that launches the selected Application.",
|
||||||
"es_ES": "Crea un acceso directo en la carpeta de Aplicaciones de macOS que inicie la Aplicación seleccionada.",
|
"es_ES": "Crea un acceso directo en la carpeta de Aplicaciones de macOS que inicie la Aplicación seleccionada.",
|
||||||
@@ -2679,7 +2704,7 @@
|
|||||||
"ID": "GameListContextMenuShowCompatEntry",
|
"ID": "GameListContextMenuShowCompatEntry",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Kompatibilitätseintrag",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Compatibility Entry",
|
"en_US": "Compatibility Entry",
|
||||||
"es_ES": "Entrada de Compatibilidad",
|
"es_ES": "Entrada de Compatibilidad",
|
||||||
@@ -2704,7 +2729,7 @@
|
|||||||
"ID": "GameListContextMenuShowCompatEntryToolTip",
|
"ID": "GameListContextMenuShowCompatEntryToolTip",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Zeigt das ausgewählte Spiel in der Kompatibilitätsliste an, die normalerweise über das Hilfe-Menü aufgerufen werden kann.",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Shows the selected game in the Compatibility List, normally accessible via the Help menu.",
|
"en_US": "Shows the selected game in the Compatibility List, normally accessible via the Help menu.",
|
||||||
"es_ES": "Mostra el juego seleccionado en la lista de compatibilidad, a la que normalmente se accede desde el menú Ayuda.",
|
"es_ES": "Mostra el juego seleccionado en la lista de compatibilidad, a la que normalmente se accede desde el menú Ayuda.",
|
||||||
@@ -2729,7 +2754,7 @@
|
|||||||
"ID": "CreateCustomConfigurationToolTip",
|
"ID": "CreateCustomConfigurationToolTip",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "ينشئ تكوينًا مستقلًا للعبة الحالية",
|
"ar_SA": "ينشئ تكوينًا مستقلًا للعبة الحالية",
|
||||||
"de_DE": "Erstellt eine unabhängige Konfiguration für das aktuelle Spiel",
|
"de_DE": "Erstellt eine benutzerdefinierte Konfiguration für das ausgewählte Spiel",
|
||||||
"el_GR": "Δημιουργεί μια ανεξάρτητη διαμόρφωση για το τρέχον παιχνίδι",
|
"el_GR": "Δημιουργεί μια ανεξάρτητη διαμόρφωση για το τρέχον παιχνίδι",
|
||||||
"en_US": "Creates an independent configuration for the selected game",
|
"en_US": "Creates an independent configuration for the selected game",
|
||||||
"es_ES": "Crea una configuración independiente para el juego actual",
|
"es_ES": "Crea una configuración independiente para el juego actual",
|
||||||
@@ -2754,7 +2779,7 @@
|
|||||||
"ID": "EditCustomConfigurationToolTip",
|
"ID": "EditCustomConfigurationToolTip",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Bearbeitet deine benutzerdefinierte Konfiguration für das ausgewählte Spiel.",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Edit your existing independent configuration for the selected game",
|
"en_US": "Edit your existing independent configuration for the selected game",
|
||||||
"es_ES": "Editar su configuración independiente existente para el juego seleccionado",
|
"es_ES": "Editar su configuración independiente existente para el juego seleccionado",
|
||||||
@@ -2779,7 +2804,7 @@
|
|||||||
"ID": "GameListContextMenuShowGameData",
|
"ID": "GameListContextMenuShowGameData",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Spiel-Informationen",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Game Info",
|
"en_US": "Game Info",
|
||||||
"es_ES": "Información del Juego",
|
"es_ES": "Información del Juego",
|
||||||
@@ -2879,7 +2904,7 @@
|
|||||||
"ID": "GameListContextMenuTrimXCI",
|
"ID": "GameListContextMenuTrimXCI",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "XCI-Datei prüfen & trimmen",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Check & Trim XCI File",
|
"en_US": "Check & Trim XCI File",
|
||||||
"es_ES": "Verificar & Recortar Archivo XCI",
|
"es_ES": "Verificar & Recortar Archivo XCI",
|
||||||
@@ -2904,7 +2929,7 @@
|
|||||||
"ID": "GameListContextMenuTrimXCIToolTip",
|
"ID": "GameListContextMenuTrimXCIToolTip",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Trimmt und prüft die XCI-Datei um Speicherplatz zu sparen.",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Check and Trim XCI File to Save Disk Space",
|
"en_US": "Check and Trim XCI File to Save Disk Space",
|
||||||
"es_ES": "Verificar y Recortar Archivo XCI para Ahorrar Espacio en Disco",
|
"es_ES": "Verificar y Recortar Archivo XCI para Ahorrar Espacio en Disco",
|
||||||
@@ -2979,7 +3004,7 @@
|
|||||||
"ID": "StatusBarXCIFileTrimming",
|
"ID": "StatusBarXCIFileTrimming",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "XCI-Datei trimmen: '{0}'",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Trimming XCI File '{0}'",
|
"en_US": "Trimming XCI File '{0}'",
|
||||||
"es_ES": "Recortando el Siguiente Archivo XCI: '{0}'",
|
"es_ES": "Recortando el Siguiente Archivo XCI: '{0}'",
|
||||||
@@ -3579,7 +3604,7 @@
|
|||||||
"ID": "SettingsTabGeneralDisableInputWhenOutOfFocus",
|
"ID": "SettingsTabGeneralDisableInputWhenOutOfFocus",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Eingabe deaktivieren wenn nicht fokussiert",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Disable Input when Out of Focus",
|
"en_US": "Disable Input when Out of Focus",
|
||||||
"es_ES": "Desactivar la Entrada al Perder el Foco",
|
"es_ES": "Desactivar la Entrada al Perder el Foco",
|
||||||
@@ -4604,7 +4629,7 @@
|
|||||||
"ID": "SettingsTabSystemSystemTimeMatch",
|
"ID": "SettingsTabSystemSystemTimeMatch",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Mit Systemzeit synchronisieren",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Match System Time",
|
"en_US": "Match System Time",
|
||||||
"es_ES": "Usar Hora del Sistema",
|
"es_ES": "Usar Hora del Sistema",
|
||||||
@@ -4679,7 +4704,7 @@
|
|||||||
"ID": "SettingsTabSystemTurboMultiplier",
|
"ID": "SettingsTabSystemTurboMultiplier",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Turbo-Modus Multiplikator:",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Turbo Mode Multiplier:",
|
"en_US": "Turbo Mode Multiplier:",
|
||||||
"es_ES": "Multiplicator del Modo Turbo:",
|
"es_ES": "Multiplicator del Modo Turbo:",
|
||||||
@@ -5129,7 +5154,7 @@
|
|||||||
"ID": "SettingsTabSystemIgnoreControllerApplet",
|
"ID": "SettingsTabSystemIgnoreControllerApplet",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Controller-Applet ignorieren",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Ignore Controller Applet",
|
"en_US": "Ignore Controller Applet",
|
||||||
"es_ES": "Ignorar el Applet del Controlador",
|
"es_ES": "Ignorar el Applet del Controlador",
|
||||||
@@ -6075,6 +6100,31 @@
|
|||||||
"zh_TW": "檔案系統全域存取日誌模式:"
|
"zh_TW": "檔案系統全域存取日誌模式:"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ID": "SettingsTabLoggingEnableNetLogs",
|
||||||
|
"Translations": {
|
||||||
|
"ar_SA": "",
|
||||||
|
"de_DE": "",
|
||||||
|
"el_GR": "",
|
||||||
|
"en_US": "Enable Net Logs",
|
||||||
|
"es_ES": "Habilitar registros de red.",
|
||||||
|
"fr_FR": "",
|
||||||
|
"he_IL": "",
|
||||||
|
"it_IT": "",
|
||||||
|
"ja_JP": "",
|
||||||
|
"ko_KR": "",
|
||||||
|
"no_NO": "",
|
||||||
|
"pl_PL": "",
|
||||||
|
"pt_BR": "",
|
||||||
|
"ru_RU": "",
|
||||||
|
"sv_SE": "",
|
||||||
|
"th_TH": "",
|
||||||
|
"tr_TR": "",
|
||||||
|
"uk_UA": "",
|
||||||
|
"zh_CN": "",
|
||||||
|
"zh_TW": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ID": "SettingsTabLoggingDeveloperOptions",
|
"ID": "SettingsTabLoggingDeveloperOptions",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
@@ -6454,7 +6504,7 @@
|
|||||||
"ID": "SettingsButtonResetConfirm",
|
"ID": "SettingsButtonResetConfirm",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Ich möchte meine Einstellungen zurücksetzen.",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "I want to reset my settings.",
|
"en_US": "I want to reset my settings.",
|
||||||
"es_ES": "Quiero restablecer mi Configuración.",
|
"es_ES": "Quiero restablecer mi Configuración.",
|
||||||
@@ -11300,6 +11350,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": {
|
||||||
@@ -12754,7 +12829,7 @@
|
|||||||
"ID": "DialogRebooterMessage",
|
"ID": "DialogRebooterMessage",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "من فضلك انتظر، المحاكي في طور إعادة التشغيل",
|
"ar_SA": "من فضلك انتظر، المحاكي في طور إعادة التشغيل",
|
||||||
"de_DE": "Bitte warten Sie, der Emulator wird neu gestartet",
|
"de_DE": "Bitte warte, der Emulator wird neu gestartet",
|
||||||
"el_GR": "Παρακαλώ περιμένετε, ο εξομοιωτής επανεκκινείται",
|
"el_GR": "Παρακαλώ περιμένετε, ο εξομοιωτής επανεκκινείται",
|
||||||
"en_US": "Please wait, the emulator is restarting",
|
"en_US": "Please wait, the emulator is restarting",
|
||||||
"es_ES": "Por favor, espere, el emulador se está reiniciando",
|
"es_ES": "Por favor, espere, el emulador se está reiniciando",
|
||||||
@@ -14329,7 +14404,7 @@
|
|||||||
"ID": "DialogUserProfileUnsavedChangesMessage",
|
"ID": "DialogUserProfileUnsavedChangesMessage",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "لقد قمت بإجراء تغييرات على الملف الشخصي لهذا المستخدم هذا ولم يتم حفظها.",
|
"ar_SA": "لقد قمت بإجراء تغييرات على الملف الشخصي لهذا المستخدم هذا ولم يتم حفظها.",
|
||||||
"de_DE": "Sie haben Änderungen an diesem Nutzerprofil vorgenommen, die nicht gespeichert wurden.",
|
"de_DE": "Du hast Änderungen an diesem Nutzerprofil vorgenommen, die nicht gespeichert wurden.",
|
||||||
"el_GR": "Έχετε κάνει αλλαγές σε αυτό το προφίλ χρήστη που δεν έχουν αποθηκευτεί.",
|
"el_GR": "Έχετε κάνει αλλαγές σε αυτό το προφίλ χρήστη που δεν έχουν αποθηκευτεί.",
|
||||||
"en_US": "You have made changes to this user profile that have not been saved.",
|
"en_US": "You have made changes to this user profile that have not been saved.",
|
||||||
"es_ES": "Ha realizado cambios en este perfil de usuario que no han sido guardados.",
|
"es_ES": "Ha realizado cambios en este perfil de usuario que no han sido guardados.",
|
||||||
@@ -14354,7 +14429,7 @@
|
|||||||
"ID": "DialogUserProfileUnsavedChangesSubMessage",
|
"ID": "DialogUserProfileUnsavedChangesSubMessage",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "هل تريد تجاهل التغييرات؟",
|
"ar_SA": "هل تريد تجاهل التغييرات؟",
|
||||||
"de_DE": "Möchten Sie Ihre Änderungen wirklich verwerfen?",
|
"de_DE": "Möchtest du deine Änderungen wirklich verwerfen?",
|
||||||
"el_GR": "Θέλετε να απορρίψετε τις αλλαγές σας;",
|
"el_GR": "Θέλετε να απορρίψετε τις αλλαγές σας;",
|
||||||
"en_US": "Do you want to discard your changes?",
|
"en_US": "Do you want to discard your changes?",
|
||||||
"es_ES": "¿Quieres descartar los cambios realizados?",
|
"es_ES": "¿Quieres descartar los cambios realizados?",
|
||||||
@@ -15301,28 +15376,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 網頁。"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -15381,23 +15456,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 上了解更多資訊。"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -16054,7 +16129,7 @@
|
|||||||
"ID": "AddGameDirBoxTooltip",
|
"ID": "AddGameDirBoxTooltip",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "أدخل مسار الدليل أو استخدم زر \"إضافة\"...",
|
"ar_SA": "أدخل مسار الدليل أو استخدم زر \"إضافة\"...",
|
||||||
"de_DE": "Geben Sie einen Pfad ein oder nutzen Sie \"Hinzufügen\"...",
|
"de_DE": "Gebe einen Pfad ein oder nutze \"Hinzufügen\"...",
|
||||||
"el_GR": "Εισαγάγετε διαδρομή ή χρησιμοποιήστε το \"Προσθήκη\"...",
|
"el_GR": "Εισαγάγετε διαδρομή ή χρησιμοποιήστε το \"Προσθήκη\"...",
|
||||||
"en_US": "Enter a directory path or use the \"Add\" button...",
|
"en_US": "Enter a directory path or use the \"Add\" button...",
|
||||||
"es_ES": "Escriba una ruta o use el botón \"Agregar\"...",
|
"es_ES": "Escriba una ruta o use el botón \"Agregar\"...",
|
||||||
@@ -16604,7 +16679,7 @@
|
|||||||
"ID": "SkipUserProfilesTooltip",
|
"ID": "SkipUserProfilesTooltip",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "Diese Option überspringt den Dialog 'Benutzerprofile verwalten' während des Spiels und verwendet ein voreingestelltes Profil.\n\nDie Profilumschaltung finden Sie unter 'Einstellungen' - 'Benutzerprofile verwalten'. Wählen Sie das gewünschte Profil aus, bevor Sie das Spiel laden.",
|
"de_DE": "Diese Option überspringt den Dialog 'Benutzerprofile verwalten' während des Spiels und verwendet ein voreingestelltes Profil.\n\nDie Profilumschaltung findest du unter 'Einstellungen' - 'Benutzerprofile verwalten'. Wähle das gewünschte Profil aus, bevor du das Spiel lädst.",
|
||||||
"el_GR": "Αυτή η επιλογή παρακάμπτει το παράθυρο διαλόγου 'Διαχειριστής Προφίλ Χρήστη' κατά τη διάρκεια του παιχνιδιού, χρησιμοποιώντας ένα προεπιλεγμένο προφίλ.\n\nΗ εναλλαγή προφίλ βρίσκεται στις 'Ρυθμίσεις' - 'Διαχειριστής Προφίλ Χρήστη'. Επιλέξτε το επιθυμητό προφίλ πριν φορτώσετε το παιχνίδι.",
|
"el_GR": "Αυτή η επιλογή παρακάμπτει το παράθυρο διαλόγου 'Διαχειριστής Προφίλ Χρήστη' κατά τη διάρκεια του παιχνιδιού, χρησιμοποιώντας ένα προεπιλεγμένο προφίλ.\n\nΗ εναλλαγή προφίλ βρίσκεται στις 'Ρυθμίσεις' - 'Διαχειριστής Προφίλ Χρήστη'. Επιλέξτε το επιθυμητό προφίλ πριν φορτώσετε το παιχνίδι.",
|
||||||
"en_US": "This option skips the 'Manage User Profiles' dialog during gameplay, using a pre-selected profile.\n\nProfile switching is found in 'Options' - 'User Profiles'. Select the desired profile before loading the game.",
|
"en_US": "This option skips the 'Manage User Profiles' dialog during gameplay, using a pre-selected profile.\n\nProfile switching is found in 'Options' - 'User Profiles'. Select the desired profile before loading the game.",
|
||||||
"es_ES": "Esta opción omite el diálogo de 'Gestionar perfiles de usuario' durante el juego, utilizando un perfil preseleccionado.\n\nEl cambio de perfil se encuentra en 'Opciones' - 'Perfiles de Usuario'. Seleccione el perfil deseado antes de cargar el juego.",
|
"es_ES": "Esta opción omite el diálogo de 'Gestionar perfiles de usuario' durante el juego, utilizando un perfil preseleccionado.\n\nEl cambio de perfil se encuentra en 'Opciones' - 'Perfiles de Usuario'. Seleccione el perfil deseado antes de cargar el juego.",
|
||||||
@@ -16704,7 +16779,7 @@
|
|||||||
"ID": "ResolutionScaleTooltip",
|
"ID": "ResolutionScaleTooltip",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "يضاعف دقة عرض اللعبة.\n\nقد لا تعمل بعض الألعاب مع هذا وتبدو منقطة حتى عند زيادة الدقة؛ بالنسبة لهذه الألعاب، قد تحتاج إلى العثور على تعديلات تزيل تنعيم الحواف أو تزيد من دقة العرض الداخلي. لاستخدام الأخير، من المحتمل أن ترغب في تحديد أصلي.\n\nيمكن تغيير هذا الخيار أثناء تشغيل اللعبة بالنقر فوق \"تطبيق\" أدناه؛ يمكنك ببساطة تحريك نافذة الإعدادات جانبًا والتجربة حتى تجد المظهر المفضل للعبة.\n\nضع في اعتبارك أن 4x مبالغة في أي إعداد تقريبًا.",
|
"ar_SA": "يضاعف دقة عرض اللعبة.\n\nقد لا تعمل بعض الألعاب مع هذا وتبدو منقطة حتى عند زيادة الدقة؛ بالنسبة لهذه الألعاب، قد تحتاج إلى العثور على تعديلات تزيل تنعيم الحواف أو تزيد من دقة العرض الداخلي. لاستخدام الأخير، من المحتمل أن ترغب في تحديد أصلي.\n\nيمكن تغيير هذا الخيار أثناء تشغيل اللعبة بالنقر فوق \"تطبيق\" أدناه؛ يمكنك ببساطة تحريك نافذة الإعدادات جانبًا والتجربة حتى تجد المظهر المفضل للعبة.\n\nضع في اعتبارك أن 4x مبالغة في أي إعداد تقريبًا.",
|
||||||
"de_DE": "Multipliziert die Rendering-Auflösung des Spiels.\n\nEinige wenige Spiele funktionieren damit nicht und sehen auch bei höherer Auflösung pixelig aus; für diese Spiele müssen Sie möglicherweise Mods finden, die Anti-Aliasing entfernen oder die interne Rendering-Auflösung erhöhen. Für die Verwendung von Letzterem sollten Sie Native wählen.\n\nSie können diese Option ändern, während ein Spiel läuft, indem Sie unten auf \"Übernehmen\" klicken; Sie können das Einstellungsfenster einfach zur Seite schieben und experimentieren, bis Sie Ihr bevorzugtes Aussehen für ein Spiel gefunden haben.\n\nDenken Sie daran, dass 4x für praktisch jedes Setup Overkill ist.",
|
"de_DE": "Multipliziert die Rendering-Auflösung des Spiels.\n\nEinige wenige Spiele funktionieren damit nicht und sehen auch bei höherer Auflösung pixelig aus; für diese Spiele musst du möglicherweise Mods finden, die Anti-Aliasing entfernen oder die interne Rendering-Auflösung erhöhen. Für die Verwendung von Letzterem solltest du Native wählen.\n\nDu kannst diese Option ändern, während ein Spiel läuft, indem du unten auf \"Übernehmen\" klickst; Du kannst das Einstellungsfenster einfach zur Seite schieben und experimentieren, bis das bevorzugte Aussehen für ein Spiel gefunden wurde.\n\nDenk daran, dass 4x für praktisch jedes Setup Overkill ist.",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Multiplies the game's rendering resolution.\n\nA few games may not work with this and look pixelated even when the resolution is increased; for those games, you may need to find mods that remove anti-aliasing or that increase their internal rendering resolution. For using the latter, you'll likely want to select Native.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nKeep in mind 4x is overkill for virtually any setup.",
|
"en_US": "Multiplies the game's rendering resolution.\n\nA few games may not work with this and look pixelated even when the resolution is increased; for those games, you may need to find mods that remove anti-aliasing or that increase their internal rendering resolution. For using the latter, you'll likely want to select Native.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nKeep in mind 4x is overkill for virtually any setup.",
|
||||||
"es_ES": "Multiplica la resolución de rendereo del juego.\n\nAlgunos juegos podrían no funcionar con esto y verse pixelado al aumentar la resolución; en esos casos, quizás sería necesario buscar mods que de anti-aliasing o que aumenten la resolución interna. Para usar este último, probablemente necesitarás seleccionar Nativa.\n\nEsta opción puede ser modificada mientras que un juego este corriendo haciendo click en \"Aplicar\" más abajo; simplemente puedes mover la ventana de configuración a un lado y experimentar hasta que encuentres tu estilo preferido para un juego.\n\nTener en cuenta que 4x es excesivo para prácticamente cualquier configuración.",
|
"es_ES": "Multiplica la resolución de rendereo del juego.\n\nAlgunos juegos podrían no funcionar con esto y verse pixelado al aumentar la resolución; en esos casos, quizás sería necesario buscar mods que de anti-aliasing o que aumenten la resolución interna. Para usar este último, probablemente necesitarás seleccionar Nativa.\n\nEsta opción puede ser modificada mientras que un juego este corriendo haciendo click en \"Aplicar\" más abajo; simplemente puedes mover la ventana de configuración a un lado y experimentar hasta que encuentres tu estilo preferido para un juego.\n\nTener en cuenta que 4x es excesivo para prácticamente cualquier configuración.",
|
||||||
@@ -16779,7 +16854,7 @@
|
|||||||
"ID": "AspectRatioTooltip",
|
"ID": "AspectRatioTooltip",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "يتم تطبيق نسبة العرض إلى الارتفاع على نافذة العارض.\n\nقم بتغيير هذا فقط إذا كنت تستخدم تعديل نسبة العرض إلى الارتفاع للعبتك، وإلا سيتم تمديد الرسومات.\n\nاتركه16:9 إذا لم تكن متأكدا.",
|
"ar_SA": "يتم تطبيق نسبة العرض إلى الارتفاع على نافذة العارض.\n\nقم بتغيير هذا فقط إذا كنت تستخدم تعديل نسبة العرض إلى الارتفاع للعبتك، وإلا سيتم تمديد الرسومات.\n\nاتركه16:9 إذا لم تكن متأكدا.",
|
||||||
"de_DE": "Seitenverhältnis, das auf das Renderer-Fenster angewendet wird.\n\nÄndern Sie dies nur, wenn Sie einen Seitenverhältnis-Mod für Ihr Spiel verwenden, da sonst die Grafik gestreckt wird.\n\nLassen Sie es auf 16:9, wenn Sie unsicher sind.",
|
"de_DE": "Seitenverhältnis, das auf das Renderer-Fenster angewendet wird.\n\nÄnder dies nur, wenn ein Seitenverhältnis-Mod für das Spiel verwendet wird, da sonst die Grafik gestreckt wird.\n\nLass es auf 16:9, wenn du unsicher bist.",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Aspect Ratio applied to the renderer window.\n\nOnly change this if you're using an aspect ratio mod for your game, otherwise the graphics will be stretched.\n\nLeave on 16:9 if unsure.",
|
"en_US": "Aspect Ratio applied to the renderer window.\n\nOnly change this if you're using an aspect ratio mod for your game, otherwise the graphics will be stretched.\n\nLeave on 16:9 if unsure.",
|
||||||
"es_ES": "Relación de aspecto aplicada a la ventana del renderizador.\n\nSolamente modificar esto si estás utilizando un mod de relación de aspecto para su juego, en cualquier otro caso los gráficos se estirarán.\n\nDejar en 16:9 si no sabe que hacer.",
|
"es_ES": "Relación de aspecto aplicada a la ventana del renderizador.\n\nSolamente modificar esto si estás utilizando un mod de relación de aspecto para su juego, en cualquier otro caso los gráficos se estirarán.\n\nDejar en 16:9 si no sabe que hacer.",
|
||||||
@@ -17025,6 +17100,31 @@
|
|||||||
"zh_TW": "啟用檔案系統存取日誌輸出到控制台中。可能的模式為 0 到 3。"
|
"zh_TW": "啟用檔案系統存取日誌輸出到控制台中。可能的模式為 0 到 3。"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ID": "NetLogTooltip",
|
||||||
|
"Translations": {
|
||||||
|
"ar_SA": "",
|
||||||
|
"de_DE": "",
|
||||||
|
"el_GR": "",
|
||||||
|
"en_US": "Prints network log messages in the console.",
|
||||||
|
"es_ES": "Imprimir registros de red en la consola.",
|
||||||
|
"fr_FR": "",
|
||||||
|
"he_IL": "",
|
||||||
|
"it_IT": "",
|
||||||
|
"ja_JP": "",
|
||||||
|
"ko_KR": "",
|
||||||
|
"no_NO": "",
|
||||||
|
"pl_PL": "",
|
||||||
|
"pt_BR": "",
|
||||||
|
"ru_RU": "",
|
||||||
|
"sv_SE": "",
|
||||||
|
"th_TH": "",
|
||||||
|
"tr_TR": "",
|
||||||
|
"uk_UA": "",
|
||||||
|
"zh_CN": "",
|
||||||
|
"zh_TW": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ID": "DeveloperOptionTooltip",
|
"ID": "DeveloperOptionTooltip",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
@@ -18679,7 +18779,7 @@
|
|||||||
"ID": "ControllerAppletDescription",
|
"ID": "ControllerAppletDescription",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "الإعدادات الحالية غير صالحة. افتح الإعدادات وأعد تكوين المدخلات الخاصة بك.",
|
"ar_SA": "الإعدادات الحالية غير صالحة. افتح الإعدادات وأعد تكوين المدخلات الخاصة بك.",
|
||||||
"de_DE": "Ihre aktuelle Konfiguration ist ungültig. Öffnen Sie die Einstellungen und konfigurieren Sie Ihre Eingaben neu.",
|
"de_DE": "Ihre aktuelle Konfiguration ist ungültig. Öffne die Einstellungen und konfiguriere die Eingaben neu.",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Your current configuration is invalid. Open settings and reconfigure your inputs.",
|
"en_US": "Your current configuration is invalid. Open settings and reconfigure your inputs.",
|
||||||
"es_ES": "Tu configuración actual no es válida. Abre la configuración y vuelve a configurar tus entradas",
|
"es_ES": "Tu configuración actual no es válida. Abre la configuración y vuelve a configurar tus entradas",
|
||||||
@@ -21229,7 +21329,7 @@
|
|||||||
"ID": "SettingsTabGraphicsBackendTooltip",
|
"ID": "SettingsTabGraphicsBackendTooltip",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "حدد الواجهة الخلفية للرسومات التي سيتم استخدامها في المحاكي.\n\nيعد برنامج فولكان أفضل بشكل عام لجميع بطاقات الرسومات الحديثة، طالما أن برامج التشغيل الخاصة بها محدثة. يتميز فولكان أيضا بتجميع مظللات أسرع (أقل تقطيعا) على جميع بائعي وحدات معالجة الرسومات.\n\nقد يحقق أوبن جي أل نتائج أفضل على وحدات معالجة الرسومات إنفيديا القديمة، أو على وحدات معالجة الرسومات إي إم دي القديمة على لينكس، أو على وحدات معالجة الرسومات ذات ذاكرة الوصول العشوائي للفيديوالأقل، على الرغم من أن تعثرات تجميع المظللات ستكون أكبر.\n\nاضبط على فولكان إذا لم تكن متأكدا. اضبط على أوبن جي أل إذا كانت وحدة معالجة الرسومات الخاصة بك لا تدعم فولكان حتى مع أحدث برامج تشغيل الرسومات.",
|
"ar_SA": "حدد الواجهة الخلفية للرسومات التي سيتم استخدامها في المحاكي.\n\nيعد برنامج فولكان أفضل بشكل عام لجميع بطاقات الرسومات الحديثة، طالما أن برامج التشغيل الخاصة بها محدثة. يتميز فولكان أيضا بتجميع مظللات أسرع (أقل تقطيعا) على جميع بائعي وحدات معالجة الرسومات.\n\nقد يحقق أوبن جي أل نتائج أفضل على وحدات معالجة الرسومات إنفيديا القديمة، أو على وحدات معالجة الرسومات إي إم دي القديمة على لينكس، أو على وحدات معالجة الرسومات ذات ذاكرة الوصول العشوائي للفيديوالأقل، على الرغم من أن تعثرات تجميع المظللات ستكون أكبر.\n\nاضبط على فولكان إذا لم تكن متأكدا. اضبط على أوبن جي أل إذا كانت وحدة معالجة الرسومات الخاصة بك لا تدعم فولكان حتى مع أحدث برامج تشغيل الرسومات.",
|
||||||
"de_DE": "Wählen Sie das Grafik-Backend, das im Emulator verwendet werden soll.\n\nVulkan ist insgesamt besser für alle modernen Grafikkarten geeignet, sofern deren Treiber auf dem neuesten Stand sind. Vulkan bietet auch eine schnellere Shader-Kompilierung (weniger Stottern) auf allen GPU-Anbietern.\n\nOpenGL kann auf alten Nvidia-GPUs, alten AMD-GPUs unter Linux oder auf GPUs mit geringerem VRAM bessere Ergebnisse erzielen, obwohl die Shader-Kompilierung stärker stottert.\n\nSetzen Sie auf Vulkan, wenn Sie unsicher sind. Stellen Sie OpenGL ein, wenn Ihr Grafikprozessor selbst mit den neuesten Grafiktreibern Vulkan nicht unterstützt.",
|
"de_DE": "Wähle das Grafik-Backend, das im Emulator verwendet werden soll.\n\nVulkan ist insgesamt besser für alle modernen Grafikkarten geeignet, sofern deren Treiber auf dem neuesten Stand sind. Vulkan bietet auch eine schnellere Shader-Kompilierung (weniger Stottern) auf allen GPU-Anbietern.\n\nOpenGL kann auf alten Nvidia-GPUs, alten AMD-GPUs unter Linux oder auf GPUs mit geringerem VRAM bessere Ergebnisse erzielen, obwohl die Shader-Kompilierung stärker stottert.\n\nSetze auf Vulkan, wenn du unsicher bist. Stelle auf OpenGL ein, wenn der Grafikprozessor selbst mit den neuesten Grafiktreibern Vulkan nicht unterstützt.",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Select the graphics backend that will be used in the emulator.\n\nVulkan is overall better for all modern graphics cards, as long as their drivers are up to date. Vulkan also features faster shader compilation (less stuttering) on all GPU vendors.\n\nOpenGL may achieve better results on old Nvidia GPUs, on old AMD GPUs on Linux, or on GPUs with lower VRAM, though shader compilation stutters will be greater.\n\nSet to Vulkan if unsure. Set to OpenGL if your GPU does not support Vulkan even with the latest graphics drivers.",
|
"en_US": "Select the graphics backend that will be used in the emulator.\n\nVulkan is overall better for all modern graphics cards, as long as their drivers are up to date. Vulkan also features faster shader compilation (less stuttering) on all GPU vendors.\n\nOpenGL may achieve better results on old Nvidia GPUs, on old AMD GPUs on Linux, or on GPUs with lower VRAM, though shader compilation stutters will be greater.\n\nSet to Vulkan if unsure. Set to OpenGL if your GPU does not support Vulkan even with the latest graphics drivers.",
|
||||||
"es_ES": "Seleccione el backend gráfico que utilizará el emulador.\n\nVulkan, en general, es mejor para todas las tarjetas gráficas modernas, mientras que sus controladores estén actualizados. Vulkan también cuenta con complicación más rápida de sombreadores (menos tirones) en todos los proveredores de GPU.\n\nOpenGL puede lograr mejores resultados en GPU Nvidia antiguas, GPU AMD antiguas en Linux o en GPUs con menor VRAM, aunque tirones de compilación de sombreadores serán mayores.\n\nSetear en Vulkan si no sabe que hacer. Setear en OpenGL si su GPU no tiene soporte para Vulkan aún con los últimos controladores gráficos.",
|
"es_ES": "Seleccione el backend gráfico que utilizará el emulador.\n\nVulkan, en general, es mejor para todas las tarjetas gráficas modernas, mientras que sus controladores estén actualizados. Vulkan también cuenta con complicación más rápida de sombreadores (menos tirones) en todos los proveredores de GPU.\n\nOpenGL puede lograr mejores resultados en GPU Nvidia antiguas, GPU AMD antiguas en Linux o en GPUs con menor VRAM, aunque tirones de compilación de sombreadores serán mayores.\n\nSetear en Vulkan si no sabe que hacer. Setear en OpenGL si su GPU no tiene soporte para Vulkan aún con los últimos controladores gráficos.",
|
||||||
@@ -21279,7 +21379,7 @@
|
|||||||
"ID": "SettingsEnableTextureRecompressionTooltip",
|
"ID": "SettingsEnableTextureRecompressionTooltip",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "يضغط تكستر ASTC من أجل تقليل استخدام ذاكرة الوصول العشوائي للفيديو.\n\nتتضمن الألعاب التي تستخدم تنسيق النسيج هذا Astral Chain وBayonetta 3 وFire Emblem Engage وMetroid Prime Remastered وSuper Mario Bros. Wonder وThe Legend of Zelda: Tears of the Kingdom.\n\nمن المحتمل أن تتعطل بطاقات الرسومات التي تحتوي على 4 جيجا بايت من ذاكرة الوصول العشوائي للفيديو أو أقل في مرحلة ما أثناء تشغيل هذه الألعاب.\n\nقم بالتمكين فقط في حالة نفاد ذاكرة الوصول العشوائي للفيديو في الألعاب المذكورة أعلاه. اتركه معطلا إذا لم تكن متأكدا.",
|
"ar_SA": "يضغط تكستر ASTC من أجل تقليل استخدام ذاكرة الوصول العشوائي للفيديو.\n\nتتضمن الألعاب التي تستخدم تنسيق النسيج هذا Astral Chain وBayonetta 3 وFire Emblem Engage وMetroid Prime Remastered وSuper Mario Bros. Wonder وThe Legend of Zelda: Tears of the Kingdom.\n\nمن المحتمل أن تتعطل بطاقات الرسومات التي تحتوي على 4 جيجا بايت من ذاكرة الوصول العشوائي للفيديو أو أقل في مرحلة ما أثناء تشغيل هذه الألعاب.\n\nقم بالتمكين فقط في حالة نفاد ذاكرة الوصول العشوائي للفيديو في الألعاب المذكورة أعلاه. اتركه معطلا إذا لم تكن متأكدا.",
|
||||||
"de_DE": "Komprimiert ASTC-Texturen, um die VRAM-Nutzung zu reduzieren.\n\nZu den Spielen, die dieses Texturformat verwenden, gehören Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder und The Legend of Zelda: Tears of the Kingdom.\n\nGrafikkarten mit 4GiB VRAM oder weniger werden beim Ausführen dieser Spiele wahrscheinlich irgendwann abstürzen.\n\nAktivieren Sie diese Option nur, wenn Ihnen bei den oben genannten Spielen der VRAM ausgeht. Lassen Sie es aus, wenn Sie unsicher sind.",
|
"de_DE": "Komprimiert ASTC-Texturen, um die VRAM-Nutzung zu reduzieren.\n\nZu den Spielen, die dieses Texturformat verwenden, gehören Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder und The Legend of Zelda: Tears of the Kingdom.\n\nGrafikkarten mit 4GiB VRAM oder weniger werden beim Ausführen dieser Spiele wahrscheinlich irgendwann abstürzen.\n\nAktiviere diese Option nur, wenn Ihnen bei den oben genannten Spielen der VRAM ausgeht. Lass es aus, wenn du unsicher bist.",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Compresses ASTC textures in order to reduce VRAM usage.\n\nGames using this texture format include Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder and The Legend of Zelda: Tears of the Kingdom.\n\nGraphics cards with 4GiB VRAM or less will likely crash at some point while running these games.\n\nEnable only if you're running out of VRAM on the aforementioned games. Leave OFF if unsure.",
|
"en_US": "Compresses ASTC textures in order to reduce VRAM usage.\n\nGames using this texture format include Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder and The Legend of Zelda: Tears of the Kingdom.\n\nGraphics cards with 4GiB VRAM or less will likely crash at some point while running these games.\n\nEnable only if you're running out of VRAM on the aforementioned games. Leave OFF if unsure.",
|
||||||
"es_ES": "Comprimir texturas ASTC para reducir uso de VRAM.\n\nJuegos que utilizan este formato de textura incluyen Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder y The Legend of Zelda: Tears of the Kingdom.\n\nTarjetas gráficas con 4GiB de VRAM o menos probalemente se caeran en algún momento mientras que estén corriendo estos juegos.\n\nActivar solo si está quedan sin VRAM en los juegos antes mencionados. Desactívalo si no sabes qué hacer.",
|
"es_ES": "Comprimir texturas ASTC para reducir uso de VRAM.\n\nJuegos que utilizan este formato de textura incluyen Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder y The Legend of Zelda: Tears of the Kingdom.\n\nTarjetas gráficas con 4GiB de VRAM o menos probalemente se caeran en algún momento mientras que estén corriendo estos juegos.\n\nActivar solo si está quedan sin VRAM en los juegos antes mencionados. Desactívalo si no sabes qué hacer.",
|
||||||
@@ -21375,6 +21475,31 @@
|
|||||||
"zh_TW": "需要重新啟動 Ryujinx"
|
"zh_TW": "需要重新啟動 Ryujinx"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ID": "SettingsShowConsoleRestartMessage",
|
||||||
|
"Translations": {
|
||||||
|
"ar_SA": "",
|
||||||
|
"de_DE": "",
|
||||||
|
"el_GR": "",
|
||||||
|
"en_US": "The console will be available the next time Ryujinx starts.",
|
||||||
|
"es_ES": "La consola estará disponible la próxima vez que se inicie Ryujinx.",
|
||||||
|
"fr_FR": "La console sera disponible au prochain démarrage de Ryujinx.",
|
||||||
|
"he_IL": "",
|
||||||
|
"it_IT": "",
|
||||||
|
"ja_JP": "",
|
||||||
|
"ko_KR": "",
|
||||||
|
"no_NO": "",
|
||||||
|
"pl_PL": "",
|
||||||
|
"pt_BR": "",
|
||||||
|
"ru_RU": "",
|
||||||
|
"sv_SE": "",
|
||||||
|
"th_TH": "",
|
||||||
|
"tr_TR": "",
|
||||||
|
"uk_UA": "",
|
||||||
|
"zh_CN": "",
|
||||||
|
"zh_TW": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ID": "SettingsGpuBackendRestartMessage",
|
"ID": "SettingsGpuBackendRestartMessage",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
@@ -21929,7 +22054,7 @@
|
|||||||
"ID": "GraphicsAATooltip",
|
"ID": "GraphicsAATooltip",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "يتم تطبيق تنعيم الحواف على عرض اللعبة.\n\nسوف يقوم FXAA بتعتيم معظم الصورة، بينما سيحاول SMAA العثور على حواف خشنة وتنعيمها.\n\nلا ينصح باستخدامه مع فلتر FSR لتكبير.\n\nيمكن تغيير هذا الخيار أثناء تشغيل اللعبة بالنقر فوق \"تطبيق\" أدناه؛ يمكنك ببساطة تحريك نافذة الإعدادات جانبا والتجربة حتى تجد المظهر المفضل للعبة.\n\nاتركه على لا شيء إذا لم تكن متأكدا.",
|
"ar_SA": "يتم تطبيق تنعيم الحواف على عرض اللعبة.\n\nسوف يقوم FXAA بتعتيم معظم الصورة، بينما سيحاول SMAA العثور على حواف خشنة وتنعيمها.\n\nلا ينصح باستخدامه مع فلتر FSR لتكبير.\n\nيمكن تغيير هذا الخيار أثناء تشغيل اللعبة بالنقر فوق \"تطبيق\" أدناه؛ يمكنك ببساطة تحريك نافذة الإعدادات جانبا والتجربة حتى تجد المظهر المفضل للعبة.\n\nاتركه على لا شيء إذا لم تكن متأكدا.",
|
||||||
"de_DE": "Wendet Anti-Aliasing auf das Rendering des Spiels an.\n\nFXAA verwischt den größten Teil des Bildes, während SMAA versucht, gezackte Kanten zu finden und sie zu glätten.\n\nEs wird nicht empfohlen, diese Option in Verbindung mit dem FSR-Skalierungsfilter zu verwenden.\n\nDiese Option kann geändert werden, während ein Spiel läuft, indem Sie unten auf \"Anwenden\" klicken; Sie können das Einstellungsfenster einfach zur Seite schieben und experimentieren, bis Sie Ihr bevorzugtes Aussehen für ein Spiel gefunden haben.\n\nLassen Sie die Option auf NONE, wenn Sie unsicher sind.",
|
"de_DE": "Wendet Anti-Aliasing auf das Rendering des Spiels an.\n\nFXAA verwischt den größten Teil des Bildes, während SMAA versucht, gezackte Kanten zu finden und sie zu glätten.\n\nEs wird nicht empfohlen, diese Option in Verbindung mit dem FSR-Skalierungsfilter zu verwenden.\n\nDiese Option kann geändert werden, während ein Spiel läuft, indem du unten auf \"Anwenden\" klickst; Du können das Einstellungsfenster einfach zur Seite schieben und experimentieren, bis das bevorzugte Aussehen für ein Spiel gefunden wurde.\n\nLass die Option auf NONE, wenn du unsicher bist.",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Applies anti-aliasing to the game render.\n\nFXAA will blur most of the image, while SMAA will attempt to find jagged edges and smooth them out.\n\nNot recommended to use in conjunction with the FSR scaling filter.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on NONE if unsure.",
|
"en_US": "Applies anti-aliasing to the game render.\n\nFXAA will blur most of the image, while SMAA will attempt to find jagged edges and smooth them out.\n\nNot recommended to use in conjunction with the FSR scaling filter.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on NONE if unsure.",
|
||||||
"es_ES": "Aplica antia-aliasing al rendereo del juego.\n\nFXAA desenfocará la mayor parte del la iamgen, mientras que SMAA intentará encontrar bordes irregulares y suavizarlos.\n\nNo se recomienda usar en conjunto con filtro de escala FSR.\n\nEsta opción puede ser modificada mientras que esté corriendo el juego haciendo click en \"Aplicar\" más abajo; simplemente puedes mover la ventana de configuración a un lado y experimentar hasta que encuentres tu estilo preferido para un juego.\n\nDejar en NADA si no está seguro.",
|
"es_ES": "Aplica antia-aliasing al rendereo del juego.\n\nFXAA desenfocará la mayor parte del la iamgen, mientras que SMAA intentará encontrar bordes irregulares y suavizarlos.\n\nNo se recomienda usar en conjunto con filtro de escala FSR.\n\nEsta opción puede ser modificada mientras que esté corriendo el juego haciendo click en \"Aplicar\" más abajo; simplemente puedes mover la ventana de configuración a un lado y experimentar hasta que encuentres tu estilo preferido para un juego.\n\nDejar en NADA si no está seguro.",
|
||||||
@@ -22004,7 +22129,7 @@
|
|||||||
"ID": "GraphicsScalingFilterTooltip",
|
"ID": "GraphicsScalingFilterTooltip",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "اختر فلتر التكبير الذي سيتم تطبيقه عند استخدام مقياس الدقة.\n\nيعمل Bilinear بشكل جيد مع الألعاب ثلاثية الأبعاد وهو خيار افتراضي آمن.\n\nيوصى باستخدام Nearest لألعاب البكسل الفنية.\n\nFSR 1.0 هو مجرد مرشح توضيحي، ولا ينصح باستخدامه مع FXAA أو SMAA.\n\nيمكن تغيير هذا الخيار أثناء تشغيل اللعبة بالنقر فوق \"تطبيق\" أدناه؛ يمكنك ببساطة تحريك نافذة الإعدادات جانبا والتجربة حتى تجد المظهر المفضل للعبة.\n\nاتركه على Bilinear إذا لم تكن متأكدا.",
|
"ar_SA": "اختر فلتر التكبير الذي سيتم تطبيقه عند استخدام مقياس الدقة.\n\nيعمل Bilinear بشكل جيد مع الألعاب ثلاثية الأبعاد وهو خيار افتراضي آمن.\n\nيوصى باستخدام Nearest لألعاب البكسل الفنية.\n\nFSR 1.0 هو مجرد مرشح توضيحي، ولا ينصح باستخدامه مع FXAA أو SMAA.\n\nيمكن تغيير هذا الخيار أثناء تشغيل اللعبة بالنقر فوق \"تطبيق\" أدناه؛ يمكنك ببساطة تحريك نافذة الإعدادات جانبا والتجربة حتى تجد المظهر المفضل للعبة.\n\nاتركه على Bilinear إذا لم تكن متأكدا.",
|
||||||
"de_DE": "Wählen Sie den Skalierungsfilter, der bei der Auflösungsskalierung angewendet werden soll.\n\nBilinear eignet sich gut für 3D-Spiele und ist eine sichere Standardoption.\n\nNearest wird für Pixel-Art-Spiele empfohlen.\n\nFSR 1.0 ist lediglich ein Schärfungsfilter und wird nicht für die Verwendung mit FXAA oder SMAA empfohlen.\n\nDiese Option kann geändert werden, während ein Spiel läuft, indem Sie unten auf \"Anwenden\" klicken; Sie können das Einstellungsfenster einfach zur Seite schieben und experimentieren, bis Sie Ihr bevorzugtes Aussehen für ein Spiel gefunden haben.\n\nBleiben Sie auf BILINEAR, wenn Sie unsicher sind.",
|
"de_DE": "Wähle den Skalierungsfilter, der bei der Auflösungsskalierung angewendet werden soll.\n\nBilinear eignet sich gut für 3D-Spiele und ist eine sichere Standardoption.\n\nNearest wird für Pixel-Art-Spiele empfohlen.\n\nFSR 1.0 ist lediglich ein Schärfungsfilter und wird nicht für die Verwendung mit FXAA oder SMAA empfohlen.\n\nDiese Option kann geändert werden, während ein Spiel läuft, indem du unten auf \"Anwenden\" klicken; Du kannst das Einstellungsfenster einfach zur Seite schieben und experimentieren, bis das bevorzugtes Aussehen für ein Spiel gefunden wurde.\n\nBleibe auf BILINEAR, wenn du unsicher bist.",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nArea scaling is recommended when downscaling resolutions that are larger than the output window. It can be used to achieve a supersampled anti-aliasing effect when downscaling by more than 2x.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.",
|
"en_US": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nArea scaling is recommended when downscaling resolutions that are larger than the output window. It can be used to achieve a supersampled anti-aliasing effect when downscaling by more than 2x.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.",
|
||||||
"es_ES": "Elija el filtro de escala que se aplicará al utilizar la escala de resolución.\n\nBilinear funciona bien para juegos 3D y es una opción predeterminada segura.\n\nSe recomienda el bilinear para juegos de pixel art.\n\nFSR 1.0 es simplemente un filtro de afilado, no se recomienda su uso con FXAA o SMAA.\n\nEsta opción se puede cambiar mientras se ejecuta un juego haciendo clic en \"Aplicar\" a continuación; simplemente puedes mover la ventana de configuración a un lado y experimentar hasta que encuentres tu estilo preferido para un juego.\n\nDéjelo en BILINEAR si no está seguro.",
|
"es_ES": "Elija el filtro de escala que se aplicará al utilizar la escala de resolución.\n\nBilinear funciona bien para juegos 3D y es una opción predeterminada segura.\n\nSe recomienda el bilinear para juegos de pixel art.\n\nFSR 1.0 es simplemente un filtro de afilado, no se recomienda su uso con FXAA o SMAA.\n\nEsta opción se puede cambiar mientras se ejecuta un juego haciendo clic en \"Aplicar\" a continuación; simplemente puedes mover la ventana de configuración a un lado y experimentar hasta que encuentres tu estilo preferido para un juego.\n\nDéjelo en BILINEAR si no está seguro.",
|
||||||
@@ -22629,7 +22754,7 @@
|
|||||||
"ID": "MultiplayerDisableP2P",
|
"ID": "MultiplayerDisableP2P",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "P2P-Netzwerk-Hosting deaktivieren (kann Latenz erhöhen)",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Disable P2P Network Hosting (may increase latency)",
|
"en_US": "Disable P2P Network Hosting (may increase latency)",
|
||||||
"es_ES": "Desactivar el Hosteo de Red P2P (puede aumentar latencia)",
|
"es_ES": "Desactivar el Hosteo de Red P2P (puede aumentar latencia)",
|
||||||
@@ -22654,7 +22779,7 @@
|
|||||||
"ID": "MultiplayerDisableP2PTooltip",
|
"ID": "MultiplayerDisableP2PTooltip",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Deaktiviert das P2P-Netzwerk-Hosting. Andere Teilnehmer werden über den Master-Server geleitet, anstatt eine direkte Verbindung zu dir herzustellen.",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Disable P2P network hosting, peers will proxy through the master server instead of connecting to you directly.",
|
"en_US": "Disable P2P network hosting, peers will proxy through the master server instead of connecting to you directly.",
|
||||||
"es_ES": "Desactivar el hosteo de red P2P, pares se conectarán a través del servidor maestro en lugar de conectarse directamente contigo.",
|
"es_ES": "Desactivar el hosteo de red P2P, pares se conectarán a través del servidor maestro en lugar de conectarse directamente contigo.",
|
||||||
@@ -22679,7 +22804,7 @@
|
|||||||
"ID": "LdnPassphrase",
|
"ID": "LdnPassphrase",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Netzwerk-Passphrase",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Network Passphrase:",
|
"en_US": "Network Passphrase:",
|
||||||
"es_ES": "Frase de Contraseña de la Red:",
|
"es_ES": "Frase de Contraseña de la Red:",
|
||||||
@@ -22704,7 +22829,7 @@
|
|||||||
"ID": "LdnPassphraseTooltip",
|
"ID": "LdnPassphraseTooltip",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Du wirst nur gehostete Spiele sehen können, die dieselbe Passphrase wie du verwenden.",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "You will only be able to see hosted games with the same passphrase as you.",
|
"en_US": "You will only be able to see hosted games with the same passphrase as you.",
|
||||||
"es_ES": "Solo podrás ver los juegos hosteados con la misma frase de contraseña que tú.",
|
"es_ES": "Solo podrás ver los juegos hosteados con la misma frase de contraseña que tú.",
|
||||||
@@ -22729,7 +22854,7 @@
|
|||||||
"ID": "LdnPassphraseInputTooltip",
|
"ID": "LdnPassphraseInputTooltip",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Gib eine Passphrase im Format Ryujinx-<8 Hex-Zeichen> ein. Du wirst nur gehostete Spiele sehen können, die dieselbe Passphrase wie du verwenden.",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Enter a passphrase in the format Ryujinx-<8 hex chars>. You will only be able to see hosted games with the same passphrase as you.",
|
"en_US": "Enter a passphrase in the format Ryujinx-<8 hex chars>. You will only be able to see hosted games with the same passphrase as you.",
|
||||||
"es_ES": "Ingresar una frase de contraseña en formato Ryujinx-<8 caracteres hexadecimales>. Solamente podrás ver juegos hosteados con la misma frase de contraseña que tú.",
|
"es_ES": "Ingresar una frase de contraseña en formato Ryujinx-<8 caracteres hexadecimales>. Solamente podrás ver juegos hosteados con la misma frase de contraseña que tú.",
|
||||||
@@ -22754,7 +22879,7 @@
|
|||||||
"ID": "LdnPassphraseInputPublic",
|
"ID": "LdnPassphraseInputPublic",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "(öffentlich)",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "(public)",
|
"en_US": "(public)",
|
||||||
"es_ES": "(público)",
|
"es_ES": "(público)",
|
||||||
@@ -22779,7 +22904,7 @@
|
|||||||
"ID": "GenLdnPass",
|
"ID": "GenLdnPass",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Zufällig generieren",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Generate Random",
|
"en_US": "Generate Random",
|
||||||
"es_ES": "Generar Aleatorio",
|
"es_ES": "Generar Aleatorio",
|
||||||
@@ -22804,7 +22929,7 @@
|
|||||||
"ID": "GenLdnPassTooltip",
|
"ID": "GenLdnPassTooltip",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Generiert eine neue Passphrase, welche mit anderen Spielern geteilt werden kann.",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Generates a new passphrase, which can be shared with other players.",
|
"en_US": "Generates a new passphrase, which can be shared with other players.",
|
||||||
"es_ES": "Genera una nueva frase de contraseña, que puede ser compartida con otros jugadores.",
|
"es_ES": "Genera una nueva frase de contraseña, que puede ser compartida con otros jugadores.",
|
||||||
@@ -22829,7 +22954,7 @@
|
|||||||
"ID": "ClearLdnPass",
|
"ID": "ClearLdnPass",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Löschen",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Clear",
|
"en_US": "Clear",
|
||||||
"es_ES": "Borrar",
|
"es_ES": "Borrar",
|
||||||
@@ -22854,7 +22979,7 @@
|
|||||||
"ID": "ClearLdnPassTooltip",
|
"ID": "ClearLdnPassTooltip",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Löscht die jetzige Passphrase.",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Clears the current passphrase, returning to the public network.",
|
"en_US": "Clears the current passphrase, returning to the public network.",
|
||||||
"es_ES": "Borra la frase de contraseña actual, regresando a la red pública.",
|
"es_ES": "Borra la frase de contraseña actual, regresando a la red pública.",
|
||||||
@@ -22879,7 +23004,7 @@
|
|||||||
"ID": "InvalidLdnPassphrase",
|
"ID": "InvalidLdnPassphrase",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Ungültige Passphrase! Muss im Format \"Ryujinx-<8 Hex Zeichen>\" sein",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Invalid Passphrase! Must be in the format \"Ryujinx-<8 hex chars>\"",
|
"en_US": "Invalid Passphrase! Must be in the format \"Ryujinx-<8 hex chars>\"",
|
||||||
"es_ES": "Frase de Contraseña Inválida! Debe ser en formato \"Ryujinx-<8 caracteres hexadecimales>\"",
|
"es_ES": "Frase de Contraseña Inválida! Debe ser en formato \"Ryujinx-<8 caracteres hexadecimales>\"",
|
||||||
@@ -22929,7 +23054,7 @@
|
|||||||
"ID": "SettingsTabSystemEnableCustomVSyncInterval",
|
"ID": "SettingsTabSystemEnableCustomVSyncInterval",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Benutzerdefinierte Bildwiederholrate aktivieren (Experimentell)",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Enable Custom Refresh Rate (Experimental)",
|
"en_US": "Enable Custom Refresh Rate (Experimental)",
|
||||||
"es_ES": "Activar Frecuencia de Actualización Personalizada (Experimental)",
|
"es_ES": "Activar Frecuencia de Actualización Personalizada (Experimental)",
|
||||||
@@ -22979,7 +23104,7 @@
|
|||||||
"ID": "SettingsTabSystemVSyncModeUnbounded",
|
"ID": "SettingsTabSystemVSyncModeUnbounded",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Unbegrenzt",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Unbounded",
|
"en_US": "Unbounded",
|
||||||
"es_ES": "Sin Límite",
|
"es_ES": "Sin Límite",
|
||||||
@@ -23079,7 +23204,7 @@
|
|||||||
"ID": "SettingsTabSystemEnableCustomVSyncIntervalTooltip",
|
"ID": "SettingsTabSystemEnableCustomVSyncIntervalTooltip",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "Ermöglicht es dem Benutzer, eine emulierte Bildwiederholfrequenz festzulegen. In einigen Titeln kann dies die Geschwindigkeit der Spiel-Logik erhöhen oder verringern. In anderen Titeln kann dies dazu führen, dass die FPS auf ein Vielfaches der Bildwiederholfrequenz begrenzt werden oder zu unvorhersehbarem Verhalten führen. Dies ist eine experimentelle Funktion, ohne Garantien dafür, wie sich das Gameplay auswirkt. \n\nLassen Sie diese Option deaktiviert, wenn Sie sich nicht sicher sind.",
|
"de_DE": "Ermöglicht es dem Benutzer, eine emulierte Bildwiederholfrequenz festzulegen. In einigen Titeln kann dies die Geschwindigkeit der Spiel-Logik erhöhen oder verringern. In anderen Titeln kann dies dazu führen, dass die FPS auf ein Vielfaches der Bildwiederholfrequenz begrenzt werden oder zu unvorhersehbarem Verhalten führen. Dies ist eine experimentelle Funktion, ohne Garantien dafür, wie sich das Gameplay auswirkt. \n\nLass diese Option deaktiviert, wenn du dir nicht sicher bist.",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Allows the user to specify an emulated refresh rate. In some titles, this may speed up or slow down the rate of gameplay logic. In other titles, it may allow for capping FPS at some multiple of the refresh rate, or lead to unpredictable behavior. This is an experimental feature, with no guarantees for how gameplay will be affected. \n\nLeave OFF if unsure.",
|
"en_US": "Allows the user to specify an emulated refresh rate. In some titles, this may speed up or slow down the rate of gameplay logic. In other titles, it may allow for capping FPS at some multiple of the refresh rate, or lead to unpredictable behavior. This is an experimental feature, with no guarantees for how gameplay will be affected. \n\nLeave OFF if unsure.",
|
||||||
"es_ES": "Permite al usuario especificar una frecuencia de actualización emulada. En algunos títulos, esto puede acelerar o ralentizar la lógica del juego. En otros títulos, puede permitir limitar los FPS a algún múltiplo de la frecuencia de actualización, o provocar un comportamiento impredecible. Esta es una función experimental, sin garantías sobre cómo se verá afectada la jugabilidad.\n\nDéjalo DESACTIVADO si no estás seguro.",
|
"es_ES": "Permite al usuario especificar una frecuencia de actualización emulada. En algunos títulos, esto puede acelerar o ralentizar la lógica del juego. En otros títulos, puede permitir limitar los FPS a algún múltiplo de la frecuencia de actualización, o provocar un comportamiento impredecible. Esta es una función experimental, sin garantías sobre cómo se verá afectada la jugabilidad.\n\nDéjalo DESACTIVADO si no estás seguro.",
|
||||||
@@ -24104,7 +24229,7 @@
|
|||||||
"ID": "SettingsTabDebugEnableGDBStub",
|
"ID": "SettingsTabDebugEnableGDBStub",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "GDB Stub aktivieren",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Enable GDB Stub",
|
"en_US": "Enable GDB Stub",
|
||||||
"es_ES": "Activar Stub GDB",
|
"es_ES": "Activar Stub GDB",
|
||||||
@@ -24129,7 +24254,7 @@
|
|||||||
"ID": "SettingsTabDebugGDBStubToggleTooltip",
|
"ID": "SettingsTabDebugGDBStubToggleTooltip",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Aktiviert den GDB Stub, welcher debugging an der Anwendung ermöglicht. Nur für Entwicklerzwecke!",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Enables the GDB stub which makes it possible to debug the running application. For development use only!",
|
"en_US": "Enables the GDB stub which makes it possible to debug the running application. For development use only!",
|
||||||
"es_ES": "Activa el stub GDB, lo que permite depurar la aplicación en ejecución. ¡Solo para uso de desarrollo!",
|
"es_ES": "Activa el stub GDB, lo que permite depurar la aplicación en ejecución. ¡Solo para uso de desarrollo!",
|
||||||
@@ -24179,7 +24304,7 @@
|
|||||||
"ID": "SettingsTabDebugSuspendOnStart",
|
"ID": "SettingsTabDebugSuspendOnStart",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Anwendung beim Start anhalten.",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Suspend Application on Start",
|
"en_US": "Suspend Application on Start",
|
||||||
"es_ES": "Suspender la Aplicación al Iniciar",
|
"es_ES": "Suspender la Aplicación al Iniciar",
|
||||||
@@ -24204,7 +24329,7 @@
|
|||||||
"ID": "SettingsTabDebugSuspendOnStartTooltip",
|
"ID": "SettingsTabDebugSuspendOnStartTooltip",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Hält die Anwendung vor der Ausführung des ersten Befehls an, um Debugging vom frühestmöglichen Zeitpunkt an zu ermöglichen.",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Suspends the application before executing the first instruction, allowing for debugging from the earliest point.",
|
"en_US": "Suspends the application before executing the first instruction, allowing for debugging from the earliest point.",
|
||||||
"es_ES": "Suspende la aplicación antes de ejecutar la primera instrucción, permitiendo depurar desde el punto más temprano.",
|
"es_ES": "Suspende la aplicación antes de ejecutar la primera instrucción, permitiendo depurar desde el punto más temprano.",
|
||||||
@@ -24229,7 +24354,7 @@
|
|||||||
"ID": "LdnGameListOpen",
|
"ID": "LdnGameListOpen",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "LDN Spiele-Liste",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "LDN Game List",
|
"en_US": "LDN Game List",
|
||||||
"es_ES": "Lista de Juegos LDN",
|
"es_ES": "Lista de Juegos LDN",
|
||||||
@@ -24254,7 +24379,7 @@
|
|||||||
"ID": "LdnGameListTitle",
|
"ID": "LdnGameListTitle",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "LDN-Spiele-Browser - {0} Spiele",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "LDN Game Browser - {0} games",
|
"en_US": "LDN Game Browser - {0} games",
|
||||||
"es_ES": "Navegador de Juegos LDN - {0} juegos",
|
"es_ES": "Navegador de Juegos LDN - {0} juegos",
|
||||||
@@ -24279,7 +24404,7 @@
|
|||||||
"ID": "LdnGameListSearchBoxWatermark",
|
"ID": "LdnGameListSearchBoxWatermark",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Suche {0} LDN Spiele...",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Search {0} LDN games...",
|
"en_US": "Search {0} LDN games...",
|
||||||
"es_ES": "Buscar {0} Juegos LDN...",
|
"es_ES": "Buscar {0} Juegos LDN...",
|
||||||
@@ -24304,7 +24429,7 @@
|
|||||||
"ID": "LdnGameListInfoButtonToolTip",
|
"ID": "LdnGameListInfoButtonToolTip",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Was ist LDN?",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "What is LDN?",
|
"en_US": "What is LDN?",
|
||||||
"es_ES": "¿Qué es LDN?",
|
"es_ES": "¿Qué es LDN?",
|
||||||
@@ -24329,7 +24454,7 @@
|
|||||||
"ID": "LdnGameListRefreshToolTip",
|
"ID": "LdnGameListRefreshToolTip",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Verfügbare Spiele vom Server unter {0} aktualisieren.",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Refresh available games from the server at {0}.",
|
"en_US": "Refresh available games from the server at {0}.",
|
||||||
"es_ES": "Actualizar los juegos disponibles del servidor a las {0}.",
|
"es_ES": "Actualizar los juegos disponibles del servidor a las {0}.",
|
||||||
@@ -24354,7 +24479,7 @@
|
|||||||
"ID": "LdnGameListPlayerSortDisable",
|
"ID": "LdnGameListPlayerSortDisable",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Spieleranzahl - Deaktivieren",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Player Count - Disable",
|
"en_US": "Player Count - Disable",
|
||||||
"es_ES": "Contador de jugadores - Desactivar",
|
"es_ES": "Contador de jugadores - Desactivar",
|
||||||
@@ -24379,7 +24504,7 @@
|
|||||||
"ID": "LdnGameListPlayerSortAscending",
|
"ID": "LdnGameListPlayerSortAscending",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Spieleranzahl - Aufsteigend",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Player Count - Ascending",
|
"en_US": "Player Count - Ascending",
|
||||||
"es_ES": "Contador de jugadores - Ascendente",
|
"es_ES": "Contador de jugadores - Ascendente",
|
||||||
@@ -24404,7 +24529,7 @@
|
|||||||
"ID": "LdnGameListPlayerSortDescending",
|
"ID": "LdnGameListPlayerSortDescending",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Spieleranzahl - Absteigend",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Player Count - Descending",
|
"en_US": "Player Count - Descending",
|
||||||
"es_ES": "Contador de jugadores - Descendente",
|
"es_ES": "Contador de jugadores - Descendente",
|
||||||
@@ -24429,7 +24554,7 @@
|
|||||||
"ID": "LdnGameListFiltersHeading",
|
"ID": "LdnGameListFiltersHeading",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Filter",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Filters",
|
"en_US": "Filters",
|
||||||
"es_ES": "Filtros",
|
"es_ES": "Filtros",
|
||||||
@@ -24454,7 +24579,7 @@
|
|||||||
"ID": "LdnGameListFiltersOnlyShowPublicGames",
|
"ID": "LdnGameListFiltersOnlyShowPublicGames",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Nur öffentliche Spiele anzeigen",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Only show public games",
|
"en_US": "Only show public games",
|
||||||
"es_ES": "Solo mostrar juegos públicos",
|
"es_ES": "Solo mostrar juegos públicos",
|
||||||
@@ -24479,7 +24604,7 @@
|
|||||||
"ID": "LdnGameListFiltersOnlyShowJoinableGames",
|
"ID": "LdnGameListFiltersOnlyShowJoinableGames",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Nur beitretbare Spiele anzeigen",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Only show joinable games",
|
"en_US": "Only show joinable games",
|
||||||
"es_ES": "Solo mostrar juegos a los que se puede unir",
|
"es_ES": "Solo mostrar juegos a los que se puede unir",
|
||||||
@@ -24554,7 +24679,7 @@
|
|||||||
"ID": "LdnGameListConnectionTypeMasterServerProxyToolTip",
|
"ID": "LdnGameListConnectionTypeMasterServerProxyToolTip",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Verbindet durch RyuLDN-Server (langsamer)",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Connects through the RyuLDN server (slower).",
|
"en_US": "Connects through the RyuLDN server (slower).",
|
||||||
"es_ES": "Conexión a través del servidor RyuLDN (más lento).",
|
"es_ES": "Conexión a través del servidor RyuLDN (más lento).",
|
||||||
@@ -24579,7 +24704,7 @@
|
|||||||
"ID": "LdnGameListConnectionTypeP2PToolTip",
|
"ID": "LdnGameListConnectionTypeP2PToolTip",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Verbindet über Peer-to-Peer durch UPnP (schneller)",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Connects via Peer-to-Peer via UPnP (faster).",
|
"en_US": "Connects via Peer-to-Peer via UPnP (faster).",
|
||||||
"es_ES": "Conexión Peer-to-Peer a través de UPnP (más rápido).",
|
"es_ES": "Conexión Peer-to-Peer a través de UPnP (más rápido).",
|
||||||
@@ -24604,7 +24729,7 @@
|
|||||||
"ID": "LdnGameListCreatedAt",
|
"ID": "LdnGameListCreatedAt",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Erstellt: {0}",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Created: {0}",
|
"en_US": "Created: {0}",
|
||||||
"es_ES": "Creado: {0}",
|
"es_ES": "Creado: {0}",
|
||||||
@@ -24629,7 +24754,7 @@
|
|||||||
"ID": "LdnGameListPlayersAndPlayerCount",
|
"ID": "LdnGameListPlayersAndPlayerCount",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Spieler ({0} von {1}:",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Players ({0} out of {1}):",
|
"en_US": "Players ({0} out of {1}):",
|
||||||
"es_ES": "Jugadores ({0} de {1}):",
|
"es_ES": "Jugadores ({0} de {1}):",
|
||||||
@@ -24654,7 +24779,7 @@
|
|||||||
"ID": "LdnGameListJoinable",
|
"ID": "LdnGameListJoinable",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Beitretbar",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Joinable",
|
"en_US": "Joinable",
|
||||||
"es_ES": "Unible",
|
"es_ES": "Unible",
|
||||||
@@ -24679,7 +24804,7 @@
|
|||||||
"ID": "LdnGameListJoinableToolTip",
|
"ID": "LdnGameListJoinableToolTip",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Spiel ist beitrittbar, wenn es öffentlich ist, oder du die Passphrase kennst.",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Game is joinable if it is public or if you know the passphrase.",
|
"en_US": "Game is joinable if it is public or if you know the passphrase.",
|
||||||
"es_ES": "El juego es unible si es público o si conoces la frase de contraseña.",
|
"es_ES": "El juego es unible si es público o si conoces la frase de contraseña.",
|
||||||
@@ -24704,7 +24829,7 @@
|
|||||||
"ID": "LdnGameListNotJoinable",
|
"ID": "LdnGameListNotJoinable",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Nicht beitretbar",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Not Joinable",
|
"en_US": "Not Joinable",
|
||||||
"es_ES": "No se puede unir",
|
"es_ES": "No se puede unir",
|
||||||
@@ -24729,7 +24854,7 @@
|
|||||||
"ID": "LdnGameListNotJoinableToolTip",
|
"ID": "LdnGameListNotJoinableToolTip",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Spiel läuft bereits.",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Game is currently in progress.",
|
"en_US": "Game is currently in progress.",
|
||||||
"es_ES": "El juego está en curso.",
|
"es_ES": "El juego está en curso.",
|
||||||
@@ -24754,7 +24879,7 @@
|
|||||||
"ID": "LdnGameListPublic",
|
"ID": "LdnGameListPublic",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Öffentlich",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Public",
|
"en_US": "Public",
|
||||||
"es_ES": "Público",
|
"es_ES": "Público",
|
||||||
@@ -24779,7 +24904,7 @@
|
|||||||
"ID": "LdnGameListPublicToolTip",
|
"ID": "LdnGameListPublicToolTip",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Jeder kann das Spiel beitreten.",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Anyone can join this game.",
|
"en_US": "Anyone can join this game.",
|
||||||
"es_ES": "Cualquiera puede unirse a este juego.",
|
"es_ES": "Cualquiera puede unirse a este juego.",
|
||||||
@@ -24804,7 +24929,7 @@
|
|||||||
"ID": "LdnGameListPrivate",
|
"ID": "LdnGameListPrivate",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Privat",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Private",
|
"en_US": "Private",
|
||||||
"es_ES": "Privado",
|
"es_ES": "Privado",
|
||||||
@@ -24829,7 +24954,7 @@
|
|||||||
"ID": "LdnGameListPrivateToolTip",
|
"ID": "LdnGameListPrivateToolTip",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Du kannst dieses Spiel nur beitreten, wenn du die selbe LDN-Passphrase eingestellt hast.",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "You can only join this game if you also have the same LDN Passphrase in your settings.",
|
"en_US": "You can only join this game if you also have the same LDN Passphrase in your settings.",
|
||||||
"es_ES": "Solo puedes unirte a este juego si también tienes la misma frase de contraseña LDN en tus ajustes.",
|
"es_ES": "Solo puedes unirte a este juego si también tienes la misma frase de contraseña LDN en tus ajustes.",
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -12,20 +12,12 @@ namespace Ryujinx.Common.Helper
|
|||||||
private static partial nint GetConsoleWindow();
|
private static partial nint GetConsoleWindow();
|
||||||
|
|
||||||
[SupportedOSPlatform("windows")]
|
[SupportedOSPlatform("windows")]
|
||||||
[LibraryImport("user32")]
|
[LibraryImport("kernel32", SetLastError = true)]
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
private static partial bool ShowWindow(nint hWnd, int nCmdShow);
|
private static partial bool FreeConsole();
|
||||||
|
|
||||||
[SupportedOSPlatform("windows")]
|
|
||||||
[LibraryImport("user32")]
|
|
||||||
private static partial nint GetForegroundWindow();
|
|
||||||
|
|
||||||
[SupportedOSPlatform("windows")]
|
|
||||||
[LibraryImport("user32")]
|
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
|
||||||
private static partial bool SetForegroundWindow(nint hWnd);
|
|
||||||
|
|
||||||
public static bool SetConsoleWindowStateSupported => OperatingSystem.IsWindows();
|
public static bool SetConsoleWindowStateSupported => OperatingSystem.IsWindows();
|
||||||
|
public static bool HasConsoleWindow => OperatingSystem.IsWindows() && GetConsoleWindow() != nint.Zero;
|
||||||
|
|
||||||
public static void SetConsoleWindowState(bool show)
|
public static void SetConsoleWindowState(bool show)
|
||||||
{
|
{
|
||||||
@@ -42,22 +34,31 @@ namespace Ryujinx.Common.Helper
|
|||||||
[SupportedOSPlatform("windows")]
|
[SupportedOSPlatform("windows")]
|
||||||
private static void SetConsoleWindowStateWindows(bool show)
|
private static void SetConsoleWindowStateWindows(bool show)
|
||||||
{
|
{
|
||||||
const int SW_HIDE = 0;
|
if (show)
|
||||||
const int SW_SHOW = 5;
|
|
||||||
|
|
||||||
nint hWnd = GetConsoleWindow();
|
|
||||||
|
|
||||||
if (hWnd == nint.Zero)
|
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.Application, "Attempted to show/hide console window but console window does not exist");
|
if (GetConsoleWindow() != nint.Zero)
|
||||||
|
{
|
||||||
|
Logger.SetConsoleTargetEnabled(true);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetForegroundWindow(hWnd);
|
Logger.SetConsoleTargetEnabled(false);
|
||||||
|
DetachConsole();
|
||||||
|
}
|
||||||
|
|
||||||
hWnd = GetForegroundWindow();
|
[SupportedOSPlatform("windows")]
|
||||||
|
private static void DetachConsole()
|
||||||
|
{
|
||||||
|
if (GetConsoleWindow() == nint.Zero)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ShowWindow(hWnd, show ? SW_SHOW : SW_HIDE);
|
if (!FreeConsole())
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.Application, "Attempted to detach console window but the operation failed");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ namespace Ryujinx.Common.Logging
|
|||||||
ServiceNgct,
|
ServiceNgct,
|
||||||
ServiceNifm,
|
ServiceNifm,
|
||||||
ServiceNim,
|
ServiceNim,
|
||||||
|
ServiceNotification,
|
||||||
ServiceNs,
|
ServiceNs,
|
||||||
ServiceNsd,
|
ServiceNsd,
|
||||||
ServiceNtc,
|
ServiceNtc,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ namespace Ryujinx.Common.Logging
|
|||||||
Error,
|
Error,
|
||||||
Guest,
|
Guest,
|
||||||
AccessLog,
|
AccessLog,
|
||||||
|
NetLog,
|
||||||
Notice,
|
Notice,
|
||||||
Trace,
|
Trace,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ namespace Ryujinx.Common.Logging
|
|||||||
public static Log? Error { get; private set; }
|
public static Log? Error { get; private set; }
|
||||||
public static Log? Guest { get; private set; }
|
public static Log? Guest { get; private set; }
|
||||||
public static Log? AccessLog { get; private set; }
|
public static Log? AccessLog { get; private set; }
|
||||||
|
public static Log? NetLog { get; private set; }
|
||||||
public static Log? Stub { get; private set; }
|
public static Log? Stub { get; private set; }
|
||||||
public static Log? Trace { get; private set; }
|
public static Log? Trace { get; private set; }
|
||||||
public static Log Notice { get; } // Always enabled
|
public static Log Notice { get; } // Always enabled
|
||||||
@@ -136,11 +137,7 @@ namespace Ryujinx.Common.Logging
|
|||||||
|
|
||||||
_time = Stopwatch.StartNew();
|
_time = Stopwatch.StartNew();
|
||||||
|
|
||||||
// Logger should log to console by default
|
SetConsoleTargetEnabled(true);
|
||||||
AddTarget(new AsyncLogTargetWrapper(
|
|
||||||
new ConsoleLogTarget("console"),
|
|
||||||
1000,
|
|
||||||
AsyncLogTargetOverflowAction.Discard));
|
|
||||||
|
|
||||||
Notice = new Log(LogLevel.Notice);
|
Notice = new Log(LogLevel.Notice);
|
||||||
|
|
||||||
@@ -173,6 +170,21 @@ namespace Ryujinx.Common.Logging
|
|||||||
Updated += target.Log;
|
Updated += target.Log;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void SetConsoleTargetEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
if (enabled)
|
||||||
|
{
|
||||||
|
AddTarget(new AsyncLogTargetWrapper(
|
||||||
|
new ConsoleLogTarget("console"),
|
||||||
|
1000,
|
||||||
|
AsyncLogTargetOverflowAction.Discard));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RemoveTarget("console");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void RemoveTarget(string target)
|
public static void RemoveTarget(string target)
|
||||||
{
|
{
|
||||||
ILogTarget logTarget = GetTarget(target);
|
ILogTarget logTarget = GetTarget(target);
|
||||||
@@ -236,6 +248,7 @@ namespace Ryujinx.Common.Logging
|
|||||||
case LogLevel.Error : Error = enabled ? new Log(LogLevel.Error) : null; break;
|
case LogLevel.Error : Error = enabled ? new Log(LogLevel.Error) : null; break;
|
||||||
case LogLevel.Guest : Guest = enabled ? new Log(LogLevel.Guest) : null; break;
|
case LogLevel.Guest : Guest = enabled ? new Log(LogLevel.Guest) : null; break;
|
||||||
case LogLevel.AccessLog : AccessLog = enabled ? new Log(LogLevel.AccessLog) : null; break;
|
case LogLevel.AccessLog : AccessLog = enabled ? new Log(LogLevel.AccessLog) : null; break;
|
||||||
|
case LogLevel.NetLog : NetLog = enabled ? new Log(LogLevel.NetLog) : null; break;
|
||||||
case LogLevel.Stub : Stub = enabled ? new Log(LogLevel.Stub) : null; break;
|
case LogLevel.Stub : Stub = enabled ? new Log(LogLevel.Stub) : null; break;
|
||||||
case LogLevel.Trace : Trace = enabled ? new Log(LogLevel.Trace) : null; break;
|
case LogLevel.Trace : Trace = enabled ? new Log(LogLevel.Trace) : null; break;
|
||||||
case LogLevel.Notice : break;
|
case LogLevel.Notice : break;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
private static readonly int _pageMask = _pageSize - 1;
|
private static readonly int _pageMask = _pageSize - 1;
|
||||||
|
|
||||||
private const int CodeAlignment = 4; // Bytes.
|
private const int CodeAlignment = 4; // Bytes.
|
||||||
private const int CacheSize = 256 * 1024 * 1024;
|
// TODO: JIT Cache size should be application dependent, not global.
|
||||||
|
private const int CacheSize = 1024 * (1024 * 1024); // Megabytes * Size of Megabytes (since its in bytes).
|
||||||
|
|
||||||
private static JitCacheInvalidation _jitCacheInvalidator;
|
private static JitCacheInvalidation _jitCacheInvalidator;
|
||||||
|
|
||||||
@@ -34,6 +35,14 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
[LibraryImport("kernel32.dll", SetLastError = true)]
|
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||||||
public static partial nint FlushInstructionCache(nint hProcess, nint lpAddress, nuint dwSize);
|
public static partial nint FlushInstructionCache(nint hProcess, nint lpAddress, nuint dwSize);
|
||||||
|
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
|
[LibraryImport("libSystem.dylib", EntryPoint = "sys_icache_invalidate")]
|
||||||
|
internal static partial void SysICacheInvalidate(nint start, nuint len);
|
||||||
|
|
||||||
|
[SupportedOSPlatform("linux")]
|
||||||
|
[LibraryImport("libgcc_s.so.1", EntryPoint = "__clear_cache")]
|
||||||
|
internal static partial void ClearCache(nint begin, nint end);
|
||||||
|
|
||||||
public static void Initialize(IJitMemoryAllocator allocator)
|
public static void Initialize(IJitMemoryAllocator allocator)
|
||||||
{
|
{
|
||||||
if (_initialized)
|
if (_initialized)
|
||||||
|
|||||||
@@ -551,7 +551,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
level,
|
level,
|
||||||
x,
|
x,
|
||||||
width,
|
width,
|
||||||
format.PixelFormat,
|
(InternalFormat) format.PixelFormat,
|
||||||
mipSize,
|
mipSize,
|
||||||
data);
|
data);
|
||||||
}
|
}
|
||||||
@@ -579,7 +579,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
layer,
|
layer,
|
||||||
width,
|
width,
|
||||||
1,
|
1,
|
||||||
format.PixelFormat,
|
(InternalFormat) format.PixelFormat,
|
||||||
mipSize,
|
mipSize,
|
||||||
data);
|
data);
|
||||||
}
|
}
|
||||||
@@ -609,7 +609,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
y,
|
y,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
format.PixelFormat,
|
(InternalFormat) format.PixelFormat,
|
||||||
mipSize,
|
mipSize,
|
||||||
data);
|
data);
|
||||||
}
|
}
|
||||||
@@ -643,7 +643,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
1,
|
1,
|
||||||
format.PixelFormat,
|
(InternalFormat) format.PixelFormat,
|
||||||
mipSize,
|
mipSize,
|
||||||
data);
|
data);
|
||||||
}
|
}
|
||||||
@@ -675,7 +675,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
y,
|
y,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
format.PixelFormat,
|
(InternalFormat) format.PixelFormat,
|
||||||
mipSize,
|
mipSize,
|
||||||
data);
|
data);
|
||||||
}
|
}
|
||||||
@@ -744,7 +744,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
level,
|
level,
|
||||||
0,
|
0,
|
||||||
width,
|
width,
|
||||||
format.PixelFormat,
|
(InternalFormat) format.PixelFormat,
|
||||||
mipSize,
|
mipSize,
|
||||||
data);
|
data);
|
||||||
}
|
}
|
||||||
@@ -773,7 +773,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
0,
|
0,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
format.PixelFormat,
|
(InternalFormat) format.PixelFormat,
|
||||||
mipSize,
|
mipSize,
|
||||||
data);
|
data);
|
||||||
}
|
}
|
||||||
@@ -807,7 +807,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
depth,
|
depth,
|
||||||
format.PixelFormat,
|
(InternalFormat) format.PixelFormat,
|
||||||
mipSize,
|
mipSize,
|
||||||
data);
|
data);
|
||||||
}
|
}
|
||||||
@@ -843,7 +843,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
0,
|
0,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
format.PixelFormat,
|
(InternalFormat) format.PixelFormat,
|
||||||
mipSize / 6,
|
mipSize / 6,
|
||||||
data + faceOffset);
|
data + faceOffset);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
public void Map(BufferHandle handle, int size)
|
public void Map(BufferHandle handle, int size)
|
||||||
{
|
{
|
||||||
GL.BindBuffer(BufferTarget.CopyWriteBuffer, handle);
|
GL.BindBuffer(BufferTarget.CopyWriteBuffer, handle);
|
||||||
nint ptr = GL.MapBufferRange(BufferTarget.CopyWriteBuffer, nint.Zero, size, BufferAccessMask.MapReadBit | BufferAccessMask.MapPersistentBit);
|
nint ptr = GL.MapBufferRange(BufferTarget.CopyWriteBuffer, nint.Zero, size, MapBufferAccessMask.MapReadBit | MapBufferAccessMask.MapPersistentBit);
|
||||||
|
|
||||||
_maps[handle] = ptr;
|
_maps[handle] = ptr;
|
||||||
}
|
}
|
||||||
@@ -75,7 +75,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
GL.BindBuffer(BufferTarget.CopyWriteBuffer, _copyBufferHandle);
|
GL.BindBuffer(BufferTarget.CopyWriteBuffer, _copyBufferHandle);
|
||||||
GL.BufferStorage(BufferTarget.CopyWriteBuffer, requiredSize, nint.Zero, BufferStorageFlags.MapReadBit | BufferStorageFlags.MapPersistentBit);
|
GL.BufferStorage(BufferTarget.CopyWriteBuffer, requiredSize, nint.Zero, BufferStorageFlags.MapReadBit | BufferStorageFlags.MapPersistentBit);
|
||||||
|
|
||||||
_bufferMap = GL.MapBufferRange(BufferTarget.CopyWriteBuffer, nint.Zero, requiredSize, BufferAccessMask.MapReadBit | BufferAccessMask.MapPersistentBit);
|
_bufferMap = GL.MapBufferRange(BufferTarget.CopyWriteBuffer, nint.Zero, requiredSize, MapBufferAccessMask.MapReadBit | MapBufferAccessMask.MapPersistentBit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -925,7 +925,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
GL.CullFace(face.Convert());
|
GL.CullFace((TriangleFace) face.Convert());
|
||||||
|
|
||||||
GL.Enable(EnableCap.CullFace);
|
GL.Enable(EnableCap.CullFace);
|
||||||
}
|
}
|
||||||
@@ -1085,12 +1085,12 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
{
|
{
|
||||||
if (frontMode == backMode)
|
if (frontMode == backMode)
|
||||||
{
|
{
|
||||||
GL.PolygonMode(MaterialFace.FrontAndBack, frontMode.Convert());
|
GL.PolygonMode((TriangleFace) MaterialFace.FrontAndBack, frontMode.Convert());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
GL.PolygonMode(MaterialFace.Front, frontMode.Convert());
|
GL.PolygonMode((TriangleFace) MaterialFace.Front, frontMode.Convert());
|
||||||
GL.PolygonMode(MaterialFace.Back, backMode.Convert());
|
GL.PolygonMode((TriangleFace) MaterialFace.Back, backMode.Convert());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
GL.CompileShader(shaderHandle);
|
GL.CompileShader(shaderHandle);
|
||||||
break;
|
break;
|
||||||
case TargetLanguage.Spirv:
|
case TargetLanguage.Spirv:
|
||||||
GL.ShaderBinary(1, ref shaderHandle, (BinaryFormat)All.ShaderBinaryFormatSpirVArb, shader.BinaryCode, shader.BinaryCode.Length);
|
GL.ShaderBinary(1, ref shaderHandle, ShaderBinaryFormat.ShaderBinaryFormatSpirV, shader.BinaryCode, shader.BinaryCode.Length);
|
||||||
GL.SpecializeShader(shaderHandle, "main", 0, (int[])null, (int[])null);
|
GL.SpecializeShader(shaderHandle, "main", 0, (int[])null, (int[])null);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries
|
|||||||
GL.BufferStorage(BufferTarget.QueryBuffer, sizeof(long), (nint)(&defaultValue), BufferStorageFlags.MapReadBit | BufferStorageFlags.MapWriteBit | BufferStorageFlags.MapPersistentBit);
|
GL.BufferStorage(BufferTarget.QueryBuffer, sizeof(long), (nint)(&defaultValue), BufferStorageFlags.MapReadBit | BufferStorageFlags.MapWriteBit | BufferStorageFlags.MapPersistentBit);
|
||||||
}
|
}
|
||||||
|
|
||||||
_bufferMap = GL.MapBufferRange(BufferTarget.QueryBuffer, nint.Zero, sizeof(long), BufferAccessMask.MapReadBit | BufferAccessMask.MapWriteBit | BufferAccessMask.MapPersistentBit);
|
_bufferMap = GL.MapBufferRange(BufferTarget.QueryBuffer, nint.Zero, sizeof(long), MapBufferAccessMask.MapReadBit | MapBufferAccessMask.MapWriteBit | MapBufferAccessMask.MapPersistentBit);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Reset()
|
public void Reset()
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Ryujinx.Common.Logging;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -114,7 +115,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
cbs.AddDependant(this);
|
cbs.AddDependant(this);
|
||||||
|
|
||||||
// We need to add a dependency on the command buffer to all objects this object
|
// We need to add a dependency on the command buffer to all objects this object
|
||||||
// references aswell.
|
// references as well.
|
||||||
if (_referencedObjs != null)
|
if (_referencedObjs != null)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < _referencedObjs.Length; i++)
|
for (int i = 0; i < _referencedObjs.Length; i++)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -246,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();
|
||||||
}
|
}
|
||||||
@@ -286,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 =
|
||||||
[
|
[
|
||||||
|
|||||||
@@ -45,6 +45,22 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib
|
|||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandCmif(1)]
|
||||||
|
// PushOutData(object<nn::am::service::IStorage>)
|
||||||
|
public ResultCode PushOutData(ServiceCtx context)
|
||||||
|
{
|
||||||
|
IStorage appletData = GetObject<IStorage>(context, 0);
|
||||||
|
|
||||||
|
if (appletData == null || appletData.Data.Length == 0) // is this necessary?
|
||||||
|
{
|
||||||
|
return ResultCode.NullObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
_appletStandalone.InputData.Enqueue(appletData.Data);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
[CommandCmif(11)]
|
[CommandCmif(11)]
|
||||||
// GetLibraryAppletInfo() -> nn::am::service::LibraryAppletInfo
|
// GetLibraryAppletInfo() -> nn::am::service::LibraryAppletInfo
|
||||||
public ResultCode GetLibraryAppletInfo(ServiceCtx context)
|
public ResultCode GetLibraryAppletInfo(ServiceCtx context)
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.HLE.HOS.Services.Caps.Types;
|
using Ryujinx.HLE.HOS.Services.Caps.Types;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
@@ -9,16 +11,20 @@ using System.Security.Cryptography;
|
|||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Caps
|
namespace Ryujinx.HLE.HOS.Services.Caps
|
||||||
{
|
{
|
||||||
class CaptureManager
|
internal class CaptureManager
|
||||||
{
|
{
|
||||||
private readonly string _sdCardPath;
|
public CaptureManager(Switch device)
|
||||||
|
{
|
||||||
|
_ = device;
|
||||||
|
}
|
||||||
|
private readonly string _sdCardPath = FileSystem.VirtualFileSystem.GetSdCardPath();
|
||||||
|
|
||||||
private uint _shimLibraryVersion;
|
private uint _shimLibraryVersion;
|
||||||
|
|
||||||
public CaptureManager(Switch device)
|
private const int ScreenshotWidth = 1280;
|
||||||
{
|
private const int ScreenshotHeight = 720;
|
||||||
_sdCardPath = FileSystem.VirtualFileSystem.GetSdCardPath();
|
private const int ScreenshotBytesPerPixel = 4;
|
||||||
}
|
private const int ScreenshotDataSize = ScreenshotWidth * ScreenshotHeight * ScreenshotBytesPerPixel; // 0x384000
|
||||||
|
|
||||||
public ResultCode SetShimLibraryVersion(ServiceCtx context)
|
public ResultCode SetShimLibraryVersion(ServiceCtx context)
|
||||||
{
|
{
|
||||||
@@ -53,81 +59,94 @@ namespace Ryujinx.HLE.HOS.Services.Caps
|
|||||||
return resultCode;
|
return resultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResultCode SaveScreenShot(byte[] screenshotData, ulong appletResourceUserId, ulong titleId, out ApplicationAlbumEntry applicationAlbumEntry)
|
public ResultCode SaveScreenShot(
|
||||||
|
byte[] screenshotData,
|
||||||
|
ulong appletResourceUserId,
|
||||||
|
ulong titleId,
|
||||||
|
out ApplicationAlbumEntry applicationAlbumEntry)
|
||||||
{
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceCaps, new
|
||||||
|
{
|
||||||
|
appletResourceUserId,
|
||||||
|
titleId,
|
||||||
|
screenshotDataLength = screenshotData?.Length ?? 0,
|
||||||
|
});
|
||||||
|
|
||||||
applicationAlbumEntry = default;
|
applicationAlbumEntry = default;
|
||||||
|
|
||||||
if (screenshotData.Length == 0)
|
if (screenshotData == null || screenshotData.Length == 0)
|
||||||
{
|
{
|
||||||
return ResultCode.NullInputBuffer;
|
return ResultCode.NullInputBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
if (screenshotData.Length < ScreenshotDataSize)
|
||||||
// NOTE: On our current implementation, appletResourceUserId starts at 0, disable it for now.
|
{
|
||||||
if (appletResourceUserId == 0)
|
Logger.Warning?.PrintMsg(
|
||||||
|
LogClass.ServiceCaps,
|
||||||
|
$"Invalid screenshot buffer size 0x{screenshotData.Length:X}; expected at least 0x{ScreenshotDataSize:X}.");
|
||||||
|
|
||||||
|
return ResultCode.NullInputBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime currentDateTime = DateTime.Now;
|
||||||
|
|
||||||
|
applicationAlbumEntry = new ApplicationAlbumEntry()
|
||||||
|
{
|
||||||
|
Size = (ulong)Unsafe.SizeOf<ApplicationAlbumEntry>(),
|
||||||
|
TitleId = titleId,
|
||||||
|
AlbumFileDateTime = new AlbumFileDateTime()
|
||||||
|
{
|
||||||
|
Year = (ushort)currentDateTime.Year,
|
||||||
|
Month = (byte)currentDateTime.Month,
|
||||||
|
Day = (byte)currentDateTime.Day,
|
||||||
|
Hour = (byte)currentDateTime.Hour,
|
||||||
|
Minute = (byte)currentDateTime.Minute,
|
||||||
|
Second = (byte)currentDateTime.Second,
|
||||||
|
UniqueId = 0,
|
||||||
|
},
|
||||||
|
AlbumStorage = AlbumStorage.Sd,
|
||||||
|
ContentType = ContentType.Screenshot,
|
||||||
|
Padding = new Array5<byte>(),
|
||||||
|
Unknown0x1f = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
// NOTE: The hex hash is a HMAC-SHA256 (first 32 bytes) using a hardcoded secret key over the titleId, we can simulate it by hashing the titleId instead.
|
||||||
|
string hash = Convert.ToHexString(SHA256.HashData(BitConverter.GetBytes(titleId)))[..0x20];
|
||||||
|
|
||||||
|
string folderPath = Path.Combine(
|
||||||
|
_sdCardPath,
|
||||||
|
"Nintendo",
|
||||||
|
"Album",
|
||||||
|
currentDateTime.Year.ToString("0000", CultureInfo.InvariantCulture),
|
||||||
|
currentDateTime.Month.ToString("00", CultureInfo.InvariantCulture),
|
||||||
|
currentDateTime.Day.ToString("00", CultureInfo.InvariantCulture));
|
||||||
|
|
||||||
|
string filePath = GenerateFilePath(folderPath, applicationAlbumEntry, currentDateTime, hash);
|
||||||
|
|
||||||
|
_ = Directory.CreateDirectory(folderPath);
|
||||||
|
|
||||||
|
while (File.Exists(filePath))
|
||||||
|
{
|
||||||
|
applicationAlbumEntry.AlbumFileDateTime.UniqueId++;
|
||||||
|
filePath = GenerateFilePath(folderPath, applicationAlbumEntry, currentDateTime, hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
using SKBitmap bitmap = new(new SKImageInfo(ScreenshotWidth, ScreenshotHeight, SKColorType.Rgba8888));
|
||||||
|
|
||||||
|
IntPtr pixels = bitmap.GetPixels();
|
||||||
|
|
||||||
|
if (pixels == IntPtr.Zero)
|
||||||
{
|
{
|
||||||
return ResultCode.InvalidArgument;
|
return ResultCode.InvalidArgument;
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
Marshal.Copy(screenshotData, 0, pixels, ScreenshotDataSize);
|
||||||
// Doesn't occur in our case.
|
|
||||||
if (applicationAlbumEntry == null)
|
|
||||||
{
|
|
||||||
return ResultCode.NullOutputBuffer;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (screenshotData.Length >= 0x384000)
|
using SKData data = bitmap.Encode(SKEncodedImageFormat.Jpeg, 80);
|
||||||
{
|
using FileStream file = File.OpenWrite(filePath);
|
||||||
DateTime currentDateTime = DateTime.Now;
|
data.SaveTo(file);
|
||||||
|
|
||||||
applicationAlbumEntry = new ApplicationAlbumEntry()
|
return ResultCode.Success;
|
||||||
{
|
|
||||||
Size = (ulong)Unsafe.SizeOf<ApplicationAlbumEntry>(),
|
|
||||||
TitleId = titleId,
|
|
||||||
AlbumFileDateTime = new AlbumFileDateTime()
|
|
||||||
{
|
|
||||||
Year = (ushort)currentDateTime.Year,
|
|
||||||
Month = (byte)currentDateTime.Month,
|
|
||||||
Day = (byte)currentDateTime.Day,
|
|
||||||
Hour = (byte)currentDateTime.Hour,
|
|
||||||
Minute = (byte)currentDateTime.Minute,
|
|
||||||
Second = (byte)currentDateTime.Second,
|
|
||||||
UniqueId = 0,
|
|
||||||
},
|
|
||||||
AlbumStorage = AlbumStorage.Sd,
|
|
||||||
ContentType = ContentType.Screenshot,
|
|
||||||
Padding = new Array5<byte>(),
|
|
||||||
Unknown0x1f = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
// NOTE: The hex hash is a HMAC-SHA256 (first 32 bytes) using a hardcoded secret key over the titleId, we can simulate it by hashing the titleId instead.
|
|
||||||
string hash = Convert.ToHexString(SHA256.HashData(BitConverter.GetBytes(titleId)))[..0x20];
|
|
||||||
string folderPath = Path.Combine(_sdCardPath, "Nintendo", "Album", currentDateTime.Year.ToString("00"), currentDateTime.Month.ToString("00"), currentDateTime.Day.ToString("00"));
|
|
||||||
string filePath = GenerateFilePath(folderPath, applicationAlbumEntry, currentDateTime, hash);
|
|
||||||
|
|
||||||
// TODO: Handle that using the FS service implementation and return the right error code instead of throwing exceptions.
|
|
||||||
Directory.CreateDirectory(folderPath);
|
|
||||||
|
|
||||||
while (File.Exists(filePath))
|
|
||||||
{
|
|
||||||
applicationAlbumEntry.AlbumFileDateTime.UniqueId++;
|
|
||||||
|
|
||||||
filePath = GenerateFilePath(folderPath, applicationAlbumEntry, currentDateTime, hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: The saved JPEG file doesn't have the limitation in the extra EXIF data.
|
|
||||||
using SKBitmap bitmap = new(new SKImageInfo(1280, 720, SKColorType.Rgba8888));
|
|
||||||
Marshal.Copy(screenshotData, 0, bitmap.GetPixels(), screenshotData.Length);
|
|
||||||
using SKData data = bitmap.Encode(SKEncodedImageFormat.Jpeg, 80);
|
|
||||||
using FileStream file = File.OpenWrite(filePath);
|
|
||||||
data.SaveTo(file);
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResultCode.NullInputBuffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GenerateFilePath(string folderPath, ApplicationAlbumEntry applicationAlbumEntry, DateTime currentDateTime, string hash)
|
private string GenerateFilePath(string folderPath, ApplicationAlbumEntry applicationAlbumEntry, DateTime currentDateTime, string hash)
|
||||||
|
|||||||
@@ -1,13 +1,19 @@
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Services.Caps.Types;
|
using Ryujinx.HLE.HOS.Services.Caps.Types;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Caps
|
namespace Ryujinx.HLE.HOS.Services.Caps
|
||||||
{
|
{
|
||||||
[Service("caps:su")] // 6.0.0+
|
[Service("caps:su")] // 6.0.0+
|
||||||
class IScreenShotApplicationService : IpcService
|
internal class IScreenShotApplicationService : IpcService
|
||||||
{
|
{
|
||||||
public IScreenShotApplicationService(ServiceCtx context) { }
|
private const ulong ScreenshotDataSize = 0x384000;
|
||||||
|
private const ulong ApplicationDataSize = 0x404;
|
||||||
|
|
||||||
|
public IScreenShotApplicationService(ServiceCtx context)
|
||||||
|
{
|
||||||
|
_ = context;
|
||||||
|
}
|
||||||
[CommandCmif(32)] // 7.0.0+
|
[CommandCmif(32)] // 7.0.0+
|
||||||
// SetShimLibraryVersion(pid, u64, nn::applet::AppletResourceUserId)
|
// SetShimLibraryVersion(pid, u64, nn::applet::AppletResourceUserId)
|
||||||
public ResultCode SetShimLibraryVersion(ServiceCtx context)
|
public ResultCode SetShimLibraryVersion(ServiceCtx context)
|
||||||
@@ -33,6 +39,15 @@ namespace Ryujinx.HLE.HOS.Services.Caps
|
|||||||
ulong screenshotDataPosition = context.Request.SendBuff[0].Position;
|
ulong screenshotDataPosition = context.Request.SendBuff[0].Position;
|
||||||
ulong screenshotDataSize = context.Request.SendBuff[0].Size;
|
ulong screenshotDataSize = context.Request.SendBuff[0].Size;
|
||||||
|
|
||||||
|
if (screenshotDataSize < ScreenshotDataSize)
|
||||||
|
{
|
||||||
|
Logger.Warning?.PrintMsg(
|
||||||
|
LogClass.ServiceCaps,
|
||||||
|
$"Invalid screenshot buffer size 0x{screenshotDataSize:X}; expected at least 0x{ScreenshotDataSize:X}.");
|
||||||
|
|
||||||
|
return ResultCode.NullInputBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
byte[] screenshotData = context.Memory.GetSpan(screenshotDataPosition, (int)screenshotDataSize, true).ToArray();
|
byte[] screenshotData = context.Memory.GetSpan(screenshotDataPosition, (int)screenshotDataSize, true).ToArray();
|
||||||
|
|
||||||
ResultCode resultCode = context.Device.System.CaptureManager.SaveScreenShot(screenshotData, appletResourceUserId, context.Device.Processes.ActiveApplication.ProgramId, out ApplicationAlbumEntry applicationAlbumEntry);
|
ResultCode resultCode = context.Device.System.CaptureManager.SaveScreenShot(screenshotData, appletResourceUserId, context.Device.Processes.ActiveApplication.ProgramId, out ApplicationAlbumEntry applicationAlbumEntry);
|
||||||
@@ -60,6 +75,24 @@ namespace Ryujinx.HLE.HOS.Services.Caps
|
|||||||
ulong screenshotDataPosition = context.Request.SendBuff[1].Position;
|
ulong screenshotDataPosition = context.Request.SendBuff[1].Position;
|
||||||
ulong screenshotDataSize = context.Request.SendBuff[1].Size;
|
ulong screenshotDataSize = context.Request.SendBuff[1].Size;
|
||||||
|
|
||||||
|
if (applicationDataSize != ApplicationDataSize)
|
||||||
|
{
|
||||||
|
Logger.Warning?.PrintMsg(
|
||||||
|
LogClass.ServiceCaps,
|
||||||
|
$"Invalid ApplicationData size 0x{applicationDataSize:X}; expected 0x{ApplicationDataSize:X}.");
|
||||||
|
|
||||||
|
return ResultCode.InvalidArgument;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (screenshotDataSize < ScreenshotDataSize)
|
||||||
|
{
|
||||||
|
Logger.Warning?.PrintMsg(
|
||||||
|
LogClass.ServiceCaps,
|
||||||
|
$"Invalid screenshot buffer size 0x{screenshotDataSize:X}; expected at least 0x{ScreenshotDataSize:X}.");
|
||||||
|
|
||||||
|
return ResultCode.NullInputBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Parse the application data: At 0x00 it's UserData (Size of 0x400), at 0x404 it's a uint UserDataSize (Always empty for now).
|
// TODO: Parse the application data: At 0x00 it's UserData (Size of 0x400), at 0x404 it's a uint UserDataSize (Always empty for now).
|
||||||
_ = context.Memory.GetSpan(applicationDataPosition, (int)applicationDataSize).ToArray();
|
_ = context.Memory.GetSpan(applicationDataPosition, (int)applicationDataSize).ToArray();
|
||||||
|
|
||||||
@@ -88,6 +121,23 @@ namespace Ryujinx.HLE.HOS.Services.Caps
|
|||||||
ulong screenshotDataPosition = context.Request.SendBuff[1].Position;
|
ulong screenshotDataPosition = context.Request.SendBuff[1].Position;
|
||||||
ulong screenshotDataSize = context.Request.SendBuff[1].Size;
|
ulong screenshotDataSize = context.Request.SendBuff[1].Size;
|
||||||
|
|
||||||
|
if (userIdListSize != 0x88)
|
||||||
|
{
|
||||||
|
Logger.Warning?.PrintMsg(
|
||||||
|
LogClass.ServiceCaps,
|
||||||
|
$"Invalid UserIdList size 0x{userIdListSize:X}; expected 0x88.");
|
||||||
|
return ResultCode.InvalidArgument;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (screenshotDataSize < ScreenshotDataSize)
|
||||||
|
{
|
||||||
|
Logger.Warning?.PrintMsg(
|
||||||
|
LogClass.ServiceCaps,
|
||||||
|
$"Invalid screenshot buffer size 0x{screenshotDataSize:X}; expected at least 0x{ScreenshotDataSize:X}.");
|
||||||
|
|
||||||
|
return ResultCode.NullInputBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Parse the UserIdList.
|
// TODO: Parse the UserIdList.
|
||||||
_ = context.Memory.GetSpan(userIdListPosition, (int)userIdListSize).ToArray();
|
_ = context.Memory.GetSpan(userIdListPosition, (int)userIdListSize).ToArray();
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -581,6 +582,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)
|
||||||
{
|
{
|
||||||
ref NpadInternalState currentNpad = ref _device.Hid.SharedMemory.Npads[(int)index].InternalState;
|
ref NpadInternalState currentNpad = ref _device.Hid.SharedMemory.Npads[(int)index].InternalState;
|
||||||
|
|||||||
@@ -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);
|
|
||||||
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, sixAxisSensorHandle, isAtRest });
|
|
||||||
|
|
||||||
|
context.ResponseData.Write(context.Device.Hid.Npads.isAtRest(playerNumber));
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ using Ryujinx.Common.Logging;
|
|||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
using Ryujinx.Cpu;
|
using Ryujinx.Cpu;
|
||||||
using Ryujinx.HLE.Exceptions;
|
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using Ryujinx.HLE.HOS.Services.Ldn.Types;
|
using Ryujinx.HLE.HOS.Services.Ldn.Types;
|
||||||
@@ -15,7 +14,6 @@ using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types;
|
|||||||
using Ryujinx.Horizon.Common;
|
using Ryujinx.Horizon.Common;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.NetworkInformation;
|
using System.Net.NetworkInformation;
|
||||||
@@ -68,10 +66,11 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
{
|
{
|
||||||
if (localCommunicationId == localCommunicationIdChecked)
|
if (localCommunicationId == localCommunicationIdChecked)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,"CheckLocalCommumicationIdPermission: Checked!");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,"CheckLocalCommumicationIdPermission: Check failed!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,7 +81,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
context.ResponseData.Write((int)NetworkState.Error);
|
context.ResponseData.Write((int)NetworkState.Error);
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,$"GetState: _nifmResultCode = {_nifmResultCode.ToString()}");
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,12 +113,14 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
|
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,$"GetNetworkInfo: _nifmResultCode = {_nifmResultCode.ToString()}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo);
|
ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo);
|
||||||
if (resultCode != ResultCode.Success)
|
if (resultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,$"GetState: resultCode = {resultCode.ToString()}");
|
||||||
return resultCode;
|
return resultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,18 +136,22 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
if (_state == NetworkState.StationConnected)
|
if (_state == NetworkState.StationConnected)
|
||||||
{
|
{
|
||||||
networkInfo = _station.NetworkInfo;
|
networkInfo = _station.NetworkInfo;
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,"GetNetworkInfoImpl: _station");
|
||||||
}
|
}
|
||||||
else if (_state == NetworkState.AccessPointCreated)
|
else if (_state == NetworkState.AccessPointCreated)
|
||||||
{
|
{
|
||||||
networkInfo = _accessPoint.NetworkInfo;
|
networkInfo = _accessPoint.NetworkInfo;
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,"GetNetworkInfoImpl: _accessPoint");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
networkInfo = new NetworkInfo();
|
networkInfo = new NetworkInfo();
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,"GetNetworkInfoImpl: Invalid state!");
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,$"GetNetworkInfoImpl: networkInfo = {networkInfo}");
|
||||||
return ResultCode.InvalidState;
|
return ResultCode.InvalidState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,$"GetNetworkInfoImpl: networkInfo = {networkInfo}");
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,7 +203,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.Info?.Print(LogClass.ServiceLdn, $"Console's LDN IP is \"{unicastAddress.Address}\".");
|
Logger.NetLog?.Print(LogClass.ServiceLdn, $"Console's LDN IP is \"{unicastAddress.Address}\".");
|
||||||
|
|
||||||
context.ResponseData.Write(NetworkHelpers.ConvertIpv4Address(unicastAddress.Address));
|
context.ResponseData.Write(NetworkHelpers.ConvertIpv4Address(unicastAddress.Address));
|
||||||
context.ResponseData.Write(NetworkHelpers.ConvertIpv4Address(unicastAddress.IPv4Mask));
|
context.ResponseData.Write(NetworkHelpers.ConvertIpv4Address(unicastAddress.IPv4Mask));
|
||||||
@@ -206,7 +211,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.Info?.Print(LogClass.ServiceLdn, $"LDN obtained proxy IP.");
|
Logger.NetLog?.Print(LogClass.ServiceLdn, $"LDN obtained proxy IP.");
|
||||||
|
|
||||||
context.ResponseData.Write(config.ProxyIp);
|
context.ResponseData.Write(config.ProxyIp);
|
||||||
context.ResponseData.Write(config.ProxySubnetMask);
|
context.ResponseData.Write(config.ProxySubnetMask);
|
||||||
@@ -227,7 +232,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
// NOTE: Returns ResultCode.InvalidArgument if _disconnectReason is null, doesn't occur in our case.
|
// NOTE: Returns ResultCode.InvalidArgument if _disconnectReason is null, doesn't occur in our case.
|
||||||
|
|
||||||
context.ResponseData.Write((short)_disconnectReason);
|
context.ResponseData.Write((short)_disconnectReason);
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetDisconnectReason: {_disconnectReason}");
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,12 +252,14 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
{
|
{
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetSecurityParameter: _nifmResultCode = {_nifmResultCode}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo);
|
ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo);
|
||||||
if (resultCode != ResultCode.Success)
|
if (resultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetSecurityParameter: resultCode = {resultCode}");
|
||||||
return resultCode;
|
return resultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,6 +271,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
|
|
||||||
context.ResponseData.WriteStruct(securityParameter);
|
context.ResponseData.WriteStruct(securityParameter);
|
||||||
|
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetSecurityParameter: securityParameter = {securityParameter}");
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,12 +281,14 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
{
|
{
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetNetworkConfig: _nifmResultCode = {_nifmResultCode}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo);
|
ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo);
|
||||||
if (resultCode != ResultCode.Success)
|
if (resultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetNetworkConfig: resultCode = {resultCode}");
|
||||||
return resultCode;
|
return resultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,6 +303,8 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
|
|
||||||
context.ResponseData.WriteStruct(networkConfig);
|
context.ResponseData.WriteStruct(networkConfig);
|
||||||
|
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetNetworkConfig: networkConfig = {networkConfig}");
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,12 +334,14 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
|
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetNetworkInfoLatestUpdate: _nifmResultCode = {_nifmResultCode}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo);
|
ResultCode resultCode = GetNetworkInfoImpl(out NetworkInfo networkInfo);
|
||||||
if (resultCode != ResultCode.Success)
|
if (resultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"GetNetworkInfoLatestUpdate: resultCode = {resultCode}");
|
||||||
return resultCode;
|
return resultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -378,6 +392,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
|
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ScanImpl: _nifmResultCode = {_nifmResultCode}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -400,6 +415,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
{
|
{
|
||||||
if (scanFilter.Ssid.Length <= 31)
|
if (scanFilter.Ssid.Length <= 31)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ScanImpl: resultCode = {resultCode}");
|
||||||
return resultCode;
|
return resultCode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -408,11 +424,13 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
{
|
{
|
||||||
if (scanFilterFlag > ScanFilterFlag.All)
|
if (scanFilterFlag > ScanFilterFlag.All)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ScanImpl: resultCode = {resultCode}");
|
||||||
return resultCode;
|
return resultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_state - 3 >= NetworkState.AccessPoint)
|
if (_state - 3 >= NetworkState.AccessPoint)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "ScanImpl: Invalid state!");
|
||||||
resultCode = ResultCode.InvalidState;
|
resultCode = ResultCode.InvalidState;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -438,6 +456,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ScanImpl: resultCode = {resultCode}");
|
||||||
return resultCode;
|
return resultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -462,6 +481,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ScanInternal: availableGames = {availableGames}");
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -502,7 +522,8 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
throw new ArgumentException($"{GetType().FullName}: Protocol value is not 1 or 3!! Protocol value: {protocolValue}");
|
throw new ArgumentException($"{GetType().FullName}: Protocol value is not 1 or 3!! Protocol value: {protocolValue}");
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceLdn, $"Protocol value: {protocolValue}");
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"SetProtocol: protocolValue = {protocolValue}");
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceLdn, new { protocolValue});
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -512,11 +533,13 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
{
|
{
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"OpenAccessPoint: _nifmResultCode = {_nifmResultCode}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_state != NetworkState.Initialized)
|
if (_state != NetworkState.Initialized)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "OpenAccessPoint: Invalid state!");
|
||||||
return ResultCode.InvalidState;
|
return ResultCode.InvalidState;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -538,6 +561,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
{
|
{
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"CloseAccessPoint: _nifmResultCode = {_nifmResultCode}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -547,6 +571,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "CloseAccessPoint: Invalid state!");
|
||||||
return ResultCode.InvalidState;
|
return ResultCode.InvalidState;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -596,11 +621,13 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
bool isLocalCommunicationIdValid = CheckLocalCommunicationIdPermission(context, (ulong)networkConfig.IntentId.LocalCommunicationId);
|
bool isLocalCommunicationIdValid = CheckLocalCommunicationIdPermission(context, (ulong)networkConfig.IntentId.LocalCommunicationId);
|
||||||
if (!isLocalCommunicationIdValid && NetworkClient.NeedsRealId)
|
if (!isLocalCommunicationIdValid && NetworkClient.NeedsRealId)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "CreateNetworkImpl: Invalid object!");
|
||||||
return ResultCode.InvalidObject;
|
return ResultCode.InvalidObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"CreateNetworkImpl: _nifmResultCode = {_nifmResultCode}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -629,16 +656,22 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
AddressList addressList = MemoryMarshal.Cast<byte, AddressList>(addressListBytes)[0];
|
AddressList addressList = MemoryMarshal.Cast<byte, AddressList>(addressListBytes)[0];
|
||||||
|
|
||||||
_accessPoint.CreateNetworkPrivate(securityConfig, securityParameter, userConfig, networkConfig, addressList);
|
_accessPoint.CreateNetworkPrivate(securityConfig, securityParameter, userConfig, networkConfig, addressList);
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"CreateNetworkImpl: Created private network! " +
|
||||||
|
$"| securityConfig = {securityConfig} | securityParameter = {securityParameter} " +
|
||||||
|
$"| userConfig = {userConfig} | networkConfig = {networkConfig} | addressList = {addressList}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_accessPoint.CreateNetwork(securityConfig, userConfig, networkConfig);
|
_accessPoint.CreateNetwork(securityConfig, userConfig, networkConfig);
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"CreateNetworkImpl: Created network! " +
|
||||||
|
$"| securityConfig = {securityConfig} | userConfig = {userConfig} | networkConfig = {networkConfig}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "CreateNetworkImpl: Invalid state!");
|
||||||
return ResultCode.InvalidState;
|
return ResultCode.InvalidState;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -660,6 +693,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
{
|
{
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"DestroyNetworkImpl: _nifmResultCode = {_nifmResultCode}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -676,9 +710,11 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
|
|
||||||
CloseAccessPoint();
|
CloseAccessPoint();
|
||||||
|
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "DestroyNetworkImpl: Invalid state!");
|
||||||
return ResultCode.InvalidState;
|
return ResultCode.InvalidState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "DestroyNetworkImpl: Invalid argument!");
|
||||||
return ResultCode.InvalidArgument;
|
return ResultCode.InvalidArgument;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -695,14 +731,17 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
{
|
{
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"RejectImpl: _nifmResultCode = {_nifmResultCode}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_state != NetworkState.AccessPointCreated)
|
if (_state != NetworkState.AccessPointCreated)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "RejectImpl: Invalid state!");
|
||||||
return ResultCode.InvalidState; // Must be network host to reject nodes.
|
return ResultCode.InvalidState; // Must be network host to reject nodes.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"RejectImpl: disconnectReason = {disconnectReason} | nodeId = {nodeId}");
|
||||||
return NetworkClient.Reject(disconnectReason, nodeId);
|
return NetworkClient.Reject(disconnectReason, nodeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -714,11 +753,13 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
|
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"SetAdvertiseData: _nifmResultCode = {_nifmResultCode}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bufferSize is 0 or > LdnConst.AdvertiseDataSizeMax)
|
if (bufferSize is 0 or > LdnConst.AdvertiseDataSizeMax)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "SetAdvertiseData: Invalid argument!");
|
||||||
return ResultCode.InvalidArgument;
|
return ResultCode.InvalidArgument;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -727,11 +768,12 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
byte[] advertiseData = new byte[bufferSize];
|
byte[] advertiseData = new byte[bufferSize];
|
||||||
|
|
||||||
context.Memory.Read(bufferPosition, advertiseData);
|
context.Memory.Read(bufferPosition, advertiseData);
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"SetAdvertiseData: advertiseData = {advertiseData}");
|
||||||
return _accessPoint.SetAdvertiseData(advertiseData);
|
return _accessPoint.SetAdvertiseData(advertiseData);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "SetAdvertiseData: Invalid state!");
|
||||||
return ResultCode.InvalidState;
|
return ResultCode.InvalidState;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -744,20 +786,24 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
|
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"SetStationAcceptPolicy: _nifmResultCode = {_nifmResultCode}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (acceptPolicy > AcceptPolicy.WhiteList)
|
if (acceptPolicy > AcceptPolicy.WhiteList)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "SetStationAcceptPolicy: Invalid argument!");
|
||||||
return ResultCode.InvalidArgument;
|
return ResultCode.InvalidArgument;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_state is NetworkState.AccessPoint or NetworkState.AccessPointCreated)
|
if (_state is NetworkState.AccessPoint or NetworkState.AccessPointCreated)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"SetStationAcceptPolicy: acceptPolicy = {acceptPolicy}");
|
||||||
return _accessPoint.SetStationAcceptPolicy(acceptPolicy);
|
return _accessPoint.SetStationAcceptPolicy(acceptPolicy);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "SetStationAcceptPolicy: Invalid state!");
|
||||||
return ResultCode.InvalidState;
|
return ResultCode.InvalidState;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -768,6 +814,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
{
|
{
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"AddAcceptFilterEntry: _nifmResultCode = {_nifmResultCode}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -782,6 +829,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
{
|
{
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ClearAcceptFilter: _nifmResultCode = {_nifmResultCode}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -796,11 +844,13 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
{
|
{
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"OpenStation: _nifmResultCode = {_nifmResultCode}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_state != NetworkState.Initialized)
|
if (_state != NetworkState.Initialized)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "OpenStation: Invalid state!");
|
||||||
return ResultCode.InvalidState;
|
return ResultCode.InvalidState;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -814,6 +864,8 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
// NOTE: Calls nifm service and returns related result codes.
|
// NOTE: Calls nifm service and returns related result codes.
|
||||||
// Since we use our own implementation we can return ResultCode.Success.
|
// Since we use our own implementation we can return ResultCode.Success.
|
||||||
|
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"OpenStation: _station = {_station}");
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -823,6 +875,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
{
|
{
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"CloseStation: _nifmResultCode = {_nifmResultCode}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -832,11 +885,13 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "CloseStation: Invalid state!");
|
||||||
return ResultCode.InvalidState;
|
return ResultCode.InvalidState;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetState(NetworkState.Initialized);
|
SetState(NetworkState.Initialized);
|
||||||
|
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "CloseStation: Closed.");
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -901,11 +956,13 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
bool isLocalCommunicationIdValid = CheckLocalCommunicationIdPermission(context, (ulong)networkInfo.NetworkId.IntentId.LocalCommunicationId);
|
bool isLocalCommunicationIdValid = CheckLocalCommunicationIdPermission(context, (ulong)networkInfo.NetworkId.IntentId.LocalCommunicationId);
|
||||||
if (!isLocalCommunicationIdValid && NetworkClient.NeedsRealId)
|
if (!isLocalCommunicationIdValid && NetworkClient.NeedsRealId)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "ConnectImpl: Invalid object!");
|
||||||
return ResultCode.InvalidObject;
|
return ResultCode.InvalidObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ConnectImpl: _nifmResultCode = {_nifmResultCode}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -925,6 +982,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
{
|
{
|
||||||
if (_state != NetworkState.Station)
|
if (_state != NetworkState.Station)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "ConnectImpl: Invalid state!");
|
||||||
resultCode = ResultCode.InvalidState;
|
resultCode = ResultCode.InvalidState;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -932,10 +990,16 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
if (isPrivate)
|
if (isPrivate)
|
||||||
{
|
{
|
||||||
resultCode = _station.ConnectPrivate(securityConfig, securityParameter, userConfig, localCommunicationVersion, optionUnknown, networkConfig);
|
resultCode = _station.ConnectPrivate(securityConfig, securityParameter, userConfig, localCommunicationVersion, optionUnknown, networkConfig);
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ConnectImpl: Private connection established! " +
|
||||||
|
$"| securityConfig = {securityConfig} | securityParameter = {securityParameter} | userConfig = {userConfig} " +
|
||||||
|
$"| localCommunicationVersion = {localCommunicationVersion} | optionUnknown = {optionUnknown} | networkConfig = {networkConfig}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
resultCode = _station.Connect(securityConfig, userConfig, localCommunicationVersion, optionUnknown, networkInfo);
|
resultCode = _station.Connect(securityConfig, userConfig, localCommunicationVersion, optionUnknown, networkInfo);
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ConnectImpl: Connection established! " +
|
||||||
|
$"| securityConfig = {securityConfig} | userConfig = {userConfig} " +
|
||||||
|
$"| localCommunicationVersion = {localCommunicationVersion} | optionUnknown = {optionUnknown} | networkConfig = {networkConfig}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -943,6 +1007,8 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"ConnectImpl: resultCode = {resultCode}");
|
||||||
|
|
||||||
return resultCode;
|
return resultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -957,6 +1023,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
{
|
{
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"DisconnectImpl: _nifmResultCode = {_nifmResultCode}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -970,14 +1037,17 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
|
|
||||||
_disconnectReason = disconnectReason;
|
_disconnectReason = disconnectReason;
|
||||||
|
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"DisconnectImpl: _disconnectReason = {_disconnectReason}");
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
CloseStation();
|
CloseStation();
|
||||||
|
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "DisconnectImpl: Invalid state!");
|
||||||
return ResultCode.InvalidState;
|
return ResultCode.InvalidState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "DisconnectImpl: Invalid argument!");
|
||||||
return ResultCode.InvalidArgument;
|
return ResultCode.InvalidArgument;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -994,6 +1064,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
{
|
{
|
||||||
if (_nifmResultCode != ResultCode.Success)
|
if (_nifmResultCode != ResultCode.Success)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"Finalize: _disconnectReason = {_disconnectReason}");
|
||||||
return _nifmResultCode;
|
return _nifmResultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1010,11 +1081,13 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
_stateChangeEventHandle = 0;
|
_stateChangeEventHandle = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"Finalize: resultCode = {resultCode}");
|
||||||
return resultCode;
|
return resultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResultCode FinalizeImpl(bool isCausedBySystem)
|
private ResultCode FinalizeImpl(bool isCausedBySystem)
|
||||||
{
|
{
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, "FinalizeImpl");
|
||||||
DisconnectReason disconnectReason;
|
DisconnectReason disconnectReason;
|
||||||
|
|
||||||
switch (_state)
|
switch (_state)
|
||||||
@@ -1138,7 +1211,6 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
NetworkClient.SetGameVersion(context.Device.Processes.ActiveApplication.ApplicationControlProperties.DisplayVersion);
|
NetworkClient.SetGameVersion(context.Device.Processes.ActiveApplication.ApplicationControlProperties.DisplayVersion);
|
||||||
|
|
||||||
resultCode = ResultCode.Success;
|
resultCode = ResultCode.Success;
|
||||||
|
|
||||||
_nifmResultCode = resultCode;
|
_nifmResultCode = resultCode;
|
||||||
|
|
||||||
SetState(NetworkState.Initialized);
|
SetState(NetworkState.Initialized);
|
||||||
@@ -1152,6 +1224,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"InitializeImpl: resultCode = {resultCode}");
|
||||||
return resultCode;
|
return resultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu
|
|||||||
|
|
||||||
protected override void OnConnected()
|
protected override void OnConnected()
|
||||||
{
|
{
|
||||||
Logger.Info?.PrintMsg(LogClass.ServiceLdn, $"LDN TCP client connected a new session with Id {Id}");
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"LDN TCP client connected a new session with Id {Id}");
|
||||||
|
|
||||||
UpdatePassphraseIfNeeded();
|
UpdatePassphraseIfNeeded();
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu
|
|||||||
|
|
||||||
protected override void OnDisconnected()
|
protected override void OnDisconnected()
|
||||||
{
|
{
|
||||||
Logger.Info?.PrintMsg(LogClass.ServiceLdn, $"LDN TCP client disconnected a session with Id {Id}");
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"LDN TCP client disconnected a session with Id {Id}");
|
||||||
|
|
||||||
_passphrase = null;
|
_passphrase = null;
|
||||||
|
|
||||||
@@ -174,7 +174,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu
|
|||||||
|
|
||||||
protected override void OnError(SocketError error)
|
protected override void OnError(SocketError error)
|
||||||
{
|
{
|
||||||
Logger.Info?.PrintMsg(LogClass.ServiceLdn, $"LDN TCP client caught an error with code {error}");
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn, $"LDN TCP client caught an error with code {error}");
|
||||||
|
|
||||||
_error.Set();
|
_error.Set();
|
||||||
}
|
}
|
||||||
@@ -428,7 +428,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.Info?.Print(LogClass.ServiceLdn, $"Created a wireless P2P network on port {request.ExternalProxyPort}.");
|
Logger.NetLog?.Print(LogClass.ServiceLdn, $"Created a wireless P2P network on port {request.ExternalProxyPort}.");
|
||||||
_hostedProxy.Start();
|
_hostedProxy.Start();
|
||||||
|
|
||||||
(_, UnicastIPAddressInformation unicastAddress) = NetworkHelpers.GetLocalInterface();
|
(_, UnicastIPAddressInformation unicastAddress) = NetworkHelpers.GetLocalInterface();
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.HLE.HOS.Services.Ldn.Types;
|
using Ryujinx.HLE.HOS.Services.Ldn.Types;
|
||||||
using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types;
|
using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.Types;
|
||||||
@@ -36,10 +37,12 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
if (Connected)
|
if (Connected)
|
||||||
{
|
{
|
||||||
_parent.SetState(NetworkState.StationConnected);
|
_parent.SetState(NetworkState.StationConnected);
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,$"NetworkChanged: {NetworkInfo}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_parent.SetDisconnectReason(e.DisconnectReasonOrDefault(DisconnectReason.DestroyedByUser));
|
_parent.SetDisconnectReason(e.DisconnectReasonOrDefault(DisconnectReason.DestroyedByUser));
|
||||||
|
Logger.NetLog?.PrintMsg(LogClass.ServiceLdn,"NetworkChanged: Disconnected (DestroyedByUser)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -81,8 +81,10 @@ namespace Ryujinx.HLE.HOS.Services.Mii
|
|||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResultCode UpdateLatest<T>(DatabaseSessionMetadata metadata, IStoredData<T> oldMiiData, SourceFlag flag, IStoredData<T> newMiiData) where T : unmanaged
|
public ResultCode UpdateLatest<T>(DatabaseSessionMetadata metadata, T oldMiiData, SourceFlag flag, out T newMiiData) where T : unmanaged, IStoredData<T>
|
||||||
{
|
{
|
||||||
|
newMiiData = default;
|
||||||
|
|
||||||
if (!flag.HasFlag(SourceFlag.Database))
|
if (!flag.HasFlag(SourceFlag.Database))
|
||||||
{
|
{
|
||||||
return ResultCode.NotFound;
|
return ResultCode.NotFound;
|
||||||
@@ -106,7 +108,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii
|
|||||||
|
|
||||||
newMiiData.SetFromStoreData(storeData);
|
newMiiData.SetFromStoreData(storeData);
|
||||||
|
|
||||||
if (oldMiiData == newMiiData)
|
if (oldMiiData.Equals(newMiiData))
|
||||||
{
|
{
|
||||||
return ResultCode.NotUpdated;
|
return ResultCode.NotUpdated;
|
||||||
}
|
}
|
||||||
@@ -286,6 +288,18 @@ namespace Ryujinx.HLE.HOS.Services.Mii
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ResultCode Append(DatabaseSessionMetadata metadata, CharInfo charInfo)
|
||||||
|
{
|
||||||
|
ResultCode result = _miiDatabase.Append(metadata, _utilityImpl, charInfo);
|
||||||
|
|
||||||
|
if (result == ResultCode.Success)
|
||||||
|
{
|
||||||
|
result = _miiDatabase.SaveDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public ResultCode ConvertCharInfoToCoreData(CharInfo charInfo, out CoreData coreData)
|
public ResultCode ConvertCharInfoToCoreData(CharInfo charInfo, out CoreData coreData)
|
||||||
{
|
{
|
||||||
coreData = new CoreData();
|
coreData = new CoreData();
|
||||||
|
|||||||
@@ -449,6 +449,32 @@ namespace Ryujinx.HLE.HOS.Services.Mii
|
|||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ResultCode Append(DatabaseSessionMetadata metadata, UtilityImpl utilityImpl, CharInfo charInfo)
|
||||||
|
{
|
||||||
|
if (!charInfo.IsValid())
|
||||||
|
{
|
||||||
|
return ResultCode.InvalidCharInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (charInfo.Type == 1)
|
||||||
|
{
|
||||||
|
return ResultCode.InvalidOperationOnSpecialMii;
|
||||||
|
}
|
||||||
|
|
||||||
|
CoreData coreData = new();
|
||||||
|
coreData.SetFromCharInfo(charInfo);
|
||||||
|
|
||||||
|
StoreData storeData;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
storeData = StoreData.BuildFromCoreData(utilityImpl, coreData);
|
||||||
|
}
|
||||||
|
while (_database.GetIndexByCreatorId(out _, storeData.CreateId));
|
||||||
|
|
||||||
|
return AddOrReplace(metadata, storeData);
|
||||||
|
}
|
||||||
|
|
||||||
public ResultCode Delete(DatabaseSessionMetadata metadata, CreateId createId)
|
public ResultCode Delete(DatabaseSessionMetadata metadata, CreateId createId)
|
||||||
{
|
{
|
||||||
if (!_database.GetIndexByCreatorId(out int index, createId))
|
if (!_database.GetIndexByCreatorId(out int index, createId))
|
||||||
|
|||||||
@@ -54,9 +54,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService
|
|||||||
|
|
||||||
protected override ResultCode UpdateLatest(CharInfo oldCharInfo, SourceFlag flag, out CharInfo newCharInfo)
|
protected override ResultCode UpdateLatest(CharInfo oldCharInfo, SourceFlag flag, out CharInfo newCharInfo)
|
||||||
{
|
{
|
||||||
newCharInfo = default;
|
return _database.UpdateLatest(_metadata, oldCharInfo, flag, out newCharInfo);
|
||||||
|
|
||||||
return _database.UpdateLatest(_metadata, oldCharInfo, flag, newCharInfo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override ResultCode BuildRandom(Age age, Gender gender, Race race, out CharInfo charInfo)
|
protected override ResultCode BuildRandom(Age age, Gender gender, Race race, out CharInfo charInfo)
|
||||||
@@ -113,14 +111,14 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService
|
|||||||
|
|
||||||
protected override ResultCode UpdateLatest1(StoreData oldStoreData, SourceFlag flag, out StoreData newStoreData)
|
protected override ResultCode UpdateLatest1(StoreData oldStoreData, SourceFlag flag, out StoreData newStoreData)
|
||||||
{
|
{
|
||||||
newStoreData = default;
|
|
||||||
|
|
||||||
if (!_isSystem)
|
if (!_isSystem)
|
||||||
{
|
{
|
||||||
|
newStoreData = default;
|
||||||
|
|
||||||
return ResultCode.PermissionDenied;
|
return ResultCode.PermissionDenied;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _database.UpdateLatest(_metadata, oldStoreData, flag, newStoreData);
|
return _database.UpdateLatest(_metadata, oldStoreData, flag, out newStoreData);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override ResultCode FindIndex(CreateId createId, bool isSpecial, out int index)
|
protected override ResultCode FindIndex(CreateId createId, bool isSpecial, out int index)
|
||||||
@@ -262,5 +260,10 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService
|
|||||||
{
|
{
|
||||||
return _database.ConvertCharInfoToCoreData(charInfo, out coreData);
|
return _database.ConvertCharInfoToCoreData(charInfo, out coreData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override ResultCode Append(CharInfo charInfo)
|
||||||
|
{
|
||||||
|
return _database.Append(_metadata, charInfo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -340,6 +340,15 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandCmif(26)] // 10.2.0+
|
||||||
|
// Append(nn::mii::CharInfo char_info)
|
||||||
|
public ResultCode Append(ServiceCtx context)
|
||||||
|
{
|
||||||
|
CharInfo charInfo = context.RequestData.ReadStruct<CharInfo>();
|
||||||
|
|
||||||
|
return Append(charInfo);
|
||||||
|
}
|
||||||
|
|
||||||
private Span<byte> CreateByteSpanFromBuffer(ServiceCtx context, IpcBuffDesc ipcBuff, bool isOutput)
|
private Span<byte> CreateByteSpanFromBuffer(ServiceCtx context, IpcBuffDesc ipcBuff, bool isOutput)
|
||||||
{
|
{
|
||||||
byte[] rawData;
|
byte[] rawData;
|
||||||
@@ -421,5 +430,7 @@ namespace Ryujinx.HLE.HOS.Services.Mii.StaticService
|
|||||||
protected abstract ResultCode ConvertCoreDataToCharInfo(CoreData coreData, out CharInfo charInfo);
|
protected abstract ResultCode ConvertCoreDataToCharInfo(CoreData coreData, out CharInfo charInfo);
|
||||||
|
|
||||||
protected abstract ResultCode ConvertCharInfoToCoreData(CharInfo charInfo, out CoreData coreData);
|
protected abstract ResultCode ConvertCharInfoToCoreData(CharInfo charInfo, out CoreData coreData);
|
||||||
|
|
||||||
|
protected abstract ResultCode Append(CharInfo charInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
namespace Ryujinx.HLE.HOS.Services.Notification
|
||||||
|
{
|
||||||
|
[Service("notif:s")] // 9.0.0+
|
||||||
|
class INotificationServices : IpcService
|
||||||
|
{
|
||||||
|
public INotificationServices(ServiceCtx context) { }
|
||||||
|
|
||||||
|
[CommandCmif(1000)] // 9.0.0+
|
||||||
|
// GetNotificationCount() -> nn::notification::server::INotificationSystemEventAccessor
|
||||||
|
public ResultCode GetNotificationCount(ServiceCtx context)
|
||||||
|
{
|
||||||
|
MakeObject(context, new INotificationSystemEventAccessor(context));
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(1040)] // 9.0.0+
|
||||||
|
// GetNotificationSendingNotifier() -> nn::notification::server::INotificationSystemEventAccessor
|
||||||
|
public ResultCode GetNotificationSendingNotifier(ServiceCtx context)
|
||||||
|
{
|
||||||
|
MakeObject(context, new INotificationSystemEventAccessor(context));
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,33 @@
|
|||||||
|
using Ryujinx.Common.Logging;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Notification
|
namespace Ryujinx.HLE.HOS.Services.Notification
|
||||||
{
|
{
|
||||||
[Service("notif:a")] // 9.0.0+
|
[Service("notif:a")] // 9.0.0+
|
||||||
class INotificationServicesForApplication : IpcService
|
class INotificationServicesForApplication : IpcService
|
||||||
{
|
{
|
||||||
public INotificationServicesForApplication(ServiceCtx context) { }
|
public INotificationServicesForApplication(ServiceCtx context) { }
|
||||||
|
|
||||||
|
// Leaving this here since I can never find it: https://switchbrew.org/wiki/Glue_services
|
||||||
|
|
||||||
|
[CommandCmif(520)] // 9.0.0+
|
||||||
|
// ListAlarmSettings(nn::arp::ApplicationCertificate) -> s32 AlarmSettingsCount
|
||||||
|
public ResultCode ListAlarmSettings(ServiceCtx context)
|
||||||
|
{
|
||||||
|
// TO-DO: Currently just returns 0. Should read in an ApplicationCertificate.
|
||||||
|
int alarmSettingsCount = 0;
|
||||||
|
context.ResponseData.Write(alarmSettingsCount);
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(1000)] // 9.0.0+
|
||||||
|
// Initialize(PID-descriptor, u64 pid_reserved)
|
||||||
|
public ResultCode Intialize(ServiceCtx context)
|
||||||
|
{
|
||||||
|
ulong pid = context.Request.HandleDesc.PId;
|
||||||
|
context.RequestData.ReadUInt64(); // pid placeholder, zero
|
||||||
|
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceNotification, new { pid });
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
namespace Ryujinx.HLE.HOS.Services.Notification
|
|
||||||
{
|
|
||||||
[Service("notif:s")] // 9.0.0+
|
|
||||||
class INotificationServicesForSystem : IpcService
|
|
||||||
{
|
|
||||||
public INotificationServicesForSystem(ServiceCtx context) { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
|
using Ryujinx.Horizon.Common;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Notification
|
||||||
|
{
|
||||||
|
class INotificationSystemEventAccessor : IpcService
|
||||||
|
{
|
||||||
|
|
||||||
|
private readonly KEvent _getNotificationSendingNotifierEvent;
|
||||||
|
private int _getNotificationSendingNotifierEventHandle;
|
||||||
|
public INotificationSystemEventAccessor(ServiceCtx context) { }
|
||||||
|
|
||||||
|
[CommandCmif(0)] // 9.0.0+
|
||||||
|
// GetNotificationSendingNotifier() -> nn::notification::server::INotificationSystemEventAccessor
|
||||||
|
public ResultCode GetSystemEvent(ServiceCtx context)
|
||||||
|
{
|
||||||
|
if (_getNotificationSendingNotifierEventHandle == 0)
|
||||||
|
{
|
||||||
|
if (context.Process.HandleTable.GenerateHandle(_getNotificationSendingNotifierEvent.ReadableEvent, out _getNotificationSendingNotifierEventHandle) != Result.Success)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Out of handles!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_getNotificationSendingNotifierEventHandle);
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ using Ryujinx.HLE.Loaders.Executables;
|
|||||||
using Ryujinx.HLE.Loaders.Processes.Extensions;
|
using Ryujinx.HLE.Loaders.Processes.Extensions;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Path = System.IO.Path;
|
using Path = System.IO.Path;
|
||||||
|
|
||||||
@@ -27,10 +28,16 @@ namespace Ryujinx.HLE.Loaders.Processes
|
|||||||
|
|
||||||
private ulong _latestPid;
|
private ulong _latestPid;
|
||||||
|
|
||||||
public ProcessResult ActiveApplication
|
#nullable enable
|
||||||
|
public ProcessResult? ActiveApplication
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
return _processesByPid.GetValueOrDefault(_latestPid);
|
||||||
|
|
||||||
|
// Using this if statement locks up the UI and prevents a new game from loading.
|
||||||
|
// Haven't quite deduced why yet.
|
||||||
|
|
||||||
if (!_processesByPid.TryGetValue(_latestPid, out ProcessResult value))
|
if (!_processesByPid.TryGetValue(_latestPid, out ProcessResult value))
|
||||||
throw new RyujinxException(
|
throw new RyujinxException(
|
||||||
$"The HLE Process map did not have a process with ID {_latestPid}. Are you missing firmware?");
|
$"The HLE Process map did not have a process with ID {_latestPid}. Are you missing firmware?");
|
||||||
@@ -38,6 +45,7 @@ namespace Ryujinx.HLE.Loaders.Processes
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
public ProcessLoader(Switch device)
|
public ProcessLoader(Switch device)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ using LibHac.Ns;
|
|||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Cpu;
|
using Ryujinx.Cpu;
|
||||||
using Ryujinx.HLE.HOS.SystemState;
|
using Ryujinx.HLE.HOS.SystemState;
|
||||||
using Ryujinx.HLE.Loaders.Processes.Extensions;
|
|
||||||
using Ryujinx.Horizon.Common;
|
using Ryujinx.Horizon.Common;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.Loaders.Processes
|
namespace Ryujinx.HLE.Loaders.Processes
|
||||||
@@ -52,6 +51,7 @@ namespace Ryujinx.HLE.Loaders.Processes
|
|||||||
|
|
||||||
if (metaLoader is not null)
|
if (metaLoader is not null)
|
||||||
{
|
{
|
||||||
|
Logger.Info?.Print(LogClass.Application,$"metaLoader: {metaLoader}");
|
||||||
ulong programId = metaLoader.ProgramId;
|
ulong programId = metaLoader.ProgramId;
|
||||||
|
|
||||||
Name = ApplicationControlProperties.Title[(int)titleLanguage].NameString.ToString();
|
Name = ApplicationControlProperties.Title[(int)titleLanguage].NameString.ToString();
|
||||||
@@ -73,6 +73,13 @@ namespace Ryujinx.HLE.Loaders.Processes
|
|||||||
Is64Bit = metaLoader.IsProgram64Bit;
|
Is64Bit = metaLoader.IsProgram64Bit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Application,$"metaLoader is null !!!");
|
||||||
|
ProcessId = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
DiskCacheEnabled = diskCacheEnabled;
|
DiskCacheEnabled = diskCacheEnabled;
|
||||||
AllowCodeMemoryForJit = allowCodeMemoryForJit;
|
AllowCodeMemoryForJit = allowCodeMemoryForJit;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,9 @@
|
|||||||
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" />
|
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" />
|
||||||
<PackageReference Include="MsgPack.Cli" />
|
<PackageReference Include="MsgPack.Cli" />
|
||||||
<PackageReference Include="SkiaSharp" />
|
<PackageReference Include="SkiaSharp" />
|
||||||
<PackageReference Include="SkiaSharp.NativeAssets.Linux" />
|
<PackageReference Include="SkiaSharp.NativeAssets.Win32" />
|
||||||
|
<PackageReference Include="SkiaSharp.NativeAssets.macOS" />
|
||||||
|
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" />
|
||||||
<PackageReference Include="NetCoreServer" />
|
<PackageReference Include="NetCoreServer" />
|
||||||
<PackageReference Include="Open.NAT.Core" />
|
<PackageReference Include="Open.NAT.Core" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -75,6 +85,47 @@ namespace Ryujinx.Horizon.Sdk.Ns
|
|||||||
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
|
||||||
{
|
{
|
||||||
public ByteArray512 Name;
|
public ByteArray512 Name;
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -331,28 +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();
|
||||||
}
|
}
|
||||||
|
|
||||||
lock (_joyConsIds)
|
foreach (string id in ids)
|
||||||
{
|
{
|
||||||
foreach (var gamepad in _joyConsIds)
|
yield return GetGamepad(id);
|
||||||
{
|
|
||||||
yield return GetGamepad(gamepad.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (_linkedJoyConsIds)
|
|
||||||
{
|
|
||||||
foreach (var gamepad in _linkedJoyConsIds)
|
|
||||||
{
|
|
||||||
yield return GetGamepad(gamepad.Value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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}, " +
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.SDL3-CS" />
|
<PackageReference Include="Ryujinx.SDL3-CS" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -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>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
187
src/Ryujinx.Tests/HLE/CaptureManagerTests.cs
Normal file
187
src/Ryujinx.Tests/HLE/CaptureManagerTests.cs
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
using NUnit.Framework;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Caps;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Caps.Types;
|
||||||
|
using SkiaSharp;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Tests.HLE
|
||||||
|
{
|
||||||
|
public class CaptureManagerTests
|
||||||
|
{
|
||||||
|
private const int ScreenshotWidth = 1280;
|
||||||
|
private const int ScreenshotHeight = 720;
|
||||||
|
private const int BytesPerPixel = 4;
|
||||||
|
|
||||||
|
private const int ScreenshotDataSize = ScreenshotWidth * ScreenshotHeight * BytesPerPixel; // 0x384000
|
||||||
|
private const int PaddedScreenshotDataSize = ScreenshotWidth * 768 * BytesPerPixel; // 0x3C0000
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SaveScreenShotRejectsBufferSmallerThan720p()
|
||||||
|
{
|
||||||
|
using TempSdCard tempSdCard = new();
|
||||||
|
|
||||||
|
CaptureManager captureManager = CreateCaptureManager(tempSdCard.Path);
|
||||||
|
byte[] screenshotData = new byte[ScreenshotDataSize - 1];
|
||||||
|
|
||||||
|
ResultCode result = captureManager.SaveScreenShot(
|
||||||
|
screenshotData,
|
||||||
|
appletResourceUserId: 0,
|
||||||
|
titleId: 0x0100000000001000,
|
||||||
|
out _);
|
||||||
|
|
||||||
|
Assert.That(result, Is.EqualTo(ResultCode.NullInputBuffer));
|
||||||
|
Assert.That(Directory.Exists(Path.Combine(tempSdCard.Path, "Nintendo", "Album")), Is.False);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SaveScreenShotAcceptsExact720pBuffer()
|
||||||
|
{
|
||||||
|
using TempSdCard tempSdCard = new();
|
||||||
|
|
||||||
|
CaptureManager captureManager = CreateCaptureManager(tempSdCard.Path);
|
||||||
|
byte[] screenshotData = CreateTestPattern(ScreenshotDataSize);
|
||||||
|
|
||||||
|
ResultCode result = captureManager.SaveScreenShot(
|
||||||
|
screenshotData,
|
||||||
|
appletResourceUserId: 0,
|
||||||
|
titleId: 0x0100000000001000,
|
||||||
|
out ApplicationAlbumEntry applicationAlbumEntry);
|
||||||
|
|
||||||
|
string filePath = GetSingleAlbumFile(tempSdCard.Path);
|
||||||
|
|
||||||
|
using SKBitmap bitmap = SKBitmap.Decode(filePath);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(result, Is.EqualTo(ResultCode.Success));
|
||||||
|
Assert.That(bitmap.Width, Is.EqualTo(ScreenshotWidth));
|
||||||
|
Assert.That(bitmap.Height, Is.EqualTo(ScreenshotHeight));
|
||||||
|
Assert.That(applicationAlbumEntry.TitleId, Is.EqualTo(0x0100000000001000));
|
||||||
|
Assert.That(applicationAlbumEntry.AlbumStorage, Is.EqualTo(AlbumStorage.Sd));
|
||||||
|
Assert.That(applicationAlbumEntry.ContentType, Is.EqualTo(ContentType.Screenshot));
|
||||||
|
Assert.That(applicationAlbumEntry.Unknown0x1f, Is.EqualTo(1));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SaveScreenShotAcceptsBufferLargerThan720p()
|
||||||
|
{
|
||||||
|
using TempSdCard tempSdCard = new();
|
||||||
|
|
||||||
|
CaptureManager captureManager = CreateCaptureManager(tempSdCard.Path);
|
||||||
|
byte[] screenshotData = CreateTestPattern(PaddedScreenshotDataSize);
|
||||||
|
|
||||||
|
ResultCode result = captureManager.SaveScreenShot(
|
||||||
|
screenshotData,
|
||||||
|
appletResourceUserId: 0,
|
||||||
|
titleId: 0x0100000000001000,
|
||||||
|
out ApplicationAlbumEntry applicationAlbumEntry);
|
||||||
|
|
||||||
|
string filePath = GetSingleAlbumFile(tempSdCard.Path);
|
||||||
|
|
||||||
|
using SKBitmap bitmap = SKBitmap.Decode(filePath);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(result, Is.EqualTo(ResultCode.Success));
|
||||||
|
Assert.That(bitmap.Width, Is.EqualTo(ScreenshotWidth));
|
||||||
|
Assert.That(bitmap.Height, Is.EqualTo(ScreenshotHeight));
|
||||||
|
Assert.That(applicationAlbumEntry.TitleId, Is.EqualTo(0x0100000000001000));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SaveScreenShotCreatesUniqueFileNamesForRepeatedSaves()
|
||||||
|
{
|
||||||
|
using TempSdCard tempSdCard = new();
|
||||||
|
|
||||||
|
CaptureManager captureManager = CreateCaptureManager(tempSdCard.Path);
|
||||||
|
byte[] screenshotData = CreateTestPattern(ScreenshotDataSize);
|
||||||
|
|
||||||
|
ResultCode firstResult = captureManager.SaveScreenShot(
|
||||||
|
screenshotData,
|
||||||
|
appletResourceUserId: 0,
|
||||||
|
titleId: 0x0100000000001000,
|
||||||
|
out _);
|
||||||
|
|
||||||
|
ResultCode secondResult = captureManager.SaveScreenShot(
|
||||||
|
screenshotData,
|
||||||
|
appletResourceUserId: 0,
|
||||||
|
titleId: 0x0100000000001000,
|
||||||
|
out _);
|
||||||
|
|
||||||
|
string[] files = Directory.GetFiles(
|
||||||
|
Path.Combine(tempSdCard.Path, "Nintendo", "Album"),
|
||||||
|
"*.jpg",
|
||||||
|
SearchOption.AllDirectories);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(firstResult, Is.EqualTo(ResultCode.Success));
|
||||||
|
Assert.That(secondResult, Is.EqualTo(ResultCode.Success));
|
||||||
|
Assert.That(files, Has.Length.EqualTo(2));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CaptureManager CreateCaptureManager(string sdCardPath)
|
||||||
|
{
|
||||||
|
CaptureManager captureManager = (CaptureManager)RuntimeHelpers.GetUninitializedObject(typeof(CaptureManager));
|
||||||
|
|
||||||
|
typeof(CaptureManager)
|
||||||
|
.GetField("_sdCardPath", BindingFlags.Instance | BindingFlags.NonPublic)
|
||||||
|
.SetValue(captureManager, sdCardPath);
|
||||||
|
|
||||||
|
return captureManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetSingleAlbumFile(string sdCardPath)
|
||||||
|
{
|
||||||
|
string albumPath = Path.Combine(sdCardPath, "Nintendo", "Album");
|
||||||
|
|
||||||
|
string[] files = Directory.GetFiles(albumPath, "*.jpg", SearchOption.AllDirectories);
|
||||||
|
|
||||||
|
Assert.That(files, Has.Length.EqualTo(1));
|
||||||
|
|
||||||
|
return files.Single();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] CreateTestPattern(int size)
|
||||||
|
{
|
||||||
|
byte[] data = new byte[size];
|
||||||
|
|
||||||
|
int pixelCount = size / BytesPerPixel;
|
||||||
|
|
||||||
|
for (int i = 0; i < pixelCount; i++)
|
||||||
|
{
|
||||||
|
int x = i % ScreenshotWidth;
|
||||||
|
int y = i / ScreenshotWidth;
|
||||||
|
|
||||||
|
data[(i * 4) + 0] = (byte)(x & 0xff);
|
||||||
|
data[(i * 4) + 1] = (byte)(y & 0xff);
|
||||||
|
data[(i * 4) + 2] = 0x80;
|
||||||
|
data[(i * 4) + 3] = 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class TempSdCard : IDisposable
|
||||||
|
{
|
||||||
|
public string Path { get; } = System.IO.Path.Combine(
|
||||||
|
TestContext.CurrentContext.WorkDirectory,
|
||||||
|
"sdcard-" + Guid.NewGuid());
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (Directory.Exists(Path))
|
||||||
|
{
|
||||||
|
Directory.Delete(Path, recursive: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
122
src/Ryujinx.Tests/HLE/MiiDatabaseTests.cs
Normal file
122
src/Ryujinx.Tests/HLE/MiiDatabaseTests.cs
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
using Ryujinx.Cpu;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Mii;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Mii.StaticService;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Mii.Types;
|
||||||
|
|
||||||
|
namespace Ryujinx.Tests.HLE
|
||||||
|
{
|
||||||
|
public class MiiDatabaseTests
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void UpdateLatestReturnsStoredCharInfo()
|
||||||
|
{
|
||||||
|
DatabaseImpl database = new();
|
||||||
|
StoreData storedData = StoreData.BuildDefault(new UtilityImpl(new TickSource(19200000)), 0);
|
||||||
|
MiiDatabaseManager databaseManager = GetDatabaseManager(database);
|
||||||
|
|
||||||
|
NintendoFigurineDatabase figurineDatabase = new();
|
||||||
|
figurineDatabase.Format();
|
||||||
|
figurineDatabase.Add(storedData);
|
||||||
|
SetFigurineDatabase(databaseManager, figurineDatabase);
|
||||||
|
|
||||||
|
TestDatabaseService service = new(database);
|
||||||
|
|
||||||
|
CharInfo oldCharInfo = new();
|
||||||
|
oldCharInfo.SetFromStoreData(storedData);
|
||||||
|
oldCharInfo.Height--;
|
||||||
|
|
||||||
|
ResultCode result = service.UpdateLatestForTest(oldCharInfo, SourceFlag.Database, out CharInfo newCharInfo);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(result, Is.EqualTo(ResultCode.Success));
|
||||||
|
Assert.That(newCharInfo.CreateId, Is.EqualTo(oldCharInfo.CreateId));
|
||||||
|
Assert.That(newCharInfo.Height, Is.EqualTo(storedData.CoreData.Height));
|
||||||
|
Assert.That(newCharInfo.IsValid(), Is.True);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void AppendAddsRegularCharInfoToDatabase()
|
||||||
|
{
|
||||||
|
DatabaseImpl database = new();
|
||||||
|
UtilityImpl utilityImpl = new(new TickSource(19200000));
|
||||||
|
SetUtilityImpl(database, utilityImpl);
|
||||||
|
MiiDatabaseManager databaseManager = GetDatabaseManager(database);
|
||||||
|
SetFigurineDatabase(databaseManager, CreateFormattedDatabase());
|
||||||
|
|
||||||
|
StoreData defaultStoreData = StoreData.BuildDefault(utilityImpl, 0);
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(defaultStoreData.CoreData.IsValid(), Is.True);
|
||||||
|
Assert.That(defaultStoreData.IsValidDataCrc(), Is.True);
|
||||||
|
Assert.That(defaultStoreData.IsValidDeviceCrc(), Is.True);
|
||||||
|
Assert.That(defaultStoreData.IsValid(), Is.True);
|
||||||
|
});
|
||||||
|
|
||||||
|
CharInfo charInfo = new();
|
||||||
|
charInfo.SetFromStoreData(defaultStoreData);
|
||||||
|
|
||||||
|
DatabaseSessionMetadata metadata = database.CreateSessionMetadata(new SpecialMiiKeyCode());
|
||||||
|
|
||||||
|
ResultCode result = databaseManager.Append(metadata, utilityImpl, charInfo);
|
||||||
|
|
||||||
|
int count = databaseManager.GetCount(metadata);
|
||||||
|
databaseManager.Get(metadata, 0, out StoreData storedData);
|
||||||
|
|
||||||
|
CoreData expectedCoreData = new();
|
||||||
|
expectedCoreData.SetFromCharInfo(charInfo);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(result, Is.EqualTo(ResultCode.Success));
|
||||||
|
Assert.That(count, Is.EqualTo(1));
|
||||||
|
Assert.That(storedData.IsValid(), Is.True);
|
||||||
|
Assert.That(storedData.CreateId, Is.Not.EqualTo(charInfo.CreateId));
|
||||||
|
Assert.That(storedData.CoreData, Is.EqualTo(expectedCoreData));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class TestDatabaseService(DatabaseImpl database) : DatabaseServiceImpl(database, true, new SpecialMiiKeyCode())
|
||||||
|
{
|
||||||
|
public ResultCode UpdateLatestForTest(CharInfo oldCharInfo, SourceFlag flag, out CharInfo newCharInfo)
|
||||||
|
{
|
||||||
|
return UpdateLatest(oldCharInfo, flag, out newCharInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MiiDatabaseManager GetDatabaseManager(DatabaseImpl database)
|
||||||
|
{
|
||||||
|
return (MiiDatabaseManager)typeof(DatabaseImpl)
|
||||||
|
.GetField("_miiDatabase", BindingFlags.Instance | BindingFlags.NonPublic)
|
||||||
|
.GetValue(database);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetFigurineDatabase(MiiDatabaseManager databaseManager, NintendoFigurineDatabase figurineDatabase)
|
||||||
|
{
|
||||||
|
typeof(MiiDatabaseManager)
|
||||||
|
.GetField("_database", BindingFlags.Instance | BindingFlags.NonPublic)
|
||||||
|
.SetValue(databaseManager, figurineDatabase);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static NintendoFigurineDatabase CreateFormattedDatabase()
|
||||||
|
{
|
||||||
|
NintendoFigurineDatabase figurineDatabase = new();
|
||||||
|
figurineDatabase.Format();
|
||||||
|
|
||||||
|
return figurineDatabase;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetUtilityImpl(DatabaseImpl database, UtilityImpl utilityImpl)
|
||||||
|
{
|
||||||
|
typeof(DatabaseImpl)
|
||||||
|
.GetField("_utilityImpl", BindingFlags.Instance | BindingFlags.NonPublic)
|
||||||
|
.SetValue(database, utilityImpl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
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 |
@@ -254,6 +254,7 @@ namespace Ryujinx.Headless
|
|||||||
Logger.SetEnable(LogLevel.Trace, option.LoggingEnableTrace);
|
Logger.SetEnable(LogLevel.Trace, option.LoggingEnableTrace);
|
||||||
Logger.SetEnable(LogLevel.Guest, !option.LoggingDisableGuest);
|
Logger.SetEnable(LogLevel.Guest, !option.LoggingDisableGuest);
|
||||||
Logger.SetEnable(LogLevel.AccessLog, option.LoggingEnableFsAccessLog);
|
Logger.SetEnable(LogLevel.AccessLog, option.LoggingEnableFsAccessLog);
|
||||||
|
Logger.SetEnable(LogLevel.NetLog, option.LoggingEnableFsAccessLog);
|
||||||
|
|
||||||
if (!option.DisableFileLog)
|
if (!option.DisableFileLog)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -109,6 +109,9 @@ namespace Ryujinx.Headless
|
|||||||
if (NeedsOverride(nameof(LoggingEnableFsAccessLog)))
|
if (NeedsOverride(nameof(LoggingEnableFsAccessLog)))
|
||||||
LoggingEnableFsAccessLog = configurationState.Logger.EnableFsAccessLog;
|
LoggingEnableFsAccessLog = configurationState.Logger.EnableFsAccessLog;
|
||||||
|
|
||||||
|
if (NeedsOverride(nameof(LoggingEnableNetLog)))
|
||||||
|
LoggingEnableNetLog = configurationState.Logger.EnableNetLog;
|
||||||
|
|
||||||
if (NeedsOverride(nameof(LoggingGraphicsDebugLevel)))
|
if (NeedsOverride(nameof(LoggingGraphicsDebugLevel)))
|
||||||
LoggingGraphicsDebugLevel = configurationState.Logger.GraphicsDebugLevel;
|
LoggingGraphicsDebugLevel = configurationState.Logger.GraphicsDebugLevel;
|
||||||
|
|
||||||
@@ -371,6 +374,9 @@ namespace Ryujinx.Headless
|
|||||||
[Option("enable-fs-access-logs", Required = false, Default = false, HelpText = "Enables printing FS access log messages.")]
|
[Option("enable-fs-access-logs", Required = false, Default = false, HelpText = "Enables printing FS access log messages.")]
|
||||||
public bool LoggingEnableFsAccessLog { get; set; }
|
public bool LoggingEnableFsAccessLog { get; set; }
|
||||||
|
|
||||||
|
[Option("enable-net-logs", Required = false, Default = false, HelpText = "Enables printing net log messages.")]
|
||||||
|
public bool LoggingEnableNetLog { get; set; }
|
||||||
|
|
||||||
[Option("graphics-debug-level", Required = false, Default = GraphicsDebugLevel.None, HelpText = "Change Graphics API debug log level.")]
|
[Option("graphics-debug-level", Required = false, Default = GraphicsDebugLevel.None, HelpText = "Change Graphics API debug log level.")]
|
||||||
public GraphicsDebugLevel LoggingGraphicsDebugLevel { get; set; }
|
public GraphicsDebugLevel LoggingGraphicsDebugLevel { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -29,11 +29,6 @@
|
|||||||
<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.
|
||||||
See:
|
See:
|
||||||
@@ -51,10 +46,14 @@
|
|||||||
<PackageReference Include="Avalonia.Diagnostics" Condition="'$(Configuration)'=='Debug'" />
|
<PackageReference Include="Avalonia.Diagnostics" Condition="'$(Configuration)'=='Debug'" />
|
||||||
<PackageReference Include="Avalonia.Controls.DataGrid" />
|
<PackageReference Include="Avalonia.Controls.DataGrid" />
|
||||||
<PackageReference Include="Avalonia.Markup.Xaml.Loader" />
|
<PackageReference Include="Avalonia.Markup.Xaml.Loader" />
|
||||||
|
<PackageReference Include="SharpCompress" />
|
||||||
<PackageReference Include="Svg.Controls.Avalonia" />
|
<PackageReference Include="Svg.Controls.Avalonia" />
|
||||||
<PackageReference Include="Svg.Controls.Skia.Avalonia" />
|
<PackageReference Include="Svg.Controls.Skia.Avalonia" />
|
||||||
|
<PackageReference Include="SkiaSharp.NativeAssets.Win32" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'" />
|
||||||
|
<PackageReference Include="SkiaSharp.NativeAssets.macOS" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'win-arm64'" />
|
||||||
|
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Condition="'$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'win-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'" />
|
||||||
<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" />
|
||||||
@@ -62,7 +61,7 @@
|
|||||||
<PackageReference Include="Projektanker.Icons.Avalonia.FontAwesome" />
|
<PackageReference Include="Projektanker.Icons.Avalonia.FontAwesome" />
|
||||||
<PackageReference Include="Projektanker.Icons.Avalonia.MaterialDesign" />
|
<PackageReference Include="Projektanker.Icons.Avalonia.MaterialDesign" />
|
||||||
<PackageReference Include="OpenTK.Core" />
|
<PackageReference Include="OpenTK.Core" />
|
||||||
<PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'" />
|
<PackageReference Include="Ryujinx.Audio.OpenAL" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'" />
|
||||||
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies.AllArch" />
|
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies.AllArch" />
|
||||||
<PackageReference Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'win-arm64'" />
|
<PackageReference Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'linux-arm64' AND '$(RuntimeIdentifier)' != 'win-x64' AND '$(RuntimeIdentifier)' != 'win-arm64'" />
|
||||||
<PackageReference Include="Ryujinx.UpdateClient" />
|
<PackageReference Include="Ryujinx.UpdateClient" />
|
||||||
@@ -73,7 +72,6 @@
|
|||||||
<PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" />
|
<PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" />
|
||||||
<PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" />
|
<PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" />
|
||||||
<PackageReference Include="SPB" />
|
<PackageReference Include="SPB" />
|
||||||
<PackageReference Include="SharpZipLib" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -174,9 +172,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>
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ using VSyncMode = Ryujinx.Common.Configuration.VSyncMode;
|
|||||||
|
|
||||||
namespace Ryujinx.Ava.Systems
|
namespace Ryujinx.Ava.Systems
|
||||||
{
|
{
|
||||||
internal class AppHost
|
internal class AppHost : IDisposable
|
||||||
{
|
{
|
||||||
private const int CursorHideIdleTime = 5; // Hide Cursor seconds.
|
private const int CursorHideIdleTime = 5; // Hide Cursor seconds.
|
||||||
private const float MaxResolutionScale = 4.0f; // Max resolution hotkeys can scale to before wrapping.
|
private const float MaxResolutionScale = 4.0f; // Max resolution hotkeys can scale to before wrapping.
|
||||||
@@ -438,7 +438,7 @@ namespace Ryujinx.Ava.Systems
|
|||||||
|
|
||||||
SaveBitmapAsPng(bitmapToSave, path);
|
SaveBitmapAsPng(bitmapToSave, path);
|
||||||
|
|
||||||
Logger.Notice.Print(LogClass.Application, $"Screenshot saved to {path}", "Screenshot");
|
Logger.Notice.Print(LogClass.Application, $"Screenshot saved to '{path}'.", "Screenshot");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -611,26 +611,39 @@ namespace Ryujinx.Ava.Systems
|
|||||||
|
|
||||||
_isActive = false;
|
_isActive = false;
|
||||||
|
|
||||||
// NOTE: The render loop is allowed to stay alive until the renderer itself is disposed, as it may handle resource dispose.
|
|
||||||
// We only need to wait for all commands submitted during the main gpu loop to be processed.
|
|
||||||
_gpuDoneEvent.WaitOne();
|
|
||||||
_gpuDoneEvent.Dispose();
|
|
||||||
|
|
||||||
DisplaySleep.Restore();
|
DisplaySleep.Restore();
|
||||||
|
|
||||||
NpadManager.Dispose();
|
NpadManager.Dispose();
|
||||||
TouchScreenManager.Dispose();
|
TouchScreenManager.Dispose();
|
||||||
Device.Dispose();
|
Device.Dispose();
|
||||||
|
|
||||||
DisposeGpu();
|
// NOTE: The render loop is allowed to stay alive until the renderer itself is disposed, as it may handle resource dispose.
|
||||||
|
// We only need to wait for all commands submitted during the main gpu loop to be processed.
|
||||||
|
// If the GPU has no work and is cancelled, we need to handle that as well.
|
||||||
|
|
||||||
|
WaitHandle.WaitAny(new[] { _gpuDoneEvent, _gpuCancellationTokenSource.Token.WaitHandle });
|
||||||
|
|
||||||
|
if (_renderingStarted)
|
||||||
|
{
|
||||||
|
// Waiting for work to be finished before we dispose.
|
||||||
|
Device.Gpu.WaitUntilGpuReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
_gpuDoneEvent.Dispose();
|
||||||
|
_gpuCancellationTokenSource.Dispose();
|
||||||
|
|
||||||
|
DisposeGpu();
|
||||||
AppExit?.Invoke(this, EventArgs.Empty);
|
AppExit?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Dispose()
|
// MUST be public to inherit from IDisposable
|
||||||
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (Device.Processes != null)
|
if (Device.Processes != null)
|
||||||
MainWindowViewModel.UpdateGameMetadata(Device.Processes.ActiveApplication.ProgramIdText, _playTimer.Elapsed);
|
{
|
||||||
|
MainWindowViewModel.UpdateGameMetadata(Device.Processes.ActiveApplication?.ProgramIdText,
|
||||||
|
_playTimer.Elapsed);
|
||||||
|
}
|
||||||
|
|
||||||
ConfigurationState.Instance.System.IgnoreMissingServices.Event -= UpdateIgnoreMissingServicesState;
|
ConfigurationState.Instance.System.IgnoreMissingServices.Event -= UpdateIgnoreMissingServicesState;
|
||||||
ConfigurationState.Instance.Graphics.AspectRatio.Event -= UpdateAspectRatioState;
|
ConfigurationState.Instance.Graphics.AspectRatio.Event -= UpdateAspectRatioState;
|
||||||
@@ -646,7 +659,6 @@ namespace Ryujinx.Ava.Systems
|
|||||||
_topLevel.PointerExited -= TopLevel_PointerExited;
|
_topLevel.PointerExited -= TopLevel_PointerExited;
|
||||||
|
|
||||||
_gpuCancellationTokenSource.Cancel();
|
_gpuCancellationTokenSource.Cancel();
|
||||||
_gpuCancellationTokenSource.Dispose();
|
|
||||||
|
|
||||||
_chrono.Stop();
|
_chrono.Stop();
|
||||||
_playTimer.Stop();
|
_playTimer.Stop();
|
||||||
@@ -672,6 +684,12 @@ namespace Ryujinx.Ava.Systems
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// No use waiting on something that never started work
|
||||||
|
if (_renderingStarted)
|
||||||
|
{
|
||||||
|
Device.Gpu.WaitUntilGpuReady();
|
||||||
|
}
|
||||||
|
|
||||||
Device.DisposeGpu();
|
Device.DisposeGpu();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -686,7 +704,7 @@ namespace Ryujinx.Ava.Systems
|
|||||||
_cursorState = CursorStates.ForceChangeCursor;
|
_cursorState = CursorStates.ForceChangeCursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> LoadGuestApplication(BlitStruct<ApplicationControlProperty>? customNacpData = null)
|
public async Task LoadGuestApplication(CancellationTokenSource cts, BlitStruct<ApplicationControlProperty>? customNacpData = null)
|
||||||
{
|
{
|
||||||
DiscordIntegrationModule.GuestAppStartedAt = Timestamps.Now;
|
DiscordIntegrationModule.GuestAppStartedAt = Timestamps.Now;
|
||||||
|
|
||||||
@@ -715,7 +733,8 @@ namespace Ryujinx.Ava.Systems
|
|||||||
await UserErrorDialog.ShowUserErrorDialog(userError);
|
await UserErrorDialog.ShowUserErrorDialog(userError);
|
||||||
Device.Dispose();
|
Device.Dispose();
|
||||||
|
|
||||||
return false;
|
cts.Cancel();
|
||||||
|
throw new OperationCanceledException(cts.Token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -724,10 +743,11 @@ namespace Ryujinx.Ava.Systems
|
|||||||
await UserErrorDialog.ShowUserErrorDialog(userError);
|
await UserErrorDialog.ShowUserErrorDialog(userError);
|
||||||
Device.Dispose();
|
Device.Dispose();
|
||||||
|
|
||||||
return false;
|
cts.Cancel();
|
||||||
|
throw new OperationCanceledException(cts.Token);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tell the user that we installed a firmware for them.
|
// Tell the user that we installed firmware for them.
|
||||||
if (userError is UserError.NoFirmware)
|
if (userError is UserError.NoFirmware)
|
||||||
{
|
{
|
||||||
firmwareVersion = ContentManager.GetCurrentFirmwareVersion();
|
firmwareVersion = ContentManager.GetCurrentFirmwareVersion();
|
||||||
@@ -747,7 +767,8 @@ namespace Ryujinx.Ava.Systems
|
|||||||
await UserErrorDialog.ShowUserErrorDialog(userError);
|
await UserErrorDialog.ShowUserErrorDialog(userError);
|
||||||
Device.Dispose();
|
Device.Dispose();
|
||||||
|
|
||||||
return false;
|
cts.Cancel();
|
||||||
|
throw new OperationCanceledException(cts.Token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -762,7 +783,8 @@ namespace Ryujinx.Ava.Systems
|
|||||||
{
|
{
|
||||||
Device.Dispose();
|
Device.Dispose();
|
||||||
|
|
||||||
return false;
|
cts.Cancel();
|
||||||
|
throw new OperationCanceledException(cts.Token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (Directory.Exists(ApplicationPath))
|
else if (Directory.Exists(ApplicationPath))
|
||||||
@@ -782,20 +804,24 @@ namespace Ryujinx.Ava.Systems
|
|||||||
|
|
||||||
if (!Device.LoadCart(ApplicationPath, romFsFiles[0]))
|
if (!Device.LoadCart(ApplicationPath, romFsFiles[0]))
|
||||||
{
|
{
|
||||||
|
await ContentDialogHelper.CreateErrorDialog(
|
||||||
|
"Please specify an unpacked game directory with a valid exefs or NSO/NRO.");
|
||||||
Device.Dispose();
|
Device.Dispose();
|
||||||
|
|
||||||
return false;
|
cts.Cancel();
|
||||||
|
throw new OperationCanceledException(cts.Token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.Info?.Print(LogClass.Application, "Loading as cart WITHOUT RomFS.");
|
Logger.Info?.Print(LogClass.Application, "Loading as cart WITHOUT RomFS.");
|
||||||
|
|
||||||
if (!Device.LoadCart(ApplicationPath))
|
if (!Device.LoadCart(ApplicationPath))
|
||||||
{
|
{
|
||||||
|
await ContentDialogHelper.CreateErrorDialog(
|
||||||
|
"Please specify an unpacked game directory with a valid exefs or NSO/NRO.");
|
||||||
Device.Dispose();
|
Device.Dispose();
|
||||||
|
cts.Cancel();
|
||||||
return false;
|
throw new OperationCanceledException(cts.Token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -813,7 +839,8 @@ namespace Ryujinx.Ava.Systems
|
|||||||
{
|
{
|
||||||
Device.Dispose();
|
Device.Dispose();
|
||||||
|
|
||||||
return false;
|
cts.Cancel();
|
||||||
|
throw new OperationCanceledException(cts.Token);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -826,7 +853,8 @@ namespace Ryujinx.Ava.Systems
|
|||||||
{
|
{
|
||||||
Device.Dispose();
|
Device.Dispose();
|
||||||
|
|
||||||
return false;
|
cts.Cancel();
|
||||||
|
throw new OperationCanceledException(cts.Token);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -840,7 +868,8 @@ namespace Ryujinx.Ava.Systems
|
|||||||
{
|
{
|
||||||
Device.Dispose();
|
Device.Dispose();
|
||||||
|
|
||||||
return false;
|
cts.Cancel();
|
||||||
|
throw new OperationCanceledException(cts.Token);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -855,7 +884,8 @@ namespace Ryujinx.Ava.Systems
|
|||||||
{
|
{
|
||||||
Device.Dispose();
|
Device.Dispose();
|
||||||
|
|
||||||
return false;
|
cts.Cancel();
|
||||||
|
throw new OperationCanceledException(cts.Token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (ArgumentOutOfRangeException)
|
catch (ArgumentOutOfRangeException)
|
||||||
@@ -864,7 +894,8 @@ namespace Ryujinx.Ava.Systems
|
|||||||
|
|
||||||
Device.Dispose();
|
Device.Dispose();
|
||||||
|
|
||||||
return false;
|
cts.Cancel();
|
||||||
|
throw new OperationCanceledException(cts.Token);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -873,19 +904,18 @@ namespace Ryujinx.Ava.Systems
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.Application, "Please specify a valid XCI/NCA/NSP/PFS0/NRO file.");
|
Logger.Warning?.Print(LogClass.Application, "Please specify a valid XCI/NCA/NSP/PFS0/NSO/NRO file.");
|
||||||
|
|
||||||
Device.Dispose();
|
Device.Dispose();
|
||||||
|
|
||||||
return false;
|
cts.Cancel();
|
||||||
|
throw new OperationCanceledException(cts.Token);
|
||||||
}
|
}
|
||||||
|
|
||||||
ApplicationLibrary.LoadAndSaveMetaData(Device.Processes.ActiveApplication.ProgramIdText,
|
ApplicationLibrary.LoadAndSaveMetaData(Device.Processes.ActiveApplication.ProgramIdText,
|
||||||
appMetadata => appMetadata.UpdatePreGame()
|
appMetadata => appMetadata.UpdatePreGame()
|
||||||
);
|
);
|
||||||
_playTimer.Start();
|
_playTimer.Start();
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Resume()
|
internal void Resume()
|
||||||
@@ -895,7 +925,7 @@ namespace Ryujinx.Ava.Systems
|
|||||||
_viewModel.IsPaused = false;
|
_viewModel.IsPaused = false;
|
||||||
_playTimer.Start();
|
_playTimer.Start();
|
||||||
_viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version, !ConfigurationState.Instance.ShowOldUI);
|
_viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version, !ConfigurationState.Instance.ShowOldUI);
|
||||||
Logger.Info?.Print(LogClass.Emulation, "Emulation was resumed");
|
Logger.Info?.Print(LogClass.Emulation, "Emulation was resumed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Pause()
|
internal void Pause()
|
||||||
@@ -905,7 +935,7 @@ namespace Ryujinx.Ava.Systems
|
|||||||
_viewModel.IsPaused = true;
|
_viewModel.IsPaused = true;
|
||||||
_playTimer.Stop();
|
_playTimer.Stop();
|
||||||
_viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version, !ConfigurationState.Instance.ShowOldUI, LocaleManager.Instance[LocaleKeys.Paused]);
|
_viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version, !ConfigurationState.Instance.ShowOldUI, LocaleManager.Instance[LocaleKeys.Paused]);
|
||||||
Logger.Info?.Print(LogClass.Emulation, "Emulation was paused");
|
Logger.Info?.Print(LogClass.Emulation, "Emulation was paused.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitEmulatedSwitch()
|
private void InitEmulatedSwitch()
|
||||||
@@ -1065,49 +1095,56 @@ namespace Ryujinx.Ava.Systems
|
|||||||
|
|
||||||
Device.Gpu.Renderer.RunLoop(() =>
|
Device.Gpu.Renderer.RunLoop(() =>
|
||||||
{
|
{
|
||||||
Device.Gpu.SetGpuThread();
|
try
|
||||||
Device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token);
|
|
||||||
|
|
||||||
_renderer.Window.ChangeVSyncMode(Device.VSyncMode);
|
|
||||||
|
|
||||||
while (_isActive)
|
|
||||||
{
|
{
|
||||||
_ticks += _chrono.ElapsedTicks;
|
Device.Gpu.SetGpuThread();
|
||||||
|
Device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token);
|
||||||
|
|
||||||
_chrono.Restart();
|
_renderer.Window.ChangeVSyncMode(Device.VSyncMode);
|
||||||
|
|
||||||
if (Device.WaitFifo())
|
while (_isActive)
|
||||||
{
|
{
|
||||||
Device.Statistics.RecordFifoStart();
|
_ticks += _chrono.ElapsedTicks;
|
||||||
Device.ProcessFrame();
|
|
||||||
Device.Statistics.RecordFifoEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
while (Device.ConsumeFrameAvailable())
|
_chrono.Restart();
|
||||||
{
|
|
||||||
if (!_renderingStarted)
|
if (Device.WaitFifo())
|
||||||
{
|
{
|
||||||
_renderingStarted = true;
|
Device.Statistics.RecordFifoStart();
|
||||||
_viewModel.SwitchToRenderer(false);
|
Device.ProcessFrame();
|
||||||
InitStatus();
|
Device.Statistics.RecordFifoEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
Device.PresentFrame(() => (RendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.SwapBuffers());
|
while (Device.ConsumeFrameAvailable())
|
||||||
}
|
{
|
||||||
|
if (!_renderingStarted)
|
||||||
|
{
|
||||||
|
_renderingStarted = true;
|
||||||
|
_viewModel.SwitchToRenderer(false);
|
||||||
|
InitStatus();
|
||||||
|
}
|
||||||
|
|
||||||
if (_ticks >= _ticksPerFrame)
|
Device.PresentFrame(() =>
|
||||||
{
|
(RendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.SwapBuffers());
|
||||||
UpdateStatus();
|
}
|
||||||
|
|
||||||
|
if (_ticks >= _ticksPerFrame)
|
||||||
|
{
|
||||||
|
UpdateStatus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
// Make sure all commands in the run loop are fully executed before leaving the loop.
|
|
||||||
if (Device.Gpu.Renderer is ThreadedRenderer threaded)
|
|
||||||
{
|
{
|
||||||
threaded.FlushThreadedCommands();
|
// Make sure all commands in the run loop are fully executed before leaving the loop.
|
||||||
|
if (Device.Gpu.Renderer is ThreadedRenderer threaded)
|
||||||
|
{
|
||||||
|
Logger.Info?.PrintMsg(LogClass.Gpu, "Flushing threaded commands...");
|
||||||
|
threaded.FlushThreadedCommands();
|
||||||
|
Logger.Info?.PrintMsg(LogClass.Gpu, "Flushed!");
|
||||||
|
}
|
||||||
|
_gpuDoneEvent.Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
_gpuDoneEvent.Set();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
(RendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(true);
|
(RendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(true);
|
||||||
|
|||||||
@@ -849,7 +849,8 @@ namespace Ryujinx.Ava.Systems.AppLibrary
|
|||||||
|
|
||||||
foreach (ApplicationData installedApplication in Applications.Items)
|
foreach (ApplicationData installedApplication in Applications.Items)
|
||||||
{
|
{
|
||||||
temporary += LoadAndSaveMetaData(installedApplication.IdString).TimePlayed;
|
// this should always exist... should...
|
||||||
|
temporary += LoadAndSaveMetaData(installedApplication.IdString).Value.TimePlayed;
|
||||||
}
|
}
|
||||||
|
|
||||||
TotalTimePlayed = temporary;
|
TotalTimePlayed = temporary;
|
||||||
@@ -1159,8 +1160,14 @@ namespace Ryujinx.Ava.Systems.AppLibrary
|
|||||||
ApplicationCountUpdated?.Invoke(null, e);
|
ApplicationCountUpdated?.Invoke(null, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ApplicationMetadata LoadAndSaveMetaData(string titleId, Action<ApplicationMetadata> modifyFunction = null)
|
public static Gommon.Optional<ApplicationMetadata> LoadAndSaveMetaData(string titleId, Action<ApplicationMetadata> modifyFunction = null)
|
||||||
{
|
{
|
||||||
|
if (titleId is null)
|
||||||
|
{
|
||||||
|
Logger.Warning?.PrintMsg(LogClass.Application, "Cannot save metadata because title ID is invalid.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
string metadataFolder = Path.Combine(AppDataManager.GamesDirPath, titleId, "gui");
|
string metadataFolder = Path.Combine(AppDataManager.GamesDirPath, titleId, "gui");
|
||||||
string metadataFile = Path.Combine(metadataFolder, "metadata.json");
|
string metadataFile = Path.Combine(metadataFolder, "metadata.json");
|
||||||
|
|
||||||
@@ -1168,6 +1175,7 @@ namespace Ryujinx.Ava.Systems.AppLibrary
|
|||||||
|
|
||||||
if (!File.Exists(metadataFile))
|
if (!File.Exists(metadataFile))
|
||||||
{
|
{
|
||||||
|
Logger.Info?.Print(LogClass.Application, $"Metadata file does not exist. Creating metadata for {titleId}...");
|
||||||
Directory.CreateDirectory(metadataFolder);
|
Directory.CreateDirectory(metadataFolder);
|
||||||
|
|
||||||
appMetadata = new ApplicationMetadata();
|
appMetadata = new ApplicationMetadata();
|
||||||
@@ -1177,12 +1185,12 @@ namespace Ryujinx.Ava.Systems.AppLibrary
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
Logger.Debug?.Print(LogClass.Application, $"Deserializing metadata for {titleId}...");
|
||||||
appMetadata = JsonHelper.DeserializeFromFile(metadataFile, _serializerContext.ApplicationMetadata);
|
appMetadata = JsonHelper.DeserializeFromFile(metadataFile, _serializerContext.ApplicationMetadata);
|
||||||
}
|
}
|
||||||
catch (JsonException)
|
catch (JsonException)
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.Application, $"Failed to parse metadata json for {titleId}. Loading defaults.");
|
Logger.Warning?.Print(LogClass.Application, $"Failed to parse metadata json for {titleId}. Loading defaults.");
|
||||||
|
|
||||||
appMetadata = new ApplicationMetadata();
|
appMetadata = new ApplicationMetadata();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1404,7 +1412,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 +1425,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())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current version of the file format
|
/// The current version of the file format
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const int CurrentVersion = 71;
|
public const int CurrentVersion = 72;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Version of the configuration file format
|
/// Version of the configuration file format
|
||||||
@@ -114,6 +114,11 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool LoggingEnableFsAccessLog { get; set; }
|
public bool LoggingEnableFsAccessLog { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enables printing network log messages
|
||||||
|
/// </summary>
|
||||||
|
public bool LoggingEnableNetLog { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enables log messages from Avalonia
|
/// Enables log messages from Avalonia
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
using Gommon;
|
using Gommon;
|
||||||
using Ryujinx.Ava.Systems.Configuration.System;
|
using Ryujinx.Ava.Systems.Configuration.System;
|
||||||
using Ryujinx.Ava.Systems.Configuration.UI;
|
using Ryujinx.Ava.Systems.Configuration.UI;
|
||||||
@@ -68,6 +68,7 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||||||
Logger.EnableTrace.Value = cff.LoggingEnableTrace;
|
Logger.EnableTrace.Value = cff.LoggingEnableTrace;
|
||||||
Logger.EnableGuest.Value = cff.LoggingEnableGuest;
|
Logger.EnableGuest.Value = cff.LoggingEnableGuest;
|
||||||
Logger.EnableFsAccessLog.Value = cff.LoggingEnableFsAccessLog;
|
Logger.EnableFsAccessLog.Value = cff.LoggingEnableFsAccessLog;
|
||||||
|
Logger.EnableNetLog.Value = cff.LoggingEnableNetLog;
|
||||||
Logger.FilteredClasses.Value = cff.LoggingFilteredClasses;
|
Logger.FilteredClasses.Value = cff.LoggingFilteredClasses;
|
||||||
Logger.GraphicsDebugLevel.Value = cff.LoggingGraphicsDebugLevel;
|
Logger.GraphicsDebugLevel.Value = cff.LoggingGraphicsDebugLevel;
|
||||||
|
|
||||||
|
|||||||
@@ -258,6 +258,11 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ReactiveObject<bool> EnableFsAccessLog { get; private set; }
|
public ReactiveObject<bool> EnableFsAccessLog { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enables printing network log messages
|
||||||
|
/// </summary>
|
||||||
|
public ReactiveObject<bool> EnableNetLog { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enables log messages from Avalonia
|
/// Enables log messages from Avalonia
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -289,6 +294,7 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||||||
EnableTrace = new ReactiveObject<bool>();
|
EnableTrace = new ReactiveObject<bool>();
|
||||||
EnableGuest = new ReactiveObject<bool>();
|
EnableGuest = new ReactiveObject<bool>();
|
||||||
EnableFsAccessLog = new ReactiveObject<bool>();
|
EnableFsAccessLog = new ReactiveObject<bool>();
|
||||||
|
EnableNetLog = new ReactiveObject<bool>();
|
||||||
EnableAvaloniaLog = new ReactiveObject<bool>();
|
EnableAvaloniaLog = new ReactiveObject<bool>();
|
||||||
FilteredClasses = new ReactiveObject<LogClass[]>();
|
FilteredClasses = new ReactiveObject<LogClass[]>();
|
||||||
EnableFileLog = new ReactiveObject<bool>();
|
EnableFileLog = new ReactiveObject<bool>();
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||||||
LoggingEnableTrace = Logger.EnableTrace,
|
LoggingEnableTrace = Logger.EnableTrace,
|
||||||
LoggingEnableGuest = Logger.EnableGuest,
|
LoggingEnableGuest = Logger.EnableGuest,
|
||||||
LoggingEnableFsAccessLog = Logger.EnableFsAccessLog,
|
LoggingEnableFsAccessLog = Logger.EnableFsAccessLog,
|
||||||
|
LoggingEnableNetLog = Logger.EnableNetLog,
|
||||||
LoggingEnableAvalonia = Logger.EnableAvaloniaLog,
|
LoggingEnableAvalonia = Logger.EnableAvaloniaLog,
|
||||||
LoggingFilteredClasses = Logger.FilteredClasses,
|
LoggingFilteredClasses = Logger.FilteredClasses,
|
||||||
LoggingGraphicsDebugLevel = Logger.GraphicsDebugLevel,
|
LoggingGraphicsDebugLevel = Logger.GraphicsDebugLevel,
|
||||||
@@ -176,6 +177,7 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||||||
Logger.EnableTrace.Value = false;
|
Logger.EnableTrace.Value = false;
|
||||||
Logger.EnableGuest.Value = true;
|
Logger.EnableGuest.Value = true;
|
||||||
Logger.EnableFsAccessLog.Value = false;
|
Logger.EnableFsAccessLog.Value = false;
|
||||||
|
Logger.EnableNetLog.Value = false;
|
||||||
Logger.EnableAvaloniaLog.Value = false;
|
Logger.EnableAvaloniaLog.Value = false;
|
||||||
Logger.FilteredClasses.Value = [];
|
Logger.FilteredClasses.Value = [];
|
||||||
Logger.GraphicsDebugLevel.Value = GraphicsDebugLevel.None;
|
Logger.GraphicsDebugLevel.Value = GraphicsDebugLevel.None;
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||||||
(_, e) => Logger.SetEnable(LogLevel.Guest, e.NewValue);
|
(_, e) => Logger.SetEnable(LogLevel.Guest, e.NewValue);
|
||||||
ConfigurationState.Instance.Logger.EnableFsAccessLog.Event +=
|
ConfigurationState.Instance.Logger.EnableFsAccessLog.Event +=
|
||||||
(_, e) => Logger.SetEnable(LogLevel.AccessLog, e.NewValue);
|
(_, e) => Logger.SetEnable(LogLevel.AccessLog, e.NewValue);
|
||||||
|
ConfigurationState.Instance.Logger.EnableNetLog.Event +=
|
||||||
|
(_, e) => Logger.SetEnable(LogLevel.NetLog, e.NewValue);
|
||||||
|
|
||||||
ConfigurationState.Instance.Logger.FilteredClasses.Event += (_, e) =>
|
ConfigurationState.Instance.Logger.FilteredClasses.Event += (_, e) =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ namespace Ryujinx.Ava.Systems
|
|||||||
|
|
||||||
public static void Use(Optional<string> titleId)
|
public static void Use(Optional<string> titleId)
|
||||||
{
|
{
|
||||||
if (titleId.TryGet(out string tid))
|
if (titleId.TryGet(out string tid) && Switch.Shared.Processes.ActiveApplication is not null)
|
||||||
SwitchToPlayingState(
|
SwitchToPlayingState(
|
||||||
ApplicationLibrary.LoadAndSaveMetaData(tid),
|
ApplicationLibrary.LoadAndSaveMetaData(tid),
|
||||||
Switch.Shared.Processes.ActiveApplication
|
Switch.Shared.Processes.ActiveApplication
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ namespace Ryujinx.Ava.Systems
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If build URL not found, assume no new update is available.
|
// If build URL not found, assume no new update is available.
|
||||||
if (_versionResponse.ArtifactUrl is null or "")
|
if (string.IsNullOrEmpty(_versionResponse.ArtifactUrl))
|
||||||
{
|
{
|
||||||
if (showVersionUpToDate)
|
if (showVersionUpToDate)
|
||||||
{
|
{
|
||||||
@@ -123,6 +123,8 @@ namespace Ryujinx.Ava.Systems
|
|||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_connectionCount = (int)_versionResponse.MaxConcurrency;
|
||||||
|
|
||||||
return (currentVersion, newVersion);
|
return (currentVersion, newVersion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using FluentAvalonia.UI.Controls;
|
using FluentAvalonia.UI.Controls;
|
||||||
using Gommon;
|
using Gommon;
|
||||||
using ICSharpCode.SharpZipLib.GZip;
|
|
||||||
using ICSharpCode.SharpZipLib.Tar;
|
|
||||||
using ICSharpCode.SharpZipLib.Zip;
|
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
using Ryujinx.Ava.UI.Helpers;
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
using Ryujinx.Ava.Utilities;
|
using Ryujinx.Ava.Utilities;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Helper;
|
using Ryujinx.Common.Helper;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using SharpCompress.Archives;
|
||||||
|
using SharpCompress.Compressors.Xz;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Formats.Tar;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
@@ -21,7 +22,6 @@ using System.Net.NetworkInformation;
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@@ -32,8 +32,8 @@ namespace Ryujinx.Ava.Systems
|
|||||||
private static readonly string _homeDir = AppDomain.CurrentDomain.BaseDirectory;
|
private static readonly string _homeDir = AppDomain.CurrentDomain.BaseDirectory;
|
||||||
private static readonly string _updateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update");
|
private static readonly string _updateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update");
|
||||||
private static readonly string _updatePublishDir = Path.Combine(_updateDir, "publish");
|
private static readonly string _updatePublishDir = Path.Combine(_updateDir, "publish");
|
||||||
private const int ConnectionCount = 4;
|
|
||||||
|
|
||||||
|
private static int _connectionCount = 1;
|
||||||
private static long _buildSize;
|
private static long _buildSize;
|
||||||
private static bool _updateSuccessful;
|
private static bool _updateSuccessful;
|
||||||
private static bool _running;
|
private static bool _running;
|
||||||
@@ -73,27 +73,6 @@ namespace Ryujinx.Ava.Systems
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch build size information to learn chunk sizes.
|
|
||||||
using HttpClient buildSizeClient = ConstructHttpClient();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
buildSizeClient.DefaultRequestHeaders.Add("Range", "bytes=0-0");
|
|
||||||
|
|
||||||
// GitLab instance is located in Ukraine. Connection times will vary across the world.
|
|
||||||
buildSizeClient.Timeout = TimeSpan.FromSeconds(10);
|
|
||||||
|
|
||||||
HttpResponseMessage message = await buildSizeClient.GetAsync(new Uri(_versionResponse.ArtifactUrl), HttpCompletionOption.ResponseHeadersRead);
|
|
||||||
|
|
||||||
_buildSize = message.Content.Headers.ContentRange.Length.Value;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logger.Warning?.Print(LogClass.Application, ex.Message);
|
|
||||||
Logger.Warning?.Print(LogClass.Application, "Couldn't determine build size for update, using single-threaded updater");
|
|
||||||
|
|
||||||
_buildSize = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
await Dispatcher.UIThread.InvokeAsync(async () =>
|
await Dispatcher.UIThread.InvokeAsync(async () =>
|
||||||
{
|
{
|
||||||
string newVersionString = ReleaseInformation.IsCanaryBuild
|
string newVersionString = ReleaseInformation.IsCanaryBuild
|
||||||
@@ -143,6 +122,14 @@ namespace Ryujinx.Ava.Systems
|
|||||||
|
|
||||||
Directory.CreateDirectory(_updateDir);
|
Directory.CreateDirectory(_updateDir);
|
||||||
|
|
||||||
|
// If we get a .zip url switch it to the preferred .7z file instead
|
||||||
|
// The update server still returns the .zip url by default for legacy support
|
||||||
|
downloadUrl = downloadUrl.Replace(".zip", ".7z");
|
||||||
|
|
||||||
|
// If we get a .tar.gz url switch it to the preferred .tar.xz file instead
|
||||||
|
// The update server still returns the .tar.gz url by default for legacy support
|
||||||
|
downloadUrl = downloadUrl.Replace(".tar.gz", ".tar.xz");
|
||||||
|
|
||||||
string updateFile = Path.Combine(_updateDir, "update.bin");
|
string updateFile = Path.Combine(_updateDir, "update.bin");
|
||||||
|
|
||||||
TaskDialog taskDialog = new()
|
TaskDialog taskDialog = new()
|
||||||
@@ -154,6 +141,27 @@ namespace Ryujinx.Ava.Systems
|
|||||||
XamlRoot = RyujinxApp.MainWindow,
|
XamlRoot = RyujinxApp.MainWindow,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Fetch build size information to learn chunk sizes.
|
||||||
|
using HttpClient buildSizeClient = ConstructHttpClient();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
buildSizeClient.DefaultRequestHeaders.Add("Range", "bytes=0-0");
|
||||||
|
|
||||||
|
// Forgejo instance is located in Ukraine. Connection times will vary across the world.
|
||||||
|
buildSizeClient.Timeout = TimeSpan.FromSeconds(10);
|
||||||
|
|
||||||
|
HttpResponseMessage message = await buildSizeClient.GetAsync(new Uri(_versionResponse.ArtifactUrl), HttpCompletionOption.ResponseHeadersRead);
|
||||||
|
|
||||||
|
_buildSize = message.Content.Headers.ContentRange.Length.Value;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.Application, ex.Message);
|
||||||
|
Logger.Warning?.Print(LogClass.Application, "Couldn't determine build size for update, using single-threaded updater");
|
||||||
|
|
||||||
|
_buildSize = -1;
|
||||||
|
}
|
||||||
|
|
||||||
taskDialog.Opened += (s, e) =>
|
taskDialog.Opened += (s, e) =>
|
||||||
{
|
{
|
||||||
if (_buildSize >= 0)
|
if (_buildSize >= 0)
|
||||||
@@ -234,22 +242,22 @@ namespace Ryujinx.Ava.Systems
|
|||||||
private static void DoUpdateWithMultipleThreads(TaskDialog taskDialog, string downloadUrl, string updateFile)
|
private static void DoUpdateWithMultipleThreads(TaskDialog taskDialog, string downloadUrl, string updateFile)
|
||||||
{
|
{
|
||||||
// Multi-Threaded Updater
|
// Multi-Threaded Updater
|
||||||
long chunkSize = _buildSize / ConnectionCount;
|
long chunkSize = _buildSize / _connectionCount;
|
||||||
long remainderChunk = _buildSize % ConnectionCount;
|
long remainderChunk = _buildSize % _connectionCount;
|
||||||
|
|
||||||
int completedRequests = 0;
|
int completedRequests = 0;
|
||||||
int totalProgressPercentage = 0;
|
int totalProgressPercentage = 0;
|
||||||
int[] progressPercentage = new int[ConnectionCount];
|
int[] progressPercentage = new int[_connectionCount];
|
||||||
|
|
||||||
List<byte[]> list = new(ConnectionCount);
|
List<byte[]> list = new(_connectionCount);
|
||||||
List<WebClient> webClients = new(ConnectionCount);
|
List<WebClient> webClients = new(_connectionCount);
|
||||||
|
|
||||||
for (int i = 0; i < ConnectionCount; i++)
|
for (int i = 0; i < _connectionCount; i++)
|
||||||
{
|
{
|
||||||
list.Add([]);
|
list.Add([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < ConnectionCount; i++)
|
for (int i = 0; i < _connectionCount; i++)
|
||||||
{
|
{
|
||||||
#pragma warning disable SYSLIB0014
|
#pragma warning disable SYSLIB0014
|
||||||
// TODO: WebClient is obsolete and need to be replaced with a more complex logic using HttpClient.
|
// TODO: WebClient is obsolete and need to be replaced with a more complex logic using HttpClient.
|
||||||
@@ -258,7 +266,7 @@ namespace Ryujinx.Ava.Systems
|
|||||||
|
|
||||||
webClients.Add(client);
|
webClients.Add(client);
|
||||||
|
|
||||||
if (i == ConnectionCount - 1)
|
if (i == _connectionCount - 1)
|
||||||
{
|
{
|
||||||
client.Headers.Add("Range", $"bytes={chunkSize * i}-{(chunkSize * (i + 1) - 1) + remainderChunk}");
|
client.Headers.Add("Range", $"bytes={chunkSize * i}-{(chunkSize * (i + 1) - 1) + remainderChunk}");
|
||||||
}
|
}
|
||||||
@@ -275,7 +283,7 @@ namespace Ryujinx.Ava.Systems
|
|||||||
Interlocked.Exchange(ref progressPercentage[index], args.ProgressPercentage);
|
Interlocked.Exchange(ref progressPercentage[index], args.ProgressPercentage);
|
||||||
Interlocked.Add(ref totalProgressPercentage, args.ProgressPercentage);
|
Interlocked.Add(ref totalProgressPercentage, args.ProgressPercentage);
|
||||||
|
|
||||||
taskDialog.SetProgressBarState(totalProgressPercentage / ConnectionCount, TaskDialogProgressState.Normal);
|
taskDialog.SetProgressBarState(totalProgressPercentage / _connectionCount, TaskDialogProgressState.Normal);
|
||||||
};
|
};
|
||||||
|
|
||||||
client.DownloadDataCompleted += (_, args) =>
|
client.DownloadDataCompleted += (_, args) =>
|
||||||
@@ -294,10 +302,10 @@ namespace Ryujinx.Ava.Systems
|
|||||||
list[index] = args.Result;
|
list[index] = args.Result;
|
||||||
Interlocked.Increment(ref completedRequests);
|
Interlocked.Increment(ref completedRequests);
|
||||||
|
|
||||||
if (Equals(completedRequests, ConnectionCount))
|
if (Equals(completedRequests, _connectionCount))
|
||||||
{
|
{
|
||||||
byte[] mergedFileBytes = new byte[_buildSize];
|
byte[] mergedFileBytes = new byte[_buildSize];
|
||||||
for (int connectionIndex = 0, destinationOffset = 0; connectionIndex < ConnectionCount; connectionIndex++)
|
for (int connectionIndex = 0, destinationOffset = 0; connectionIndex < _connectionCount; connectionIndex++)
|
||||||
{
|
{
|
||||||
Array.Copy(list[connectionIndex], 0, mergedFileBytes, destinationOffset, list[connectionIndex].Length);
|
Array.Copy(list[connectionIndex], 0, mergedFileBytes, destinationOffset, list[connectionIndex].Length);
|
||||||
destinationOffset += list[connectionIndex].Length;
|
destinationOffset += list[connectionIndex].Length;
|
||||||
@@ -402,73 +410,33 @@ namespace Ryujinx.Ava.Systems
|
|||||||
|
|
||||||
[SupportedOSPlatform("linux")]
|
[SupportedOSPlatform("linux")]
|
||||||
[SupportedOSPlatform("macos")]
|
[SupportedOSPlatform("macos")]
|
||||||
private static void ExtractTarGzipFile(TaskDialog taskDialog, string archivePath, string outputDirectoryPath)
|
private static void ExtractTarGzipFile(string archivePath, string outputDirectoryPath)
|
||||||
{
|
{
|
||||||
using FileStream inStream = File.OpenRead(archivePath);
|
using FileStream inStream = File.OpenRead(archivePath);
|
||||||
using GZipInputStream gzipStream = new(inStream);
|
using GZipStream gzipStream = new(inStream, CompressionMode.Decompress);
|
||||||
using TarInputStream tarStream = new(gzipStream, Encoding.ASCII);
|
|
||||||
|
|
||||||
TarEntry tarEntry;
|
TarFile.ExtractToDirectory(gzipStream, outputDirectoryPath, true);
|
||||||
|
|
||||||
while ((tarEntry = tarStream.GetNextEntry()) is not null)
|
|
||||||
{
|
|
||||||
if (tarEntry.IsDirectory)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
string outPath = Path.Combine(outputDirectoryPath, tarEntry.Name);
|
|
||||||
|
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(outPath));
|
|
||||||
|
|
||||||
using FileStream outStream = File.OpenWrite(outPath);
|
|
||||||
tarStream.CopyEntryContents(outStream);
|
|
||||||
|
|
||||||
File.SetUnixFileMode(outPath, (UnixFileMode)tarEntry.TarHeader.Mode);
|
|
||||||
File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc));
|
|
||||||
|
|
||||||
Dispatcher.UIThread.Post(() =>
|
|
||||||
{
|
|
||||||
if (tarEntry is null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
taskDialog.SetProgressBarState(GetPercentage(tarEntry.Size, inStream.Length), TaskDialogProgressState.Normal);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ExtractZipFile(TaskDialog taskDialog, string archivePath, string outputDirectoryPath)
|
[SupportedOSPlatform("linux")]
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
|
private static void ExtractTarXzipFile(string archivePath, string outputDirectoryPath)
|
||||||
{
|
{
|
||||||
using Stream inStream = File.OpenRead(archivePath);
|
using FileStream inStream = File.OpenRead(archivePath);
|
||||||
using ZipFile zipFile = new(inStream);
|
using XZStream gzipStream = new(inStream);
|
||||||
|
|
||||||
double count = 0;
|
TarFile.ExtractToDirectory(gzipStream, outputDirectoryPath, true);
|
||||||
foreach (ZipEntry zipEntry in zipFile)
|
}
|
||||||
{
|
|
||||||
count++;
|
|
||||||
if (zipEntry.IsDirectory)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
string outPath = Path.Combine(outputDirectoryPath, zipEntry.Name);
|
private static void ExtractZipFile(string archivePath, string outputDirectoryPath)
|
||||||
|
{
|
||||||
|
ZipFile.ExtractToDirectory(archivePath, outputDirectoryPath);
|
||||||
|
}
|
||||||
|
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(outPath));
|
private static void Extract7ZipFile(string archivePath, string outputDirectoryPath)
|
||||||
|
{
|
||||||
using Stream zipStream = zipFile.GetInputStream(zipEntry);
|
IArchive archive = ArchiveFactory.OpenArchive(archivePath);
|
||||||
using FileStream outStream = File.OpenWrite(outPath);
|
archive.WriteToDirectory(outputDirectoryPath);
|
||||||
|
|
||||||
zipStream.CopyTo(outStream);
|
|
||||||
|
|
||||||
File.SetLastWriteTime(outPath, DateTime.SpecifyKind(zipEntry.DateTime, DateTimeKind.Utc));
|
|
||||||
|
|
||||||
Dispatcher.UIThread.Post(() =>
|
|
||||||
{
|
|
||||||
taskDialog.SetProgressBarState(GetPercentage(count, zipFile.Count), TaskDialogProgressState.Normal);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void InstallUpdate(TaskDialog taskDialog, string updateFile)
|
private static void InstallUpdate(TaskDialog taskDialog, string updateFile)
|
||||||
@@ -479,17 +447,21 @@ namespace Ryujinx.Ava.Systems
|
|||||||
|
|
||||||
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||||
{
|
{
|
||||||
ExtractTarGzipFile(taskDialog, updateFile, _updateDir);
|
ExtractTarXzipFile(updateFile, _updateDir);
|
||||||
}
|
}
|
||||||
else if (OperatingSystem.IsWindows())
|
else if (OperatingSystem.IsWindows())
|
||||||
{
|
{
|
||||||
ExtractZipFile(taskDialog, updateFile, _updateDir);
|
Extract7ZipFile(updateFile, _updateDir);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The new decompression implementations don't have a way to show progress
|
||||||
|
// so the progressbar is just set to 100% after the decompression is done
|
||||||
|
taskDialog.SetProgressBarState(100, TaskDialogProgressState.Normal);
|
||||||
|
|
||||||
// Delete downloaded zip
|
// Delete downloaded zip
|
||||||
File.Delete(updateFile);
|
File.Delete(updateFile);
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,12 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||||||
internal partial class Win32NativeInterop
|
internal partial class Win32NativeInterop
|
||||||
{
|
{
|
||||||
internal const int GWLP_WNDPROC = -4;
|
internal const int GWLP_WNDPROC = -4;
|
||||||
|
internal const int GWL_STYLE = -16;
|
||||||
|
internal const int GWL_EXSTYLE = -20;
|
||||||
|
|
||||||
|
internal const uint WS_OVERLAPPEDWINDOW = 0x00CF0000;
|
||||||
|
internal const uint WS_POPUP = 0x80000000;
|
||||||
|
internal const uint WS_VISIBLE = 0x10000000;
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
public enum ClassStyles : uint
|
public enum ClassStyles : uint
|
||||||
@@ -107,9 +113,29 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||||||
nint hInstance,
|
nint hInstance,
|
||||||
nint lpParam);
|
nint lpParam);
|
||||||
|
|
||||||
|
[LibraryImport("user32.dll", SetLastError = true, EntryPoint = "GetWindowLongPtrW")]
|
||||||
|
public static partial nint GetWindowLongPtrW(nint hWnd, int nIndex);
|
||||||
|
|
||||||
[LibraryImport("user32.dll", SetLastError = true)]
|
[LibraryImport("user32.dll", SetLastError = true)]
|
||||||
public static partial nint SetWindowLongPtrW(nint hWnd, int nIndex, nint value);
|
public static partial nint SetWindowLongPtrW(nint hWnd, int nIndex, nint value);
|
||||||
|
|
||||||
|
[LibraryImport("user32.dll", SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
public static partial bool SetWindowPos(
|
||||||
|
nint hWnd,
|
||||||
|
nint hWndInsertAfter,
|
||||||
|
int x,
|
||||||
|
int y,
|
||||||
|
int cx,
|
||||||
|
int cy,
|
||||||
|
uint uFlags);
|
||||||
|
|
||||||
|
internal const uint SWP_NOZORDER = 0x0004;
|
||||||
|
internal const uint SWP_NOACTIVATE = 0x0010;
|
||||||
|
internal const uint SWP_FRAMECHANGED = 0x0020;
|
||||||
|
internal const uint SWP_NOMOVE = 0x0002;
|
||||||
|
internal const uint SWP_NOSIZE = 0x0001;
|
||||||
|
|
||||||
[LibraryImport("user32.dll", SetLastError = true)]
|
[LibraryImport("user32.dll", SetLastError = true)]
|
||||||
public static partial ushort GetAsyncKeyState(int nVirtKey);
|
public static partial ushort GetAsyncKeyState(int nVirtKey);
|
||||||
|
|
||||||
|
|||||||
@@ -57,8 +57,15 @@ namespace Ryujinx.Ava.UI.Models
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ApplicationMetadata appMetadata = ApplicationLibrary.LoadAndSaveMetaData(TitleIdString);
|
Gommon.Optional<ApplicationMetadata> appMetadata = ApplicationLibrary.LoadAndSaveMetaData(TitleIdString);
|
||||||
Title = appMetadata.Title ?? TitleIdString;
|
if (appMetadata != null)
|
||||||
|
{
|
||||||
|
Title = appMetadata.Value.Title ?? TitleIdString;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Title = "<INVALID>";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Task.Run(() =>
|
Task.Run(() =>
|
||||||
|
|||||||
@@ -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())
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user