mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2026-05-28 16:09:15 +00:00
Compare commits
71 Commits
Canary-1.3
...
Canary-1.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e756ad4556 | ||
|
|
58bd19a2f3 | ||
|
|
48888bd014 | ||
|
|
d307afc454 | ||
|
|
134453e62b | ||
|
|
298b6c3959 | ||
|
|
c96433eb13 | ||
|
|
c0078088dd | ||
|
|
dcac29680a | ||
|
|
ec07e51807 | ||
|
|
e7aa6775af | ||
|
|
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 |
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."
|
||||||
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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -35,9 +35,9 @@ jobs:
|
|||||||
|
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
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
|
||||||
|
|
||||||
@@ -46,13 +46,17 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
token: ${{ secrets.SETUP_GLI_TOKEN }}
|
token: ${{ secrets.SETUP_GLI_TOKEN }}
|
||||||
|
|
||||||
|
- name: Install 7zip
|
||||||
|
run: |
|
||||||
|
sudo apt update && sudo apt install -y 7zip
|
||||||
|
|
||||||
- name: Overwrite csc problem matcher
|
- name: Overwrite csc problem matcher
|
||||||
run: echo "::add-matcher::.forgejo/csc.json"
|
run: echo "::add-matcher::.forgejo/csc.json"
|
||||||
|
|
||||||
- name: Get version info
|
- name: Get version info
|
||||||
id: version_info
|
id: version_info
|
||||||
run: |
|
run: |
|
||||||
echo "result=$(gli get-next-version -c Stable -R)" >> $FORGEJO_OUTPUT
|
echo "result=$(gli get-next-version -c Canary -R)" >> $FORGEJO_OUTPUT
|
||||||
echo "git_short_hash=$(git rev-parse --short "${{ forgejo.sha }}")" >> $FORGEJO_OUTPUT
|
echo "git_short_hash=$(git rev-parse --short "${{ forgejo.sha }}")" >> $FORGEJO_OUTPUT
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
@@ -83,14 +87,24 @@ jobs:
|
|||||||
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
|
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'
|
if: forgejo.event_name == 'pull_request'
|
||||||
|
|
||||||
- name: Set executable bit
|
- name: Packing Windows builds
|
||||||
|
if: contains(matrix.platform.name, 'win')
|
||||||
run: |
|
run: |
|
||||||
chmod +x ./publish/Ryujinx ./publish/Ryujinx.sh
|
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
|
||||||
if: forgejo.event_name == 'pull_request' && contains(matrix.platform.name, 'linux')
|
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
|
- name: Build AppImage
|
||||||
if: forgejo.event_name == 'pull_request' && contains(matrix.platform.name, 'linux')
|
if: forgejo.event_name == 'pull_request' && contains(matrix.platform.name, 'linux')
|
||||||
run: |
|
run: |
|
||||||
|
chmod +x ./publish/Ryujinx ./publish/Ryujinx.sh
|
||||||
|
|
||||||
PLATFORM_NAME="${{ matrix.platform.name }}"
|
PLATFORM_NAME="${{ matrix.platform.name }}"
|
||||||
|
|
||||||
sudo apt update && sudo apt install -y zsync desktop-file-utils appstream libfuse2t64
|
sudo apt update && sudo apt install -y zsync desktop-file-utils appstream libfuse2t64
|
||||||
@@ -118,15 +132,8 @@ jobs:
|
|||||||
BUILDDIR=publish OUTDIR=publish_appimage distribution/linux/appimage/build-appimage.sh
|
BUILDDIR=publish OUTDIR=publish_appimage distribution/linux/appimage/build-appimage.sh
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Upload Ryujinx artifact
|
- name: Upload Ryujinx AppImage artifact
|
||||||
uses: actions/upload-artifact@v4
|
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: publish
|
|
||||||
if: forgejo.event_name == 'pull_request'
|
|
||||||
|
|
||||||
- name: Upload Ryujinx (AppImage) artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
if: forgejo.event_name == 'pull_request' && contains(matrix.platform.name, 'linux')
|
if: forgejo.event_name == 'pull_request' && contains(matrix.platform.name, 'linux')
|
||||||
with:
|
with:
|
||||||
name: ryujinx-${{ matrix.configuration }}-${{ steps.version_info.outputs.result }}+${{ steps.version_info.outputs.git_short_hash }}-${{ matrix.platform.zip_os_name }}-AppImage
|
name: ryujinx-${{ matrix.configuration }}-${{ steps.version_info.outputs.result }}+${{ steps.version_info.outputs.git_short_hash }}-${{ matrix.platform.zip_os_name }}-AppImage
|
||||||
@@ -141,9 +148,9 @@ jobs:
|
|||||||
configuration: [ Release ]
|
configuration: [ Release ]
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
@@ -190,7 +197,7 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Upload Ryujinx artifact
|
- name: Upload Ryujinx artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v5
|
||||||
with:
|
with:
|
||||||
name: ryujinx-${{ matrix.configuration }}-${{ steps.version_info.outputs.result }}+${{ steps.version_info.outputs.git_short_hash }}-macos_universal
|
name: ryujinx-${{ matrix.configuration }}-${{ steps.version_info.outputs.result }}+${{ steps.version_info.outputs.git_short_hash }}-macos_universal
|
||||||
path: "publish/*.tar.gz"
|
path: "publish/*.tar.gz"
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ 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
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
@@ -94,6 +95,7 @@ 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
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
@@ -215,7 +217,7 @@ jobs:
|
|||||||
- macos_release
|
- macos_release
|
||||||
- release
|
- release
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Install GLI
|
- name: Install GLI
|
||||||
uses: actions/setup-gli@v1
|
uses: actions/setup-gli@v1
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ jobs:
|
|||||||
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
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ 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
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
@@ -91,6 +92,7 @@ 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
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
@@ -141,9 +143,7 @@ jobs:
|
|||||||
|
|
||||||
macos_release:
|
macos_release:
|
||||||
name: Release MacOS universal
|
name: Release MacOS universal
|
||||||
runs-on: docker
|
runs-on: ubuntu-latest
|
||||||
container:
|
|
||||||
image: ghcr.io/catthehacker/ubuntu:act-latest
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
@@ -205,12 +205,12 @@ jobs:
|
|||||||
|
|
||||||
post_ci:
|
post_ci:
|
||||||
name: Post-CI Steps
|
name: Post-CI Steps
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-latest
|
||||||
needs:
|
needs:
|
||||||
- macos_release
|
- macos_release
|
||||||
- release
|
- release
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Install GLI
|
- name: Install GLI
|
||||||
uses: actions/setup-gli@v1
|
uses: actions/setup-gli@v1
|
||||||
|
|||||||
@@ -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,59 +3,65 @@
|
|||||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageVersion Include="Avalonia" Version="11.3.12" />
|
<PackageVersion Include="Avalonia" Version="11.3.15" />
|
||||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.12" />
|
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.13" />
|
||||||
<PackageVersion Include="Avalonia.Desktop" Version="11.3.12" />
|
<PackageVersion Include="Avalonia.Desktop" Version="11.3.15" />
|
||||||
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.12" />
|
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.15" />
|
||||||
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.3.12" />
|
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.3.15" />
|
||||||
<PackageVersion Include="Svg.Controls.Avalonia" Version="11.3.9.4" />
|
<PackageVersion Include="SharpCompress" Version="0.48.1" />
|
||||||
<PackageVersion Include="Svg.Controls.Skia.Avalonia" Version="11.3.9.4" />
|
<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="2026.320.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" Version="2.5.0" />
|
<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.129" />
|
<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.14.1" />
|
||||||
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
||||||
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
|
||||||
<PackageVersion Include="Silk.NET.Vulkan" Version="2.22.0" />
|
<PackageVersion Include="Silk.NET.Vulkan" Version="2.22.0" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.22.0" />
|
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.22.0" />
|
||||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.22.0" />
|
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.22.0" />
|
||||||
<PackageVersion Include="SkiaSharp" Version="2.88.9" />
|
<PackageVersion Include="SkiaSharp" Version="2.88.9" />
|
||||||
<PackageVersion Include="SkiaSharp.NativeAssets.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="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
|
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.1.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
</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)
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
Ryujinx is an open-source Nintendo Switch emulator, originally created by gdkchan, written in C#.
|
Ryujinx is an open-source Nintendo Switch emulator, originally created by gdkchan, written in C#.
|
||||||
This emulator aims at providing excellent accuracy and performance, a user-friendly interface and consistent builds.
|
This emulator aims at providing excellent accuracy and performance, a user-friendly interface and consistent builds.
|
||||||
It was written from scratch and development on the project began in September 2017.
|
It was written from scratch and development on the project began in September 2017.
|
||||||
Ryujinx is available on a self-managed <a href="https://github.com/Ryubing/forgejo" target="_blank">modified</a> <a href="https://forgejo.org/" target="_blank">Forgejo</a> instance under the <a href="https://git.ryujinx.app/projects/Ryubing/src/branch/master/LICENSE.txt" target="_blank">MIT license</a>.
|
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">
|
||||||
|
|||||||
10
Ryujinx.sln
10
Ryujinx.sln
@@ -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",
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
"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",
|
||||||
@@ -54,7 +54,7 @@
|
|||||||
"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",
|
||||||
@@ -79,7 +79,7 @@
|
|||||||
"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.",
|
||||||
|
|||||||
@@ -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",
|
||||||
@@ -579,7 +579,7 @@
|
|||||||
"ID": "MenuBarOptionsRestartEmulation",
|
"ID": "MenuBarOptionsRestartEmulation",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
"ar_SA": "",
|
"ar_SA": "",
|
||||||
"de_DE": "",
|
"de_DE": "Emulation neustarten",
|
||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Restart Emulation",
|
"en_US": "Restart Emulation",
|
||||||
"es_ES": "Reiniciar Emulación",
|
"es_ES": "Reiniciar Emulación",
|
||||||
@@ -596,7 +596,7 @@
|
|||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "",
|
"zh_CN": "重启模拟",
|
||||||
"zh_TW": "重新啟動模擬"
|
"zh_TW": "重新啟動模擬"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1054,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",
|
||||||
@@ -1354,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)",
|
||||||
@@ -1804,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",
|
||||||
@@ -1829,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",
|
||||||
@@ -1954,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:",
|
||||||
@@ -2254,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",
|
||||||
@@ -2279,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",
|
||||||
@@ -2604,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.",
|
||||||
@@ -2629,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",
|
||||||
@@ -2654,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",
|
||||||
@@ -2679,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.",
|
||||||
@@ -2704,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",
|
||||||
@@ -2729,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.",
|
||||||
@@ -2754,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",
|
||||||
@@ -2779,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",
|
||||||
@@ -2804,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",
|
||||||
@@ -2904,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",
|
||||||
@@ -2929,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",
|
||||||
@@ -3004,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}'",
|
||||||
@@ -3604,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",
|
||||||
@@ -4629,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",
|
||||||
@@ -4704,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:",
|
||||||
@@ -5154,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",
|
||||||
@@ -6100,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": {
|
||||||
@@ -6479,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.",
|
||||||
@@ -11346,7 +11371,7 @@
|
|||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "",
|
"uk_UA": "",
|
||||||
"zh_CN": "",
|
"zh_CN": "保存",
|
||||||
"zh_TW": "儲存"
|
"zh_TW": "儲存"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -12804,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",
|
||||||
@@ -14379,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.",
|
||||||
@@ -14404,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?",
|
||||||
@@ -16104,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\"...",
|
||||||
@@ -16654,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.",
|
||||||
@@ -16754,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.",
|
||||||
@@ -16829,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.",
|
||||||
@@ -17075,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": {
|
||||||
@@ -18729,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",
|
||||||
@@ -21279,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.",
|
||||||
@@ -21329,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.",
|
||||||
@@ -21425,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": "控制台将会在下次启动 Ryujinx 时可用。",
|
||||||
|
"zh_TW": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ID": "SettingsGpuBackendRestartMessage",
|
"ID": "SettingsGpuBackendRestartMessage",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
@@ -21979,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.",
|
||||||
@@ -22054,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.",
|
||||||
@@ -22679,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)",
|
||||||
@@ -22704,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.",
|
||||||
@@ -22729,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:",
|
||||||
@@ -22754,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ú.",
|
||||||
@@ -22779,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ú.",
|
||||||
@@ -22804,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)",
|
||||||
@@ -22829,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",
|
||||||
@@ -22854,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.",
|
||||||
@@ -22879,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",
|
||||||
@@ -22904,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.",
|
||||||
@@ -22929,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>\"",
|
||||||
@@ -22979,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)",
|
||||||
@@ -23029,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",
|
||||||
@@ -23129,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.",
|
||||||
@@ -24154,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",
|
||||||
@@ -24179,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!",
|
||||||
@@ -24229,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",
|
||||||
@@ -24254,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.",
|
||||||
@@ -24279,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",
|
||||||
@@ -24304,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",
|
||||||
@@ -24329,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...",
|
||||||
@@ -24354,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?",
|
||||||
@@ -24379,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}.",
|
||||||
@@ -24404,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",
|
||||||
@@ -24429,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",
|
||||||
@@ -24454,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",
|
||||||
@@ -24479,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",
|
||||||
@@ -24504,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",
|
||||||
@@ -24529,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",
|
||||||
@@ -24604,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).",
|
||||||
@@ -24629,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).",
|
||||||
@@ -24654,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}",
|
||||||
@@ -24679,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}):",
|
||||||
@@ -24704,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",
|
||||||
@@ -24729,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.",
|
||||||
@@ -24754,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",
|
||||||
@@ -24779,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.",
|
||||||
@@ -24804,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",
|
||||||
@@ -24829,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.",
|
||||||
@@ -24854,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",
|
||||||
@@ -24879,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.",
|
||||||
|
|||||||
@@ -1061,6 +1061,7 @@
|
|||||||
0100BCA016636000,"eBaseball Powerful Pro Yakyuu 2022",gpu;services-horizon;crash,nothing,2024-05-26 23:07:19
|
0100BCA016636000,"eBaseball Powerful Pro Yakyuu 2022",gpu;services-horizon;crash,nothing,2024-05-26 23:07:19
|
||||||
01001F20100B8000,"Eclipse: Edge of Light",,playable,2020-08-11 23:06:29
|
01001F20100B8000,"Eclipse: Edge of Light",,playable,2020-08-11 23:06:29
|
||||||
0100E0A0110F4000,"eCrossminton",,playable,2020-07-11 18:24:27
|
0100E0A0110F4000,"eCrossminton",,playable,2020-07-11 18:24:27
|
||||||
|
010054601D54C000,"Emio – The Smiling Man: Famicom Detective Club (DEMO)",demo,playable,2026-05-13 18:32:12
|
||||||
0100ABE00DB4E000,"Edna & Harvey: Harvey's New Eyes",nvdec,playable,2021-01-26 14:36:08
|
0100ABE00DB4E000,"Edna & Harvey: Harvey's New Eyes",nvdec,playable,2021-01-26 14:36:08
|
||||||
01004F000B716000,"Edna & Harvey: The Breakout – Anniversary Edition",crash;nvdec,ingame,2022-08-01 16:59:56
|
01004F000B716000,"Edna & Harvey: The Breakout – Anniversary Edition",crash;nvdec,ingame,2022-08-01 16:59:56
|
||||||
01002550129F0000,"Effie",,playable,2022-10-27 14:36:39
|
01002550129F0000,"Effie",,playable,2022-10-27 14:36:39
|
||||||
@@ -1204,7 +1205,7 @@
|
|||||||
01003B200E440000,"Five Nights at Freddy's: Sister Location",,playable,2023-10-06 09:00:58
|
01003B200E440000,"Five Nights at Freddy's: Sister Location",,playable,2023-10-06 09:00:58
|
||||||
010038200E088000,"Flan",crash;regression,ingame,2021-11-17 07:39:28
|
010038200E088000,"Flan",crash;regression,ingame,2021-11-17 07:39:28
|
||||||
01000A0004C50000,"FLASHBACK™",nvdec,playable,2020-05-14 13:57:29
|
01000A0004C50000,"FLASHBACK™",nvdec,playable,2020-05-14 13:57:29
|
||||||
0100C53004C52000,"Flat Heroes",gpu,ingame,2022-07-26 19:37:37
|
0100C53004C52000,"Flat Heroes",,playable,2026-02-27 17:00:00
|
||||||
0100B54012798000,"Flatland: Prologue",,playable,2020-12-11 20:41:12
|
0100B54012798000,"Flatland: Prologue",,playable,2020-12-11 20:41:12
|
||||||
0100307004B4C000,"Flinthook",online,playable,2021-03-25 20:42:29
|
0100307004B4C000,"Flinthook",online,playable,2021-03-25 20:42:29
|
||||||
010095A004040000,"Flip Wars",services;ldn-untested,ingame,2022-05-02 15:39:18
|
010095A004040000,"Flip Wars",services;ldn-untested,ingame,2022-05-02 15:39:18
|
||||||
@@ -1394,6 +1395,7 @@
|
|||||||
0100c3c012718000,"Grand Theft Auto: III – The Definitive Edition",gpu;UE4,ingame,2022-10-31 20:13:52
|
0100c3c012718000,"Grand Theft Auto: III – The Definitive Edition",gpu;UE4,ingame,2022-10-31 20:13:52
|
||||||
0100182014022000,"Grand Theft Auto: Vice City – The Definitive Edition",gpu;UE4,ingame,2022-10-31 20:13:52
|
0100182014022000,"Grand Theft Auto: Vice City – The Definitive Edition",gpu;UE4,ingame,2022-10-31 20:13:52
|
||||||
010065a014024000,"Grand Theft Auto: San Andreas – The Definitive Edition",gpu;UE4,ingame,2022-10-31 20:13:52
|
010065a014024000,"Grand Theft Auto: San Andreas – The Definitive Edition",gpu;UE4,ingame,2022-10-31 20:13:52
|
||||||
|
0100EB500D92E000,"GROOVE COASTER WAI WAI PARTY!!!!",gpu,ingame,2026-05-13 18:32:12
|
||||||
0100822012D76000,"HAAK",gpu,ingame,2023-02-19 14:31:05
|
0100822012D76000,"HAAK",gpu,ingame,2023-02-19 14:31:05
|
||||||
01007E100EFA8000,"Habroxia",,playable,2020-06-16 23:04:42
|
01007E100EFA8000,"Habroxia",,playable,2020-06-16 23:04:42
|
||||||
0100535012974000,"Hades",vulkan,playable,2022-10-05 10:45:21
|
0100535012974000,"Hades",vulkan,playable,2022-10-05 10:45:21
|
||||||
@@ -1832,6 +1834,7 @@
|
|||||||
010055200E87E000,"Metamorphosis",UE4;audout;gpu;nvdec,ingame,2021-06-16 16:18:11
|
010055200E87E000,"Metamorphosis",UE4;audout;gpu;nvdec,ingame,2021-06-16 16:18:11
|
||||||
0100D4900E82C000,"Metro 2033 Redux",gpu,ingame,2022-11-09 10:53:13
|
0100D4900E82C000,"Metro 2033 Redux",gpu,ingame,2022-11-09 10:53:13
|
||||||
0100F0400E850000,"Metro: Last Light Redux",slow;nvdec;vulkan-backend-bug,ingame,2023-11-01 11:53:52
|
0100F0400E850000,"Metro: Last Light Redux",slow;nvdec;vulkan-backend-bug,ingame,2023-11-01 11:53:52
|
||||||
|
010019A01E2F2000,"Metroid Prime 4: Beyond",,ingame,2026-05-13 18:32:12
|
||||||
010012101468C000,"Metroid Prime™ Remastered",gpu;Needs Update;vulkan-backend-bug;opengl-backend-bug,ingame,2024-05-07 22:48:15
|
010012101468C000,"Metroid Prime™ Remastered",gpu;Needs Update;vulkan-backend-bug;opengl-backend-bug,ingame,2024-05-07 22:48:15
|
||||||
010093801237C000,"Metroid™ Dread",,playable,2023-11-13 04:02:36
|
010093801237C000,"Metroid™ Dread",,playable,2023-11-13 04:02:36
|
||||||
0100A1200F20C000,"Midnight Evil",,playable,2022-10-18 22:55:19
|
0100A1200F20C000,"Midnight Evil",,playable,2022-10-18 22:55:19
|
||||||
@@ -1945,6 +1948,7 @@
|
|||||||
0100C3E00ACAA000,"Mutant Football League: Dynasty Edition",online-broken,playable,2022-08-05 17:01:51
|
0100C3E00ACAA000,"Mutant Football League: Dynasty Edition",online-broken,playable,2022-08-05 17:01:51
|
||||||
01004BE004A86000,"Mutant Mudds Collection",,playable,2022-08-05 17:11:38
|
01004BE004A86000,"Mutant Mudds Collection",,playable,2022-08-05 17:11:38
|
||||||
0100E6B00DEA4000,"Mutant Year Zero: Road to Eden - Deluxe Edition",nvdec;UE4,playable,2022-09-10 13:31:10
|
0100E6B00DEA4000,"Mutant Year Zero: Road to Eden - Deluxe Edition",nvdec;UE4,playable,2022-09-10 13:31:10
|
||||||
|
010037501F864000,"Mute Crimson DX",,ingame,2026-05-13 18:32:12
|
||||||
0100161009E5C000,"MX Nitro: Unleashed",,playable,2022-09-27 22:34:33
|
0100161009E5C000,"MX Nitro: Unleashed",,playable,2022-09-27 22:34:33
|
||||||
0100218011E7E000,"MX vs ATV All Out",nvdec;UE4;vulkan-backend-bug,playable,2022-10-25 19:51:46
|
0100218011E7E000,"MX vs ATV All Out",nvdec;UE4;vulkan-backend-bug,playable,2022-10-25 19:51:46
|
||||||
0100D940063A0000,"MXGP3 - The Official Motocross Videogame",UE4;gpu;nvdec,ingame,2020-12-16 14:00:20
|
0100D940063A0000,"MXGP3 - The Official Motocross Videogame",UE4;gpu;nvdec,ingame,2020-12-16 14:00:20
|
||||||
@@ -2268,6 +2272,7 @@
|
|||||||
010086F0064CE000,"Poi: Explorer Edition",nvdec,playable,2021-01-21 19:32:00
|
010086F0064CE000,"Poi: Explorer Edition",nvdec,playable,2021-01-21 19:32:00
|
||||||
0100EB6012FD2000,"Poison Control",,playable,2021-05-16 14:01:54
|
0100EB6012FD2000,"Poison Control",,playable,2021-05-16 14:01:54
|
||||||
010072400E04A000,"Pokémon Café ReMix",,playable,2021-08-17 20:00:04
|
010072400E04A000,"Pokémon Café ReMix",,playable,2021-08-17 20:00:04
|
||||||
|
01005B7008C52800,"Pokémon Champions",Needs Update;services;online-broke,menus,2026-05-13 18:32:12
|
||||||
010008c01e742000,"Pokémon Friends",crash;services,menus,2025-07-24 13:32:00
|
010008c01e742000,"Pokémon Friends",crash;services,menus,2025-07-24 13:32:00
|
||||||
01003D200BAA2000,"Pokémon Mystery Dungeon™: Rescue Team DX",mac-bug,playable,2024-01-21 00:16:32
|
01003D200BAA2000,"Pokémon Mystery Dungeon™: Rescue Team DX",mac-bug,playable,2024-01-21 00:16:32
|
||||||
01008DB008C2C000,"Pokémon Shield + Pokémon Shield Expansion Pass",deadlock;crash;online-broken;ldn-works;LAN,ingame,2024-08-12 07:20:22
|
01008DB008C2C000,"Pokémon Shield + Pokémon Shield Expansion Pass",deadlock;crash;online-broken;ldn-works;LAN,ingame,2024-08-12 07:20:22
|
||||||
@@ -2275,6 +2280,8 @@
|
|||||||
01009AD008C4C000,"Pokémon: Let's Go, Pikachu! demo",slow;demo,playable,2023-11-26 11:23:20
|
01009AD008C4C000,"Pokémon: Let's Go, Pikachu! demo",slow;demo,playable,2023-11-26 11:23:20
|
||||||
0100000011D90000,"Pokémon™ Brilliant Diamond",gpu;ldn-works,ingame,2024-08-28 13:26:35
|
0100000011D90000,"Pokémon™ Brilliant Diamond",gpu;ldn-works,ingame,2024-08-28 13:26:35
|
||||||
010018E011D92000,"Pokémon™ Shining Pearl",gpu;ldn-works,ingame,2024-08-28 13:26:35
|
010018E011D92000,"Pokémon™ Shining Pearl",gpu;ldn-works,ingame,2024-08-28 13:26:35
|
||||||
|
100554023408000,"Pokémon FireRed Version",crashes,nothing,2026-05-13 18:32:12
|
||||||
|
010034D02340E000,"Pokémon LeafGreen Version",crashes,nothing,2026-05-13 18:32:12
|
||||||
010015F008C54000,"Pokémon™ HOME",Needs Update;crash;services,menus,2020-12-06 06:01:51
|
010015F008C54000,"Pokémon™ HOME",Needs Update;crash;services,menus,2020-12-06 06:01:51
|
||||||
01001F5010DFA000,"Pokémon™ Legends: Arceus",gpu;Needs Update;ldn-works,ingame,2024-09-19 10:02:02
|
01001F5010DFA000,"Pokémon™ Legends: Arceus",gpu;Needs Update;ldn-works,ingame,2024-09-19 10:02:02
|
||||||
0100F43008C44000,"Pokémon™ Legends: Z-A",gpu;crash;ldn-works,ingame,2025-11-16 00:30:00
|
0100F43008C44000,"Pokémon™ Legends: Z-A",gpu;crash;ldn-works,ingame,2025-11-16 00:30:00
|
||||||
@@ -2866,7 +2873,7 @@
|
|||||||
0100277011F1A000,"Super Mario Bros.™ 35",online-broken,menus,2022-08-07 16:27:25
|
0100277011F1A000,"Super Mario Bros.™ 35",online-broken,menus,2022-08-07 16:27:25
|
||||||
010015100B514000,"Super Mario Bros.™ Wonder",amd-vendor-bug,playable,2024-09-06 13:21:21
|
010015100B514000,"Super Mario Bros.™ Wonder",amd-vendor-bug,playable,2024-09-06 13:21:21
|
||||||
01009B90006DC000,"Super Mario Maker™ 2",online-broken;ldn-broken,playable,2024-08-25 11:05:19
|
01009B90006DC000,"Super Mario Maker™ 2",online-broken;ldn-broken,playable,2024-08-25 11:05:19
|
||||||
0100000000010000,"Super Mario Odyssey™",nvdec;intel-vendor-bug;mac-bug,playable,2024-08-25 01:32:34
|
0100000000010000,"Super Mario Odyssey™",nvdec;intel-vendor-bug;mac-bug;amd-vendor-bug,playable,2026-05-13 18:32:12
|
||||||
010036B0034E4000,"Super Mario Party™",gpu;Needs Update;ldn-works,ingame,2024-06-21 05:10:16
|
010036B0034E4000,"Super Mario Party™",gpu;Needs Update;ldn-works,ingame,2024-06-21 05:10:16
|
||||||
0100965017338000,"Super Mario Party Jamboree",mac-bug;gpu,ingame,2025-02-17 02:09:20
|
0100965017338000,"Super Mario Party Jamboree",mac-bug;gpu,ingame,2025-02-17 02:09:20
|
||||||
0100BC0018138000,"Super Mario RPG™",gpu;audio;nvdec,ingame,2024-06-19 17:43:42
|
0100BC0018138000,"Super Mario RPG™",gpu;audio;nvdec,ingame,2024-06-19 17:43:42
|
||||||
@@ -3163,6 +3170,8 @@
|
|||||||
0100E2E00CB14000,"Tokyo School Life",,playable,2022-09-16 20:25:54
|
0100E2E00CB14000,"Tokyo School Life",,playable,2022-09-16 20:25:54
|
||||||
010024601BB16000,"Tomb Raider I-III Remastered Starring Lara Croft",gpu;opengl,ingame,2024-09-27 12:32:04
|
010024601BB16000,"Tomb Raider I-III Remastered Starring Lara Croft",gpu;opengl,ingame,2024-09-27 12:32:04
|
||||||
0100D7F01E49C000,"Tomba! Special Edition",services-horizon,nothing,2024-09-15 21:59:54
|
0100D7F01E49C000,"Tomba! Special Edition",services-horizon,nothing,2024-09-15 21:59:54
|
||||||
|
010051F0207B2000,"Tomodachi Life: Living the Dream",amd-vendor-bug;gpu;intel-vendor-bug;ldn-broken,ingame,2026-05-13 18:32:12
|
||||||
|
0100CA502552A000,"Tomodachi Life: Living the Dream – Welcome Edtion",amd-vendor-bug;demo,playable,2026-05-13 18:32:12
|
||||||
0100D400100F8000,"Tonight We Riot",,playable,2021-02-26 15:55:09
|
0100D400100F8000,"Tonight We Riot",,playable,2021-02-26 15:55:09
|
||||||
0100CC00102B4000,"Tony Hawk's™ Pro Skater™ 1 + 2",gpu;Needs Update,ingame,2024-09-24 08:18:14
|
0100CC00102B4000,"Tony Hawk's™ Pro Skater™ 1 + 2",gpu;Needs Update,ingame,2024-09-24 08:18:14
|
||||||
010093F00E818000,"Tools Up!",crash,ingame,2020-07-21 12:58:17
|
010093F00E818000,"Tools Up!",crash,ingame,2020-07-21 12:58:17
|
||||||
|
|||||||
|
@@ -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;
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ namespace Ryujinx.Common
|
|||||||
|
|
||||||
//Mario Franchise
|
//Mario Franchise
|
||||||
"010021d00812a000", // Arcade Archives VS. SUPER MARIO BROS.
|
"010021d00812a000", // Arcade Archives VS. SUPER MARIO BROS.
|
||||||
|
"01007fe0221d8000", // Hello, Mario!
|
||||||
"01006d0017f7a000", // Mario & Luigi: Brothership
|
"01006d0017f7a000", // Mario & Luigi: Brothership
|
||||||
"010003000e146000", // Mario & Sonic at the Olympic Games Tokyo 2020
|
"010003000e146000", // Mario & Sonic at the Olympic Games Tokyo 2020
|
||||||
"010067300059a000", // Mario + Rabbids: Kingdom Battle
|
"010067300059a000", // Mario + Rabbids: Kingdom Battle
|
||||||
@@ -70,6 +71,9 @@ namespace Ryujinx.Common
|
|||||||
"0100bde00862a000", // Mario Tennis Aces
|
"0100bde00862a000", // Mario Tennis Aces
|
||||||
"0100b99019412000", // Mario vs. Donkey Kong
|
"0100b99019412000", // Mario vs. Donkey Kong
|
||||||
"010049900f546000", // Super Mario 3D All-Stars
|
"010049900f546000", // Super Mario 3D All-Stars
|
||||||
|
"010049900f546001", // Super Mario 3D All-Stars | Super Mario 64
|
||||||
|
"010049900f546002", // Super Mario 3D All-Stars | Super Mario Sunshine
|
||||||
|
"010049900f546003", // Super Mario 3D All-Stars | Super Mario Galaxy
|
||||||
"010028600ebda000", // Super Mario 3D World + Bowser's Fury
|
"010028600ebda000", // Super Mario 3D World + Bowser's Fury
|
||||||
"010049900F546001", // Super Mario 64
|
"010049900F546001", // Super Mario 64
|
||||||
"0100ea80032ea000", // Super Mario Bros. U Deluxe
|
"0100ea80032ea000", // Super Mario Bros. U Deluxe
|
||||||
@@ -107,6 +111,11 @@ namespace Ryujinx.Common
|
|||||||
"0100187003a36000", // Pokémon: Let's Go Eevee!
|
"0100187003a36000", // Pokémon: Let's Go Eevee!
|
||||||
"010003f003a34000", // Pokémon: Let's Go Pikachu!
|
"010003f003a34000", // Pokémon: Let's Go Pikachu!
|
||||||
"0100f43008c44000", // Pokémon Legends: Z-A
|
"0100f43008c44000", // Pokémon Legends: Z-A
|
||||||
|
"0100554023408000", // Pokémon FireRed Version (EN)
|
||||||
|
"01006fa0233f8000", // Pokémon FireRed Version (JP)
|
||||||
|
"0100fd6023430000", // Pokémon LeafGreen Version (DE)
|
||||||
|
"0100f1e0233fa000", // Pokémon LeafGreen Version (JP)
|
||||||
|
"01005b7008c52000", // Pokémon Champions
|
||||||
|
|
||||||
//Splatoon Franchise
|
//Splatoon Franchise
|
||||||
"0100f8f0000a2000", // Splatoon 2 (EU)
|
"0100f8f0000a2000", // Splatoon 2 (EU)
|
||||||
@@ -116,13 +125,14 @@ namespace Ryujinx.Common
|
|||||||
"0100ba0018500000", // Splatoon 3: Splatfest World Premiere
|
"0100ba0018500000", // Splatoon 3: Splatfest World Premiere
|
||||||
|
|
||||||
//NSO Membership games
|
//NSO Membership games
|
||||||
|
"0100d870045b6000", // NES - Nintendo Switch Online
|
||||||
|
"01008d300c50c000", // SNES - Nintendo Switch Online
|
||||||
"0100c62011050000", // GB - Nintendo Switch Online
|
"0100c62011050000", // GB - Nintendo Switch Online
|
||||||
"010012f017576000", // GBA - Nintendo Switch Online
|
"010012f017576000", // GBA - Nintendo Switch Online
|
||||||
"0100c9a00ece6000", // N64 - Nintendo Switch Online
|
"0100c9a00ece6000", // N64 - Nintendo Switch Online
|
||||||
"0100e0601c632000", // N64 - Nintendo Switch Online 18+
|
"0100e0601c632000", // N64 - Nintendo Switch Online 18+
|
||||||
"0100d870045b6000", // NES - Nintendo Switch Online
|
|
||||||
"0100b3c014bda000", // SEGA Genesis - Nintendo Switch Online
|
"0100b3c014bda000", // SEGA Genesis - Nintendo Switch Online
|
||||||
"01008d300c50c000", // SNES - Nintendo Switch Online
|
"0100bfc01d976000", // Virtual Boy - Nintendo Switch Online
|
||||||
"0100ccf019c8c000", // F-ZERO 99
|
"0100ccf019c8c000", // F-ZERO 99
|
||||||
"0100ad9012510000", // PAC-MAN 99
|
"0100ad9012510000", // PAC-MAN 99
|
||||||
"010040600c5ce000", // Tetris 99
|
"010040600c5ce000", // Tetris 99
|
||||||
@@ -141,12 +151,17 @@ namespace Ryujinx.Common
|
|||||||
"0100704000B3A000", // Snipperclips
|
"0100704000B3A000", // Snipperclips
|
||||||
"01006a800016e000", // Super Smash Bros. Ultimate
|
"01006a800016e000", // Super Smash Bros. Ultimate
|
||||||
"0100a9400c9c2000", // Tokyo Mirage Sessions #FE Encore
|
"0100a9400c9c2000", // Tokyo Mirage Sessions #FE Encore
|
||||||
|
"0100ca502552a000", // Tomodachi Life: Living the Dream - Welcome Edition
|
||||||
|
"010051f0207b2000", // Tomodachi Life: Living the Dream
|
||||||
|
|
||||||
//Bayonetta Franchise
|
//Bayonetta Franchise
|
||||||
"010076f0049a2000", // Bayonetta
|
"010076f0049a2000", // Bayonetta
|
||||||
"01007960049a0000", // Bayonetta 2
|
"01007960049a0000", // Bayonetta 2
|
||||||
"01004a4010fea000", // Bayonetta 3
|
"01004a4010fea000", // Bayonetta 3
|
||||||
"0100cf5010fec000", // Bayonetta Origins: Cereza and the Lost Demon
|
"0100cf5010fec000", // Bayonetta Origins: Cereza and the Lost Demon
|
||||||
|
|
||||||
|
// Famicom Detective Club Franchise
|
||||||
|
"010054601d54c000", // Emio - The Smiling Man: Famicom Detective Series (DEMO)
|
||||||
|
|
||||||
//Persona Franchise
|
//Persona Franchise
|
||||||
"0100dcd01525a000", // Persona 3 Portable
|
"0100dcd01525a000", // Persona 3 Portable
|
||||||
@@ -171,7 +186,9 @@ namespace Ryujinx.Common
|
|||||||
"0100453019aa8000", // Xenoblade Chronicles: X Definitive Edition
|
"0100453019aa8000", // Xenoblade Chronicles: X Definitive Edition
|
||||||
|
|
||||||
//Misc Games
|
//Misc Games
|
||||||
|
"01003670066de000", // 36 Fragments of Midnight
|
||||||
"010056e00853a000", // A Hat in Time
|
"010056e00853a000", // A Hat in Time
|
||||||
|
"0100c9f00aaee000", // Ascendence
|
||||||
"0100fd1014726000", // Baldurs Gate: Dark Alliance
|
"0100fd1014726000", // Baldurs Gate: Dark Alliance
|
||||||
"01008c2019598000", // Bluey: The Video Game
|
"01008c2019598000", // Bluey: The Video Game
|
||||||
"010096f00ff22000", // Borderlands 2: Game of the Year Edition
|
"010096f00ff22000", // Borderlands 2: Game of the Year Edition
|
||||||
@@ -185,8 +202,10 @@ namespace Ryujinx.Common
|
|||||||
"010027400cdc6000", // Divinity Original 2 - Definitive Edition
|
"010027400cdc6000", // Divinity Original 2 - Definitive Edition
|
||||||
"01008c8012920000", // Dying Light Platinum Edition
|
"01008c8012920000", // Dying Light Platinum Edition
|
||||||
"0100d11013e6a000", // Eschatos
|
"0100d11013e6a000", // Eschatos
|
||||||
|
"01000490067ae000", // Frederic 2: Evil Strikes Back
|
||||||
"01001cc01b2d4000", // Goat Simulator 3
|
"01001cc01b2d4000", // Goat Simulator 3
|
||||||
"01003620068ea000", // Hand of Fate 2
|
"01003620068ea000", // Hand of Fate 2
|
||||||
|
"01007ac00e012000", // HEXAGRAVITY
|
||||||
"0100f7e00c70e000", // Hogwarts Legacy
|
"0100f7e00c70e000", // Hogwarts Legacy
|
||||||
"010013c00e930000", // Hollow Knight: Silksong
|
"010013c00e930000", // Hollow Knight: Silksong
|
||||||
"010085500130a000", // Lego City: Undercover
|
"010085500130a000", // Lego City: Undercover
|
||||||
@@ -196,6 +215,7 @@ namespace Ryujinx.Common
|
|||||||
"0100853015e86000", // No Man's Sky
|
"0100853015e86000", // No Man's Sky
|
||||||
"0100f85014ed0000", // No More Heroes
|
"0100f85014ed0000", // No More Heroes
|
||||||
"0100463014ed4000", // No More Heroes 2
|
"0100463014ed4000", // No More Heroes 2
|
||||||
|
"0100f7d00a1bc000", // NO THING
|
||||||
"0100e570094e8000", // Owlboy
|
"0100e570094e8000", // Owlboy
|
||||||
"01007bb017812000", // Portal
|
"01007bb017812000", // Portal
|
||||||
"0100abd01785c000", // Portal 2
|
"0100abd01785c000", // Portal 2
|
||||||
@@ -204,11 +224,14 @@ namespace Ryujinx.Common
|
|||||||
"01008e200c5c2000", // Muse Dash
|
"01008e200c5c2000", // Muse Dash
|
||||||
"01005ff002e2a000", // Rayman Legends
|
"01005ff002e2a000", // Rayman Legends
|
||||||
"01007820196a6000", // Red Dead Redemption
|
"01007820196a6000", // Red Dead Redemption
|
||||||
|
"01007a800d520000", // REFUNCT
|
||||||
"0100e8300a67a000", // Risk
|
"0100e8300a67a000", // Risk
|
||||||
"01002f7013224000", // Rune Factory 5
|
"01002f7013224000", // Rune Factory 5
|
||||||
"01008d100d43e000", // Saints Row IV
|
"01008d100d43e000", // Saints Row IV
|
||||||
"0100de600beee000", // Saints Row: The Third - The Full Package
|
"0100de600beee000", // Saints Row: The Third - The Full Package
|
||||||
"01001180021fa000", // Shovel Knight: Specter of Torment
|
"01001180021fa000", // Shovel Knight: Specter of Torment
|
||||||
|
"010079f00671c000", // Sparkle 2: Evo
|
||||||
|
"010077b00e046000", // Spyro: Reignited Trilogy
|
||||||
"0100e1D01eb2e000", // Squeakross: Home Squeak Home
|
"0100e1D01eb2e000", // Squeakross: Home Squeak Home
|
||||||
"0100e65002bb8000", // Stardew Valley
|
"0100e65002bb8000", // Stardew Valley
|
||||||
"0100d7a01b7a2000", // Star Wars: Bounty Hunter
|
"0100d7a01b7a2000", // Star Wars: Bounty Hunter
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
@@ -33,6 +34,14 @@ namespace Ryujinx.Cpu.LightningJit.Cache
|
|||||||
[SupportedOSPlatform("windows")]
|
[SupportedOSPlatform("windows")]
|
||||||
[LibraryImport("kernel32.dll", SetLastError = true)]
|
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||||||
public static partial nint FlushInstructionCache(nint hProcess, nint lpAddress, nuint dwSize);
|
public static partial nint FlushInstructionCache(nint hProcess, nint lpAddress, nuint dwSize);
|
||||||
|
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
|
[LibraryImport("libSystem.dylib", EntryPoint = "sys_icache_invalidate")]
|
||||||
|
internal static partial void SysICacheInvalidate(nint start, nuint len);
|
||||||
|
|
||||||
|
[SupportedOSPlatform("linux")]
|
||||||
|
[LibraryImport("libgcc_s.so.1", EntryPoint = "__clear_cache")]
|
||||||
|
internal static partial void ClearCache(nint begin, nint end);
|
||||||
|
|
||||||
public static void Initialize(IJitMemoryAllocator allocator)
|
public static void Initialize(IJitMemoryAllocator allocator)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
public readonly bool SupportsShaderBallot;
|
public readonly bool SupportsShaderBallot;
|
||||||
public readonly bool SupportsShaderBarrierDivergence;
|
public readonly bool SupportsShaderBarrierDivergence;
|
||||||
public readonly bool SupportsShaderFloat64;
|
public readonly bool SupportsShaderFloat64;
|
||||||
|
public readonly bool SupportsShaderNonUniformIndexing;
|
||||||
public readonly bool SupportsTextureGatherOffsets;
|
public readonly bool SupportsTextureGatherOffsets;
|
||||||
public readonly bool SupportsTextureShadowLod;
|
public readonly bool SupportsTextureShadowLod;
|
||||||
public readonly bool SupportsVertexStoreAndAtomics;
|
public readonly bool SupportsVertexStoreAndAtomics;
|
||||||
@@ -110,6 +111,7 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
bool supportsShaderBallot,
|
bool supportsShaderBallot,
|
||||||
bool supportsShaderBarrierDivergence,
|
bool supportsShaderBarrierDivergence,
|
||||||
bool supportsShaderFloat64,
|
bool supportsShaderFloat64,
|
||||||
|
bool supportsShaderNonUniformIndexing,
|
||||||
bool supportsTextureGatherOffsets,
|
bool supportsTextureGatherOffsets,
|
||||||
bool supportsTextureShadowLod,
|
bool supportsTextureShadowLod,
|
||||||
bool supportsVertexStoreAndAtomics,
|
bool supportsVertexStoreAndAtomics,
|
||||||
@@ -172,6 +174,7 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
SupportsShaderBallot = supportsShaderBallot;
|
SupportsShaderBallot = supportsShaderBallot;
|
||||||
SupportsShaderBarrierDivergence = supportsShaderBarrierDivergence;
|
SupportsShaderBarrierDivergence = supportsShaderBarrierDivergence;
|
||||||
SupportsShaderFloat64 = supportsShaderFloat64;
|
SupportsShaderFloat64 = supportsShaderFloat64;
|
||||||
|
SupportsShaderNonUniformIndexing = supportsShaderNonUniformIndexing;
|
||||||
SupportsTextureGatherOffsets = supportsTextureGatherOffsets;
|
SupportsTextureGatherOffsets = supportsTextureGatherOffsets;
|
||||||
SupportsTextureShadowLod = supportsTextureShadowLod;
|
SupportsTextureShadowLod = supportsTextureShadowLod;
|
||||||
SupportsVertexStoreAndAtomics = supportsVertexStoreAndAtomics;
|
SupportsVertexStoreAndAtomics = supportsVertexStoreAndAtomics;
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
private const ushort FileFormatVersionMajor = 1;
|
private const ushort FileFormatVersionMajor = 1;
|
||||||
private const ushort FileFormatVersionMinor = 2;
|
private const ushort FileFormatVersionMinor = 2;
|
||||||
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
||||||
private const uint CodeGenVersion = 7353;
|
private const uint CodeGenVersion = 7354;
|
||||||
|
|
||||||
private const string SharedTocFileName = "shared.toc";
|
private const string SharedTocFileName = "shared.toc";
|
||||||
private const string SharedDataFileName = "shared.data";
|
private const string SharedDataFileName = "shared.data";
|
||||||
|
|||||||
@@ -231,6 +231,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
|
|
||||||
public bool QueryHostSupportsShaderFloat64() => _context.Capabilities.SupportsShaderFloat64;
|
public bool QueryHostSupportsShaderFloat64() => _context.Capabilities.SupportsShaderFloat64;
|
||||||
|
|
||||||
|
public bool QueryHostSupportsShaderNonUniformIndexing() => _context.Capabilities.SupportsShaderNonUniformIndexing;
|
||||||
|
|
||||||
public bool QueryHostSupportsSnormBufferTextureFormat() => _context.Capabilities.SupportsSnormBufferTextureFormat;
|
public bool QueryHostSupportsSnormBufferTextureFormat() => _context.Capabilities.SupportsSnormBufferTextureFormat;
|
||||||
|
|
||||||
public bool QueryHostSupportsTextureGatherOffsets() => _context.Capabilities.SupportsTextureGatherOffsets;
|
public bool QueryHostSupportsTextureGatherOffsets() => _context.Capabilities.SupportsTextureGatherOffsets;
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -184,6 +184,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
supportsShaderBallot: HwCapabilities.SupportsShaderBallot,
|
supportsShaderBallot: HwCapabilities.SupportsShaderBallot,
|
||||||
supportsShaderBarrierDivergence: !(intelWindows || intelUnix),
|
supportsShaderBarrierDivergence: !(intelWindows || intelUnix),
|
||||||
supportsShaderFloat64: true,
|
supportsShaderFloat64: true,
|
||||||
|
supportsShaderNonUniformIndexing: false,
|
||||||
supportsTextureGatherOffsets: true,
|
supportsTextureGatherOffsets: true,
|
||||||
supportsTextureShadowLod: HwCapabilities.SupportsTextureShadowLod,
|
supportsTextureShadowLod: HwCapabilities.SupportsTextureShadowLod,
|
||||||
supportsVertexStoreAndAtomics: true,
|
supportsVertexStoreAndAtomics: true,
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -924,8 +924,8 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
GL.Disable(EnableCap.CullFace);
|
GL.Disable(EnableCap.CullFace);
|
||||||
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()
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
|
|
||||||
public bool IsMainFunction { get; private set; }
|
public bool IsMainFunction { get; private set; }
|
||||||
public bool MayHaveReturned { get; set; }
|
public bool MayHaveReturned { get; set; }
|
||||||
|
public bool WasNonUniformAccessDeclared { get; set; }
|
||||||
|
|
||||||
public CodeGenContext(
|
public CodeGenContext(
|
||||||
StructuredProgramInfo info,
|
StructuredProgramInfo info,
|
||||||
@@ -89,6 +90,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
GeneratorPool<Instruction> instPool,
|
GeneratorPool<Instruction> instPool,
|
||||||
GeneratorPool<LiteralInteger> integerPool) : base(SpirvVersionPacked, instPool, integerPool)
|
GeneratorPool<LiteralInteger> integerPool) : base(SpirvVersionPacked, instPool, integerPool)
|
||||||
{
|
{
|
||||||
|
WasNonUniformAccessDeclared = false;
|
||||||
|
|
||||||
Info = info;
|
Info = info;
|
||||||
AttributeUsage = parameters.AttributeUsage;
|
AttributeUsage = parameters.AttributeUsage;
|
||||||
Definitions = parameters.Definitions;
|
Definitions = parameters.Definitions;
|
||||||
|
|||||||
@@ -587,6 +587,23 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
return OperationResult.Invalid;
|
return OperationResult.Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void MarkNonUniform(CodeGenContext context, SpvInstruction inst)
|
||||||
|
{
|
||||||
|
if (context.HostCapabilities.SupportsShaderNonUniformIndexing)
|
||||||
|
{
|
||||||
|
if (!context.WasNonUniformAccessDeclared)
|
||||||
|
{
|
||||||
|
context.AddExtension("SPV_EXT_descriptor_indexing");
|
||||||
|
context.AddCapability(Capability.ShaderNonUniform);
|
||||||
|
context.AddCapability(Capability.SampledImageArrayNonUniformIndexing);
|
||||||
|
context.AddCapability(Capability.StorageImageArrayNonUniformIndexing);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Decorate(inst, Decoration.NonUniform);
|
||||||
|
context.WasNonUniformAccessDeclared = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static OperationResult GenerateImageAtomic(CodeGenContext context, AstOperation operation)
|
private static OperationResult GenerateImageAtomic(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
AstTextureOperation texOp = (AstTextureOperation)operation;
|
AstTextureOperation texOp = (AstTextureOperation)operation;
|
||||||
@@ -613,6 +630,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
SpvInstruction textureIndex = Src(AggregateType.S32);
|
SpvInstruction textureIndex = Src(AggregateType.S32);
|
||||||
|
|
||||||
image = context.AccessChain(imagePointerType, image, textureIndex);
|
image = context.AccessChain(imagePointerType, image, textureIndex);
|
||||||
|
MarkNonUniform(context, image);
|
||||||
}
|
}
|
||||||
|
|
||||||
int coordsCount = texOp.Type.Dimensions;
|
int coordsCount = texOp.Type.Dimensions;
|
||||||
@@ -683,15 +701,21 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
|
|
||||||
ImageDeclaration declaration = context.Images[texOp.GetTextureSetAndBinding()];
|
ImageDeclaration declaration = context.Images[texOp.GetTextureSetAndBinding()];
|
||||||
SpvInstruction image = declaration.Image;
|
SpvInstruction image = declaration.Image;
|
||||||
|
bool isIndexed = declaration.IsIndexed;
|
||||||
|
|
||||||
if (declaration.IsIndexed)
|
if (isIndexed)
|
||||||
{
|
{
|
||||||
SpvInstruction textureIndex = Src(AggregateType.S32);
|
SpvInstruction textureIndex = Src(AggregateType.S32);
|
||||||
|
|
||||||
image = context.AccessChain(declaration.ImagePointerType, image, textureIndex);
|
image = context.AccessChain(declaration.ImagePointerType, image, textureIndex);
|
||||||
|
MarkNonUniform(context, image);
|
||||||
}
|
}
|
||||||
|
|
||||||
image = context.Load(declaration.ImageType, image);
|
image = context.Load(declaration.ImageType, image);
|
||||||
|
if (isIndexed)
|
||||||
|
{
|
||||||
|
MarkNonUniform(context, image);
|
||||||
|
}
|
||||||
|
|
||||||
int coordsCount = texOp.Type.Dimensions;
|
int coordsCount = texOp.Type.Dimensions;
|
||||||
|
|
||||||
@@ -740,15 +764,21 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
|
|
||||||
ImageDeclaration declaration = context.Images[texOp.GetTextureSetAndBinding()];
|
ImageDeclaration declaration = context.Images[texOp.GetTextureSetAndBinding()];
|
||||||
SpvInstruction image = declaration.Image;
|
SpvInstruction image = declaration.Image;
|
||||||
|
bool isIndexed = declaration.IsIndexed;
|
||||||
|
|
||||||
if (declaration.IsIndexed)
|
if (isIndexed)
|
||||||
{
|
{
|
||||||
SpvInstruction textureIndex = Src(AggregateType.S32);
|
SpvInstruction textureIndex = Src(AggregateType.S32);
|
||||||
|
|
||||||
image = context.AccessChain(declaration.ImagePointerType, image, textureIndex);
|
image = context.AccessChain(declaration.ImagePointerType, image, textureIndex);
|
||||||
|
MarkNonUniform(context, image);
|
||||||
}
|
}
|
||||||
|
|
||||||
image = context.Load(declaration.ImageType, image);
|
image = context.Load(declaration.ImageType, image);
|
||||||
|
if (isIndexed)
|
||||||
|
{
|
||||||
|
MarkNonUniform(context, image);
|
||||||
|
}
|
||||||
|
|
||||||
int coordsCount = texOp.Type.Dimensions;
|
int coordsCount = texOp.Type.Dimensions;
|
||||||
|
|
||||||
@@ -1878,35 +1908,56 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
private static SpvInstruction GenerateSampledImageLoad(CodeGenContext context, AstTextureOperation texOp, SamplerDeclaration declaration, ref int srcIndex)
|
private static SpvInstruction GenerateSampledImageLoad(CodeGenContext context, AstTextureOperation texOp, SamplerDeclaration declaration, ref int srcIndex)
|
||||||
{
|
{
|
||||||
SpvInstruction image = declaration.Image;
|
SpvInstruction image = declaration.Image;
|
||||||
|
bool imageIndexed = declaration.IsIndexed;
|
||||||
|
|
||||||
if (declaration.IsIndexed)
|
if (imageIndexed)
|
||||||
{
|
{
|
||||||
SpvInstruction textureIndex = context.Get(AggregateType.S32, texOp.GetSource(srcIndex++));
|
SpvInstruction textureIndex = context.Get(AggregateType.S32, texOp.GetSource(srcIndex++));
|
||||||
|
|
||||||
image = context.AccessChain(declaration.SampledImagePointerType, image, textureIndex);
|
image = context.AccessChain(declaration.SampledImagePointerType, image, textureIndex);
|
||||||
|
MarkNonUniform(context, image);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (texOp.IsSeparate)
|
if (texOp.IsSeparate)
|
||||||
{
|
{
|
||||||
image = context.Load(declaration.ImageType, image);
|
image = context.Load(declaration.ImageType, image);
|
||||||
|
if (imageIndexed)
|
||||||
|
{
|
||||||
|
MarkNonUniform(context, image);
|
||||||
|
}
|
||||||
|
|
||||||
SamplerDeclaration samplerDeclaration = context.Samplers[texOp.GetSamplerSetAndBinding()];
|
SamplerDeclaration samplerDeclaration = context.Samplers[texOp.GetSamplerSetAndBinding()];
|
||||||
|
|
||||||
SpvInstruction sampler = samplerDeclaration.Image;
|
SpvInstruction sampler = samplerDeclaration.Image;
|
||||||
|
bool samplerIndexed = samplerDeclaration.IsIndexed;
|
||||||
|
|
||||||
if (samplerDeclaration.IsIndexed)
|
if (samplerIndexed)
|
||||||
{
|
{
|
||||||
SpvInstruction samplerIndex = context.Get(AggregateType.S32, texOp.GetSource(srcIndex++));
|
SpvInstruction samplerIndex = context.Get(AggregateType.S32, texOp.GetSource(srcIndex++));
|
||||||
|
|
||||||
sampler = context.AccessChain(samplerDeclaration.SampledImagePointerType, sampler, samplerIndex);
|
sampler = context.AccessChain(samplerDeclaration.SampledImagePointerType, sampler, samplerIndex);
|
||||||
|
MarkNonUniform(context, sampler);
|
||||||
}
|
}
|
||||||
|
|
||||||
sampler = context.Load(samplerDeclaration.ImageType, sampler);
|
sampler = context.Load(samplerDeclaration.ImageType, sampler);
|
||||||
|
if (samplerIndexed)
|
||||||
|
{
|
||||||
|
MarkNonUniform(context, sampler);
|
||||||
|
}
|
||||||
|
|
||||||
image = context.SampledImage(declaration.SampledImageType, image, sampler);
|
image = context.SampledImage(declaration.SampledImageType, image, sampler);
|
||||||
|
if (imageIndexed || samplerIndexed)
|
||||||
|
{
|
||||||
|
MarkNonUniform(context, image);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
image = context.Load(declaration.SampledImageType, image);
|
image = context.Load(declaration.SampledImageType, image);
|
||||||
|
if (imageIndexed)
|
||||||
|
{
|
||||||
|
MarkNonUniform(context, image);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return image;
|
return image;
|
||||||
|
|||||||
@@ -336,6 +336,10 @@ namespace Ryujinx.Graphics.Shader
|
|||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
bool QueryHostSupportsShaderNonUniformIndexing()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Queries host GPU support for signed normalized buffer texture formats.
|
/// Queries host GPU support for signed normalized buffer texture formats.
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
public readonly bool SupportsShaderBallot;
|
public readonly bool SupportsShaderBallot;
|
||||||
public readonly bool SupportsShaderBarrierDivergence;
|
public readonly bool SupportsShaderBarrierDivergence;
|
||||||
public readonly bool SupportsShaderFloat64;
|
public readonly bool SupportsShaderFloat64;
|
||||||
|
public readonly bool SupportsShaderNonUniformIndexing;
|
||||||
public readonly bool SupportsTextureShadowLod;
|
public readonly bool SupportsTextureShadowLod;
|
||||||
public readonly bool SupportsViewportMask;
|
public readonly bool SupportsViewportMask;
|
||||||
|
|
||||||
@@ -20,6 +21,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
bool supportsShaderBallot,
|
bool supportsShaderBallot,
|
||||||
bool supportsShaderBarrierDivergence,
|
bool supportsShaderBarrierDivergence,
|
||||||
bool supportsShaderFloat64,
|
bool supportsShaderFloat64,
|
||||||
|
bool supportsShaderNonUniformIndexing,
|
||||||
bool supportsTextureShadowLod,
|
bool supportsTextureShadowLod,
|
||||||
bool supportsViewportMask)
|
bool supportsViewportMask)
|
||||||
{
|
{
|
||||||
@@ -30,6 +32,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
SupportsShaderBallot = supportsShaderBallot;
|
SupportsShaderBallot = supportsShaderBallot;
|
||||||
SupportsShaderBarrierDivergence = supportsShaderBarrierDivergence;
|
SupportsShaderBarrierDivergence = supportsShaderBarrierDivergence;
|
||||||
SupportsShaderFloat64 = supportsShaderFloat64;
|
SupportsShaderFloat64 = supportsShaderFloat64;
|
||||||
|
SupportsShaderNonUniformIndexing = supportsShaderNonUniformIndexing;
|
||||||
SupportsTextureShadowLod = supportsTextureShadowLod;
|
SupportsTextureShadowLod = supportsTextureShadowLod;
|
||||||
SupportsViewportMask = supportsViewportMask;
|
SupportsViewportMask = supportsViewportMask;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -364,6 +364,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
GpuAccessor.QueryHostSupportsShaderBallot(),
|
GpuAccessor.QueryHostSupportsShaderBallot(),
|
||||||
GpuAccessor.QueryHostSupportsShaderBarrierDivergence(),
|
GpuAccessor.QueryHostSupportsShaderBarrierDivergence(),
|
||||||
GpuAccessor.QueryHostSupportsShaderFloat64(),
|
GpuAccessor.QueryHostSupportsShaderFloat64(),
|
||||||
|
GpuAccessor.QueryHostSupportsShaderNonUniformIndexing(),
|
||||||
GpuAccessor.QueryHostSupportsTextureShadowLod(),
|
GpuAccessor.QueryHostSupportsTextureShadowLod(),
|
||||||
GpuAccessor.QueryHostSupportsViewportMask());
|
GpuAccessor.QueryHostSupportsViewportMask());
|
||||||
|
|
||||||
|
|||||||
@@ -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++)
|
||||||
@@ -175,7 +176,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug.Assert(_referenceCount >= 0);
|
Debug.Assert(_referenceCount >= 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,14 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public static (AccessFlags Access, PipelineStageFlags Stages) GetSubpassAccessSuperset(VulkanRenderer gd)
|
public static (AccessFlags Access, PipelineStageFlags Stages) GetSubpassAccessSuperset(VulkanRenderer gd)
|
||||||
{
|
{
|
||||||
AccessFlags access = BufferAccess;
|
AccessFlags access = BufferAccess |
|
||||||
|
AccessFlags.ShaderReadBit |
|
||||||
|
AccessFlags.ShaderWriteBit |
|
||||||
|
AccessFlags.ColorAttachmentReadBit |
|
||||||
|
AccessFlags.ColorAttachmentWriteBit |
|
||||||
|
AccessFlags.DepthStencilAttachmentReadBit |
|
||||||
|
AccessFlags.DepthStencilAttachmentWriteBit;
|
||||||
|
|
||||||
PipelineStageFlags stages = PipelineStageFlags.AllGraphicsBit;
|
PipelineStageFlags stages = PipelineStageFlags.AllGraphicsBit;
|
||||||
|
|
||||||
if (gd.TransformFeedbackApi != null)
|
if (gd.TransformFeedbackApi != null)
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
Image dstImage,
|
Image dstImage,
|
||||||
TextureCreateInfo srcInfo,
|
TextureCreateInfo srcInfo,
|
||||||
TextureCreateInfo dstInfo,
|
TextureCreateInfo dstInfo,
|
||||||
|
TextureCreateInfo srcStorageInfo,
|
||||||
|
TextureCreateInfo dstStorageInfo,
|
||||||
Extents2D srcRegion,
|
Extents2D srcRegion,
|
||||||
Extents2D dstRegion,
|
Extents2D dstRegion,
|
||||||
int srcLayer,
|
int srcLayer,
|
||||||
@@ -40,6 +42,13 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return (xy1, xy2);
|
return (xy1, xy2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static (Offset3D, Offset3D) ClampOffsetsToMip(Offset3D xy1, Offset3D xy2, int mipW, int mipH)
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
new Offset3D(Math.Min(xy1.X, mipW), Math.Min(xy1.Y, mipH), xy1.Z),
|
||||||
|
new Offset3D(Math.Min(xy2.X, mipW), Math.Min(xy2.Y, mipH), xy2.Z));
|
||||||
|
}
|
||||||
|
|
||||||
if (srcAspectFlags == 0)
|
if (srcAspectFlags == 0)
|
||||||
{
|
{
|
||||||
srcAspectFlags = srcInfo.Format.ConvertAspectFlags();
|
srcAspectFlags = srcInfo.Format.ConvertAspectFlags();
|
||||||
@@ -80,6 +89,14 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
(srcOffsets.Element0, srcOffsets.Element1) = ExtentsToOffset3D(srcRegion, srcInfo.Width, srcInfo.Height, level);
|
(srcOffsets.Element0, srcOffsets.Element1) = ExtentsToOffset3D(srcRegion, srcInfo.Width, srcInfo.Height, level);
|
||||||
(dstOffsets.Element0, dstOffsets.Element1) = ExtentsToOffset3D(dstRegion, dstInfo.Width, dstInfo.Height, level);
|
(dstOffsets.Element0, dstOffsets.Element1) = ExtentsToOffset3D(dstRegion, dstInfo.Width, dstInfo.Height, level);
|
||||||
|
|
||||||
|
int srcMipW = Math.Max(1, srcStorageInfo.Width >> (int)copySrcLevel);
|
||||||
|
int srcMipH = Math.Max(1, srcStorageInfo.Height >> (int)copySrcLevel);
|
||||||
|
int dstMipW = Math.Max(1, dstStorageInfo.Width >> (int)copyDstLevel);
|
||||||
|
int dstMipH = Math.Max(1, dstStorageInfo.Height >> (int)copyDstLevel);
|
||||||
|
|
||||||
|
(srcOffsets.Element0, srcOffsets.Element1) = ClampOffsetsToMip(srcOffsets.Element0, srcOffsets.Element1, srcMipW, srcMipH);
|
||||||
|
(dstOffsets.Element0, dstOffsets.Element1) = ClampOffsetsToMip(dstOffsets.Element0, dstOffsets.Element1, dstMipW, dstMipH);
|
||||||
|
|
||||||
ImageBlit region = new()
|
ImageBlit region = new()
|
||||||
{
|
{
|
||||||
SrcSubresource = srcSl,
|
SrcSubresource = srcSl,
|
||||||
@@ -121,6 +138,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
Image dstImage,
|
Image dstImage,
|
||||||
TextureCreateInfo srcInfo,
|
TextureCreateInfo srcInfo,
|
||||||
TextureCreateInfo dstInfo,
|
TextureCreateInfo dstInfo,
|
||||||
|
TextureCreateInfo srcStorageInfo,
|
||||||
|
TextureCreateInfo dstStorageInfo,
|
||||||
int srcViewLayer,
|
int srcViewLayer,
|
||||||
int dstViewLayer,
|
int dstViewLayer,
|
||||||
int srcViewLevel,
|
int srcViewLevel,
|
||||||
@@ -151,6 +170,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
dstImage,
|
dstImage,
|
||||||
srcInfo,
|
srcInfo,
|
||||||
dstInfo,
|
dstInfo,
|
||||||
|
srcStorageInfo,
|
||||||
|
dstStorageInfo,
|
||||||
srcViewLayer,
|
srcViewLayer,
|
||||||
dstViewLayer,
|
dstViewLayer,
|
||||||
srcViewLevel,
|
srcViewLevel,
|
||||||
@@ -186,6 +207,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
Image dstImage,
|
Image dstImage,
|
||||||
TextureCreateInfo srcInfo,
|
TextureCreateInfo srcInfo,
|
||||||
TextureCreateInfo dstInfo,
|
TextureCreateInfo dstInfo,
|
||||||
|
TextureCreateInfo srcStorageInfo,
|
||||||
|
TextureCreateInfo dstStorageInfo,
|
||||||
int srcViewLayer,
|
int srcViewLayer,
|
||||||
int dstViewLayer,
|
int dstViewLayer,
|
||||||
int srcViewLevel,
|
int srcViewLevel,
|
||||||
@@ -314,6 +337,14 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
int copyWidth = sizeInBlocks ? BitUtils.DivRoundUp(width, blockWidth) : width;
|
int copyWidth = sizeInBlocks ? BitUtils.DivRoundUp(width, blockWidth) : width;
|
||||||
int copyHeight = sizeInBlocks ? BitUtils.DivRoundUp(height, blockHeight) : height;
|
int copyHeight = sizeInBlocks ? BitUtils.DivRoundUp(height, blockHeight) : height;
|
||||||
|
|
||||||
|
int srcMipW = Math.Max(1, srcStorageInfo.Width >> (srcViewLevel + srcLevel + level));
|
||||||
|
int srcMipH = Math.Max(1, srcStorageInfo.Height >> (srcViewLevel + srcLevel + level));
|
||||||
|
int dstMipW = Math.Max(1, dstStorageInfo.Width >> (dstViewLevel + dstLevel + level));
|
||||||
|
int dstMipH = Math.Max(1, dstStorageInfo.Height >> (dstViewLevel + dstLevel + level));
|
||||||
|
|
||||||
|
copyWidth = Math.Min(copyWidth, Math.Min(srcMipW, dstMipW));
|
||||||
|
copyHeight = Math.Min(copyHeight, Math.Min(srcMipH, dstMipH));
|
||||||
|
|
||||||
Extent3D extent = new((uint)copyWidth, (uint)copyHeight, (uint)srcDepth);
|
Extent3D extent = new((uint)copyWidth, (uint)copyHeight, (uint)srcDepth);
|
||||||
|
|
||||||
if (srcInfo.Samples > 1 && srcInfo.Samples != dstInfo.Samples)
|
if (srcInfo.Samples > 1 && srcInfo.Samples != dstInfo.Samples)
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public VkFormat VkFormat { get; }
|
public VkFormat VkFormat { get; }
|
||||||
|
|
||||||
|
public ImageUsageFlags UsageFlags { get; }
|
||||||
|
|
||||||
public unsafe TextureStorage(
|
public unsafe TextureStorage(
|
||||||
VulkanRenderer gd,
|
VulkanRenderer gd,
|
||||||
Device device,
|
Device device,
|
||||||
@@ -93,7 +95,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
SampleCountFlags sampleCountFlags = ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)info.Samples);
|
SampleCountFlags sampleCountFlags = ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)info.Samples);
|
||||||
|
|
||||||
ImageUsageFlags usage = GetImageUsage(info.Format, gd.Capabilities, isMsImageStorageSupported, true);
|
ImageUsageFlags usage = GetImageUsage(info.Format, gd.Capabilities, isMsImageStorageSupported);
|
||||||
|
UsageFlags = usage;
|
||||||
|
|
||||||
ImageCreateFlags flags = ImageCreateFlags.CreateMutableFormatBit | ImageCreateFlags.CreateExtendedUsageBit;
|
ImageCreateFlags flags = ImageCreateFlags.CreateMutableFormatBit | ImageCreateFlags.CreateExtendedUsageBit;
|
||||||
|
|
||||||
@@ -159,7 +162,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
_imageAuto = new Auto<DisposableImage>(new DisposableImage(_gd.Api, device, _image));
|
_imageAuto = new Auto<DisposableImage>(new DisposableImage(_gd.Api, device, _image));
|
||||||
|
|
||||||
InitialTransition(ImageLayout.Preinitialized, ImageLayout.General);
|
InitialTransition(ImageLayout.Undefined, ImageLayout.General);
|
||||||
}
|
}
|
||||||
|
|
||||||
_slices = new TextureSliceInfo[levels * _depthOrLayers];
|
_slices = new TextureSliceInfo[levels * _depthOrLayers];
|
||||||
@@ -307,7 +310,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ImageUsageFlags GetImageUsage(Format format, in HardwareCapabilities capabilities, bool isMsImageStorageSupported, bool extendedUsage)
|
public static ImageUsageFlags GetImageUsage(Format format, in HardwareCapabilities capabilities, bool isMsImageStorageSupported)
|
||||||
{
|
{
|
||||||
ImageUsageFlags usage = DefaultUsageFlags;
|
ImageUsageFlags usage = DefaultUsageFlags;
|
||||||
|
|
||||||
@@ -320,7 +323,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
usage |= ImageUsageFlags.ColorAttachmentBit;
|
usage |= ImageUsageFlags.ColorAttachmentBit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((format.IsImageCompatible && isMsImageStorageSupported) || extendedUsage)
|
if (format.IsImageCompatible && isMsImageStorageSupported)
|
||||||
{
|
{
|
||||||
usage |= ImageUsageFlags.StorageBit;
|
usage |= ImageUsageFlags.StorageBit;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
bool isMsImageStorageSupported = gd.Capabilities.SupportsShaderStorageImageMultisample || !info.Target.IsMultisample;
|
bool isMsImageStorageSupported = gd.Capabilities.SupportsShaderStorageImageMultisample || !info.Target.IsMultisample;
|
||||||
|
|
||||||
VkFormat format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format, isMsImageStorageSupported);
|
VkFormat format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format, isMsImageStorageSupported);
|
||||||
ImageUsageFlags usage = TextureStorage.GetImageUsage(info.Format, gd.Capabilities, isMsImageStorageSupported, false);
|
ImageUsageFlags usage = TextureStorage.GetImageUsage(info.Format, gd.Capabilities, isMsImageStorageSupported) & storage.UsageFlags;
|
||||||
|
|
||||||
uint levels = (uint)info.Levels;
|
uint levels = (uint)info.Levels;
|
||||||
uint layers = (uint)info.GetLayers();
|
uint layers = (uint)info.GetLayers();
|
||||||
@@ -133,6 +133,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
shaderUsage |= ImageUsageFlags.StorageBit;
|
shaderUsage |= ImageUsageFlags.StorageBit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shaderUsage &= storage.UsageFlags;
|
||||||
|
|
||||||
_imageView = CreateImageView(componentMapping, subresourceRange, type, shaderUsage);
|
_imageView = CreateImageView(componentMapping, subresourceRange, type, shaderUsage);
|
||||||
|
|
||||||
// Framebuffer attachments and storage images requires a identity component mapping.
|
// Framebuffer attachments and storage images requires a identity component mapping.
|
||||||
@@ -257,6 +259,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
dstImage,
|
dstImage,
|
||||||
src.Info,
|
src.Info,
|
||||||
dst.Info,
|
dst.Info,
|
||||||
|
src.Storage.Info,
|
||||||
|
dst.Storage.Info,
|
||||||
src.FirstLayer,
|
src.FirstLayer,
|
||||||
dst.FirstLayer,
|
dst.FirstLayer,
|
||||||
src.FirstLevel,
|
src.FirstLevel,
|
||||||
@@ -310,6 +314,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
dstImage,
|
dstImage,
|
||||||
src.Info,
|
src.Info,
|
||||||
dst.Info,
|
dst.Info,
|
||||||
|
src.Storage.Info,
|
||||||
|
dst.Storage.Info,
|
||||||
src.FirstLayer,
|
src.FirstLayer,
|
||||||
dst.FirstLayer,
|
dst.FirstLayer,
|
||||||
src.FirstLevel,
|
src.FirstLevel,
|
||||||
@@ -385,6 +391,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
dst.GetImage().Get(cbs).Value,
|
dst.GetImage().Get(cbs).Value,
|
||||||
src.Info,
|
src.Info,
|
||||||
dst.Info,
|
dst.Info,
|
||||||
|
src.Storage.Info,
|
||||||
|
dst.Storage.Info,
|
||||||
src.FirstLayer,
|
src.FirstLayer,
|
||||||
dst.FirstLayer,
|
dst.FirstLayer,
|
||||||
src.FirstLevel,
|
src.FirstLevel,
|
||||||
@@ -410,6 +418,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
dst.GetImage().Get(cbs).Value,
|
dst.GetImage().Get(cbs).Value,
|
||||||
src.Info,
|
src.Info,
|
||||||
dst.Info,
|
dst.Info,
|
||||||
|
src.Storage.Info,
|
||||||
|
dst.Storage.Info,
|
||||||
srcRegion,
|
srcRegion,
|
||||||
dstRegion,
|
dstRegion,
|
||||||
src.FirstLayer,
|
src.FirstLayer,
|
||||||
@@ -463,6 +473,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
dstImage.Get(cbs).Value,
|
dstImage.Get(cbs).Value,
|
||||||
src.Info,
|
src.Info,
|
||||||
dst.Info,
|
dst.Info,
|
||||||
|
src.Storage.Info,
|
||||||
|
dst.Storage.Info,
|
||||||
srcRegion,
|
srcRegion,
|
||||||
dstRegion,
|
dstRegion,
|
||||||
src.FirstLayer,
|
src.FirstLayer,
|
||||||
|
|||||||
@@ -68,9 +68,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
int stride = (_stride + (alignment - 1)) & -alignment;
|
int stride = (_stride + (alignment - 1)) & -alignment;
|
||||||
int newSize = (_size / _stride) * stride;
|
int newSize = (_size / _stride) * stride;
|
||||||
|
|
||||||
Buffer buffer = autoBuffer.Get(cbs, 0, newSize).Value;
|
updater.BindVertexBuffer(cbs, binding, autoBuffer, 0, newSize, (ulong)stride);
|
||||||
|
|
||||||
updater.BindVertexBuffer(cbs, binding, buffer, 0, (ulong)newSize, (ulong)stride);
|
|
||||||
|
|
||||||
_buffer = autoBuffer;
|
_buffer = autoBuffer;
|
||||||
|
|
||||||
@@ -93,11 +91,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
if (autoBuffer != null)
|
if (autoBuffer != null)
|
||||||
{
|
{
|
||||||
int offset = _offset;
|
updater.BindVertexBuffer(cbs, binding, autoBuffer, _offset, _size, (ulong)_stride);
|
||||||
bool mirrorable = _size <= VertexBufferMaxMirrorable;
|
|
||||||
Buffer buffer = mirrorable ? autoBuffer.GetMirrorable(cbs, ref offset, _size, out _).Value : autoBuffer.Get(cbs, offset, _size).Value;
|
|
||||||
|
|
||||||
updater.BindVertexBuffer(cbs, binding, buffer, (ulong)offset, (ulong)_size, (ulong)_stride);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private readonly NativeArray<ulong> _sizes;
|
private readonly NativeArray<ulong> _sizes;
|
||||||
private readonly NativeArray<ulong> _strides;
|
private readonly NativeArray<ulong> _strides;
|
||||||
|
|
||||||
|
private readonly Auto<DisposableBuffer>[] _bufferAutos;
|
||||||
|
private readonly int[] _bufferOffsetsForGet;
|
||||||
|
private readonly int[] _bufferSizesForGet;
|
||||||
|
|
||||||
public VertexBufferUpdater(VulkanRenderer gd)
|
public VertexBufferUpdater(VulkanRenderer gd)
|
||||||
{
|
{
|
||||||
_gd = gd;
|
_gd = gd;
|
||||||
@@ -23,9 +27,13 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_offsets = new NativeArray<ulong>(Constants.MaxVertexBuffers);
|
_offsets = new NativeArray<ulong>(Constants.MaxVertexBuffers);
|
||||||
_sizes = new NativeArray<ulong>(Constants.MaxVertexBuffers);
|
_sizes = new NativeArray<ulong>(Constants.MaxVertexBuffers);
|
||||||
_strides = new NativeArray<ulong>(Constants.MaxVertexBuffers);
|
_strides = new NativeArray<ulong>(Constants.MaxVertexBuffers);
|
||||||
|
|
||||||
|
_bufferAutos = new Auto<DisposableBuffer>[Constants.MaxVertexBuffers];
|
||||||
|
_bufferOffsetsForGet = new int[Constants.MaxVertexBuffers];
|
||||||
|
_bufferSizesForGet = new int[Constants.MaxVertexBuffers];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BindVertexBuffer(CommandBufferScoped cbs, uint binding, VkBuffer buffer, ulong offset, ulong size, ulong stride)
|
public void BindVertexBuffer(CommandBufferScoped cbs, uint binding, Auto<DisposableBuffer> autoBuffer, int offset, int size, ulong stride)
|
||||||
{
|
{
|
||||||
if (_count == 0)
|
if (_count == 0)
|
||||||
{
|
{
|
||||||
@@ -39,9 +47,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
int index = (int)_count;
|
int index = (int)_count;
|
||||||
|
|
||||||
_buffers[index] = buffer;
|
_bufferAutos[index] = autoBuffer;
|
||||||
_offsets[index] = offset;
|
_bufferOffsetsForGet[index] = offset;
|
||||||
_sizes[index] = size;
|
_bufferSizesForGet[index] = size;
|
||||||
|
_offsets[index] = (ulong)offset;
|
||||||
|
_sizes[index] = (ulong)size;
|
||||||
_strides[index] = stride;
|
_strides[index] = stride;
|
||||||
|
|
||||||
_count++;
|
_count++;
|
||||||
@@ -51,6 +61,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
if (_count != 0)
|
if (_count != 0)
|
||||||
{
|
{
|
||||||
|
for (int i = 0; i < _count; i++)
|
||||||
|
{
|
||||||
|
_buffers[i] = _bufferAutos[i].Get(cbs, _bufferOffsetsForGet[i], _bufferSizesForGet[i]).Value;
|
||||||
|
_bufferAutos[i] = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (_gd.Capabilities.SupportsExtendedDynamicState)
|
if (_gd.Capabilities.SupportsExtendedDynamicState)
|
||||||
{
|
{
|
||||||
_gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2(
|
_gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2(
|
||||||
|
|||||||
@@ -494,6 +494,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
UniformBufferStandardLayout = supportedPhysicalDeviceVulkan12Features.UniformBufferStandardLayout,
|
UniformBufferStandardLayout = supportedPhysicalDeviceVulkan12Features.UniformBufferStandardLayout,
|
||||||
UniformAndStorageBuffer8BitAccess = supportedPhysicalDeviceVulkan12Features.UniformAndStorageBuffer8BitAccess,
|
UniformAndStorageBuffer8BitAccess = supportedPhysicalDeviceVulkan12Features.UniformAndStorageBuffer8BitAccess,
|
||||||
StorageBuffer8BitAccess = supportedPhysicalDeviceVulkan12Features.StorageBuffer8BitAccess,
|
StorageBuffer8BitAccess = supportedPhysicalDeviceVulkan12Features.StorageBuffer8BitAccess,
|
||||||
|
ShaderSampledImageArrayNonUniformIndexing = supportedPhysicalDeviceVulkan12Features.ShaderSampledImageArrayNonUniformIndexing,
|
||||||
|
ShaderStorageImageArrayNonUniformIndexing = supportedPhysicalDeviceVulkan12Features.ShaderStorageImageArrayNonUniformIndexing,
|
||||||
};
|
};
|
||||||
|
|
||||||
pExtendedFeatures = &featuresVk12;
|
pExtendedFeatures = &featuresVk12;
|
||||||
|
|||||||
@@ -775,6 +775,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
supportsShaderBallot: false,
|
supportsShaderBallot: false,
|
||||||
supportsShaderBarrierDivergence: Vendor != Vendor.Intel,
|
supportsShaderBarrierDivergence: Vendor != Vendor.Intel,
|
||||||
supportsShaderFloat64: Capabilities.SupportsShaderFloat64,
|
supportsShaderFloat64: Capabilities.SupportsShaderFloat64,
|
||||||
|
supportsShaderNonUniformIndexing:
|
||||||
|
featuresVk12.ShaderSampledImageArrayNonUniformIndexing &&
|
||||||
|
featuresVk12.ShaderStorageImageArrayNonUniformIndexing,
|
||||||
supportsTextureGatherOffsets: features2.Features.ShaderImageGatherExtended && !IsMoltenVk,
|
supportsTextureGatherOffsets: features2.Features.ShaderImageGatherExtended && !IsMoltenVk,
|
||||||
supportsTextureShadowLod: false,
|
supportsTextureShadowLod: false,
|
||||||
supportsVertexStoreAndAtomics: features2.Features.VertexPipelineStoresAndAtomics,
|
supportsVertexStoreAndAtomics: features2.Features.VertexPipelineStoresAndAtomics,
|
||||||
|
|||||||
@@ -391,12 +391,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
if (_effect != null)
|
if (_effect != null)
|
||||||
{
|
{
|
||||||
|
_gd.FlushAllCommands();
|
||||||
_gd.CommandBufferPool.Return(
|
_gd.CommandBufferPool.Return(
|
||||||
cbs,
|
cbs,
|
||||||
null,
|
null,
|
||||||
[PipelineStageFlags.ColorAttachmentOutputBit],
|
[PipelineStageFlags.ColorAttachmentOutputBit],
|
||||||
null);
|
null);
|
||||||
_gd.FlushAllCommands();
|
|
||||||
cbs.GetFence().Wait();
|
cbs.GetFence().Wait();
|
||||||
cbs = _gd.CommandBufferPool.Rent();
|
cbs = _gd.CommandBufferPool.Rent();
|
||||||
}
|
}
|
||||||
@@ -455,6 +455,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
ImageLayout.General,
|
ImageLayout.General,
|
||||||
ImageLayout.PresentSrcKhr);
|
ImageLayout.PresentSrcKhr);
|
||||||
|
|
||||||
|
_gd.FlushAllCommands();
|
||||||
|
|
||||||
_gd.CommandBufferPool.Return(
|
_gd.CommandBufferPool.Return(
|
||||||
cbs,
|
cbs,
|
||||||
[_imageAvailableSemaphores[semaphoreIndex]],
|
[_imageAvailableSemaphores[semaphoreIndex]],
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.LibraryAppletProxy
|
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.LibraryAppletProxy
|
||||||
@@ -44,6 +45,35 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib
|
|||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandCmif(1)]
|
||||||
|
// PushOutData(object<nn::am::service::IStorage>)
|
||||||
|
public ResultCode PushOutData(ServiceCtx context)
|
||||||
|
{
|
||||||
|
IStorage appletData = GetObject<IStorage>(context, 0);
|
||||||
|
|
||||||
|
if (appletData == null || appletData.Data.Length == 0) // is this necessary?
|
||||||
|
{
|
||||||
|
return ResultCode.NullObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
_appletStandalone.InputData.Enqueue(appletData.Data);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(10)]
|
||||||
|
// ExitProcessAndReturn -> nn::am::service::LibraryAppletInfo
|
||||||
|
public ResultCode ExitProcessAndReturn(ServiceCtx context)
|
||||||
|
{
|
||||||
|
// Exits the LibraryApplet and returns to running the title which launched this LibraryApplet (qlaunch for example).
|
||||||
|
// On success, official sw will enter an infinite loop with sleep-thread value 86400000000000.
|
||||||
|
// Since we don't currently support qlaunch, it's fine to stub it.
|
||||||
|
|
||||||
|
Logger.Stub?.PrintStub(LogClass.Service);
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
[CommandCmif(11)]
|
[CommandCmif(11)]
|
||||||
// GetLibraryAppletInfo() -> nn::am::service::LibraryAppletInfo
|
// GetLibraryAppletInfo() -> nn::am::service::LibraryAppletInfo
|
||||||
@@ -67,7 +97,8 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib
|
|||||||
AppletIdentifyInfo appletIdentifyInfo = new()
|
AppletIdentifyInfo appletIdentifyInfo = new()
|
||||||
{
|
{
|
||||||
AppletId = AppletId.QLaunch,
|
AppletId = AppletId.QLaunch,
|
||||||
TitleId = 0x0100000000001000,
|
// 0x4 padding
|
||||||
|
TitleId = 0x0100000000001000, // qlaunch systemAppletMenu title ID
|
||||||
};
|
};
|
||||||
|
|
||||||
context.ResponseData.WriteStruct(appletIdentifyInfo);
|
context.ResponseData.WriteStruct(appletIdentifyInfo);
|
||||||
|
|||||||
@@ -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,84 +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, SKAlphaType.Premul));
|
|
||||||
int dataLen = screenshotData.Length > bitmap.ByteCount ? bitmap.ByteCount : screenshotData.Length;
|
|
||||||
|
|
||||||
Marshal.Copy(screenshotData, 0, bitmap.GetPixels(), dataLen);
|
|
||||||
|
|
||||||
using SKData data = bitmap.Encode(SKEncodedImageFormat.Jpeg, 80);
|
|
||||||
using 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();
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,7 +270,8 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,6 +302,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
|
||||||
@@ -437,7 +455,8 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -813,6 +863,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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,17 +28,64 @@ namespace Ryujinx.HLE.Loaders.Processes
|
|||||||
|
|
||||||
private ulong _latestPid;
|
private ulong _latestPid;
|
||||||
|
|
||||||
public ProcessResult ActiveApplication
|
private readonly object _pidLock = new();
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
public ProcessResult? ActiveApplication
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (!_processesByPid.TryGetValue(_latestPid, out ProcessResult value))
|
lock (_pidLock)
|
||||||
throw new RyujinxException(
|
{
|
||||||
$"The HLE Process map did not have a process with ID {_latestPid}. Are you missing firmware?");
|
// Check if _latestPid is still valid
|
||||||
|
if (_latestPid == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return value;
|
// Verify process still exists in kernel (authoritative source)
|
||||||
|
if (!_device.System.KernelContext.Processes.TryGetValue(_latestPid, out HOS.Kernel.Process.KProcess? kernelProcess))
|
||||||
|
{
|
||||||
|
// Process no longer exists in kernel, clear stale state
|
||||||
|
Logger.Warning?.Print(LogClass.Loader,
|
||||||
|
$"ActiveApplication PID {_latestPid} no longer exists in kernel, clearing stale state");
|
||||||
|
|
||||||
|
_processesByPid.TryRemove(_latestPid, out _);
|
||||||
|
_latestPid = 0;
|
||||||
|
TitleIDs.CurrentApplication.Value = null;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify process still exists in ProcessLoader's dictionary
|
||||||
|
if (_processesByPid.TryGetValue(_latestPid, out ProcessResult? processResult))
|
||||||
|
{
|
||||||
|
// Additional check: verify process state
|
||||||
|
if (kernelProcess.State == HOS.Kernel.Process.ProcessState.Exited ||
|
||||||
|
kernelProcess.State == HOS.Kernel.Process.ProcessState.Exiting)
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.Loader,
|
||||||
|
$"ActiveApplication PID {_latestPid} is in state {kernelProcess.State}, clearing");
|
||||||
|
|
||||||
|
_processesByPid.TryRemove(_latestPid, out _);
|
||||||
|
_latestPid = 0;
|
||||||
|
TitleIDs.CurrentApplication.Value = null;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return processResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: clear stale PID if not in our dictionary
|
||||||
|
Logger.Warning?.Print(LogClass.Loader,
|
||||||
|
$"ActiveApplication PID {_latestPid} not in ProcessLoader dictionary, clearing");
|
||||||
|
_latestPid = 0;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
public ProcessLoader(Switch device)
|
public ProcessLoader(Switch device)
|
||||||
{
|
{
|
||||||
@@ -144,7 +192,7 @@ namespace Ryujinx.HLE.Loaders.Processes
|
|||||||
public bool LoadUnpackedNca(string exeFsDirPath, string romFsPath = null)
|
public bool LoadUnpackedNca(string exeFsDirPath, string romFsPath = null)
|
||||||
{
|
{
|
||||||
ProcessResult processResult = new LocalFileSystem(exeFsDirPath).Load(_device, romFsPath);
|
ProcessResult processResult = new LocalFileSystem(exeFsDirPath).Load(_device, romFsPath);
|
||||||
|
|
||||||
if (processResult.ProcessId != 0 && _processesByPid.TryAdd(processResult.ProcessId, processResult))
|
if (processResult.ProcessId != 0 && _processesByPid.TryAdd(processResult.ProcessId, processResult))
|
||||||
{
|
{
|
||||||
if (processResult.Start(_device))
|
if (processResult.Start(_device))
|
||||||
@@ -277,5 +325,39 @@ namespace Ryujinx.HLE.Loaders.Processes
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clears a specific process from the ProcessLoader's tracking.
|
||||||
|
/// This should be called when a process exits or is terminated.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pid">The process ID to clear</param>
|
||||||
|
public void ClearProcess(ulong pid)
|
||||||
|
{
|
||||||
|
lock (_pidLock)
|
||||||
|
{
|
||||||
|
if (_processesByPid.TryRemove(pid, out _))
|
||||||
|
{
|
||||||
|
if (_latestPid == pid)
|
||||||
|
{
|
||||||
|
_latestPid = 0;
|
||||||
|
TitleIDs.CurrentApplication.Value = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clears all processes from the ProcessLoader's tracking.
|
||||||
|
/// This should be called during system shutdown.
|
||||||
|
/// </summary>
|
||||||
|
public void ClearAllProcesses()
|
||||||
|
{
|
||||||
|
lock (_pidLock)
|
||||||
|
{
|
||||||
|
_processesByPid.Clear();
|
||||||
|
_latestPid = 0;
|
||||||
|
TitleIDs.CurrentApplication.Value = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
@@ -71,8 +71,15 @@ namespace Ryujinx.HLE.Loaders.Processes
|
|||||||
ProgramId = programId;
|
ProgramId = programId;
|
||||||
ProgramIdText = $"{programId:x16}";
|
ProgramIdText = $"{programId:x16}";
|
||||||
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>
|
||||||
|
|||||||
@@ -183,6 +183,7 @@ namespace Ryujinx.HLE
|
|||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing)
|
||||||
{
|
{
|
||||||
|
Processes.ClearAllProcesses();
|
||||||
System.Dispose();
|
System.Dispose();
|
||||||
AudioDeviceDriver.Dispose();
|
AudioDeviceDriver.Dispose();
|
||||||
FileSystem.Dispose();
|
FileSystem.Dispose();
|
||||||
|
|||||||
151
src/Ryujinx.Input.SDL3/NpadHdRumble.cs
Normal file
151
src/Ryujinx.Input.SDL3/NpadHdRumble.cs
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
using Ryujinx.HLE.HOS.Services.Hid;
|
||||||
|
using SDL;
|
||||||
|
using static SDL.SDL3;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Input.SDL3
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Manages a HID handle of a gamepad to encode and write HD rumble commands for Nin controllers.
|
||||||
|
/// </summary>
|
||||||
|
public unsafe class NpadHdRumble : IDisposable
|
||||||
|
{
|
||||||
|
private readonly SDL_hid_device* _hidHandle;
|
||||||
|
|
||||||
|
private int _globalCount;
|
||||||
|
|
||||||
|
private NpadHdRumble(SDL_hid_device* hidHandle)
|
||||||
|
{
|
||||||
|
_hidHandle = hidHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NpadHdRumble Create(SDL_Gamepad* gamepadHandle)
|
||||||
|
{
|
||||||
|
ushort vendor = SDL_GetGamepadVendor(gamepadHandle);
|
||||||
|
if (vendor != 0x057e)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ushort product = SDL_GetGamepadProduct(gamepadHandle);
|
||||||
|
if (product != 0x2006 && product != 0x2007 && product != 0x2009 && product != 0x200e)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new NpadHdRumble(SDL_hid_open(vendor, product, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some of the code was translated from https://github.com/MIZUSHIKI/JoyShockLibrary-plus-HDRumble
|
||||||
|
private void WriteHdRumble(
|
||||||
|
int encLeftLowFreq, int encLeftLowAmp,
|
||||||
|
int encLeftHighFreq, int encLeftHighAmp,
|
||||||
|
int encRightLowFreq, int encRightLowAmp,
|
||||||
|
int encRightHighFreq, int encRightHighAmp)
|
||||||
|
{
|
||||||
|
byte[] buf = new byte[10];
|
||||||
|
|
||||||
|
buf[0] = 0x10;
|
||||||
|
buf[1] = (byte)((++_globalCount) & 0xF);
|
||||||
|
|
||||||
|
buf[2] = (byte)(encLeftHighFreq & 0xFF);
|
||||||
|
buf[3] = (byte)(encLeftHighAmp + ((encLeftHighFreq >> 8) & 0xFF));
|
||||||
|
buf[4] = (byte)(encLeftLowFreq + ((encLeftLowAmp >> 8) & 0xFF));
|
||||||
|
buf[5] = (byte)(encLeftLowAmp & 0xFF);
|
||||||
|
|
||||||
|
buf[6] = (byte)(encRightHighFreq & 0xFF);
|
||||||
|
buf[7] = (byte)(encRightHighAmp + ((encRightHighFreq >> 8) & 0xFF));
|
||||||
|
buf[8] = (byte)(encRightLowFreq + ((encRightLowAmp >> 8) & 0xFF));
|
||||||
|
buf[9] = (byte)(encRightLowAmp & 0xFF);
|
||||||
|
|
||||||
|
if (_globalCount > 0xF)
|
||||||
|
{
|
||||||
|
_globalCount = 0x0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fixed (byte* ptr = buf)
|
||||||
|
{
|
||||||
|
SDL_hid_write(_hidHandle, ptr, (nuint)buf.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static int EncodeLowFreq(float lowFreq)
|
||||||
|
{
|
||||||
|
float lf = Math.Clamp(lowFreq, 40.875885f, 626.286133f);
|
||||||
|
return (int)Math.Round(32 * Math.Log2(lf * 0.1f)) - 0x40;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int EncodeHighFreq(float highFreq)
|
||||||
|
{
|
||||||
|
float hf = Math.Clamp(highFreq, 81.75177f, 1252.572266f);
|
||||||
|
return ((int)Math.Round(32 * Math.Log2(hf * 0.1f)) - 0x60) * 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int EncodeLowAmp(float rawAmp)
|
||||||
|
{
|
||||||
|
int encodedAmp = 0;
|
||||||
|
|
||||||
|
if (rawAmp is > 0 and < 0.012f)
|
||||||
|
{
|
||||||
|
encodedAmp = 1;
|
||||||
|
}
|
||||||
|
else if (rawAmp is >= 0.012f and < 0.112f)
|
||||||
|
{
|
||||||
|
encodedAmp = (int)Math.Round(4 * Math.Log2(rawAmp * 110f));
|
||||||
|
}
|
||||||
|
else if (rawAmp is >= 0.112f and < 0.225f)
|
||||||
|
{
|
||||||
|
encodedAmp = (int)Math.Round(16 * Math.Log2(rawAmp * 17f));
|
||||||
|
}
|
||||||
|
else if (rawAmp is >= 0.225f and <= 1f)
|
||||||
|
{
|
||||||
|
encodedAmp = (int)Math.Round(32 * Math.Log2(rawAmp * 8.7f));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int)Math.Floor(encodedAmp / 2.0) + 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int EncodeHighAmp(float rawAmp)
|
||||||
|
{
|
||||||
|
int encodedAmp = 0;
|
||||||
|
|
||||||
|
if (rawAmp is > 0 and < 0.012f)
|
||||||
|
{
|
||||||
|
encodedAmp = 1;
|
||||||
|
}
|
||||||
|
else if (rawAmp is >= 0.012f and < 0.112f)
|
||||||
|
{
|
||||||
|
encodedAmp = (int)Math.Round(4 * Math.Log2(rawAmp * 110f));
|
||||||
|
}
|
||||||
|
else if (rawAmp is >= 0.112f and < 0.225f)
|
||||||
|
{
|
||||||
|
encodedAmp = (int)Math.Round(16 * Math.Log2(rawAmp * 17f));
|
||||||
|
}
|
||||||
|
else if (rawAmp is >= 0.225f and <= 1f)
|
||||||
|
{
|
||||||
|
encodedAmp = (int)Math.Round(32 * Math.Log2(rawAmp * 8.7f));
|
||||||
|
}
|
||||||
|
|
||||||
|
return encodedAmp * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HdRumble(VibrationValue left, VibrationValue right)
|
||||||
|
{
|
||||||
|
WriteHdRumble(EncodeLowFreq(left.FrequencyLow),
|
||||||
|
EncodeLowAmp(left.AmplitudeLow),
|
||||||
|
EncodeHighFreq(left.FrequencyHigh),
|
||||||
|
EncodeHighAmp(left.AmplitudeHigh),
|
||||||
|
EncodeLowFreq(right.FrequencyLow),
|
||||||
|
EncodeLowAmp(right.AmplitudeLow),
|
||||||
|
EncodeHighFreq(right.FrequencyHigh),
|
||||||
|
EncodeHighAmp(right.AmplitudeHigh));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
SDL_hid_close(_hidHandle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ using Ryujinx.Common.Configuration.Hid;
|
|||||||
using Ryujinx.Common.Configuration.Hid.Controller;
|
using Ryujinx.Common.Configuration.Hid.Controller;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
@@ -76,11 +77,14 @@ namespace Ryujinx.Input.SDL3
|
|||||||
|
|
||||||
private SDL_Gamepad* _gamepadHandle;
|
private SDL_Gamepad* _gamepadHandle;
|
||||||
|
|
||||||
|
private NpadHdRumble _hdRumble;
|
||||||
|
|
||||||
private float _triggerThreshold;
|
private float _triggerThreshold;
|
||||||
|
|
||||||
public SDL3Gamepad(SDL_Gamepad* gamepadHandle, string driverId)
|
public SDL3Gamepad(SDL_Gamepad* gamepadHandle, string driverId)
|
||||||
{
|
{
|
||||||
_gamepadHandle = gamepadHandle;
|
_gamepadHandle = gamepadHandle;
|
||||||
|
_hdRumble = NpadHdRumble.Create(gamepadHandle);
|
||||||
_buttonsUserMapping = new List<ButtonMappingEntry>(20);
|
_buttonsUserMapping = new List<ButtonMappingEntry>(20);
|
||||||
|
|
||||||
Name = SDL_GetGamepadName(_gamepadHandle);
|
Name = SDL_GetGamepadName(_gamepadHandle);
|
||||||
@@ -151,7 +155,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;
|
||||||
}
|
}
|
||||||
@@ -163,6 +169,10 @@ namespace Ryujinx.Input.SDL3
|
|||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
|
if (disposing && _hdRumble != null)
|
||||||
|
{
|
||||||
|
_hdRumble.Dispose();
|
||||||
|
}
|
||||||
if (disposing && _gamepadHandle != null)
|
if (disposing && _gamepadHandle != null)
|
||||||
{
|
{
|
||||||
SDL_CloseGamepad(_gamepadHandle);
|
SDL_CloseGamepad(_gamepadHandle);
|
||||||
@@ -182,6 +192,11 @@ namespace Ryujinx.Input.SDL3
|
|||||||
_triggerThreshold = triggerThreshold;
|
_triggerThreshold = triggerThreshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool HDRumble(VibrationValue left, VibrationValue right)
|
||||||
|
{
|
||||||
|
return _hdRumble?.HdRumble(left, right) ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
|
public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
|
||||||
{
|
{
|
||||||
if ((Features & GamepadFeaturesFlag.Rumble) == 0)
|
if ((Features & GamepadFeaturesFlag.Rumble) == 0)
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Ryujinx.Common.Configuration.Hid;
|
using Ryujinx.Common.Configuration.Hid;
|
||||||
using Ryujinx.Common.Configuration.Hid.Controller;
|
using Ryujinx.Common.Configuration.Hid.Controller;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
@@ -61,6 +62,8 @@ namespace Ryujinx.Input.SDL3
|
|||||||
public GamepadFeaturesFlag Features { get; }
|
public GamepadFeaturesFlag Features { get; }
|
||||||
|
|
||||||
private SDL_Gamepad* _gamepadHandle;
|
private SDL_Gamepad* _gamepadHandle;
|
||||||
|
|
||||||
|
private NpadHdRumble _hdRumble;
|
||||||
|
|
||||||
private enum JoyConType
|
private enum JoyConType
|
||||||
{
|
{
|
||||||
@@ -76,6 +79,7 @@ namespace Ryujinx.Input.SDL3
|
|||||||
public SDL3JoyCon(SDL_Gamepad* gamepadHandle, string driverId)
|
public SDL3JoyCon(SDL_Gamepad* gamepadHandle, string driverId)
|
||||||
{
|
{
|
||||||
_gamepadHandle = gamepadHandle;
|
_gamepadHandle = gamepadHandle;
|
||||||
|
_hdRumble = NpadHdRumble.Create(gamepadHandle);
|
||||||
_buttonsUserMapping = new List<ButtonMappingEntry>(10);
|
_buttonsUserMapping = new List<ButtonMappingEntry>(10);
|
||||||
|
|
||||||
Name = SDL_GetGamepadName(_gamepadHandle);
|
Name = SDL_GetGamepadName(_gamepadHandle);
|
||||||
@@ -139,6 +143,10 @@ namespace Ryujinx.Input.SDL3
|
|||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
|
if (disposing && _hdRumble != null)
|
||||||
|
{
|
||||||
|
_hdRumble.Dispose();
|
||||||
|
}
|
||||||
if (disposing && _gamepadHandle != null)
|
if (disposing && _gamepadHandle != null)
|
||||||
{
|
{
|
||||||
SDL_CloseGamepad(_gamepadHandle);
|
SDL_CloseGamepad(_gamepadHandle);
|
||||||
@@ -156,6 +164,11 @@ namespace Ryujinx.Input.SDL3
|
|||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool HDRumble(VibrationValue left, VibrationValue right)
|
||||||
|
{
|
||||||
|
return _hdRumble?.HdRumble(left, right) ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
|
public void Rumble(float lowFrequency, float highFrequency, uint durationMs)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -559,18 +559,29 @@ namespace Ryujinx.Input.HLE
|
|||||||
{
|
{
|
||||||
VibrationValue leftVibrationValue = dualVibrationValue.Item1;
|
VibrationValue leftVibrationValue = dualVibrationValue.Item1;
|
||||||
VibrationValue rightVibrationValue = dualVibrationValue.Item2;
|
VibrationValue rightVibrationValue = dualVibrationValue.Item2;
|
||||||
|
|
||||||
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));
|
||||||
|
|
||||||
|
leftVibrationValue.AmplitudeLow *= controllerConfig.Rumble.WeakRumble;
|
||||||
|
leftVibrationValue.AmplitudeHigh *= controllerConfig.Rumble.StrongRumble;
|
||||||
|
rightVibrationValue.AmplitudeLow *= controllerConfig.Rumble.WeakRumble;
|
||||||
|
rightVibrationValue.AmplitudeHigh *= controllerConfig.Rumble.StrongRumble;
|
||||||
|
|
||||||
_gamepad.Rumble(low, high, uint.MaxValue);
|
if (_gamepad?.HDRumble(leftVibrationValue, rightVibrationValue) == false)
|
||||||
|
{
|
||||||
|
_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}, " +
|
||||||
$"L.high.amp={leftVibrationValue.AmplitudeHigh}, " +
|
$"L.high.amp={leftVibrationValue.AmplitudeHigh}, " +
|
||||||
|
$"L.low.freq={leftVibrationValue.FrequencyLow}, " +
|
||||||
|
$"L.high.freq={leftVibrationValue.FrequencyHigh}, " +
|
||||||
$"R.low.amp={rightVibrationValue.AmplitudeLow}, " +
|
$"R.low.amp={rightVibrationValue.AmplitudeLow}, " +
|
||||||
$"R.high.amp={rightVibrationValue.AmplitudeHigh} " +
|
$"R.high.amp={rightVibrationValue.AmplitudeHigh} " +
|
||||||
$"--> ({low}, {high})");
|
$"R.low.freq={rightVibrationValue.FrequencyLow}, " +
|
||||||
|
$"R.high.freq={rightVibrationValue.FrequencyHigh}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Ryujinx.Common.Configuration.Hid;
|
using Ryujinx.Common.Configuration.Hid;
|
||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Hid;
|
||||||
using System;
|
using System;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
@@ -74,6 +75,16 @@ namespace Ryujinx.Input
|
|||||||
|
|
||||||
public void ClearLed() => SetLed(0);
|
public void ClearLed() => SetLed(0);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Starts an HD vibration effect on the gamepad if available.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left">The vibration data for the left side</param>
|
||||||
|
/// <param name="right">The vibration data for the right side</param>
|
||||||
|
bool HDRumble(VibrationValue left, VibrationValue right)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Starts a rumble effect on the gamepad.
|
/// Starts a rumble effect on the gamepad.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.SDL3-CS" />
|
<PackageReference Include="Ryujinx.SDL3-CS" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -108,6 +108,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;
|
||||||
@@ -370,6 +373,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; }
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ namespace Ryujinx.Ava
|
|||||||
CoreDumpArg = coreDumpArg;
|
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.
|
||||||
// This needs to be investigated, but calling prctl() is better than modifying system-wide settings or leaving this be.
|
// This needs to be investigated, but calling prctl() is better than modifying system-wide settings or leaving this be.
|
||||||
if (!coreDumpArg)
|
if (!coreDumpArg)
|
||||||
@@ -242,7 +242,7 @@ namespace Ryujinx.Ava
|
|||||||
ConfigurationPath = appDataConfigurationPath;
|
ConfigurationPath = appDataConfigurationPath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ConfigurationPath == null)
|
if (ConfigurationPath == null)
|
||||||
{
|
{
|
||||||
// No configuration, we load the default values and save it to disk
|
// No configuration, we load the default values and save it to disk
|
||||||
@@ -313,28 +313,28 @@ namespace Ryujinx.Ava
|
|||||||
_ => ConfigurationState.Instance.HideCursor,
|
_ => ConfigurationState.Instance.HideCursor,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check if memoryManagerMode was overridden.
|
// Check if memoryManagerMode was overridden.
|
||||||
if (CommandLineState.OverrideMemoryManagerMode is not null)
|
if (CommandLineState.OverrideMemoryManagerMode is not null)
|
||||||
if (Enum.TryParse(CommandLineState.OverrideMemoryManagerMode, true, out MemoryManagerMode result))
|
if (Enum.TryParse(CommandLineState.OverrideMemoryManagerMode, true, out MemoryManagerMode result))
|
||||||
{
|
{
|
||||||
ConfigurationState.Instance.System.MemoryManagerMode.Value = result;
|
ConfigurationState.Instance.System.MemoryManagerMode.Value = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if PPTC was overridden.
|
// Check if PPTC was overridden.
|
||||||
if (CommandLineState.OverridePPTC is not null)
|
if (CommandLineState.OverridePPTC is not null)
|
||||||
if (Enum.TryParse(CommandLineState.OverridePPTC, true, out bool result))
|
if (Enum.TryParse(CommandLineState.OverridePPTC, true, out bool result))
|
||||||
{
|
{
|
||||||
ConfigurationState.Instance.System.EnablePtc.Value = result;
|
ConfigurationState.Instance.System.EnablePtc.Value = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if region was overridden.
|
// Check if region was overridden.
|
||||||
if (CommandLineState.OverrideSystemRegion is not null)
|
if (CommandLineState.OverrideSystemRegion is not null)
|
||||||
if (Enum.TryParse(CommandLineState.OverrideSystemRegion, true, out Region result))
|
if (Enum.TryParse(CommandLineState.OverrideSystemRegion, true, out Region result))
|
||||||
{
|
{
|
||||||
ConfigurationState.Instance.System.Region.Value = result;
|
ConfigurationState.Instance.System.Region.Value = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Check if language was overridden.
|
//Check if language was overridden.
|
||||||
if (CommandLineState.OverrideSystemLanguage is not null)
|
if (CommandLineState.OverrideSystemLanguage is not null)
|
||||||
if (Enum.TryParse(CommandLineState.OverrideSystemLanguage, true, out Language result))
|
if (Enum.TryParse(CommandLineState.OverrideSystemLanguage, true, out Language result))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -46,8 +46,12 @@
|
|||||||
<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" />
|
<PackageReference Include="FluentAvaloniaUI" />
|
||||||
<PackageReference Include="CommandLineParser" />
|
<PackageReference Include="CommandLineParser" />
|
||||||
@@ -57,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" />
|
||||||
@@ -68,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>
|
||||||
|
|||||||
@@ -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,27 +611,40 @@ 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();
|
||||||
|
|
||||||
|
// 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();
|
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;
|
||||||
ConfigurationState.Instance.System.EnableDockedMode.Event -= UpdateDockedModeState;
|
ConfigurationState.Instance.System.EnableDockedMode.Event -= UpdateDockedModeState;
|
||||||
@@ -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,15 +1160,22 @@ 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");
|
||||||
|
|
||||||
ApplicationMetadata appMetadata;
|
ApplicationMetadata appMetadata;
|
||||||
|
|
||||||
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -113,6 +113,11 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||||||
/// Enables printing FS access log messages
|
/// Enables printing FS access log messages
|
||||||
/// </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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -257,6 +257,11 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||||||
/// Enables printing FS access log messages
|
/// Enables printing FS access log messages
|
||||||
/// </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
|
||||||
@@ -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) =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Gommon;
|
using Gommon;
|
||||||
using Humanizer;
|
using Humanizer;
|
||||||
|
using MsgPack;
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -23,24 +24,382 @@ namespace Ryujinx.Ava.Systems.PlayReport
|
|||||||
private static FormattedValue SkywardSwordHD_Rupees(SingleValue value)
|
private static FormattedValue SkywardSwordHD_Rupees(SingleValue value)
|
||||||
=> "rupee".ToQuantity(value.Matched.IntValue);
|
=> "rupee".ToQuantity(value.Matched.IntValue);
|
||||||
|
|
||||||
private static FormattedValue SuperMarioOdyssey_AssistMode(SingleValue value)
|
private static FormattedValue EchoesOfWisdom_Warp(SingleValue value)
|
||||||
=> value.Matched.BoxedValue is 1 ? "Playing in Assist Mode" : "Playing in Regular Mode";
|
{
|
||||||
|
FormattedValue locations = value.Matched.IntValue switch
|
||||||
|
{
|
||||||
|
// Hyrule Field
|
||||||
|
23 => "Hyrule Field: Kakariko Village",
|
||||||
|
43 => "Hyrule Field: West of Hyrule Ranch",
|
||||||
|
45 => "Hyrule Field: North of Hyrule Ranch",
|
||||||
|
25 => "Hyrule Field: Hyrule Ranch",
|
||||||
|
26 => "Hyrule Field: West of Hyrule Castle",
|
||||||
|
48 => "Hyrule Field: Haunted Grove",
|
||||||
|
24 => "Hyrule Field: Hyrule Castle",
|
||||||
|
27 => "Hyrule Field: Northern Sanctuary",
|
||||||
|
28 => "Eastern Hyrule Field: Eastern Temple",
|
||||||
|
41 => "Eastern Hyrule Field: Dampé Studio",
|
||||||
|
22 => "Lake Hylia: Great Fairy Shrine",
|
||||||
|
// Eternal Forest
|
||||||
|
47 => "Eternal Forest: Entrance",
|
||||||
|
46 => "Eternal Forest: Great Deku Tree",
|
||||||
|
752 => "Eternal Forest: Stilled Ancient Ruins (Halfway Point)",
|
||||||
|
753 => "Eternal Forest: Stilled Ancient Ruins (Null)",
|
||||||
|
// Suthorn
|
||||||
|
33 => "Suthorn Prairie: Lueburry's House",
|
||||||
|
20 => "Suthorn Prairie: Suthorn Village",
|
||||||
|
21 => "Suthorn Forest: Suthorn Ruins",
|
||||||
|
// Faron Wetlands
|
||||||
|
13 => "Faron Wetlands: Entrance",
|
||||||
|
15 => "Faron Wetlands: Scrubton",
|
||||||
|
18 => "Faron Wetlands: Blossu's House",
|
||||||
|
17 => "Faron Wetlands: Heart Lake",
|
||||||
|
852 => "Faron Wetlands: Stilled Faron Wetlands",
|
||||||
|
601 => "Faron Wetlands: Faron Temple 3F",
|
||||||
|
602 => "Faron Wetlands: Faron Temple 2F (Underwater Entrance)",
|
||||||
|
603 => "Faron Wetlands: Faron Temple 2F (West Entrance)",
|
||||||
|
604 => "Faron Wetlands: Faron Temple 2F (Cliff Entrance)",
|
||||||
|
605 => "Faron Wetlands: Faron Temple 1F (Diababa)",
|
||||||
|
606 => "Faron Wetlands: Faron Temple 1F (Gohma)",
|
||||||
|
// Jabul Waters
|
||||||
|
11 => "Jabul Waters: River Zora Village",
|
||||||
|
9 => "Jabul Waters: Crossflows Plaza",
|
||||||
|
8 => "Jabul Waters: Seesyde Village",
|
||||||
|
12 => "Jabul Waters: Sea Zora Village",
|
||||||
|
10 => "Jabul Waters: Lord Jabu-Jabu's Den",
|
||||||
|
201 => "Jabul Waters: Jabul Ruins 1F (Entrance)",
|
||||||
|
202 => "Jabul Waters: Jabul Ruins 1F (Vocavor)",
|
||||||
|
// Gerudo Desert
|
||||||
|
40 => "Gerudo Desert: Entrance",
|
||||||
|
29 => "Gerudo Desert: Oasis",
|
||||||
|
32 => "Gerudo Desert: Ancestor's Cave Of Rest",
|
||||||
|
30 => "Gerudo Desert: Gerudo Town",
|
||||||
|
31 => "Gerudo Desert: Gerudo Sanctum",
|
||||||
|
351 => "Gerudo Desert: Stilled Gerudo Sanctum",
|
||||||
|
303 => "Gerudo Desert: Gerudo Sanctum 1F (West Entrance)",
|
||||||
|
304 => "Gerudo Desert: Gerudo Sanctum 1F (East Entrance)",
|
||||||
|
301 => "Gerudo Desert: Gerudo Sanctum 2F (The Key)",
|
||||||
|
302 => "Gerudo Desert: Gerudo Sanctum 2F (The Elephant Room)",
|
||||||
|
305 => "Gerudo Desert: Gerudo Sanctum 2F (Mogryph)",
|
||||||
|
// Eldin Volcano
|
||||||
|
4 => "Eldin Volcano: Eldin Volcano Trail",
|
||||||
|
44 => "Eldin Volcano: Lava Lake",
|
||||||
|
3 => "Eldin Volcano: Goron City",
|
||||||
|
5 => "Eldin Volcano: Rock Roast Volcano",
|
||||||
|
49 => "Eldin Volcano: Crater Shortcut",
|
||||||
|
552 => "Eldin Volcano: Stilled Eldin Volcano",
|
||||||
|
501 => "Eldin Volcano: Eldin Temple 1F",
|
||||||
|
503 => "Eldin Volcano: Eldin Temple 2F",
|
||||||
|
502 => "Eldin Volcano: Eldin Temple 3F",
|
||||||
|
// Hebra Mountain
|
||||||
|
34 => "Hebra Mountain: Hebra Mountain Passage (1)",
|
||||||
|
35 => "Hebra Mountain: Sheltered Hot Spring",
|
||||||
|
36 => "Hebra Mountain: Condé's House",
|
||||||
|
38 => "Hebra Mountain: Hebra Mountain Passage (2)",
|
||||||
|
37 => "Hebra Mountain: Hebra Mountain Passage (3)",
|
||||||
|
39 => "Hebra Mountain: Summit",
|
||||||
|
652 => "Hebra Mountain: Stilled Holy Mount Lanayru",
|
||||||
|
801 => "Hebra Mountain: Lanayru Temple 1F",
|
||||||
|
802 => "Hebra Mountain: Lanayru Temple B2",
|
||||||
|
803 => "Hebra Mountain: Lanayru Temple B4",
|
||||||
|
_ => FormattedValue.ForceReset
|
||||||
|
};
|
||||||
|
|
||||||
private static FormattedValue SuperMarioOdysseyChina_AssistMode(SingleValue value)
|
return locations.Reset
|
||||||
=> value.Matched.BoxedValue is 1 ? "Playing in 帮助模式" : "Playing in 普通模式";
|
? FormattedValue.ForceReset
|
||||||
|
: $"Warped to {locations}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FormattedValue SuperMario3DAllStars(SingleValue value)
|
||||||
|
{
|
||||||
|
// TODO: Is this really necessary?
|
||||||
|
FormattedValue title = value.Matched.IntValue switch
|
||||||
|
{
|
||||||
|
1 => "Super Mario 64",
|
||||||
|
2 => "Super Mario Sunshine",
|
||||||
|
3 => "Super Mario Galaxy",
|
||||||
|
_ => FormattedValue.ForceReset
|
||||||
|
};
|
||||||
|
|
||||||
|
return title.Reset
|
||||||
|
? FormattedValue.ForceReset
|
||||||
|
: $"Playing {title}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FormattedValue SuperMario3DAllStars_MainMenu(MultiValue value)
|
||||||
|
{
|
||||||
|
int albumId = value.Matched[0].IntValue;
|
||||||
|
int songId = value.Matched[1].IntValue;
|
||||||
|
|
||||||
|
string album = value.Matched[0].IntValue switch
|
||||||
|
{
|
||||||
|
1 => "Super Mario 64 OST",
|
||||||
|
2 => "Super Mario Sunshine OST",
|
||||||
|
3 => "Super Mario Galaxy OST",
|
||||||
|
_ => "Listening to Super Mario 3D All-Stars"
|
||||||
|
};
|
||||||
|
|
||||||
|
string song = (albumId, songId) switch
|
||||||
|
{
|
||||||
|
// Super Mario 64
|
||||||
|
(1, 0) => "It's a Me, Mario!",
|
||||||
|
(1, 1) => "Title Theme",
|
||||||
|
(1, 2) => "Peach's Message",
|
||||||
|
(1, 3) => "Opening",
|
||||||
|
(1, 4) => "Super Mario 64 Main Theme",
|
||||||
|
(1, 5) => "Slider",
|
||||||
|
(1, 6) => "Inside the Castle Walls",
|
||||||
|
(1, 7) => "Looping Steps",
|
||||||
|
(1, 8) => "Dire, Dire Docks",
|
||||||
|
(1, 9) => "Lethal Lava Land",
|
||||||
|
(1, 10) => "Snow Mountain",
|
||||||
|
(1, 11) => "Haunted House",
|
||||||
|
(1, 12) => "Merry-Go-Round",
|
||||||
|
(1, 13) => "Cave Dungeon",
|
||||||
|
(1, 14) => "Piranha Plant's Lullaby",
|
||||||
|
(1, 15) => "Powerful Mario",
|
||||||
|
(1, 16) => "Metallic Mario",
|
||||||
|
(1, 17) => "File Select",
|
||||||
|
(1, 18) => "Correct Solution",
|
||||||
|
(1, 19) => "Toad's Message",
|
||||||
|
(1, 20) => "Power Star",
|
||||||
|
(1, 21) => "Race Fanfare",
|
||||||
|
(1, 22) => "Star Catch Fanfare",
|
||||||
|
(1, 23) => "Game Start",
|
||||||
|
(1, 24) => "Course Clear",
|
||||||
|
(1, 25) => "Game Over",
|
||||||
|
(1, 26) => "Stage Boss",
|
||||||
|
(1, 27) => "Koopa's Message",
|
||||||
|
(1, 28) => "Koopa's Road",
|
||||||
|
(1, 29) => "Koopa's Theme",
|
||||||
|
(1, 30) => "Koopa Clear",
|
||||||
|
(1, 31) => "Ultimate Koopa",
|
||||||
|
(1, 32) => "Ultimate Koopa Clear",
|
||||||
|
(1, 33) => "Ending Demo",
|
||||||
|
(1, 34) => "Staff Roll",
|
||||||
|
(1, 35) => "Piranha Plant's Lullaby - Piano",
|
||||||
|
|
||||||
|
// Super Mario Sunshine
|
||||||
|
(2, 0) => "Isle Delfino",
|
||||||
|
(2, 1) => "Delfino Airstrip",
|
||||||
|
(2, 2) => "Bianco Hills",
|
||||||
|
(2, 3) => "Ricco Harbor",
|
||||||
|
(2, 4) => "Gelato Beach",
|
||||||
|
(2, 5) => "Pinna Beach",
|
||||||
|
(2, 6) => "Pinna Park",
|
||||||
|
(2, 7) => "Sirena Beach",
|
||||||
|
(2, 8) => "Hotel Delfino",
|
||||||
|
(2, 9) => "Casino",
|
||||||
|
(2, 10) => "Noki Bay",
|
||||||
|
(2, 11) => "Noki Depths",
|
||||||
|
(2, 12) => "Pianta Village",
|
||||||
|
(2, 13) => "Pianta Hot Spring",
|
||||||
|
(2, 14) => "Pianta Rescue",
|
||||||
|
(2, 15) => "Pianta Village - Fluff Festival",
|
||||||
|
(2, 16) => "Underground",
|
||||||
|
(2, 17) => "Secret Course",
|
||||||
|
(2, 18) => "Secret Course - Sky and Sea",
|
||||||
|
(2, 19) => "Corona Mountain",
|
||||||
|
(2, 20) => "Mid-Boss",
|
||||||
|
(2, 21) => "Proto Piranha",
|
||||||
|
(2, 22) => "Phantamanta",
|
||||||
|
(2, 23) => "Boss Battle",
|
||||||
|
(2, 24) => "Gooper Blooper Intro",
|
||||||
|
(2, 25) => "Wiggler Intro",
|
||||||
|
(2, 26) => "Mecha-Bowser",
|
||||||
|
(2, 27) => "Bowser",
|
||||||
|
(2, 28) => "Shadow Mario",
|
||||||
|
(2, 29) => "Racing Il Piantissimo",
|
||||||
|
(2, 30) => "Event",
|
||||||
|
(2, 31) => "Timed Event",
|
||||||
|
(2, 32) => "Yoshi-Go-Round",
|
||||||
|
(2, 33) => "Title Screen",
|
||||||
|
(2, 34) => "Opening Demo",
|
||||||
|
(2, 35) => "Select Data",
|
||||||
|
(2, 36) => "Select Scenario",
|
||||||
|
(2, 37) => "Course Intro",
|
||||||
|
(2, 38) => "Course Intro - Shadow Mario",
|
||||||
|
(2, 39) => "A Shine Sprite Appears",
|
||||||
|
(2, 40) => "Shine!",
|
||||||
|
(2, 41) => "Race Fanfare",
|
||||||
|
(2, 42) => "Casino Fanfare",
|
||||||
|
(2, 43) => "Too Bad!",
|
||||||
|
(2, 44) => "Game Over",
|
||||||
|
(2, 45) => "Welcome to Isle Delfino (Movie)",
|
||||||
|
(2, 46) => "Icky Goop (Movie)",
|
||||||
|
(2, 47) => "Mario on Trial (Movie)",
|
||||||
|
(2, 48) => "How to Use FLUDD (Movie)",
|
||||||
|
(2, 49) => "Shadow Mario Appears (Movie)",
|
||||||
|
(2, 50) => "The Kidnapping of Princess Peach (Movie)",
|
||||||
|
(2, 51) => "Mecha-Bowser Rises (Movie)",
|
||||||
|
(2, 52) => "Meet Bowser Jr. (Movie)",
|
||||||
|
(2, 53) => "FLUDD Theft (Movie)",
|
||||||
|
(2, 54) => "Hot Tub Intrusion (Movie)",
|
||||||
|
(2, 55) => "Epilogue (Movie)",
|
||||||
|
(2, 56) => "Staff Credits",
|
||||||
|
(2, 57) => "Have a Relaxing Vacation!",
|
||||||
|
|
||||||
|
// Super Mario Galaxy
|
||||||
|
(3, 0) => "Overture",
|
||||||
|
(3, 1) => "The Star Festival",
|
||||||
|
(3, 2) => "Attack of the Airships",
|
||||||
|
(3, 3) => "Catastrophe",
|
||||||
|
(3, 4) => "Peach's Castle Stolen",
|
||||||
|
(3, 5) => "Enter the Galaxy",
|
||||||
|
(3, 6) => "Egg Planet",
|
||||||
|
(3, 7) => "Rosaline in the Observatory 1",
|
||||||
|
(3, 8) => "The Honeyhive",
|
||||||
|
(3, 9) => "Space Junk Road",
|
||||||
|
(3, 10) => "Battlerock Galaxy",
|
||||||
|
(3, 11) => "Beach Bowl Galaxy",
|
||||||
|
(3, 12) => "Rosalina in the Observatory 2",
|
||||||
|
(3, 13) => "Enter Bowser Jr.!",
|
||||||
|
(3, 14) => "Waltz of the Boos",
|
||||||
|
(3, 15) => "Buoy Base Galaxy",
|
||||||
|
(3, 16) => "Gusty Garden Galaxy",
|
||||||
|
(3, 17) => "Rosaline in the Observatory 3",
|
||||||
|
(3, 18) => "King Bowser",
|
||||||
|
(3, 19) => "Melty Molten Galaxy",
|
||||||
|
(3, 20) => "The Galaxy Reactor",
|
||||||
|
(3, 21) => "Final Battle with Bowser",
|
||||||
|
(3, 22) => "A New Dawn",
|
||||||
|
(3, 23) => "Birth",
|
||||||
|
(3, 24) => "Super Mario Galaxy",
|
||||||
|
(3, 25) => "Purple Comet",
|
||||||
|
(3, 26) => "Blue Sky Athletic",
|
||||||
|
(3, 27) => "Super Mario 2007",
|
||||||
|
(3, 28) => "File Select",
|
||||||
|
(3, 29) => "Luma",
|
||||||
|
(3, 30) => "Gateway Galaxy",
|
||||||
|
(3, 31) => "Stolen Grand Star",
|
||||||
|
(3, 32) => "To the Observatory Grounds 1",
|
||||||
|
(3, 33) => "Observation Dome",
|
||||||
|
(3, 34) => "Course Select",
|
||||||
|
(3, 35) => "Dino Piranha",
|
||||||
|
(3, 36) => "A Chance to Grab a Star!",
|
||||||
|
(3, 37) => "A Tense Moment",
|
||||||
|
(3, 38) => "Big Bad Bugaboom",
|
||||||
|
(3, 39) => "King Kaliente",
|
||||||
|
(3, 40) => "The Toad Brigade",
|
||||||
|
(3, 41) => "Airship Armada",
|
||||||
|
(3, 42) => "Aquatic Race",
|
||||||
|
(3, 43) => "Space Fantasy",
|
||||||
|
(3, 44) => "Megaleg",
|
||||||
|
(3, 45) => "To The Observatory Grounds 2",
|
||||||
|
(3, 46) => "Space Athletic",
|
||||||
|
(3, 47) => "Speedy Comet",
|
||||||
|
(3, 48) => "Beach Bowl Galaxy - Undersea",
|
||||||
|
(3, 49) => "Interlude",
|
||||||
|
(3, 50) => "Bowser's Stronghold Appears",
|
||||||
|
(3, 51) => "The Fiery Stronghold",
|
||||||
|
(3, 52) => "The Big Staircase",
|
||||||
|
(3, 53) => "Bowser Appears",
|
||||||
|
(3, 54) => "Star Ball",
|
||||||
|
(3, 55) => "The Library",
|
||||||
|
(3, 56) => "Buoy Base Galaxy - Undersea",
|
||||||
|
(3, 57) => "Rainbow Mario",
|
||||||
|
(3, 58) => "Chase the Bunnies",
|
||||||
|
(3, 59) => "Help!",
|
||||||
|
(3, 60) => "Major Burrows",
|
||||||
|
(3, 61) => "Pipe Interior",
|
||||||
|
(3, 62) => "Cosmic Comet",
|
||||||
|
(3, 63) => "Drip Drop Galaxy",
|
||||||
|
(3, 64) => "Kingfin",
|
||||||
|
(3, 65) => "Boo Race",
|
||||||
|
(3, 66) => "Ice Mountain",
|
||||||
|
(3, 67) => "Ice Mario",
|
||||||
|
(3, 68) => "Lava Path",
|
||||||
|
(3, 69) => "Fire Mario",
|
||||||
|
(3, 70) => "Dusty Dune Galaxy",
|
||||||
|
(3, 71) => "Heavy Metal Mecha-Bowser",
|
||||||
|
(3, 72) => "A-wa-wa-wa!",
|
||||||
|
(3, 73) => "Deep Dark Galaxy",
|
||||||
|
(3, 74) => "Kamella",
|
||||||
|
(3, 75) => "Star Ball 2",
|
||||||
|
(3, 76) => "Sad Girl",
|
||||||
|
(3, 77) => "Flying Mario",
|
||||||
|
(3, 78) => "Star Child",
|
||||||
|
(3, 79) => "A Wish",
|
||||||
|
(3, 80) => "Family",
|
||||||
|
_ => ""
|
||||||
|
};
|
||||||
|
|
||||||
|
return string.IsNullOrEmpty(song) ? FormattedValue.ForceReset : $"{album} - {song}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FormattedValue SuperMarioOdyssey(SingleValue value)
|
||||||
|
=> value.Matched.LongValue switch
|
||||||
|
{
|
||||||
|
// TODO: Needs updated for sub-areas.
|
||||||
|
2973331007 => "Cap Kingdom: Bonneton",
|
||||||
|
2661781375 => "Cascade Kingdom: Fossil Falls",
|
||||||
|
512560049 => "Sand Kingdom: Tostarena",
|
||||||
|
3079659402 => "Wooded Kingdom: Steam Gardens",
|
||||||
|
1941286268 => "Lake Kingdom: Lake Lamode",
|
||||||
|
3098209122 => "Cloud Kingdom: Nimbus Arena",
|
||||||
|
4088050842 => "Lost Kingdom: Forgotten Isle",
|
||||||
|
53003352 => "Metro Kingdom: New Donk City",
|
||||||
|
4265839612 => "Seaside Kingdom: Bubblaine",
|
||||||
|
3288863344 => "Snow Kingdom: Shiveria",
|
||||||
|
3180104973 => "Luncheon Kingdom: Mount Volbono",
|
||||||
|
2284558980 => "Ruined Kingdom: Crumbleden",
|
||||||
|
3024139598 => "Bowser's Kingdom: Bowser's Castle",
|
||||||
|
1351608174 => "Moon Kingdom: Honeylune Ridge",
|
||||||
|
1698750149 => "Dark Side: Rabbit Ridge",
|
||||||
|
3206301958 => "Darker Side: Culmina Crater",
|
||||||
|
3963002526 => "Mushroom Kingdom: Peach's Castle",
|
||||||
|
_ => FormattedValue.ForceReset
|
||||||
|
};
|
||||||
|
|
||||||
private static FormattedValue SuperMario3DWorldOrBowsersFury(SingleValue value)
|
private static FormattedValue SuperMario3DWorldOrBowsersFury(SingleValue value)
|
||||||
=> value.Matched.BoxedValue is 0 ? "Playing Super Mario 3D World" : "Playing Bowser's Fury";
|
=> value.Matched.BoxedValue is 0 ? "Playing Super Mario 3D World" : "Playing Bowser's Fury";
|
||||||
|
|
||||||
|
private static FormattedValue SuperMarioWonder(SingleValue value)
|
||||||
|
{
|
||||||
|
// TODO: Needs updated for course names.
|
||||||
|
MessagePackObject messagePackObject = value.Matched.PackedValue;
|
||||||
|
MessagePackObjectDictionary messagePackObjectDictionary = messagePackObject.AsDictionary();
|
||||||
|
|
||||||
|
int worldNumber = messagePackObjectDictionary["world_no"].AsInt32();
|
||||||
|
int courseNumber = 0;
|
||||||
|
|
||||||
|
if (messagePackObjectDictionary.TryGetValue("course_no", out MessagePackObject courseNumberVariable))
|
||||||
|
{
|
||||||
|
courseNumber = courseNumberVariable.AsInt32();
|
||||||
|
}
|
||||||
|
|
||||||
|
FormattedValue world = worldNumber switch
|
||||||
|
{
|
||||||
|
1 => "Pipe-Rock Plateau",
|
||||||
|
2 => "Petal Isles",
|
||||||
|
3 => "Fluff-Puff Peaks",
|
||||||
|
4 => "Shining Falls",
|
||||||
|
5 => "Sunbaked Desert",
|
||||||
|
6 => "Fungi Mines",
|
||||||
|
7 => "Deep Magma Bog",
|
||||||
|
9 => "Special World",
|
||||||
|
_ => FormattedValue.ForceReset
|
||||||
|
};
|
||||||
|
|
||||||
|
if (courseNumber == 0)
|
||||||
|
{
|
||||||
|
return FormattedValue.ForceReset;
|
||||||
|
}
|
||||||
|
|
||||||
|
return world.Reset
|
||||||
|
? FormattedValue.ForceReset
|
||||||
|
: $"{world}: {worldNumber}-{courseNumber}";
|
||||||
|
}
|
||||||
|
|
||||||
private static FormattedValue MarioKart8Deluxe_Mode(SingleValue value)
|
private static FormattedValue MarioKart8Deluxe_Mode(SingleValue value)
|
||||||
=> value.Matched.StringValue switch
|
=> value.Matched.StringValue switch
|
||||||
{
|
{
|
||||||
// Single Player
|
// Single Player
|
||||||
"Single" => "Single Player",
|
"Single" => "Single Player",
|
||||||
// Multiplayer
|
// Multiplayer
|
||||||
"Multi-2players" => "Multiplayer 2 Players",
|
"Multi-2players" => "Multiplayer: 2 Players",
|
||||||
"Multi-3players" => "Multiplayer 3 Players",
|
"Multi-3players" => "Multiplayer: 3 Players",
|
||||||
"Multi-4players" => "Multiplayer 4 Players",
|
"Multi-4players" => "Multiplayer: 4 Players",
|
||||||
// Wireless/LAN Play
|
// Wireless/LAN Play
|
||||||
"Local-Single" => "Wireless/LAN Play",
|
"Local-Single" => "Wireless/LAN Play",
|
||||||
"Local-2players" => "Wireless/LAN Play 2 Players",
|
"Local-2players" => "Wireless/LAN Play 2 Players",
|
||||||
@@ -62,8 +421,9 @@ namespace Ryujinx.Ava.Systems.PlayReport
|
|||||||
|
|
||||||
private static FormattedValue PokemonSV(MultiValue values)
|
private static FormattedValue PokemonSV(MultiValue values)
|
||||||
{
|
{
|
||||||
|
string region = PokemonSV_Region(values.Matched[1].ToString());
|
||||||
string playStatus = values.Matched[0].BoxedValue is 0 ? "Playing Alone" : "Playing in a group";
|
string union = values.Matched[0].BoxedValue is 0 ? "" : " with friends";
|
||||||
|
string academyName = PokemonSV_AcademyName(values.Application.Title);
|
||||||
|
|
||||||
FormattedValue locations = values.Matched[1].ToString() switch
|
FormattedValue locations = values.Matched[1].ToString() switch
|
||||||
{
|
{
|
||||||
@@ -89,18 +449,86 @@ namespace Ryujinx.Ava.Systems.PlayReport
|
|||||||
"a_w20" => "North Area Three",
|
"a_w20" => "North Area Three",
|
||||||
"a_w21" => "North Area One",
|
"a_w21" => "North Area One",
|
||||||
"a_w22" => "North Area Two",
|
"a_w22" => "North Area Two",
|
||||||
"a_w23" => "The Great Crater of Paldea",
|
"a_w23" => "Area Zero: The Great Crater of Paldea",
|
||||||
"a_w24" => "South Paldean Sea",
|
"a_w24" => "South Paldean Sea",
|
||||||
"a_w25" => "West Paldean Sea",
|
"a_w25" => "West Paldean Sea",
|
||||||
"a_w26" => "East Paldean Sea",
|
"a_w26" => "East Paldean Sea",
|
||||||
"a_w27" => "North Paldean Sea",
|
"a_w27" => "North Paldean Sea",
|
||||||
//TODO DLC Locations
|
// Naranja / Uva Academy
|
||||||
|
"a_sch_entrance01" => $"{academyName} Academy: Entrance",
|
||||||
|
"a_sch_cafe01" => $"{academyName} Academy: Cafeteria",
|
||||||
|
"a_sch_shop01" => $"{academyName} Academy: School Store",
|
||||||
|
"a_sch_room01" => $"{academyName} Academy: Home Ec Room",
|
||||||
|
"a_sch_room02" => $"{academyName} Academy: Art Room",
|
||||||
|
"a_sch_room03" => $"{academyName} Academy: Biology Lab",
|
||||||
|
"a_sch_room04" => $"{academyName} Academy: Staff Room",
|
||||||
|
"a_sch_office01" => $"{academyName} Academy: Director's Office",
|
||||||
|
"a_sch_office03" => $"{academyName} Academy: Nurse's Office",
|
||||||
|
"a_sch_ground01" => $"{academyName} Academy: School Yard",
|
||||||
|
"a_sch_class1a" => $"{academyName} Academy: Classroom 1-A",
|
||||||
|
"a_sch_class1d" => $"{academyName} Academy: Classroom 1-D",
|
||||||
|
"a_sch_class2g" => $"{academyName} Academy: Classroom 2-G",
|
||||||
|
"a_sch_dorm01" => $"{academyName} Academy: Dorm Room (Trainer)",
|
||||||
|
"a_sch_dorm02" => $"{academyName} Academy: Dorm Room (Nemona)",
|
||||||
|
"a_sch_dorm03" => $"{academyName} Academy: Dorm Room (Arven)",
|
||||||
|
"a_sch_dorm04" => $"{academyName} Academy: Dorm Room (Penny)",
|
||||||
|
// DLC
|
||||||
|
// Kitakami
|
||||||
|
"a_su0101" => "Mossui Town",
|
||||||
|
"a_su0102" => "Loyalty Plaza",
|
||||||
|
"a_su0103" => "Kitakami Hall",
|
||||||
|
"a_su0104" => "Oni Mountain",
|
||||||
|
"a_su0105" => "Infernal Pass",
|
||||||
|
"a_su0106" => "Crystal Pool",
|
||||||
|
"a_su0107" => "Wistful Fields",
|
||||||
|
"a_su0108" => "Mossfell Confluence",
|
||||||
|
"a_su0109" => "Fellhorn Gorge",
|
||||||
|
"a_su0110" => "Paradise Barrens",
|
||||||
|
"a_su0111" => "Timeless Woods",
|
||||||
|
// Blueberry Academy: School
|
||||||
|
"a_sch_2_entrance0" => "Blueberry Academy: Entrance",
|
||||||
|
"a_sch_2_clubroom" => "Blueberry Academy: League Clubroom",
|
||||||
|
"a_sch_2_class1" => "Blueberry Academy: Classroom 1-4",
|
||||||
|
"a_sch_2_class2" => "Blueberry Academy: Classroom 3-2",
|
||||||
|
"a_sch_2_shop01" => "Blueberry Academy: School Store",
|
||||||
|
"a_sch_2_cafe01" => "Blueberry Academy: Cafeteria",
|
||||||
|
"a_sch_2_dorm01" => "Blueberry Academy: Dorm Room (Trainer)",
|
||||||
|
"a_sch_2_dorm02" => "Blueberry Academy: Dorm Room (Carmine)",
|
||||||
|
// Blueberry Academy: Terrarium
|
||||||
|
"a_su0201" => "Savanna Biome",
|
||||||
|
"a_su0202" => "Coastal Biome",
|
||||||
|
"a_su0203" => "Canyon Biome",
|
||||||
|
"a_su0204" => "Polar Biome",
|
||||||
_ => FormattedValue.ForceReset
|
_ => FormattedValue.ForceReset
|
||||||
};
|
};
|
||||||
|
|
||||||
return locations.Reset
|
return locations.Reset
|
||||||
? FormattedValue.ForceReset
|
? FormattedValue.ForceReset
|
||||||
: $"{playStatus} in {locations}";
|
: $"Exploring {region}{union} | {locations}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string PokemonSV_Region(string location)
|
||||||
|
{
|
||||||
|
if (location.Contains("a_su02") || location.Contains("a_sch_2")) return "Unova";
|
||||||
|
if (location.Contains("a_su01")) return "Kitakami";
|
||||||
|
return "Paldea";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string PokemonSV_AcademyName(string title)
|
||||||
|
{
|
||||||
|
// TODO: Is this even necessary?
|
||||||
|
if (
|
||||||
|
title.Contains("Scarlet")
|
||||||
|
|| title.Contains("Escarlata")
|
||||||
|
|| title.Contains("Écarlate")
|
||||||
|
|| title.Contains("Karmesin")
|
||||||
|
|| title.Contains("Scarlatto")
|
||||||
|
|| title.Contains("スカーレット")
|
||||||
|
|| title.Contains("스칼렛")
|
||||||
|
|| title.Contains("朱")
|
||||||
|
|
||||||
|
) { return "Naranja"; }
|
||||||
|
return "Uva";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static FormattedValue SuperSmashBrosUltimate_Mode(SparseMultiValue values)
|
private static FormattedValue SuperSmashBrosUltimate_Mode(SparseMultiValue values)
|
||||||
@@ -641,5 +1069,7 @@ namespace Ryujinx.Ava.Systems.PlayReport
|
|||||||
|
|
||||||
_ => FormattedValue.ForceReset
|
_ => FormattedValue.ForceReset
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ namespace Ryujinx.Ava.Systems.PlayReport
|
|||||||
|
|
||||||
private static readonly Lazy<Analyzer> _analyzerLazy = new(() => new Analyzer()
|
private static readonly Lazy<Analyzer> _analyzerLazy = new(() => new Analyzer()
|
||||||
.AddSpec(
|
.AddSpec(
|
||||||
"01007ef00011e000",
|
"01007ef00011e000", // Breath of the Wild
|
||||||
spec => spec
|
spec => spec
|
||||||
.WithDescription("based on being in Master Mode.")
|
.WithDescription("based on being in Master Mode.")
|
||||||
.AddValueFormatter("IsHardMode", BreathOfTheWild_MasterMode)
|
.AddValueFormatter("IsHardMode", BreathOfTheWild_MasterMode)
|
||||||
@@ -22,48 +22,74 @@ namespace Ryujinx.Ava.Systems.PlayReport
|
|||||||
.AddValueFormatter("AoCVer", FormattedValue.SingleAlwaysResets)
|
.AddValueFormatter("AoCVer", FormattedValue.SingleAlwaysResets)
|
||||||
)
|
)
|
||||||
.AddSpec(
|
.AddSpec(
|
||||||
"0100f2c0115b6000",
|
"0100f2c0115b6000", // Tears of the Kingdom
|
||||||
spec => spec
|
spec => spec
|
||||||
.WithDescription("based on where you are in Hyrule (Depths, Surface, Sky).")
|
.WithDescription("based on where you are in Hyrule (Depths, Surface, Sky).")
|
||||||
.AddValueFormatter("PlayerPosY", TearsOfTheKingdom_CurrentField))
|
.AddValueFormatter("PlayerPosY", TearsOfTheKingdom_CurrentField))
|
||||||
.AddSpec(
|
.AddSpec(
|
||||||
"01002da013484000",
|
"01002da013484000", // Skyward Sword
|
||||||
spec => spec
|
spec => spec
|
||||||
.WithDescription("based on how many Rupees you have.")
|
.WithDescription("based on how many Rupees you have.")
|
||||||
.AddValueFormatter("rupees", SkywardSwordHD_Rupees))
|
.AddValueFormatter("rupees", SkywardSwordHD_Rupees))
|
||||||
|
|
||||||
.AddSpec(
|
.AddSpec(
|
||||||
"0100000000010000",
|
"01008cf01baac000", // Echoes of Wisdom
|
||||||
spec => spec
|
spec => spec
|
||||||
.WithDescription("based on if you're playing with Assist Mode.")
|
.WithDescription("based on where you've warped.")
|
||||||
.AddValueFormatter("is_kids_mode", SuperMarioOdyssey_AssistMode)
|
.AddValueFormatter("dest_index", EchoesOfWisdom_Warp)
|
||||||
|
)
|
||||||
|
|
||||||
|
.AddSpec(
|
||||||
|
"010049900f546000", // Super Mario 3D All Stars
|
||||||
|
spec => spec
|
||||||
|
.WithDescription("based on what album and track you're listening to.")
|
||||||
|
.AddMultiValueFormatter(["app_id","song_id"], SuperMario3DAllStars_MainMenu)
|
||||||
)
|
)
|
||||||
.AddSpec(
|
.AddSpec(
|
||||||
"010075000ecbe000",
|
["010049900f546001", "010049900f546002", "010049900F546003"], // Super Mario 3D All Stars
|
||||||
spec => spec
|
spec => spec
|
||||||
.WithDescription("based on if you're playing with Assist Mode.")
|
.WithDescription("based on which game you've selected to play in the collection.")
|
||||||
.AddValueFormatter("is_kids_mode", SuperMarioOdysseyChina_AssistMode)
|
.AddValueFormatter("program_id", SuperMario3DAllStars)
|
||||||
)
|
)
|
||||||
.AddSpec(
|
.AddSpec(
|
||||||
"010028600ebda000",
|
"0100000000010000", // Super Mario Odyssey
|
||||||
|
spec => spec
|
||||||
|
.WithDescription("based on what kingdom you're in.")
|
||||||
|
.AddValueFormatter("stage_name", SuperMarioOdyssey)
|
||||||
|
)
|
||||||
|
.AddSpec(
|
||||||
|
"010028600ebda000", // Super Mario 3D World + Bowser's Fury
|
||||||
spec => spec
|
spec => spec
|
||||||
.WithDescription("based on being in either Super Mario 3D World or Bowser's Fury.")
|
.WithDescription("based on being in either Super Mario 3D World or Bowser's Fury.")
|
||||||
.AddValueFormatter("mode", SuperMario3DWorldOrBowsersFury)
|
.AddValueFormatter("mode", SuperMario3DWorldOrBowsersFury)
|
||||||
)
|
)
|
||||||
|
.AddSpec(
|
||||||
|
["010049900f546000", "010049900f546001", "010049900f546002", "010049900F546003"],
|
||||||
|
spec => spec
|
||||||
|
.WithDescription("based on which game you've selected to play in the collection.")
|
||||||
|
.AddValueFormatter("program_id", SuperMario3DAllStars)
|
||||||
|
)
|
||||||
|
.AddSpec(
|
||||||
|
"010015100b514000", // Super Mario Bros. Wonder
|
||||||
|
spec => spec
|
||||||
|
.WithDescription("based on what world and course you're in.")
|
||||||
|
.AddValueFormatter("stage_info", SuperMarioWonder)
|
||||||
|
)
|
||||||
.AddSpec( // Global & China IDs
|
.AddSpec( // Global & China IDs
|
||||||
["0100152000022000", "010075100e8ec000"],
|
["0100152000022000", "010075100e8ec000"], // Mario Kart 8 Deluxe
|
||||||
spec => spec
|
spec => spec
|
||||||
.WithDescription(
|
.WithDescription(
|
||||||
"based on what modes you're selecting in the menu & whether or not you're in a race.")
|
"based on what modes you're selecting in the menu & whether or not you're in a race.")
|
||||||
.AddValueFormatter("To", MarioKart8Deluxe_Mode)
|
.AddValueFormatter("To", MarioKart8Deluxe_Mode)
|
||||||
)
|
)
|
||||||
.AddSpec(
|
.AddSpec(
|
||||||
["0100a3d008c5c000", "01008f6008c5e000"],
|
["0100a3d008c5c000", "01008f6008c5e000"], // Pokemon Scarlet/Violet
|
||||||
spec => spec
|
spec => spec
|
||||||
.WithDescription("based on if you're playing alone or in a group and what area of Paldea you're exploring.")
|
.WithDescription("based on if you're playing alone or in a group and what area of Paldea you're exploring.")
|
||||||
.AddMultiValueFormatter(["team_circle", "area_no"], PokemonSV)
|
.AddMultiValueFormatter(["team_circle", "area_no"], PokemonSV)
|
||||||
)
|
)
|
||||||
.AddSpec(
|
.AddSpec(
|
||||||
"01006a800016e000",
|
"01006a800016e000", // Super Smash Bros. Ultimate
|
||||||
spec => spec
|
spec => spec
|
||||||
.WithDescription("based on what mode you're playing, who won, and what characters were present.")
|
.WithDescription("based on what mode you're playing, who won, and what characters were present.")
|
||||||
.AddSparseMultiValueFormatter(
|
.AddSparseMultiValueFormatter(
|
||||||
@@ -83,8 +109,10 @@ namespace Ryujinx.Ava.Systems.PlayReport
|
|||||||
)
|
)
|
||||||
.AddSpec(
|
.AddSpec(
|
||||||
[
|
[
|
||||||
"0100c9a00ece6000", "01008d300c50c000", "0100d870045b6000",
|
"0100B4E00444C000", "0100d870045b6000", "01008d300c50c000", "0100c62011050000", "010012f017576000",
|
||||||
"010012f017576000", "0100c62011050000", "0100b3c014bda000"
|
/*Famicom*/ /*NES*/ /*SNES*/ /*GBC*/ /*GBA*/
|
||||||
|
"0100b3c014bda000", "0100c9a00ece6000", "0100e0601c632000", "0100bfc01d976000"
|
||||||
|
/*SEGA Genesis*/ /*N64*/ /*N64 MATURE*/ /*Virtual Boy*/
|
||||||
],
|
],
|
||||||
spec => spec
|
spec => spec
|
||||||
.WithDescription(
|
.WithDescription(
|
||||||
|
|||||||
@@ -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");
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
||||||
@@ -153,6 +140,27 @@ namespace Ryujinx.Ava.Systems
|
|||||||
ShowProgressBar = true,
|
ShowProgressBar = true,
|
||||||
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) =>
|
||||||
{
|
{
|
||||||
@@ -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);
|
|
||||||
|
TarFile.ExtractToDirectory(gzipStream, outputDirectoryPath, true);
|
||||||
TarEntry tarEntry;
|
}
|
||||||
|
|
||||||
while ((tarEntry = tarStream.GetNextEntry()) is not null)
|
[SupportedOSPlatform("linux")]
|
||||||
{
|
[SupportedOSPlatform("macos")]
|
||||||
if (tarEntry.IsDirectory)
|
private static void ExtractTarXzipFile(string archivePath, string outputDirectoryPath)
|
||||||
{
|
{
|
||||||
continue;
|
using FileStream inStream = File.OpenRead(archivePath);
|
||||||
}
|
using XZStream gzipStream = new(inStream);
|
||||||
|
|
||||||
string outPath = Path.Combine(outputDirectoryPath, tarEntry.Name);
|
TarFile.ExtractToDirectory(gzipStream, outputDirectoryPath, true);
|
||||||
|
|
||||||
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)
|
private static void ExtractZipFile(string archivePath, string outputDirectoryPath)
|
||||||
{
|
{
|
||||||
using Stream inStream = File.OpenRead(archivePath);
|
ZipFile.ExtractToDirectory(archivePath, outputDirectoryPath);
|
||||||
using ZipFile zipFile = new(inStream);
|
}
|
||||||
|
|
||||||
double count = 0;
|
private static void Extract7ZipFile(string archivePath, string outputDirectoryPath)
|
||||||
foreach (ZipEntry zipEntry in zipFile)
|
{
|
||||||
{
|
IArchive archive = ArchiveFactory.OpenArchive(archivePath);
|
||||||
count++;
|
archive.WriteToDirectory(outputDirectoryPath);
|
||||||
if (zipEntry.IsDirectory)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
string outPath = Path.Combine(outputDirectoryPath, zipEntry.Name);
|
|
||||||
|
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(outPath));
|
|
||||||
|
|
||||||
using Stream zipStream = zipFile.GetInputStream(zipEntry);
|
|
||||||
using FileStream outStream = File.OpenWrite(outPath);
|
|
||||||
|
|
||||||
zipStream.CopyTo(outStream);
|
|
||||||
|
|
||||||
File.SetLastWriteTime(outPath, DateTime.SpecifyKind(zipEntry.DateTime, DateTimeKind.Utc));
|
|
||||||
|
|
||||||
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,16 +447,20 @@ 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(() =>
|
||||||
|
|||||||
@@ -574,7 +574,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
|||||||
if (Devices.Any(controller => controller.Name == name))
|
if (Devices.Any(controller => controller.Name == name))
|
||||||
{
|
{
|
||||||
controllerNumber++;
|
controllerNumber++;
|
||||||
name = GetGamepadName(gamepad, controllerNumber);
|
name = GetUniqueGamepadName(gamepad, ref controllerNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
return name;
|
return name;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using Avalonia.Input;
|
|||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
using Avalonia.Platform.Storage;
|
using Avalonia.Platform.Storage;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using DynamicData;
|
using DynamicData;
|
||||||
@@ -656,10 +657,19 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
get => ConfigurationState.Instance.UI.ShowConsole;
|
get => ConfigurationState.Instance.UI.ShowConsole;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
bool restartRequired = value && !ConsoleHelper.HasConsoleWindow;
|
||||||
|
|
||||||
ConfigurationState.Instance.UI.ShowConsole.Value = value;
|
ConfigurationState.Instance.UI.ShowConsole.Value = value;
|
||||||
|
|
||||||
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
||||||
|
|
||||||
|
if (restartRequired)
|
||||||
|
{
|
||||||
|
NotificationHelper.ShowInformation(
|
||||||
|
LocaleManager.Instance[LocaleKeys.SettingsAppRequiredRestartMessage],
|
||||||
|
LocaleManager.Instance[LocaleKeys.SettingsShowConsoleRestartMessage]);
|
||||||
|
}
|
||||||
|
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1760,11 +1770,6 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
Logger.RestartTime();
|
Logger.RestartTime();
|
||||||
|
|
||||||
SelectedIcon ??= ApplicationLibrary.GetApplicationIcon(application.Path,
|
|
||||||
ConfigurationState.Instance.System.Language, application.Id);
|
|
||||||
|
|
||||||
PrepareLoadScreen();
|
|
||||||
|
|
||||||
RendererHostControl = new RendererHost();
|
RendererHostControl = new RendererHost();
|
||||||
|
|
||||||
AppHost = new AppHost(
|
AppHost = new AppHost(
|
||||||
@@ -1778,18 +1783,34 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
UserChannelPersistence,
|
UserChannelPersistence,
|
||||||
this,
|
this,
|
||||||
TopLevel);
|
TopLevel);
|
||||||
|
|
||||||
|
CancellationTokenSource cts = new CancellationTokenSource();
|
||||||
|
|
||||||
if (!await AppHost.LoadGuestApplication(customNacpData))
|
try
|
||||||
{
|
{
|
||||||
|
await AppHost.LoadGuestApplication(cts, customNacpData);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException exception)
|
||||||
|
{
|
||||||
|
Logger.Info?.Print(LogClass.Application,
|
||||||
|
"LoadGuestApplication was interrupted !!! " + exception.Message);
|
||||||
AppHost.DisposeContext();
|
AppHost.DisposeContext();
|
||||||
AppHost = null;
|
AppHost = null;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
cts.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
CanUpdate = false;
|
CanUpdate = false;
|
||||||
|
|
||||||
application.Name ??= AppHost.Device.Processes.ActiveApplication.Name;
|
application.Name ??= AppHost.Device.Processes.ActiveApplication.Name;
|
||||||
|
|
||||||
|
SelectedIcon ??= ApplicationLibrary.GetApplicationIcon(application.Path,
|
||||||
|
ConfigurationState.Instance.System.Language, application.Id);
|
||||||
|
|
||||||
|
PrepareLoadScreen();
|
||||||
|
|
||||||
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, application.Name);
|
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, application.Name);
|
||||||
|
|
||||||
@@ -1811,9 +1832,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
RendererHostControl.Focus();
|
RendererHostControl.Focus();
|
||||||
});
|
});
|
||||||
|
|
||||||
public static void UpdateGameMetadata(string titleId, TimeSpan playTime)
|
public static void UpdateGameMetadata(string titleId, TimeSpan playTime)
|
||||||
=> ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata => appMetadata.UpdatePostGame(playTime));
|
=> ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata => appMetadata.UpdatePostGame(playTime));
|
||||||
|
|
||||||
public void RefreshFirmwareStatus()
|
public void RefreshFirmwareStatus()
|
||||||
{
|
{
|
||||||
SystemVersion version = null;
|
SystemVersion version = null;
|
||||||
@@ -1994,7 +2015,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
LastFullscreenToggle = Environment.TickCount64;
|
LastFullscreenToggle = Environment.TickCount64;
|
||||||
|
|
||||||
if (WindowState is not WindowState.Normal)
|
if (WindowState is WindowState.FullScreen)
|
||||||
{
|
{
|
||||||
WindowState = WindowState.Normal;
|
WindowState = WindowState.Normal;
|
||||||
Window.TitleBar.ExtendsContentIntoTitleBar = !ConfigurationState.Instance.ShowOldUI;
|
Window.TitleBar.ExtendsContentIntoTitleBar = !ConfigurationState.Instance.ShowOldUI;
|
||||||
@@ -2003,21 +2024,74 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
ShowMenuAndStatusBar = true;
|
ShowMenuAndStatusBar = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (OperatingSystem.IsWindows())
|
||||||
|
{
|
||||||
|
RestoreWindowFromFullscreen();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
WindowState = WindowState.FullScreen;
|
|
||||||
Window.TitleBar.ExtendsContentIntoTitleBar = true;
|
Window.TitleBar.ExtendsContentIntoTitleBar = true;
|
||||||
|
|
||||||
if (IsGameRunning)
|
if (IsGameRunning)
|
||||||
{
|
{
|
||||||
ShowMenuAndStatusBar = false;
|
ShowMenuAndStatusBar = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (OperatingSystem.IsWindows())
|
||||||
|
{
|
||||||
|
MakeWindowFullscreen();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WindowState = WindowState.FullScreen;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IsFullScreen = WindowState is WindowState.FullScreen;
|
IsFullScreen = WindowState is WindowState.FullScreen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private nint _savedWindowStyle;
|
||||||
|
|
||||||
|
[SupportedOSPlatform("windows")]
|
||||||
|
private void MakeWindowFullscreen()
|
||||||
|
{
|
||||||
|
nint hwnd = Window.TryGetPlatformHandle()?.Handle ?? nint.Zero;
|
||||||
|
if (hwnd == nint.Zero) return;
|
||||||
|
|
||||||
|
// Save current style and placement
|
||||||
|
_savedWindowStyle = Win32NativeInterop.GetWindowLongPtrW(hwnd, Win32NativeInterop.GWL_STYLE);
|
||||||
|
|
||||||
|
// Remove window chrome: WS_OVERLAPPEDWINDOW -> WS_POPUP | WS_VISIBLE
|
||||||
|
Win32NativeInterop.SetWindowLongPtrW(hwnd, Win32NativeInterop.GWL_STYLE,
|
||||||
|
unchecked((nint)(Win32NativeInterop.WS_POPUP | Win32NativeInterop.WS_VISIBLE)));
|
||||||
|
|
||||||
|
Avalonia.Platform.Screen? screen = Window.Screens.ScreenFromVisual(Window);
|
||||||
|
int w = screen?.Bounds.Width ?? 0;
|
||||||
|
int h = screen?.Bounds.Height ?? 0;
|
||||||
|
|
||||||
|
Win32NativeInterop.SetWindowPos(hwnd, nint.Zero, 0, 0, w, h,
|
||||||
|
Win32NativeInterop.SWP_NOZORDER | Win32NativeInterop.SWP_NOACTIVATE | Win32NativeInterop.SWP_FRAMECHANGED);
|
||||||
|
|
||||||
|
WindowState = WindowState.FullScreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
[SupportedOSPlatform("windows")]
|
||||||
|
private void RestoreWindowFromFullscreen()
|
||||||
|
{
|
||||||
|
nint hwnd = Window.TryGetPlatformHandle()?.Handle ?? nint.Zero;
|
||||||
|
if (hwnd == nint.Zero) return;
|
||||||
|
|
||||||
|
// Restore original window style
|
||||||
|
Win32NativeInterop.SetWindowLongPtrW(hwnd, Win32NativeInterop.GWL_STYLE, _savedWindowStyle);
|
||||||
|
|
||||||
|
Win32NativeInterop.SetWindowPos(hwnd, nint.Zero, 0, 0, 0, 0,
|
||||||
|
Win32NativeInterop.SWP_NOZORDER | Win32NativeInterop.SWP_NOACTIVATE |
|
||||||
|
Win32NativeInterop.SWP_FRAMECHANGED | Win32NativeInterop.SWP_NOMOVE | Win32NativeInterop.SWP_NOSIZE);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public static void SaveConfig()
|
public static void SaveConfig()
|
||||||
{
|
{
|
||||||
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
||||||
|
|||||||
@@ -273,6 +273,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
public bool EnableTrace { get; set; }
|
public bool EnableTrace { get; set; }
|
||||||
public bool EnableGuest { get; set; }
|
public bool EnableGuest { get; set; }
|
||||||
public bool EnableFsAccessLog { get; set; }
|
public bool EnableFsAccessLog { get; set; }
|
||||||
|
public bool EnableNetLog { get; set; }
|
||||||
public bool EnableAvaloniaLog { get; set; }
|
public bool EnableAvaloniaLog { get; set; }
|
||||||
public bool EnableDebug { get; set; }
|
public bool EnableDebug { get; set; }
|
||||||
public bool IsOpenAlEnabled { get; set; }
|
public bool IsOpenAlEnabled { get; set; }
|
||||||
@@ -725,6 +726,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
EnableGuest = config.Logger.EnableGuest;
|
EnableGuest = config.Logger.EnableGuest;
|
||||||
EnableDebug = config.Logger.EnableDebug;
|
EnableDebug = config.Logger.EnableDebug;
|
||||||
EnableFsAccessLog = config.Logger.EnableFsAccessLog;
|
EnableFsAccessLog = config.Logger.EnableFsAccessLog;
|
||||||
|
EnableNetLog = config.Logger.EnableNetLog;
|
||||||
EnableAvaloniaLog = config.Logger.EnableAvaloniaLog;
|
EnableAvaloniaLog = config.Logger.EnableAvaloniaLog;
|
||||||
FsGlobalAccessLogMode = config.System.FsGlobalAccessLogMode;
|
FsGlobalAccessLogMode = config.System.FsGlobalAccessLogMode;
|
||||||
OpenglDebugLevel = (int)config.Logger.GraphicsDebugLevel.Value;
|
OpenglDebugLevel = (int)config.Logger.GraphicsDebugLevel.Value;
|
||||||
@@ -848,6 +850,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
config.Logger.EnableGuest.Value = EnableGuest;
|
config.Logger.EnableGuest.Value = EnableGuest;
|
||||||
config.Logger.EnableDebug.Value = EnableDebug;
|
config.Logger.EnableDebug.Value = EnableDebug;
|
||||||
config.Logger.EnableFsAccessLog.Value = EnableFsAccessLog;
|
config.Logger.EnableFsAccessLog.Value = EnableFsAccessLog;
|
||||||
|
config.Logger.EnableNetLog.Value = EnableNetLog;
|
||||||
config.Logger.EnableAvaloniaLog.Value = EnableAvaloniaLog;
|
config.Logger.EnableAvaloniaLog.Value = EnableAvaloniaLog;
|
||||||
config.System.FsGlobalAccessLogMode.Value = FsGlobalAccessLogMode;
|
config.System.FsGlobalAccessLogMode.Value = FsGlobalAccessLogMode;
|
||||||
config.Logger.GraphicsDebugLevel.Value = (GraphicsDebugLevel)OpenglDebugLevel;
|
config.Logger.GraphicsDebugLevel.Value = (GraphicsDebugLevel)OpenglDebugLevel;
|
||||||
|
|||||||
@@ -70,6 +70,10 @@
|
|||||||
ToolTip.Tip="{ext:Locale FileAccessLogTooltip}">
|
ToolTip.Tip="{ext:Locale FileAccessLogTooltip}">
|
||||||
<TextBlock Text="{ext:Locale SettingsTabLoggingEnableFsAccessLogs}" />
|
<TextBlock Text="{ext:Locale SettingsTabLoggingEnableFsAccessLogs}" />
|
||||||
</CheckBox>
|
</CheckBox>
|
||||||
|
<CheckBox IsChecked="{Binding EnableNetLog}"
|
||||||
|
ToolTip.Tip="{ext:Locale NetLogTooltip}">
|
||||||
|
<TextBlock Text="{ext:Locale SettingsTabLoggingEnableNetLogs}" />
|
||||||
|
</CheckBox>
|
||||||
<CheckBox IsChecked="{Binding EnableDebug}"
|
<CheckBox IsChecked="{Binding EnableDebug}"
|
||||||
ToolTip.Tip="{ext:Locale DebugLogTooltip}">
|
ToolTip.Tip="{ext:Locale DebugLogTooltip}">
|
||||||
<TextBlock Text="{ext:Locale SettingsTabLoggingEnableDebugLogs}" />
|
<TextBlock Text="{ext:Locale SettingsTabLoggingEnableDebugLogs}" />
|
||||||
|
|||||||
Reference in New Issue
Block a user