From 68d134305834bfb32e999453be58ac8218b641ef Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Sun, 15 Jan 2023 15:49:09 -0500 Subject: [PATCH] Move around functions --- .../DownloadableContentManagerViewModel.cs | 269 ++++++++++++++++ .../DownloadableContentManagerWindow.axaml | 4 +- .../DownloadableContentManagerWindow.axaml.cs | 300 ++---------------- 3 files changed, 296 insertions(+), 277 deletions(-) diff --git a/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs b/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs index cf29a1d50..8d100f987 100644 --- a/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs +++ b/Ryujinx.Ava/UI/ViewModels/DownloadableContentManagerViewModel.cs @@ -1,7 +1,276 @@ +using Avalonia.Collections; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Threading; +using LibHac.Common; +using LibHac.Fs; +using LibHac.Fs.Fsa; +using LibHac.FsSystem; +using LibHac.Tools.Fs; +using LibHac.Tools.FsSystem; +using LibHac.Tools.FsSystem.NcaUtils; +using Ryujinx.Ava.Common.Locale; +using Ryujinx.Ava.UI.Helpers; +using Ryujinx.Ava.UI.Models; +using Ryujinx.Common.Configuration; +using Ryujinx.Common.Utilities; +using Ryujinx.HLE.FileSystem; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Path = System.IO.Path; + namespace Ryujinx.Ava.UI.ViewModels { public class DownloadableContentManagerViewModel : BaseModel { + private readonly List _downloadableContentContainerList; + private readonly string _downloadableContentJsonPath; + + private VirtualFileSystem _virtualFileSystem; + private AvaloniaList _downloadableContents = new(); + + private ulong _titleId; + private string _titleName; + + public DownloadableContentManagerViewModel(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) + { + _virtualFileSystem = virtualFileSystem; + + _titleId = titleId; + _titleName = titleName; + + _downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "dlc.json"); + + try + { + _downloadableContentContainerList = JsonHelper.DeserializeFromFile>(_downloadableContentJsonPath); + } + catch + { + _downloadableContentContainerList = new List(); + } + + LoadDownloadableContents(); + } + + private void LoadDownloadableContents() + { + foreach (DownloadableContentContainer downloadableContentContainer in _downloadableContentContainerList) + { + if (File.Exists(downloadableContentContainer.ContainerPath)) + { + using FileStream containerFile = File.OpenRead(downloadableContentContainer.ContainerPath); + + PartitionFileSystem partitionFileSystem = new(containerFile.AsStorage()); + + _virtualFileSystem.ImportTickets(partitionFileSystem); + + foreach (DownloadableContentNca downloadableContentNca in downloadableContentContainer.DownloadableContentNcaList) + { + using UniqueRef ncaFile = new(); + + partitionFileSystem.OpenFile(ref ncaFile.Ref(), downloadableContentNca.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + + Nca nca = TryOpenNca(ncaFile.Get.AsStorage(), downloadableContentContainer.ContainerPath); + if (nca != null) + { + _downloadableContents.Add(new DownloadableContentModel(nca.Header.TitleId.ToString("X16"), + downloadableContentContainer.ContainerPath, + downloadableContentNca.FullPath, + downloadableContentNca.Enabled)); + } + } + } + } + + // NOTE: Save the list again to remove leftovers. + Save(); + } + + private Nca TryOpenNca(IStorage ncaStorage, string containerPath) + { + try + { + return new Nca(_virtualFileSystem.KeySet, ncaStorage); + } + catch (Exception ex) + { + Dispatcher.UIThread.InvokeAsync(async () => + { + await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogDlcLoadNcaErrorMessage], ex.Message, containerPath)); + }); + } + + return null; + } + + public async void Add() + { + OpenFileDialog dialog = new OpenFileDialog() + { + Title = LocaleManager.Instance[LocaleKeys.SelectDlcDialogTitle], + AllowMultiple = true + }; + + dialog.Filters.Add(new FileDialogFilter + { + Name = "NSP", + Extensions = { "nsp" } + }); + + if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + string[] files = await dialog.ShowAsync(desktop.MainWindow); + + if (files != null) + { + foreach (string file in files) + { + await AddDownloadableContent(file); + } + } + } + } + + private async Task AddDownloadableContent(string path) + { + if (!File.Exists(path) || _downloadableContents.FirstOrDefault(x => x.ContainerPath == path) != null) + { + return; + } + + using FileStream containerFile = File.OpenRead(path); + + PartitionFileSystem partitionFileSystem = new(containerFile.AsStorage()); + bool containsDownloadableContent = false; + + _virtualFileSystem.ImportTickets(partitionFileSystem); + + foreach (DirectoryEntryEx fileEntry in partitionFileSystem.EnumerateEntries("/", "*.nca")) + { + using var ncaFile = new UniqueRef(); + + partitionFileSystem.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); + + Nca nca = TryOpenNca(ncaFile.Get.AsStorage(), path); + if (nca == null) + { + continue; + } + + if (nca.Header.ContentType == NcaContentType.PublicData) + { + if ((nca.Header.TitleId & 0xFFFFFFFFFFFFE000) != _titleId) + { + break; + } + + _downloadableContents.Add(new DownloadableContentModel(nca.Header.TitleId.ToString("X16"), path, fileEntry.FullPath, true)); + + containsDownloadableContent = true; + } + } + + if (!containsDownloadableContent) + { + await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogDlcNoDlcErrorMessage]); + } + } + + private void RemoveDownloadableContents(bool removeSelectedOnly = false) + { + if (removeSelectedOnly) + { + AvaloniaList removedItems = new(); + + foreach (var item in DlcDataGrid.SelectedItems) + { + removedItems.Add(item as DownloadableContentModel); + } + + DlcDataGrid.SelectedItems.Clear(); + + foreach (var item in removedItems) + { + _downloadableContents.RemoveAll(_downloadableContents.Where(x => x.TitleId == item.TitleId).ToList()); + } + } + else + { + _downloadableContents.Clear(); + } + } + + public void RemoveSelected() + { + RemoveDownloadableContents(true); + } + + public void RemoveAll() + { + RemoveDownloadableContents(); + } + + public void EnableAll() + { + foreach(var item in _downloadableContents) + { + item.Enabled = true; + } + } + + public void DisableAll() + { + foreach (var item in _downloadableContents) + { + item.Enabled = false; + } + } + + public void Save() + { + _downloadableContentContainerList.Clear(); + + DownloadableContentContainer container = default; + + foreach (DownloadableContentModel downloadableContent in _downloadableContents) + { + if (container.ContainerPath != downloadableContent.ContainerPath) + { + if (!string.IsNullOrWhiteSpace(container.ContainerPath)) + { + _downloadableContentContainerList.Add(container); + } + + container = new DownloadableContentContainer + { + ContainerPath = downloadableContent.ContainerPath, + DownloadableContentNcaList = new List() + }; + } + + container.DownloadableContentNcaList.Add(new DownloadableContentNca + { + Enabled = downloadableContent.Enabled, + TitleId = Convert.ToUInt64(downloadableContent.TitleId, 16), + FullPath = downloadableContent.FullPath + }); + } + + if (!string.IsNullOrWhiteSpace(container.ContainerPath)) + { + _downloadableContentContainerList.Add(container); + } + + using (FileStream downloadableContentJsonStream = File.Create(_downloadableContentJsonPath, 4096, FileOptions.WriteThrough)) + { + downloadableContentJsonStream.Write(Encoding.UTF8.GetBytes(JsonHelper.Serialize(_downloadableContentContainerList, true))); + } + } } } \ No newline at end of file diff --git a/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml b/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml index f81fee397..9549ba77b 100644 --- a/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml +++ b/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml @@ -38,14 +38,14 @@ Name="EnableAllButton" MinWidth="90" Margin="5" - Command="{Binding EnableAll}"> + Command="{ReflectionBinding EnableAll}"> diff --git a/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml.cs b/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml.cs index 47216c489..c560c6d20 100644 --- a/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml.cs +++ b/Ryujinx.Ava/UI/Windows/DownloadableContentManagerWindow.axaml.cs @@ -1,82 +1,53 @@ -using Avalonia.Collections; using Avalonia.Controls; -using Avalonia.Threading; -using LibHac.Common; -using LibHac.Fs; -using LibHac.Fs.Fsa; -using LibHac.FsSystem; -using LibHac.Tools.Fs; -using LibHac.Tools.FsSystem; -using LibHac.Tools.FsSystem.NcaUtils; +using Avalonia.Styling; +using FluentAvalonia.UI.Controls; using Ryujinx.Ava.Common.Locale; -using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Helpers; -using Ryujinx.Ava.UI.Models; -using Ryujinx.Common.Configuration; -using Ryujinx.Common.Utilities; +using Ryujinx.Ava.UI.ViewModels; using Ryujinx.HLE.FileSystem; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reactive.Linq; -using System.Text; using System.Threading.Tasks; -using Path = System.IO.Path; namespace Ryujinx.Ava.UI.Windows { - public partial class DownloadableContentManagerWindow : StyleableWindow + public partial class DownloadableContentManagerWindow : UserControl { - private readonly List _downloadableContentContainerList; - private readonly string _downloadableContentJsonPath; - - private VirtualFileSystem _virtualFileSystem { get; } - private AvaloniaList _downloadableContents { get; set; } - - private ulong _titleId { get; } - private string _titleName { get; } + public DownloadableContentManagerViewModel ViewModel; public DownloadableContentManagerWindow() { DataContext = this; InitializeComponent(); - - Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance[LocaleKeys.DlcWindowTitle]} - {_titleName} ({_titleId:X16})"; } public DownloadableContentManagerWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) { - _virtualFileSystem = virtualFileSystem; - _downloadableContents = new AvaloniaList(); - - _titleId = titleId; - _titleName = titleName; - - _downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "dlc.json"); - - try - { - _downloadableContentContainerList = JsonHelper.DeserializeFromFile>(_downloadableContentJsonPath); - } - catch - { - _downloadableContentContainerList = new List(); - } - - DataContext = this; + DataContext = ViewModel = new DownloadableContentManagerViewModel(virtualFileSystem, titleId, titleName); InitializeComponent(); RemoveButton.IsEnabled = false; DlcDataGrid.SelectionChanged += DlcDataGrid_SelectionChanged; + } - Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance[LocaleKeys.DlcWindowTitle]} - {_titleName} ({_titleId:X16})"; + public static async Task Show(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) + { + ContentDialog contentDialog = new() + { + PrimaryButtonText = "", + SecondaryButtonText = "", + CloseButtonText = "", + Content = new DownloadableContentManagerViewModel(virtualFileSystem, titleId, titleName), + Title = string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowTitle], titleName, titleId.ToString("X16")) + }; - LoadDownloadableContents(); - PrintHeading(); + Style bottomBorder = new(x => x.OfType().Name("DialogSpace").Child().OfType()); + bottomBorder.Setters.Add(new Setter(IsVisibleProperty, false)); + + contentDialog.Styles.Add(bottomBorder); + + await ContentDialogHelper.ShowAsync(contentDialog); } private void DlcDataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e) @@ -84,231 +55,10 @@ namespace Ryujinx.Ava.UI.Windows RemoveButton.IsEnabled = (DlcDataGrid.SelectedItems.Count > 0); } - private void PrintHeading() - { - Heading.Text = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DlcWindowHeading, _downloadableContents.Count, _titleName, _titleId.ToString("X16")); - } - - private void LoadDownloadableContents() - { - foreach (DownloadableContentContainer downloadableContentContainer in _downloadableContentContainerList) - { - if (File.Exists(downloadableContentContainer.ContainerPath)) - { - using FileStream containerFile = File.OpenRead(downloadableContentContainer.ContainerPath); - - PartitionFileSystem partitionFileSystem = new(containerFile.AsStorage()); - - _virtualFileSystem.ImportTickets(partitionFileSystem); - - foreach (DownloadableContentNca downloadableContentNca in downloadableContentContainer.DownloadableContentNcaList) - { - using UniqueRef ncaFile = new(); - - partitionFileSystem.OpenFile(ref ncaFile.Ref(), downloadableContentNca.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - Nca nca = TryOpenNca(ncaFile.Get.AsStorage(), downloadableContentContainer.ContainerPath); - if (nca != null) - { - _downloadableContents.Add(new DownloadableContentModel(nca.Header.TitleId.ToString("X16"), - downloadableContentContainer.ContainerPath, - downloadableContentNca.FullPath, - downloadableContentNca.Enabled)); - } - } - } - } - - // NOTE: Save the list again to remove leftovers. - Save(); - } - - private Nca TryOpenNca(IStorage ncaStorage, string containerPath) - { - try - { - return new Nca(_virtualFileSystem.KeySet, ncaStorage); - } - catch (Exception ex) - { - Dispatcher.UIThread.InvokeAsync(async () => - { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadNcaErrorMessage, ex.Message, containerPath)); - }); - } - - return null; - } - - private async Task AddDownloadableContent(string path) - { - if (!File.Exists(path) || _downloadableContents.FirstOrDefault(x => x.ContainerPath == path) != null) - { - return; - } - - using FileStream containerFile = File.OpenRead(path); - - PartitionFileSystem partitionFileSystem = new(containerFile.AsStorage()); - bool containsDownloadableContent = false; - - _virtualFileSystem.ImportTickets(partitionFileSystem); - - foreach (DirectoryEntryEx fileEntry in partitionFileSystem.EnumerateEntries("/", "*.nca")) - { - using var ncaFile = new UniqueRef(); - - partitionFileSystem.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - Nca nca = TryOpenNca(ncaFile.Get.AsStorage(), path); - if (nca == null) - { - continue; - } - - if (nca.Header.ContentType == NcaContentType.PublicData) - { - if ((nca.Header.TitleId & 0xFFFFFFFFFFFFE000) != _titleId) - { - break; - } - - _downloadableContents.Add(new DownloadableContentModel(nca.Header.TitleId.ToString("X16"), path, fileEntry.FullPath, true)); - - containsDownloadableContent = true; - } - } - - if (!containsDownloadableContent) - { - await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogDlcNoDlcErrorMessage]); - } - } - - private void RemoveDownloadableContents(bool removeSelectedOnly = false) - { - if (removeSelectedOnly) - { - AvaloniaList removedItems = new(); - - foreach (var item in DlcDataGrid.SelectedItems) - { - removedItems.Add(item as DownloadableContentModel); - } - - DlcDataGrid.SelectedItems.Clear(); - - foreach (var item in removedItems) - { - _downloadableContents.RemoveAll(_downloadableContents.Where(x => x.TitleId == item.TitleId).ToList()); - } - } - else - { - _downloadableContents.Clear(); - } - - PrintHeading(); - } - - public void RemoveSelected() - { - RemoveDownloadableContents(true); - } - - public void RemoveAll() - { - RemoveDownloadableContents(); - } - - public void EnableAll() - { - foreach(var item in _downloadableContents) - { - item.Enabled = true; - } - } - - public void DisableAll() - { - foreach (var item in _downloadableContents) - { - item.Enabled = false; - } - } - - public async void Add() - { - OpenFileDialog dialog = new OpenFileDialog() - { - Title = LocaleManager.Instance[LocaleKeys.SelectDlcDialogTitle], - AllowMultiple = true - }; - - dialog.Filters.Add(new FileDialogFilter - { - Name = "NSP", - Extensions = { "nsp" } - }); - - string[] files = await dialog.ShowAsync(this); - - if (files != null) - { - foreach (string file in files) - { - await AddDownloadableContent(file); - } - } - - PrintHeading(); - } - - public void Save() - { - _downloadableContentContainerList.Clear(); - - DownloadableContentContainer container = default; - - foreach (DownloadableContentModel downloadableContent in _downloadableContents) - { - if (container.ContainerPath != downloadableContent.ContainerPath) - { - if (!string.IsNullOrWhiteSpace(container.ContainerPath)) - { - _downloadableContentContainerList.Add(container); - } - - container = new DownloadableContentContainer - { - ContainerPath = downloadableContent.ContainerPath, - DownloadableContentNcaList = new List() - }; - } - - container.DownloadableContentNcaList.Add(new DownloadableContentNca - { - Enabled = downloadableContent.Enabled, - TitleId = Convert.ToUInt64(downloadableContent.TitleId, 16), - FullPath = downloadableContent.FullPath - }); - } - - if (!string.IsNullOrWhiteSpace(container.ContainerPath)) - { - _downloadableContentContainerList.Add(container); - } - - using (FileStream downloadableContentJsonStream = File.Create(_downloadableContentJsonPath, 4096, FileOptions.WriteThrough)) - { - downloadableContentJsonStream.Write(Encoding.UTF8.GetBytes(JsonHelper.Serialize(_downloadableContentContainerList, true))); - } - } - public void SaveAndClose() { - Save(); - Close(); + ViewModel.Save(); + ((ContentDialog)Parent).Hide(); } } } \ No newline at end of file