Initial moving of DLC/updates to UI.Common

This commit is contained in:
Jimmy Reichley 2024-08-15 21:47:04 -04:00
parent 2e93c96c86
commit e1171086f4
No known key found for this signature in database
GPG key ID: 67715DC5A329803C
13 changed files with 167 additions and 79 deletions

View file

@ -497,7 +497,6 @@ namespace Ryujinx.UI.App.Common
: IntegrityCheckLevel.None; : IntegrityCheckLevel.None;
using IFileSystem pfs = PartitionFileSystemUtils.OpenApplicationFileSystem(filePath, _virtualFileSystem); using IFileSystem pfs = PartitionFileSystemUtils.OpenApplicationFileSystem(filePath, _virtualFileSystem);
// Dictionary<ulong, ContentMetaData> updates = pfs.GetContentData(ContentMetaType.AddOnContent, _virtualFileSystem, checkLevel);
foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
{ {
@ -517,11 +516,7 @@ namespace Ryujinx.UI.App.Common
} }
} }
if (titleUpdates.Count == 0) return titleUpdates.Count != 0;
{
return false;
}
return true;
} }
} }
} }

View file

@ -0,0 +1,23 @@
namespace Ryujinx.UI.Common.Models
{
public class DownloadableContentModel
{
public ulong TitleId { get; }
public string ContainerPath { get; }
public string FullPath { get; }
public bool Enabled { get; }
public bool IsBundled { get; }
public string FileName => System.IO.Path.GetFileName(ContainerPath);
public string TitleIdStr => TitleId.ToString("X16");
public DownloadableContentModel(ulong titleId, string containerPath, string fullPath, bool enabled)
{
TitleId = titleId;
ContainerPath = containerPath;
FullPath = fullPath;
Enabled = enabled;
IsBundled = System.IO.Path.GetExtension(containerPath)?.ToLower() == ".xci";
}
}
}

View file

@ -0,0 +1,22 @@
namespace Ryujinx.UI.Common.Models
{
public class TitleUpdateModel
{
public ulong TitleId { get; }
public ulong Version { get; }
public string DisplayVersion { get; }
public string Path { get; }
public bool IsBundled { get; }
public string TitleIdStr => TitleId.ToString("X16");
public TitleUpdateModel(ulong titleId, ulong version, string displayVersion, string path)
{
TitleId = titleId;
Version = version;
DisplayVersion = displayVersion;
Path = path;
IsBundled = System.IO.Path.GetExtension(path)?.ToLower() == ".xci";
}
}
}

View file

@ -0,0 +1,42 @@
using Avalonia;
using Avalonia.Data;
using Avalonia.Data.Converters;
using Ryujinx.Ava.Common.Locale;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace Ryujinx.Ava.UI.Helpers
{
internal class DownloadableContentLabelConverter : IMultiValueConverter
{
public static DownloadableContentLabelConverter Instance = new();
public object Convert(IList<object> values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Any(it => it is UnsetValueType))
{
return BindingOperations.DoNothing;
}
if (values.Count != 2 || !targetType.IsAssignableFrom(typeof(string)))
{
return null;
}
if (values is not [string label, bool isBundled])
{
return null;
}
return isBundled ? $"{LocaleManager.Instance[LocaleKeys.TitleBundledDlcLabel]} {label}" : label;
}
public object[] ConvertBack(object[] values, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View file

@ -0,0 +1,42 @@
using Avalonia;
using Avalonia.Data;
using Avalonia.Data.Converters;
using Ryujinx.Ava.Common.Locale;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace Ryujinx.Ava.UI.Helpers
{
internal class TitleUpdateLabelConverter : IMultiValueConverter
{
public static TitleUpdateLabelConverter Instance = new();
public object Convert(IList<object> values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Any(it => it is UnsetValueType))
{
return BindingOperations.DoNothing;
}
if (values.Count != 2 || !targetType.IsAssignableFrom(typeof(string)))
{
return null;
}
if (values is not [string label, bool isBundled])
{
return null;
}
var key = isBundled ? LocaleKeys.TitleBundledUpdateVersionLabel : LocaleKeys.TitleUpdateVersionLabel;
return LocaleManager.Instance.UpdateAndGetDynamicValue(key, label);
}
public object[] ConvertBack(object[] values, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View file

@ -1,39 +0,0 @@
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.ViewModels;
using System.IO;
namespace Ryujinx.Ava.UI.Models
{
public class DownloadableContentModel : BaseModel
{
private bool _enabled;
public bool Enabled
{
get => _enabled;
set
{
_enabled = value;
OnPropertyChanged();
}
}
public string TitleId { get; }
public string ContainerPath { get; }
public string FullPath { get; }
public string FileName => Path.GetFileName(ContainerPath);
public string Label =>
Path.GetExtension(FileName)?.ToLower() == ".xci" ? $"{LocaleManager.Instance[LocaleKeys.TitleBundledDlcLabel]} {FileName}" : FileName;
public DownloadableContentModel(string titleId, string containerPath, string fullPath, bool enabled)
{
TitleId = titleId;
ContainerPath = containerPath;
FullPath = fullPath;
Enabled = enabled;
}
}
}

View file

@ -1,21 +0,0 @@
using Ryujinx.Ava.Common.Locale;
namespace Ryujinx.Ava.UI.Models
{
public class TitleUpdateModel
{
public uint Version { get; }
public string Path { get; }
public string Label { get; }
public TitleUpdateModel(uint version, string displayVersion, string path)
{
Version = version;
Label = LocaleManager.Instance.UpdateAndGetDynamicValue(
System.IO.Path.GetExtension(path)?.ToLower() == ".xci" ? LocaleKeys.TitleBundledUpdateVersionLabel : LocaleKeys.TitleUpdateVersionLabel,
displayVersion
);
Path = path;
}
}
}

View file

@ -19,6 +19,7 @@ using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.Loaders.Processes.Extensions; using Ryujinx.HLE.Loaders.Processes.Extensions;
using Ryujinx.HLE.Utilities; using Ryujinx.HLE.Utilities;
using Ryujinx.UI.App.Common; using Ryujinx.UI.App.Common;
using Ryujinx.UI.Common.Models;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@ -142,7 +143,7 @@ namespace Ryujinx.Ava.UI.ViewModels
Nca nca = TryOpenNca(ncaFile.Get.AsStorage(), downloadableContentContainer.ContainerPath); Nca nca = TryOpenNca(ncaFile.Get.AsStorage(), downloadableContentContainer.ContainerPath);
if (nca != null) if (nca != null)
{ {
var content = new DownloadableContentModel(nca.Header.TitleId.ToString("X16"), var content = new DownloadableContentModel(nca.Header.TitleId,
downloadableContentContainer.ContainerPath, downloadableContentContainer.ContainerPath,
downloadableContentNca.FullPath, downloadableContentNca.FullPath,
downloadableContentNca.Enabled); downloadableContentNca.Enabled);
@ -183,7 +184,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
if (arg is DownloadableContentModel content) if (arg is DownloadableContentModel content)
{ {
return string.IsNullOrWhiteSpace(_search) || content.FileName.ToLower().Contains(_search.ToLower()) || content.TitleId.ToLower().Contains(_search.ToLower()); return string.IsNullOrWhiteSpace(_search) || content.FileName.ToLower().Contains(_search.ToLower()) || content.TitleIdStr.ToLower().Contains(_search.ToLower());
} }
return false; return false;
@ -261,7 +262,7 @@ namespace Ryujinx.Ava.UI.ViewModels
continue; continue;
} }
var content = new DownloadableContentModel(nca.Header.TitleId.ToString("X16"), path, fileEntry.FullPath, true); var content = new DownloadableContentModel(nca.Header.TitleId, path, fileEntry.FullPath, true);
DownloadableContents.Add(content); DownloadableContents.Add(content);
Dispatcher.UIThread.InvokeAsync(() => SelectedDownloadableContents.Add(content)); Dispatcher.UIThread.InvokeAsync(() => SelectedDownloadableContents.Add(content));
@ -327,7 +328,7 @@ namespace Ryujinx.Ava.UI.ViewModels
container.DownloadableContentNcaList.Add(new DownloadableContentNca container.DownloadableContentNcaList.Add(new DownloadableContentNca
{ {
Enabled = downloadableContent.Enabled, Enabled = downloadableContent.Enabled,
TitleId = Convert.ToUInt64(downloadableContent.TitleId, 16), TitleId = downloadableContent.TitleId,
FullPath = downloadableContent.FullPath, FullPath = downloadableContent.FullPath,
}); });
} }

View file

@ -20,6 +20,7 @@ using Ryujinx.HLE.Loaders.Processes.Extensions;
using Ryujinx.HLE.Utilities; using Ryujinx.HLE.Utilities;
using Ryujinx.UI.App.Common; using Ryujinx.UI.App.Common;
using Ryujinx.UI.Common.Configuration; using Ryujinx.UI.Common.Configuration;
using Ryujinx.UI.Common.Models;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@ -190,7 +191,7 @@ namespace Ryujinx.Ava.UI.ViewModels
nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure(); nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure();
var displayVersion = controlData.DisplayVersionString.ToString(); var displayVersion = controlData.DisplayVersionString.ToString();
var update = new TitleUpdateModel(content.Version.Version, displayVersion, path); var update = new TitleUpdateModel(content.ApplicationId, content.Version.Version, displayVersion, path);
TitleUpdates.Add(update); TitleUpdates.Add(update);

View file

@ -6,13 +6,17 @@
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
xmlns:models="clr-namespace:Ryujinx.Ava.UI.Models" xmlns:models="clr-namespace:Ryujinx.UI.Common.Models;assembly=Ryujinx.UI.Common"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
Width="500" Width="500"
Height="380" Height="380"
mc:Ignorable="d" mc:Ignorable="d"
x:DataType="viewModels:DownloadableContentManagerViewModel" x:DataType="viewModels:DownloadableContentManagerViewModel"
Focusable="True"> Focusable="True">
<UserControl.Resources>
<helpers:DownloadableContentLabelConverter x:Key="DownloadableContentLabel" />
</UserControl.Resources>
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
@ -96,8 +100,14 @@
VerticalAlignment="Center" VerticalAlignment="Center"
MaxLines="2" MaxLines="2"
TextWrapping="Wrap" TextWrapping="Wrap"
TextTrimming="CharacterEllipsis" TextTrimming="CharacterEllipsis">
Text="{Binding Label}" /> <TextBlock.Text>
<MultiBinding Converter="{StaticResource DownloadableContentLabel}">
<Binding Path="FileName" />
<Binding Path="IsBundled" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBlock <TextBlock
Grid.Column="1" Grid.Column="1"
Margin="10 0" Margin="10 0"

View file

@ -8,6 +8,7 @@ using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.UI.App.Common; using Ryujinx.UI.App.Common;
using Ryujinx.UI.Common.Helper; using Ryujinx.UI.Common.Helper;
using Ryujinx.UI.Common.Models;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.Windows namespace Ryujinx.Ava.UI.Windows
@ -92,7 +93,7 @@ namespace Ryujinx.Ava.UI.Windows
if (index != -1) if (index != -1)
{ {
ViewModel.DownloadableContents[index].Enabled = true; // ViewModel.DownloadableContents[index].Enabled = true;
} }
} }
} }
@ -105,7 +106,7 @@ namespace Ryujinx.Ava.UI.Windows
if (index != -1) if (index != -1)
{ {
ViewModel.DownloadableContents[index].Enabled = false; // ViewModel.DownloadableContents[index].Enabled = false;
} }
} }
} }

View file

@ -6,13 +6,17 @@
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
xmlns:models="clr-namespace:Ryujinx.Ava.UI.Models" xmlns:models="clr-namespace:Ryujinx.UI.Common.Models;assembly=Ryujinx.UI.Common"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
Width="500" Width="500"
Height="300" Height="300"
mc:Ignorable="d" mc:Ignorable="d"
x:DataType="viewModels:TitleUpdateViewModel" x:DataType="viewModels:TitleUpdateViewModel"
Focusable="True"> Focusable="True">
<UserControl.Resources>
<helpers:TitleUpdateLabelConverter x:Key="TitleUpdateLabel" />
</UserControl.Resources>
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="*" /> <RowDefinition Height="*" />
@ -38,8 +42,14 @@
<TextBlock <TextBlock
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Center" VerticalAlignment="Center"
TextWrapping="Wrap" TextWrapping="Wrap">
Text="{Binding Label}" /> <TextBlock.Text>
<MultiBinding Converter="{StaticResource TitleUpdateLabel}">
<Binding Path="DisplayVersion" />
<Binding Path="IsBundled" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<StackPanel <StackPanel
Spacing="10" Spacing="10"
Orientation="Horizontal" Orientation="Horizontal"

View file

@ -10,6 +10,7 @@ using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.UI.App.Common; using Ryujinx.UI.App.Common;
using Ryujinx.UI.Common.Helper; using Ryujinx.UI.Common.Helper;
using Ryujinx.UI.Common.Models;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.Windows namespace Ryujinx.Ava.UI.Windows