diff --git a/CreamInstaller/Components/ContextMenuItem.cs b/CreamInstaller/Components/ContextMenuItem.cs index 15b5ce9..d063613 100644 --- a/CreamInstaller/Components/ContextMenuItem.cs +++ b/CreamInstaller/Components/ContextMenuItem.cs @@ -98,20 +98,27 @@ internal sealed class ContextMenuItem : ToolStripMenuItem private static async Task TryImageIdentifierInfo(ContextMenuItem item, (string id, string iconUrl) imageIdentifierInfo, Action onFail = null) => await Task.Run(async () => { - (string id, string iconUrl) = imageIdentifierInfo; - string imageIdentifier = "Icon_" + id; - if (Images.TryGetValue(imageIdentifier, out Image image) && image is not null) - item.Image = image; - else + try { - image = await HttpClientManager.GetImageFromUrl(iconUrl); - if (image is not null) - { - Images[imageIdentifier] = image; + (string id, string iconUrl) = imageIdentifierInfo; + string imageIdentifier = "Icon_" + id; + if (Images.TryGetValue(imageIdentifier, out Image image) && image is not null) item.Image = image; - } else - onFail?.Invoke(); + { + image = await HttpClientManager.GetImageFromUrl(iconUrl); + if (image is not null) + { + Images[imageIdentifier] = image; + item.Image = image; + } + else + onFail?.Invoke(); + } + } + catch + { + // ignored } }); diff --git a/CreamInstaller/CreamInstaller.csproj b/CreamInstaller/CreamInstaller.csproj index 32629b6..cdc5f61 100644 --- a/CreamInstaller/CreamInstaller.csproj +++ b/CreamInstaller/CreamInstaller.csproj @@ -4,7 +4,7 @@ net7.0-windows True Resources\ini.ico - 4.6.1.0 + 4.7.0.0 2021, pointfeev (https://github.com/pointfeev) CreamInstaller Automatic DLC Unlocker Installer & Configuration Generator diff --git a/CreamInstaller/Forms/SelectForm.cs b/CreamInstaller/Forms/SelectForm.cs index 5ccb44a..f6c735a 100644 --- a/CreamInstaller/Forms/SelectForm.cs +++ b/CreamInstaller/Forms/SelectForm.cs @@ -208,15 +208,17 @@ internal sealed partial class SelectForm : CustomForm while (!Program.Canceled && steamGamesToCheck > 0); if (Program.Canceled) return; + string fullGameAppId = null; string dlcName = null; string dlcIcon = null; bool onSteamStore = false; AppData dlcAppData = await SteamStore.QueryStoreAPI(dlcAppId, true); if (dlcAppData is not null) { - dlcName = dlcAppData.name; - dlcIcon = dlcAppData.header_image; + dlcName = dlcAppData.Name; + dlcIcon = dlcAppData.HeaderImage; onSteamStore = true; + fullGameAppId = dlcAppData.FullGame?.AppId; } else { @@ -229,8 +231,39 @@ internal sealed partial class SelectForm : CustomForm dlcIconStaticId ??= dlcAppInfo.Value.GetChild("common")?.GetChild("logo")?.ToString(); if (dlcIconStaticId is not null) dlcIcon = IconGrabber.SteamAppImagesPath + @$"\{dlcAppId}\{dlcIconStaticId}.jpg"; + fullGameAppId = dlcAppInfo.Value.GetChild("common")?.GetChild("parent")?.ToString(); } } + if (fullGameAppId != null && fullGameAppId != appId) + { + string fullGameName = null; + string fullGameIcon = null; + bool fullGameOnSteamStore = false; + AppData fullGameAppData = await SteamStore.QueryStoreAPI(fullGameAppId, true); + if (fullGameAppData is not null) + { + fullGameName = fullGameAppData.Name; + fullGameIcon = fullGameAppData.HeaderImage; + fullGameOnSteamStore = true; + } + else + { + VProperty fullGameAppInfo = await SteamCMD.GetAppInfo(fullGameAppId); + if (fullGameAppInfo is not null) + { + fullGameName = fullGameAppInfo.Value.GetChild("common")?.GetChild("name")?.ToString(); + string fullGameIconStaticId = fullGameAppInfo.Value.GetChild("common")?.GetChild("icon")?.ToString(); + fullGameIconStaticId ??= fullGameAppInfo.Value.GetChild("common")?.GetChild("logo_small")?.ToString(); + fullGameIconStaticId ??= fullGameAppInfo.Value.GetChild("common")?.GetChild("logo")?.ToString(); + if (fullGameIconStaticId is not null) + dlcIcon = IconGrabber.SteamAppImagesPath + @$"\{fullGameAppId}\{fullGameIconStaticId}.jpg"; + } + } + if (Program.Canceled) + return; + if (!string.IsNullOrWhiteSpace(fullGameName)) + dlc[fullGameAppId] = (fullGameOnSteamStore ? DlcType.Steam : DlcType.SteamHidden, fullGameName, fullGameIcon); + } if (Program.Canceled) return; if (string.IsNullOrWhiteSpace(dlcName)) @@ -259,17 +292,17 @@ internal sealed partial class SelectForm : CustomForm if (koaloaderAllCheckBox.Checked) selection.Koaloader = true; selection.Id = appId; - selection.Name = appData?.name ?? name; + selection.Name = appData?.Name ?? name; selection.RootDirectory = gameDirectory; selection.ExecutableDirectories = await SteamLibrary.GetExecutableDirectories(selection.RootDirectory); selection.DllDirectories = dllDirectories; 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 + selection.SubIconUrl = appData?.HeaderImage ?? IconGrabber.SteamAppImagesPath + @$"\{appId}\{appInfo?.Value.GetChild("common")?.GetChild("clienticon")}.ico"; - selection.Publisher = appData?.publishers[0] ?? appInfo?.Value.GetChild("extended")?.GetChild("publisher")?.ToString(); - selection.WebsiteUrl = appData?.website; + selection.Publisher = appData?.Publishers[0] ?? appInfo?.Value.GetChild("extended")?.GetChild("publisher")?.ToString(); + selection.WebsiteUrl = appData?.Website; if (Program.Canceled) return; selectionTreeView.Invoke(delegate @@ -279,7 +312,7 @@ internal sealed partial class SelectForm : CustomForm TreeNode programNode = treeNodes.Find(s => s.Tag is Platform.Steam && s.Name == appId) ?? new TreeNode(); programNode.Tag = selection.Platform; programNode.Name = appId; - programNode.Text = appData?.name ?? name; + programNode.Text = appData?.Name ?? name; programNode.Checked = selection.Enabled; if (programNode.TreeView is null) _ = selectionTreeView.Nodes.Add(programNode); diff --git a/CreamInstaller/Platforms/Steam/AppDetails.cs b/CreamInstaller/Platforms/Steam/AppDetails.cs index cb19475..114482f 100644 --- a/CreamInstaller/Platforms/Steam/AppDetails.cs +++ b/CreamInstaller/Platforms/Steam/AppDetails.cs @@ -1,36 +1,49 @@ -#pragma warning disable IDE1006 // Naming Styles -#pragma warning disable CA1002 // Do not expose generic lists -#pragma warning disable CA1707 // Identifiers should not contain underscores -#pragma warning disable CA2227 // Collection properties should be read only - -using System.Collections.Generic; +using System.Collections.Generic; +using Newtonsoft.Json; namespace CreamInstaller.Platforms.Steam; +public class AppFullGame +{ + [JsonProperty(PropertyName = "appid")] public string AppId { get; set; } + + [JsonProperty(PropertyName = "name")] public string Name { get; set; } +} + public class AppData { - public string type { get; set; } + [JsonProperty(PropertyName = "type")] public string Type { get; set; } - public string name { get; set; } + [JsonProperty(PropertyName = "name")] public string Name { get; set; } - public int steam_appid { get; set; } + [JsonProperty(PropertyName = "steam_appid")] + public int SteamAppId { get; set; } - public List dlc { get; set; } + [JsonProperty(PropertyName = "fullgame")] + public AppFullGame FullGame { get; set; } - public string header_image { get; set; } + [JsonProperty(PropertyName = "dlc")] public List DLC { get; set; } - public string website { get; set; } + [JsonProperty(PropertyName = "header_image")] + public string HeaderImage { get; set; } - public List developers { get; set; } + [JsonProperty(PropertyName = "website")] + public string Website { get; set; } - public List publishers { get; set; } + [JsonProperty(PropertyName = "developers")] + public List Developers { get; set; } - public List packages { get; set; } + [JsonProperty(PropertyName = "publishers")] + public List Publishers { get; set; } + + [JsonProperty(PropertyName = "packages")] + public List Packages { get; set; } } public class AppDetails { - public bool success { get; set; } + [JsonProperty(PropertyName = "success")] + public bool Success { get; set; } - public AppData data { get; set; } + [JsonProperty(PropertyName = "data")] public AppData Data { get; set; } } \ No newline at end of file diff --git a/CreamInstaller/Platforms/Steam/SteamStore.cs b/CreamInstaller/Platforms/Steam/SteamStore.cs index 05f256f..0d38df4 100644 --- a/CreamInstaller/Platforms/Steam/SteamStore.cs +++ b/CreamInstaller/Platforms/Steam/SteamStore.cs @@ -22,9 +22,9 @@ internal static class SteamStore => await Task.Run(() => { List dlcIds = new(); - if (appData.dlc is null) + if (appData.DLC is null) return dlcIds; - dlcIds.AddRange(from appId in appData.dlc where appId > 0 select appId.ToString(CultureInfo.InvariantCulture)); + dlcIds.AddRange(from appId in appData.DLC where appId > 0 select appId.ToString(CultureInfo.InvariantCulture)); return dlcIds; }); @@ -48,8 +48,8 @@ internal static class SteamStore AppDetails appDetails = JsonConvert.DeserializeObject(app.Value.ToString()); if (appDetails is not null) { - AppData data = appDetails.data; - if (!appDetails.success) + AppData data = appDetails.Data; + if (!appDetails.Success) { #if DEBUG DebugForm.Current.Log( diff --git a/CreamInstaller/Utility/HttpClientManager.cs b/CreamInstaller/Utility/HttpClientManager.cs index e0115be..af35eb6 100644 --- a/CreamInstaller/Utility/HttpClientManager.cs +++ b/CreamInstaller/Utility/HttpClientManager.cs @@ -1,5 +1,6 @@ using System; using System.Drawing; +using System.Net; using System.Net.Http; using System.Threading.Tasks; using HtmlAgilityPack; @@ -28,6 +29,19 @@ internal static class HttpClientManager _ = response.EnsureSuccessStatusCode(); return await response.Content.ReadAsStringAsync(); } + catch (HttpRequestException e) + { + if (e.StatusCode != HttpStatusCode.TooManyRequests) + { + DebugForm.Current.Log("Get request failed to " + url + ": " + e, LogTextBox.Warning); + return null; + } +#if DEBUG + DebugForm.Current.Log("Too many requests to " + url, LogTextBox.Error); +#endif + // do something special? + return null; + } #if DEBUG catch (Exception e) { diff --git a/CreamInstaller/Utility/ProgramData.cs b/CreamInstaller/Utility/ProgramData.cs index 9cadd53..2578a0b 100644 --- a/CreamInstaller/Utility/ProgramData.cs +++ b/CreamInstaller/Utility/ProgramData.cs @@ -17,7 +17,7 @@ internal static class ProgramData internal static readonly string AppInfoPath = DirectoryPath + @"\appinfo"; private static readonly string AppInfoVersionPath = AppInfoPath + @"\version.txt"; - private static readonly Version MinimumAppInfoVersion = Version.Parse("3.2.0.0"); + private static readonly Version MinimumAppInfoVersion = Version.Parse("4.7.0.0"); internal static readonly string CooldownPath = DirectoryPath + @"\cooldown";