From 9318fd7768f4d8b319cb2fb0442575243dd1e147 Mon Sep 17 00:00:00 2001 From: pointfeev Date: Sat, 27 May 2023 17:50:03 -0400 Subject: [PATCH] start of v4.9.0 - Large refactoring & optimization - Fixed & optimized right-click context menu - Fixed silent HTTP cache concurrency exception - Added a better SafeIO exception output for anti-virus detections - Updated the help button dialog --- CreamInstaller/Components/CustomForm.cs | 25 +- CreamInstaller/Components/CustomTreeView.cs | 20 +- CreamInstaller/CreamInstaller.csproj | 2 +- CreamInstaller/Forms/DialogForm.cs | 7 +- CreamInstaller/Forms/InstallForm.cs | 18 +- CreamInstaller/Forms/SelectDialogForm.cs | 10 +- CreamInstaller/Forms/SelectForm.cs | 359 ++++++++++-------- CreamInstaller/Platforms/Epic/EpicLibrary.cs | 8 +- CreamInstaller/Platforms/Epic/EpicStore.cs | 4 +- .../Platforms/Paradox/ParadoxLauncher.cs | 58 ++- .../Platforms/Steam/SteamLibrary.cs | 20 +- .../Platforms/Ubisoft/UbisoftLibrary.cs | 10 +- CreamInstaller/Program.cs | 2 +- CreamInstaller/Resources/Koaloader.cs | 14 +- CreamInstaller/Resources/Resources.cs | 30 +- CreamInstaller/Resources/ScreamAPI.cs | 59 ++- CreamInstaller/Resources/SmokeAPI.cs | 98 +++-- CreamInstaller/Resources/UplayR1.cs | 30 +- CreamInstaller/Resources/UplayR2.cs | 39 +- .../{ProgramSelection.cs => Selection.cs} | 109 ++---- CreamInstaller/SelectionDLC.cs | 38 ++ CreamInstaller/Utility/HttpClientManager.cs | 4 +- CreamInstaller/Utility/SafeIO.cs | 9 +- 23 files changed, 484 insertions(+), 489 deletions(-) rename CreamInstaller/{ProgramSelection.cs => Selection.cs} (56%) create mode 100644 CreamInstaller/SelectionDLC.cs diff --git a/CreamInstaller/Components/CustomForm.cs b/CreamInstaller/Components/CustomForm.cs index 7b84a3b..51481f0 100644 --- a/CreamInstaller/Components/CustomForm.cs +++ b/CreamInstaller/Components/CustomForm.cs @@ -51,14 +51,10 @@ internal class CustomForm : Form _ = helpDialog.Show(SystemIcons.Information, "Automatically finds all installed Steam, Epic and Ubisoft games with their respective DLC-related DLL locations on the user's computer,\n" + "parses SteamCMD, Steam Store and Epic Games Store for user-selected games' DLCs, then provides a very simple graphical interface\n" - + "utilizing the gathered information for the maintenance of DLC unlockers.\n" + "\n" + + "utilizing the gathered information for the maintenance of DLC unlockers.\n\n" + $"The program utilizes the latest versions of [Koaloader]({acidicoala}/Koaloader), [SmokeAPI]({acidicoala}/SmokeAPI), [ScreamAPI]({acidicoala}/ScreamAPI), [Uplay R1 Unlocker]({acidicoala}/UplayR1Unlocker) and [Uplay R2 Unlocker]({acidicoala}/UplayR2Unlocker), all by\n" - + $"the wonderful [acidicoala]({acidicoala}), and all downloaded and embedded into the program itself; no further downloads necessary on your part!\n" - + "\n" + "NOTE: This program does not automatically download nor install actual DLC files for you. As the title of the program says, it's\n" - + "only a DLC Unlocker installer. Should the game you wish to unlock DLC for not already come with the DLCs installed (very many\n" - + "do not), you have to find, download, and install those yourself. Preferably, you should be referring to the proper cs.rin.ru post for\n" - + "the game(s) you're tinkering with; you'll usually find any answer to your problems there.\n" + "\n" + "USAGE:\n" - + " 1. Choose which programs and/or games the program should scan for DLC.\n" + + $"the wonderful [acidicoala]({acidicoala}), and all downloaded and embedded into the program itself; no further downloads necessary on your part!\n\n" + + "USAGE:\n" + " 1. Choose which programs and/or games the program should scan for DLC.\n" + " The program automatically gathers all installed games from Steam, Epic and Ubisoft directories.\n" + " 2. Wait for the program to download and install SteamCMD (if you chose a Steam game).\n" + " 3. Wait for the program to gather and cache the chosen games' information && DLCs.\n" @@ -69,10 +65,17 @@ internal class CustomForm : Form + " If the default \'version.dll\' doesn't work, then see [here](https://cs.rin.ru/forum/viewtopic.php?p=2552172#p2552172) to find one that does.\n" + " 6. Click the \"Generate and Install\" button.\n" + " 7. Click the \"OK\" button to close the program.\n" + " 8. If any of the DLC unlockers cause problems with any of the games you installed them on, simply go back\n" - + " to step 5 and select what games you wish you revert changes to, and instead click the \"Uninstall\" button this time.\n" + "\n" - + $"For reliable and quick assistance, all bugs, crashes and other issues should be referred to the [GitHub Issues]({repository}/issues) page!\n" - + "\n" + "SteamCMD installation and appinfo cache can be found at [C:\\ProgramData\\CreamInstaller]().\n" - + $"The program automatically and very quickly updates from [GitHub]({repository}) using [Onova](https://github.com/Tyrrrz/Onova). (updates can be ignored)\n" + + " to step 5 and select what games you wish you revert changes to, and instead click the \"Uninstall\" button this time.\n\n" + + "NOTE: This program does not automatically download nor install actual DLC files for you; as the title of the program states, this program\n" + + "is only a DLC Unlocker installer. Should the game you wish to unlock DLC for not already come with the DLCs installed, as is the case with\n" + + "a good majority of games, you must find, download and install those to the game yourself. This process includes manually installing new\n" + + "DLCs and manually updating the previously manually installed DLCs after game updates.\n\n" + + $"For reliable and quick assistance, all bugs, crashes and other issues should be referred to the [GitHub Issues]({repository}/issues) page!\n\n" + + $"HOWEVER: Please read the template issue corresponding to your problem should one exist! Also, note that the [GitHub Issues]({repository}/issues) page is not\n" + + "your personal assistance hotline, rather it is for genuine bugs/crashes/issues with the program itself. If you post an issue which has already\n" + + "been explained within the template issues and/or within this text, I will just close it.\n\n" + + "SteamCMD installation and appinfo cache can be found at [C:\\ProgramData\\CreamInstaller]().\n" + + $"The program automatically and very quickly updates from [GitHub]({repository}) by choice of the user through a dialog on startup.\n" + $"The program source and other information can be found on [GitHub]({repository})."); } diff --git a/CreamInstaller/Components/CustomTreeView.cs b/CreamInstaller/Components/CustomTreeView.cs index d307e91..154bf1a 100644 --- a/CreamInstaller/Components/CustomTreeView.cs +++ b/CreamInstaller/Components/CustomTreeView.cs @@ -24,8 +24,8 @@ internal sealed class CustomTreeView : TreeView private static readonly Color C7 = ColorTranslator.FromHtml("#006900"); private static readonly Color C8 = ColorTranslator.FromHtml("#69AA69"); - private readonly Dictionary checkBoxBounds = new(); - private readonly Dictionary comboBoxBounds = new(); + private readonly Dictionary checkBoxBounds = new(); + private readonly Dictionary comboBoxBounds = new(); private readonly Dictionary selectionBounds = new(); private SolidBrush backBrush; @@ -110,7 +110,7 @@ internal sealed class CustomTreeView : TreeView } if (form is SelectForm) { - ProgramSelection selection = ProgramSelection.FromPlatformId(platform, platformId); + Selection selection = Selection.FromPlatformId(platform, platformId); if (selection is not null) { if (bounds == node.Bounds) @@ -145,7 +145,7 @@ internal sealed class CustomTreeView : TreeView { comboBoxFont ??= new(font.FontFamily, 6, font.Style, font.Unit, font.GdiCharSet, font.GdiVerticalFont); ComboBoxState comboBoxState = Enabled ? ComboBoxState.Normal : ComboBoxState.Disabled; - text = (selection.KoaloaderProxy ?? ProgramSelection.DefaultKoaloaderProxy) + ".dll"; + text = (selection.KoaloaderProxy ?? Selection.DefaultKoaloaderProxy) + ".dll"; size = TextRenderer.MeasureText(graphics, text, comboBoxFont) + new Size(6, 0); const int padding = 2; bounds = new(bounds.X + bounds.Width, bounds.Y + padding / 2, size.Width, bounds.Height - padding); @@ -186,9 +186,9 @@ internal sealed class CustomTreeView : TreeView } if (e.Button is not MouseButtons.Left) return; - if (comboBoxBounds.Any() && selectForm is not null) - foreach (KeyValuePair pair in comboBoxBounds) - if (!ProgramSelection.All.Contains(pair.Key)) + if (comboBoxBounds.Count > 0 && selectForm is not null) + foreach (KeyValuePair pair in comboBoxBounds) + if (!Selection.All.Contains(pair.Key)) _ = comboBoxBounds.Remove(pair.Key); else if (pair.Value.Contains(clickPoint)) { @@ -214,15 +214,15 @@ internal sealed class CustomTreeView : TreeView if (canUse) _ = comboBoxDropDown.Items.Add(new ToolStripButton(proxy + ".dll", null, (_, _) => { - pair.Key.KoaloaderProxy = proxy == ProgramSelection.DefaultKoaloaderProxy ? null : proxy; + pair.Key.KoaloaderProxy = proxy == Selection.DefaultKoaloaderProxy ? null : proxy; selectForm.OnKoaloaderChanged(); }) { Font = comboBoxFont }); } comboBoxDropDown.Show(this, PointToScreen(new(pair.Value.Left, pair.Value.Bottom - 1))); break; } - foreach (KeyValuePair pair in checkBoxBounds) - if (!ProgramSelection.All.Contains(pair.Key)) + foreach (KeyValuePair pair in checkBoxBounds) + if (!Selection.All.Contains(pair.Key)) _ = checkBoxBounds.Remove(pair.Key); else if (pair.Value.Contains(clickPoint)) { diff --git a/CreamInstaller/CreamInstaller.csproj b/CreamInstaller/CreamInstaller.csproj index 64c99f4..bc8425b 100644 --- a/CreamInstaller/CreamInstaller.csproj +++ b/CreamInstaller/CreamInstaller.csproj @@ -4,7 +4,7 @@ net7.0-windows True Resources\ini.ico - 4.8.1 + 4.9.0 2021, pointfeev (https://github.com/pointfeev) CreamInstaller Automatic DLC Unlocker Installer & Configuration Generator diff --git a/CreamInstaller/Forms/DialogForm.cs b/CreamInstaller/Forms/DialogForm.cs index fe1633b..1d83fe7 100644 --- a/CreamInstaller/Forms/DialogForm.cs +++ b/CreamInstaller/Forms/DialogForm.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Drawing; -using System.Linq; using System.Windows.Forms; using CreamInstaller.Components; @@ -52,14 +51,14 @@ internal sealed partial class DialogForm : CustomForm } if (customFormIcon is not null) Icon = customFormIcon; - if (!links.Any()) + if (links.Count < 1) return ShowDialog(); foreach (LinkLabel.Link link in links) _ = descriptionLabel.Links.Add(link); - descriptionLabel.LinkClicked += (_, e) => + descriptionLabel.LinkClicked += (s, e) => { if (e.Link != null) - Process.Start(new ProcessStartInfo((string)e.Link.LinkData) { UseShellExecute = true }); + _ = Process.Start(new ProcessStartInfo((string)e.Link.LinkData) { UseShellExecute = true }); }; return ShowDialog(); } diff --git a/CreamInstaller/Forms/InstallForm.cs b/CreamInstaller/Forms/InstallForm.cs index 5107abc..fa05a30 100644 --- a/CreamInstaller/Forms/InstallForm.cs +++ b/CreamInstaller/Forms/InstallForm.cs @@ -15,9 +15,9 @@ namespace CreamInstaller.Forms; internal sealed partial class InstallForm : CustomForm { - private readonly List disabledSelections = new(); + private readonly List disabledSelections = new(); - private readonly int programCount = ProgramSelection.AllEnabled.Count; + private readonly int programCount = Selection.AllEnabled.Count; private readonly bool uninstalling; private int completeOperationsCount; @@ -58,7 +58,7 @@ internal sealed partial class InstallForm : CustomForm }); } - private async Task OperateFor(ProgramSelection selection) + private async Task OperateFor(Selection selection) { UpdateProgress(0); if (selection.Id == "PL") @@ -189,10 +189,10 @@ internal sealed partial class InstallForm : CustomForm private async Task Operate() { - List programSelections = ProgramSelection.AllEnabled; + List programSelections = Selection.AllEnabled; operationsCount = programSelections.Count; completeOperationsCount = 0; - foreach (ProgramSelection selection in programSelections) + foreach (Selection selection in programSelections) { if (Program.Canceled || !Program.AreDllsLockedDialog(this, selection)) throw new CustomMessageException("The operation was canceled."); @@ -210,13 +210,13 @@ internal sealed partial class InstallForm : CustomForm ++completeOperationsCount; } Program.Cleanup(); - List failedSelections = ProgramSelection.AllEnabled; - if (failedSelections.Any()) + List failedSelections = Selection.AllEnabled; + if (failedSelections.Count > 0) if (failedSelections.Count == 1) throw new CustomMessageException($"Operation failed for {failedSelections.First().Name}."); else throw new CustomMessageException($"Operation failed for {failedSelections.Count} programs."); - foreach (ProgramSelection selection in disabledSelections) + foreach (Selection selection in disabledSelections) selection.Enabled = true; disabledSelections.Clear(); } @@ -281,7 +281,7 @@ internal sealed partial class InstallForm : CustomForm { Program.Cleanup(); Reselecting = true; - foreach (ProgramSelection selection in disabledSelections) + foreach (Selection selection in disabledSelections) selection.Enabled = true; disabledSelections.Clear(); Close(); diff --git a/CreamInstaller/Forms/SelectDialogForm.cs b/CreamInstaller/Forms/SelectDialogForm.cs index 4675b09..99b7a4c 100644 --- a/CreamInstaller/Forms/SelectDialogForm.cs +++ b/CreamInstaller/Forms/SelectDialogForm.cs @@ -16,7 +16,7 @@ internal sealed partial class SelectDialogForm : CustomForm out List<(Platform platform, string id, string name)> choices) { choices = null; - if (!potentialChoices.Any()) + if (potentialChoices.Count < 1) return DialogResult.Cancel; groupBox.Text = groupBoxText; allCheckBox.Enabled = false; @@ -28,13 +28,13 @@ internal sealed partial class SelectDialogForm : CustomForm OnTreeNodeChecked(node); _ = selectionTreeView.Nodes.Add(node); } - if (!selected.Any()) + if (selected.Count < 1) OnLoad(null, null); allCheckBox.CheckedChanged -= OnAllCheckBoxChanged; allCheckBox.Checked = selectionTreeView.Nodes.Cast().All(n => n.Checked); allCheckBox.CheckedChanged += OnAllCheckBoxChanged; allCheckBox.Enabled = true; - acceptButton.Enabled = selected.Any(); + acceptButton.Enabled = selected.Count > 0; saveButton.Enabled = acceptButton.Enabled; loadButton.Enabled = ProgramData.ReadProgramChoices() is not null; OnResize(null, null); @@ -46,7 +46,7 @@ internal sealed partial class SelectDialogForm : CustomForm private void OnTreeNodeChecked(object sender, TreeViewEventArgs e) { OnTreeNodeChecked(e.Node); - acceptButton.Enabled = selected.Any(); + acceptButton.Enabled = selected.Count > 0; saveButton.Enabled = acceptButton.Enabled; } @@ -85,7 +85,7 @@ internal sealed partial class SelectDialogForm : CustomForm private void OnLoad(object sender, EventArgs e) { List<(Platform platform, string id)> choices = ProgramData.ReadProgramChoices().ToList(); - if (!choices.Any()) + if (choices.Count < 1) return; foreach (TreeNode node in selectionTreeView.Nodes) { diff --git a/CreamInstaller/Forms/SelectForm.cs b/CreamInstaller/Forms/SelectForm.cs index 73dfc7e..56fa244 100644 --- a/CreamInstaller/Forms/SelectForm.cs +++ b/CreamInstaller/Forms/SelectForm.cs @@ -23,9 +23,9 @@ internal sealed partial class SelectForm : CustomForm { private const string HelpButtonListPrefix = "\n • "; - private readonly SynchronizedCollection remainingDLCs = new(); + private readonly ConcurrentDictionary remainingDLCs = new(); - private readonly SynchronizedCollection remainingGames = new(); + private readonly ConcurrentDictionary remainingGames = new(); private List<(Platform platform, string id, string name)> programsToScan; @@ -39,8 +39,8 @@ internal sealed partial class SelectForm : CustomForm private List TreeNodes => GatherTreeNodes(selectionTreeView.Nodes); - private static void UpdateRemaining(Label label, SynchronizedCollection list, string descriptor) - => label.Text = list.Any() ? $"Remaining {descriptor} ({list.Count}): " + string.Join(", ", list).Replace("&", "&&") : ""; + private static void UpdateRemaining(Label label, ConcurrentDictionary list, string descriptor) + => label.Text = list.IsEmpty ? "" : $"Remaining {descriptor} ({list.Count}): " + string.Join(", ", list.Values).Replace("&", "&&"); private void UpdateRemainingGames() => UpdateRemaining(progressLabelGames, remainingGames, "games"); @@ -52,8 +52,7 @@ internal sealed partial class SelectForm : CustomForm { if (Program.Canceled) return; - if (!remainingGames.Contains(gameName)) - remainingGames.Add(gameName); + remainingGames[gameName] = gameName; UpdateRemainingGames(); }); } @@ -66,7 +65,7 @@ internal sealed partial class SelectForm : CustomForm { if (Program.Canceled) return; - remainingGames.Remove(gameName); + _ = remainingGames.Remove(gameName, out _); UpdateRemainingGames(); }); } @@ -81,8 +80,7 @@ internal sealed partial class SelectForm : CustomForm { if (Program.Canceled) return; - if (!remainingDLCs.Contains(dlcId)) - remainingDLCs.Add(dlcId); + remainingDLCs[dlcId] = dlcId; UpdateRemainingDLCs(); }); } @@ -95,14 +93,14 @@ internal sealed partial class SelectForm : CustomForm { if (Program.Canceled) return; - remainingDLCs.Remove(dlcId); + _ = remainingDLCs.Remove(dlcId, out _); UpdateRemainingDLCs(); }); } private async Task GetApplicablePrograms(IProgress progress, bool uninstallAll = false) { - if (!uninstallAll && (programsToScan is null || !programsToScan.Any())) + if (!uninstallAll && (programsToScan is null || programsToScan.Count < 1)) return; int totalGameCount = 0; int completeGameCount = 0; @@ -126,12 +124,12 @@ internal sealed partial class SelectForm : CustomForm if (uninstallAll || programsToScan.Any(c => c.platform is Platform.Paradox)) { AddToRemainingGames("Paradox Launcher"); - List dllDirectories = await ParadoxLauncher.InstallPath.GetDllDirectoriesFromGameDirectory(Platform.Paradox); + HashSet dllDirectories = await ParadoxLauncher.InstallPath.GetDllDirectoriesFromGameDirectory(Platform.Paradox); if (dllDirectories is not null) { if (uninstallAll) { - ProgramSelection bareSelection = ProgramSelection.FromPlatformId(Platform.Paradox, "PL") ?? new(); + Selection bareSelection = Selection.FromPlatformId(Platform.Paradox, "PL") ?? new(); bareSelection.Enabled = true; bareSelection.Id = "PL"; bareSelection.Name = "Paradox Launcher"; @@ -142,7 +140,7 @@ internal sealed partial class SelectForm : CustomForm } else { - ProgramSelection selection = ProgramSelection.FromPlatformId(Platform.Paradox, "PL") ?? new(); + Selection selection = Selection.FromPlatformId(Platform.Paradox, "PL") ?? new(); if (allCheckBox.Checked) selection.Enabled = true; if (koaloaderAllCheckBox.Checked) @@ -167,7 +165,7 @@ internal sealed partial class SelectForm : CustomForm int steamGamesToCheck; if (uninstallAll || programsToScan.Any(c => c.platform is Platform.Steam)) { - List<(string appId, string name, string branch, int buildId, string gameDirectory)> steamGames = await SteamLibrary.GetGames(); + HashSet<(string appId, string name, string branch, int buildId, string gameDirectory)> steamGames = await SteamLibrary.GetGames(); steamGamesToCheck = steamGames.Count; foreach ((string appId, string name, string branch, int buildId, string gameDirectory) in steamGames) { @@ -175,7 +173,7 @@ internal sealed partial class SelectForm : CustomForm return; if (!uninstallAll && (Program.IsGameBlocked(name, gameDirectory) || !programsToScan.Any(c => c.platform is Platform.Steam && c.id == appId))) { - Interlocked.Decrement(ref steamGamesToCheck); + _ = Interlocked.Decrement(ref steamGamesToCheck); continue; } AddToRemainingGames(name); @@ -183,16 +181,16 @@ internal sealed partial class SelectForm : CustomForm { if (Program.Canceled) return; - List dllDirectories = await gameDirectory.GetDllDirectoriesFromGameDirectory(Platform.Steam); + HashSet dllDirectories = await gameDirectory.GetDllDirectoriesFromGameDirectory(Platform.Steam); if (dllDirectories is null) { - Interlocked.Decrement(ref steamGamesToCheck); + _ = Interlocked.Decrement(ref steamGamesToCheck); RemoveFromRemainingGames(name); return; } if (uninstallAll) { - ProgramSelection bareSelection = ProgramSelection.FromPlatformId(Platform.Steam, appId) ?? new ProgramSelection(); + Selection bareSelection = Selection.FromPlatformId(Platform.Steam, appId) ?? new Selection(); bareSelection.Enabled = true; bareSelection.Id = appId; bareSelection.Name = name; @@ -206,7 +204,7 @@ internal sealed partial class SelectForm : CustomForm if (Program.Canceled) return; AppData appData = await SteamStore.QueryStoreAPI(appId); - Interlocked.Decrement(ref steamGamesToCheck); + _ = Interlocked.Decrement(ref steamGamesToCheck); VProperty appInfo = await SteamCMD.GetAppInfo(appId, branch, buildId); if (appData is null && appInfo is null) { @@ -215,7 +213,7 @@ internal sealed partial class SelectForm : CustomForm } if (Program.Canceled) return; - ConcurrentDictionary dlc = new(); + ConcurrentDictionary dlc = new(); List dlcTasks = new(); List dlcIds = new(); if (appData is not null) @@ -292,13 +290,20 @@ internal sealed partial class SelectForm : CustomForm if (Program.Canceled) return; if (!string.IsNullOrWhiteSpace(fullGameName)) - dlc[fullGameAppId] = (fullGameOnSteamStore ? DlcType.Steam : DlcType.SteamHidden, fullGameName, fullGameIcon); + dlc[fullGameAppId] = new() + { + Id = fullGameAppId, Type = fullGameOnSteamStore ? DLCType.Steam : DLCType.SteamHidden, Name = fullGameName, + Icon = fullGameIcon + }; } if (Program.Canceled) return; if (string.IsNullOrWhiteSpace(dlcName)) dlcName = "Unknown"; - dlc[dlcAppId] = (onSteamStore ? DlcType.Steam : DlcType.SteamHidden, dlcName, dlcIcon); + dlc[dlcAppId] = new() + { + Id = dlcAppId, Type = onSteamStore ? DLCType.Steam : DLCType.SteamHidden, Name = dlcName, Icon = dlcIcon + }; RemoveFromRemainingDLCs(dlcAppId); }); dlcTasks.Add(task); @@ -317,8 +322,14 @@ internal sealed partial class SelectForm : CustomForm await task; } steamGamesToCheck = 0; - ProgramSelection selection = ProgramSelection.FromPlatformId(Platform.Steam, appId) ?? new ProgramSelection(); - selection.Enabled = allCheckBox.Checked || selection.SelectedDlc.Any() || selection.ExtraSelectedDlc.Any(); + if (dlc.IsEmpty) + { + RemoveFromRemainingGames(name); + return; + } + Selection selection = Selection.FromPlatformId(Platform.Steam, appId) ?? new Selection(); + selection.Enabled = allCheckBox.Checked || selection.DLC.Any(dlc => dlc.Enabled) + || selection.ExtraSelections.Any(extraSelection => extraSelection.DLC.Any(dlc => dlc.Enabled)); if (koaloaderAllCheckBox.Checked) selection.Koaloader = true; selection.Id = appId; @@ -327,12 +338,12 @@ internal sealed partial class SelectForm : CustomForm selection.ExecutableDirectories = await SteamLibrary.GetExecutableDirectories(selection.RootDirectory); selection.DllDirectories = dllDirectories; selection.Platform = Platform.Steam; - selection.ProductUrl = "https://store.steampowered.com/app/" + appId; - selection.IconUrl = IconGrabber.SteamAppImagesPath + @$"\{appId}\{appInfo?.Value.GetChild("common")?.GetChild("icon")}.jpg"; - selection.SubIconUrl = appData?.HeaderImage ?? IconGrabber.SteamAppImagesPath + selection.Product = "https://store.steampowered.com/app/" + appId; + selection.Icon = IconGrabber.SteamAppImagesPath + @$"\{appId}\{appInfo?.Value.GetChild("common")?.GetChild("icon")}.jpg"; + selection.SubIcon = appData?.HeaderImage ?? IconGrabber.SteamAppImagesPath + @$"\{appId}\{appInfo?.Value.GetChild("common")?.GetChild("clienticon")}.ico"; selection.Publisher = appData?.Publishers[0] ?? appInfo?.Value.GetChild("extended")?.GetChild("publisher")?.ToString(); - selection.WebsiteUrl = appData?.Website; + selection.Website = appData?.Website; if (Program.Canceled) return; selectionTreeView.Invoke(delegate @@ -346,18 +357,17 @@ internal sealed partial class SelectForm : CustomForm programNode.Checked = selection.Enabled; if (programNode.TreeView is null) _ = selectionTreeView.Nodes.Add(programNode); - foreach ((string appId, (DlcType type, string name, string icon) dlcApp) in dlc) + foreach ((_, SelectionDLC dlc) in dlc) { if (Program.Canceled) return; - selection.AllDlc[appId] = dlcApp; - if (allCheckBox.Checked && dlcApp.name != "Unknown") - selection.SelectedDlc[appId] = dlcApp; - TreeNode dlcNode = treeNodes.Find(s => s.Tag is Platform.Steam && s.Name == appId) ?? new TreeNode(); + dlc.Selection = selection; + dlc.Enabled = dlc.Name != "Unknown" && allCheckBox.Checked; + TreeNode dlcNode = treeNodes.Find(s => s.Tag is Platform.Steam && s.Name == dlc.Id) ?? new TreeNode(); dlcNode.Tag = selection.Platform; - dlcNode.Name = appId; - dlcNode.Text = dlcApp.name; - dlcNode.Checked = selection.SelectedDlc.ContainsKey(appId); + dlcNode.Name = dlc.Id; + dlcNode.Text = dlc.Name; + dlcNode.Checked = dlc.Enabled; if (dlcNode.Parent is null) _ = programNode.Nodes.Add(dlcNode); } @@ -371,7 +381,7 @@ internal sealed partial class SelectForm : CustomForm } if (uninstallAll || programsToScan.Any(c => c.platform is Platform.Epic)) { - List epicGames = await EpicLibrary.GetGames(); + HashSet epicGames = await EpicLibrary.GetGames(); foreach (Manifest manifest in epicGames) { string @namespace = manifest.CatalogNamespace; @@ -386,7 +396,7 @@ internal sealed partial class SelectForm : CustomForm { if (Program.Canceled) return; - List dllDirectories = await directory.GetDllDirectoriesFromGameDirectory(Platform.Epic); + HashSet dllDirectories = await directory.GetDllDirectoriesFromGameDirectory(Platform.Epic); if (dllDirectories is null) { RemoveFromRemainingGames(name); @@ -394,7 +404,7 @@ internal sealed partial class SelectForm : CustomForm } if (uninstallAll) { - ProgramSelection bareSelection = ProgramSelection.FromPlatformId(Platform.Epic, @namespace) ?? new ProgramSelection(); + Selection bareSelection = Selection.FromPlatformId(Platform.Epic, @namespace) ?? new Selection(); bareSelection.Enabled = true; bareSelection.Id = @namespace; bareSelection.Name = name; @@ -407,11 +417,13 @@ internal sealed partial class SelectForm : CustomForm } if (Program.Canceled) return; - ConcurrentDictionary entitlements = new(); + ConcurrentDictionary catalogItems = new(); + // get catalog items + ConcurrentDictionary entitlements = new(); List dlcTasks = new(); - List<(string id, string name, string product, string icon, string developer)> entitlementIds - = await EpicStore.QueryEntitlements(@namespace); - if (entitlementIds.Any()) + List<(string id, string name, string product, string icon, string developer)> + entitlementIds = await EpicStore.QueryEntitlements(@namespace); + if (entitlementIds.Count > 0) foreach ((string id, string name, string product, string icon, string developer) in entitlementIds) { if (Program.Canceled) @@ -421,16 +433,15 @@ internal sealed partial class SelectForm : CustomForm { if (Program.Canceled) return; - entitlements[id] = (name, product, icon, developer); + entitlements[id] = new() + { + Id = id, Name = name, Product = product, Icon = icon, + Publisher = developer + }; RemoveFromRemainingDLCs(id); }); dlcTasks.Add(task); } - if ( /*!catalogItems.Any() && */!entitlements.Any()) - { - RemoveFromRemainingGames(name); - return; - } if (Program.Canceled) return; foreach (Task task in dlcTasks) @@ -439,8 +450,14 @@ internal sealed partial class SelectForm : CustomForm return; await task; } - ProgramSelection selection = ProgramSelection.FromPlatformId(Platform.Epic, @namespace) ?? new ProgramSelection(); - selection.Enabled = allCheckBox.Checked || selection.SelectedDlc.Any() || selection.ExtraSelectedDlc.Any(); + if (catalogItems.IsEmpty && entitlements.IsEmpty) + { + RemoveFromRemainingGames(name); + return; + } + Selection selection = Selection.FromPlatformId(Platform.Epic, @namespace) ?? new Selection(); + selection.Enabled = allCheckBox.Checked || selection.DLC.Any(dlc => dlc.Enabled) + || selection.ExtraSelections.Any(extraSelection => extraSelection.DLC.Any(dlc => dlc.Enabled)); if (koaloaderAllCheckBox.Checked) selection.Koaloader = true; selection.Id = @namespace; @@ -449,12 +466,13 @@ internal sealed partial class SelectForm : CustomForm selection.ExecutableDirectories = await EpicLibrary.GetExecutableDirectories(selection.RootDirectory); selection.DllDirectories = dllDirectories; selection.Platform = Platform.Epic; - foreach (KeyValuePair pair in entitlements.Where(p - => p.Value.name == selection.Name)) + foreach (KeyValuePair dlc in entitlements.Where(dlc => dlc.Value.Name == selection.Name)) { - selection.ProductUrl = "https://www.epicgames.com/store/product/" + pair.Value.product; - selection.IconUrl = pair.Value.icon; - selection.Publisher = pair.Value.developer; + if (Program.Canceled) + return; + selection.Product = "https://www.epicgames.com/store/product/" + dlc.Value.Product; + selection.Icon = dlc.Value.Icon; + selection.Publisher = dlc.Value.Publisher; } if (Program.Canceled) return; @@ -469,35 +487,49 @@ internal sealed partial class SelectForm : CustomForm programNode.Checked = selection.Enabled; if (programNode.TreeView is null) _ = selectionTreeView.Nodes.Add(programNode); - /*TreeNode catalogItemsNode = treeNodes.Find(s => s.Tag is Platform.Epic && s.Name == @namespace + "_catalogItems") ?? new(); - catalogItemsNode.Tag = selection.Platform; - catalogItemsNode.Name = @namespace + "_catalogItems"; - catalogItemsNode.Text = "Catalog Items"; - catalogItemsNode.Checked = selection.SelectedDlc.Any(pair => pair.Value.type == DlcType.CatalogItem); - if (catalogItemsNode.Parent is null) - programNode.Nodes.Add(catalogItemsNode);*/ - if (entitlements.Any()) - /*TreeNode entitlementsNode = treeNodes.Find(s => s.Tag is Platform.Epic && s.Name == @namespace + "_entitlements") ?? new(); - entitlementsNode.Tag = selection.Platform; - entitlementsNode.Name = @namespace + "_entitlements"; - entitlementsNode.Text = "Entitlements"; - entitlementsNode.Checked = selection.SelectedDlc.Any(pair => pair.Value.type == DlcType.Entitlement); - if (entitlementsNode.Parent is null) - programNode.Nodes.Add(entitlementsNode);*/ - foreach ((string dlcId, (string name, string product, string icon, string developer) value) in entitlements) + if (!catalogItems.IsEmpty) + /*TreeNode catalogItemsNode = treeNodes.Find(node => node.Tag is Platform.Epic && node.Name == @namespace + "_catalogItems") ?? new(); + catalogItemsNode.Name = @namespace + "_catalogItems"; + catalogItemsNode.Text = "Catalog Items"; + catalogItemsNode.Checked = selection.DLC.Any(dlc => dlc.Type is DLCType.EpicCatalogItem && dlc.Enabled); + if (catalogItemsNode.Parent is null) + _ = programNode.Nodes.Add(catalogItemsNode);*/ + foreach ((_, SelectionDLC dlc) in catalogItems) { - (DlcType type, string name, string icon) dlcApp = (DlcType.EpicEntitlement, value.name, value.icon); - selection.AllDlc[dlcId] = dlcApp; - if (allCheckBox.Checked) - selection.SelectedDlc[dlcId] = dlcApp; - TreeNode dlcNode = treeNodes.Find(s => s.Tag is Platform.Epic && s.Name == dlcId) ?? new TreeNode(); + if (Program.Canceled) + return; + dlc.Selection = selection; + dlc.Enabled = allCheckBox.Checked; + TreeNode dlcNode = treeNodes.Find(s => s.Tag is Platform.Epic && s.Name == dlc.Id) ?? new TreeNode(); dlcNode.Tag = selection.Platform; - dlcNode.Name = dlcId; - dlcNode.Text = dlcApp.name; - dlcNode.Checked = selection.SelectedDlc.ContainsKey(dlcId); + dlcNode.Name = dlc.Id; + dlcNode.Text = dlc.Name; + dlcNode.Checked = dlc.Enabled; if (dlcNode.Parent is null) - _ = programNode.Nodes.Add(dlcNode); //entitlementsNode.Nodes.Add(dlcNode); + _ = programNode.Nodes.Add(dlcNode); //_ = catalogItemsNode.Nodes.Add(dlcNode); } + if (entitlements.IsEmpty) + return; + /*TreeNode entitlementsNode = treeNodes.Find(node => node.Tag is Platform.Epic && node.Name == @namespace + "_entitlements") ?? new(); + entitlementsNode.Name = @namespace + "_entitlements"; + entitlementsNode.Text = "Entitlements"; + entitlementsNode.Checked = selection.DLC.Any(dlc => dlc.Type is DLCType.EpicEntitlement && dlc.Enabled); + if (entitlementsNode.Parent is null) + _ = programNode.Nodes.Add(entitlementsNode);*/ + foreach ((_, SelectionDLC dlc) in entitlements) + { + if (Program.Canceled) + return; + dlc.Selection = selection; + dlc.Enabled = allCheckBox.Checked; + TreeNode dlcNode = treeNodes.Find(s => s.Tag is Platform.Epic && s.Name == dlc.Id) ?? new TreeNode(); + dlcNode.Tag = selection.Platform; + dlcNode.Name = dlc.Id; + dlcNode.Text = dlc.Name; + dlcNode.Checked = dlc.Enabled; + if (dlcNode.Parent is null) + _ = programNode.Nodes.Add(dlcNode); //_ = entitlementsNode.Nodes.Add(dlcNode); + } }); if (Program.Canceled) return; @@ -508,7 +540,7 @@ internal sealed partial class SelectForm : CustomForm } if (uninstallAll || programsToScan.Any(c => c.platform is Platform.Ubisoft)) { - List<(string gameId, string name, string gameDirectory)> ubisoftGames = await UbisoftLibrary.GetGames(); + HashSet<(string gameId, string name, string gameDirectory)> ubisoftGames = await UbisoftLibrary.GetGames(); foreach ((string gameId, string name, string gameDirectory) in ubisoftGames) { if (Program.Canceled) @@ -520,7 +552,7 @@ internal sealed partial class SelectForm : CustomForm { if (Program.Canceled) return; - List dllDirectories = await gameDirectory.GetDllDirectoriesFromGameDirectory(Platform.Ubisoft); + HashSet dllDirectories = await gameDirectory.GetDllDirectoriesFromGameDirectory(Platform.Ubisoft); if (dllDirectories is null) { RemoveFromRemainingGames(name); @@ -528,7 +560,7 @@ internal sealed partial class SelectForm : CustomForm } if (uninstallAll) { - ProgramSelection bareSelection = ProgramSelection.FromPlatformId(Platform.Ubisoft, gameId) ?? new ProgramSelection(); + Selection bareSelection = Selection.FromPlatformId(Platform.Ubisoft, gameId) ?? new Selection(); bareSelection.Enabled = true; bareSelection.Id = gameId; bareSelection.Name = name; @@ -541,8 +573,9 @@ internal sealed partial class SelectForm : CustomForm } if (Program.Canceled) return; - ProgramSelection selection = ProgramSelection.FromPlatformId(Platform.Ubisoft, gameId) ?? new ProgramSelection(); - selection.Enabled = allCheckBox.Checked || selection.SelectedDlc.Any() || selection.ExtraSelectedDlc.Any(); + Selection selection = Selection.FromPlatformId(Platform.Ubisoft, gameId) ?? new Selection(); + selection.Enabled = allCheckBox.Checked || selection.DLC.Any(dlc => dlc.Enabled) + || selection.ExtraSelections.Any(extraSelection => extraSelection.DLC.Any(dlc => dlc.Enabled)); if (koaloaderAllCheckBox.Checked) selection.Koaloader = true; selection.Id = gameId; @@ -551,7 +584,7 @@ internal sealed partial class SelectForm : CustomForm selection.ExecutableDirectories = await UbisoftLibrary.GetExecutableDirectories(selection.RootDirectory); selection.DllDirectories = dllDirectories; selection.Platform = Platform.Ubisoft; - selection.IconUrl = IconGrabber.GetDomainFaviconUrl("store.ubi.com"); + selection.Icon = IconGrabber.GetDomainFaviconUrl("store.ubi.com"); selectionTreeView.Invoke(delegate { if (Program.Canceled) @@ -603,7 +636,7 @@ internal sealed partial class SelectForm : CustomForm ShowProgressBar(); await ProgramData.Setup(this); bool scan = forceScan; - if (!scan && (programsToScan is null || !programsToScan.Any() || forceProvideChoices)) + if (!scan && (programsToScan is null || programsToScan.Count < 1 || forceProvideChoices)) { List<(Platform platform, string id, string name, bool alreadySelected)> gameChoices = new(); if (ParadoxLauncher.InstallPath.DirectoryExists()) @@ -621,7 +654,7 @@ internal sealed partial class SelectForm : CustomForm foreach ((string gameId, string name, string _) in (await UbisoftLibrary.GetGames()).Where(g => !Program.IsGameBlocked(g.name, g.gameDirectory))) gameChoices.Add((Platform.Ubisoft, gameId, name, programsToScan is not null && programsToScan.Any(p => p.platform is Platform.Ubisoft && p.id == gameId))); - if (gameChoices.Any()) + if (gameChoices.Count > 0) { using SelectDialogForm form = new(this); DialogResult selectResult = form.QueryUser("Choose which programs and/or games to scan:", gameChoices, @@ -649,11 +682,11 @@ internal sealed partial class SelectForm : CustomForm await GetApplicablePrograms(iProgress, true); if (!Program.Canceled) OnUninstall(null, null); - ProgramSelection.All.Clear(); + Selection.All.Clear(); programsToScan = null; } else - scan = selectResult == DialogResult.OK && choices is not null && choices.Any(); + scan = selectResult == DialogResult.OK && choices is not null && choices.Count > 0; const string retry = "\n\nPress the \"Rescan\" button to re-choose."; if (scan) { @@ -696,7 +729,7 @@ internal sealed partial class SelectForm : CustomForm } setup = false; progressLabel.Text = "Gathering and caching your applicable games and their DLCs . . . "; - ProgramSelection.ValidateAll(programsToScan); + Selection.ValidateAll(programsToScan); TreeNodes.ForEach(node => node.Remove()); await GetApplicablePrograms(iProgress); await SteamCMD.Cleanup(); @@ -704,11 +737,11 @@ internal sealed partial class SelectForm : CustomForm OnLoadDlc(null, null); OnLoadKoaloader(null, null); HideProgressBar(); - selectionTreeView.Enabled = ProgramSelection.All.Any(); + selectionTreeView.Enabled = Selection.All.Count > 0; allCheckBox.Enabled = selectionTreeView.Enabled; koaloaderAllCheckBox.Enabled = selectionTreeView.Enabled; noneFoundLabel.Visible = !selectionTreeView.Enabled; - installButton.Enabled = ProgramSelection.AllEnabled.Any(); + installButton.Enabled = Selection.AllEnabled.Count > 0; uninstallButton.Enabled = installButton.Enabled; saveButton.Enabled = CanSaveDlc(); loadButton.Enabled = CanLoadDlc(); @@ -735,7 +768,7 @@ internal sealed partial class SelectForm : CustomForm allCheckBox.CheckedChanged -= OnAllCheckBoxChanged; allCheckBox.Checked = TreeNodes.TrueForAll(node => node.Text == "Unknown" || node.Checked); allCheckBox.CheckedChanged += OnAllCheckBoxChanged; - installButton.Enabled = ProgramSelection.AllEnabled.Any(); + installButton.Enabled = Selection.AllEnabled.Count > 0; uninstallButton.Enabled = installButton.Enabled; saveButton.Enabled = CanSaveDlc(); resetButton.Enabled = CanResetDlc(); @@ -770,19 +803,15 @@ internal sealed partial class SelectForm : CustomForm { string id = node.Name; Platform platform = (Platform)node.Tag; - (string gameId, (DlcType type, string name, string icon) app)? dlc = ProgramSelection.GetDlcFromPlatformId(platform, id); - if (dlc.HasValue) + Selection selection = Selection.FromPlatformId(platform, id); + if (selection is not null) { - (string gameId, _) = dlc.Value; - ProgramSelection selection = ProgramSelection.FromPlatformId(platform, gameId); - selection?.ToggleDlc(node.Name, node.Checked); - } - else - { - ProgramSelection selection = ProgramSelection.FromPlatformId(platform, id); - if (selection is not null) - selection.Enabled = node.Checked; + selection.Enabled = node.Checked; + return; } + SelectionDLC dlc = SelectionDLC.FromPlatformId(platform, id); + if (dlc is not null) + dlc.Enabled = node.Checked; } private static List GatherTreeNodes(TreeNodeCollection nodeCollection) @@ -830,31 +859,25 @@ internal sealed partial class SelectForm : CustomForm internal void OnNodeRightClick(TreeNode node, Point location) => Invoke(() => { - ContextMenuStrip contextMenuStrip = ContextMenuStrip; - while (ContextMenuStrip.Tag is true) - Thread.Sleep(100); - ContextMenuStrip.Tag = true; + ContextMenuStrip contextMenuStrip = new(); ToolStripItemCollection items = contextMenuStrip.Items; - items.Clear(); string id = node.Name; Platform platform = (Platform)node.Tag; - ProgramSelection selection = ProgramSelection.FromPlatformId(platform, id); - (string gameAppId, (DlcType type, string name, string icon) app)? dlc = null; + Selection selection = Selection.FromPlatformId(platform, id); + SelectionDLC dlc = null; if (selection is null) - dlc = ProgramSelection.GetDlcFromPlatformId(platform, id); - ProgramSelection dlcParentSelection = null; + dlc = SelectionDLC.FromPlatformId(platform, id); + Selection dlcParentSelection = null; if (dlc is not null) - dlcParentSelection = ProgramSelection.FromPlatformId(platform, dlc.Value.gameAppId); + dlcParentSelection = Selection.FromPlatformId(platform, dlc.Selection.Id); if (selection is null && dlcParentSelection is null) return; - ContextMenuItem header; - if (id == "PL") - header = new(node.Text, "Paradox Launcher"); - else if (selection is not null) - header = new(node.Text, (id, selection.IconUrl)); - else - header = new(node.Text, (id, dlc.Value.app.icon), (id, dlcParentSelection.IconUrl)); - items.Add(header); + ContextMenuItem header = id == "PL" + ? new(node.Text, "Paradox Launcher") + : selection is not null + ? new(node.Text, (id, selection.Icon)) + : new(node.Text, (id, dlc.Icon), (id, dlcParentSelection.Icon)); + _ = items.Add(header); string appInfoVDF = $@"{SteamCMD.AppInfoPath}\{id}.vdf"; string appInfoJSON = $@"{SteamCMD.AppInfoPath}\{id}.json"; string cooldown = $@"{ProgramData.CooldownPath}\{id}.txt"; @@ -872,12 +895,12 @@ internal sealed partial class SelectForm : CustomForm } if (appInfoVDF.FileExists()) queries.Add(new("Open SteamCMD Query", "Notepad", (_, _) => Diagnostics.OpenFileInNotepad(appInfoVDF))); - if (queries.Any()) + if (queries.Count > 0) { - items.Add(new ToolStripSeparator()); + _ = items.Add(new ToolStripSeparator()); foreach (ContextMenuItem query in queries) - items.Add(query); - items.Add(new ContextMenuItem("Refresh Queries", "Command Prompt", (_, _) => + _ = items.Add(query); + _ = items.Add(new ContextMenuItem("Refresh Queries", "Command Prompt", (_, _) => { appInfoVDF.DeleteFile(); appInfoJSON.DeleteFile(); @@ -890,16 +913,16 @@ internal sealed partial class SelectForm : CustomForm { if (id == "PL") { - items.Add(new ToolStripSeparator()); + _ = items.Add(new ToolStripSeparator()); async void EventHandler(object sender, EventArgs e) => await ParadoxLauncher.Repair(this, selection); - items.Add(new ContextMenuItem("Repair", "Command Prompt", EventHandler)); + _ = items.Add(new ContextMenuItem("Repair", "Command Prompt", EventHandler)); } - items.Add(new ToolStripSeparator()); - items.Add(new ContextMenuItem("Open Root Directory", "File Explorer", + _ = items.Add(new ToolStripSeparator()); + _ = items.Add(new ContextMenuItem("Open Root Directory", "File Explorer", (_, _) => Diagnostics.OpenDirectoryInFileExplorer(selection.RootDirectory))); int executables = 0; foreach ((string directory, BinaryType binaryType) in selection.ExecutableDirectories.ToList()) - items.Add(new ContextMenuItem($"Open Executable Directory #{++executables} ({(binaryType == BinaryType.BIT32 ? "32" : "64")}-bit)", + _ = items.Add(new ContextMenuItem($"Open Executable Directory #{++executables} ({(binaryType == BinaryType.BIT32 ? "32" : "64")}-bit)", "File Explorer", (_, _) => Diagnostics.OpenDirectoryInFileExplorer(directory))); List directories = selection.DllDirectories.ToList(); int steam = 0, epic = 0, r1 = 0, r2 = 0; @@ -910,7 +933,7 @@ internal sealed partial class SelectForm : CustomForm out string config, out string old_log, out string log, out string cache); if (api32.FileExists() || api32_o.FileExists() || api64.FileExists() || api64_o.FileExists() || old_config.FileExists() || config.FileExists() || old_log.FileExists() || log.FileExists() || cache.FileExists()) - items.Add(new ContextMenuItem($"Open Steamworks Directory #{++steam}", "File Explorer", + _ = items.Add(new ContextMenuItem($"Open Steamworks Directory #{++steam}", "File Explorer", (_, _) => Diagnostics.OpenDirectoryInFileExplorer(directory))); } if (selection.Platform is Platform.Epic or Platform.Paradox) @@ -919,7 +942,7 @@ internal sealed partial class SelectForm : CustomForm directory.GetScreamApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config, out string log); if (api32.FileExists() || api32_o.FileExists() || api64.FileExists() || api64_o.FileExists() || config.FileExists() || log.FileExists()) - items.Add(new ContextMenuItem($"Open EOS Directory #{++epic}", "File Explorer", + _ = items.Add(new ContextMenuItem($"Open EOS Directory #{++epic}", "File Explorer", (_, _) => Diagnostics.OpenDirectoryInFileExplorer(directory))); } if (selection.Platform is Platform.Ubisoft) @@ -928,13 +951,13 @@ internal sealed partial class SelectForm : CustomForm directory.GetUplayR1Components(out string api32, out string api32_o, out string api64, out string api64_o, out string config, out string log); if (api32.FileExists() || api32_o.FileExists() || api64.FileExists() || api64_o.FileExists() || config.FileExists() || log.FileExists()) - items.Add(new ContextMenuItem($"Open Uplay R1 Directory #{++r1}", "File Explorer", + _ = items.Add(new ContextMenuItem($"Open Uplay R1 Directory #{++r1}", "File Explorer", (_, _) => Diagnostics.OpenDirectoryInFileExplorer(directory))); directory.GetUplayR2Components(out string old_api32, out string old_api64, out api32, out api32_o, out api64, out api64_o, out config, out log); if (old_api32.FileExists() || old_api64.FileExists() || api32.FileExists() || api32_o.FileExists() || api64.FileExists() || api64_o.FileExists() || config.FileExists() || log.FileExists()) - items.Add(new ContextMenuItem($"Open Uplay R2 Directory #{++r2}", "File Explorer", + _ = items.Add(new ContextMenuItem($"Open Uplay R2 Directory #{++r2}", "File Explorer", (_, _) => Diagnostics.OpenDirectoryInFileExplorer(directory))); } } @@ -942,39 +965,39 @@ internal sealed partial class SelectForm : CustomForm { if (selection?.Platform is Platform.Steam || dlcParentSelection?.Platform is Platform.Steam) { - items.Add(new ToolStripSeparator()); - items.Add(new ContextMenuItem("Open SteamDB", "SteamDB", (_, _) => Diagnostics.OpenUrlInInternetBrowser("https://steamdb.info/app/" + id))); + _ = items.Add(new ToolStripSeparator()); + _ = items.Add(new ContextMenuItem("Open SteamDB", "SteamDB", + (_, _) => Diagnostics.OpenUrlInInternetBrowser("https://steamdb.info/app/" + id))); } if (selection is not null) switch (selection.Platform) { case Platform.Steam: - items.Add(new ContextMenuItem("Open Steam Store", "Steam Store", - (_, _) => Diagnostics.OpenUrlInInternetBrowser(selection.ProductUrl))); - items.Add(new ContextMenuItem("Open Steam Community", ("Sub_" + id, selection.SubIconUrl), "Steam Community", + _ = items.Add(new ContextMenuItem("Open Steam Store", "Steam Store", + (_, _) => Diagnostics.OpenUrlInInternetBrowser(selection.Product))); + _ = items.Add(new ContextMenuItem("Open Steam Community", ("Sub_" + id, selection.SubIcon), "Steam Community", (_, _) => Diagnostics.OpenUrlInInternetBrowser("https://steamcommunity.com/app/" + id))); break; case Platform.Epic: - items.Add(new ToolStripSeparator()); - items.Add(new ContextMenuItem("Open ScreamDB", "ScreamDB", + _ = items.Add(new ToolStripSeparator()); + _ = items.Add(new ContextMenuItem("Open ScreamDB", "ScreamDB", (_, _) => Diagnostics.OpenUrlInInternetBrowser("https://scream-db.web.app/offers/" + id))); - items.Add(new ContextMenuItem("Open Epic Games Store", "Epic Games", - (_, _) => Diagnostics.OpenUrlInInternetBrowser(selection.ProductUrl))); + _ = items.Add(new ContextMenuItem("Open Epic Games Store", "Epic Games", + (_, _) => Diagnostics.OpenUrlInInternetBrowser(selection.Product))); break; case Platform.Ubisoft: - items.Add(new ToolStripSeparator()); - items.Add(new ContextMenuItem("Open Ubisoft Store", "Ubisoft Store", + _ = items.Add(new ToolStripSeparator()); + _ = items.Add(new ContextMenuItem("Open Ubisoft Store", "Ubisoft Store", (_, _) => Diagnostics.OpenUrlInInternetBrowser( "https://store.ubi.com/us/" + selection.Name.Replace(" ", "-").ToLowerInvariant()))); break; } } - if (selection?.WebsiteUrl != null) - items.Add(new ContextMenuItem("Open Official Website", ("Web_" + id, IconGrabber.GetDomainFaviconUrl(selection.WebsiteUrl)), - (_, _) => Diagnostics.OpenUrlInInternetBrowser(selection.WebsiteUrl))); + if (selection?.Website is not null) + _ = items.Add(new ContextMenuItem("Open Official Website", ("Web_" + id, IconGrabber.GetDomainFaviconUrl(selection.Website)), + (_, _) => Diagnostics.OpenUrlInInternetBrowser(selection.Website))); contextMenuStrip.Show(selectionTreeView, location); contextMenuStrip.Refresh(); - ContextMenuStrip.Tag = null; }); private void OnLoad(object sender, EventArgs _) @@ -996,9 +1019,9 @@ internal sealed partial class SelectForm : CustomForm private void OnAccept(bool uninstall = false) { - if (!ProgramSelection.All.Any()) + if (Selection.All.Count < 1) return; - if (ProgramSelection.AllEnabled.Any(selection => !Program.AreDllsLockedDialog(this, selection))) + if (Selection.AllEnabled.Any(selection => !Program.AreDllsLockedDialog(this, selection))) return; if (!uninstall && ParadoxLauncher.DlcDialog(this)) return; @@ -1053,8 +1076,8 @@ internal sealed partial class SelectForm : CustomForm private void OnKoaloaderAllCheckBoxChanged(object sender, EventArgs e) { - bool shouldCheck = ProgramSelection.AllSafe.Any(selection => !selection.Koaloader); - foreach (ProgramSelection selection in ProgramSelection.AllSafe) + bool shouldCheck = Selection.AllSafe.Any(selection => !selection.Koaloader); + foreach (Selection selection in Selection.AllSafe) selection.Koaloader = shouldCheck; selectionTreeView.Invalidate(); koaloaderAllCheckBox.CheckedChanged -= OnKoaloaderAllCheckBoxChanged; @@ -1076,7 +1099,7 @@ internal sealed partial class SelectForm : CustomForm if (node.Text == "Unknown" ? node.Checked : !node.Checked) choices.Add((platform, node.Parent.Name, node.Name)); else - choices.RemoveAll(n => n.platform == platform && n.gameId == parent.Name && n.dlcId == node.Name); + _ = choices.RemoveAll(n => n.platform == platform && n.gameId == parent.Name && n.dlcId == node.Name); } choices = choices.Distinct().ToList(); ProgramData.WriteDlcChoices(choices); @@ -1111,19 +1134,19 @@ internal sealed partial class SelectForm : CustomForm resetButton.Enabled = CanResetDlc(); } - private static bool AreKoaloaderSelectionsDefault() => ProgramSelection.AllSafe.All(selection => selection.Koaloader && selection.KoaloaderProxy is null); + private static bool AreKoaloaderSelectionsDefault() => Selection.AllSafe.All(selection => selection.Koaloader && selection.KoaloaderProxy is null); private static bool CanSaveKoaloader() => ProgramData.ReadKoaloaderChoices().Any() || !AreKoaloaderSelectionsDefault(); private void OnSaveKoaloader(object sender, EventArgs e) { List<(Platform platform, string id, string proxy, bool enabled)> choices = ProgramData.ReadKoaloaderChoices().ToList(); - foreach (ProgramSelection selection in ProgramSelection.AllSafe) + foreach (Selection selection in Selection.AllSafe) { _ = choices.RemoveAll(c => c.platform == selection.Platform && c.id == selection.Id); - if (selection.KoaloaderProxy is not null and not ProgramSelection.DefaultKoaloaderProxy || !selection.Koaloader) - choices.Add((selection.Platform, selection.Id, - selection.KoaloaderProxy == ProgramSelection.DefaultKoaloaderProxy ? null : selection.KoaloaderProxy, selection.Koaloader)); + if (selection.KoaloaderProxy is not null and not Selection.DefaultKoaloaderProxy || !selection.Koaloader) + choices.Add((selection.Platform, selection.Id, selection.KoaloaderProxy == Selection.DefaultKoaloaderProxy ? null : selection.KoaloaderProxy, + selection.Koaloader)); } ProgramData.WriteKoaloaderProxyChoices(choices); saveKoaloaderButton.Enabled = CanSaveKoaloader(); @@ -1135,7 +1158,7 @@ internal sealed partial class SelectForm : CustomForm private void OnLoadKoaloader(object sender, EventArgs e) { List<(Platform platform, string id, string proxy, bool enabled)> choices = ProgramData.ReadKoaloaderChoices().ToList(); - foreach (ProgramSelection selection in ProgramSelection.AllSafe) + foreach (Selection selection in Selection.AllSafe) if (choices.Any(c => c.platform == selection.Platform && c.id == selection.Id)) { (Platform platform, string id, string proxy, bool enabled) @@ -1146,12 +1169,12 @@ internal sealed partial class SelectForm : CustomForm proxy.GetProxyInfoFromIdentifier(out currentProxy, out _); if (proxy != currentProxy && choices.Remove(choice)) // convert pre-v4.1.0.0 choices choices.Add((platform, id, currentProxy, enabled)); - if (currentProxy is null or ProgramSelection.DefaultKoaloaderProxy && enabled) + if (currentProxy is null or Selection.DefaultKoaloaderProxy && enabled) _ = choices.RemoveAll(c => c.platform == platform && c.id == id); else { selection.Koaloader = enabled; - selection.KoaloaderProxy = currentProxy == ProgramSelection.DefaultKoaloaderProxy ? currentProxy : proxy; + selection.KoaloaderProxy = currentProxy == Selection.DefaultKoaloaderProxy ? currentProxy : proxy; } } else @@ -1168,7 +1191,7 @@ internal sealed partial class SelectForm : CustomForm private void OnResetKoaloader(object sender, EventArgs e) { - foreach (ProgramSelection selection in ProgramSelection.AllSafe) + foreach (Selection selection in Selection.AllSafe) { selection.Koaloader = true; selection.KoaloaderProxy = null; @@ -1182,7 +1205,7 @@ internal sealed partial class SelectForm : CustomForm saveKoaloaderButton.Enabled = CanSaveKoaloader(); resetKoaloaderButton.Enabled = CanResetKoaloader(); koaloaderAllCheckBox.CheckedChanged -= OnKoaloaderAllCheckBoxChanged; - koaloaderAllCheckBox.Checked = ProgramSelection.AllSafe.TrueForAll(selection => selection.Koaloader); + koaloaderAllCheckBox.Checked = Selection.AllSafe.TrueForAll(selection => selection.Koaloader); koaloaderAllCheckBox.CheckedChanged += OnKoaloaderAllCheckBoxChanged; } diff --git a/CreamInstaller/Platforms/Epic/EpicLibrary.cs b/CreamInstaller/Platforms/Epic/EpicLibrary.cs index 3f70e63..0d0fba5 100644 --- a/CreamInstaller/Platforms/Epic/EpicLibrary.cs +++ b/CreamInstaller/Platforms/Epic/EpicLibrary.cs @@ -27,13 +27,13 @@ internal static class EpicLibrary } } - internal static async Task> GetExecutableDirectories(string gameDirectory) + internal static async Task> GetExecutableDirectories(string gameDirectory) => await Task.Run(async () => await gameDirectory.GetExecutableDirectories(true)); - internal static async Task> GetGames() + internal static async Task> GetGames() => await Task.Run(() => { - List games = new(); + HashSet games = new(); string manifests = EpicManifestsPath; if (!manifests.DirectoryExists()) return games; @@ -47,7 +47,7 @@ internal static class EpicLibrary Manifest manifest = JsonConvert.DeserializeObject(json); if (manifest is not null && manifest.CatalogItemId == manifest.MainGameCatalogItemId && !games.Any(g => g.CatalogItemId == manifest.CatalogItemId && g.InstallLocation == manifest.InstallLocation)) - games.Add(manifest); + _ = games.Add(manifest); } catch { diff --git a/CreamInstaller/Platforms/Epic/EpicStore.cs b/CreamInstaller/Platforms/Epic/EpicStore.cs index a3883a9..9c492e5 100644 --- a/CreamInstaller/Platforms/Epic/EpicStore.cs +++ b/CreamInstaller/Platforms/Epic/EpicStore.cs @@ -54,7 +54,7 @@ internal static class EpicStore foreach (Element element in searchStore) { string title = element.Title; - string product = element.CatalogNs is not null && element.CatalogNs.Mappings.Any() ? element.CatalogNs.Mappings.First().PageSlug : null; + string product = element.CatalogNs is not null && element.CatalogNs.Mappings.Length > 0 ? element.CatalogNs.Mappings.First().PageSlug : null; string icon = null; for (int i = 0; i < element.KeyImages?.Length; i++) { @@ -71,7 +71,7 @@ internal static class EpicStore foreach (Element element in catalogOffers) { string title = element.Title; - string product = element.CatalogNs is not null && element.CatalogNs.Mappings.Any() ? element.CatalogNs.Mappings.First().PageSlug : null; + string product = element.CatalogNs is not null && element.CatalogNs.Mappings.Length > 0 ? element.CatalogNs.Mappings.First().PageSlug : null; string icon = null; for (int i = 0; i < element.KeyImages?.Length; i++) { diff --git a/CreamInstaller/Platforms/Paradox/ParadoxLauncher.cs b/CreamInstaller/Platforms/Paradox/ParadoxLauncher.cs index 327cf16..a730be3 100644 --- a/CreamInstaller/Platforms/Paradox/ParadoxLauncher.cs +++ b/CreamInstaller/Platforms/Paradox/ParadoxLauncher.cs @@ -34,49 +34,39 @@ internal static class ParadoxLauncher } } - internal static async Task> GetExecutableDirectories(string gameDirectory) + internal static async Task> GetExecutableDirectories(string gameDirectory) => await Task.Run(async () => await gameDirectory.GetExecutableDirectories(validFunc: path => !Path.GetFileName(path).Contains("bootstrapper"))); - private static void PopulateDlc(ProgramSelection paradoxLauncher = null) + private static void PopulateDlc(Selection paradoxLauncher = null) { - paradoxLauncher ??= ProgramSelection.FromPlatformId(Platform.Paradox, "PL"); - if (paradoxLauncher is not null) - { - paradoxLauncher.ExtraDlc.Clear(); - paradoxLauncher.ExtraSelectedDlc.Clear(); - foreach (ProgramSelection selection in ProgramSelection.AllEnabled.Where(s => s != paradoxLauncher && s.Publisher == "Paradox Interactive")) - { - paradoxLauncher.ExtraDlc.Add(selection.Id, (selection.Name, selection.AllDlc)); - paradoxLauncher.ExtraSelectedDlc.Add(selection.Id, (selection.Name, selection.SelectedDlc)); - } - if (!paradoxLauncher.ExtraDlc.Any()) - foreach (ProgramSelection selection in ProgramSelection.AllSafe.Where(s => s != paradoxLauncher && s.Publisher == "Paradox Interactive")) - { - paradoxLauncher.ExtraDlc.Add(selection.Id, (selection.Name, selection.AllDlc)); - paradoxLauncher.ExtraSelectedDlc.Add(selection.Id, (selection.Name, selection.AllDlc)); - } - } + paradoxLauncher ??= Selection.FromPlatformId(Platform.Paradox, "PL"); + if (paradoxLauncher is null) + return; + paradoxLauncher.ExtraSelections.Clear(); + foreach (Selection selection in Selection.AllEnabled.Where(s => s != paradoxLauncher && s.Publisher == "Paradox Interactive")) + _ = paradoxLauncher.ExtraSelections.Add(selection); + if (paradoxLauncher.ExtraSelections.Count > 0) + return; + foreach (Selection selection in Selection.AllSafe.Where(s => s != paradoxLauncher && s.Publisher == "Paradox Interactive")) + _ = paradoxLauncher.ExtraSelections.Add(selection); } internal static bool DlcDialog(Form form) { - ProgramSelection paradoxLauncher = ProgramSelection.FromPlatformId(Platform.Paradox, "PL"); - if (paradoxLauncher is not null && paradoxLauncher.Enabled) - { - PopulateDlc(paradoxLauncher); - if (!paradoxLauncher.ExtraDlc.Any()) - { - using DialogForm dialogForm = new(form); - return dialogForm.Show(SystemIcons.Warning, - "WARNING: There are no scanned games with DLC that can be added to the Paradox Launcher!" - + "\n\nInstalling DLC unlockers for the Paradox Launcher alone can cause existing configurations to be deleted!", "Ignore", "Cancel", - "Paradox Launcher") != DialogResult.OK; - } - } - return false; + Selection paradoxLauncher = Selection.FromPlatformId(Platform.Paradox, "PL"); + if (paradoxLauncher is null || !paradoxLauncher.Enabled) + return false; + PopulateDlc(paradoxLauncher); + if (paradoxLauncher.ExtraSelections.Count > 0) + return false; + using DialogForm dialogForm = new(form); + return dialogForm.Show(SystemIcons.Warning, + "WARNING: There are no scanned games with DLC that can be added to the Paradox Launcher!" + + "\n\nInstalling DLC unlockers for the Paradox Launcher alone can cause existing configurations to be deleted!", "Ignore", "Cancel", + "Paradox Launcher") != DialogResult.OK; } - internal static async Task Repair(Form form, ProgramSelection selection) + internal static async Task Repair(Form form, Selection selection) { InstallForm installForm = form as InstallForm; if (!Program.AreDllsLockedDialog(form, selection)) diff --git a/CreamInstaller/Platforms/Steam/SteamLibrary.cs b/CreamInstaller/Platforms/Steam/SteamLibrary.cs index 028e9fa..e3fc37d 100644 --- a/CreamInstaller/Platforms/Steam/SteamLibrary.cs +++ b/CreamInstaller/Platforms/Steam/SteamLibrary.cs @@ -22,14 +22,14 @@ internal static class SteamLibrary } } - internal static async Task> GetExecutableDirectories(string gameDirectory) + internal static async Task> GetExecutableDirectories(string gameDirectory) => await Task.Run(async () => await gameDirectory.GetExecutableDirectories(true)); - internal static async Task> GetGames() + internal static async Task> GetGames() => await Task.Run(async () => { - List<(string appId, string name, string branch, int buildId, string gameDirectory)> games = new(); - List gameLibraryDirectories = await GetLibraryDirectories(); + HashSet<(string appId, string name, string branch, int buildId, string gameDirectory)> games = new(); + HashSet gameLibraryDirectories = await GetLibraryDirectories(); foreach (string libraryDirectory in gameLibraryDirectories) { if (Program.Canceled) @@ -37,7 +37,7 @@ internal static class SteamLibrary foreach ((string appId, string name, string branch, int buildId, string gameDirectory) game in (await GetGamesFromLibraryDirectory(libraryDirectory)).Where(game => !games.Any(_game => _game.appId == game.appId && _game.gameDirectory == game.gameDirectory))) - games.Add(game); + _ = games.Add(game); } return games; }); @@ -85,10 +85,10 @@ internal static class SteamLibrary return games; }); - private static async Task> GetLibraryDirectories() + private static async Task> GetLibraryDirectories() => await Task.Run(() => { - List gameDirectories = new(); + HashSet gameDirectories = new(); if (Program.Canceled) return gameDirectories; string steamInstallPath = InstallPath; @@ -97,7 +97,7 @@ internal static class SteamLibrary string libraryFolder = steamInstallPath + @"\steamapps"; if (!libraryFolder.DirectoryExists()) return gameDirectories; - gameDirectories.Add(libraryFolder); + _ = gameDirectories.Add(libraryFolder); string libraryFolders = libraryFolder + @"\libraryfolders.vdf"; if (!libraryFolders.FileExists() || !ValveDataFile.TryDeserialize(libraryFolders.ReadFile(), out VProperty result)) return gameDirectories; @@ -108,8 +108,8 @@ internal static class SteamLibrary if (string.IsNullOrWhiteSpace(path)) continue; path += @"\steamapps"; - if (path.DirectoryExists() && !gameDirectories.Contains(path)) - gameDirectories.Add(path); + if (path.DirectoryExists()) + _ = gameDirectories.Add(path); } return gameDirectories; }); diff --git a/CreamInstaller/Platforms/Ubisoft/UbisoftLibrary.cs b/CreamInstaller/Platforms/Ubisoft/UbisoftLibrary.cs index 752ec2e..0db5b06 100644 --- a/CreamInstaller/Platforms/Ubisoft/UbisoftLibrary.cs +++ b/CreamInstaller/Platforms/Ubisoft/UbisoftLibrary.cs @@ -12,7 +12,7 @@ internal static class UbisoftLibrary { private static RegistryKey installsKey; - internal static RegistryKey InstallsKey + private static RegistryKey InstallsKey { get { @@ -22,13 +22,13 @@ internal static class UbisoftLibrary } } - internal static async Task> GetExecutableDirectories(string gameDirectory) + internal static async Task> GetExecutableDirectories(string gameDirectory) => await Task.Run(async () => await gameDirectory.GetExecutableDirectories(true)); - internal static async Task> GetGames() + internal static async Task> GetGames() => await Task.Run(() => { - List<(string gameId, string name, string gameDirectory)> games = new(); + HashSet<(string gameId, string name, string gameDirectory)> games = new(); RegistryKey installsKey = InstallsKey; if (installsKey is null) return games; @@ -37,7 +37,7 @@ internal static class UbisoftLibrary RegistryKey installKey = installsKey.OpenSubKey(gameId); string installDir = installKey?.GetValue("InstallDir")?.ToString()?.BeautifyPath(); if (installDir is not null && !games.Any(g => g.gameId == gameId && g.gameDirectory == installDir)) - games.Add((gameId, new DirectoryInfo(installDir).Name, installDir)); + _ = games.Add((gameId, new DirectoryInfo(installDir).Name, installDir)); } return games; }); diff --git a/CreamInstaller/Program.cs b/CreamInstaller/Program.cs index 4f96ec0..23fc08d 100644 --- a/CreamInstaller/Program.cs +++ b/CreamInstaller/Program.cs @@ -48,7 +48,7 @@ internal static class Program return ProtectedGameDirectories.Any(path => (directory + path).DirectoryExists()); } - internal static bool AreDllsLockedDialog(Form form, ProgramSelection selection) + internal static bool AreDllsLockedDialog(Form form, Selection selection) { while (true) { diff --git a/CreamInstaller/Resources/Koaloader.cs b/CreamInstaller/Resources/Koaloader.cs index da1311c..8375a1c 100644 --- a/CreamInstaller/Resources/Koaloader.cs +++ b/CreamInstaller/Resources/Koaloader.cs @@ -75,7 +75,7 @@ internal static class Koaloader } SortedList targets = new(PlatformIdComparer.String); SortedList modules = new(PlatformIdComparer.String); - if (targets.Any() || modules.Any()) + if (targets.Count > 0 || modules.Count > 0) { /*if (installForm is not null) installForm.UpdateUser("Generating Koaloader configuration for " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);*/ @@ -92,13 +92,13 @@ internal static class Koaloader } } - private static void WriteConfig(StreamWriter writer, SortedList targets, SortedList modules, InstallForm installForm = null) + private static void WriteConfig(TextWriter writer, SortedList targets, SortedList modules, InstallForm installForm = null) { writer.WriteLine("{"); writer.WriteLine(" \"logging\": false,"); writer.WriteLine(" \"enabled\": true,"); - writer.WriteLine(" \"auto_load\": " + (modules.Any() ? "false" : "true") + ","); - if (targets.Any()) + writer.WriteLine(" \"auto_load\": " + (modules.Count > 0 ? "false" : "true") + ","); + if (targets.Count > 0) { writer.WriteLine(" \"targets\": ["); KeyValuePair lastTarget = targets.Last(); @@ -112,7 +112,7 @@ internal static class Koaloader } else writer.WriteLine(" \"targets\": []"); - if (modules.Any()) + if (modules.Count > 0) { writer.WriteLine(" \"modules\": ["); KeyValuePair lastModule = modules.Last(); @@ -166,11 +166,11 @@ internal static class Koaloader await Uninstall(rootDirectory, null, installForm, deleteConfig); }); - internal static async Task Install(string directory, BinaryType binaryType, ProgramSelection selection, string rootDirectory = null, + internal static async Task Install(string directory, BinaryType binaryType, Selection selection, string rootDirectory = null, InstallForm installForm = null, bool generateConfig = true) => await Task.Run(() => { - string proxy = selection.KoaloaderProxy ?? ProgramSelection.DefaultKoaloaderProxy; + string proxy = selection.KoaloaderProxy ?? Selection.DefaultKoaloaderProxy; string path = directory + @"\" + proxy + ".dll"; foreach (string _path in directory.GetKoaloaderProxies().Where(p => p != path && p.FileExists() && p.IsResourceFile(ResourceIdentifier.Koaloader))) { diff --git a/CreamInstaller/Resources/Resources.cs b/CreamInstaller/Resources/Resources.cs index 769f961..cbaad1e 100644 --- a/CreamInstaller/Resources/Resources.cs +++ b/CreamInstaller/Resources/Resources.cs @@ -483,21 +483,21 @@ internal static class Resources internal static bool TryGetFileBinaryType(this string path, out BinaryType binaryType) => NativeImports.GetBinaryType(path, out binaryType); - internal static async Task> GetExecutableDirectories(this string rootDirectory, bool filterCommon = false, - Func validFunc = null) + internal static async Task> GetExecutableDirectories(this string rootDirectory, + bool filterCommon = false, Func validFunc = null) => await Task.Run(async () => (await rootDirectory.GetExecutables(filterCommon, validFunc) ?? (filterCommon || validFunc is not null ? await rootDirectory.GetExecutables() : null))?.Select(e => { e.path = Path.GetDirectoryName(e.path); return e; - }).DistinctBy(e => e.path).ToList()); + }).DistinctBy(e => e.path).ToHashSet()); - internal static async Task> GetExecutables(this string rootDirectory, bool filterCommon = false, + internal static async Task> GetExecutables(this string rootDirectory, bool filterCommon = false, Func validFunc = null) => await Task.Run(() => { - List<(string path, BinaryType binaryType)> executables = new(); + HashSet<(string path, BinaryType binaryType)> executables = new(); if (Program.Canceled || !rootDirectory.DirectoryExists()) return null; foreach (string path in rootDirectory.EnumerateDirectory("*.exe", true)) @@ -507,7 +507,7 @@ internal static class Resources if (executables.All(e => e.path != path) && (!filterCommon || !rootDirectory.IsCommonIncorrectExecutable(path)) && (validFunc is null || validFunc(path)) && path.TryGetFileBinaryType(out BinaryType binaryType) && binaryType is BinaryType.BIT64) - executables.Add((path, binaryType)); + _ = executables.Add((path, binaryType)); Thread.Sleep(1); } foreach (string path in rootDirectory.EnumerateDirectory("*.exe", true)) @@ -517,10 +517,10 @@ internal static class Resources if (executables.All(e => e.path != path) && (!filterCommon || !rootDirectory.IsCommonIncorrectExecutable(path)) && (validFunc is null || validFunc(path)) && path.TryGetFileBinaryType(out BinaryType binaryType) && binaryType is BinaryType.BIT32) - executables.Add((path, binaryType)); + _ = executables.Add((path, binaryType)); Thread.Sleep(1); } - return !executables.Any() ? null : executables; + return executables.Count > 0 ? executables : null; }); private static bool IsCommonIncorrectExecutable(this string rootDirectory, string path) @@ -533,10 +533,10 @@ internal static class Resources || subPath.Contains("ANTICHEAT"); } - internal static async Task> GetDllDirectoriesFromGameDirectory(this string gameDirectory, Platform platform) + internal static async Task> GetDllDirectoriesFromGameDirectory(this string gameDirectory, Platform platform) => await Task.Run(() => { - List dllDirectories = new(); + HashSet dllDirectories = new(); if (Program.Canceled || !gameDirectory.DirectoryExists()) return null; foreach (string directory in gameDirectory.EnumerateSubdirectories("*", true).Append(gameDirectory)) @@ -555,7 +555,7 @@ internal static class Resources if (api.FileExists() || api_o.FileExists() || api64.FileExists() || api64_o.FileExists() || (old_config.FileExists() || config.FileExists() || old_log.FileExists() || log.FileExists() || cache.FileExists()) && !koaloaderInstalled) - dllDirectories.Add(subDirectory); + _ = dllDirectories.Add(subDirectory); } if (platform is Platform.Epic or Platform.Paradox) { @@ -563,7 +563,7 @@ internal static class Resources out string log); if (api32.FileExists() || api32_o.FileExists() || api64.FileExists() || api64_o.FileExists() || (config.FileExists() || log.FileExists()) && !koaloaderInstalled) - dllDirectories.Add(subDirectory); + _ = dllDirectories.Add(subDirectory); } if (platform is Platform.Ubisoft) { @@ -571,15 +571,15 @@ internal static class Resources out string log); if (api32.FileExists() || api32_o.FileExists() || api64.FileExists() || api64_o.FileExists() || (config.FileExists() || log.FileExists()) && !koaloaderInstalled) - dllDirectories.Add(subDirectory); + _ = dllDirectories.Add(subDirectory); subDirectory.GetUplayR2Components(out string old_api32, out string old_api64, out api32, out api32_o, out api64, out api64_o, out config, out log); if (old_api32.FileExists() || old_api64.FileExists() || api32.FileExists() || api32_o.FileExists() || api64.FileExists() || api64_o.FileExists() || (config.FileExists() || log.FileExists()) && !koaloaderInstalled) - dllDirectories.Add(subDirectory); + _ = dllDirectories.Add(subDirectory); } } - return !dllDirectories.Any() ? null : dllDirectories; + return dllDirectories.Count > 0 ? dllDirectories : null; }); internal static void GetCreamApiComponents(this string directory, out string api32, out string api32_o, out string api64, out string api64_o, diff --git a/CreamInstaller/Resources/ScreamAPI.cs b/CreamInstaller/Resources/ScreamAPI.cs index 2daaf93..3950029 100644 --- a/CreamInstaller/Resources/ScreamAPI.cs +++ b/CreamInstaller/Resources/ScreamAPI.cs @@ -22,27 +22,24 @@ internal static class ScreamAPI log = directory + @"\ScreamAPI.log"; } - internal static void CheckConfig(string directory, ProgramSelection selection, InstallForm installForm = null) + internal static void CheckConfig(string directory, Selection selection, InstallForm installForm = null) { directory.GetScreamApiComponents(out _, out _, out _, out _, out string config, out _); - IEnumerable> overrideCatalogItems - = selection.AllDlc.Where(pair => pair.Value.type is DlcType.EpicCatalogItem).Except(selection.SelectedDlc); - foreach (KeyValuePair extraDlc)> pair in selection.ExtraSelectedDlc) - overrideCatalogItems = overrideCatalogItems.Except(pair.Value.extraDlc); - IEnumerable> entitlements - = selection.SelectedDlc.Where(pair => pair.Value.type == DlcType.EpicEntitlement); - foreach (KeyValuePair dlc)> pair in selection.ExtraSelectedDlc) - entitlements = entitlements.Concat(pair.Value.dlc.Where(pair => pair.Value.type == DlcType.EpicEntitlement)); - overrideCatalogItems = overrideCatalogItems.ToList(); - entitlements = entitlements.ToList(); - if (overrideCatalogItems.Any() || entitlements.Any()) + List overrideCatalogItems = selection.DLC.Where(dlc => dlc.Type is DLCType.EpicCatalogItem && !dlc.Enabled).ToList(); + List overrideEntitlements = selection.DLC.Where(dlc => dlc.Type is DLCType.EpicEntitlement && !dlc.Enabled).ToList(); + foreach (Selection extraSelection in selection.ExtraSelections) + { + overrideCatalogItems.AddRange(extraSelection.DLC.Where(dlc => dlc.Type is DLCType.EpicCatalogItem && !dlc.Enabled)); + overrideEntitlements.AddRange(extraSelection.DLC.Where(dlc => dlc.Type is DLCType.EpicEntitlement && !dlc.Enabled)); + } + if (overrideCatalogItems.Count > 0 || overrideEntitlements.Count > 0) { /*if (installForm is not null) installForm.UpdateUser("Generating ScreamAPI configuration for " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);*/ config.CreateFile(true, installForm).Close(); StreamWriter writer = new(config, true, Encoding.UTF8); - WriteConfig(writer, new(overrideCatalogItems.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String), - new(entitlements.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String), installForm); + WriteConfig(writer, new(overrideCatalogItems.ToDictionary(dlc => dlc.Id, dlc => dlc), PlatformIdComparer.String), + new(overrideEntitlements.ToDictionary(dlc => dlc.Id, dlc => dlc), PlatformIdComparer.String), installForm); writer.Flush(); writer.Close(); } @@ -53,8 +50,8 @@ internal static class ScreamAPI } } - private static void WriteConfig(StreamWriter writer, SortedList overrideCatalogItems, - SortedList entitlements, InstallForm installForm = null) + private static void WriteConfig(TextWriter writer, SortedList overrideCatalogItems, SortedList entitlements, + InstallForm installForm = null) { writer.WriteLine("{"); writer.WriteLine(" \"version\": 2,"); @@ -63,16 +60,16 @@ internal static class ScreamAPI writer.WriteLine(" \"block_metrics\": false,"); writer.WriteLine(" \"catalog_items\": {"); writer.WriteLine(" \"unlock_all\": true,"); - if (overrideCatalogItems.Any()) + if (overrideCatalogItems.Count > 0) { writer.WriteLine(" \"override\": ["); - KeyValuePair lastOverrideCatalogItem = overrideCatalogItems.Last(); - foreach (KeyValuePair pair in overrideCatalogItems) + KeyValuePair lastOverrideCatalogItem = overrideCatalogItems.Last(); + foreach (KeyValuePair pair in overrideCatalogItems) { - string id = pair.Key; - (_, string name, _) = pair.Value; - writer.WriteLine($" \"{id}\"{(pair.Equals(lastOverrideCatalogItem) ? "" : ",")}"); - installForm?.UpdateUser($"Added override catalog item to ScreamAPI.json with id {id} ({name})", LogTextBox.Action, false); + SelectionDLC selectionDlc = pair.Value; + writer.WriteLine($" \"{selectionDlc.Id}\"{(pair.Equals(lastOverrideCatalogItem) ? "" : ",")}"); + installForm?.UpdateUser($"Added locked catalog item to ScreamAPI.json with id {selectionDlc.Id} ({selectionDlc.Name})", LogTextBox.Action, + false); } writer.WriteLine(" ]"); } @@ -82,16 +79,16 @@ internal static class ScreamAPI writer.WriteLine(" \"entitlements\": {"); writer.WriteLine(" \"unlock_all\": true,"); writer.WriteLine(" \"auto_inject\": true,"); - if (entitlements.Any()) + if (entitlements.Count > 0) { writer.WriteLine(" \"inject\": ["); - KeyValuePair lastEntitlement = entitlements.Last(); - foreach (KeyValuePair pair in entitlements) + KeyValuePair lastEntitlement = entitlements.Last(); + foreach (KeyValuePair pair in entitlements) { - string id = pair.Key; - (_, string name, _) = pair.Value; - writer.WriteLine($" \"{id}\"{(pair.Equals(lastEntitlement) ? "" : ",")}"); - installForm?.UpdateUser($"Added entitlement to ScreamAPI.json with id {id} ({name})", LogTextBox.Action, false); + SelectionDLC selectionDlc = pair.Value; + writer.WriteLine($" \"{selectionDlc.Id}\"{(pair.Equals(lastEntitlement) ? "" : ",")}"); + installForm?.UpdateUser($"Added locked entitlement to ScreamAPI.json with id {selectionDlc.Id} ({selectionDlc.Name})", LogTextBox.Action, + false); } writer.WriteLine(" ]"); } @@ -139,7 +136,7 @@ internal static class ScreamAPI } }); - internal static async Task Install(string directory, ProgramSelection selection, InstallForm installForm = null, bool generateConfig = true) + internal static async Task Install(string directory, Selection selection, InstallForm installForm = null, bool generateConfig = true) => await Task.Run(() => { directory.GetScreamApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out _, out _); diff --git a/CreamInstaller/Resources/SmokeAPI.cs b/CreamInstaller/Resources/SmokeAPI.cs index 830ffbe..538b7cb 100644 --- a/CreamInstaller/Resources/SmokeAPI.cs +++ b/CreamInstaller/Resources/SmokeAPI.cs @@ -25,46 +25,38 @@ internal static class SmokeAPI cache = directory + @"\SmokeAPI.cache.json"; } - internal static void CheckConfig(string directory, ProgramSelection selection, InstallForm installForm = null) + internal static void CheckConfig(string directory, Selection selection, InstallForm installForm = null) { directory.GetSmokeApiComponents(out _, out _, out _, out _, out string old_config, out string config, out _, out _, out _); - List> overrideDlc = selection.AllDlc.Except(selection.SelectedDlc).ToList(); - foreach (KeyValuePair dlc)> pair in selection.ExtraDlc) - if (selection.ExtraSelectedDlc.TryGetValue(pair.Key, - out (string name, SortedList dlc) selectedPair)) - overrideDlc.AddRange(pair.Value.dlc.Except(selectedPair.dlc)); - List> injectDlc = new(); - if (selection.AllDlc.Count > 64) - injectDlc.AddRange(selection.SelectedDlc.Where(pair => pair.Value.type is DlcType.SteamHidden)); - List injectDlc)>> extraApps = new(); - if (selection.ExtraDlc.Any(e => e.Value.dlc.Count > 64)) - foreach (KeyValuePair injectDlc)> pair in selection - .ExtraSelectedDlc) - if (selection.ExtraDlc.First(e => e.Key == pair.Key).Value.dlc.Count > 64) - { - SortedList extraInjectDlc = new(PlatformIdComparer.String); - foreach (KeyValuePair extraPair in pair.Value.injectDlc.Where(extraPair - => extraPair.Value.type is DlcType.SteamHidden)) - extraInjectDlc.Add(extraPair.Key, extraPair.Value); - KeyValuePair injectDlc)> newExtraPair = new(pair.Key, - (pair.Value.name, extraInjectDlc)); - extraApps.Add(newExtraPair); - } - injectDlc = injectDlc.ToList(); + List overrideDlc = selection.DLC.Where(dlc => !dlc.Enabled).ToList(); + foreach (Selection extraSelection in selection.ExtraSelections) + overrideDlc.AddRange(extraSelection.DLC.Where(dlc => !dlc.Enabled)); + List injectDlc = new(); + if (selection.DLC.Count() > 64) + injectDlc.AddRange(selection.DLC.Where(dlc => dlc.Enabled && dlc.Type is DLCType.SteamHidden)); + List injectDlc)>> extraApps = new(); + foreach (Selection extraSelection in selection.ExtraSelections.Where(extraSelection => extraSelection.DLC.Count() > 64)) + { + SortedList extraInjectDlc = new(PlatformIdComparer.String); + foreach (SelectionDLC extraDlc in extraSelection.DLC.Where(extraDlc => extraDlc.Enabled && extraDlc.Type is DLCType.SteamHidden)) + extraInjectDlc.Add(extraDlc.Id, extraDlc); + if (extraInjectDlc.Count > 0) + extraApps.Add(new(extraSelection.Id, (extraSelection.Name, extraInjectDlc))); + } if (old_config.FileExists()) { old_config.DeleteFile(); installForm?.UpdateUser($"Deleted old configuration: {Path.GetFileName(old_config)}", LogTextBox.Action, false); } - if (selection.ExtraSelectedDlc.Any(p => p.Value.dlc.Any()) || overrideDlc.Any() || injectDlc.Any()) + if (selection.ExtraSelections.Any(extraSelection => extraSelection.DLC.Any()) || overrideDlc.Count > 0 || injectDlc.Count > 0) { /*if (installForm is not null) installForm.UpdateUser("Generating SmokeAPI configuration for " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);*/ config.CreateFile(true, installForm).Close(); StreamWriter writer = new(config, true, Encoding.UTF8); - WriteConfig(writer, selection.Id, new(extraApps.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String), - new(overrideDlc.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String), - new(injectDlc.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String), installForm); + WriteConfig(writer, selection.Id, new(extraApps.ToDictionary(extraApp => extraApp.Key, extraApp => extraApp.Value), PlatformIdComparer.String), + new(overrideDlc.ToDictionary(dlc => dlc.Id, dlc => dlc), PlatformIdComparer.String), + new(injectDlc.ToDictionary(dlc => dlc.Id, dlc => dlc), PlatformIdComparer.String), installForm); writer.Flush(); writer.Close(); } @@ -75,10 +67,8 @@ internal static class SmokeAPI } } - private static void WriteConfig(StreamWriter writer, string appId, - SortedList injectDlc)> extraApps, - SortedList overrideDlc, SortedList injectDlc, - InstallForm installForm = null) + private static void WriteConfig(TextWriter writer, string appId, SortedList injectDlc)> extraApps, + SortedList overrideDlc, SortedList injectDlc, InstallForm installForm = null) { writer.WriteLine("{"); writer.WriteLine(" \"$version\": 2,"); @@ -89,13 +79,13 @@ internal static class SmokeAPI if (overrideDlc.Count > 0) { writer.WriteLine(" \"override_dlc_status\": {"); - KeyValuePair lastOverrideDlc = overrideDlc.Last(); - foreach (KeyValuePair pair in overrideDlc) + KeyValuePair lastOverrideDlc = overrideDlc.Last(); + foreach (KeyValuePair pair in overrideDlc) { - string dlcId = pair.Key; - (_, string dlcName, _) = pair.Value; - writer.WriteLine($" \"{dlcId}\": \"locked\"{(pair.Equals(lastOverrideDlc) ? "" : ",")}"); - installForm?.UpdateUser($"Added locked DLC to SmokeAPI.config.json with appid {dlcId} ({dlcName})", LogTextBox.Action, false); + SelectionDLC selectionDlc = pair.Value; + writer.WriteLine($" \"{selectionDlc.Id}\": \"locked\"{(pair.Equals(lastOverrideDlc) ? "" : ",")}"); + installForm?.UpdateUser($"Added locked DLC to SmokeAPI.config.json with appid {selectionDlc.Id} ({selectionDlc.Name})", LogTextBox.Action, + false); } writer.WriteLine(" },"); } @@ -110,34 +100,34 @@ internal static class SmokeAPI { writer.WriteLine(" \"" + appId + "\": {"); writer.WriteLine(" \"dlcs\": {"); - KeyValuePair lastInjectDlc = injectDlc.Last(); - foreach (KeyValuePair pair in injectDlc) + KeyValuePair lastInjectDlc = injectDlc.Last(); + foreach (KeyValuePair pair in injectDlc) { - string dlcId = pair.Key; - (_, string dlcName, _) = pair.Value; - writer.WriteLine($" \"{dlcId}\": \"{dlcName}\"{(pair.Equals(lastInjectDlc) ? "" : ",")}"); - installForm?.UpdateUser($"Added extra DLC to SmokeAPI.config.json with appid {dlcId} ({dlcName})", LogTextBox.Action, false); + SelectionDLC selectionDlc = pair.Value; + writer.WriteLine($" \"{selectionDlc.Id}\": \"{selectionDlc.Name}\"{(pair.Equals(lastInjectDlc) ? "" : ",")}"); + installForm?.UpdateUser($"Added extra DLC to SmokeAPI.config.json with appid {selectionDlc.Id} ({selectionDlc.Name})", LogTextBox.Action, + false); } writer.WriteLine(" }"); writer.WriteLine(extraApps.Count > 0 ? " }," : " }"); } if (extraApps.Count > 0) { - KeyValuePair injectDlc)> lastExtraApp = extraApps.Last(); - foreach (KeyValuePair injectDlc)> pair in extraApps) + KeyValuePair injectDlc)> lastExtraApp = extraApps.Last(); + foreach (KeyValuePair injectDlc)> pair in extraApps) { string extraAppId = pair.Key; - (string _ /*extraAppName*/, SortedList extraInjectDlc) = pair.Value; + (string _ /*extraAppName*/, SortedList extraInjectDlc) = pair.Value; writer.WriteLine(" \"" + extraAppId + "\": {"); writer.WriteLine(" \"dlcs\": {"); //installForm?.UpdateUser($"Added extra app to SmokeAPI.config.json with appid {extraAppId} ({extraAppName})", LogTextBox.Action, false); - KeyValuePair lastExtraAppDlc = extraInjectDlc.Last(); - foreach (KeyValuePair extraPair in extraInjectDlc) + KeyValuePair lastExtraAppDlc = extraInjectDlc.Last(); + foreach (KeyValuePair extraPair in extraInjectDlc) { - string dlcId = extraPair.Key; - (_, string dlcName, _) = extraPair.Value; - writer.WriteLine($" \"{dlcId}\": \"{dlcName}\"{(extraPair.Equals(lastExtraAppDlc) ? "" : ",")}"); - installForm?.UpdateUser($"Added extra DLC to SmokeAPI.config.json with appid {dlcId} ({dlcName})", LogTextBox.Action, false); + SelectionDLC selectionDlc = extraPair.Value; + writer.WriteLine($" \"{selectionDlc.Id}\": \"{selectionDlc.Name}\"{(extraPair.Equals(lastExtraAppDlc) ? "" : ",")}"); + installForm?.UpdateUser($"Added extra DLC to SmokeAPI.config.json with appid {selectionDlc.Id} ({selectionDlc.Name})", + LogTextBox.Action, false); } writer.WriteLine(" }"); writer.WriteLine(pair.Equals(lastExtraApp) ? " }" : " },"); @@ -211,7 +201,7 @@ internal static class SmokeAPI } }); - internal static async Task Install(string directory, ProgramSelection selection, InstallForm installForm = null, bool generateConfig = true) + internal static async Task Install(string directory, Selection selection, InstallForm installForm = null, bool generateConfig = true) => await Task.Run(() => { directory.GetCreamApiComponents(out _, out _, out _, out _, out string oldConfig); diff --git a/CreamInstaller/Resources/UplayR1.cs b/CreamInstaller/Resources/UplayR1.cs index e79cf08..73f00cb 100644 --- a/CreamInstaller/Resources/UplayR1.cs +++ b/CreamInstaller/Resources/UplayR1.cs @@ -22,20 +22,19 @@ internal static class UplayR1 log = directory + @"\UplayR1Unlocker.log"; } - internal static void CheckConfig(string directory, ProgramSelection selection, InstallForm installForm = null) + internal static void CheckConfig(string directory, Selection selection, InstallForm installForm = null) { directory.GetUplayR1Components(out _, out _, out _, out _, out string config, out _); - IEnumerable> blacklistDlc = selection.AllDlc.Except(selection.SelectedDlc); - foreach (KeyValuePair extraDlc)> pair in selection.ExtraSelectedDlc) - blacklistDlc = blacklistDlc.Except(pair.Value.extraDlc); - blacklistDlc = blacklistDlc.ToList(); - if (blacklistDlc.Any()) + List blacklistDlc = selection.DLC.Where(dlc => !dlc.Enabled).ToList(); + foreach (Selection extraSelection in selection.ExtraSelections) + blacklistDlc.AddRange(extraSelection.DLC.Where(dlc => !dlc.Enabled)); + if (blacklistDlc.Count > 0) { /*if (installForm is not null) installForm.UpdateUser("Generating Uplay R1 Unlocker configuration for " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);*/ config.CreateFile(true, installForm).Close(); StreamWriter writer = new(config, true, Encoding.UTF8); - WriteConfig(writer, new(blacklistDlc.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String), installForm); + WriteConfig(writer, new(blacklistDlc.ToDictionary(dlc => dlc.Id, dlc => dlc), PlatformIdComparer.String), installForm); writer.Flush(); writer.Close(); } @@ -46,8 +45,7 @@ internal static class UplayR1 } } - private static void WriteConfig(StreamWriter writer, SortedList blacklistDlc, - InstallForm installForm = null) + private static void WriteConfig(TextWriter writer, SortedList blacklistDlc, InstallForm installForm = null) { writer.WriteLine("{"); writer.WriteLine(" \"logging\": false,"); @@ -56,13 +54,13 @@ internal static class UplayR1 if (blacklistDlc.Count > 0) { writer.WriteLine(" \"blacklist\": ["); - KeyValuePair lastBlacklistDlc = blacklistDlc.Last(); - foreach (KeyValuePair pair in blacklistDlc) + KeyValuePair lastBlacklistDlc = blacklistDlc.Last(); + foreach (KeyValuePair pair in blacklistDlc) { - string dlcId = pair.Key; - (_, string dlcName, _) = pair.Value; - writer.WriteLine($" {dlcId}{(pair.Equals(lastBlacklistDlc) ? "" : ",")}"); - installForm?.UpdateUser($"Added blacklist DLC to UplayR1Unlocker.jsonc with appid {dlcId} ({dlcName})", LogTextBox.Action, false); + SelectionDLC selectionDlc = pair.Value; + writer.WriteLine($" {selectionDlc.Id}{(pair.Equals(lastBlacklistDlc) ? "" : ",")}"); + installForm?.UpdateUser($"Added blacklist DLC to UplayR1Unlocker.jsonc with appid {selectionDlc.Id} ({selectionDlc.Name})", LogTextBox.Action, + false); } writer.WriteLine(" ],"); } @@ -109,7 +107,7 @@ internal static class UplayR1 } }); - internal static async Task Install(string directory, ProgramSelection selection, InstallForm installForm = null, bool generateConfig = true) + internal static async Task Install(string directory, Selection selection, InstallForm installForm = null, bool generateConfig = true) => await Task.Run(() => { directory.GetUplayR1Components(out string api32, out string api32_o, out string api64, out string api64_o, out _, out _); diff --git a/CreamInstaller/Resources/UplayR2.cs b/CreamInstaller/Resources/UplayR2.cs index 688da87..41adcb6 100644 --- a/CreamInstaller/Resources/UplayR2.cs +++ b/CreamInstaller/Resources/UplayR2.cs @@ -24,20 +24,19 @@ internal static class UplayR2 log = directory + @"\UplayR2Unlocker.log"; } - internal static void CheckConfig(string directory, ProgramSelection selection, InstallForm installForm = null) + internal static void CheckConfig(string directory, Selection selection, InstallForm installForm = null) { directory.GetUplayR2Components(out _, out _, out _, out _, out _, out _, out string config, out _); - IEnumerable> blacklistDlc = selection.AllDlc.Except(selection.SelectedDlc); - foreach (KeyValuePair extraDlc)> pair in selection.ExtraSelectedDlc) - blacklistDlc = blacklistDlc.Except(pair.Value.extraDlc); - blacklistDlc = blacklistDlc.ToList(); - if (blacklistDlc.Any()) + List blacklistDlc = selection.DLC.Where(dlc => !dlc.Enabled).ToList(); + foreach (Selection extraSelection in selection.ExtraSelections) + blacklistDlc.AddRange(extraSelection.DLC.Where(dlc => !dlc.Enabled)); + if (blacklistDlc.Count > 0) { /*if (installForm is not null) installForm.UpdateUser("Generating Uplay R2 Unlocker configuration for " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);*/ config.CreateFile(true, installForm).Close(); StreamWriter writer = new(config, true, Encoding.UTF8); - WriteConfig(writer, new(blacklistDlc.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.String), installForm); + WriteConfig(writer, new(blacklistDlc.ToDictionary(dlc => dlc.Id, dlc => dlc), PlatformIdComparer.String), installForm); writer.Flush(); writer.Close(); } @@ -48,8 +47,7 @@ internal static class UplayR2 } } - private static void WriteConfig(StreamWriter writer, SortedList blacklistDlc, - InstallForm installForm = null) + private static void WriteConfig(TextWriter writer, SortedList blacklistDlc, InstallForm installForm = null) { writer.WriteLine("{"); writer.WriteLine(" \"logging\": false,"); @@ -60,13 +58,13 @@ internal static class UplayR2 if (blacklistDlc.Count > 0) { writer.WriteLine(" \"blacklist\": ["); - KeyValuePair lastBlacklistDlc = blacklistDlc.Last(); - foreach (KeyValuePair pair in blacklistDlc) + KeyValuePair lastBlacklistDlc = blacklistDlc.Last(); + foreach (KeyValuePair pair in blacklistDlc) { - string dlcId = pair.Key; - (_, string dlcName, _) = pair.Value; - writer.WriteLine($" {dlcId}{(pair.Equals(lastBlacklistDlc) ? "" : ",")}"); - installForm?.UpdateUser($"Added blacklist DLC to UplayR2Unlocker.jsonc with appid {dlcId} ({dlcName})", LogTextBox.Action, false); + SelectionDLC selectionDlc = pair.Value; + writer.WriteLine($" {selectionDlc.Id}{(pair.Equals(lastBlacklistDlc) ? "" : ",")}"); + installForm?.UpdateUser($"Added blacklist DLC to UplayR2Unlocker.jsonc with appid {selectionDlc.Id} ({selectionDlc.Name})", LogTextBox.Action, + false); } writer.WriteLine(" ],"); } @@ -109,14 +107,13 @@ internal static class UplayR2 config.DeleteFile(); installForm?.UpdateUser($"Deleted configuration: {Path.GetFileName(config)}", LogTextBox.Action, false); } - if (log.FileExists()) - { - log.DeleteFile(); - installForm?.UpdateUser($"Deleted log: {Path.GetFileName(log)}", LogTextBox.Action, false); - } + if (!log.FileExists()) + return; + log.DeleteFile(); + installForm?.UpdateUser($"Deleted log: {Path.GetFileName(log)}", LogTextBox.Action, false); }); - internal static async Task Install(string directory, ProgramSelection selection, InstallForm installForm = null, bool generateConfig = true) + internal static async Task Install(string directory, Selection selection, InstallForm installForm = null, bool generateConfig = true) => await Task.Run(() => { directory.GetUplayR2Components(out string old_api32, out string old_api64, out string api32, out string api32_o, out string api64, diff --git a/CreamInstaller/ProgramSelection.cs b/CreamInstaller/Selection.cs similarity index 56% rename from CreamInstaller/ProgramSelection.cs rename to CreamInstaller/Selection.cs index 4e0b0bc..f7b8c13 100644 --- a/CreamInstaller/ProgramSelection.cs +++ b/CreamInstaller/Selection.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Linq; -using CreamInstaller.Components; using CreamInstaller.Resources; using CreamInstaller.Utility; using static CreamInstaller.Resources.Resources; @@ -13,47 +12,31 @@ public enum Platform Epic, Ubisoft } -public enum DlcType -{ - Steam, SteamHidden, EpicCatalogItem, - EpicEntitlement -} - -internal sealed class ProgramSelection +internal sealed class Selection { internal const string DefaultKoaloaderProxy = "version"; - internal static readonly List All = new(); + internal static readonly HashSet All = new(); + internal readonly HashSet ExtraSelections = new(); - internal readonly SortedList AllDlc = new(PlatformIdComparer.String); - - internal readonly SortedList dlc)> ExtraDlc = new(); - - internal readonly SortedList dlc)> ExtraSelectedDlc = new(); - - internal readonly SortedList SelectedDlc = new(PlatformIdComparer.String); - - internal List DllDirectories; + internal HashSet DllDirectories; internal bool Enabled; - internal List<(string directory, BinaryType binaryType)> ExecutableDirectories; - internal string IconUrl; + internal HashSet<(string directory, BinaryType binaryType)> ExecutableDirectories; + internal string Icon; internal string Id = "0"; internal bool Koaloader; internal string KoaloaderProxy; internal string Name = "Program"; - internal Platform Platform; - - internal string ProductUrl; - + internal string Product; internal string Publisher; - internal string RootDirectory; - internal string SubIconUrl; + internal string SubIcon; + internal string Website; - internal string WebsiteUrl; + internal Selection() => All.Add(this); - internal ProgramSelection() => All.Add(this); + internal IEnumerable DLC => SelectionDLC.AllSafe.Where(dlc => dlc.Selection == this); internal bool AreDllsLocked { @@ -96,69 +79,41 @@ internal sealed class ProgramSelection } } - internal static List AllSafe => All.ToList(); + internal static List AllSafe => All.ToList(); - internal static List AllEnabled => AllSafe.FindAll(s => s.Enabled); + internal static List AllEnabled => AllSafe.FindAll(s => s.Enabled); - private void Toggle(string dlcAppId, (DlcType type, string name, string icon) dlcApp, bool enabled) + private void Remove() { - if (enabled) - SelectedDlc[dlcAppId] = dlcApp; - else - _ = SelectedDlc.Remove(dlcAppId); - } - - internal void ToggleDlc(string dlcId, bool enabled) - { - foreach ((string appId, (DlcType type, string name, string icon) dlcApp) in AllDlc) - { - if (appId != dlcId) - continue; - Toggle(appId, dlcApp, enabled); - break; - } - Enabled = SelectedDlc.Any() || ExtraSelectedDlc.Any(); - } - - private void Validate() - { - if (Program.IsGameBlocked(Name, RootDirectory)) - { - _ = All.Remove(this); - return; - } - if (!RootDirectory.DirectoryExists()) - { - _ = All.Remove(this); - return; - } - _ = DllDirectories.RemoveAll(directory => !directory.DirectoryExists()); - if (!DllDirectories.Any()) - _ = All.Remove(this); + _ = All.Remove(this); + foreach (SelectionDLC dlc in DLC) + dlc.Selection = null; } private void Validate(List<(Platform platform, string id, string name)> programsToScan) { if (programsToScan is null || !programsToScan.Any(p => p.platform == Platform && p.id == Id)) { - _ = All.Remove(this); + Remove(); return; } - Validate(); + if (Program.IsGameBlocked(Name, RootDirectory)) + { + Remove(); + return; + } + if (!RootDirectory.DirectoryExists()) + { + Remove(); + return; + } + _ = DllDirectories.RemoveWhere(directory => !directory.DirectoryExists()); + if (DllDirectories.Count < 1) + Remove(); } - internal static void ValidateAll() => AllSafe.ForEach(selection => selection.Validate()); - internal static void ValidateAll(List<(Platform platform, string id, string name)> programsToScan) => AllSafe.ForEach(selection => selection.Validate(programsToScan)); - internal static ProgramSelection FromPlatformId(Platform platform, string gameId) => AllSafe.Find(s => s.Platform == platform && s.Id == gameId); - - internal static (string gameId, (DlcType type, string name, string icon) app)? GetDlcFromPlatformId(Platform platform, string dlcId) - { - foreach (ProgramSelection selection in AllSafe.Where(s => s.Platform == platform)) - foreach (KeyValuePair pair in selection.AllDlc.Where(p => p.Key == dlcId)) - return (selection.Id, pair.Value); - return null; - } + internal static Selection FromPlatformId(Platform platform, string gameId) => AllSafe.Find(s => s.Platform == platform && s.Id == gameId); } \ No newline at end of file diff --git a/CreamInstaller/SelectionDLC.cs b/CreamInstaller/SelectionDLC.cs new file mode 100644 index 0000000..4727f42 --- /dev/null +++ b/CreamInstaller/SelectionDLC.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using System.Linq; + +namespace CreamInstaller; + +public enum DLCType +{ + Steam, SteamHidden, EpicCatalogItem, + EpicEntitlement +} + +internal sealed class SelectionDLC +{ + private static readonly HashSet All = new(); + + internal bool Enabled; + internal string Icon; + internal string Id; + internal string Name; + internal string Product; + internal string Publisher; + private Selection selection; + internal DLCType Type; + + internal Selection Selection + { + get => selection; + set + { + selection = value; + _ = value is null ? All.Remove(this) : All.Add(this); + } + } + + internal static List AllSafe => All.ToList(); + + internal static SelectionDLC FromPlatformId(Platform platform, string dlcId) => AllSafe.Find(dlc => dlc.Selection.Platform == platform && dlc.Id == dlcId); +} \ No newline at end of file diff --git a/CreamInstaller/Utility/HttpClientManager.cs b/CreamInstaller/Utility/HttpClientManager.cs index 64815f7..7109170 100644 --- a/CreamInstaller/Utility/HttpClientManager.cs +++ b/CreamInstaller/Utility/HttpClientManager.cs @@ -1,5 +1,5 @@ using System; -using System.Collections.Generic; +using System.Collections.Concurrent; using System.Drawing; using System.Globalization; using System.Net; @@ -15,7 +15,7 @@ internal static class HttpClientManager { internal static HttpClient HttpClient; - private static readonly Dictionary HttpContentCache = new(); + private static readonly ConcurrentDictionary HttpContentCache = new(); internal static void Setup() { diff --git a/CreamInstaller/Utility/SafeIO.cs b/CreamInstaller/Utility/SafeIO.cs index 5b5b2cf..49395eb 100644 --- a/CreamInstaller/Utility/SafeIO.cs +++ b/CreamInstaller/Utility/SafeIO.cs @@ -36,7 +36,7 @@ internal static class SafeIO while (!Program.Canceled) try { - Directory.CreateDirectory(directoryPath); + _ = Directory.CreateDirectory(directoryPath); break; } catch (Exception e) @@ -251,6 +251,11 @@ internal static class SafeIO private static DialogResult IOWarnInternal(this string filePath, string message, Exception e, Form form = null) { using DialogForm dialogForm = new(form); - return dialogForm.Show(SystemIcons.Warning, message + ": " + filePath.BeautifyPath() + "\n\n" + e.FormatException(), "Retry", "OK"); + string description = message + ": " + filePath.BeautifyPath() + "\n\n"; + if (e is IOException && (e.HResult & 0x0000FFFF) == 225) // virus or potentially unwanted software + description += "Please resolve your anti-virus and press retry to continue . . . "; + else + description += e.FormatException(); + return dialogForm.Show(SystemIcons.Warning, description, "Retry", "OK"); } } \ No newline at end of file