- Hash & concurrency fixes to stop duplicate games & DLC
- Threading fixes for significantly faster operations
- More significant refactoring to prevent possible issues
This commit is contained in:
pointfeev 2023-05-28 21:20:35 -04:00
parent 0974f78889
commit 1669f8c887
22 changed files with 263 additions and 272 deletions

View file

@ -76,16 +76,24 @@ internal sealed class CustomTreeView : TreeView
Form form = FindForm();
if (form is not SelectForm and not SelectDialogForm)
return;
string platformId = node.Name;
string id = node.Name;
Platform platform = (node.Tag as Platform?).GetValueOrDefault(Platform.None);
if (string.IsNullOrWhiteSpace(platformId) || platform is Platform.None)
DLCType dlcType = (node.Tag as DLCType?).GetValueOrDefault(DLCType.None);
if (string.IsNullOrWhiteSpace(id) || platform is Platform.None && dlcType is DLCType.None)
return;
Color color = highlighted
? C1
: Enabled
? C2
: C3;
string text = platform.ToString();
string text;
if (dlcType is not DLCType.None)
{
SelectionDLC dlc = SelectionDLC.FromTypeId(dlcType, id);
text = dlc is not null ? dlc.Selection.Platform.ToString() : dlcType.ToString();
}
else
text = platform.ToString();
Size size = TextRenderer.MeasureText(graphics, text, font);
bounds = bounds with { X = bounds.X + bounds.Width, Width = size.Width };
selectionBounds = new(selectionBounds.Location, selectionBounds.Size + bounds.Size with { Height = 0 });
@ -99,7 +107,7 @@ internal sealed class CustomTreeView : TreeView
: Enabled
? C5
: C6;
text = platformId;
text = id;
size = TextRenderer.MeasureText(graphics, text, font);
const int left = -4;
bounds = bounds with { X = bounds.X + bounds.Width + left, Width = size.Width };
@ -110,7 +118,7 @@ internal sealed class CustomTreeView : TreeView
}
if (form is SelectForm)
{
Selection selection = Selection.FromPlatformId(platform, platformId);
Selection selection = Selection.FromPlatformId(platform, id);
if (selection is not null)
{
if (bounds == node.Bounds)
@ -188,15 +196,15 @@ internal sealed class CustomTreeView : TreeView
return;
if (comboBoxBounds.Count > 0 && selectForm is not null)
foreach (KeyValuePair<Selection, Rectangle> pair in comboBoxBounds)
if (!Selection.All.Contains(pair.Key))
if (!Selection.All.ContainsKey(pair.Key))
_ = comboBoxBounds.Remove(pair.Key);
else if (pair.Value.Contains(clickPoint))
{
List<string> proxies = EmbeddedResources.FindAll(r => r.StartsWith("Koaloader", StringComparison.Ordinal)).Select(p =>
HashSet<string> proxies = EmbeddedResources.Where(r => r.StartsWith("Koaloader", StringComparison.Ordinal)).Select(p =>
{
p.GetProxyInfoFromIdentifier(out string proxyName, out _);
return proxyName;
}).Distinct().ToList();
}).ToHashSet();
comboBoxDropDown ??= new();
comboBoxDropDown.ShowItemToolTips = false;
comboBoxDropDown.Items.Clear();
@ -222,7 +230,7 @@ internal sealed class CustomTreeView : TreeView
break;
}
foreach (KeyValuePair<Selection, Rectangle> pair in checkBoxBounds)
if (!Selection.All.Contains(pair.Key))
if (!Selection.All.ContainsKey(pair.Key))
_ = checkBoxBounds.Remove(pair.Key);
else if (pair.Value.Contains(clickPoint))
{

View file

@ -8,12 +8,10 @@ namespace CreamInstaller.Components;
internal static class PlatformIdComparer
{
private static StringComparer stringComparer;
private static NodeComparer nodeComparer;
private static NodeNameComparer nodeNameComparer;
private static NodeTextComparer nodeTextComparer;
internal static StringComparer String => stringComparer ??= new();
internal static NodeComparer Node => nodeComparer ??= new();
internal static NodeNameComparer NodeName => nodeNameComparer ??= new();
@ -39,15 +37,17 @@ internal sealed class StringComparer : IComparer<string>
internal sealed class NodeComparer : IComparer<TreeNode>
{
public int Compare(TreeNode a, TreeNode b)
=> a?.Tag is not Platform A
=> a is null
? 1
: b?.Tag is not Platform B
: b is null
? -1
: A > B
? 1
: A < B
? -1
: 0;
: a.Tag is not Platform pA || b.Tag is not Platform pB
? 0
: pA > pB
? 1
: pA < pB
? -1
: 0;
}
internal sealed class NodeNameComparer : IComparer

View file

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

View file

@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using CreamInstaller.Components;
using CreamInstaller.Resources;
@ -15,7 +14,7 @@ namespace CreamInstaller.Forms;
internal sealed partial class InstallForm : CustomForm
{
private readonly List<Selection> disabledSelections = new();
private readonly HashSet<Selection> disabledSelections = new();
private readonly int programCount = Selection.AllEnabled.Count;
private readonly bool uninstalling;
@ -87,7 +86,6 @@ internal sealed partial class InstallForm : CustomForm
UpdateUser("Uninstalling Koaloader from " + selection.Name + $" in incorrect directory \"{directory}\" . . . ", LogTextBox.Operation);
await Koaloader.Uninstall(directory, selection.RootDirectory, this);
}
Thread.Sleep(1);
}
if (uninstalling || !selection.Koaloader)
foreach ((string directory, BinaryType _) in selection.ExecutableDirectories)
@ -101,7 +99,6 @@ internal sealed partial class InstallForm : CustomForm
UpdateUser("Uninstalling Koaloader from " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);
await Koaloader.Uninstall(directory, selection.RootDirectory, this);
}
Thread.Sleep(1);
}
bool uninstallProxy = uninstalling || selection.Koaloader;
int count = selection.DllDirectories.Count, cur = 0;
@ -173,7 +170,6 @@ internal sealed partial class InstallForm : CustomForm
}
}
UpdateProgress(++cur / count * 100);
Thread.Sleep(1);
}
if (selection.Koaloader && !uninstalling)
foreach ((string directory, BinaryType binaryType) in selection.ExecutableDirectories)
@ -182,14 +178,13 @@ internal sealed partial class InstallForm : CustomForm
throw new CustomMessageException("The operation was canceled.");
UpdateUser("Installing Koaloader to " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);
await Koaloader.Install(directory, binaryType, selection, selection.RootDirectory, this);
Thread.Sleep(1);
}
UpdateProgress(100);
}
private async Task Operate()
{
List<Selection> programSelections = Selection.AllEnabled;
HashSet<Selection> programSelections = Selection.AllEnabled;
operationsCount = programSelections.Count;
completeOperationsCount = 0;
foreach (Selection selection in programSelections)
@ -201,7 +196,7 @@ internal sealed partial class InstallForm : CustomForm
await OperateFor(selection);
UpdateUser($"Operation succeeded for {selection.Name}.", LogTextBox.Success);
selection.Enabled = false;
disabledSelections.Add(selection);
_ = disabledSelections.Add(selection);
}
catch (Exception exception)
{
@ -210,7 +205,7 @@ internal sealed partial class InstallForm : CustomForm
++completeOperationsCount;
}
Program.Cleanup();
List<Selection> failedSelections = Selection.AllEnabled;
HashSet<Selection> failedSelections = Selection.AllEnabled;
if (failedSelections.Count > 0)
if (failedSelections.Count == 1)
throw new CustomMessageException($"Operation failed for {failedSelections.First().Name}.");

View file

@ -2,6 +2,7 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
@ -128,30 +129,16 @@ internal sealed partial class SelectForm : CustomForm
HashSet<string> dllDirectories = await ParadoxLauncher.InstallPath.GetDllDirectoriesFromGameDirectory(Platform.Paradox);
if (dllDirectories is not null)
{
Selection selection = Selection.GetOrCreate(Platform.Paradox, "PL", "Paradox Launcher", ParadoxLauncher.InstallPath, dllDirectories,
await ParadoxLauncher.InstallPath.GetExecutableDirectories(validFunc: path => !Path.GetFileName(path).Contains("bootstrapper")));
if (uninstallAll)
{
Selection bareSelection = Selection.FromPlatformId(Platform.Paradox, "PL") ?? new();
bareSelection.Enabled = true;
bareSelection.Id = "PL";
bareSelection.Name = "Paradox Launcher";
bareSelection.RootDirectory = ParadoxLauncher.InstallPath;
bareSelection.ExecutableDirectories = await ParadoxLauncher.GetExecutableDirectories(bareSelection.RootDirectory);
bareSelection.DllDirectories = dllDirectories;
bareSelection.Platform = Platform.Paradox;
}
selection.Enabled = true;
else
{
Selection selection = Selection.FromPlatformId(Platform.Paradox, "PL") ?? new();
if (allCheckBox.Checked)
selection.Enabled = true;
if (koaloaderAllCheckBox.Checked)
selection.Koaloader = true;
selection.Id = "PL";
selection.Name = "Paradox Launcher";
selection.RootDirectory = ParadoxLauncher.InstallPath;
selection.ExecutableDirectories = await ParadoxLauncher.GetExecutableDirectories(selection.RootDirectory);
selection.DllDirectories = dllDirectories;
selection.Platform = Platform.Paradox;
TreeNode programNode = treeNodes.Find(s => s.Tag is Platform.Paradox && s.Name == selection.Id) ?? new TreeNode();
programNode.Tag = selection.Platform;
programNode.Name = selection.Id;
@ -166,7 +153,7 @@ internal sealed partial class SelectForm : CustomForm
int steamGamesToCheck;
if (uninstallAll || programsToScan.Any(c => c.platform is Platform.Steam))
{
HashSet<(string appId, string name, string branch, int buildId, string gameDirectory)> steamGames = await SteamLibrary.GetGames();
List<(string appId, string name, string branch, int buildId, string gameDirectory)> steamGames = await SteamLibrary.GetGames();
steamGamesToCheck = steamGames.Count;
foreach ((string appId, string name, string branch, int buildId, string gameDirectory) in steamGames)
{
@ -191,14 +178,9 @@ internal sealed partial class SelectForm : CustomForm
}
if (uninstallAll)
{
Selection bareSelection = Selection.FromPlatformId(Platform.Steam, appId) ?? new Selection();
Selection bareSelection = Selection.GetOrCreate(Platform.Steam, appId, name, gameDirectory, dllDirectories,
await gameDirectory.GetExecutableDirectories(true));
bareSelection.Enabled = true;
bareSelection.Id = appId;
bareSelection.Name = name;
bareSelection.RootDirectory = gameDirectory;
bareSelection.ExecutableDirectories = await SteamLibrary.GetExecutableDirectories(bareSelection.RootDirectory);
bareSelection.DllDirectories = dllDirectories;
bareSelection.Platform = Platform.Steam;
RemoveFromRemainingGames(name);
return;
}
@ -214,14 +196,15 @@ internal sealed partial class SelectForm : CustomForm
}
if (Program.Canceled)
return;
ConcurrentDictionary<string, SelectionDLC> dlc = new();
ConcurrentDictionary<SelectionDLC, byte> dlc = new();
List<Task> dlcTasks = new();
List<string> dlcIds = new();
HashSet<string> dlcIds = new();
if (appData is not null)
dlcIds.AddRange(await SteamStore.ParseDlcAppIds(appData));
foreach (string dlcId in await SteamStore.ParseDlcAppIds(appData))
_ = dlcIds.Add(dlcId);
if (appInfo is not null)
dlcIds.AddRange(await SteamCMD.ParseDlcAppIds(appInfo));
dlcIds = dlcIds.Distinct().ToList();
foreach (string dlcId in await SteamCMD.ParseDlcAppIds(appInfo))
_ = dlcIds.Add(dlcId);
if (dlcIds.Count > 0)
foreach (string dlcAppId in dlcIds)
{
@ -291,20 +274,20 @@ internal sealed partial class SelectForm : CustomForm
if (Program.Canceled)
return;
if (!string.IsNullOrWhiteSpace(fullGameName))
dlc[fullGameAppId] = new()
{
Id = fullGameAppId, Type = fullGameOnSteamStore ? DLCType.Steam : DLCType.SteamHidden, Name = fullGameName,
Icon = fullGameIcon
};
{
SelectionDLC fullGameDlc = SelectionDLC.GetOrCreate(fullGameOnSteamStore ? DLCType.Steam : DLCType.SteamHidden,
fullGameAppId, fullGameName);
fullGameDlc.Icon = fullGameIcon;
_ = dlc.TryAdd(fullGameDlc, default);
}
}
if (Program.Canceled)
return;
if (string.IsNullOrWhiteSpace(dlcName))
dlcName = "Unknown";
dlc[dlcAppId] = new()
{
Id = dlcAppId, Type = onSteamStore ? DLCType.Steam : DLCType.SteamHidden, Name = dlcName, Icon = dlcIcon
};
SelectionDLC _dlc = SelectionDLC.GetOrCreate(onSteamStore ? DLCType.Steam : DLCType.SteamHidden, dlcAppId, dlcName);
_dlc.Icon = dlcIcon;
_ = dlc.TryAdd(_dlc, default);
RemoveFromRemainingDLCs(dlcAppId);
});
dlcTasks.Add(task);
@ -328,17 +311,12 @@ internal sealed partial class SelectForm : CustomForm
RemoveFromRemainingGames(name);
return;
}
Selection selection = Selection.FromPlatformId(Platform.Steam, appId) ?? new Selection();
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.Id = appId;
selection.Name = appData?.Name ?? name;
selection.RootDirectory = gameDirectory;
selection.ExecutableDirectories = await SteamLibrary.GetExecutableDirectories(selection.RootDirectory);
selection.DllDirectories = dllDirectories;
selection.Platform = Platform.Steam;
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
@ -351,21 +329,21 @@ internal sealed partial class SelectForm : CustomForm
{
if (Program.Canceled)
return;
TreeNode programNode = treeNodes.Find(s => s.Tag is Platform.Steam && s.Name == appId) ?? new TreeNode();
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);
foreach ((_, SelectionDLC dlc) in dlc)
foreach ((SelectionDLC dlc, _) in dlc)
{
if (Program.Canceled)
return;
dlc.Selection = selection;
dlc.Enabled = dlc.Name != "Unknown" && allCheckBox.Checked;
TreeNode dlcNode = treeNodes.Find(s => s.Tag is Platform.Steam && s.Name == dlc.Id) ?? new TreeNode();
dlcNode.Tag = selection.Platform;
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;
@ -382,7 +360,7 @@ internal sealed partial class SelectForm : CustomForm
}
if (uninstallAll || programsToScan.Any(c => c.platform is Platform.Epic))
{
HashSet<Manifest> epicGames = await EpicLibrary.GetGames();
List<Manifest> epicGames = await EpicLibrary.GetGames();
foreach (Manifest manifest in epicGames)
{
string @namespace = manifest.CatalogNamespace;
@ -405,22 +383,17 @@ internal sealed partial class SelectForm : CustomForm
}
if (uninstallAll)
{
Selection bareSelection = Selection.FromPlatformId(Platform.Epic, @namespace) ?? new Selection();
Selection bareSelection = Selection.GetOrCreate(Platform.Epic, @namespace, name, directory, dllDirectories,
await directory.GetExecutableDirectories(true));
bareSelection.Enabled = true;
bareSelection.Id = @namespace;
bareSelection.Name = name;
bareSelection.RootDirectory = directory;
bareSelection.ExecutableDirectories = await EpicLibrary.GetExecutableDirectories(bareSelection.RootDirectory);
bareSelection.DllDirectories = dllDirectories;
bareSelection.Platform = Platform.Epic;
RemoveFromRemainingGames(name);
return;
}
if (Program.Canceled)
return;
ConcurrentDictionary<string, SelectionDLC> catalogItems = new();
ConcurrentDictionary<SelectionDLC, byte> catalogItems = new();
// get catalog items
ConcurrentDictionary<string, SelectionDLC> entitlements = new();
ConcurrentDictionary<SelectionDLC, byte> entitlements = new();
List<Task> dlcTasks = new();
List<(string id, string name, string product, string icon, string developer)>
entitlementIds = await EpicStore.QueryEntitlements(@namespace);
@ -434,11 +407,11 @@ internal sealed partial class SelectForm : CustomForm
{
if (Program.Canceled)
return;
entitlements[id] = new()
{
Id = id, Name = name, Product = product, Icon = icon,
Publisher = developer
};
SelectionDLC entitlement = SelectionDLC.GetOrCreate(DLCType.EpicEntitlement, id, name);
entitlement.Icon = icon;
entitlement.Product = product;
entitlement.Publisher = developer;
_ = entitlements.TryAdd(entitlement, default);
RemoveFromRemainingDLCs(id);
});
dlcTasks.Add(task);
@ -456,24 +429,19 @@ internal sealed partial class SelectForm : CustomForm
RemoveFromRemainingGames(name);
return;
}
Selection selection = Selection.FromPlatformId(Platform.Epic, @namespace) ?? new Selection();
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;
selection.Id = @namespace;
selection.Name = name;
selection.RootDirectory = directory;
selection.ExecutableDirectories = await EpicLibrary.GetExecutableDirectories(selection.RootDirectory);
selection.DllDirectories = dllDirectories;
selection.Platform = Platform.Epic;
foreach (KeyValuePair<string, SelectionDLC> dlc in entitlements.Where(dlc => dlc.Value.Name == selection.Name))
foreach ((SelectionDLC dlc, _) in entitlements.Where(dlc => dlc.Key.Name == selection.Name))
{
if (Program.Canceled)
return;
selection.Product = "https://www.epicgames.com/store/product/" + dlc.Value.Product;
selection.Icon = dlc.Value.Icon;
selection.Publisher = dlc.Value.Publisher;
selection.Product = "https://www.epicgames.com/store/product/" + dlc.Product;
selection.Icon = dlc.Icon;
selection.Publisher = dlc.Publisher;
}
if (Program.Canceled)
return;
@ -481,7 +449,7 @@ internal sealed partial class SelectForm : CustomForm
{
if (Program.Canceled)
return;
TreeNode programNode = treeNodes.Find(s => s.Tag is Platform.Epic && s.Name == @namespace) ?? new TreeNode();
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;
@ -489,20 +457,21 @@ internal sealed partial class SelectForm : CustomForm
if (programNode.TreeView is null)
_ = selectionTreeView.Nodes.Add(programNode);
if (!catalogItems.IsEmpty)
/*TreeNode catalogItemsNode = treeNodes.Find(node => node.Tag is Platform.Epic && node.Name == @namespace + "_catalogItems") ?? new();
/*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)
foreach ((SelectionDLC dlc, _) in catalogItems)
{
if (Program.Canceled)
return;
dlc.Selection = selection;
dlc.Enabled = allCheckBox.Checked;
TreeNode dlcNode = treeNodes.Find(s => s.Tag is Platform.Epic && s.Name == dlc.Id) ?? new TreeNode();
dlcNode.Tag = selection.Platform;
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;
@ -511,20 +480,21 @@ internal sealed partial class SelectForm : CustomForm
}
if (entitlements.IsEmpty)
return;
/*TreeNode entitlementsNode = treeNodes.Find(node => node.Tag is Platform.Epic && node.Name == @namespace + "_entitlements") ?? new();
/*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)
foreach ((SelectionDLC dlc, _) in entitlements)
{
if (Program.Canceled)
return;
dlc.Selection = selection;
dlc.Enabled = allCheckBox.Checked;
TreeNode dlcNode = treeNodes.Find(s => s.Tag is Platform.Epic && s.Name == dlc.Id) ?? new TreeNode();
dlcNode.Tag = selection.Platform;
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;
@ -541,7 +511,7 @@ internal sealed partial class SelectForm : CustomForm
}
if (uninstallAll || programsToScan.Any(c => c.platform is Platform.Ubisoft))
{
HashSet<(string gameId, string name, string gameDirectory)> ubisoftGames = await UbisoftLibrary.GetGames();
List<(string gameId, string name, string gameDirectory)> ubisoftGames = await UbisoftLibrary.GetGames();
foreach ((string gameId, string name, string gameDirectory) in ubisoftGames)
{
if (Program.Canceled)
@ -561,36 +531,26 @@ internal sealed partial class SelectForm : CustomForm
}
if (uninstallAll)
{
Selection bareSelection = Selection.FromPlatformId(Platform.Ubisoft, gameId) ?? new Selection();
Selection bareSelection = Selection.GetOrCreate(Platform.Ubisoft, gameId, name, gameDirectory, dllDirectories,
await gameDirectory.GetExecutableDirectories(true));
bareSelection.Enabled = true;
bareSelection.Id = gameId;
bareSelection.Name = name;
bareSelection.RootDirectory = gameDirectory;
bareSelection.ExecutableDirectories = await UbisoftLibrary.GetExecutableDirectories(bareSelection.RootDirectory);
bareSelection.DllDirectories = dllDirectories;
bareSelection.Platform = Platform.Ubisoft;
RemoveFromRemainingGames(name);
return;
}
if (Program.Canceled)
return;
Selection selection = Selection.FromPlatformId(Platform.Ubisoft, gameId) ?? new Selection();
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.Id = gameId;
selection.Name = name;
selection.RootDirectory = gameDirectory;
selection.ExecutableDirectories = await UbisoftLibrary.GetExecutableDirectories(selection.RootDirectory);
selection.DllDirectories = dllDirectories;
selection.Platform = Platform.Ubisoft;
selection.Icon = IconGrabber.GetDomainFaviconUrl("store.ubi.com");
selectionTreeView.Invoke(delegate
{
if (Program.Canceled)
return;
TreeNode programNode = treeNodes.Find(s => s.Tag is Platform.Ubisoft && s.Name == gameId) ?? new TreeNode();
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;
@ -738,7 +698,7 @@ internal sealed partial class SelectForm : CustomForm
OnLoadDlc(null, null);
OnLoadKoaloader(null, null);
HideProgressBar();
selectionTreeView.Enabled = Selection.All.Count > 0;
selectionTreeView.Enabled = !Selection.All.IsEmpty;
allCheckBox.Enabled = selectionTreeView.Enabled;
koaloaderAllCheckBox.Enabled = selectionTreeView.Enabled;
noneFoundLabel.Visible = !selectionTreeView.Enabled;
@ -791,14 +751,16 @@ internal sealed partial class SelectForm : CustomForm
}
private static void SyncNodeDescendants(TreeNode node)
=> node.Nodes.Cast<TreeNode>().ToList().ForEach(childNode =>
{
foreach (TreeNode childNode in node.Nodes)
{
if (childNode.Text == "Unknown")
return;
childNode.Checked = node.Checked;
SyncNode(childNode);
SyncNodeDescendants(childNode);
});
}
}
private static void SyncNode(TreeNode node)
{
@ -810,7 +772,8 @@ internal sealed partial class SelectForm : CustomForm
selection.Enabled = node.Checked;
return;
}
SelectionDLC dlc = SelectionDLC.FromPlatformId(platform, id);
DLCType type = (DLCType)node.Tag;
SelectionDLC dlc = SelectionDLC.FromTypeId(type, id);
if (dlc is not null)
dlc.Enabled = node.Checked;
}
@ -867,10 +830,10 @@ internal sealed partial class SelectForm : CustomForm
Selection selection = Selection.FromPlatformId(platform, id);
SelectionDLC dlc = null;
if (selection is null)
dlc = SelectionDLC.FromPlatformId(platform, id);
dlc = SelectionDLC.FromTypeId((DLCType)node.Tag, id);
Selection dlcParentSelection = null;
if (dlc is not null)
dlcParentSelection = Selection.FromPlatformId(platform, dlc.Selection.Id);
dlcParentSelection = dlc.Selection;
if (selection is null && dlcParentSelection is null)
return;
ContextMenuItem header = id == "PL"
@ -922,10 +885,10 @@ internal sealed partial class SelectForm : CustomForm
_ = items.Add(new ContextMenuItem("Open Root Directory", "File Explorer",
(_, _) => Diagnostics.OpenDirectoryInFileExplorer(selection.RootDirectory)));
int executables = 0;
foreach ((string directory, BinaryType binaryType) in selection.ExecutableDirectories.ToList())
foreach ((string directory, BinaryType binaryType) in selection.ExecutableDirectories)
_ = items.Add(new ContextMenuItem($"Open Executable Directory #{++executables} ({(binaryType == BinaryType.BIT32 ? "32" : "64")}-bit)",
"File Explorer", (_, _) => Diagnostics.OpenDirectoryInFileExplorer(directory)));
List<string> directories = selection.DllDirectories.ToList();
HashSet<string> directories = selection.DllDirectories;
int steam = 0, epic = 0, r1 = 0, r2 = 0;
if (selection.Platform is Platform.Steam or Platform.Paradox)
foreach (string directory in directories)
@ -1020,7 +983,7 @@ internal sealed partial class SelectForm : CustomForm
private void OnAccept(bool uninstall = false)
{
if (Selection.All.Count < 1)
if (Selection.All.IsEmpty)
return;
if (Selection.AllEnabled.Any(selection => !Program.AreDllsLockedDialog(this, selection)))
return;
@ -1097,12 +1060,12 @@ internal sealed partial class SelectForm : CustomForm
foreach (TreeNode node in TreeNodes)
if (node.Parent is { } parent && node.Tag is Platform platform)
{
if (node.Text == "Unknown" ? node.Checked : !node.Checked)
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);
}
choices = choices.Distinct().ToList();
ProgramData.WriteDlcChoices(choices);
loadButton.Enabled = CanLoadDlc();
saveButton.Enabled = CanSaveDlc();
@ -1206,7 +1169,7 @@ internal sealed partial class SelectForm : CustomForm
saveKoaloaderButton.Enabled = CanSaveKoaloader();
resetKoaloaderButton.Enabled = CanResetKoaloader();
koaloaderAllCheckBox.CheckedChanged -= OnKoaloaderAllCheckBoxChanged;
koaloaderAllCheckBox.Checked = Selection.AllSafe.TrueForAll(selection => selection.Koaloader);
koaloaderAllCheckBox.Checked = Selection.AllSafe.All(selection => selection.Koaloader);
koaloaderAllCheckBox.CheckedChanged += OnKoaloaderAllCheckBoxChanged;
}

View file

@ -146,7 +146,7 @@ internal sealed partial class UpdateForm : CustomForm
throw new TaskCanceledException();
using HttpResponseMessage response = await HttpClientManager.HttpClient.GetAsync(latestRelease.Asset.BrowserDownloadUrl,
HttpCompletionOption.ResponseHeadersRead, cancellation.Token);
response.EnsureSuccessStatusCode();
_ = response.EnsureSuccessStatusCode();
if (cancellation is null || Program.Canceled)
throw new TaskCanceledException();
await using Stream download = await response.Content.ReadAsStreamAsync(cancellation.Token);
@ -196,18 +196,18 @@ internal sealed partial class UpdateForm : CustomForm
string properExecutable = Path.GetFileName(ExecutablePath);
string properExecutablePath = Path.Combine(currentDirectory!, properExecutable!);
StringBuilder commands = new();
commands.AppendLine(CultureInfo.InvariantCulture, $"\nTASKKILL /F /T /PID {Program.CurrentProcessId}");
commands.AppendLine(CultureInfo.InvariantCulture, $":LOOP");
commands.AppendLine(CultureInfo.InvariantCulture, $"TASKLIST | FIND \" {Program.CurrentProcessId}\" ");
commands.AppendLine(CultureInfo.InvariantCulture, $"IF NOT ERRORLEVEL 1 (");
commands.AppendLine(CultureInfo.InvariantCulture, $" TIMEOUT /T 1");
commands.AppendLine(CultureInfo.InvariantCulture, $" GOTO LOOP");
commands.AppendLine(CultureInfo.InvariantCulture, $")");
commands.AppendLine(CultureInfo.InvariantCulture, $"DEL /F /Q \"{currentPath}\"");
commands.AppendLine(CultureInfo.InvariantCulture, $"DEL /F /Q \"{properExecutablePath}\"");
commands.AppendLine(CultureInfo.InvariantCulture, $"MOVE /Y \"{ExecutablePath}\" \"{properExecutablePath}\"");
commands.AppendLine(CultureInfo.InvariantCulture, $"START \"\" /D \"{currentDirectory}\" \"{properExecutable}\"");
commands.AppendLine(CultureInfo.InvariantCulture, $"EXIT");
_ = commands.AppendLine(CultureInfo.InvariantCulture, $"\nTASKKILL /F /T /PID {Program.CurrentProcessId}");
_ = commands.AppendLine(CultureInfo.InvariantCulture, $":LOOP");
_ = commands.AppendLine(CultureInfo.InvariantCulture, $"TASKLIST | FIND \" {Program.CurrentProcessId}\" ");
_ = commands.AppendLine(CultureInfo.InvariantCulture, $"IF NOT ERRORLEVEL 1 (");
_ = commands.AppendLine(CultureInfo.InvariantCulture, $" TIMEOUT /T 1");
_ = commands.AppendLine(CultureInfo.InvariantCulture, $" GOTO LOOP");
_ = commands.AppendLine(CultureInfo.InvariantCulture, $")");
_ = commands.AppendLine(CultureInfo.InvariantCulture, $"DEL /F /Q \"{currentPath}\"");
_ = commands.AppendLine(CultureInfo.InvariantCulture, $"DEL /F /Q \"{properExecutablePath}\"");
_ = commands.AppendLine(CultureInfo.InvariantCulture, $"MOVE /Y \"{ExecutablePath}\" \"{properExecutablePath}\"");
_ = commands.AppendLine(CultureInfo.InvariantCulture, $"START \"\" /D \"{currentDirectory}\" \"{properExecutable}\"");
_ = commands.AppendLine(CultureInfo.InvariantCulture, $"EXIT");
UpdaterPath.WriteFile(commands.ToString(), true, this);
Process process = new();
ProcessStartInfo startInfo = new()
@ -216,7 +216,7 @@ internal sealed partial class UpdateForm : CustomForm
CreateNoWindow = true
};
process.StartInfo = startInfo;
process.Start();
_ = process.Start();
return;
}
if (!retry)

View file

@ -6,7 +6,6 @@ using CreamInstaller.Platforms.Epic.Heroic;
using CreamInstaller.Utility;
using Microsoft.Win32;
using Newtonsoft.Json;
using static CreamInstaller.Resources.Resources;
namespace CreamInstaller.Platforms.Epic;
@ -26,13 +25,10 @@ internal static class EpicLibrary
}
}
internal static async Task<HashSet<(string directory, BinaryType binaryType)>> GetExecutableDirectories(string gameDirectory)
=> await Task.Run(async () => await gameDirectory.GetExecutableDirectories(true));
internal static async Task<HashSet<Manifest>> GetGames()
internal static async Task<List<Manifest>> GetGames()
=> await Task.Run(async () =>
{
HashSet<Manifest> games = new();
List<Manifest> games = new();
string manifests = EpicManifestsPath;
if (manifests.DirectoryExists())
foreach (string item in manifests.EnumerateDirectory("*.item"))
@ -45,7 +41,7 @@ internal static class EpicLibrary
Manifest manifest = JsonConvert.DeserializeObject<Manifest>(json);
if (manifest is not null && !games.Any(g
=> g.CatalogNamespace == manifest.CatalogNamespace && g.InstallLocation == manifest.InstallLocation))
_ = games.Add(manifest);
games.Add(manifest);
}
catch
{

View file

@ -14,6 +14,4 @@ public class HeroicAppData
[JsonProperty("namespace")] public string Namespace { get; set; }
[JsonProperty("title")] public string Title { get; set; }
[JsonProperty("runner")] public string Runner { get; set; }
}

View file

@ -12,7 +12,7 @@ internal static class HeroicLibrary
internal static readonly string HeroicLibraryPath
= Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\heroic\store_cache\legendary_library.json";
internal static async Task GetGames(HashSet<Manifest> games)
internal static async Task GetGames(List<Manifest> games)
=> await Task.Run(() =>
{
string libraryPath = HeroicLibraryPath;
@ -28,14 +28,14 @@ internal static class HeroicLibrary
try
{
HeroicAppData appData = token.ToObject<HeroicAppData>();
if (appData is null || appData.Runner != "legendary" || string.IsNullOrWhiteSpace(appData.Install.InstallPath))
if (appData is null || string.IsNullOrWhiteSpace(appData.Install.InstallPath))
continue;
Manifest manifest = new()
{
DisplayName = appData.Title, CatalogNamespace = appData.Namespace, InstallLocation = appData.Install.InstallPath
};
if (!games.Any(g => g.CatalogNamespace == manifest.CatalogNamespace && g.InstallLocation == manifest.InstallLocation))
_ = games.Add(manifest);
games.Add(manifest);
}
catch
{

View file

@ -1,6 +1,4 @@
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Drawing;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
@ -31,9 +29,6 @@ internal static class ParadoxLauncher
}
}
internal static async Task<HashSet<(string directory, BinaryType binaryType)>> GetExecutableDirectories(string gameDirectory)
=> await Task.Run(async () => await gameDirectory.GetExecutableDirectories(validFunc: path => !Path.GetFileName(path).Contains("bootstrapper")));
private static void PopulateDlc(Selection paradoxLauncher = null)
{
paradoxLauncher ??= Selection.FromPlatformId(Platform.Paradox, "PL");

View file

@ -59,7 +59,7 @@ internal static class SteamCMD
{
if (appId != null)
{
AttemptCount.TryGetValue(appId, out int count);
_ = AttemptCount.TryGetValue(appId, out int count);
AttemptCount[appId] = ++count;
}
if (Program.Canceled)
@ -113,7 +113,6 @@ internal static class SteamCMD
_ = Interlocked.Decrement(ref Locks[i]);
return appInfo.ToString();
}
Thread.Sleep(200);
}
Thread.Sleep(200);
goto wait_for_lock;
@ -232,7 +231,7 @@ internal static class SteamCMD
#endif
continue;
}
if (appInfo.Value.Children().ToList().Count == 0)
if (!appInfo.Value.Children().Any())
return appInfo;
VToken type = appInfo.Value.GetChild("common")?.GetChild("type");
if (type is not null && type.ToString() != "Game")
@ -242,7 +241,7 @@ internal static class SteamCMD
return appInfo;
if (type is not null && (!int.TryParse(buildid, out int gamebuildId) || gamebuildId >= buildId))
return appInfo;
List<string> dlcAppIds = await ParseDlcAppIds(appInfo);
HashSet<string> dlcAppIds = await ParseDlcAppIds(appInfo);
foreach (string dlcAppUpdateFile in dlcAppIds.Select(id => $@"{AppInfoPath}\{id}.vdf"))
dlcAppUpdateFile.DeleteFile();
appUpdateFile.DeleteFile();
@ -253,10 +252,10 @@ internal static class SteamCMD
return null;
}
internal static async Task<List<string>> ParseDlcAppIds(VProperty appInfo)
internal static async Task<HashSet<string>> ParseDlcAppIds(VProperty appInfo)
=> await Task.Run(() =>
{
List<string> dlcIds = new();
HashSet<string> dlcIds = new();
if (Program.Canceled || appInfo is null)
return dlcIds;
VToken extended = appInfo.Value.GetChild("extended");
@ -265,8 +264,8 @@ internal static class SteamCMD
{
VProperty property = (VProperty)vToken;
foreach (string id in property.Value.ToString().Split(","))
if (int.TryParse(id, out int appId) && appId > 0 && !dlcIds.Contains("" + appId))
dlcIds.Add("" + appId);
if (int.TryParse(id, out int appId) && appId > 0)
_ = dlcIds.Add("" + appId);
}
VToken depots = appInfo.Value.GetChild("depots");
if (depots is null)
@ -274,8 +273,8 @@ internal static class SteamCMD
foreach (VToken vToken in depots.Where(p => p is VProperty property && int.TryParse(property.Key, out int _)))
{
VProperty property = (VProperty)vToken;
if (int.TryParse(property.Value.GetChild("dlcappid")?.ToString(), out int appId) && appId > 0 && !dlcIds.Contains("" + appId))
dlcIds.Add("" + appId);
if (int.TryParse(property.Value.GetChild("dlcappid")?.ToString(), out int appId) && appId > 0)
_ = dlcIds.Add("" + appId);
}
return dlcIds;
});

View file

@ -4,7 +4,6 @@ using System.Threading.Tasks;
using CreamInstaller.Utility;
using Gameloop.Vdf.Linq;
using Microsoft.Win32;
using static CreamInstaller.Resources.Resources;
namespace CreamInstaller.Platforms.Steam;
@ -22,13 +21,10 @@ internal static class SteamLibrary
}
}
internal static async Task<HashSet<(string directory, BinaryType binaryType)>> GetExecutableDirectories(string gameDirectory)
=> await Task.Run(async () => await gameDirectory.GetExecutableDirectories(true));
internal static async Task<HashSet<(string appId, string name, string branch, int buildId, string gameDirectory)>> GetGames()
internal static async Task<List<(string appId, string name, string branch, int buildId, string gameDirectory)>> GetGames()
=> await Task.Run(async () =>
{
HashSet<(string appId, string name, string branch, int buildId, string gameDirectory)> games = new();
List<(string appId, string name, string branch, int buildId, string gameDirectory)> games = new();
HashSet<string> gameLibraryDirectories = await GetLibraryDirectories();
foreach (string libraryDirectory in gameLibraryDirectories)
{
@ -37,7 +33,7 @@ internal static class SteamLibrary
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);
games.Add(game);
}
return games;
});

View file

@ -18,13 +18,14 @@ internal static class SteamStore
private const int CooldownGame = 600;
private const int CooldownDlc = 1200;
internal static async Task<List<string>> ParseDlcAppIds(AppData appData)
internal static async Task<HashSet<string>> ParseDlcAppIds(AppData appData)
=> await Task.Run(() =>
{
List<string> dlcIds = new();
HashSet<string> dlcIds = new();
if (appData.DLC is null)
return dlcIds;
dlcIds.AddRange(from appId in appData.DLC where appId > 0 select appId.ToString(CultureInfo.InvariantCulture));
foreach (string dlcId in from appId in appData.DLC where appId > 0 select appId.ToString(CultureInfo.InvariantCulture))
_ = dlcIds.Add(dlcId);
return dlcIds;
});

View file

@ -4,7 +4,6 @@ using System.Linq;
using System.Threading.Tasks;
using CreamInstaller.Utility;
using Microsoft.Win32;
using static CreamInstaller.Resources.Resources;
namespace CreamInstaller.Platforms.Ubisoft;
@ -21,13 +20,10 @@ internal static class UbisoftLibrary
}
}
internal static async Task<HashSet<(string directory, BinaryType binaryType)>> GetExecutableDirectories(string gameDirectory)
=> await Task.Run(async () => await gameDirectory.GetExecutableDirectories(true));
internal static async Task<HashSet<(string gameId, string name, string gameDirectory)>> GetGames()
internal static async Task<List<(string gameId, string name, string gameDirectory)>> GetGames()
=> await Task.Run(() =>
{
HashSet<(string gameId, string name, string gameDirectory)> games = new();
List<(string gameId, string name, string gameDirectory)> games = new();
RegistryKey installsKey = InstallsKey;
if (installsKey is null)
return games;
@ -36,7 +32,7 @@ internal static class UbisoftLibrary
RegistryKey installKey = installsKey.OpenSubKey(gameId);
string installDir = installKey?.GetValue("InstallDir")?.ToString()?.BeautifyPath();
if (installDir is not null && !games.Any(g => g.gameId == gameId && g.gameDirectory == installDir))
_ = games.Add((gameId, new DirectoryInfo(installDir).Name, installDir));
games.Add((gameId, new DirectoryInfo(installDir).Name, installDir));
}
return games;
});

View file

@ -38,7 +38,7 @@ internal static class Koaloader
private static void WriteProxy(this string path, string proxyName, BinaryType binaryType)
{
foreach (string resourceIdentifier in EmbeddedResources.FindAll(r => r.StartsWith("Koaloader", StringComparison.Ordinal)))
foreach (string resourceIdentifier in EmbeddedResources.Where(r => r.StartsWith("Koaloader", StringComparison.Ordinal)))
{
resourceIdentifier.GetProxyInfoFromIdentifier(out string _proxyName, out BinaryType _binaryType);
if (_proxyName != proxyName || _binaryType != binaryType)

View file

@ -4,7 +4,6 @@ using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using CreamInstaller.Utility;
@ -13,12 +12,12 @@ namespace CreamInstaller.Resources;
internal static class Resources
{
private static List<string> embeddedResources;
private static HashSet<string> embeddedResources;
private static readonly Dictionary<ResourceIdentifier, IReadOnlyList<string>> ResourceMD5s = new()
private static readonly Dictionary<ResourceIdentifier, HashSet<string>> ResourceMD5s = new()
{
{
ResourceIdentifier.Koaloader, new List<string>
ResourceIdentifier.Koaloader, new()
{
"8A0958687B5ED7C34DAD037856DD1530", // Koaloader v2.0.0
"8FECDEB40980F4E687C10E056232D96B", // Koaloader v2.0.0
@ -355,7 +354,7 @@ internal static class Resources
}
},
{
ResourceIdentifier.EpicOnlineServices32, new List<string>
ResourceIdentifier.EpicOnlineServices32, new()
{
"069A57B1834A960193D2AD6B96926D70", // ScreamAPI v3.0.0
"E2FB3A4A9583FDC215832E5F935E4440", // ScreamAPI v3.0.1
@ -363,7 +362,7 @@ internal static class Resources
}
},
{
ResourceIdentifier.EpicOnlineServices64, new List<string>
ResourceIdentifier.EpicOnlineServices64, new()
{
"0D62E57139F1A64F807A9934946A9474", // ScreamAPI v3.0.0
"3875C7B735EE80C23239CC4749FDCBE6", // ScreamAPI v3.0.1
@ -371,7 +370,7 @@ internal static class Resources
}
},
{
ResourceIdentifier.Steamworks32, new List<string>
ResourceIdentifier.Steamworks32, new()
{
"02594110FE56B2945955D46670B9A094", // CreamAPI v4.5.0.0 Hotfix
"B2434578957CBE38BDCE0A671C1262FC", // SmokeAPI v1.0.0
@ -389,7 +388,7 @@ internal static class Resources
}
},
{
ResourceIdentifier.Steamworks64, new List<string>
ResourceIdentifier.Steamworks64, new()
{
"30091B91923D9583A54A93ED1145554B", // CreamAPI v4.5.0.0 Hotfix
"08713035CAD6F52548FF324D0487B88D", // SmokeAPI v1.0.0
@ -407,26 +406,26 @@ internal static class Resources
}
},
{
ResourceIdentifier.Uplay32, new List<string>
ResourceIdentifier.Uplay32, new()
{
"1977967B2549A38EC2DB39D4C8ED499B" // Uplay R1 Unlocker v2.0.0
}
},
{
ResourceIdentifier.Uplay64, new List<string>
ResourceIdentifier.Uplay64, new()
{
"333FEDD9DC2B299419B37ED1624FF8DB" // Uplay R1 Unlocker v2.0.0
}
},
{
ResourceIdentifier.Upc32, new List<string>
ResourceIdentifier.Upc32, new()
{
"C14368BC4EE19FDE8DBAC07E31C67AE4", // Uplay R2 Unlocker v3.0.0
"DED3A3EA1876E3110D7D87B9A22946B0" // Uplay R2 Unlocker v3.0.1
}
},
{
ResourceIdentifier.Upc64, new List<string>
ResourceIdentifier.Upc64, new()
{
"7D9A4C12972BAABCB6C181920CC0F19B", // Uplay R2 Unlocker v3.0.0
"D7FDBFE0FC8D7600FEB8EC0A97713184" // Uplay R2 Unlocker v3.0.1
@ -434,7 +433,7 @@ internal static class Resources
}
};
internal static List<string> EmbeddedResources
internal static HashSet<string> EmbeddedResources
{
get
{
@ -443,7 +442,7 @@ internal static class Resources
string[] names = Assembly.GetExecutingAssembly().GetManifestResourceNames();
embeddedResources = new();
foreach (string resourceName in names.Where(n => n.StartsWith("CreamInstaller.Resources.", StringComparison.Ordinal)))
embeddedResources.Add(resourceName[25..]);
_ = embeddedResources.Add(resourceName[25..]);
return embeddedResources;
}
}
@ -483,21 +482,21 @@ internal static class Resources
internal static bool TryGetFileBinaryType(this string path, out BinaryType binaryType) => NativeImports.GetBinaryType(path, out binaryType);
internal static async Task<HashSet<(string directory, BinaryType binaryType)>> GetExecutableDirectories(this string rootDirectory,
bool filterCommon = false, Func<string, bool> validFunc = null)
internal static async Task<List<(string directory, BinaryType binaryType)>> GetExecutableDirectories(this string rootDirectory, bool filterCommon = false,
Func<string, bool> validFunc = null)
=> await Task.Run(async ()
=> (await rootDirectory.GetExecutables(filterCommon, validFunc)
?? (filterCommon || validFunc is not null ? await rootDirectory.GetExecutables() : null))?.Select(e =>
{
e.path = Path.GetDirectoryName(e.path);
return e;
}).DistinctBy(e => e.path).ToHashSet());
}).DistinctBy(e => e.path).ToList());
internal static async Task<HashSet<(string path, BinaryType binaryType)>> GetExecutables(this string rootDirectory, bool filterCommon = false,
internal static async Task<List<(string path, BinaryType binaryType)>> GetExecutables(this string rootDirectory, bool filterCommon = false,
Func<string, bool> validFunc = null)
=> await Task.Run(() =>
{
HashSet<(string path, BinaryType binaryType)> executables = new();
List<(string path, BinaryType binaryType)> executables = new();
if (Program.Canceled || !rootDirectory.DirectoryExists())
return null;
foreach (string path in rootDirectory.EnumerateDirectory("*.exe", true))
@ -507,8 +506,7 @@ internal static class Resources
if (executables.All(e => e.path != path) && (!filterCommon || !rootDirectory.IsCommonIncorrectExecutable(path))
&& (validFunc is null || validFunc(path)) && path.TryGetFileBinaryType(out BinaryType binaryType)
&& binaryType is BinaryType.BIT64)
_ = executables.Add((path, binaryType));
Thread.Sleep(1);
executables.Add((path, binaryType));
}
foreach (string path in rootDirectory.EnumerateDirectory("*.exe", true))
{
@ -517,8 +515,7 @@ internal static class Resources
if (executables.All(e => e.path != path) && (!filterCommon || !rootDirectory.IsCommonIncorrectExecutable(path))
&& (validFunc is null || validFunc(path)) && path.TryGetFileBinaryType(out BinaryType binaryType)
&& binaryType is BinaryType.BIT32)
_ = executables.Add((path, binaryType));
Thread.Sleep(1);
executables.Add((path, binaryType));
}
return executables.Count > 0 ? executables : null;
});

View file

@ -25,12 +25,14 @@ internal static class ScreamAPI
internal static void CheckConfig(string directory, Selection selection, InstallForm installForm = null)
{
directory.GetScreamApiComponents(out _, out _, out _, out _, out string config, out _);
List<SelectionDLC> overrideCatalogItems = selection.DLC.Where(dlc => dlc.Type is DLCType.EpicCatalogItem && !dlc.Enabled).ToList();
List<SelectionDLC> overrideEntitlements = selection.DLC.Where(dlc => dlc.Type is DLCType.EpicEntitlement && !dlc.Enabled).ToList();
HashSet<SelectionDLC> overrideCatalogItems = selection.DLC.Where(dlc => dlc.Type is DLCType.EpicCatalogItem && !dlc.Enabled).ToHashSet();
HashSet<SelectionDLC> overrideEntitlements = selection.DLC.Where(dlc => dlc.Type is DLCType.EpicEntitlement && !dlc.Enabled).ToHashSet();
foreach (Selection extraSelection in selection.ExtraSelections)
{
overrideCatalogItems.AddRange(extraSelection.DLC.Where(dlc => dlc.Type is DLCType.EpicCatalogItem && !dlc.Enabled));
overrideEntitlements.AddRange(extraSelection.DLC.Where(dlc => dlc.Type is DLCType.EpicEntitlement && !dlc.Enabled));
foreach (SelectionDLC extraDlc in extraSelection.DLC.Where(dlc => dlc.Type is DLCType.EpicCatalogItem && !dlc.Enabled))
_ = overrideCatalogItems.Add(extraDlc);
foreach (SelectionDLC extraDlc in extraSelection.DLC.Where(dlc => dlc.Type is DLCType.EpicEntitlement && !dlc.Enabled))
_ = overrideEntitlements.Add(extraDlc);
}
if (overrideCatalogItems.Count > 0 || overrideEntitlements.Count > 0)
{

View file

@ -28,12 +28,13 @@ internal static class SmokeAPI
internal static void CheckConfig(string directory, Selection selection, InstallForm installForm = null)
{
directory.GetSmokeApiComponents(out _, out _, out _, out _, out string old_config, out string config, out _, out _, out _);
List<SelectionDLC> overrideDlc = selection.DLC.Where(dlc => !dlc.Enabled).ToList();
foreach (Selection extraSelection in selection.ExtraSelections)
overrideDlc.AddRange(extraSelection.DLC.Where(dlc => !dlc.Enabled));
List<SelectionDLC> injectDlc = new();
HashSet<SelectionDLC> overrideDlc = selection.DLC.Where(dlc => !dlc.Enabled).ToHashSet();
foreach (SelectionDLC extraDlc in selection.ExtraSelections.SelectMany(extraSelection => extraSelection.DLC.Where(dlc => !dlc.Enabled)))
_ = overrideDlc.Add(extraDlc);
HashSet<SelectionDLC> injectDlc = new();
if (selection.DLC.Count() > 64)
injectDlc.AddRange(selection.DLC.Where(dlc => dlc.Enabled && dlc.Type is DLCType.SteamHidden));
foreach (SelectionDLC hiddenDlc in selection.DLC.Where(dlc => dlc.Enabled && dlc.Type is DLCType.SteamHidden))
_ = injectDlc.Add(hiddenDlc);
List<KeyValuePair<string, (string name, SortedList<string, SelectionDLC> injectDlc)>> extraApps = new();
foreach (Selection extraSelection in selection.ExtraSelections.Where(extraSelection => extraSelection.DLC.Count() > 64))
{

View file

@ -25,9 +25,9 @@ internal static class UplayR1
internal static void CheckConfig(string directory, Selection selection, InstallForm installForm = null)
{
directory.GetUplayR1Components(out _, out _, out _, out _, out string config, out _);
List<SelectionDLC> blacklistDlc = selection.DLC.Where(dlc => !dlc.Enabled).ToList();
foreach (Selection extraSelection in selection.ExtraSelections)
blacklistDlc.AddRange(extraSelection.DLC.Where(dlc => !dlc.Enabled));
HashSet<SelectionDLC> blacklistDlc = selection.DLC.Where(dlc => !dlc.Enabled).ToHashSet();
foreach (SelectionDLC extraDlc in selection.ExtraSelections.SelectMany(extraSelection => extraSelection.DLC.Where(dlc => !dlc.Enabled)))
_ = blacklistDlc.Add(extraDlc);
if (blacklistDlc.Count > 0)
{
/*if (installForm is not null)
@ -100,11 +100,10 @@ internal static class UplayR1
config.DeleteFile();
installForm?.UpdateUser($"Deleted configuration: {Path.GetFileName(config)}", LogTextBox.Action, false);
}
if (log.FileExists())
{
log.DeleteFile();
installForm?.UpdateUser($"Deleted log: {Path.GetFileName(log)}", LogTextBox.Action, false);
}
if (!log.FileExists())
return;
log.DeleteFile();
installForm?.UpdateUser($"Deleted log: {Path.GetFileName(log)}", LogTextBox.Action, false);
});
internal static async Task Install(string directory, Selection selection, InstallForm installForm = null, bool generateConfig = true)

View file

@ -27,9 +27,9 @@ internal static class UplayR2
internal static void CheckConfig(string directory, Selection selection, InstallForm installForm = null)
{
directory.GetUplayR2Components(out _, out _, out _, out _, out _, out _, out string config, out _);
List<SelectionDLC> blacklistDlc = selection.DLC.Where(dlc => !dlc.Enabled).ToList();
foreach (Selection extraSelection in selection.ExtraSelections)
blacklistDlc.AddRange(extraSelection.DLC.Where(dlc => !dlc.Enabled));
HashSet<SelectionDLC> blacklistDlc = selection.DLC.Where(dlc => !dlc.Enabled).ToHashSet();
foreach (SelectionDLC extraDlc in selection.ExtraSelections.SelectMany(extraSelection => extraSelection.DLC.Where(dlc => !dlc.Enabled)))
_ = blacklistDlc.Add(extraDlc);
if (blacklistDlc.Count > 0)
{
/*if (installForm is not null)

View file

@ -1,4 +1,6 @@
using System.Collections.Generic;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using CreamInstaller.Resources;
using CreamInstaller.Utility;
@ -12,31 +14,42 @@ public enum Platform
Epic, Ubisoft
}
internal sealed class Selection
internal sealed class Selection : IEquatable<Selection>
{
internal const string DefaultKoaloaderProxy = "version";
internal static readonly HashSet<Selection> All = new();
internal readonly HashSet<Selection> ExtraSelections = new();
internal static readonly ConcurrentDictionary<Selection, byte> All = new();
internal readonly HashSet<string> DllDirectories;
internal readonly List<(string directory, BinaryType binaryType)> ExecutableDirectories;
internal readonly HashSet<Selection> ExtraSelections = new();
internal readonly string Id;
internal readonly string Name;
internal readonly Platform Platform;
internal readonly string RootDirectory;
internal HashSet<string> DllDirectories;
internal bool Enabled;
internal HashSet<(string directory, BinaryType binaryType)> ExecutableDirectories;
internal string Icon;
internal string Id = "0";
internal bool Koaloader;
internal string KoaloaderProxy;
internal string Name = "Program";
internal Platform Platform;
internal string Product;
internal string Publisher;
internal string RootDirectory;
internal string SubIcon;
internal string Website;
internal Selection() => All.Add(this);
private Selection(Platform platform, string id, string name, string rootDirectory, HashSet<string> dllDirectories,
List<(string directory, BinaryType binaryType)> executableDirectories)
{
Platform = platform;
Id = id;
Name = name;
RootDirectory = rootDirectory;
DllDirectories = dllDirectories;
ExecutableDirectories = executableDirectories;
_ = All.TryAdd(this, default);
}
internal IEnumerable<SelectionDLC> DLC => SelectionDLC.AllSafe.Where(dlc => dlc.Selection == this);
internal IEnumerable<SelectionDLC> DLC => SelectionDLC.AllSafe.Where(dlc => dlc.Selection.Equals(this));
internal bool AreDllsLocked
{
@ -79,13 +92,20 @@ internal sealed class Selection
}
}
internal static List<Selection> AllSafe => All.ToList();
internal static HashSet<Selection> AllSafe => All.Keys.ToHashSet();
internal static List<Selection> AllEnabled => AllSafe.FindAll(s => s.Enabled);
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);
internal static Selection GetOrCreate(Platform platform, string id, string name, string rootDirectory, HashSet<string> dllDirectories,
List<(string directory, BinaryType binaryType)> executableDirectories)
=> FromPlatformId(platform, id) ?? new Selection(platform, id, name, rootDirectory, dllDirectories, executableDirectories);
private void Remove()
{
_ = All.Remove(this);
_ = All.TryRemove(this, out _);
foreach (SelectionDLC dlc in DLC)
dlc.Selection = null;
}
@ -113,7 +133,14 @@ internal sealed class Selection
}
internal static void ValidateAll(List<(Platform platform, string id, string name)> programsToScan)
=> AllSafe.ForEach(selection => selection.Validate(programsToScan));
{
foreach (Selection selection in AllSafe)
selection.Validate(programsToScan);
}
internal static Selection FromPlatformId(Platform platform, string gameId) => AllSafe.Find(s => s.Platform == platform && s.Id == gameId);
internal static Selection FromPlatformId(Platform platform, string gameId) => AllSafe.FirstOrDefault(s => s.Platform == platform && s.Id == gameId);
public override bool Equals(object obj) => ReferenceEquals(this, obj) || obj is Selection other && Equals(other);
public override int GetHashCode() => HashCode.Combine(Id, (int)Platform, RootDirectory);
}

View file

@ -1,26 +1,36 @@
using System.Collections.Generic;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
namespace CreamInstaller;
public enum DLCType
{
Steam, SteamHidden, EpicCatalogItem,
None = 0, Steam, SteamHidden, EpicCatalogItem,
EpicEntitlement
}
internal sealed class SelectionDLC
internal sealed class SelectionDLC : IEquatable<SelectionDLC>
{
private static readonly HashSet<SelectionDLC> All = new();
private static readonly ConcurrentDictionary<SelectionDLC, byte> All = new();
internal readonly string Id;
internal readonly string Name;
internal readonly DLCType Type;
internal bool Enabled;
internal string Icon;
internal string Id;
internal string Name;
internal string Product;
internal string Publisher;
private Selection selection;
internal DLCType Type;
private SelectionDLC(DLCType type, string id, string name)
{
Type = type;
Id = id;
Name = name;
}
internal Selection Selection
{
@ -28,11 +38,19 @@ internal sealed class SelectionDLC
set
{
selection = value;
_ = value is null ? All.Remove(this) : All.Add(this);
_ = value is null ? All.TryRemove(this, out _) : All.TryAdd(this, default);
}
}
internal static List<SelectionDLC> AllSafe => All.ToList();
internal static HashSet<SelectionDLC> AllSafe => All.Keys.ToHashSet();
internal static SelectionDLC FromPlatformId(Platform platform, string dlcId) => AllSafe.Find(dlc => dlc.Selection.Platform == platform && dlc.Id == dlcId);
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);
public override bool Equals(object obj) => ReferenceEquals(this, obj) || obj is SelectionDLC other && Equals(other);
public override int GetHashCode() => HashCode.Combine(Id, (int)Type);
}