From be6a3966dc2d36d321581c13e36c3b5d923056bc Mon Sep 17 00:00:00 2001 From: pointfeev Date: Fri, 18 Mar 2022 19:04:49 -0400 Subject: [PATCH] v3.4.0.0 - Added more files to SteamCMD cleanup - Added a save and load feature to the new pre-scan choices dialog - Re-enabled ReadyToRun compilation - Moved the program to 64-bit - Added Notepad++ support, so the program opens query files with it if it exists - Cancelling the pre-scan choices dialog now cancels scanning - The pre-scan choices dialog now doesn't show at all if no installed games are found on the system - Fixed the installation form cancel button - Selection form now no longer scans at unnecessary locations (e.g. after installation form re-select) - Large code refactoring --- CreamInstaller/CreamInstaller.csproj | 2 +- CreamInstaller/Epic/EpicStore.cs | 2 +- CreamInstaller/Forms/InstallForm.cs | 20 ++- .../Forms/SelectDialogForm.Designer.cs | 38 +++++ CreamInstaller/Forms/SelectDialogForm.cs | 27 ++++ CreamInstaller/Forms/SelectForm.Designer.cs | 43 ++--- CreamInstaller/Forms/SelectForm.cs | 148 ++++++++++-------- CreamInstaller/Paradox/ParadoxLauncher.cs | 4 +- CreamInstaller/ProgramSelection.cs | 27 ++-- CreamInstaller/Steam/SteamCMD.cs | 16 ++ CreamInstaller/Steam/SteamStore.cs | 2 +- CreamInstaller/Utility/Diagnostics.cs | 9 +- CreamInstaller/Utility/ProgramData.cs | 25 +++ 13 files changed, 246 insertions(+), 117 deletions(-) diff --git a/CreamInstaller/CreamInstaller.csproj b/CreamInstaller/CreamInstaller.csproj index a965909..6768131 100644 --- a/CreamInstaller/CreamInstaller.csproj +++ b/CreamInstaller/CreamInstaller.csproj @@ -5,7 +5,7 @@ True Resources\ini.ico true - 3.3.0.2 + 3.4.0.0 Resources\ini.ico LICENSE 2021, pointfeev (https://github.com/pointfeev) diff --git a/CreamInstaller/Epic/EpicStore.cs b/CreamInstaller/Epic/EpicStore.cs index 31a71cc..2acc802 100644 --- a/CreamInstaller/Epic/EpicStore.cs +++ b/CreamInstaller/Epic/EpicStore.cs @@ -28,7 +28,7 @@ internal static class EpicStore { List<(string id, string name, string product, string icon, string developer)> dlcIds = new(); string cacheFile = ProgramData.AppInfoPath + @$"\{categoryNamespace}.json"; - bool cachedExists = Directory.Exists(Directory.GetDirectoryRoot(cacheFile)) && File.Exists(cacheFile); + bool cachedExists = File.Exists(cacheFile); Response response = null; if (!cachedExists || ProgramData.CheckCooldown(categoryNamespace, COOLDOWN_ENTITLEMENT)) { diff --git a/CreamInstaller/Forms/InstallForm.cs b/CreamInstaller/Forms/InstallForm.cs index 92620cd..226bc1b 100644 --- a/CreamInstaller/Forms/InstallForm.cs +++ b/CreamInstaller/Forms/InstallForm.cs @@ -285,17 +285,20 @@ internal partial class InstallForm : CustomForm int cur = 0; foreach (string directory in selection.DllDirectories) { - UpdateUser($"{(Uninstalling ? "Uninstalling" : "Installing")} {(selection.IsSteam ? "CreamAPI" : "ScreamAPI")}" + + string platform = selection.Platform == Platform.Steam ? "CreamAPI" + : selection.Platform == Platform.Epic ? "ScreamAPI" + : throw new InvalidPlatformException(selection.Platform); + UpdateUser($"{(Uninstalling ? "Uninstalling" : "Installing")} {platform}" + $" {(Uninstalling ? "from" : "for")} " + selection.Name + $" in directory \"{directory}\" . . . ", InstallationLog.Operation); - if (!Program.IsProgramRunningDialog(this, selection)) throw new OperationCanceledException(); - if (selection.IsSteam) + if (Program.Canceled || !Program.IsProgramRunningDialog(this, selection)) throw new CustomMessageException("The operation was canceled."); + if (platform == "CreamAPI") { if (Uninstalling) await UninstallCreamAPI(directory, this); else await InstallCreamAPI(directory, selection, this); } - else + else if (platform == "ScreamAPI") { if (Uninstalling) await UninstallScreamAPI(directory, this); @@ -309,13 +312,13 @@ internal partial class InstallForm : CustomForm private async Task Operate() { - List programSelections = ProgramSelection.AllUsableEnabled; + List programSelections = ProgramSelection.AllEnabled; OperationsCount = programSelections.Count; CompleteOperationsCount = 0; List disabledSelections = new(); foreach (ProgramSelection selection in programSelections) { - if (!Program.IsProgramRunningDialog(this, selection)) throw new OperationCanceledException(); + if (Program.Canceled || !Program.IsProgramRunningDialog(this, selection)) throw new CustomMessageException("The operation was canceled."); try { await OperateFor(selection); @@ -330,17 +333,18 @@ internal partial class InstallForm : CustomForm ++CompleteOperationsCount; } Program.Cleanup(); - List FailedSelections = ProgramSelection.AllUsableEnabled; + List FailedSelections = ProgramSelection.AllEnabled; if (FailedSelections.Any()) 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) selection.Enabled = true; } - private readonly int ProgramCount = ProgramSelection.AllUsableEnabled.Count; + private readonly int ProgramCount = ProgramSelection.AllEnabled.Count; private async void Start() { + Program.Canceled = false; acceptButton.Enabled = false; retryButton.Enabled = false; cancelButton.Enabled = true; diff --git a/CreamInstaller/Forms/SelectDialogForm.Designer.cs b/CreamInstaller/Forms/SelectDialogForm.Designer.cs index 35fdbed..47ac284 100644 --- a/CreamInstaller/Forms/SelectDialogForm.Designer.cs +++ b/CreamInstaller/Forms/SelectDialogForm.Designer.cs @@ -35,6 +35,8 @@ namespace CreamInstaller this.flowLayoutPanel2 = new System.Windows.Forms.FlowLayoutPanel(); this.allCheckBox = new System.Windows.Forms.CheckBox(); this.cancelButton = new System.Windows.Forms.Button(); + this.loadButton = new System.Windows.Forms.Button(); + this.saveButton = new System.Windows.Forms.Button(); this.groupBox.SuspendLayout(); this.flowLayoutPanel2.SuspendLayout(); this.SuspendLayout(); @@ -124,12 +126,46 @@ namespace CreamInstaller this.cancelButton.Text = "Cancel"; this.cancelButton.UseVisualStyleBackColor = true; // + // loadButton + // + this.loadButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.loadButton.AutoSize = true; + this.loadButton.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.loadButton.Enabled = false; + this.loadButton.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.loadButton.Location = new System.Drawing.Point(283, 243); + this.loadButton.Name = "loadButton"; + this.loadButton.Padding = new System.Windows.Forms.Padding(12, 0, 12, 0); + this.loadButton.Size = new System.Drawing.Size(71, 24); + this.loadButton.TabIndex = 4; + this.loadButton.Text = "Load"; + this.loadButton.UseVisualStyleBackColor = true; + this.loadButton.Click += new System.EventHandler(this.OnLoad); + // + // saveButton + // + this.saveButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.saveButton.AutoSize = true; + this.saveButton.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.saveButton.Enabled = false; + this.saveButton.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.saveButton.Location = new System.Drawing.Point(208, 243); + this.saveButton.Name = "saveButton"; + this.saveButton.Padding = new System.Windows.Forms.Padding(12, 0, 12, 0); + this.saveButton.Size = new System.Drawing.Size(69, 24); + this.saveButton.TabIndex = 5; + this.saveButton.Text = "Save"; + this.saveButton.UseVisualStyleBackColor = true; + this.saveButton.Click += new System.EventHandler(this.OnSave); + // // 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.saveButton); + this.Controls.Add(this.loadButton); this.Controls.Add(this.cancelButton); this.Controls.Add(this.acceptButton); this.Controls.Add(this.groupBox); @@ -155,5 +191,7 @@ namespace CreamInstaller private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel2; private System.Windows.Forms.CheckBox allCheckBox; private System.Windows.Forms.Button cancelButton; + private System.Windows.Forms.Button loadButton; + private System.Windows.Forms.Button saveButton; } } \ No newline at end of file diff --git a/CreamInstaller/Forms/SelectDialogForm.cs b/CreamInstaller/Forms/SelectDialogForm.cs index 4728444..369d52a 100644 --- a/CreamInstaller/Forms/SelectDialogForm.cs +++ b/CreamInstaller/Forms/SelectDialogForm.cs @@ -1,10 +1,12 @@ using System; using System.Collections.Generic; using System.Drawing; +using System.IO; using System.Linq; using System.Windows.Forms; using CreamInstaller.Components; +using CreamInstaller.Utility; namespace CreamInstaller; @@ -15,6 +17,7 @@ internal partial class SelectDialogForm : CustomForm 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) { + if (!choices.Any()) return null; groupBox.Text = groupBoxText; allCheckBox.Enabled = false; acceptButton.Enabled = false; @@ -29,11 +32,14 @@ internal partial class SelectDialogForm : CustomForm OnTreeNodeChecked(node); selectionTreeView.Nodes.Add(node); } + if (!selected.Any()) 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(); + saveButton.Enabled = acceptButton.Enabled; + loadButton.Enabled = File.Exists(ProgramData.ChoicesPath); OnResize(null, null); Resize += OnResize; return ShowDialog() == DialogResult.OK ? selected : null; @@ -43,6 +49,7 @@ internal partial class SelectDialogForm : CustomForm { OnTreeNodeChecked(e.Node); acceptButton.Enabled = selected.Any(); + saveButton.Enabled = acceptButton.Enabled; } private void OnTreeNodeChecked(TreeNode node) @@ -77,4 +84,24 @@ internal partial class SelectDialogForm : CustomForm allCheckBox.Checked = shouldCheck; allCheckBox.CheckedChanged += OnAllCheckBoxChanged; } + + private void OnLoad(object sender, EventArgs e) + { + List choices = ProgramData.ReadChoices(); + foreach (TreeNode node in selectionTreeView.Nodes) + { + node.Checked = choices.Contains(node.Name); + OnTreeNodeChecked(node); + } + } + + private void OnSave(object sender, EventArgs e) + { + List choices = new(); + foreach (TreeNode node in selectionTreeView.Nodes) + if (node.Checked) + choices.Add(node.Name); + ProgramData.WriteChoices(choices); + loadButton.Enabled = File.Exists(ProgramData.ChoicesPath); + } } diff --git a/CreamInstaller/Forms/SelectForm.Designer.cs b/CreamInstaller/Forms/SelectForm.Designer.cs index 44a3f82..3ae7217 100644 --- a/CreamInstaller/Forms/SelectForm.Designer.cs +++ b/CreamInstaller/Forms/SelectForm.Designer.cs @@ -1,8 +1,6 @@  using System.Windows.Forms; -using CreamInstaller.Components; - namespace CreamInstaller { partial class SelectForm @@ -33,7 +31,6 @@ namespace CreamInstaller /// private void InitializeComponent() { - this.components = new System.ComponentModel.Container(); this.installButton = new System.Windows.Forms.Button(); this.cancelButton = new System.Windows.Forms.Button(); this.label1 = new System.Windows.Forms.Label(); @@ -42,7 +39,7 @@ namespace CreamInstaller this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); this.blockedGamesCheckBox = new System.Windows.Forms.CheckBox(); this.blockProtectedHelpButton = new System.Windows.Forms.Button(); - this.selectionTreeView = new CustomTreeView(); + this.selectionTreeView = new CreamInstaller.Components.CustomTreeView(); this.flowLayoutPanel2 = new System.Windows.Forms.FlowLayoutPanel(); this.allCheckBox = new System.Windows.Forms.CheckBox(); this.progressBar = new System.Windows.Forms.ProgressBar(); @@ -161,17 +158,17 @@ namespace CreamInstaller // // selectionTreeView // - this.selectionTreeView.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.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.Enabled = false; this.selectionTreeView.FullRowSelect = true; - this.selectionTreeView.Location = new System.Drawing.Point(6, 22); + this.selectionTreeView.Location = new System.Drawing.Point(3, 19); this.selectionTreeView.Name = "selectionTreeView"; - this.selectionTreeView.Size = new System.Drawing.Size(548, 280); + this.selectionTreeView.Size = new System.Drawing.Size(554, 218); + this.selectionTreeView.Sorted = true; this.selectionTreeView.TabIndex = 1001; // // flowLayoutPanel2 @@ -189,6 +186,8 @@ namespace CreamInstaller // allCheckBox // this.allCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.allCheckBox.Checked = true; + this.allCheckBox.CheckState = System.Windows.Forms.CheckState.Checked; this.allCheckBox.Enabled = false; this.allCheckBox.FlatStyle = System.Windows.Forms.FlatStyle.System; this.allCheckBox.Location = new System.Drawing.Point(3, 0); @@ -199,28 +198,12 @@ namespace CreamInstaller this.allCheckBox.Text = "All"; this.allCheckBox.CheckedChanged += new System.EventHandler(this.OnAllCheckBoxChanged); // - // selectionTreeView - // - this.selectionTreeView.Dock = System.Windows.Forms.DockStyle.Fill; - this.selectionTreeView.Location = new System.Drawing.Point(3, 19); - this.selectionTreeView.Size = new System.Drawing.Size(554, 218); - 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.Enabled = false; - this.selectionTreeView.FullRowSelect = true; - this.selectionTreeView.LineColor = System.Drawing.Color.Empty; - this.selectionTreeView.Name = "selectionTreeView"; - this.selectionTreeView.TabIndex = 1001; - // - // progressBar1 + // progressBar // this.progressBar.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.progressBar.Location = new System.Drawing.Point(12, 297); - this.progressBar.Name = "progressBar1"; + this.progressBar.Name = "progressBar"; this.progressBar.Size = new System.Drawing.Size(560, 23); this.progressBar.TabIndex = 9; // @@ -271,13 +254,13 @@ namespace CreamInstaller this.progressLabelGames.TabIndex = 11; this.progressLabelGames.Text = "Remaining games (2): Game 1, Game 2"; // - // progressLabelDLC + // progressLabelDLCs // this.progressLabelDLCs.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.progressLabelDLCs.Font = new System.Drawing.Font("Segoe UI", 7F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); this.progressLabelDLCs.Location = new System.Drawing.Point(12, 282); - this.progressLabelDLCs.Name = "progressLabelDLC"; + this.progressLabelDLCs.Name = "progressLabelDLCs"; this.progressLabelDLCs.Size = new System.Drawing.Size(560, 12); this.progressLabelDLCs.TabIndex = 10004; this.progressLabelDLCs.Text = "Remaining DLC (2): 123456, 654321"; @@ -326,7 +309,7 @@ namespace CreamInstaller private System.Windows.Forms.CheckBox allCheckBox; private Button scanButton; private Label noneFoundLabel; - private CustomTreeView selectionTreeView; + private CreamInstaller.Components.CustomTreeView selectionTreeView; private CheckBox blockedGamesCheckBox; private Button blockProtectedHelpButton; private FlowLayoutPanel flowLayoutPanel1; diff --git a/CreamInstaller/Forms/SelectForm.cs b/CreamInstaller/Forms/SelectForm.cs index 8cacf7d..8e73041 100644 --- a/CreamInstaller/Forms/SelectForm.cs +++ b/CreamInstaller/Forms/SelectForm.cs @@ -101,23 +101,28 @@ internal partial class SelectForm : CustomForm List appTasks = new(); if (Directory.Exists(ParadoxLauncher.InstallPath) && ProgramsToScan.Any(c => c.platform == "Paradox" && c.id == "ParadoxLauncher")) { - ProgramSelection selection = ProgramSelection.FromId("ParadoxLauncher"); - selection ??= new(); - if (allCheckBox.Checked) selection.Enabled = true; - selection.Usable = true; - selection.Id = "ParadoxLauncher"; - selection.Name = "Paradox Launcher"; - selection.RootDirectory = ParadoxLauncher.InstallPath; - List steamDllDirectories = await SteamLibrary.GetDllDirectoriesFromGameDirectory(selection.RootDirectory); - selection.DllDirectories = steamDllDirectories ?? await EpicLibrary.GetDllDirectoriesFromGameDirectory(selection.RootDirectory); - selection.IsSteam = steamDllDirectories is not null; + List steamDllDirectories = await SteamLibrary.GetDllDirectoriesFromGameDirectory(ParadoxLauncher.InstallPath); + List epicDllDirectories = null; + if (steamDllDirectories is null) + epicDllDirectories = await EpicLibrary.GetDllDirectoriesFromGameDirectory(ParadoxLauncher.InstallPath); + if (steamDllDirectories is not null || epicDllDirectories is not null) + { + ProgramSelection selection = ProgramSelection.FromId("ParadoxLauncher"); + selection ??= new(); + if (allCheckBox.Checked) selection.Enabled = true; + selection.Id = "ParadoxLauncher"; + selection.Name = "Paradox Launcher"; + selection.RootDirectory = ParadoxLauncher.InstallPath; + selection.DllDirectories = steamDllDirectories ?? epicDllDirectories; + selection.Platform = steamDllDirectories is not null ? Platform.Steam : Platform.Epic; - TreeNode programNode = treeNodes.Find(s => s.Name == selection.Id) ?? new(); - programNode.Name = selection.Id; - programNode.Text = selection.Name; - programNode.Checked = selection.Enabled; - programNode.Remove(); - selectionTreeView.Nodes.Add(programNode); + TreeNode programNode = treeNodes.Find(s => s.Name == selection.Id) ?? new(); + programNode.Name = selection.Id; + programNode.Text = selection.Name; + programNode.Checked = selection.Enabled; + programNode.Remove(); + selectionTreeView.Nodes.Add(programNode); + } } if (Directory.Exists(SteamLibrary.InstallPath) && ProgramsToScan.Any(c => c.platform == "Steam")) { @@ -211,12 +216,11 @@ internal partial class SelectForm : CustomForm ProgramSelection selection = ProgramSelection.FromId(appId) ?? new(); selection.Enabled = allCheckBox.Checked || selection.SelectedDlc.Any() || selection.ExtraDlc.Any(); - selection.Usable = true; + selection.Platform = Platform.Steam; selection.Id = appId; selection.Name = appData?.name ?? name; selection.RootDirectory = directory; selection.DllDirectories = dllDirectories; - selection.IsSteam = true; selection.ProductUrl = "https://store.steampowered.com/app/" + appId; selection.IconUrl = IconGrabber.SteamAppImagesPath + @$"\{appId}\{appInfo?.Value?.GetChild("common")?.GetChild("icon")?.ToString()}.jpg"; selection.SubIconUrl = appData?.header_image ?? IconGrabber.SteamAppImagesPath + @$"\{appId}\{appInfo?.Value?.GetChild("common")?.GetChild("clienticon")?.ToString()}.ico"; @@ -313,7 +317,7 @@ internal partial class SelectForm : CustomForm ProgramSelection selection = ProgramSelection.FromId(@namespace) ?? new(); selection.Enabled = allCheckBox.Checked || selection.SelectedDlc.Any() || selection.ExtraDlc.Any(); - selection.Usable = true; + selection.Platform = Platform.Epic; selection.Id = @namespace; selection.Name = name; selection.RootDirectory = directory; @@ -385,7 +389,7 @@ internal partial class SelectForm : CustomForm } private List<(string platform, string id, string name)> ProgramsToScan; - private async void OnLoad(bool selectGamesToScan = false) + private async void OnLoad(bool forceScan = false, bool forceProvideChoices = false) { Program.Canceled = false; blockedGamesCheckBox.Enabled = false; @@ -400,7 +404,8 @@ internal partial class SelectForm : CustomForm progressLabel.Text = "Waiting for user to select which programs/games to scan . . ."; ShowProgressBar(); - if (ProgramsToScan is null || !ProgramsToScan.Any() || selectGamesToScan) + bool scan = forceScan; + if (!scan && (ProgramsToScan is null || !ProgramsToScan.Any() || forceProvideChoices)) { List<(string platform, string id, string name, bool alreadySelected)> gameChoices = new(); if (Directory.Exists(ParadoxLauncher.InstallPath)) @@ -417,48 +422,58 @@ internal partial class SelectForm : CustomForm { using SelectDialogForm form = new(this); List<(string platform, string id, string name)> choices = form.QueryUser("Choose which programs/games to scan for DLC:", 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."; + scan = choices is not null && choices.Any(); + string retry = "\n\nPress the \"Rescan Programs / Games\" button to re-choose."; + if (scan) + { + ProgramsToScan = choices; + noneFoundLabel.Text = "None of the chosen programs/games were CreamAPI-applicable or ScreamAPI-applicable!" + retry; + } + else + noneFoundLabel.Text = "You didn't choose any programs/games!" + retry; } 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; - Progress progress = new(); - IProgress iProgress = progress; - progress.ProgressChanged += (sender, _progress) => + if (scan) { - if (Program.Canceled) return; + bool setup = true; + int maxProgress = 0; + int curProgress = 0; + Progress progress = new(); + IProgress iProgress = progress; + 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); + progressLabel.Text = setup ? $"Setting up SteamCMD . . . {p}%" + : $"Gathering and caching your applicable games and their DLCs . . . {p}%"; + progressBar.Value = p; + }; + await ProgramData.Setup(); + 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(ProgramsToScan); + TreeNodes.ForEach(node => node.Remove()); 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); - progressLabel.Text = setup ? $"Setting up SteamCMD . . . {p}%" - : $"Gathering and caching your applicable games and their DLCs . . . {p}%"; - progressBar.Value = p; - }; - - await ProgramData.Setup(); - if (Directory.Exists(SteamLibrary.InstallPath) && ProgramsToScan is not null && ProgramsToScan.Any(c => c.platform == "Steam")) - { - progressLabel.Text = $"Setting up SteamCMD . . . "; - await SteamCMD.Setup(iProgress); + await GetApplicablePrograms(iProgress); + await SteamCMD.Cleanup(); } - setup = false; - progressLabel.Text = "Gathering and caching your applicable games and their DLCs . . . "; - ProgramSelection.ValidateAll(ProgramsToScan); - TreeNodes.ForEach(node => node.Remove()); - await GetApplicablePrograms(iProgress); - await SteamCMD.Cleanup(); HideProgressBar(); selectionTreeView.Enabled = ProgramSelection.All.Any(); allCheckBox.Enabled = selectionTreeView.Enabled; noneFoundLabel.Visible = !selectionTreeView.Enabled; - installButton.Enabled = ProgramSelection.AllUsableEnabled.Any(); + installButton.Enabled = ProgramSelection.AllEnabled.Any(); uninstallButton.Enabled = installButton.Enabled; cancelButton.Enabled = false; scanButton.Enabled = true; @@ -477,7 +492,7 @@ internal partial class SelectForm : CustomForm allCheckBox.CheckedChanged -= OnAllCheckBoxChanged; allCheckBox.Checked = TreeNodes.TrueForAll(treeNode => treeNode.Checked); allCheckBox.CheckedChanged += OnAllCheckBoxChanged; - installButton.Enabled = ProgramSelection.AllUsableEnabled.Any(); + installButton.Enabled = ProgramSelection.AllEnabled.Any(); uninstallButton.Enabled = installButton.Enabled; } @@ -592,12 +607,17 @@ internal partial class SelectForm : CustomForm string appInfoVDF = $@"{SteamCMD.AppInfoPath}\{id}.vdf"; string appInfoJSON = $@"{SteamCMD.AppInfoPath}\{id}.json"; string cooldown = $@"{ProgramData.CooldownPath}\{id}.txt"; - if (Directory.Exists(Directory.GetDirectoryRoot(appInfoVDF)) && (File.Exists(appInfoVDF) || File.Exists(appInfoJSON))) + if ((File.Exists(appInfoVDF) || File.Exists(appInfoJSON)) && (selection is not null || dlc is not null)) { List queries = new(); if (File.Exists(appInfoJSON)) - queries.Add(new ContextMenuItem((selection is null || selection.IsSteam) ? "Open Steam Store Query" : "Open Epic GraphQL Query", "Notepad", + { + string platform = (selection is null || selection.Platform == Platform.Steam) ? "Steam Store" + : selection.Platform == Platform.Epic ? "Epic GraphQL" + : throw new InvalidPlatformException(selection.Platform); + queries.Add(new ContextMenuItem($"Open {platform} Query", "Notepad", new EventHandler((sender, e) => Diagnostics.OpenFileInNotepad(appInfoJSON)))); + } if (File.Exists(appInfoVDF)) queries.Add(new ContextMenuItem("Open SteamCMD Query", "Notepad", new EventHandler((sender, e) => Diagnostics.OpenFileInNotepad(appInfoVDF)))); @@ -624,7 +644,7 @@ internal partial class SelectForm : CustomForm File.Delete(cooldown); } catch { } - OnLoad(); + OnLoad(forceScan: true); }))); } } @@ -640,16 +660,20 @@ internal partial class SelectForm : CustomForm contextMenuStrip.Items.Add(new ContextMenuItem("Open Root Directory", "File Explorer", new EventHandler((sender, e) => Diagnostics.OpenDirectoryInFileExplorer(selection.RootDirectory)))); List directories = selection.DllDirectories.ToList(); + string platform = selection.Platform == Platform.Steam ? "Steamworks" + : selection.Platform == Platform.Epic ? "Epic Online Services" + : throw new InvalidPlatformException(selection.Platform); for (int i = 0; i < directories.Count; i++) { string directory = directories[i]; - contextMenuStrip.Items.Add(new ContextMenuItem($"Open {(selection.IsSteam ? "Steamworks" : "Epic Online Services")} SDK Directory #{i + 1}", "File Explorer", + contextMenuStrip.Items.Add(new ContextMenuItem($"Open {platform} SDK Directory #{i + 1}", "File Explorer", new EventHandler((sender, e) => Diagnostics.OpenDirectoryInFileExplorer(directory)))); } } if (id != "ParadoxLauncher") { - if (selection is not null && selection.IsSteam || dlcParentSelection is not null && dlcParentSelection.IsSteam) + if (selection is not null && selection.Platform == Platform.Steam + || dlcParentSelection is not null && dlcParentSelection.Platform == Platform.Steam) { contextMenuStrip.Items.Add(new ToolStripSeparator()); contextMenuStrip.Items.Add(new ContextMenuItem("Open SteamDB", "SteamDB", @@ -657,14 +681,14 @@ internal partial class SelectForm : CustomForm } if (selection is not null) { - if (selection.IsSteam) + if (selection.Platform == Platform.Steam) { contextMenuStrip.Items.Add(new ContextMenuItem("Open Steam Store", "Steam Store", new EventHandler((sender, e) => Diagnostics.OpenUrlInInternetBrowser(selection.ProductUrl)))); contextMenuStrip.Items.Add(new ContextMenuItem("Open Steam Community", (id, selection.SubIconUrl, true), "Steam Community", new EventHandler((sender, e) => Diagnostics.OpenUrlInInternetBrowser("https://steamcommunity.com/app/" + id)))); } - else + else if (selection.Platform == Platform.Epic) { contextMenuStrip.Items.Add(new ToolStripSeparator()); contextMenuStrip.Items.Add(new ContextMenuItem("Open ScreamDB", "ScreamDB", @@ -676,7 +700,7 @@ internal partial class SelectForm : CustomForm } contextMenuStrip.Show(selectionTreeView, e.Location); }; - OnLoad(true); + OnLoad(forceProvideChoices: true); } catch (Exception e) { @@ -689,7 +713,7 @@ internal partial class SelectForm : CustomForm { if (ProgramSelection.All.Any()) { - foreach (ProgramSelection selection in ProgramSelection.AllUsableEnabled) + foreach (ProgramSelection selection in ProgramSelection.AllEnabled) if (!Program.IsProgramRunningDialog(this, selection)) return; if (ParadoxLauncher.DlcDialog(this)) return; Hide(); @@ -709,7 +733,7 @@ internal partial class SelectForm : CustomForm private void OnUninstall(object sender, EventArgs e) => OnAccept(true); - private void OnScan(object sender, EventArgs e) => OnLoad(true); + private void OnScan(object sender, EventArgs e) => OnLoad(forceProvideChoices: true); private void OnCancel(object sender, EventArgs e) { @@ -738,7 +762,7 @@ internal partial class SelectForm : CustomForm private void OnBlockProtectedGamesCheckBoxChanged(object sender, EventArgs e) { Program.BlockProtectedGames = blockedGamesCheckBox.Checked; - OnLoad(true); + OnLoad(forceProvideChoices: true); } private readonly string helpButtonListPrefix = "\n • "; diff --git a/CreamInstaller/Paradox/ParadoxLauncher.cs b/CreamInstaller/Paradox/ParadoxLauncher.cs index 316eecd..78a826f 100644 --- a/CreamInstaller/Paradox/ParadoxLauncher.cs +++ b/CreamInstaller/Paradox/ParadoxLauncher.cs @@ -29,13 +29,13 @@ internal static class ParadoxLauncher if (paradoxLauncher is not null) { paradoxLauncher.ExtraDlc.Clear(); - foreach (ProgramSelection selection in ProgramSelection.AllUsableEnabled) + foreach (ProgramSelection selection in ProgramSelection.AllEnabled) { if (selection == paradoxLauncher || selection.Publisher != "Paradox Interactive") continue; paradoxLauncher.ExtraDlc.Add(new(selection.Id, selection.Name, selection.SelectedDlc)); } if (!paradoxLauncher.ExtraDlc.Any()) - foreach (ProgramSelection selection in ProgramSelection.AllUsable) + foreach (ProgramSelection selection in ProgramSelection.AllSafe) { if (selection == paradoxLauncher || selection.Publisher != "Paradox Interactive") continue; paradoxLauncher.ExtraDlc.Add(new(selection.Id, selection.Name, selection.AllDlc)); diff --git a/CreamInstaller/ProgramSelection.cs b/CreamInstaller/ProgramSelection.cs index 57f1b69..3b8be85 100644 --- a/CreamInstaller/ProgramSelection.cs +++ b/CreamInstaller/ProgramSelection.cs @@ -7,7 +7,20 @@ using CreamInstaller.Components; namespace CreamInstaller; -internal enum DlcType +public enum Platform +{ + Steam = 0, + Epic = 1 +} +public class InvalidPlatformException : Exception +{ + public InvalidPlatformException() : base("Invalid platform!") { } + public InvalidPlatformException(Platform platform) : base($"Invalid platform ({platform})!") { } + public InvalidPlatformException(string message) : base(message) { } + public InvalidPlatformException(string message, Exception innerException) : base(message, innerException) { } +} + +public enum DlcType { Default = 0, CatalogItem = 1, @@ -17,7 +30,7 @@ internal enum DlcType internal class ProgramSelection { internal bool Enabled; - internal bool Usable = true; + internal Platform Platform = (Platform)(-1); internal string Id = "0"; internal string Name = "Program"; @@ -31,8 +44,6 @@ internal class ProgramSelection internal string RootDirectory; internal List DllDirectories; - internal bool IsSteam; - internal readonly SortedList AllDlc = new(AppIdComparer.Comparer); internal readonly SortedList SelectedDlc = new(AppIdComparer.Comparer); internal readonly List>> ExtraDlc = new(); // for Paradox Launcher @@ -119,15 +130,13 @@ internal class ProgramSelection internal static List AllSafe => All.ToList(); - internal static List AllUsable => All.FindAll(s => s.Usable); + internal static List AllEnabled => AllSafe.FindAll(s => s.Enabled); - internal static List AllUsableEnabled => AllUsable.FindAll(s => s.Enabled); - - internal static ProgramSelection FromId(string gameId) => AllUsable.Find(s => s.Id == gameId); + internal static ProgramSelection FromId(string gameId) => AllSafe.Find(s => s.Id == gameId); internal static (string gameId, (DlcType type, string name, string icon) app)? GetDlcFromId(string dlcId) { - foreach (ProgramSelection selection in AllUsable) + foreach (ProgramSelection selection in AllSafe) foreach (KeyValuePair pair in selection.AllDlc) if (pair.Key == dlcId) return (selection.Id, pair.Value); return null; diff --git a/CreamInstaller/Steam/SteamCMD.cs b/CreamInstaller/Steam/SteamCMD.cs index 4ea4e4e..44b0047 100644 --- a/CreamInstaller/Steam/SteamCMD.cs +++ b/CreamInstaller/Steam/SteamCMD.cs @@ -147,6 +147,7 @@ internal static class SteamCMD } internal static readonly string AppCachePath = DirectoryPath + @"\appcache"; + internal static readonly string ConfigPath = DirectoryPath + @"\config"; internal static readonly string DumpsPath = DirectoryPath + @"\dumps"; internal static readonly string LogsPath = DirectoryPath + @"\logs"; internal static readonly string SteamAppsPath = DirectoryPath + @"\steamapps"; @@ -156,6 +157,21 @@ internal static class SteamCMD if (!Directory.Exists(DirectoryPath)) return; await Kill(); try + { + string[] ntfsFiles = Directory.GetFiles(DirectoryPath, "*.ntfs_transaction_failed"); + foreach (string file in ntfsFiles) File.Delete(file); + } + catch { } + try + { + if (Directory.Exists(ConfigPath)) + { + string[] tmpFiles = Directory.GetFiles(ConfigPath, "*.tmp"); + foreach (string file in tmpFiles) File.Delete(file); + } + } + catch { } + try { string[] oldFiles = Directory.GetFiles(DirectoryPath, "*.old"); foreach (string file in oldFiles) File.Delete(file); diff --git a/CreamInstaller/Steam/SteamStore.cs b/CreamInstaller/Steam/SteamStore.cs index c2a1227..9b06e4b 100644 --- a/CreamInstaller/Steam/SteamStore.cs +++ b/CreamInstaller/Steam/SteamStore.cs @@ -27,7 +27,7 @@ internal static class SteamStore { if (Program.Canceled) return null; string cacheFile = ProgramData.AppInfoPath + @$"\{appId}.json"; - bool cachedExists = Directory.Exists(Directory.GetDirectoryRoot(cacheFile)) && File.Exists(cacheFile); + bool cachedExists = File.Exists(cacheFile); if (!cachedExists || ProgramData.CheckCooldown(appId, isDlc ? COOLDOWN_DLC : COOLDOWN_GAME)) { string response = await HttpClientManager.EnsureGet($"https://store.steampowered.com/api/appdetails?appids={appId}"); diff --git a/CreamInstaller/Utility/Diagnostics.cs b/CreamInstaller/Utility/Diagnostics.cs index fabc564..fc8c9b4 100644 --- a/CreamInstaller/Utility/Diagnostics.cs +++ b/CreamInstaller/Utility/Diagnostics.cs @@ -19,13 +19,16 @@ internal static class Diagnostics } } - internal static string GetNotepadPath() => NotepadPlusPlusPath is not null ? NotepadPlusPlusPath + @"\notepad++.exe" - : Environment.GetFolderPath(Environment.SpecialFolder.Windows) + @"\notepad.exe"; + internal static string GetNotepadPath() + { + string npp = NotepadPlusPlusPath + @"\notepad++.exe"; + return File.Exists(npp) ? npp : Environment.GetFolderPath(Environment.SpecialFolder.Windows) + @"\notepad.exe"; + } internal static void OpenFileInNotepad(string path) { string npp = NotepadPlusPlusPath + @"\notepad++.exe"; - if (Directory.Exists(NotepadPlusPlusPath) && File.Exists(npp)) + if (File.Exists(npp)) OpenFileInNotepadPlusPlus(npp, path); else OpenFileInWindowsNotepad(path); diff --git a/CreamInstaller/Utility/ProgramData.cs b/CreamInstaller/Utility/ProgramData.cs index 9228e51..95e769b 100644 --- a/CreamInstaller/Utility/ProgramData.cs +++ b/CreamInstaller/Utility/ProgramData.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; @@ -18,6 +20,8 @@ internal static class ProgramData internal static readonly string CooldownPath = DirectoryPath + @"\cooldown"; + internal static readonly string ChoicesPath = DirectoryPath + @"\choices.txt"; + internal static async Task Setup() => await Task.Run(() => { if (Directory.Exists(DirectoryPathOld)) @@ -73,4 +77,25 @@ internal static class ProgramData } catch { } } + + internal static List ReadChoices() + { + if (!File.Exists(ChoicesPath)) return new(); + try + { + return File.ReadAllLines(ChoicesPath).ToList(); + } + catch + { + return new(); + } + } + internal static void WriteChoices(List choices) + { + try + { + File.WriteAllLines(ChoicesPath, choices.ToArray()); + } + catch { } + } }