mirror of
https://github.com/ryujinx-mirror/ryujinx.git
synced 2024-12-22 19:05:46 +00:00
Automatically remove invalid dlc and updates as part of auto load
Fixed some minor label spacing issues in options dialog Removal of unused variable in input view model
This commit is contained in:
parent
57d8f5b4b5
commit
ce24341d1d
8 changed files with 117 additions and 53 deletions
|
@ -802,17 +802,31 @@ namespace Ryujinx.UI.App.Common
|
|||
|
||||
// Searches the provided directories for DLC NSP files that are _valid for the currently detected games in the
|
||||
// library_, and then enables those DLC.
|
||||
public int AutoLoadDownloadableContents(List<string> appDirs)
|
||||
public int AutoLoadDownloadableContents(List<string> appDirs, out int numDlcRemoved)
|
||||
{
|
||||
_cancellationToken = new CancellationTokenSource();
|
||||
|
||||
List<string> dlcPaths = new();
|
||||
int newDlcLoaded = 0;
|
||||
numDlcRemoved = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// Remove any downloadable content which can no longer be located on disk
|
||||
Logger.Notice.Print(LogClass.Application, $"Removing non-existing Title DLCs");
|
||||
var dlcToRemove = _downloadableContents.Items
|
||||
.Where(dlc => !File.Exists(dlc.Dlc.ContainerPath))
|
||||
.ToList();
|
||||
dlcToRemove.ForEach(dlc =>
|
||||
Logger.Warning?.Print(LogClass.Application, $"Title DLC removed: {dlc.Dlc.ContainerPath}")
|
||||
);
|
||||
numDlcRemoved += dlcToRemove.Distinct().Count();
|
||||
_downloadableContents.RemoveKeys(dlcToRemove.Select(dlc => dlc.Dlc));
|
||||
|
||||
foreach (string appDir in appDirs)
|
||||
{
|
||||
Logger.Notice.Print(LogClass.Application, $"Auto loading DLC from: {appDir}");
|
||||
|
||||
if (_cancellationToken.Token.IsCancellationRequested)
|
||||
{
|
||||
return newDlcLoaded;
|
||||
|
@ -901,17 +915,37 @@ namespace Ryujinx.UI.App.Common
|
|||
// Searches the provided directories for update NSP files that are _valid for the currently detected games in the
|
||||
// library_, and then applies those updates. If a newly-detected update is a newer version than the currently
|
||||
// selected update (or if no update is currently selected), then that update will be selected.
|
||||
public int AutoLoadTitleUpdates(List<string> appDirs)
|
||||
public int AutoLoadTitleUpdates(List<string> appDirs, out int numUpdatesRemoved)
|
||||
{
|
||||
_cancellationToken = new CancellationTokenSource();
|
||||
|
||||
List<string> updatePaths = new();
|
||||
int numUpdatesLoaded = 0;
|
||||
numUpdatesRemoved = 0;
|
||||
|
||||
try
|
||||
{
|
||||
var titleIdsToSave = new HashSet<ulong>();
|
||||
var titleIdsToRefresh = new HashSet<ulong>();
|
||||
|
||||
// Remove any updates which can no longer be located on disk
|
||||
Logger.Notice.Print(LogClass.Application, $"Removing non-existing Title Updates");
|
||||
var updatesToRemove = _titleUpdates.Items
|
||||
.Where(it => !File.Exists(it.TitleUpdate.Path))
|
||||
.ToList();
|
||||
|
||||
numUpdatesRemoved += updatesToRemove.Select(it => it.TitleUpdate).Distinct().Count();
|
||||
updatesToRemove.ForEach(ti =>
|
||||
Logger.Warning?.Print(LogClass.Application, $"Title update removed: {ti.TitleUpdate.Path}")
|
||||
);
|
||||
_titleUpdates.RemoveKeys(updatesToRemove.Select(it => it.TitleUpdate));
|
||||
titleIdsToSave.UnionWith(updatesToRemove.Select(it => it.TitleUpdate.TitleIdBase));
|
||||
titleIdsToRefresh.UnionWith(updatesToRemove.Where(it => it.IsSelected).Select(update => update.TitleUpdate.TitleIdBase));
|
||||
|
||||
foreach (string appDir in appDirs)
|
||||
{
|
||||
Logger.Notice.Print(LogClass.Application, $"Auto loading updates from: {appDir}");
|
||||
|
||||
if (_cancellationToken.Token.IsCancellationRequested)
|
||||
{
|
||||
return numUpdatesLoaded;
|
||||
|
@ -980,27 +1014,24 @@ namespace Ryujinx.UI.App.Common
|
|||
{
|
||||
if (!_titleUpdates.Lookup(update).HasValue)
|
||||
{
|
||||
var currentlySelected = TitleUpdates.Items.FirstOrOptional(it =>
|
||||
it.TitleUpdate.TitleIdBase == update.TitleIdBase && it.IsSelected);
|
||||
|
||||
var shouldSelect = !currentlySelected.HasValue ||
|
||||
currentlySelected.Value.TitleUpdate.Version < update.Version;
|
||||
_titleUpdates.AddOrUpdate((update, shouldSelect));
|
||||
|
||||
if (currentlySelected.HasValue && shouldSelect)
|
||||
_titleUpdates.AddOrUpdate((currentlySelected.Value.TitleUpdate, false));
|
||||
|
||||
SaveTitleUpdatesForGame(update.TitleIdBase);
|
||||
bool shouldSelect = AddAndAutoSelectUpdate(update);
|
||||
titleIdsToSave.Add(update.TitleIdBase);
|
||||
numUpdatesLoaded++;
|
||||
|
||||
if (shouldSelect)
|
||||
{
|
||||
RefreshApplicationInfo(update.TitleIdBase);
|
||||
titleIdsToRefresh.Add(update.TitleIdBase);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var titleId in titleIdsToSave)
|
||||
SaveTitleUpdatesForGame(titleId);
|
||||
|
||||
foreach (var titleId in titleIdsToRefresh)
|
||||
RefreshApplicationInfo(titleId);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -1011,6 +1042,24 @@ namespace Ryujinx.UI.App.Common
|
|||
return numUpdatesLoaded;
|
||||
}
|
||||
|
||||
private bool AddAndAutoSelectUpdate(TitleUpdateModel update)
|
||||
{
|
||||
var currentlySelected = TitleUpdates.Items.FirstOrOptional(it =>
|
||||
it.TitleUpdate.TitleIdBase == update.TitleIdBase && it.IsSelected);
|
||||
|
||||
var shouldSelect = !currentlySelected.HasValue ||
|
||||
currentlySelected.Value.TitleUpdate.Version < update.Version;
|
||||
|
||||
_titleUpdates.AddOrUpdate((update, shouldSelect));
|
||||
|
||||
if (currentlySelected.HasValue && shouldSelect)
|
||||
{
|
||||
_titleUpdates.AddOrUpdate((currentlySelected.Value.TitleUpdate, false));
|
||||
}
|
||||
|
||||
return shouldSelect;
|
||||
}
|
||||
|
||||
protected void OnApplicationCountUpdated(ApplicationCountUpdatedEventArgs e)
|
||||
{
|
||||
ApplicationCountUpdated?.Invoke(null, e);
|
||||
|
@ -1395,8 +1444,8 @@ namespace Ryujinx.UI.App.Common
|
|||
if (TryGetTitleUpdatesFromFile(application.Path, out var bundledUpdates))
|
||||
{
|
||||
var savedUpdateLookup = savedUpdates.Select(update => update.Item1).ToHashSet();
|
||||
bool updatesChanged = false;
|
||||
|
||||
bool addedNewUpdate = false;
|
||||
foreach (var update in bundledUpdates.OrderByDescending(bundled => bundled.Version))
|
||||
{
|
||||
if (!savedUpdateLookup.Contains(update))
|
||||
|
@ -1405,17 +1454,19 @@ namespace Ryujinx.UI.App.Common
|
|||
if (!selectedUpdate.HasValue || selectedUpdate.Value.Item1.Version < update.Version)
|
||||
{
|
||||
shouldSelect = true;
|
||||
selectedUpdate = Optional<(TitleUpdateModel, bool IsSelected)>.Create((update, true));
|
||||
if (selectedUpdate.HasValue)
|
||||
_titleUpdates.AddOrUpdate((selectedUpdate.Value.Item1, false));
|
||||
selectedUpdate = DynamicData.Kernel.Optional<(TitleUpdateModel, bool IsSelected)>.Create((update, true));
|
||||
}
|
||||
|
||||
modifiedVersion = modifiedVersion || shouldSelect;
|
||||
it.AddOrUpdate((update, shouldSelect));
|
||||
|
||||
addedNewUpdate = true;
|
||||
updatesChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (addedNewUpdate)
|
||||
if (updatesChanged)
|
||||
{
|
||||
var gameUpdates = it.Items.Where(update => update.TitleUpdate.TitleIdBase == application.IdBase).ToList();
|
||||
TitleUpdatesHelper.SaveTitleUpdatesJson(_virtualFileSystem, application.IdBase, gameUpdates);
|
||||
|
|
|
@ -106,6 +106,7 @@
|
|||
"SettingsTabGeneralHideCursorAlways": "Always",
|
||||
"SettingsTabGeneralGameDirectories": "Game Directories",
|
||||
"SettingsTabGeneralAutoloadDirectories": "Autoload DLC/Updates Directories",
|
||||
"SettingsTabGeneralAutoloadNote": "DLC and Updates which refer to missing files will be unloaded automatically",
|
||||
"SettingsTabGeneralAdd": "Add",
|
||||
"SettingsTabGeneralRemove": "Remove",
|
||||
"SettingsTabSystem": "System",
|
||||
|
@ -725,8 +726,9 @@
|
|||
"DlcWindowHeading": "{0} Downloadable Content(s)",
|
||||
"DlcWindowDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcAddedMessage": "{0} new downloadable content(s) added",
|
||||
"AutoloadDlcRemovedMessage": "{0} missing downloadable content(s) removed",
|
||||
"AutoloadUpdateAddedMessage": "{0} new update(s) added",
|
||||
"AutoloadDlcAndUpdateAddedMessage": "{0} new downloadable content(s) and {1} new update(s) added",
|
||||
"AutoloadUpdateRemovedMessage": "{0} missing update(s) removed",
|
||||
"ModWindowHeading": "{0} Mod(s)",
|
||||
"UserProfilesEditProfile": "Edit Selected",
|
||||
"Cancel": "Cancel",
|
||||
|
|
|
@ -102,6 +102,8 @@
|
|||
"SettingsTabGeneralHideCursorOnIdle": "Masquer le curseur si inactif",
|
||||
"SettingsTabGeneralHideCursorAlways": "Toujours",
|
||||
"SettingsTabGeneralGameDirectories": "Dossiers des jeux",
|
||||
"SettingsTabGeneralAutoloadDirectories": "Dossiers des mises à jour/DLC",
|
||||
"SettingsTabGeneralAutoloadNote": "Les DLC et les mises à jour faisant référence aux fichiers manquants seront automatiquement déchargés.",
|
||||
"SettingsTabGeneralAdd": "Ajouter",
|
||||
"SettingsTabGeneralRemove": "Retirer",
|
||||
"SettingsTabSystem": "Système",
|
||||
|
@ -708,6 +710,11 @@
|
|||
"CheatWindowHeading": "Cheats disponibles pour {0} [{1}]",
|
||||
"BuildId": "BuildId:",
|
||||
"DlcWindowHeading": "{0} Contenu(s) téléchargeable(s)",
|
||||
"DlcWindowDlcAddedMessage": "{0} nouveau(x) contenu(s) téléchargeable(s) ajouté(s)",
|
||||
"AutoloadDlcAddedMessage": "{0} nouveau(x) contenu(s) téléchargeable(s) ajouté(s)",
|
||||
"AutoloadDlcRemovedMessage": "{0} contenu(s) téléchargeable(s) manquant(s) supprimé(s)",
|
||||
"AutoloadUpdateAddedMessage": "{0} nouvelle(s) mise(s) à jour ajoutée(s)",
|
||||
"AutoloadUpdateRemovedMessage": "{0} mises à jour manquantes supprimées",
|
||||
"ModWindowHeading": "{0} Mod(s)",
|
||||
"UserProfilesEditProfile": "Éditer la sélection",
|
||||
"Cancel": "Annuler",
|
||||
|
|
|
@ -52,6 +52,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
public class MainWindowViewModel : BaseModel
|
||||
{
|
||||
private const int HotKeyPressDelayMs = 500;
|
||||
private delegate int LoadContentFromFolderDelegate(List<string> dirs, out int numRemoved);
|
||||
|
||||
private ObservableCollectionExtended<ApplicationData> _applications;
|
||||
private string _aspectStatusText;
|
||||
|
@ -1259,7 +1260,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
_rendererWaitEvent.Set();
|
||||
}
|
||||
|
||||
private async Task LoadContentFromFolder(LocaleKeys localeMessageKey, Func<List<string>, int> onDirsSelected)
|
||||
private async Task LoadContentFromFolder(LocaleKeys localeMessageAddedKey, LocaleKeys localeMessageRemovedKey, LoadContentFromFolderDelegate onDirsSelected)
|
||||
{
|
||||
var result = await StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
||||
{
|
||||
|
@ -1270,14 +1271,17 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
if (result.Count > 0)
|
||||
{
|
||||
var dirs = result.Select(it => it.Path.LocalPath).ToList();
|
||||
var numAdded = onDirsSelected(dirs);
|
||||
var numAdded = onDirsSelected(dirs, out int numRemoved);
|
||||
|
||||
var msg = string.Format(LocaleManager.Instance[localeMessageKey], numAdded);
|
||||
var msg = String.Join("\r\n", new string[] {
|
||||
string.Format(LocaleManager.Instance[localeMessageRemovedKey], numRemoved),
|
||||
string.Format(LocaleManager.Instance[localeMessageAddedKey], numAdded)
|
||||
});
|
||||
|
||||
await Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
await ContentDialogHelper.ShowTextDialog(
|
||||
LocaleManager.Instance[numAdded > 0 ? LocaleKeys.RyujinxConfirm : LocaleKeys.RyujinxInfo],
|
||||
LocaleManager.Instance[numAdded > 0 || numRemoved > 0 ? LocaleKeys.RyujinxConfirm : LocaleKeys.RyujinxInfo],
|
||||
msg, "", "", "", LocaleManager.Instance[LocaleKeys.InputDialogOk], (int)Symbol.Checkmark);
|
||||
});
|
||||
}
|
||||
|
@ -1533,14 +1537,18 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||
|
||||
public async Task LoadDlcFromFolder()
|
||||
{
|
||||
await LoadContentFromFolder(LocaleKeys.AutoloadDlcAddedMessage,
|
||||
dirs => ApplicationLibrary.AutoLoadDownloadableContents(dirs));
|
||||
await LoadContentFromFolder(
|
||||
LocaleKeys.AutoloadDlcAddedMessage,
|
||||
LocaleKeys.AutoloadDlcRemovedMessage,
|
||||
ApplicationLibrary.AutoLoadDownloadableContents);
|
||||
}
|
||||
|
||||
public async Task LoadTitleUpdatesFromFolder()
|
||||
{
|
||||
await LoadContentFromFolder(LocaleKeys.AutoloadUpdateAddedMessage,
|
||||
dirs => ApplicationLibrary.AutoLoadTitleUpdates(dirs));
|
||||
await LoadContentFromFolder(
|
||||
LocaleKeys.AutoloadUpdateAddedMessage,
|
||||
LocaleKeys.AutoloadUpdateRemovedMessage,
|
||||
ApplicationLibrary.AutoLoadTitleUpdates);
|
||||
}
|
||||
|
||||
public async Task OpenFolder()
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
</CheckBox>
|
||||
</StackPanel>
|
||||
<Separator Height="1" />
|
||||
<StackPanel Orientation="Vertical" Spacing="2">
|
||||
<StackPanel Orientation="Vertical" Spacing="5">
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabLoggingDeveloperOptions}" />
|
||||
<TextBlock Foreground="{DynamicResource SecondaryTextColor}" Text="{locale:Locale SettingsTabLoggingDeveloperOptionsNote}" />
|
||||
</StackPanel>
|
||||
|
|
|
@ -195,7 +195,7 @@
|
|||
<Separator Height="1" />
|
||||
<StackPanel
|
||||
Orientation="Vertical"
|
||||
Spacing="2">
|
||||
Spacing="5">
|
||||
<TextBlock
|
||||
Classes="h1"
|
||||
Text="{locale:Locale SettingsTabSystemHacks}" />
|
||||
|
|
|
@ -129,7 +129,10 @@
|
|||
</Grid>
|
||||
</StackPanel>
|
||||
<Separator Height="1" />
|
||||
<StackPanel Orientation="Vertical" Spacing="5">
|
||||
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabGeneralAutoloadDirectories}" />
|
||||
<TextBlock Foreground="{DynamicResource SecondaryTextColor}" Text="{locale:Locale SettingsTabGeneralAutoloadNote}" />
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Margin="10,0,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
|
@ -137,7 +140,7 @@
|
|||
Spacing="10">
|
||||
<ListBox
|
||||
Name="AutoloadDirsList"
|
||||
MinHeight="120"
|
||||
MinHeight="100"
|
||||
ItemsSource="{Binding AutoloadDirectories}">
|
||||
<ListBox.Styles>
|
||||
<Style Selector="ListBoxItem">
|
||||
|
|
|
@ -647,10 +647,10 @@ namespace Ryujinx.Ava.UI.Windows
|
|||
var autoloadDirs = ConfigurationState.Instance.UI.AutoloadDirs.Value;
|
||||
if (autoloadDirs.Count > 0)
|
||||
{
|
||||
var updatesLoaded = ApplicationLibrary.AutoLoadTitleUpdates(autoloadDirs);
|
||||
var dlcLoaded = ApplicationLibrary.AutoLoadDownloadableContents(autoloadDirs);
|
||||
var updatesLoaded = ApplicationLibrary.AutoLoadTitleUpdates(autoloadDirs, out int updatesRemoved);
|
||||
var dlcLoaded = ApplicationLibrary.AutoLoadDownloadableContents(autoloadDirs, out int dlcRemoved);
|
||||
|
||||
ShowNewContentAddedDialog(dlcLoaded, updatesLoaded);
|
||||
ShowNewContentAddedDialog(dlcLoaded, dlcRemoved, updatesLoaded, updatesRemoved);
|
||||
}
|
||||
|
||||
_isLoading = false;
|
||||
|
@ -662,28 +662,21 @@ namespace Ryujinx.Ava.UI.Windows
|
|||
applicationLibraryThread.Start();
|
||||
}
|
||||
|
||||
private Task ShowNewContentAddedDialog(int numDlcAdded, int numUpdatesAdded)
|
||||
private void ShowNewContentAddedDialog(int numDlcAdded, int numDlcRemoved, int numUpdatesAdded, int numUpdatesRemoved)
|
||||
{
|
||||
var msg = "";
|
||||
string[] messages = {
|
||||
numDlcRemoved > 0 ? string.Format(LocaleManager.Instance[LocaleKeys.AutoloadDlcRemovedMessage], numDlcRemoved): null,
|
||||
numDlcAdded > 0 ? string.Format(LocaleManager.Instance[LocaleKeys.AutoloadDlcAddedMessage], numDlcAdded): null,
|
||||
numUpdatesRemoved > 0 ? string.Format(LocaleManager.Instance[LocaleKeys.AutoloadUpdateRemovedMessage], numUpdatesRemoved): null,
|
||||
numUpdatesAdded > 0 ? string.Format(LocaleManager.Instance[LocaleKeys.AutoloadUpdateAddedMessage], numUpdatesAdded) : null
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
string msg = String.Join("\r\n", messages);
|
||||
|
||||
return Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
if (String.IsNullOrWhiteSpace(msg))
|
||||
return;
|
||||
|
||||
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