diff --git a/CreamInstaller/InstallForm.cs b/CreamInstaller/InstallForm.cs index 231e6a4..b82d72e 100644 --- a/CreamInstaller/InstallForm.cs +++ b/CreamInstaller/InstallForm.cs @@ -52,7 +52,7 @@ namespace CreamInstaller int cur = 0; foreach (string directory in selection.SteamApiDllDirectories) { - UpdateUser("Installing CreamAPI for " + selection.DisplayName + $" in directory \"{directory}\" . . . ", LogColor.Operation); + UpdateUser("Installing CreamAPI for " + selection.Name + $" in directory \"{directory}\" . . . ", LogColor.Operation); if (!Program.IsProgramRunningDialog(this, selection)) throw new OperationCanceledException(); string api = directory + @"\steam_api.dll"; string api_o = directory + @"\steam_api_o.dll"; @@ -89,7 +89,7 @@ namespace CreamInstaller writer.WriteLine($"appid = {selection.SteamAppId}"); writer.WriteLine(); writer.WriteLine("[dlc]"); - UpdateUser($"Added game to cream_api.ini with appid {selection.SteamAppId} ({selection.DisplayName})", LogColor.Resource); + UpdateUser($"Added game to cream_api.ini with appid {selection.SteamAppId} ({selection.Name})", LogColor.Resource); foreach (Tuple dlcApp in selection.SelectedSteamDlc) { writer.WriteLine($"{dlcApp.Item1} = {dlcApp.Item2}"); @@ -129,12 +129,12 @@ namespace CreamInstaller try { await OperateFor(selection); - UpdateUser($"Operation succeeded for {selection.DisplayName}.", LogColor.Success); + UpdateUser($"Operation succeeded for {selection.Name}.", LogColor.Success); selection.Enabled = false; } catch (Exception exception) { - UpdateUser($"Operation failed for {selection.DisplayName}: " + exception.ToString(), LogColor.Error); + UpdateUser($"Operation failed for {selection.Name}: " + exception.ToString(), LogColor.Error); } ++CompleteOperationsCount; } @@ -144,7 +144,7 @@ namespace CreamInstaller { if (FailedSelections.Count == 1) { - throw new CustomMessageException($"Operation failed for {FailedSelections.First().DisplayName}."); + throw new CustomMessageException($"Operation failed for {FailedSelections.First().Name}."); } else { diff --git a/CreamInstaller/Program.cs b/CreamInstaller/Program.cs index bb7c63d..461d390 100644 --- a/CreamInstaller/Program.cs +++ b/CreamInstaller/Program.cs @@ -39,7 +39,7 @@ namespace CreamInstaller if (selection.AreSteamApiDllsLocked) { if (new DialogForm(form).Show(ApplicationName, SystemIcons.Error, - $"ERROR: {selection.DisplayName} is currently running!" + + $"ERROR: {selection.Name} is currently running!" + "\n\nPlease close the program/game to continue . . . ", "Retry", "Cancel") == DialogResult.OK) { diff --git a/CreamInstaller/ProgramSelection.cs b/CreamInstaller/ProgramSelection.cs index 56a6561..ba6111e 100644 --- a/CreamInstaller/ProgramSelection.cs +++ b/CreamInstaller/ProgramSelection.cs @@ -1,4 +1,5 @@ -using System; +using Gameloop.Vdf.Linq; +using System; using System.Collections.Generic; using System.Linq; @@ -8,13 +9,13 @@ namespace CreamInstaller { public bool Enabled = true; - public string Identifier; - public string DisplayName; + public string Name; public string RootDirectory; public int SteamAppId; public List SteamApiDllDirectories; - public Dictionary AppInfo = new(); + public VProperty AppInfo = null; + public List> AllSteamDlc = new(); public List> SelectedSteamDlc = new(); public List>>> ExtraSteamAppIdDlc = new(); @@ -47,9 +48,10 @@ namespace CreamInstaller } } else SelectedSteamDlc.Remove(dlcApp); - return; + break; } } + if (!SelectedSteamDlc.Any()) this.Enabled = false; } public ProgramSelection() => All.Add(this); @@ -60,8 +62,6 @@ namespace CreamInstaller public static List AllSafeEnabled => AllSafe.FindAll(s => s.Enabled); - public static ProgramSelection FromIdentifier(string identifier) => AllSafe.Find(s => s.Identifier == identifier); - - public static ProgramSelection FromDisplayName(string displayName) => AllSafe.Find(s => s.DisplayName == displayName); + public static ProgramSelection FromName(string displayName) => AllSafe.Find(s => s.Name == displayName); } } \ No newline at end of file diff --git a/CreamInstaller/SelectForm.Designer.cs b/CreamInstaller/SelectForm.Designer.cs index 0d41770..52275d4 100644 --- a/CreamInstaller/SelectForm.Designer.cs +++ b/CreamInstaller/SelectForm.Designer.cs @@ -108,11 +108,11 @@ namespace CreamInstaller this.treeView1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); + this.treeView1.BackColor = System.Drawing.SystemColors.Control; + this.treeView1.BorderStyle = System.Windows.Forms.BorderStyle.None; this.treeView1.CheckBoxes = true; this.treeView1.Location = new System.Drawing.Point(6, 22); this.treeView1.Name = "treeView1"; - this.treeView1.ShowPlusMinus = false; - this.treeView1.ShowRootLines = false; this.treeView1.Size = new System.Drawing.Size(448, 208); this.treeView1.TabIndex = 1001; // diff --git a/CreamInstaller/SelectForm.cs b/CreamInstaller/SelectForm.cs index 1ee32e3..9b00364 100644 --- a/CreamInstaller/SelectForm.cs +++ b/CreamInstaller/SelectForm.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; -using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows.Forms; @@ -32,63 +31,70 @@ namespace CreamInstaller if (steamInstallPath == null) steamInstallPath = Registry.GetValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Valve\\Steam", "InstallPath", null) as string; if (steamInstallPath != null) { - string mainLibraryFolder = steamInstallPath + "\\steamapps\\common"; - gameDirectories.Add(mainLibraryFolder); - string libraryFolders = steamInstallPath + "\\steamapps\\libraryfolders.vdf"; - VProperty property = VdfConvert.Deserialize(File.ReadAllText(libraryFolders)); - foreach (VProperty _property in property.Value) - if (int.TryParse(_property.Key, out _) && Directory.Exists(_property.Value.ToString())) - gameDirectories.Add(_property.Value.ToString()); + string libraryFolder = steamInstallPath + @"\steamapps"; + gameDirectories.Add(libraryFolder); + string libraryFolders = libraryFolder + @"\libraryfolders.vdf"; + dynamic property = VdfConvert.Deserialize(File.ReadAllText(libraryFolders)); + foreach (dynamic _property in property.Value) + { + if (int.TryParse(_property.Key, out int _)) + { + string path = _property.Value.path.ToString() + @"\steamapps"; + if (string.IsNullOrWhiteSpace(path)) continue; + if (!gameDirectories.Contains(path)) gameDirectories.Add(path); + } + } } return gameDirectories; } } - private bool GetSteamApiDllDirectoriesFromGameDirectory(string gameDirectory, out List steamApiDllDirectories) + private bool GetDllDirectoriesFromGameDirectory(string gameDirectory, out List dllDirectories) { - steamApiDllDirectories = new(); + dllDirectories = new(); if (Program.Canceled) return false; string api = gameDirectory + @"\steam_api.dll"; string api64 = gameDirectory + @"\steam_api64.dll"; - if (File.Exists(api) || File.Exists(api64)) steamApiDllDirectories.Add(gameDirectory); + if (File.Exists(api) || File.Exists(api64)) dllDirectories.Add(gameDirectory); foreach (string _directory in Directory.GetDirectories(gameDirectory)) { if (Program.Canceled) return false; try { - if (GetSteamApiDllDirectoriesFromGameDirectory(_directory, out List _steamApiDllDirectories)) - steamApiDllDirectories.AddRange(_steamApiDllDirectories); + if (GetDllDirectoriesFromGameDirectory(_directory, out List _dllDirectories)) + dllDirectories.AddRange(_dllDirectories); } catch { } } - if (!steamApiDllDirectories.Any()) return false; + if (!dllDirectories.Any()) return false; return true; } - private bool GetSteamAppIdFromGameDirectory(string gameDirectory, out int appId) + private bool GetGamesFromLibraryDirectory(string libraryDirectory, out List> games) { - appId = 0; + games = new(); if (Program.Canceled) return false; - string file = gameDirectory + "\\steam_appid.txt"; - if (File.Exists(file) && int.TryParse(File.ReadAllText(file), out appId)) return true; - foreach (string _directory in Directory.GetDirectories(gameDirectory)) + foreach (string directory in Directory.GetFiles(libraryDirectory)) { if (Program.Canceled) return false; - if (GetSteamAppIdFromGameDirectory(_directory, out appId)) return true; + if (Path.GetExtension(directory) == ".acf") + { + dynamic property = VdfConvert.Deserialize(File.ReadAllText(directory)); + 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 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, buildid, gameDirectory)); + } } - return false; - } - - private bool GetGameDirectoriesFromLibraryDirectory(string libraryDirectory, out List gameDirectories) - { - gameDirectories = new(); - if (Program.Canceled) return false; - foreach (string _directory in Directory.GetDirectories(libraryDirectory)) - { - if (Program.Canceled) return false; - if (Directory.Exists(_directory)) gameDirectories.Add(_directory); - } - if (!gameDirectories.Any()) return false; + if (!games.Any()) return false; return true; } @@ -102,120 +108,121 @@ namespace CreamInstaller { int cur = 0; if (Program.Canceled) return; - List> applicablePrograms = new(); + List> applicablePrograms = new(); string launcherRootDirectory = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\Programs\\Paradox Interactive"; - if (Directory.Exists(launcherRootDirectory)) applicablePrograms.Add(new Tuple("Paradox Launcher", launcherRootDirectory)); + if (Directory.Exists(launcherRootDirectory)) applicablePrograms.Add(new(0, "Paradox Launcher", 0, launcherRootDirectory)); foreach (string libraryDirectory in GameLibraryDirectories) - if (GetGameDirectoriesFromLibraryDirectory(libraryDirectory, out List gameDirectories)) - foreach (string gameDirectory in gameDirectories) - applicablePrograms.Add(new Tuple(Path.GetFileName(gameDirectory) ?? "unknown_" + applicablePrograms.Count, gameDirectory)); + if (GetGamesFromLibraryDirectory(libraryDirectory, out List> games)) + foreach (Tuple game in games) + applicablePrograms.Add(game); RunningTasks = new(); - foreach (Tuple program in applicablePrograms) + foreach (Tuple program in applicablePrograms) { - string identifier = program.Item1; - string rootDirectory = program.Item2; + int appId = program.Item1; + string name = program.Item2; + int buildId = program.Item3; + string directory = program.Item4; if (Program.Canceled) return; + if (Directory.Exists(directory + @"\EasyAntiCheat")) continue; Task task = new(() => { - try + if (Program.Canceled || !GetDllDirectoriesFromGameDirectory(directory, out List dllDirectories)) return; + VProperty appInfo = null; + if (Program.Canceled || (name != "Paradox Launcher" && !SteamCMD.GetAppInfo(appId, buildId, out appInfo))) return; + List> dlc = null; + if (!DLC.TryGetValue(appId, out dlc)) { - int steamAppId = 0; - if (Program.Canceled - || (identifier != "Paradox Launcher" && !GetSteamAppIdFromGameDirectory(rootDirectory, out steamAppId)) - || !GetSteamApiDllDirectoriesFromGameDirectory(rootDirectory, out List steamApiDllDirectories)) - return; - - Dictionary appInfo = null; - if (Program.Canceled || (identifier != "Paradox Launcher" && !SteamCMD.GetAppInfo(steamAppId, out appInfo))) return; - string list = null; - List> dlc = null; - if (!DLC.TryGetValue(steamAppId, out dlc)) + dlc = new(); + DLC.Add(appId, dlc); + } + if (Program.Canceled) return; + List dlcTasks = new(); + List dlcIds = new(); + if (!(appInfo is null)) + { + try { - dlc = new(); - DLC.Add(steamAppId, dlc); + if (!(appInfo.Value["extended"] is 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 (Program.Canceled) return; - List dlcTasks = new(); - if (!(appInfo is null) && appInfo.TryGetValue("listofdlc", out list)) + catch { } + try + { + if (!(appInfo.Value["depots"] is 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); + } + catch { } + } + if (!(dlcIds is null) && dlcIds.Count > 0) + { + foreach (int id in dlcIds) { if (Program.Canceled) return; - string[] nums = Regex.Replace(list, "[^0-9,]+", "").Split(","); - List ids = new(); - foreach (string s in nums) - { - if (Program.Canceled) return; - ids.Add(int.Parse(s)); - } Task task = new(() => { - try - { - foreach (int id in ids) - { - if (Program.Canceled) return; - string dlcName = null; - Dictionary dlcAppInfo = null; - if (SteamCMD.GetAppInfo(id, out dlcAppInfo)) dlcAppInfo.TryGetValue("name", out dlcName); - if (Program.Canceled) return; - if (string.IsNullOrWhiteSpace(dlcName)) dlcName = "Unknown DLC"; - dlc.Add(new Tuple(id, dlcName)); - } - } - catch { } + if (Program.Canceled) return; + string dlcName = null; + VProperty dlcAppInfo = null; + if (SteamCMD.GetAppInfo(id, 0, out dlcAppInfo)) try { dlcName = dlcAppInfo?.Value?["common"]?["name"]?.ToString(); } catch { } + if (Program.Canceled) return; + if (string.IsNullOrWhiteSpace(dlcName)) dlcName = $"Unnamed DLC ({id})"; + dlc.Add(new Tuple(id, dlcName)); }); dlcTasks.Add(task); RunningTasks.Add(task); task.Start(); progress.Report(-RunningTasks.Count); } - else if (identifier != "Paradox Launcher") return; - if (Program.Canceled) return; - - if (string.IsNullOrWhiteSpace(identifier)) return; - string displayName = identifier; - if (!(appInfo is null)) appInfo.TryGetValue("name", out displayName); - if (string.IsNullOrWhiteSpace(displayName)) displayName = "Unknown Game"; - if (Program.Canceled) return; - - ProgramSelection selection = ProgramSelection.FromIdentifier(identifier) ?? new(); - selection.Identifier = identifier; - selection.DisplayName = displayName; - selection.RootDirectory = rootDirectory; - selection.SteamAppId = steamAppId; - selection.SteamApiDllDirectories = steamApiDllDirectories; - selection.AppInfo = appInfo; - - foreach (Task task in dlcTasks.ToList()) - { - if (Program.Canceled) return; - progress.Report(cur++); - task.Wait(); - } - if (Program.Canceled) return; - treeView1.Invoke((MethodInvoker)delegate - { - if (Program.Canceled) return; - TreeNode programNode = treeNodes.Find(s => s.Text == displayName) ?? new(); - programNode.Text = displayName; - programNode.Remove(); - treeView1.Nodes.Add(programNode); - treeNodes.Remove(programNode); - treeNodes.Add(programNode); - foreach (Tuple dlcApp in dlc.ToList()) - { - if (Program.Canceled || programNode is null) return; - TreeNode dlcNode = treeNodes.Find(s => s.Text == dlcApp.Item2) ?? new(); - dlcNode.Text = dlcApp.Item2; - dlcNode.Remove(); - programNode.Nodes.Add(dlcNode); - treeNodes.Remove(dlcNode); - treeNodes.Add(dlcNode); - selection.AllSteamDlc.Add(dlcApp); - selection.SelectedSteamDlc.Add(dlcApp); - } - }); } - catch { } + else if (name != "Paradox Launcher") return; + if (Program.Canceled) return; + + if (string.IsNullOrWhiteSpace(name)) return; + if (Program.Canceled) return; + + ProgramSelection selection = ProgramSelection.FromName(name) ?? new(); + selection.Name = name; + selection.RootDirectory = directory; + selection.SteamAppId = appId; + selection.SteamApiDllDirectories = dllDirectories; + selection.AppInfo = appInfo; + + foreach (Task task in dlcTasks.ToList()) + { + if (Program.Canceled) return; + progress.Report(cur++); + task.Wait(); + } + if (Program.Canceled) return; + treeView1.Invoke((MethodInvoker)delegate + { + if (Program.Canceled) return; + TreeNode programNode = treeNodes.Find(s => s.Text == name) ?? new(); + programNode.Text = name; + programNode.Remove(); + treeView1.Nodes.Add(programNode); + treeNodes.Remove(programNode); + treeNodes.Add(programNode); + foreach (Tuple dlcApp in dlc.ToList()) + { + if (Program.Canceled || programNode is null) return; + TreeNode dlcNode = treeNodes.Find(s => s.Text == dlcApp.Item2) ?? new(); + dlcNode.Text = dlcApp.Item2; + dlcNode.Remove(); + programNode.Nodes.Add(dlcNode); + treeNodes.Remove(dlcNode); + treeNodes.Add(dlcNode); + selection.AllSteamDlc.Add(dlcApp); + selection.SelectedSteamDlc.Add(dlcApp); + } + }); }); RunningTasks.Add(task); task.Start(); @@ -282,7 +289,7 @@ namespace CreamInstaller ProgramSelection.All.RemoveAll(selection => !Directory.Exists(selection.RootDirectory) || !selection.SteamApiDllDirectories.Any()); foreach (TreeNode treeNode in treeNodes) { - if (treeNode.Parent is null && ProgramSelection.FromDisplayName(treeNode.Text) is null) + if (treeNode.Parent is null && ProgramSelection.FromName(treeNode.Text) is null) { treeNode.Remove(); } @@ -297,7 +304,6 @@ namespace CreamInstaller { allCheckBox.Enabled = true; treeView1.CheckBoxes = true; - treeView1.ExpandAll(); treeNodes.ForEach(node => node.Checked = true); if (ProgramSelection.AllSafeEnabled.Any()) { @@ -315,10 +321,10 @@ namespace CreamInstaller private void OnTreeViewNodeCheckedChanged(object sender, TreeViewEventArgs e) { - ProgramSelection selection = ProgramSelection.FromDisplayName(e.Node.Text); + ProgramSelection selection = ProgramSelection.FromName(e.Node.Text); if (selection is null) { - selection = ProgramSelection.FromDisplayName(e.Node.Parent.Text); + selection = ProgramSelection.FromName(e.Node.Parent.Text); treeView1.AfterCheck -= OnTreeViewNodeCheckedChanged; e.Node.Parent.Checked = e.Node.Parent.Nodes.Cast().ToList().Any(treeNode => treeNode.Checked); treeView1.AfterCheck += OnTreeViewNodeCheckedChanged; @@ -339,30 +345,29 @@ namespace CreamInstaller private void OnLoad(object sender, EventArgs e) { - treeView1.BeforeCollapse += (sender, e) => e.Cancel = true; treeView1.AfterCheck += OnTreeViewNodeCheckedChanged; OnLoad(); } private static bool ParadoxLauncherDlcDialog(Form form) { - ProgramSelection paradoxLauncher = ProgramSelection.FromIdentifier("Paradox Launcher"); + ProgramSelection paradoxLauncher = ProgramSelection.FromName("Paradox Launcher"); if (!(paradoxLauncher is null) && paradoxLauncher.Enabled) { paradoxLauncher.ExtraSteamAppIdDlc = new(); foreach (ProgramSelection selection in ProgramSelection.AllSafeEnabled) { - if (selection.Identifier == paradoxLauncher.Identifier) continue; - if (!selection.AppInfo.TryGetValue("publisher", out string publisher) || publisher != "Paradox Interactive") continue; - paradoxLauncher.ExtraSteamAppIdDlc.Add(new(selection.SteamAppId, selection.DisplayName, selection.SelectedSteamDlc)); + if (selection.Name == paradoxLauncher.Name) continue; + if (selection.AppInfo.Value["extended"]["publisher"].ToString() != "Paradox Interactive") continue; + paradoxLauncher.ExtraSteamAppIdDlc.Add(new(selection.SteamAppId, selection.Name, selection.SelectedSteamDlc)); } if (!paradoxLauncher.ExtraSteamAppIdDlc.Any()) { foreach (ProgramSelection selection in ProgramSelection.AllSafe) { - if (selection.Identifier == paradoxLauncher.Identifier) continue; - if (!selection.AppInfo.TryGetValue("publisher", out string publisher) || publisher != "Paradox Interactive") continue; - paradoxLauncher.ExtraSteamAppIdDlc.Add(new(selection.SteamAppId, selection.DisplayName, selection.AllSteamDlc)); + if (selection.Name == paradoxLauncher.Name) continue; + if (selection.AppInfo.Value["extended"]["publisher"].ToString() != "Paradox Interactive") continue; + paradoxLauncher.ExtraSteamAppIdDlc.Add(new(selection.SteamAppId, selection.Name, selection.AllSteamDlc)); } } if (!paradoxLauncher.ExtraSteamAppIdDlc.Any()) diff --git a/CreamInstaller/SteamCMD.cs b/CreamInstaller/SteamCMD.cs index aa98e40..4c0ed2e 100644 --- a/CreamInstaller/SteamCMD.cs +++ b/CreamInstaller/SteamCMD.cs @@ -1,4 +1,5 @@ -using System; +using Gameloop.Vdf; +using Gameloop.Vdf.Linq; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -53,37 +54,48 @@ namespace CreamInstaller if (!File.Exists(DllPath)) Run($@"+quit", out _); } - public static bool GetAppInfo(int steamAppId, out Dictionary appInfo) + private static Dictionary retries = new(); + + public static bool GetAppInfo(int appId, int buildId, out VProperty appInfo) { - appInfo = new(); + appInfo = null; if (Program.Canceled) return false; string output; - string appUpdatePath = $@"{AppInfoCachePath}\{steamAppId}"; + string appUpdatePath = $@"{AppInfoCachePath}\{appId}"; string appUpdateFile = $@"{appUpdatePath}\appinfo.txt"; - // need a fast way to make sure the cached info is up-to-date and correct - //if (Directory.Exists(appUpdatePath) && File.Exists(appUpdateFile)) output = File.ReadAllText(appUpdateFile); - //else - //{ - Run($@"+@ShutdownOnFailedCommand 0 +login anonymous +app_info_print {steamAppId} +force_install_dir {appUpdatePath} +app_update 4 +quit", out _); - Run($@"+@ShutdownOnFailedCommand 0 +login anonymous +app_info_print {steamAppId} +quit", out output); - File.WriteAllText(appUpdateFile, output); - //} - if (Program.Canceled || output is null) return false; - foreach (string s in output.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)) + if (Directory.Exists(appUpdatePath) && File.Exists(appUpdateFile)) output = File.ReadAllText(appUpdateFile); + else { - if (Program.Canceled) return false; - int first = s.IndexOf("\""); - int second = 1 + first + s.Substring(first + 1).IndexOf("\""); - int third = 1 + second + s.Substring(second + 1).IndexOf("\""); - int fourth = 1 + third + s.Substring(third + 1).IndexOf("\""); - if (first > -1 && second > 0 && third > 0 && fourth > 0) + Run($@"+@ShutdownOnFailedCommand 0 +login anonymous +app_info_print {appId} +force_install_dir {appUpdatePath} +app_update 4 +quit", out _); + Run($@"+@ShutdownOnFailedCommand 0 +login anonymous +app_info_print {appId} +quit", out output); + int openBracket = output.IndexOf("{"); + int closeBracket = output.LastIndexOf("}"); + output = $"\"{appId}\"\n" + output.Substring(openBracket, 1 + closeBracket - openBracket); + File.WriteAllText(appUpdateFile, output); + } + if (Program.Canceled || output is null) return false; + appInfo = VdfConvert.Deserialize(output); + try + { + VToken type = appInfo?.Value?["common"]?["type"]; + if (type is null || type.ToString() == "Game") { - string a = s.Substring(first + 1, Math.Max(second - first - 1, 0)); - string b = s.Substring(third + 1, Math.Max(fourth - third - 1, 0)); - if (string.IsNullOrWhiteSpace(a) || string.IsNullOrWhiteSpace(b)) continue; - if (!appInfo.TryGetValue(a, out _)) appInfo.Add(a, b); + string buildid = appInfo.Value["depots"]?["public"]?["buildid"]?.ToString(); + buildid = buildid ?? appInfo.Value["depots"]?["branches"]?["public"]?["buildid"]?.ToString(); + if (type is null || int.Parse(buildid) < buildId + || appInfo.Value["extended"] is null + || appInfo.Value["depots"] is null) + { + if (retries.TryGetValue(appId, out int count)) retries[appId] = ++count; + else retries.Add(appId, 1); + if (count > 3) return false; + File.Delete(appUpdateFile); + bool success = GetAppInfo(appId, buildId, out appInfo); + return success; + } } } + catch { } return true; }