Jelajahi Sumber

ava: Refactor Title Update Manager window (#3898)

* ava: Refactor TitleUpdate Manager window

* Update locale
Ac_K 3 tahun lalu
induk
melakukan
5d3ef7761b

+ 5 - 5
Ryujinx.Ava/Assets/Locales/en_US.json

@@ -564,10 +564,10 @@
   "Writable": "Writable",
   "SelectDlcDialogTitle": "Select DLC files",
   "SelectUpdateDialogTitle": "Select update files",
-  "UserProfileWindowTitle": "Manage User Profiles",
-  "CheatWindowTitle": "Manage Game Cheats",
-  "DlcWindowTitle": "Manage Game DLC",
-  "UpdateWindowTitle": "Manage Game Updates",
+  "UserProfileWindowTitle": "User Profiles Manager",
+  "CheatWindowTitle": "Cheats Manager",
+  "DlcWindowTitle": "Downloadable Content Manager",
+  "UpdateWindowTitle": "Title Update Manager",
   "CheatWindowHeading": "Cheats Available for {0} [{1}]",
   "DlcWindowHeading": "{0} Downloadable Content(s) available for {1} ({2})",
   "UserProfilesEditProfile": "Edit Selected",
@@ -577,7 +577,7 @@
   "UserProfilesSetProfileImage": "Set Profile Image",
   "UserProfileEmptyNameError": "Name is required",
   "UserProfileNoImageError": "Profile image must be set",
-  "GameUpdateWindowHeading": "Updates Available for {0} [{1}]",
+  "GameUpdateWindowHeading": "{0} Update(s) available for {1} ({2})",
   "SettingsTabHotkeysResScaleUpHotkey": "Increase resolution:",
   "SettingsTabHotkeysResScaleDownHotkey": "Decrease resolution:",
   "UserProfilesName": "Name:",

+ 1 - 1
Ryujinx.Ava/Ui/ViewModels/MainWindowViewModel.cs

@@ -1283,7 +1283,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
             ApplicationData selection = SelectedApplication;
             if (selection != null)
             {
-                await new TitleUpdateWindow(_owner.VirtualFileSystem, selection.TitleId, selection.TitleName).ShowDialog(_owner);
+                await new TitleUpdateWindow(_owner.VirtualFileSystem, ulong.Parse(selection.TitleId, NumberStyles.HexNumber), selection.TitleName).ShowDialog(_owner);
             }
         }
 

+ 3 - 1
Ryujinx.Ava/Ui/Windows/DownloadableContentManagerWindow.axaml

@@ -8,8 +8,10 @@
     xmlns:window="clr-namespace:Ryujinx.Ava.Ui.Windows"
     Width="800"
     Height="500"
-    MinWidth="600"
+    MinWidth="800"
     MinHeight="500"
+    MaxWidth="800"
+    MaxHeight="500"
     SizeToContent="Height"
     WindowStartupLocation="CenterOwner"
     mc:Ignorable="d">

+ 12 - 14
Ryujinx.Ava/Ui/Windows/DownloadableContentManagerWindow.axaml.cs

@@ -8,7 +8,6 @@ using LibHac.FsSystem;
 using LibHac.Tools.Fs;
 using LibHac.Tools.FsSystem;
 using LibHac.Tools.FsSystem.NcaUtils;
-using LibHac.Tools.FsSystem.Save;
 using Ryujinx.Ava.Common.Locale;
 using Ryujinx.Ava.Ui.Controls;
 using Ryujinx.Ava.Ui.Models;
@@ -17,8 +16,6 @@ using Ryujinx.Common.Utilities;
 using Ryujinx.HLE.FileSystem;
 using System;
 using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.ComponentModel;
 using System.IO;
 using System.Linq;
 using System.Reactive.Linq;
@@ -36,8 +33,8 @@ namespace Ryujinx.Ava.Ui.Windows
         private VirtualFileSystem                      _virtualFileSystem    { get; }
         private AvaloniaList<DownloadableContentModel> _downloadableContents { get; set; }
 
-        private ulong  TitleId   { get; }
-        private string TitleName { get; }
+        private ulong  _titleId   { get; }
+        private string _titleName { get; }
 
         public DownloadableContentManagerWindow()
         {
@@ -45,15 +42,16 @@ namespace Ryujinx.Ava.Ui.Windows
 
             InitializeComponent();
 
-            Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance["DlcWindowTitle"]} - {TitleName} ({TitleId:X16})";
+            Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance["DlcWindowTitle"]} - {_titleName} ({_titleId:X16})";
         }
 
         public DownloadableContentManagerWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName)
         {
             _virtualFileSystem    = virtualFileSystem;
             _downloadableContents = new AvaloniaList<DownloadableContentModel>();
-            TitleId               = titleId;
-            TitleName             = titleName;
+
+            _titleId   = titleId;
+            _titleName = titleName;
 
             _downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "dlc.json");
 
@@ -74,7 +72,7 @@ namespace Ryujinx.Ava.Ui.Windows
 
             DlcDataGrid.SelectionChanged += DlcDataGrid_SelectionChanged;
 
-            Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance["DlcWindowTitle"]} - {TitleName} ({TitleId:X16})";
+            Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance["DlcWindowTitle"]} - {_titleName} ({_titleId:X16})";
 
             LoadDownloadableContents();
             PrintHeading();
@@ -87,7 +85,7 @@ namespace Ryujinx.Ava.Ui.Windows
 
         private void PrintHeading()
         {
-            Heading.Text = string.Format(LocaleManager.Instance["DlcWindowHeading"], _downloadableContents.Count, TitleName, TitleId.ToString("X16"));
+            Heading.Text = string.Format(LocaleManager.Instance["DlcWindowHeading"], _downloadableContents.Count, _titleName, _titleId.ToString("X16"));
         }
 
         private void LoadDownloadableContents()
@@ -98,15 +96,15 @@ namespace Ryujinx.Ava.Ui.Windows
                 {
                     using FileStream containerFile = File.OpenRead(downloadableContentContainer.ContainerPath);
 
-                    PartitionFileSystem pfs = new(containerFile.AsStorage());
+                    PartitionFileSystem partitionFileSystem = new(containerFile.AsStorage());
 
-                    _virtualFileSystem.ImportTickets(pfs);
+                    _virtualFileSystem.ImportTickets(partitionFileSystem);
 
                     foreach (DownloadableContentNca downloadableContentNca in downloadableContentContainer.DownloadableContentNcaList)
                     {
                         using UniqueRef<IFile> ncaFile = new();
 
-                        pfs.OpenFile(ref ncaFile.Ref(), downloadableContentNca.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
+                        partitionFileSystem.OpenFile(ref ncaFile.Ref(), downloadableContentNca.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
 
                         Nca nca = TryOpenNca(ncaFile.Get.AsStorage(), downloadableContentContainer.ContainerPath);
                         if (nca != null)
@@ -169,7 +167,7 @@ namespace Ryujinx.Ava.Ui.Windows
 
                 if (nca.Header.ContentType == NcaContentType.PublicData)
                 {
-                    if ((nca.Header.TitleId & 0xFFFFFFFFFFFFE000) != TitleId)
+                    if ((nca.Header.TitleId & 0xFFFFFFFFFFFFE000) != _titleId)
                     {
                         break;
                     }

+ 22 - 12
Ryujinx.Ava/Ui/Windows/TitleUpdateWindow.axaml

@@ -3,13 +3,17 @@
     xmlns="https://github.com/avaloniaui"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
-    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
     xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
     xmlns:window="clr-namespace:Ryujinx.Ava.Ui.Windows"
+    Width="600"
+    Height="400"
+    MinWidth="600"
+    MinHeight="400"
+    MaxWidth="600"
+    MaxHeight="400"
     SizeToContent="Height"
-    Width="600" MinHeight="500" Height="500"
     WindowStartupLocation="CenterOwner"
-    MinWidth="600"
     mc:Ignorable="d">
     <Grid Margin="15">
         <Grid.RowDefinitions>
@@ -19,15 +23,15 @@
             <RowDefinition Height="Auto" />
         </Grid.RowDefinitions>
         <TextBlock
+            Name="Heading"
             Grid.Row="1"
+            MaxWidth="500"
             Margin="20,15,20,20"
             HorizontalAlignment="Center"
             VerticalAlignment="Center"
-            MaxWidth="500"
             LineHeight="18"
-            TextWrapping="Wrap"
-            Text="{Binding Heading}"
-            TextAlignment="Center" />
+            TextAlignment="Center"
+            TextWrapping="Wrap" />
         <Border
             Grid.Row="2"
             Margin="5"
@@ -36,8 +40,6 @@
             BorderBrush="Gray"
             BorderThickness="1">
             <ScrollViewer
-                Width="550"
-                MinHeight="200"
                 VerticalAlignment="Stretch"
                 HorizontalScrollBarVisibility="Auto"
                 VerticalScrollBarVisibility="Auto">
@@ -45,11 +47,19 @@
                     Margin="10"
                     HorizontalAlignment="Stretch"
                     VerticalAlignment="Stretch"
-                    Items="{Binding TitleUpdates}">
+                    Items="{Binding _titleUpdates}">
                     <ItemsControl.ItemTemplate>
                         <DataTemplate>
-                            <RadioButton Padding="8, 0" VerticalContentAlignment="Center" GroupName="Update" IsChecked="{Binding IsEnabled, Mode=TwoWay}">
-                                <Label Margin="0" VerticalAlignment="Center" Content="{Binding Label}" />
+                            <RadioButton
+                                Padding="8,0"
+                                VerticalContentAlignment="Center"
+                                GroupName="Update"
+                                IsChecked="{Binding IsEnabled, Mode=TwoWay}">
+                                <Label
+                                    Margin="0"
+                                    VerticalAlignment="Center"
+                                    Content="{Binding Label}"
+                                    FontSize="12" />
                             </RadioButton>
                         </DataTemplate>
                     </ItemsControl.ItemTemplate>

+ 58 - 53
Ryujinx.Ava/Ui/Windows/TitleUpdateWindow.axaml.cs

@@ -30,13 +30,11 @@ namespace Ryujinx.Ava.Ui.Windows
         private readonly string     _titleUpdateJsonPath;
         private TitleUpdateMetadata _titleUpdateWindowData;
 
-        public VirtualFileSystem VirtualFileSystem { get; }
+        private VirtualFileSystem              _virtualFileSystem { get; }
+        private AvaloniaList<TitleUpdateModel> _titleUpdates      { get; set; }
 
-        internal AvaloniaList<TitleUpdateModel> TitleUpdates { get; set; } = new AvaloniaList<TitleUpdateModel>();
-        public string TitleId { get; }
-        public string TitleName { get; }
-
-        public string Heading => string.Format(LocaleManager.Instance["GameUpdateWindowHeading"], TitleName, TitleId.ToUpper());
+        private ulong  _titleId   { get; }
+        private string _titleName { get; }
 
         public TitleUpdateWindow()
         {
@@ -44,16 +42,18 @@ namespace Ryujinx.Ava.Ui.Windows
 
             InitializeComponent();
             
-            Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["UpdateWindowTitle"];
+            Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance["UpdateWindowTitle"]} - {_titleName} ({_titleId:X16})";
         }
 
-        public TitleUpdateWindow(VirtualFileSystem virtualFileSystem, string titleId, string titleName)
+        public TitleUpdateWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName)
         {
-            VirtualFileSystem = virtualFileSystem;
-            TitleId           = titleId;
-            TitleName         = titleName;
+            _virtualFileSystem = virtualFileSystem;
+            _titleUpdates      = new AvaloniaList<TitleUpdateModel>();
+
+            _titleId   = titleId;
+            _titleName = titleName;
 
-            _titleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId, "updates.json");
+            _titleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json");
 
             try
             {
@@ -64,7 +64,7 @@ namespace Ryujinx.Ava.Ui.Windows
                 _titleUpdateWindowData = new TitleUpdateMetadata 
                 {
                     Selected = "",
-                    Paths = new List<string>()
+                    Paths    = new List<string>()
                 };
             }
 
@@ -72,14 +72,20 @@ namespace Ryujinx.Ava.Ui.Windows
 
             InitializeComponent();
 
-            Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["UpdateWindowTitle"];
+            Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance["UpdateWindowTitle"]} - {_titleName} ({_titleId:X16})";
 
             LoadUpdates();
+            PrintHeading();
+        }
+
+        private void PrintHeading()
+        {
+            Heading.Text = string.Format(LocaleManager.Instance["GameUpdateWindowHeading"], _titleUpdates.Count, _titleName, _titleId.ToString("X16"));
         }
 
         private void LoadUpdates()
         {
-            TitleUpdates.Add(new TitleUpdateModel(default, string.Empty, true));
+            _titleUpdates.Add(new TitleUpdateModel(default, string.Empty, true));
 
             foreach (string path in _titleUpdateWindowData.Paths)
             {
@@ -88,12 +94,12 @@ namespace Ryujinx.Ava.Ui.Windows
 
             if (_titleUpdateWindowData.Selected == "")
             {
-                TitleUpdates[0].IsEnabled = true;
+                _titleUpdates[0].IsEnabled = true;
             }
             else
             {
-                TitleUpdateModel       selected = TitleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected);
-                List<TitleUpdateModel> enabled  = TitleUpdates.Where(x => x.IsEnabled).ToList();
+                TitleUpdateModel       selected = _titleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected);
+                List<TitleUpdateModel> enabled  = _titleUpdates.Where(x => x.IsEnabled).ToList();
 
                 foreach (TitleUpdateModel update in enabled)
                 {
@@ -111,50 +117,47 @@ namespace Ryujinx.Ava.Ui.Windows
 
         private void AddUpdate(string path)
         {
-            if (File.Exists(path) && !TitleUpdates.Any(x => x.Path == path))
+            if (File.Exists(path) && !_titleUpdates.Any(x => x.Path == path))
             {
-                using (FileStream file = new(path, FileMode.Open, FileAccess.Read))
+                using FileStream file = new(path, FileMode.Open, FileAccess.Read);
+
+                try
                 {
-                    PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage());
+                    (Nca patchNca, Nca controlNca) = ApplicationLoader.GetGameUpdateDataFromPartition(_virtualFileSystem, new PartitionFileSystem(file.AsStorage()), _titleId.ToString("x16"), 0);
 
-                    try
+                    if (controlNca != null && patchNca != null)
                     {
-                        (Nca patchNca, Nca controlNca) = ApplicationLoader.GetGameUpdateDataFromPartition(VirtualFileSystem, nsp, TitleId, 0);
-
-                        if (controlNca != null && patchNca != null)
-                        {
-                            ApplicationControlProperty controlData = new ApplicationControlProperty();
-
-                            using var nacpFile = new UniqueRef<IFile>();
+                        ApplicationControlProperty controlData = new();
 
-                            controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref(), "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
-                            nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure();
+                        using UniqueRef<IFile> nacpFile = new();
 
-                            TitleUpdates.Add(new TitleUpdateModel(controlData, path));
+                        controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref(), "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
+                        nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure();
 
-                            foreach (var update in TitleUpdates)
-                            {
-                                update.IsEnabled = false;
-                            }
+                        _titleUpdates.Add(new TitleUpdateModel(controlData, path));
 
-                            TitleUpdates.Last().IsEnabled = true;
-                        }
-                        else
+                        foreach (var update in _titleUpdates)
                         {
-                            Dispatcher.UIThread.Post(async () =>
-                            {
-                                await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogUpdateAddUpdateErrorMessage"]);
-                            });
+                            update.IsEnabled = false;
                         }
+
+                        _titleUpdates.Last().IsEnabled = true;
                     }
-                    catch (Exception ex)
+                    else
                     {
                         Dispatcher.UIThread.Post(async () =>
                         {
-                            await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance["DialogDlcLoadNcaErrorMessage"], ex.Message, path));
+                            await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogUpdateAddUpdateErrorMessage"]);
                         });
                     }
                 }
+                catch (Exception ex)
+                {
+                    Dispatcher.UIThread.Post(async () =>
+                    {
+                        await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance["DialogDlcLoadNcaErrorMessage"], ex.Message, path));
+                    });
+                }
             }
         }
 
@@ -162,16 +165,17 @@ namespace Ryujinx.Ava.Ui.Windows
         {
             if (removeSelectedOnly)
             {
-                TitleUpdates.RemoveAll(TitleUpdates.Where(x => x.IsEnabled && !x.IsNoUpdate).ToList());
+                _titleUpdates.RemoveAll(_titleUpdates.Where(x => x.IsEnabled && !x.IsNoUpdate).ToList());
             }
             else
             {
-                TitleUpdates.RemoveAll(TitleUpdates.Where(x => !x.IsNoUpdate).ToList());
+                _titleUpdates.RemoveAll(_titleUpdates.Where(x => !x.IsNoUpdate).ToList());
             }
 
-            TitleUpdates.FirstOrDefault(x => x.IsNoUpdate).IsEnabled = true;
+            _titleUpdates.FirstOrDefault(x => x.IsNoUpdate).IsEnabled = true;
 
             SortUpdates();
+            PrintHeading();
         }
 
         public void RemoveSelected()
@@ -186,7 +190,7 @@ namespace Ryujinx.Ava.Ui.Windows
 
         public async void Add()
         {
-            OpenFileDialog dialog = new OpenFileDialog()
+            OpenFileDialog dialog = new()
             {
                 Title         = LocaleManager.Instance["SelectUpdateDialogTitle"],
                 AllowMultiple = true
@@ -209,11 +213,12 @@ namespace Ryujinx.Ava.Ui.Windows
             }
 
             SortUpdates();
+            PrintHeading();
         }
 
         private void SortUpdates()
         {
-            var list = TitleUpdates.ToList();
+            var list = _titleUpdates.ToList();
 
             list.Sort((first, second) =>
             {
@@ -229,8 +234,8 @@ namespace Ryujinx.Ava.Ui.Windows
                 return Version.Parse(first.Control.DisplayVersionString.ToString()).CompareTo(Version.Parse(second.Control.DisplayVersionString.ToString())) * -1;
             });
 
-            TitleUpdates.Clear();
-            TitleUpdates.AddRange(list);
+            _titleUpdates.Clear();
+            _titleUpdates.AddRange(list);
         }
 
         public void Save()
@@ -239,7 +244,7 @@ namespace Ryujinx.Ava.Ui.Windows
 
             _titleUpdateWindowData.Selected = "";
 
-            foreach (TitleUpdateModel update in TitleUpdates)
+            foreach (TitleUpdateModel update in _titleUpdates)
             {
                 _titleUpdateWindowData.Paths.Add(update.Path);