Autoload DLC
This commit is contained in:
parent
867bc7021f
commit
a381cea311
3 changed files with 166 additions and 137 deletions
|
@ -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)
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue