Autoload DLC

This commit is contained in:
Jimmy Reichley 2024-08-17 17:10:27 -04:00
parent 867bc7021f
commit a381cea311
No known key found for this signature in database
GPG Key ID: 67715DC5A329803C
3 changed files with 166 additions and 137 deletions

View File

@ -1,4 +1,5 @@
using DynamicData; using DynamicData;
using DynamicData.Kernel;
using LibHac; using LibHac;
using LibHac.Common; using LibHac.Common;
using LibHac.Fs; using LibHac.Fs;
@ -813,6 +814,12 @@ namespace Ryujinx.UI.App.Common
}); });
} }
private void SaveDownloadableContentsForGame(ulong titleIdBase)
{
var dlcs = DownloadableContents.Items.Where(dlc => dlc.Dlc.TitleIdBase == titleIdBase).ToList();
DownloadableContentsHelper.SaveDownloadableContentsJson(_virtualFileSystem, titleIdBase, dlcs);
}
public void SaveDownloadableContentsForGame(ApplicationData application, List<(DownloadableContentModel, bool IsEnabled)> dlcs) public void SaveDownloadableContentsForGame(ApplicationData application, List<(DownloadableContentModel, bool IsEnabled)> dlcs)
{ {
_downloadableContents.Edit(it => _downloadableContents.Edit(it =>
@ -824,18 +831,12 @@ namespace Ryujinx.UI.App.Common
}); });
} }
// public void LoadTitleUpdates() public int AutoLoadDownloadableContents(List<string> appDirs)
// {
//
// }
public void AutoLoadDownloadableContents(List<string> appDirs)
{ {
_cancellationToken = new CancellationTokenSource(); _cancellationToken = new CancellationTokenSource();
_downloadableContents.Clear();
// Builds the applications list with paths to found applications List<string> dlcPaths = new();
List<string> applicationPaths = new(); int newDlcLoaded = 0;
try try
{ {
@ -843,7 +844,7 @@ namespace Ryujinx.UI.App.Common
{ {
if (_cancellationToken.Token.IsCancellationRequested) if (_cancellationToken.Token.IsCancellationRequested)
{ {
return; return newDlcLoaded;
} }
if (!Directory.Exists(appDir)) if (!Directory.Exists(appDir))
@ -867,16 +868,14 @@ namespace Ryujinx.UI.App.Common
{ {
return return
(Path.GetExtension(file).ToLower() is ".nsp" && (Path.GetExtension(file).ToLower() is ".nsp" &&
ConfigurationState.Instance.UI.ShownFileTypes.NSP.Value) || ConfigurationState.Instance.UI.ShownFileTypes.NSP.Value);
(Path.GetExtension(file).ToLower() is ".xci" &&
ConfigurationState.Instance.UI.ShownFileTypes.XCI.Value);
}); });
foreach (string app in files) foreach (string app in files)
{ {
if (_cancellationToken.Token.IsCancellationRequested) if (_cancellationToken.Token.IsCancellationRequested)
{ {
return; return newDlcLoaded;
} }
var fileInfo = new FileInfo(app); var fileInfo = new FileInfo(app);
@ -885,7 +884,7 @@ namespace Ryujinx.UI.App.Common
{ {
var fullPath = fileInfo.ResolveLinkTarget(true)?.FullName ?? fileInfo.FullName; var fullPath = fileInfo.ResolveLinkTarget(true)?.FullName ?? fileInfo.FullName;
applicationPaths.Add(fullPath); dlcPaths.Add(fullPath);
} }
catch (IOException exception) catch (IOException exception)
{ {
@ -901,31 +900,26 @@ namespace Ryujinx.UI.App.Common
} }
} }
// Loops through applications list, creating a struct and then firing an event containing the struct for each application var appIdLookup = Applications.Items.Select(it => it.IdBase).ToHashSet();
foreach (string applicationPath in applicationPaths)
foreach (string dlcPath in dlcPaths)
{ {
if (_cancellationToken.Token.IsCancellationRequested) if (_cancellationToken.Token.IsCancellationRequested)
{ {
return; return newDlcLoaded;
} }
if (TryGetDownloadableContentFromFile(applicationPath, out List<DownloadableContentModel> downloadableContents)) if (TryGetDownloadableContentFromFile(dlcPath, out var foundDlcs))
{ {
foreach (var downloadableContent in downloadableContents) foreach (var dlc in foundDlcs.Where(it => appIdLookup.Contains(it.TitleIdBase)))
{ {
OnDownloadableContentAdded(new DownloadableContentAddedEventArgs if (!DownloadableContents.Items.Any(it => it.Dlc == dlc))
{ {
DownloadableContent = downloadableContent, _downloadableContents.AddOrUpdate((dlc, true));
}); SaveDownloadableContentsForGame(dlc.TitleIdBase);
newDlcLoaded++;
} }
_downloadableContents.Edit(it =>
{
foreach (var downloadableContent in downloadableContents)
{
it.AddOrUpdate((downloadableContent, true));
} }
});
} }
} }
} }
@ -934,114 +928,117 @@ namespace Ryujinx.UI.App.Common
_cancellationToken.Dispose(); _cancellationToken.Dispose();
_cancellationToken = null; _cancellationToken = null;
} }
return newDlcLoaded;
} }
public void AutoLoadTitleUpdates(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();
try
{
foreach (string appDir in appDirs)
{
if (_cancellationToken.Token.IsCancellationRequested)
{ {
return; return;
} // _cancellationToken = new CancellationTokenSource();
// _titleUpdates.Clear();
if (!Directory.Exists(appDir)) //
{ // // Builds the applications list with paths to found applications
Logger.Warning?.Print(LogClass.Application, // List<string> applicationPaths = new();
$"The specified game directory \"{appDir}\" does not exist."); //
// try
continue; // {
} // foreach (string appDir in appDirs)
// {
try // if (_cancellationToken.Token.IsCancellationRequested)
{ // {
EnumerationOptions options = new() // return;
{ // }
RecurseSubdirectories = true, //
IgnoreInaccessible = false, // if (!Directory.Exists(appDir))
}; // {
// Logger.Warning?.Print(LogClass.Application,
IEnumerable<string> files = Directory.EnumerateFiles(appDir, "*", options) // $"The specified game directory \"{appDir}\" does not exist.");
.Where(file => //
{ // continue;
return // }
(Path.GetExtension(file).ToLower() is ".nsp" && //
ConfigurationState.Instance.UI.ShownFileTypes.NSP.Value) || // try
(Path.GetExtension(file).ToLower() is ".xci" && // {
ConfigurationState.Instance.UI.ShownFileTypes.XCI.Value); // EnumerationOptions options = new()
}); // {
// RecurseSubdirectories = true,
foreach (string app in files) // IgnoreInaccessible = false,
{ // };
if (_cancellationToken.Token.IsCancellationRequested) //
{ // IEnumerable<string> files = Directory.EnumerateFiles(appDir, "*", options)
return; // .Where(file =>
} // {
// return
var fileInfo = new FileInfo(app); // (Path.GetExtension(file).ToLower() is ".nsp" &&
// ConfigurationState.Instance.UI.ShownFileTypes.NSP.Value) ||
try // (Path.GetExtension(file).ToLower() is ".xci" &&
{ // ConfigurationState.Instance.UI.ShownFileTypes.XCI.Value);
var fullPath = fileInfo.ResolveLinkTarget(true)?.FullName ?? // });
fileInfo.FullName; //
// foreach (string app in files)
applicationPaths.Add(fullPath); // {
} // if (_cancellationToken.Token.IsCancellationRequested)
catch (IOException exception) // {
{ // return;
Logger.Warning?.Print(LogClass.Application, // }
$"Failed to resolve the full path to file: \"{app}\" Error: {exception}"); //
} // var fileInfo = new FileInfo(app);
} //
} // try
catch (UnauthorizedAccessException) // {
{ // var fullPath = fileInfo.ResolveLinkTarget(true)?.FullName ??
Logger.Warning?.Print(LogClass.Application, // fileInfo.FullName;
$"Failed to get access to directory: \"{appDir}\""); //
} // applicationPaths.Add(fullPath);
} // }
// catch (IOException exception)
// Loops through applications list, creating a struct and then firing an event containing the struct for each application // {
foreach (string applicationPath in applicationPaths) // Logger.Warning?.Print(LogClass.Application,
{ // $"Failed to resolve the full path to file: \"{app}\" Error: {exception}");
if (_cancellationToken.Token.IsCancellationRequested) // }
{ // }
return; // }
} // catch (UnauthorizedAccessException)
// {
if (TryGetTitleUpdatesFromFile(applicationPath, out List<TitleUpdateModel> titleUpdates)) // Logger.Warning?.Print(LogClass.Application,
{ // $"Failed to get access to directory: \"{appDir}\"");
foreach (var titleUpdate in titleUpdates) // }
{ // }
OnTitleUpdateAdded(new TitleUpdateAddedEventArgs() //
{ // // Loops through applications list, creating a struct and then firing an event containing the struct for each application
TitleUpdate = titleUpdate, // foreach (string applicationPath in applicationPaths)
}); // {
} // if (_cancellationToken.Token.IsCancellationRequested)
// {
_titleUpdates.Edit(it => // return;
{ // }
foreach (var titleUpdate in titleUpdates) //
{ // if (TryGetTitleUpdatesFromFile(applicationPath, out List<TitleUpdateModel> titleUpdates))
it.AddOrUpdate((titleUpdate, false)); // {
} // foreach (var titleUpdate in titleUpdates)
}); // {
} // OnTitleUpdateAdded(new TitleUpdateAddedEventArgs()
} // {
} // TitleUpdate = titleUpdate,
finally // });
{ // }
_cancellationToken.Dispose(); //
_cancellationToken = null; // _titleUpdates.Edit(it =>
} // {
// foreach (var titleUpdate in titleUpdates)
// {
// it.AddOrUpdate((titleUpdate, false));
// }
// });
// }
// }
// }
// finally
// {
// _cancellationToken.Dispose();
// _cancellationToken = null;
// }
} }
protected void OnApplicationAdded(ApplicationAddedEventArgs e) protected void OnApplicationAdded(ApplicationAddedEventArgs e)

View File

@ -709,11 +709,15 @@
"DlcWindowTitle": "Manage Downloadable Content for {0} ({1})", "DlcWindowTitle": "Manage Downloadable Content for {0} ({1})",
"ModWindowTitle": "Manage Mods for {0} ({1})", "ModWindowTitle": "Manage Mods for {0} ({1})",
"UpdateWindowTitle": "Title Update Manager", "UpdateWindowTitle": "Title Update Manager",
"UpdateWindowUpdateAddedMessage": "{0} new update(s) added",
"CheatWindowHeading": "Cheats Available for {0} [{1}]", "CheatWindowHeading": "Cheats Available for {0} [{1}]",
"BuildId": "BuildId:", "BuildId": "BuildId:",
"DlcWindowBundledContentNotice": "Bundled DLC cannot be removed, only disabled.", "DlcWindowBundledContentNotice": "Bundled DLC cannot be removed, only disabled.",
"DlcWindowHeading": "{0} Downloadable Content(s)", "DlcWindowHeading": "{0} Downloadable Content(s)",
"DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added", "DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added",
"AutoloadDlcAddedMessage": "{0} new downloadable content(s) added",
"AutoloadUpdateAddedMessage": "{0} new update(s) added",
"AutoloadDlcAndUpdateAddedMessage": "{0} new downloadable content(s) and {0} new update(s) added",
"ModWindowHeading": "{0} Mod(s)", "ModWindowHeading": "{0} Mod(s)",
"UserProfilesEditProfile": "Edit Selected", "UserProfilesEditProfile": "Edit Selected",
"Cancel": "Cancel", "Cancel": "Cancel",

View File

@ -485,8 +485,6 @@ namespace Ryujinx.Ava.UI.Windows
ApplicationLibrary.ApplicationCountUpdated += ApplicationLibrary_ApplicationCountUpdated; ApplicationLibrary.ApplicationCountUpdated += ApplicationLibrary_ApplicationCountUpdated;
ApplicationLibrary.ApplicationAdded += ApplicationLibrary_ApplicationAdded; ApplicationLibrary.ApplicationAdded += ApplicationLibrary_ApplicationAdded;
ApplicationLibrary.DownloadableContentAdded += ApplicationLibrary_DownloadableContentAdded;
ApplicationLibrary.TitleUpdateAdded += ApplicationLibrary_TitleUpdateAdded;
ViewModel.RefreshFirmwareStatus(); ViewModel.RefreshFirmwareStatus();
@ -655,6 +653,14 @@ namespace Ryujinx.Ava.UI.Windows
TimeIt("games", () => ApplicationLibrary.LoadApplications(ConfigurationState.Instance.UI.GameDirs)); TimeIt("games", () => ApplicationLibrary.LoadApplications(ConfigurationState.Instance.UI.GameDirs));
// TimeIt("updates", () => ApplicationLibrary.LoadTitleUpdates(ConfigurationState.Instance.UI.GameDirs)); // TimeIt("updates", () => ApplicationLibrary.LoadTitleUpdates(ConfigurationState.Instance.UI.GameDirs));
TimeIt("DLC", () => ApplicationLibrary.LoadDownloadableContents()); TimeIt("DLC", () => ApplicationLibrary.LoadDownloadableContents());
// TODO(jpr): conditional
var dlcLoaded = 0;
TimeIt("AUTO DLC", () => dlcLoaded = ApplicationLibrary.AutoLoadDownloadableContents(ConfigurationState.Instance.UI.GameDirs));
if (dlcLoaded > 0)
{
ShowNewContentAddedDialog(dlcLoaded, 0);
}
_isLoading = false; _isLoading = false;
}) })
@ -673,5 +679,27 @@ namespace Ryujinx.Ava.UI.Windows
var elapsedMs = watch.ElapsedMilliseconds; var elapsedMs = watch.ElapsedMilliseconds;
Console.WriteLine("[{0}] {1} ms", tag, elapsedMs); Console.WriteLine("[{0}] {1} ms", tag, elapsedMs);
} }
private Task ShowNewContentAddedDialog(int numDlcAdded, int numUpdatesAdded)
{
var msg = "";
if (numDlcAdded > 0 && numUpdatesAdded > 0)
{
msg = string.Format(LocaleManager.Instance[LocaleKeys.AutoloadDlcAndUpdateAddedMessage], numDlcAdded, numUpdatesAdded);
} else if (numDlcAdded > 0)
{
msg = string.Format(LocaleManager.Instance[LocaleKeys.AutoloadDlcAddedMessage], numDlcAdded);
} else if (numUpdatesAdded > 0)
{
msg = string.Format(LocaleManager.Instance[LocaleKeys.AutoloadUpdateAddedMessage], numUpdatesAdded);
}
return msg == "" ? Task.CompletedTask : Dispatcher.UIThread.InvokeAsync(async () =>
{
await ContentDialogHelper.ShowTextDialog(LocaleManager.Instance[LocaleKeys.DialogConfirmationTitle],
msg, "", "", "", LocaleManager.Instance[LocaleKeys.InputDialogOk], (int)Symbol.Checkmark);
});
}
} }
} }