mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2025-06-28 13:16:24 +02:00
Compare commits
No commits in common. "master" and "Canary-1.3.80" have entirely different histories.
master
...
Canary-1.3
35 changed files with 897 additions and 627 deletions
4
.github/workflows/canary.yml
vendored
4
.github/workflows/canary.yml
vendored
|
@ -243,7 +243,3 @@ jobs:
|
||||||
- name: Send notification webhook
|
- name: Send notification webhook
|
||||||
run: |
|
run: |
|
||||||
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/canary --command=SendUpdateMessage "${{ steps.version_info.outputs.build_version }}|FF4500|${{ secrets.CANARY_DISCORD_WEBHOOK }}|https://avatars.githubusercontent.com/u/192939710?s=200&v=4|false"
|
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/canary --command=SendUpdateMessage "${{ steps.version_info.outputs.build_version }}|FF4500|${{ secrets.CANARY_DISCORD_WEBHOOK }}|https://avatars.githubusercontent.com/u/192939710?s=200&v=4|false"
|
||||||
|
|
||||||
- name: Notify update server of new builds
|
|
||||||
run: |
|
|
||||||
curl 'https://update.ryujinx.app/api/v1/admin/refresh_cache?rc=canary' -X PATCH -H 'accept: */*' -H 'Authorization: ${{ secrets.UPDATE_SERVER_ADMIN_TOKEN }}'
|
|
||||||
|
|
77
.github/workflows/release.yml
vendored
77
.github/workflows/release.yml
vendored
|
@ -14,6 +14,38 @@ env:
|
||||||
RELEASE: 1
|
RELEASE: 1
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
tag:
|
||||||
|
name: Create tag
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
steps:
|
||||||
|
- name: Get version info
|
||||||
|
id: version_info
|
||||||
|
run: |
|
||||||
|
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
|
||||||
|
echo "prev_build_version=${{ env.RYUJINX_BASE_VERSION }}.$((${{ github.run_number }} - 1))" >> $GITHUB_OUTPUT
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Create release
|
||||||
|
uses: ncipollo/release-action@v1
|
||||||
|
with:
|
||||||
|
name: ${{ steps.version_info.outputs.build_version }}
|
||||||
|
tag: ${{ steps.version_info.outputs.build_version }}
|
||||||
|
body: |
|
||||||
|
# Stable builds:
|
||||||
|
| Platform | Artifact |
|
||||||
|
|--|--|
|
||||||
|
| Windows 64-bit | [Stable Windows Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_STABLE_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip) |
|
||||||
|
| Windows ARM 64-bit | [Stable Windows ARM Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_STABLE_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-win_arm64.zip) |
|
||||||
|
| Linux 64-bit | [Stable Linux Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_STABLE_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz) |
|
||||||
|
| Linux ARM 64-bit | [Stable Linux ARM Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_STABLE_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) |
|
||||||
|
| macOS | [Stable macOS Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_STABLE_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz) |
|
||||||
|
|
||||||
|
**[Full Changelog](https://git.ryujinx.app/ryubing/ryujinx/-/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }})**
|
||||||
|
omitBodyDuringUpdate: true
|
||||||
|
owner: ${{ secrets.RC_OWNER }}
|
||||||
|
repo: ${{ secrets.RC_STABLE_NAME }}
|
||||||
|
token: ${{ secrets.ALT_RELEASE_TOKEN }}
|
||||||
|
|
||||||
release:
|
release:
|
||||||
name: Release for ${{ matrix.platform.name }}
|
name: Release for ${{ matrix.platform.name }}
|
||||||
runs-on: ${{ matrix.platform.os }}
|
runs-on: ${{ matrix.platform.os }}
|
||||||
|
@ -137,6 +169,30 @@ jobs:
|
||||||
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=UploadGenericPackage "Ryubing|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage.zsync"
|
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=UploadGenericPackage "Ryubing|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-$BUILD_VERSION-$ARCH_NAME.AppImage.zsync"
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
|
- name: Pushing new release
|
||||||
|
uses: ncipollo/release-action@v1
|
||||||
|
with:
|
||||||
|
name: ${{ steps.version_info.outputs.build_version }}
|
||||||
|
artifacts: "release_output/*.tar.gz,release_output/*.zip,release_output/*AppImage*"
|
||||||
|
tag: ${{ steps.version_info.outputs.build_version }}
|
||||||
|
body: |
|
||||||
|
# Stable builds:
|
||||||
|
| Platform | Artifact |
|
||||||
|
|--|--|
|
||||||
|
| Windows 64-bit | [Stable Windows Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_STABLE_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-win_x64.zip) |
|
||||||
|
| Windows ARM 64-bit | [Stable Windows ARM Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_STABLE_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-win_arm64.zip) |
|
||||||
|
| Linux 64-bit | [Stable Linux Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_STABLE_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz) |
|
||||||
|
| Linux ARM 64-bit | [Stable Linux ARM Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_STABLE_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) |
|
||||||
|
| macOS | [Stable macOS Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_STABLE_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz) |
|
||||||
|
|
||||||
|
**[Full Changelog](https://git.ryujinx.app/ryubing/ryujinx/-/compare/${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }})**
|
||||||
|
omitBodyDuringUpdate: true
|
||||||
|
allowUpdates: true
|
||||||
|
replacesArtifacts: true
|
||||||
|
owner: ${{ secrets.RC_OWNER }}
|
||||||
|
repo: ${{ secrets.RC_STABLE_NAME }}
|
||||||
|
token: ${{ secrets.ALT_RELEASE_TOKEN }}
|
||||||
|
|
||||||
macos_release:
|
macos_release:
|
||||||
name: Release MacOS universal
|
name: Release MacOS universal
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
|
@ -193,11 +249,26 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 0
|
./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 0
|
||||||
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=UploadGenericPackage "Ryubing|${{ steps.version_info.outputs.build_version }}|publish/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz"
|
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=UploadGenericPackage "Ryubing|${{ steps.version_info.outputs.build_version }}|publish/ryujinx-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz"
|
||||||
|
|
||||||
|
- name: Pushing new release
|
||||||
|
uses: ncipollo/release-action@v1
|
||||||
|
with:
|
||||||
|
name: ${{ steps.version_info.outputs.build_version }}
|
||||||
|
artifacts: "publish/*.tar.gz"
|
||||||
|
tag: ${{ steps.version_info.outputs.build_version }}
|
||||||
|
body: ""
|
||||||
|
omitBodyDuringUpdate: true
|
||||||
|
allowUpdates: true
|
||||||
|
replacesArtifacts: true
|
||||||
|
owner: ${{ secrets.RC_OWNER }}
|
||||||
|
repo: ${{ secrets.RC_STABLE_NAME }}
|
||||||
|
token: ${{ secrets.ALT_RELEASE_TOKEN }}
|
||||||
|
|
||||||
create_gitlab_release:
|
create_gitlab_release:
|
||||||
name: Create GitLab Release
|
name: Create GitLab Release
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
needs:
|
needs:
|
||||||
|
- tag
|
||||||
- macos_release
|
- macos_release
|
||||||
- release
|
- release
|
||||||
steps:
|
steps:
|
||||||
|
@ -228,7 +299,3 @@ jobs:
|
||||||
- name: Send notification webhook
|
- name: Send notification webhook
|
||||||
run: |
|
run: |
|
||||||
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=SendUpdateMessage "${{ steps.version_info.outputs.build_version }}|32cd32|${{ secrets.STABLE_DISCORD_WEBHOOK }}|https://avatars.githubusercontent.com/u/192939710?s=200&v=4|false"
|
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=SendUpdateMessage "${{ steps.version_info.outputs.build_version }}|32cd32|${{ secrets.STABLE_DISCORD_WEBHOOK }}|https://avatars.githubusercontent.com/u/192939710?s=200&v=4|false"
|
||||||
|
|
||||||
- name: Notify update server of new builds
|
|
||||||
run: |
|
|
||||||
curl 'https://update.ryujinx.app/api/v1/admin/refresh_cache?rc=stable' -X PATCH -H 'accept: */*' -H 'Authorization: ${{ secrets.UPDATE_SERVER_ADMIN_TOKEN }}'
|
|
||||||
|
|
|
@ -2,17 +2,20 @@
|
||||||
|
|
||||||
All updates to this Ryujinx branch will be documented in this file.
|
All updates to this Ryujinx branch will be documented in this file.
|
||||||
|
|
||||||
## [1.3.2](<https://git.ryujinx.app/ryubing/ryujinx/-/releases/1.3.2>) - 2025-06-09
|
|
||||||
|
|
||||||
## [1.3.1](<https://git.ryujinx.app/ryubing/ryujinx/-/releases/1.3.1>) - 2025-04-23
|
## [1.3.1](<https://git.ryujinx.app/ryubing/ryujinx/-/releases/1.3.1>) - 2025-04-23
|
||||||
|
A list of notable changes can be found on the release linked in the version number above.
|
||||||
|
|
||||||
## [1.2.86](<https://github.com/Ryubing/Stable-Releases/releases/tag/1.2.86>) - 2025-03-13
|
## [1.2.86](<https://github.com/Ryubing/Stable-Releases/releases/tag/1.2.86>) - 2025-03-13
|
||||||
|
A list of notable changes can be found on the release linked in the version number above.
|
||||||
|
|
||||||
## [1.2.82](<https://web.archive.org/web/20250312010534/https://github.com/Ryubing/Ryujinx/releases/tag/1.2.82>) - 2025-02-16
|
## [1.2.82](<https://web.archive.org/web/20250312010534/https://github.com/Ryubing/Ryujinx/releases/tag/1.2.82>) - 2025-02-16
|
||||||
|
A list of notable changes can be found on the release linked in the version number above.
|
||||||
|
|
||||||
## [1.2.80-81](<https://web.archive.org/web/20250302064257/https://github.com/Ryubing/Ryujinx/releases/tag/1.2.81>) - 2025-01-22
|
## [1.2.80-81](<https://web.archive.org/web/20250302064257/https://github.com/Ryubing/Ryujinx/releases/tag/1.2.81>) - 2025-01-22
|
||||||
|
A list of notable changes can be found on the release linked in the version number above.
|
||||||
|
|
||||||
## [1.2.78](<https://web.archive.org/web/20250301174537/https://github.com/Ryubing/Ryujinx/releases/tag/1.2.78>) - 2024-12-19
|
## [1.2.78](<https://web.archive.org/web/20250301174537/https://github.com/Ryubing/Ryujinx/releases/tag/1.2.78>) - 2024-12-19
|
||||||
|
A list of notable changes can be found on the release linked in the version number above.
|
||||||
|
|
||||||
## [1.2.73-1.2.76](<https://web.archive.org/web/20250209202612/https://github.com/Ryubing/Ryujinx/releases/tag/1.2.76>) - 2024-11-19
|
## [1.2.73-1.2.76](<https://web.archive.org/web/20250209202612/https://github.com/Ryubing/Ryujinx/releases/tag/1.2.76>) - 2024-11-19
|
||||||
A list of notable changes can be found on the release linked in the version number above.
|
A list of notable changes can be found on the release linked in the version number above.
|
||||||
|
@ -251,4 +254,4 @@ Added Low-power PPTC mode strings to the translation files.
|
||||||
- Autoload DLC/Updates from dir ([#12](https://github.com/GreemDev/Ryujinx/pull/12)).
|
- Autoload DLC/Updates from dir ([#12](https://github.com/GreemDev/Ryujinx/pull/12)).
|
||||||
- Changed executable icon to rainbow logo.
|
- Changed executable icon to rainbow logo.
|
||||||
- Extract Data > Logo now also extracts the square thumbnail you see for the game in the UI.
|
- Extract Data > Logo now also extracts the square thumbnail you see for the game in the UI.
|
||||||
- The "use random UUID hack" checkbox in the Amiibo screen now remembers its last state when you reopen the window in a given session.
|
- The "use random UUID hack" checkbox in the Amiibo screen now remembers its last state when you reopen the window in a given session.
|
|
@ -42,8 +42,6 @@
|
||||||
<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.20.0" />
|
<PackageVersion Include="Ryujinx.LibHac" Version="0.20.0" />
|
||||||
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.30.0-build32" />
|
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.30.0-build32" />
|
||||||
<PackageVersion Include="Ryujinx.UpdateClient" Version="1.0.29" />
|
|
||||||
<PackageVersion Include="Ryujinx.Systems.Update.Common" Version="1.0.29" />
|
|
||||||
<PackageVersion Include="Gommon" Version="2.7.1.1" />
|
<PackageVersion Include="Gommon" Version="2.7.1.1" />
|
||||||
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
||||||
<PackageVersion Include="Sep" Version="0.6.0" />
|
<PackageVersion Include="Sep" Version="0.6.0" />
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
|
|
||||||
# Ryujinx
|
# Ryujinx
|
||||||
|
|
||||||
[](https://update.ryujinx.app/latest/stable)
|
[](https://git.ryujinx.app/ryubing/ryujinx/-/releases)
|
||||||
[](https://update.ryujinx.app/latest/canary)
|
[](https://git.ryujinx.app/ryubing/canary/-/releases)
|
||||||
<br>
|
<br>
|
||||||
<a href="https://discord.gg/PEuzjrFXUA">
|
<a href="https://discord.gg/PEuzjrFXUA">
|
||||||
<img src="https://img.shields.io/discord/1294443224030511104?color=5865F2&label=Ryubing&logo=discord&logoColor=white" alt="Discord">
|
<img src="https://img.shields.io/discord/1294443224030511104?color=5865F2&label=Ryubing&logo=discord&logoColor=white" alt="Discord">
|
||||||
|
|
|
@ -77,8 +77,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Kernel.Gene
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE.Generators", "src\Ryujinx.HLE.Generators\Ryujinx.HLE.Generators.csproj", "{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE.Generators", "src\Ryujinx.HLE.Generators\Ryujinx.HLE.Generators.csproj", "{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.BuildValidationTasks", "src\Ryujinx.BuildValidationTasks\Ryujinx.BuildValidationTasks.csproj", "{4A89A234-4F19-497D-A576-DDE8CDFC5B22}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{36F870C1-3E5F-485F-B426-F0645AF78751}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{36F870C1-3E5F-485F-B426-F0645AF78751}"
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
.editorconfig = .editorconfig
|
.editorconfig = .editorconfig
|
||||||
|
@ -86,9 +84,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||||
.github\workflows\canary.yml = .github\workflows\canary.yml
|
.github\workflows\canary.yml = .github\workflows\canary.yml
|
||||||
Directory.Packages.props = Directory.Packages.props
|
Directory.Packages.props = Directory.Packages.props
|
||||||
.github\workflows\release.yml = .github\workflows\release.yml
|
.github\workflows\release.yml = .github\workflows\release.yml
|
||||||
nuget.config = nuget.config
|
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.BuildValidationTasks", "src\Ryujinx.BuildValidationTasks\Ryujinx.BuildValidationTasks.csproj", "{4A89A234-4F19-497D-A576-DDE8CDFC5B22}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2436,7 +2436,6 @@
|
||||||
0100E9C010EA8000,"Rise of Insanity",,playable,2020-08-30 15:42:14
|
0100E9C010EA8000,"Rise of Insanity",,playable,2020-08-30 15:42:14
|
||||||
01006BA00E652000,"Rise: Race The Future",,playable,2021-02-27 13:29:06
|
01006BA00E652000,"Rise: Race The Future",,playable,2021-02-27 13:29:06
|
||||||
010020C012F48000,"Rising Hell",,playable,2022-10-31 13:54:02
|
010020C012F48000,"Rising Hell",,playable,2022-10-31 13:54:02
|
||||||
0100D1801A0F4000,"Risk of Rain Returns",,playable,2025-06-28 04:24:04
|
|
||||||
010076D00E4BA000,"Risk of Rain 2",online-broken,playable,2024-03-04 17:01:05
|
010076D00E4BA000,"Risk of Rain 2",online-broken,playable,2024-03-04 17:01:05
|
||||||
0100E8300A67A000,"RISK® Global Domination",nvdec;online-broken,playable,2022-08-01 18:53:28
|
0100E8300A67A000,"RISK® Global Domination",nvdec;online-broken,playable,2022-08-01 18:53:28
|
||||||
010042500FABA000,"Ritual: Crown of Horns",,playable,2021-01-26 16:01:47
|
010042500FABA000,"Ritual: Crown of Horns",,playable,2021-01-26 16:01:47
|
||||||
|
@ -2747,7 +2746,6 @@
|
||||||
01005D701264A000,"SpyHack",,playable,2021-04-15 10:53:51
|
01005D701264A000,"SpyHack",,playable,2021-04-15 10:53:51
|
||||||
010077B00E046000,"Spyro™ Reignited Trilogy",nvdec;UE4,playable,2022-09-11 18:38:33
|
010077B00E046000,"Spyro™ Reignited Trilogy",nvdec;UE4,playable,2022-09-11 18:38:33
|
||||||
0100085012A0E000,"Squeakers",,playable,2020-12-13 12:13:05
|
0100085012A0E000,"Squeakers",,playable,2020-12-13 12:13:05
|
||||||
0100E1D01EB2E000,"Squeakross: Home Squeak Home",,playable,2025-06-16 02:02:00
|
|
||||||
010009300D31C000,"Squidgies Takeover",,playable,2020-07-20 22:28:08
|
010009300D31C000,"Squidgies Takeover",,playable,2020-07-20 22:28:08
|
||||||
0100FCD0102EC000,"Squidlit",,playable,2020-08-06 12:38:32
|
0100FCD0102EC000,"Squidlit",,playable,2020-08-06 12:38:32
|
||||||
0100EBF00E702000,"STAR OCEAN First Departure R",nvdec,playable,2021-07-05 19:29:16
|
0100EBF00E702000,"STAR OCEAN First Departure R",nvdec,playable,2021-07-05 19:29:16
|
||||||
|
@ -3018,7 +3016,6 @@
|
||||||
01009B101044C000,"The Legend of Heroes: Trails of Cold Steel III Demo",demo;nvdec,playable,2021-04-23 01:07:32
|
01009B101044C000,"The Legend of Heroes: Trails of Cold Steel III Demo",demo;nvdec,playable,2021-04-23 01:07:32
|
||||||
0100D3C010DE8000,"The Legend of Heroes: Trails of Cold Steel IV",nvdec,playable,2021-04-23 14:01:05
|
0100D3C010DE8000,"The Legend of Heroes: Trails of Cold Steel IV",nvdec,playable,2021-04-23 14:01:05
|
||||||
01005E5013862000,"THE LEGEND OF HEROES: ZERO NO KISEKI KAI [英雄傳說 零之軌跡:改]",crash,nothing,2021-09-30 14:41:07
|
01005E5013862000,"THE LEGEND OF HEROES: ZERO NO KISEKI KAI [英雄傳說 零之軌跡:改]",crash,nothing,2021-09-30 14:41:07
|
||||||
01009C901ACEE000,"The Legend of Nayuta: Boundless Trails",,ingame,2025-06-12 15:47
|
|
||||||
01008CF01BAAC000,"The Legend of Zelda Echoes of Wisdom",nvdec;ASTC;intel-vendor-bug,playable,2024-10-01 14:11:01
|
01008CF01BAAC000,"The Legend of Zelda Echoes of Wisdom",nvdec;ASTC;intel-vendor-bug,playable,2024-10-01 14:11:01
|
||||||
0100509005AF2000,"The Legend of Zelda: Breath of the Wild Demo",demo,ingame,2022-12-24 05:02:58
|
0100509005AF2000,"The Legend of Zelda: Breath of the Wild Demo",demo,ingame,2022-12-24 05:02:58
|
||||||
01007EF00011E000,"The Legend of Zelda™: Breath of the Wild",gpu;amd-vendor-bug;mac-bug,ingame,2024-09-23 19:35:46
|
01007EF00011E000,"The Legend of Zelda™: Breath of the Wild",gpu;amd-vendor-bug;mac-bug,ingame,2024-09-23 19:35:46
|
||||||
|
|
|
15
nuget.config
15
nuget.config
|
@ -6,20 +6,5 @@
|
||||||
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
|
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
|
||||||
<!-- Only needed when using pre-release versions of Ryujinx.LibHac. -->
|
<!-- Only needed when using pre-release versions of Ryujinx.LibHac. -->
|
||||||
<!--<add key="LibHacAlpha" value="https://git.ryujinx.app/api/v4/projects/17/packages/nuget/index.json" />-->
|
<!--<add key="LibHacAlpha" value="https://git.ryujinx.app/api/v4/projects/17/packages/nuget/index.json" />-->
|
||||||
<add key="Ryujinx.UpdateClient" value="https://git.ryujinx.app/api/v4/projects/71/packages/nuget/index.json" />
|
|
||||||
</packageSources>
|
</packageSources>
|
||||||
<packageSourceMapping>
|
|
||||||
<!-- key value for <packageSource> should match key values from <packageSources> element -->
|
|
||||||
<!-- These are defined and .NET still yells about multiple package sources with no mappings. Not sure what to do, this is in the docs lol -->
|
|
||||||
<packageSource key="nuget.org">
|
|
||||||
<package pattern="*" />
|
|
||||||
</packageSource>
|
|
||||||
<packageSource key="Ryujinx.UpdateClient">
|
|
||||||
<package pattern="Ryujinx.UpdateClient" />
|
|
||||||
<package pattern="Ryujinx.Systems.Update.Common" />
|
|
||||||
</packageSource>
|
|
||||||
<!--<packageSource key="LibHacAlpha">
|
|
||||||
<package pattern="Ryujinx.LibHac" />
|
|
||||||
</packageSource>-->
|
|
||||||
</packageSourceMapping>
|
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|
|
@ -195,7 +195,6 @@ namespace Ryujinx.Common
|
||||||
"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
|
||||||
"0100e1D01eb2e000", // Squeakross: Home Squeak Home
|
|
||||||
"0100e65002bb8000", // Stardew Valley
|
"0100e65002bb8000", // Stardew Valley
|
||||||
"0100d7a01b7a2000", // Star Wars: Bounty Hunter
|
"0100d7a01b7a2000", // Star Wars: Bounty Hunter
|
||||||
"0100800015926000", // Suika Game
|
"0100800015926000", // Suika Game
|
||||||
|
|
|
@ -377,7 +377,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
||||||
|
|
||||||
bool cursorVisible = false;
|
bool cursorVisible = false;
|
||||||
|
|
||||||
if (state.CursorBegin != state.CursorEnd && state.CursorEnd <= state.InputText.Length)
|
if (state.CursorBegin != state.CursorEnd)
|
||||||
{
|
{
|
||||||
Debug.Assert(state.InputText.Length > 0);
|
Debug.Assert(state.InputText.Length > 0);
|
||||||
|
|
||||||
|
|
|
@ -21,21 +21,6 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
[CommandCmif(3)] // 20.0.0+
|
|
||||||
// CreateLibraryAppletEx(u32, u32, u64) -> object<nn::am::service::ILibraryAppletAccessor>
|
|
||||||
public ResultCode CreateLibraryAppletEx(ServiceCtx context)
|
|
||||||
{
|
|
||||||
AppletId appletId = (AppletId)context.RequestData.ReadInt32();
|
|
||||||
|
|
||||||
_ = context.RequestData.ReadInt32(); // libraryAppletMode
|
|
||||||
|
|
||||||
_ = context.RequestData.ReadUInt64(); // threadId
|
|
||||||
|
|
||||||
MakeObject(context, new ILibraryAppletAccessor(appletId, context.Device.System));
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandCmif(10)]
|
[CommandCmif(10)]
|
||||||
// CreateStorage(u64) -> object<nn::am::service::IStorage>
|
// CreateStorage(u64) -> object<nn::am::service::IStorage>
|
||||||
public ResultCode CreateStorage(ServiceCtx context)
|
public ResultCode CreateStorage(ServiceCtx context)
|
||||||
|
|
|
@ -885,7 +885,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
|
||||||
// F_SETFL
|
// F_SETFL
|
||||||
else if (cmd == 0x4)
|
else if (cmd == 0x4)
|
||||||
{
|
{
|
||||||
socket.Blocking = (arg & 0x800) == 0;
|
socket.Blocking = (arg & 0x800) != 0;
|
||||||
result = 0;
|
result = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
namespace Ryujinx.Ava.Common.Models.Github
|
||||||
|
{
|
||||||
|
public class GithubReleaseAssetJsonResponse
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string State { get; set; }
|
||||||
|
public string BrowserDownloadUrl { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ava.Common.Models.Github
|
||||||
|
{
|
||||||
|
public class GithubReleasesJsonResponse
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public string TagName { get; set; }
|
||||||
|
public List<GithubReleaseAssetJsonResponse> Assets { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ava.Common.Models.Github
|
||||||
|
{
|
||||||
|
[JsonSerializable(typeof(GithubReleasesJsonResponse), GenerationMode = JsonSourceGenerationMode.Metadata)]
|
||||||
|
public partial class GithubReleasesJsonSerializerContext : JsonSerializerContext;
|
||||||
|
}
|
|
@ -25,6 +25,7 @@ using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Silk.NET.Vulkan;
|
||||||
|
|
||||||
namespace Ryujinx.Ava
|
namespace Ryujinx.Ava
|
||||||
{
|
{
|
||||||
|
@ -35,7 +36,7 @@ namespace Ryujinx.Ava
|
||||||
public static string Version { get; private set; }
|
public static string Version { get; private set; }
|
||||||
public static string ConfigurationPath { get; private set; }
|
public static string ConfigurationPath { get; private set; }
|
||||||
public static string GlobalConfigurationPath { get; private set; }
|
public static string GlobalConfigurationPath { get; private set; }
|
||||||
public static bool UseExtraConfig { get; set; }
|
public static bool UseExtraConfig{ get; private set; }
|
||||||
public static bool PreviewerDetached { get; private set; }
|
public static bool PreviewerDetached { get; private set; }
|
||||||
public static bool UseHardwareAcceleration { get; private set; }
|
public static bool UseHardwareAcceleration { get; private set; }
|
||||||
public static string BackendThreadingArg { get; private set; }
|
public static string BackendThreadingArg { get; private set; }
|
||||||
|
@ -161,7 +162,7 @@ namespace Ryujinx.Ava
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static string GetDirGameUserConfig(string gameId, bool changeFolderForGame = false)
|
public static string GetDirGameUserConfig(string gameId, bool rememberGlobalDir = false, bool changeFolderForGame = false)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(gameId))
|
if (string.IsNullOrEmpty(gameId))
|
||||||
{
|
{
|
||||||
|
@ -179,7 +180,12 @@ namespace Ryujinx.Ava
|
||||||
return gameDir;
|
return gameDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ReloadConfig()
|
public static void SetUseExtraConfig(bool value)
|
||||||
|
{
|
||||||
|
UseExtraConfig = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ReloadConfig(bool rememberGlobalDir = false)
|
||||||
{
|
{
|
||||||
|
|
||||||
string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ReleaseInformation.ConfigName);
|
string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ReleaseInformation.ConfigName);
|
||||||
|
|
|
@ -65,8 +65,6 @@
|
||||||
<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.Dependencies" 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.Systems.Update.Common" />
|
|
||||||
<PackageReference Include="securifybv.ShellLink" />
|
<PackageReference Include="securifybv.ShellLink" />
|
||||||
<PackageReference Include="Sep" />
|
<PackageReference Include="Sep" />
|
||||||
<PackageReference Include="Silk.NET.Vulkan" />
|
<PackageReference Include="Silk.NET.Vulkan" />
|
||||||
|
|
|
@ -75,7 +75,6 @@ namespace Ryujinx.Ava.Systems
|
||||||
|
|
||||||
private readonly long _ticksPerFrame;
|
private readonly long _ticksPerFrame;
|
||||||
private readonly Stopwatch _chrono;
|
private readonly Stopwatch _chrono;
|
||||||
private readonly Stopwatch _playTimer;
|
|
||||||
private long _ticks;
|
private long _ticks;
|
||||||
|
|
||||||
private readonly AccountManager _accountManager;
|
private readonly AccountManager _accountManager;
|
||||||
|
@ -176,7 +175,6 @@ namespace Ryujinx.Ava.Systems
|
||||||
|
|
||||||
_chrono = new Stopwatch();
|
_chrono = new Stopwatch();
|
||||||
_ticksPerFrame = Stopwatch.Frequency / TargetFps;
|
_ticksPerFrame = Stopwatch.Frequency / TargetFps;
|
||||||
_playTimer = new Stopwatch();
|
|
||||||
|
|
||||||
if (ApplicationPath.StartsWith("@SystemContent"))
|
if (ApplicationPath.StartsWith("@SystemContent"))
|
||||||
{
|
{
|
||||||
|
@ -567,7 +565,6 @@ namespace Ryujinx.Ava.Systems
|
||||||
public void Stop()
|
public void Stop()
|
||||||
{
|
{
|
||||||
_isActive = false;
|
_isActive = false;
|
||||||
_playTimer.Stop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Exit()
|
private void Exit()
|
||||||
|
@ -619,7 +616,7 @@ namespace Ryujinx.Ava.Systems
|
||||||
private void Dispose()
|
private void Dispose()
|
||||||
{
|
{
|
||||||
if (Device.Processes != null)
|
if (Device.Processes != null)
|
||||||
MainWindowViewModel.UpdateGameMetadata(Device.Processes.ActiveApplication.ProgramIdText, _playTimer.Elapsed);
|
MainWindowViewModel.UpdateGameMetadata(Device.Processes.ActiveApplication.ProgramIdText);
|
||||||
|
|
||||||
ConfigurationState.Instance.System.IgnoreMissingServices.Event -= UpdateIgnoreMissingServicesState;
|
ConfigurationState.Instance.System.IgnoreMissingServices.Event -= UpdateIgnoreMissingServicesState;
|
||||||
ConfigurationState.Instance.Graphics.AspectRatio.Event -= UpdateAspectRatioState;
|
ConfigurationState.Instance.Graphics.AspectRatio.Event -= UpdateAspectRatioState;
|
||||||
|
@ -638,7 +635,6 @@ namespace Ryujinx.Ava.Systems
|
||||||
_gpuCancellationTokenSource.Dispose();
|
_gpuCancellationTokenSource.Dispose();
|
||||||
|
|
||||||
_chrono.Stop();
|
_chrono.Stop();
|
||||||
_playTimer.Stop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DisposeGpu()
|
public void DisposeGpu()
|
||||||
|
@ -872,7 +868,6 @@ namespace Ryujinx.Ava.Systems
|
||||||
ApplicationLibrary.LoadAndSaveMetaData(Device.Processes.ActiveApplication.ProgramIdText,
|
ApplicationLibrary.LoadAndSaveMetaData(Device.Processes.ActiveApplication.ProgramIdText,
|
||||||
appMetadata => appMetadata.UpdatePreGame()
|
appMetadata => appMetadata.UpdatePreGame()
|
||||||
);
|
);
|
||||||
_playTimer.Start();
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -882,7 +877,6 @@ namespace Ryujinx.Ava.Systems
|
||||||
Device?.System.TogglePauseEmulation(false);
|
Device?.System.TogglePauseEmulation(false);
|
||||||
|
|
||||||
_viewModel.IsPaused = false;
|
_viewModel.IsPaused = false;
|
||||||
_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");
|
||||||
}
|
}
|
||||||
|
@ -892,7 +886,6 @@ namespace Ryujinx.Ava.Systems
|
||||||
Device?.System.TogglePauseEmulation(true);
|
Device?.System.TogglePauseEmulation(true);
|
||||||
|
|
||||||
_viewModel.IsPaused = true;
|
_viewModel.IsPaused = true;
|
||||||
_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");
|
||||||
}
|
}
|
||||||
|
@ -1159,24 +1152,6 @@ namespace Ryujinx.Ava.Systems
|
||||||
|
|
||||||
_dialogShown = true;
|
_dialogShown = true;
|
||||||
|
|
||||||
// The hard-coded hotkey mapped to exit is Escape, but it's also the same key
|
|
||||||
// that causes the dialog we launch to close (without doing anything). In release
|
|
||||||
// mode, a race is observed that between ShowExitPrompt() appearing on KeyDown
|
|
||||||
// and the ContentDialog we create seeing the key state before KeyUp. Merely waiting
|
|
||||||
// for the key to no longer be pressed appears to be insufficient.
|
|
||||||
// NB: Using _keyboardInterface.IsPressed(Key.Escape) does not currently work.
|
|
||||||
if (OperatingSystem.IsWindows())
|
|
||||||
{
|
|
||||||
while (GetAsyncKeyState(0x1B) != 0)
|
|
||||||
{
|
|
||||||
await Task.Delay(100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await Task.Delay(250);
|
|
||||||
}
|
|
||||||
|
|
||||||
shouldExit = await ContentDialogHelper.CreateStopEmulationDialog();
|
shouldExit = await ContentDialogHelper.CreateStopEmulationDialog();
|
||||||
|
|
||||||
_dialogShown = false;
|
_dialogShown = false;
|
||||||
|
|
|
@ -556,7 +556,7 @@ namespace Ryujinx.Ava.Systems.AppLibrary
|
||||||
data.Favorite = appMetadata.Favorite;
|
data.Favorite = appMetadata.Favorite;
|
||||||
data.TimePlayed = appMetadata.TimePlayed;
|
data.TimePlayed = appMetadata.TimePlayed;
|
||||||
data.LastPlayed = appMetadata.LastPlayed;
|
data.LastPlayed = appMetadata.LastPlayed;
|
||||||
data.HasIndependentConfiguration = File.Exists(Program.GetDirGameUserConfig(data.IdBaseString)); // Just check user config
|
data.HasIndependentConfiguration = File.Exists(Program.GetDirGameUserConfig(data.IdBaseString, false, false)); // Just check user config
|
||||||
}
|
}
|
||||||
|
|
||||||
data.FileExtension = Path.GetExtension(applicationPath).TrimStart('.').ToUpper();
|
data.FileExtension = Path.GetExtension(applicationPath).TrimStart('.').ToUpper();
|
||||||
|
|
|
@ -33,11 +33,19 @@ namespace Ryujinx.Ava.Systems.AppLibrary
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates <see cref="LastPlayed"/> and <see cref="TimePlayed"/>. Call this after a game ends.
|
/// Updates <see cref="LastPlayed"/> and <see cref="TimePlayed"/>. Call this after a game ends.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="playTime">The active gameplay time this past session.</param>
|
public void UpdatePostGame()
|
||||||
public void UpdatePostGame(TimeSpan playTime)
|
|
||||||
{
|
{
|
||||||
|
DateTime? prevLastPlayed = LastPlayed;
|
||||||
UpdatePreGame();
|
UpdatePreGame();
|
||||||
TimePlayed += playTime;
|
|
||||||
|
if (!prevLastPlayed.HasValue)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeSpan diff = DateTime.UtcNow - prevLastPlayed.Value;
|
||||||
|
double newTotalSeconds = TimePlayed.Add(diff).TotalSeconds;
|
||||||
|
TimePlayed = TimeSpan.FromSeconds(Math.Round(newTotalSeconds, MidpointRounding.AwayFromZero));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
190
src/Ryujinx/Systems/Updater/Updater.GitHub.cs
Normal file
190
src/Ryujinx/Systems/Updater/Updater.GitHub.cs
Normal file
|
@ -0,0 +1,190 @@
|
||||||
|
using Gommon;
|
||||||
|
using Ryujinx.Ava.Common.Locale;
|
||||||
|
using Ryujinx.Ava.Common.Models.Github;
|
||||||
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Common.Helper;
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Http.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ava.Systems
|
||||||
|
{
|
||||||
|
internal static partial class Updater
|
||||||
|
{
|
||||||
|
private static GitHubReleaseChannels.Channel? _currentGitHubReleaseChannel;
|
||||||
|
|
||||||
|
private static async Task<Optional<(Version Current, Version Incoming)>> CheckGitHubVersionAsync(bool showVersionUpToDate = false)
|
||||||
|
{
|
||||||
|
if (!Version.TryParse(Program.Version, out Version currentVersion))
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Application, $"Failed to convert the current {RyujinxApp.FullAppName} version!");
|
||||||
|
|
||||||
|
await ContentDialogHelper.CreateWarningDialog(
|
||||||
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedMessage],
|
||||||
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]);
|
||||||
|
|
||||||
|
_running = false;
|
||||||
|
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.Info?.Print(LogClass.Application, "Checking for updates from GitHub.");
|
||||||
|
|
||||||
|
// Get latest version number from GitHub API
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using HttpClient jsonClient = ConstructHttpClient();
|
||||||
|
|
||||||
|
if (_currentGitHubReleaseChannel == null)
|
||||||
|
{
|
||||||
|
GitHubReleaseChannels releaseChannels = await GitHubReleaseChannels.GetAsync(jsonClient);
|
||||||
|
|
||||||
|
_currentGitHubReleaseChannel = ReleaseInformation.IsCanaryBuild
|
||||||
|
? releaseChannels.Canary
|
||||||
|
: releaseChannels.Stable;
|
||||||
|
|
||||||
|
Logger.Info?.Print(LogClass.Application, $"Loaded GitHub release channel for '{(ReleaseInformation.IsCanaryBuild ? "canary" : "stable")}'");
|
||||||
|
|
||||||
|
_changelogUrlFormat = _currentGitHubReleaseChannel.Value.UrlFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
string fetchedJson = await jsonClient.GetStringAsync(_currentGitHubReleaseChannel.Value.GetLatestReleaseApiUrl());
|
||||||
|
GithubReleasesJsonResponse fetched = JsonHelper.Deserialize(fetchedJson, _ghSerializerContext.GithubReleasesJsonResponse);
|
||||||
|
_buildVer = fetched.TagName;
|
||||||
|
|
||||||
|
foreach (GithubReleaseAssetJsonResponse asset in fetched.Assets)
|
||||||
|
{
|
||||||
|
if (asset.Name.StartsWith("ryujinx") && asset.Name.EndsWith(_platformExt))
|
||||||
|
{
|
||||||
|
_buildUrl = asset.BrowserDownloadUrl;
|
||||||
|
|
||||||
|
if (asset.State != "uploaded")
|
||||||
|
{
|
||||||
|
if (showVersionUpToDate)
|
||||||
|
{
|
||||||
|
UserResult userResult = await ContentDialogHelper.CreateUpdaterUpToDateInfoDialog(
|
||||||
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage],
|
||||||
|
string.Empty);
|
||||||
|
|
||||||
|
if (userResult is UserResult.Ok)
|
||||||
|
{
|
||||||
|
OpenHelper.OpenUrl(_changelogUrlFormat.Format(currentVersion));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.Info?.Print(LogClass.Application, "Up to date.");
|
||||||
|
|
||||||
|
_running = false;
|
||||||
|
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If build not done, assume no new update is available.
|
||||||
|
if (_buildUrl is null)
|
||||||
|
{
|
||||||
|
if (showVersionUpToDate)
|
||||||
|
{
|
||||||
|
UserResult userResult = await ContentDialogHelper.CreateUpdaterUpToDateInfoDialog(
|
||||||
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage],
|
||||||
|
string.Empty);
|
||||||
|
|
||||||
|
if (userResult is UserResult.Ok)
|
||||||
|
{
|
||||||
|
OpenHelper.OpenUrl(_changelogUrlFormat.Format(currentVersion));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.Info?.Print(LogClass.Application, "Up to date.");
|
||||||
|
|
||||||
|
_running = false;
|
||||||
|
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Application, exception.Message);
|
||||||
|
|
||||||
|
await ContentDialogHelper.CreateErrorDialog(
|
||||||
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterFailedToGetVersionMessage]);
|
||||||
|
|
||||||
|
_running = false;
|
||||||
|
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Version.TryParse(_buildVer, out Version newVersion))
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Application, $"Failed to convert the received {RyujinxApp.FullAppName} version from GitHub!");
|
||||||
|
|
||||||
|
await ContentDialogHelper.CreateWarningDialog(
|
||||||
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedGithubMessage],
|
||||||
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]);
|
||||||
|
|
||||||
|
_running = false;
|
||||||
|
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (currentVersion, newVersion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly struct GitHubReleaseChannels
|
||||||
|
{
|
||||||
|
public static async Task<GitHubReleaseChannels> GetAsync(HttpClient httpClient)
|
||||||
|
{
|
||||||
|
ReleaseChannelPair releaseChannelPair = await httpClient.GetFromJsonAsync("https://ryujinx.app/api/release-channels", ReleaseChannelPairContext.Default.ReleaseChannelPair);
|
||||||
|
return new GitHubReleaseChannels(releaseChannelPair);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal GitHubReleaseChannels(ReleaseChannelPair channelPair)
|
||||||
|
{
|
||||||
|
Stable = new Channel(channelPair.Stable);
|
||||||
|
Canary = new Channel(channelPair.Canary);
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly Channel Stable;
|
||||||
|
public readonly Channel Canary;
|
||||||
|
|
||||||
|
public readonly struct Channel
|
||||||
|
{
|
||||||
|
public Channel(string raw)
|
||||||
|
{
|
||||||
|
string[] parts = raw.Split('/');
|
||||||
|
Owner = parts[0];
|
||||||
|
Repo = parts[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly string Owner;
|
||||||
|
public readonly string Repo;
|
||||||
|
|
||||||
|
public string UrlFormat => $"https://github.com/{ToString()}/releases/{{0}}";
|
||||||
|
|
||||||
|
public override string ToString() => $"{Owner}/{Repo}";
|
||||||
|
|
||||||
|
public string GetLatestReleaseApiUrl() =>
|
||||||
|
$"https://api.github.com/repos/{ToString()}/releases/latest";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonSerializable(typeof(ReleaseChannelPair))]
|
||||||
|
partial class ReleaseChannelPairContext : JsonSerializerContext;
|
||||||
|
|
||||||
|
class ReleaseChannelPair
|
||||||
|
{
|
||||||
|
[JsonPropertyName("stable")]
|
||||||
|
public string Stable { get; set; }
|
||||||
|
[JsonPropertyName("canary")]
|
||||||
|
public string Canary { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,28 +4,44 @@ using Ryujinx.Ava.UI.Helpers;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Helper;
|
using Ryujinx.Common.Helper;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Systems.Update.Client;
|
|
||||||
using Ryujinx.Systems.Update.Common;
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Http.Json;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.Systems
|
namespace Ryujinx.Ava.Systems
|
||||||
{
|
{
|
||||||
internal static partial class Updater
|
internal static partial class Updater
|
||||||
{
|
{
|
||||||
private static VersionResponse _versionResponse;
|
private static string CreateUpdateQueryUrl()
|
||||||
|
{
|
||||||
|
#pragma warning disable CS8524
|
||||||
|
var os = RunningPlatform.CurrentOS switch
|
||||||
|
#pragma warning restore CS8524
|
||||||
|
{
|
||||||
|
OperatingSystemType.MacOS => "mac",
|
||||||
|
OperatingSystemType.Linux => "linux",
|
||||||
|
OperatingSystemType.Windows => "win"
|
||||||
|
};
|
||||||
|
|
||||||
private static UpdateClient CreateUpdateClient()
|
var arch = RunningPlatform.Architecture switch
|
||||||
=> UpdateClient.Builder()
|
{
|
||||||
.WithServerEndpoint("https://update.ryujinx.app") // This is the default, and doesn't need to be provided; it's here for transparency.
|
Architecture.Arm64 => "arm",
|
||||||
.WithLogger((format, args, caller) =>
|
Architecture.X64 => "amd64",
|
||||||
Logger.Info?.Print(
|
_ => null
|
||||||
LogClass.Application,
|
};
|
||||||
args.Length is 0 ? format : format.Format(args),
|
|
||||||
caller: caller)
|
|
||||||
);
|
|
||||||
|
|
||||||
public static async Task<Optional<(Version Current, Version Incoming)>> CheckVersionAsync(bool showVersionUpToDate = false)
|
if (arch is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var rc = ReleaseInformation.IsCanaryBuild ? "canary" : "stable";
|
||||||
|
|
||||||
|
return $"https://update.ryujinx.app/latest/query?os={os}&arch={arch}&rc={rc}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<Optional<(Version Current, Version Incoming)>> CheckGitLabVersionAsync(bool showVersionUpToDate = false)
|
||||||
{
|
{
|
||||||
if (!Version.TryParse(Program.Version, out Version currentVersion))
|
if (!Version.TryParse(Program.Version, out Version currentVersion))
|
||||||
{
|
{
|
||||||
|
@ -41,31 +57,41 @@ namespace Ryujinx.Ava.Systems
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
using UpdateClient updateClient = CreateUpdateClient();
|
if (CreateUpdateQueryUrl() is not {} updateUrl)
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.Application, "Could not determine URL for updates.");
|
||||||
|
|
||||||
|
_running = false;
|
||||||
|
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.Info?.Print(LogClass.Application, $"Checking for updates from {updateUrl}.");
|
||||||
|
|
||||||
|
// Get latest version number from GitLab API
|
||||||
|
using HttpClient jsonClient = ConstructHttpClient();
|
||||||
|
|
||||||
|
// GitLab instance is located in Ukraine. Connection times will vary across the world.
|
||||||
|
jsonClient.Timeout = TimeSpan.FromSeconds(10);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_versionResponse = await updateClient.QueryLatestAsync(ReleaseInformation.IsCanaryBuild
|
UpdaterResponse response =
|
||||||
? ReleaseChannel.Canary
|
await jsonClient.GetFromJsonAsync(updateUrl, UpdaterResponseJsonContext.Default.UpdaterResponse);
|
||||||
: ReleaseChannel.Stable);
|
|
||||||
|
_buildVer = response.Tag;
|
||||||
|
_buildUrl = response.DownloadUrl;
|
||||||
|
_changelogUrlFormat = response.ReleaseUrlFormat;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logger.Error?.Print(LogClass.Application, $"An error occurred when requesting for updates ({e.GetType().AsFullNamePrettyString()}): {e.Message}");
|
throw new AggregateException(
|
||||||
|
$"An error occurred when parsing JSON response from API ({e.GetType().AsFullNamePrettyString()}): {e.Message}",
|
||||||
_running = false;
|
e);
|
||||||
return default;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_versionResponse == null)
|
|
||||||
{
|
|
||||||
// logging is done via the UpdateClient library
|
|
||||||
_running = false;
|
|
||||||
return default;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 (_buildUrl is null or "")
|
||||||
{
|
{
|
||||||
if (showVersionUpToDate)
|
if (showVersionUpToDate)
|
||||||
{
|
{
|
||||||
|
@ -75,7 +101,7 @@ namespace Ryujinx.Ava.Systems
|
||||||
|
|
||||||
if (userResult is UserResult.Ok)
|
if (userResult is UserResult.Ok)
|
||||||
{
|
{
|
||||||
OpenHelper.OpenUrl(_versionResponse.ReleaseUrlFormat.Format(currentVersion));
|
OpenHelper.OpenUrl(_changelogUrlFormat.Format(currentVersion));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,13 +113,13 @@ namespace Ryujinx.Ava.Systems
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!Version.TryParse(_versionResponse.Version, out Version newVersion))
|
if (!Version.TryParse(_buildVer, out Version newVersion))
|
||||||
{
|
{
|
||||||
Logger.Error?.Print(LogClass.Application,
|
Logger.Error?.Print(LogClass.Application,
|
||||||
$"Failed to convert the received {RyujinxApp.FullAppName} version from the update server!");
|
$"Failed to convert the received {RyujinxApp.FullAppName} version from GitLab!");
|
||||||
|
|
||||||
await ContentDialogHelper.CreateWarningDialog(
|
await ContentDialogHelper.CreateWarningDialog(
|
||||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedServerMessage],
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterConvertFailedGithubMessage],
|
||||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]);
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterCancelUpdateMessage]);
|
||||||
|
|
||||||
_running = false;
|
_running = false;
|
||||||
|
@ -103,5 +129,17 @@ namespace Ryujinx.Ava.Systems
|
||||||
|
|
||||||
return (currentVersion, newVersion);
|
return (currentVersion, newVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[JsonSerializable(typeof(UpdaterResponse))]
|
||||||
|
partial class UpdaterResponseJsonContext : JsonSerializerContext;
|
||||||
|
|
||||||
|
public class UpdaterResponse
|
||||||
|
{
|
||||||
|
[JsonPropertyName("tag")] public string Tag { get; set; }
|
||||||
|
[JsonPropertyName("download_url")] public string DownloadUrl { get; set; }
|
||||||
|
[JsonPropertyName("web_url")] public string ReleaseUrl { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore] public string ReleaseUrlFormat => ReleaseUrl.Replace(Tag, "{0}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,11 +5,13 @@ using ICSharpCode.SharpZipLib.GZip;
|
||||||
using ICSharpCode.SharpZipLib.Tar;
|
using ICSharpCode.SharpZipLib.Tar;
|
||||||
using ICSharpCode.SharpZipLib.Zip;
|
using ICSharpCode.SharpZipLib.Zip;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
|
using Ryujinx.Ava.Common.Models.Github;
|
||||||
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 Ryujinx.Common.Utilities;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
@ -29,17 +31,43 @@ namespace Ryujinx.Ava.Systems
|
||||||
{
|
{
|
||||||
internal static partial class Updater
|
internal static partial class Updater
|
||||||
{
|
{
|
||||||
|
private static readonly GithubReleasesJsonSerializerContext _ghSerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||||
|
|
||||||
|
private static readonly string _platformExt = BuildPlatformExtension();
|
||||||
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 const int ConnectionCount = 4;
|
||||||
|
|
||||||
|
private static string _buildVer;
|
||||||
|
private static string _buildUrl;
|
||||||
private static long _buildSize;
|
private static long _buildSize;
|
||||||
private static bool _updateSuccessful;
|
private static bool _updateSuccessful;
|
||||||
private static bool _running;
|
private static bool _running;
|
||||||
|
|
||||||
private static readonly string[] _windowsDependencyDirs = [];
|
private static readonly string[] _windowsDependencyDirs = [];
|
||||||
|
|
||||||
|
private static string _changelogUrlFormat = null;
|
||||||
|
|
||||||
|
public static async Task<Optional<(Version, Version)>> CheckVersionAsync(bool showVersionUpToDate = false)
|
||||||
|
{
|
||||||
|
Optional<(Version, Version)> versionTuple;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
versionTuple = await CheckGitLabVersionAsync(showVersionUpToDate);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.Error?.PrintMsg(LogClass.Application, "Update checking from GitLab failed; falling back to GitHub.");
|
||||||
|
Logger.Error?.PrintMsg(LogClass.Application, e.Message);
|
||||||
|
versionTuple = await CheckGitHubVersionAsync(showVersionUpToDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
return versionTuple;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static async Task BeginUpdateAsync(bool showVersionUpToDate = false)
|
public static async Task BeginUpdateAsync(bool showVersionUpToDate = false)
|
||||||
{
|
{
|
||||||
if (_running)
|
if (_running)
|
||||||
|
@ -66,7 +94,7 @@ namespace Ryujinx.Ava.Systems
|
||||||
|
|
||||||
if (userResult is UserResult.Ok)
|
if (userResult is UserResult.Ok)
|
||||||
{
|
{
|
||||||
OpenHelper.OpenUrl(_versionResponse.ReleaseUrlFormat.Format(currentVersion));
|
OpenHelper.OpenUrl(_changelogUrlFormat.Format(currentVersion));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +114,7 @@ namespace Ryujinx.Ava.Systems
|
||||||
// GitLab instance is located in Ukraine. Connection times will vary across the world.
|
// GitLab instance is located in Ukraine. Connection times will vary across the world.
|
||||||
buildSizeClient.Timeout = TimeSpan.FromSeconds(10);
|
buildSizeClient.Timeout = TimeSpan.FromSeconds(10);
|
||||||
|
|
||||||
HttpResponseMessage message = await buildSizeClient.GetAsync(new Uri(_versionResponse.ArtifactUrl), HttpCompletionOption.ResponseHeadersRead);
|
HttpResponseMessage message = await buildSizeClient.GetAsync(new Uri(_buildUrl), HttpCompletionOption.ResponseHeadersRead);
|
||||||
|
|
||||||
_buildSize = message.Content.Headers.ContentRange.Length.Value;
|
_buildSize = message.Content.Headers.ContentRange.Length.Value;
|
||||||
}
|
}
|
||||||
|
@ -116,7 +144,7 @@ namespace Ryujinx.Ava.Systems
|
||||||
switch (shouldUpdate)
|
switch (shouldUpdate)
|
||||||
{
|
{
|
||||||
case UserResult.Yes:
|
case UserResult.Yes:
|
||||||
await UpdateRyujinx(_versionResponse.ArtifactUrl);
|
await UpdateRyujinx(_buildUrl);
|
||||||
break;
|
break;
|
||||||
// Secondary button maps to no, which in this case is the show changelog button.
|
// Secondary button maps to no, which in this case is the show changelog button.
|
||||||
case UserResult.No:
|
case UserResult.No:
|
||||||
|
|
|
@ -2,104 +2,29 @@ using Avalonia.Media;
|
||||||
using Ryujinx.Ava.UI.Windows;
|
using Ryujinx.Ava.UI.Windows;
|
||||||
using Ryujinx.HLE.UI;
|
using Ryujinx.HLE.UI;
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Applet
|
namespace Ryujinx.Ava.UI.Applet
|
||||||
{
|
{
|
||||||
class AvaloniaHostUITheme : IHostUITheme
|
class AvaloniaHostUITheme(MainWindow parent) : IHostUITheme
|
||||||
{
|
{
|
||||||
private readonly MainWindow _parent;
|
public string FontFamily { get; } = OperatingSystem.IsWindows() && OperatingSystem.IsWindowsVersionAtLeast(10, 0, 22000) ? "Segoe UI Variable" : parent.FontFamily.Name;
|
||||||
|
|
||||||
public string FontFamily { get; }
|
public ThemeColor DefaultBackgroundColor { get; } = BrushToThemeColor(parent.Background);
|
||||||
public ThemeColor DefaultBackgroundColor { get; }
|
public ThemeColor DefaultForegroundColor { get; } = BrushToThemeColor(parent.Foreground);
|
||||||
public ThemeColor DefaultForegroundColor { get; }
|
public ThemeColor DefaultBorderColor { get; } = BrushToThemeColor(parent.BorderBrush);
|
||||||
public ThemeColor DefaultBorderColor { get; }
|
public ThemeColor SelectionBackgroundColor { get; } = BrushToThemeColor(parent.ViewControls.SearchBox.SelectionBrush);
|
||||||
public ThemeColor SelectionBackgroundColor { get; }
|
public ThemeColor SelectionForegroundColor { get; } = BrushToThemeColor(parent.ViewControls.SearchBox.SelectionForegroundBrush);
|
||||||
public ThemeColor SelectionForegroundColor { get; }
|
|
||||||
|
|
||||||
public AvaloniaHostUITheme(MainWindow parent)
|
|
||||||
{
|
|
||||||
_parent = parent;
|
|
||||||
|
|
||||||
// Initialize font property
|
|
||||||
FontFamily = GetSystemFontFamily();
|
|
||||||
|
|
||||||
// Initialize all properties that depend on parent
|
|
||||||
DefaultBackgroundColor = BrushToThemeColor(parent.Background);
|
|
||||||
DefaultForegroundColor = BrushToThemeColor(parent.Foreground);
|
|
||||||
DefaultBorderColor = BrushToThemeColor(parent.BorderBrush);
|
|
||||||
SelectionBackgroundColor = BrushToThemeColor(parent.ViewControls.SearchBox.SelectionBrush);
|
|
||||||
SelectionForegroundColor = BrushToThemeColor(parent.ViewControls.SearchBox.SelectionForegroundBrush);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetSystemFontFamily()
|
|
||||||
{
|
|
||||||
if (OperatingSystem.IsWindows())
|
|
||||||
{
|
|
||||||
return GetWindowsFontByLanguage();
|
|
||||||
}
|
|
||||||
else if (OperatingSystem.IsMacOS())
|
|
||||||
{
|
|
||||||
return GetMacOSFontByLanguage();
|
|
||||||
}
|
|
||||||
else // Linux and other platforms
|
|
||||||
{
|
|
||||||
return GetLinuxFontByLanguage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetWindowsFontByLanguage()
|
|
||||||
{
|
|
||||||
var culture = CultureInfo.CurrentUICulture;
|
|
||||||
string langCode = culture.Name;
|
|
||||||
|
|
||||||
return culture.TwoLetterISOLanguageName switch
|
|
||||||
{
|
|
||||||
"zh" => langCode == "zh-CN" || langCode == "zh-Hans" || langCode == "zh-SG"
|
|
||||||
? "Microsoft YaHei UI" // Simplified Chinese
|
|
||||||
: "Microsoft JhengHei UI", // Traditional Chinese
|
|
||||||
|
|
||||||
"ja" => "Yu Gothic UI", // Japanese
|
|
||||||
"ko" => "Malgun Gothic", // Korean
|
|
||||||
_ => OperatingSystem.IsWindowsVersionAtLeast(10, 0, 22000)
|
|
||||||
? "Segoe UI Variable" // Other languages - Windows 11+
|
|
||||||
: _parent.FontFamily.Name // Fallback to parent window font
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetMacOSFontByLanguage()
|
|
||||||
{
|
|
||||||
return CultureInfo.CurrentUICulture.TwoLetterISOLanguageName switch
|
|
||||||
{
|
|
||||||
"zh" => "PingFang SC", // Chinese (both simplified and traditional)
|
|
||||||
"ja" => "Hiragino Sans", // Japanese
|
|
||||||
"ko" => "Apple SD Gothic Neo", // Korean
|
|
||||||
_ => _parent.FontFamily.Name // Fallback to parent window font
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetLinuxFontByLanguage()
|
|
||||||
{
|
|
||||||
return CultureInfo.CurrentUICulture.TwoLetterISOLanguageName switch
|
|
||||||
{
|
|
||||||
"zh" => "Noto Sans CJK SC", // Chinese
|
|
||||||
"ja" => "Noto Sans CJK JP", // Japanese
|
|
||||||
"ko" => "Noto Sans CJK KR", // Korean
|
|
||||||
_ => _parent.FontFamily.Name // Fallback to parent window font
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ThemeColor BrushToThemeColor(IBrush brush)
|
private static ThemeColor BrushToThemeColor(IBrush brush)
|
||||||
{
|
{
|
||||||
if (brush is SolidColorBrush solidColor)
|
if (brush is SolidColorBrush solidColor)
|
||||||
{
|
{
|
||||||
return new ThemeColor(
|
return new ThemeColor((float)solidColor.Color.A / 255,
|
||||||
(float)solidColor.Color.A / 255,
|
|
||||||
(float)solidColor.Color.R / 255,
|
(float)solidColor.Color.R / 255,
|
||||||
(float)solidColor.Color.G / 255,
|
(float)solidColor.Color.G / 255,
|
||||||
(float)solidColor.Color.B / 255
|
(float)solidColor.Color.B / 255);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ThemeColor();
|
return new ThemeColor();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,8 +110,5 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||||
|
|
||||||
[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)]
|
|
||||||
public static partial ushort GetAsyncKeyState(int nVirtKey);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,9 +50,6 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||||
private string _controllerImage;
|
private string _controllerImage;
|
||||||
private int _device;
|
private int _device;
|
||||||
private object _configViewModel;
|
private object _configViewModel;
|
||||||
private bool _isChangeTrackingActive;
|
|
||||||
private string _chosenProfile;
|
|
||||||
[ObservableProperty] private bool _isModified;
|
|
||||||
[ObservableProperty] private string _profileName;
|
[ObservableProperty] private string _profileName;
|
||||||
[ObservableProperty] private bool _notificationIsVisible; // Automatically call the NotificationView property with OnPropertyChanged()
|
[ObservableProperty] private bool _notificationIsVisible; // Automatically call the NotificationView property with OnPropertyChanged()
|
||||||
[ObservableProperty] private string _notificationText; // Automatically call the NotificationText property with OnPropertyChanged()
|
[ObservableProperty] private string _notificationText; // Automatically call the NotificationText property with OnPropertyChanged()
|
||||||
|
@ -87,7 +84,9 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||||
public AvaloniaList<string> ProfilesList { get; set; }
|
public AvaloniaList<string> ProfilesList { get; set; }
|
||||||
public AvaloniaList<string> DeviceList { get; set; }
|
public AvaloniaList<string> DeviceList { get; set; }
|
||||||
|
|
||||||
public bool UseGlobalConfig;
|
public bool _useExtraConfig;
|
||||||
|
|
||||||
|
public bool _useGlobalInput;
|
||||||
|
|
||||||
// XAML Flags
|
// XAML Flags
|
||||||
public bool ShowSettings => _device > 0;
|
public bool ShowSettings => _device > 0;
|
||||||
|
@ -99,16 +98,31 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||||
public bool HasLed => SelectedGamepad.Features.HasFlag(GamepadFeaturesFlag.Led);
|
public bool HasLed => SelectedGamepad.Features.HasFlag(GamepadFeaturesFlag.Led);
|
||||||
public bool CanClearLed => SelectedGamepad.Name.ContainsIgnoreCase("DualSense");
|
public bool CanClearLed => SelectedGamepad.Name.ContainsIgnoreCase("DualSense");
|
||||||
|
|
||||||
public event Action NotifyChangesEvent;
|
public bool _isChangeTrackingActive;
|
||||||
|
|
||||||
public string ChosenProfile
|
public bool _isModified;
|
||||||
|
|
||||||
|
public bool IsModified
|
||||||
{
|
{
|
||||||
get => _chosenProfile;
|
get => _isModified;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_isModified = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public event Action NotifyChangesEvent;
|
||||||
|
|
||||||
|
public string _profileChoose;
|
||||||
|
public string ProfileChoose
|
||||||
|
{
|
||||||
|
get => _profileChoose;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
// When you select a profile, the settings from the profile will be applied.
|
// When you select a profile, the settings from the profile will be applied.
|
||||||
// To save the settings, you still need to click the apply button
|
// To save the settings, you still need to click the apply button
|
||||||
_chosenProfile = value;
|
_profileChoose = value;
|
||||||
LoadProfile();
|
LoadProfile();
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
}
|
}
|
||||||
|
@ -280,12 +294,14 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||||
|
|
||||||
public InputConfig Config { get; set; }
|
public InputConfig Config { get; set; }
|
||||||
|
|
||||||
public InputViewModel(UserControl owner, bool useGlobal = false) : this()
|
public InputViewModel(UserControl owner, bool UseGlobalInput = false) : this()
|
||||||
{
|
{
|
||||||
if (Program.PreviewerDetached)
|
if (Program.PreviewerDetached)
|
||||||
{
|
{
|
||||||
_mainWindow = RyujinxApp.MainWindow;
|
_mainWindow = RyujinxApp.MainWindow;
|
||||||
|
|
||||||
|
_useExtraConfig = Program.UseExtraConfig;
|
||||||
|
|
||||||
AvaloniaKeyboardDriver = new AvaloniaKeyboardDriver(owner);
|
AvaloniaKeyboardDriver = new AvaloniaKeyboardDriver(owner);
|
||||||
|
|
||||||
_mainWindow.InputManager.GamepadDriver.OnGamepadConnected += HandleOnGamepadConnected;
|
_mainWindow.InputManager.GamepadDriver.OnGamepadConnected += HandleOnGamepadConnected;
|
||||||
|
@ -293,7 +309,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||||
|
|
||||||
_mainWindow.ViewModel.AppHost?.NpadManager.BlockInputUpdates();
|
_mainWindow.ViewModel.AppHost?.NpadManager.BlockInputUpdates();
|
||||||
|
|
||||||
UseGlobalConfig = useGlobal;
|
_useGlobalInput = UseGlobalInput;
|
||||||
|
|
||||||
_isLoaded = false;
|
_isLoaded = false;
|
||||||
|
|
||||||
|
@ -331,7 +347,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||||
|
|
||||||
private void LoadConfiguration(InputConfig inputConfig = null)
|
private void LoadConfiguration(InputConfig inputConfig = null)
|
||||||
{
|
{
|
||||||
if (UseGlobalConfig && Program.UseExtraConfig)
|
if (_useGlobalInput && _useExtraConfig)
|
||||||
{
|
{
|
||||||
Config = inputConfig ?? ConfigurationState.InstanceExtra.Hid.InputConfig.Value.FirstOrDefault(inputConfig => inputConfig.PlayerIndex == _playerId);
|
Config = inputConfig ?? ConfigurationState.InstanceExtra.Hid.InputConfig.Value.FirstOrDefault(inputConfig => inputConfig.PlayerIndex == _playerId);
|
||||||
}
|
}
|
||||||
|
@ -903,7 +919,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||||
|
|
||||||
LoadProfiles();
|
LoadProfiles();
|
||||||
|
|
||||||
ChosenProfile = ProfileName; // Show new profile
|
ProfileChoose = ProfileName; // Show new profile
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -937,7 +953,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||||
|
|
||||||
LoadProfiles();
|
LoadProfiles();
|
||||||
|
|
||||||
ChosenProfile = ProfilesList[0].ToString(); // Show default profile
|
ProfileChoose = ProfilesList[0].ToString(); // Show default profile
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -967,7 +983,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||||
|
|
||||||
List<InputConfig> newConfig = [];
|
List<InputConfig> newConfig = [];
|
||||||
|
|
||||||
if (UseGlobalConfig && Program.UseExtraConfig)
|
if (_useGlobalInput && _useExtraConfig)
|
||||||
{
|
{
|
||||||
newConfig.AddRange(ConfigurationState.InstanceExtra.Hid.InputConfig.Value);
|
newConfig.AddRange(ConfigurationState.InstanceExtra.Hid.InputConfig.Value);
|
||||||
}
|
}
|
||||||
|
@ -1019,7 +1035,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||||
// NOTE: Do not modify InputConfig.Value directly as other code depends on the on-change event.
|
// NOTE: Do not modify InputConfig.Value directly as other code depends on the on-change event.
|
||||||
_mainWindow.ViewModel.AppHost?.NpadManager.ReloadConfiguration(newConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse);
|
_mainWindow.ViewModel.AppHost?.NpadManager.ReloadConfiguration(newConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse);
|
||||||
|
|
||||||
if (UseGlobalConfig && Program.UseExtraConfig)
|
if (_useGlobalInput && _useExtraConfig)
|
||||||
{
|
{
|
||||||
// In User Settings when "Use Global Input" is enabled, it saves global input to global setting
|
// In User Settings when "Use Global Input" is enabled, it saves global input to global setting
|
||||||
ConfigurationState.InstanceExtra.Hid.InputConfig.Value = newConfig;
|
ConfigurationState.InstanceExtra.Hid.InputConfig.Value = newConfig;
|
||||||
|
@ -1030,6 +1046,12 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||||
ConfigurationState.Instance.Hid.InputConfig.Value = newConfig;
|
ConfigurationState.Instance.Hid.InputConfig.Value = newConfig;
|
||||||
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void NotifyChange(string property)
|
||||||
|
{
|
||||||
|
OnPropertyChanged(property);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void NotifyChanges()
|
public void NotifyChanges()
|
||||||
|
|
|
@ -310,15 +310,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
|
|
||||||
private void TotalTimePlayed_Recalculated(Optional<TimeSpan> ts)
|
private void TotalTimePlayed_Recalculated(Optional<TimeSpan> ts)
|
||||||
{
|
{
|
||||||
if (ts.HasValue)
|
|
||||||
{
|
|
||||||
var formattedPlayTime = ValueFormatUtils.FormatTimeSpan(ts.Value);
|
|
||||||
LocaleManager.Instance.SetDynamicValues(LocaleKeys.GameListLabelTotalTimePlayed, formattedPlayTime);
|
|
||||||
ShowTotalTimePlayed = formattedPlayTime != string.Empty;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ShowTotalTimePlayed = ts.HasValue;
|
ShowTotalTimePlayed = ts.HasValue;
|
||||||
|
|
||||||
|
if (ts.HasValue)
|
||||||
|
LocaleManager.Instance.SetDynamicValues(LocaleKeys.GameListLabelTotalTimePlayed, ValueFormatUtils.FormatTimeSpan(ts.Value));
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ShowTotalTimePlayed
|
public bool ShowTotalTimePlayed
|
||||||
|
@ -339,6 +334,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
_listSelectedApplication = value;
|
_listSelectedApplication = value;
|
||||||
|
|
||||||
if (_listSelectedApplication != null && ListAppContextMenu == null)
|
if (_listSelectedApplication != null && ListAppContextMenu == null)
|
||||||
|
|
||||||
ListAppContextMenu = new ApplicationContextMenu();
|
ListAppContextMenu = new ApplicationContextMenu();
|
||||||
else if (_listSelectedApplication == null && ListAppContextMenu != null)
|
else if (_listSelectedApplication == null && ListAppContextMenu != null)
|
||||||
ListAppContextMenu = null!;
|
ListAppContextMenu = null!;
|
||||||
|
@ -1579,14 +1575,16 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
public bool InitializeUserConfig(ApplicationData application)
|
public bool InitializeUserConfig(ApplicationData application)
|
||||||
{
|
{
|
||||||
// Code where conditions will be met before loading the user configuration (Global Config)
|
// Code where conditions will be met before loading the user configuration (Global Config)
|
||||||
string backendThreadingInit = Program.BackendThreadingArg ?? ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString();
|
string BackendThreadingInit = Program.BackendThreadingArg;
|
||||||
|
|
||||||
|
BackendThreadingInit ??= ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString();
|
||||||
|
|
||||||
// If a configuration is found in the "/games/xxxxxxxxxxxxxx" folder, the program will load the user setting.
|
// If a configuration is found in the "/games/xxxxxxxxxxxxxx" folder, the program will load the user setting.
|
||||||
string idGame = application.IdBaseString;
|
string idGame = application.IdBaseString;
|
||||||
if (ConfigurationFileFormat.TryLoad(Program.GetDirGameUserConfig(idGame), out ConfigurationFileFormat configurationFileFormat))
|
if (ConfigurationFileFormat.TryLoad(Program.GetDirGameUserConfig(idGame), out ConfigurationFileFormat configurationFileFormat))
|
||||||
{
|
{
|
||||||
// Loads the user configuration, having previously changed the global configuration to the user configuration
|
// Loads the user configuration, having previously changed the global configuration to the user configuration
|
||||||
ConfigurationState.Instance.Load(configurationFileFormat, Program.GetDirGameUserConfig(idGame, true), idGame);
|
ConfigurationState.Instance.Load(configurationFileFormat, Program.GetDirGameUserConfig(idGame, true, true), idGame);
|
||||||
|
|
||||||
if (ConfigurationFileFormat.TryLoad(Program.GlobalConfigurationPath, out ConfigurationFileFormat configurationFileFormatExtra))
|
if (ConfigurationFileFormat.TryLoad(Program.GlobalConfigurationPath, out ConfigurationFileFormat configurationFileFormatExtra))
|
||||||
{
|
{
|
||||||
|
@ -1597,13 +1595,15 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
}
|
}
|
||||||
|
|
||||||
// Code where conditions will be executed after loading user configuration
|
// Code where conditions will be executed after loading user configuration
|
||||||
if (ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString() != backendThreadingInit)
|
if (ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString() != BackendThreadingInit)
|
||||||
{
|
{
|
||||||
Rebooter.RebootAppWithGame(application.Path,
|
|
||||||
[
|
List<string> Arguments = new()
|
||||||
"--bt",
|
{
|
||||||
ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString()
|
"--bt", ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString() // BackendThreading
|
||||||
]);
|
};
|
||||||
|
|
||||||
|
Rebooter.RebootAppWithGame(application.Path, Arguments);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1692,8 +1692,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
RendererHostControl.Focus();
|
RendererHostControl.Focus();
|
||||||
});
|
});
|
||||||
|
|
||||||
public static void UpdateGameMetadata(string titleId, TimeSpan playTime)
|
public static void UpdateGameMetadata(string titleId)
|
||||||
=> ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata => appMetadata.UpdatePostGame(playTime));
|
=> ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata => appMetadata.UpdatePostGame());
|
||||||
|
|
||||||
public void RefreshFirmwareStatus()
|
public void RefreshFirmwareStatus()
|
||||||
{
|
{
|
||||||
|
@ -1998,7 +1998,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
|
|
||||||
// just checking for file presence
|
// just checking for file presence
|
||||||
viewModel.SelectedApplication.HasIndependentConfiguration = File.Exists(
|
viewModel.SelectedApplication.HasIndependentConfiguration = File.Exists(
|
||||||
Program.GetDirGameUserConfig(viewModel.SelectedApplication.IdString));
|
Program.GetDirGameUserConfig(viewModel.SelectedApplication.IdString, false, false));
|
||||||
|
|
||||||
viewModel.RefreshView();
|
viewModel.RefreshView();
|
||||||
});
|
});
|
||||||
|
|
|
@ -147,19 +147,19 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
public bool DisableInputWhenOutOfFocus { get; set; }
|
public bool DisableInputWhenOutOfFocus { get; set; }
|
||||||
public int FocusLostActionType { get; set; }
|
public int FocusLostActionType { get; set; }
|
||||||
|
|
||||||
public bool UseGlobalInputConfig
|
public bool EnableConfigGlobal
|
||||||
{
|
{
|
||||||
get => _useInputGlobalConfig;
|
get => _useInputGlobalConfig;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_useInputGlobalConfig = value;
|
_useInputGlobalConfig = value;
|
||||||
LocalGlobalInputSwitchEvent?.Invoke(_useInputGlobalConfig);
|
LocalGlobalInputSwitchEvent?.Invoke(_useInputGlobalConfig);
|
||||||
OnPropertyChanged(nameof(InputPanelOpacity));
|
OnPropertyChanged(nameof(PanelOpacityInput));
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public double InputPanelOpacity => UseGlobalInputConfig ? 0.5 : 1;
|
public double PanelOpacityInput => EnableConfigGlobal ? 0.5 : 1;
|
||||||
|
|
||||||
public VSyncMode VSyncMode
|
public VSyncMode VSyncMode
|
||||||
{
|
{
|
||||||
|
@ -387,7 +387,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
|
|
||||||
public bool IsInvalidLdnPassphraseVisible { get; set; }
|
public bool IsInvalidLdnPassphraseVisible { get; set; }
|
||||||
|
|
||||||
public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this()
|
public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this(false)
|
||||||
{
|
{
|
||||||
_virtualFileSystem = virtualFileSystem;
|
_virtualFileSystem = virtualFileSystem;
|
||||||
_contentManager = contentManager;
|
_contentManager = contentManager;
|
||||||
|
@ -408,7 +408,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
string gameName,
|
string gameName,
|
||||||
string gameId,
|
string gameId,
|
||||||
byte[] gameIconData,
|
byte[] gameIconData,
|
||||||
bool customConfig) : this()
|
bool enableToLoadCustomConfig) : this(enableToLoadCustomConfig)
|
||||||
{
|
{
|
||||||
_virtualFileSystem = virtualFileSystem;
|
_virtualFileSystem = virtualFileSystem;
|
||||||
_contentManager = contentManager;
|
_contentManager = contentManager;
|
||||||
|
@ -424,11 +424,11 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
_gameTitle = gameName;
|
_gameTitle = gameName;
|
||||||
_gameId = gameId;
|
_gameId = gameId;
|
||||||
|
|
||||||
if (customConfig) // During the game. If there is no user config, then load the global config window
|
if (enableToLoadCustomConfig) // During the game. If there is no user config, then load the global config window
|
||||||
{
|
{
|
||||||
string gameDir = Program.GetDirGameUserConfig(gameId, true);
|
string gameDir = Program.GetDirGameUserConfig(gameId, true, true);
|
||||||
|
|
||||||
Program.UseExtraConfig = true;
|
Program.SetUseExtraConfig(true);
|
||||||
|
|
||||||
if (ConfigurationFileFormat.TryLoad(Program.GlobalConfigurationPath, out ConfigurationFileFormat configurationFileFormatExtra))
|
if (ConfigurationFileFormat.TryLoad(Program.GlobalConfigurationPath, out ConfigurationFileFormat configurationFileFormatExtra))
|
||||||
{
|
{
|
||||||
|
@ -451,7 +451,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public SettingsViewModel()
|
public SettingsViewModel(bool noLoadGlobalConfig = false)
|
||||||
{
|
{
|
||||||
GameDirectories = [];
|
GameDirectories = [];
|
||||||
AutoloadDirectories = [];
|
AutoloadDirectories = [];
|
||||||
|
@ -603,7 +603,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
};
|
};
|
||||||
|
|
||||||
// Input
|
// Input
|
||||||
UseGlobalInputConfig = config.System.UseInputGlobalConfig;
|
EnableConfigGlobal = config.System.UseInputGlobalConfig;
|
||||||
EnableDockedMode = config.System.EnableDockedMode;
|
EnableDockedMode = config.System.EnableDockedMode;
|
||||||
EnableKeyboard = config.Hid.EnableKeyboard;
|
EnableKeyboard = config.Hid.EnableKeyboard;
|
||||||
EnableMouse = config.Hid.EnableMouse;
|
EnableMouse = config.Hid.EnableMouse;
|
||||||
|
@ -710,7 +710,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
};
|
};
|
||||||
|
|
||||||
// Input
|
// Input
|
||||||
config.System.UseInputGlobalConfig.Value = UseGlobalInputConfig;
|
config.System.UseInputGlobalConfig.Value = EnableConfigGlobal;
|
||||||
config.System.EnableDockedMode.Value = EnableDockedMode;
|
config.System.EnableDockedMode.Value = EnableDockedMode;
|
||||||
config.Hid.EnableKeyboard.Value = EnableKeyboard;
|
config.Hid.EnableKeyboard.Value = EnableKeyboard;
|
||||||
config.Hid.EnableMouse.Value = EnableMouse;
|
config.Hid.EnableMouse.Value = EnableMouse;
|
||||||
|
@ -840,7 +840,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||||
|
|
||||||
public void DeleteConfigGame()
|
public void DeleteConfigGame()
|
||||||
{
|
{
|
||||||
string gameDir = Program.GetDirGameUserConfig(GameId);
|
string gameDir = Program.GetDirGameUserConfig(GameId, false, false);
|
||||||
|
|
||||||
if (File.Exists(gameDir))
|
if (File.Exists(gameDir))
|
||||||
{
|
{
|
||||||
|
|
|
@ -100,7 +100,7 @@
|
||||||
Name="ProfileBox"
|
Name="ProfileBox"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
SelectedItem="{Binding ChosenProfile, Mode=TwoWay}"
|
SelectedItem="{Binding ProfileChoose, Mode=TwoWay}"
|
||||||
SelectionChanged="ComboBox_SelectionChanged"
|
SelectionChanged="ComboBox_SelectionChanged"
|
||||||
ItemsSource="{Binding ProfilesList}"
|
ItemsSource="{Binding ProfilesList}"
|
||||||
Text="{Binding ProfileName, Mode=TwoWay}" />
|
Text="{Binding ProfileName, Mode=TwoWay}" />
|
||||||
|
@ -203,6 +203,7 @@
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<ContentControl IsVisible="{Binding NotificationIsVisible}">
|
<ContentControl IsVisible="{Binding NotificationIsVisible}">
|
||||||
<ContentControl.Content>
|
<ContentControl.Content>
|
||||||
|
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="5,20,0,0"
|
Margin="5,20,0,0"
|
||||||
|
|
|
@ -15,7 +15,7 @@ namespace Ryujinx.Ava.UI.Views.Input
|
||||||
|
|
||||||
public InputView()
|
public InputView()
|
||||||
{
|
{
|
||||||
ViewModel = new InputViewModel(this, ConfigurationState.Instance.System.UseInputGlobalConfig);
|
ViewModel = new InputViewModel(this, ConfigurationState.Instance.System.UseInputGlobalConfig.Value);
|
||||||
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
@ -27,8 +27,9 @@ namespace Ryujinx.Ava.UI.Views.Input
|
||||||
|
|
||||||
public void ToggleLocalGlobalInput(bool enableConfigGlobal)
|
public void ToggleLocalGlobalInput(bool enableConfigGlobal)
|
||||||
{
|
{
|
||||||
|
|
||||||
Dispose();
|
Dispose();
|
||||||
ViewModel = new InputViewModel(this, enableConfigGlobal); // Create new Input Page with global input configs
|
ViewModel = new InputViewModel(this, enableConfigGlobal); // Create new Input Page with other input configs
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,6 @@
|
||||||
MinWidth="200"
|
MinWidth="200"
|
||||||
Height="6"
|
Height="6"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Margin="0, 0, 5, 0"
|
|
||||||
Foreground="{DynamicResource SystemAccentColorLight2}"
|
Foreground="{DynamicResource SystemAccentColorLight2}"
|
||||||
IsVisible="{Binding StatusBarVisible}"
|
IsVisible="{Binding StatusBarVisible}"
|
||||||
Maximum="{Binding StatusBarProgressMaximum}"
|
Maximum="{Binding StatusBarProgressMaximum}"
|
||||||
|
|
|
@ -44,7 +44,7 @@
|
||||||
<CheckBox
|
<CheckBox
|
||||||
ToolTip.Tip="{ext:Locale UseGlobalInputTooltip}"
|
ToolTip.Tip="{ext:Locale UseGlobalInputTooltip}"
|
||||||
MinWidth="0"
|
MinWidth="0"
|
||||||
IsChecked="{Binding UseGlobalInputConfig}">
|
IsChecked="{Binding EnableConfigGlobal}">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Text="{ext:Locale SettingsTabInputUseGlobalInput}" />
|
Text="{ext:Locale SettingsTabInputUseGlobalInput}" />
|
||||||
</CheckBox>
|
</CheckBox>
|
||||||
|
|
|
@ -46,7 +46,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||||
|
|
||||||
private void Load()
|
private void Load()
|
||||||
{
|
{
|
||||||
Pages.Children.Clear();
|
Pages.Children.Clear();
|
||||||
NavPanel.SelectionChanged += NavPanelOnSelectionChanged;
|
NavPanel.SelectionChanged += NavPanelOnSelectionChanged;
|
||||||
NavPanel.SelectedItem = NavPanel.MenuItems.ElementAt(0);
|
NavPanel.SelectedItem = NavPanel.MenuItems.ElementAt(0);
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||||
|
|
||||||
protected override void OnClosing(WindowClosingEventArgs e)
|
protected override void OnClosing(WindowClosingEventArgs e)
|
||||||
{
|
{
|
||||||
Program.UseExtraConfig = false;
|
Program.SetUseExtraConfig(false);
|
||||||
InputPage.Dispose(); // You need to unload the gamepad settings, otherwise the controls will be blocked
|
InputPage.Dispose(); // You need to unload the gamepad settings, otherwise the controls will be blocked
|
||||||
base.OnClosing(e);
|
base.OnClosing(e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -213,13 +213,13 @@ namespace Ryujinx.Ava.UI.Windows
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void Application_Opened(object sender, ApplicationOpenedEventArgs args)
|
public void Application_Opened(object sender, ApplicationOpenedEventArgs args)
|
||||||
{
|
{
|
||||||
if (args.Application != null)
|
if (args.Application != null)
|
||||||
{
|
{
|
||||||
ViewModel.SelectedIcon = args.Application.Icon;
|
ViewModel.SelectedIcon = args.Application.Icon;
|
||||||
|
|
||||||
await ViewModel.LoadApplication(args.Application);
|
ViewModel.LoadApplication(args.Application).Wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue