diff --git a/CreamInstaller/CreamInstaller.csproj b/CreamInstaller/CreamInstaller.csproj
index 19033c3..d3f22da 100644
--- a/CreamInstaller/CreamInstaller.csproj
+++ b/CreamInstaller/CreamInstaller.csproj
@@ -5,7 +5,7 @@
True
Resources\ini.ico
true
- 3.1.0.1
+ 3.2.0.0
Resources\ini.ico
diff --git a/CreamInstaller/DialogForm.cs b/CreamInstaller/DialogForm.cs
index ab494ec..ef294e8 100644
--- a/CreamInstaller/DialogForm.cs
+++ b/CreamInstaller/DialogForm.cs
@@ -14,6 +14,8 @@ internal partial class DialogForm : CustomForm
{
if (customFormIcon is not null)
Icon = customFormIcon;
+ if (descriptionIcon is null)
+ descriptionIcon = Icon;
icon.Image = descriptionIcon.ToBitmap();
descriptionLabel.Text = descriptionText;
acceptButton.Text = acceptButtonText;
diff --git a/CreamInstaller/Epic/EpicStore.cs b/CreamInstaller/Epic/EpicStore.cs
index 3466832..6d39929 100644
--- a/CreamInstaller/Epic/EpicStore.cs
+++ b/CreamInstaller/Epic/EpicStore.cs
@@ -24,8 +24,20 @@ internal static class EpicStore
{
List<(string id, string name, string product, string icon, string developer)> dlcIds = new();
Response response = await QueryGraphQL(categoryNamespace);
+ string cacheFile = ProgramData.AppInfoPath + @$"\{categoryNamespace}.json";
+ if (response is null)
+ if (Directory.Exists(Directory.GetDirectoryRoot(cacheFile)) && File.Exists(cacheFile))
+ try
+ {
+ response = JsonConvert.DeserializeObject(File.ReadAllText(cacheFile));
+ }
+ catch { }
if (response is null) return dlcIds;
- try { File.WriteAllText(ProgramData.AppInfoPath + @$"\{categoryNamespace}.json", JsonConvert.SerializeObject(response, Formatting.Indented)); } catch { }
+ try
+ {
+ File.WriteAllText(cacheFile, JsonConvert.SerializeObject(response, Formatting.Indented));
+ }
+ catch { }
List searchStore = new(response.Data.Catalog.SearchStore.Elements);
foreach (Element element in searchStore)
{
diff --git a/CreamInstaller/ProgramSelection.cs b/CreamInstaller/ProgramSelection.cs
index 35c6e18..0c88f12 100644
--- a/CreamInstaller/ProgramSelection.cs
+++ b/CreamInstaller/ProgramSelection.cs
@@ -3,8 +3,6 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
-using Gameloop.Vdf.Linq;
-
namespace CreamInstaller;
internal enum DlcType
@@ -24,7 +22,7 @@ internal class ProgramSelection
internal string ProductUrl = null;
internal string IconUrl = null;
- internal string ClientIconUrl = null;
+ internal string SubIconUrl = null;
internal string Publisher = null;
@@ -32,7 +30,6 @@ internal class ProgramSelection
internal List DllDirectories = null;
internal bool IsSteam = false;
- internal VProperty AppInfo = null;
internal readonly SortedList AllDlc = new();
internal readonly SortedList SelectedDlc = new();
diff --git a/CreamInstaller/SelectForm.cs b/CreamInstaller/SelectForm.cs
index a2f2fb7..ce85588 100644
--- a/CreamInstaller/SelectForm.cs
+++ b/CreamInstaller/SelectForm.cs
@@ -142,8 +142,9 @@ internal partial class SelectForm : CustomForm
RemoveFromRemainingGames(name);
return;
}
+ AppData appData = await SteamStore.QueryStoreAPI(appId);
VProperty appInfo = appInfo = await SteamCMD.GetAppInfo(appId, branch, buildId);
- if (appInfo is null)
+ if (appInfo is null || appData is null)
{
RemoveFromRemainingGames(name);
return;
@@ -151,8 +152,8 @@ internal partial class SelectForm : CustomForm
if (Program.Canceled) return;
ConcurrentDictionary dlc = new();
List dlcTasks = new();
- List dlcIds = await SteamCMD.ParseDlcAppIds(appInfo);
- await SteamStore.ParseDlcAppIds(appId, dlcIds);
+ List dlcIds = await SteamStore.ParseDlcAppIds(appData);
+ dlcIds.AddRange(await SteamCMD.ParseDlcAppIds(appInfo));
if (dlcIds.Count > 0)
{
foreach (string dlcAppId in dlcIds)
@@ -163,20 +164,30 @@ internal partial class SelectForm : CustomForm
{
if (Program.Canceled) return;
string dlcName = null;
- string dlcIconStaticId = null;
- VProperty dlcAppInfo = await SteamCMD.GetAppInfo(dlcAppId);
- if (dlcAppInfo is not null)
+ string dlcIcon = null;
+ AppData dlcAppData = await SteamStore.QueryStoreAPI(dlcAppId);
+ if (dlcAppData is not null)
{
- dlcName = dlcAppInfo.Value?.GetChild("common")?.GetChild("name")?.ToString();
- dlcIconStaticId = dlcAppInfo.Value?.GetChild("common")?.GetChild("icon")?.ToString();
- dlcIconStaticId ??= dlcAppInfo.Value?.GetChild("common")?.GetChild("logo_small")?.ToString();
- dlcIconStaticId ??= dlcAppInfo.Value?.GetChild("common")?.GetChild("logo")?.ToString();
- if (dlcIconStaticId is not null)
- dlcIconStaticId = IconGrabber.SteamAppImagesPath + @$"\{dlcAppId}\{dlcIconStaticId}.jpg";
+ dlcName = dlcAppData.name;
+ dlcIcon = dlcAppData.header_image;
+ }
+ else
+ {
+ VProperty dlcAppInfo = await SteamCMD.GetAppInfo(dlcAppId);
+ if (dlcAppInfo is not null)
+ {
+ dlcName = dlcAppInfo.Value?.GetChild("common")?.GetChild("name")?.ToString();
+ string dlcIconStaticId = dlcAppInfo.Value?.GetChild("common")?.GetChild("icon")?.ToString();
+ dlcIconStaticId ??= dlcAppInfo.Value?.GetChild("common")?.GetChild("logo_small")?.ToString();
+ dlcIconStaticId ??= dlcAppInfo.Value?.GetChild("common")?.GetChild("logo")?.ToString();
+ if (dlcIconStaticId is not null)
+ dlcIcon = IconGrabber.SteamAppImagesPath + @$"\{dlcAppId}\{dlcIconStaticId}.jpg";
+ }
}
if (Program.Canceled) return;
- if (!string.IsNullOrWhiteSpace(dlcName))
- dlc[dlcAppId] = (DlcType.Default, dlcName, dlcIconStaticId);
+ if (string.IsNullOrWhiteSpace(dlcName))
+ return; //dlcName = "Unknown DLC";
+ dlc[dlcAppId] = (DlcType.Default, dlcName, dlcIcon);
RemoveFromRemainingDLCs(dlcAppId);
});
dlcTasks.Add(task);
@@ -195,6 +206,7 @@ internal partial class SelectForm : CustomForm
await task;
}
+ name = appData.name ?? name;
selection ??= new();
selection.Enabled = allCheckBox.Checked || selection.SelectedDlc.Any() || selection.ExtraDlc.Any();
selection.Usable = true;
@@ -203,11 +215,10 @@ internal partial class SelectForm : CustomForm
selection.RootDirectory = directory;
selection.DllDirectories = dllDirectories;
selection.IsSteam = true;
- selection.AppInfo = appInfo;
selection.ProductUrl = "https://store.steampowered.com/app/" + appId;
- selection.IconUrl = IconGrabber.SteamAppImagesPath + @$"\{appId}\{appInfo?.Value?.GetChild("common")?.GetChild("icon")?.ToString()}.jpg";
- selection.ClientIconUrl = IconGrabber.SteamAppImagesPath + @$"\{appId}\{appInfo?.Value?.GetChild("common")?.GetChild("clienticon")?.ToString()}.ico";
- selection.Publisher = appInfo?.Value?.GetChild("extended")?.GetChild("publisher")?.ToString();
+ selection.IconUrl = IconGrabber.SteamAppImagesPath + @$"\{appId}\{appInfo?.Value?.GetChild("common")?.GetChild("clienticon")?.ToString()}.ico";
+ selection.SubIconUrl = appData.header_image ?? IconGrabber.SteamAppImagesPath + @$"\{appId}\{appInfo?.Value?.GetChild("common")?.GetChild("icon")?.ToString()}.jpg";
+ selection.Publisher = appData.publishers[0] ?? appInfo?.Value?.GetChild("extended")?.GetChild("publisher")?.ToString();
if (Program.Canceled) return;
Program.Invoke(selectionTreeView, delegate
@@ -600,30 +611,30 @@ internal partial class SelectForm : CustomForm
}
}
nodeContextMenu.Items.Add(header);
- string appInfo = $@"{SteamCMD.AppInfoPath}\{id}.vdf";
- string appInfoEpic = $@"{SteamCMD.AppInfoPath}\{id}.json";
- if (Directory.Exists(Directory.GetDirectoryRoot(appInfo)) && (File.Exists(appInfo) || File.Exists(appInfoEpic)))
+ string appInfoVDF = $@"{SteamCMD.AppInfoPath}\{id}.vdf";
+ string appInfoJSON = $@"{SteamCMD.AppInfoPath}\{id}.json";
+ if (Directory.Exists(Directory.GetDirectoryRoot(appInfoVDF)) && (File.Exists(appInfoVDF) || File.Exists(appInfoJSON)))
{
nodeContextMenu.Items.Add(new ToolStripSeparator());
nodeContextMenu.Items.Add(new ToolStripMenuItem("Open AppInfo", Image("Notepad"),
new EventHandler((sender, e) =>
{
- if (File.Exists(appInfo))
- Diagnostics.OpenFileInNotepad(appInfo);
- else
- Diagnostics.OpenFileInNotepad(appInfoEpic);
+ if (File.Exists(appInfoVDF))
+ Diagnostics.OpenFileInNotepad(appInfoVDF);
+ else if (File.Exists(appInfoJSON))
+ Diagnostics.OpenFileInNotepad(appInfoJSON);
})));
nodeContextMenu.Items.Add(new ToolStripMenuItem("Refresh AppInfo", Image("Command Prompt"),
new EventHandler((sender, e) =>
{
try
{
- File.Delete(appInfo);
+ File.Delete(appInfoVDF);
}
catch { }
try
{
- File.Delete(appInfoEpic);
+ File.Delete(appInfoJSON);
}
catch { }
OnLoad();
@@ -713,7 +724,7 @@ internal partial class SelectForm : CustomForm
}
else
new DialogForm(this).Show(SystemIcons.Error, "Paradox Launcher repair failed!"
- + "\n\nAn original Steamworks API or Epic Online Services SDK file could not be found."
+ + "\n\nAn original Steamworks/Epic Online Services SDK file could not be found."
+ "\nYou must reinstall Paradox Launcher to fix this issue.", "OK");
})));
}
@@ -723,17 +734,21 @@ internal partial class SelectForm : CustomForm
for (int i = 0; i < selection.DllDirectories.Count; i++)
{
string directory = selection.DllDirectories[i];
- nodeContextMenu.Items.Add(new ToolStripMenuItem($"Open {(selection.IsSteam ? "Steamworks API" : "Epic Online Services SDK")} Directory ({i + 1})", Image("File Explorer"),
+ nodeContextMenu.Items.Add(new ToolStripMenuItem($"Open {(selection.IsSteam ? "Steamworks" : "Epic Online Services")} SDK Directory ({i + 1})", Image("File Explorer"),
new EventHandler((sender, e) => Diagnostics.OpenDirectoryInFileExplorer(directory))));
}
}
+ ProgramSelection dlcParentSelection = dlc.HasValue ? ProgramSelection.FromId(dlc.Value.gameAppId) : null;
+ if (selection is not null || dlcParentSelection is not null && dlcParentSelection.IsSteam)
+ {
+ nodeContextMenu.Items.Add(new ToolStripSeparator());
+ nodeContextMenu.Items.Add(new ToolStripMenuItem("Open SteamDB", Image("SteamDB"),
+ new EventHandler((sender, e) => Diagnostics.OpenUrlInInternetBrowser("https://steamdb.info/app/" + id))));
+ }
if (id != "ParadoxLauncher" && selection is not null)
{
if (selection.IsSteam)
{
- nodeContextMenu.Items.Add(new ToolStripSeparator());
- nodeContextMenu.Items.Add(new ToolStripMenuItem("Open SteamDB", Image("SteamDB"),
- new EventHandler((sender, e) => Diagnostics.OpenUrlInInternetBrowser("https://steamdb.info/app/" + id))));
nodeContextMenu.Items.Add(new ToolStripMenuItem("Open Steam Store", Image("Steam Store"),
new EventHandler((sender, e) => Diagnostics.OpenUrlInInternetBrowser(selection.ProductUrl))));
ToolStripMenuItem steamCommunity = new("Open Steam Community", Image("ClientIcon_" + id),
@@ -742,7 +757,7 @@ internal partial class SelectForm : CustomForm
if (steamCommunity.Image is null)
{
steamCommunity.Image = Image("Steam Community");
- TrySetImageAsync(steamCommunity, id, selection.ClientIconUrl, true);
+ TrySetImageAsync(steamCommunity, id, selection.SubIconUrl, true);
}
}
else
diff --git a/CreamInstaller/Steam/AppDetails.cs b/CreamInstaller/Steam/AppDetails.cs
new file mode 100644
index 0000000..5e0b568
--- /dev/null
+++ b/CreamInstaller/Steam/AppDetails.cs
@@ -0,0 +1,147 @@
+using System.Collections.Generic;
+
+namespace CreamInstaller.Steam;
+
+#pragma warning disable IDE1006 // Naming Styles
+public class PriceOverview
+{
+ public string currency { get; set; }
+ public int initial { get; set; }
+ public int final { get; set; }
+ public int discount_percent { get; set; }
+ public string initial_formatted { get; set; }
+ public string final_formatted { get; set; }
+}
+
+public class Sub
+{
+ public int packageid { get; set; }
+ public string percent_savings_text { get; set; }
+ public int percent_savings { get; set; }
+ public string option_text { get; set; }
+ public string option_description { get; set; }
+ public string can_get_free_license { get; set; }
+ public bool is_free_license { get; set; }
+ public int price_in_cents_with_discount { get; set; }
+}
+
+public class PackageGroup
+{
+ public string name { get; set; }
+ public string title { get; set; }
+ public string description { get; set; }
+ public string selection_text { get; set; }
+ public string save_text { get; set; }
+ public object display_type { get; set; }
+ public string is_recurring_subscription { get; set; }
+ public List subs { get; set; }
+}
+
+public class Platforms
+{
+ public bool windows { get; set; }
+ public bool mac { get; set; }
+ public bool linux { get; set; }
+}
+
+public class Metacritic
+{
+ public int score { get; set; }
+ public string url { get; set; }
+}
+
+public class Category
+{
+ public int id { get; set; }
+ public string description { get; set; }
+}
+
+public class Genre
+{
+ public string id { get; set; }
+ public string description { get; set; }
+}
+
+public class Screenshot
+{
+ public int id { get; set; }
+ public string path_thumbnail { get; set; }
+ public string path_full { get; set; }
+}
+
+public class Recommendations
+{
+ public int total { get; set; }
+}
+
+public class Highlighted
+{
+ public string name { get; set; }
+ public string path { get; set; }
+}
+
+public class Achievements
+{
+ public int total { get; set; }
+ public List highlighted { get; set; }
+}
+
+public class ReleaseDate
+{
+ public bool coming_soon { get; set; }
+ public string date { get; set; }
+}
+
+public class SupportInfo
+{
+ public string url { get; set; }
+ public string email { get; set; }
+}
+
+public class ContentDescriptors
+{
+ public List