Add setting for autoloading dlc/updates

This commit is contained in:
Jimmy Reichley 2024-08-17 17:26:13 -04:00
parent a381cea311
commit bc60126a24
No known key found for this signature in database
GPG key ID: 67715DC5A329803C
7 changed files with 172 additions and 124 deletions

View file

@ -779,6 +779,11 @@ namespace Ryujinx.UI.App.Common
} }
} }
public void LoadTitleUpdates()
{
return;
}
public void LoadDownloadableContents() public void LoadDownloadableContents()
{ {
_downloadableContents.Edit(it => _downloadableContents.Edit(it =>
@ -789,8 +794,8 @@ namespace Ryujinx.UI.App.Common
{ {
var savedDlc = DownloadableContentsHelper.LoadDownloadableContentsJson(_virtualFileSystem, application.IdBase); var savedDlc = DownloadableContentsHelper.LoadDownloadableContentsJson(_virtualFileSystem, application.IdBase);
it.AddOrUpdate(savedDlc); it.AddOrUpdate(savedDlc);
if(TryGetDownloadableContentFromFile(application.Path, out var bundledDlc)) if (TryGetDownloadableContentFromFile(application.Path, out var bundledDlc))
{ {
var savedDlcLookup = savedDlc.Select(dlc => dlc.Item1).ToHashSet(); var savedDlcLookup = savedDlc.Select(dlc => dlc.Item1).ToHashSet();
@ -813,13 +818,13 @@ namespace Ryujinx.UI.App.Common
} }
}); });
} }
private void SaveDownloadableContentsForGame(ulong titleIdBase) private void SaveDownloadableContentsForGame(ulong titleIdBase)
{ {
var dlcs = DownloadableContents.Items.Where(dlc => dlc.Dlc.TitleIdBase == titleIdBase).ToList(); var dlcs = DownloadableContents.Items.Where(dlc => dlc.Dlc.TitleIdBase == titleIdBase).ToList();
DownloadableContentsHelper.SaveDownloadableContentsJson(_virtualFileSystem, titleIdBase, dlcs); 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 =>
@ -932,113 +937,113 @@ namespace Ryujinx.UI.App.Common
return newDlcLoaded; return newDlcLoaded;
} }
public void AutoLoadTitleUpdates(List<string> appDirs) public int AutoLoadTitleUpdates(List<string> appDirs)
{ {
return; return 0;
// _cancellationToken = new CancellationTokenSource(); // _cancellationToken = new CancellationTokenSource();
// _titleUpdates.Clear(); // _titleUpdates.Clear();
// //
// // Builds the applications list with paths to found applications // // Builds the applications list with paths to found applications
// List<string> applicationPaths = new(); // List<string> applicationPaths = new();
// //
// try // try
// { // {
// foreach (string appDir in appDirs) // foreach (string appDir in appDirs)
// { // {
// if (_cancellationToken.Token.IsCancellationRequested) // if (_cancellationToken.Token.IsCancellationRequested)
// { // {
// return; // return;
// } // }
// //
// if (!Directory.Exists(appDir)) // if (!Directory.Exists(appDir))
// { // {
// Logger.Warning?.Print(LogClass.Application, // Logger.Warning?.Print(LogClass.Application,
// $"The specified game directory \"{appDir}\" does not exist."); // $"The specified game directory \"{appDir}\" does not exist.");
// //
// continue; // continue;
// } // }
// //
// try // try
// { // {
// EnumerationOptions options = new() // EnumerationOptions options = new()
// { // {
// RecurseSubdirectories = true, // RecurseSubdirectories = true,
// IgnoreInaccessible = false, // IgnoreInaccessible = false,
// }; // };
// //
// IEnumerable<string> files = Directory.EnumerateFiles(appDir, "*", options) // IEnumerable<string> files = Directory.EnumerateFiles(appDir, "*", options)
// .Where(file => // .Where(file =>
// { // {
// 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" && // (Path.GetExtension(file).ToLower() is ".xci" &&
// ConfigurationState.Instance.UI.ShownFileTypes.XCI.Value); // 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;
// } // }
// //
// var fileInfo = new FileInfo(app); // var fileInfo = new FileInfo(app);
// //
// try // try
// { // {
// var fullPath = fileInfo.ResolveLinkTarget(true)?.FullName ?? // var fullPath = fileInfo.ResolveLinkTarget(true)?.FullName ??
// fileInfo.FullName; // fileInfo.FullName;
// //
// applicationPaths.Add(fullPath); // applicationPaths.Add(fullPath);
// } // }
// catch (IOException exception) // catch (IOException exception)
// { // {
// Logger.Warning?.Print(LogClass.Application, // Logger.Warning?.Print(LogClass.Application,
// $"Failed to resolve the full path to file: \"{app}\" Error: {exception}"); // $"Failed to resolve the full path to file: \"{app}\" Error: {exception}");
// } // }
// } // }
// } // }
// catch (UnauthorizedAccessException) // catch (UnauthorizedAccessException)
// { // {
// Logger.Warning?.Print(LogClass.Application, // Logger.Warning?.Print(LogClass.Application,
// $"Failed to get access to directory: \"{appDir}\""); // $"Failed to get access to directory: \"{appDir}\"");
// } // }
// } // }
// //
// // Loops through applications list, creating a struct and then firing an event containing the struct for each application // // Loops through applications list, creating a struct and then firing an event containing the struct for each application
// foreach (string applicationPath in applicationPaths) // foreach (string applicationPath in applicationPaths)
// { // {
// if (_cancellationToken.Token.IsCancellationRequested) // if (_cancellationToken.Token.IsCancellationRequested)
// { // {
// return; // return;
// } // }
// //
// if (TryGetTitleUpdatesFromFile(applicationPath, out List<TitleUpdateModel> titleUpdates)) // if (TryGetTitleUpdatesFromFile(applicationPath, out List<TitleUpdateModel> titleUpdates))
// { // {
// foreach (var titleUpdate in titleUpdates) // foreach (var titleUpdate in titleUpdates)
// { // {
// OnTitleUpdateAdded(new TitleUpdateAddedEventArgs() // OnTitleUpdateAdded(new TitleUpdateAddedEventArgs()
// { // {
// TitleUpdate = titleUpdate, // TitleUpdate = titleUpdate,
// }); // });
// } // }
// //
// _titleUpdates.Edit(it => // _titleUpdates.Edit(it =>
// { // {
// foreach (var titleUpdate in titleUpdates) // foreach (var titleUpdate in titleUpdates)
// { // {
// it.AddOrUpdate((titleUpdate, false)); // it.AddOrUpdate((titleUpdate, false));
// } // }
// }); // });
// } // }
// } // }
// } // }
// finally // finally
// { // {
// _cancellationToken.Dispose(); // _cancellationToken.Dispose();
// _cancellationToken = null; // _cancellationToken = null;
// } // }
} }
protected void OnApplicationAdded(ApplicationAddedEventArgs e) protected void OnApplicationAdded(ApplicationAddedEventArgs e)

View file

@ -15,7 +15,7 @@ namespace Ryujinx.UI.Common.Configuration
/// <summary> /// <summary>
/// The current version of the file format /// The current version of the file format
/// </summary> /// </summary>
public const int CurrentVersion = 51; public const int CurrentVersion = 52;
/// <summary> /// <summary>
/// Version of the configuration file format /// Version of the configuration file format
@ -167,6 +167,11 @@ namespace Ryujinx.UI.Common.Configuration
/// </summary> /// </summary>
public bool RememberWindowState { get; set; } public bool RememberWindowState { get; set; }
/// <summary>
/// Enables or disables automatically loading DLC/title updates on library refresh.
/// </summary>
public bool AutoloadContent { get; set; }
/// <summary> /// <summary>
/// Enables hardware-accelerated rendering for Avalonia /// Enables hardware-accelerated rendering for Avalonia
/// </summary> /// </summary>

View file

@ -632,6 +632,11 @@ namespace Ryujinx.UI.Common.Configuration
/// </summary> /// </summary>
public ReactiveObject<bool> RememberWindowState { get; private set; } public ReactiveObject<bool> RememberWindowState { get; private set; }
/// <summary>
/// Enables or disables automatically loading DLC/title updates on library refresh.
/// </summary>
public ReactiveObject<bool> AutoloadContent { get; private set; }
/// <summary> /// <summary>
/// Enables hardware-accelerated rendering for Avalonia /// Enables hardware-accelerated rendering for Avalonia
/// </summary> /// </summary>
@ -654,6 +659,7 @@ namespace Ryujinx.UI.Common.Configuration
CheckUpdatesOnStart = new ReactiveObject<bool>(); CheckUpdatesOnStart = new ReactiveObject<bool>();
ShowConfirmExit = new ReactiveObject<bool>(); ShowConfirmExit = new ReactiveObject<bool>();
RememberWindowState = new ReactiveObject<bool>(); RememberWindowState = new ReactiveObject<bool>();
AutoloadContent = new ReactiveObject<bool>();
EnableHardwareAcceleration = new ReactiveObject<bool>(); EnableHardwareAcceleration = new ReactiveObject<bool>();
HideCursor = new ReactiveObject<HideCursorMode>(); HideCursor = new ReactiveObject<HideCursorMode>();
} }
@ -692,6 +698,7 @@ namespace Ryujinx.UI.Common.Configuration
CheckUpdatesOnStart = CheckUpdatesOnStart, CheckUpdatesOnStart = CheckUpdatesOnStart,
ShowConfirmExit = ShowConfirmExit, ShowConfirmExit = ShowConfirmExit,
RememberWindowState = RememberWindowState, RememberWindowState = RememberWindowState,
AutoloadContent = AutoloadContent,
EnableHardwareAcceleration = EnableHardwareAcceleration, EnableHardwareAcceleration = EnableHardwareAcceleration,
HideCursor = HideCursor, HideCursor = HideCursor,
EnableVsync = Graphics.EnableVsync, EnableVsync = Graphics.EnableVsync,
@ -801,6 +808,7 @@ namespace Ryujinx.UI.Common.Configuration
CheckUpdatesOnStart.Value = true; CheckUpdatesOnStart.Value = true;
ShowConfirmExit.Value = true; ShowConfirmExit.Value = true;
RememberWindowState.Value = true; RememberWindowState.Value = true;
AutoloadContent.Value = false;
EnableHardwareAcceleration.Value = true; EnableHardwareAcceleration.Value = true;
HideCursor.Value = HideCursorMode.OnIdle; HideCursor.Value = HideCursorMode.OnIdle;
Graphics.EnableVsync.Value = true; Graphics.EnableVsync.Value = true;
@ -1477,6 +1485,15 @@ namespace Ryujinx.UI.Common.Configuration
configurationFileUpdated = true; configurationFileUpdated = true;
} }
if (configurationFileFormat.Version < 52)
{
Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 52.");
configurationFileFormat.AutoloadContent = false;
configurationFileUpdated = true;
}
Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog; Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog;
Graphics.ResScale.Value = configurationFileFormat.ResScale; Graphics.ResScale.Value = configurationFileFormat.ResScale;
Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom; Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom;
@ -1508,6 +1525,7 @@ namespace Ryujinx.UI.Common.Configuration
CheckUpdatesOnStart.Value = configurationFileFormat.CheckUpdatesOnStart; CheckUpdatesOnStart.Value = configurationFileFormat.CheckUpdatesOnStart;
ShowConfirmExit.Value = configurationFileFormat.ShowConfirmExit; ShowConfirmExit.Value = configurationFileFormat.ShowConfirmExit;
RememberWindowState.Value = configurationFileFormat.RememberWindowState; RememberWindowState.Value = configurationFileFormat.RememberWindowState;
AutoloadContent.Value = configurationFileFormat.AutoloadContent;
EnableHardwareAcceleration.Value = configurationFileFormat.EnableHardwareAcceleration; EnableHardwareAcceleration.Value = configurationFileFormat.EnableHardwareAcceleration;
HideCursor.Value = configurationFileFormat.HideCursor; HideCursor.Value = configurationFileFormat.HideCursor;
Graphics.EnableVsync.Value = configurationFileFormat.EnableVsync; Graphics.EnableVsync.Value = configurationFileFormat.EnableVsync;

View file

@ -98,6 +98,7 @@
"SettingsTabGeneralCheckUpdatesOnLaunch": "Check for Updates on Launch", "SettingsTabGeneralCheckUpdatesOnLaunch": "Check for Updates on Launch",
"SettingsTabGeneralShowConfirmExitDialog": "Show \"Confirm Exit\" Dialog", "SettingsTabGeneralShowConfirmExitDialog": "Show \"Confirm Exit\" Dialog",
"SettingsTabGeneralRememberWindowState": "Remember Window Size/Position", "SettingsTabGeneralRememberWindowState": "Remember Window Size/Position",
"SettingsTabGeneralAutoloadContent": "Automatically load DLC/updates",
"SettingsTabGeneralHideCursor": "Hide Cursor:", "SettingsTabGeneralHideCursor": "Hide Cursor:",
"SettingsTabGeneralHideCursorNever": "Never", "SettingsTabGeneralHideCursorNever": "Never",
"SettingsTabGeneralHideCursorOnIdle": "On Idle", "SettingsTabGeneralHideCursorOnIdle": "On Idle",
@ -709,7 +710,7 @@
"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", "UpdateWindowDlcAddedMessage": "{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.",

View file

@ -132,6 +132,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool CheckUpdatesOnStart { get; set; } public bool CheckUpdatesOnStart { get; set; }
public bool ShowConfirmExit { get; set; } public bool ShowConfirmExit { get; set; }
public bool RememberWindowState { get; set; } public bool RememberWindowState { get; set; }
public bool AutoloadContent { get; set; }
public int HideCursor { get; set; } public int HideCursor { get; set; }
public bool EnableDockedMode { get; set; } public bool EnableDockedMode { get; set; }
public bool EnableKeyboard { get; set; } public bool EnableKeyboard { get; set; }
@ -392,6 +393,7 @@ namespace Ryujinx.Ava.UI.ViewModels
CheckUpdatesOnStart = config.CheckUpdatesOnStart; CheckUpdatesOnStart = config.CheckUpdatesOnStart;
ShowConfirmExit = config.ShowConfirmExit; ShowConfirmExit = config.ShowConfirmExit;
RememberWindowState = config.RememberWindowState; RememberWindowState = config.RememberWindowState;
AutoloadContent = config.AutoloadContent;
HideCursor = (int)config.HideCursor.Value; HideCursor = (int)config.HideCursor.Value;
GameDirectories.Clear(); GameDirectories.Clear();
@ -484,6 +486,7 @@ namespace Ryujinx.Ava.UI.ViewModels
config.CheckUpdatesOnStart.Value = CheckUpdatesOnStart; config.CheckUpdatesOnStart.Value = CheckUpdatesOnStart;
config.ShowConfirmExit.Value = ShowConfirmExit; config.ShowConfirmExit.Value = ShowConfirmExit;
config.RememberWindowState.Value = RememberWindowState; config.RememberWindowState.Value = RememberWindowState;
config.AutoloadContent.Value = AutoloadContent;
config.HideCursor.Value = (HideCursorMode)HideCursor; config.HideCursor.Value = (HideCursorMode)HideCursor;
if (_directoryChanged) if (_directoryChanged)

View file

@ -39,6 +39,9 @@
<CheckBox IsChecked="{Binding RememberWindowState}"> <CheckBox IsChecked="{Binding RememberWindowState}">
<TextBlock Text="{locale:Locale SettingsTabGeneralRememberWindowState}" /> <TextBlock Text="{locale:Locale SettingsTabGeneralRememberWindowState}" />
</CheckBox> </CheckBox>
<CheckBox IsChecked="{Binding AutoloadContent}">
<TextBlock Text="{locale:Locale SettingsTabGeneralAutoloadContent}" />
</CheckBox>
<StackPanel Margin="0, 15, 0, 0" Orientation="Horizontal"> <StackPanel Margin="0, 15, 0, 0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" <TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabGeneralHideCursor}" Text="{locale:Locale SettingsTabGeneralHideCursor}"

View file

@ -651,15 +651,22 @@ namespace Ryujinx.Ava.UI.Windows
{ {
ApplicationLibrary.DesiredLanguage = ConfigurationState.Instance.System.Language; ApplicationLibrary.DesiredLanguage = ConfigurationState.Instance.System.Language;
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());
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) if (ConfigurationState.Instance.AutoloadContent)
{ {
ShowNewContentAddedDialog(dlcLoaded, 0); var updatesLoaded = 0;
TimeIt("auto updates",
() => updatesLoaded =
ApplicationLibrary.AutoLoadTitleUpdates(ConfigurationState.Instance.UI.GameDirs));
var dlcLoaded = 0;
TimeIt("auto dlc",
() => dlcLoaded =
ApplicationLibrary.AutoLoadDownloadableContents(ConfigurationState.Instance.UI.GameDirs));
ShowNewContentAddedDialog(dlcLoaded, updatesLoaded);
} }
_isLoading = false; _isLoading = false;
@ -679,23 +686,29 @@ 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) private Task ShowNewContentAddedDialog(int numDlcAdded, int numUpdatesAdded)
{ {
var msg = ""; var msg = "";
if (numDlcAdded > 0 && numUpdatesAdded > 0) if (numDlcAdded > 0 && numUpdatesAdded > 0)
{ {
msg = string.Format(LocaleManager.Instance[LocaleKeys.AutoloadDlcAndUpdateAddedMessage], numDlcAdded, numUpdatesAdded); msg = string.Format(LocaleManager.Instance[LocaleKeys.AutoloadDlcAndUpdateAddedMessage], numDlcAdded, numUpdatesAdded);
} else if (numDlcAdded > 0) }
else if (numDlcAdded > 0)
{ {
msg = string.Format(LocaleManager.Instance[LocaleKeys.AutoloadDlcAddedMessage], numDlcAdded); msg = string.Format(LocaleManager.Instance[LocaleKeys.AutoloadDlcAddedMessage], numDlcAdded);
} else if (numUpdatesAdded > 0) }
else if (numUpdatesAdded > 0)
{ {
msg = string.Format(LocaleManager.Instance[LocaleKeys.AutoloadUpdateAddedMessage], numUpdatesAdded); msg = string.Format(LocaleManager.Instance[LocaleKeys.AutoloadUpdateAddedMessage], numUpdatesAdded);
} }
else
return msg == "" ? Task.CompletedTask : Dispatcher.UIThread.InvokeAsync(async () => {
return Task.CompletedTask;
}
return Dispatcher.UIThread.InvokeAsync(async () =>
{ {
await ContentDialogHelper.ShowTextDialog(LocaleManager.Instance[LocaleKeys.DialogConfirmationTitle], await ContentDialogHelper.ShowTextDialog(LocaleManager.Instance[LocaleKeys.DialogConfirmationTitle],
msg, "", "", "", LocaleManager.Instance[LocaleKeys.InputDialogOk], (int)Symbol.Checkmark); msg, "", "", "", LocaleManager.Instance[LocaleKeys.InputDialogOk], (int)Symbol.Checkmark);