From f1b7e04842de6714ae30537af88f27bdd7eccc0e Mon Sep 17 00:00:00 2001 From: pointfeev Date: Mon, 24 Jan 2022 02:36:25 -0500 Subject: [PATCH] v2.2.4.1 - Vastly improved the stability of VDF file parsing --- CreamInstaller/Classes/SteamCMD.cs | 33 +++++------ CreamInstaller/Classes/ValveDataFile.cs | 29 ++++++++++ CreamInstaller/CreamInstaller.csproj | 2 +- CreamInstaller/Forms/SelectForm.cs | 74 +++++++++++-------------- 4 files changed, 76 insertions(+), 62 deletions(-) create mode 100644 CreamInstaller/Classes/ValveDataFile.cs diff --git a/CreamInstaller/Classes/SteamCMD.cs b/CreamInstaller/Classes/SteamCMD.cs index e12ecb3..44436ab 100644 --- a/CreamInstaller/Classes/SteamCMD.cs +++ b/CreamInstaller/Classes/SteamCMD.cs @@ -9,7 +9,6 @@ using System.Text; using System.Threading.Tasks; using System.Windows.Forms; -using Gameloop.Vdf; using Gameloop.Vdf.Linq; namespace CreamInstaller @@ -78,7 +77,6 @@ namespace CreamInstaller internal static async Task GetAppInfo(int appId, string branch = "public", int buildId = 0) { - VProperty appInfo = null; if (Program.Canceled) return null; string output; string appUpdatePath = $@"{AppInfoPath}\{appId}"; @@ -99,8 +97,7 @@ namespace CreamInstaller } } if (Program.Canceled || output is null) return null; - try { appInfo = VdfConvert.Deserialize(output); } - catch + if (!ValveDataFile.TryDeserialize(output, out VProperty appInfo)) { if (Directory.Exists(appUpdatePath)) { @@ -109,12 +106,11 @@ namespace CreamInstaller } } if (appInfo.Value is VValue) goto restart; - if (appInfo is null || appInfo.Value is not VValue && appInfo.Value.Children().ToList().Count == 0) - return appInfo; - VToken type = appInfo.Value is VValue ? null : appInfo.Value?["common"]?["type"]; + if (appInfo is null || appInfo.Value?.Children()?.ToList()?.Count == 0) return appInfo; + VToken type = appInfo.Value?.TryGet("common")?.TryGet("type"); if (type is null || type.ToString() == "Game") { - string buildid = appInfo.Value is VValue ? null : appInfo.Value["depots"]?["branches"]?[branch]?["buildid"]?.ToString(); + string buildid = appInfo.Value?.TryGet("depots")?.TryGet("branches")?.TryGet(branch)?.TryGet("buildid")?.ToString(); if (buildid is null && type is not null) return appInfo; if (type is null || int.Parse(buildid) < buildId) { @@ -135,17 +131,16 @@ namespace CreamInstaller { List dlcIds = new(); if (Program.Canceled || appInfo is not VProperty) return dlcIds; - if (appInfo.Value["extended"] is not null) - foreach (VProperty property in appInfo.Value["extended"]) - if (property.Key.ToString() == "listofdlc") - foreach (string id in property.Value.ToString().Split(",")) - if (!dlcIds.Contains(int.Parse(id))) - dlcIds.Add(int.Parse(id)); - if (appInfo.Value["depots"] is not null) - foreach (VProperty _property in appInfo.Value["depots"]) - if (int.TryParse(_property.Key.ToString(), out int _)) - if (int.TryParse(_property.Value?["dlcappid"]?.ToString(), out int appid) && !dlcIds.Contains(appid)) - dlcIds.Add(appid); + VToken extended = appInfo.Value.TryGet("extended"); + if (extended is not null) foreach (VProperty property in extended) + if (property.Key.ToString() == "listofdlc") foreach (string id in property.Value.ToString().Split(",")) + if (!dlcIds.Contains(int.Parse(id))) dlcIds.Add(int.Parse(id)); + VToken depots = appInfo.Value.TryGet("depots"); + if (depots is not null) foreach (VProperty property in depots) + if (int.TryParse(property.Key.ToString(), out int _) + && int.TryParse(property.Value.TryGet("dlcappid")?.ToString(), out int appid) + && !dlcIds.Contains(appid)) + dlcIds.Add(appid); return dlcIds; }); diff --git a/CreamInstaller/Classes/ValveDataFile.cs b/CreamInstaller/Classes/ValveDataFile.cs new file mode 100644 index 0000000..635be12 --- /dev/null +++ b/CreamInstaller/Classes/ValveDataFile.cs @@ -0,0 +1,29 @@ +using Gameloop.Vdf; +using Gameloop.Vdf.Linq; + +namespace CreamInstaller; + +internal static class ValveDataFile +{ + internal static bool TryDeserialize(string value, out VProperty result) + { + result = null; + try + { + result = VdfConvert.Deserialize(value); + return true; + } + catch { } + return false; + } + + internal static VToken TryGet(this VToken token, string index) + { + try + { + return token[index]; + } + catch { } + return null; + } +} \ No newline at end of file diff --git a/CreamInstaller/CreamInstaller.csproj b/CreamInstaller/CreamInstaller.csproj index fe81bae..5de2530 100644 --- a/CreamInstaller/CreamInstaller.csproj +++ b/CreamInstaller/CreamInstaller.csproj @@ -5,7 +5,7 @@ true Resources\ini.ico true - 2.2.4.0 + 2.2.4.1 Resources\ini.ico Automatically generates and installs CreamAPI files for Steam games on the user's computer. It can also generate and install CreamAPI for the Paradox Launcher should the user select a Paradox Interactive game. diff --git a/CreamInstaller/Forms/SelectForm.cs b/CreamInstaller/Forms/SelectForm.cs index c627a19..bfa5d4b 100644 --- a/CreamInstaller/Forms/SelectForm.cs +++ b/CreamInstaller/Forms/SelectForm.cs @@ -10,7 +10,6 @@ using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; -using Gameloop.Vdf; using Gameloop.Vdf.Linq; using Microsoft.Win32; @@ -38,22 +37,18 @@ namespace CreamInstaller if (Directory.Exists(libraryFolder)) { gameDirectories.Add(libraryFolder); - try + string libraryFolders = libraryFolder + @"\libraryfolders.vdf"; + if (File.Exists(libraryFolders) && ValveDataFile.TryDeserialize(File.ReadAllText(libraryFolders, Encoding.UTF8), out VProperty _result)) { - string libraryFolders = libraryFolder + @"\libraryfolders.vdf"; - if (File.Exists(libraryFolders)) - { - dynamic property = VdfConvert.Deserialize(File.ReadAllText(libraryFolders, Encoding.UTF8)).Value; - foreach (dynamic _property in property) - if (int.TryParse(_property.Key, out int _)) - { - string path = _property.Value.path.ToString() + @"\steamapps"; - if (string.IsNullOrWhiteSpace(path) || !Directory.Exists(path)) continue; - if (!gameDirectories.Contains(path)) gameDirectories.Add(path); - } - } + dynamic result = _result; + foreach (dynamic property in result?.Value) if (int.TryParse(property.Key, out int _)) + { + string path = property.Value?.path?.ToString(); + if (string.IsNullOrWhiteSpace(path)) continue; + path += @"\steamapps"; + if (Directory.Exists(path) && !gameDirectories.Contains(path)) gameDirectories.Add(path); + } } - catch { } } } return gameDirectories; @@ -89,28 +84,24 @@ namespace CreamInstaller foreach (string file in files) { if (Program.Canceled) return null; - if (Path.GetExtension(file) == ".acf") + if (Path.GetExtension(file) == ".acf" && ValveDataFile.TryDeserialize(File.ReadAllText(file, Encoding.UTF8), out VProperty _result)) { - try - { - dynamic property = VdfConvert.Deserialize(File.ReadAllText(file, Encoding.UTF8)); - string _appid = property.Value.appid.ToString(); - string installdir = property.Value.installdir.ToString(); - string name = property.Value.name.ToString(); - string _buildid = property.Value.buildid.ToString(); - if (string.IsNullOrWhiteSpace(_appid) - || string.IsNullOrWhiteSpace(installdir) - || string.IsNullOrWhiteSpace(name) - || string.IsNullOrWhiteSpace(_buildid)) - continue; - string branch = property.Value.UserConfig?.betakey?.ToString(); - if (string.IsNullOrWhiteSpace(branch)) branch = "public"; - string gameDirectory = libraryDirectory + @"\common\" + installdir; - if (!int.TryParse(_appid, out int appid)) continue; - if (!int.TryParse(_buildid, out int buildid)) continue; - games.Add(new(appid, name, branch, buildid, gameDirectory)); - } - catch { } + dynamic result = _result; + string _appid = result.Value?.appid?.ToString(); + string installdir = result.Value?.installdir?.ToString(); + string name = result.Value?.name?.ToString(); + string _buildid = result.Value?.buildid?.ToString(); + if (string.IsNullOrWhiteSpace(_appid) + || string.IsNullOrWhiteSpace(installdir) + || string.IsNullOrWhiteSpace(name) + || string.IsNullOrWhiteSpace(_buildid)) + continue; + string branch = result.Value?.UserConfig?.betakey?.ToString(); + if (string.IsNullOrWhiteSpace(branch)) branch = "public"; + string gameDirectory = libraryDirectory + @"\common\" + installdir; + if (!int.TryParse(_appid, out int appid)) continue; + if (!int.TryParse(_buildid, out int buildid)) continue; + games.Add(new(appid, name, branch, buildid, gameDirectory)); } } if (!games.Any()) return null; @@ -182,8 +173,7 @@ namespace CreamInstaller if (Program.Canceled) return; string dlcName = null; VProperty dlcAppInfo = await SteamCMD.GetAppInfo(id); - if (dlcAppInfo is not null) - dlcName = dlcAppInfo?.Value?["common"]?["name"]?.ToString(); + if (dlcAppInfo is not null) dlcName = dlcAppInfo.Value?.TryGet("common")?.TryGet("name")?.ToString(); if (Program.Canceled) return; if (string.IsNullOrWhiteSpace(dlcName)) return; //dlcName = "Unknown DLC"; dlc[id] = /*$"[{id}] " +*/ dlcName; @@ -211,8 +201,8 @@ namespace CreamInstaller if (appId == 0) selection.Icon = Program.GetFileIconImage(directory + @"\launcher\bootstrapper-v2.exe"); else { - selection.IconStaticID = appInfo?.Value?["common"]?["icon"]?.ToString(); - selection.ClientIconStaticID = appInfo?.Value?["common"]?["clienticon"]?.ToString(); + selection.IconStaticID = appInfo?.Value?.TryGet("common")?.TryGet("icon")?.ToString(); + selection.ClientIconStaticID = appInfo?.Value?.TryGet("common")?.TryGet("clienticon")?.ToString(); } } if (allCheckBox.Checked) selection.Enabled = true; @@ -453,14 +443,14 @@ namespace CreamInstaller foreach (ProgramSelection selection in ProgramSelection.AllUsableEnabled) { if (selection.Name == paradoxLauncher.Name) continue; - if (selection.AppInfo.Value["extended"]["publisher"].ToString() != "Paradox Interactive") continue; + if (selection.AppInfo.Value?.TryGet("extended")?.TryGet("publisher")?.ToString() != "Paradox Interactive") continue; paradoxLauncher.ExtraSteamAppIdDlc.Add(new(selection.SteamAppId, selection.Name, selection.SelectedSteamDlc)); } if (!paradoxLauncher.ExtraSteamAppIdDlc.Any()) foreach (ProgramSelection selection in ProgramSelection.AllUsable) { if (selection.Name == paradoxLauncher.Name) continue; - if (selection.AppInfo.Value["extended"]["publisher"].ToString() != "Paradox Interactive") continue; + if (selection.AppInfo.Value?.TryGet("extended")?.TryGet("publisher")?.ToString() != "Paradox Interactive") continue; paradoxLauncher.ExtraSteamAppIdDlc.Add(new(selection.SteamAppId, selection.Name, selection.AllSteamDlc)); } }