Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
6adf15e479 | ||
|
2747f12591 | ||
|
a47824f961 | ||
|
8474d52778 | ||
|
dd7a924596 | ||
|
a76eaf9a9a |
@@ -44,7 +44,7 @@
|
||||
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
|
||||
<PackageVersion Include="SPB" Version="0.0.4-build28" />
|
||||
<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.Management" Version="7.0.0" />
|
||||
<PackageVersion Include="System.Net.NameResolution" Version="4.3.0" />
|
||||
|
@@ -462,8 +462,7 @@ namespace Ryujinx.Ava
|
||||
{
|
||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogFirmwareNoFirmwareInstalledMessage],
|
||||
string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallEmbeddedMessage],
|
||||
firmwareVersion.VersionString),
|
||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallEmbeddedMessage, firmwareVersion.VersionString),
|
||||
LocaleManager.Instance[LocaleKeys.InputDialogYes],
|
||||
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
||||
"");
|
||||
@@ -493,10 +492,8 @@ namespace Ryujinx.Ava
|
||||
_viewModel.RefreshFirmwareStatus();
|
||||
|
||||
await ContentDialogHelper.CreateInfoDialog(
|
||||
string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstalledMessage],
|
||||
firmwareVersion.VersionString),
|
||||
string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallEmbeddedSuccessMessage],
|
||||
firmwareVersion.VersionString),
|
||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstalledMessage, firmwareVersion.VersionString),
|
||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallEmbeddedSuccessMessage, firmwareVersion.VersionString),
|
||||
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
||||
"",
|
||||
LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
|
||||
|
@@ -370,7 +370,7 @@
|
||||
"DialogUserProfileDeletionConfirmMessage": "Möchtest du das ausgewählte Profil löschen?",
|
||||
"DialogControllerSettingsModifiedConfirmMessage": "Die aktuellen Controller-Einstellungen wurden aktualisiert.",
|
||||
"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!",
|
||||
"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?",
|
||||
|
@@ -370,7 +370,7 @@
|
||||
"DialogUserProfileDeletionConfirmMessage": "Θέλετε να διαγράψετε το επιλεγμένο προφίλ",
|
||||
"DialogControllerSettingsModifiedConfirmMessage": "Οι τρέχουσες ρυθμίσεις χειρισμού έχουν ενημερωθεί.",
|
||||
"DialogControllerSettingsModifiedConfirmSubMessage": "Θέλετε να αποθηκεύσετε;",
|
||||
"DialogDlcLoadNcaErrorMessage": "{0}. Σφάλμα Αρχείου: {1}",
|
||||
"DialogLoadNcaErrorMessage": "{0}. Σφάλμα Αρχείου: {1}",
|
||||
"DialogDlcNoDlcErrorMessage": "Το αρχείο δεν περιέχει DLC για τον επιλεγμένο τίτλο!",
|
||||
"DialogPerformanceCheckLoggingEnabledMessage": "Έχετε ενεργοποιημένη την καταγραφή εντοπισμού σφαλμάτων, η οποία έχει σχεδιαστεί για χρήση μόνο από προγραμματιστές.",
|
||||
"DialogPerformanceCheckLoggingEnabledConfirmMessage": "Για βέλτιστη απόδοση, συνιστάται η απενεργοποίηση καταγραφής εντοπισμού σφαλμάτων. Θέλετε να απενεργοποιήσετε την καταγραφή τώρα;",
|
||||
|
@@ -375,7 +375,7 @@
|
||||
"DialogUserProfileUnsavedChangesSubMessage": "Do you want to discard your changes?",
|
||||
"DialogControllerSettingsModifiedConfirmMessage": "The current controller settings has been updated.",
|
||||
"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!",
|
||||
"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?",
|
||||
|
@@ -370,7 +370,7 @@
|
||||
"DialogUserProfileDeletionConfirmMessage": "¿Quieres eliminar el perfil seleccionado?",
|
||||
"DialogControllerSettingsModifiedConfirmMessage": "Se ha actualizado la configuración del mando actual.",
|
||||
"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!",
|
||||
"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?",
|
||||
|
@@ -370,7 +370,7 @@
|
||||
"DialogUserProfileDeletionConfirmMessage": "Voulez-vous supprimer le profil sélectionné ?",
|
||||
"DialogControllerSettingsModifiedConfirmMessage": "Les paramètres actuels du contrôleur ont été mis à jour.",
|
||||
"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é !",
|
||||
"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 ?",
|
||||
|
@@ -370,7 +370,7 @@
|
||||
"DialogUserProfileDeletionConfirmMessage": "Vuoi eliminare il profilo selezionato?",
|
||||
"DialogControllerSettingsModifiedConfirmMessage": "Le attuali impostazioni del controller sono state aggiornate.",
|
||||
"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!",
|
||||
"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?",
|
||||
|
@@ -370,7 +370,7 @@
|
||||
"DialogUserProfileDeletionConfirmMessage": "選択されたプロファイルを削除しますか",
|
||||
"DialogControllerSettingsModifiedConfirmMessage": "現在のコントローラ設定が更新されました.",
|
||||
"DialogControllerSettingsModifiedConfirmSubMessage": "セーブしますか?",
|
||||
"DialogDlcLoadNcaErrorMessage": "{0}. エラー発生ファイル: {1}",
|
||||
"DialogLoadNcaErrorMessage": "{0}. エラー発生ファイル: {1}",
|
||||
"DialogDlcNoDlcErrorMessage": "選択されたファイルはこのタイトル用の DLC ではありません!",
|
||||
"DialogPerformanceCheckLoggingEnabledMessage": "トレースロギングを有効にします. これは開発者のみに有用な機能です.",
|
||||
"DialogPerformanceCheckLoggingEnabledConfirmMessage": "パフォーマンス最適化のためには,トレースロギングを無効にすることを推奨します. トレースロギングを無効にしてよろしいですか?",
|
||||
|
@@ -370,7 +370,7 @@
|
||||
"DialogUserProfileDeletionConfirmMessage": "선택한 프로파일을 삭제하겠습니까?",
|
||||
"DialogControllerSettingsModifiedConfirmMessage": "현재 컨트롤러 설정이 업데이트되었습니다.",
|
||||
"DialogControllerSettingsModifiedConfirmSubMessage": "저장하겠습니까?",
|
||||
"DialogDlcLoadNcaErrorMessage": "{0}입니다. 오류 발생 파일: {1}",
|
||||
"DialogLoadNcaErrorMessage": "{0}입니다. 오류 발생 파일: {1}",
|
||||
"DialogDlcNoDlcErrorMessage": "지정된 파일에 선택한 타이틀에 대한 DLC가 포함되어 있지 않습니다!",
|
||||
"DialogPerformanceCheckLoggingEnabledMessage": "개발자만 사용하도록 설계된 추적 로깅이 활성화되어 있습니다.",
|
||||
"DialogPerformanceCheckLoggingEnabledConfirmMessage": "최적의 성능을 위해 추적 로깅을 비활성화하는 것이 좋습니다. 지금 추적 로깅을 비활성화하겠습니까?",
|
||||
|
@@ -370,7 +370,7 @@
|
||||
"DialogUserProfileDeletionConfirmMessage": "Czy chcesz usunąć wybrany profil",
|
||||
"DialogControllerSettingsModifiedConfirmMessage": "Aktualne ustawienia kontrolera zostały zaktualizowane.",
|
||||
"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!",
|
||||
"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?",
|
||||
|
@@ -370,7 +370,7 @@
|
||||
"DialogUserProfileDeletionConfirmMessage": "Deseja deletar o perfil selecionado",
|
||||
"DialogControllerSettingsModifiedConfirmMessage": "As configurações de controle atuais foram atualizadas.",
|
||||
"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!",
|
||||
"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?",
|
||||
|
@@ -370,7 +370,7 @@
|
||||
"DialogUserProfileDeletionConfirmMessage": "Вы хотите удалить выбранный профиль",
|
||||
"DialogControllerSettingsModifiedConfirmMessage": "Текущие настройки контроллера обновлены.",
|
||||
"DialogControllerSettingsModifiedConfirmSubMessage": "Вы хотите сохранить?",
|
||||
"DialogDlcLoadNcaErrorMessage": "{0}. Файл с ошибкой: {1}",
|
||||
"DialogLoadNcaErrorMessage": "{0}. Файл с ошибкой: {1}",
|
||||
"DialogDlcNoDlcErrorMessage": "Указанный файл не содержит DLC для выбранной игры!",
|
||||
"DialogPerformanceCheckLoggingEnabledMessage": "У вас включено ведение журнала отладки, предназначенное только для разработчиков.",
|
||||
"DialogPerformanceCheckLoggingEnabledConfirmMessage": "Для оптимальной производительности рекомендуется отключить ведение журнала отладки. Вы хотите отключить ведение журнала отладки сейчас?",
|
||||
|
@@ -370,7 +370,7 @@
|
||||
"DialogUserProfileDeletionConfirmMessage": "Seçilen profili silmek istiyor musunuz",
|
||||
"DialogControllerSettingsModifiedConfirmMessage": "Güncel kontrolcü seçenekleri güncellendi.",
|
||||
"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!",
|
||||
"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?",
|
||||
|
@@ -370,7 +370,7 @@
|
||||
"DialogUserProfileDeletionConfirmMessage": "Ви хочете видалити вибраний профіль",
|
||||
"DialogControllerSettingsModifiedConfirmMessage": "Поточні налаштування контролера оновлено.",
|
||||
"DialogControllerSettingsModifiedConfirmSubMessage": "Ви хочете зберегти?",
|
||||
"DialogDlcLoadNcaErrorMessage": "{0}. Файл з помилкою: {1}",
|
||||
"DialogLoadNcaErrorMessage": "{0}. Файл з помилкою: {1}",
|
||||
"DialogDlcNoDlcErrorMessage": "Зазначений файл не містить DLC для вибраного заголовку!",
|
||||
"DialogPerformanceCheckLoggingEnabledMessage": "Ви увімкнули журнал налагодження, призначений лише для розробників.",
|
||||
"DialogPerformanceCheckLoggingEnabledConfirmMessage": "Для оптимальної продуктивності рекомендується вимкнути ведення журналу налагодження. Ви хочете вимкнути ведення журналу налагодження зараз?",
|
||||
|
@@ -370,7 +370,7 @@
|
||||
"DialogUserProfileDeletionConfirmMessage": "是否删除选择的账户",
|
||||
"DialogControllerSettingsModifiedConfirmMessage": "目前的输入预设已更新",
|
||||
"DialogControllerSettingsModifiedConfirmSubMessage": "要保存吗?",
|
||||
"DialogDlcLoadNcaErrorMessage": "{0}. 错误的文件: {1}",
|
||||
"DialogLoadNcaErrorMessage": "{0}. 错误的文件: {1}",
|
||||
"DialogDlcNoDlcErrorMessage": "选择的文件不包含所选游戏的 DLC!",
|
||||
"DialogPerformanceCheckLoggingEnabledMessage": "您启用了跟踪日志,仅供开发人员使用。",
|
||||
"DialogPerformanceCheckLoggingEnabledConfirmMessage": "为了获得最佳性能,建议禁用跟踪日志记录。您是否要立即禁用?",
|
||||
|
@@ -370,7 +370,7 @@
|
||||
"DialogUserProfileDeletionConfirmMessage": "是否刪除選擇的帳號",
|
||||
"DialogControllerSettingsModifiedConfirmMessage": "目前的輸入預設已更新",
|
||||
"DialogControllerSettingsModifiedConfirmSubMessage": "要儲存嗎?",
|
||||
"DialogDlcLoadNcaErrorMessage": "{0}. 錯誤的檔案: {1}",
|
||||
"DialogLoadNcaErrorMessage": "{0}. 錯誤的檔案: {1}",
|
||||
"DialogDlcNoDlcErrorMessage": "選擇的檔案不包含所選遊戲的 DLC!",
|
||||
"DialogPerformanceCheckLoggingEnabledMessage": "您啟用了跟蹤日誌,僅供開發人員使用。",
|
||||
"DialogPerformanceCheckLoggingEnabledConfirmMessage": "為了獲得最佳效能,建議停用跟蹤日誌記錄。您是否要立即停用?",
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Notifications;
|
||||
using Avalonia.Threading;
|
||||
using LibHac;
|
||||
using LibHac.Account;
|
||||
@@ -12,7 +13,6 @@ using LibHac.Tools.Fs;
|
||||
using LibHac.Tools.FsSystem;
|
||||
using LibHac.Tools.FsSystem.NcaUtils;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Controls;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.Common.Logging;
|
||||
@@ -44,14 +44,11 @@ namespace Ryujinx.Ava.Common
|
||||
_accountManager = accountManager;
|
||||
}
|
||||
|
||||
private static bool TryFindSaveData(string titleName, ulong titleId,
|
||||
BlitStruct<ApplicationControlProperty> controlHolder, in SaveDataFilter filter, out ulong saveDataId)
|
||||
private static bool TryFindSaveData(string titleName, ulong titleId, BlitStruct<ApplicationControlProperty> controlHolder, in SaveDataFilter filter, out ulong saveDataId)
|
||||
{
|
||||
saveDataId = default;
|
||||
|
||||
Result result = _horizonClient.Fs.FindSaveDataWithFilter(out SaveDataInfo saveDataInfo,
|
||||
SaveDataSpaceId.User, in filter);
|
||||
|
||||
Result result = _horizonClient.Fs.FindSaveDataWithFilter(out SaveDataInfo saveDataInfo, SaveDataSpaceId.User, in filter);
|
||||
if (ResultFs.TargetNotFound.Includes(result))
|
||||
{
|
||||
ref ApplicationControlProperty control = ref controlHolder.Value;
|
||||
@@ -68,20 +65,17 @@ namespace Ryujinx.Ava.Common
|
||||
control.UserAccountSaveDataSize = 0x4000;
|
||||
control.UserAccountSaveDataJournalSize = 0x4000;
|
||||
|
||||
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.");
|
||||
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.");
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (result.IsFailure())
|
||||
{
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(
|
||||
string.Format(LocaleManager.Instance[LocaleKeys.DialogMessageCreateSaveErrorMessage], result.ToStringWithName()));
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogMessageCreateSaveErrorMessage, result.ToStringWithName()));
|
||||
});
|
||||
|
||||
return false;
|
||||
@@ -98,16 +92,15 @@ namespace Ryujinx.Ava.Common
|
||||
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;
|
||||
}
|
||||
|
||||
public static void OpenSaveDir(in SaveDataFilter saveDataFilter, ulong titleId,
|
||||
BlitStruct<ApplicationControlProperty> controlData, string titleName)
|
||||
public static void OpenSaveDir(in SaveDataFilter saveDataFilter, ulong titleId, BlitStruct<ApplicationControlProperty> controlData, string titleName)
|
||||
{
|
||||
if (!TryFindSaveData(titleName, titleId, controlData, in saveDataFilter, out ulong saveDataId))
|
||||
{
|
||||
@@ -148,14 +141,15 @@ namespace Ryujinx.Ava.Common
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task ExtractSection(NcaSectionType ncaSectionType, string titleFilePath,
|
||||
int programIndex = 0)
|
||||
public static async Task ExtractSection(NcaSectionType ncaSectionType, string titleFilePath, string titleName, 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);
|
||||
|
||||
var cancellationToken = new CancellationTokenSource();
|
||||
string destination = await folderDialog.ShowAsync(_owner);
|
||||
var cancellationToken = new CancellationTokenSource();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(destination))
|
||||
{
|
||||
@@ -164,7 +158,7 @@ namespace Ryujinx.Ava.Common
|
||||
Dispatcher.UIThread.Post(async () =>
|
||||
{
|
||||
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],
|
||||
@@ -175,133 +169,122 @@ namespace Ryujinx.Ava.Common
|
||||
cancellationToken.Cancel();
|
||||
}
|
||||
});
|
||||
|
||||
using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read);
|
||||
|
||||
Thread.Sleep(1000);
|
||||
Nca mainNca = null;
|
||||
Nca patchNca = null;
|
||||
|
||||
using (FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read))
|
||||
string extension = Path.GetExtension(titleFilePath).ToLower();
|
||||
if (extension == ".nsp" || extension == ".pfs0" || extension == ".xci")
|
||||
{
|
||||
Nca mainNca = null;
|
||||
Nca patchNca = null;
|
||||
PartitionFileSystem pfs;
|
||||
|
||||
string extension = Path.GetExtension(titleFilePath).ToLower();
|
||||
|
||||
if (extension == ".nsp" || extension == ".pfs0" || extension == ".xci")
|
||||
if (extension == ".xci")
|
||||
{
|
||||
PartitionFileSystem pfs;
|
||||
pfs = new Xci(_virtualFileSystem.KeySet, file.AsStorage()).OpenPartition(XciPartitionType.Secure);
|
||||
}
|
||||
else
|
||||
{
|
||||
pfs = new PartitionFileSystem(file.AsStorage());
|
||||
}
|
||||
|
||||
if (extension == ".xci")
|
||||
foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
|
||||
{
|
||||
using var ncaFile = new UniqueRef<IFile>();
|
||||
|
||||
pfs.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||
|
||||
Nca nca = new(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage());
|
||||
if (nca.Header.ContentType == NcaContentType.Program)
|
||||
{
|
||||
Xci xci = new(_virtualFileSystem.KeySet, file.AsStorage());
|
||||
|
||||
pfs = xci.OpenPartition(XciPartitionType.Secure);
|
||||
}
|
||||
else
|
||||
{
|
||||
pfs = new PartitionFileSystem(file.AsStorage());
|
||||
}
|
||||
|
||||
foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
|
||||
{
|
||||
using var ncaFile = new UniqueRef<IFile>();
|
||||
|
||||
pfs.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||
|
||||
Nca nca = new(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage());
|
||||
|
||||
if (nca.Header.ContentType == NcaContentType.Program)
|
||||
int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
|
||||
if (nca.Header.GetFsHeader(dataIndex).IsPatchSection())
|
||||
{
|
||||
int dataIndex =
|
||||
Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
|
||||
if (nca.Header.GetFsHeader(dataIndex).IsPatchSection())
|
||||
{
|
||||
patchNca = nca;
|
||||
}
|
||||
else
|
||||
{
|
||||
mainNca = nca;
|
||||
}
|
||||
patchNca = nca;
|
||||
}
|
||||
else
|
||||
{
|
||||
mainNca = nca;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (extension == ".nca")
|
||||
{
|
||||
mainNca = new Nca(_virtualFileSystem.KeySet, file.AsStorage());
|
||||
}
|
||||
}
|
||||
else if (extension == ".nca")
|
||||
{
|
||||
mainNca = new Nca(_virtualFileSystem.KeySet, file.AsStorage());
|
||||
}
|
||||
|
||||
if (mainNca == null)
|
||||
if (mainNca == null)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, "Extraction failure. The main NCA was not present in the selected file");
|
||||
|
||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application,
|
||||
"Extraction failure. The main NCA was not present in the selected file");
|
||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionMainNcaNotFoundErrorMessage]);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
(Nca updatePatchNca, _) = ApplicationLoader.GetGameUpdateData(_virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), programIndex, out _);
|
||||
if (updatePatchNca != null)
|
||||
{
|
||||
patchNca = updatePatchNca;
|
||||
}
|
||||
|
||||
int index = Nca.GetSectionIndexFromType(ncaSectionType, mainNca.Header.ContentType);
|
||||
|
||||
try
|
||||
{
|
||||
IFileSystem ncaFileSystem = patchNca != null
|
||||
? mainNca.OpenFileSystemWithPatch(patchNca, index, IntegrityCheckLevel.ErrorOnInvalid)
|
||||
: mainNca.OpenFileSystem(index, IntegrityCheckLevel.ErrorOnInvalid);
|
||||
|
||||
FileSystemClient fsClient = _horizonClient.Fs;
|
||||
|
||||
string source = DateTime.Now.ToFileTime().ToString()[10..];
|
||||
string output = DateTime.Now.ToFileTime().ToString()[10..];
|
||||
|
||||
using var uniqueSourceFs = new UniqueRef<IFileSystem>(ncaFileSystem);
|
||||
using var uniqueOutputFs = new UniqueRef<IFileSystem>(new LocalFileSystem(destination));
|
||||
|
||||
fsClient.Register(source.ToU8Span(), ref uniqueSourceFs.Ref());
|
||||
fsClient.Register(output.ToU8Span(), ref uniqueOutputFs.Ref());
|
||||
|
||||
(Result? resultCode, bool canceled) = CopyDirectory(fsClient, $"{source}:/", $"{output}:/", cancellationToken.Token);
|
||||
|
||||
if (!canceled)
|
||||
{
|
||||
if (resultCode.Value.IsFailure())
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionMainNcaNotFoundErrorMessage]);
|
||||
});
|
||||
return;
|
||||
}
|
||||
Logger.Error?.Print(LogClass.Application, $"LibHac returned error code: {resultCode.Value.ErrorCode}");
|
||||
|
||||
(Nca updatePatchNca, _) = ApplicationLoader.GetGameUpdateData(_virtualFileSystem,
|
||||
mainNca.Header.TitleId.ToString("x16"), programIndex, out _);
|
||||
if (updatePatchNca != null)
|
||||
{
|
||||
patchNca = updatePatchNca;
|
||||
}
|
||||
|
||||
int index = Nca.GetSectionIndexFromType(ncaSectionType, mainNca.Header.ContentType);
|
||||
|
||||
try
|
||||
{
|
||||
IFileSystem ncaFileSystem = patchNca != null
|
||||
? mainNca.OpenFileSystemWithPatch(patchNca, index, IntegrityCheckLevel.ErrorOnInvalid)
|
||||
: mainNca.OpenFileSystem(index, IntegrityCheckLevel.ErrorOnInvalid);
|
||||
|
||||
FileSystemClient fsClient = _horizonClient.Fs;
|
||||
|
||||
string source = DateTime.Now.ToFileTime().ToString()[10..];
|
||||
string output = DateTime.Now.ToFileTime().ToString()[10..];
|
||||
|
||||
using var uniqueSourceFs = new UniqueRef<IFileSystem>(ncaFileSystem);
|
||||
using var uniqueOutputFs = new UniqueRef<IFileSystem>(new LocalFileSystem(destination));
|
||||
|
||||
fsClient.Register(source.ToU8Span(), ref uniqueSourceFs.Ref());
|
||||
fsClient.Register(output.ToU8Span(), ref uniqueOutputFs.Ref());
|
||||
|
||||
(Result? resultCode, bool canceled) = CopyDirectory(fsClient, $"{source}:/", $"{output}:/", cancellationToken.Token);
|
||||
|
||||
if (!canceled)
|
||||
{
|
||||
if (resultCode.Value.IsFailure())
|
||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application,
|
||||
$"LibHac returned error code: {resultCode.Value.ErrorCode}");
|
||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionCheckLogErrorMessage]);
|
||||
});
|
||||
}
|
||||
else if (resultCode.Value.IsSuccess())
|
||||
{
|
||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateInfoDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogNcaExtractionSuccessMessage],
|
||||
"",
|
||||
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
||||
"",
|
||||
LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle]);
|
||||
});
|
||||
}
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogNcaExtractionCheckLogErrorMessage]);
|
||||
});
|
||||
}
|
||||
|
||||
fsClient.Unmount(source.ToU8Span());
|
||||
fsClient.Unmount(output.ToU8Span());
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
else if (resultCode.Value.IsSuccess())
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(ex.Message);
|
||||
});
|
||||
NotificationHelper.Show(
|
||||
LocaleManager.Instance[LocaleKeys.DialogNcaExtractionTitle],
|
||||
$"{titleName}\n\n{LocaleManager.Instance[LocaleKeys.DialogNcaExtractionSuccessMessage]}",
|
||||
NotificationType.Information);
|
||||
}
|
||||
}
|
||||
|
||||
fsClient.Unmount(source.ToU8Span());
|
||||
fsClient.Unmount(output.ToU8Span());
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, $"{ex.Message}");
|
||||
|
||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(ex.Message);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
@@ -20,7 +20,7 @@ namespace Ryujinx.Ava.Common.Locale
|
||||
|
||||
ReflectionBindingExtension binding = new($"[{keyToUse}]")
|
||||
{
|
||||
Mode = BindingMode.OneWay,
|
||||
Mode = BindingMode.OneWay,
|
||||
Source = LocaleManager.Instance
|
||||
};
|
||||
|
||||
|
@@ -13,56 +13,87 @@ namespace Ryujinx.Ava.Common.Locale
|
||||
{
|
||||
private const string DefaultLanguageCode = "en_US";
|
||||
|
||||
private Dictionary<LocaleKeys, string> _localeStrings;
|
||||
private ConcurrentDictionary<LocaleKeys, object[]> _dynamicValues;
|
||||
private Dictionary<LocaleKeys, string> _localeStrings;
|
||||
private Dictionary<LocaleKeys, string> _localeDefaultStrings;
|
||||
private readonly ConcurrentDictionary<LocaleKeys, object[]> _dynamicValues;
|
||||
|
||||
public static LocaleManager Instance { get; } = new LocaleManager();
|
||||
public Dictionary<LocaleKeys, string> LocaleStrings { get => _localeStrings; set => _localeStrings = value; }
|
||||
|
||||
|
||||
public LocaleManager()
|
||||
{
|
||||
_localeStrings = new Dictionary<LocaleKeys, string>();
|
||||
_dynamicValues = new ConcurrentDictionary<LocaleKeys, object[]>();
|
||||
_localeStrings = new Dictionary<LocaleKeys, string>();
|
||||
_localeDefaultStrings = new Dictionary<LocaleKeys, string>();
|
||||
_dynamicValues = new ConcurrentDictionary<LocaleKeys, object[]>();
|
||||
|
||||
Load();
|
||||
}
|
||||
|
||||
public void Load()
|
||||
{
|
||||
// Load the system Language Code.
|
||||
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 (!string.IsNullOrEmpty(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();
|
||||
|
||||
if (localeLanguageCode != DefaultLanguageCode)
|
||||
{
|
||||
LoadLanguage(localeLanguageCode);
|
||||
}
|
||||
LoadLanguage(localeLanguageCode);
|
||||
}
|
||||
|
||||
public string this[LocaleKeys key]
|
||||
{
|
||||
get
|
||||
{
|
||||
// Check if the locale contains the key.
|
||||
if (_localeStrings.TryGetValue(key, out string value))
|
||||
{
|
||||
// Check if the localized string needs to be formatted.
|
||||
if (_dynamicValues.TryGetValue(key, out var dynamicValue))
|
||||
{
|
||||
return string.Format(value, dynamicValue);
|
||||
try
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
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;
|
||||
|
||||
OnPropertyChanged("Item");
|
||||
|
||||
return this[key];
|
||||
}
|
||||
|
||||
public void LoadDefaultLanguage()
|
||||
private void LoadDefaultLanguage()
|
||||
{
|
||||
LoadLanguage(DefaultLanguageCode);
|
||||
_localeDefaultStrings = LoadJsonLanguage();
|
||||
}
|
||||
|
||||
public void LoadLanguage(string languageCode)
|
||||
{
|
||||
string languageJson = EmbeddedResources.ReadAllText($"Ryujinx.Ava/Assets/Locales/{languageCode}.json");
|
||||
|
||||
if (languageJson == null)
|
||||
foreach (var item in LoadJsonLanguage(languageCode))
|
||||
{
|
||||
return;
|
||||
this[item.Key] = item.Value;
|
||||
}
|
||||
}
|
||||
|
||||
var strings = JsonHelper.Deserialize<Dictionary<string, string>>(languageJson);
|
||||
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);
|
||||
|
||||
foreach (var item in strings)
|
||||
{
|
||||
if (Enum.TryParse<LocaleKeys>(item.Key, out var key))
|
||||
{
|
||||
this[key] = item.Value;
|
||||
localeStrings[key] = item.Value;
|
||||
}
|
||||
}
|
||||
|
||||
if (Program.PreviewerDetached)
|
||||
{
|
||||
ConfigurationState.Instance.Ui.LanguageCode.Value = languageCode;
|
||||
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
||||
}
|
||||
return localeStrings;
|
||||
}
|
||||
}
|
||||
}
|
@@ -588,7 +588,7 @@ namespace Ryujinx.Modules
|
||||
}
|
||||
catch
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, string.Format(LocaleManager.Instance[LocaleKeys.UpdaterRenameFailed], file));
|
||||
Logger.Warning?.Print(LogClass.Application, LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.UpdaterRenameFailed, file));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -29,17 +29,12 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
|
||||
public bool DisplayMessageDialog(ControllerAppletUiArgs args)
|
||||
{
|
||||
string playerCount = 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,
|
||||
string.Join(", ", args.SupportedPlayers),
|
||||
args.IsDocked ? LocaleManager.Instance[LocaleKeys.DialogControllerAppletDockModeSet] : "");
|
||||
string message = LocaleManager.Instance.UpdateAndGetDynamicValue(
|
||||
args.PlayerCountMin == args.PlayerCountMax ? LocaleKeys.DialogControllerAppletMessage : LocaleKeys.DialogControllerAppletMessagePlayerRange,
|
||||
args.PlayerCountMin == args.PlayerCountMax ? args.PlayerCountMin.ToString() : $"{args.PlayerCountMin}-{args.PlayerCountMax}",
|
||||
args.SupportedStyles,
|
||||
string.Join(", ", args.SupportedPlayers),
|
||||
args.IsDocked ? LocaleManager.Instance[LocaleKeys.DialogControllerAppletDockModeSet] : "");
|
||||
|
||||
return DisplayMessageDialog(LocaleManager.Instance[LocaleKeys.DialogControllerAppletTitle], message);
|
||||
}
|
||||
@@ -92,7 +87,7 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogMessageDialogErrorExceptionMessage], ex));
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogMessageDialogErrorExceptionMessage, ex));
|
||||
|
||||
dialogCloseEvent.Set();
|
||||
}
|
||||
@@ -126,7 +121,8 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = true;
|
||||
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogSoftwareKeyboardErrorExceptionMessage], ex));
|
||||
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogSoftwareKeyboardErrorExceptionMessage, ex));
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -181,7 +177,8 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
catch (Exception ex)
|
||||
{
|
||||
dialogCloseEvent.Set();
|
||||
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogErrorAppletErrorExceptionMessage], ex));
|
||||
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogErrorAppletErrorExceptionMessage, ex));
|
||||
}
|
||||
});
|
||||
|
||||
|
@@ -139,14 +139,16 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
else if (_inputMin > 0 && _inputMax == int.MaxValue)
|
||||
{
|
||||
Error.IsVisible = true;
|
||||
Error.Text = string.Format(LocaleManager.Instance[LocaleKeys.SwkbdMinCharacters], _inputMin);
|
||||
|
||||
Error.Text = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SwkbdMinCharacters, _inputMin);
|
||||
|
||||
_checkLength = length => _inputMin <= length;
|
||||
}
|
||||
else
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
@@ -14,7 +14,7 @@
|
||||
Focusable="True">
|
||||
<UserControl.Resources>
|
||||
<helpers:BitmapArrayValueConverter x:Key="ByteImage" />
|
||||
<MenuFlyout x:Key="GameContextMenu" Opened="MenuBase_OnMenuOpened">
|
||||
<MenuFlyout x:Key="GameContextMenu">
|
||||
<MenuItem
|
||||
Command="{Binding ToggleFavorite}"
|
||||
Header="{locale:Locale GameListContextMenuToggleFavorite}"
|
||||
@@ -22,14 +22,17 @@
|
||||
<Separator />
|
||||
<MenuItem
|
||||
Command="{Binding OpenUserSaveDirectory}"
|
||||
IsEnabled="{Binding EnabledUserSaveDirectory}"
|
||||
Header="{locale:Locale GameListContextMenuOpenUserSaveDirectory}"
|
||||
ToolTip.Tip="{locale:Locale GameListContextMenuOpenUserSaveDirectoryToolTip}" />
|
||||
<MenuItem
|
||||
Command="{Binding OpenDeviceSaveDirectory}"
|
||||
IsEnabled="{Binding EnabledDeviceSaveDirectory}"
|
||||
Header="{locale:Locale GameListContextMenuOpenDeviceSaveDirectory}"
|
||||
ToolTip.Tip="{locale:Locale GameListContextMenuOpenDeviceSaveDirectoryToolTip}" />
|
||||
<MenuItem
|
||||
Command="{Binding OpenBcatSaveDirectory}"
|
||||
IsEnabled="{Binding EnabledBcatSaveDirectory}"
|
||||
Header="{locale:Locale GameListContextMenuOpenBcatSaveDirectory}"
|
||||
ToolTip.Tip="{locale:Locale GameListContextMenuOpenBcatSaveDirectoryToolTip}" />
|
||||
<Separator />
|
||||
|
@@ -1,9 +1,7 @@
|
||||
using Avalonia.Collections;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using LibHac.Common;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ui.App.Common;
|
||||
@@ -13,16 +11,25 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
{
|
||||
public partial class GameGridView : UserControl
|
||||
{
|
||||
private ApplicationData _selectedApplication;
|
||||
public static readonly RoutedEvent<ApplicationOpenedEventArgs> ApplicationOpenedEvent =
|
||||
RoutedEvent.Register<GameGridView, ApplicationOpenedEventArgs>(nameof(ApplicationOpened), RoutingStrategies.Bubble);
|
||||
|
||||
public event EventHandler<ApplicationOpenedEventArgs> ApplicationOpened
|
||||
{
|
||||
add { AddHandler(ApplicationOpenedEvent, value); }
|
||||
add { AddHandler(ApplicationOpenedEvent, value); }
|
||||
remove { RemoveHandler(ApplicationOpenedEvent, value); }
|
||||
}
|
||||
|
||||
public GameGridView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public void GameList_DoubleTapped(object sender, RoutedEventArgs args)
|
||||
{
|
||||
if (sender is ListBox listBox)
|
||||
@@ -38,46 +45,13 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
{
|
||||
if (sender is ListBox listBox)
|
||||
{
|
||||
_selectedApplication = listBox.SelectedItem as ApplicationData;
|
||||
|
||||
(DataContext as MainWindowViewModel).GridSelectedApplication = _selectedApplication;
|
||||
(DataContext as MainWindowViewModel).GridSelectedApplication = listBox.SelectedItem as ApplicationData;
|
||||
}
|
||||
}
|
||||
|
||||
public ApplicationData SelectedApplication => _selectedApplication;
|
||||
|
||||
public GameGridView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
private void SearchBox_OnKeyUp(object sender, KeyEventArgs e)
|
||||
{
|
||||
(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -13,7 +13,7 @@
|
||||
mc:Ignorable="d">
|
||||
<UserControl.Resources>
|
||||
<helpers:BitmapArrayValueConverter x:Key="ByteImage" />
|
||||
<MenuFlyout x:Key="GameContextMenu" Opened="MenuBase_OnMenuOpened">
|
||||
<MenuFlyout x:Key="GameContextMenu">
|
||||
<MenuItem
|
||||
Command="{Binding ToggleFavorite}"
|
||||
Header="{locale:Locale GameListContextMenuToggleFavorite}"
|
||||
@@ -21,14 +21,17 @@
|
||||
<Separator />
|
||||
<MenuItem
|
||||
Command="{Binding OpenUserSaveDirectory}"
|
||||
IsEnabled="{Binding EnabledUserSaveDirectory}"
|
||||
Header="{locale:Locale GameListContextMenuOpenUserSaveDirectory}"
|
||||
ToolTip.Tip="{locale:Locale GameListContextMenuOpenUserSaveDirectoryToolTip}" />
|
||||
<MenuItem
|
||||
Command="{Binding OpenDeviceSaveDirectory}"
|
||||
IsEnabled="{Binding EnabledDeviceSaveDirectory}"
|
||||
Header="{locale:Locale GameListContextMenuOpenDeviceSaveDirectory}"
|
||||
ToolTip.Tip="{locale:Locale GameListContextMenuOpenDeviceSaveDirectoryToolTip}" />
|
||||
<MenuItem
|
||||
Command="{Binding OpenBcatSaveDirectory}"
|
||||
IsEnabled="{Binding EnabledBcatSaveDirectory}"
|
||||
Header="{locale:Locale GameListContextMenuOpenBcatSaveDirectory}"
|
||||
ToolTip.Tip="{locale:Locale GameListContextMenuOpenBcatSaveDirectoryToolTip}" />
|
||||
<Separator />
|
||||
|
@@ -1,9 +1,7 @@
|
||||
using Avalonia.Collections;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using LibHac.Common;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ui.App.Common;
|
||||
@@ -13,16 +11,25 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
{
|
||||
public partial class GameListView : UserControl
|
||||
{
|
||||
private ApplicationData _selectedApplication;
|
||||
public static readonly RoutedEvent<ApplicationOpenedEventArgs> ApplicationOpenedEvent =
|
||||
RoutedEvent.Register<GameGridView, ApplicationOpenedEventArgs>(nameof(ApplicationOpened), RoutingStrategies.Bubble);
|
||||
|
||||
public event EventHandler<ApplicationOpenedEventArgs> ApplicationOpened
|
||||
{
|
||||
add { AddHandler(ApplicationOpenedEvent, value); }
|
||||
add { AddHandler(ApplicationOpenedEvent, value); }
|
||||
remove { RemoveHandler(ApplicationOpenedEvent, value); }
|
||||
}
|
||||
|
||||
public GameListView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public void GameList_DoubleTapped(object sender, RoutedEventArgs args)
|
||||
{
|
||||
if (sender is ListBox listBox)
|
||||
@@ -38,46 +45,13 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
{
|
||||
if (sender is ListBox listBox)
|
||||
{
|
||||
_selectedApplication = listBox.SelectedItem as ApplicationData;
|
||||
|
||||
(DataContext as MainWindowViewModel).ListSelectedApplication = _selectedApplication;
|
||||
(DataContext as MainWindowViewModel).ListSelectedApplication = listBox.SelectedItem as ApplicationData;
|
||||
}
|
||||
}
|
||||
|
||||
public ApplicationData SelectedApplication => _selectedApplication;
|
||||
|
||||
public GameListView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
private void SearchBox_OnKeyUp(object sender, KeyEventArgs e)
|
||||
{
|
||||
(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
65
Ryujinx.Ava/UI/Helpers/NotificationHelper.cs
Normal file
65
Ryujinx.Ava/UI/Helpers/NotificationHelper.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -76,11 +76,11 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
string setupButtonLabel = isInSetupGuide ? LocaleManager.Instance[LocaleKeys.OpenSetupGuideMessage] : "";
|
||||
|
||||
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
|
||||
? LocaleManager.Instance[LocaleKeys.DialogUserErrorDialogInfoMessage]
|
||||
: ""), setupButtonLabel, LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
||||
string.Format(LocaleManager.Instance[LocaleKeys.DialogUserErrorDialogTitle], errorCode));
|
||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogUserErrorDialogTitle, errorCode));
|
||||
|
||||
if (result == UserResult.Ok)
|
||||
{
|
||||
|
@@ -8,7 +8,7 @@ namespace Ryujinx.Ava.UI.Models
|
||||
public ApplicationControlProperty Control { 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)
|
||||
{
|
||||
|
@@ -81,10 +81,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public string Developers
|
||||
{
|
||||
get => string.Format(LocaleManager.Instance[LocaleKeys.AboutPageDeveloperListMore], "gdkchan, Ac_K, marysaka, rip in peri peri, LDj3SNuD, emmaus, Thealexbarney, GoffyDude, TSRBerry, IsaacMarovitz");
|
||||
}
|
||||
public string Developers => LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.AboutPageDeveloperListMore, "gdkchan, Ac_K, marysaka, rip in peri peri, LDj3SNuD, emmaus, Thealexbarney, GoffyDude, TSRBerry, IsaacMarovitz");
|
||||
|
||||
public AboutWindowViewModel()
|
||||
{
|
||||
|
@@ -3,6 +3,8 @@ using Avalonia.Controls;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Svg.Skia;
|
||||
using Avalonia.Threading;
|
||||
using LibHac.Bcat;
|
||||
using LibHac.Tools.Fs;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.Input;
|
||||
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.");
|
||||
|
||||
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogProfileInvalidProfileErrorMessage], ProfileName));
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogProfileInvalidProfileErrorMessage, ProfileName));
|
||||
|
||||
return;
|
||||
}
|
||||
|
@@ -5,8 +5,10 @@ using Avalonia.Media;
|
||||
using Avalonia.Threading;
|
||||
using DynamicData;
|
||||
using DynamicData.Binding;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.FsSystem;
|
||||
using LibHac.Tools.Fs;
|
||||
using Ryujinx.Ava.Common;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
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
|
||||
{
|
||||
get => _loadHeading;
|
||||
@@ -733,19 +741,14 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (ConfigurationState.Instance.Ui.GridSize)
|
||||
return ConfigurationState.Instance.Ui.GridSize.Value switch
|
||||
{
|
||||
case 1:
|
||||
return 78;
|
||||
case 2:
|
||||
return 100;
|
||||
case 3:
|
||||
return 120;
|
||||
case 4:
|
||||
return 140;
|
||||
default:
|
||||
return 16;
|
||||
}
|
||||
1 => 78,
|
||||
2 => 100,
|
||||
3 => 120,
|
||||
4 => 140,
|
||||
_ => 16,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -753,19 +756,14 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (ConfigurationState.Instance.Ui.GridSize)
|
||||
return ConfigurationState.Instance.Ui.GridSize.Value switch
|
||||
{
|
||||
case 1:
|
||||
return 120;
|
||||
case 2:
|
||||
return ShowNames ? 210 : 150;
|
||||
case 3:
|
||||
return ShowNames ? 240 : 180;
|
||||
case 4:
|
||||
return ShowNames ? 280 : 220;
|
||||
default:
|
||||
return 16;
|
||||
}
|
||||
1 => 120,
|
||||
2 => ShowNames ? 210 : 150,
|
||||
3 => ShowNames ? 240 : 180,
|
||||
4 => ShowNames ? 280 : 220,
|
||||
_ => 16,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -947,20 +945,18 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
if (firmwareVersion == null)
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareNotFoundErrorMessage], filename));
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogFirmwareInstallerFirmwareNotFoundErrorMessage, filename));
|
||||
|
||||
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();
|
||||
|
||||
string dialogMessage = string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallerFirmwareInstallMessage], firmwareVersion.VersionString);
|
||||
|
||||
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];
|
||||
@@ -993,7 +989,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
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]);
|
||||
|
||||
@@ -1063,7 +1059,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
IsLoadingIndeterminate = false;
|
||||
break;
|
||||
case LoadState.Loaded:
|
||||
LoadHeading = string.Format(LocaleManager.Instance[LocaleKeys.LoadingHeading], TitleName);
|
||||
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, TitleName);
|
||||
IsLoadingIndeterminate = true;
|
||||
CacheLoadStatus = "";
|
||||
break;
|
||||
@@ -1079,7 +1075,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
IsLoadingIndeterminate = false;
|
||||
break;
|
||||
case ShaderCacheLoadingState.Loaded:
|
||||
LoadHeading = string.Format(LocaleManager.Instance[LocaleKeys.LoadingHeading], TitleName);
|
||||
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, TitleName);
|
||||
IsLoadingIndeterminate = true;
|
||||
CacheLoadStatus = "";
|
||||
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()
|
||||
{
|
||||
var selection = SelectedApplication;
|
||||
if (selection != null)
|
||||
if (SelectedApplication != null)
|
||||
{
|
||||
await ApplicationHelper.ExtractSection(NcaSectionType.Logo, selection.Path);
|
||||
await ApplicationHelper.ExtractSection(NcaSectionType.Logo, SelectedApplication.Path, SelectedApplication.TitleName);
|
||||
}
|
||||
}
|
||||
|
||||
private async void ExtractRomFs()
|
||||
{
|
||||
var selection = SelectedApplication;
|
||||
if (selection != null)
|
||||
if (SelectedApplication != null)
|
||||
{
|
||||
await ApplicationHelper.ExtractSection(NcaSectionType.Data, selection.Path);
|
||||
await ApplicationHelper.ExtractSection(NcaSectionType.Data, SelectedApplication.Path, SelectedApplication.TitleName);
|
||||
}
|
||||
}
|
||||
|
||||
private async void ExtractExeFs()
|
||||
{
|
||||
var selection = SelectedApplication;
|
||||
if (selection != null)
|
||||
if (SelectedApplication != 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)obj);
|
||||
LocaleManager.Instance.LoadLanguage((string)languageCode);
|
||||
|
||||
if (Program.PreviewerDetached)
|
||||
{
|
||||
ConfigurationState.Instance.Ui.LanguageCode.Value = (string)languageCode;
|
||||
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
||||
}
|
||||
}
|
||||
|
||||
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?).
|
||||
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.InputDialogNo],
|
||||
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
|
||||
@@ -1401,7 +1394,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
}
|
||||
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?).
|
||||
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.InputDialogNo],
|
||||
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
|
||||
@@ -1463,7 +1456,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
}
|
||||
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)
|
||||
{
|
||||
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()
|
||||
{
|
||||
ApplicationData selection = SelectedApplication;
|
||||
@@ -1550,37 +1493,45 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
public void OpenUserSaveDirectory()
|
||||
{
|
||||
ApplicationData selection = SelectedApplication;
|
||||
if (selection != null)
|
||||
OpenSaveDirectory(SaveDataType.Account, userId: new UserId((ulong)AccountManager.LastOpenedUser.UserId.High, (ulong)AccountManager.LastOpenedUser.UserId.Low));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
Task.Run(() =>
|
||||
if (!ulong.TryParse(SelectedApplication.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber))
|
||||
{
|
||||
if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber))
|
||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
async void Action()
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogRyujinxErrorMessage], LocaleManager.Instance[LocaleKeys.DialogInvalidTitleIdErrorMessage]);
|
||||
}
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogRyujinxErrorMessage], LocaleManager.Instance[LocaleKeys.DialogInvalidTitleIdErrorMessage]);
|
||||
});
|
||||
|
||||
Dispatcher.UIThread.Post(Action);
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
var saveDataFilter = SaveDataFilter.Make(titleIdNumber, saveDataType, userId, saveDataId: default, index: default);
|
||||
|
||||
UserId userId = new((ulong)AccountManager.LastOpenedUser.UserId.High, (ulong)AccountManager.LastOpenedUser.UserId.Low);
|
||||
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()
|
||||
{
|
||||
ApplicationData selection = SelectedApplication;
|
||||
if (selection != null)
|
||||
if (SelectedApplication != null)
|
||||
{
|
||||
string modsBasePath = VirtualFileSystem.ModLoader.GetModsBasePath();
|
||||
string titleModsPath = VirtualFileSystem.ModLoader.GetTitleDir(modsBasePath, selection.TitleId);
|
||||
string titleModsPath = VirtualFileSystem.ModLoader.GetTitleDir(modsBasePath, SelectedApplication.TitleId);
|
||||
|
||||
OpenHelper.OpenFolder(titleModsPath);
|
||||
}
|
||||
@@ -1588,12 +1539,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
public void OpenSdModsDirectory()
|
||||
{
|
||||
ApplicationData selection = SelectedApplication;
|
||||
|
||||
if (selection != null)
|
||||
if (SelectedApplication != null)
|
||||
{
|
||||
string sdModsBasePath = VirtualFileSystem.ModLoader.GetSdModsBasePath();
|
||||
string titleModsPath = VirtualFileSystem.ModLoader.GetTitleDir(sdModsBasePath, selection.TitleId);
|
||||
string titleModsPath = VirtualFileSystem.ModLoader.GetTitleDir(sdModsBasePath, SelectedApplication.TitleId);
|
||||
|
||||
OpenHelper.OpenFolder(titleModsPath);
|
||||
}
|
||||
@@ -1609,25 +1558,17 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
public async void OpenDownloadableContentManager()
|
||||
{
|
||||
ApplicationData selection = SelectedApplication;
|
||||
if (selection != null)
|
||||
if (SelectedApplication != null)
|
||||
{
|
||||
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
await new DownloadableContentManagerWindow(VirtualFileSystem, ulong.Parse(selection.TitleId, NumberStyles.HexNumber), selection.TitleName).ShowDialog(desktop.MainWindow);
|
||||
}
|
||||
await new DownloadableContentManagerWindow(VirtualFileSystem, ulong.Parse(SelectedApplication.TitleId, NumberStyles.HexNumber), SelectedApplication.TitleName).ShowDialog(TopLevel as Window);
|
||||
}
|
||||
}
|
||||
|
||||
public async void OpenCheatManager()
|
||||
{
|
||||
ApplicationData selection = SelectedApplication;
|
||||
if (selection != null)
|
||||
if (SelectedApplication != null)
|
||||
{
|
||||
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
await new CheatWindow(VirtualFileSystem, selection.TitleId, selection.TitleName).ShowDialog(desktop.MainWindow);
|
||||
}
|
||||
await new CheatWindow(VirtualFileSystem, SelectedApplication.TitleId, SelectedApplication.TitleName).ShowDialog(TopLevel as Window);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1641,7 +1582,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
StatusBarProgressMaximum = 0;
|
||||
StatusBarProgressValue = 0;
|
||||
|
||||
LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0);
|
||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0);
|
||||
});
|
||||
|
||||
ReloadGameList?.Invoke();
|
||||
@@ -1755,8 +1696,14 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -1807,14 +1754,13 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
if (version != null)
|
||||
{
|
||||
LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarSystemVersion,
|
||||
version.VersionString);
|
||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarSystemVersion, version.VersionString);
|
||||
|
||||
hasApplet = version.Major > 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarSystemVersion, "0.0");
|
||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarSystemVersion, "0.0");
|
||||
}
|
||||
|
||||
IsAppletMenuActive = hasApplet;
|
||||
|
@@ -181,7 +181,7 @@ public class TitleUpdateViewModel : BaseModel
|
||||
{
|
||||
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));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -17,8 +17,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
private ObservableCollection<SaveModel> _views = new();
|
||||
private AccountManager _accountManager;
|
||||
|
||||
public string SaveManagerHeading =>
|
||||
string.Format(LocaleManager.Instance[LocaleKeys.SaveManagerHeading], _accountManager.LastOpenedUser.Name, _accountManager.LastOpenedUser.UserId);
|
||||
public string SaveManagerHeading => LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SaveManagerHeading, _accountManager.LastOpenedUser.Name, _accountManager.LastOpenedUser.UserId);
|
||||
|
||||
public int SortIndex
|
||||
{
|
||||
|
@@ -31,7 +31,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
{
|
||||
LoadedCheats = new AvaloniaList<CheatsList>();
|
||||
|
||||
Heading = string.Format(LocaleManager.Instance[LocaleKeys.CheatWindowHeading], titleName, titleId.ToUpper());
|
||||
Heading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.CheatWindowHeading, titleName, titleId.ToUpper());
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
|
@@ -86,7 +86,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
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()
|
||||
@@ -133,7 +133,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
{
|
||||
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));
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -43,6 +43,7 @@
|
||||
<StackPanel Grid.Row="0" IsVisible="False">
|
||||
<helpers:HotKeyControl Name="FullscreenHotKey" 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="ExitHotKey" Command="{ReflectionBinding ExitCurrentState}" />
|
||||
</StackPanel>
|
||||
|
@@ -104,6 +104,8 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
ApplicationLibrary.ApplicationCountUpdated += ApplicationLibrary_ApplicationCountUpdated;
|
||||
ApplicationLibrary.ApplicationAdded += ApplicationLibrary_ApplicationAdded;
|
||||
ViewModel.ReloadGameList += ReloadGameList;
|
||||
|
||||
NotificationHelper.SetNotificationManager(this);
|
||||
}
|
||||
|
||||
private void IsActiveChanged(bool obj)
|
||||
@@ -146,7 +148,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
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(() =>
|
||||
{
|
||||
@@ -327,10 +329,11 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
public void LoadHotKeys()
|
||||
{
|
||||
HotKeyManager.SetHotKey(FullscreenHotKey, new KeyGesture(Key.Enter, KeyModifiers.Alt));
|
||||
HotKeyManager.SetHotKey(FullscreenHotKey2, new KeyGesture(Key.F11));
|
||||
HotKeyManager.SetHotKey(DockToggleHotKey, new KeyGesture(Key.F9));
|
||||
HotKeyManager.SetHotKey(ExitHotKey, new KeyGesture(Key.Escape));
|
||||
HotKeyManager.SetHotKey(FullscreenHotKey, new KeyGesture(Key.Enter, KeyModifiers.Alt));
|
||||
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(ExitHotKey, new KeyGesture(Key.Escape));
|
||||
}
|
||||
|
||||
private void VolumeStatus_CheckedChanged(object sender, SplitButtonClickEventArgs e)
|
||||
@@ -415,7 +418,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
ViewModel.StatusBarProgressMaximum = 0;
|
||||
ViewModel.StatusBarProgressValue = 0;
|
||||
|
||||
LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0);
|
||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0);
|
||||
});
|
||||
|
||||
ReloadGameList();
|
||||
|
@@ -42,7 +42,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
SecondaryButtonText = "",
|
||||
CloseButtonText = "",
|
||||
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>());
|
||||
|
@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
private const ushort FileFormatVersionMajor = 1;
|
||||
private const ushort FileFormatVersionMinor = 2;
|
||||
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 SharedDataFileName = "shared.data";
|
||||
|
@@ -91,6 +91,7 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
||||
Neu = 13,
|
||||
Geu = 14,
|
||||
T = 15,
|
||||
Off = 16,
|
||||
Lo = 17,
|
||||
Sff = 18,
|
||||
Ls = 19,
|
||||
|
@@ -54,13 +54,6 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||
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)
|
||||
{
|
||||
InstCs2r op = context.GetOp<InstCs2r>();
|
||||
|
@@ -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)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@@ -257,7 +257,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Support CC here aswell (condition).
|
||||
// TODO: Support CC here as well (condition).
|
||||
foreach (SyncTarget target in targets.Values)
|
||||
{
|
||||
PushOpInfo pushOpInfo = target.PushOpInfo;
|
||||
@@ -318,21 +318,5 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -11,23 +11,6 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||
{
|
||||
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)
|
||||
{
|
||||
InstIcmpR op = context.GetOp<InstIcmpR>();
|
||||
|
@@ -1,5 +1,7 @@
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using Ryujinx.Common.Logging;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@@ -43,16 +45,25 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
|
||||
|
||||
if (File.Exists(_profilesJsonPath))
|
||||
{
|
||||
ProfilesJson profilesJson = JsonHelper.DeserializeFromFile<ProfilesJson>(_profilesJsonPath);
|
||||
|
||||
foreach (var profile in profilesJson.Profiles)
|
||||
try
|
||||
{
|
||||
UserProfile addedProfile = new UserProfile(new UserId(profile.UserId), profile.Name, profile.Image, profile.LastModifiedTimestamp);
|
||||
ProfilesJson profilesJson = JsonHelper.DeserializeFromFile<ProfilesJson>(_profilesJsonPath);
|
||||
|
||||
profiles.AddOrUpdate(profile.UserId, addedProfile, (key, old) => addedProfile);
|
||||
foreach (var profile in profilesJson.Profiles)
|
||||
{
|
||||
UserProfile addedProfile = new UserProfile(new UserId(profile.UserId), profile.Name, profile.Image, profile.LastModifiedTimestamp);
|
||||
|
||||
profiles.AddOrUpdate(profile.UserId, addedProfile, (key, old) => addedProfile);
|
||||
}
|
||||
|
||||
LastOpened = new UserId(profilesJson.LastOpened);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, $"Failed to parse {_profilesJsonPath}: {e.Message} Loading default profile!");
|
||||
|
||||
LastOpened = new UserId(profilesJson.LastOpened);
|
||||
LastOpened = AccountManager.DefaultUserId;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
Reference in New Issue
Block a user