From c5fc5f561adf65966845b86d296e71f249958880 Mon Sep 17 00:00:00 2001 From: pointfeev Date: Fri, 9 Jun 2023 19:04:54 -0400 Subject: [PATCH] v4.9.5 - Minor refactoring & optimization - Removed the no-longer-necessary locked DLL check in favor of SafeIO - Fixed uninstall all cancel functionality - Improved SafeIO crucial checks and warning outputs --- CreamInstaller/CreamInstaller.csproj | 2 +- CreamInstaller/Forms/InstallForm.cs | 70 +++++++++---------- CreamInstaller/Forms/SelectForm.cs | 18 ++--- CreamInstaller/Forms/UpdateForm.cs | 2 +- .../Platforms/Paradox/ParadoxLauncher.cs | 61 ++++++++-------- CreamInstaller/Platforms/Steam/SteamCMD.cs | 4 +- .../Platforms/Steam/SteamLibrary.cs | 2 +- CreamInstaller/Platforms/Steam/SteamStore.cs | 10 +-- CreamInstaller/Program.cs | 23 +----- CreamInstaller/Resources/Koaloader.cs | 20 +++--- CreamInstaller/Resources/Resources.cs | 18 +++-- CreamInstaller/Resources/ScreamAPI.cs | 8 +-- CreamInstaller/Resources/SmokeAPI.cs | 8 +-- CreamInstaller/Resources/UplayR1.cs | 8 +-- CreamInstaller/Resources/UplayR2.cs | 8 +-- CreamInstaller/Selection.cs | 42 ----------- CreamInstaller/Utility/ExceptionHandler.cs | 2 +- CreamInstaller/Utility/SafeIO.cs | 62 ++++++++-------- 18 files changed, 151 insertions(+), 217 deletions(-) diff --git a/CreamInstaller/CreamInstaller.csproj b/CreamInstaller/CreamInstaller.csproj index d899c11..90acdf8 100644 --- a/CreamInstaller/CreamInstaller.csproj +++ b/CreamInstaller/CreamInstaller.csproj @@ -4,7 +4,7 @@ net7.0-windows True Resources\ini.ico - 4.9.4 + 4.9.5 2021, pointfeev (https://github.com/pointfeev) CreamInstaller Automatic DLC Unlocker Installer & Configuration Generator diff --git a/CreamInstaller/Forms/InstallForm.cs b/CreamInstaller/Forms/InstallForm.cs index 43bbbb0..20b2d82 100644 --- a/CreamInstaller/Forms/InstallForm.cs +++ b/CreamInstaller/Forms/InstallForm.cs @@ -14,14 +14,12 @@ namespace CreamInstaller.Forms; internal sealed partial class InstallForm : CustomForm { - private readonly HashSet disabledSelections = new(); - - private readonly int programCount = Selection.AllEnabled.Count(); + private readonly HashSet activeSelections = new(); private readonly bool uninstalling; private int completeOperationsCount; - private int operationsCount; internal bool Reselecting; + private int selectionCount; internal InstallForm(bool uninstall = false) { @@ -77,7 +75,7 @@ internal sealed partial class InstallForm : CustomForm foreach (string directory in invalidDirectories) { if (Program.Canceled) - throw new CustomMessageException("The operation was canceled."); + return; directory.GetKoaloaderComponents(out string old_config, out string config); if (directory.GetKoaloaderProxies().Any(proxy => proxy.FileExists() && proxy.IsResourceFile(ResourceIdentifier.Koaloader)) || directory != selection.RootDirectory && Koaloader.AutoLoadDLLs.Any(pair => (directory + @"\" + pair.dll).FileExists()) @@ -91,7 +89,7 @@ internal sealed partial class InstallForm : CustomForm foreach ((string directory, BinaryType _) in selection.ExecutableDirectories) { if (Program.Canceled) - throw new CustomMessageException("The operation was canceled."); + return; directory.GetKoaloaderComponents(out string old_config, out string config); if (directory.GetKoaloaderProxies().Any(proxy => proxy.FileExists() && proxy.IsResourceFile(ResourceIdentifier.Koaloader)) || Koaloader.AutoLoadDLLs.Any(pair => (directory + @"\" + pair.dll).FileExists()) || old_config.FileExists() || config.FileExists()) @@ -105,15 +103,15 @@ internal sealed partial class InstallForm : CustomForm foreach (string directory in selection.DllDirectories) { if (Program.Canceled) - throw new CustomMessageException("The operation was canceled."); + return; if (selection.Platform is Platform.Steam or Platform.Paradox) { directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string old_config, out string config, out string old_log, out string log, out string cache); if (uninstallProxy - ? api32_o.FileExists() || api64_o.FileExists() || old_config.FileExists() || config.FileExists() || old_log.FileExists() - || log.FileExists() || cache.FileExists() - : api32.FileExists() || api64.FileExists()) + ? api32_o.FileExists() || api64_o.FileExists() || old_config.FileExists() || config.FileExists() || old_log.FileExists() || log.FileExists() + || cache.FileExists() + : api32.FileExists() || api64.FileExists()) { UpdateUser( $"{(uninstallProxy ? "Uninstalling" : "Installing")} SmokeAPI" + $" {(uninstallProxy ? "from" : "for")} " + selection.Name @@ -128,8 +126,8 @@ internal sealed partial class InstallForm : CustomForm { directory.GetScreamApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config, out string log); if (uninstallProxy - ? api32_o.FileExists() || api64_o.FileExists() || config.FileExists() || log.FileExists() - : api32.FileExists() || api64.FileExists()) + ? api32_o.FileExists() || api64_o.FileExists() || config.FileExists() || log.FileExists() + : api32.FileExists() || api64.FileExists()) { UpdateUser( $"{(uninstallProxy ? "Uninstalling" : "Installing")} ScreamAPI" + $" {(uninstallProxy ? "from" : "for")} " + selection.Name @@ -144,8 +142,8 @@ internal sealed partial class InstallForm : CustomForm { directory.GetUplayR1Components(out string api32, out string api32_o, out string api64, out string api64_o, out string config, out string log); if (uninstallProxy - ? api32_o.FileExists() || api64_o.FileExists() || config.FileExists() || log.FileExists() - : api32.FileExists() || api64.FileExists()) + ? api32_o.FileExists() || api64_o.FileExists() || config.FileExists() || log.FileExists() + : api32.FileExists() || api64.FileExists()) { UpdateUser( $"{(uninstallProxy ? "Uninstalling" : "Installing")} Uplay R1 Unlocker" + $" {(uninstallProxy ? "from" : "for")} " + selection.Name @@ -157,8 +155,8 @@ internal sealed partial class InstallForm : CustomForm } directory.GetUplayR2Components(out string old_api32, out string old_api64, out api32, out api32_o, out api64, out api64_o, out config, out log); if (uninstallProxy - ? api32_o.FileExists() || api64_o.FileExists() || config.FileExists() || log.FileExists() - : old_api32.FileExists() || old_api64.FileExists() || api32.FileExists() || api64.FileExists()) + ? api32_o.FileExists() || api64_o.FileExists() || config.FileExists() || log.FileExists() + : old_api32.FileExists() || old_api64.FileExists() || api32.FileExists() || api64.FileExists()) { UpdateUser( $"{(uninstallProxy ? "Uninstalling" : "Installing")} Uplay R2 Unlocker" + $" {(uninstallProxy ? "from" : "for")} " + selection.Name @@ -175,7 +173,7 @@ internal sealed partial class InstallForm : CustomForm foreach ((string directory, BinaryType binaryType) in selection.ExecutableDirectories) { if (Program.Canceled) - throw new CustomMessageException("The operation was canceled."); + return; UpdateUser("Installing Koaloader to " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation); await Koaloader.Install(directory, binaryType, selection, selection.RootDirectory, this); } @@ -184,19 +182,19 @@ internal sealed partial class InstallForm : CustomForm private async Task Operate() { - HashSet programSelections = Selection.AllEnabled.ToHashSet(); - operationsCount = programSelections.Count; + operationsCount = activeSelections.Count; completeOperationsCount = 0; - foreach (Selection selection in programSelections) + foreach (Selection selection in activeSelections) { - if (Program.Canceled || !Program.AreDllsLockedDialog(this, selection)) + if (Program.Canceled) throw new CustomMessageException("The operation was canceled."); try { await OperateFor(selection); + if (Program.Canceled) + throw new CustomMessageException("The operation was canceled."); UpdateUser($"Operation succeeded for {selection.Name}.", LogTextBox.Success); - selection.Enabled = false; - _ = disabledSelections.Add(selection); + _ = activeSelections.Remove(selection); } catch (Exception exception) { @@ -205,15 +203,12 @@ internal sealed partial class InstallForm : CustomForm ++completeOperationsCount; } Program.Cleanup(); - HashSet failedSelections = Selection.AllEnabled.ToHashSet(); - if (failedSelections.Count > 0) - if (failedSelections.Count == 1) - throw new CustomMessageException($"Operation failed for {failedSelections.First().Name}."); + int activeCount = activeSelections.Count; + if (activeCount > 0) + if (activeCount == 1) + throw new CustomMessageException($"Operation failed for {activeSelections.First().Name}."); else - throw new CustomMessageException($"Operation failed for {failedSelections.Count} programs."); - foreach (Selection selection in disabledSelections) - selection.Enabled = true; - disabledSelections.Clear(); + throw new CustomMessageException($"Operation failed for {activeCount} programs."); } private async void Start() @@ -227,7 +222,7 @@ internal sealed partial class InstallForm : CustomForm try { await Operate(); - UpdateUser($"DLC unlocker(s) successfully {(uninstalling ? "uninstalled" : "installed and generated")} for " + programCount + " program(s).", + UpdateUser($"DLC unlocker(s) successfully {(uninstalling ? "uninstalled" : "installed and generated")} for " + selectionCount + " program(s).", LogTextBox.Success); } catch (Exception exception) @@ -241,13 +236,19 @@ internal sealed partial class InstallForm : CustomForm reselectButton.Enabled = true; } - private void OnLoad(object sender, EventArgs _) + private void OnLoad(object sender, EventArgs a) { retry: try { userInfoLabel.Text = "Loading . . . "; logTextBox.Text = string.Empty; + selectionCount = 0; + foreach (Selection selection in Selection.AllEnabled) + { + selectionCount++; + _ = activeSelections.Add(selection); + } Start(); } catch (Exception e) @@ -276,9 +277,6 @@ internal sealed partial class InstallForm : CustomForm { Program.Cleanup(); Reselecting = true; - foreach (Selection selection in disabledSelections) - selection.Enabled = true; - disabledSelections.Clear(); Close(); } } \ No newline at end of file diff --git a/CreamInstaller/Forms/SelectForm.cs b/CreamInstaller/Forms/SelectForm.cs index 096be6c..f157dfc 100644 --- a/CreamInstaller/Forms/SelectForm.cs +++ b/CreamInstaller/Forms/SelectForm.cs @@ -535,7 +535,7 @@ internal sealed partial class SelectForm : CustomForm programsToScan is not null && programsToScan.Any(p => p.platform is Platform.Paradox && p.id == "PL"))); if (SteamLibrary.InstallPath.DirectoryExists()) foreach ((string appId, string name, string _, int _, string _) in (await SteamLibrary.GetGames()).Where(g - => !Program.IsGameBlocked(g.name, g.gameDirectory))) + => !Program.IsGameBlocked(g.name, g.gameDirectory))) gameChoices.Add((Platform.Steam, appId, name, programsToScan is not null && programsToScan.Any(p => p.platform is Platform.Steam && p.id == appId))); if (EpicLibrary.EpicManifestsPath.DirectoryExists() || HeroicLibrary.HeroicLibraryPath.DirectoryExists()) @@ -709,7 +709,7 @@ internal sealed partial class SelectForm : CustomForm programsGroupBox.Size = programsGroupBox.Size with { Height = programsGroupBox.Size.Height - 3 - progressLabel.Size.Height - progressLabelGames.Size.Height - progressLabelDLCs.Size.Height - - progressBar.Size.Height + - progressBar.Size.Height }; } @@ -723,7 +723,7 @@ internal sealed partial class SelectForm : CustomForm programsGroupBox.Size = programsGroupBox.Size with { Height = programsGroupBox.Size.Height + 3 + progressLabel.Size.Height + progressLabelGames.Size.Height + progressLabelDLCs.Size.Height - + progressBar.Size.Height + + progressBar.Size.Height }; } @@ -785,7 +785,11 @@ internal sealed partial class SelectForm : CustomForm if (id == "PL") { _ = items.Add(new ToolStripSeparator()); - async void EventHandler(object sender, EventArgs e) => await ParadoxLauncher.Repair(this, selection); + async void EventHandler(object sender, EventArgs e) + { + _ = await ParadoxLauncher.Repair(this, selection); + Program.Canceled = false; + } _ = items.Add(new ContextMenuItem("Repair", "Command Prompt", EventHandler)); } _ = items.Add(new ToolStripSeparator()); @@ -890,11 +894,7 @@ internal sealed partial class SelectForm : CustomForm private void OnAccept(bool uninstall = false) { - if (Selection.All.IsEmpty) - return; - if (Selection.AllEnabled.Any(selection => !Program.AreDllsLockedDialog(this, selection))) - return; - if (!uninstall && ParadoxLauncher.DlcDialog(this)) + if (Selection.All.IsEmpty || !uninstall && ParadoxLauncher.DlcDialog(this)) return; Hide(); InstallForm form = new(uninstall); diff --git a/CreamInstaller/Forms/UpdateForm.cs b/CreamInstaller/Forms/UpdateForm.cs index da6d9dc..b7a6696 100644 --- a/CreamInstaller/Forms/UpdateForm.cs +++ b/CreamInstaller/Forms/UpdateForm.cs @@ -155,7 +155,7 @@ internal sealed partial class UpdateForm : CustomForm long bytesRead = 0; int newBytes; while (cancellation is not null && !Program.Canceled - && (newBytes = await download.ReadAsync(buffer.AsMemory(0, buffer.Length), cancellation.Token)) != 0) + && (newBytes = await download.ReadAsync(buffer.AsMemory(0, buffer.Length), cancellation.Token)) != 0) { if (cancellation is null || Program.Canceled) throw new TaskCanceledException(); diff --git a/CreamInstaller/Platforms/Paradox/ParadoxLauncher.cs b/CreamInstaller/Platforms/Paradox/ParadoxLauncher.cs index f06af41..51dee5d 100644 --- a/CreamInstaller/Platforms/Paradox/ParadoxLauncher.cs +++ b/CreamInstaller/Platforms/Paradox/ParadoxLauncher.cs @@ -12,11 +12,7 @@ namespace CreamInstaller.Platforms.Paradox; internal static class ParadoxLauncher { - public enum RepairResult - { - ProgramRunning = -2, Failure, Unnecessary = 0, - Success - } + public enum RepairResult { Failure = -1, Unnecessary = 0, Success } private static string installPath; @@ -61,26 +57,23 @@ internal static class ParadoxLauncher internal static async Task Repair(Form form, Selection selection) { InstallForm installForm = form as InstallForm; - if (!Program.AreDllsLockedDialog(form, selection)) - return form is InstallForm - ? throw new CustomMessageException("Repair failed! One or more DLLs crucial to unlocker installation are locked!") - : RepairResult.ProgramRunning; + using DialogForm dialogForm = new(form); bool smokeInstalled = false; byte[] steamOriginalSdk32 = null; byte[] steamOriginalSdk64 = null; bool screamInstalled = false; byte[] epicOriginalSdk32 = null; byte[] epicOriginalSdk64 = null; - foreach (string directory in selection.DllDirectories) + foreach (string directory in selection.DllDirectories.TakeWhile(_ => !Program.Canceled)) { bool koaloaderInstalled = Koaloader.AutoLoadDLLs.Select(pair => (pair.unlocker, path: directory + @"\" + pair.dll)) .Any(pair => pair.path.FileExists() && pair.path.IsResourceFile()); directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string old_config, out string config, out _, out _, out _); smokeInstalled = smokeInstalled || api32_o.FileExists() || api64_o.FileExists() - || (old_config.FileExists() || config.FileExists()) && !koaloaderInstalled - || api32.FileExists() && api32.IsResourceFile(ResourceIdentifier.Steamworks32) - || api64.FileExists() && api64.IsResourceFile(ResourceIdentifier.Steamworks64); + || (old_config.FileExists() || config.FileExists()) && !koaloaderInstalled + || api32.FileExists() && api32.IsResourceFile(ResourceIdentifier.Steamworks32) + || api64.FileExists() && api64.IsResourceFile(ResourceIdentifier.Steamworks64); await SmokeAPI.Uninstall(directory, deleteOthers: false); if (steamOriginalSdk32 is null && api32.FileExists() && !api32.IsResourceFile(ResourceIdentifier.Steamworks32)) steamOriginalSdk32 = api32.ReadFileBytes(true); @@ -88,20 +81,19 @@ internal static class ParadoxLauncher steamOriginalSdk64 = api64.ReadFileBytes(true); directory.GetScreamApiComponents(out api32, out api32_o, out api64, out api64_o, out config, out string log); screamInstalled = screamInstalled || api32_o.FileExists() || api64_o.FileExists() - || (config.FileExists() || log.FileExists()) && !koaloaderInstalled - || api32.FileExists() && api32.IsResourceFile(ResourceIdentifier.EpicOnlineServices32) - || api64.FileExists() && api64.IsResourceFile(ResourceIdentifier.EpicOnlineServices64); + || (config.FileExists() || log.FileExists()) && !koaloaderInstalled + || api32.FileExists() && api32.IsResourceFile(ResourceIdentifier.EpicOnlineServices32) + || api64.FileExists() && api64.IsResourceFile(ResourceIdentifier.EpicOnlineServices64); await ScreamAPI.Uninstall(directory, deleteOthers: false); if (epicOriginalSdk32 is null && api32.FileExists() && !api32.IsResourceFile(ResourceIdentifier.EpicOnlineServices32)) epicOriginalSdk32 = api32.ReadFileBytes(true); if (epicOriginalSdk64 is null && api64.FileExists() && !api64.IsResourceFile(ResourceIdentifier.EpicOnlineServices64)) epicOriginalSdk64 = api64.ReadFileBytes(true); } - using DialogForm dialogForm = new(form); if (steamOriginalSdk32 is not null || steamOriginalSdk64 is not null || epicOriginalSdk32 is not null || epicOriginalSdk64 is not null) { bool neededRepair = false; - foreach (string directory in selection.DllDirectories) + foreach (string directory in selection.DllDirectories.TakeWhile(_ => !Program.Canceled)) { directory.GetSmokeApiComponents(out string api32, out _, out string api64, out _, out _, out _, out _, out _, out _); if (steamOriginalSdk32 is not null && api32.IsResourceFile(ResourceIdentifier.Steamworks32)) @@ -134,27 +126,36 @@ internal static class ParadoxLauncher if (screamInstalled) await ScreamAPI.Install(directory, selection, generateConfig: false); } - if (neededRepair) + if (!Program.Canceled) { + if (neededRepair) + { + if (installForm is not null) + installForm.UpdateUser("Paradox Launcher successfully repaired!", LogTextBox.Success); + else + _ = dialogForm.Show(form.Icon, "Paradox Launcher successfully repaired!", customFormText: "Paradox Launcher"); + return RepairResult.Success; + } if (installForm is not null) - installForm.UpdateUser("Paradox Launcher successfully repaired!", LogTextBox.Success); + installForm.UpdateUser("Paradox Launcher did not need to be repaired.", LogTextBox.Success); else - _ = dialogForm.Show(form.Icon, "Paradox Launcher successfully repaired!", customFormText: "Paradox Launcher"); - return RepairResult.Success; + _ = dialogForm.Show(SystemIcons.Information, "Paradox Launcher does not need to be repaired.", customFormText: "Paradox Launcher"); + return RepairResult.Unnecessary; } - if (installForm is not null) - installForm.UpdateUser("Paradox Launcher did not need to be repaired.", LogTextBox.Success); - else - _ = dialogForm.Show(SystemIcons.Information, "Paradox Launcher does not need to be repaired.", customFormText: "Paradox Launcher"); - return RepairResult.Unnecessary; + } + if (Program.Canceled) + { + _ = form is InstallForm + ? throw new CustomMessageException("Repair failed! The operation was canceled.") + : dialogForm.Show(SystemIcons.Error, "Paradox Launcher repair failed! The operation was canceled.", customFormText: "Paradox Launcher"); + return RepairResult.Failure; } _ = form is InstallForm ? throw new CustomMessageException("Repair failed! " + "An original Steamworks and/or Epic Online Services file could not be found. " - + "You will likely have to reinstall Paradox Launcher to fix this issue.") + + "You will likely have to reinstall Paradox Launcher to fix this issue.") : dialogForm.Show(SystemIcons.Error, "Paradox Launcher repair failed!" + "\n\nAn original Steamworks and/or Epic Online Services file could not be found." - + "\nYou will likely have to reinstall Paradox Launcher to fix this issue.", - customFormText: "Paradox Launcher"); + + "\nYou will likely have to reinstall Paradox Launcher to fix this issue.", customFormText: "Paradox Launcher"); return RepairResult.Failure; } } \ No newline at end of file diff --git a/CreamInstaller/Platforms/Steam/SteamCMD.cs b/CreamInstaller/Platforms/Steam/SteamCMD.cs index b057c41..fd89ff7 100644 --- a/CreamInstaller/Platforms/Steam/SteamCMD.cs +++ b/CreamInstaller/Platforms/Steam/SteamCMD.cs @@ -42,7 +42,7 @@ internal static class SteamCMD private static string GetArguments(string appId) => AttemptCount.TryGetValue(appId, out int attempts) ? $@"@ShutdownOnFailedCommand 0 +force_install_dir {DirectoryPath} +login anonymous +app_info_print {appId} " - + string.Concat(Enumerable.Repeat("+app_update 4 ", attempts)) + "+quit" + + string.Concat(Enumerable.Repeat("+app_update 4 ", attempts)) + "+quit" : $"+login anonymous +app_info_print {appId} +quit"; private static async Task Run(string appId) @@ -129,7 +129,7 @@ internal static class SteamCMD try { byte[] file = await httpClient.GetByteArrayAsync(new Uri("https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip")); - file.WriteResource(ArchivePath); + _ = file.WriteResource(ArchivePath); ArchivePath.ExtractZip(DirectoryPath); ArchivePath.DeleteFile(); break; diff --git a/CreamInstaller/Platforms/Steam/SteamLibrary.cs b/CreamInstaller/Platforms/Steam/SteamLibrary.cs index 4084f9c..4de8132 100644 --- a/CreamInstaller/Platforms/Steam/SteamLibrary.cs +++ b/CreamInstaller/Platforms/Steam/SteamLibrary.cs @@ -31,7 +31,7 @@ internal static class SteamLibrary if (Program.Canceled) return games; foreach ((string appId, string name, string branch, int buildId, string gameDirectory) game in (await GetGamesFromLibraryDirectory( - libraryDirectory)).Where(game => games.All(_game => _game.appId != game.appId))) + libraryDirectory)).Where(game => games.All(_game => _game.appId != game.appId))) games.Add(game); } return games; diff --git a/CreamInstaller/Platforms/Steam/SteamStore.cs b/CreamInstaller/Platforms/Steam/SteamStore.cs index deb5672..b695ba3 100644 --- a/CreamInstaller/Platforms/Steam/SteamStore.cs +++ b/CreamInstaller/Platforms/Steam/SteamStore.cs @@ -71,7 +71,7 @@ internal static class SteamStore (Exception e) { DebugForm.Current.Log("Steam store query failed on attempt #" + attempts + " for " + appId + (isDlc ? " (DLC)" : "") - + ": Unsuccessful serialization (" + e.Message + ")"); + + ": Unsuccessful serialization (" + e.Message + ")"); } #else { @@ -82,13 +82,13 @@ internal static class SteamStore } #if DEBUG DebugForm.Current.Log("Steam store query failed on attempt #" + attempts + " for " + appId + (isDlc ? " (DLC)" : "") - + ": Response data null (" + app.Value.ToString(Formatting.None) + ")"); + + ": Response data null (" + app.Value.ToString(Formatting.None) + ")"); #endif } #if DEBUG else DebugForm.Current.Log("Steam store query failed on attempt #" + attempts + " for " + appId + (isDlc ? " (DLC)" : "") - + ": Response details null (" + app.Value.ToString(Formatting.None) + ")"); + + ": Response details null (" + app.Value.ToString(Formatting.None) + ")"); #endif } catch @@ -96,7 +96,7 @@ internal static class SteamStore (Exception e) { DebugForm.Current.Log("Steam store query failed on attempt #" + attempts + " for " + appId + (isDlc ? " (DLC)" : "") - + ": Unsuccessful deserialization (" + e.Message + ")"); + + ": Unsuccessful deserialization (" + e.Message + ")"); } #else { @@ -106,7 +106,7 @@ internal static class SteamStore #if DEBUG else DebugForm.Current.Log("Steam store query failed on attempt #" + attempts + " for " + appId + (isDlc ? " (DLC)" : "") - + ": Response deserialization null"); + + ": Response deserialization null"); #endif } else diff --git a/CreamInstaller/Program.cs b/CreamInstaller/Program.cs index d98100e..79849c5 100644 --- a/CreamInstaller/Program.cs +++ b/CreamInstaller/Program.cs @@ -1,6 +1,5 @@ using System; using System.Diagnostics; -using System.Drawing; using System.Linq; using System.Threading; using System.Windows.Forms; @@ -39,27 +38,7 @@ internal static class Program internal static bool IsGameBlocked(string name, string directory = null) => BlockProtectedGames && (ProtectedGames.Contains(name) || directory is not null && !ProtectedGameDirectoryExceptions.Contains(name) - && ProtectedGameDirectories.Any(path - => (directory + path).DirectoryExists())); - - internal static bool AreDllsLockedDialog(Form form, Selection selection) - { - while (true) - { - if (selection.AreDllsLocked) - { - using DialogForm dialogForm = new(form); - if (dialogForm.Show(SystemIcons.Error, - $"ERROR: One or more DLLs crucial to unlocker installation are locked for {selection.Name}!" - + "\n\nPlease close the program/game or resolve your anti-virus and press retry to continue . . . ", "Retry", "Cancel") - == DialogResult.OK) - continue; - } - else - return true; - return false; - } - } + && ProtectedGameDirectories.Any(path => (directory + path).DirectoryExists())); [STAThread] private static void Main() diff --git a/CreamInstaller/Resources/Koaloader.cs b/CreamInstaller/Resources/Koaloader.cs index fa7f5f3..09867d7 100644 --- a/CreamInstaller/Resources/Koaloader.cs +++ b/CreamInstaller/Resources/Koaloader.cs @@ -24,11 +24,11 @@ internal static class Koaloader internal static IEnumerable GetKoaloaderProxies(this string directory) => from resource in EmbeddedResources - select resource[(resource.IndexOf('.') + 1)..] - into resource - select resource[(resource.IndexOf('.') + 1)..] - into resource - select directory + @"\" + resource; + select resource[(resource.IndexOf('.') + 1)..] + into resource + select resource[(resource.IndexOf('.') + 1)..] + into resource + select directory + @"\" + resource; internal static void GetKoaloaderComponents(this string directory, out string old_config, out string config) { @@ -137,15 +137,15 @@ internal static class Koaloader { directory.GetKoaloaderComponents(out string old_config, out string config); foreach (string proxyPath in directory.GetKoaloaderProxies().Where(proxyPath - => proxyPath.FileExists() && proxyPath.IsResourceFile(ResourceIdentifier.Koaloader))) + => proxyPath.FileExists() && proxyPath.IsResourceFile(ResourceIdentifier.Koaloader))) { - proxyPath.DeleteFile(); + proxyPath.DeleteFile(true); installForm?.UpdateUser($"Deleted Koaloader: {Path.GetFileName(proxyPath)}", LogTextBox.Action, false); } foreach ((string unlocker, string path) in AutoLoadDLLs.Select(pair => (pair.unlocker, path: directory + @"\" + pair.dll)) - .Where(pair => pair.path.FileExists() && pair.path.IsResourceFile())) + .Where(pair => pair.path.FileExists() && pair.path.IsResourceFile())) { - path.DeleteFile(); + path.DeleteFile(true); installForm?.UpdateUser($"Deleted {unlocker}: {Path.GetFileName(path)}", LogTextBox.Action, false); } if (deleteConfig && old_config.FileExists()) @@ -174,7 +174,7 @@ internal static class Koaloader string path = directory + @"\" + proxy + ".dll"; foreach (string _path in directory.GetKoaloaderProxies().Where(p => p != path && p.FileExists() && p.IsResourceFile(ResourceIdentifier.Koaloader))) { - _path.DeleteFile(); + _path.DeleteFile(true); installForm?.UpdateUser($"Deleted Koaloader: {Path.GetFileName(_path)}", LogTextBox.Action, false); } if (path.FileExists() && !path.IsResourceFile(ResourceIdentifier.Koaloader)) diff --git a/CreamInstaller/Resources/Resources.cs b/CreamInstaller/Resources/Resources.cs index e756f8a..f61e872 100644 --- a/CreamInstaller/Resources/Resources.cs +++ b/CreamInstaller/Resources/Resources.cs @@ -464,20 +464,21 @@ internal static class Resources } } - internal static void WriteResource(this byte[] resource, string filePath) + internal static bool WriteResource(this byte[] resource, string filePath) { while (!Program.Canceled) try { using FileStream fileStream = new(filePath, FileMode.Create, FileAccess.Write); fileStream.Write(resource); - break; + return true; } catch (Exception e) { if (filePath.IOWarn("Failed to write a crucial resource", e) is not DialogResult.OK) break; } + return false; } internal static bool TryGetFileBinaryType(this string path, out BinaryType binaryType) => NativeImports.GetBinaryType(path, out binaryType); @@ -504,8 +505,7 @@ internal static class Resources if (Program.Canceled) return null; if (executables.All(e => e.path != path) && (!filterCommon || !rootDirectory.IsCommonIncorrectExecutable(path)) - && (validFunc is null || validFunc(path)) && path.TryGetFileBinaryType(out BinaryType binaryType) - && binaryType is BinaryType.BIT64) + && (validFunc is null || validFunc(path)) && path.TryGetFileBinaryType(out BinaryType binaryType) && binaryType is BinaryType.BIT64) executables.Add((path, binaryType)); } foreach (string path in rootDirectory.EnumerateDirectory("*.exe", true)) @@ -513,8 +513,7 @@ internal static class Resources if (Program.Canceled) return null; if (executables.All(e => e.path != path) && (!filterCommon || !rootDirectory.IsCommonIncorrectExecutable(path)) - && (validFunc is null || validFunc(path)) && path.TryGetFileBinaryType(out BinaryType binaryType) - && binaryType is BinaryType.BIT32) + && (validFunc is null || validFunc(path)) && path.TryGetFileBinaryType(out BinaryType binaryType) && binaryType is BinaryType.BIT32) executables.Add((path, binaryType)); } return executables.Count > 0 ? executables : null; @@ -524,10 +523,9 @@ internal static class Resources { string subPath = path[rootDirectory.Length..].ToUpperInvariant(); return subPath.Contains("SETUP") || subPath.Contains("REDIST") || subPath.Contains("SUPPORT") - || subPath.Contains("CRASH") && (subPath.Contains("PAD") || subPath.Contains("REPORT")) || subPath.Contains("HELPER") - || subPath.Contains("CEFPROCESS") || subPath.Contains("ZFGAMEBROWSER") || subPath.Contains("MONO") || subPath.Contains("PLUGINS") - || subPath.Contains("MODDING") || subPath.Contains("MOD") && subPath.Contains("MANAGER") || subPath.Contains("BATTLEYE") - || subPath.Contains("ANTICHEAT"); + || subPath.Contains("CRASH") && (subPath.Contains("PAD") || subPath.Contains("REPORT")) || subPath.Contains("HELPER") || subPath.Contains("CEFPROCESS") + || subPath.Contains("ZFGAMEBROWSER") || subPath.Contains("MONO") || subPath.Contains("PLUGINS") || subPath.Contains("MODDING") + || subPath.Contains("MOD") && subPath.Contains("MANAGER") || subPath.Contains("BATTLEYE") || subPath.Contains("ANTICHEAT"); } internal static async Task> GetDllDirectoriesFromGameDirectory(this string gameDirectory, Platform platform) diff --git a/CreamInstaller/Resources/ScreamAPI.cs b/CreamInstaller/Resources/ScreamAPI.cs index 74f50d5..be54244 100644 --- a/CreamInstaller/Resources/ScreamAPI.cs +++ b/CreamInstaller/Resources/ScreamAPI.cs @@ -108,7 +108,7 @@ internal static class ScreamAPI { if (api32.FileExists()) { - api32.DeleteFile(); + api32.DeleteFile(true); installForm?.UpdateUser($"Deleted ScreamAPI: {Path.GetFileName(api32)}", LogTextBox.Action, false); } api32_o.MoveFile(api32!); @@ -118,7 +118,7 @@ internal static class ScreamAPI { if (api64.FileExists()) { - api64.DeleteFile(); + api64.DeleteFile(true); installForm?.UpdateUser($"Deleted ScreamAPI: {Path.GetFileName(api64)}", LogTextBox.Action, false); } api64_o.MoveFile(api64!); @@ -144,7 +144,7 @@ internal static class ScreamAPI directory.GetScreamApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out _, out _); if (api32.FileExists() && !api32_o.FileExists()) { - api32.MoveFile(api32_o!); + api32.MoveFile(api32_o!, true); installForm?.UpdateUser($"Renamed EOS: {Path.GetFileName(api32)} -> {Path.GetFileName(api32_o)}", LogTextBox.Action, false); } if (api32_o.FileExists()) @@ -154,7 +154,7 @@ internal static class ScreamAPI } if (api64.FileExists() && !api64_o.FileExists()) { - api64.MoveFile(api64_o!); + api64.MoveFile(api64_o!, true); installForm?.UpdateUser($"Renamed EOS: {Path.GetFileName(api64)} -> {Path.GetFileName(api64_o)}", LogTextBox.Action, false); } if (api64_o.FileExists()) diff --git a/CreamInstaller/Resources/SmokeAPI.cs b/CreamInstaller/Resources/SmokeAPI.cs index 07fd865..14c3adc 100644 --- a/CreamInstaller/Resources/SmokeAPI.cs +++ b/CreamInstaller/Resources/SmokeAPI.cs @@ -157,7 +157,7 @@ internal static class SmokeAPI { if (api32.FileExists()) { - api32.DeleteFile(); + api32.DeleteFile(true); installForm?.UpdateUser($"Deleted SmokeAPI: {Path.GetFileName(api32)}", LogTextBox.Action, false); } api32_o.MoveFile(api32!); @@ -167,7 +167,7 @@ internal static class SmokeAPI { if (api64.FileExists()) { - api64.DeleteFile(); + api64.DeleteFile(true); installForm?.UpdateUser($"Deleted SmokeAPI: {Path.GetFileName(api64)}", LogTextBox.Action, false); } api64_o.MoveFile(api64!); @@ -214,7 +214,7 @@ internal static class SmokeAPI directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out _, out _, out _, out _, out _); if (api32.FileExists() && !api32_o.FileExists()) { - api32.MoveFile(api32_o!); + api32.MoveFile(api32_o!, true); installForm?.UpdateUser($"Renamed Steamworks: {Path.GetFileName(api32)} -> {Path.GetFileName(api32_o)}", LogTextBox.Action, false); } if (api32_o.FileExists()) @@ -224,7 +224,7 @@ internal static class SmokeAPI } if (api64.FileExists() && !api64_o.FileExists()) { - api64.MoveFile(api64_o!); + api64.MoveFile(api64_o!, true); installForm?.UpdateUser($"Renamed Steamworks: {Path.GetFileName(api64)} -> {Path.GetFileName(api64_o)}", LogTextBox.Action, false); } if (api64_o.FileExists()) diff --git a/CreamInstaller/Resources/UplayR1.cs b/CreamInstaller/Resources/UplayR1.cs index eed5ab6..6ba5b93 100644 --- a/CreamInstaller/Resources/UplayR1.cs +++ b/CreamInstaller/Resources/UplayR1.cs @@ -77,7 +77,7 @@ internal static class UplayR1 { if (api32.FileExists()) { - api32.DeleteFile(); + api32.DeleteFile(true); installForm?.UpdateUser($"Deleted Uplay R1 Unlocker: {Path.GetFileName(api32)}", LogTextBox.Action, false); } api32_o.MoveFile(api32!); @@ -87,7 +87,7 @@ internal static class UplayR1 { if (api64.FileExists()) { - api64.DeleteFile(); + api64.DeleteFile(true); installForm?.UpdateUser($"Deleted Uplay R1 Unlocker: {Path.GetFileName(api64)}", LogTextBox.Action, false); } api64_o.MoveFile(api64!); @@ -112,7 +112,7 @@ internal static class UplayR1 directory.GetUplayR1Components(out string api32, out string api32_o, out string api64, out string api64_o, out _, out _); if (api32.FileExists() && !api32_o.FileExists()) { - api32.MoveFile(api32_o!); + api32.MoveFile(api32_o!, true); installForm?.UpdateUser($"Renamed Uplay R1: {Path.GetFileName(api32)} -> {Path.GetFileName(api32_o)}", LogTextBox.Action, false); } if (api32_o.FileExists()) @@ -122,7 +122,7 @@ internal static class UplayR1 } if (api64.FileExists() && !api64_o.FileExists()) { - api64.MoveFile(api64_o!); + api64.MoveFile(api64_o!, true); installForm?.UpdateUser($"Renamed Uplay R1: {Path.GetFileName(api64)} -> {Path.GetFileName(api64_o)}", LogTextBox.Action, false); } if (api64_o.FileExists()) diff --git a/CreamInstaller/Resources/UplayR2.cs b/CreamInstaller/Resources/UplayR2.cs index 09d49d2..bcfbc64 100644 --- a/CreamInstaller/Resources/UplayR2.cs +++ b/CreamInstaller/Resources/UplayR2.cs @@ -83,7 +83,7 @@ internal static class UplayR2 string api = old_api32.FileExists() ? old_api32 : api32; if (api.FileExists()) { - api.DeleteFile(); + api.DeleteFile(true); installForm?.UpdateUser($"Deleted Uplay R2 Unlocker: {Path.GetFileName(api)}", LogTextBox.Action, false); } api32_o.MoveFile(api!); @@ -94,7 +94,7 @@ internal static class UplayR2 string api = old_api64.FileExists() ? old_api64 : api64; if (api.FileExists()) { - api.DeleteFile(); + api.DeleteFile(true); installForm?.UpdateUser($"Deleted Uplay R2 Unlocker: {Path.GetFileName(api)}", LogTextBox.Action, false); } api64_o.MoveFile(api!); @@ -121,7 +121,7 @@ internal static class UplayR2 string api = old_api32.FileExists() ? old_api32 : api32; if (api.FileExists() && !api32_o.FileExists()) { - api.MoveFile(api32_o!); + api.MoveFile(api32_o!, true); installForm?.UpdateUser($"Renamed Uplay R2: {Path.GetFileName(api)} -> {Path.GetFileName(api32_o)}", LogTextBox.Action, false); } if (api32_o.FileExists()) @@ -132,7 +132,7 @@ internal static class UplayR2 api = old_api64.FileExists() ? old_api64 : api64; if (api.FileExists() && !api64_o.FileExists()) { - api.MoveFile(api64_o!); + api.MoveFile(api64_o!, true); installForm?.UpdateUser($"Renamed Uplay R2: {Path.GetFileName(api)} -> {Path.GetFileName(api64_o)}", LogTextBox.Action, false); } if (api64_o.FileExists()) diff --git a/CreamInstaller/Selection.cs b/CreamInstaller/Selection.cs index c921db0..4c27805 100644 --- a/CreamInstaller/Selection.cs +++ b/CreamInstaller/Selection.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Linq; using System.Windows.Forms; using CreamInstaller.Forms; -using CreamInstaller.Resources; using CreamInstaller.Utility; using static CreamInstaller.Resources.Resources; @@ -62,47 +61,6 @@ internal sealed class Selection : IEquatable internal IEnumerable DLC => SelectionDLC.All.Keys.Where(dlc => dlc.Selection.Equals(this)); - internal bool AreDllsLocked - { - get - { - foreach (string directory in DllDirectories) - { - if (Platform is Platform.Steam or Platform.Paradox) - { - directory.GetCreamApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config); - if (api32.FileLocked() || api32_o.FileLocked() || api64.FileLocked() || api64_o.FileLocked() || config.FileLocked()) - return true; - directory.GetSmokeApiComponents(out api32, out api32_o, out api64, out api64_o, out string old_config, out config, out string old_log, - out string log, out string cache); - if (api32.FileLocked() || api32_o.FileLocked() || api64.FileLocked() || api64_o.FileLocked() || old_config.FileLocked() - || config.FileLocked() || old_log.FileLocked() || log.FileLocked() || cache.FileLocked()) - return true; - } - if (Platform is Platform.Epic or Platform.Paradox) - { - directory.GetScreamApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config, - out string log); - if (api32.FileLocked() || api32_o.FileLocked() || api64.FileLocked() || api64_o.FileLocked() || config.FileLocked() || log.FileLocked()) - return true; - } - if (Platform is Platform.Ubisoft) - { - directory.GetUplayR1Components(out string api32, out string api32_o, out string api64, out string api64_o, out string config, - out string log); - if (api32.FileLocked() || api32_o.FileLocked() || api64.FileLocked() || api64_o.FileLocked() || config.FileLocked() || log.FileLocked()) - return true; - directory.GetUplayR2Components(out string old_api32, out string old_api64, out api32, out api32_o, out api64, out api64_o, out config, - out log); - if (old_api32.FileLocked() || old_api64.FileLocked() || api32.FileLocked() || api32_o.FileLocked() || api64.FileLocked() - || api64_o.FileLocked() || config.FileLocked() || log.FileLocked()) - return true; - } - } - return false; - } - } - public bool Equals(Selection other) => other is not null && (ReferenceEquals(this, other) || Id == other.Id && Platform == other.Platform); internal static Selection GetOrCreate(Platform platform, string id, string name, string rootDirectory, HashSet dllDirectories, diff --git a/CreamInstaller/Utility/ExceptionHandler.cs b/CreamInstaller/Utility/ExceptionHandler.cs index 307c1e4..4fd1128 100644 --- a/CreamInstaller/Utility/ExceptionHandler.cs +++ b/CreamInstaller/Utility/ExceptionHandler.cs @@ -21,7 +21,7 @@ internal static class ExceptionHandler string[] stackTrace = e.StackTrace?.Split('\n'); if (stackTrace is not null && stackTrace.Length > 0) { - _ = output.Append(e.GetType() + (": " + e.Message)); + _ = output.Append($"[{e.HResult & 0x0000FFFF}] {e.GetType()}: {e.Message}"); foreach (string line in stackTrace) { int atNum = line.IndexOf("at ", StringComparison.Ordinal); diff --git a/CreamInstaller/Utility/SafeIO.cs b/CreamInstaller/Utility/SafeIO.cs index e60e59a..37ee4b2 100644 --- a/CreamInstaller/Utility/SafeIO.cs +++ b/CreamInstaller/Utility/SafeIO.cs @@ -12,21 +12,6 @@ namespace CreamInstaller.Utility; internal static class SafeIO { - internal static bool FileLocked(this string filePath) - { - if (!FileExists(filePath)) - return false; - try - { - File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None).Close(); - } - catch - { - return true; - } - return false; - } - internal static bool DirectoryExists(this string directoryPath) => Directory.Exists(directoryPath); internal static void CreateDirectory(this string directoryPath, bool crucial = false, Form form = null) @@ -37,7 +22,7 @@ internal static class SafeIO try { _ = Directory.CreateDirectory(directoryPath); - break; + return; } catch (Exception e) { @@ -54,7 +39,7 @@ internal static class SafeIO try { Directory.Move(directoryPath, newDirectoryPath); - break; + return; } catch (Exception e) { @@ -71,12 +56,12 @@ internal static class SafeIO try { Directory.Delete(directoryPath, true); - break; + return; } catch (Exception e) { if (!crucial || !directoryPath.DirectoryExists() - || directoryPath.IOWarn("Failed to delete a crucial directory", e, form) is not DialogResult.OK) + || directoryPath.IOWarn("Failed to delete a crucial directory", e, form) is not DialogResult.OK) break; } } @@ -96,7 +81,7 @@ internal static class SafeIO catch (Exception e) { if (!crucial || !directoryPath.DirectoryExists() - || directoryPath.IOWarn("Failed to enumerate a crucial directory's files", e, form) is not DialogResult.OK) + || directoryPath.IOWarn("Failed to enumerate a crucial directory's files", e, form) is not DialogResult.OK) break; } return Enumerable.Empty(); @@ -117,7 +102,7 @@ internal static class SafeIO catch (Exception e) { if (!crucial || !directoryPath.DirectoryExists() - || directoryPath.IOWarn("Failed to enumerate a crucial directory's subdirectories", e, form) is not DialogResult.OK) + || directoryPath.IOWarn("Failed to enumerate a crucial directory's subdirectories", e, form) is not DialogResult.OK) break; } return Enumerable.Empty(); @@ -148,7 +133,7 @@ internal static class SafeIO try { File.Move(filePath, newFilePath); - break; + return; } catch (Exception e) { @@ -165,7 +150,7 @@ internal static class SafeIO try { File.Delete(filePath); - break; + return; } catch (Exception e) { @@ -214,7 +199,7 @@ internal static class SafeIO try { File.WriteAllText(filePath, text, Encoding.UTF8); - break; + return; } catch (Exception e) { @@ -231,7 +216,7 @@ internal static class SafeIO try { ZipFile.ExtractToDirectory(archivePath, destinationPath); - break; + return; } catch (Exception e) { @@ -251,11 +236,26 @@ internal static class SafeIO private static DialogResult IOWarnInternal(this string filePath, string message, Exception e, Form form = null) { using DialogForm dialogForm = new(form); - string description = message + ": " + filePath.ResolvePath() + "\n\n"; - if (e is IOException && (e.HResult & 0x0000FFFF) == 225) // virus or potentially unwanted software - description += "Please resolve your anti-virus and press retry to continue . . . "; - else - description += e.FormatException(); - return dialogForm.Show(SystemIcons.Warning, description, "Retry", "OK"); + string description = message + ": " + (filePath.ResolvePath() ?? filePath) + "\n\n"; + int code = e.HResult & 0x0000FFFF; + switch (code) // https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes#system-error-codes + { + case 5: // ERROR_ACCESS_DENIED + case 32: // ERROR_SHARING_VIOLATION + case 33: // ERROR_LOCK_VIOLATION + description += "Please close the program/game and press retry to continue . . . "; + break; + case 225: // ERROR_VIRUS_INFECTED + case 226: // ERROR_VIRUS_DELETED + description += "Please resolve your anti-virus and press retry to continue . . . "; + break; + default: + description += e.FormatException(); + break; + } + DialogResult result = dialogForm.Show(SystemIcons.Warning, description, "Retry", "Cancel"); + if (result is not DialogResult.OK) + Program.Canceled = true; + return result; } } \ No newline at end of file