v4.9.2
- 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:
parent
1669f8c887
commit
e333483b8f
11 changed files with 177 additions and 259 deletions
|
@ -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 & Configuration Generator</Product>
|
||||
|
|
|
@ -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}.");
|
||||
|
|
4
CreamInstaller/Forms/SelectForm.Designer.cs
generated
4
CreamInstaller/Forms/SelectForm.Designer.cs
generated
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue