diff --git a/CreamInstaller/Components/CustomTreeView.cs b/CreamInstaller/Components/CustomTreeView.cs
index 74ae1e0..8b510bc 100644
--- a/CreamInstaller/Components/CustomTreeView.cs
+++ b/CreamInstaller/Components/CustomTreeView.cs
@@ -34,7 +34,8 @@ internal class CustomTreeView : TreeView
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" || subText[0] == 'v' && Version.TryParse(subText[1..], out _))
+ if (string.IsNullOrWhiteSpace(subText) || subText == "ParadoxLauncher"
+ || ProgramSelection.FromId(subText) is null && ProgramSelection.GetDlcFromId(subText) is null)
return;
Size subSize = TextRenderer.MeasureText(graphics, subText, subFont);
diff --git a/CreamInstaller/CreamInstaller.csproj b/CreamInstaller/CreamInstaller.csproj
index 280cb25..11ffc83 100644
--- a/CreamInstaller/CreamInstaller.csproj
+++ b/CreamInstaller/CreamInstaller.csproj
@@ -5,7 +5,7 @@
True
Resources\ini.ico
true
- 3.0.1.1
+ 3.0.2.0
Resources\ini.ico
diff --git a/CreamInstaller/Epic/EpicStore.cs b/CreamInstaller/Epic/EpicStore.cs
index c23ee5a..bf24ce8 100644
--- a/CreamInstaller/Epic/EpicStore.cs
+++ b/CreamInstaller/Epic/EpicStore.cs
@@ -15,18 +15,41 @@ namespace CreamInstaller.Epic;
internal static class EpicStore
{
- internal static async Task> ParseDlcIds(string categoryNamespace)
+ // need a method to query catalog items
+
+ internal static async Task> QueryEntitlements(Manifest manifest)
{
- // this method does not yet find ALL dlcs
+ string @namespace = manifest.CatalogNamespace;
+ string mainId = manifest.MainGameCatalogItemId;
List<(string id, string name, string product, string icon, string developer)> dlcIds = new();
- Response response = await QueryGraphQL(categoryNamespace);
+ Response response = await QueryGraphQL(@namespace);
if (response is null) return dlcIds;
- try { File.WriteAllText(ProgramData.AppInfoPath + @$"\{categoryNamespace}.json", JsonConvert.SerializeObject(response, Formatting.Indented)); } catch { }
- List elements = new(response.Data.Catalog.CatalogOffers.Elements);
- foreach (Element element in elements)
+ try { File.WriteAllText(ProgramData.AppInfoPath + @$"\{@namespace}.json", JsonConvert.SerializeObject(response, Formatting.Indented)); } catch { }
+ List storeElements = new(response.Data.Catalog.SearchStore.Elements);
+ foreach (Element element in storeElements)
{
- string product = null;
- try { product = element.CatalogNs.Mappings[0].PageSlug; } catch { }
+ string title = element.Title;
+ string product = (element.CatalogNs is not null && element.CatalogNs.Mappings.Any())
+ ? element.CatalogNs.Mappings.First().PageSlug : null;
+ string icon = null;
+ for (int i = 0; i < element.KeyImages?.Length; i++)
+ {
+ KeyImage keyImage = element.KeyImages[i];
+ if (keyImage.Type == "DieselStoreFront")
+ {
+ icon = keyImage.Url;
+ break;
+ }
+ }
+ foreach (Item item in element.Items)
+ dlcIds.Populate(item.Id, title, product, icon, null, canOverwrite: element.Items.Length == 1);
+ }
+ List catalogElements = new(response.Data.Catalog.CatalogOffers.Elements);
+ foreach (Element element in catalogElements)
+ {
+ string title = element.Title;
+ string product = (element.CatalogNs is not null && element.CatalogNs.Mappings.Any())
+ ? element.CatalogNs.Mappings.First().PageSlug : null;
string icon = null;
for (int i = 0; i < element.KeyImages?.Length; i++)
{
@@ -38,16 +61,31 @@ internal static class EpicStore
}
}
foreach (Item item in element.Items)
- {
- (string id, string name, string product, string icon, string developer) app = (item.Id, element.Title, product, icon, item.Developer);
- if (!dlcIds.Any(a => a.id == app.id))
- dlcIds.Add(app);
- }
+ dlcIds.Populate(item.Id, title, product, icon, item.Developer, canOverwrite: element.Items.Length == 1);
}
return dlcIds;
}
- internal static async Task QueryGraphQL(string categoryNamespace)
+ private static void Populate(this List<(string id, string name, string product, string icon, string developer)> dlcIds, string id, string title, string product, string icon, string developer, bool canOverwrite = false)
+ {
+ if (id == null) return;
+ bool found = false;
+ for (int i = 0; i < dlcIds.Count; i++)
+ {
+ (string id, string name, string product, string icon, string developer) app = dlcIds[i];
+ if (app.id == id)
+ {
+ found = true;
+ dlcIds[i] = canOverwrite
+ ? (app.id, title ?? app.name, product ?? app.product, icon ?? app.icon, developer ?? app.developer)
+ : (app.id, app.name ?? title, app.product ?? product, app.icon ?? icon, app.developer ?? developer);
+ break;
+ }
+ }
+ if (!found) dlcIds.Add((id, title, product, icon, developer));
+ }
+
+ private static async Task QueryGraphQL(string categoryNamespace)
{
string encoded = HttpUtility.UrlEncode(categoryNamespace);
Request request = new(encoded);
diff --git a/CreamInstaller/Epic/GraphQL/Request.cs b/CreamInstaller/Epic/GraphQL/Request.cs
index ff937b9..1d76786 100644
--- a/CreamInstaller/Epic/GraphQL/Request.cs
+++ b/CreamInstaller/Epic/GraphQL/Request.cs
@@ -13,6 +13,21 @@ internal class Request
[JsonProperty(PropertyName = "query")]
private string _gqlQuery => @"query searchOffers($namespace: String!) {
Catalog {
+ searchStore(category: ""*"", namespace: $namespace){
+ elements {
+ id
+ title
+ developer
+ items {
+ id
+ }
+ catalogNs {
+ mappings(pageType: ""productHome"") {
+ pageSlug
+ }
+ }
+ }
+ }
catalogOffers(
namespace: $namespace
params: {
@@ -20,6 +35,7 @@ internal class Request
}
) {
elements {
+ id
title
keyImages {
type
@@ -27,6 +43,7 @@ internal class Request
}
items {
id
+ title
developer
}
catalogNs {
@@ -52,15 +69,6 @@ internal class Request
private class Variables
{
- [JsonProperty(PropertyName = "category")]
- private string _category => "games/edition/base|bundles/games|editors|software/edition/base";
-
- [JsonProperty(PropertyName = "count")]
- private int _count => 1000;
-
- [JsonProperty(PropertyName = "keywords")]
- private string _keywords => "";
-
[JsonProperty(PropertyName = "namespace")]
private string _namespace { get; set; }
diff --git a/CreamInstaller/Epic/GraphQL/Response.cs b/CreamInstaller/Epic/GraphQL/Response.cs
index 53f01ce..b4a6924 100644
--- a/CreamInstaller/Epic/GraphQL/Response.cs
+++ b/CreamInstaller/Epic/GraphQL/Response.cs
@@ -17,10 +17,19 @@ public class Data
public class Catalog
{
+ [JsonProperty(PropertyName = "searchStore")]
+ public SearchStore SearchStore { get; protected set; }
+
[JsonProperty(PropertyName = "catalogOffers")]
public CatalogOffers CatalogOffers { get; protected set; }
}
+public class SearchStore
+{
+ [JsonProperty(PropertyName = "elements")]
+ public Element[] Elements { get; protected set; }
+}
+
public class CatalogOffers
{
[JsonProperty(PropertyName = "elements")]
@@ -29,6 +38,9 @@ public class CatalogOffers
public class Element
{
+ [JsonProperty(PropertyName = "id")]
+ public string Id { get; protected set; }
+
[JsonProperty(PropertyName = "title")]
public string Title { get; protected set; }
@@ -47,6 +59,9 @@ public class Item
[JsonProperty(PropertyName = "id")]
public string Id { get; protected set; }
+ [JsonProperty(PropertyName = "title")]
+ public string Title { get; protected set; }
+
[JsonProperty(PropertyName = "developer")]
public string Developer { get; protected set; }
}
diff --git a/CreamInstaller/InstallForm.cs b/CreamInstaller/InstallForm.cs
index 6587bcc..cf00e42 100644
--- a/CreamInstaller/InstallForm.cs
+++ b/CreamInstaller/InstallForm.cs
@@ -53,22 +53,22 @@ internal partial class InstallForm : CustomForm
}
}
- internal static void WriteCreamConfiguration(StreamWriter writer, string steamAppId, string name, SortedList steamDlcApps, InstallForm installForm = null)
+ internal static void WriteCreamConfiguration(StreamWriter writer, string appId, string name, SortedList dlc, InstallForm installForm = null)
{
writer.WriteLine($"; {name}");
writer.WriteLine("[steam]");
- writer.WriteLine($"appid = {steamAppId}");
+ writer.WriteLine($"appid = {appId}");
writer.WriteLine();
writer.WriteLine("[dlc]");
if (installForm is not null)
- installForm.UpdateUser($"Added game to cream_api.ini with appid {steamAppId} ({name})", InstallationLog.Resource, info: false);
- foreach (KeyValuePair pair in steamDlcApps)
+ installForm.UpdateUser($"Added game to cream_api.ini with appid {appId} ({name})", InstallationLog.Resource, info: false);
+ foreach (KeyValuePair pair in dlc)
{
- string appId = pair.Key;
- (string dlcName, _) = pair.Value;
- writer.WriteLine($"{appId} = {dlcName}");
+ string dlcId = pair.Key;
+ (_, string dlcName, _) = pair.Value;
+ writer.WriteLine($"{dlcId} = {dlcName}");
if (installForm is not null)
- installForm.UpdateUser($"Added DLC to cream_api.ini with appid {appId} ({dlcName})", InstallationLog.Resource, info: false);
+ installForm.UpdateUser($"Added DLC to cream_api.ini with appid {dlcId} ({dlcName})", InstallationLog.Resource, info: false);
}
}
@@ -140,13 +140,13 @@ internal partial class InstallForm : CustomForm
StreamWriter writer = new(cApi, true, Encoding.UTF8);
if (selection.Id != "ParadoxLauncher")
WriteCreamConfiguration(writer, selection.Id, selection.Name, selection.SelectedDlc, installForm);
- foreach (Tuple> extraAppDlc in selection.ExtraDlc)
+ foreach (Tuple> extraAppDlc in selection.ExtraDlc)
WriteCreamConfiguration(writer, extraAppDlc.Item1, extraAppDlc.Item2, extraAppDlc.Item3, installForm);
writer.Flush();
writer.Close();
});
- internal static void WriteScreamConfiguration(StreamWriter writer, SortedList dlcApps, InstallForm installForm = null)
+ internal static void WriteScreamConfiguration(StreamWriter writer, SortedList dlc, InstallForm installForm = null)
{
writer.WriteLine("{");
writer.WriteLine(" \"version\": 2,");
@@ -154,34 +154,54 @@ internal partial class InstallForm : CustomForm
writer.WriteLine(" \"eos_logging\": false,");
writer.WriteLine(" \"block_metrics\": false,");
writer.WriteLine(" \"catalog_items\": {");
- writer.WriteLine(" \"unlock_all\": true,"); //writer.WriteLine(" \"unlock_all\": false,");
- writer.WriteLine(" \"override\": []"); //writer.WriteLine(" \"override\": [");
- /*KeyValuePair last = dlcApps.Last();
- foreach (KeyValuePair pair in dlcApps)
+ IEnumerable> catalogItems = dlc.Where(pair => pair.Value.type == DlcType.CatalogItem);
+ if (catalogItems.Any())
{
- string id = pair.Key;
- (string name, _) = pair.Value;
- writer.WriteLine($" \"{id}\"{(pair.Equals(last) ? "" : ",")}");
- if (installForm is not null)
- installForm.UpdateUser($"Added DLC to ScreamAPI.json with id {id} ({name})", InstallationLog.Resource, info: false);
+ writer.WriteLine(" \"unlock_all\": false,");
+ writer.WriteLine(" \"override\": [");
+ KeyValuePair lastCatalogItem = catalogItems.Last();
+ foreach (KeyValuePair pair in catalogItems)
+ {
+ string id = pair.Key;
+ (_, string name, _) = pair.Value;
+ writer.WriteLine($" \"{id}\"{(pair.Equals(lastCatalogItem) ? "" : ",")}");
+ if (installForm is not null)
+ installForm.UpdateUser($"Added catalog item to ScreamAPI.json with id {id} ({name})", InstallationLog.Resource, info: false);
+ }
+ writer.WriteLine(" ]");
+ }
+ else
+ {
+ writer.WriteLine(" \"unlock_all\": true,");
+ writer.WriteLine(" \"override\": []");
}
- writer.WriteLine(" ]");*/
writer.WriteLine(" },");
writer.WriteLine(" \"entitlements\": {");
- writer.WriteLine(" \"unlock_all\": true,"); //writer.WriteLine(" \"unlock_all\": false,");
- writer.WriteLine(" \"auto_inject\": true,"); //writer.WriteLine(" \"auto_inject\": false,");
- writer.WriteLine(" \"inject\": []"); //writer.WriteLine(" \"inject\": [");
- /*foreach (KeyValuePair pair in dlcApps)
+ IEnumerable> entitlements = dlc.Where(pair => pair.Value.type == DlcType.Entitlement);
+ if (entitlements.Any())
{
- string id = pair.Key;
- (string name, _) = pair.Value;
- writer.WriteLine($" \"{id}\"{(pair.Equals(last) ? "" : ",")}");
+ writer.WriteLine(" \"unlock_all\": false,");
+ writer.WriteLine(" \"auto_inject\": false,");
+ writer.WriteLine(" \"inject\": [");
+ KeyValuePair lastEntitlement = entitlements.Last();
+ foreach (KeyValuePair pair in entitlements)
+ {
+ string id = pair.Key;
+ (_, string name, _) = pair.Value;
+ writer.WriteLine($" \"{id}\"{(pair.Equals(lastEntitlement) ? "" : ",")}");
+ if (installForm is not null)
+ installForm.UpdateUser($"Added entitlement to ScreamAPI.json with id {id} ({name})", InstallationLog.Resource, info: false);
+ }
+ writer.WriteLine(" ]");
+ }
+ else
+ {
+ writer.WriteLine(" \"unlock_all\": true,");
+ writer.WriteLine(" \"auto_inject\": true,");
+ writer.WriteLine(" \"inject\": []");
}
- writer.WriteLine(" ]");*/
writer.WriteLine(" }");
writer.WriteLine("}");
- if (installForm is not null)
- installForm.UpdateUser($"Created 'unlock_all: true' ScreamAPI.json configuration (temporary until I figure out how to properly get all DLC ids)", InstallationLog.Resource, info: false);
}
internal static async Task UninstallScreamAPI(string directory, InstallForm installForm = null) => await Task.Run(() =>
@@ -252,7 +272,7 @@ internal partial class InstallForm : CustomForm
StreamWriter writer = new(sApi, true, Encoding.UTF8);
if (selection.Id != "ParadoxLauncher")
WriteScreamConfiguration(writer, selection.SelectedDlc, installForm);
- foreach (Tuple> extraAppDlc in selection.ExtraDlc)
+ foreach (Tuple> extraAppDlc in selection.ExtraDlc)
WriteScreamConfiguration(writer, extraAppDlc.Item3, installForm);
writer.Flush();
writer.Close();
diff --git a/CreamInstaller/ProgramSelection.cs b/CreamInstaller/ProgramSelection.cs
index 2f0b5af..d206712 100644
--- a/CreamInstaller/ProgramSelection.cs
+++ b/CreamInstaller/ProgramSelection.cs
@@ -7,6 +7,13 @@ using Gameloop.Vdf.Linq;
namespace CreamInstaller;
+internal enum DlcType
+{
+ Default = 0,
+ CatalogItem = 1,
+ Entitlement = 2
+}
+
internal class ProgramSelection
{
internal bool Enabled = false;
@@ -16,7 +23,6 @@ internal class ProgramSelection
internal string Name = "Program";
internal string ProductUrl = null;
-
internal string IconUrl = null;
internal string ClientIconUrl = null;
@@ -28,9 +34,9 @@ internal class ProgramSelection
internal bool IsSteam = false;
internal VProperty AppInfo = null;
- internal readonly SortedList AllDlc = new();
- internal readonly SortedList SelectedDlc = new();
- internal readonly List>> ExtraDlc = new();
+ internal readonly SortedList AllDlc = new();
+ internal readonly SortedList SelectedDlc = new();
+ internal readonly List>> ExtraDlc = new(); // for Paradox Launcher
internal bool AreDllsLocked
{
@@ -57,19 +63,19 @@ internal class ProgramSelection
}
}
- private void Toggle(string dlcAppId, (string name, string icon) dlcApp, bool enabled)
+ private void Toggle(string dlcAppId, (DlcType type, string name, string icon) dlcApp, bool enabled)
{
if (enabled) SelectedDlc[dlcAppId] = dlcApp;
else SelectedDlc.Remove(dlcAppId);
}
- internal void ToggleDlc(string dlcAppId, bool enabled)
+ internal void ToggleDlc(string dlcId, bool enabled)
{
- foreach (KeyValuePair pair in AllDlc)
+ foreach (KeyValuePair pair in AllDlc)
{
string appId = pair.Key;
- (string name, string icon) dlcApp = pair.Value;
- if (appId == dlcAppId)
+ (DlcType type, string name, string icon) dlcApp = pair.Value;
+ if (appId == dlcId)
{
Toggle(appId, dlcApp, enabled);
break;
@@ -78,18 +84,6 @@ internal class ProgramSelection
Enabled = SelectedDlc.Any();
}
- internal void ToggleAllDlc(bool enabled)
- {
- if (!enabled) SelectedDlc.Clear();
- else foreach (KeyValuePair pair in AllDlc)
- {
- string appId = pair.Key;
- (string name, string icon) dlcApp = pair.Value;
- Toggle(appId, dlcApp, enabled);
- }
- Enabled = SelectedDlc.Any();
- }
-
internal ProgramSelection() => All.Add(this);
internal void Validate()
@@ -118,13 +112,13 @@ internal class ProgramSelection
internal static List AllUsableEnabled => AllUsable.FindAll(s => s.Enabled);
- internal static ProgramSelection FromId(string id) => AllUsable.Find(s => s.Id == id);
+ internal static ProgramSelection FromId(string gameId) => AllUsable.Find(s => s.Id == gameId);
- internal static (string gameAppId, (string name, string icon) app)? GetDlcFromId(string appId)
+ internal static (string gameId, (DlcType type, string name, string icon) app)? GetDlcFromId(string dlcId)
{
foreach (ProgramSelection selection in AllUsable)
- foreach (KeyValuePair pair in selection.AllDlc)
- if (pair.Key == appId) return (selection.Id, pair.Value);
+ foreach (KeyValuePair pair in selection.AllDlc)
+ if (pair.Key == dlcId) return (selection.Id, pair.Value);
return null;
}
}
diff --git a/CreamInstaller/SelectForm.cs b/CreamInstaller/SelectForm.cs
index a85fea8..8864534 100644
--- a/CreamInstaller/SelectForm.cs
+++ b/CreamInstaller/SelectForm.cs
@@ -140,7 +140,7 @@ internal partial class SelectForm : CustomForm
return;
}
if (Program.Canceled) return;
- ConcurrentDictionary dlc = new();
+ ConcurrentDictionary dlc = new();
List dlcTasks = new();
List dlcIds = await SteamCMD.ParseDlcAppIds(appInfo);
await SteamStore.ParseDlcAppIds(appId, dlcIds);
@@ -167,7 +167,7 @@ internal partial class SelectForm : CustomForm
}
if (Program.Canceled) return;
if (!string.IsNullOrWhiteSpace(dlcName))
- dlc[dlcAppId] = (dlcName, dlcIconStaticId);
+ dlc[dlcAppId] = (DlcType.Default, dlcName, dlcIconStaticId);
RemoveFromRemainingDLCs(dlcAppId);
progress.Report(++CompleteTasks);
});
@@ -190,7 +190,7 @@ internal partial class SelectForm : CustomForm
}
selection ??= new();
- if (allCheckBox.Checked) selection.Enabled = true;
+ selection.Enabled = allCheckBox.Checked || selection.SelectedDlc.Any();
selection.Usable = true;
selection.Id = appId;
selection.Name = name;
@@ -213,11 +213,11 @@ internal partial class SelectForm : CustomForm
programNode.Checked = selection.Enabled;
programNode.Remove();
selectionTreeView.Nodes.Add(programNode);
- foreach (KeyValuePair pair in dlc)
+ foreach (KeyValuePair pair in dlc)
{
if (Program.Canceled || programNode is null) return;
string appId = pair.Key;
- (string name, string icon) dlcApp = pair.Value;
+ (DlcType type, string name, string icon) dlcApp = pair.Value;
selection.AllDlc[appId] = dlcApp;
if (allCheckBox.Checked) selection.SelectedDlc[appId] = dlcApp;
TreeNode dlcNode = TreeNodes.Find(s => s.Name == appId) ?? new();
@@ -243,7 +243,6 @@ internal partial class SelectForm : CustomForm
foreach (Manifest manifest in epicGames)
{
string @namespace = manifest.CatalogNamespace;
- string mainId = manifest.MainGameCatalogItemId;
string name = manifest.DisplayName;
string directory = manifest.InstallLocation;
ProgramSelection selection = ProgramSelection.FromId(@namespace);
@@ -260,19 +259,19 @@ internal partial class SelectForm : CustomForm
return;
}
if (Program.Canceled) return;
- ConcurrentDictionary dlc = new();
+ ConcurrentDictionary entitlements = new();
List dlcTasks = new();
- List<(string id, string name, string product, string icon, string developer)> dlcIds = await EpicStore.ParseDlcIds(@namespace);
- if (dlcIds.Count > 0)
+ List<(string id, string name, string product, string icon, string developer)> entitlementIds = await EpicStore.QueryEntitlements(manifest);
+ if (entitlementIds.Any())
{
- foreach ((string id, string name, string product, string icon, string developer) in dlcIds)
+ foreach ((string id, string name, string product, string icon, string developer) in entitlementIds)
{
if (Program.Canceled) return;
AddToRemainingDLCs(id);
Task task = Task.Run(() =>
{
if (Program.Canceled) return;
- dlc[id] = (name, product, icon, developer);
+ entitlements[id] = (name, product, icon, developer);
RemoveFromRemainingDLCs(id);
progress.Report(++CompleteTasks);
});
@@ -282,7 +281,7 @@ internal partial class SelectForm : CustomForm
Thread.Sleep(10); // to reduce control & window freezing
}
}
- else
+ if (/*!catalogItems.Any() && */!entitlements.Any())
{
RemoveFromRemainingGames(name);
return;
@@ -295,13 +294,13 @@ internal partial class SelectForm : CustomForm
}
selection ??= new();
- if (allCheckBox.Checked) selection.Enabled = true;
+ selection.Enabled = allCheckBox.Checked || selection.SelectedDlc.Any();
selection.Usable = true;
selection.Id = @namespace;
selection.Name = name;
selection.RootDirectory = directory;
selection.DllDirectories = dllDirectories;
- foreach (KeyValuePair pair in dlc)
+ foreach (KeyValuePair pair in entitlements)
if (pair.Value.name == selection.Name)
{
selection.ProductUrl = "https://www.epicgames.com/store/product/" + pair.Value.product;
@@ -319,20 +318,35 @@ internal partial class SelectForm : CustomForm
programNode.Checked = selection.Enabled;
programNode.Remove();
selectionTreeView.Nodes.Add(programNode);
- /*foreach (KeyValuePair pair in dlc)
+ /*TreeNode catalogItemsNode = TreeNodes.Find(s => s.Name == @namespace + "_catalogItems") ?? new();
+ catalogItemsNode.Name = @namespace + "_catalogItems";
+ catalogItemsNode.Text = "Catalog Items";
+ catalogItemsNode.Checked = selection.SelectedDlc.Any(pair => pair.Value.type == DlcType.CatalogItem);
+ catalogItemsNode.Remove();
+ programNode.Nodes.Add(catalogItemsNode);*/
+ if (entitlements.Any())
{
- if (Program.Canceled || programNode is null) return;
- string dlcId = pair.Key;
- (string name, string icon) dlcApp = (pair.Value.name, pair.Value.icon);
- selection.AllDlc[dlcId] = dlcApp;
- if (allCheckBox.Checked) selection.SelectedDlc[dlcId] = dlcApp;
- TreeNode dlcNode = TreeNodes.Find(s => s.Name == dlcId) ?? new();
- dlcNode.Name = dlcId;
- dlcNode.Text = dlcApp.name;
- dlcNode.Checked = selection.SelectedDlc.ContainsKey(dlcId);
- dlcNode.Remove();
- programNode.Nodes.Add(dlcNode);
- }*/
+ /*TreeNode entitlementsNode = TreeNodes.Find(s => s.Name == @namespace + "_entitlements") ?? new();
+ entitlementsNode.Name = @namespace + "_entitlements";
+ entitlementsNode.Text = "Entitlements";
+ entitlementsNode.Checked = selection.SelectedDlc.Any(pair => pair.Value.type == DlcType.Entitlement);
+ entitlementsNode.Remove();
+ programNode.Nodes.Add(entitlementsNode);*/
+ foreach (KeyValuePair pair in entitlements)
+ {
+ if (Program.Canceled || programNode is null/* || entitlementsNode is null*/) return;
+ string dlcId = pair.Key;
+ (DlcType type, string name, string icon) dlcApp = (DlcType.Entitlement, pair.Value.name, pair.Value.icon);
+ selection.AllDlc[dlcId] = dlcApp;
+ if (allCheckBox.Checked) selection.SelectedDlc[dlcId] = dlcApp;
+ TreeNode dlcNode = TreeNodes.Find(s => s.Name == dlcId) ?? new();
+ dlcNode.Name = dlcId;
+ dlcNode.Text = dlcApp.name;
+ dlcNode.Checked = selection.SelectedDlc.ContainsKey(dlcId);
+ dlcNode.Remove();
+ programNode.Nodes.Add(dlcNode); //entitlementsNode.Nodes.Add(dlcNode);
+ }
+ }
});
if (Program.Canceled) return;
RemoveFromRemainingGames(name);
@@ -395,7 +409,8 @@ internal partial class SelectForm : CustomForm
ProgramSelection.ValidateAll();
TreeNodes.ForEach(node =>
{
- if (!int.TryParse(node.Name, out int appId) || node.Parent is null && ProgramSelection.FromId(node.Name) is null) node.Remove();
+ if (node.Parent is null && ProgramSelection.FromId(node.Name) is null)
+ node.Remove();
});
await GetApplicablePrograms(iProgress);
await SteamCMD.Cleanup();
@@ -422,37 +437,47 @@ internal partial class SelectForm : CustomForm
{
if (e.Action == TreeViewAction.Unknown) return;
TreeNode node = e.Node;
- if (node is not null)
- {
- string appId = node.Name;
- ProgramSelection selection = ProgramSelection.FromId(appId);
- if (selection is null)
- {
- TreeNode parent = node.Parent;
- if (parent is not null)
- {
- string gameAppId = parent.Name;
- ProgramSelection.FromId(gameAppId).ToggleDlc(appId, node.Checked);
- parent.Checked = parent.Nodes.Cast().ToList().Any(treeNode => treeNode.Checked);
- }
- }
- else
- {
- if (selection.AllDlc.Any())
- {
- selection.ToggleAllDlc(node.Checked);
- node.Nodes.Cast().ToList().ForEach(treeNode => treeNode.Checked = node.Checked);
- }
- else selection.Enabled = node.Checked;
- allCheckBox.CheckedChanged -= OnAllCheckBoxChanged;
- allCheckBox.Checked = TreeNodes.TrueForAll(treeNode => treeNode.Checked);
- allCheckBox.CheckedChanged += OnAllCheckBoxChanged;
- }
- }
+ if (node is null) return;
+ CheckNode(node);
+ SyncNodeParents(node);
+ SyncNodeDescendants(node);
+ allCheckBox.CheckedChanged -= OnAllCheckBoxChanged;
+ allCheckBox.Checked = TreeNodes.TrueForAll(treeNode => treeNode.Checked);
+ allCheckBox.CheckedChanged += OnAllCheckBoxChanged;
installButton.Enabled = ProgramSelection.AllUsableEnabled.Any();
uninstallButton.Enabled = installButton.Enabled;
}
+ private static void SyncNodeParents(TreeNode node)
+ {
+ TreeNode parentNode = node.Parent;
+ if (parentNode is not null)
+ {
+ parentNode.Checked = parentNode.Nodes.Cast().ToList().Any(childNode => childNode.Checked);
+ SyncNodeParents(parentNode);
+ }
+ }
+
+ private static void SyncNodeDescendants(TreeNode node) =>
+ node.Nodes.Cast().ToList().ForEach(childNode =>
+ {
+ childNode.Checked = node.Checked;
+ CheckNode(childNode);
+ SyncNodeDescendants(childNode);
+ });
+
+ private static void CheckNode(TreeNode node)
+ {
+ (string gameId, (DlcType type, string name, string icon) app)? dlc = ProgramSelection.GetDlcFromId(node.Name);
+ if (dlc.HasValue)
+ {
+ (string gameId, _) = dlc.Value;
+ ProgramSelection selection = ProgramSelection.FromId(gameId);
+ if (selection is not null)
+ selection.ToggleDlc(node.Name, node.Checked);
+ }
+ }
+
internal List TreeNodes => GatherTreeNodes(selectionTreeView.Nodes);
private List GatherTreeNodes(TreeNodeCollection nodeCollection)
{
@@ -547,14 +572,14 @@ internal partial class SelectForm : CustomForm
selectionTreeView.NodeMouseClick += (sender, e) =>
{
TreeNode node = e.Node;
- TreeNode parentNode = node.Parent;
+ if (!node.Bounds.Contains(e.Location) || e.Button != MouseButtons.Right) return;
+ selectionTreeView.SelectedNode = node;
string id = node.Name;
ProgramSelection selection = ProgramSelection.FromId(id);
- (string gameAppId, (string name, string icon) app)? dlc = null;
+ (string gameAppId, (DlcType type, string name, string icon) app)? dlc = null;
if (selection is null) dlc = ProgramSelection.GetDlcFromId(id);
- if (e.Button == MouseButtons.Right && node.Bounds.Contains(e.Location))
+ if (selection is not null || dlc is not null)
{
- selectionTreeView.SelectedNode = node;
nodeContextMenu.Items.Clear();
ToolStripMenuItem header = new(selection?.Name ?? node.Text, Image("Icon_" + id));
if (header.Image is null)