Refactor more logic out of DLC manager VM
This commit is contained in:
parent
1eb7146b90
commit
57de6a7dc5
8 changed files with 87 additions and 130 deletions
|
@ -22,8 +22,7 @@ namespace Ryujinx.UI.Common.Helper
|
|||
|
||||
public static List<(DownloadableContentModel, bool IsEnabled)> LoadDownloadableContentsJson(VirtualFileSystem vfs, ulong applicationIdBase)
|
||||
{
|
||||
// _downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, applicationData.IdBaseString, "dlc.json");
|
||||
var downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, applicationIdBase.ToString("x16"), "dlc.json");
|
||||
var downloadableContentJsonPath = PathToGameDLCJson(applicationIdBase);
|
||||
|
||||
if (!File.Exists(downloadableContentJsonPath))
|
||||
{
|
||||
|
@ -77,9 +76,7 @@ namespace Ryujinx.UI.Common.Helper
|
|||
downloadableContentContainerList.Add(container);
|
||||
}
|
||||
|
||||
// _downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, applicationData.IdBaseString, "dlc.json");
|
||||
// var downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, applicationIdBase.ToString("x16"), "dlc.json");
|
||||
var downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, applicationIdBase.ToString("x16"), "dlc.json");
|
||||
var downloadableContentJsonPath = PathToGameDLCJson(applicationIdBase);
|
||||
JsonHelper.SerializeToFile(downloadableContentJsonPath, downloadableContentContainerList, _serializerContext.ListDownloadableContentContainer);
|
||||
}
|
||||
|
||||
|
@ -102,11 +99,9 @@ namespace Ryujinx.UI.Common.Helper
|
|||
|
||||
partitionFileSystem.OpenFile(ref ncaFile.Ref, downloadableContentNca.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||
|
||||
// Nca nca = TryOpenNca(vfs, ncaFile.Get.AsStorage(), downloadableContentContainer.ContainerPath);
|
||||
Nca nca = TryOpenNca(vfs, ncaFile.Get.AsStorage());
|
||||
if (nca == null)
|
||||
{
|
||||
// result.Add((content, downloadableContentNca.Enabled));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -115,13 +110,6 @@ namespace Ryujinx.UI.Common.Helper
|
|||
downloadableContentNca.FullPath);
|
||||
|
||||
result.Add((content, downloadableContentNca.Enabled));
|
||||
|
||||
// if (downloadableContentNca.Enabled)
|
||||
// {
|
||||
// SelectedDownloadableContents.Add(content);
|
||||
// }
|
||||
|
||||
// OnPropertyChanged(nameof(UpdateCount));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -136,6 +124,7 @@ namespace Ryujinx.UI.Common.Helper
|
|||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// TODO(jpr): emit failure
|
||||
// Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
// {
|
||||
// await ContentDialogHelper.CreateErrorDialog(string.Format(LocaleManager.Instance[LocaleKeys.DialogLoadFileErrorMessage], ex.Message, containerPath));
|
||||
|
@ -144,5 +133,10 @@ namespace Ryujinx.UI.Common.Helper
|
|||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string PathToGameDLCJson(ulong applicationIdBase)
|
||||
{
|
||||
return Path.Combine(AppDataManager.GamesDirPath, applicationIdBase.ToString("x16"), "dlc.json");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
namespace Ryujinx.UI.Common.Models
|
||||
{
|
||||
// NOTE: most consuming code relies on this model being value-comparable
|
||||
public record DownloadableContentModel(ulong TitleId, string ContainerPath, string FullPath)
|
||||
{
|
||||
public bool IsBundled { get; } = System.IO.Path.GetExtension(ContainerPath)?.ToLower() == ".xci";
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
namespace Ryujinx.UI.Common.Models
|
||||
{
|
||||
// NOTE: most consuming code relies on this model being value-comparable
|
||||
public record TitleUpdateModel(ulong TitleId, ulong Version, string DisplayVersion, string Path)
|
||||
{
|
||||
public bool IsBundled { get; } = System.IO.Path.GetExtension(Path)?.ToLower() == ".xci";
|
||||
|
|
|
@ -711,7 +711,9 @@
|
|||
"UpdateWindowTitle": "Title Update Manager",
|
||||
"CheatWindowHeading": "Cheats Available for {0} [{1}]",
|
||||
"BuildId": "BuildId:",
|
||||
"DlcWindowBundledContentNotice": "Bundled DLC cannot be removed, only disabled.",
|
||||
"DlcWindowHeading": "{0} Downloadable Content(s)",
|
||||
"DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"ModWindowHeading": "{0} Mod(s)",
|
||||
"UserProfilesEditProfile": "Edit Selected",
|
||||
"Cancel": "Cancel",
|
||||
|
|
|
@ -5,5 +5,6 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||
List,
|
||||
Grid,
|
||||
Chip,
|
||||
Important,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||
{ Glyph.List, char.ConvertFromUtf32((int)Symbol.List) },
|
||||
{ Glyph.Grid, char.ConvertFromUtf32((int)Symbol.ViewAll) },
|
||||
{ Glyph.Chip, char.ConvertFromUtf32(59748) },
|
||||
{ Glyph.Important, char.ConvertFromUtf32((int)Symbol.Important) },
|
||||
};
|
||||
|
||||
public GlyphValueConverter(string key)
|
||||
|
|
|
@ -3,39 +3,22 @@ using Avalonia.Controls.ApplicationLifetimes;
|
|||
using Avalonia.Platform.Storage;
|
||||
using Avalonia.Threading;
|
||||
using DynamicData;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.Tools.Fs;
|
||||
using LibHac.Tools.FsSystem;
|
||||
using LibHac.Tools.FsSystem.NcaUtils;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.Models;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.Loaders.Processes.Extensions;
|
||||
using Ryujinx.HLE.Utilities;
|
||||
using Ryujinx.UI.App.Common;
|
||||
using Ryujinx.UI.Common.Helper;
|
||||
using Ryujinx.UI.Common.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Application = Avalonia.Application;
|
||||
using Path = System.IO.Path;
|
||||
|
||||
namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
public class DownloadableContentManagerViewModel : BaseModel
|
||||
{
|
||||
private readonly List<DownloadableContentContainer> _downloadableContentContainerList;
|
||||
private readonly string _downloadableContentJsonPath;
|
||||
|
||||
private readonly VirtualFileSystem _virtualFileSystem;
|
||||
private readonly ApplicationLibrary _applicationLibrary;
|
||||
private AvaloniaList<DownloadableContentModel> _downloadableContents = new();
|
||||
private AvaloniaList<DownloadableContentModel> _views = new();
|
||||
|
@ -45,8 +28,6 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
private readonly ApplicationData _applicationData;
|
||||
private readonly IStorageProvider _storageProvider;
|
||||
|
||||
private static readonly DownloadableContentJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||
|
||||
public AvaloniaList<DownloadableContentModel> DownloadableContents
|
||||
{
|
||||
get => _downloadableContents;
|
||||
|
@ -97,7 +78,6 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
|
||||
public DownloadableContentManagerViewModel(VirtualFileSystem virtualFileSystem, ApplicationLibrary applicationLibrary, ApplicationData applicationData)
|
||||
{
|
||||
_virtualFileSystem = virtualFileSystem;
|
||||
_applicationLibrary = applicationLibrary;
|
||||
|
||||
_applicationData = applicationData;
|
||||
|
@ -107,31 +87,14 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
_storageProvider = desktop.MainWindow.StorageProvider;
|
||||
}
|
||||
|
||||
_downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, applicationData.IdBaseString, "dlc.json");
|
||||
|
||||
if (!File.Exists(_downloadableContentJsonPath))
|
||||
{
|
||||
_downloadableContentContainerList = new List<DownloadableContentContainer>();
|
||||
|
||||
Save();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_downloadableContentContainerList = JsonHelper.DeserializeFromFile(_downloadableContentJsonPath, _serializerContext.ListDownloadableContentContainer);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Configuration, "Downloadable Content JSON failed to deserialize.");
|
||||
_downloadableContentContainerList = new List<DownloadableContentContainer>();
|
||||
}
|
||||
|
||||
LoadDownloadableContents();
|
||||
}
|
||||
|
||||
private void LoadDownloadableContents()
|
||||
{
|
||||
foreach ((DownloadableContentModel dlc, bool isEnabled) in _applicationLibrary.DownloadableContents.Items.Where(it => it.Dlc.TitleIdBase == _applicationData.IdBase))
|
||||
var dlcs = _applicationLibrary.DownloadableContents.Items
|
||||
.Where(it => it.Dlc.TitleIdBase == _applicationData.IdBase);
|
||||
foreach ((DownloadableContentModel dlc, bool isEnabled) in dlcs)
|
||||
{
|
||||
DownloadableContents.Add(dlc);
|
||||
|
||||
|
@ -144,7 +107,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
}
|
||||
|
||||
// NOTE: Try to load downloadable contents from PFS last to preserve enabled state.
|
||||
AddDownloadableContent(_applicationData.Path);
|
||||
if (AddDownloadableContent(_applicationData.Path, out var newDlc) && newDlc > 0)
|
||||
{
|
||||
ShowNewDlcAddedDialog(newDlc);
|
||||
}
|
||||
|
||||
// NOTE: Save the list again to remove leftovers.
|
||||
Save();
|
||||
|
@ -153,7 +119,11 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
|
||||
public void Sort()
|
||||
{
|
||||
DownloadableContents.AsObservableChangeSet()
|
||||
DownloadableContents
|
||||
// Sort bundled last
|
||||
.OrderBy(it => it.IsBundled ? 0 : 1)
|
||||
.ThenBy(it => it.TitleId)
|
||||
.AsObservableChangeSet()
|
||||
.Filter(Filter)
|
||||
.Bind(out var view).AsObservableList();
|
||||
|
||||
|
@ -182,23 +152,6 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
return false;
|
||||
}
|
||||
|
||||
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.DialogLoadFileErrorMessage], ex.Message, containerPath));
|
||||
});
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public async void Add()
|
||||
{
|
||||
var result = await _storageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
|
||||
|
@ -216,20 +169,30 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
},
|
||||
});
|
||||
|
||||
var totalDlcAdded = 0;
|
||||
foreach (var file in result)
|
||||
{
|
||||
if (!AddDownloadableContent(file.Path.LocalPath))
|
||||
if (!AddDownloadableContent(file.Path.LocalPath, out var newDlcAdded))
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogDlcNoDlcErrorMessage]);
|
||||
}
|
||||
|
||||
totalDlcAdded += newDlcAdded;
|
||||
}
|
||||
|
||||
if (totalDlcAdded > 0)
|
||||
{
|
||||
await ShowNewDlcAddedDialog(0);
|
||||
}
|
||||
}
|
||||
|
||||
private bool AddDownloadableContent(string path)
|
||||
private bool AddDownloadableContent(string path, out int numDlcAdded)
|
||||
{
|
||||
if (!File.Exists(path) || _downloadableContentContainerList.Any(x => x.ContainerPath == path))
|
||||
numDlcAdded = 0;
|
||||
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_applicationLibrary.TryGetDownloadableContentFromFile(path, out var dlcs))
|
||||
|
@ -237,41 +200,43 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
return false;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
foreach (var dlc in dlcs)
|
||||
foreach (var dlc in dlcs.Where(dlc => dlc.TitleIdBase == _applicationData.IdBase))
|
||||
{
|
||||
if (dlc.TitleIdBase != _applicationData.IdBase)
|
||||
if (!DownloadableContents.Contains(dlc))
|
||||
{
|
||||
continue;
|
||||
DownloadableContents.Add(dlc);
|
||||
Dispatcher.UIThread.InvokeAsync(() => SelectedDownloadableContents.ReplaceOrAdd(dlc, dlc));
|
||||
|
||||
numDlcAdded++;
|
||||
}
|
||||
|
||||
DownloadableContents.Add(dlc);
|
||||
Dispatcher.UIThread.InvokeAsync(() => SelectedDownloadableContents.Add(dlc));
|
||||
|
||||
success = true;
|
||||
}
|
||||
|
||||
if (success)
|
||||
if (numDlcAdded > 0)
|
||||
{
|
||||
OnPropertyChanged(nameof(UpdateCount));
|
||||
Sort();
|
||||
}
|
||||
|
||||
return success;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Remove(DownloadableContentModel model)
|
||||
{
|
||||
DownloadableContents.Remove(model);
|
||||
SelectedDownloadableContents.Remove(model);
|
||||
OnPropertyChanged(nameof(UpdateCount));
|
||||
Sort();
|
||||
|
||||
if (!model.IsBundled)
|
||||
{
|
||||
DownloadableContents.Remove(model);
|
||||
OnPropertyChanged(nameof(UpdateCount));
|
||||
Sort();
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveAll()
|
||||
{
|
||||
DownloadableContents.Clear();
|
||||
SelectedDownloadableContents.Clear();
|
||||
DownloadableContents.RemoveMany(DownloadableContents.Where(it => !it.IsBundled));
|
||||
|
||||
OnPropertyChanged(nameof(UpdateCount));
|
||||
Sort();
|
||||
}
|
||||
|
@ -301,40 +266,15 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
{
|
||||
var dlcs = DownloadableContents.Select(it => (it, SelectedDownloadableContents.Contains(it))).ToList();
|
||||
_applicationLibrary.SaveDownloadableContentsForGame(_applicationData, dlcs);
|
||||
// _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<DownloadableContentNca>(),
|
||||
// };
|
||||
// }
|
||||
//
|
||||
// container.DownloadableContentNcaList.Add(new DownloadableContentNca
|
||||
// {
|
||||
// Enabled = SelectedDownloadableContents.Contains(downloadableContent),
|
||||
// TitleId = downloadableContent.TitleId,
|
||||
// FullPath = downloadableContent.FullPath,
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// if (!string.IsNullOrWhiteSpace(container.ContainerPath))
|
||||
// {
|
||||
// _downloadableContentContainerList.Add(container);
|
||||
// }
|
||||
//
|
||||
// JsonHelper.SerializeToFile(_downloadableContentJsonPath, _downloadableContentContainerList, _serializerContext.ListDownloadableContentContainer);
|
||||
private Task ShowNewDlcAddedDialog(int numAdded)
|
||||
{
|
||||
var msg = string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowDlcAddedMessage], numAdded);
|
||||
return Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
await ContentDialogHelper.ShowTextDialog(LocaleManager.Instance[LocaleKeys.DialogConfirmationTitle], msg, "", "", "", LocaleManager.Instance[LocaleKeys.InputDialogOk], (int)Symbol.Checkmark);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,13 +19,30 @@
|
|||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel
|
||||
Grid.Row="0"
|
||||
Margin="0 0 0 10"
|
||||
Spacing="5"
|
||||
Orientation="Horizontal">
|
||||
<ui:FontIcon
|
||||
Margin="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
FontFamily="avares://FluentAvalonia/Fonts#Symbols"
|
||||
Glyph="{helpers:GlyphValueConverter Important}" />
|
||||
<!-- NOTE: aligning to bottom for better visual alignment with glyph -->
|
||||
<TextBlock
|
||||
FontStyle="Italic"
|
||||
VerticalAlignment="Bottom"
|
||||
Text="{locale:Locale DlcWindowBundledContentNotice}" />
|
||||
</StackPanel>
|
||||
<Panel
|
||||
Margin="0 0 0 10"
|
||||
Grid.Row="0">
|
||||
Grid.Row="1">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
|
@ -64,7 +81,7 @@
|
|||
</Grid>
|
||||
</Panel>
|
||||
<Border
|
||||
Grid.Row="1"
|
||||
Grid.Row="2"
|
||||
Margin="0 0 0 24"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
|
@ -157,7 +174,7 @@
|
|||
</ListBox>
|
||||
</Border>
|
||||
<Panel
|
||||
Grid.Row="2"
|
||||
Grid.Row="3"
|
||||
HorizontalAlignment="Stretch">
|
||||
<StackPanel
|
||||
Orientation="Horizontal"
|
||||
|
|
Loading…
Reference in a new issue