Move more DLC logic out of view model
This commit is contained in:
parent
a90a6b2786
commit
7850a2b2aa
7 changed files with 160 additions and 47 deletions
|
@ -1,6 +1,6 @@
|
|||
using DynamicData;
|
||||
using LibHac;
|
||||
using LibHac.Common;
|
||||
using LibHac.Common.Keys;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.FsSystem;
|
||||
|
@ -19,9 +19,11 @@ using Ryujinx.HLE.Loaders.Processes.Extensions;
|
|||
using Ryujinx.HLE.Utilities;
|
||||
using Ryujinx.UI.Common.Configuration;
|
||||
using Ryujinx.UI.Common.Configuration.System;
|
||||
using Ryujinx.UI.Common.Helper;
|
||||
using Ryujinx.UI.Common.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
@ -29,6 +31,7 @@ using System.Text;
|
|||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using ContentType = LibHac.Ncm.ContentType;
|
||||
using MissingKeyException = LibHac.Common.Keys.MissingKeyException;
|
||||
using Path = System.IO.Path;
|
||||
using SpanHelpers = LibHac.Common.SpanHelpers;
|
||||
using TimeSpan = System.TimeSpan;
|
||||
|
@ -43,6 +46,10 @@ namespace Ryujinx.UI.App.Common
|
|||
public event EventHandler<TitleUpdateAddedEventArgs> TitleUpdateAdded;
|
||||
public event EventHandler<DownloadableContentAddedEventArgs> DownloadableContentAdded;
|
||||
|
||||
public IObservableCache<ApplicationData, string> Applications;
|
||||
public IObservableCache<(TitleUpdateModel TitleUpdate, bool IsSelected), TitleUpdateModel> TitleUpdates;
|
||||
public IObservableCache<(DownloadableContentModel Dlc, bool IsEnabled), DownloadableContentModel> DownloadableContents;
|
||||
|
||||
private readonly byte[] _nspIcon;
|
||||
private readonly byte[] _xciIcon;
|
||||
private readonly byte[] _ncaIcon;
|
||||
|
@ -52,6 +59,9 @@ namespace Ryujinx.UI.App.Common
|
|||
private readonly VirtualFileSystem _virtualFileSystem;
|
||||
private readonly IntegrityCheckLevel _checkLevel;
|
||||
private CancellationTokenSource _cancellationToken;
|
||||
private readonly SourceCache<ApplicationData, string> _applications = new(it => it.Path);
|
||||
private readonly SourceCache<(TitleUpdateModel TitleUpdate, bool IsSelected), TitleUpdateModel> _titleUpdates = new(it => it.TitleUpdate);
|
||||
private readonly SourceCache<(DownloadableContentModel Dlc, bool IsEnabled), DownloadableContentModel> _downloadableContents = new(it => it.Dlc);
|
||||
|
||||
private static readonly ApplicationJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||
|
||||
|
@ -60,6 +70,10 @@ namespace Ryujinx.UI.App.Common
|
|||
_virtualFileSystem = virtualFileSystem;
|
||||
_checkLevel = checkLevel;
|
||||
|
||||
Applications = _applications.AsObservableCache();
|
||||
TitleUpdates = _titleUpdates.AsObservableCache();
|
||||
DownloadableContents = _downloadableContents.AsObservableCache();
|
||||
|
||||
_nspIcon = GetResourceBytes("Ryujinx.UI.Common.Resources.Icon_NSP.png");
|
||||
_xciIcon = GetResourceBytes("Ryujinx.UI.Common.Resources.Icon_XCI.png");
|
||||
_ncaIcon = GetResourceBytes("Ryujinx.UI.Common.Resources.Icon_NCA.png");
|
||||
|
@ -105,7 +119,7 @@ namespace Ryujinx.UI.App.Common
|
|||
return data;
|
||||
}
|
||||
|
||||
/// <exception cref="MissingKeyException">The configured key set is missing a key.</exception>
|
||||
/// <exception cref="LibHac.Common.Keys.MissingKeyException">The configured key set is missing a key.</exception>
|
||||
/// <exception cref="InvalidDataException">The NCA header could not be decrypted.</exception>
|
||||
/// <exception cref="NotSupportedException">The NCA version is not supported.</exception>
|
||||
/// <exception cref="HorizonResultException">An error occured while reading PFS data.</exception>
|
||||
|
@ -181,7 +195,7 @@ namespace Ryujinx.UI.App.Common
|
|||
return null;
|
||||
}
|
||||
|
||||
/// <exception cref="MissingKeyException">The configured key set is missing a key.</exception>
|
||||
/// <exception cref="LibHac.Common.Keys.MissingKeyException">The configured key set is missing a key.</exception>
|
||||
/// <exception cref="InvalidDataException">The NCA header could not be decrypted.</exception>
|
||||
/// <exception cref="NotSupportedException">The NCA version is not supported.</exception>
|
||||
/// <exception cref="HorizonResultException">An error occured while reading PFS data.</exception>
|
||||
|
@ -638,6 +652,7 @@ namespace Ryujinx.UI.App.Common
|
|||
int numApplicationsLoaded = 0;
|
||||
|
||||
_cancellationToken = new CancellationTokenSource();
|
||||
_applications.Clear();
|
||||
|
||||
// Builds the applications list with paths to found applications
|
||||
List<string> applicationPaths = new();
|
||||
|
@ -723,6 +738,14 @@ namespace Ryujinx.UI.App.Common
|
|||
});
|
||||
}
|
||||
|
||||
_applications.Edit(it =>
|
||||
{
|
||||
foreach (var application in applications)
|
||||
{
|
||||
it.AddOrUpdate(application);
|
||||
}
|
||||
});
|
||||
|
||||
if (applications.Count > 1)
|
||||
{
|
||||
numApplicationsFound += applications.Count - 1;
|
||||
|
@ -755,9 +778,40 @@ namespace Ryujinx.UI.App.Common
|
|||
}
|
||||
}
|
||||
|
||||
public void LoadDownloadableContents(List<string> appDirs)
|
||||
public void LoadDownloadableContents()
|
||||
{
|
||||
_downloadableContents.Edit(it =>
|
||||
{
|
||||
it.Clear();
|
||||
|
||||
foreach (ApplicationData application in Applications.Items)
|
||||
{
|
||||
var res = DownloadableContentsHelper.LoadDownloadableContentsJson(_virtualFileSystem, application.IdBase);
|
||||
it.AddOrUpdate(res);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void SaveDownloadableContentsForGame(ApplicationData application, List<(DownloadableContentModel, bool IsEnabled)> dlcs)
|
||||
{
|
||||
_downloadableContents.Edit(it =>
|
||||
{
|
||||
DownloadableContentsHelper.SaveDownloadableContentsJson(_virtualFileSystem, application.IdBase, dlcs);
|
||||
|
||||
it.Remove(it.Items.Where(item => item.Dlc.TitleIdBase == application.IdBase));
|
||||
it.AddOrUpdate(dlcs);
|
||||
});
|
||||
}
|
||||
|
||||
// public void LoadTitleUpdates()
|
||||
// {
|
||||
//
|
||||
// }
|
||||
|
||||
public void AutoLoadDownloadableContents(List<string> appDirs)
|
||||
{
|
||||
_cancellationToken = new CancellationTokenSource();
|
||||
_downloadableContents.Clear();
|
||||
|
||||
// Builds the applications list with paths to found applications
|
||||
List<string> applicationPaths = new();
|
||||
|
@ -842,6 +896,14 @@ namespace Ryujinx.UI.App.Common
|
|||
DownloadableContent = downloadableContent,
|
||||
});
|
||||
}
|
||||
|
||||
_downloadableContents.Edit(it =>
|
||||
{
|
||||
foreach (var downloadableContent in downloadableContents)
|
||||
{
|
||||
it.AddOrUpdate((downloadableContent, true));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -850,12 +912,12 @@ namespace Ryujinx.UI.App.Common
|
|||
_cancellationToken.Dispose();
|
||||
_cancellationToken = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void LoadTitleUpdates(List<string> appDirs)
|
||||
public void AutoLoadTitleUpdates(List<string> appDirs)
|
||||
{
|
||||
_cancellationToken = new CancellationTokenSource();
|
||||
_titleUpdates.Clear();
|
||||
|
||||
// Builds the applications list with paths to found applications
|
||||
List<string> applicationPaths = new();
|
||||
|
@ -941,6 +1003,14 @@ namespace Ryujinx.UI.App.Common
|
|||
TitleUpdate = titleUpdate,
|
||||
});
|
||||
}
|
||||
|
||||
_titleUpdates.Edit(it =>
|
||||
{
|
||||
foreach (var titleUpdate in titleUpdates)
|
||||
{
|
||||
it.AddOrUpdate((titleUpdate, false));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,9 +20,10 @@ namespace Ryujinx.UI.Common.Helper
|
|||
{
|
||||
private static readonly DownloadableContentJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
|
||||
|
||||
public static List<(DownloadableContentModel, bool IsEnabled)> LoadSavedDownloadableContents(VirtualFileSystem vfs, ulong applicationIdBase)
|
||||
public static List<(DownloadableContentModel, bool IsEnabled)> LoadDownloadableContentsJson(VirtualFileSystem vfs, ulong applicationIdBase)
|
||||
{
|
||||
var downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, applicationIdBase.ToString("X16"), "dlc.json");
|
||||
// _downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, applicationData.IdBaseString, "dlc.json");
|
||||
var downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, applicationIdBase.ToString("x16"), "dlc.json");
|
||||
|
||||
if (!File.Exists(downloadableContentJsonPath))
|
||||
{
|
||||
|
@ -42,6 +43,46 @@ namespace Ryujinx.UI.Common.Helper
|
|||
}
|
||||
}
|
||||
|
||||
public static void SaveDownloadableContentsJson(VirtualFileSystem vfs, ulong applicationIdBase, List<(DownloadableContentModel, bool IsEnabled)> dlcs)
|
||||
{
|
||||
DownloadableContentContainer container = default;
|
||||
List<DownloadableContentContainer> downloadableContentContainerList = new();
|
||||
|
||||
foreach ((DownloadableContentModel dlc, bool isEnabled) in dlcs)
|
||||
{
|
||||
if (container.ContainerPath != dlc.ContainerPath)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(container.ContainerPath))
|
||||
{
|
||||
downloadableContentContainerList.Add(container);
|
||||
}
|
||||
|
||||
container = new DownloadableContentContainer
|
||||
{
|
||||
ContainerPath = dlc.ContainerPath,
|
||||
DownloadableContentNcaList = [],
|
||||
};
|
||||
}
|
||||
|
||||
container.DownloadableContentNcaList.Add(new DownloadableContentNca
|
||||
{
|
||||
Enabled = isEnabled,
|
||||
TitleId = dlc.TitleId,
|
||||
FullPath = dlc.FullPath,
|
||||
});
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(container.ContainerPath))
|
||||
{
|
||||
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");
|
||||
JsonHelper.SerializeToFile(downloadableContentJsonPath, downloadableContentContainerList, _serializerContext.ListDownloadableContentContainer);
|
||||
}
|
||||
|
||||
private static List<(DownloadableContentModel, bool IsEnabled)> LoadDownloadableContents(VirtualFileSystem vfs, List<DownloadableContentContainer> downloadableContentContainers)
|
||||
{
|
||||
var result = new List<(DownloadableContentModel, bool IsEnabled)>();
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace Ryujinx.UI.Common.Models
|
|||
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 string TitleIdStr => TitleId.ToString("x16");
|
||||
public ulong TitleIdBase => TitleId & ~0x1FFFUL;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ namespace Ryujinx.UI.Common.Models
|
|||
{
|
||||
public bool IsBundled { get; } = System.IO.Path.GetExtension(Path)?.ToLower() == ".xci";
|
||||
|
||||
public string TitleIdStr => TitleId.ToString("X16");
|
||||
public string TitleIdStr => TitleId.ToString("x16");
|
||||
public ulong TitleIdBase => TitleId & ~0x1FFFUL;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,6 +56,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DiscordRichPresence" />
|
||||
<PackageReference Include="DynamicData" />
|
||||
<PackageReference Include="securifybv.ShellLink" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -131,8 +131,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
|
||||
private void LoadDownloadableContents()
|
||||
{
|
||||
var savedDlc = DownloadableContentsHelper.LoadSavedDownloadableContents(_virtualFileSystem, _applicationData.IdBase);
|
||||
foreach ((DownloadableContentModel dlc, bool isEnabled) in savedDlc)
|
||||
foreach ((DownloadableContentModel dlc, bool isEnabled) in _applicationLibrary.DownloadableContents.Items.Where(it => it.Dlc.TitleIdBase == _applicationData.IdBase))
|
||||
{
|
||||
DownloadableContents.Add(dlc);
|
||||
|
||||
|
@ -300,40 +299,42 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
|
||||
public void Save()
|
||||
{
|
||||
_downloadableContentContainerList.Clear();
|
||||
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);
|
||||
// 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -653,8 +653,8 @@ namespace Ryujinx.Ava.UI.Windows
|
|||
{
|
||||
ApplicationLibrary.DesiredLanguage = ConfigurationState.Instance.System.Language;
|
||||
TimeIt("games", () => ApplicationLibrary.LoadApplications(ConfigurationState.Instance.UI.GameDirs));
|
||||
TimeIt("updates", () => ApplicationLibrary.LoadTitleUpdates(ConfigurationState.Instance.UI.GameDirs));
|
||||
TimeIt("DLC", () => ApplicationLibrary.LoadDownloadableContents(ConfigurationState.Instance.UI.GameDirs));
|
||||
// TimeIt("updates", () => ApplicationLibrary.LoadTitleUpdates(ConfigurationState.Instance.UI.GameDirs));
|
||||
TimeIt("DLC", () => ApplicationLibrary.LoadDownloadableContents());
|
||||
|
||||
_isLoading = false;
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue