- Selections are now properly separated by platform, fixing potential future issues related to two games on different platforms having the same id
- Saved pre-scan choices now have to be saved in JSON, and thus will be reset
- Selection nodes now display their platform and are now also sorted by their platform
This commit is contained in:
pointfeev 2022-08-17 18:48:05 -04:00
parent b22dddde48
commit 5f3329305c
14 changed files with 187 additions and 157 deletions

View file

@ -1,19 +0,0 @@
using System.Collections.Generic;
namespace CreamInstaller.Components;
internal class AppIdComparer : IComparer<string>
{
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;
}

View file

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

View file

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

View file

@ -5,7 +5,7 @@
<UseWindowsForms>True</UseWindowsForms>
<ApplicationIcon>Resources\ini.ico</ApplicationIcon>
<IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>
<Version>3.6.0.0</Version>
<Version>3.6.1.0</Version>
<PackageIcon>Resources\ini.ico</PackageIcon>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<Copyright>2021, pointfeev (https://github.com/pointfeev)</Copyright>

View file

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

View file

@ -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<TreeNode>().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<string> 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<string> choices = new();
List<(Platform platform, string id)> choices = new();
foreach (TreeNode node in selectionTreeView.Nodes.Cast<TreeNode>().Where(n => n.Checked))
choices.Add(node.Name);
choices.Add(((Platform)node.Tag, node.Name));
ProgramData.WriteChoices(choices);
loadButton.Enabled = File.Exists(ProgramData.ChoicesPath);
}

View file

@ -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<Task> appTasks = new();
if (ProgramsToScan.Any(c => c.platform == "Paradox"))
if (ProgramsToScan.Any(c => c.platform is Platform.Paradox))
{
List<string> steamDllDirectories = await SteamLibrary.GetDllDirectoriesFromGameDirectory(ParadoxLauncher.InstallPath);
List<string> epicDllDirectories = await EpicLibrary.GetDllDirectoriesFromGameDirectory(ParadoxLauncher.InstallPath);
List<string> 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<Manifest> 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<string, (string name, string product, string icon, string developer)> 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<ContextMenuItem> 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<string> 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

View file

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

View file

@ -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<string> DllDirectories;
internal bool IsSteam;
internal bool IsEpic;
internal bool IsUbisoft;
internal readonly SortedList<string, (DlcType type, string name, string icon)> AllDlc = new(AppIdComparer.Comparer);
internal readonly SortedList<string, (DlcType type, string name, string icon)> SelectedDlc = new(AppIdComparer.Comparer);
internal readonly SortedList<string, (DlcType type, string name, string icon)> AllDlc = new(PlatformIdComparer.Strings);
internal readonly SortedList<string, (DlcType type, string name, string icon)> SelectedDlc = new(PlatformIdComparer.Strings);
internal readonly List<(string id, string name, SortedList<string, (DlcType type, string name, string icon)> dlc)> ExtraDlc = new(); // for Paradox Launcher
internal readonly List<(string id, string name, SortedList<string, (DlcType type, string name, string icon)> 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<ProgramSelection> All = new();
@ -160,11 +167,11 @@ internal class ProgramSelection
internal static List<ProgramSelection> 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<string, (DlcType type, string name, string icon)> pair in selection.AllDlc.Where(p => p.Key == dlcId))
return (selection.Id, pair.Value);
return null;

View file

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

View file

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

View file

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

View file

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

View file

@ -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<string> 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<string> choices)
internal static void WriteChoices(List<(Platform platform, string id)> choices)
{
try
{
File.WriteAllLines(ChoicesPath, choices.ToArray());
File.WriteAllText(ChoicesPath, JsonConvert.SerializeObject(choices));
}
catch { }
}