mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2025-06-28 01:49:39 -06:00
Compare commits
18 Commits
Canary-1.3
...
1.3.2
Author | SHA1 | Date | |
---|---|---|---|
9d83dfd19c | |||
ce31a47934 | |||
d31d1f91cf | |||
ef02194a77 | |||
a16764d191 | |||
5108ab790f | |||
71dc71fee8 | |||
c95bf748b2 | |||
b5e9acc50b | |||
e3fba4e32f | |||
efa25d471e | |||
b37aa61e47 | |||
8feeb977b7 | |||
b761a2c86d | |||
693837dca7 | |||
70abff072b | |||
1e861b99a9 | |||
13e404bde0 |
112
.github/workflows/canary.yml
vendored
112
.github/workflows/canary.yml
vendored
@ -24,54 +24,6 @@ env:
|
||||
RELEASE: 1
|
||||
|
||||
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: Install GitLabCli
|
||||
run: |
|
||||
mkdir -p $HOME/.bin
|
||||
gh release download -R GreemDev/GLI -O gli -p 'GitLabCli-linux_x64'
|
||||
chmod +x gli
|
||||
mv gli $HOME/.bin/
|
||||
echo "$HOME/.bin" >> $GITHUB_PATH
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Create GitLab tag
|
||||
run: gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=CreateTag "Canary-${{ steps.version_info.outputs.build_version }}|master"
|
||||
|
||||
- name: Create release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
name: "Canary ${{ steps.version_info.outputs.build_version }}"
|
||||
tag: ${{ steps.version_info.outputs.build_version }}
|
||||
body: |
|
||||
# Canary builds:
|
||||
|
||||
These builds are experimental and may sometimes not work, use [regular builds](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_STABLE_NAME }}/releases/latest) instead if that sounds like something you don't want to deal with.
|
||||
|
||||
| Platform | Artifact |
|
||||
|--|--|
|
||||
| Windows 64-bit | [Canary Windows Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_CANARY_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-win_x64.zip) |
|
||||
| Windows ARM 64-bit | [Canary Windows ARM Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_CANARY_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-win_arm64.zip) |
|
||||
| Linux 64-bit | [Canary Linux Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_CANARY_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz) |
|
||||
| Linux ARM 64-bit | [Canary Linux ARM Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_CANARY_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) |
|
||||
| macOS | [Canary macOS Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_CANARY_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz) |
|
||||
|
||||
**[Full Changelog](https://git.ryujinx.app/ryubing/ryujinx/-/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }})**
|
||||
omitBodyDuringUpdate: true
|
||||
owner: ${{ secrets.RC_OWNER }}
|
||||
repo: ${{ secrets.RC_CANARY_NAME }}
|
||||
token: ${{ secrets.ALT_RELEASE_TOKEN }}
|
||||
|
||||
release:
|
||||
name: Release for ${{ matrix.platform.name }}
|
||||
runs-on: ${{ matrix.platform.os }}
|
||||
@ -91,16 +43,6 @@ jobs:
|
||||
|
||||
- name: Overwrite csc problem matcher
|
||||
run: echo "::add-matcher::.github/csc.json"
|
||||
|
||||
- name: Install GitLabCli
|
||||
run: |
|
||||
mkdir -p $HOME/.bin
|
||||
gh release download -R GreemDev/GLI -O gli -p 'GitLabCli-linux_x64'
|
||||
chmod +x gli
|
||||
mv gli $HOME/.bin/
|
||||
echo "$HOME/.bin" >> $GITHUB_PATH
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Get version info
|
||||
id: version_info
|
||||
@ -202,34 +144,7 @@ jobs:
|
||||
|
||||
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/canary --command=UploadGenericPackage "Ryubing-Canary|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-canary-$BUILD_VERSION-$ARCH_NAME.AppImage"
|
||||
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/canary --command=UploadGenericPackage "Ryubing-Canary|${{ steps.version_info.outputs.build_version }}|release_output/ryujinx-canary-$BUILD_VERSION-$ARCH_NAME.AppImage.zsync"
|
||||
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: |
|
||||
# Canary builds:
|
||||
|
||||
These builds are experimental and may sometimes not work, use [regular builds](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_STABLE_NAME }}/releases/latest) instead if that sounds like something you don't want to deal with.
|
||||
|
||||
| Platform | Artifact |
|
||||
|--|--|
|
||||
| Windows 64-bit | [Canary Windows Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_CANARY_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-win_x64.zip) |
|
||||
| Windows ARM 64-bit | [Canary Windows ARM Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_CANARY_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-win_arm64.zip) |
|
||||
| Linux 64-bit | [Canary Linux Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_CANARY_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz) |
|
||||
| Linux ARM 64-bit | [Canary Linux ARM Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_CANARY_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-linux_arm64.tar.gz) |
|
||||
| macOS | [Canary macOS Artifact](https://github.com/${{ secrets.RC_OWNER }}/${{ secrets.RC_CANARY_NAME }}/releases/download/${{ steps.version_info.outputs.build_version }}/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz) |
|
||||
|
||||
**[Full Changelog](https://git.ryujinx.app/ryubing/ryujinx/-/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }})**
|
||||
omitBodyDuringUpdate: true
|
||||
allowUpdates: true
|
||||
replacesArtifacts: true
|
||||
owner: ${{ secrets.RC_OWNER }}
|
||||
repo: ${{ secrets.RC_CANARY_NAME }}
|
||||
token: ${{ secrets.ALT_RELEASE_TOKEN }}
|
||||
shell: bash
|
||||
|
||||
macos_release:
|
||||
name: Release MacOS universal
|
||||
@ -290,28 +205,15 @@ jobs:
|
||||
./distribution/macos/create_macos_build_ava.sh . publish_tmp_ava publish_ava ./distribution/macos/entitlements.xml "${{ steps.version_info.outputs.build_version }}" "${{ steps.version_info.outputs.git_short_hash }}" Release 1
|
||||
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/canary --command=UploadGenericPackage "Ryubing-Canary|${{ steps.version_info.outputs.build_version }}|publish_ava/ryujinx-canary-${{ steps.version_info.outputs.build_version }}-macos_universal.app.tar.gz"
|
||||
|
||||
- name: Pushing new release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
name: "Canary ${{ steps.version_info.outputs.build_version }}"
|
||||
artifacts: "publish_ava/*.tar.gz"
|
||||
tag: ${{ steps.version_info.outputs.build_version }}
|
||||
body: ""
|
||||
omitBodyDuringUpdate: true
|
||||
allowUpdates: true
|
||||
replacesArtifacts: true
|
||||
owner: ${{ secrets.RC_OWNER }}
|
||||
repo: ${{ secrets.RC_CANARY_NAME }}
|
||||
token: ${{ secrets.ALT_RELEASE_TOKEN }}
|
||||
|
||||
create_gitlab_release:
|
||||
name: Create GitLab Release
|
||||
runs-on: ubuntu-24.04
|
||||
needs:
|
||||
- tag
|
||||
- macos_release
|
||||
- release
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Get version info
|
||||
id: version_info
|
||||
run: |
|
||||
@ -330,10 +232,14 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Create tag
|
||||
run: |
|
||||
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/ryujinx --command=CreateTag "Canary-${{ steps.version_info.outputs.build_version }}|${{ steps.version_info.outputs.git_short_hash }}"
|
||||
|
||||
- name: Create release
|
||||
run: |
|
||||
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/canary --command=CreateReleaseFromGenericPackageFiles "Ryubing-Canary|${{ steps.version_info.outputs.build_version }}|main|Canary ${{ steps.version_info.outputs.build_version }}|**[Full Changelog](https://git.ryujinx.app/ryubing/ryujinx/-/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }})**"
|
||||
gli --access-token=${{ secrets.GITLAB_TOKEN }} --project=ryubing/canary --command=CreateReleaseFromGenericPackageFiles "Ryubing-Canary|${{ steps.version_info.outputs.build_version }}|main|Canary ${{ steps.version_info.outputs.build_version }}|**Full Changelog:** [${{ steps.version_info.outputs.prev_build_version }}...${{ steps.version_info.outputs.build_version }}](https://git.ryujinx.app/ryubing/ryujinx/-/compare/Canary-${{ steps.version_info.outputs.prev_build_version }}...Canary-${{ steps.version_info.outputs.build_version }})"
|
||||
|
||||
- name: Send notification webhook
|
||||
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"
|
||||
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"
|
||||
|
@ -40,7 +40,7 @@
|
||||
<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />
|
||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.AllArch" Version="6.1.2-build3" />
|
||||
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
||||
<PackageVersion Include="Ryujinx.LibHac" Version="0.20.0-alpha.107" />
|
||||
<PackageVersion Include="Ryujinx.LibHac" Version="0.20.0" />
|
||||
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.30.0-build32" />
|
||||
<PackageVersion Include="Gommon" Version="2.7.1.1" />
|
||||
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
||||
|
@ -1847,6 +1847,131 @@
|
||||
"zh_TW": "路徑"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "GameListSortStatusNameAscending",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "Όνομα: A-Z",
|
||||
"en_US": "Title: A-Z",
|
||||
"es_ES": "Título: A-Z",
|
||||
"fr_FR": "Titre : A-Z",
|
||||
"he_IL": "",
|
||||
"it_IT": "Titolo: A-Z",
|
||||
"ja_JP": "タイトル:A-Z",
|
||||
"ko_KR": "제목: A-Z",
|
||||
"no_NO": "Tittel: A-Z",
|
||||
"pl_PL": "Tytuł: A-Z",
|
||||
"pt_BR": "Título: A-Z",
|
||||
"ru_RU": "Название: А-Z",
|
||||
"sv_SE": "Titel: A-Z",
|
||||
"th_TH": "ชื่อเรื่อง: A-Z",
|
||||
"tr_TR": "Başlık: A-Z",
|
||||
"uk_UA": "Назва: A-Z",
|
||||
"zh_CN": "标题:A-Z",
|
||||
"zh_TW": "標題:A-Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "GameListSortStatusNameDescending",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "Τίτλος: Z-A",
|
||||
"en_US": "Title: Z-A",
|
||||
"es_ES": "Título: Z-A",
|
||||
"fr_FR": "Titre : Z-A",
|
||||
"he_IL": "",
|
||||
"it_IT": "Titolo: Z-A",
|
||||
"ja_JP": "タイトル:Z-A",
|
||||
"ko_KR": "제목: Z-A",
|
||||
"no_NO": "Tittel: Z-A",
|
||||
"pl_PL": "Tytuł: Z-A",
|
||||
"pt_BR": "Título: Z-A",
|
||||
"ru_RU": "Название: Z-A",
|
||||
"sv_SE": "Titel: Z-A",
|
||||
"th_TH": "ชื่อเรื่อง: Z-A",
|
||||
"tr_TR": "Başlık: Z-A",
|
||||
"uk_UA": "Назва: Z-A",
|
||||
"zh_CN": "标题:Z-A",
|
||||
"zh_TW": "標題:Z-A"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "GameListSortStatusDisable",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "Status: Deaktiviert",
|
||||
"el_GR": "Κατάσταση: Απενεργοποιημένο",
|
||||
"en_US": "Status: Disabled",
|
||||
"es_ES": "Estado: Desactivado",
|
||||
"fr_FR": "Statut : Désactivé",
|
||||
"he_IL": "",
|
||||
"it_IT": "Stato: Disabilitato",
|
||||
"ja_JP": "ステータス:無効",
|
||||
"ko_KR": "상태: 비활성화됨",
|
||||
"no_NO": "Status: Deaktivert",
|
||||
"pl_PL": "Status: Wyłączony",
|
||||
"pt_BR": "Status: Desativado",
|
||||
"ru_RU": "Статус: Отключено",
|
||||
"sv_SE": "Status: Inaktiverad",
|
||||
"th_TH": "",
|
||||
"tr_TR": "Durum: Devre Dışı",
|
||||
"uk_UA": "Статус: Вимкнено",
|
||||
"zh_CN": "状态:禁用",
|
||||
"zh_TW": "狀態:停用"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "GameListSortStatusAscending",
|
||||
"Translations": {
|
||||
"ar_SA": "الحالة: تصاعدي",
|
||||
"de_DE": "Status: Aufsteigend",
|
||||
"el_GR": "Κατάσταση: Αναγόμενη",
|
||||
"en_US": "Status: Ascending",
|
||||
"es_ES": "",
|
||||
"fr_FR": "Statut : Croissant",
|
||||
"he_IL": "סטטוס: עולה",
|
||||
"it_IT": "Stato: Crescente",
|
||||
"ja_JP": "ステータス:昇順",
|
||||
"ko_KR": "상태: 오름차순",
|
||||
"no_NO": "Status: Stigende",
|
||||
"pl_PL": "Stan: Rosnący",
|
||||
"pt_BR": "Status: Crescente",
|
||||
"ru_RU": "Статус: По возрастанию",
|
||||
"sv_SE": "Status: Stigande",
|
||||
"th_TH": "สถานะ: เพิ่มขึ้น",
|
||||
"tr_TR": "Durum: Artan",
|
||||
"uk_UA": "Статус: Зростання",
|
||||
"zh_CN": "状态:升序",
|
||||
"zh_TW": "狀態:遞增"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "GameListSortStatusDescending",
|
||||
"Translations": {
|
||||
"ar_SA": "الحالة: تنازلي",
|
||||
"de_DE": "Status: Absteigend",
|
||||
"el_GR": "Κατάσταση: Καθοδική",
|
||||
"en_US": "Status: Descending",
|
||||
"es_ES": "",
|
||||
"fr_FR": "Statut : Décroissant",
|
||||
"he_IL": "סטטוס: יורד",
|
||||
"it_IT": "Stato: Decrescente",
|
||||
"ja_JP": "ステータス:降順",
|
||||
"ko_KR": "상태: 내림차순",
|
||||
"no_NO": "Status: Synkende",
|
||||
"pl_PL": "Stan: Malejący",
|
||||
"pt_BR": "Status: Decrescente",
|
||||
"ru_RU": "Статус: По Убыванию",
|
||||
"sv_SE": "Status: Fallande",
|
||||
"th_TH": "สถานะ: ลดลง",
|
||||
"tr_TR": "Durum: Azalan",
|
||||
"uk_UA": "Статус: Зменшення",
|
||||
"zh_CN": "状态:降序",
|
||||
"zh_TW": "狀態:遞減"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "GameListHeaderCompatibilityStatus",
|
||||
"Translations": {
|
||||
@ -5450,26 +5575,26 @@
|
||||
{
|
||||
"ID": "SettingsTabGraphicsAPI",
|
||||
"Translations": {
|
||||
"ar_SA": "API الرسومات ",
|
||||
"de_DE": "Grafik-API",
|
||||
"el_GR": "API Γραφικά",
|
||||
"en_US": "Graphics API",
|
||||
"es_ES": "API de gráficos",
|
||||
"fr_FR": "API Graphique",
|
||||
"he_IL": "ממשק גראפי",
|
||||
"it_IT": "API grafica",
|
||||
"ja_JP": "グラフィックスAPI",
|
||||
"ko_KR": "그래픽 API",
|
||||
"no_NO": "Grafikk API",
|
||||
"pl_PL": "Graficzne API",
|
||||
"pt_BR": "API gráfica",
|
||||
"ru_RU": "Графические API",
|
||||
"sv_SE": "Grafik-API",
|
||||
"th_TH": "API กราฟฟิก",
|
||||
"tr_TR": "Grafikler API",
|
||||
"uk_UA": "Графічний API",
|
||||
"zh_CN": "图形 API",
|
||||
"zh_TW": "圖形 API"
|
||||
"ar_SA": "API الرسومات و تحسين",
|
||||
"de_DE": "Grafik-API & Optimierung",
|
||||
"el_GR": "API Γραφικά & Βελτιστοποίηση",
|
||||
"en_US": "Graphics API & Optimization",
|
||||
"es_ES": "API de gráficos & Optimización",
|
||||
"fr_FR": "API Graphique & Optimisation",
|
||||
"he_IL": "ממשק גראפי & אופטימיזציה",
|
||||
"it_IT": "API grafica & Ottimizzazione",
|
||||
"ja_JP": "グラフィックスAPI&最適化",
|
||||
"ko_KR": "그래픽 API & 최적화",
|
||||
"no_NO": "Grafikk-API & Optimalisering",
|
||||
"pl_PL": "Graficzne API & Optymalizacja",
|
||||
"pt_BR": "API gráfica & Otimização",
|
||||
"ru_RU": "Графический API & Оптимизация",
|
||||
"sv_SE": "Grafik-API & Optimering",
|
||||
"th_TH": "API กราฟฟิก & การเพิ่มประสิทธิภาพ",
|
||||
"tr_TR": "Grafikler API & Optimizasyon",
|
||||
"uk_UA": "Графічний API & Оптимізація",
|
||||
"zh_CN": "图形 API & 优化",
|
||||
"zh_TW": "圖形 API & 優化"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -6547,6 +6672,31 @@
|
||||
"zh_TW": "輸入"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "SettingsTabInputUseGlobalInput",
|
||||
"Translations": {
|
||||
"ar_SA": "إدخال عالمي",
|
||||
"de_DE": "Globale Eingabe",
|
||||
"el_GR": "Παγκόσμια εισαγωγή",
|
||||
"en_US": "Global Input",
|
||||
"es_ES": "Entrada Global",
|
||||
"fr_FR": "Saisie Globale",
|
||||
"he_IL": "קלט גלובלי",
|
||||
"it_IT": "Input Globale",
|
||||
"ja_JP": "グローバル入力",
|
||||
"ko_KR": "글로벌 입력",
|
||||
"no_NO": "Global Inndata",
|
||||
"pl_PL": "Globalny Wpis",
|
||||
"pt_BR": "Entrada Global",
|
||||
"ru_RU": "Глобальный Ввод",
|
||||
"sv_SE": "Global Input",
|
||||
"th_TH": "การป้อนข้อมูลแบบโกลบอล",
|
||||
"tr_TR": "Küresel Girdi",
|
||||
"uk_UA": "Глобальний Ввід",
|
||||
"zh_CN": "全局输入",
|
||||
"zh_TW": "全域輸入"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "SettingsTabInputEnableDockedMode",
|
||||
"Translations": {
|
||||
@ -16422,6 +16572,31 @@
|
||||
"zh_TW": "瀏覽自訂 GUI 佈景主題"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "UseGlobalInputTooltip",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "",
|
||||
"en_US": "If this option is enabled in custom settings, the global input configuration will be used.\n\nIn the global settings: you can enable or disable it as needed; this setting will be inherited by any new custom configurations created.",
|
||||
"es_ES": "",
|
||||
"fr_FR": "",
|
||||
"he_IL": "",
|
||||
"it_IT": "",
|
||||
"ja_JP": "",
|
||||
"ko_KR": "",
|
||||
"no_NO": "",
|
||||
"pl_PL": "",
|
||||
"pt_BR": "",
|
||||
"ru_RU": "Если эта опция включена в пользовательских настройках, будет использована глобальная конфигурация ввода.\n\nВ глобальных настройках: переключите эту опцию по своему усмотрению, это будет унаследовано для вновь созданых пользовательских конфигураций",
|
||||
"sv_SE": "",
|
||||
"th_TH": "",
|
||||
"tr_TR": "",
|
||||
"uk_UA": "",
|
||||
"zh_CN": "",
|
||||
"zh_TW": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "DockModeToggleTooltip",
|
||||
"Translations": {
|
||||
@ -24372,6 +24547,106 @@
|
||||
"zh_TW": "開啟相容性列表"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "CompatibilityListGamesAndApplications",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "Spiele & Anwendungen",
|
||||
"el_GR": "Παιχνίδια και Εφαρμογές",
|
||||
"en_US": "Games & Applications",
|
||||
"es_ES": "Juegos y Aplicaciones",
|
||||
"fr_FR": "Jeux et Applications",
|
||||
"he_IL": "משחקים ואפליקציות",
|
||||
"it_IT": "Giochi e Applicazioni",
|
||||
"ja_JP": "ゲームとアプリケーション",
|
||||
"ko_KR": "게임 및 애플리케이션",
|
||||
"no_NO": "Spill og Applikasjoner",
|
||||
"pl_PL": "Gry i Aplikacje",
|
||||
"pt_BR": "Jogos e Aplicativos",
|
||||
"ru_RU": "Игры и Приложения",
|
||||
"sv_SE": "Spel och Applikationer",
|
||||
"th_TH": "",
|
||||
"tr_TR": "Oyunlar ve Uygulamalar",
|
||||
"uk_UA": "Ігри та Додатки",
|
||||
"zh_CN": "游戏和应用程序",
|
||||
"zh_TW": "遊戲與應用程式"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "CompatibilityListStatus",
|
||||
"Translations": {
|
||||
"ar_SA": "الحالة",
|
||||
"de_DE": "",
|
||||
"el_GR": "Κατάσταση",
|
||||
"en_US": "Status",
|
||||
"es_ES": "Estado",
|
||||
"fr_FR": "Statut",
|
||||
"he_IL": "מצב",
|
||||
"it_IT": "Stato",
|
||||
"ja_JP": "状況",
|
||||
"ko_KR": "상태",
|
||||
"no_NO": "",
|
||||
"pl_PL": "Stan",
|
||||
"pt_BR": "Estado",
|
||||
"ru_RU": "Статус",
|
||||
"sv_SE": "",
|
||||
"th_TH": "สถานะ",
|
||||
"tr_TR": "Durum",
|
||||
"uk_UA": "Статус",
|
||||
"zh_CN": "状态",
|
||||
"zh_TW": "狀態"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "CompatibilityListDescription",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "Probleme und Merkmale",
|
||||
"el_GR": "Προβλήματα και Χαρακτηριστικά",
|
||||
"en_US": "Issues & Features",
|
||||
"es_ES": "Problemas y Características",
|
||||
"fr_FR": "Problèmes et Caractéristiques",
|
||||
"he_IL": "",
|
||||
"it_IT": "Problemi e Caratteristiche",
|
||||
"ja_JP": "問題点と特徴",
|
||||
"ko_KR": "문제점 및 특징",
|
||||
"no_NO": "Problemer og Egenskaper",
|
||||
"pl_PL": "Problemy i Cechy",
|
||||
"pt_BR": "Problemas e Características",
|
||||
"ru_RU": "Проблемы и Особенности",
|
||||
"sv_SE": "Problem och Egenskaper",
|
||||
"th_TH": "",
|
||||
"tr_TR": "Sorunlar ve Özellikler",
|
||||
"uk_UA": "Проблеми та Особливості",
|
||||
"zh_CN": "问题和特性",
|
||||
"zh_TW": "問題與特性"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "CompatibilityListInfo",
|
||||
"Translations": {
|
||||
"ar_SA": "",
|
||||
"de_DE": "",
|
||||
"el_GR": "Πληροφορίες",
|
||||
"en_US": "Info",
|
||||
"es_ES": "Información",
|
||||
"fr_FR": "",
|
||||
"he_IL": "מידע",
|
||||
"it_IT": "",
|
||||
"ja_JP": "情報",
|
||||
"ko_KR": "정보",
|
||||
"no_NO": "",
|
||||
"pl_PL": "Informacja",
|
||||
"pt_BR": "",
|
||||
"ru_RU": "Инфо",
|
||||
"sv_SE": "",
|
||||
"th_TH": "",
|
||||
"tr_TR": "Bilgi",
|
||||
"uk_UA": "Інфо",
|
||||
"zh_CN": "信息",
|
||||
"zh_TW": "資訊"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ID": "CompatibilityListOnlyShowOwnedGames",
|
||||
"Translations": {
|
||||
|
@ -1125,6 +1125,7 @@
|
||||
0100034012606000,"Family Mysteries: Poisonous Promises",audio;crash,menus,2021-11-26 12:35:06
|
||||
010017C012726000,"Fantasy Friends",,playable,2022-10-17 19:42:39
|
||||
0100767008502000,"FANTASY HERO ~unsigned legacy~",,playable,2022-07-26 12:28:52
|
||||
0100755017EE0000,"FANTASY LIFE i: The Girl Who Steals Time",gpu;crash;vulkan-backend-bug,ingame,2025-06-08 20:41:00
|
||||
0100944003820000,"Fantasy Strike",online,playable,2021-02-27 01:59:18
|
||||
01000E2012F6E000,"Fantasy Tavern Sextet -Vol.1 New World Days-",gpu;crash;Needs Update,ingame,2022-12-05 16:48:00
|
||||
01005C10136CA000,"Fantasy Tavern Sextet -Vol.2 Adventurer's Days-",gpu;slow;crash,ingame,2021-11-06 02:57:29
|
||||
|
|
17
nuget.config
17
nuget.config
@ -4,20 +4,7 @@
|
||||
<packageSources>
|
||||
<clear />
|
||||
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
|
||||
<add key="LibHacAlpha" value="https://git.ryujinx.app/api/v4/projects/17/packages/nuget/index.json" />
|
||||
<add key="RyubingPkgs" value="https://git.ryujinx.app/api/v4/projects/1/packages/nuget/index.json" />
|
||||
<!-- 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" />-->
|
||||
</packageSources>
|
||||
|
||||
<!-- Define mappings by adding package patterns beneath the target source. -->
|
||||
<!-- Ryujinx.LibHac packages will be restored from LibHacAlpha,
|
||||
everything else from nuget.org. -->
|
||||
<packageSourceMapping>
|
||||
<!-- key value for <packageSource> should match key values from <packageSources> element -->
|
||||
<packageSource key="nuget.org">
|
||||
<package pattern="*" />
|
||||
</packageSource>
|
||||
<packageSource key="LibHacAlpha">
|
||||
<package pattern="Ryujinx.LibHac" />
|
||||
</packageSource>
|
||||
</packageSourceMapping>
|
||||
</configuration>
|
||||
|
@ -133,7 +133,6 @@ namespace Ryujinx.Common
|
||||
"0100c1f0051b6000", // Donkey Kong Country: Tropical Freeze
|
||||
"0100ed000d390000", // Dr. Kawashima's Brain Training
|
||||
"010067b017588000", // Endless Ocean Luminous
|
||||
"0100d2f00d5c0000", // Nintendo Switch Sports
|
||||
"01006b5012b32000", // Part Time UFO
|
||||
"0100704000B3A000", // Snipperclips
|
||||
"01006a800016e000", // Super Smash Bros. Ultimate
|
||||
@ -169,6 +168,8 @@ namespace Ryujinx.Common
|
||||
"010056e00853a000", // A Hat in Time
|
||||
"0100fd1014726000", // Baldurs Gate: Dark Alliance
|
||||
"01008c2019598000", // Bluey: The Video Game
|
||||
"010096f00ff22000", // Borderlands 2: Game of the Year Edition
|
||||
"010007400ff24000", // Borderlands: The Pre-Sequel Ultimate Edition
|
||||
"0100c6800b934000", // Brawlhalla
|
||||
"0100dbf01000a000", // Burnout Paradise Remastered
|
||||
"0100744001588000", // Cars 3: Driven to Win
|
||||
|
@ -372,6 +372,12 @@
|
||||
<Setter Property="BorderThickness"
|
||||
Value="2"/>
|
||||
</Style>
|
||||
<Style Selector="Border.listbox-item-style">
|
||||
<Setter Property="Padding" Value="10" />
|
||||
<Setter Property="Margin" Value="5,0,5,0" />
|
||||
<Setter Property="CornerRadius" Value="5" />
|
||||
<Setter Property="Background" Value="{DynamicResource AppListBackgroundColor}" />
|
||||
</Style>
|
||||
<Style Selector="ListBox ListBoxItem:selected /template/ ContentPresenter">
|
||||
<Setter Property="Background"
|
||||
Value="{DynamicResource AppListBackgroundColor}" />
|
||||
|
@ -1,20 +0,0 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ryujinx.Ava.Common.Models.GitLab
|
||||
{
|
||||
public class GitLabReleaseAssetJsonResponse
|
||||
{
|
||||
[JsonPropertyName("links")]
|
||||
public GitLabReleaseAssetLinkJsonResponse[] Links { get; set; }
|
||||
|
||||
public class GitLabReleaseAssetLinkJsonResponse
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
public long Id { get; set; }
|
||||
[JsonPropertyName("name")]
|
||||
public string AssetName { get; set; }
|
||||
[JsonPropertyName("url")]
|
||||
public string Url { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ryujinx.Ava.Common.Models.GitLab
|
||||
{
|
||||
public class GitLabReleasesJsonResponse
|
||||
{
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonPropertyName("tag_name")]
|
||||
public string TagName { get; set; }
|
||||
|
||||
[JsonPropertyName("assets")]
|
||||
public GitLabReleaseAssetJsonResponse Assets { get; set; }
|
||||
}
|
||||
|
||||
[JsonSerializable(typeof(GitLabReleasesJsonResponse), GenerationMode = JsonSourceGenerationMode.Metadata)]
|
||||
public partial class GitLabReleasesJsonSerializerContext : JsonSerializerContext;
|
||||
}
|
@ -35,6 +35,7 @@ namespace Ryujinx.Ava
|
||||
public static string Version { get; private set; }
|
||||
public static string ConfigurationPath { get; private set; }
|
||||
public static string GlobalConfigurationPath { get; private set; }
|
||||
public static bool UseExtraConfig { get; set; }
|
||||
public static bool PreviewerDetached { get; private set; }
|
||||
public static bool UseHardwareAcceleration { get; private set; }
|
||||
public static string BackendThreadingArg { get; private set; }
|
||||
@ -159,7 +160,8 @@ namespace Ryujinx.Ava
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetDirGameUserConfig(string gameId, bool rememberGlobalDir = false, bool changeFolderForGame = false)
|
||||
|
||||
public static string GetDirGameUserConfig(string gameId, bool changeFolderForGame = false)
|
||||
{
|
||||
if (string.IsNullOrEmpty(gameId))
|
||||
{
|
||||
@ -168,15 +170,10 @@ namespace Ryujinx.Ava
|
||||
|
||||
string gameDir = Path.Combine(AppDataManager.GamesDirPath, gameId, ReleaseInformation.ConfigName);
|
||||
|
||||
// Should load with the game if there is a custom setting for the game
|
||||
if (rememberGlobalDir)
|
||||
{
|
||||
GlobalConfigurationPath = ConfigurationPath;
|
||||
}
|
||||
|
||||
if (changeFolderForGame)
|
||||
{
|
||||
ConfigurationPath = gameDir;
|
||||
UseExtraConfig = true;
|
||||
}
|
||||
|
||||
return gameDir;
|
||||
@ -184,8 +181,6 @@ namespace Ryujinx.Ava
|
||||
|
||||
public static void ReloadConfig()
|
||||
{
|
||||
//It is necessary that when a user setting appears, the global setting remains available
|
||||
GlobalConfigurationPath = null;
|
||||
|
||||
string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ReleaseInformation.ConfigName);
|
||||
string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, ReleaseInformation.ConfigName);
|
||||
@ -225,6 +220,12 @@ namespace Ryujinx.Ava
|
||||
}
|
||||
}
|
||||
|
||||
// When you first load the program, copy to remember the path for the global configuration
|
||||
if (GlobalConfigurationPath == null)
|
||||
{
|
||||
GlobalConfigurationPath = ConfigurationPath;
|
||||
}
|
||||
|
||||
UseHardwareAcceleration = ConfigurationState.Instance.EnableHardwareAcceleration;
|
||||
|
||||
// Check if graphics backend was overridden
|
||||
|
@ -461,7 +461,15 @@ namespace Ryujinx.Ava.Systems
|
||||
|
||||
DisplaySleep.Prevent();
|
||||
|
||||
NpadManager.Initialize(Device, ConfigurationState.Instance.Hid.InputConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse);
|
||||
if (ConfigurationState.Instance.System.UseInputGlobalConfig.Value && Program.UseExtraConfig)
|
||||
{
|
||||
NpadManager.Initialize(Device, ConfigurationState.InstanceExtra.Hid.InputConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse);
|
||||
}
|
||||
else
|
||||
{
|
||||
NpadManager.Initialize(Device, ConfigurationState.Instance.Hid.InputConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse);
|
||||
}
|
||||
|
||||
TouchScreenManager.Initialize(Device);
|
||||
|
||||
_viewModel.IsGameRunning = true;
|
||||
|
@ -556,7 +556,7 @@ namespace Ryujinx.Ava.Systems.AppLibrary
|
||||
data.Favorite = appMetadata.Favorite;
|
||||
data.TimePlayed = appMetadata.TimePlayed;
|
||||
data.LastPlayed = appMetadata.LastPlayed;
|
||||
data.HasIndependentConfiguration = File.Exists(Program.GetDirGameUserConfig(data.IdBaseString, false, false)); // Just check user config
|
||||
data.HasIndependentConfiguration = File.Exists(Program.GetDirGameUserConfig(data.IdBaseString)); // Just check user config
|
||||
}
|
||||
|
||||
data.FileExtension = Path.GetExtension(applicationPath).TrimStart('.').ToUpper();
|
||||
|
@ -15,7 +15,7 @@ namespace Ryujinx.Ava.Systems.Configuration
|
||||
/// <summary>
|
||||
/// The current version of the file format
|
||||
/// </summary>
|
||||
public const int CurrentVersion = 69;
|
||||
public const int CurrentVersion = 70;
|
||||
|
||||
/// <summary>
|
||||
/// Version of the configuration file format
|
||||
@ -152,6 +152,11 @@ namespace Ryujinx.Ava.Systems.Configuration
|
||||
/// </summary>
|
||||
public bool MatchSystemTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enable or disable use global input config (Independent from controllers binding)
|
||||
/// </summary>
|
||||
public bool UseInputGlobalConfig { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables Docked Mode
|
||||
/// </summary>
|
||||
|
@ -90,6 +90,7 @@ namespace Ryujinx.Ava.Systems.Configuration
|
||||
System.TimeZone.Value = cff.SystemTimeZone;
|
||||
System.SystemTimeOffset.Value = shouldLoadFromFile ? cff.SystemTimeOffset : System.SystemTimeOffset.Value; // Get from global config only
|
||||
System.MatchSystemTime.Value = shouldLoadFromFile ? cff.MatchSystemTime : System.MatchSystemTime.Value; // Get from global config only
|
||||
System.UseInputGlobalConfig.Value = cff.UseInputGlobalConfig;
|
||||
System.EnableDockedMode.Value = cff.DockedMode;
|
||||
System.EnablePtc.Value = cff.EnablePtc;
|
||||
System.EnableLowPowerPtc.Value = cff.EnableLowPowerPtc;
|
||||
@ -146,7 +147,7 @@ namespace Ryujinx.Ava.Systems.Configuration
|
||||
Hid.EnableMouse.Value = cff.EnableMouse;
|
||||
Hid.DisableInputWhenOutOfFocus.Value = shouldLoadFromFile ? cff.DisableInputWhenOutOfFocus : Hid.DisableInputWhenOutOfFocus.Value; // Get from global config only
|
||||
Hid.Hotkeys.Value = shouldLoadFromFile ? cff.Hotkeys : Hid.Hotkeys.Value; // Get from global config only
|
||||
Hid.InputConfig.Value = cff.InputConfig ?? [];
|
||||
Hid.InputConfig.Value = cff.InputConfig ?? [] ;
|
||||
Hid.RainbowSpeed.Value = cff.RainbowSpeed;
|
||||
|
||||
Multiplayer.LanInterfaceId.Value = cff.MultiplayerLanInterfaceId;
|
||||
|
@ -326,6 +326,12 @@ namespace Ryujinx.Ava.Systems.Configuration
|
||||
/// </summary>
|
||||
public ReactiveObject<bool> MatchSystemTime { get; private set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Enable or disable use global input config (Independent from controllers binding)
|
||||
/// </summary>
|
||||
public ReactiveObject<bool> UseInputGlobalConfig { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables Docked Mode
|
||||
/// </summary>
|
||||
@ -417,6 +423,8 @@ namespace Ryujinx.Ava.Systems.Configuration
|
||||
SystemTimeOffset.LogChangesToValue(nameof(SystemTimeOffset));
|
||||
MatchSystemTime = new ReactiveObject<bool>();
|
||||
MatchSystemTime.LogChangesToValue(nameof(MatchSystemTime));
|
||||
UseInputGlobalConfig = new ReactiveObject<bool>();
|
||||
UseInputGlobalConfig.LogChangesToValue(nameof(UseInputGlobalConfig));
|
||||
EnableDockedMode = new ReactiveObject<bool>();
|
||||
EnableDockedMode.LogChangesToValue(nameof(EnableDockedMode));
|
||||
EnablePtc = new ReactiveObject<bool>();
|
||||
@ -761,6 +769,8 @@ namespace Ryujinx.Ava.Systems.Configuration
|
||||
/// </summary>
|
||||
public static ConfigurationState Instance { get; private set; }
|
||||
|
||||
public static ConfigurationState InstanceExtra{ get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The UI section
|
||||
/// </summary>
|
||||
|
@ -15,12 +15,13 @@ namespace Ryujinx.Ava.Systems.Configuration
|
||||
{
|
||||
public static void Initialize()
|
||||
{
|
||||
if (Instance != null)
|
||||
if (Instance != null || InstanceExtra!= null)
|
||||
{
|
||||
throw new InvalidOperationException("Configuration is already initialized");
|
||||
}
|
||||
|
||||
Instance = new ConfigurationState();
|
||||
InstanceExtra= new ConfigurationState();
|
||||
}
|
||||
|
||||
public ConfigurationFileFormat ToFileFormat()
|
||||
@ -54,6 +55,7 @@ namespace Ryujinx.Ava.Systems.Configuration
|
||||
SystemTimeZone = System.TimeZone,
|
||||
SystemTimeOffset = System.SystemTimeOffset,
|
||||
MatchSystemTime = System.MatchSystemTime,
|
||||
UseInputGlobalConfig = System.UseInputGlobalConfig,
|
||||
DockedMode = System.EnableDockedMode,
|
||||
EnableDiscordIntegration = EnableDiscordIntegration,
|
||||
UpdateCheckerType = UpdateCheckerType,
|
||||
@ -178,6 +180,7 @@ namespace Ryujinx.Ava.Systems.Configuration
|
||||
System.Region.Value = Region.USA;
|
||||
System.TimeZone.Value = "UTC";
|
||||
System.SystemTimeOffset.Value = 0;
|
||||
System.UseInputGlobalConfig.Value = false;
|
||||
System.EnableDockedMode.Value = true;
|
||||
EnableDiscordIntegration.Value = true;
|
||||
UpdateCheckerType.Value = UpdaterType.PromptAtStartup;
|
||||
|
@ -1,15 +1,13 @@
|
||||
using Gommon;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.Common.Models.GitLab;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Helper;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@ -17,7 +15,31 @@ namespace Ryujinx.Ava.Systems
|
||||
{
|
||||
internal static partial class Updater
|
||||
{
|
||||
private static GitLabReleaseChannels.ChannelType _currentGitLabReleaseChannel;
|
||||
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"
|
||||
};
|
||||
|
||||
var arch = RunningPlatform.Architecture switch
|
||||
{
|
||||
Architecture.Arm64 => "arm",
|
||||
Architecture.X64 => "amd64",
|
||||
_ => null
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
@ -35,38 +57,41 @@ namespace Ryujinx.Ava.Systems
|
||||
return default;
|
||||
}
|
||||
|
||||
Logger.Info?.Print(LogClass.Application, "Checking for updates from https://git.ryujinx.app.");
|
||||
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);
|
||||
jsonClient.Timeout = TimeSpan.FromSeconds(10);
|
||||
|
||||
if (_currentGitLabReleaseChannel == null)
|
||||
try
|
||||
{
|
||||
GitLabReleaseChannels releaseChannels = await GitLabReleaseChannels.GetAsync(jsonClient);
|
||||
UpdaterResponse response =
|
||||
await jsonClient.GetFromJsonAsync(updateUrl, UpdaterResponseJsonContext.Default.UpdaterResponse);
|
||||
|
||||
_currentGitLabReleaseChannel = ReleaseInformation.IsCanaryBuild
|
||||
? releaseChannels.Canary
|
||||
: releaseChannels.Stable;
|
||||
|
||||
Logger.Info?.Print(LogClass.Application, $"Loaded GitLab release channel for '{(ReleaseInformation.IsCanaryBuild ? "canary" : "stable")}'");
|
||||
|
||||
_changelogUrlFormat = _currentGitLabReleaseChannel.UrlFormat;
|
||||
_buildVer = response.Tag;
|
||||
_buildUrl = response.DownloadUrl;
|
||||
_changelogUrlFormat = response.ReleaseUrlFormat;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new AggregateException(
|
||||
$"An error occurred when parsing JSON response from API ({e.GetType().AsFullNamePrettyString()}): {e.Message}",
|
||||
e);
|
||||
}
|
||||
|
||||
string fetchedJson = await jsonClient.GetStringAsync(_currentGitLabReleaseChannel.GetLatestReleaseApiUrl());
|
||||
GitLabReleasesJsonResponse fetched = JsonHelper.Deserialize(fetchedJson, _glSerializerContext.GitLabReleasesJsonResponse);
|
||||
|
||||
_buildVer = fetched.TagName;
|
||||
_buildUrl = fetched.Assets.Links
|
||||
.FirstOrDefault(link =>
|
||||
link.AssetName.StartsWith("ryujinx") && link.AssetName.EndsWith(_platformExt)
|
||||
)?.Url;
|
||||
|
||||
// If build URL not found, assume no new update is available.
|
||||
if (_buildUrl is null)
|
||||
if (_buildUrl is null or "")
|
||||
{
|
||||
if (showVersionUpToDate)
|
||||
{
|
||||
@ -104,35 +129,17 @@ namespace Ryujinx.Ava.Systems
|
||||
|
||||
return (currentVersion, newVersion);
|
||||
}
|
||||
|
||||
[JsonSerializable(typeof(UpdaterResponse))]
|
||||
partial class UpdaterResponseJsonContext : JsonSerializerContext;
|
||||
|
||||
[JsonSerializable(typeof(GitLabReleaseChannels))]
|
||||
partial class GitLabReleaseChannelPairContext : JsonSerializerContext;
|
||||
|
||||
public class GitLabReleaseChannels
|
||||
public class UpdaterResponse
|
||||
{
|
||||
public static async Task<GitLabReleaseChannels> GetAsync(HttpClient httpClient)
|
||||
=> await httpClient.GetFromJsonAsync(
|
||||
"https://git.ryujinx.app/ryubing/ryujinx/-/snippets/1/raw/main/meta.json",
|
||||
GitLabReleaseChannelPairContext.Default.GitLabReleaseChannels);
|
||||
[JsonPropertyName("tag")] public string Tag { get; set; }
|
||||
[JsonPropertyName("download_url")] public string DownloadUrl { get; set; }
|
||||
[JsonPropertyName("web_url")] public string ReleaseUrl { get; set; }
|
||||
|
||||
[JsonPropertyName("stable")] public ChannelType Stable { get; set; }
|
||||
[JsonPropertyName("canary")] public ChannelType Canary { get; set; }
|
||||
|
||||
public class ChannelType
|
||||
{
|
||||
[JsonPropertyName("id")] public long Id { get; set; }
|
||||
|
||||
[JsonPropertyName("group")] public string Group { get; set; }
|
||||
|
||||
[JsonPropertyName("project")] public string Project { get; set; }
|
||||
|
||||
public string UrlFormat => $"https://git.ryujinx.app/{ToString()}/-/releases/{{0}}";
|
||||
|
||||
public override string ToString() => $"{Group}/{Project}";
|
||||
|
||||
public string GetLatestReleaseApiUrl() =>
|
||||
$"https://git.ryujinx.app/api/v4/projects/{Id}/releases/permalink/latest";
|
||||
}
|
||||
[JsonIgnore] public string ReleaseUrlFormat => ReleaseUrl.Replace(Tag, "{0}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ using ICSharpCode.SharpZipLib.Tar;
|
||||
using ICSharpCode.SharpZipLib.Zip;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.Common.Models.Github;
|
||||
using Ryujinx.Ava.Common.Models.GitLab;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.Utilities;
|
||||
using Ryujinx.Common;
|
||||
@ -33,19 +32,14 @@ namespace Ryujinx.Ava.Systems
|
||||
internal static partial class Updater
|
||||
{
|
||||
private static readonly GithubReleasesJsonSerializerContext _ghSerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||
private static readonly GitLabReleasesJsonSerializerContext _glSerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||
|
||||
|
||||
private static readonly string _platformExt = BuildPlatformExtension();
|
||||
private static readonly string _homeDir = AppDomain.CurrentDomain.BaseDirectory;
|
||||
private static readonly string _updateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update");
|
||||
private static readonly string _updatePublishDir = Path.Combine(_updateDir, "publish");
|
||||
private const int ConnectionCount = 4;
|
||||
|
||||
private static string _buildVer;
|
||||
|
||||
|
||||
private static readonly string _platformExt = BuildPlatformExtension();
|
||||
|
||||
private static string _buildUrl;
|
||||
private static long _buildSize;
|
||||
private static bool _updateSuccessful;
|
||||
@ -168,7 +162,7 @@ namespace Ryujinx.Ava.Systems
|
||||
HttpClient result = new();
|
||||
|
||||
// Required by GitHub to interact with APIs.
|
||||
result.DefaultRequestHeaders.Add("User-Agent", "Ryujinx-Updater/1.0.0");
|
||||
result.DefaultRequestHeaders.Add("User-Agent", $"Ryujinx-Updater/{ReleaseInformation.Version}");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ using Ryujinx.Ava.Systems.AppLibrary;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
|
||||
namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
@ -11,15 +12,37 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
private readonly ApplicationLibrary _appLibrary;
|
||||
|
||||
private (int Status, int Name) _sorting;
|
||||
|
||||
public bool IsSortedByTitle => true;
|
||||
public bool IsSortedByStatus => true;
|
||||
|
||||
// Avalonia takes names of status from these variables
|
||||
public LocaleKeys IsStringPlayable => LocaleKeys.CompatibilityListPlayable;
|
||||
public LocaleKeys IsStringInGame => LocaleKeys.CompatibilityListIngame;
|
||||
public LocaleKeys IsStringMenus => LocaleKeys.CompatibilityListMenus;
|
||||
public LocaleKeys IsStringBoots => LocaleKeys.CompatibilityListBoots;
|
||||
public LocaleKeys IsStringNothing => LocaleKeys.CompatibilityListNothing;
|
||||
|
||||
public string PlayableInfoText { get; set; }
|
||||
public string InGameInfoText { get; set; }
|
||||
public string MenusInfoText { get; set; }
|
||||
public string BootsInfoText { get; set; }
|
||||
public string NothingInfoText { get; set; }
|
||||
|
||||
|
||||
private IEnumerable<CompatibilityEntry> _currentEntries = CompatibilityDatabase.Entries;
|
||||
|
||||
private string[] _ownedGameTitleIds = [];
|
||||
|
||||
private Func<CompatibilityEntry, object> _sortKeySelector = x => x.GameName; // Default sort by GameName
|
||||
|
||||
public IEnumerable<CompatibilityEntry> CurrentEntries => OnlyShowOwnedGames
|
||||
? _currentEntries.Where(x =>
|
||||
x.TitleId.Check(tid => _ownedGameTitleIds.ContainsIgnoreCase(tid)))
|
||||
: _currentEntries;
|
||||
|
||||
public CompatibilityViewModel() { }
|
||||
public CompatibilityViewModel() {}
|
||||
|
||||
private void AppCountUpdated(object _, ApplicationCountUpdatedEventArgs __)
|
||||
=> _ownedGameTitleIds = _appLibrary.Applications.Keys.Select(x => x.ToString("X16")).ToArray();
|
||||
@ -27,19 +50,29 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
public CompatibilityViewModel(ApplicationLibrary appLibrary)
|
||||
{
|
||||
_appLibrary = appLibrary;
|
||||
|
||||
AppCountUpdated(null, null);
|
||||
|
||||
CountByStatus();
|
||||
_appLibrary.ApplicationCountUpdated += AppCountUpdated;
|
||||
}
|
||||
|
||||
public void CountByStatus()
|
||||
{
|
||||
PlayableInfoText = LocaleManager.Instance[LocaleKeys.CompatibilityListPlayable] + ": " + CurrentEntries.Count(x => x.Status == LocaleKeys.CompatibilityListPlayable);
|
||||
InGameInfoText = LocaleManager.Instance[LocaleKeys.CompatibilityListIngame] + ": " + CurrentEntries.Count(x => x.Status == LocaleKeys.CompatibilityListIngame);
|
||||
MenusInfoText = LocaleManager.Instance[LocaleKeys.CompatibilityListMenus] + ": " + CurrentEntries.Count(x => x.Status == LocaleKeys.CompatibilityListMenus);
|
||||
BootsInfoText = LocaleManager.Instance[LocaleKeys.CompatibilityListBoots] + ": " + CurrentEntries.Count(x => x.Status == LocaleKeys.CompatibilityListBoots);
|
||||
NothingInfoText = LocaleManager.Instance[LocaleKeys.CompatibilityListNothing] + ": " + CurrentEntries.Count(x => x.Status == LocaleKeys.CompatibilityListNothing);
|
||||
|
||||
_onlyShowOwnedGames = true;
|
||||
}
|
||||
|
||||
void IDisposable.Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
_appLibrary.ApplicationCountUpdated -= AppCountUpdated;
|
||||
}
|
||||
|
||||
private bool _onlyShowOwnedGames = true;
|
||||
private bool _onlyShowOwnedGames;
|
||||
|
||||
public bool OnlyShowOwnedGames
|
||||
{
|
||||
@ -54,17 +87,37 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void NameSorting(int nameSort = 0)
|
||||
{
|
||||
_sorting.Name = nameSort;
|
||||
SortApply();
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(SortName));
|
||||
}
|
||||
|
||||
public void StatusSorting(int statusSort = 0)
|
||||
{
|
||||
_sorting.Status = statusSort;
|
||||
SortApply();
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(SortName));
|
||||
}
|
||||
|
||||
public void Search(string searchTerm)
|
||||
{
|
||||
if (string.IsNullOrEmpty(searchTerm))
|
||||
{
|
||||
SetEntries(CompatibilityDatabase.Entries);
|
||||
SortApply();
|
||||
return;
|
||||
}
|
||||
|
||||
SetEntries(CompatibilityDatabase.Entries.Where(x =>
|
||||
x.GameName.ContainsIgnoreCase(searchTerm)
|
||||
|| x.TitleId.Check(tid => tid.ContainsIgnoreCase(searchTerm))));
|
||||
|
||||
SortApply();
|
||||
}
|
||||
|
||||
private void SetEntries(IEnumerable<CompatibilityEntry> entries)
|
||||
@ -72,5 +125,43 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
_currentEntries = entries.ToList();
|
||||
OnPropertyChanged(nameof(CurrentEntries));
|
||||
}
|
||||
|
||||
private void SortApply()
|
||||
{
|
||||
try
|
||||
{
|
||||
_currentEntries = (_sorting switch
|
||||
{
|
||||
(0, 0) => _currentEntries.OrderBy(x => _sortKeySelector(x) ?? string.Empty), // A - Z
|
||||
(0, 1) => _currentEntries.OrderByDescending(x => _sortKeySelector(x) ?? string.Empty), // Z - A
|
||||
(1, 0) => _currentEntries.OrderBy(x => x.Status).ThenBy(x => x.GameName, StringComparer.OrdinalIgnoreCase), // Status Playable - Nothing, then A - Z
|
||||
(1, 1) => _currentEntries.OrderBy(x => x.Status).ThenByDescending(x => x.GameName, StringComparer.OrdinalIgnoreCase), // Status Nothing - Playable, then A - Z
|
||||
(2, 0) => _currentEntries.OrderByDescending(x => x.Status).ThenBy(x => x.GameName, StringComparer.OrdinalIgnoreCase), // Status Playable - Nothing, then Z - A
|
||||
(2, 1) => _currentEntries.OrderByDescending(x => x.Status).ThenByDescending(x => x.GameName, StringComparer.OrdinalIgnoreCase), // Status Nothing - Playable, then Z - A
|
||||
_ => _currentEntries.OrderBy(x => x.Status)
|
||||
}).ToList();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(CurrentEntries));
|
||||
}
|
||||
|
||||
public string SortName
|
||||
{
|
||||
get
|
||||
{
|
||||
return (_sorting.Name) switch
|
||||
{
|
||||
(0) => LocaleManager.Instance[LocaleKeys.GameListSortStatusNameAscending],
|
||||
(1) => LocaleManager.Instance[LocaleKeys.GameListSortStatusNameDescending],
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -50,6 +50,9 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
private string _controllerImage;
|
||||
private int _device;
|
||||
private object _configViewModel;
|
||||
private bool _isChangeTrackingActive;
|
||||
private string _chosenProfile;
|
||||
[ObservableProperty] private bool _isModified;
|
||||
[ObservableProperty] private string _profileName;
|
||||
[ObservableProperty] private bool _notificationIsVisible; // Automatically call the NotificationView property with OnPropertyChanged()
|
||||
[ObservableProperty] private string _notificationText; // Automatically call the NotificationText property with OnPropertyChanged()
|
||||
@ -84,6 +87,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
public AvaloniaList<string> ProfilesList { get; set; }
|
||||
public AvaloniaList<string> DeviceList { get; set; }
|
||||
|
||||
public bool UseGlobalConfig;
|
||||
|
||||
// XAML Flags
|
||||
public bool ShowSettings => _device > 0;
|
||||
public bool IsController => _device > 1;
|
||||
@ -94,31 +99,16 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
public bool HasLed => SelectedGamepad.Features.HasFlag(GamepadFeaturesFlag.Led);
|
||||
public bool CanClearLed => SelectedGamepad.Name.ContainsIgnoreCase("DualSense");
|
||||
|
||||
public bool _isChangeTrackingActive;
|
||||
|
||||
public bool _isModified;
|
||||
|
||||
public bool IsModified
|
||||
{
|
||||
get => _isModified;
|
||||
set
|
||||
{
|
||||
_isModified = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public event Action NotifyChangesEvent;
|
||||
|
||||
public string _profileChoose;
|
||||
public string ProfileChoose
|
||||
|
||||
public string ChosenProfile
|
||||
{
|
||||
get => _profileChoose;
|
||||
get => _chosenProfile;
|
||||
set
|
||||
{
|
||||
// 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
|
||||
_profileChoose = value;
|
||||
_chosenProfile = value;
|
||||
LoadProfile();
|
||||
OnPropertyChanged();
|
||||
}
|
||||
@ -290,7 +280,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
|
||||
public InputConfig Config { get; set; }
|
||||
|
||||
public InputViewModel(UserControl owner) : this()
|
||||
public InputViewModel(UserControl owner, bool useGlobal = false) : this()
|
||||
{
|
||||
if (Program.PreviewerDetached)
|
||||
{
|
||||
@ -303,6 +293,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
|
||||
_mainWindow.ViewModel.AppHost?.NpadManager.BlockInputUpdates();
|
||||
|
||||
UseGlobalConfig = useGlobal;
|
||||
|
||||
_isLoaded = false;
|
||||
|
||||
LoadDevices();
|
||||
@ -335,9 +327,18 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
PlayerIndexes.Add(new(PlayerIndex.Handheld, LocaleManager.Instance[LocaleKeys.ControllerSettingsHandheld]));
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void LoadConfiguration(InputConfig inputConfig = null)
|
||||
{
|
||||
Config = inputConfig ?? ConfigurationState.Instance.Hid.InputConfig.Value.FirstOrDefault(inputConfig => inputConfig.PlayerIndex == _playerId);
|
||||
if (UseGlobalConfig && Program.UseExtraConfig)
|
||||
{
|
||||
Config = inputConfig ?? ConfigurationState.InstanceExtra.Hid.InputConfig.Value.FirstOrDefault(inputConfig => inputConfig.PlayerIndex == _playerId);
|
||||
}
|
||||
else
|
||||
{
|
||||
Config = inputConfig ?? ConfigurationState.Instance.Hid.InputConfig.Value.FirstOrDefault(inputConfig => inputConfig.PlayerIndex == _playerId);
|
||||
}
|
||||
|
||||
if (Config is StandardKeyboardInputConfig keyboardInputConfig)
|
||||
{
|
||||
@ -902,7 +903,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
|
||||
LoadProfiles();
|
||||
|
||||
ProfileChoose = ProfileName; // Show new profile
|
||||
ChosenProfile = ProfileName; // Show new profile
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -936,7 +937,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
|
||||
LoadProfiles();
|
||||
|
||||
ProfileChoose = ProfilesList[0].ToString(); // Show default profile
|
||||
ChosenProfile = ProfilesList[0].ToString(); // Show default profile
|
||||
}
|
||||
}
|
||||
|
||||
@ -966,7 +967,14 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
|
||||
List<InputConfig> newConfig = [];
|
||||
|
||||
newConfig.AddRange(ConfigurationState.Instance.Hid.InputConfig.Value);
|
||||
if (UseGlobalConfig && Program.UseExtraConfig)
|
||||
{
|
||||
newConfig.AddRange(ConfigurationState.InstanceExtra.Hid.InputConfig.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
newConfig.AddRange(ConfigurationState.Instance.Hid.InputConfig.Value);
|
||||
}
|
||||
|
||||
newConfig.Remove(newConfig.FirstOrDefault(x => x == null));
|
||||
|
||||
@ -1007,18 +1015,21 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
}
|
||||
}
|
||||
|
||||
_mainWindow.ViewModel.AppHost?.NpadManager.ReloadConfiguration(newConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse);
|
||||
|
||||
// Atomically replace and signal input change.
|
||||
// NOTE: Do not modify InputConfig.Value directly as other code depends on the on-change event.
|
||||
ConfigurationState.Instance.Hid.InputConfig.Value = newConfig;
|
||||
_mainWindow.ViewModel.AppHost?.NpadManager.ReloadConfiguration(newConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse);
|
||||
|
||||
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
||||
}
|
||||
|
||||
public void NotifyChange(string property)
|
||||
{
|
||||
OnPropertyChanged(property);
|
||||
if (UseGlobalConfig && Program.UseExtraConfig)
|
||||
{
|
||||
// In User Settings when "Use Global Input" is enabled, it saves global input to global setting
|
||||
ConfigurationState.InstanceExtra.Hid.InputConfig.Value = newConfig;
|
||||
ConfigurationState.InstanceExtra.ToFileFormat().SaveConfig(Program.GlobalConfigurationPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
ConfigurationState.Instance.Hid.InputConfig.Value = newConfig;
|
||||
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
||||
}
|
||||
}
|
||||
|
||||
public void NotifyChanges()
|
||||
|
@ -1575,28 +1575,31 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
public bool InitializeUserConfig(ApplicationData application)
|
||||
{
|
||||
// Code where conditions will be met before loading the user configuration (Global Config)
|
||||
string BackendThreadingInit = Program.BackendThreadingArg;
|
||||
|
||||
BackendThreadingInit ??= ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString();
|
||||
string backendThreadingInit = Program.BackendThreadingArg ?? ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString();
|
||||
|
||||
// If a configuration is found in the "/games/xxxxxxxxxxxxxx" folder, the program will load the user setting.
|
||||
string idGame = application.IdBaseString;
|
||||
if (ConfigurationFileFormat.TryLoad(Program.GetDirGameUserConfig(idGame), out ConfigurationFileFormat configurationFileFormat))
|
||||
{
|
||||
// Loads the user configuration, having previously changed the global configuration to the user configuration
|
||||
ConfigurationState.Instance.Load(configurationFileFormat, Program.GetDirGameUserConfig(idGame, true, true), idGame);
|
||||
// Loads the user configuration, having previously changed the global configuration to the user configuration
|
||||
ConfigurationState.Instance.Load(configurationFileFormat, Program.GetDirGameUserConfig(idGame, true), idGame);
|
||||
|
||||
if (ConfigurationFileFormat.TryLoad(Program.GlobalConfigurationPath, out ConfigurationFileFormat configurationFileFormatExtra))
|
||||
{
|
||||
//This is where the global configuration will be stored.
|
||||
//This allows you to change the global configuration settings during the game (for example, the global input setting)
|
||||
ConfigurationState.InstanceExtra.Load(configurationFileFormatExtra, Program.GlobalConfigurationPath);
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
|
||||
List<string> Arguments = new()
|
||||
{
|
||||
"--bt", ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString() // BackendThreading
|
||||
};
|
||||
|
||||
Rebooter.RebootAppWithGame(application.Path, Arguments);
|
||||
Rebooter.RebootAppWithGame(application.Path,
|
||||
[
|
||||
"--bt",
|
||||
ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString()
|
||||
]);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1991,7 +1994,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
// just checking for file presence
|
||||
viewModel.SelectedApplication.HasIndependentConfiguration = File.Exists(
|
||||
Program.GetDirGameUserConfig(viewModel.SelectedApplication.IdString, false, false));
|
||||
Program.GetDirGameUserConfig(viewModel.SelectedApplication.IdString));
|
||||
|
||||
viewModel.RefreshView();
|
||||
});
|
||||
|
@ -53,6 +53,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
[ObservableProperty] private bool _isVulkanAvailable = true;
|
||||
[ObservableProperty] private bool _gameListNeedsRefresh;
|
||||
private readonly List<string> _gpuIds = [];
|
||||
public bool _useInputGlobalConfig;
|
||||
private int _graphicsBackendIndex;
|
||||
private int _scalingFilter;
|
||||
private int _scalingFilterLevel;
|
||||
@ -64,6 +65,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
public event Action CloseWindow;
|
||||
public event Action SaveSettingsEvent;
|
||||
public event Action<bool> LocalGlobalInputSwitchEvent;
|
||||
private int _networkInterfaceIndex;
|
||||
private int _multiplayerModeIndex;
|
||||
private string _ldnPassphrase;
|
||||
@ -84,6 +86,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
public bool IsGameTitleNotNull => !string.IsNullOrEmpty(GameTitle);
|
||||
public double PanelOpacity => IsGameTitleNotNull ? 0.5 : 1;
|
||||
|
||||
|
||||
public int ResolutionScale
|
||||
{
|
||||
get => _resolutionScale;
|
||||
@ -141,13 +144,26 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
public bool EnableDockedMode { get; set; }
|
||||
public bool EnableKeyboard { get; set; }
|
||||
public bool EnableMouse { get; set; }
|
||||
public bool DisableInputWhenOutOfFocus { get; set; }
|
||||
|
||||
public bool DisableInputWhenOutOfFocus { get; set; }
|
||||
public int FocusLostActionType { get; set; }
|
||||
|
||||
public bool UseGlobalInputConfig
|
||||
{
|
||||
get => _useInputGlobalConfig;
|
||||
set
|
||||
{
|
||||
_useInputGlobalConfig = value;
|
||||
LocalGlobalInputSwitchEvent?.Invoke(_useInputGlobalConfig);
|
||||
OnPropertyChanged(nameof(InputPanelOpacity));
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public double InputPanelOpacity => UseGlobalInputConfig ? 0.5 : 1;
|
||||
|
||||
public VSyncMode VSyncMode
|
||||
{
|
||||
get => _vSyncMode;
|
||||
get => _vSyncMode;
|
||||
set
|
||||
{
|
||||
if (value is VSyncMode.Custom or VSyncMode.Switch or VSyncMode.Unbounded)
|
||||
@ -371,7 +387,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
public bool IsInvalidLdnPassphraseVisible { get; set; }
|
||||
|
||||
public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this(false)
|
||||
public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this()
|
||||
{
|
||||
_virtualFileSystem = virtualFileSystem;
|
||||
_contentManager = contentManager;
|
||||
@ -392,7 +408,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
string gameName,
|
||||
string gameId,
|
||||
byte[] gameIconData,
|
||||
bool enableToLoadCustomConfig) : this(enableToLoadCustomConfig)
|
||||
bool customConfig) : this()
|
||||
{
|
||||
_virtualFileSystem = virtualFileSystem;
|
||||
_contentManager = contentManager;
|
||||
@ -408,9 +424,18 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
_gameTitle = gameName;
|
||||
_gameId = gameId;
|
||||
|
||||
if (enableToLoadCustomConfig) // During the game. If there is no user config, then load the global config window
|
||||
if (customConfig) // During the game. If there is no user config, then load the global config window
|
||||
{
|
||||
string gameDir = Program.GetDirGameUserConfig(gameId, false, true);
|
||||
string gameDir = Program.GetDirGameUserConfig(gameId, true);
|
||||
|
||||
Program.UseExtraConfig = true;
|
||||
|
||||
if (ConfigurationFileFormat.TryLoad(Program.GlobalConfigurationPath, out ConfigurationFileFormat configurationFileFormatExtra))
|
||||
{
|
||||
// Extra load global configuration for input setting and save global input setting with other global config
|
||||
ConfigurationState.InstanceExtra.Load(configurationFileFormatExtra, Program.GlobalConfigurationPath);
|
||||
}
|
||||
|
||||
if (ConfigurationFileFormat.TryLoad(gameDir, out ConfigurationFileFormat configurationFileFormat))
|
||||
{
|
||||
ConfigurationState.Instance.Load(configurationFileFormat, gameDir, gameId);
|
||||
@ -426,7 +451,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public SettingsViewModel(bool noLoadGlobalConfig = false)
|
||||
public SettingsViewModel()
|
||||
{
|
||||
GameDirectories = [];
|
||||
AutoloadDirectories = [];
|
||||
@ -550,9 +575,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadCurrentConfiguration()
|
||||
public void LoadCurrentConfiguration(bool global = false)
|
||||
{
|
||||
ConfigurationState config = ConfigurationState.Instance;
|
||||
ConfigurationState config = global ? ConfigurationState.InstanceExtra: ConfigurationState.Instance;
|
||||
|
||||
// User Interface
|
||||
EnableDiscordIntegration = config.EnableDiscordIntegration;
|
||||
@ -578,6 +603,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
};
|
||||
|
||||
// Input
|
||||
UseGlobalInputConfig = config.System.UseInputGlobalConfig;
|
||||
EnableDockedMode = config.System.EnableDockedMode;
|
||||
EnableKeyboard = config.Hid.EnableKeyboard;
|
||||
EnableMouse = config.Hid.EnableMouse;
|
||||
@ -660,9 +686,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
LdnServer = config.Multiplayer.LdnServer;
|
||||
}
|
||||
|
||||
public void SaveSettings()
|
||||
public void SaveSettings(bool global = false)
|
||||
{
|
||||
ConfigurationState config = ConfigurationState.Instance;
|
||||
ConfigurationState config = global ? ConfigurationState.InstanceExtra: ConfigurationState.Instance;
|
||||
|
||||
// User Interface
|
||||
config.EnableDiscordIntegration.Value = EnableDiscordIntegration;
|
||||
@ -684,6 +710,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
};
|
||||
|
||||
// Input
|
||||
config.System.UseInputGlobalConfig.Value = UseGlobalInputConfig;
|
||||
config.System.EnableDockedMode.Value = EnableDockedMode;
|
||||
config.Hid.EnableKeyboard.Value = EnableKeyboard;
|
||||
config.Hid.EnableMouse.Value = EnableMouse;
|
||||
@ -796,11 +823,14 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
private static void RevertIfNotSaved()
|
||||
{
|
||||
// maybe this is an unnecessary check(all options need to be tested)
|
||||
/*
|
||||
maybe this is an unnecessary check(all options need to be tested)
|
||||
if (string.IsNullOrEmpty(Program.GlobalConfigurationPath))
|
||||
{
|
||||
Program.ReloadConfig();
|
||||
}
|
||||
*/
|
||||
Program.ReloadConfig();
|
||||
}
|
||||
|
||||
public void ApplyButton()
|
||||
@ -810,7 +840,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
public void DeleteConfigGame()
|
||||
{
|
||||
string gameDir = Program.GetDirGameUserConfig(GameId, false, false);
|
||||
string gameDir = Program.GetDirGameUserConfig(GameId);
|
||||
|
||||
if (File.Exists(gameDir))
|
||||
{
|
||||
|
@ -100,7 +100,7 @@
|
||||
Name="ProfileBox"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
SelectedItem="{Binding ProfileChoose, Mode=TwoWay}"
|
||||
SelectedItem="{Binding ChosenProfile, Mode=TwoWay}"
|
||||
SelectionChanged="ComboBox_SelectionChanged"
|
||||
ItemsSource="{Binding ProfilesList}"
|
||||
Text="{Binding ProfileName, Mode=TwoWay}" />
|
||||
@ -203,7 +203,6 @@
|
||||
</StackPanel>
|
||||
<ContentControl IsVisible="{Binding NotificationIsVisible}">
|
||||
<ContentControl.Content>
|
||||
|
||||
<StackPanel>
|
||||
<TextBlock
|
||||
Margin="5,20,0,0"
|
||||
|
@ -1,6 +1,7 @@
|
||||
using Avalonia.Controls;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.Systems.Configuration;
|
||||
using Ryujinx.Ava.UI.Controls;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.Models;
|
||||
@ -14,7 +15,7 @@ namespace Ryujinx.Ava.UI.Views.Input
|
||||
|
||||
public InputView()
|
||||
{
|
||||
ViewModel = new InputViewModel(this);
|
||||
ViewModel = new InputViewModel(this, ConfigurationState.Instance.System.UseInputGlobalConfig);
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
@ -24,6 +25,13 @@ namespace Ryujinx.Ava.UI.Views.Input
|
||||
ViewModel.Save();
|
||||
}
|
||||
|
||||
public void ToggleLocalGlobalInput(bool enableConfigGlobal)
|
||||
{
|
||||
Dispose();
|
||||
ViewModel = new InputViewModel(this, enableConfigGlobal); // Create new Input Page with global input configs
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private async void PlayerIndexBox_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (PlayerIndexBox != null)
|
||||
|
@ -56,7 +56,27 @@
|
||||
SelectedIndex="{Binding PreferredGpuIndex}"
|
||||
ItemsSource="{Binding AvailableGpus}"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
ToolTip.Tip="{ext:Locale GraphicsBackendThreadingTooltip}"
|
||||
Text="{ext:Locale SettingsTabGraphicsBackendMultithreading}"
|
||||
Width="250" />
|
||||
<ComboBox Width="350"
|
||||
HorizontalContentAlignment="Left"
|
||||
ToolTip.Tip="{ext:Locale GalThreadingTooltip}"
|
||||
SelectedIndex="{Binding GraphicsBackendMultithreadingIndex}">
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale CommonAuto}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale CommonOff}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale CommonOn}" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<Separator Height="1" />
|
||||
<TextBlock Classes="h1" Text="{ext:Locale SettingsTabGraphicsFeatures}" />
|
||||
<StackPanel Margin="10,0,0,0" Orientation="Vertical" Spacing="10">
|
||||
@ -255,32 +275,7 @@
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Margin="10,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical"
|
||||
Spacing="10">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock VerticalAlignment="Center"
|
||||
ToolTip.Tip="{ext:Locale GraphicsBackendThreadingTooltip}"
|
||||
Text="{ext:Locale SettingsTabGraphicsBackendMultithreading}"
|
||||
Width="250" />
|
||||
<ComboBox Width="350"
|
||||
HorizontalContentAlignment="Left"
|
||||
ToolTip.Tip="{ext:Locale GalThreadingTooltip}"
|
||||
SelectedIndex="{Binding GraphicsBackendMultithreadingIndex}">
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale CommonAuto}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale CommonOff}" />
|
||||
</ComboBoxItem>
|
||||
<ComboBoxItem>
|
||||
<TextBlock Text="{ext:Locale CommonOn}" />
|
||||
</ComboBoxItem>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
<Separator Height="1" />
|
||||
<TextBlock Classes="h1" Text="{ext:Locale SettingsTabGraphicsDeveloperOptions}" />
|
||||
<StackPanel
|
||||
|
@ -1,4 +1,4 @@
|
||||
<UserControl
|
||||
<UserControl
|
||||
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsInputView"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
@ -22,9 +22,16 @@
|
||||
<Panel
|
||||
Margin="10">
|
||||
<Grid RowDefinitions="Auto,*,Auto">
|
||||
<views:InputView
|
||||
Grid.Row="0"
|
||||
Name="InputView" />
|
||||
<StackPanel>
|
||||
<!--
|
||||
Opacity="{Binding PanelOpacityInput}">
|
||||
|
||||
IsEnabled="{Binding !EnableConfigGlobal}">
|
||||
-->
|
||||
<views:InputView
|
||||
Grid.Row="0"
|
||||
Name="InputView" />
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Orientation="Vertical"
|
||||
Grid.Row="2">
|
||||
@ -34,6 +41,13 @@
|
||||
<StackPanel
|
||||
Orientation="Horizontal"
|
||||
Spacing="10">
|
||||
<CheckBox
|
||||
ToolTip.Tip="{ext:Locale UseGlobalInputTooltip}"
|
||||
MinWidth="0"
|
||||
IsChecked="{Binding UseGlobalInputConfig}">
|
||||
<TextBlock
|
||||
Text="{ext:Locale SettingsTabInputUseGlobalInput}" />
|
||||
</CheckBox>
|
||||
<CheckBox
|
||||
ToolTip.Tip="{ext:Locale DockModeToggleTooltip}"
|
||||
MinWidth="0"
|
||||
|
@ -1,4 +1,4 @@
|
||||
<window:StyleableAppWindow xmlns="https://github.com/avaloniaui"
|
||||
<window:StyleableAppWindow xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:helpers="using:Ryujinx.Ava.UI.Helpers"
|
||||
@ -17,65 +17,277 @@
|
||||
<window:StyleableAppWindow.DataContext>
|
||||
<viewModels:CompatibilityViewModel />
|
||||
</window:StyleableAppWindow.DataContext>
|
||||
<Grid RowDefinitions="Auto,*">
|
||||
<Grid Grid.Row="0" ColumnDefinitions="Auto,*,Auto,Auto" Name="FlushControls">
|
||||
<controls:RyujinxLogo
|
||||
<Grid RowDefinitions="Auto,Auto,*">
|
||||
|
||||
<!-- UI FlushControls -->
|
||||
<Grid Grid.Row="0" ColumnDefinitions="Auto,*,Auto,Auto,Auto" Name="FlushControls">
|
||||
<controls:RyujinxLogo
|
||||
Grid.Column="0"
|
||||
Margin="15, 0, 7, 0"
|
||||
ToolTip.Tip="{ext:WindowTitle CompatibilityListTitle, False}"/>
|
||||
<TextBox Name="SearchBoxFlush" Grid.Column="1" Margin="0, 5, 0, 5" HorizontalAlignment="Stretch" Watermark="{ext:Locale CompatibilityListSearchBoxWatermarkWithCount}" TextChanged="TextBox_OnTextChanged" />
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal" Margin="10, 5, 0, 5">
|
||||
<TextBlock
|
||||
Margin="10,0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
DockPanel.Dock="Right"
|
||||
Text="{ext:Locale CommonSort}" />
|
||||
<DropDownButton
|
||||
Width="150"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Content="{Binding SortName}"
|
||||
DockPanel.Dock="Right">
|
||||
<DropDownButton.Flyout>
|
||||
<Flyout Placement="Bottom">
|
||||
<StackPanel
|
||||
Margin="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical">
|
||||
<StackPanel>
|
||||
<RadioButton
|
||||
Checked="Sort_Name_Checked"
|
||||
Content="{ext:Locale GameListSortStatusNameAscending}"
|
||||
GroupName="Sort"
|
||||
IsChecked="{Binding IsSortedByTitle, Mode=OneTime}"
|
||||
Tag="0" />
|
||||
<RadioButton
|
||||
Checked="Sort_Name_Checked"
|
||||
Content="{ext:Locale GameListSortStatusNameDescending}"
|
||||
GroupName="Sort"
|
||||
Tag="1" />
|
||||
</StackPanel>
|
||||
<Border
|
||||
Width="60"
|
||||
Height="2"
|
||||
Margin="5"
|
||||
HorizontalAlignment="Stretch"
|
||||
BorderBrush="White"
|
||||
BorderThickness="0,1,0,0">
|
||||
<Separator Height="0" HorizontalAlignment="Stretch" />
|
||||
</Border>
|
||||
<RadioButton
|
||||
Checked="Sort_Status_Checked"
|
||||
Content="{ext:Locale GameListSortStatusDisable}"
|
||||
GroupName="Order"
|
||||
IsChecked="{Binding IsSortedByStatus, Mode=OneTime}"
|
||||
Tag="0" />
|
||||
<RadioButton
|
||||
Checked="Sort_Status_Checked"
|
||||
Content="{ext:Locale GameListSortStatusAscending}"
|
||||
GroupName="Order"
|
||||
Tag="1" />
|
||||
<RadioButton
|
||||
Checked="Sort_Status_Checked"
|
||||
Content="{ext:Locale GameListSortStatusDescending}"
|
||||
GroupName="Order"
|
||||
Tag="2" />
|
||||
</StackPanel>
|
||||
</Flyout>
|
||||
</DropDownButton.Flyout>
|
||||
</DropDownButton>
|
||||
</StackPanel>
|
||||
<CheckBox Grid.Column="3" Margin="7, 0, 0, 0" IsChecked="{Binding OnlyShowOwnedGames}" />
|
||||
<TextBlock Grid.Column="4" Padding="0, 0, 138, 0" Margin="-10, 0, 18, 0" Text="{ext:Locale CompatibilityListOnlyShowOwnedGames}" />
|
||||
</Grid>
|
||||
|
||||
<!-- UI NormalControls -->
|
||||
<Grid Grid.Row="0" ColumnDefinitions="*,Auto,Auto,Auto" Name="NormalControls">
|
||||
<TextBox Name="SearchBoxNormal" Grid.Column="0" Margin="15, 0, 0, 5" HorizontalAlignment="Stretch" Watermark="{ext:Locale CompatibilityListSearchBoxWatermarkWithCount}" TextChanged="TextBox_OnTextChanged" />
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal" Margin="10, 0, 5, 5">
|
||||
<TextBlock
|
||||
Margin="10,0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
DockPanel.Dock="Right"
|
||||
Text="{ext:Locale CommonSort}" />
|
||||
<DropDownButton
|
||||
Width="150"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Content="{Binding SortName}"
|
||||
DockPanel.Dock="Right">
|
||||
<DropDownButton.Flyout>
|
||||
<Flyout Placement="Bottom">
|
||||
<StackPanel
|
||||
Margin="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Orientation="Vertical">
|
||||
<StackPanel>
|
||||
<RadioButton
|
||||
Checked="Sort_Name_Checked"
|
||||
Content="{ext:Locale GameListSortStatusNameAscending}"
|
||||
GroupName="Sort"
|
||||
IsChecked="{Binding IsSortedByTitle, Mode=OneTime}"
|
||||
Tag="0" />
|
||||
<RadioButton
|
||||
Checked="Sort_Name_Checked"
|
||||
Content="{ext:Locale GameListSortStatusNameDescending}"
|
||||
GroupName="Sort"
|
||||
Tag="1" />
|
||||
</StackPanel>
|
||||
<Border
|
||||
Width="60"
|
||||
Height="2"
|
||||
Margin="5"
|
||||
HorizontalAlignment="Stretch"
|
||||
BorderBrush="White"
|
||||
BorderThickness="0,1,0,0">
|
||||
<Separator Height="0" HorizontalAlignment="Stretch" />
|
||||
</Border>
|
||||
<RadioButton
|
||||
Checked="Sort_Status_Checked"
|
||||
Content="{ext:Locale GameListSortStatusDisable}"
|
||||
GroupName="Order"
|
||||
IsChecked="{Binding IsSortedByStatus, Mode=OneTime}"
|
||||
Tag="0" />
|
||||
<RadioButton
|
||||
Checked="Sort_Status_Checked"
|
||||
Content="{ext:Locale GameListSortStatusAscending}"
|
||||
GroupName="Order"
|
||||
Tag="1" />
|
||||
<RadioButton
|
||||
Checked="Sort_Status_Checked"
|
||||
Content="{ext:Locale GameListSortStatusDescending}"
|
||||
GroupName="Order"
|
||||
Tag="2" />
|
||||
</StackPanel>
|
||||
</Flyout>
|
||||
</DropDownButton.Flyout>
|
||||
</DropDownButton>
|
||||
</StackPanel>
|
||||
<CheckBox Grid.Column="2" Margin="7, 0, 0, 0" IsChecked="{Binding OnlyShowOwnedGames}" />
|
||||
<TextBlock Grid.Column="3" Padding="0, 0, 138, 0" Margin="-10, 0, 18, 0" Text="{ext:Locale CompatibilityListOnlyShowOwnedGames}" />
|
||||
<TextBlock Grid.Column="3" Padding="0, 0, 1, 0" Margin="-10, 0, 18, 0" Text="{ext:Locale CompatibilityListOnlyShowOwnedGames}" />
|
||||
</Grid>
|
||||
<Grid Grid.Row="0" ColumnDefinitions="*,Auto,Auto" Name="NormalControls">
|
||||
<TextBox Name="SearchBoxNormal" Grid.Column="0" Margin="15, 0, 0, 5" HorizontalAlignment="Stretch" Watermark="{ext:Locale CompatibilityListSearchBoxWatermark}" TextChanged="TextBox_OnTextChanged" />
|
||||
<CheckBox Grid.Column="1" Margin="7, 0, 0, 0" IsChecked="{Binding OnlyShowOwnedGames}" />
|
||||
<TextBlock Grid.Column="2" Padding="0, 0, 1, 0" Margin="-10, 0, 18, 0" Text="{ext:Locale CompatibilityListOnlyShowOwnedGames}" />
|
||||
|
||||
<!-- Description Field Above ScrollViewer -->
|
||||
<Grid Grid.Row="1" ColumnDefinitions="*,Auto" Margin="10, 5, 10, 5">
|
||||
<Grid Grid.Column="0">
|
||||
<Border Classes="listbox-item-style">
|
||||
<Grid MinWidth="800"
|
||||
ColumnDefinitions="Auto,Auto,Auto,*,Auto"
|
||||
Background="Transparent">
|
||||
<TextBlock Grid.Column="0"
|
||||
Text="{ext:Locale CompatibilityListGamesAndApplications}"
|
||||
FontWeight="Bold"
|
||||
Width="525"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
<TextBlock Grid.Column="1"
|
||||
Text="ID"
|
||||
FontWeight="Bold"
|
||||
Width="135"
|
||||
Padding="7, 0, 0, 0"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
<TextBlock Grid.Column="2"
|
||||
Padding="7, 0"
|
||||
Text="{ext:Locale CompatibilityListStatus}"
|
||||
FontWeight="Bold"
|
||||
Width="100"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
TextWrapping="NoWrap" />
|
||||
<TextBlock Grid.Column="3"
|
||||
Text="{ext:Locale CompatibilityListDescription}"
|
||||
FontWeight="Bold"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Left"
|
||||
TextWrapping="WrapWithOverflow" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
<Grid Grid.Column="1">
|
||||
<DropDownButton
|
||||
Width="80"
|
||||
Height="35"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Content="Info"
|
||||
DockPanel.Dock="Right">
|
||||
<DropDownButton.Flyout>
|
||||
<Flyout Placement="Bottom">
|
||||
<StackPanel>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Left"
|
||||
Padding="0,5"
|
||||
Text="Compatibility verified:" />
|
||||
<TextBlock
|
||||
HorizontalAlignment="Left"
|
||||
Foreground="{Binding IsStringPlayable, Converter={x:Static helpers:PlayabilityStatusConverter.Shared}}"
|
||||
Text="{Binding PlayableInfoText }" />
|
||||
<TextBlock
|
||||
HorizontalAlignment="Left"
|
||||
Foreground="{Binding IsStringInGame, Converter={x:Static helpers:PlayabilityStatusConverter.Shared}}"
|
||||
Text="{Binding InGameInfoText }" />
|
||||
<TextBlock
|
||||
HorizontalAlignment="Left"
|
||||
Foreground="{Binding IsStringMenus, Converter={x:Static helpers:PlayabilityStatusConverter.Shared}}"
|
||||
Text="{Binding MenusInfoText }" />
|
||||
<TextBlock
|
||||
HorizontalAlignment="Left"
|
||||
Foreground="{Binding IsStringBoots, Converter={x:Static helpers:PlayabilityStatusConverter.Shared}}"
|
||||
Text="{Binding BootsInfoText }" />
|
||||
<TextBlock
|
||||
HorizontalAlignment="Left"
|
||||
Foreground="{Binding IsStringNothing, Converter={x:Static helpers:PlayabilityStatusConverter.Shared}}"
|
||||
Text="{Binding NothingInfoText }" />
|
||||
</StackPanel>
|
||||
</Flyout>
|
||||
</DropDownButton.Flyout>
|
||||
</DropDownButton>
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
<ScrollViewer Grid.Row="1">
|
||||
<ListBox Margin="12, 0, 13, 0"
|
||||
Background="Transparent"
|
||||
ItemsSource="{Binding CurrentEntries}">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate DataType="{x:Type systems:CompatibilityEntry}">
|
||||
<Grid MinWidth="800"
|
||||
Margin="10"
|
||||
ColumnDefinitions="Auto,Auto,Auto,*"
|
||||
Background="Transparent"
|
||||
ToolTip.Tip="{Binding LocalizedLastUpdated}">
|
||||
<TextBlock Grid.Column="0"
|
||||
Text="{Binding GameName}"
|
||||
Width="525"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
<TextBlock Grid.Column="1"
|
||||
Width="135"
|
||||
Padding="7, 0, 0, 0"
|
||||
FontFamily="{StaticResource JetBrainsMono}"
|
||||
Text="{Binding FormattedTitleId}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
<TextBlock Grid.Column="2"
|
||||
Padding="7, 0"
|
||||
Text="{Binding LocalizedStatus}"
|
||||
Width="90"
|
||||
Background="Transparent"
|
||||
ToolTip.Tip="{Binding LocalizedStatusDescription}"
|
||||
Foreground="{Binding Status, Converter={x:Static helpers:PlayabilityStatusConverter.Shared}}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
TextWrapping="NoWrap" />
|
||||
<TextBlock Grid.Column="3"
|
||||
Text="{Binding FormattedIssueLabels}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Left"
|
||||
TextWrapping="WrapWithOverflow" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
|
||||
<!-- List of compatible games -->
|
||||
<ScrollViewer Grid.Row="2">
|
||||
<ListBox Margin="12, 0, 13, 0"
|
||||
Background="Transparent"
|
||||
ItemsSource="{Binding CurrentEntries}">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate DataType="{x:Type systems:CompatibilityEntry}">
|
||||
<Grid MinWidth="800"
|
||||
Margin="10"
|
||||
ColumnDefinitions="Auto,Auto,Auto,*"
|
||||
Background="Transparent"
|
||||
ToolTip.Tip="{Binding LocalizedLastUpdated}">
|
||||
<TextBlock Grid.Column="0"
|
||||
Text="{Binding GameName}"
|
||||
Width="525"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
<TextBlock Grid.Column="1"
|
||||
Width="135"
|
||||
Padding="7, 0, 0, 0"
|
||||
FontFamily="{StaticResource JetBrainsMono}"
|
||||
Text="{Binding FormattedTitleId}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
<TextBlock Grid.Column="2"
|
||||
Padding="7, 0"
|
||||
Text="{Binding LocalizedStatus}"
|
||||
Width="100"
|
||||
Background="Transparent"
|
||||
ToolTip.Tip="{Binding LocalizedStatusDescription}"
|
||||
Foreground="{Binding Status, Converter={x:Static helpers:PlayabilityStatusConverter.Shared}}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
TextWrapping="NoWrap" />
|
||||
<TextBlock Grid.Column="3"
|
||||
Text="{Binding FormattedIssueLabels}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Left"
|
||||
TextWrapping="WrapWithOverflow" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</ScrollViewer>
|
||||
<Grid></Grid>
|
||||
</Grid>
|
||||
|
@ -1,5 +1,6 @@
|
||||
using Avalonia.Controls;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Avalonia.Interactivity;
|
||||
using Ryujinx.Ava.Systems.Configuration;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using System.Threading.Tasks;
|
||||
@ -42,5 +43,28 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
cvm.Search(searchBox.Text);
|
||||
}
|
||||
|
||||
public void Sort_Name_Checked(object sender, RoutedEventArgs args)
|
||||
{
|
||||
if (sender is RadioButton { Tag: string sortStrategy })
|
||||
{
|
||||
if (DataContext is not CompatibilityViewModel cvm)
|
||||
return;
|
||||
|
||||
cvm.NameSorting(int.Parse(sortStrategy));
|
||||
}
|
||||
}
|
||||
|
||||
public void Sort_Status_Checked(object sender, RoutedEventArgs args)
|
||||
{
|
||||
if (sender is RadioButton { Tag: string sortStrategy })
|
||||
{
|
||||
if (DataContext is not CompatibilityViewModel cvm)
|
||||
return;
|
||||
|
||||
cvm.StatusSorting(int.Parse(sortStrategy));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -120,30 +120,34 @@
|
||||
IconSource="Code" />
|
||||
</ui:NavigationView.MenuItems>
|
||||
</ui:NavigationView>
|
||||
|
||||
<ReversibleStackPanel
|
||||
Grid.Row="2"
|
||||
Margin="10"
|
||||
Spacing="10"
|
||||
Orientation="Horizontal"
|
||||
HorizontalAlignment="Right"
|
||||
ReverseOrder="{x:Static helper:RunningPlatform.IsMacOS}">
|
||||
<Button
|
||||
Content="{ext:Locale SettingsButtonSave}"
|
||||
Command="{Binding SaveUserConfig}" />
|
||||
<Button
|
||||
HotKey="Escape"
|
||||
Content="{ext:Locale SettingsButtonClose}"
|
||||
Command="{Binding CancelButton}" />
|
||||
<Button
|
||||
IsVisible="{Binding IsGameRunning}"
|
||||
Content="{ext:Locale SettingsButtonApply}"
|
||||
Command="{Binding ApplyButton}" />
|
||||
<Button
|
||||
IsVisible="{Binding !IsGameRunning}"
|
||||
Content="{ext:Locale UserProfilesDelete}"
|
||||
Command="{Binding DeleteConfigGame}"
|
||||
Classes="red"/>
|
||||
</ReversibleStackPanel>
|
||||
<Grid Grid.Row="3"
|
||||
ColumnDefinitions="Auto,*,Auto">
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal" Margin="10" Spacing="10">
|
||||
<Button
|
||||
IsVisible="{Binding !IsGameRunning}"
|
||||
Content="{ext:Locale UserProfilesDelete}"
|
||||
Command="{Binding DeleteConfigGame}"
|
||||
Classes="red"/>
|
||||
</StackPanel>
|
||||
<ReversibleStackPanel
|
||||
Grid.Column="2"
|
||||
Margin="10"
|
||||
Spacing="10"
|
||||
Orientation="Horizontal"
|
||||
HorizontalAlignment="Right"
|
||||
ReverseOrder="{x:Static helper:RunningPlatform.IsMacOS}">
|
||||
<Button
|
||||
Classes="accent"
|
||||
Content="{ext:Locale SettingsButtonSave}"
|
||||
Command="{Binding SaveUserConfig}" />
|
||||
<Button
|
||||
HotKey="Escape"
|
||||
Content="{ext:Locale SettingsButtonClose}"
|
||||
Command="{Binding CancelButton}" />
|
||||
<Button
|
||||
Content="{ext:Locale SettingsButtonApply}"
|
||||
Command="{Binding ApplyButton}" />
|
||||
</ReversibleStackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</window:StyleableAppWindow>
|
||||
|
@ -28,6 +28,8 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
ViewModel.CloseWindow += Close;
|
||||
ViewModel.SaveSettingsEvent += SaveSettings;
|
||||
|
||||
ViewModel.LocalGlobalInputSwitchEvent += ToggleLocalGlobalInput;
|
||||
|
||||
InitializeComponent();
|
||||
Load();
|
||||
}
|
||||
@ -37,6 +39,11 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
InputPage.InputView?.SaveCurrentProfile();
|
||||
}
|
||||
|
||||
public void ToggleLocalGlobalInput(bool enableConfigGlobal)
|
||||
{
|
||||
InputPage.InputView?.ToggleLocalGlobalInput(enableConfigGlobal);
|
||||
}
|
||||
|
||||
private void Load()
|
||||
{
|
||||
Pages.Children.Clear();
|
||||
@ -90,6 +97,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
protected override void OnClosing(WindowClosingEventArgs e)
|
||||
{
|
||||
Program.UseExtraConfig = false;
|
||||
InputPage.Dispose(); // You need to unload the gamepad settings, otherwise the controls will be blocked
|
||||
base.OnClosing(e);
|
||||
}
|
||||
|
Reference in New Issue
Block a user