Make dlc/updates records; use ApplicationLibrary for loading logic
This commit is contained in:
parent
8073b8d189
commit
48b7517284
9 changed files with 77 additions and 125 deletions
|
@ -514,7 +514,7 @@ namespace Ryujinx.UI.App.Common
|
|||
|
||||
if (nca.Header.ContentType == NcaContentType.PublicData)
|
||||
{
|
||||
titleUpdates.Add(new DownloadableContentModel(nca.Header.TitleId, filePath, fileEntry.FullPath, false));
|
||||
titleUpdates.Add(new DownloadableContentModel(nca.Header.TitleId, filePath, fileEntry.FullPath));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1294,7 +1294,7 @@ namespace Ryujinx.UI.App.Common
|
|||
{
|
||||
return new Nca(_virtualFileSystem.KeySet, ncaStorage);
|
||||
}
|
||||
catch (Exception ex) { }
|
||||
catch (Exception) { }
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -1,24 +1,11 @@
|
|||
namespace Ryujinx.UI.Common.Models
|
||||
{
|
||||
public class DownloadableContentModel
|
||||
public record DownloadableContentModel(ulong TitleId, string ContainerPath, string FullPath)
|
||||
{
|
||||
public ulong TitleId { get; }
|
||||
public string ContainerPath { get; }
|
||||
public string FullPath { get; }
|
||||
public bool Enabled { get; }
|
||||
public bool IsBundled { get; }
|
||||
public bool IsBundled { get; } = System.IO.Path.GetExtension(ContainerPath)?.ToLower() == ".xci";
|
||||
|
||||
public string FileName => System.IO.Path.GetFileName(ContainerPath);
|
||||
public string TitleIdStr => TitleId.ToString("X16");
|
||||
public ulong TitleIdBase => TitleId & ~0x1FFFUL;
|
||||
|
||||
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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,23 +1,10 @@
|
|||
namespace Ryujinx.UI.Common.Models
|
||||
{
|
||||
public class TitleUpdateModel
|
||||
public record TitleUpdateModel(ulong TitleId, ulong Version, string DisplayVersion, string Path)
|
||||
{
|
||||
public ulong TitleId { get; }
|
||||
public ulong Version { get; }
|
||||
public string DisplayVersion { get; }
|
||||
public string Path { get; }
|
||||
public bool IsBundled { get; }
|
||||
|
||||
public bool IsBundled { get; } = System.IO.Path.GetExtension(Path)?.ToLower() == ".xci";
|
||||
|
||||
public string TitleIdStr => TitleId.ToString("X16");
|
||||
public ulong TitleIdBase => TitleId & ~0x1FFFUL;
|
||||
|
||||
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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ namespace Ryujinx.Ava.UI.Controls
|
|||
|
||||
if (viewModel?.SelectedApplication != null)
|
||||
{
|
||||
await TitleUpdateWindow.Show(viewModel.VirtualFileSystem, viewModel.SelectedApplication);
|
||||
await TitleUpdateWindow.Show(viewModel.VirtualFileSystem, viewModel.ApplicationLibrary, viewModel.SelectedApplication);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,7 +96,7 @@ namespace Ryujinx.Ava.UI.Controls
|
|||
|
||||
if (viewModel?.SelectedApplication != null)
|
||||
{
|
||||
await DownloadableContentManagerWindow.Show(viewModel.VirtualFileSystem, viewModel.SelectedApplication);
|
||||
await DownloadableContentManagerWindow.Show(viewModel.VirtualFileSystem, viewModel.ApplicationLibrary, viewModel.SelectedApplication);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
private readonly string _downloadableContentJsonPath;
|
||||
|
||||
private readonly VirtualFileSystem _virtualFileSystem;
|
||||
private readonly ApplicationLibrary _applicationLibrary;
|
||||
private AvaloniaList<DownloadableContentModel> _downloadableContents = new();
|
||||
private AvaloniaList<DownloadableContentModel> _views = new();
|
||||
private AvaloniaList<DownloadableContentModel> _selectedDownloadableContents = new();
|
||||
|
@ -93,9 +94,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
get => string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowHeading], DownloadableContents.Count);
|
||||
}
|
||||
|
||||
public DownloadableContentManagerViewModel(VirtualFileSystem virtualFileSystem, ApplicationData applicationData)
|
||||
public DownloadableContentManagerViewModel(VirtualFileSystem virtualFileSystem, ApplicationLibrary applicationLibrary, ApplicationData applicationData)
|
||||
{
|
||||
_virtualFileSystem = virtualFileSystem;
|
||||
_applicationLibrary = applicationLibrary;
|
||||
|
||||
_applicationData = applicationData;
|
||||
|
||||
|
@ -145,12 +147,11 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
{
|
||||
var content = new DownloadableContentModel(nca.Header.TitleId,
|
||||
downloadableContentContainer.ContainerPath,
|
||||
downloadableContentNca.FullPath,
|
||||
downloadableContentNca.Enabled);
|
||||
downloadableContentNca.FullPath);
|
||||
|
||||
DownloadableContents.Add(content);
|
||||
|
||||
if (content.Enabled)
|
||||
if (downloadableContentNca.Enabled)
|
||||
{
|
||||
SelectedDownloadableContents.Add(content);
|
||||
}
|
||||
|
@ -240,34 +241,23 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
return true;
|
||||
}
|
||||
|
||||
using IFileSystem partitionFileSystem = PartitionFileSystemUtils.OpenApplicationFileSystem(path, _virtualFileSystem);
|
||||
if (!_applicationLibrary.TryGetDownloadableContentFromFile(path, out var dlcs))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
foreach (DirectoryEntryEx fileEntry in partitionFileSystem.EnumerateEntries("/", "*.nca"))
|
||||
foreach (var dlc in dlcs)
|
||||
{
|
||||
using var ncaFile = new UniqueRef<IFile>();
|
||||
|
||||
partitionFileSystem.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||
|
||||
Nca nca = TryOpenNca(ncaFile.Get.AsStorage(), path);
|
||||
if (nca == null)
|
||||
if (dlc.TitleIdBase != _applicationData.IdBase)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nca.Header.ContentType == NcaContentType.PublicData)
|
||||
{
|
||||
if (nca.GetProgramIdBase() != _applicationData.IdBase)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
DownloadableContents.Add(dlc);
|
||||
Dispatcher.UIThread.InvokeAsync(() => SelectedDownloadableContents.Add(dlc));
|
||||
|
||||
var content = new DownloadableContentModel(nca.Header.TitleId, path, fileEntry.FullPath, true);
|
||||
DownloadableContents.Add(content);
|
||||
Dispatcher.UIThread.InvokeAsync(() => SelectedDownloadableContents.Add(content));
|
||||
|
||||
success = true;
|
||||
}
|
||||
success = true;
|
||||
}
|
||||
|
||||
if (success)
|
||||
|
@ -282,6 +272,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
public void Remove(DownloadableContentModel model)
|
||||
{
|
||||
DownloadableContents.Remove(model);
|
||||
SelectedDownloadableContents.Remove(model);
|
||||
OnPropertyChanged(nameof(UpdateCount));
|
||||
Sort();
|
||||
}
|
||||
|
@ -289,13 +280,15 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
public void RemoveAll()
|
||||
{
|
||||
DownloadableContents.Clear();
|
||||
SelectedDownloadableContents.Clear();
|
||||
OnPropertyChanged(nameof(UpdateCount));
|
||||
Sort();
|
||||
}
|
||||
|
||||
public void EnableAll()
|
||||
{
|
||||
SelectedDownloadableContents = new(DownloadableContents);
|
||||
SelectedDownloadableContents.Clear();
|
||||
SelectedDownloadableContents.AddRange(DownloadableContents);
|
||||
}
|
||||
|
||||
public void DisableAll()
|
||||
|
@ -303,6 +296,16 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
SelectedDownloadableContents.Clear();
|
||||
}
|
||||
|
||||
public void Enable(DownloadableContentModel model)
|
||||
{
|
||||
SelectedDownloadableContents.ReplaceOrAdd(model, model);
|
||||
}
|
||||
|
||||
public void Disable(DownloadableContentModel model)
|
||||
{
|
||||
SelectedDownloadableContents.Remove(model);
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
_downloadableContentContainerList.Clear();
|
||||
|
@ -327,7 +330,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
|
||||
container.DownloadableContentNcaList.Add(new DownloadableContentNca
|
||||
{
|
||||
Enabled = downloadableContent.Enabled,
|
||||
Enabled = SelectedDownloadableContents.Contains(downloadableContent),
|
||||
TitleId = downloadableContent.TitleId,
|
||||
FullPath = downloadableContent.FullPath,
|
||||
});
|
||||
|
|
|
@ -38,6 +38,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
public TitleUpdateMetadata TitleUpdateWindowData;
|
||||
public readonly string TitleUpdateJsonPath;
|
||||
private VirtualFileSystem VirtualFileSystem { get; }
|
||||
private ApplicationLibrary ApplicationLibrary { get; }
|
||||
private ApplicationData ApplicationData { get; }
|
||||
|
||||
private AvaloniaList<TitleUpdateModel> _titleUpdates = new();
|
||||
|
@ -78,9 +79,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
|
||||
public IStorageProvider StorageProvider;
|
||||
|
||||
public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ApplicationData applicationData)
|
||||
public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ApplicationLibrary applicationLibrary, ApplicationData applicationData)
|
||||
{
|
||||
VirtualFileSystem = virtualFileSystem;
|
||||
ApplicationLibrary = applicationLibrary;
|
||||
|
||||
ApplicationData = applicationData;
|
||||
|
||||
|
@ -161,54 +163,36 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
|
||||
? IntegrityCheckLevel.ErrorOnInvalid
|
||||
: IntegrityCheckLevel.None;
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
using IFileSystem pfs = PartitionFileSystemUtils.OpenApplicationFileSystem(path, VirtualFileSystem);
|
||||
|
||||
Dictionary<ulong, ContentMetaData> updates = pfs.GetContentData(ContentMetaType.Patch, VirtualFileSystem, checkLevel);
|
||||
|
||||
Nca patchNca = null;
|
||||
Nca controlNca = null;
|
||||
|
||||
if (updates.TryGetValue(ApplicationData.Id, out ContentMetaData content))
|
||||
{
|
||||
patchNca = content.GetNcaByType(VirtualFileSystem.KeySet, ContentType.Program);
|
||||
controlNca = content.GetNcaByType(VirtualFileSystem.KeySet, ContentType.Control);
|
||||
}
|
||||
|
||||
if (controlNca != null && patchNca != null)
|
||||
{
|
||||
ApplicationControlProperty controlData = new();
|
||||
|
||||
using UniqueRef<IFile> nacpFile = 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();
|
||||
|
||||
var displayVersion = controlData.DisplayVersionString.ToString();
|
||||
var update = new TitleUpdateModel(content.ApplicationId, content.Version.Version, displayVersion, path);
|
||||
|
||||
TitleUpdates.Add(update);
|
||||
|
||||
if (selected)
|
||||
{
|
||||
Dispatcher.UIThread.InvokeAsync(() => SelectedUpdate = update);
|
||||
}
|
||||
}
|
||||
else
|
||||
if (!ApplicationLibrary.TryGetTitleUpdatesFromFile(path, out var titleUpdates))
|
||||
{
|
||||
if (!ignoreNotFound)
|
||||
{
|
||||
Dispatcher.UIThread.InvokeAsync(() => ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]));
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
ContentDialogHelper.CreateErrorDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var titleUpdate in titleUpdates)
|
||||
{
|
||||
if (titleUpdate.TitleIdBase != ApplicationData.Id)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
TitleUpdates.Add(titleUpdate);
|
||||
|
||||
if (selected)
|
||||
{
|
||||
Dispatcher.UIThread.InvokeAsync(() => SelectedUpdate = titleUpdate);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
} catch (Exception ex)
|
||||
{
|
||||
Dispatcher.UIThread.InvokeAsync(() => ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadFileErrorMessage, ex.Message, path)));
|
||||
}
|
||||
|
@ -254,7 +238,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
{
|
||||
TitleUpdateWindowData.Paths.Add(update.Path);
|
||||
|
||||
if (update == SelectedUpdate)
|
||||
if (update == SelectedUpdate as TitleUpdateModel)
|
||||
{
|
||||
TitleUpdateWindowData.Selected = update.Path;
|
||||
}
|
||||
|
|
|
@ -77,8 +77,9 @@
|
|||
SelectionMode="Multiple, Toggle"
|
||||
Background="Transparent"
|
||||
SelectionChanged="OnSelectionChanged"
|
||||
SelectedItems="{Binding SelectedDownloadableContents, Mode=TwoWay}"
|
||||
SelectedItems="{Binding SelectedDownloadableContents}"
|
||||
ItemsSource="{Binding Views}">
|
||||
<!-- SelectedItems="{Binding SelectedDownloadableContents, Mode=TwoWay}" -->
|
||||
<ListBox.DataTemplates>
|
||||
<DataTemplate
|
||||
DataType="models:DownloadableContentModel">
|
||||
|
|
|
@ -24,21 +24,21 @@ namespace Ryujinx.Ava.UI.Windows
|
|||
InitializeComponent();
|
||||
}
|
||||
|
||||
public DownloadableContentManagerWindow(VirtualFileSystem virtualFileSystem, ApplicationData applicationData)
|
||||
public DownloadableContentManagerWindow(VirtualFileSystem virtualFileSystem, ApplicationLibrary applicationLibrary, ApplicationData applicationData)
|
||||
{
|
||||
DataContext = ViewModel = new DownloadableContentManagerViewModel(virtualFileSystem, applicationData);
|
||||
DataContext = ViewModel = new DownloadableContentManagerViewModel(virtualFileSystem, applicationLibrary, applicationData);
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public static async Task Show(VirtualFileSystem virtualFileSystem, ApplicationData applicationData)
|
||||
public static async Task Show(VirtualFileSystem virtualFileSystem, ApplicationLibrary applicationLibrary, ApplicationData applicationData)
|
||||
{
|
||||
ContentDialog contentDialog = new()
|
||||
{
|
||||
PrimaryButtonText = "",
|
||||
SecondaryButtonText = "",
|
||||
CloseButtonText = "",
|
||||
Content = new DownloadableContentManagerWindow(virtualFileSystem, applicationData),
|
||||
Content = new DownloadableContentManagerWindow(virtualFileSystem, applicationLibrary, applicationData),
|
||||
Title = string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowTitle], applicationData.Name, applicationData.IdBaseString),
|
||||
};
|
||||
|
||||
|
@ -89,12 +89,7 @@ namespace Ryujinx.Ava.UI.Windows
|
|||
{
|
||||
if (content is DownloadableContentModel model)
|
||||
{
|
||||
var index = ViewModel.DownloadableContents.IndexOf(model);
|
||||
|
||||
if (index != -1)
|
||||
{
|
||||
// ViewModel.DownloadableContents[index].Enabled = true;
|
||||
}
|
||||
ViewModel.Enable(model);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,12 +97,7 @@ namespace Ryujinx.Ava.UI.Windows
|
|||
{
|
||||
if (content is DownloadableContentModel model)
|
||||
{
|
||||
var index = ViewModel.DownloadableContents.IndexOf(model);
|
||||
|
||||
if (index != -1)
|
||||
{
|
||||
// ViewModel.DownloadableContents[index].Enabled = false;
|
||||
}
|
||||
ViewModel.Disable(model);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,21 +26,21 @@ namespace Ryujinx.Ava.UI.Windows
|
|||
InitializeComponent();
|
||||
}
|
||||
|
||||
public TitleUpdateWindow(VirtualFileSystem virtualFileSystem, ApplicationData applicationData)
|
||||
public TitleUpdateWindow(VirtualFileSystem virtualFileSystem, ApplicationLibrary applicationLibrary, ApplicationData applicationData)
|
||||
{
|
||||
DataContext = ViewModel = new TitleUpdateViewModel(virtualFileSystem, applicationData);
|
||||
DataContext = ViewModel = new TitleUpdateViewModel(virtualFileSystem, applicationLibrary, applicationData);
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public static async Task Show(VirtualFileSystem virtualFileSystem, ApplicationData applicationData)
|
||||
public static async Task Show(VirtualFileSystem virtualFileSystem, ApplicationLibrary applicationLibrary, ApplicationData applicationData)
|
||||
{
|
||||
ContentDialog contentDialog = new()
|
||||
{
|
||||
PrimaryButtonText = "",
|
||||
SecondaryButtonText = "",
|
||||
CloseButtonText = "",
|
||||
Content = new TitleUpdateWindow(virtualFileSystem, applicationData),
|
||||
Content = new TitleUpdateWindow(virtualFileSystem, applicationLibrary, applicationData),
|
||||
Title = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.GameUpdateWindowHeading, applicationData.Name, applicationData.IdBaseString),
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue