diff --git a/src/Ryujinx.Ava/Ryujinx.Ava.csproj b/src/Ryujinx.Ava/Ryujinx.Ava.csproj index 7509798b..2b6432e9 100644 --- a/src/Ryujinx.Ava/Ryujinx.Ava.csproj +++ b/src/Ryujinx.Ava/Ryujinx.Ava.csproj @@ -103,50 +103,6 @@ MSBuild:Compile - - - App.axaml - Code - - - MainWindow.axaml - Code - - - AboutWindow.axaml - Code - - - ProfileWindow.axaml - Code - - - ProfileWindow.axaml - Code - - - InputDialog.axaml - Code - - - ContentDialogOverlay.xaml - - - GameListView.axaml - Code - - - UserEditor.axaml - Code - - - UserRecoverer.axaml - Code - - - UserSelector.axaml - Code - diff --git a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml new file mode 100644 index 00000000..1750e800 --- /dev/null +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs new file mode 100644 index 00000000..dc0dee2a --- /dev/null +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationContextMenu.axaml.cs @@ -0,0 +1,320 @@ +using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.Markup.Xaml; +using Avalonia.Threading; +using LibHac.Fs; +using LibHac.Tools.FsSystem.NcaUtils; +using Ryujinx.Ava.Common; +using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.UI.Helpers; +using Ryujinx.Ava.UI.ViewModels; +using Ryujinx.Ava.UI.Windows; +using Ryujinx.Common.Configuration; +using Ryujinx.Ui.Common.Helper; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using Path = System.IO.Path; +using UserId = LibHac.Fs.UserId; + +namespace Ryujinx.Ava.UI.Controls +{ + public class ApplicationContextMenu : MenuFlyout + { + public ApplicationContextMenu() + { + InitializeComponent(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + + public void ToggleFavorite_Click(object sender, RoutedEventArgs args) + { + var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; + + if (viewModel.SelectedApplication != null) + { + viewModel.SelectedApplication.Favorite = !viewModel.SelectedApplication.Favorite; + + viewModel.ApplicationLibrary.LoadAndSaveMetaData(viewModel.SelectedApplication.TitleId, appMetadata => + { + appMetadata.Favorite = viewModel.SelectedApplication.Favorite; + }); + + viewModel.RefreshView(); + } + } + + public void OpenUserSaveDirectory_Click(object sender, RoutedEventArgs args) + { + var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; + + OpenSaveDirectory(viewModel, SaveDataType.Account, userId: new UserId((ulong)viewModel.AccountManager.LastOpenedUser.UserId.High, (ulong)viewModel.AccountManager.LastOpenedUser.UserId.Low)); + } + + public void OpenDeviceSaveDirectory_Click(object sender, RoutedEventArgs args) + { + var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; + + OpenSaveDirectory(viewModel, SaveDataType.Device, userId: default); + } + + public void OpenBcatSaveDirectory_Click(object sender, RoutedEventArgs args) + { + var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; + + OpenSaveDirectory(viewModel, SaveDataType.Bcat, userId: default); + } + + private void OpenSaveDirectory(MainWindowViewModel viewModel, SaveDataType saveDataType, UserId userId) + { + if (viewModel.SelectedApplication != null) + { + if (!ulong.TryParse(viewModel.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; + } + + var saveDataFilter = SaveDataFilter.Make(titleIdNumber, saveDataType, userId, saveDataId: default, index: default); + + ApplicationHelper.OpenSaveDir(in saveDataFilter, titleIdNumber, viewModel.SelectedApplication.ControlHolder, viewModel.SelectedApplication.TitleName); + } + } + + public async void OpenTitleUpdateManager_Click(object sender, RoutedEventArgs args) + { + var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; + + if (viewModel.SelectedApplication != null) + { + await TitleUpdateWindow.Show(viewModel.VirtualFileSystem, ulong.Parse(viewModel.SelectedApplication.TitleId, NumberStyles.HexNumber), viewModel.SelectedApplication.TitleName); + } + } + + public async void OpenDownloadableContentManager_Click(object sender, RoutedEventArgs args) + { + var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; + + if (viewModel.SelectedApplication != null) + { + await DownloadableContentManagerWindow.Show(viewModel.VirtualFileSystem, ulong.Parse(viewModel.SelectedApplication.TitleId, NumberStyles.HexNumber), viewModel.SelectedApplication.TitleName); + } + } + + public async void OpenCheatManager_Click(object sender, RoutedEventArgs args) + { + var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; + + if (viewModel.SelectedApplication != null) + { + await new CheatWindow(viewModel.VirtualFileSystem, viewModel.SelectedApplication.TitleId, viewModel.SelectedApplication.TitleName).ShowDialog(viewModel.TopLevel as Window); + } + } + + public void OpenModsDirectory_Click(object sender, RoutedEventArgs args) + { + var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; + + if (viewModel.SelectedApplication != null) + { + string modsBasePath = viewModel.VirtualFileSystem.ModLoader.GetModsBasePath(); + string titleModsPath = viewModel.VirtualFileSystem.ModLoader.GetTitleDir(modsBasePath, viewModel.SelectedApplication.TitleId); + + OpenHelper.OpenFolder(titleModsPath); + } + } + + public void OpenSdModsDirectory_Click(object sender, RoutedEventArgs args) + { + var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; + + if (viewModel.SelectedApplication != null) + { + string sdModsBasePath = viewModel.VirtualFileSystem.ModLoader.GetSdModsBasePath(); + string titleModsPath = viewModel.VirtualFileSystem.ModLoader.GetTitleDir(sdModsBasePath, viewModel.SelectedApplication.TitleId); + + OpenHelper.OpenFolder(titleModsPath); + } + } + + public async void PurgePtcCache_Click(object sender, RoutedEventArgs args) + { + var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; + + if (viewModel.SelectedApplication != null) + { + UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning], + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionMessage, viewModel.SelectedApplication.TitleName), + LocaleManager.Instance[LocaleKeys.InputDialogYes], + LocaleManager.Instance[LocaleKeys.InputDialogNo], + LocaleManager.Instance[LocaleKeys.RyujinxConfirm]); + + if (result == UserResult.Yes) + { + DirectoryInfo mainDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.TitleId, "cache", "cpu", "0")); + DirectoryInfo backupDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.TitleId, "cache", "cpu", "1")); + + List cacheFiles = new(); + + if (mainDir.Exists) + { + cacheFiles.AddRange(mainDir.EnumerateFiles("*.cache")); + } + + if (backupDir.Exists) + { + cacheFiles.AddRange(backupDir.EnumerateFiles("*.cache")); + } + + if (cacheFiles.Count > 0) + { + foreach (FileInfo file in cacheFiles) + { + try + { + file.Delete(); + } + catch (Exception ex) + { + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionErrorMessage, file.Name, ex)); + } + } + } + } + } + } + + public async void PurgeShaderCache_Click(object sender, RoutedEventArgs args) + { + var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; + + if (viewModel.SelectedApplication != null) + { + UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning], + LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogShaderDeletionMessage, viewModel.SelectedApplication.TitleName), + LocaleManager.Instance[LocaleKeys.InputDialogYes], + LocaleManager.Instance[LocaleKeys.InputDialogNo], + LocaleManager.Instance[LocaleKeys.RyujinxConfirm]); + + if (result == UserResult.Yes) + { + DirectoryInfo shaderCacheDir = new(Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.TitleId, "cache", "shader")); + + List oldCacheDirectories = new(); + List newCacheFiles = new(); + + if (shaderCacheDir.Exists) + { + oldCacheDirectories.AddRange(shaderCacheDir.EnumerateDirectories("*")); + newCacheFiles.AddRange(shaderCacheDir.GetFiles("*.toc")); + newCacheFiles.AddRange(shaderCacheDir.GetFiles("*.data")); + } + + if ((oldCacheDirectories.Count > 0 || newCacheFiles.Count > 0)) + { + foreach (DirectoryInfo directory in oldCacheDirectories) + { + try + { + directory.Delete(true); + } + catch (Exception ex) + { + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionErrorMessage, directory.Name, ex)); + } + } + + foreach (FileInfo file in newCacheFiles) + { + try + { + file.Delete(); + } + catch (Exception ex) + { + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.ShaderCachePurgeError, file.Name, ex)); + } + } + } + } + } + } + + public void OpenPtcDirectory_Click(object sender, RoutedEventArgs args) + { + var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; + + if (viewModel.SelectedApplication != null) + { + string ptcDir = Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.TitleId, "cache", "cpu"); + string mainDir = Path.Combine(ptcDir, "0"); + string backupDir = Path.Combine(ptcDir, "1"); + + if (!Directory.Exists(ptcDir)) + { + Directory.CreateDirectory(ptcDir); + Directory.CreateDirectory(mainDir); + Directory.CreateDirectory(backupDir); + } + + OpenHelper.OpenFolder(ptcDir); + } + } + + public void OpenShaderCacheDirectory_Click(object sender, RoutedEventArgs args) + { + var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; + + if (viewModel.SelectedApplication != null) + { + string shaderCacheDir = Path.Combine(AppDataManager.GamesDirPath, viewModel.SelectedApplication.TitleId, "cache", "shader"); + + if (!Directory.Exists(shaderCacheDir)) + { + Directory.CreateDirectory(shaderCacheDir); + } + + OpenHelper.OpenFolder(shaderCacheDir); + } + } + + public async void ExtractApplicationLogo_Click(object sender, RoutedEventArgs args) + { + var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; + + if (viewModel.SelectedApplication != null) + { + await ApplicationHelper.ExtractSection(NcaSectionType.Logo, viewModel.SelectedApplication.Path, viewModel.SelectedApplication.TitleName); + } + } + + public async void ExtractApplicationRomFs_Click(object sender, RoutedEventArgs args) + { + var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; + + if (viewModel.SelectedApplication != null) + { + await ApplicationHelper.ExtractSection(NcaSectionType.Data, viewModel.SelectedApplication.Path, viewModel.SelectedApplication.TitleName); + } + } + + public async void ExtractApplicationExeFs_Click(object sender, RoutedEventArgs args) + { + var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; + + if (viewModel.SelectedApplication != null) + { + await ApplicationHelper.ExtractSection(NcaSectionType.Code, viewModel.SelectedApplication.Path, viewModel.SelectedApplication.TitleName); + } + } + } +} diff --git a/src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml b/src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml new file mode 100644 index 00000000..f7a120b1 --- /dev/null +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Ryujinx.Ava/UI/Controls/GameGridView.axaml.cs b/src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml.cs similarity index 87% rename from src/Ryujinx.Ava/UI/Controls/GameGridView.axaml.cs rename to src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml.cs index aa76b7c9..c30c75b3 100644 --- a/src/Ryujinx.Ava/UI/Controls/GameGridView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationGridView.axaml.cs @@ -9,10 +9,10 @@ using System; namespace Ryujinx.Ava.UI.Controls { - public partial class GameGridView : UserControl + public partial class ApplicationGridView : UserControl { public static readonly RoutedEvent ApplicationOpenedEvent = - RoutedEvent.Register(nameof(ApplicationOpened), RoutingStrategies.Bubble); + RoutedEvent.Register(nameof(ApplicationOpened), RoutingStrategies.Bubble); public event EventHandler ApplicationOpened { @@ -20,7 +20,7 @@ namespace Ryujinx.Ava.UI.Controls remove { RemoveHandler(ApplicationOpenedEvent, value); } } - public GameGridView() + public ApplicationGridView() { InitializeComponent(); } @@ -49,7 +49,7 @@ namespace Ryujinx.Ava.UI.Controls } } - private void SearchBox_OnKeyUp(object sender, KeyEventArgs e) + private void SearchBox_OnKeyUp(object sender, KeyEventArgs args) { (DataContext as MainWindowViewModel).SearchText = (sender as TextBox).Text; } diff --git a/src/Ryujinx.Ava/UI/Controls/GameListView.axaml b/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml similarity index 62% rename from src/Ryujinx.Ava/UI/Controls/GameListView.axaml rename to src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml index c13eaae8..fa8ebf62 100644 --- a/src/Ryujinx.Ava/UI/Controls/GameListView.axaml +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml @@ -1,10 +1,10 @@ - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -100,7 +25,7 @@ Padding="8" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" - ContextFlyout="{StaticResource GameContextMenu}" + ContextFlyout="{StaticResource ApplicationContextMenu}" DoubleTapped="GameList_DoubleTapped" Items="{Binding AppsObservableList}" SelectionChanged="GameList_SelectionChanged"> diff --git a/src/Ryujinx.Ava/UI/Controls/GameListView.axaml.cs b/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml.cs similarity index 87% rename from src/Ryujinx.Ava/UI/Controls/GameListView.axaml.cs rename to src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml.cs index a6449709..1a07c425 100644 --- a/src/Ryujinx.Ava/UI/Controls/GameListView.axaml.cs +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml.cs @@ -9,10 +9,10 @@ using System; namespace Ryujinx.Ava.UI.Controls { - public partial class GameListView : UserControl + public partial class ApplicationListView : UserControl { public static readonly RoutedEvent ApplicationOpenedEvent = - RoutedEvent.Register(nameof(ApplicationOpened), RoutingStrategies.Bubble); + RoutedEvent.Register(nameof(ApplicationOpened), RoutingStrategies.Bubble); public event EventHandler ApplicationOpened { @@ -20,7 +20,7 @@ namespace Ryujinx.Ava.UI.Controls remove { RemoveHandler(ApplicationOpenedEvent, value); } } - public GameListView() + public ApplicationListView() { InitializeComponent(); } @@ -49,7 +49,7 @@ namespace Ryujinx.Ava.UI.Controls } } - private void SearchBox_OnKeyUp(object sender, KeyEventArgs e) + private void SearchBox_OnKeyUp(object sender, KeyEventArgs args) { (DataContext as MainWindowViewModel).SearchText = (sender as TextBox).Text; } diff --git a/src/Ryujinx.Ava/UI/Controls/GameGridView.axaml b/src/Ryujinx.Ava/UI/Controls/GameGridView.axaml deleted file mode 100644 index 32cabfaa..00000000 --- a/src/Ryujinx.Ava/UI/Controls/GameGridView.axaml +++ /dev/null @@ -1,177 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index f4556bc3..b5c82d65 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -6,8 +6,6 @@ using Avalonia.Threading; using DynamicData; using DynamicData.Binding; using LibHac.Common; -using LibHac.Fs; -using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Ava.Common; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Input; @@ -33,13 +31,11 @@ using SixLabors.ImageSharp.PixelFormats; using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Globalization; using System.IO; using System.Threading; using System.Threading.Tasks; using Path = System.IO.Path; using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState; -using UserId = LibHac.Fs.UserId; namespace Ryujinx.Ava.UI.ViewModels { @@ -346,11 +342,11 @@ namespace Ryujinx.Ava.UI.ViewModels } } - public bool EnabledUserSaveDirectory => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.UserAccountSaveDataSize > 0; + public bool OpenUserSaveDirectoryEnabled => !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 OpenDeviceSaveDirectoryEnabled => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.DeviceSaveDataSize > 0; - public bool EnabledBcatSaveDirectory => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0; + public bool OpenBcatSaveDirectoryEnabled => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0; public string LoadHeading { @@ -941,7 +937,7 @@ namespace Ryujinx.Ava.UI.ViewModels }; } - private void RefreshView() + public void RefreshView() { RefreshGrid(); } @@ -1116,30 +1112,6 @@ namespace Ryujinx.Ava.UI.ViewModels })); } - private async void ExtractLogo() - { - if (SelectedApplication != null) - { - await ApplicationHelper.ExtractSection(NcaSectionType.Logo, SelectedApplication.Path, SelectedApplication.TitleName); - } - } - - private async void ExtractRomFs() - { - if (SelectedApplication != null) - { - await ApplicationHelper.ExtractSection(NcaSectionType.Data, SelectedApplication.Path, SelectedApplication.TitleName); - } - } - - private async void ExtractExeFs() - { - if (SelectedApplication != null) - { - await ApplicationHelper.ExtractSection(NcaSectionType.Code, SelectedApplication.Path, SelectedApplication.TitleName); - } - } - private void PrepareLoadScreen() { using MemoryStream stream = new(SelectedIcon); @@ -1383,241 +1355,11 @@ namespace Ryujinx.Ava.UI.ViewModels await NavigationDialogHost.Show(AccountManager, ContentManager, VirtualFileSystem, LibHacHorizonManager.RyujinxClient); } - public void OpenPtcDirectory() - { - ApplicationData selection = SelectedApplication; - if (selection != null) - { - string ptcDir = Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "cpu"); - string mainPath = Path.Combine(ptcDir, "0"); - string backupPath = Path.Combine(ptcDir, "1"); - - if (!Directory.Exists(ptcDir)) - { - Directory.CreateDirectory(ptcDir); - Directory.CreateDirectory(mainPath); - Directory.CreateDirectory(backupPath); - } - - OpenHelper.OpenFolder(ptcDir); - } - } - - public async void PurgePtcCache() - { - ApplicationData selection = SelectedApplication; - if (selection != null) - { - DirectoryInfo mainDir = new(Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "cpu", "0")); - DirectoryInfo backupDir = new(Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "cpu", "1")); - - // FIXME: Found a way to reproduce the bold effect on the title name (fork?). - UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning], - LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionMessage, selection.TitleName), - LocaleManager.Instance[LocaleKeys.InputDialogYes], - LocaleManager.Instance[LocaleKeys.InputDialogNo], - LocaleManager.Instance[LocaleKeys.RyujinxConfirm]); - - List cacheFiles = new(); - - if (mainDir.Exists) - { - cacheFiles.AddRange(mainDir.EnumerateFiles("*.cache")); - } - - if (backupDir.Exists) - { - cacheFiles.AddRange(backupDir.EnumerateFiles("*.cache")); - } - - if (cacheFiles.Count > 0 && result == UserResult.Yes) - { - foreach (FileInfo file in cacheFiles) - { - try - { - file.Delete(); - } - catch (Exception e) - { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionErrorMessage, file.Name, e)); - } - } - } - } - } - - public void OpenShaderCacheDirectory() - { - ApplicationData selection = SelectedApplication; - if (selection != null) - { - string shaderCacheDir = Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "shader"); - - if (!Directory.Exists(shaderCacheDir)) - { - Directory.CreateDirectory(shaderCacheDir); - } - - OpenHelper.OpenFolder(shaderCacheDir); - } - } - public void SimulateWakeUpMessage() { AppHost.Device.System.SimulateWakeUpMessage(); } - public async void PurgeShaderCache() - { - ApplicationData selection = SelectedApplication; - if (selection != null) - { - DirectoryInfo shaderCacheDir = new(Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "shader")); - - // FIXME: Found a way to reproduce the bold effect on the title name (fork?). - UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance[LocaleKeys.DialogWarning], - LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogShaderDeletionMessage, selection.TitleName), - LocaleManager.Instance[LocaleKeys.InputDialogYes], - LocaleManager.Instance[LocaleKeys.InputDialogNo], - LocaleManager.Instance[LocaleKeys.RyujinxConfirm]); - - List oldCacheDirectories = new(); - List newCacheFiles = new(); - - if (shaderCacheDir.Exists) - { - oldCacheDirectories.AddRange(shaderCacheDir.EnumerateDirectories("*")); - newCacheFiles.AddRange(shaderCacheDir.GetFiles("*.toc")); - newCacheFiles.AddRange(shaderCacheDir.GetFiles("*.data")); - } - - if ((oldCacheDirectories.Count > 0 || newCacheFiles.Count > 0) && result == UserResult.Yes) - { - foreach (DirectoryInfo directory in oldCacheDirectories) - { - try - { - directory.Delete(true); - } - catch (Exception e) - { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogPPTCDeletionErrorMessage, directory.Name, e)); - } - } - } - - foreach (FileInfo file in newCacheFiles) - { - try - { - file.Delete(); - } - catch (Exception e) - { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.ShaderCachePurgeError, file.Name, e)); - } - } - } - } - - public void ToggleFavorite() - { - ApplicationData selection = SelectedApplication; - if (selection != null) - { - selection.Favorite = !selection.Favorite; - - ApplicationLibrary.LoadAndSaveMetaData(selection.TitleId, appMetadata => - { - appMetadata.Favorite = selection.Favorite; - }); - - RefreshView(); - } - } - - public void OpenUserSaveDirectory() - { - 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) - { - 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; - } - - var saveDataFilter = SaveDataFilter.Make(titleIdNumber, saveDataType, userId, saveDataId: default, index: default); - - ApplicationHelper.OpenSaveDir(in saveDataFilter, titleIdNumber, SelectedApplication.ControlHolder, SelectedApplication.TitleName); - } - } - - public void OpenModsDirectory() - { - if (SelectedApplication != null) - { - string modsBasePath = VirtualFileSystem.ModLoader.GetModsBasePath(); - string titleModsPath = VirtualFileSystem.ModLoader.GetTitleDir(modsBasePath, SelectedApplication.TitleId); - - OpenHelper.OpenFolder(titleModsPath); - } - } - - public void OpenSdModsDirectory() - { - if (SelectedApplication != null) - { - string sdModsBasePath = VirtualFileSystem.ModLoader.GetSdModsBasePath(); - string titleModsPath = VirtualFileSystem.ModLoader.GetTitleDir(sdModsBasePath, SelectedApplication.TitleId); - - OpenHelper.OpenFolder(titleModsPath); - } - } - - public async void OpenTitleUpdateManager() - { - if (SelectedApplication != null) - { - await TitleUpdateWindow.Show(VirtualFileSystem, ulong.Parse(SelectedApplication.TitleId, NumberStyles.HexNumber), SelectedApplication.TitleName); - } - } - - public async void OpenDownloadableContentManager() - { - if (SelectedApplication != null) - { - await DownloadableContentManagerWindow.Show(VirtualFileSystem, ulong.Parse(SelectedApplication.TitleId, NumberStyles.HexNumber), SelectedApplication.TitleName); - } - } - - public async void OpenCheatManager() - { - if (SelectedApplication != null) - { - await new CheatWindow(VirtualFileSystem, SelectedApplication.TitleId, SelectedApplication.TitleName).ShowDialog(TopLevel as Window); - } - } - public async void LoadApplications() { await Dispatcher.UIThread.InvokeAsync(() => diff --git a/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml b/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml index de01f90f..fa07d977 100644 --- a/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml +++ b/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml @@ -88,16 +88,16 @@ - -