- More significant refactoring & optimization
- Tethered tree nodes to selection objects for optimization & to further reduce the chance of duplicate nodes
- Fixed a minor steam game gathering check that may have been letting through duplicate games
- Fixed a few selection form functions from the refactoring & optimization in v4.9.0
- Fixed the Koaloader reset button not updating after toggling the Koaloader all check box
This commit is contained in:
pointfeev 2023-05-29 15:57:12 -04:00
parent 1669f8c887
commit e333483b8f
11 changed files with 177 additions and 259 deletions

View file

@ -4,7 +4,7 @@
<TargetFramework>net7.0-windows</TargetFramework>
<UseWindowsForms>True</UseWindowsForms>
<ApplicationIcon>Resources\ini.ico</ApplicationIcon>
<Version>4.9.1</Version>
<Version>4.9.2</Version>
<Copyright>2021, pointfeev (https://github.com/pointfeev)</Copyright>
<Company>CreamInstaller</Company>
<Product>Automatic DLC Unlocker Installer &amp; Configuration Generator</Product>

View file

@ -16,7 +16,7 @@ internal sealed partial class InstallForm : CustomForm
{
private readonly HashSet<Selection> 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<Selection> programSelections = Selection.AllEnabled;
HashSet<Selection> 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<Selection> failedSelections = Selection.AllEnabled;
HashSet<Selection> failedSelections = Selection.AllEnabled.ToHashSet();
if (failedSelections.Count > 0)
if (failedSelections.Count == 1)
throw new CustomMessageException($"Operation failed for {failedSelections.First().Name}.");

View file

@ -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;

View file

@ -25,21 +25,29 @@ internal sealed partial class SelectForm : CustomForm
{
private const string HelpButtonListPrefix = "\n • ";
private static SelectForm current;
private readonly ConcurrentDictionary<string, string> remainingDLCs = new();
private readonly ConcurrentDictionary<string, string> 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<TreeNode> 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<string, string> 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<TreeNode> treeNodes = TreeNodes;
remainingGames.Clear(); // for display purposes only, otherwise ignorable
remainingDLCs.Clear(); // for display purposes only, otherwise ignorable
List<Task> 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<TreeNode>().Any(childNode => childNode.Checked);
node = parentNode;
continue;
}
break;
}
TreeNode parentNode = node.Parent;
if (parentNode is null)
return;
parentNode.Checked = parentNode.Nodes.Cast<TreeNode>().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<TreeNode> 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<TreeNode> GatherTreeNodes(TreeNodeCollection nodeCollection)
{
List<TreeNode> 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;
}

View file

@ -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();

View file

@ -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);
}

View file

@ -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;

View file

@ -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;
});

View file

@ -38,15 +38,9 @@ internal static class Program
internal static readonly string[] ProtectedGameDirectoryExceptions = Array.Empty<string>();
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

View file

@ -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<Selection>
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<Selection>
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<SelectionDLC> DLC => SelectionDLC.AllSafe.Where(dlc => dlc.Selection.Equals(this));
internal static IEnumerable<Selection> AllEnabled => All.Keys.Where(s => s.Enabled);
internal bool Enabled { get => TreeNode.Checked; set => TreeNode.Checked = value; }
internal IEnumerable<SelectionDLC> DLC => SelectionDLC.All.Keys.Where(dlc => dlc.Selection.Equals(this));
internal bool AreDllsLocked
{
@ -92,10 +103,6 @@ internal sealed class Selection : IEquatable<Selection>
}
}
internal static HashSet<Selection> AllSafe => All.Keys.ToHashSet();
internal static HashSet<Selection> 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<Selection>
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<Selection>
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);

View file

@ -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<SelectionDLC>
{
private static readonly ConcurrentDictionary<SelectionDLC, byte> All = new();
internal static readonly ConcurrentDictionary<SelectionDLC, byte> 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<SelectionDLC>
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<SelectionDLC> 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);