From cdb9d6a658455585c2dd5bc2a970fdf0b134d24f Mon Sep 17 00:00:00 2001 From: pointfeev Date: Thu, 11 Nov 2021 02:29:59 -0500 Subject: [PATCH] fix concurrency error, add better exception handling, create folders --- CreamInstaller/CreamInstaller.csproj | 9 +-- CreamInstaller/CustomMessageException.cs | 17 ------ CreamInstaller/ExceptionHandler.cs | 55 ++++++++++++++++++ .../{ => Forms}/DialogForm.Designer.cs | 0 CreamInstaller/{ => Forms}/DialogForm.cs | 0 CreamInstaller/{ => Forms}/DialogForm.resx | 0 .../{ => Forms}/InstallForm.Designer.cs | 0 CreamInstaller/{ => Forms}/InstallForm.cs | 0 CreamInstaller/{ => Forms}/InstallForm.resx | 0 .../{ => Forms}/MainForm.Designer.cs | 0 CreamInstaller/{ => Forms}/MainForm.cs | 0 CreamInstaller/{ => Forms}/MainForm.resx | 0 .../{ => Forms}/SelectForm.Designer.cs | 0 CreamInstaller/{ => Forms}/SelectForm.cs | 48 +++++++-------- CreamInstaller/{ => Forms}/SelectForm.resx | 0 CreamInstaller/Program.cs | 10 +++- CreamInstaller/{ => Resources}/Resources.cs | 2 +- CreamInstaller/{ => Resources}/steam_api.dll | Bin .../{ => Resources}/steam_api64.dll | Bin CreamInstaller/SteamCMD.cs | 29 ++++++--- 20 files changed, 113 insertions(+), 57 deletions(-) delete mode 100644 CreamInstaller/CustomMessageException.cs create mode 100644 CreamInstaller/ExceptionHandler.cs rename CreamInstaller/{ => Forms}/DialogForm.Designer.cs (100%) rename CreamInstaller/{ => Forms}/DialogForm.cs (100%) rename CreamInstaller/{ => Forms}/DialogForm.resx (100%) rename CreamInstaller/{ => Forms}/InstallForm.Designer.cs (100%) rename CreamInstaller/{ => Forms}/InstallForm.cs (100%) rename CreamInstaller/{ => Forms}/InstallForm.resx (100%) rename CreamInstaller/{ => Forms}/MainForm.Designer.cs (100%) rename CreamInstaller/{ => Forms}/MainForm.cs (100%) rename CreamInstaller/{ => Forms}/MainForm.resx (100%) rename CreamInstaller/{ => Forms}/SelectForm.Designer.cs (100%) rename CreamInstaller/{ => Forms}/SelectForm.cs (92%) rename CreamInstaller/{ => Forms}/SelectForm.resx (100%) rename CreamInstaller/{ => Resources}/Resources.cs (84%) rename CreamInstaller/{ => Resources}/steam_api.dll (100%) rename CreamInstaller/{ => Resources}/steam_api64.dll (100%) diff --git a/CreamInstaller/CreamInstaller.csproj b/CreamInstaller/CreamInstaller.csproj index 2659c7b..6a1a5ea 100644 --- a/CreamInstaller/CreamInstaller.csproj +++ b/CreamInstaller/CreamInstaller.csproj @@ -24,6 +24,7 @@ pointfeev pointfeev.creaminstaller CreamInstaller.Program + false @@ -38,11 +39,11 @@ - + - + @@ -51,8 +52,8 @@ - - + + True True diff --git a/CreamInstaller/CustomMessageException.cs b/CreamInstaller/CustomMessageException.cs deleted file mode 100644 index d42b3b4..0000000 --- a/CreamInstaller/CustomMessageException.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; - -namespace CreamInstaller -{ - public class CustomMessageException : Exception - { - private string message; - public override string Message => message ?? "CustomMessageException"; - - public override string ToString() => Message; - - public CustomMessageException(string message) - { - this.message = message; - } - } -} \ No newline at end of file diff --git a/CreamInstaller/ExceptionHandler.cs b/CreamInstaller/ExceptionHandler.cs new file mode 100644 index 0000000..b3fdfce --- /dev/null +++ b/CreamInstaller/ExceptionHandler.cs @@ -0,0 +1,55 @@ +using System; +using System.Windows.Forms; + +namespace CreamInstaller +{ + public static class ExceptionHandler + { + public static bool OutputException(Exception e) + { + while (!(e.InnerException is null)) e = e.InnerException; + string output = ""; + string[] stackTrace = e.StackTrace?.Split('\n'); + if (!(stackTrace is null) && stackTrace.Length > 0) + { + output += "STACK TRACE\n"; + for (int i = 0; i < Math.Min(stackTrace.Length, 3); i++) + { + string line = stackTrace[i]; + if (!(line is null)) + { + output += "\n " + line.Substring(line.IndexOf("at")); + } + } + } + string[] messageLines = e.Message?.Split('\n'); + if (!(messageLines is null) && messageLines.Length > 0) + { + if (output.Length > 0) output += "\n\n"; + output += "MESSAGE\n"; + for (int i = 0; i < messageLines.Length; i++) + { + string line = messageLines[i]; + if (!(line is null)) + { + output += "\n " + messageLines[i]; + } + } + } + return MessageBox.Show(output, caption: "CreamInstaller encountered an exception", buttons: MessageBoxButtons.RetryCancel, icon: MessageBoxIcon.Error) == DialogResult.Retry; + } + } + + public class CustomMessageException : Exception + { + private string message; + public override string Message => message ?? "CustomMessageException"; + + public override string ToString() => Message; + + public CustomMessageException(string message) + { + this.message = message; + } + } +} diff --git a/CreamInstaller/DialogForm.Designer.cs b/CreamInstaller/Forms/DialogForm.Designer.cs similarity index 100% rename from CreamInstaller/DialogForm.Designer.cs rename to CreamInstaller/Forms/DialogForm.Designer.cs diff --git a/CreamInstaller/DialogForm.cs b/CreamInstaller/Forms/DialogForm.cs similarity index 100% rename from CreamInstaller/DialogForm.cs rename to CreamInstaller/Forms/DialogForm.cs diff --git a/CreamInstaller/DialogForm.resx b/CreamInstaller/Forms/DialogForm.resx similarity index 100% rename from CreamInstaller/DialogForm.resx rename to CreamInstaller/Forms/DialogForm.resx diff --git a/CreamInstaller/InstallForm.Designer.cs b/CreamInstaller/Forms/InstallForm.Designer.cs similarity index 100% rename from CreamInstaller/InstallForm.Designer.cs rename to CreamInstaller/Forms/InstallForm.Designer.cs diff --git a/CreamInstaller/InstallForm.cs b/CreamInstaller/Forms/InstallForm.cs similarity index 100% rename from CreamInstaller/InstallForm.cs rename to CreamInstaller/Forms/InstallForm.cs diff --git a/CreamInstaller/InstallForm.resx b/CreamInstaller/Forms/InstallForm.resx similarity index 100% rename from CreamInstaller/InstallForm.resx rename to CreamInstaller/Forms/InstallForm.resx diff --git a/CreamInstaller/MainForm.Designer.cs b/CreamInstaller/Forms/MainForm.Designer.cs similarity index 100% rename from CreamInstaller/MainForm.Designer.cs rename to CreamInstaller/Forms/MainForm.Designer.cs diff --git a/CreamInstaller/MainForm.cs b/CreamInstaller/Forms/MainForm.cs similarity index 100% rename from CreamInstaller/MainForm.cs rename to CreamInstaller/Forms/MainForm.cs diff --git a/CreamInstaller/MainForm.resx b/CreamInstaller/Forms/MainForm.resx similarity index 100% rename from CreamInstaller/MainForm.resx rename to CreamInstaller/Forms/MainForm.resx diff --git a/CreamInstaller/SelectForm.Designer.cs b/CreamInstaller/Forms/SelectForm.Designer.cs similarity index 100% rename from CreamInstaller/SelectForm.Designer.cs rename to CreamInstaller/Forms/SelectForm.Designer.cs diff --git a/CreamInstaller/SelectForm.cs b/CreamInstaller/Forms/SelectForm.cs similarity index 92% rename from CreamInstaller/SelectForm.cs rename to CreamInstaller/Forms/SelectForm.cs index 76bd106..50875c6 100644 --- a/CreamInstaller/SelectForm.cs +++ b/CreamInstaller/Forms/SelectForm.cs @@ -2,6 +2,7 @@ using Gameloop.Vdf.Linq; using Microsoft.Win32; using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; @@ -75,7 +76,7 @@ namespace CreamInstaller return true; } - private bool GetGamesFromLibraryDirectory(string libraryDirectory, out List> games) + private bool GetGamesFromLibraryDirectory(string libraryDirectory, out List> games) { games = new(); if (Program.Canceled) return false; @@ -91,14 +92,16 @@ namespace CreamInstaller string installdir = property.Value.installdir.ToString(); string name = property.Value.name.ToString(); string _buildid = property.Value.buildid.ToString(); - if (string.IsNullOrWhiteSpace(_appid) + if (string.IsNullOrWhiteSpace(_appid) || string.IsNullOrWhiteSpace(installdir) || string.IsNullOrWhiteSpace(name) || string.IsNullOrWhiteSpace(_buildid)) continue; - string gameDirectory = libraryDirectory + @"\common\" + installdir; + 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, buildid, gameDirectory)); + games.Add(new(appid, name, branch, buildid, gameDirectory)); } catch {} } @@ -109,46 +112,39 @@ namespace CreamInstaller private readonly List treeNodes = new(); - internal readonly Dictionary> DLC = new(); - - internal List RunningTasks = null; + internal List RunningTasks = new(); private void GetCreamApiApplicablePrograms(IProgress progress) { 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(0, "Paradox Launcher", 0, launcherRootDirectory)); + if (Directory.Exists(launcherRootDirectory)) applicablePrograms.Add(new(0, "Paradox Launcher", "", 0, launcherRootDirectory)); foreach (string libraryDirectory in GameLibraryDirectories) - if (GetGamesFromLibraryDirectory(libraryDirectory, out List> games)) - foreach (Tuple game in games) + if (GetGamesFromLibraryDirectory(libraryDirectory, out List> games)) + foreach (Tuple game in games) applicablePrograms.Add(game); - RunningTasks = new(); - foreach (Tuple program in applicablePrograms) + RunningTasks.Clear(); + foreach (Tuple program in applicablePrograms) { int appId = program.Item1; string name = program.Item2; - int buildId = program.Item3; - string directory = program.Item4; + string branch = program.Item3; + int buildId = program.Item4; + string directory = program.Item5; if (Program.Canceled) return; // easy anti cheat detects DLL changes, so skip those games if (Directory.Exists(directory + @"\EasyAntiCheat")) continue; // battleye in DayZ detects DLL changes, but not in Arma3? - //if (Directory.Exists(directory + @"\BattlEye")) continue; - if (name == "DayZ") continue; + if (name != "Arma 3" && Directory.Exists(directory + @"\BattlEye")) continue; Task task = new(() => { 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; - Dictionary dlc = null; - if (!DLC.TryGetValue(appId, out dlc)) - { - dlc = new(); - DLC.Add(appId, dlc); - } + if (Program.Canceled || (name != "Paradox Launcher" && !SteamCMD.GetAppInfo(appId, out appInfo, branch, buildId))) return; if (Program.Canceled) return; + ConcurrentDictionary dlc = new(); List dlcTasks = new(); List dlcIds = new(); if (!(appInfo is null)) @@ -175,9 +171,9 @@ namespace CreamInstaller if (Program.Canceled) return; string dlcName = null; VProperty dlcAppInfo = null; - if (SteamCMD.GetAppInfo(id, 0, out dlcAppInfo)) dlcName = dlcAppInfo?.Value?["common"]?["name"]?.ToString(); + if (SteamCMD.GetAppInfo(id, out dlcAppInfo)) dlcName = dlcAppInfo?.Value?["common"]?["name"]?.ToString(); if (Program.Canceled) return; - if (string.IsNullOrWhiteSpace(dlcName)) dlcName = $"Unnamed DLC ({id})"; + if (string.IsNullOrWhiteSpace(dlcName)) dlcName = $"Unknown DLC ({id})"; dlc[id] = dlcName; }); dlcTasks.Add(task); diff --git a/CreamInstaller/SelectForm.resx b/CreamInstaller/Forms/SelectForm.resx similarity index 100% rename from CreamInstaller/SelectForm.resx rename to CreamInstaller/Forms/SelectForm.resx diff --git a/CreamInstaller/Program.cs b/CreamInstaller/Program.cs index e847bcf..6ce1ad4 100644 --- a/CreamInstaller/Program.cs +++ b/CreamInstaller/Program.cs @@ -29,7 +29,15 @@ namespace CreamInstaller Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.ApplicationExit += new EventHandler(OnApplicationExit); - Application.Run(new MainForm()); + retry: + try + { + Application.Run(new MainForm()); + } + catch (Exception e) + { + if (ExceptionHandler.OutputException(e)) goto retry; + } } mutex.Close(); } diff --git a/CreamInstaller/Resources.cs b/CreamInstaller/Resources/Resources.cs similarity index 84% rename from CreamInstaller/Resources.cs rename to CreamInstaller/Resources/Resources.cs index cfaf885..59f7ba3 100644 --- a/CreamInstaller/Resources.cs +++ b/CreamInstaller/Resources/Resources.cs @@ -7,7 +7,7 @@ namespace CreamInstaller { public static void WriteResourceToFile(string resourceName, string filePath) { - using Stream resource = Assembly.GetExecutingAssembly().GetManifestResourceStream("CreamInstaller." + resourceName); + using Stream resource = Assembly.GetExecutingAssembly().GetManifestResourceStream(@"CreamInstaller.Resources." + resourceName); using FileStream file = new(filePath, FileMode.Create, FileAccess.Write); resource.CopyTo(file); } diff --git a/CreamInstaller/steam_api.dll b/CreamInstaller/Resources/steam_api.dll similarity index 100% rename from CreamInstaller/steam_api.dll rename to CreamInstaller/Resources/steam_api.dll diff --git a/CreamInstaller/steam_api64.dll b/CreamInstaller/Resources/steam_api64.dll similarity index 100% rename from CreamInstaller/steam_api64.dll rename to CreamInstaller/Resources/steam_api64.dll diff --git a/CreamInstaller/SteamCMD.cs b/CreamInstaller/SteamCMD.cs index ffd7e2f..969ad02 100644 --- a/CreamInstaller/SteamCMD.cs +++ b/CreamInstaller/SteamCMD.cs @@ -1,5 +1,6 @@ using Gameloop.Vdf; using Gameloop.Vdf.Linq; +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -15,7 +16,9 @@ namespace CreamInstaller public static string FilePath = DirectoryPath + @"\steamcmd.exe"; public static string ArchivePath = DirectoryPath + @"\steamcmd.zip"; public static string DllPath = DirectoryPath + @"\steamclient.dll"; - public static string AppInfoCachePath = DirectoryPath + @"\appinfocache"; + public static string AppCachePath = DirectoryPath + @"\appcache"; + public static string AppCacheAppInfoPath = AppCachePath + @"\appinfo.vdf"; + public static string AppInfoPath = DirectoryPath + @"\appinfo"; public static bool Run(string command, out string output) { @@ -52,16 +55,18 @@ namespace CreamInstaller ZipFile.ExtractToDirectory(ArchivePath, DirectoryPath); File.Delete(ArchivePath); } + if (File.Exists(AppCacheAppInfoPath)) File.Delete(AppCacheAppInfoPath); if (!File.Exists(DllPath)) Run($@"+quit", out _); } - public static bool GetAppInfo(int appId, int buildId, out VProperty appInfo) + public static bool GetAppInfo(int appId, out VProperty appInfo, string branch = "public", int buildId = 0) { appInfo = null; if (Program.Canceled) return false; string output; - string appUpdatePath = $@"{AppInfoCachePath}\{appId}"; + string appUpdatePath = $@"{AppInfoPath}\{appId}"; string appUpdateFile = $@"{appUpdatePath}\appinfo.txt"; + restart: if (Directory.Exists(appUpdatePath) && File.Exists(appUpdateFile)) output = File.ReadAllText(appUpdateFile); else { @@ -76,18 +81,26 @@ namespace CreamInstaller } } if (Program.Canceled || output is null) return false; - try { appInfo = VdfConvert.Deserialize(output); } catch { } + try { appInfo = VdfConvert.Deserialize(output); } + catch + { + if (File.Exists(appUpdateFile)) + { + File.Delete(appUpdateFile); + goto restart; + } + } + if (!(appInfo is null) && appInfo.Value is VValue) goto restart; if (appInfo is null || (!(appInfo.Value is VValue) && appInfo.Value.Children().ToList().Count == 0)) return true; VToken type = appInfo.Value is VValue ? null : appInfo.Value?["common"]?["type"]; if (type is null || type.ToString() == "Game") { - string buildid = appInfo.Value is VValue ? null : appInfo.Value["depots"]?["public"]?["buildid"]?.ToString(); + string buildid = appInfo.Value is VValue ? null : appInfo.Value["depots"]?["branches"]?[branch]?["buildid"]?.ToString(); if (buildid is null && !(type is null)) return true; if (type is null || int.Parse(buildid) < buildId) { - File.Delete(appUpdateFile); - bool success = GetAppInfo(appId, buildId, out appInfo); - return success; + if (File.Exists(appUpdateFile)) File.Delete(appUpdateFile); + goto restart; } } return true;