Compare commits

...

6 Commits

Author SHA1 Message Date
gdkchan
6adf15e479 Implement CSET and CSETP shader instructions (#4318)
* Implement CSET and CSETP shader instructions

* Shader cache version bump

* Fix CC.HI
2023-01-21 12:18:05 -03:00
dependabot[bot]
2747f12591 nuget: bump System.IdentityModel.Tokens.Jwt from 6.25.1 to 6.26.0 (#4322)
Bumps [System.IdentityModel.Tokens.Jwt](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 6.25.1 to 6.26.0.
- [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases)
- [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/commits)

---
updated-dependencies:
- dependency-name: System.IdentityModel.Tokens.Jwt
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-21 05:24:57 +01:00
Ac_K
a47824f961 Ava UI: Add Notifications and Cleanup (#4275)
* Ava UI: Add Notifications and Cleanup

* Revert notifications on ErrorDialog

* remove unused code from game list views

* Fix cast
2023-01-21 02:57:37 +01:00
Ac_K
8474d52778 Ava UI: Fix string.Format issues in Locale (#4305)
* Ava UI: Fix `string.Format` issues in Locale

* LoacLanguage everytime now

* Apply suggestions from code review

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* fix UpdateAndGetDynamicValue

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
2023-01-21 02:06:19 +01:00
Phi
dd7a924596 Catch Profile.json parse to prevent crash on launch (#3393)
* Catch Profile.json parse to prevent crash on launch

* Update Ryujinx.HLE/HOS/Services/Account/Acc/AccountSaveDataManager.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

Co-authored-by: PhiZero <wolkan.craanen@gmail.com>
Co-authored-by: Ac_K <Acoustik666@gmail.com>
2023-01-21 01:36:57 +01:00
gnisman
a76eaf9a9a Ava UI: Add Control+Cmd+F HotKey for Mac OS (#4317)
* Ava UI: Add Control+Cmd+F HotKey for Mac OS

* fix aligned

* Remove comment from code
2023-01-20 22:18:01 +01:00
47 changed files with 527 additions and 490 deletions

View File

@@ -44,7 +44,7 @@
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" /> <PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
<PackageVersion Include="SPB" Version="0.0.4-build28" /> <PackageVersion Include="SPB" Version="0.0.4-build28" />
<PackageVersion Include="System.Drawing.Common" Version="7.0.0" /> <PackageVersion Include="System.Drawing.Common" Version="7.0.0" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.25.1" /> <PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.26.0" />
<PackageVersion Include="System.IO.FileSystem.Primitives" Version="4.3.0" /> <PackageVersion Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
<PackageVersion Include="System.Management" Version="7.0.0" /> <PackageVersion Include="System.Management" Version="7.0.0" />
<PackageVersion Include="System.Net.NameResolution" Version="4.3.0" /> <PackageVersion Include="System.Net.NameResolution" Version="4.3.0" />

View File

@@ -462,8 +462,7 @@ namespace Ryujinx.Ava
{ {
UserResult result = await ContentDialogHelper.CreateConfirmationDialog( UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
LocaleManager.Instance[LocaleKeys.DialogFirmwareNoFirmwareInstalledMessage], LocaleManager.Instance[LocaleKeys.DialogFirmwareNoFirmwareInstalledMessage],
string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallEmbeddedMessage], LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallEmbeddedMessage, firmwareVersion.VersionString),
firmwareVersion.VersionString),
LocaleManager.Instance[LocaleKeys.InputDialogYes], LocaleManager.Instance[LocaleKeys.InputDialogYes],
LocaleManager.Instance[LocaleKeys.InputDialogNo], LocaleManager.Instance[LocaleKeys.InputDialogNo],
""); "");
@@ -493,10 +492,8 @@ namespace Ryujinx.Ava
_viewModel.RefreshFirmwareStatus(); _viewModel.RefreshFirmwareStatus();
await ContentDialogHelper.CreateInfoDialog( await ContentDialogHelper.CreateInfoDialog(
string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstalledMessage], LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstalledMessage, firmwareVersion.VersionString),
firmwareVersion.VersionString), LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallEmbeddedSuccessMessage, firmwareVersion.VersionString),
string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallEmbeddedSuccessMessage],
firmwareVersion.VersionString),
LocaleManager.Instance[LocaleKeys.InputDialogOk], LocaleManager.Instance[LocaleKeys.InputDialogOk],
"", "",
LocaleManager.Instance[LocaleKeys.RyujinxInfo]); LocaleManager.Instance[LocaleKeys.RyujinxInfo]);

View File

@@ -370,7 +370,7 @@
"DialogUserProfileDeletionConfirmMessage": "Möchtest du das ausgewählte Profil löschen?", "DialogUserProfileDeletionConfirmMessage": "Möchtest du das ausgewählte Profil löschen?",
"DialogControllerSettingsModifiedConfirmMessage": "Die aktuellen Controller-Einstellungen wurden aktualisiert.", "DialogControllerSettingsModifiedConfirmMessage": "Die aktuellen Controller-Einstellungen wurden aktualisiert.",
"DialogControllerSettingsModifiedConfirmSubMessage": "Controller-Einstellungen speichern?", "DialogControllerSettingsModifiedConfirmSubMessage": "Controller-Einstellungen speichern?",
"DialogDlcLoadNcaErrorMessage": "{0}. Fehlerhafte Datei: {1}", "DialogLoadNcaErrorMessage": "{0}. Fehlerhafte Datei: {1}",
"DialogDlcNoDlcErrorMessage": "Die angegebene Datei enthält keinen DLC für den ausgewählten Titel!", "DialogDlcNoDlcErrorMessage": "Die angegebene Datei enthält keinen DLC für den ausgewählten Titel!",
"DialogPerformanceCheckLoggingEnabledMessage": "Es wurde die Debug Protokollierung aktiviert", "DialogPerformanceCheckLoggingEnabledMessage": "Es wurde die Debug Protokollierung aktiviert",
"DialogPerformanceCheckLoggingEnabledConfirmMessage": "Um eine optimale Leistung zu erzielen, wird empfohlen, die Debug Protokollierung zu deaktivieren. Debug Protokollierung jetzt deaktivieren?", "DialogPerformanceCheckLoggingEnabledConfirmMessage": "Um eine optimale Leistung zu erzielen, wird empfohlen, die Debug Protokollierung zu deaktivieren. Debug Protokollierung jetzt deaktivieren?",

View File

@@ -370,7 +370,7 @@
"DialogUserProfileDeletionConfirmMessage": "Θέλετε να διαγράψετε το επιλεγμένο προφίλ", "DialogUserProfileDeletionConfirmMessage": "Θέλετε να διαγράψετε το επιλεγμένο προφίλ",
"DialogControllerSettingsModifiedConfirmMessage": "Οι τρέχουσες ρυθμίσεις χειρισμού έχουν ενημερωθεί.", "DialogControllerSettingsModifiedConfirmMessage": "Οι τρέχουσες ρυθμίσεις χειρισμού έχουν ενημερωθεί.",
"DialogControllerSettingsModifiedConfirmSubMessage": "Θέλετε να αποθηκεύσετε;", "DialogControllerSettingsModifiedConfirmSubMessage": "Θέλετε να αποθηκεύσετε;",
"DialogDlcLoadNcaErrorMessage": "{0}. Σφάλμα Αρχείου: {1}", "DialogLoadNcaErrorMessage": "{0}. Σφάλμα Αρχείου: {1}",
"DialogDlcNoDlcErrorMessage": "Το αρχείο δεν περιέχει DLC για τον επιλεγμένο τίτλο!", "DialogDlcNoDlcErrorMessage": "Το αρχείο δεν περιέχει DLC για τον επιλεγμένο τίτλο!",
"DialogPerformanceCheckLoggingEnabledMessage": "Έχετε ενεργοποιημένη την καταγραφή εντοπισμού σφαλμάτων, η οποία έχει σχεδιαστεί για χρήση μόνο από προγραμματιστές.", "DialogPerformanceCheckLoggingEnabledMessage": "Έχετε ενεργοποιημένη την καταγραφή εντοπισμού σφαλμάτων, η οποία έχει σχεδιαστεί για χρήση μόνο από προγραμματιστές.",
"DialogPerformanceCheckLoggingEnabledConfirmMessage": "Για βέλτιστη απόδοση, συνιστάται η απενεργοποίηση καταγραφής εντοπισμού σφαλμάτων. Θέλετε να απενεργοποιήσετε την καταγραφή τώρα;", "DialogPerformanceCheckLoggingEnabledConfirmMessage": "Για βέλτιστη απόδοση, συνιστάται η απενεργοποίηση καταγραφής εντοπισμού σφαλμάτων. Θέλετε να απενεργοποιήσετε την καταγραφή τώρα;",

View File

@@ -375,7 +375,7 @@
"DialogUserProfileUnsavedChangesSubMessage": "Do you want to discard your changes?", "DialogUserProfileUnsavedChangesSubMessage": "Do you want to discard your changes?",
"DialogControllerSettingsModifiedConfirmMessage": "The current controller settings has been updated.", "DialogControllerSettingsModifiedConfirmMessage": "The current controller settings has been updated.",
"DialogControllerSettingsModifiedConfirmSubMessage": "Do you want to save?", "DialogControllerSettingsModifiedConfirmSubMessage": "Do you want to save?",
"DialogDlcLoadNcaErrorMessage": "{0}. Errored File: {1}", "DialogLoadNcaErrorMessage": "{0}. Errored File: {1}",
"DialogDlcNoDlcErrorMessage": "The specified file does not contain a DLC for the selected title!", "DialogDlcNoDlcErrorMessage": "The specified file does not contain a DLC for the selected title!",
"DialogPerformanceCheckLoggingEnabledMessage": "You have trace logging enabled, which is designed to be used by developers only.", "DialogPerformanceCheckLoggingEnabledMessage": "You have trace logging enabled, which is designed to be used by developers only.",
"DialogPerformanceCheckLoggingEnabledConfirmMessage": "For optimal performance, it's recommended to disable trace logging. Would you like to disable trace logging now?", "DialogPerformanceCheckLoggingEnabledConfirmMessage": "For optimal performance, it's recommended to disable trace logging. Would you like to disable trace logging now?",

View File

@@ -370,7 +370,7 @@
"DialogUserProfileDeletionConfirmMessage": "¿Quieres eliminar el perfil seleccionado?", "DialogUserProfileDeletionConfirmMessage": "¿Quieres eliminar el perfil seleccionado?",
"DialogControllerSettingsModifiedConfirmMessage": "Se ha actualizado la configuración del mando actual.", "DialogControllerSettingsModifiedConfirmMessage": "Se ha actualizado la configuración del mando actual.",
"DialogControllerSettingsModifiedConfirmSubMessage": "¿Guardar cambios?", "DialogControllerSettingsModifiedConfirmSubMessage": "¿Guardar cambios?",
"DialogDlcLoadNcaErrorMessage": "{0}. Archivo con error: {1}", "DialogLoadNcaErrorMessage": "{0}. Archivo con error: {1}",
"DialogDlcNoDlcErrorMessage": "¡Ese archivo no contiene contenido descargable para el título seleccionado!", "DialogDlcNoDlcErrorMessage": "¡Ese archivo no contiene contenido descargable para el título seleccionado!",
"DialogPerformanceCheckLoggingEnabledMessage": "Has habilitado los registros debug, diseñados solo para uso de los desarrolladores.", "DialogPerformanceCheckLoggingEnabledMessage": "Has habilitado los registros debug, diseñados solo para uso de los desarrolladores.",
"DialogPerformanceCheckLoggingEnabledConfirmMessage": "Para un rendimiento óptimo, se recomienda deshabilitar los registros debug. ¿Quieres deshabilitarlos ahora?", "DialogPerformanceCheckLoggingEnabledConfirmMessage": "Para un rendimiento óptimo, se recomienda deshabilitar los registros debug. ¿Quieres deshabilitarlos ahora?",

View File

@@ -370,7 +370,7 @@
"DialogUserProfileDeletionConfirmMessage": "Voulez-vous supprimer le profil sélectionné ?", "DialogUserProfileDeletionConfirmMessage": "Voulez-vous supprimer le profil sélectionné ?",
"DialogControllerSettingsModifiedConfirmMessage": "Les paramètres actuels du contrôleur ont été mis à jour.", "DialogControllerSettingsModifiedConfirmMessage": "Les paramètres actuels du contrôleur ont été mis à jour.",
"DialogControllerSettingsModifiedConfirmSubMessage": "Voulez-vous sauvegarder?", "DialogControllerSettingsModifiedConfirmSubMessage": "Voulez-vous sauvegarder?",
"DialogDlcLoadNcaErrorMessage": "{0}. Fichier erroné : {1}", "DialogLoadNcaErrorMessage": "{0}. Fichier erroné : {1}",
"DialogDlcNoDlcErrorMessage": "Le fichier spécifié ne contient pas de DLC pour le titre sélectionné !", "DialogDlcNoDlcErrorMessage": "Le fichier spécifié ne contient pas de DLC pour le titre sélectionné !",
"DialogPerformanceCheckLoggingEnabledMessage": "Vous avez activé la journalisation des traces, conçue pour être utilisée uniquement par les développeurs.", "DialogPerformanceCheckLoggingEnabledMessage": "Vous avez activé la journalisation des traces, conçue pour être utilisée uniquement par les développeurs.",
"DialogPerformanceCheckLoggingEnabledConfirmMessage": "Pour des performances optimales, il est recommandé de désactiver la journalisation des traces. Souhaitez-vous désactiver la journalisation des traces maintenant ?", "DialogPerformanceCheckLoggingEnabledConfirmMessage": "Pour des performances optimales, il est recommandé de désactiver la journalisation des traces. Souhaitez-vous désactiver la journalisation des traces maintenant ?",

View File

@@ -370,7 +370,7 @@
"DialogUserProfileDeletionConfirmMessage": "Vuoi eliminare il profilo selezionato?", "DialogUserProfileDeletionConfirmMessage": "Vuoi eliminare il profilo selezionato?",
"DialogControllerSettingsModifiedConfirmMessage": "Le attuali impostazioni del controller sono state aggiornate.", "DialogControllerSettingsModifiedConfirmMessage": "Le attuali impostazioni del controller sono state aggiornate.",
"DialogControllerSettingsModifiedConfirmSubMessage": "Vuoi salvare?", "DialogControllerSettingsModifiedConfirmSubMessage": "Vuoi salvare?",
"DialogDlcLoadNcaErrorMessage": "{0}. File errato: {1}", "DialogLoadNcaErrorMessage": "{0}. File errato: {1}",
"DialogDlcNoDlcErrorMessage": "Il file specificato non contiene un DLC per il titolo selezionato!", "DialogDlcNoDlcErrorMessage": "Il file specificato non contiene un DLC per il titolo selezionato!",
"DialogPerformanceCheckLoggingEnabledMessage": "Hai abilitato il trace logging, che è progettato per essere usato solo dagli sviluppatori.", "DialogPerformanceCheckLoggingEnabledMessage": "Hai abilitato il trace logging, che è progettato per essere usato solo dagli sviluppatori.",
"DialogPerformanceCheckLoggingEnabledConfirmMessage": "Per prestazioni ottimali, si raccomanda di disabilitare il trace logging. Vuoi disabilitare il debug logging adesso?", "DialogPerformanceCheckLoggingEnabledConfirmMessage": "Per prestazioni ottimali, si raccomanda di disabilitare il trace logging. Vuoi disabilitare il debug logging adesso?",

View File

@@ -370,7 +370,7 @@
"DialogUserProfileDeletionConfirmMessage": "選択されたプロファイルを削除しますか", "DialogUserProfileDeletionConfirmMessage": "選択されたプロファイルを削除しますか",
"DialogControllerSettingsModifiedConfirmMessage": "現在のコントローラ設定が更新されました.", "DialogControllerSettingsModifiedConfirmMessage": "現在のコントローラ設定が更新されました.",
"DialogControllerSettingsModifiedConfirmSubMessage": "セーブしますか?", "DialogControllerSettingsModifiedConfirmSubMessage": "セーブしますか?",
"DialogDlcLoadNcaErrorMessage": "{0}. エラー発生ファイル: {1}", "DialogLoadNcaErrorMessage": "{0}. エラー発生ファイル: {1}",
"DialogDlcNoDlcErrorMessage": "選択されたファイルはこのタイトル用の DLC ではありません!", "DialogDlcNoDlcErrorMessage": "選択されたファイルはこのタイトル用の DLC ではありません!",
"DialogPerformanceCheckLoggingEnabledMessage": "トレースロギングを有効にします. これは開発者のみに有用な機能です.", "DialogPerformanceCheckLoggingEnabledMessage": "トレースロギングを有効にします. これは開発者のみに有用な機能です.",
"DialogPerformanceCheckLoggingEnabledConfirmMessage": "パフォーマンス最適化のためには,トレースロギングを無効にすることを推奨します. トレースロギングを無効にしてよろしいですか?", "DialogPerformanceCheckLoggingEnabledConfirmMessage": "パフォーマンス最適化のためには,トレースロギングを無効にすることを推奨します. トレースロギングを無効にしてよろしいですか?",

View File

@@ -370,7 +370,7 @@
"DialogUserProfileDeletionConfirmMessage": "선택한 프로파일을 삭제하겠습니까?", "DialogUserProfileDeletionConfirmMessage": "선택한 프로파일을 삭제하겠습니까?",
"DialogControllerSettingsModifiedConfirmMessage": "현재 컨트롤러 설정이 업데이트되었습니다.", "DialogControllerSettingsModifiedConfirmMessage": "현재 컨트롤러 설정이 업데이트되었습니다.",
"DialogControllerSettingsModifiedConfirmSubMessage": "저장하겠습니까?", "DialogControllerSettingsModifiedConfirmSubMessage": "저장하겠습니까?",
"DialogDlcLoadNcaErrorMessage": "{0}입니다. 오류 발생 파일: {1}", "DialogLoadNcaErrorMessage": "{0}입니다. 오류 발생 파일: {1}",
"DialogDlcNoDlcErrorMessage": "지정된 파일에 선택한 타이틀에 대한 DLC가 포함되어 있지 않습니다!", "DialogDlcNoDlcErrorMessage": "지정된 파일에 선택한 타이틀에 대한 DLC가 포함되어 있지 않습니다!",
"DialogPerformanceCheckLoggingEnabledMessage": "개발자만 사용하도록 설계된 추적 로깅이 활성화되어 있습니다.", "DialogPerformanceCheckLoggingEnabledMessage": "개발자만 사용하도록 설계된 추적 로깅이 활성화되어 있습니다.",
"DialogPerformanceCheckLoggingEnabledConfirmMessage": "최적의 성능을 위해 추적 로깅을 비활성화하는 것이 좋습니다. 지금 추적 로깅을 비활성화하겠습니까?", "DialogPerformanceCheckLoggingEnabledConfirmMessage": "최적의 성능을 위해 추적 로깅을 비활성화하는 것이 좋습니다. 지금 추적 로깅을 비활성화하겠습니까?",

View File

@@ -370,7 +370,7 @@
"DialogUserProfileDeletionConfirmMessage": "Czy chcesz usunąć wybrany profil", "DialogUserProfileDeletionConfirmMessage": "Czy chcesz usunąć wybrany profil",
"DialogControllerSettingsModifiedConfirmMessage": "Aktualne ustawienia kontrolera zostały zaktualizowane.", "DialogControllerSettingsModifiedConfirmMessage": "Aktualne ustawienia kontrolera zostały zaktualizowane.",
"DialogControllerSettingsModifiedConfirmSubMessage": "Czy chcesz zapisać?", "DialogControllerSettingsModifiedConfirmSubMessage": "Czy chcesz zapisać?",
"DialogDlcLoadNcaErrorMessage": "{0}. Błędny Plik: {1}", "DialogLoadNcaErrorMessage": "{0}. Błędny Plik: {1}",
"DialogDlcNoDlcErrorMessage": "Określony plik nie zawiera DLC dla wybranego tytułu!", "DialogDlcNoDlcErrorMessage": "Określony plik nie zawiera DLC dla wybranego tytułu!",
"DialogPerformanceCheckLoggingEnabledMessage": "Masz włączone rejestrowanie śledzenia, które jest przeznaczone tylko dla programistów.", "DialogPerformanceCheckLoggingEnabledMessage": "Masz włączone rejestrowanie śledzenia, które jest przeznaczone tylko dla programistów.",
"DialogPerformanceCheckLoggingEnabledConfirmMessage": "Aby uzyskać optymalną wydajność, zaleca się wyłączenie rejestrowania śledzenia. Czy chcesz teraz wyłączyć rejestrowanie śledzenia?", "DialogPerformanceCheckLoggingEnabledConfirmMessage": "Aby uzyskać optymalną wydajność, zaleca się wyłączenie rejestrowania śledzenia. Czy chcesz teraz wyłączyć rejestrowanie śledzenia?",

View File

@@ -370,7 +370,7 @@
"DialogUserProfileDeletionConfirmMessage": "Deseja deletar o perfil selecionado", "DialogUserProfileDeletionConfirmMessage": "Deseja deletar o perfil selecionado",
"DialogControllerSettingsModifiedConfirmMessage": "As configurações de controle atuais foram atualizadas.", "DialogControllerSettingsModifiedConfirmMessage": "As configurações de controle atuais foram atualizadas.",
"DialogControllerSettingsModifiedConfirmSubMessage": "Deseja salvar?", "DialogControllerSettingsModifiedConfirmSubMessage": "Deseja salvar?",
"DialogDlcLoadNcaErrorMessage": "{0}. Arquivo com erro: {1}", "DialogLoadNcaErrorMessage": "{0}. Arquivo com erro: {1}",
"DialogDlcNoDlcErrorMessage": "O arquivo especificado não contém DLCs para o título selecionado!", "DialogDlcNoDlcErrorMessage": "O arquivo especificado não contém DLCs para o título selecionado!",
"DialogPerformanceCheckLoggingEnabledMessage": "Os logs de depuração estão ativos, esse recurso é feito para ser usado apenas por desenvolvedores.", "DialogPerformanceCheckLoggingEnabledMessage": "Os logs de depuração estão ativos, esse recurso é feito para ser usado apenas por desenvolvedores.",
"DialogPerformanceCheckLoggingEnabledConfirmMessage": "Para melhor performance, é recomendável desabilitar os logs de depuração. Gostaria de desabilitar os logs de depuração agora?", "DialogPerformanceCheckLoggingEnabledConfirmMessage": "Para melhor performance, é recomendável desabilitar os logs de depuração. Gostaria de desabilitar os logs de depuração agora?",

View File

@@ -370,7 +370,7 @@
"DialogUserProfileDeletionConfirmMessage": "Вы хотите удалить выбранный профиль", "DialogUserProfileDeletionConfirmMessage": "Вы хотите удалить выбранный профиль",
"DialogControllerSettingsModifiedConfirmMessage": "Текущие настройки контроллера обновлены.", "DialogControllerSettingsModifiedConfirmMessage": "Текущие настройки контроллера обновлены.",
"DialogControllerSettingsModifiedConfirmSubMessage": "Вы хотите сохранить?", "DialogControllerSettingsModifiedConfirmSubMessage": "Вы хотите сохранить?",
"DialogDlcLoadNcaErrorMessage": "{0}. Файл с ошибкой: {1}", "DialogLoadNcaErrorMessage": "{0}. Файл с ошибкой: {1}",
"DialogDlcNoDlcErrorMessage": "Указанный файл не содержит DLC для выбранной игры!", "DialogDlcNoDlcErrorMessage": "Указанный файл не содержит DLC для выбранной игры!",
"DialogPerformanceCheckLoggingEnabledMessage": "У вас включено ведение журнала отладки, предназначенное только для разработчиков.", "DialogPerformanceCheckLoggingEnabledMessage": "У вас включено ведение журнала отладки, предназначенное только для разработчиков.",
"DialogPerformanceCheckLoggingEnabledConfirmMessage": "Для оптимальной производительности рекомендуется отключить ведение журнала отладки. Вы хотите отключить ведение журнала отладки сейчас?", "DialogPerformanceCheckLoggingEnabledConfirmMessage": "Для оптимальной производительности рекомендуется отключить ведение журнала отладки. Вы хотите отключить ведение журнала отладки сейчас?",

View File

@@ -370,7 +370,7 @@
"DialogUserProfileDeletionConfirmMessage": "Seçilen profili silmek istiyor musunuz", "DialogUserProfileDeletionConfirmMessage": "Seçilen profili silmek istiyor musunuz",
"DialogControllerSettingsModifiedConfirmMessage": "Güncel kontrolcü seçenekleri güncellendi.", "DialogControllerSettingsModifiedConfirmMessage": "Güncel kontrolcü seçenekleri güncellendi.",
"DialogControllerSettingsModifiedConfirmSubMessage": "Kaydetmek istiyor musunuz?", "DialogControllerSettingsModifiedConfirmSubMessage": "Kaydetmek istiyor musunuz?",
"DialogDlcLoadNcaErrorMessage": "{0}. Hatalı Dosya: {1}", "DialogLoadNcaErrorMessage": "{0}. Hatalı Dosya: {1}",
"DialogDlcNoDlcErrorMessage": "Belirtilen dosya seçilen oyun için DLC içermiyor!", "DialogDlcNoDlcErrorMessage": "Belirtilen dosya seçilen oyun için DLC içermiyor!",
"DialogPerformanceCheckLoggingEnabledMessage": "Sadece geliştiriler için dizayn edilen Trace Loglama seçeneği etkin.", "DialogPerformanceCheckLoggingEnabledMessage": "Sadece geliştiriler için dizayn edilen Trace Loglama seçeneği etkin.",
"DialogPerformanceCheckLoggingEnabledConfirmMessage": "En iyi performans için trace loglama'nın devre dışı bırakılması tavsiye edilir. Trace loglama seçeneğini şimdi devre dışı bırakmak ister misiniz?", "DialogPerformanceCheckLoggingEnabledConfirmMessage": "En iyi performans için trace loglama'nın devre dışı bırakılması tavsiye edilir. Trace loglama seçeneğini şimdi devre dışı bırakmak ister misiniz?",

View File

@@ -370,7 +370,7 @@
"DialogUserProfileDeletionConfirmMessage": "Ви хочете видалити вибраний профіль", "DialogUserProfileDeletionConfirmMessage": "Ви хочете видалити вибраний профіль",
"DialogControllerSettingsModifiedConfirmMessage": "Поточні налаштування контролера оновлено.", "DialogControllerSettingsModifiedConfirmMessage": "Поточні налаштування контролера оновлено.",
"DialogControllerSettingsModifiedConfirmSubMessage": "Ви хочете зберегти?", "DialogControllerSettingsModifiedConfirmSubMessage": "Ви хочете зберегти?",
"DialogDlcLoadNcaErrorMessage": "{0}. Файл з помилкою: {1}", "DialogLoadNcaErrorMessage": "{0}. Файл з помилкою: {1}",
"DialogDlcNoDlcErrorMessage": "Зазначений файл не містить DLC для вибраного заголовку!", "DialogDlcNoDlcErrorMessage": "Зазначений файл не містить DLC для вибраного заголовку!",
"DialogPerformanceCheckLoggingEnabledMessage": "Ви увімкнули журнал налагодження, призначений лише для розробників.", "DialogPerformanceCheckLoggingEnabledMessage": "Ви увімкнули журнал налагодження, призначений лише для розробників.",
"DialogPerformanceCheckLoggingEnabledConfirmMessage": "Для оптимальної продуктивності рекомендується вимкнути ведення журналу налагодження. Ви хочете вимкнути ведення журналу налагодження зараз?", "DialogPerformanceCheckLoggingEnabledConfirmMessage": "Для оптимальної продуктивності рекомендується вимкнути ведення журналу налагодження. Ви хочете вимкнути ведення журналу налагодження зараз?",

View File

@@ -370,7 +370,7 @@
"DialogUserProfileDeletionConfirmMessage": "是否删除选择的账户", "DialogUserProfileDeletionConfirmMessage": "是否删除选择的账户",
"DialogControllerSettingsModifiedConfirmMessage": "目前的输入预设已更新", "DialogControllerSettingsModifiedConfirmMessage": "目前的输入预设已更新",
"DialogControllerSettingsModifiedConfirmSubMessage": "要保存吗?", "DialogControllerSettingsModifiedConfirmSubMessage": "要保存吗?",
"DialogDlcLoadNcaErrorMessage": "{0}. 错误的文件: {1}", "DialogLoadNcaErrorMessage": "{0}. 错误的文件: {1}",
"DialogDlcNoDlcErrorMessage": "选择的文件不包含所选游戏的 DLC", "DialogDlcNoDlcErrorMessage": "选择的文件不包含所选游戏的 DLC",
"DialogPerformanceCheckLoggingEnabledMessage": "您启用了跟踪日志,仅供开发人员使用。", "DialogPerformanceCheckLoggingEnabledMessage": "您启用了跟踪日志,仅供开发人员使用。",
"DialogPerformanceCheckLoggingEnabledConfirmMessage": "为了获得最佳性能,建议禁用跟踪日志记录。您是否要立即禁用?", "DialogPerformanceCheckLoggingEnabledConfirmMessage": "为了获得最佳性能,建议禁用跟踪日志记录。您是否要立即禁用?",

View File

@@ -370,7 +370,7 @@
"DialogUserProfileDeletionConfirmMessage": "是否刪除選擇的帳號", "DialogUserProfileDeletionConfirmMessage": "是否刪除選擇的帳號",
"DialogControllerSettingsModifiedConfirmMessage": "目前的輸入預設已更新", "DialogControllerSettingsModifiedConfirmMessage": "目前的輸入預設已更新",
"DialogControllerSettingsModifiedConfirmSubMessage": "要儲存嗎?", "DialogControllerSettingsModifiedConfirmSubMessage": "要儲存嗎?",
"DialogDlcLoadNcaErrorMessage": "{0}. 錯誤的檔案: {1}", "DialogLoadNcaErrorMessage": "{0}. 錯誤的檔案: {1}",
"DialogDlcNoDlcErrorMessage": "選擇的檔案不包含所選遊戲的 DLC", "DialogDlcNoDlcErrorMessage": "選擇的檔案不包含所選遊戲的 DLC",
"DialogPerformanceCheckLoggingEnabledMessage": "您啟用了跟蹤日誌,僅供開發人員使用。", "DialogPerformanceCheckLoggingEnabledMessage": "您啟用了跟蹤日誌,僅供開發人員使用。",
"DialogPerformanceCheckLoggingEnabledConfirmMessage": "為了獲得最佳效能,建議停用跟蹤日誌記錄。您是否要立即停用?", "DialogPerformanceCheckLoggingEnabledConfirmMessage": "為了獲得最佳效能,建議停用跟蹤日誌記錄。您是否要立即停用?",

View File

@@ -1,4 +1,5 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Notifications;
using Avalonia.Threading; using Avalonia.Threading;
using LibHac; using LibHac;
using LibHac.Account; using LibHac.Account;
@@ -12,7 +13,6 @@ using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils; using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
@@ -44,14 +44,11 @@ namespace Ryujinx.Ava.Common
_accountManager = accountManager; _accountManager = accountManager;
} }
private static bool TryFindSaveData(string titleName, ulong titleId, private static bool TryFindSaveData(string titleName, ulong titleId, BlitStruct<ApplicationControlProperty> controlHolder, in SaveDataFilter filter, out ulong saveDataId)
BlitStruct<ApplicationControlProperty> controlHolder, in SaveDataFilter filter, out ulong saveDataId)
{ {
saveDataId = default; saveDataId = default;
Result result = _horizonClient.Fs.FindSaveDataWithFilter(out SaveDataInfo saveDataInfo, Result result = _horizonClient.Fs.FindSaveDataWithFilter(out SaveDataInfo saveDataInfo, SaveDataSpaceId.User, in filter);
SaveDataSpaceId.User, in filter);
if (ResultFs.TargetNotFound.Includes(result)) if (ResultFs.TargetNotFound.Includes(result))
{ {
ref ApplicationControlProperty control = ref controlHolder.Value; ref ApplicationControlProperty control = ref controlHolder.Value;
@@ -68,20 +65,17 @@ namespace Ryujinx.Ava.Common
control.UserAccountSaveDataSize = 0x4000; control.UserAccountSaveDataSize = 0x4000;
control.UserAccountSaveDataJournalSize = 0x4000; control.UserAccountSaveDataJournalSize = 0x4000;
Logger.Warning?.Print(LogClass.Application, Logger.Warning?.Print(LogClass.Application, "No control file was found for this game. Using a dummy one instead. This may cause inaccuracies in some games.");
"No control file was found for this game. Using a dummy one instead. This may cause inaccuracies in some games.");
} }
Uid user = new Uid((ulong)_accountManager.LastOpenedUser.UserId.High, (ulong)_accountManager.LastOpenedUser.UserId.Low); Uid user = new((ulong)_accountManager.LastOpenedUser.UserId.High, (ulong)_accountManager.LastOpenedUser.UserId.Low);
result = _horizonClient.Fs.EnsureApplicationSaveData(out _, new LibHac.Ncm.ApplicationId(titleId), in control, in user); result = _horizonClient.Fs.EnsureApplicationSaveData(out _, new LibHac.Ncm.ApplicationId(titleId), in control, in user);
if (result.IsFailure()) if (result.IsFailure())
{ {
Dispatcher.UIThread.Post(async () => Dispatcher.UIThread.InvokeAsync(async () =>
{ {
await ContentDialogHelper.CreateErrorDialog( await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogMessageCreateSaveErrorMessage, result.ToStringWithName()));
string.Format(LocaleManager.Instance[LocaleKeys.DialogMessageCreateSaveErrorMessage], result.ToStringWithName()));
}); });
return false; return false;
@@ -98,16 +92,15 @@ namespace Ryujinx.Ava.Common
return true; return true;
} }
Dispatcher.UIThread.Post(async () => Dispatcher.UIThread.InvokeAsync(async () =>
{ {
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogMessageFindSaveErrorMessage], result.ToStringWithName())); await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogMessageFindSaveErrorMessage, result.ToStringWithName()));
}); });
return false; return false;
} }
public static void OpenSaveDir(in SaveDataFilter saveDataFilter, ulong titleId, public static void OpenSaveDir(in SaveDataFilter saveDataFilter, ulong titleId, BlitStruct<ApplicationControlProperty> controlData, string titleName)
BlitStruct<ApplicationControlProperty> controlData, string titleName)
{ {
if (!TryFindSaveData(titleName, titleId, controlData, in saveDataFilter, out ulong saveDataId)) if (!TryFindSaveData(titleName, titleId, controlData, in saveDataFilter, out ulong saveDataId))
{ {
@@ -148,13 +141,14 @@ namespace Ryujinx.Ava.Common
} }
} }
public static async Task ExtractSection(NcaSectionType ncaSectionType, string titleFilePath, public static async Task ExtractSection(NcaSectionType ncaSectionType, string titleFilePath, string titleName, int programIndex = 0)
int programIndex = 0)
{ {
OpenFolderDialog folderDialog = new() { Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle] }; OpenFolderDialog folderDialog = new()
{
Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle]
};
string destination = await folderDialog.ShowAsync(_owner); string destination = await folderDialog.ShowAsync(_owner);
var cancellationToken = new CancellationTokenSource(); var cancellationToken = new CancellationTokenSource();
if (!string.IsNullOrWhiteSpace(destination)) if (!string.IsNullOrWhiteSpace(destination))
@@ -164,7 +158,7 @@ namespace Ryujinx.Ava.Common
Dispatcher.UIThread.Post(async () => Dispatcher.UIThread.Post(async () =>
{ {
UserResult result = await ContentDialogHelper.CreateConfirmationDialog( UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
string.Format(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionMessage], ncaSectionType, Path.GetFileName(titleFilePath)), LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogNcaExtractionMessage, ncaSectionType, Path.GetFileName(titleFilePath)),
"", "",
"", "",
LocaleManager.Instance[LocaleKeys.InputDialogCancel], LocaleManager.Instance[LocaleKeys.InputDialogCancel],
@@ -176,24 +170,19 @@ namespace Ryujinx.Ava.Common
} }
}); });
Thread.Sleep(1000); using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read);
using (FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read))
{
Nca mainNca = null; Nca mainNca = null;
Nca patchNca = null; Nca patchNca = null;
string extension = Path.GetExtension(titleFilePath).ToLower(); string extension = Path.GetExtension(titleFilePath).ToLower();
if (extension == ".nsp" || extension == ".pfs0" || extension == ".xci") if (extension == ".nsp" || extension == ".pfs0" || extension == ".xci")
{ {
PartitionFileSystem pfs; PartitionFileSystem pfs;
if (extension == ".xci") if (extension == ".xci")
{ {
Xci xci = new(_virtualFileSystem.KeySet, file.AsStorage()); pfs = new Xci(_virtualFileSystem.KeySet, file.AsStorage()).OpenPartition(XciPartitionType.Secure);
pfs = xci.OpenPartition(XciPartitionType.Secure);
} }
else else
{ {
@@ -207,11 +196,9 @@ namespace Ryujinx.Ava.Common
pfs.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); pfs.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
Nca nca = new(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage()); Nca nca = new(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage());
if (nca.Header.ContentType == NcaContentType.Program) if (nca.Header.ContentType == NcaContentType.Program)
{ {
int dataIndex = int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
if (nca.Header.GetFsHeader(dataIndex).IsPatchSection()) if (nca.Header.GetFsHeader(dataIndex).IsPatchSection())
{ {
patchNca = nca; patchNca = nca;
@@ -230,17 +217,17 @@ namespace Ryujinx.Ava.Common
if (mainNca == null) if (mainNca == null)
{ {
Logger.Error?.Print(LogClass.Application, Logger.Error?.Print(LogClass.Application, "Extraction failure. The main NCA was not present in the selected file");
"Extraction failure. The main NCA was not present in the selected file");
Dispatcher.UIThread.InvokeAsync(async () => Dispatcher.UIThread.InvokeAsync(async () =>
{ {
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionMainNcaNotFoundErrorMessage]); await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionMainNcaNotFoundErrorMessage]);
}); });
return; return;
} }
(Nca updatePatchNca, _) = ApplicationLoader.GetGameUpdateData(_virtualFileSystem, (Nca updatePatchNca, _) = ApplicationLoader.GetGameUpdateData(_virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), programIndex, out _);
mainNca.Header.TitleId.ToString("x16"), programIndex, out _);
if (updatePatchNca != null) if (updatePatchNca != null)
{ {
patchNca = updatePatchNca; patchNca = updatePatchNca;
@@ -271,8 +258,8 @@ namespace Ryujinx.Ava.Common
{ {
if (resultCode.Value.IsFailure()) if (resultCode.Value.IsFailure())
{ {
Logger.Error?.Print(LogClass.Application, Logger.Error?.Print(LogClass.Application, $"LibHac returned error code: {resultCode.Value.ErrorCode}");
$"LibHac returned error code: {resultCode.Value.ErrorCode}");
Dispatcher.UIThread.InvokeAsync(async () => Dispatcher.UIThread.InvokeAsync(async () =>
{ {
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionCheckLogErrorMessage]); await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionCheckLogErrorMessage]);
@@ -280,15 +267,10 @@ namespace Ryujinx.Ava.Common
} }
else if (resultCode.Value.IsSuccess()) else if (resultCode.Value.IsSuccess())
{ {
Dispatcher.UIThread.InvokeAsync(async () => NotificationHelper.Show(
{ LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle],
await ContentDialogHelper.CreateInfoDialog( $"{titleName}\n\n{LocaleManager.Instance[LocaleKeys.DialogNcaExtractionSuccessMessage]}",
LocaleManager.Instance[LocaleKeys.DialogNcaExtractionSuccessMessage], NotificationType.Information);
"",
LocaleManager.Instance[LocaleKeys.InputDialogOk],
"",
LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle]);
});
} }
} }
@@ -297,12 +279,13 @@ namespace Ryujinx.Ava.Common
} }
catch (ArgumentException ex) catch (ArgumentException ex)
{ {
Logger.Error?.Print(LogClass.Application, $"{ex.Message}");
Dispatcher.UIThread.InvokeAsync(async () => Dispatcher.UIThread.InvokeAsync(async () =>
{ {
await ContentDialogHelper.CreateErrorDialog(ex.Message); await ContentDialogHelper.CreateErrorDialog(ex.Message);
}); });
} }
}
}); });
extractorThread.Name = "GUI.NcaSectionExtractorThread"; extractorThread.Name = "GUI.NcaSectionExtractorThread";

View File

@@ -14,15 +14,15 @@ namespace Ryujinx.Ava.Common.Locale
private const string DefaultLanguageCode = "en_US"; private const string DefaultLanguageCode = "en_US";
private Dictionary<LocaleKeys, string> _localeStrings; private Dictionary<LocaleKeys, string> _localeStrings;
private ConcurrentDictionary<LocaleKeys, object[]> _dynamicValues; private Dictionary<LocaleKeys, string> _localeDefaultStrings;
private readonly ConcurrentDictionary<LocaleKeys, object[]> _dynamicValues;
public static LocaleManager Instance { get; } = new LocaleManager(); public static LocaleManager Instance { get; } = new LocaleManager();
public Dictionary<LocaleKeys, string> LocaleStrings { get => _localeStrings; set => _localeStrings = value; }
public LocaleManager() public LocaleManager()
{ {
_localeStrings = new Dictionary<LocaleKeys, string>(); _localeStrings = new Dictionary<LocaleKeys, string>();
_localeDefaultStrings = new Dictionary<LocaleKeys, string>();
_dynamicValues = new ConcurrentDictionary<LocaleKeys, object[]>(); _dynamicValues = new ConcurrentDictionary<LocaleKeys, object[]>();
Load(); Load();
@@ -30,39 +30,70 @@ namespace Ryujinx.Ava.Common.Locale
public void Load() public void Load()
{ {
// Load the system Language Code.
string localeLanguageCode = CultureInfo.CurrentCulture.Name.Replace('-', '_'); string localeLanguageCode = CultureInfo.CurrentCulture.Name.Replace('-', '_');
// If the view is loaded with the UI Previewer detached, then override it with the saved one or default.
if (Program.PreviewerDetached) if (Program.PreviewerDetached)
{ {
if (!string.IsNullOrEmpty(ConfigurationState.Instance.Ui.LanguageCode.Value)) if (!string.IsNullOrEmpty(ConfigurationState.Instance.Ui.LanguageCode.Value))
{ {
localeLanguageCode = ConfigurationState.Instance.Ui.LanguageCode.Value; localeLanguageCode = ConfigurationState.Instance.Ui.LanguageCode.Value;
} }
else
{
localeLanguageCode = DefaultLanguageCode;
}
} }
// Load english first, if the target language translation is incomplete, we default to english. // Load en_US as default, if the target language translation is incomplete.
LoadDefaultLanguage(); LoadDefaultLanguage();
if (localeLanguageCode != DefaultLanguageCode)
{
LoadLanguage(localeLanguageCode); LoadLanguage(localeLanguageCode);
} }
}
public string this[LocaleKeys key] public string this[LocaleKeys key]
{ {
get get
{ {
// Check if the locale contains the key.
if (_localeStrings.TryGetValue(key, out string value)) if (_localeStrings.TryGetValue(key, out string value))
{ {
// Check if the localized string needs to be formatted.
if (_dynamicValues.TryGetValue(key, out var dynamicValue)) if (_dynamicValues.TryGetValue(key, out var dynamicValue))
{
try
{ {
return string.Format(value, dynamicValue); return string.Format(value, dynamicValue);
} }
catch (Exception)
{
// If formatting failed use the default text instead.
if (_localeDefaultStrings.TryGetValue(key, out value))
{
try
{
return string.Format(value, dynamicValue);
}
catch (Exception)
{
// If formatting the default text failed return the key.
return key.ToString();
}
}
}
}
return value; return value;
} }
// If the locale doesn't contain the key return the default one.
if (_localeDefaultStrings.TryGetValue(key, out string defaultValue))
{
return defaultValue;
}
// If the locale text doesn't exist return the key.
return key.ToString(); return key.ToString();
} }
set set
@@ -73,42 +104,43 @@ namespace Ryujinx.Ava.Common.Locale
} }
} }
public void UpdateDynamicValue(LocaleKeys key, params object[] values) public string UpdateAndGetDynamicValue(LocaleKeys key, params object[] values)
{ {
_dynamicValues[key] = values; _dynamicValues[key] = values;
OnPropertyChanged("Item"); OnPropertyChanged("Item");
return this[key];
} }
public void LoadDefaultLanguage() private void LoadDefaultLanguage()
{ {
LoadLanguage(DefaultLanguageCode); _localeDefaultStrings = LoadJsonLanguage();
} }
public void LoadLanguage(string languageCode) public void LoadLanguage(string languageCode)
{ {
string languageJson = EmbeddedResources.ReadAllText($"Ryujinx.Ava/Assets/Locales/{languageCode}.json"); foreach (var item in LoadJsonLanguage(languageCode))
if (languageJson == null)
{ {
return; this[item.Key] = item.Value;
}
} }
private Dictionary<LocaleKeys, string> LoadJsonLanguage(string languageCode = DefaultLanguageCode)
{
var localeStrings = new Dictionary<LocaleKeys, string>();
string languageJson = EmbeddedResources.ReadAllText($"Ryujinx.Ava/Assets/Locales/{languageCode}.json");
var strings = JsonHelper.Deserialize<Dictionary<string, string>>(languageJson); var strings = JsonHelper.Deserialize<Dictionary<string, string>>(languageJson);
foreach (var item in strings) foreach (var item in strings)
{ {
if (Enum.TryParse<LocaleKeys>(item.Key, out var key)) if (Enum.TryParse<LocaleKeys>(item.Key, out var key))
{ {
this[key] = item.Value; localeStrings[key] = item.Value;
} }
} }
if (Program.PreviewerDetached) return localeStrings;
{
ConfigurationState.Instance.Ui.LanguageCode.Value = languageCode;
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
}
} }
} }
} }

View File

@@ -588,7 +588,7 @@ namespace Ryujinx.Modules
} }
catch catch
{ {
Logger.Warning?.Print(LogClass.Application, string.Format(LocaleManager.Instance[LocaleKeys.UpdaterRenameFailed], file)); Logger.Warning?.Print(LogClass.Application, LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.UpdaterRenameFailed, file));
} }
} }

View File

@@ -29,14 +29,9 @@ namespace Ryujinx.Ava.UI.Applet
public bool DisplayMessageDialog(ControllerAppletUiArgs args) public bool DisplayMessageDialog(ControllerAppletUiArgs args)
{ {
string playerCount = args.PlayerCountMin == args.PlayerCountMax string message = LocaleManager.Instance.UpdateAndGetDynamicValue(
? args.PlayerCountMin.ToString() args.PlayerCountMin == args.PlayerCountMax ? LocaleKeys.DialogControllerAppletMessage : LocaleKeys.DialogControllerAppletMessagePlayerRange,
: $"{args.PlayerCountMin}-{args.PlayerCountMax}"; args.PlayerCountMin == args.PlayerCountMax ? args.PlayerCountMin.ToString() : $"{args.PlayerCountMin}-{args.PlayerCountMax}",
LocaleKeys key = args.PlayerCountMin == args.PlayerCountMax ? LocaleKeys.DialogControllerAppletMessage : LocaleKeys.DialogControllerAppletMessagePlayerRange;
string message = string.Format(LocaleManager.Instance[key],
playerCount,
args.SupportedStyles, args.SupportedStyles,
string.Join(", ", args.SupportedPlayers), string.Join(", ", args.SupportedPlayers),
args.IsDocked ? LocaleManager.Instance[LocaleKeys.DialogControllerAppletDockModeSet] : ""); args.IsDocked ? LocaleManager.Instance[LocaleKeys.DialogControllerAppletDockModeSet] : "");
@@ -92,7 +87,7 @@ namespace Ryujinx.Ava.UI.Applet
} }
catch (Exception ex) catch (Exception ex)
{ {
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogMessageDialogErrorExceptionMessage], ex)); await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogMessageDialogErrorExceptionMessage, ex));
dialogCloseEvent.Set(); dialogCloseEvent.Set();
} }
@@ -126,7 +121,8 @@ namespace Ryujinx.Ava.UI.Applet
catch (Exception ex) catch (Exception ex)
{ {
error = true; error = true;
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogSoftwareKeyboardErrorExceptionMessage], ex));
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogSoftwareKeyboardErrorExceptionMessage, ex));
} }
finally finally
{ {
@@ -181,7 +177,8 @@ namespace Ryujinx.Ava.UI.Applet
catch (Exception ex) catch (Exception ex)
{ {
dialogCloseEvent.Set(); dialogCloseEvent.Set();
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogErrorAppletErrorExceptionMessage], ex));
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogErrorAppletErrorExceptionMessage, ex));
} }
}); });

View File

@@ -139,14 +139,16 @@ namespace Ryujinx.Ava.UI.Controls
else if (_inputMin > 0 && _inputMax == int.MaxValue) else if (_inputMin > 0 && _inputMax == int.MaxValue)
{ {
Error.IsVisible = true; Error.IsVisible = true;
Error.Text = string.Format(LocaleManager.Instance[LocaleKeys.SwkbdMinCharacters], _inputMin);
Error.Text = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SwkbdMinCharacters, _inputMin);
_checkLength = length => _inputMin <= length; _checkLength = length => _inputMin <= length;
} }
else else
{ {
Error.IsVisible = true; Error.IsVisible = true;
Error.Text = string.Format(LocaleManager.Instance[LocaleKeys.SwkbdMinRangeCharacters], _inputMin, _inputMax);
Error.Text = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SwkbdMinRangeCharacters, _inputMin, _inputMax);
_checkLength = length => _inputMin <= length && length <= _inputMax; _checkLength = length => _inputMin <= length && length <= _inputMax;
} }

View File

@@ -14,7 +14,7 @@
Focusable="True"> Focusable="True">
<UserControl.Resources> <UserControl.Resources>
<helpers:BitmapArrayValueConverter x:Key="ByteImage" /> <helpers:BitmapArrayValueConverter x:Key="ByteImage" />
<MenuFlyout x:Key="GameContextMenu" Opened="MenuBase_OnMenuOpened"> <MenuFlyout x:Key="GameContextMenu">
<MenuItem <MenuItem
Command="{Binding ToggleFavorite}" Command="{Binding ToggleFavorite}"
Header="{locale:Locale GameListContextMenuToggleFavorite}" Header="{locale:Locale GameListContextMenuToggleFavorite}"
@@ -22,14 +22,17 @@
<Separator /> <Separator />
<MenuItem <MenuItem
Command="{Binding OpenUserSaveDirectory}" Command="{Binding OpenUserSaveDirectory}"
IsEnabled="{Binding EnabledUserSaveDirectory}"
Header="{locale:Locale GameListContextMenuOpenUserSaveDirectory}" Header="{locale:Locale GameListContextMenuOpenUserSaveDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuOpenUserSaveDirectoryToolTip}" /> ToolTip.Tip="{locale:Locale GameListContextMenuOpenUserSaveDirectoryToolTip}" />
<MenuItem <MenuItem
Command="{Binding OpenDeviceSaveDirectory}" Command="{Binding OpenDeviceSaveDirectory}"
IsEnabled="{Binding EnabledDeviceSaveDirectory}"
Header="{locale:Locale GameListContextMenuOpenDeviceSaveDirectory}" Header="{locale:Locale GameListContextMenuOpenDeviceSaveDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuOpenDeviceSaveDirectoryToolTip}" /> ToolTip.Tip="{locale:Locale GameListContextMenuOpenDeviceSaveDirectoryToolTip}" />
<MenuItem <MenuItem
Command="{Binding OpenBcatSaveDirectory}" Command="{Binding OpenBcatSaveDirectory}"
IsEnabled="{Binding EnabledBcatSaveDirectory}"
Header="{locale:Locale GameListContextMenuOpenBcatSaveDirectory}" Header="{locale:Locale GameListContextMenuOpenBcatSaveDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuOpenBcatSaveDirectoryToolTip}" /> ToolTip.Tip="{locale:Locale GameListContextMenuOpenBcatSaveDirectoryToolTip}" />
<Separator /> <Separator />

View File

@@ -1,9 +1,7 @@
using Avalonia.Collections;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using LibHac.Common;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ui.App.Common; using Ryujinx.Ui.App.Common;
@@ -13,7 +11,6 @@ namespace Ryujinx.Ava.UI.Controls
{ {
public partial class GameGridView : UserControl public partial class GameGridView : UserControl
{ {
private ApplicationData _selectedApplication;
public static readonly RoutedEvent<ApplicationOpenedEventArgs> ApplicationOpenedEvent = public static readonly RoutedEvent<ApplicationOpenedEventArgs> ApplicationOpenedEvent =
RoutedEvent.Register<GameGridView, ApplicationOpenedEventArgs>(nameof(ApplicationOpened), RoutingStrategies.Bubble); RoutedEvent.Register<GameGridView, ApplicationOpenedEventArgs>(nameof(ApplicationOpened), RoutingStrategies.Bubble);
@@ -23,6 +20,16 @@ namespace Ryujinx.Ava.UI.Controls
remove { RemoveHandler(ApplicationOpenedEvent, value); } remove { RemoveHandler(ApplicationOpenedEvent, value); }
} }
public GameGridView()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
public void GameList_DoubleTapped(object sender, RoutedEventArgs args) public void GameList_DoubleTapped(object sender, RoutedEventArgs args)
{ {
if (sender is ListBox listBox) if (sender is ListBox listBox)
@@ -38,46 +45,13 @@ namespace Ryujinx.Ava.UI.Controls
{ {
if (sender is ListBox listBox) if (sender is ListBox listBox)
{ {
_selectedApplication = listBox.SelectedItem as ApplicationData; (DataContext as MainWindowViewModel).GridSelectedApplication = listBox.SelectedItem as ApplicationData;
(DataContext as MainWindowViewModel).GridSelectedApplication = _selectedApplication;
} }
} }
public ApplicationData SelectedApplication => _selectedApplication;
public GameGridView()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private void SearchBox_OnKeyUp(object sender, KeyEventArgs e) private void SearchBox_OnKeyUp(object sender, KeyEventArgs e)
{ {
(DataContext as MainWindowViewModel).SearchText = (sender as TextBox).Text; (DataContext as MainWindowViewModel).SearchText = (sender as TextBox).Text;
} }
private void MenuBase_OnMenuOpened(object sender, EventArgs e)
{
var selection = SelectedApplication;
if (selection != null)
{
if (sender is ContextMenu menu)
{
bool canHaveUserSave = !Utilities.IsZeros(selection.ControlHolder.ByteSpan) && selection.ControlHolder.Value.UserAccountSaveDataSize > 0;
bool canHaveDeviceSave = !Utilities.IsZeros(selection.ControlHolder.ByteSpan) && selection.ControlHolder.Value.DeviceSaveDataSize > 0;
bool canHaveBcatSave = !Utilities.IsZeros(selection.ControlHolder.ByteSpan) && selection.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0;
((menu.Items as AvaloniaList<object>)[2] as MenuItem).IsEnabled = canHaveUserSave;
((menu.Items as AvaloniaList<object>)[3] as MenuItem).IsEnabled = canHaveDeviceSave;
((menu.Items as AvaloniaList<object>)[4] as MenuItem).IsEnabled = canHaveBcatSave;
}
}
}
} }
} }

View File

@@ -13,7 +13,7 @@
mc:Ignorable="d"> mc:Ignorable="d">
<UserControl.Resources> <UserControl.Resources>
<helpers:BitmapArrayValueConverter x:Key="ByteImage" /> <helpers:BitmapArrayValueConverter x:Key="ByteImage" />
<MenuFlyout x:Key="GameContextMenu" Opened="MenuBase_OnMenuOpened"> <MenuFlyout x:Key="GameContextMenu">
<MenuItem <MenuItem
Command="{Binding ToggleFavorite}" Command="{Binding ToggleFavorite}"
Header="{locale:Locale GameListContextMenuToggleFavorite}" Header="{locale:Locale GameListContextMenuToggleFavorite}"
@@ -21,14 +21,17 @@
<Separator /> <Separator />
<MenuItem <MenuItem
Command="{Binding OpenUserSaveDirectory}" Command="{Binding OpenUserSaveDirectory}"
IsEnabled="{Binding EnabledUserSaveDirectory}"
Header="{locale:Locale GameListContextMenuOpenUserSaveDirectory}" Header="{locale:Locale GameListContextMenuOpenUserSaveDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuOpenUserSaveDirectoryToolTip}" /> ToolTip.Tip="{locale:Locale GameListContextMenuOpenUserSaveDirectoryToolTip}" />
<MenuItem <MenuItem
Command="{Binding OpenDeviceSaveDirectory}" Command="{Binding OpenDeviceSaveDirectory}"
IsEnabled="{Binding EnabledDeviceSaveDirectory}"
Header="{locale:Locale GameListContextMenuOpenDeviceSaveDirectory}" Header="{locale:Locale GameListContextMenuOpenDeviceSaveDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuOpenDeviceSaveDirectoryToolTip}" /> ToolTip.Tip="{locale:Locale GameListContextMenuOpenDeviceSaveDirectoryToolTip}" />
<MenuItem <MenuItem
Command="{Binding OpenBcatSaveDirectory}" Command="{Binding OpenBcatSaveDirectory}"
IsEnabled="{Binding EnabledBcatSaveDirectory}"
Header="{locale:Locale GameListContextMenuOpenBcatSaveDirectory}" Header="{locale:Locale GameListContextMenuOpenBcatSaveDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuOpenBcatSaveDirectoryToolTip}" /> ToolTip.Tip="{locale:Locale GameListContextMenuOpenBcatSaveDirectoryToolTip}" />
<Separator /> <Separator />

View File

@@ -1,9 +1,7 @@
using Avalonia.Collections;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using LibHac.Common;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ui.App.Common; using Ryujinx.Ui.App.Common;
@@ -13,7 +11,6 @@ namespace Ryujinx.Ava.UI.Controls
{ {
public partial class GameListView : UserControl public partial class GameListView : UserControl
{ {
private ApplicationData _selectedApplication;
public static readonly RoutedEvent<ApplicationOpenedEventArgs> ApplicationOpenedEvent = public static readonly RoutedEvent<ApplicationOpenedEventArgs> ApplicationOpenedEvent =
RoutedEvent.Register<GameGridView, ApplicationOpenedEventArgs>(nameof(ApplicationOpened), RoutingStrategies.Bubble); RoutedEvent.Register<GameGridView, ApplicationOpenedEventArgs>(nameof(ApplicationOpened), RoutingStrategies.Bubble);
@@ -23,6 +20,16 @@ namespace Ryujinx.Ava.UI.Controls
remove { RemoveHandler(ApplicationOpenedEvent, value); } remove { RemoveHandler(ApplicationOpenedEvent, value); }
} }
public GameListView()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
public void GameList_DoubleTapped(object sender, RoutedEventArgs args) public void GameList_DoubleTapped(object sender, RoutedEventArgs args)
{ {
if (sender is ListBox listBox) if (sender is ListBox listBox)
@@ -38,46 +45,13 @@ namespace Ryujinx.Ava.UI.Controls
{ {
if (sender is ListBox listBox) if (sender is ListBox listBox)
{ {
_selectedApplication = listBox.SelectedItem as ApplicationData; (DataContext as MainWindowViewModel).ListSelectedApplication = listBox.SelectedItem as ApplicationData;
(DataContext as MainWindowViewModel).ListSelectedApplication = _selectedApplication;
} }
} }
public ApplicationData SelectedApplication => _selectedApplication;
public GameListView()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private void SearchBox_OnKeyUp(object sender, KeyEventArgs e) private void SearchBox_OnKeyUp(object sender, KeyEventArgs e)
{ {
(DataContext as MainWindowViewModel).SearchText = (sender as TextBox).Text; (DataContext as MainWindowViewModel).SearchText = (sender as TextBox).Text;
} }
private void MenuBase_OnMenuOpened(object sender, EventArgs e)
{
var selection = SelectedApplication;
if (selection != null)
{
if (sender is ContextMenu menu)
{
bool canHaveUserSave = !Utilities.IsZeros(selection.ControlHolder.ByteSpan) && selection.ControlHolder.Value.UserAccountSaveDataSize > 0;
bool canHaveDeviceSave = !Utilities.IsZeros(selection.ControlHolder.ByteSpan) && selection.ControlHolder.Value.DeviceSaveDataSize > 0;
bool canHaveBcatSave = !Utilities.IsZeros(selection.ControlHolder.ByteSpan) && selection.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0;
((menu.Items as AvaloniaList<object>)[2] as MenuItem).IsEnabled = canHaveUserSave;
((menu.Items as AvaloniaList<object>)[3] as MenuItem).IsEnabled = canHaveDeviceSave;
((menu.Items as AvaloniaList<object>)[4] as MenuItem).IsEnabled = canHaveBcatSave;
}
}
}
} }
} }

View File

@@ -0,0 +1,65 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Notifications;
using Avalonia.Threading;
using Ryujinx.Ava.Common.Locale;
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.Helpers
{
public static class NotificationHelper
{
private const int MaxNotifications = 4;
private const int NotificationDelayInMs = 5000;
private static WindowNotificationManager _notificationManager;
private static readonly ManualResetEvent _templateAppliedEvent = new(false);
private static readonly BlockingCollection<Notification> _notifications = new();
public static void SetNotificationManager(Window host)
{
_notificationManager = new WindowNotificationManager(host)
{
Position = NotificationPosition.BottomRight,
MaxItems = MaxNotifications,
Margin = new Thickness(0, 0, 15, 40)
};
_notificationManager.TemplateApplied += (sender, args) =>
{
_templateAppliedEvent.Set();
};
Task.Run(async () =>
{
_templateAppliedEvent.WaitOne();
foreach (var notification in _notifications.GetConsumingEnumerable())
{
Dispatcher.UIThread.Post(() =>
{
_notificationManager.Show(notification);
});
await Task.Delay(NotificationDelayInMs / MaxNotifications);
}
});
}
public static void Show(string title, string text, NotificationType type, bool waitingExit = false, Action onClick = null, Action onClose = null)
{
var delay = waitingExit ? TimeSpan.FromMilliseconds(0) : TimeSpan.FromMilliseconds(NotificationDelayInMs);
_notifications.Add(new Notification(title, text, type, delay, onClick, onClose));
}
public static void ShowError(string message)
{
Show(LocaleManager.Instance[LocaleKeys.DialogErrorTitle], $"{LocaleManager.Instance[LocaleKeys.DialogErrorMessage]}\n\n{message}", NotificationType.Error);
}
}
}

View File

@@ -76,11 +76,11 @@ namespace Ryujinx.Ava.UI.Helpers
string setupButtonLabel = isInSetupGuide ? LocaleManager.Instance[LocaleKeys.OpenSetupGuideMessage] : ""; string setupButtonLabel = isInSetupGuide ? LocaleManager.Instance[LocaleKeys.OpenSetupGuideMessage] : "";
var result = await ContentDialogHelper.CreateInfoDialog( var result = await ContentDialogHelper.CreateInfoDialog(
string.Format(LocaleManager.Instance[LocaleKeys.DialogUserErrorDialogMessage], errorCode, GetErrorTitle(error)), LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogUserErrorDialogMessage, errorCode, GetErrorTitle(error)),
GetErrorDescription(error) + (isInSetupGuide GetErrorDescription(error) + (isInSetupGuide
? LocaleManager.Instance[LocaleKeys.DialogUserErrorDialogInfoMessage] ? LocaleManager.Instance[LocaleKeys.DialogUserErrorDialogInfoMessage]
: ""), setupButtonLabel, LocaleManager.Instance[LocaleKeys.InputDialogOk], : ""), setupButtonLabel, LocaleManager.Instance[LocaleKeys.InputDialogOk],
string.Format(LocaleManager.Instance[LocaleKeys.DialogUserErrorDialogTitle], errorCode)); LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogUserErrorDialogTitle, errorCode));
if (result == UserResult.Ok) if (result == UserResult.Ok)
{ {

View File

@@ -8,7 +8,7 @@ namespace Ryujinx.Ava.UI.Models
public ApplicationControlProperty Control { get; } public ApplicationControlProperty Control { get; }
public string Path { get; } public string Path { get; }
public string Label => string.Format(LocaleManager.Instance[LocaleKeys.TitleUpdateVersionLabel], Control.DisplayVersionString.ToString()); public string Label => LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.TitleUpdateVersionLabel, Control.DisplayVersionString.ToString());
public TitleUpdateModel(ApplicationControlProperty control, string path) public TitleUpdateModel(ApplicationControlProperty control, string path)
{ {

View File

@@ -81,10 +81,7 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public string Developers public string Developers => LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.AboutPageDeveloperListMore, "gdkchan, Ac_K, marysaka, rip in peri peri, LDj3SNuD, emmaus, Thealexbarney, GoffyDude, TSRBerry, IsaacMarovitz");
{
get => string.Format(LocaleManager.Instance[LocaleKeys.AboutPageDeveloperListMore], "gdkchan, Ac_K, marysaka, rip in peri peri, LDj3SNuD, emmaus, Thealexbarney, GoffyDude, TSRBerry, IsaacMarovitz");
}
public AboutWindowViewModel() public AboutWindowViewModel()
{ {

View File

@@ -3,6 +3,8 @@ using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Svg.Skia; using Avalonia.Svg.Skia;
using Avalonia.Threading; using Avalonia.Threading;
using LibHac.Bcat;
using LibHac.Tools.Fs;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Input; using Ryujinx.Ava.Input;
using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Controls;
@@ -717,7 +719,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
Logger.Error?.Print(LogClass.Configuration, $"Profile {ProfileName} is incompatible with the current input configuration system."); Logger.Error?.Print(LogClass.Configuration, $"Profile {ProfileName} is incompatible with the current input configuration system.");
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogProfileInvalidProfileErrorMessage], ProfileName)); await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogProfileInvalidProfileErrorMessage, ProfileName));
return; return;
} }

View File

@@ -5,8 +5,10 @@ using Avalonia.Media;
using Avalonia.Threading; using Avalonia.Threading;
using DynamicData; using DynamicData;
using DynamicData.Binding; using DynamicData.Binding;
using LibHac.Common;
using LibHac.Fs; using LibHac.Fs;
using LibHac.FsSystem; using LibHac.FsSystem;
using LibHac.Tools.Fs;
using Ryujinx.Ava.Common; using Ryujinx.Ava.Common;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Input; using Ryujinx.Ava.Input;
@@ -342,6 +344,12 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public bool EnabledUserSaveDirectory => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.UserAccountSaveDataSize > 0;
public bool EnabledDeviceSaveDirectory => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.DeviceSaveDataSize > 0;
public bool EnabledBcatSaveDirectory => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0;
public string LoadHeading public string LoadHeading
{ {
get => _loadHeading; get => _loadHeading;
@@ -733,19 +741,14 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
get get
{ {
switch (ConfigurationState.Instance.Ui.GridSize) return ConfigurationState.Instance.Ui.GridSize.Value switch
{ {
case 1: 1 => 78,
return 78; 2 => 100,
case 2: 3 => 120,
return 100; 4 => 140,
case 3: _ => 16,
return 120; };
case 4:
return 140;
default:
return 16;
}
} }
} }
@@ -753,19 +756,14 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
get get
{ {
switch (ConfigurationState.Instance.Ui.GridSize) return ConfigurationState.Instance.Ui.GridSize.Value switch
{ {
case 1: 1 => 120,
return 120; 2 => ShowNames ? 210 : 150,
case 2: 3 => ShowNames ? 240 : 180,
return ShowNames ? 210 : 150; 4 => ShowNames ? 280 : 220,
case 3: _ => 16,
return ShowNames ? 240 : 180; };
case 4:
return ShowNames ? 280 : 220;
default:
return 16;
}
} }
} }
@@ -947,20 +945,18 @@ namespace Ryujinx.Ava.UI.ViewModels
if (firmwareVersion == null) if (firmwareVersion == null)
{ {
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareNotFoundErrorMessage], filename)); await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallerFirmwareNotFoundErrorMessage, filename));
return; return;
} }
string dialogTitle = string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallTitle], firmwareVersion.VersionString); string dialogTitle = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallerFirmwareInstallTitle, firmwareVersion.VersionString);
string dialogMessage = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallerFirmwareInstallMessage, firmwareVersion.VersionString);
SystemVersion currentVersion = ContentManager.GetCurrentFirmwareVersion(); SystemVersion currentVersion = ContentManager.GetCurrentFirmwareVersion();
string dialogMessage = string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallMessage], firmwareVersion.VersionString);
if (currentVersion != null) if (currentVersion != null)
{ {
dialogMessage += string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallSubMessage], currentVersion.VersionString); dialogMessage += LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallerFirmwareInstallSubMessage, currentVersion.VersionString);
} }
dialogMessage += LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallConfirmMessage]; dialogMessage += LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallConfirmMessage];
@@ -993,7 +989,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
waitingDialog.Close(); waitingDialog.Close();
string message = string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallSuccessMessage], firmwareVersion.VersionString); string message = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallerFirmwareInstallSuccessMessage, firmwareVersion.VersionString);
await ContentDialogHelper.CreateInfoDialog(dialogTitle, message, LocaleManager.Instance[LocaleKeys.InputDialogOk], "", LocaleManager.Instance[LocaleKeys.RyujinxInfo]); await ContentDialogHelper.CreateInfoDialog(dialogTitle, message, LocaleManager.Instance[LocaleKeys.InputDialogOk], "", LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
@@ -1063,7 +1059,7 @@ namespace Ryujinx.Ava.UI.ViewModels
IsLoadingIndeterminate = false; IsLoadingIndeterminate = false;
break; break;
case LoadState.Loaded: case LoadState.Loaded:
LoadHeading = string.Format(LocaleManager.Instance[LocaleKeys.LoadingHeading], TitleName); LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, TitleName);
IsLoadingIndeterminate = true; IsLoadingIndeterminate = true;
CacheLoadStatus = ""; CacheLoadStatus = "";
break; break;
@@ -1079,7 +1075,7 @@ namespace Ryujinx.Ava.UI.ViewModels
IsLoadingIndeterminate = false; IsLoadingIndeterminate = false;
break; break;
case ShaderCacheLoadingState.Loaded: case ShaderCacheLoadingState.Loaded:
LoadHeading = string.Format(LocaleManager.Instance[LocaleKeys.LoadingHeading], TitleName); LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, TitleName);
IsLoadingIndeterminate = true; IsLoadingIndeterminate = true;
CacheLoadStatus = ""; CacheLoadStatus = "";
break; break;
@@ -1091,35 +1087,27 @@ namespace Ryujinx.Ava.UI.ViewModels
})); }));
} }
private void OpenSaveDirectory(in SaveDataFilter filter, ApplicationData data, ulong titleId)
{
ApplicationHelper.OpenSaveDir(in filter, titleId, data.ControlHolder, data.TitleName);
}
private async void ExtractLogo() private async void ExtractLogo()
{ {
var selection = SelectedApplication; if (SelectedApplication != null)
if (selection != null)
{ {
await ApplicationHelper.ExtractSection(NcaSectionType.Logo, selection.Path); await ApplicationHelper.ExtractSection(NcaSectionType.Logo, SelectedApplication.Path, SelectedApplication.TitleName);
} }
} }
private async void ExtractRomFs() private async void ExtractRomFs()
{ {
var selection = SelectedApplication; if (SelectedApplication != null)
if (selection != null)
{ {
await ApplicationHelper.ExtractSection(NcaSectionType.Data, selection.Path); await ApplicationHelper.ExtractSection(NcaSectionType.Data, SelectedApplication.Path, SelectedApplication.TitleName);
} }
} }
private async void ExtractExeFs() private async void ExtractExeFs()
{ {
var selection = SelectedApplication; if (SelectedApplication != null)
if (selection != null)
{ {
await ApplicationHelper.ExtractSection(NcaSectionType.Code, selection.Path); await ApplicationHelper.ExtractSection(NcaSectionType.Code, SelectedApplication.Path, SelectedApplication.TitleName);
} }
} }
@@ -1333,10 +1321,15 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public void ChangeLanguage(object obj) public void ChangeLanguage(object languageCode)
{ {
LocaleManager.Instance.LoadDefaultLanguage(); LocaleManager.Instance.LoadLanguage((string)languageCode);
LocaleManager.Instance.LoadLanguage((string)obj);
if (Program.PreviewerDetached)
{
ConfigurationState.Instance.Ui.LanguageCode.Value = (string)languageCode;
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
}
} }
public async void ManageProfiles() public async void ManageProfiles()
@@ -1374,7 +1367,7 @@ namespace Ryujinx.Ava.UI.ViewModels
// FIXME: Found a way to reproduce the bold effect on the title name (fork?). // FIXME: Found a way to reproduce the bold effect on the title name (fork?).
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning], UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning],
string.Format(LocaleManager.Instance[LocaleKeys.DialogPPTCDeletionMessage], selection.TitleName), LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionMessage, selection.TitleName),
LocaleManager.Instance[LocaleKeys.InputDialogYes], LocaleManager.Instance[LocaleKeys.InputDialogYes],
LocaleManager.Instance[LocaleKeys.InputDialogNo], LocaleManager.Instance[LocaleKeys.InputDialogNo],
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]); LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
@@ -1401,7 +1394,7 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
catch (Exception e) catch (Exception e)
{ {
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogPPTCDeletionErrorMessage], file.Name, e)); await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionErrorMessage, file.Name, e));
} }
} }
} }
@@ -1438,7 +1431,7 @@ namespace Ryujinx.Ava.UI.ViewModels
// FIXME: Found a way to reproduce the bold effect on the title name (fork?). // FIXME: Found a way to reproduce the bold effect on the title name (fork?).
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning], UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning],
string.Format(LocaleManager.Instance[LocaleKeys.DialogShaderDeletionMessage], selection.TitleName), LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogShaderDeletionMessage, selection.TitleName),
LocaleManager.Instance[LocaleKeys.InputDialogYes], LocaleManager.Instance[LocaleKeys.InputDialogYes],
LocaleManager.Instance[LocaleKeys.InputDialogNo], LocaleManager.Instance[LocaleKeys.InputDialogNo],
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]); LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
@@ -1463,7 +1456,7 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
catch (Exception e) catch (Exception e)
{ {
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogPPTCDeletionErrorMessage], directory.Name, e)); await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionErrorMessage, directory.Name, e));
} }
} }
} }
@@ -1476,62 +1469,12 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
catch (Exception e) catch (Exception e)
{ {
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.ShaderCachePurgeError], file.Name, e)); await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.ShaderCachePurgeError, file.Name, e));
} }
} }
} }
} }
public void OpenDeviceSaveDirectory()
{
ApplicationData selection = SelectedApplication;
if (selection != null)
{
Task.Run(() =>
{
if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber))
{
async void Action()
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogRyujinxErrorMessage], LocaleManager.Instance[LocaleKeys.DialogInvalidTitleIdErrorMessage]);
}
Dispatcher.UIThread.Post(Action);
return;
}
var saveDataFilter = SaveDataFilter.Make(titleIdNumber, SaveDataType.Device, userId: default, saveDataId: default, index: default);
OpenSaveDirectory(in saveDataFilter, selection, titleIdNumber);
});
}
}
public void OpenBcatSaveDirectory()
{
ApplicationData selection = SelectedApplication;
if (selection != null)
{
Task.Run(() =>
{
if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber))
{
async void Action()
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogRyujinxErrorMessage], LocaleManager.Instance[LocaleKeys.DialogInvalidTitleIdErrorMessage]);
}
Dispatcher.UIThread.Post(Action);
return;
}
var saveDataFilter = SaveDataFilter.Make(titleIdNumber, SaveDataType.Bcat, userId: default, saveDataId: default, index: default);
OpenSaveDirectory(in saveDataFilter, selection, titleIdNumber);
});
}
}
public void ToggleFavorite() public void ToggleFavorite()
{ {
ApplicationData selection = SelectedApplication; ApplicationData selection = SelectedApplication;
@@ -1550,37 +1493,45 @@ namespace Ryujinx.Ava.UI.ViewModels
public void OpenUserSaveDirectory() public void OpenUserSaveDirectory()
{ {
ApplicationData selection = SelectedApplication; OpenSaveDirectory(SaveDataType.Account, userId: new UserId((ulong)AccountManager.LastOpenedUser.UserId.High, (ulong)AccountManager.LastOpenedUser.UserId.Low));
if (selection != null)
{
Task.Run(() =>
{
if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber))
{
async void Action()
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogRyujinxErrorMessage], LocaleManager.Instance[LocaleKeys.DialogInvalidTitleIdErrorMessage]);
} }
Dispatcher.UIThread.Post(Action); public void OpenDeviceSaveDirectory()
{
OpenSaveDirectory(SaveDataType.Device, userId: default);
}
public void OpenBcatSaveDirectory()
{
OpenSaveDirectory(SaveDataType.Bcat, userId: default);
}
private void OpenSaveDirectory(SaveDataType saveDataType, UserId userId)
{
if (SelectedApplication != null)
{
if (!ulong.TryParse(SelectedApplication.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber))
{
Dispatcher.UIThread.InvokeAsync(async () =>
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogRyujinxErrorMessage], LocaleManager.Instance[LocaleKeys.DialogInvalidTitleIdErrorMessage]);
});
return; return;
} }
UserId userId = new((ulong)AccountManager.LastOpenedUser.UserId.High, (ulong)AccountManager.LastOpenedUser.UserId.Low); var saveDataFilter = SaveDataFilter.Make(titleIdNumber, saveDataType, userId, saveDataId: default, index: default);
SaveDataFilter saveDataFilter = SaveDataFilter.Make(titleIdNumber, saveType: default, userId, saveDataId: default, index: default);
OpenSaveDirectory(in saveDataFilter, selection, titleIdNumber); ApplicationHelper.OpenSaveDir(in saveDataFilter, titleIdNumber, SelectedApplication.ControlHolder, SelectedApplication.TitleName);
});
} }
} }
public void OpenModsDirectory() public void OpenModsDirectory()
{ {
ApplicationData selection = SelectedApplication; if (SelectedApplication != null)
if (selection != null)
{ {
string modsBasePath = VirtualFileSystem.ModLoader.GetModsBasePath(); string modsBasePath = VirtualFileSystem.ModLoader.GetModsBasePath();
string titleModsPath = VirtualFileSystem.ModLoader.GetTitleDir(modsBasePath, selection.TitleId); string titleModsPath = VirtualFileSystem.ModLoader.GetTitleDir(modsBasePath, SelectedApplication.TitleId);
OpenHelper.OpenFolder(titleModsPath); OpenHelper.OpenFolder(titleModsPath);
} }
@@ -1588,12 +1539,10 @@ namespace Ryujinx.Ava.UI.ViewModels
public void OpenSdModsDirectory() public void OpenSdModsDirectory()
{ {
ApplicationData selection = SelectedApplication; if (SelectedApplication != null)
if (selection != null)
{ {
string sdModsBasePath = VirtualFileSystem.ModLoader.GetSdModsBasePath(); string sdModsBasePath = VirtualFileSystem.ModLoader.GetSdModsBasePath();
string titleModsPath = VirtualFileSystem.ModLoader.GetTitleDir(sdModsBasePath, selection.TitleId); string titleModsPath = VirtualFileSystem.ModLoader.GetTitleDir(sdModsBasePath, SelectedApplication.TitleId);
OpenHelper.OpenFolder(titleModsPath); OpenHelper.OpenFolder(titleModsPath);
} }
@@ -1609,25 +1558,17 @@ namespace Ryujinx.Ava.UI.ViewModels
public async void OpenDownloadableContentManager() public async void OpenDownloadableContentManager()
{ {
ApplicationData selection = SelectedApplication; if (SelectedApplication != null)
if (selection != null)
{ {
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) await new DownloadableContentManagerWindow(VirtualFileSystem, ulong.Parse(SelectedApplication.TitleId, NumberStyles.HexNumber), SelectedApplication.TitleName).ShowDialog(TopLevel as Window);
{
await new DownloadableContentManagerWindow(VirtualFileSystem, ulong.Parse(selection.TitleId, NumberStyles.HexNumber), selection.TitleName).ShowDialog(desktop.MainWindow);
}
} }
} }
public async void OpenCheatManager() public async void OpenCheatManager()
{ {
ApplicationData selection = SelectedApplication; if (SelectedApplication != null)
if (selection != null)
{ {
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) await new CheatWindow(VirtualFileSystem, SelectedApplication.TitleId, SelectedApplication.TitleName).ShowDialog(TopLevel as Window);
{
await new CheatWindow(VirtualFileSystem, selection.TitleId, selection.TitleName).ShowDialog(desktop.MainWindow);
}
} }
} }
@@ -1641,7 +1582,7 @@ namespace Ryujinx.Ava.UI.ViewModels
StatusBarProgressMaximum = 0; StatusBarProgressMaximum = 0;
StatusBarProgressValue = 0; StatusBarProgressValue = 0;
LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0); LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0);
}); });
ReloadGameList?.Invoke(); ReloadGameList?.Invoke();
@@ -1755,8 +1696,14 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
CanUpdate = false; CanUpdate = false;
LoadHeading = string.IsNullOrWhiteSpace(titleName) ? string.Format(LocaleManager.Instance[LocaleKeys.LoadingHeading], AppHost.Device.Application.TitleName) : titleName;
TitleName = string.IsNullOrWhiteSpace(titleName) ? AppHost.Device.Application.TitleName : titleName; LoadHeading = TitleName = titleName;
if (string.IsNullOrWhiteSpace(titleName))
{
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, AppHost.Device.Application.TitleName);
TitleName = AppHost.Device.Application.TitleName;
}
SwitchToRenderer(startFullscreen); SwitchToRenderer(startFullscreen);
@@ -1807,14 +1754,13 @@ namespace Ryujinx.Ava.UI.ViewModels
if (version != null) if (version != null)
{ {
LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarSystemVersion, LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarSystemVersion, version.VersionString);
version.VersionString);
hasApplet = version.Major > 3; hasApplet = version.Major > 3;
} }
else else
{ {
LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarSystemVersion, "0.0"); LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarSystemVersion, "0.0");
} }
IsAppletMenuActive = hasApplet; IsAppletMenuActive = hasApplet;

View File

@@ -181,7 +181,7 @@ public class TitleUpdateViewModel : BaseModel
{ {
Dispatcher.UIThread.Post(async () => Dispatcher.UIThread.Post(async () =>
{ {
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogDlcLoadNcaErrorMessage], ex.Message, path)); await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadNcaErrorMessage, ex.Message, path));
}); });
} }
} }

View File

@@ -17,8 +17,7 @@ namespace Ryujinx.Ava.UI.ViewModels
private ObservableCollection<SaveModel> _views = new(); private ObservableCollection<SaveModel> _views = new();
private AccountManager _accountManager; private AccountManager _accountManager;
public string SaveManagerHeading => public string SaveManagerHeading => LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SaveManagerHeading, _accountManager.LastOpenedUser.Name, _accountManager.LastOpenedUser.UserId);
string.Format(LocaleManager.Instance[LocaleKeys.SaveManagerHeading], _accountManager.LastOpenedUser.Name, _accountManager.LastOpenedUser.UserId);
public int SortIndex public int SortIndex
{ {

View File

@@ -31,7 +31,7 @@ namespace Ryujinx.Ava.UI.Windows
{ {
LoadedCheats = new AvaloniaList<CheatsList>(); LoadedCheats = new AvaloniaList<CheatsList>();
Heading = string.Format(LocaleManager.Instance[LocaleKeys.CheatWindowHeading], titleName, titleId.ToUpper()); Heading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.CheatWindowHeading, titleName, titleId.ToUpper());
InitializeComponent(); InitializeComponent();

View File

@@ -86,7 +86,7 @@ namespace Ryujinx.Ava.UI.Windows
private void PrintHeading() private void PrintHeading()
{ {
Heading.Text = string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowHeading], _downloadableContents.Count, _titleName, _titleId.ToString("X16")); Heading.Text = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DlcWindowHeading, _downloadableContents.Count, _titleName, _titleId.ToString("X16"));
} }
private void LoadDownloadableContents() private void LoadDownloadableContents()
@@ -133,7 +133,7 @@ namespace Ryujinx.Ava.UI.Windows
{ {
Dispatcher.UIThread.InvokeAsync(async () => Dispatcher.UIThread.InvokeAsync(async () =>
{ {
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogDlcLoadNcaErrorMessage], ex.Message, containerPath)); await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadNcaErrorMessage, ex.Message, containerPath));
}); });
} }

View File

@@ -43,6 +43,7 @@
<StackPanel Grid.Row="0" IsVisible="False"> <StackPanel Grid.Row="0" IsVisible="False">
<helpers:HotKeyControl Name="FullscreenHotKey" Command="{ReflectionBinding ToggleFullscreen}" /> <helpers:HotKeyControl Name="FullscreenHotKey" Command="{ReflectionBinding ToggleFullscreen}" />
<helpers:HotKeyControl Name="FullscreenHotKey2" Command="{ReflectionBinding ToggleFullscreen}" /> <helpers:HotKeyControl Name="FullscreenHotKey2" Command="{ReflectionBinding ToggleFullscreen}" />
<helpers:HotKeyControl Name="FullscreenHotKeyMacOS" Command="{ReflectionBinding ToggleFullscreen}" />
<helpers:HotKeyControl Name="DockToggleHotKey" Command="{ReflectionBinding ToggleDockMode}" /> <helpers:HotKeyControl Name="DockToggleHotKey" Command="{ReflectionBinding ToggleDockMode}" />
<helpers:HotKeyControl Name="ExitHotKey" Command="{ReflectionBinding ExitCurrentState}" /> <helpers:HotKeyControl Name="ExitHotKey" Command="{ReflectionBinding ExitCurrentState}" />
</StackPanel> </StackPanel>

View File

@@ -104,6 +104,8 @@ namespace Ryujinx.Ava.UI.Windows
ApplicationLibrary.ApplicationCountUpdated += ApplicationLibrary_ApplicationCountUpdated; ApplicationLibrary.ApplicationCountUpdated += ApplicationLibrary_ApplicationCountUpdated;
ApplicationLibrary.ApplicationAdded += ApplicationLibrary_ApplicationAdded; ApplicationLibrary.ApplicationAdded += ApplicationLibrary_ApplicationAdded;
ViewModel.ReloadGameList += ReloadGameList; ViewModel.ReloadGameList += ReloadGameList;
NotificationHelper.SetNotificationManager(this);
} }
private void IsActiveChanged(bool obj) private void IsActiveChanged(bool obj)
@@ -146,7 +148,7 @@ namespace Ryujinx.Ava.UI.Windows
private void ApplicationLibrary_ApplicationCountUpdated(object sender, ApplicationCountUpdatedEventArgs e) private void ApplicationLibrary_ApplicationCountUpdated(object sender, ApplicationCountUpdatedEventArgs e)
{ {
LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarGamesLoaded, e.NumAppsLoaded, e.NumAppsFound); LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarGamesLoaded, e.NumAppsLoaded, e.NumAppsFound);
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
{ {
@@ -329,6 +331,7 @@ namespace Ryujinx.Ava.UI.Windows
{ {
HotKeyManager.SetHotKey(FullscreenHotKey, new KeyGesture(Key.Enter, KeyModifiers.Alt)); HotKeyManager.SetHotKey(FullscreenHotKey, new KeyGesture(Key.Enter, KeyModifiers.Alt));
HotKeyManager.SetHotKey(FullscreenHotKey2, new KeyGesture(Key.F11)); HotKeyManager.SetHotKey(FullscreenHotKey2, new KeyGesture(Key.F11));
HotKeyManager.SetHotKey(FullscreenHotKeyMacOS, new KeyGesture(Key.F, KeyModifiers.Control | KeyModifiers.Meta));
HotKeyManager.SetHotKey(DockToggleHotKey, new KeyGesture(Key.F9)); HotKeyManager.SetHotKey(DockToggleHotKey, new KeyGesture(Key.F9));
HotKeyManager.SetHotKey(ExitHotKey, new KeyGesture(Key.Escape)); HotKeyManager.SetHotKey(ExitHotKey, new KeyGesture(Key.Escape));
} }
@@ -415,7 +418,7 @@ namespace Ryujinx.Ava.UI.Windows
ViewModel.StatusBarProgressMaximum = 0; ViewModel.StatusBarProgressMaximum = 0;
ViewModel.StatusBarProgressValue = 0; ViewModel.StatusBarProgressValue = 0;
LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0); LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0);
}); });
ReloadGameList(); ReloadGameList();

View File

@@ -42,7 +42,7 @@ namespace Ryujinx.Ava.UI.Windows
SecondaryButtonText = "", SecondaryButtonText = "",
CloseButtonText = "", CloseButtonText = "",
Content = new TitleUpdateWindow(virtualFileSystem, titleId, titleName), Content = new TitleUpdateWindow(virtualFileSystem, titleId, titleName),
Title = string.Format(LocaleManager.Instance[LocaleKeys.GameUpdateWindowHeading], titleName, titleId.ToString("X16")) Title = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.GameUpdateWindowHeading, titleName, titleId.ToString("X16"))
}; };
Style bottomBorder = new(x => x.OfType<Grid>().Name("DialogSpace").Child().OfType<Border>()); Style bottomBorder = new(x => x.OfType<Grid>().Name("DialogSpace").Child().OfType<Border>());

View File

@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2; private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
private const uint CodeGenVersion = 3939; private const uint CodeGenVersion = 4318;
private const string SharedTocFileName = "shared.toc"; private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data"; private const string SharedDataFileName = "shared.data";

View File

@@ -91,6 +91,7 @@ namespace Ryujinx.Graphics.Shader.Decoders
Neu = 13, Neu = 13,
Geu = 14, Geu = 14,
T = 15, T = 15,
Off = 16,
Lo = 17, Lo = 17,
Sff = 18, Sff = 18,
Ls = 19, Ls = 19,

View File

@@ -54,13 +54,6 @@ namespace Ryujinx.Graphics.Shader.Instructions
context.Config.GpuAccessor.Log("Shader instruction Cctlt is not implemented."); context.Config.GpuAccessor.Log("Shader instruction Cctlt is not implemented.");
} }
public static void Cset(EmitterContext context)
{
InstCset op = context.GetOp<InstCset>();
context.Config.GpuAccessor.Log("Shader instruction Cset is not implemented.");
}
public static void Cs2r(EmitterContext context) public static void Cs2r(EmitterContext context)
{ {
InstCs2r op = context.GetOp<InstCs2r>(); InstCs2r op = context.GetOp<InstCs2r>();

View File

@@ -0,0 +1,87 @@
using Ryujinx.Graphics.Shader.Decoders;
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.Translation;
using static Ryujinx.Graphics.Shader.Instructions.InstEmitAluHelper;
using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper;
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
namespace Ryujinx.Graphics.Shader.Instructions
{
static partial class InstEmit
{
public static void Cset(EmitterContext context)
{
InstCset op = context.GetOp<InstCset>();
Operand res = GetCondition(context, op.Ccc);
Operand srcPred = GetPredicate(context, op.SrcPred, op.SrcPredInv);
res = GetPredLogicalOp(context, op.Bop, res, srcPred);
Operand dest = GetDest(op.Dest);
if (op.BVal)
{
context.Copy(dest, context.ConditionalSelect(res, ConstF(1), Const(0)));
}
else
{
context.Copy(dest, res);
}
// TODO: CC.
}
public static void Csetp(EmitterContext context)
{
InstCsetp op = context.GetOp<InstCsetp>();
Operand p0Res = GetCondition(context, op.Ccc);
Operand p1Res = context.BitwiseNot(p0Res);
Operand srcPred = GetPredicate(context, op.SrcPred, op.SrcPredInv);
p0Res = GetPredLogicalOp(context, op.Bop, p0Res, srcPred);
p1Res = GetPredLogicalOp(context, op.Bop, p1Res, srcPred);
context.Copy(Register(op.DestPred, RegisterType.Predicate), p0Res);
context.Copy(Register(op.DestPredInv, RegisterType.Predicate), p1Res);
// TODO: CC.
}
private static Operand GetCondition(EmitterContext context, Ccc cond, int defaultCond = IrConsts.True)
{
return cond switch
{
Ccc.F => Const(IrConsts.False),
Ccc.Lt => context.BitwiseExclusiveOr(context.BitwiseAnd(GetNF(), context.BitwiseNot(GetZF())), GetVF()),
Ccc.Eq => context.BitwiseAnd(context.BitwiseNot(GetNF()), GetZF()),
Ccc.Le => context.BitwiseExclusiveOr(GetNF(), context.BitwiseOr(GetZF(), GetVF())),
Ccc.Gt => context.BitwiseNot(context.BitwiseOr(context.BitwiseExclusiveOr(GetNF(), GetVF()), GetZF())),
Ccc.Ne => context.BitwiseNot(GetZF()),
Ccc.Ge => context.BitwiseNot(context.BitwiseExclusiveOr(GetNF(), GetVF())),
Ccc.Num => context.BitwiseNot(context.BitwiseAnd(GetNF(), GetZF())),
Ccc.Nan => context.BitwiseAnd(GetNF(), GetZF()),
Ccc.Ltu => context.BitwiseExclusiveOr(GetNF(), GetVF()),
Ccc.Equ => GetZF(),
Ccc.Leu => context.BitwiseOr(context.BitwiseExclusiveOr(GetNF(), GetVF()), GetZF()),
Ccc.Gtu => context.BitwiseExclusiveOr(context.BitwiseNot(GetNF()), context.BitwiseOr(GetVF(), GetZF())),
Ccc.Neu => context.BitwiseOr(GetNF(), context.BitwiseNot(GetZF())),
Ccc.Geu => context.BitwiseExclusiveOr(context.BitwiseOr(context.BitwiseNot(GetNF()), GetZF()), GetVF()),
Ccc.T => Const(IrConsts.True),
Ccc.Off => context.BitwiseNot(GetVF()),
Ccc.Lo => context.BitwiseNot(GetCF()),
Ccc.Sff => context.BitwiseNot(GetNF()),
Ccc.Ls => context.BitwiseOr(GetZF(), context.BitwiseNot(GetCF())),
Ccc.Hi => context.BitwiseAnd(GetCF(), context.BitwiseNot(GetZF())),
Ccc.Sft => GetNF(),
Ccc.Hs => GetCF(),
Ccc.Oft => GetVF(),
Ccc.Rle => context.BitwiseOr(GetNF(), GetZF()),
Ccc.Rgt => context.BitwiseNot(context.BitwiseOr(GetNF(), GetZF())),
_ => Const(defaultCond)
};
}
}
}

View File

@@ -318,21 +318,5 @@ namespace Ryujinx.Graphics.Shader.Instructions
context.BranchIfTrue(label, pred); context.BranchIfTrue(label, pred);
} }
} }
private static Operand GetCondition(EmitterContext context, Ccc cond, int defaultCond = IrConsts.True)
{
// TODO: More condition codes, figure out how they work.
switch (cond)
{
case Ccc.Eq:
case Ccc.Equ:
return GetZF();
case Ccc.Ne:
case Ccc.Neu:
return context.BitwiseNot(GetZF());
}
return Const(defaultCond);
}
} }
} }

View File

@@ -11,23 +11,6 @@ namespace Ryujinx.Graphics.Shader.Instructions
{ {
static partial class InstEmit static partial class InstEmit
{ {
public static void Csetp(EmitterContext context)
{
InstCsetp op = context.GetOp<InstCsetp>();
// TODO: Implement that properly.
Operand p0Res = Const(IrConsts.True);
Operand p1Res = context.BitwiseNot(p0Res);
Operand srcPred = GetPredicate(context, op.SrcPred, op.SrcPredInv);
p0Res = GetPredLogicalOp(context, op.Bop, p0Res, srcPred);
p1Res = GetPredLogicalOp(context, op.Bop, p1Res, srcPred);
context.Copy(Register(op.DestPred, RegisterType.Predicate), p0Res);
context.Copy(Register(op.DestPredInv, RegisterType.Predicate), p1Res);
}
public static void IcmpR(EmitterContext context) public static void IcmpR(EmitterContext context)
{ {
InstIcmpR op = context.GetOp<InstIcmpR>(); InstIcmpR op = context.GetOp<InstIcmpR>();

View File

@@ -1,5 +1,7 @@
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
using Ryujinx.Common.Logging;
using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@@ -42,6 +44,8 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
// TODO: Use 0x8000000000000010 system savedata instead of a JSON file if needed. // TODO: Use 0x8000000000000010 system savedata instead of a JSON file if needed.
if (File.Exists(_profilesJsonPath)) if (File.Exists(_profilesJsonPath))
{
try
{ {
ProfilesJson profilesJson = JsonHelper.DeserializeFromFile<ProfilesJson>(_profilesJsonPath); ProfilesJson profilesJson = JsonHelper.DeserializeFromFile<ProfilesJson>(_profilesJsonPath);
@@ -54,6 +58,13 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
LastOpened = new UserId(profilesJson.LastOpened); LastOpened = new UserId(profilesJson.LastOpened);
} }
catch (Exception e)
{
Logger.Error?.Print(LogClass.Application, $"Failed to parse {_profilesJsonPath}: {e.Message} Loading default profile!");
LastOpened = AccountManager.DefaultUserId;
}
}
else else
{ {
LastOpened = AccountManager.DefaultUserId; LastOpened = AccountManager.DefaultUserId;