diff --git a/CreamInstaller/Components/CustomTreeView.cs b/CreamInstaller/Components/CustomTreeView.cs index 1fcf0e3..3da0876 100644 --- a/CreamInstaller/Components/CustomTreeView.cs +++ b/CreamInstaller/Components/CustomTreeView.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Drawing; using System.Windows.Forms; @@ -14,10 +15,16 @@ internal class CustomTreeView : TreeView base.WndProc(ref m); } + private class TreeNodeSorter : IComparer + { + public int Compare(object a, object b) => AppIdComparer.Comparer.Compare((a as TreeNode).Name, (b as TreeNode).Name); + } + internal CustomTreeView() : base() { DrawMode = TreeViewDrawMode.OwnerDrawAll; DrawNode += new DrawTreeNodeEventHandler(DrawTreeNode); + TreeViewNodeSorter = new TreeNodeSorter(); } private void DrawTreeNode(object sender, DrawTreeNodeEventArgs e) @@ -35,7 +42,7 @@ internal class CustomTreeView : TreeView string subText = node.Name; if (string.IsNullOrWhiteSpace(subText) || subText == "ParadoxLauncher" - || ProgramSelection.FromId(subText) is null && ProgramSelection.GetDlcFromId(subText) is null) + || node.Tag is null && ProgramSelection.FromId(subText) is null && ProgramSelection.GetDlcFromId(subText) is null) return; Size subSize = TextRenderer.MeasureText(graphics, subText, subFont); @@ -46,4 +53,4 @@ internal class CustomTreeView : TreeView Point subLocation = new(location.X - 1, location.Y + 1); TextRenderer.DrawText(graphics, subText, subFont, subLocation, Color.Gray); } -} +} \ No newline at end of file diff --git a/CreamInstaller/CreamInstaller.csproj b/CreamInstaller/CreamInstaller.csproj index 3a2bf21..81b5ae9 100644 --- a/CreamInstaller/CreamInstaller.csproj +++ b/CreamInstaller/CreamInstaller.csproj @@ -5,7 +5,7 @@ True Resources\ini.ico true - 3.2.3.1 + 3.3.0.0 Resources\ini.ico LICENSE 2021, pointfeev (https://github.com/pointfeev) @@ -42,6 +42,9 @@ + + Form + @@ -51,6 +54,9 @@ + + Designer + ResXFileCodeGenerator Resources.Designer.cs diff --git a/CreamInstaller/Epic/EpicLibrary.cs b/CreamInstaller/Epic/EpicLibrary.cs index 675cfea..9db641c 100644 --- a/CreamInstaller/Epic/EpicLibrary.cs +++ b/CreamInstaller/Epic/EpicLibrary.cs @@ -2,6 +2,7 @@ using System.IO; using System.Linq; using System.Text.Json; +using System.Threading; using System.Threading.Tasks; using Microsoft.Win32; @@ -31,6 +32,7 @@ internal static class EpicLibrary foreach (string file in files) { if (Program.Canceled) return games; + Thread.Sleep(0); string json = File.ReadAllText(file); try { @@ -58,6 +60,7 @@ internal static class EpicLibrary foreach (string _directory in directories) { if (Program.Canceled) return null; + Thread.Sleep(0); try { List moreDllDirectories = await GetDllDirectoriesFromGameDirectory(_directory); diff --git a/CreamInstaller/Forms/DialogForm.cs b/CreamInstaller/Forms/DialogForm.cs index ef294e8..4ce0447 100644 --- a/CreamInstaller/Forms/DialogForm.cs +++ b/CreamInstaller/Forms/DialogForm.cs @@ -30,7 +30,7 @@ internal partial class DialogForm : CustomForm return ShowDialog(); } - internal void OnResize(object s, EventArgs e) => + private void OnResize(object s, EventArgs e) => Text = TextRenderer.MeasureText(Program.ApplicationName, Font).Width > Size.Width - 100 ? Program.ApplicationNameShort : Program.ApplicationName; diff --git a/CreamInstaller/Forms/SelectDialogForm.Designer.cs b/CreamInstaller/Forms/SelectDialogForm.Designer.cs new file mode 100644 index 0000000..fb5a51a --- /dev/null +++ b/CreamInstaller/Forms/SelectDialogForm.Designer.cs @@ -0,0 +1,159 @@ + +namespace CreamInstaller +{ + partial class SelectDialogForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.acceptButton = new System.Windows.Forms.Button(); + this.groupBox = new System.Windows.Forms.GroupBox(); + this.selectionTreeView = new Components.CustomTreeView(); + this.flowLayoutPanel2 = new System.Windows.Forms.FlowLayoutPanel(); + this.allCheckBox = new System.Windows.Forms.CheckBox(); + this.cancelButton = new System.Windows.Forms.Button(); + this.groupBox.SuspendLayout(); + this.flowLayoutPanel2.SuspendLayout(); + this.SuspendLayout(); + // + // acceptButton + // + this.acceptButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.acceptButton.AutoSize = true; + this.acceptButton.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.acceptButton.DialogResult = System.Windows.Forms.DialogResult.OK; + this.acceptButton.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.acceptButton.Location = new System.Drawing.Point(360, 243); + this.acceptButton.Name = "acceptButton"; + this.acceptButton.Padding = new System.Windows.Forms.Padding(12, 0, 12, 0); + this.acceptButton.Size = new System.Drawing.Size(61, 24); + this.acceptButton.TabIndex = 0; + this.acceptButton.Text = "OK"; + this.acceptButton.UseVisualStyleBackColor = true; + // + // groupBox + // + this.groupBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.groupBox.Controls.Add(this.selectionTreeView); + this.groupBox.Controls.Add(this.flowLayoutPanel2); + this.groupBox.Location = new System.Drawing.Point(12, 12); + this.groupBox.MinimumSize = new System.Drawing.Size(240, 40); + this.groupBox.Name = "groupBox"; + this.groupBox.Size = new System.Drawing.Size(409, 225); + this.groupBox.TabIndex = 3; + this.groupBox.TabStop = false; + this.groupBox.Text = "Choices"; + // + // selectionTreeView + // + this.selectionTreeView.BackColor = System.Drawing.SystemColors.Control; + this.selectionTreeView.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.selectionTreeView.CheckBoxes = true; + this.selectionTreeView.Dock = System.Windows.Forms.DockStyle.Fill; + this.selectionTreeView.DrawMode = System.Windows.Forms.TreeViewDrawMode.OwnerDrawAll; + this.selectionTreeView.Location = new System.Drawing.Point(3, 19); + this.selectionTreeView.Name = "selectionTreeView"; + this.selectionTreeView.ShowLines = false; + this.selectionTreeView.ShowPlusMinus = false; + this.selectionTreeView.ShowRootLines = false; + this.selectionTreeView.Size = new System.Drawing.Size(403, 203); + this.selectionTreeView.Sorted = true; + this.selectionTreeView.TabIndex = 0; + // + // flowLayoutPanel2 + // + this.flowLayoutPanel2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.flowLayoutPanel2.AutoSize = true; + this.flowLayoutPanel2.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.flowLayoutPanel2.Controls.Add(this.allCheckBox); + this.flowLayoutPanel2.Location = new System.Drawing.Point(366, -1); + this.flowLayoutPanel2.Margin = new System.Windows.Forms.Padding(0); + this.flowLayoutPanel2.Name = "flowLayoutPanel2"; + this.flowLayoutPanel2.Size = new System.Drawing.Size(37, 19); + this.flowLayoutPanel2.TabIndex = 1007; + // + // allCheckBox + // + this.allCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.allCheckBox.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.allCheckBox.Location = new System.Drawing.Point(3, 0); + this.allCheckBox.Margin = new System.Windows.Forms.Padding(3, 0, 0, 0); + this.allCheckBox.Name = "allCheckBox"; + this.allCheckBox.Size = new System.Drawing.Size(34, 19); + this.allCheckBox.TabIndex = 1; + this.allCheckBox.Text = "All"; + this.allCheckBox.CheckedChanged += new System.EventHandler(this.OnAllCheckBoxChanged); + // + // cancelButton + // + this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancelButton.AutoSize = true; + this.cancelButton.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.cancelButton.Location = new System.Drawing.Point(12, 243); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Padding = new System.Windows.Forms.Padding(12, 0, 12, 0); + this.cancelButton.Size = new System.Drawing.Size(81, 24); + this.cancelButton.TabIndex = 4; + this.cancelButton.Text = "Cancel"; + this.cancelButton.UseVisualStyleBackColor = true; + // + // SelectDialogForm + // + this.AcceptButton = this.acceptButton; + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(433, 279); + this.Controls.Add(this.cancelButton); + this.Controls.Add(this.acceptButton); + this.Controls.Add(this.groupBox); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "SelectDialogForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "SelectDialogForm"; + this.TopMost = true; + this.groupBox.ResumeLayout(false); + this.groupBox.PerformLayout(); + this.flowLayoutPanel2.ResumeLayout(false); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + private System.Windows.Forms.Button acceptButton; + private System.Windows.Forms.GroupBox groupBox; + private Components.CustomTreeView selectionTreeView; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel2; + private System.Windows.Forms.CheckBox allCheckBox; + private System.Windows.Forms.Button cancelButton; + } +} \ No newline at end of file diff --git a/CreamInstaller/Forms/SelectDialogForm.cs b/CreamInstaller/Forms/SelectDialogForm.cs new file mode 100644 index 0000000..4728444 --- /dev/null +++ b/CreamInstaller/Forms/SelectDialogForm.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Windows.Forms; + +using CreamInstaller.Components; + +namespace CreamInstaller; + +internal partial class SelectDialogForm : CustomForm +{ + internal SelectDialogForm(IWin32Window owner) : base(owner) => InitializeComponent(); + + private readonly List<(string platform, string id, string name)> selected = new(); + internal List<(string platform, string id, string name)> QueryUser(string groupBoxText, List<(string platform, string id, string name, bool alreadySelected)> choices) + { + groupBox.Text = groupBoxText; + allCheckBox.Enabled = false; + acceptButton.Enabled = false; + selectionTreeView.AfterCheck += OnTreeNodeChecked; + foreach ((string platform, string id, string name, bool alreadySelected) in choices) + { + TreeNode node = new(); + node.Tag = platform; + node.Name = id; + node.Text = name; + node.Checked = alreadySelected; + OnTreeNodeChecked(node); + selectionTreeView.Nodes.Add(node); + } + allCheckBox.CheckedChanged -= OnAllCheckBoxChanged; + allCheckBox.Checked = selectionTreeView.Nodes.Cast().All(n => n.Checked); + allCheckBox.CheckedChanged += OnAllCheckBoxChanged; + allCheckBox.Enabled = true; + acceptButton.Enabled = selected.Any(); + OnResize(null, null); + Resize += OnResize; + return ShowDialog() == DialogResult.OK ? selected : null; + } + + private void OnTreeNodeChecked(object sender, TreeViewEventArgs e) + { + OnTreeNodeChecked(e.Node); + acceptButton.Enabled = selected.Any(); + } + + private void OnTreeNodeChecked(TreeNode node) + { + string id = node.Name; + if (node.Checked) + selected.Add((node.Tag as string, id, node.Text)); + else + selected.RemoveAll(s => s.id == id); + allCheckBox.CheckedChanged -= OnAllCheckBoxChanged; + allCheckBox.Checked = selectionTreeView.Nodes.Cast().All(n => n.Checked); + allCheckBox.CheckedChanged += OnAllCheckBoxChanged; + } + + private void OnResize(object s, EventArgs e) => + Text = TextRenderer.MeasureText(Program.ApplicationName, Font).Width > Size.Width - 100 + ? Program.ApplicationNameShort + : Program.ApplicationName; + + private void OnAllCheckBoxChanged(object sender, EventArgs e) + { + bool shouldCheck = false; + foreach (TreeNode node in selectionTreeView.Nodes) + if (!node.Checked) + shouldCheck = true; + foreach (TreeNode node in selectionTreeView.Nodes) + { + node.Checked = shouldCheck; + OnTreeNodeChecked(node); + } + allCheckBox.CheckedChanged -= OnAllCheckBoxChanged; + allCheckBox.Checked = shouldCheck; + allCheckBox.CheckedChanged += OnAllCheckBoxChanged; + } +} diff --git a/CreamInstaller/Forms/SelectDialogForm.resx b/CreamInstaller/Forms/SelectDialogForm.resx new file mode 100644 index 0000000..f298a7b --- /dev/null +++ b/CreamInstaller/Forms/SelectDialogForm.resx @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/CreamInstaller/Forms/SelectForm.cs b/CreamInstaller/Forms/SelectForm.cs index 9c2364f..2a96787 100644 --- a/CreamInstaller/Forms/SelectForm.cs +++ b/CreamInstaller/Forms/SelectForm.cs @@ -1,5 +1,4 @@ using System; -using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Drawing; @@ -82,6 +81,7 @@ internal partial class SelectForm : CustomForm private async Task GetApplicablePrograms(IProgress progress) { + if (ProgramsToScan is null || !ProgramsToScan.Any()) return; int TotalGameCount = 0; int CompleteGameCount = 0; void AddToRemainingGames(string gameName) @@ -99,7 +99,7 @@ internal partial class SelectForm : CustomForm RemainingGames.Clear(); // for display purposes only, otherwise ignorable RemainingDLCs.Clear(); // for display purposes only, otherwise ignorable List appTasks = new(); - if (Directory.Exists(ParadoxLauncher.InstallPath)) + if (Directory.Exists(ParadoxLauncher.InstallPath) && ProgramsToScan.Any(c => c.platform == "Paradox" && c.id == "ParadoxLauncher")) { ProgramSelection selection = ProgramSelection.FromId("ParadoxLauncher"); selection ??= new(); @@ -119,7 +119,7 @@ internal partial class SelectForm : CustomForm programNode.Remove(); selectionTreeView.Nodes.Add(programNode); } - if (Directory.Exists(SteamLibrary.InstallPath)) + if (Directory.Exists(SteamLibrary.InstallPath) && ProgramsToScan.Any(c => c.platform == "Steam")) { List> steamGames = await SteamLibrary.GetGames(); foreach (Tuple program in steamGames) @@ -129,10 +129,9 @@ internal partial class SelectForm : CustomForm string branch = program.Item3; int buildId = program.Item4; string directory = program.Item5; - Thread.Sleep(0); - ProgramSelection selection = ProgramSelection.FromId(appId); if (Program.Canceled) return; - if (Program.IsGameBlocked(name, directory)) continue; + Thread.Sleep(0); + if (Program.IsGameBlocked(name, directory) || !ProgramsToScan.Any(c => c.id == appId)) continue; AddToRemainingGames(name); Task task = Task.Run(async () => { @@ -162,6 +161,7 @@ internal partial class SelectForm : CustomForm foreach (string dlcAppId in dlcIds) { if (Program.Canceled) return; + Thread.Sleep(0); AddToRemainingDLCs(dlcAppId); Task task = Task.Run(async () => { @@ -209,7 +209,7 @@ internal partial class SelectForm : CustomForm await task; } - selection ??= new(); + ProgramSelection selection = ProgramSelection.FromId(appId) ?? new(); selection.Enabled = allCheckBox.Checked || selection.SelectedDlc.Any() || selection.ExtraDlc.Any(); selection.Usable = true; selection.Id = appId; @@ -255,7 +255,7 @@ internal partial class SelectForm : CustomForm appTasks.Add(task); } } - if (Directory.Exists(EpicLibrary.EpicAppDataPath)) + if (Directory.Exists(EpicLibrary.EpicAppDataPath) && ProgramsToScan.Any(c => c.platform == "Epic")) { List epicGames = await EpicLibrary.GetGames(); foreach (Manifest manifest in epicGames) @@ -263,10 +263,9 @@ internal partial class SelectForm : CustomForm string @namespace = manifest.CatalogNamespace; string name = manifest.DisplayName; string directory = manifest.InstallLocation; - Thread.Sleep(0); - ProgramSelection selection = ProgramSelection.FromId(@namespace); if (Program.Canceled) return; - if (Program.IsGameBlocked(name, directory)) continue; + Thread.Sleep(0); + if (Program.IsGameBlocked(name, directory) || !ProgramsToScan.Any(c => c.id == @namespace)) continue; AddToRemainingGames(name); Task task = Task.Run(async () => { @@ -292,6 +291,7 @@ internal partial class SelectForm : CustomForm Task task = Task.Run(() => { if (Program.Canceled) return; + Thread.Sleep(0); entitlements[id] = (name, product, icon, developer); RemoveFromRemainingDLCs(id); }); @@ -311,7 +311,7 @@ internal partial class SelectForm : CustomForm await task; } - selection ??= new(); + ProgramSelection selection = ProgramSelection.FromId(@namespace) ?? new(); selection.Enabled = allCheckBox.Checked || selection.SelectedDlc.Any() || selection.ExtraDlc.Any(); selection.Usable = true; selection.Id = @namespace; @@ -356,7 +356,7 @@ internal partial class SelectForm : CustomForm programNode.Nodes.Add(entitlementsNode);*/ foreach (KeyValuePair pair in entitlements) { - if (Program.Canceled || programNode is null/* || entitlementsNode is null*/) return; + if (programNode is null/* || entitlementsNode is null*/) return; Thread.Sleep(0); string dlcId = pair.Key; (DlcType type, string name, string icon) dlcApp = (DlcType.Entitlement, pair.Value.name, pair.Value.icon); @@ -384,7 +384,8 @@ internal partial class SelectForm : CustomForm } } - private async void OnLoad() + private List<(string platform, string id, string name)> ProgramsToScan; + private async void OnLoad(bool selectGamesToScan = false) { Program.Canceled = false; blockedGamesCheckBox.Enabled = false; @@ -396,8 +397,33 @@ internal partial class SelectForm : CustomForm installButton.Enabled = false; uninstallButton.Enabled = installButton.Enabled; selectionTreeView.Enabled = false; + progressLabel.Text = "Waiting for user to select which programs/games to scan . . ."; ShowProgressBar(); + if (ProgramsToScan is null || !ProgramsToScan.Any() || selectGamesToScan) + { + List<(string platform, string id, string name, bool alreadySelected)> gameChoices = new(); + if (Directory.Exists(ParadoxLauncher.InstallPath)) + gameChoices.Add(("Paradox", "ParadoxLauncher", "Paradox Launcher", ProgramsToScan is not null && ProgramsToScan.Any(p => p.id == "ParadoxLauncher"))); + if (Directory.Exists(SteamLibrary.InstallPath)) + foreach (Tuple program in await SteamLibrary.GetGames()) + if (!Program.IsGameBlocked(program.Item1, program.Item5)) + gameChoices.Add(("Steam", program.Item1, program.Item2, ProgramsToScan is not null && ProgramsToScan.Any(p => p.id == program.Item1))); + if (Directory.Exists(EpicLibrary.EpicAppDataPath)) + foreach (Manifest manifest in await EpicLibrary.GetGames()) + if (!Program.IsGameBlocked(manifest.DisplayName, manifest.InstallLocation)) + gameChoices.Add(("Epic", manifest.CatalogNamespace, manifest.DisplayName, ProgramsToScan is not null && ProgramsToScan.Any(p => p.id == manifest.CatalogNamespace))); + if (gameChoices.Any()) + { + using SelectDialogForm form = new(this); + List<(string platform, string id, string name)> choices = form.QueryUser("Choose which programs/games to scan:", gameChoices); + if (choices is not null) ProgramsToScan = choices; + noneFoundLabel.Text = "None of the chosen programs/games were CreamAPI-applicable or ScreamAPI-applicable!\n\nPress the \"Rescan Programs / Games\" button to re-choose."; + } + else + noneFoundLabel.Text = "No CreamAPI-applicable or ScreamAPI-applicable programs/games were found on your computer!"; + } + bool setup = true; int maxProgress = 0; int curProgress = 0; @@ -406,6 +432,7 @@ internal partial class SelectForm : CustomForm progress.ProgressChanged += (sender, _progress) => { if (Program.Canceled) return; + Thread.Sleep(0); if (_progress < 0 || _progress > maxProgress) maxProgress = -_progress; else curProgress = _progress; int p = Math.Max(Math.Min((int)((float)(curProgress / (float)maxProgress) * 100), 100), 0); @@ -415,14 +442,14 @@ internal partial class SelectForm : CustomForm }; await ProgramData.Setup(); - if (Directory.Exists(SteamLibrary.InstallPath)) + if (Directory.Exists(SteamLibrary.InstallPath) && ProgramsToScan is not null && ProgramsToScan.Any(c => c.platform == "Steam")) { progressLabel.Text = $"Setting up SteamCMD . . . "; await SteamCMD.Setup(iProgress); } setup = false; progressLabel.Text = "Gathering and caching your applicable games and their DLCs . . . "; - ProgramSelection.ValidateAll(); + ProgramSelection.ValidateAll(ProgramsToScan); TreeNodes.ForEach(node => node.Remove()); await GetApplicablePrograms(iProgress); await SteamCMD.Cleanup(); @@ -502,11 +529,6 @@ internal partial class SelectForm : CustomForm return treeNodes; } - private class TreeNodeSorter : IComparer - { - public int Compare(object a, object b) => AppIdComparer.Comparer.Compare((a as TreeNode).Name, (b as TreeNode).Name); - } - private void ShowProgressBar() { progressBar.Value = 0; @@ -543,7 +565,6 @@ internal partial class SelectForm : CustomForm try { HideProgressBar(); - selectionTreeView.TreeViewNodeSorter = new TreeNodeSorter(); selectionTreeView.AfterCheck += OnTreeViewNodeCheckedChanged; selectionTreeView.NodeMouseClick += (sender, e) => { @@ -649,7 +670,7 @@ internal partial class SelectForm : CustomForm } contextMenuStrip.Show(selectionTreeView, e.Location); }; - OnLoad(); + OnLoad(true); } catch (Exception e) { @@ -682,7 +703,7 @@ internal partial class SelectForm : CustomForm private void OnUninstall(object sender, EventArgs e) => OnAccept(true); - private void OnScan(object sender, EventArgs e) => OnLoad(); + private void OnScan(object sender, EventArgs e) => OnLoad(true); private void OnCancel(object sender, EventArgs e) { @@ -711,7 +732,7 @@ internal partial class SelectForm : CustomForm private void OnBlockProtectedGamesCheckBoxChanged(object sender, EventArgs e) { Program.BlockProtectedGames = blockedGamesCheckBox.Checked; - OnLoad(); + OnLoad(true); } private readonly string helpButtonListPrefix = "\n • "; diff --git a/CreamInstaller/Forms/SelectForm.resx b/CreamInstaller/Forms/SelectForm.resx index 97370c1..f298a7b 100644 --- a/CreamInstaller/Forms/SelectForm.resx +++ b/CreamInstaller/Forms/SelectForm.resx @@ -57,7 +57,4 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - 17, 17 - \ No newline at end of file diff --git a/CreamInstaller/Program.cs b/CreamInstaller/Program.cs index d3d9d52..95150f6 100644 --- a/CreamInstaller/Program.cs +++ b/CreamInstaller/Program.cs @@ -26,11 +26,11 @@ internal static class Program internal static readonly string[] ProtectedGameDirectories = { @"\EasyAntiCheat", @"\BattlEye" }; // DLL detections internal static readonly string[] ProtectedGameDirectoryExceptions = { "Arma 3" }; // Arma 3's BattlEye doesn't detect DLL changes? - internal static bool IsGameBlocked(string name, string directory) + internal static bool IsGameBlocked(string name, string directory = null) { if (!BlockProtectedGames) return false; if (ProtectedGameNames.Contains(name)) return true; - if (!ProtectedGameDirectoryExceptions.Contains(name)) + if (directory is not null && !ProtectedGameDirectoryExceptions.Contains(name)) foreach (string path in ProtectedGameDirectories) if (Directory.Exists(directory + path)) return true; return false; diff --git a/CreamInstaller/ProgramSelection.cs b/CreamInstaller/ProgramSelection.cs index e81263e..57f1b69 100644 --- a/CreamInstaller/ProgramSelection.cs +++ b/CreamInstaller/ProgramSelection.cs @@ -101,8 +101,20 @@ internal class ProgramSelection if (!DllDirectories.Any()) All.Remove(this); } + internal void Validate(List<(string platform, string id, string name)> programsToScan) + { + if (programsToScan is null || !programsToScan.Any(p => p.id == Id)) + { + All.Remove(this); + return; + } + Validate(); + } + internal static void ValidateAll() => AllSafe.ForEach(selection => selection.Validate()); + internal static void ValidateAll(List<(string platform, string id, string name)> programsToScan) => AllSafe.ForEach(selection => selection.Validate(programsToScan)); + internal static List All = new(); internal static List AllSafe => All.ToList(); diff --git a/CreamInstaller/Steam/SteamCMD.cs b/CreamInstaller/Steam/SteamCMD.cs index e3f4ece..4ea4e4e 100644 --- a/CreamInstaller/Steam/SteamCMD.cs +++ b/CreamInstaller/Steam/SteamCMD.cs @@ -36,9 +36,11 @@ internal static class SteamCMD { wait_for_lock: if (Program.Canceled) return ""; + Thread.Sleep(0); for (int i = 0; i < locks.Length; i++) { if (Program.Canceled) return ""; + Thread.Sleep(0); if (Interlocked.CompareExchange(ref locks[i], 1, 0) == 0) { if (appId is not null) @@ -107,7 +109,7 @@ internal static class SteamCMD Interlocked.Decrement(ref locks[i]); return appInfo; } - Thread.Sleep(0); + Thread.Sleep(200); } Thread.Sleep(200); goto wait_for_lock; @@ -121,11 +123,10 @@ internal static class SteamCMD await Cleanup(); if (!File.Exists(FilePath)) { - using (HttpClient httpClient = new()) - { - byte[] file = await httpClient.GetByteArrayAsync(new Uri("https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip")); - file.Write(ArchivePath); - } + HttpClient httpClient = HttpClientManager.HttpClient; + if (httpClient is null) return; + byte[] file = await httpClient.GetByteArrayAsync(new Uri("https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip")); + file.Write(ArchivePath); ZipFile.ExtractToDirectory(ArchivePath, DirectoryPath); File.Delete(ArchivePath); } diff --git a/CreamInstaller/Steam/SteamLibrary.cs b/CreamInstaller/Steam/SteamLibrary.cs index 3bcb8ec..bdb6c53 100644 --- a/CreamInstaller/Steam/SteamLibrary.cs +++ b/CreamInstaller/Steam/SteamLibrary.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; using Gameloop.Vdf.Linq; @@ -30,6 +31,8 @@ internal static class SteamLibrary List gameLibraryDirectories = await GetLibraryDirectories(); foreach (string libraryDirectory in gameLibraryDirectories) { + if (Program.Canceled) return games; + Thread.Sleep(0); List> directoryGames = await GetGamesFromLibraryDirectory(libraryDirectory); if (directoryGames is not null) foreach (Tuple game in directoryGames) @@ -54,6 +57,7 @@ internal static class SteamLibrary foreach (string _directory in directories) { if (Program.Canceled) return null; + Thread.Sleep(0); try { List moreDllDirectories = await GetDllDirectoriesFromGameDirectory(_directory); @@ -72,6 +76,7 @@ internal static class SteamLibrary foreach (string file in files) { if (Program.Canceled) return null; + Thread.Sleep(0); if (ValveDataFile.TryDeserialize(File.ReadAllText(file, Encoding.UTF8), out VProperty result)) { string appId = result.Value.GetChild("appid")?.ToString(); diff --git a/CreamInstaller/Utility/HttpClientManager.cs b/CreamInstaller/Utility/HttpClientManager.cs index 7dc9961..0a9e6c8 100644 --- a/CreamInstaller/Utility/HttpClientManager.cs +++ b/CreamInstaller/Utility/HttpClientManager.cs @@ -14,7 +14,7 @@ internal static class HttpClientManager internal static void Setup() { HttpClient = new(); - HttpClient.DefaultRequestHeaders.Add("User-Agent", $"{Environment.MachineName}CI{Application.ProductVersion.Replace(".", "")}{Environment.UserName}"); + HttpClient.DefaultRequestHeaders.Add("User-Agent", $"CI{Application.ProductVersion.Replace(".", "")}"); } internal static async Task EnsureGet(string url)