diff --git a/CreamInstaller/CreamInstaller.csproj b/CreamInstaller/CreamInstaller.csproj index 4defe3c..0f30b9c 100644 --- a/CreamInstaller/CreamInstaller.csproj +++ b/CreamInstaller/CreamInstaller.csproj @@ -4,7 +4,7 @@ net7.0-windows True Resources\ini.ico - 4.9.1 + 4.9.2 2021, pointfeev (https://github.com/pointfeev) CreamInstaller Automatic DLC Unlocker Installer & Configuration Generator diff --git a/CreamInstaller/Forms/InstallForm.cs b/CreamInstaller/Forms/InstallForm.cs index e7264b1..43bbbb0 100644 --- a/CreamInstaller/Forms/InstallForm.cs +++ b/CreamInstaller/Forms/InstallForm.cs @@ -16,7 +16,7 @@ internal sealed partial class InstallForm : CustomForm { private readonly HashSet disabledSelections = new(); - private readonly int programCount = Selection.AllEnabled.Count; + private readonly int programCount = Selection.AllEnabled.Count(); private readonly bool uninstalling; private int completeOperationsCount; @@ -184,7 +184,7 @@ internal sealed partial class InstallForm : CustomForm private async Task Operate() { - HashSet programSelections = Selection.AllEnabled; + HashSet programSelections = Selection.AllEnabled.ToHashSet(); operationsCount = programSelections.Count; completeOperationsCount = 0; foreach (Selection selection in programSelections) @@ -205,7 +205,7 @@ internal sealed partial class InstallForm : CustomForm ++completeOperationsCount; } Program.Cleanup(); - HashSet failedSelections = Selection.AllEnabled; + HashSet failedSelections = Selection.AllEnabled.ToHashSet(); if (failedSelections.Count > 0) if (failedSelections.Count == 1) throw new CustomMessageException($"Operation failed for {failedSelections.First().Name}."); diff --git a/CreamInstaller/Forms/SelectForm.Designer.cs b/CreamInstaller/Forms/SelectForm.Designer.cs index bedda3b..4bf9a80 100644 --- a/CreamInstaller/Forms/SelectForm.Designer.cs +++ b/CreamInstaller/Forms/SelectForm.Designer.cs @@ -447,7 +447,7 @@ namespace CreamInstaller.Forms private GroupBox programsGroupBox; private ProgressBar progressBar; private Label progressLabel; - private CheckBox allCheckBox; + internal CheckBox allCheckBox; private Button scanButton; private Label noneFoundLabel; private CustomTreeView selectionTreeView; @@ -460,7 +460,7 @@ namespace CreamInstaller.Forms private Label progressLabelDLCs; private CheckBox sortCheckBox; private FlowLayoutPanel koaloaderFlowPanel; - private CheckBox koaloaderAllCheckBox; + internal CheckBox koaloaderAllCheckBox; private Button saveButton; private Button loadButton; private Button resetKoaloaderButton; diff --git a/CreamInstaller/Forms/SelectForm.cs b/CreamInstaller/Forms/SelectForm.cs index 9a656d8..096be6c 100644 --- a/CreamInstaller/Forms/SelectForm.cs +++ b/CreamInstaller/Forms/SelectForm.cs @@ -25,21 +25,29 @@ internal sealed partial class SelectForm : CustomForm { private const string HelpButtonListPrefix = "\n • "; + private static SelectForm current; + private readonly ConcurrentDictionary remainingDLCs = new(); private readonly ConcurrentDictionary remainingGames = new(); private List<(Platform platform, string id, string name)> programsToScan; - internal SelectForm() + private SelectForm() { InitializeComponent(); Text = Program.ApplicationName; } - public override ContextMenuStrip ContextMenuStrip => base.ContextMenuStrip ??= new(); - - private List TreeNodes => GatherTreeNodes(selectionTreeView.Nodes); + internal static SelectForm Current + { + get + { + if (current is not null && (current.Disposing || current.IsDisposed)) + current = null; + return current ??= new(); + } + } private static void UpdateRemaining(Label label, ConcurrentDictionary list, string descriptor) => label.Text = list.IsEmpty ? "" : $"Remaining {descriptor} ({list.Count}): " + string.Join(", ", list.Values).Replace("&", "&&"); @@ -119,7 +127,6 @@ internal sealed partial class SelectForm : CustomForm } if (Program.Canceled) return; - List treeNodes = TreeNodes; remainingGames.Clear(); // for display purposes only, otherwise ignorable remainingDLCs.Clear(); // for display purposes only, otherwise ignorable List appTasks = new(); @@ -133,20 +140,8 @@ internal sealed partial class SelectForm : CustomForm await ParadoxLauncher.InstallPath.GetExecutableDirectories(validFunc: path => !Path.GetFileName(path).Contains("bootstrapper"))); if (uninstallAll) selection.Enabled = true; - else - { - if (allCheckBox.Checked) - selection.Enabled = true; - if (koaloaderAllCheckBox.Checked) - selection.Koaloader = true; - TreeNode programNode = treeNodes.Find(s => s.Tag is Platform.Paradox && s.Name == selection.Id) ?? new TreeNode(); - programNode.Tag = selection.Platform; - programNode.Name = selection.Id; - programNode.Text = selection.Name; - programNode.Checked = selection.Enabled; - if (programNode.TreeView is null) - _ = selectionTreeView.Nodes.Add(programNode); - } + else if (selection.TreeNode.TreeView is null) + _ = selectionTreeView.Nodes.Add(selection.TreeNode); RemoveFromRemainingGames("Paradox Launcher"); } } @@ -313,10 +308,6 @@ internal sealed partial class SelectForm : CustomForm } Selection selection = Selection.GetOrCreate(Platform.Steam, appId, appData?.Name ?? name, gameDirectory, dllDirectories, await gameDirectory.GetExecutableDirectories(true)); - 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.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 @@ -329,26 +320,13 @@ internal sealed partial class SelectForm : CustomForm { if (Program.Canceled) return; - TreeNode programNode = treeNodes.Find(s => (Platform)s.Tag == selection.Platform && s.Name == appId) ?? new TreeNode(); - programNode.Tag = selection.Platform; - programNode.Name = appId; - programNode.Text = appData?.Name ?? name; - programNode.Checked = selection.Enabled; - if (programNode.TreeView is null) - _ = selectionTreeView.Nodes.Add(programNode); + if (selection.TreeNode.TreeView is null) + _ = selectionTreeView.Nodes.Add(selection.TreeNode); foreach ((SelectionDLC dlc, _) in dlc) { if (Program.Canceled) return; dlc.Selection = selection; - dlc.Enabled = dlc.Name != "Unknown" && allCheckBox.Checked; - TreeNode dlcNode = treeNodes.Find(s => (DLCType)s.Tag == dlc.Type && s.Name == dlc.Id) ?? new TreeNode(); - dlcNode.Tag = dlc.Type; - dlcNode.Name = dlc.Id; - dlcNode.Text = dlc.Name; - dlcNode.Checked = dlc.Enabled; - if (dlcNode.Parent is null) - _ = programNode.Nodes.Add(dlcNode); } }); if (Program.Canceled) @@ -431,10 +409,6 @@ internal sealed partial class SelectForm : CustomForm } Selection selection = Selection.GetOrCreate(Platform.Epic, @namespace, name, directory, dllDirectories, await directory.GetExecutableDirectories(true)); - 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; foreach ((SelectionDLC dlc, _) in entitlements.Where(dlc => dlc.Key.Name == selection.Name)) { if (Program.Canceled) @@ -449,57 +423,22 @@ internal sealed partial class SelectForm : CustomForm { if (Program.Canceled) return; - TreeNode programNode = treeNodes.Find(s => (Platform)s.Tag == selection.Platform && s.Name == @namespace) ?? new TreeNode(); - programNode.Tag = selection.Platform; - programNode.Name = @namespace; - programNode.Text = name; - programNode.Checked = selection.Enabled; - if (programNode.TreeView is null) - _ = selectionTreeView.Nodes.Add(programNode); + if (selection.TreeNode.TreeView is null) + _ = selectionTreeView.Nodes.Add(selection.TreeNode); if (!catalogItems.IsEmpty) - /*TreeNode catalogItemsNode = treeNodes.Find(node => (Platform)s.Tag == selection.Platform && node.Name == @namespace + "_catalogItems") ?? new(); - catalogItemsNode.Tag = selection.Platform; - 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) { if (Program.Canceled) return; dlc.Selection = selection; - dlc.Enabled = allCheckBox.Checked; - TreeNode dlcNode = treeNodes.Find(s => (DLCType)s.Tag == dlc.Type && s.Name == dlc.Id) ?? new TreeNode(); - dlcNode.Tag = dlc.Type; - dlcNode.Name = dlc.Id; - dlcNode.Text = dlc.Name; - dlcNode.Checked = dlc.Enabled; - if (dlcNode.Parent is null) - _ = programNode.Nodes.Add(dlcNode); //_ = catalogItemsNode.Nodes.Add(dlcNode); } if (entitlements.IsEmpty) return; - /*TreeNode entitlementsNode = treeNodes.Find(node => (Platform)s.Tag == selection.Platform && node.Name == @namespace + "_entitlements") ?? new(); - entitlementsNode.Name = selection.Platform; - 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 => (DLCType)s.Tag == dlc.Type && s.Name == dlc.Id) ?? new TreeNode(); - dlcNode.Tag = dlc.Type; - 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) @@ -541,22 +480,13 @@ internal sealed partial class SelectForm : CustomForm return; Selection selection = Selection.GetOrCreate(Platform.Ubisoft, gameId, name, gameDirectory, dllDirectories, await gameDirectory.GetExecutableDirectories(true)); - 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.Icon = IconGrabber.GetDomainFaviconUrl("store.ubi.com"); selectionTreeView.Invoke(delegate { if (Program.Canceled) return; - TreeNode programNode = treeNodes.Find(s => (Platform)s.Tag == selection.Platform && s.Name == gameId) ?? new TreeNode(); - programNode.Tag = selection.Platform; - programNode.Name = gameId; - programNode.Text = name; - programNode.Checked = selection.Enabled; - if (programNode.TreeView is null) - _ = selectionTreeView.Nodes.Add(programNode); + if (selection.TreeNode.TreeView is null) + _ = selectionTreeView.Nodes.Add(selection.TreeNode); }); if (Program.Canceled) return; @@ -639,7 +569,8 @@ internal sealed partial class SelectForm : CustomForm progressBar.Value = p; }; progressLabel.Text = "Quickly gathering games for uninstallation . . . "; - TreeNodes.ForEach(node => node.Remove()); + foreach (Selection selection in Selection.All.Keys) + selection.TreeNode.Remove(); await GetApplicablePrograms(iProgress, true); if (!Program.Canceled) OnUninstall(null, null); @@ -691,7 +622,8 @@ internal sealed partial class SelectForm : CustomForm setup = false; progressLabel.Text = "Gathering and caching your applicable games and their DLCs . . . "; Selection.ValidateAll(programsToScan); - TreeNodes.ForEach(node => node.Remove()); + foreach (Selection selection in Selection.All.Keys) + selection.TreeNode.Remove(); await GetApplicablePrograms(iProgress); await SteamCMD.Cleanup(); } @@ -702,7 +634,7 @@ internal sealed partial class SelectForm : CustomForm allCheckBox.Enabled = selectionTreeView.Enabled; koaloaderAllCheckBox.Enabled = selectionTreeView.Enabled; noneFoundLabel.Visible = !selectionTreeView.Enabled; - installButton.Enabled = Selection.AllEnabled.Count > 0; + installButton.Enabled = Selection.AllEnabled.Any(); uninstallButton.Enabled = installButton.Enabled; saveButton.Enabled = CanSaveDlc(); loadButton.Enabled = CanLoadDlc(); @@ -723,13 +655,12 @@ internal sealed partial class SelectForm : CustomForm TreeNode node = e.Node; if (node is null) return; - SyncNode(node); SyncNodeAncestors(node); SyncNodeDescendants(node); allCheckBox.CheckedChanged -= OnAllCheckBoxChanged; - allCheckBox.Checked = TreeNodes.TrueForAll(node => node.Text == "Unknown" || node.Checked); + allCheckBox.Checked = EnumerateTreeNodes(selectionTreeView.Nodes).All(node => node.Text == "Unknown" || node.Checked); allCheckBox.CheckedChanged += OnAllCheckBoxChanged; - installButton.Enabled = Selection.AllEnabled.Count > 0; + installButton.Enabled = Selection.AllEnabled.Any(); uninstallButton.Enabled = installButton.Enabled; saveButton.Enabled = CanSaveDlc(); resetButton.Enabled = CanResetDlc(); @@ -737,17 +668,11 @@ internal sealed partial class SelectForm : CustomForm private static void SyncNodeAncestors(TreeNode node) { - while (true) - { - TreeNode parentNode = node.Parent; - if (parentNode is not null) - { - parentNode.Checked = parentNode.Nodes.Cast().Any(childNode => childNode.Checked); - node = parentNode; - continue; - } - break; - } + TreeNode parentNode = node.Parent; + if (parentNode is null) + return; + parentNode.Checked = parentNode.Nodes.Cast().Any(childNode => childNode.Checked); + SyncNodeAncestors(parentNode); } private static void SyncNodeDescendants(TreeNode node) @@ -755,38 +680,20 @@ internal sealed partial class SelectForm : CustomForm foreach (TreeNode childNode in node.Nodes) { if (childNode.Text == "Unknown") - return; + continue; childNode.Checked = node.Checked; - SyncNode(childNode); SyncNodeDescendants(childNode); } } - private static void SyncNode(TreeNode node) + private static IEnumerable EnumerateTreeNodes(TreeNodeCollection nodeCollection) { - string id = node.Name; - Platform platform = (Platform)node.Tag; - Selection selection = Selection.FromPlatformId(platform, id); - if (selection is not null) - { - selection.Enabled = node.Checked; - return; - } - DLCType type = (DLCType)node.Tag; - SelectionDLC dlc = SelectionDLC.FromTypeId(type, id); - if (dlc is not null) - dlc.Enabled = node.Checked; - } - - private static List GatherTreeNodes(TreeNodeCollection nodeCollection) - { - List treeNodes = new(); foreach (TreeNode rootNode in nodeCollection) { - treeNodes.Add(rootNode); - treeNodes.AddRange(GatherTreeNodes(rootNode.Nodes)); + yield return rootNode; + foreach (TreeNode childNode in EnumerateTreeNodes(rootNode.Nodes)) + yield return childNode; } - return treeNodes; } private void ShowProgressBar() @@ -1027,45 +934,44 @@ internal sealed partial class SelectForm : CustomForm private void OnAllCheckBoxChanged(object sender, EventArgs e) { - bool shouldCheck = TreeNodes.Any(node => node.Parent is null && !node.Checked); - foreach (TreeNode node in TreeNodes.Where(node => node.Parent is null && node.Checked != shouldCheck)) + bool shouldEnable = Selection.All.Keys.Any(s => !s.Enabled); + foreach (Selection selection in Selection.All.Keys.Where(s => s.Enabled != shouldEnable)) { - node.Checked = shouldCheck; - OnTreeViewNodeCheckedChanged(null, new(node, TreeViewAction.ByMouse)); + selection.Enabled = shouldEnable; + OnTreeViewNodeCheckedChanged(null, new(selection.TreeNode, TreeViewAction.ByMouse)); } allCheckBox.CheckedChanged -= OnAllCheckBoxChanged; - allCheckBox.Checked = shouldCheck; + allCheckBox.Checked = shouldEnable; allCheckBox.CheckedChanged += OnAllCheckBoxChanged; } private void OnKoaloaderAllCheckBoxChanged(object sender, EventArgs e) { - bool shouldCheck = Selection.AllSafe.Any(selection => !selection.Koaloader); - foreach (Selection selection in Selection.AllSafe) - selection.Koaloader = shouldCheck; + bool shouldEnable = Selection.All.Keys.Any(selection => !selection.Koaloader); + foreach (Selection selection in Selection.All.Keys) + selection.Koaloader = shouldEnable; selectionTreeView.Invalidate(); koaloaderAllCheckBox.CheckedChanged -= OnKoaloaderAllCheckBoxChanged; - koaloaderAllCheckBox.Checked = shouldCheck; + koaloaderAllCheckBox.Checked = shouldEnable; koaloaderAllCheckBox.CheckedChanged += OnKoaloaderAllCheckBoxChanged; + resetKoaloaderButton.Enabled = CanResetKoaloader(); } private bool AreSelectionsDefault() - => TreeNodes.All(node => node.Parent is null || node.Tag is not Platform || (node.Text == "Unknown" ? !node.Checked : node.Checked)); + => EnumerateTreeNodes(selectionTreeView.Nodes).All(node + => node.Parent is null || node.Tag is not Platform and not DLCType || (node.Text == "Unknown" ? !node.Checked : node.Checked)); private bool CanSaveDlc() => installButton.Enabled && (ProgramData.ReadDlcChoices().Any() || !AreSelectionsDefault()); private void OnSaveDlc(object sender, EventArgs e) { List<(Platform platform, string gameId, string dlcId)> choices = ProgramData.ReadDlcChoices().ToList(); - foreach (TreeNode node in TreeNodes) - if (node.Parent is { } parent && node.Tag is Platform platform) - { - if ((node.Text == "Unknown" ? node.Checked : !node.Checked) - && !choices.Any(c => c.platform == platform && c.gameId == parent.Name && c.dlcId == node.Name)) - choices.Add((platform, node.Parent.Name, node.Name)); - else - _ = choices.RemoveAll(n => n.platform == platform && n.gameId == parent.Name && n.dlcId == node.Name); - } + foreach (SelectionDLC dlc in SelectionDLC.All.Keys) + if ((dlc.Name == "Unknown" ? dlc.Enabled : !dlc.Enabled) + && !choices.Any(c => c.platform == dlc.Selection.Platform && c.gameId == dlc.Selection.Id && c.dlcId == dlc.Id)) + choices.Add((dlc.Selection.Platform, dlc.Selection.Id, dlc.Id)); + else + _ = choices.RemoveAll(n => n.platform == dlc.Selection.Platform && n.gameId == dlc.Selection.Id && n.dlcId == dlc.Id); ProgramData.WriteDlcChoices(choices); loadButton.Enabled = CanLoadDlc(); saveButton.Enabled = CanSaveDlc(); @@ -1076,36 +982,35 @@ internal sealed partial class SelectForm : CustomForm private void OnLoadDlc(object sender, EventArgs e) { List<(Platform platform, string gameId, string dlcId)> choices = ProgramData.ReadDlcChoices().ToList(); - foreach (TreeNode node in TreeNodes) - if (node.Parent is { } parent && node.Tag is Platform platform) - { - node.Checked = choices.Any(c => c.platform == platform && c.gameId == parent.Name && c.dlcId == node.Name) - ? node.Text == "Unknown" - : node.Text != "Unknown"; - OnTreeViewNodeCheckedChanged(null, new(node, TreeViewAction.ByMouse)); - } + foreach (SelectionDLC dlc in SelectionDLC.All.Keys) + { + dlc.Enabled = choices.Any(c => c.platform == dlc.Selection.Platform && c.gameId == dlc.Selection.Id && c.dlcId == dlc.Id) + ? dlc.Name == "Unknown" + : dlc.Name != "Unknown"; + OnTreeViewNodeCheckedChanged(null, new(dlc.TreeNode, TreeViewAction.ByMouse)); + } } private bool CanResetDlc() => !AreSelectionsDefault(); private void OnResetDlc(object sender, EventArgs e) { - foreach (TreeNode node in TreeNodes.Where(node => node.Parent is not null && node.Tag is Platform)) + foreach (SelectionDLC dlc in SelectionDLC.All.Keys) { - node.Checked = node.Text != "Unknown"; - OnTreeViewNodeCheckedChanged(null, new(node, TreeViewAction.ByMouse)); + dlc.Enabled = dlc.Name != "Unknown"; + OnTreeViewNodeCheckedChanged(null, new(dlc.TreeNode, TreeViewAction.ByMouse)); } resetButton.Enabled = CanResetDlc(); } - private static bool AreKoaloaderSelectionsDefault() => Selection.AllSafe.All(selection => selection.Koaloader && selection.KoaloaderProxy is null); + private static bool AreKoaloaderSelectionsDefault() => Selection.All.Keys.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 (Selection selection in Selection.AllSafe) + foreach (Selection selection in Selection.All.Keys) { _ = choices.RemoveAll(c => c.platform == selection.Platform && c.id == selection.Id); if (selection.KoaloaderProxy is not null and not Selection.DefaultKoaloaderProxy || !selection.Koaloader) @@ -1122,7 +1027,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 (Selection selection in Selection.AllSafe) + foreach (Selection selection in Selection.All.Keys) if (choices.Any(c => c.platform == selection.Platform && c.id == selection.Id)) { (Platform platform, string id, string proxy, bool enabled) @@ -1155,7 +1060,7 @@ internal sealed partial class SelectForm : CustomForm private void OnResetKoaloader(object sender, EventArgs e) { - foreach (Selection selection in Selection.AllSafe) + foreach (Selection selection in Selection.All.Keys) { selection.Koaloader = true; selection.KoaloaderProxy = null; @@ -1169,7 +1074,7 @@ internal sealed partial class SelectForm : CustomForm saveKoaloaderButton.Enabled = CanSaveKoaloader(); resetKoaloaderButton.Enabled = CanResetKoaloader(); koaloaderAllCheckBox.CheckedChanged -= OnKoaloaderAllCheckBoxChanged; - koaloaderAllCheckBox.Checked = Selection.AllSafe.All(selection => selection.Koaloader); + koaloaderAllCheckBox.Checked = Selection.All.Keys.All(selection => selection.Koaloader); koaloaderAllCheckBox.CheckedChanged += OnKoaloaderAllCheckBoxChanged; } diff --git a/CreamInstaller/Forms/UpdateForm.cs b/CreamInstaller/Forms/UpdateForm.cs index 6175fa6..ddccab7 100644 --- a/CreamInstaller/Forms/UpdateForm.cs +++ b/CreamInstaller/Forms/UpdateForm.cs @@ -32,7 +32,7 @@ internal sealed partial class UpdateForm : CustomForm private void StartProgram() { - SelectForm form = new(); + SelectForm form = SelectForm.Current; form.InheritLocation(this); form.FormClosing += (_, _) => Close(); form.Show(); diff --git a/CreamInstaller/Platforms/Paradox/ParadoxLauncher.cs b/CreamInstaller/Platforms/Paradox/ParadoxLauncher.cs index 279b5ee..2b72b63 100644 --- a/CreamInstaller/Platforms/Paradox/ParadoxLauncher.cs +++ b/CreamInstaller/Platforms/Paradox/ParadoxLauncher.cs @@ -35,11 +35,11 @@ internal static class ParadoxLauncher if (paradoxLauncher is null) return; paradoxLauncher.ExtraSelections.Clear(); - foreach (Selection selection in Selection.AllEnabled.Where(s => s != paradoxLauncher && s.Publisher == "Paradox Interactive")) + foreach (Selection selection in Selection.AllEnabled.Where(s => !s.Equals(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")) + foreach (Selection selection in Selection.All.Keys.Where(s => !s.Equals(paradoxLauncher) && s.Publisher == "Paradox Interactive")) _ = paradoxLauncher.ExtraSelections.Add(selection); } diff --git a/CreamInstaller/Platforms/Steam/SteamCMD.cs b/CreamInstaller/Platforms/Steam/SteamCMD.cs index b57fae7..b057c41 100644 --- a/CreamInstaller/Platforms/Steam/SteamCMD.cs +++ b/CreamInstaller/Platforms/Steam/SteamCMD.cs @@ -55,64 +55,63 @@ internal static class SteamCMD { if (Program.Canceled) return ""; - if (Interlocked.CompareExchange(ref Locks[i], 1, 0) == 0) + if (Interlocked.CompareExchange(ref Locks[i], 1, 0) != 0) + continue; + if (appId != null) + { + _ = AttemptCount.TryGetValue(appId, out int count); + AttemptCount[appId] = ++count; + } + if (Program.Canceled) + return ""; + ProcessStartInfo processStartInfo = new() + { + FileName = FilePath, RedirectStandardOutput = true, RedirectStandardInput = true, RedirectStandardError = true, + UseShellExecute = false, Arguments = appId is null ? "+quit" : GetArguments(appId), CreateNoWindow = true, + StandardInputEncoding = Encoding.UTF8, StandardOutputEncoding = Encoding.UTF8, StandardErrorEncoding = Encoding.UTF8 + }; + Process process = Process.Start(processStartInfo); + StringBuilder output = new(); + StringBuilder appInfo = new(); + bool appInfoStarted = false; + DateTime lastOutput = DateTime.UtcNow; + while (process != null) { - if (appId != null) - { - _ = AttemptCount.TryGetValue(appId, out int count); - AttemptCount[appId] = ++count; - } if (Program.Canceled) - return ""; - ProcessStartInfo processStartInfo = new() { - FileName = FilePath, RedirectStandardOutput = true, RedirectStandardInput = true, RedirectStandardError = true, - UseShellExecute = false, Arguments = appId is null ? "+quit" : GetArguments(appId), CreateNoWindow = true, - StandardInputEncoding = Encoding.UTF8, StandardOutputEncoding = Encoding.UTF8, StandardErrorEncoding = Encoding.UTF8 - }; - Process process = Process.Start(processStartInfo); - StringBuilder output = new(); - StringBuilder appInfo = new(); - bool appInfoStarted = false; - DateTime lastOutput = DateTime.UtcNow; - while (process != null) - { - if (Program.Canceled) - { - process.Kill(true); - process.Close(); - break; - } - int c = process.StandardOutput.Read(); - if (c != -1) - { - lastOutput = DateTime.UtcNow; - char ch = (char)c; - if (ch == '{') - appInfoStarted = true; - _ = appInfoStarted ? appInfo.Append(ch) : output.Append(ch); - } - DateTime now = DateTime.UtcNow; - TimeSpan timeDiff = now - lastOutput; - if (!(timeDiff.TotalSeconds > 0.1)) - continue; process.Kill(true); process.Close(); - if (appId != null && output.ToString().Contains($"No app info for AppID {appId} found, requesting...")) - { - AttemptCount[appId]++; - processStartInfo.Arguments = GetArguments(appId); - process = Process.Start(processStartInfo); - appInfoStarted = false; - _ = output.Clear(); - _ = appInfo.Clear(); - } - else - break; + break; } - _ = Interlocked.Decrement(ref Locks[i]); - return appInfo.ToString(); + int c = process.StandardOutput.Read(); + if (c != -1) + { + lastOutput = DateTime.UtcNow; + char ch = (char)c; + if (ch == '{') + appInfoStarted = true; + _ = appInfoStarted ? appInfo.Append(ch) : output.Append(ch); + } + DateTime now = DateTime.UtcNow; + TimeSpan timeDiff = now - lastOutput; + if (!(timeDiff.TotalSeconds > 0.1)) + continue; + process.Kill(true); + process.Close(); + if (appId != null && output.ToString().Contains($"No app info for AppID {appId} found, requesting...")) + { + AttemptCount[appId]++; + processStartInfo.Arguments = GetArguments(appId); + process = Process.Start(processStartInfo); + appInfoStarted = false; + _ = output.Clear(); + _ = appInfo.Clear(); + } + else + break; } + _ = Interlocked.Decrement(ref Locks[i]); + return appInfo.ToString(); } Thread.Sleep(200); goto wait_for_lock; diff --git a/CreamInstaller/Platforms/Steam/SteamLibrary.cs b/CreamInstaller/Platforms/Steam/SteamLibrary.cs index 77c7fcc..88b6fec 100644 --- a/CreamInstaller/Platforms/Steam/SteamLibrary.cs +++ b/CreamInstaller/Platforms/Steam/SteamLibrary.cs @@ -30,10 +30,10 @@ internal static class SteamLibrary { if (Program.Canceled) return games; - 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); + foreach ((string appId, string name, string branch, int buildId, string gameDirectory) game in await GetGamesFromLibraryDirectory( + libraryDirectory)) + if (!games.Any(_game => _game.appId == game.appId && _game.gameDirectory == game.gameDirectory)) + games.Add(game); } return games; }); diff --git a/CreamInstaller/Program.cs b/CreamInstaller/Program.cs index 23fc08d..d98100e 100644 --- a/CreamInstaller/Program.cs +++ b/CreamInstaller/Program.cs @@ -38,15 +38,9 @@ internal static class Program internal static readonly string[] ProtectedGameDirectoryExceptions = Array.Empty(); internal static bool IsGameBlocked(string name, string directory = null) - { - if (!BlockProtectedGames) - return false; - if (ProtectedGames.Contains(name)) - return true; - if (directory is null || ProtectedGameDirectoryExceptions.Contains(name)) - return false; - return ProtectedGameDirectories.Any(path => (directory + path).DirectoryExists()); - } + => BlockProtectedGames && (ProtectedGames.Contains(name) || directory is not null && !ProtectedGameDirectoryExceptions.Contains(name) + && ProtectedGameDirectories.Any(path + => (directory + path).DirectoryExists())); internal static bool AreDllsLockedDialog(Form form, Selection selection) { @@ -57,8 +51,8 @@ internal static class Program using DialogForm dialogForm = new(form); if (dialogForm.Show(SystemIcons.Error, $"ERROR: One or more DLLs crucial to unlocker installation are locked for {selection.Name}!" - + "\n\nThis is commonly caused by the program/game being active or an anti-virus blocking access." - + "\n\nPlease close the program/game or resolve your anti-virus to continue . . . ", "Retry", "Cancel") == DialogResult.OK) + + "\n\nPlease close the program/game or resolve your anti-virus and press retry to continue . . . ", "Retry", "Cancel") + == DialogResult.OK) continue; } else diff --git a/CreamInstaller/Selection.cs b/CreamInstaller/Selection.cs index 812c42d..0b8a9aa 100644 --- a/CreamInstaller/Selection.cs +++ b/CreamInstaller/Selection.cs @@ -2,6 +2,8 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using System.Windows.Forms; +using CreamInstaller.Forms; using CreamInstaller.Resources; using CreamInstaller.Utility; using static CreamInstaller.Resources.Resources; @@ -27,8 +29,7 @@ internal sealed class Selection : IEquatable internal readonly string Name; internal readonly Platform Platform; internal readonly string RootDirectory; - - internal bool Enabled; + internal readonly TreeNode TreeNode; internal string Icon; internal bool Koaloader; internal string KoaloaderProxy; @@ -47,9 +48,19 @@ internal sealed class Selection : IEquatable DllDirectories = dllDirectories; ExecutableDirectories = executableDirectories; _ = All.TryAdd(this, default); + TreeNode = new() { Tag = Platform, Name = Id, Text = Name }; + SelectForm selectForm = SelectForm.Current; + if (selectForm is null) + return; + Enabled = selectForm.allCheckBox.Checked; + Koaloader = selectForm.koaloaderAllCheckBox.Checked; } - internal IEnumerable DLC => SelectionDLC.AllSafe.Where(dlc => dlc.Selection.Equals(this)); + internal static IEnumerable AllEnabled => All.Keys.Where(s => s.Enabled); + + internal bool Enabled { get => TreeNode.Checked; set => TreeNode.Checked = value; } + + internal IEnumerable DLC => SelectionDLC.All.Keys.Where(dlc => dlc.Selection.Equals(this)); internal bool AreDllsLocked { @@ -92,10 +103,6 @@ internal sealed class Selection : IEquatable } } - internal static HashSet AllSafe => All.Keys.ToHashSet(); - - internal static HashSet AllEnabled => All.Keys.Where(s => s.Enabled).ToHashSet(); - public bool Equals(Selection other) => other is not null && (ReferenceEquals(this, other) || Id == other.Id && Platform == other.Platform && RootDirectory == other.RootDirectory); @@ -106,6 +113,7 @@ internal sealed class Selection : IEquatable private void Remove() { _ = All.TryRemove(this, out _); + TreeNode.Remove(); foreach (SelectionDLC dlc in DLC) dlc.Selection = null; } @@ -134,11 +142,11 @@ internal sealed class Selection : IEquatable internal static void ValidateAll(List<(Platform platform, string id, string name)> programsToScan) { - foreach (Selection selection in AllSafe) + foreach (Selection selection in All.Keys.ToHashSet()) selection.Validate(programsToScan); } - internal static Selection FromPlatformId(Platform platform, string gameId) => AllSafe.FirstOrDefault(s => s.Platform == platform && s.Id == gameId); + internal static Selection FromPlatformId(Platform platform, string gameId) => All.Keys.FirstOrDefault(s => s.Platform == platform && s.Id == gameId); public override bool Equals(object obj) => ReferenceEquals(this, obj) || obj is Selection other && Equals(other); diff --git a/CreamInstaller/SelectionDLC.cs b/CreamInstaller/SelectionDLC.cs index b83cad8..a718229 100644 --- a/CreamInstaller/SelectionDLC.cs +++ b/CreamInstaller/SelectionDLC.cs @@ -1,25 +1,24 @@ using System; using System.Collections.Concurrent; -using System.Collections.Generic; using System.Linq; +using System.Windows.Forms; namespace CreamInstaller; public enum DLCType { - None = 0, Steam, SteamHidden, EpicCatalogItem, - EpicEntitlement + None = 0, Steam, SteamHidden, + EpicCatalogItem, EpicEntitlement } internal sealed class SelectionDLC : IEquatable { - private static readonly ConcurrentDictionary All = new(); + internal static readonly ConcurrentDictionary All = new(); internal readonly string Id; internal readonly string Name; + internal readonly TreeNode TreeNode; internal readonly DLCType Type; - - internal bool Enabled; internal string Icon; internal string Product; internal string Publisher; @@ -30,25 +29,38 @@ internal sealed class SelectionDLC : IEquatable Type = type; Id = id; Name = name; + TreeNode = new() { Tag = Type, Name = Id, Text = Name }; } + internal bool Enabled { get => TreeNode.Checked; set => TreeNode.Checked = value; } + internal Selection Selection { get => selection; set { + if (ReferenceEquals(selection, value)) + return; selection = value; - _ = value is null ? All.TryRemove(this, out _) : All.TryAdd(this, default); + if (value is null) + { + _ = All.TryRemove(this, out _); + TreeNode.Remove(); + } + else + { + _ = All.TryAdd(this, default); + _ = value.TreeNode.Nodes.Add(TreeNode); + Enabled = Name != "Unknown" && value.Enabled; + } } } - internal static HashSet AllSafe => All.Keys.ToHashSet(); - public bool Equals(SelectionDLC other) => other is not null && (ReferenceEquals(this, other) || Id == other.Id && Type == other.Type); internal static SelectionDLC GetOrCreate(DLCType type, string id, string name) => FromTypeId(type, id) ?? new SelectionDLC(type, id, name); - internal static SelectionDLC FromTypeId(DLCType Type, string dlcId) => AllSafe.FirstOrDefault(dlc => dlc.Type == Type && dlc.Id == dlcId); + internal static SelectionDLC FromTypeId(DLCType Type, string dlcId) => All.Keys.FirstOrDefault(dlc => dlc.Type == Type && dlc.Id == dlcId); public override bool Equals(object obj) => ReferenceEquals(this, obj) || obj is SelectionDLC other && Equals(other);