diff --git a/CreamInstaller/Components/AppIdComparer.cs b/CreamInstaller/Components/AppIdComparer.cs deleted file mode 100644 index 17e23e8..0000000 --- a/CreamInstaller/Components/AppIdComparer.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Collections.Generic; - -namespace CreamInstaller.Components; - -internal class AppIdComparer : IComparer -{ - private static AppIdComparer comparer; - public static AppIdComparer Comparer => comparer ??= new AppIdComparer(); - - public int Compare(string a, string b) => - a == "ParadoxLauncher" ? -1 - : b == "ParadoxLauncher" ? 1 - : !int.TryParse(a, out _) && !int.TryParse(b, out _) ? string.Compare(a, b, System.StringComparison.Ordinal) - : !int.TryParse(a, out int A) ? 1 - : !int.TryParse(b, out int B) ? -1 - : A > B ? 1 - : A < B ? -1 - : 0; -} diff --git a/CreamInstaller/Components/CustomTreeView.cs b/CreamInstaller/Components/CustomTreeView.cs index c1b8879..ff0e5aa 100644 --- a/CreamInstaller/Components/CustomTreeView.cs +++ b/CreamInstaller/Components/CustomTreeView.cs @@ -15,27 +15,11 @@ internal class CustomTreeView : TreeView base.WndProc(ref m); } - internal class TreeNodeSorter : IComparer - { - private readonly bool compareText; - - internal TreeNodeSorter(bool compareText = false) : base() => this.compareText = compareText; - - public int Compare(object a, object b) - { - if (a is not TreeNode NodeA) return 0; - if (b is not TreeNode NodeB) return 0; - string StringA = compareText ? NodeA.Text : NodeA.Name; - string StringB = compareText ? NodeB.Text : NodeB.Name; - return AppIdComparer.Comparer.Compare(StringA, StringB); - } - } - internal CustomTreeView() : base() { DrawMode = TreeViewDrawMode.OwnerDrawAll; DrawNode += new DrawTreeNodeEventHandler(DrawTreeNode); - TreeViewNodeSorter = new TreeNodeSorter(); + TreeViewNodeSorter = PlatformIdComparer.TreeNodes; } private void DrawTreeNode(object sender, DrawTreeNodeEventArgs e) @@ -45,23 +29,28 @@ internal class CustomTreeView : TreeView if (!node.IsVisible) return; + string subText = node.Name; + Platform? platform = node.Tag as Platform?; + string tagText = platform?.ToString(); + if (string.IsNullOrWhiteSpace(subText) || string.IsNullOrWhiteSpace(tagText) || subText == "PL") + return; + Graphics graphics = e.Graphics; Color backColor = BackColor; using SolidBrush brush = new(backColor); Font font = Font; - using Font subFont = new(font.FontFamily, font.SizeInPoints, FontStyle.Regular, font.Unit, font.GdiCharSet, font.GdiVerticalFont); - - string subText = node.Name; - if (string.IsNullOrWhiteSpace(subText) || subText == "ParadoxLauncher" - || node.Tag is null && ProgramSelection.FromId(subText) is null && ProgramSelection.GetDlcFromId(subText) is null) - return; - - Size subSize = TextRenderer.MeasureText(graphics, subText, subFont); Rectangle bounds = node.Bounds; - Rectangle subBounds = new(bounds.X + bounds.Width, bounds.Y, subSize.Width, bounds.Height); + + Size tagSize = TextRenderer.MeasureText(graphics, tagText, font); + Rectangle tagBounds = new(bounds.X + bounds.Width, bounds.Y, tagSize.Width, bounds.Height); + graphics.FillRectangle(brush, tagBounds); + Point tagLocation = new(tagBounds.Location.X - 1, tagBounds.Location.Y + 1); + TextRenderer.DrawText(graphics, tagText, font, tagLocation, Color.Gray); + + Size subSize = TextRenderer.MeasureText(graphics, subText, font); + Rectangle subBounds = new(tagBounds.X + tagBounds.Width - 4, bounds.Y, subSize.Width, bounds.Height); graphics.FillRectangle(brush, subBounds); - Point location = subBounds.Location; - Point subLocation = new(location.X - 1, location.Y + 1); - TextRenderer.DrawText(graphics, subText, subFont, subLocation, Color.Gray); + Point subLocation = new(subBounds.Location.X - 1, subBounds.Location.Y + 1); + TextRenderer.DrawText(graphics, subText, font, subLocation, Color.LightSlateGray); } } \ No newline at end of file diff --git a/CreamInstaller/Components/PlatformIdComparer.cs b/CreamInstaller/Components/PlatformIdComparer.cs new file mode 100644 index 0000000..1b0792a --- /dev/null +++ b/CreamInstaller/Components/PlatformIdComparer.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Windows.Forms; + +namespace CreamInstaller.Components; + +internal static class PlatformIdComparer +{ + private static PlatformIdStringComparer stringComparer; + internal static PlatformIdStringComparer Strings => stringComparer ??= new(); + + private static PlatformIdNodeComparer nodeComparer; + internal static PlatformIdNodeComparer TreeNodes => nodeComparer ??= new(); +} + +internal class PlatformIdStringComparer : IComparer +{ + public int Compare(string a, string b) => + !int.TryParse(a, out _) && !int.TryParse(b, out _) ? string.Compare(a, b, StringComparison.Ordinal) + : !int.TryParse(a, out int A) ? 1 : !int.TryParse(b, out int B) ? -1 + : A > B ? 1 : A < B ? -1 : 0; +} + +internal class PlatformIdNodeComparer : IComparer +{ + public int Compare(object a, object b) => + a is not TreeNode A ? 1 : b is not TreeNode B ? -1 + : A.Tag is not Platform pA ? 1 : B.Tag is not Platform pB ? -1 + : pA > pB ? 1 : pA < pB ? -1 + : PlatformIdComparer.Strings.Compare(A.Name, B.Name); + +} \ No newline at end of file diff --git a/CreamInstaller/CreamInstaller.csproj b/CreamInstaller/CreamInstaller.csproj index fc61998..fef903e 100644 --- a/CreamInstaller/CreamInstaller.csproj +++ b/CreamInstaller/CreamInstaller.csproj @@ -5,7 +5,7 @@ True Resources\ini.ico true - 3.6.0.0 + 3.6.1.0 Resources\ini.ico LICENSE 2021, pointfeev (https://github.com/pointfeev) diff --git a/CreamInstaller/Forms/InstallForm.cs b/CreamInstaller/Forms/InstallForm.cs index 51e2d97..a685332 100644 --- a/CreamInstaller/Forms/InstallForm.cs +++ b/CreamInstaller/Forms/InstallForm.cs @@ -60,14 +60,14 @@ internal partial class InstallForm : CustomForm UpdateProgress(0); int count = selection.DllDirectories.Count; int cur = 0; - if (selection.Id == "ParadoxLauncher") + if (selection.Id == "PL") { UpdateUser($"Repairing Paradox Launcher . . . ", InstallationLog.Operation); _ = await Repair(this, selection); } foreach (string directory in selection.DllDirectories) { - if (selection.IsSteam && selection.SelectedDlc.Any(d => d.Value.type is DlcType.Steam or DlcType.SteamHidden) + if (selection.Platform is Platform.Steam && selection.SelectedDlc.Any(d => d.Value.type is DlcType.Steam or DlcType.SteamHidden) || selection.ExtraSelectedDlc.Any(item => item.dlc.Any(dlc => dlc.Value.type is DlcType.Steam or DlcType.SteamHidden))) { directory.GetSmokeApiComponents(out string sdk32, out string sdk32_o, out string sdk64, out string sdk64_o, out string config, out string cache); @@ -81,7 +81,7 @@ internal partial class InstallForm : CustomForm await SmokeAPI.Install(directory, selection, this); } } - if (selection.IsEpic && selection.SelectedDlc.Any(d => d.Value.type is DlcType.EpicCatalogItem or DlcType.EpicEntitlement) + if (selection.Platform is Platform.Epic && selection.SelectedDlc.Any(d => d.Value.type is DlcType.EpicCatalogItem or DlcType.EpicEntitlement) || selection.ExtraSelectedDlc.Any(item => item.dlc.Any(dlc => dlc.Value.type is DlcType.EpicCatalogItem or DlcType.EpicEntitlement))) { directory.GetScreamApiComponents(out string sdk32, out string sdk32_o, out string sdk64, out string sdk64_o, out string config); @@ -95,7 +95,7 @@ internal partial class InstallForm : CustomForm await ScreamAPI.Install(directory, selection, this); } } - if (selection.IsUbisoft) + if (selection.Platform is Platform.Ubisoft) { directory.GetUplayR1Components(out string sdk32, out string sdk32_o, out string sdk64, out string sdk64_o, out string config); if (File.Exists(sdk32) || File.Exists(sdk32_o) || File.Exists(sdk64) || File.Exists(sdk64_o) || File.Exists(config)) diff --git a/CreamInstaller/Forms/SelectDialogForm.cs b/CreamInstaller/Forms/SelectDialogForm.cs index 579c79c..1bf7251 100644 --- a/CreamInstaller/Forms/SelectDialogForm.cs +++ b/CreamInstaller/Forms/SelectDialogForm.cs @@ -2,12 +2,15 @@ using CreamInstaller.Utility; using System; +using System.Collections; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; using System.Windows.Forms; +using Windows.Foundation.Metadata; + using static CreamInstaller.Components.CustomTreeView; namespace CreamInstaller; @@ -16,15 +19,15 @@ internal partial class SelectDialogForm : CustomForm { internal SelectDialogForm(IWin32Window owner) : base(owner) => InitializeComponent(); - private readonly List<(string platform, string id, string name)> selected = new(); - internal List<(string platform, string id, string name)> QueryUser(string groupBoxText, List<(string platform, string id, string name, bool alreadySelected)> choices) + private readonly List<(Platform platform, string id, string name)> selected = new(); + internal List<(Platform platform, string id, string name)> QueryUser(string groupBoxText, List<(Platform platform, string id, string name, bool alreadySelected)> choices) { if (!choices.Any()) return null; groupBox.Text = groupBoxText; allCheckBox.Enabled = false; acceptButton.Enabled = false; selectionTreeView.AfterCheck += OnTreeNodeChecked; - foreach ((string platform, string id, string name, bool alreadySelected) in choices) + foreach ((Platform platform, string id, string name, bool alreadySelected) in choices) { TreeNode node = new() { @@ -43,7 +46,7 @@ internal partial class SelectDialogForm : CustomForm allCheckBox.Enabled = true; acceptButton.Enabled = selected.Any(); saveButton.Enabled = acceptButton.Enabled; - loadButton.Enabled = File.Exists(ProgramData.ChoicesPath); + loadButton.Enabled = ProgramData.ReadChoices() is not null; OnResize(null, null); Resize += OnResize; return ShowDialog() == DialogResult.OK ? selected : null; @@ -59,10 +62,11 @@ internal partial class SelectDialogForm : CustomForm private void OnTreeNodeChecked(TreeNode node) { string id = node.Name; + Platform platform = (Platform)node.Tag; if (node.Checked) - selected.Add((node.Tag as string, id, node.Text)); + selected.Add((platform, id, node.Text)); else - _ = selected.RemoveAll(s => s.id == id); + _ = selected.RemoveAll(s => s.platform == platform && s.id == id); allCheckBox.CheckedChanged -= OnAllCheckBoxChanged; allCheckBox.Checked = selectionTreeView.Nodes.Cast().All(n => n.Checked); allCheckBox.CheckedChanged += OnAllCheckBoxChanged; @@ -73,7 +77,7 @@ internal partial class SelectDialogForm : CustomForm ? Program.ApplicationNameShort : Program.ApplicationName; - private void OnSortCheckBoxChanged(object sender, EventArgs e) => selectionTreeView.TreeViewNodeSorter = new TreeNodeSorter(sortCheckBox.Checked); + private void OnSortCheckBoxChanged(object sender, EventArgs e) => selectionTreeView.TreeViewNodeSorter = PlatformIdComparer.TreeNodes; private void OnAllCheckBoxChanged(object sender, EventArgs e) { @@ -92,19 +96,20 @@ internal partial class SelectDialogForm : CustomForm private void OnLoad(object sender, EventArgs e) { - List choices = ProgramData.ReadChoices(); + List<(Platform platform, string id)> choices = ProgramData.ReadChoices(); + if (choices is null) return; foreach (TreeNode node in selectionTreeView.Nodes) { - node.Checked = choices.Contains(node.Name); + node.Checked = choices.Any(n => n.platform == (Platform)node.Tag && n.id == node.Name); OnTreeNodeChecked(node); } } private void OnSave(object sender, EventArgs e) { - List choices = new(); + List<(Platform platform, string id)> choices = new(); foreach (TreeNode node in selectionTreeView.Nodes.Cast().Where(n => n.Checked)) - choices.Add(node.Name); + choices.Add(((Platform)node.Tag, node.Name)); ProgramData.WriteChoices(choices); loadButton.Enabled = File.Exists(ProgramData.ChoicesPath); } diff --git a/CreamInstaller/Forms/SelectForm.cs b/CreamInstaller/Forms/SelectForm.cs index 9594d44..01adcfe 100644 --- a/CreamInstaller/Forms/SelectForm.cs +++ b/CreamInstaller/Forms/SelectForm.cs @@ -21,6 +21,8 @@ using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; +using Windows.Foundation.Metadata; + namespace CreamInstaller; internal partial class SelectForm : CustomForm @@ -105,23 +107,28 @@ internal partial class SelectForm : CustomForm RemainingGames.Clear(); // for display purposes only, otherwise ignorable RemainingDLCs.Clear(); // for display purposes only, otherwise ignorable List appTasks = new(); - if (ProgramsToScan.Any(c => c.platform == "Paradox")) + if (ProgramsToScan.Any(c => c.platform is Platform.Paradox)) { List steamDllDirectories = await SteamLibrary.GetDllDirectoriesFromGameDirectory(ParadoxLauncher.InstallPath); List epicDllDirectories = await EpicLibrary.GetDllDirectoriesFromGameDirectory(ParadoxLauncher.InstallPath); + List dllDirectories = new(); + if (steamDllDirectories is not null) + dllDirectories = dllDirectories.Union(steamDllDirectories).ToList(); + if (epicDllDirectories is not null) + dllDirectories = dllDirectories.Union(epicDllDirectories).ToList(); if (steamDllDirectories is not null || epicDllDirectories is not null) { - ProgramSelection selection = ProgramSelection.FromId("ParadoxLauncher"); + ProgramSelection selection = ProgramSelection.FromPlatformId(Platform.Paradox, "PL"); selection ??= new(); if (allCheckBox.Checked) selection.Enabled = true; - selection.Id = "ParadoxLauncher"; + selection.Id = "PL"; selection.Name = "Paradox Launcher"; selection.RootDirectory = ParadoxLauncher.InstallPath; - selection.DllDirectories = steamDllDirectories ?? epicDllDirectories; - selection.IsSteam = steamDllDirectories is not null; - selection.IsEpic = epicDllDirectories is not null; + selection.DllDirectories = dllDirectories; + selection.Platform = Platform.Paradox; TreeNode programNode = treeNodes.Find(s => s.Name == selection.Id) ?? new(); + programNode.Tag = selection.Platform; programNode.Name = selection.Id; programNode.Text = selection.Name; programNode.Checked = selection.Enabled; @@ -129,7 +136,7 @@ internal partial class SelectForm : CustomForm _ = selectionTreeView.Nodes.Add(programNode); } } - if (ProgramsToScan.Any(c => c.platform == "Steam")) + if (ProgramsToScan.Any(c => c.platform is Platform.Steam)) { List<(string appId, string name, string branch, int buildId, string gameDirectory)> steamGames = await SteamLibrary.GetGames(); int totalGames = steamGames.Count; @@ -137,7 +144,7 @@ internal partial class SelectForm : CustomForm foreach ((string appId, string name, string branch, int buildId, string gameDirectory) in steamGames) { if (Program.Canceled) return; - if (Program.IsGameBlocked(name, gameDirectory) || !ProgramsToScan.Any(c => c.id == appId)) + if (Program.IsGameBlocked(name, gameDirectory) || !ProgramsToScan.Any(c => c.platform is Platform.Steam && c.id == appId)) { gamesChecked++; continue; @@ -223,13 +230,13 @@ internal partial class SelectForm : CustomForm await task; } - ProgramSelection selection = ProgramSelection.FromId(appId) ?? new(); + ProgramSelection selection = ProgramSelection.FromPlatformId(Platform.Steam, appId) ?? new(); selection.Enabled = allCheckBox.Checked || selection.SelectedDlc.Any() || selection.ExtraSelectedDlc.Any(); selection.Id = appId; selection.Name = appData?.name ?? name; selection.RootDirectory = gameDirectory; selection.DllDirectories = dllDirectories; - selection.IsSteam = true; + selection.Platform = Platform.Steam; selection.ProductUrl = "https://store.steampowered.com/app/" + appId; selection.IconUrl = IconGrabber.SteamAppImagesPath + @$"\{appId}\{appInfo?.Value?.GetChild("common")?.GetChild("icon")}.jpg"; selection.SubIconUrl = appData?.header_image ?? IconGrabber.SteamAppImagesPath + @$"\{appId}\{appInfo?.Value?.GetChild("common")?.GetChild("clienticon")}.ico"; @@ -241,6 +248,7 @@ internal partial class SelectForm : CustomForm { if (Program.Canceled) return; TreeNode programNode = treeNodes.Find(s => s.Name == appId) ?? new(); + programNode.Tag = selection.Platform; programNode.Name = appId; programNode.Text = appData?.name ?? name; programNode.Checked = selection.Enabled; @@ -254,6 +262,7 @@ internal partial class SelectForm : CustomForm selection.AllDlc[appId] = dlcApp; if (allCheckBox.Checked) selection.SelectedDlc[appId] = dlcApp; TreeNode dlcNode = treeNodes.Find(s => s.Name == appId) ?? new(); + dlcNode.Tag = selection.Platform; dlcNode.Name = appId; dlcNode.Text = dlcApp.name; dlcNode.Checked = selection.SelectedDlc.ContainsKey(appId); @@ -267,7 +276,7 @@ internal partial class SelectForm : CustomForm appTasks.Add(task); } } - if (ProgramsToScan.Any(c => c.platform == "Epic")) + if (ProgramsToScan.Any(c => c.platform is Platform.Epic)) { List epicGames = await EpicLibrary.GetGames(); foreach (Manifest manifest in epicGames) @@ -276,7 +285,7 @@ internal partial class SelectForm : CustomForm string name = manifest.DisplayName; string directory = manifest.InstallLocation; if (Program.Canceled) return; - if (Program.IsGameBlocked(name, directory) || !ProgramsToScan.Any(c => c.id == @namespace)) continue; + if (Program.IsGameBlocked(name, directory) || !ProgramsToScan.Any(c => c.platform is Platform.Epic && c.id == @namespace)) continue; AddToRemainingGames(name); Task task = Task.Run(async () => { @@ -318,13 +327,13 @@ internal partial class SelectForm : CustomForm await task; } - ProgramSelection selection = ProgramSelection.FromId(@namespace) ?? new(); + ProgramSelection selection = ProgramSelection.FromPlatformId(Platform.Epic, @namespace) ?? new(); selection.Enabled = allCheckBox.Checked || selection.SelectedDlc.Any() || selection.ExtraSelectedDlc.Any(); selection.Id = @namespace; selection.Name = name; selection.RootDirectory = directory; selection.DllDirectories = dllDirectories; - selection.IsEpic = true; + selection.Platform = Platform.Epic; foreach (KeyValuePair pair in entitlements.Where(p => p.Value.name == selection.Name)) { selection.ProductUrl = "https://www.epicgames.com/store/product/" + pair.Value.product; @@ -337,6 +346,7 @@ internal partial class SelectForm : CustomForm { if (Program.Canceled) return; TreeNode programNode = treeNodes.Find(s => s.Name == @namespace) ?? new(); + programNode.Tag = selection.Platform; programNode.Name = @namespace; programNode.Text = name; programNode.Checked = selection.Enabled; @@ -364,6 +374,7 @@ internal partial class SelectForm : CustomForm selection.AllDlc[dlcId] = dlcApp; if (allCheckBox.Checked) selection.SelectedDlc[dlcId] = dlcApp; TreeNode dlcNode = treeNodes.Find(s => s.Name == dlcId) ?? new(); + dlcNode.Tag = selection.Platform; dlcNode.Name = dlcId; dlcNode.Text = dlcApp.name; dlcNode.Checked = selection.SelectedDlc.ContainsKey(dlcId); @@ -378,13 +389,13 @@ internal partial class SelectForm : CustomForm appTasks.Add(task); } } - if (ProgramsToScan.Any(c => c.platform == "Ubisoft")) + if (ProgramsToScan.Any(c => c.platform is Platform.Ubisoft)) { List<(string gameId, string name, string gameDirectory)> ubisoftGames = await UbisoftLibrary.GetGames(); foreach ((string gameId, string name, string gameDirectory) in ubisoftGames) { if (Program.Canceled) return; - if (Program.IsGameBlocked(name, gameDirectory) || !ProgramsToScan.Any(c => c.id == gameId)) continue; + if (Program.IsGameBlocked(name, gameDirectory) || !ProgramsToScan.Any(c => c.platform is Platform.Ubisoft && c.id == gameId)) continue; AddToRemainingGames(name); Task task = Task.Run(async () => { @@ -397,19 +408,20 @@ internal partial class SelectForm : CustomForm } if (Program.Canceled) return; - ProgramSelection selection = ProgramSelection.FromId(gameId) ?? new(); + ProgramSelection selection = ProgramSelection.FromPlatformId(Platform.Ubisoft, gameId) ?? new(); selection.Enabled = allCheckBox.Checked || selection.SelectedDlc.Any() || selection.ExtraSelectedDlc.Any(); selection.Id = gameId; selection.Name = name; selection.RootDirectory = gameDirectory; selection.DllDirectories = dllDirectories; - selection.IsUbisoft = true; + selection.Platform = Platform.Ubisoft; selection.IconUrl = IconGrabber.GetDomainFaviconUrl("store.ubi.com"); Program.Invoke(selectionTreeView, delegate { if (Program.Canceled) return; TreeNode programNode = treeNodes.Find(s => s.Name == gameId) ?? new(); + programNode.Tag = selection.Platform; programNode.Name = gameId; programNode.Text = name; programNode.Checked = selection.Enabled; @@ -429,7 +441,7 @@ internal partial class SelectForm : CustomForm } } - private List<(string platform, string id, string name)> ProgramsToScan; + private List<(Platform platform, string id, string name)> ProgramsToScan; private async void OnLoad(bool forceScan = false, bool forceProvideChoices = false) { Program.Canceled = false; @@ -450,22 +462,21 @@ internal partial class SelectForm : CustomForm bool scan = forceScan; if (!scan && (ProgramsToScan is null || !ProgramsToScan.Any() || forceProvideChoices)) { - List<(string platform, string id, string name, bool alreadySelected)> gameChoices = new(); + List<(Platform platform, string id, string name, bool alreadySelected)> gameChoices = new(); if (Directory.Exists(ParadoxLauncher.InstallPath)) - gameChoices.Add(("Paradox", "ParadoxLauncher", "Paradox Launcher", ProgramsToScan is not null && ProgramsToScan.Any(p => p.id == "ParadoxLauncher"))); + gameChoices.Add((Platform.Paradox, "PL", "Paradox Launcher", ProgramsToScan is not null && ProgramsToScan.Any(p => p.platform is Platform.Paradox && p.id == "PL"))); if (Directory.Exists(SteamLibrary.InstallPath)) - foreach ((string appId, string name, string branch, int buildId, string gameDirectory) in await SteamLibrary.GetGames()) - if (!Program.IsGameBlocked(name, gameDirectory)) - gameChoices.Add(("Steam", appId, name, ProgramsToScan is not null && ProgramsToScan.Any(p => p.id == appId))); + foreach ((string appId, string name, string branch, int buildId, string gameDirectory) in (await SteamLibrary.GetGames()).Where(g => !Program.IsGameBlocked(g.name, g.gameDirectory))) + gameChoices.Add((Platform.Steam, appId, name, ProgramsToScan is not null && ProgramsToScan.Any(p => p.platform is Platform.Steam && p.id == appId))); if (Directory.Exists(EpicLibrary.EpicManifestsPath)) foreach (Manifest manifest in (await EpicLibrary.GetGames()).Where(m => !Program.IsGameBlocked(m.DisplayName, m.InstallLocation))) - gameChoices.Add(("Epic", manifest.CatalogNamespace, manifest.DisplayName, ProgramsToScan is not null && ProgramsToScan.Any(p => p.id == manifest.CatalogNamespace))); - foreach ((string gameId, string name, string gameDirectory) in await UbisoftLibrary.GetGames()) - gameChoices.Add(("Ubisoft", gameId, name, ProgramsToScan is not null && ProgramsToScan.Any(p => p.id == gameId))); + gameChoices.Add((Platform.Epic, manifest.CatalogNamespace, manifest.DisplayName, ProgramsToScan is not null && ProgramsToScan.Any(p => p.platform is Platform.Epic && p.id == manifest.CatalogNamespace))); + foreach ((string gameId, string name, string gameDirectory) in (await UbisoftLibrary.GetGames()).Where(g => !Program.IsGameBlocked(g.name, g.gameDirectory))) + gameChoices.Add((Platform.Ubisoft, gameId, name, ProgramsToScan is not null && ProgramsToScan.Any(p => p.platform is Platform.Ubisoft && p.id == gameId))); if (gameChoices.Any()) { using SelectDialogForm form = new(this); - List<(string platform, string id, string name)> choices = form.QueryUser("Choose which programs and/or games to scan for DLC:", gameChoices); + List<(Platform platform, string id, string name)> choices = form.QueryUser("Choose which programs and/or games to scan for DLC:", gameChoices); scan = choices is not null && choices.Any(); string retry = "\n\nPress the \"Rescan Programs / Games\" button to re-choose."; if (scan) @@ -497,7 +508,7 @@ internal partial class SelectForm : CustomForm : $"Gathering and caching your applicable games and their DLCs . . . {p}%"; progressBar.Value = p; }; - if (Directory.Exists(SteamLibrary.InstallPath) && ProgramsToScan is not null && ProgramsToScan.Any(c => c.platform == "Steam")) + if (Directory.Exists(SteamLibrary.InstallPath) && ProgramsToScan is not null && ProgramsToScan.Any(c => c.platform is Platform.Steam)) { progressLabel.Text = $"Setting up SteamCMD . . . "; await SteamCMD.Setup(iProgress); @@ -557,17 +568,19 @@ internal partial class SelectForm : CustomForm private static void SyncNode(TreeNode node) { - (string gameId, (DlcType type, string name, string icon) app)? dlc = ProgramSelection.GetDlcFromId(node.Name); + string id = node.Name; + Platform platform = (Platform)node.Tag; + (string gameId, (DlcType type, string name, string icon) app)? dlc = ProgramSelection.GetDlcFromPlatformId(platform, id); if (dlc.HasValue) { (string gameId, _) = dlc.Value; - ProgramSelection selection = ProgramSelection.FromId(gameId); + ProgramSelection selection = ProgramSelection.FromPlatformId(platform, gameId); if (selection is not null) selection.ToggleDlc(node.Name, node.Checked); } else { - ProgramSelection selection = ProgramSelection.FromId(node.Name); + ProgramSelection selection = ProgramSelection.FromPlatformId(platform, id); if (selection is not null) selection.Enabled = node.Checked; } @@ -630,16 +643,18 @@ internal partial class SelectForm : CustomForm ContextMenuStrip contextMenuStrip = new(); selectionTreeView.SelectedNode = node; string id = node.Name; - ProgramSelection selection = ProgramSelection.FromId(id); + Platform platform = (Platform)node.Tag; + ProgramSelection selection = ProgramSelection.FromPlatformId(platform, id); (string gameAppId, (DlcType type, string name, string icon) app)? dlc = null; - if (selection is null) dlc = ProgramSelection.GetDlcFromId(id); + if (selection is null) + dlc = ProgramSelection.GetDlcFromPlatformId(platform, id); ProgramSelection dlcParentSelection = null; if (dlc is not null) - dlcParentSelection = ProgramSelection.FromId(dlc.Value.gameAppId); + dlcParentSelection = ProgramSelection.FromPlatformId(platform, dlc.Value.gameAppId); if (selection is null && dlcParentSelection is null) return; ContextMenuItem header = null; - if (id == "ParadoxLauncher") + if (id == "PL") header = new(node.Text, "Paradox Launcher"); else if (selection is not null) header = new(node.Text, (id, selection.IconUrl)); @@ -654,8 +669,9 @@ internal partial class SelectForm : CustomForm List queries = new(); if (File.Exists(appInfoJSON)) { - string platform = (selection is null || selection.IsSteam) ? "Steam Store " : selection.IsEpic ? "Epic GraphQL " : ""; - queries.Add(new ContextMenuItem($"Open {platform}Query", "Notepad", + string platformString = (selection is null || selection.Platform is Platform.Steam) ? "Steam Store " + : selection.Platform is Platform.Epic ? "Epic GraphQL " : ""; + queries.Add(new ContextMenuItem($"Open {platformString}Query", "Notepad", new EventHandler((sender, e) => Diagnostics.OpenFileInNotepad(appInfoJSON)))); } if (File.Exists(appInfoVDF)) @@ -690,7 +706,7 @@ internal partial class SelectForm : CustomForm } if (selection is not null) { - if (id == "ParadoxLauncher") + if (id == "PL") { contextMenuStrip.Items.Add(new ToolStripSeparator()); contextMenuStrip.Items.Add(new ContextMenuItem("Repair", "Command Prompt", @@ -700,49 +716,40 @@ internal partial class SelectForm : CustomForm contextMenuStrip.Items.Add(new ContextMenuItem("Open Root Directory", "File Explorer", new EventHandler((sender, e) => Diagnostics.OpenDirectoryInFileExplorer(selection.RootDirectory)))); List directories = selection.DllDirectories.ToList(); - if (selection.IsSteam) - for (int i = 0; i < directories.Count; i++) + int steam = 0, epic = 0, r1 = 0, r2 = 0; + if (selection.Platform is Platform.Steam or Platform.Paradox) + foreach (string directory in directories) { - string directory = directories[i]; directory.GetSmokeApiComponents(out string sdk32, out string sdk32_o, out string sdk64, out string sdk64_o, out string config, out string cache); if (File.Exists(sdk32) || File.Exists(sdk32_o) || File.Exists(sdk64) || File.Exists(sdk64_o) || File.Exists(config) || File.Exists(cache)) - { - contextMenuStrip.Items.Add(new ContextMenuItem($"Open Steamworks SDK Directory #{i + 1}", "File Explorer", + contextMenuStrip.Items.Add(new ContextMenuItem($"Open Steamworks Directory #{++steam}", "File Explorer", new EventHandler((sender, e) => Diagnostics.OpenDirectoryInFileExplorer(directory)))); - } } - if (selection.IsEpic) - for (int i = 0; i < directories.Count; i++) + if (selection.Platform is Platform.Epic or Platform.Paradox) + foreach (string directory in directories) { - string directory = directories[i]; directory.GetScreamApiComponents(out string sdk32, out string sdk32_o, out string sdk64, out string sdk64_o, out string config); if (File.Exists(sdk32) || File.Exists(sdk32_o) || File.Exists(sdk64) || File.Exists(sdk64_o) || File.Exists(config)) - { - contextMenuStrip.Items.Add(new ContextMenuItem($"Open Epic Online Services SDK Directory #{i + 1}", "File Explorer", + contextMenuStrip.Items.Add(new ContextMenuItem($"Open Epic Online Services Directory #{++epic}", "File Explorer", new EventHandler((sender, e) => Diagnostics.OpenDirectoryInFileExplorer(directory)))); - } } - if (selection.IsUbisoft) - for (int i = 0, r1 = 0, r2 = 0; i < directories.Count; i++) + if (selection.Platform is Platform.Ubisoft) + foreach (string directory in directories) { - string directory = directories[i]; directory.GetUplayR1Components(out string sdk32, out string sdk32_o, out string sdk64, out string sdk64_o, out string config); if (File.Exists(sdk32) || File.Exists(sdk32_o) || File.Exists(sdk64) || File.Exists(sdk64_o) || File.Exists(config)) - { contextMenuStrip.Items.Add(new ContextMenuItem($"Open Uplay R1 SDK Directory #{++r1}", "File Explorer", new EventHandler((sender, e) => Diagnostics.OpenDirectoryInFileExplorer(directory)))); - } directory.GetUplayR2Components(out string old_sdk32, out string old_sdk64, out sdk32, out sdk32_o, out sdk64, out sdk64_o, out config); if (File.Exists(old_sdk32) || File.Exists(old_sdk64) || File.Exists(sdk32) || File.Exists(sdk32_o) || File.Exists(sdk64) || File.Exists(sdk64_o) || File.Exists(config)) - { contextMenuStrip.Items.Add(new ContextMenuItem($"Open Uplay R2 SDK Directory #{++r2}", "File Explorer", new EventHandler((sender, e) => Diagnostics.OpenDirectoryInFileExplorer(directory)))); - } } } - if (id != "ParadoxLauncher") + if (id != "PL") { - if (selection is not null && selection.IsSteam || dlcParentSelection is not null && dlcParentSelection.IsSteam) + if (selection is not null && selection.Platform is Platform.Steam + || dlcParentSelection is not null && dlcParentSelection.Platform is Platform.Steam) { contextMenuStrip.Items.Add(new ToolStripSeparator()); contextMenuStrip.Items.Add(new ContextMenuItem("Open SteamDB", "SteamDB", @@ -750,14 +757,14 @@ internal partial class SelectForm : CustomForm } if (selection is not null) { - if (selection.IsSteam) + if (selection.Platform is Platform.Steam) { contextMenuStrip.Items.Add(new ContextMenuItem("Open Steam Store", "Steam Store", new EventHandler((sender, e) => Diagnostics.OpenUrlInInternetBrowser(selection.ProductUrl)))); contextMenuStrip.Items.Add(new ContextMenuItem("Open Steam Community", ("Sub_" + id, selection.SubIconUrl), "Steam Community", new EventHandler((sender, e) => Diagnostics.OpenUrlInInternetBrowser("https://steamcommunity.com/app/" + id)))); } - else if (selection.IsEpic) + if (selection.Platform is Platform.Epic) { contextMenuStrip.Items.Add(new ToolStripSeparator()); contextMenuStrip.Items.Add(new ContextMenuItem("Open ScreamDB", "ScreamDB", @@ -765,7 +772,7 @@ internal partial class SelectForm : CustomForm contextMenuStrip.Items.Add(new ContextMenuItem("Open Epic Games Store", "Epic Games", new EventHandler((sender, e) => Diagnostics.OpenUrlInInternetBrowser(selection.ProductUrl)))); } - else if (selection.IsUbisoft) + if (selection.Platform is Platform.Ubisoft) { contextMenuStrip.Items.Add(new ToolStripSeparator()); #pragma warning disable CA1308 // Normalize strings to uppercase diff --git a/CreamInstaller/Paradox/ParadoxLauncher.cs b/CreamInstaller/Paradox/ParadoxLauncher.cs index 518b7e1..465d360 100644 --- a/CreamInstaller/Paradox/ParadoxLauncher.cs +++ b/CreamInstaller/Paradox/ParadoxLauncher.cs @@ -28,7 +28,7 @@ internal static class ParadoxLauncher private static void PopulateDlc(ProgramSelection paradoxLauncher = null) { - paradoxLauncher ??= ProgramSelection.FromId("ParadoxLauncher"); + paradoxLauncher ??= ProgramSelection.FromPlatformId(Platform.Paradox, "PL"); if (paradoxLauncher is not null) { paradoxLauncher.ExtraDlc.Clear(); @@ -51,7 +51,7 @@ internal static class ParadoxLauncher internal static bool DlcDialog(Form form) { - ProgramSelection paradoxLauncher = ProgramSelection.FromId("ParadoxLauncher"); + ProgramSelection paradoxLauncher = ProgramSelection.FromPlatformId(Platform.Paradox, "PL"); if (paradoxLauncher is not null && paradoxLauncher.Enabled) { PopulateDlc(paradoxLauncher); diff --git a/CreamInstaller/ProgramSelection.cs b/CreamInstaller/ProgramSelection.cs index d0c8eca..b647249 100644 --- a/CreamInstaller/ProgramSelection.cs +++ b/CreamInstaller/ProgramSelection.cs @@ -1,12 +1,22 @@ using CreamInstaller.Components; using CreamInstaller.Resources; +using System; using System.Collections.Generic; using System.IO; using System.Linq; namespace CreamInstaller; +public enum Platform +{ + None = 0, + Paradox, + Steam, + Epic, + Ubisoft +} + public enum DlcType { Steam, @@ -19,6 +29,7 @@ internal class ProgramSelection { internal bool Enabled; + internal Platform Platform; internal string Id = "0"; internal string Name = "Program"; @@ -33,12 +44,8 @@ internal class ProgramSelection internal string RootDirectory; internal List DllDirectories; - internal bool IsSteam; - internal bool IsEpic; - internal bool IsUbisoft; - - internal readonly SortedList AllDlc = new(AppIdComparer.Comparer); - internal readonly SortedList SelectedDlc = new(AppIdComparer.Comparer); + internal readonly SortedList AllDlc = new(PlatformIdComparer.Strings); + internal readonly SortedList SelectedDlc = new(PlatformIdComparer.Strings); internal readonly List<(string id, string name, SortedList dlc)> ExtraDlc = new(); // for Paradox Launcher internal readonly List<(string id, string name, SortedList dlc)> ExtraSelectedDlc = new(); // for Paradox Launcher @@ -49,7 +56,7 @@ internal class ProgramSelection { foreach (string directory in DllDirectories) { - if (IsSteam) + if (Platform is Platform.Steam or Platform.Paradox) { directory.GetCreamApiComponents(out string sdk32, out string sdk32_o, out string sdk64, out string sdk64_o, out string config); if (sdk32.IsFilePathLocked() @@ -67,7 +74,7 @@ internal class ProgramSelection || cache.IsFilePathLocked()) return true; } - else if (IsEpic) + if (Platform is Platform.Epic or Platform.Paradox) { directory.GetScreamApiComponents(out string sdk32, out string sdk32_o, out string sdk64, out string sdk64_o, out string config); if (sdk32.IsFilePathLocked() @@ -77,7 +84,7 @@ internal class ProgramSelection || config.IsFilePathLocked()) return true; } - else if (IsUbisoft) + if (Platform is Platform.Ubisoft) { directory.GetUplayR1Components(out string sdk32, out string sdk32_o, out string sdk64, out string sdk64_o, out string config); if (sdk32.IsFilePathLocked() @@ -140,9 +147,9 @@ internal class ProgramSelection if (!DllDirectories.Any()) _ = All.Remove(this); } - internal void Validate(List<(string platform, string id, string name)> programsToScan) + internal void Validate(List<(Platform platform, string id, string name)> programsToScan) { - if (programsToScan is null || !programsToScan.Any(p => p.id == Id)) + if (programsToScan is null || !programsToScan.Any(p => p.platform == Platform && p.id == Id)) { _ = All.Remove(this); return; @@ -152,7 +159,7 @@ internal class ProgramSelection internal static void ValidateAll() => AllSafe.ForEach(selection => selection.Validate()); - internal static void ValidateAll(List<(string platform, string id, string name)> programsToScan) => AllSafe.ForEach(selection => selection.Validate(programsToScan)); + internal static void ValidateAll(List<(Platform platform, string id, string name)> programsToScan) => AllSafe.ForEach(selection => selection.Validate(programsToScan)); internal static readonly List All = new(); @@ -160,11 +167,11 @@ internal class ProgramSelection internal static List AllEnabled => AllSafe.FindAll(s => s.Enabled); - internal static ProgramSelection FromId(string gameId) => AllSafe.Find(s => s.Id == gameId); + internal static ProgramSelection FromPlatformId(Platform platform, string gameId) => AllSafe.Find(s => s.Platform == platform && s.Id == gameId); - internal static (string gameId, (DlcType type, string name, string icon) app)? GetDlcFromId(string dlcId) + internal static (string gameId, (DlcType type, string name, string icon) app)? GetDlcFromPlatformId(Platform platform, string dlcId) { - foreach (ProgramSelection selection in AllSafe) + foreach (ProgramSelection selection in AllSafe.Where(s => s.Platform == platform)) foreach (KeyValuePair pair in selection.AllDlc.Where(p => p.Key == dlcId)) return (selection.Id, pair.Value); return null; diff --git a/CreamInstaller/Resources/ScreamAPI.cs b/CreamInstaller/Resources/ScreamAPI.cs index c5c3798..e564728 100644 --- a/CreamInstaller/Resources/ScreamAPI.cs +++ b/CreamInstaller/Resources/ScreamAPI.cs @@ -151,8 +151,8 @@ internal static class ScreamAPI File.Create(config).Close(); StreamWriter writer = new(config, true, Encoding.UTF8); WriteConfig(writer, - new(overrideCatalogItems.ToDictionary(pair => pair.Key, pair => pair.Value), AppIdComparer.Comparer), - new(entitlements.ToDictionary(pair => pair.Key, pair => pair.Value), AppIdComparer.Comparer), + new(overrideCatalogItems.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.Strings), + new(entitlements.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.Strings), installForm); writer.Flush(); writer.Close(); diff --git a/CreamInstaller/Resources/SmokeAPI.cs b/CreamInstaller/Resources/SmokeAPI.cs index ac60eb8..8183607 100644 --- a/CreamInstaller/Resources/SmokeAPI.cs +++ b/CreamInstaller/Resources/SmokeAPI.cs @@ -165,8 +165,8 @@ internal static class SmokeAPI File.Create(config).Close(); StreamWriter writer = new(config, true, Encoding.UTF8); WriteConfig(writer, - new(overrideDlc.ToDictionary(pair => pair.Key, pair => pair.Value), AppIdComparer.Comparer), - new(injectDlc.ToDictionary(pair => pair.Key, pair => pair.Value), AppIdComparer.Comparer), + new(overrideDlc.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.Strings), + new(injectDlc.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.Strings), installForm); writer.Flush(); writer.Close(); diff --git a/CreamInstaller/Resources/UplayR1.cs b/CreamInstaller/Resources/UplayR1.cs index b90e87f..800219f 100644 --- a/CreamInstaller/Resources/UplayR1.cs +++ b/CreamInstaller/Resources/UplayR1.cs @@ -123,7 +123,7 @@ internal static class UplayR1 installForm.UpdateUser("Generating Uplay R1 Unlocker configuration for " + selection.Name + $" in directory \"{directory}\" . . . ", InstallationLog.Operation); File.Create(config).Close(); StreamWriter writer = new(config, true, Encoding.UTF8); - WriteConfig(writer, new(blacklistDlc.ToDictionary(pair => pair.Key, pair => pair.Value), AppIdComparer.Comparer), installForm); + WriteConfig(writer, new(blacklistDlc.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.Strings), installForm); writer.Flush(); writer.Close(); } diff --git a/CreamInstaller/Resources/UplayR2.cs b/CreamInstaller/Resources/UplayR2.cs index 8408a74..0a296a6 100644 --- a/CreamInstaller/Resources/UplayR2.cs +++ b/CreamInstaller/Resources/UplayR2.cs @@ -131,7 +131,7 @@ internal static class UplayR2 installForm.UpdateUser("Generating Uplay R2 Unlocker configuration for " + selection.Name + $" in directory \"{directory}\" . . . ", InstallationLog.Operation); File.Create(config).Close(); StreamWriter writer = new(config, true, Encoding.UTF8); - WriteConfig(writer, new(blacklistDlc.ToDictionary(pair => pair.Key, pair => pair.Value), AppIdComparer.Comparer), installForm); + WriteConfig(writer, new(blacklistDlc.ToDictionary(pair => pair.Key, pair => pair.Value), PlatformIdComparer.Strings), installForm); writer.Flush(); writer.Close(); } diff --git a/CreamInstaller/Utility/ProgramData.cs b/CreamInstaller/Utility/ProgramData.cs index b4a1fe7..714f08f 100644 --- a/CreamInstaller/Utility/ProgramData.cs +++ b/CreamInstaller/Utility/ProgramData.cs @@ -1,8 +1,12 @@ -using System; +using Newtonsoft.Json; + +using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; +using System.Text.Json; +using System.Text.Json.Nodes; using System.Threading.Tasks; using System.Windows.Forms; @@ -20,7 +24,8 @@ internal static class ProgramData internal static readonly string CooldownPath = DirectoryPath + @"\cooldown"; - internal static readonly string ChoicesPath = DirectoryPath + @"\choices.txt"; + internal static readonly string OldChoicesPath = DirectoryPath + @"\choices.txt"; + internal static readonly string ChoicesPath = DirectoryPath + @"\choices.json"; internal static async Task Setup() => await Task.Run(() => { @@ -38,6 +43,8 @@ internal static class ProgramData } if (!Directory.Exists(CooldownPath)) _ = Directory.CreateDirectory(CooldownPath); + if (File.Exists(OldChoicesPath)) + File.Delete(OldChoicesPath); }); internal static bool CheckCooldown(string identifier, int cooldown) @@ -78,23 +85,24 @@ internal static class ProgramData catch { } } - internal static List ReadChoices() + internal static List<(Platform platform, string id)> ReadChoices() { - if (!File.Exists(ChoicesPath)) return new(); + if (!File.Exists(ChoicesPath)) return null; try { - return File.ReadAllLines(ChoicesPath).ToList(); + return JsonConvert.DeserializeObject(File.ReadAllText(ChoicesPath), + typeof(List<(Platform platform, string id)>)) as List<(Platform platform, string id)>; } catch { return new(); } } - internal static void WriteChoices(List choices) + internal static void WriteChoices(List<(Platform platform, string id)> choices) { try { - File.WriteAllLines(ChoicesPath, choices.ToArray()); + File.WriteAllText(ChoicesPath, JsonConvert.SerializeObject(choices)); } catch { } }