v3.0.2.0
- Overhauled and made OnTreeViewNodeCheckedChanged method more stable - Made node sub-text display more specific to only game and dlc ids - Fixed program validating tree nodes based on the pre-Epic-support id system - Overhauled Epic Games entitlement querying and parsing - Added back ScreamAPI entitlement selection, with preparations to add catalog item selection
This commit is contained in:
parent
40fe83c0b9
commit
8c8c007684
8 changed files with 241 additions and 140 deletions
|
@ -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);
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<UseWindowsForms>True</UseWindowsForms>
|
||||
<ApplicationIcon>Resources\ini.ico</ApplicationIcon>
|
||||
<IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>
|
||||
<Version>3.0.1.1</Version>
|
||||
<Version>3.0.2.0</Version>
|
||||
<PackageIcon>Resources\ini.ico</PackageIcon>
|
||||
<PackageIconUrl />
|
||||
<Description />
|
||||
|
|
|
@ -15,18 +15,41 @@ namespace CreamInstaller.Epic;
|
|||
|
||||
internal static class EpicStore
|
||||
{
|
||||
internal static async Task<List<(string id, string name, string product, string icon, string developer)>> ParseDlcIds(string categoryNamespace)
|
||||
// need a method to query catalog items
|
||||
|
||||
internal static async Task<List<(string id, string name, string product, string icon, string developer)>> 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<Element> 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<Element> 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<Element> 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<Response> 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<Response> QueryGraphQL(string categoryNamespace)
|
||||
{
|
||||
string encoded = HttpUtility.UrlEncode(categoryNamespace);
|
||||
Request request = new(encoded);
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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; }
|
||||
}
|
||||
|
|
|
@ -53,22 +53,22 @@ internal partial class InstallForm : CustomForm
|
|||
}
|
||||
}
|
||||
|
||||
internal static void WriteCreamConfiguration(StreamWriter writer, string steamAppId, string name, SortedList<string, (string name, string icon)> steamDlcApps, InstallForm installForm = null)
|
||||
internal static void WriteCreamConfiguration(StreamWriter writer, string appId, string name, SortedList<string, (DlcType type, string name, string icon)> 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<string, (string name, string icon)> pair in steamDlcApps)
|
||||
installForm.UpdateUser($"Added game to cream_api.ini with appid {appId} ({name})", InstallationLog.Resource, info: false);
|
||||
foreach (KeyValuePair<string, (DlcType type, string name, string icon)> 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<string, string, SortedList<string, (string name, string icon)>> extraAppDlc in selection.ExtraDlc)
|
||||
foreach (Tuple<string, string, SortedList<string, (DlcType type, string name, string icon)>> extraAppDlc in selection.ExtraDlc)
|
||||
WriteCreamConfiguration(writer, extraAppDlc.Item1, extraAppDlc.Item2, extraAppDlc.Item3, installForm);
|
||||
writer.Flush();
|
||||
writer.Close();
|
||||
});
|
||||
|
||||
internal static void WriteScreamConfiguration(StreamWriter writer, SortedList<string, (string name, string icon)> dlcApps, InstallForm installForm = null)
|
||||
internal static void WriteScreamConfiguration(StreamWriter writer, SortedList<string, (DlcType type, string name, string icon)> 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<string, (string name, string icon)> last = dlcApps.Last();
|
||||
foreach (KeyValuePair<string, (string name, string icon)> pair in dlcApps)
|
||||
IEnumerable<KeyValuePair<string, (DlcType type, string name, string icon)>> 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<string, (DlcType type, string name, string icon)> lastCatalogItem = catalogItems.Last();
|
||||
foreach (KeyValuePair<string, (DlcType type, string name, string icon)> 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<string, (string name, string icon)> pair in dlcApps)
|
||||
IEnumerable<KeyValuePair<string, (DlcType type, string name, string icon)>> 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<string, (DlcType type, string name, string icon)> lastEntitlement = entitlements.Last();
|
||||
foreach (KeyValuePair<string, (DlcType type, string name, string icon)> 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<string, string, SortedList<string, (string name, string icon)>> extraAppDlc in selection.ExtraDlc)
|
||||
foreach (Tuple<string, string, SortedList<string, (DlcType type, string name, string icon)>> extraAppDlc in selection.ExtraDlc)
|
||||
WriteScreamConfiguration(writer, extraAppDlc.Item3, installForm);
|
||||
writer.Flush();
|
||||
writer.Close();
|
||||
|
|
|
@ -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<string, (string name, string icon)> AllDlc = new();
|
||||
internal readonly SortedList<string, (string name, string icon)> SelectedDlc = new();
|
||||
internal readonly List<Tuple<string, string, SortedList<string, (string name, string icon)>>> ExtraDlc = new();
|
||||
internal readonly SortedList<string, (DlcType type, string name, string icon)> AllDlc = new();
|
||||
internal readonly SortedList<string, (DlcType type, string name, string icon)> SelectedDlc = new();
|
||||
internal readonly List<Tuple<string, string, SortedList<string, (DlcType type, string name, string icon)>>> 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<string, (string name, string icon)> pair in AllDlc)
|
||||
foreach (KeyValuePair<string, (DlcType type, string name, string icon)> 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<string, (string name, string icon)> 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<ProgramSelection> 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<string, (string name, string icon)> pair in selection.AllDlc)
|
||||
if (pair.Key == appId) return (selection.Id, pair.Value);
|
||||
foreach (KeyValuePair<string, (DlcType type, string name, string icon)> pair in selection.AllDlc)
|
||||
if (pair.Key == dlcId) return (selection.Id, pair.Value);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -140,7 +140,7 @@ internal partial class SelectForm : CustomForm
|
|||
return;
|
||||
}
|
||||
if (Program.Canceled) return;
|
||||
ConcurrentDictionary<string, (string name, string icon)> dlc = new();
|
||||
ConcurrentDictionary<string, (DlcType type, string name, string icon)> dlc = new();
|
||||
List<Task> dlcTasks = new();
|
||||
List<string> 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<string, (string name, string icon)> pair in dlc)
|
||||
foreach (KeyValuePair<string, (DlcType type, string name, string icon)> 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<string, (string name, string product, string icon, string developer)> dlc = new();
|
||||
ConcurrentDictionary<string, (string name, string product, string icon, string developer)> entitlements = new();
|
||||
List<Task> 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<string, (string name, string product, string icon, string developer)> pair in dlc)
|
||||
foreach (KeyValuePair<string, (string name, string product, string icon, string developer)> 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<string, (string name, string product, string icon, string developer)> 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<string, (string name, string product, string icon, string developer)> 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<TreeNode>().ToList().Any(treeNode => treeNode.Checked);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (selection.AllDlc.Any())
|
||||
{
|
||||
selection.ToggleAllDlc(node.Checked);
|
||||
node.Nodes.Cast<TreeNode>().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<TreeNode>().ToList().Any(childNode => childNode.Checked);
|
||||
SyncNodeParents(parentNode);
|
||||
}
|
||||
}
|
||||
|
||||
private static void SyncNodeDescendants(TreeNode node) =>
|
||||
node.Nodes.Cast<TreeNode>().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<TreeNode> TreeNodes => GatherTreeNodes(selectionTreeView.Nodes);
|
||||
private List<TreeNode> 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)
|
||||
|
|
Loading…
Reference in a new issue