diff --git a/CreamInstaller/Components/ContextMenuItem.cs b/CreamInstaller/Components/ContextMenuItem.cs index d063613..2e5398c 100644 --- a/CreamInstaller/Components/ContextMenuItem.cs +++ b/CreamInstaller/Components/ContextMenuItem.cs @@ -23,17 +23,20 @@ internal sealed class ContextMenuItem : ToolStripMenuItem internal ContextMenuItem(string text, string imageIdentifier, EventHandler onClick = null) : this(text, onClick) => _ = TryImageIdentifier(this, imageIdentifier); - internal ContextMenuItem(string text, (string id, string iconUrl) imageIdentifierInfo, EventHandler onClick = null) : this(text, onClick) + internal ContextMenuItem(string text, (string id, string iconUrl) imageIdentifierInfo, EventHandler onClick = null) + : this(text, onClick) => _ = TryImageIdentifierInfo(this, imageIdentifierInfo); - internal ContextMenuItem(string text, (string id, string iconUrl) imageIdentifierInfo, string imageIdentifierFallback, EventHandler onClick = null) : + internal ContextMenuItem(string text, (string id, string iconUrl) imageIdentifierInfo, + string imageIdentifierFallback, EventHandler onClick = null) : this(text, onClick) { async void OnFail() => await TryImageIdentifier(this, imageIdentifierFallback); _ = TryImageIdentifierInfo(this, imageIdentifierInfo, OnFail); } - internal ContextMenuItem(string text, (string id, string iconUrl) imageIdentifierInfo, (string id, string iconUrl) imageIdentifierInfoFallback, + internal ContextMenuItem(string text, (string id, string iconUrl) imageIdentifierInfo, + (string id, string iconUrl) imageIdentifierInfoFallback, EventHandler onClick = null) : this(text, onClick) { async void OnFail() => await TryImageIdentifierInfo(this, imageIdentifierInfoFallback); @@ -56,6 +59,7 @@ internal sealed class ContextMenuItem : ToolStripMenuItem image = file.GetFileIconImage(); break; } + break; case "Notepad": image = IconGrabber.GetNotepadImage(); @@ -67,26 +71,33 @@ internal sealed class ContextMenuItem : ToolStripMenuItem image = IconGrabber.GetFileExplorerImage(); break; case "SteamDB": - image = await HttpClientManager.GetImageFromUrl(IconGrabber.GetDomainFaviconUrl("steamdb.info")); + image = await HttpClientManager.GetImageFromUrl( + IconGrabber.GetDomainFaviconUrl("steamdb.info")); break; case "Steam Store": - image = await HttpClientManager.GetImageFromUrl(IconGrabber.GetDomainFaviconUrl("store.steampowered.com")); + image = await HttpClientManager.GetImageFromUrl( + IconGrabber.GetDomainFaviconUrl("store.steampowered.com")); break; case "Steam Community": - image = await HttpClientManager.GetImageFromUrl(IconGrabber.GetDomainFaviconUrl("steamcommunity.com")); + image = await HttpClientManager.GetImageFromUrl( + IconGrabber.GetDomainFaviconUrl("steamcommunity.com")); break; case "ScreamDB": - image = await HttpClientManager.GetImageFromUrl(IconGrabber.GetDomainFaviconUrl("scream-db.web.app")); + image = await HttpClientManager.GetImageFromUrl( + IconGrabber.GetDomainFaviconUrl("scream-db.web.app")); break; case "Epic Games": - image = await HttpClientManager.GetImageFromUrl(IconGrabber.GetDomainFaviconUrl("epicgames.com")); + image = await HttpClientManager.GetImageFromUrl( + IconGrabber.GetDomainFaviconUrl("epicgames.com")); break; case "Ubisoft Store": - image = await HttpClientManager.GetImageFromUrl(IconGrabber.GetDomainFaviconUrl("store.ubi.com")); + image = await HttpClientManager.GetImageFromUrl( + IconGrabber.GetDomainFaviconUrl("store.ubi.com")); break; default: return; } + if (image is not null) { Images[imageIdentifier] = image; @@ -95,7 +106,8 @@ internal sealed class ContextMenuItem : ToolStripMenuItem } }); - private static async Task TryImageIdentifierInfo(ContextMenuItem item, (string id, string iconUrl) imageIdentifierInfo, Action onFail = null) + private static async Task TryImageIdentifierInfo(ContextMenuItem item, + (string id, string iconUrl) imageIdentifierInfo, Action onFail = null) => await Task.Run(async () => { try diff --git a/CreamInstaller/Components/CustomForm.cs b/CreamInstaller/Components/CustomForm.cs index f1572b2..9755651 100644 --- a/CreamInstaller/Components/CustomForm.cs +++ b/CreamInstaller/Components/CustomForm.cs @@ -50,33 +50,34 @@ internal class CustomForm : Form string repository = $"https://github.com/{Program.RepositoryOwner}/{Program.RepositoryName}"; _ = helpDialog.Show(SystemIcons.Information, "Automatically finds all installed Steam, Epic and Ubisoft games with their respective DLC-related DLL locations on the user's computer,\n" - + "parses SteamCMD, Steam Store and Epic Games Store for user-selected games' DLCs, then provides a very simple graphical interface\n" - + "utilizing the gathered information for the maintenance of DLC unlockers.\n\n" - + $"The program utilizes the latest versions of [Koaloader]({acidicoala}/Koaloader), [SmokeAPI]({acidicoala}/SmokeAPI), [ScreamAPI]({acidicoala}/ScreamAPI), [Uplay R1 Unlocker]({acidicoala}/UplayR1Unlocker) and [Uplay R2 Unlocker]({acidicoala}/UplayR2Unlocker), all by\n" - + $"the wonderful [acidicoala]({acidicoala}), and all downloaded and embedded into the program itself; no further downloads necessary on your part!\n\n" - + "USAGE:\n" + " 1. Choose which programs and/or games the program should scan for DLC.\n" - + " The program automatically gathers all installed games from Steam, Epic and Ubisoft directories.\n" - + " 2. Wait for the program to download and install SteamCMD (if you chose a Steam game).\n" - + " 3. Wait for the program to gather and cache the chosen games' information && DLCs.\n" - + " May take some time on the first run; depends on how many DLCs the games you chose have.\n" - + " 4. CAREFULLY select which games' DLCs you wish to unlock.\n" - + " Obviously none of the DLC unlockers are tested for every single game!\n" - + " 5. Choose whether or not to install with Koaloader, and if so then also pick the proxy DLL to use.\n" - + " If the default \'version.dll\' doesn't work, then see [here](https://cs.rin.ru/forum/viewtopic.php?p=2552172#p2552172) to find one that does.\n" - + " 6. Click the \"Generate and Install\" button.\n" + " 7. Click the \"OK\" button to close the program.\n" - + " 8. If any of the DLC unlockers cause problems with any of the games you installed them on, simply go back\n" - + " to step 5 and select what games you wish you revert changes to, and instead click the \"Uninstall\" button this time.\n\n" - + "NOTE: This program does not automatically download nor install actual DLC files for you; as the title of the program states, this program\n" - + "is only a DLC Unlocker installer. Should the game you wish to unlock DLC for not already come with the DLCs installed, as is the case with\n" - + "a good majority of games, you must find, download and install those to the game yourself. This process includes manually installing new\n" - + "DLCs and manually updating the previously manually installed DLCs after game updates.\n\n" - + $"For reliable and quick assistance, all bugs, crashes and other issues should be referred to the [GitHub Issues]({repository}/issues) page!\n\n" - + $"HOWEVER: Please read the [FAQ entry]({repository}#faq--common-issues) and/or [template issue]({repository}/issues/new/choose) corresponding to your problem should one exist! Also, note that the [GitHub Issues]({repository}/issues)\n" - + "page is not your personal assistance hotline, rather it is for genuine bugs/crashes/issues with the program itself. If you post an issue which\n" - + "has already been explained within the FAQ, template issues, and/or within this text in general, I will just close it and you will be ignored.\n\n" - + "SteamCMD installation and appinfo cache can be found at [C:\\ProgramData\\CreamInstaller]().\n" - + $"The program automatically and very quickly updates from [GitHub]({repository}) by choice of the user through a dialog on startup.\n" - + $"The program source and other information can be found on [GitHub]({repository})."); + + "parses SteamCMD, Steam Store and Epic Games Store for user-selected games' DLCs, then provides a very simple graphical interface\n" + + "utilizing the gathered information for the maintenance of DLC unlockers.\n\n" + + $"The program utilizes the latest versions of [Koaloader]({acidicoala}/Koaloader), [SmokeAPI]({acidicoala}/SmokeAPI), [ScreamAPI]({acidicoala}/ScreamAPI), [Uplay R1 Unlocker]({acidicoala}/UplayR1Unlocker) and [Uplay R2 Unlocker]({acidicoala}/UplayR2Unlocker), all by\n" + + $"the wonderful [acidicoala]({acidicoala}), and all downloaded and embedded into the program itself; no further downloads necessary on your part!\n\n" + + "USAGE:\n" + " 1. Choose which programs and/or games the program should scan for DLC.\n" + + " The program automatically gathers all installed games from Steam, Epic and Ubisoft directories.\n" + + " 2. Wait for the program to download and install SteamCMD (if you chose a Steam game).\n" + + " 3. Wait for the program to gather and cache the chosen games' information && DLCs.\n" + + " May take some time on the first run; depends on how many DLCs the games you chose have.\n" + + " 4. CAREFULLY select which games' DLCs you wish to unlock.\n" + + " Obviously none of the DLC unlockers are tested for every single game!\n" + + " 5. Choose whether or not to install with Koaloader, and if so then also pick the proxy DLL to use.\n" + + " If the default \'version.dll\' doesn't work, then see [here](https://cs.rin.ru/forum/viewtopic.php?p=2552172#p2552172) to find one that does.\n" + + " 6. Click the \"Generate and Install\" button.\n" + + " 7. Click the \"OK\" button to close the program.\n" + + " 8. If any of the DLC unlockers cause problems with any of the games you installed them on, simply go back\n" + + " to step 5 and select what games you wish you revert changes to, and instead click the \"Uninstall\" button this time.\n\n" + + "NOTE: This program does not automatically download nor install actual DLC files for you; as the title of the program states, this program\n" + + "is only a DLC Unlocker installer. Should the game you wish to unlock DLC for not already come with the DLCs installed, as is the case with\n" + + "a good majority of games, you must find, download and install those to the game yourself. This process includes manually installing new\n" + + "DLCs and manually updating the previously manually installed DLCs after game updates.\n\n" + + $"For reliable and quick assistance, all bugs, crashes and other issues should be referred to the [GitHub Issues]({repository}/issues) page!\n\n" + + $"HOWEVER: Please read the [FAQ entry]({repository}#faq--common-issues) and/or [template issue]({repository}/issues/new/choose) corresponding to your problem should one exist! Also, note that the [GitHub Issues]({repository}/issues)\n" + + "page is not your personal assistance hotline, rather it is for genuine bugs/crashes/issues with the program itself. If you post an issue which\n" + + "has already been explained within the FAQ, template issues, and/or within this text in general, I will just close it and you will be ignored.\n\n" + + "SteamCMD installation and appinfo cache can be found at [C:\\ProgramData\\CreamInstaller]().\n" + + $"The program automatically and very quickly updates from [GitHub]({repository}) by choice of the user through a dialog on startup.\n" + + $"The program source and other information can be found on [GitHub]({repository})."); } private void OnActivation(object sender, EventArgs args) => Activate(); @@ -85,10 +86,12 @@ internal class CustomForm : Form { bool topMost = TopMost; NativeImports.SetWindowPos(Handle, NativeImports.HWND_TOPMOST, 0, 0, 0, 0, - NativeImports.SWP_NOACTIVATE | NativeImports.SWP_SHOWWINDOW | NativeImports.SWP_NOMOVE | NativeImports.SWP_NOSIZE); + NativeImports.SWP_NOACTIVATE | NativeImports.SWP_SHOWWINDOW | NativeImports.SWP_NOMOVE | + NativeImports.SWP_NOSIZE); if (!topMost) NativeImports.SetWindowPos(Handle, NativeImports.HWND_NOTOPMOST, 0, 0, 0, 0, - NativeImports.SWP_NOACTIVATE | NativeImports.SWP_SHOWWINDOW | NativeImports.SWP_NOMOVE | NativeImports.SWP_NOSIZE); + NativeImports.SWP_NOACTIVATE | NativeImports.SWP_SHOWWINDOW | NativeImports.SWP_NOMOVE | + NativeImports.SWP_NOSIZE); } internal void InheritLocation(Form fromForm) diff --git a/CreamInstaller/Components/CustomTreeView.cs b/CreamInstaller/Components/CustomTreeView.cs index 1adff2c..301d117 100644 --- a/CreamInstaller/Components/CustomTreeView.cs +++ b/CreamInstaller/Components/CustomTreeView.cs @@ -94,6 +94,7 @@ internal sealed class CustomTreeView : TreeView } else text = platform.ToString(); + Size size = TextRenderer.MeasureText(graphics, text, font); bounds = bounds with { X = bounds.X + bounds.Width, Width = size.Width }; selectionBounds = new(selectionBounds.Location, selectionBounds.Size + bounds.Size with { Height = 0 }); @@ -111,11 +112,13 @@ internal sealed class CustomTreeView : TreeView size = TextRenderer.MeasureText(graphics, text, font); const int left = -4; bounds = bounds with { X = bounds.X + bounds.Width + left, Width = size.Width }; - selectionBounds = new(selectionBounds.Location, selectionBounds.Size + new Size(bounds.Size.Width + left, 0)); + selectionBounds = new(selectionBounds.Location, + selectionBounds.Size + new Size(bounds.Size.Width + left, 0)); graphics.FillRectangle(brush, bounds); point = new(bounds.Location.X - 1, bounds.Location.Y + 1); TextRenderer.DrawText(graphics, text, font, point, color, TextFormatFlags.Default); } + if (form is SelectForm) { Selection selection = Selection.FromId(platform, id); @@ -127,6 +130,7 @@ internal sealed class CustomTreeView : TreeView bounds = bounds with { X = bounds.X + bounds.Width, Width = size.Width }; graphics.FillRectangle(brush, bounds); } + CheckBoxState checkBoxState = selection.Koaloader ? Enabled ? CheckBoxState.CheckedPressed : CheckBoxState.CheckedDisabled : Enabled @@ -151,21 +155,25 @@ internal sealed class CustomTreeView : TreeView this.checkBoxBounds[selection] = RectangleToClient(checkBoxBounds); if (selection.Koaloader) { - comboBoxFont ??= new(font.FontFamily, 6, font.Style, font.Unit, font.GdiCharSet, font.GdiVerticalFont); + comboBoxFont ??= new(font.FontFamily, 6, font.Style, font.Unit, font.GdiCharSet, + font.GdiVerticalFont); ComboBoxState comboBoxState = Enabled ? ComboBoxState.Normal : ComboBoxState.Disabled; text = (selection.KoaloaderProxy ?? Selection.DefaultKoaloaderProxy) + ".dll"; size = TextRenderer.MeasureText(graphics, text, comboBoxFont) + new Size(6, 0); const int padding = 2; bounds = new(bounds.X + bounds.Width, bounds.Y + padding / 2, size.Width, bounds.Height - padding); - selectionBounds = new(selectionBounds.Location, selectionBounds.Size + bounds.Size with { Height = 0 }); + selectionBounds = new(selectionBounds.Location, + selectionBounds.Size + bounds.Size with { Height = 0 }); Rectangle comboBoxBounds = bounds; graphics.FillRectangle(backBrush, bounds); ComboBoxRenderer.DrawTextBox(graphics, bounds, text, comboBoxFont, comboBoxState); size = new(14, 0); left = -1; bounds = bounds with { X = bounds.X + bounds.Width + left, Width = size.Width }; - selectionBounds = new(selectionBounds.Location, selectionBounds.Size + new Size(bounds.Size.Width + left, 0)); - comboBoxBounds = new(comboBoxBounds.Location, comboBoxBounds.Size + new Size(bounds.Size.Width + left, 0)); + selectionBounds = new(selectionBounds.Location, + selectionBounds.Size + new Size(bounds.Size.Width + left, 0)); + comboBoxBounds = new(comboBoxBounds.Location, + comboBoxBounds.Size + new Size(bounds.Size.Width + left, 0)); ComboBoxRenderer.DrawDropDownButton(graphics, bounds, comboBoxState); this.comboBoxBounds[selection] = RectangleToClient(comboBoxBounds); } @@ -173,6 +181,7 @@ internal sealed class CustomTreeView : TreeView _ = comboBoxBounds.Remove(selection); } } + this.selectionBounds[node] = RectangleToClient(selectionBounds); } @@ -192,6 +201,7 @@ internal sealed class CustomTreeView : TreeView selectForm.OnNodeRightClick(pair.Key, e.Location); break; } + if (e.Button is not MouseButtons.Left) return; if (comboBoxBounds.Count > 0 && selectForm is not null) @@ -200,11 +210,12 @@ internal sealed class CustomTreeView : TreeView _ = comboBoxBounds.Remove(pair.Key); else if (pair.Value.Contains(clickPoint)) { - HashSet proxies = EmbeddedResources.Where(r => r.StartsWith("Koaloader", StringComparison.Ordinal)).Select(p => - { - p.GetProxyInfoFromIdentifier(out string proxyName, out _); - return proxyName; - }).ToHashSet(); + HashSet proxies = EmbeddedResources + .Where(r => r.StartsWith("Koaloader", StringComparison.Ordinal)).Select(p => + { + p.GetProxyInfoFromIdentifier(out string proxyName, out _); + return proxyName; + }).ToHashSet(); comboBoxDropDown ??= new(); comboBoxDropDown.ShowItemToolTips = false; comboBoxDropDown.Items.Clear(); @@ -219,6 +230,7 @@ internal sealed class CustomTreeView : TreeView canUse = false; break; } + if (canUse) _ = comboBoxDropDown.Items.Add(new ToolStripButton(proxy + ".dll", null, (_, _) => { @@ -226,9 +238,11 @@ internal sealed class CustomTreeView : TreeView selectForm.OnKoaloaderChanged(); }) { Font = comboBoxFont }); } + comboBoxDropDown.Show(this, PointToScreen(new(pair.Value.Left, pair.Value.Bottom - 1))); break; } + foreach (KeyValuePair pair in checkBoxBounds) if (!Selection.All.ContainsKey(pair.Key)) _ = checkBoxBounds.Remove(pair.Key); diff --git a/CreamInstaller/CreamInstaller.csproj b/CreamInstaller/CreamInstaller.csproj index 0080819..9f5afdf 100644 --- a/CreamInstaller/CreamInstaller.csproj +++ b/CreamInstaller/CreamInstaller.csproj @@ -4,7 +4,7 @@ net8.0-windows10.0.22621.0 True Resources\program.ico - 4.10.1 + 4.10.2 2021, pointfeev (https://github.com/pointfeev) CreamInstaller Automatic DLC Unlocker Installer & Configuration Generator diff --git a/CreamInstaller/Forms/DebugForm.cs b/CreamInstaller/Forms/DebugForm.cs index c81c837..864c2dc 100644 --- a/CreamInstaller/Forms/DebugForm.cs +++ b/CreamInstaller/Forms/DebugForm.cs @@ -36,6 +36,7 @@ internal sealed partial class DebugForm : CustomForm if (command == 0xF010) // SC_MOVE return; } + base.WndProc(ref message); } @@ -48,6 +49,7 @@ internal sealed partial class DebugForm : CustomForm attachedForm.SizeChanged -= OnChange; attachedForm.VisibleChanged -= OnChange; } + attachedForm = form; attachedForm.Activated += OnChange; attachedForm.LocationChanged += OnChange; diff --git a/CreamInstaller/Forms/DialogForm.cs b/CreamInstaller/Forms/DialogForm.cs index 90a9fa1..f42f4b3 100644 --- a/CreamInstaller/Forms/DialogForm.cs +++ b/CreamInstaller/Forms/DialogForm.cs @@ -11,7 +11,8 @@ internal sealed partial class DialogForm : CustomForm { internal DialogForm(IWin32Window owner) : base(owner) => InitializeComponent(); - internal DialogResult Show(Icon descriptionIcon, string descriptionText, string acceptButtonText = "OK", string cancelButtonText = null, + internal DialogResult Show(Icon descriptionIcon, string descriptionText, string acceptButtonText = "OK", + string cancelButtonText = null, string customFormText = null, Icon customFormIcon = null) { descriptionIcon ??= Icon; @@ -33,6 +34,7 @@ internal sealed partial class DialogForm : CustomForm descriptionText = descriptionText.Remove(i, linkRight + 1 - i).Insert(i, text); links.Add(new(i, text.Length, link)); } + descriptionLabel.Text = descriptionText; acceptButton.Text = acceptButtonText; if (cancelButtonText is null) @@ -42,6 +44,7 @@ internal sealed partial class DialogForm : CustomForm } else cancelButton.Text = cancelButtonText; + if (customFormText is not null) Text = customFormText; else @@ -49,6 +52,7 @@ internal sealed partial class DialogForm : CustomForm OnResize(null, null); Resize += OnResize; } + if (customFormIcon is not null) Icon = customFormIcon; if (links.Count < 1) @@ -65,6 +69,8 @@ internal sealed partial class DialogForm : CustomForm private void OnResize(object s, EventArgs e) => Text = TextRenderer.MeasureText(Program.ApplicationName, Font).Width > Size.Width - 100 - ? TextRenderer.MeasureText(Program.ApplicationNameShort, Font).Width > Size.Width - 100 ? Program.Name : Program.ApplicationNameShort + ? TextRenderer.MeasureText(Program.ApplicationNameShort, Font).Width > Size.Width - 100 + ? Program.Name + : Program.ApplicationNameShort : Program.ApplicationName; } \ No newline at end of file diff --git a/CreamInstaller/Forms/InstallForm.cs b/CreamInstaller/Forms/InstallForm.cs index 8cf056a..af3ab3c 100644 --- a/CreamInstaller/Forms/InstallForm.cs +++ b/CreamInstaller/Forms/InstallForm.cs @@ -63,11 +63,14 @@ internal sealed partial class InstallForm : CustomForm UpdateUser("Repairing Paradox Launcher . . . ", LogTextBox.Operation); _ = await Repair(this, selection); } + UpdateUser( - $"{(uninstalling ? "Uninstalling" : "Installing")}" + $" {(uninstalling ? "from" : "for")} " + selection.Name - + $" with root directory \"{selection.RootDirectory}\" . . . ", LogTextBox.Operation); + $"{(uninstalling ? "Uninstalling" : "Installing")}" + $" {(uninstalling ? "from" : "for")} " + + selection.Name + + $" with root directory \"{selection.RootDirectory}\" . . . ", LogTextBox.Operation); IEnumerable invalidDirectories = (await selection.RootDirectory.GetExecutables()) - ?.Where(d => selection.ExecutableDirectories.All(s => s.directory != Path.GetDirectoryName(d.path))).Select(d => Path.GetDirectoryName(d.path)); + ?.Where(d => selection.ExecutableDirectories.All(s => s.directory != Path.GetDirectoryName(d.path))) + .Select(d => Path.GetDirectoryName(d.path)); if (selection.ExecutableDirectories.All(s => s.directory != selection.RootDirectory)) invalidDirectories = invalidDirectories?.Append(selection.RootDirectory); invalidDirectories = invalidDirectories?.Distinct(); @@ -77,27 +80,37 @@ internal sealed partial class InstallForm : CustomForm if (Program.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()) - || old_config.FileExists() || config.FileExists()) + if (directory.GetKoaloaderProxies().Any(proxy => + proxy.FileExists() && proxy.IsResourceFile(ResourceIdentifier.Koaloader)) + || directory != selection.RootDirectory && + Koaloader.AutoLoadDLLs.Any(pair => (directory + @"\" + pair.dll).FileExists()) + || old_config.FileExists() || config.FileExists()) { - UpdateUser("Uninstalling Koaloader from " + selection.Name + $" in incorrect directory \"{directory}\" . . . ", LogTextBox.Operation); + UpdateUser( + "Uninstalling Koaloader from " + selection.Name + + $" in incorrect directory \"{directory}\" . . . ", LogTextBox.Operation); await Koaloader.Uninstall(directory, selection.RootDirectory, this); } } + if (uninstalling || !selection.Koaloader) foreach ((string directory, BinaryType _) in selection.ExecutableDirectories) { if (Program.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()) + if (directory.GetKoaloaderProxies().Any(proxy => + proxy.FileExists() && proxy.IsResourceFile(ResourceIdentifier.Koaloader)) + || Koaloader.AutoLoadDLLs.Any(pair => (directory + @"\" + pair.dll).FileExists()) || + old_config.FileExists() || config.FileExists()) { - UpdateUser("Uninstalling Koaloader from " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation); + UpdateUser( + "Uninstalling Koaloader from " + selection.Name + $" in directory \"{directory}\" . . . ", + LogTextBox.Operation); await Koaloader.Uninstall(directory, selection.RootDirectory, this); } } + bool uninstallProxy = uninstalling || selection.Koaloader; int count = selection.DllDirectories.Count, cur = 0; foreach (string directory in selection.DllDirectories) @@ -106,77 +119,93 @@ internal sealed partial class InstallForm : CustomForm 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, + 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 - + $" in directory \"{directory}\" . . . ", LogTextBox.Operation); + $"{(uninstallProxy ? "Uninstalling" : "Installing")} SmokeAPI" + + $" {(uninstallProxy ? "from" : "for")} " + selection.Name + + $" in directory \"{directory}\" . . . ", LogTextBox.Operation); if (uninstallProxy) await SmokeAPI.Uninstall(directory, this); else await SmokeAPI.Install(directory, selection, this); } } + if (selection.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); + 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 - + $" in directory \"{directory}\" . . . ", LogTextBox.Operation); + $"{(uninstallProxy ? "Uninstalling" : "Installing")} ScreamAPI" + + $" {(uninstallProxy ? "from" : "for")} " + selection.Name + + $" in directory \"{directory}\" . . . ", LogTextBox.Operation); if (uninstallProxy) await ScreamAPI.Uninstall(directory, this); else await ScreamAPI.Install(directory, selection, this); } } + if (selection.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); + 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 - + $" in directory \"{directory}\" . . . ", LogTextBox.Operation); + $"{(uninstallProxy ? "Uninstalling" : "Installing")} Uplay R1 Unlocker" + + $" {(uninstallProxy ? "from" : "for")} " + selection.Name + + $" in directory \"{directory}\" . . . ", LogTextBox.Operation); if (uninstallProxy) await UplayR1.Uninstall(directory, this); else await UplayR1.Install(directory, selection, this); } - directory.GetUplayR2Components(out string old_api32, out string old_api64, out api32, out api32_o, out api64, out api64_o, out config, out log); + + 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 - + $" in directory \"{directory}\" . . . ", LogTextBox.Operation); + $"{(uninstallProxy ? "Uninstalling" : "Installing")} Uplay R2 Unlocker" + + $" {(uninstallProxy ? "from" : "for")} " + selection.Name + + $" in directory \"{directory}\" . . . ", LogTextBox.Operation); if (uninstallProxy) await UplayR2.Uninstall(directory, this); else await UplayR2.Install(directory, selection, this); } } + UpdateProgress(++cur / count * 100); } + if (selection.Koaloader && !uninstalling) foreach ((string directory, BinaryType binaryType) in selection.ExecutableDirectories) { if (Program.Canceled) return; - UpdateUser("Installing Koaloader to " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation); + UpdateUser("Installing Koaloader to " + selection.Name + $" in directory \"{directory}\" . . . ", + LogTextBox.Operation); await Koaloader.Install(directory, binaryType, selection, selection.RootDirectory, this); } + UpdateProgress(100); } @@ -200,8 +229,10 @@ internal sealed partial class InstallForm : CustomForm { UpdateUser($"Operation failed for {selection.Name}: " + exception, LogTextBox.Error); } + ++completeOperationsCount; } + Program.Cleanup(); int activeCount = activeSelections.Count; if (activeCount > 0) @@ -222,14 +253,19 @@ internal sealed partial class InstallForm : CustomForm try { await Operate(); - UpdateUser($"DLC unlocker(s) successfully {(uninstalling ? "uninstalled" : "installed and generated")} for " + selectionCount + " program(s).", + UpdateUser( + $"DLC unlocker(s) successfully {(uninstalling ? "uninstalled" : "installed and generated")} for " + + selectionCount + " program(s).", LogTextBox.Success); } catch (Exception exception) { - UpdateUser($"DLC unlocker {(uninstalling ? "uninstallation" : "installation and/or generation")} failed: " + exception, LogTextBox.Error); + UpdateUser( + $"DLC unlocker {(uninstalling ? "uninstallation" : "installation and/or generation")} failed: " + + exception, LogTextBox.Error); retryButton.Enabled = true; } + userProgressBar.Value = userProgressBar.Maximum; acceptButton.Enabled = true; cancelButton.Enabled = false; @@ -238,7 +274,7 @@ internal sealed partial class InstallForm : CustomForm private void OnLoad(object sender, EventArgs a) { - retry: + retry: try { userInfoLabel.Text = "Loading . . . "; @@ -249,6 +285,7 @@ internal sealed partial class InstallForm : CustomForm selectionCount++; _ = activeSelections.Add(selection); } + Start(); } catch (Exception e) diff --git a/CreamInstaller/Forms/SelectDialogForm.cs b/CreamInstaller/Forms/SelectDialogForm.cs index 99b7a4c..f763d89 100644 --- a/CreamInstaller/Forms/SelectDialogForm.cs +++ b/CreamInstaller/Forms/SelectDialogForm.cs @@ -12,7 +12,8 @@ internal sealed partial class SelectDialogForm : CustomForm private readonly List<(Platform platform, string id, string name)> selected = new(); internal SelectDialogForm(IWin32Window owner) : base(owner) => InitializeComponent(); - internal DialogResult QueryUser(string groupBoxText, List<(Platform platform, string id, string name, bool alreadySelected)> potentialChoices, + internal DialogResult QueryUser(string groupBoxText, + List<(Platform platform, string id, string name, bool alreadySelected)> potentialChoices, out List<(Platform platform, string id, string name)> choices) { choices = null; @@ -28,6 +29,7 @@ internal sealed partial class SelectDialogForm : CustomForm OnTreeNodeChecked(node); _ = selectionTreeView.Nodes.Add(node); } + if (selected.Count < 1) OnLoad(null, null); allCheckBox.CheckedChanged -= OnAllCheckBoxChanged; @@ -64,10 +66,13 @@ internal sealed partial class SelectDialogForm : CustomForm } private void OnResize(object s, EventArgs e) - => Text = TextRenderer.MeasureText(Program.ApplicationName, Font).Width > Size.Width - 100 ? Program.ApplicationNameShort : Program.ApplicationName; + => Text = TextRenderer.MeasureText(Program.ApplicationName, Font).Width > Size.Width - 100 + ? Program.ApplicationNameShort + : Program.ApplicationName; private void OnSortCheckBoxChanged(object sender, EventArgs e) - => selectionTreeView.TreeViewNodeSorter = sortCheckBox.Checked ? PlatformIdComparer.NodeText : PlatformIdComparer.NodeName; + => selectionTreeView.TreeViewNodeSorter = + sortCheckBox.Checked ? PlatformIdComparer.NodeText : PlatformIdComparer.NodeName; private void OnAllCheckBoxChanged(object sender, EventArgs e) { @@ -77,6 +82,7 @@ internal sealed partial class SelectDialogForm : CustomForm node.Checked = shouldCheck; OnTreeNodeChecked(node); } + allCheckBox.CheckedChanged -= OnAllCheckBoxChanged; allCheckBox.Checked = shouldCheck; allCheckBox.CheckedChanged += OnAllCheckBoxChanged; @@ -96,7 +102,8 @@ internal sealed partial class SelectDialogForm : CustomForm private void OnSave(object sender, EventArgs e) { - ProgramData.WriteProgramChoices(selectionTreeView.Nodes.Cast().Where(n => n.Checked).Select(node => ((Platform)node.Tag, node.Name))); + ProgramData.WriteProgramChoices(selectionTreeView.Nodes.Cast().Where(n => n.Checked) + .Select(node => ((Platform)node.Tag, node.Name))); loadButton.Enabled = ProgramData.ReadProgramChoices() is not null; } } \ No newline at end of file diff --git a/CreamInstaller/Forms/SelectForm.cs b/CreamInstaller/Forms/SelectForm.cs index c928f09..8ec420b 100644 --- a/CreamInstaller/Forms/SelectForm.cs +++ b/CreamInstaller/Forms/SelectForm.cs @@ -50,7 +50,9 @@ internal sealed partial class SelectForm : CustomForm } private static void UpdateRemaining(Label label, ConcurrentDictionary list, string descriptor) - => label.Text = list.IsEmpty ? "" : $"Remaining {descriptor} ({list.Count}): " + string.Join(", ", list.Values).Replace("&", "&&"); + => label.Text = list.IsEmpty + ? "" + : $"Remaining {descriptor} ({list.Count}): " + string.Join(", ", list.Values).Replace("&", "&&"); private void UpdateRemainingGames() => UpdateRemaining(progressLabelGames, remainingGames, "games"); @@ -114,17 +116,20 @@ internal sealed partial class SelectForm : CustomForm return; int totalGameCount = 0; int completeGameCount = 0; + void AddToRemainingGames(string gameName) { this.AddToRemainingGames(gameName); progress.Report(-Interlocked.Increment(ref totalGameCount)); progress.Report(completeGameCount); } + void RemoveFromRemainingGames(string gameName) { this.RemoveFromRemainingGames(gameName); progress.Report(Interlocked.Increment(ref completeGameCount)); } + if (Program.Canceled) return; remainingGames.Clear(); // for display purposes only, otherwise ignorable @@ -133,11 +138,14 @@ internal sealed partial class SelectForm : CustomForm if (uninstallAll || programsToScan.Any(c => c.platform is Platform.Paradox)) { AddToRemainingGames("Paradox Launcher"); - HashSet dllDirectories = await ParadoxLauncher.InstallPath.GetDllDirectoriesFromGameDirectory(Platform.Paradox); + HashSet dllDirectories = + await ParadoxLauncher.InstallPath.GetDllDirectoriesFromGameDirectory(Platform.Paradox); if (dllDirectories is not null) { - Selection selection = Selection.GetOrCreate(Platform.Paradox, "PL", "Paradox Launcher", ParadoxLauncher.InstallPath, dllDirectories, - await ParadoxLauncher.InstallPath.GetExecutableDirectories(validFunc: path => !Path.GetFileName(path).Contains("bootstrapper"))); + Selection selection = Selection.GetOrCreate(Platform.Paradox, "PL", "Paradox Launcher", + ParadoxLauncher.InstallPath, dllDirectories, + await ParadoxLauncher.InstallPath.GetExecutableDirectories(validFunc: path => + !Path.GetFileName(path).Contains("bootstrapper"))); if (uninstallAll) selection.Enabled = true; else if (selection.TreeNode.TreeView is null) @@ -145,40 +153,48 @@ internal sealed partial class SelectForm : CustomForm RemoveFromRemainingGames("Paradox Launcher"); } } + int steamGamesToCheck; if (uninstallAll || programsToScan.Any(c => c.platform is Platform.Steam)) { - List<(string appId, string name, string branch, int buildId, string gameDirectory)> steamGames = await SteamLibrary.GetGames(); + List<(string appId, string name, string branch, int buildId, string gameDirectory)> steamGames = + await SteamLibrary.GetGames(); steamGamesToCheck = steamGames.Count; foreach ((string appId, string name, string branch, int buildId, string gameDirectory) in steamGames) { if (Program.Canceled) return; - if (!uninstallAll && (Program.IsGameBlocked(name, gameDirectory) || !programsToScan.Any(c => c.platform is Platform.Steam && c.id == appId))) + if (!uninstallAll && (Program.IsGameBlocked(name, gameDirectory) || + !programsToScan.Any(c => c.platform is Platform.Steam && c.id == appId))) { _ = Interlocked.Decrement(ref steamGamesToCheck); continue; } + AddToRemainingGames(name); Task task = Task.Run(async () => { if (Program.Canceled) return; - HashSet dllDirectories = await gameDirectory.GetDllDirectoriesFromGameDirectory(Platform.Steam); + HashSet dllDirectories = + await gameDirectory.GetDllDirectoriesFromGameDirectory(Platform.Steam); if (dllDirectories is null) { _ = Interlocked.Decrement(ref steamGamesToCheck); RemoveFromRemainingGames(name); return; } + if (uninstallAll) { - Selection bareSelection = Selection.GetOrCreate(Platform.Steam, appId, name, gameDirectory, dllDirectories, + Selection bareSelection = Selection.GetOrCreate(Platform.Steam, appId, name, gameDirectory, + dllDirectories, await gameDirectory.GetExecutableDirectories(true)); bareSelection.Enabled = true; RemoveFromRemainingGames(name); return; } + if (Program.Canceled) return; AppData appData = await SteamStore.QueryStoreAPI(appId); @@ -189,6 +205,7 @@ internal sealed partial class SelectForm : CustomForm RemoveFromRemainingGames(name); return; } + if (Program.Canceled) return; ConcurrentDictionary dlc = new(); @@ -233,14 +250,20 @@ internal sealed partial class SelectForm : CustomForm if (dlcAppInfo is not null) { dlcName = dlcAppInfo.Value.GetChild("common")?.GetChild("name")?.ToString(); - string dlcIconStaticId = dlcAppInfo.Value.GetChild("common")?.GetChild("icon")?.ToString(); - dlcIconStaticId ??= dlcAppInfo.Value.GetChild("common")?.GetChild("logo_small")?.ToString(); - dlcIconStaticId ??= dlcAppInfo.Value.GetChild("common")?.GetChild("logo")?.ToString(); + string dlcIconStaticId = dlcAppInfo.Value.GetChild("common")?.GetChild("icon") + ?.ToString(); + dlcIconStaticId ??= dlcAppInfo.Value.GetChild("common")?.GetChild("logo_small") + ?.ToString(); + dlcIconStaticId ??= dlcAppInfo.Value.GetChild("common")?.GetChild("logo") + ?.ToString(); if (dlcIconStaticId is not null) - dlcIcon = IconGrabber.SteamAppImagesPath + @$"\{dlcAppId}\{dlcIconStaticId}.jpg"; - fullGameAppId = dlcAppInfo.Value.GetChild("common")?.GetChild("parent")?.ToString(); + dlcIcon = IconGrabber.SteamAppImagesPath + + @$"\{dlcAppId}\{dlcIconStaticId}.jpg"; + fullGameAppId = dlcAppInfo.Value.GetChild("common")?.GetChild("parent") + ?.ToString(); } } + if (fullGameAppId != null && fullGameAppId != appId) { string fullGameName = null; @@ -258,29 +281,38 @@ internal sealed partial class SelectForm : CustomForm VProperty fullGameAppInfo = await SteamCMD.GetAppInfo(fullGameAppId); if (fullGameAppInfo is not null) { - fullGameName = fullGameAppInfo.Value.GetChild("common")?.GetChild("name")?.ToString(); - string fullGameIconStaticId = fullGameAppInfo.Value.GetChild("common")?.GetChild("icon")?.ToString(); - fullGameIconStaticId ??= fullGameAppInfo.Value.GetChild("common")?.GetChild("logo_small")?.ToString(); - fullGameIconStaticId ??= fullGameAppInfo.Value.GetChild("common")?.GetChild("logo")?.ToString(); + fullGameName = fullGameAppInfo.Value.GetChild("common")?.GetChild("name") + ?.ToString(); + string fullGameIconStaticId = fullGameAppInfo.Value.GetChild("common") + ?.GetChild("icon")?.ToString(); + fullGameIconStaticId ??= fullGameAppInfo.Value.GetChild("common") + ?.GetChild("logo_small")?.ToString(); + fullGameIconStaticId ??= fullGameAppInfo.Value.GetChild("common") + ?.GetChild("logo")?.ToString(); if (fullGameIconStaticId is not null) - dlcIcon = IconGrabber.SteamAppImagesPath + @$"\{fullGameAppId}\{fullGameIconStaticId}.jpg"; + dlcIcon = IconGrabber.SteamAppImagesPath + + @$"\{fullGameAppId}\{fullGameIconStaticId}.jpg"; } } + if (Program.Canceled) return; if (!string.IsNullOrWhiteSpace(fullGameName)) { - SelectionDLC fullGameDlc = SelectionDLC.GetOrCreate(fullGameOnSteamStore ? DLCType.Steam : DLCType.SteamHidden, appId, + SelectionDLC fullGameDlc = SelectionDLC.GetOrCreate( + fullGameOnSteamStore ? DLCType.Steam : DLCType.SteamHidden, appId, fullGameAppId, fullGameName); fullGameDlc.Icon = fullGameIcon; _ = dlc.TryAdd(fullGameDlc, default); } } + if (Program.Canceled) return; if (string.IsNullOrWhiteSpace(dlcName)) dlcName = "Unknown"; - SelectionDLC _dlc = SelectionDLC.GetOrCreate(onSteamStore ? DLCType.Steam : DLCType.SteamHidden, appId, dlcAppId, dlcName); + SelectionDLC _dlc = SelectionDLC.GetOrCreate( + onSteamStore ? DLCType.Steam : DLCType.SteamHidden, appId, dlcAppId, dlcName); _dlc.Icon = dlcIcon; _ = dlc.TryAdd(_dlc, default); RemoveFromRemainingDLCs(dlcAppId); @@ -292,6 +324,7 @@ internal sealed partial class SelectForm : CustomForm RemoveFromRemainingGames(name); return; } + if (Program.Canceled) return; foreach (Task task in dlcTasks) @@ -300,19 +333,24 @@ internal sealed partial class SelectForm : CustomForm return; await task; } + steamGamesToCheck = 0; if (dlc.IsEmpty) { RemoveFromRemainingGames(name); return; } - Selection selection = Selection.GetOrCreate(Platform.Steam, appId, appData?.Name ?? name, gameDirectory, dllDirectories, + + Selection selection = Selection.GetOrCreate(Platform.Steam, appId, appData?.Name ?? name, + gameDirectory, dllDirectories, await gameDirectory.GetExecutableDirectories(true)); selection.Product = "https://store.steampowered.com/app/" + appId; - selection.Icon = IconGrabber.SteamAppImagesPath + @$"\{appId}\{appInfo?.Value.GetChild("common")?.GetChild("icon")}.jpg"; + selection.Icon = IconGrabber.SteamAppImagesPath + + @$"\{appId}\{appInfo?.Value.GetChild("common")?.GetChild("icon")}.jpg"; selection.SubIcon = appData?.HeaderImage ?? IconGrabber.SteamAppImagesPath - + @$"\{appId}\{appInfo?.Value.GetChild("common")?.GetChild("clienticon")}.ico"; - selection.Publisher = appData?.Publishers[0] ?? appInfo?.Value.GetChild("extended")?.GetChild("publisher")?.ToString(); + + @$"\{appId}\{appInfo?.Value.GetChild("common")?.GetChild("clienticon")}.ico"; + selection.Publisher = appData?.Publishers[0] ?? + appInfo?.Value.GetChild("extended")?.GetChild("publisher")?.ToString(); selection.Website = appData?.Website; if (Program.Canceled) return; @@ -336,6 +374,7 @@ internal sealed partial class SelectForm : CustomForm appTasks.Add(task); } } + if (uninstallAll || programsToScan.Any(c => c.platform is Platform.Epic)) { List epicGames = await EpicLibrary.GetGames(); @@ -346,7 +385,8 @@ internal sealed partial class SelectForm : CustomForm string directory = manifest.InstallLocation; if (Program.Canceled) return; - if (!uninstallAll && (Program.IsGameBlocked(name, directory) || !programsToScan.Any(c => c.platform is Platform.Epic && c.id == @namespace))) + if (!uninstallAll && (Program.IsGameBlocked(name, directory) || + !programsToScan.Any(c => c.platform is Platform.Epic && c.id == @namespace))) continue; AddToRemainingGames(name); Task task = Task.Run(async () => @@ -359,19 +399,23 @@ internal sealed partial class SelectForm : CustomForm RemoveFromRemainingGames(name); return; } + if (uninstallAll) { - Selection bareSelection = Selection.GetOrCreate(Platform.Epic, @namespace, name, directory, dllDirectories, + Selection bareSelection = Selection.GetOrCreate(Platform.Epic, @namespace, name, directory, + dllDirectories, await directory.GetExecutableDirectories(true)); bareSelection.Enabled = true; RemoveFromRemainingGames(name); return; } + if (Program.Canceled) return; List dlcTasks = new(); ConcurrentDictionary catalogItems = new(); - List<(string id, string name, string product, string icon, string developer)> catalogIds = await EpicStore.QueryCatalog(@namespace); + List<(string id, string name, string product, string icon, string developer)> catalogIds = + await EpicStore.QueryCatalog(@namespace); if (catalogIds.Count > 0) foreach ((string id, string name, string product, string icon, string developer) in catalogIds) { @@ -391,6 +435,7 @@ internal sealed partial class SelectForm : CustomForm }); dlcTasks.Add(task); } + if (Program.Canceled) return; foreach (Task task in dlcTasks) @@ -399,12 +444,15 @@ internal sealed partial class SelectForm : CustomForm return; await task; } + if (catalogItems.IsEmpty) { RemoveFromRemainingGames(name); return; } - Selection selection = Selection.GetOrCreate(Platform.Epic, @namespace, name, directory, dllDirectories, + + Selection selection = Selection.GetOrCreate(Platform.Epic, @namespace, name, directory, + dllDirectories, await directory.GetExecutableDirectories(true)); foreach ((SelectionDLC dlc, _) in catalogItems.Where(dlc => dlc.Key.Name == selection.Name)) { @@ -414,6 +462,7 @@ internal sealed partial class SelectForm : CustomForm selection.Icon = dlc.Icon; selection.Publisher = dlc.Publisher; } + if (Program.Canceled) return; Invoke(delegate @@ -438,6 +487,7 @@ internal sealed partial class SelectForm : CustomForm appTasks.Add(task); } } + if (uninstallAll || programsToScan.Any(c => c.platform is Platform.Ubisoft)) { List<(string gameId, string name, string gameDirectory)> ubisoftGames = await UbisoftLibrary.GetGames(); @@ -445,30 +495,36 @@ internal sealed partial class SelectForm : CustomForm { if (Program.Canceled) return; - if (!uninstallAll && (Program.IsGameBlocked(name, gameDirectory) || !programsToScan.Any(c => c.platform is Platform.Ubisoft && c.id == gameId))) + if (!uninstallAll && (Program.IsGameBlocked(name, gameDirectory) || + !programsToScan.Any(c => c.platform is Platform.Ubisoft && c.id == gameId))) continue; AddToRemainingGames(name); Task task = Task.Run(async () => { if (Program.Canceled) return; - HashSet dllDirectories = await gameDirectory.GetDllDirectoriesFromGameDirectory(Platform.Ubisoft); + HashSet dllDirectories = + await gameDirectory.GetDllDirectoriesFromGameDirectory(Platform.Ubisoft); if (dllDirectories is null) { RemoveFromRemainingGames(name); return; } + if (uninstallAll) { - Selection bareSelection = Selection.GetOrCreate(Platform.Ubisoft, gameId, name, gameDirectory, dllDirectories, + Selection bareSelection = Selection.GetOrCreate(Platform.Ubisoft, gameId, name, gameDirectory, + dllDirectories, await gameDirectory.GetExecutableDirectories(true)); bareSelection.Enabled = true; RemoveFromRemainingGames(name); return; } + if (Program.Canceled) return; - Selection selection = Selection.GetOrCreate(Platform.Ubisoft, gameId, name, gameDirectory, dllDirectories, + Selection selection = Selection.GetOrCreate(Platform.Ubisoft, gameId, name, gameDirectory, + dllDirectories, await gameDirectory.GetExecutableDirectories(true)); selection.Icon = IconGrabber.GetDomainFaviconUrl("store.ubi.com"); Invoke(delegate @@ -485,12 +541,14 @@ internal sealed partial class SelectForm : CustomForm appTasks.Add(task); } } + foreach (Task task in appTasks) { if (Program.Canceled) return; await task; } + steamGamesToCheck = 0; } @@ -522,19 +580,26 @@ internal sealed partial class SelectForm : CustomForm List<(Platform platform, string id, string name, bool alreadySelected)> gameChoices = new(); if (ParadoxLauncher.InstallPath.DirectoryExists()) gameChoices.Add((Platform.Paradox, "PL", "Paradox Launcher", - programsToScan is not null && programsToScan.Any(p => p.platform is Platform.Paradox && p.id == "PL"))); + 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))) + foreach ((string appId, string name, string _, int _, string _) in + (await SteamLibrary.GetGames()).Where(g + => !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))); + programsToScan is not null && + programsToScan.Any(p => p.platform is Platform.Steam && p.id == appId))); if (EpicLibrary.EpicManifestsPath.DirectoryExists() || HeroicLibrary.HeroicLibraryPath.DirectoryExists()) - gameChoices.AddRange((await EpicLibrary.GetGames()).Where(m => !Program.IsGameBlocked(m.DisplayName, m.InstallLocation)).Select(manifest - => (Platform.Epic, manifest.CatalogNamespace, manifest.DisplayName, - programsToScan is not null && programsToScan.Any(p => p.platform is Platform.Epic && p.id == manifest.CatalogNamespace)))); - foreach ((string gameId, string name, string _) in (await UbisoftLibrary.GetGames()).Where(g => !Program.IsGameBlocked(g.name, g.gameDirectory))) + gameChoices.AddRange((await EpicLibrary.GetGames()) + .Where(m => !Program.IsGameBlocked(m.DisplayName, m.InstallLocation)).Select(manifest + => (Platform.Epic, manifest.CatalogNamespace, manifest.DisplayName, + programsToScan is not null && programsToScan.Any(p => + p.platform is Platform.Epic && p.id == manifest.CatalogNamespace)))); + foreach ((string gameId, string name, string _) in (await UbisoftLibrary.GetGames()).Where(g => + !Program.IsGameBlocked(g.name, g.gameDirectory))) gameChoices.Add((Platform.Ubisoft, gameId, name, - programsToScan is not null && programsToScan.Any(p => p.platform is Platform.Ubisoft && p.id == gameId))); + programsToScan is not null && + programsToScan.Any(p => p.platform is Platform.Ubisoft && p.id == gameId))); if (gameChoices.Count > 0) { using SelectDialogForm form = new(this); @@ -569,6 +634,7 @@ internal sealed partial class SelectForm : CustomForm } else scan = selectResult == DialogResult.OK && choices is not null && choices.Count > 0; + const string retry = "\n\nPress the \"Rescan\" button to re-choose."; if (scan) { @@ -581,6 +647,7 @@ internal sealed partial class SelectForm : CustomForm else noneFoundLabel.Text = "No applicable programs nor games were found on your computer!"; } + if (scan) { bool setup = true; @@ -597,10 +664,14 @@ internal sealed partial class SelectForm : CustomForm else curProgress = _progress; int p = Math.Max(Math.Min((int)((float)curProgress / maxProgress * 100), 100), 0); - progressLabel.Text = setup ? $"Setting up SteamCMD . . . {p}%" : $"Gathering and caching your applicable games and their DLCs . . . {p}%"; + progressLabel.Text = + setup + ? $"Setting up SteamCMD . . . {p}%" + : $"Gathering and caching your applicable games and their DLCs . . . {p}%"; progressBar.Value = p; }; - if (SteamLibrary.InstallPath.DirectoryExists() && programsToScan is not null && programsToScan.Any(c => c.platform is Platform.Steam)) + if (SteamLibrary.InstallPath.DirectoryExists() && programsToScan is not null && + programsToScan.Any(c => c.platform is Platform.Steam)) { progressLabel.Text = "Setting up SteamCMD . . . "; if (!await SteamCMD.Setup(iProgress)) @@ -610,6 +681,7 @@ internal sealed partial class SelectForm : CustomForm return; } } + setup = false; progressLabel.Text = "Gathering and caching your applicable games and their DLCs . . . "; Selection.ValidateAll(programsToScan); @@ -618,6 +690,7 @@ internal sealed partial class SelectForm : CustomForm await GetApplicablePrograms(iProgress); await SteamCMD.Cleanup(); } + OnLoadDlc(null, null); OnLoadKoaloader(null, null); HideProgressBar(); @@ -649,7 +722,8 @@ internal sealed partial class SelectForm : CustomForm SyncNodeAncestors(node); SyncNodeDescendants(node); allCheckBox.CheckedChanged -= OnAllCheckBoxChanged; - allCheckBox.Checked = EnumerateTreeNodes(selectionTreeView.Nodes).All(node => node.Text == "Unknown" || node.Checked); + allCheckBox.Checked = EnumerateTreeNodes(selectionTreeView.Nodes) + .All(node => node.Text == "Unknown" || node.Checked); allCheckBox.CheckedChanged += OnAllCheckBoxChanged; installButton.Enabled = Selection.AllEnabled.Any(); uninstallButton.Enabled = installButton.Enabled; @@ -699,8 +773,9 @@ internal sealed partial class SelectForm : CustomForm progressBar.Visible = true; programsGroupBox.Size = programsGroupBox.Size with { - Height = programsGroupBox.Size.Height - 3 - progressLabel.Size.Height - progressLabelGames.Size.Height - progressLabelDLCs.Size.Height - - progressBar.Size.Height + Height = programsGroupBox.Size.Height - 3 - progressLabel.Size.Height - progressLabelGames.Size.Height - + progressLabelDLCs.Size.Height + - progressBar.Size.Height }; } @@ -713,8 +788,9 @@ internal sealed partial class SelectForm : CustomForm progressBar.Visible = false; programsGroupBox.Size = programsGroupBox.Size with { - Height = programsGroupBox.Size.Height + 3 + progressLabel.Size.Height + progressLabelGames.Size.Height + progressLabelDLCs.Size.Height - + progressBar.Size.Height + Height = programsGroupBox.Size.Height + 3 + progressLabel.Size.Height + progressLabelGames.Size.Height + + progressLabelDLCs.Size.Height + + progressBar.Size.Height }; } @@ -753,10 +829,13 @@ internal sealed partial class SelectForm : CustomForm : selection.Platform is Platform.Epic ? "Epic GraphQL " : ""; - queries.Add(new($"Open {platformString}Query", "Notepad", (_, _) => Diagnostics.OpenFileInNotepad(appInfoJSON))); + queries.Add(new($"Open {platformString}Query", "Notepad", + (_, _) => Diagnostics.OpenFileInNotepad(appInfoJSON))); } + if (appInfoVDF.FileExists()) - queries.Add(new("Open SteamCMD Query", "Notepad", (_, _) => Diagnostics.OpenFileInNotepad(appInfoVDF))); + queries.Add(new("Open SteamCMD Query", "Notepad", + (_, _) => Diagnostics.OpenFileInNotepad(appInfoVDF))); if (queries.Count > 0) { _ = items.Add(new ToolStripSeparator()); @@ -774,62 +853,78 @@ internal sealed partial class SelectForm : CustomForm })); } } + if (selection is not null) { if (id == "PL") { _ = items.Add(new ToolStripSeparator()); + 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()); _ = items.Add(new ContextMenuItem("Open Root Directory", "File Explorer", (_, _) => Diagnostics.OpenDirectoryInFileExplorer(selection.RootDirectory))); int executables = 0; foreach ((string directory, BinaryType binaryType) in selection.ExecutableDirectories) - _ = items.Add(new ContextMenuItem($"Open Executable Directory #{++executables} ({(binaryType == BinaryType.BIT32 ? "32" : "64")}-bit)", + _ = items.Add(new ContextMenuItem( + $"Open Executable Directory #{++executables} ({(binaryType == BinaryType.BIT32 ? "32" : "64")}-bit)", "File Explorer", (_, _) => Diagnostics.OpenDirectoryInFileExplorer(directory))); HashSet directories = selection.DllDirectories; int steam = 0, epic = 0, r1 = 0, r2 = 0; if (selection.Platform is Platform.Steam or Platform.Paradox) foreach (string directory in directories) { - directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string old_config, + 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 (api32.FileExists() || api32_o.FileExists() || api64.FileExists() || api64_o.FileExists() || old_config.FileExists() - || config.FileExists() || old_log.FileExists() || log.FileExists() || cache.FileExists()) + if (api32.FileExists() || api32_o.FileExists() || api64.FileExists() || api64_o.FileExists() || + old_config.FileExists() + || config.FileExists() || old_log.FileExists() || log.FileExists() || cache.FileExists()) _ = items.Add(new ContextMenuItem($"Open Steamworks Directory #{++steam}", "File Explorer", (_, _) => Diagnostics.OpenDirectoryInFileExplorer(directory))); } + if (selection.Platform is Platform.Epic or Platform.Paradox) foreach (string directory in directories) { - directory.GetScreamApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config, + directory.GetScreamApiComponents(out string api32, out string api32_o, out string api64, + out string api64_o, out string config, out string log); - if (api32.FileExists() || api32_o.FileExists() || api64.FileExists() || api64_o.FileExists() || config.FileExists() || log.FileExists()) + if (api32.FileExists() || api32_o.FileExists() || api64.FileExists() || api64_o.FileExists() || + config.FileExists() || log.FileExists()) _ = items.Add(new ContextMenuItem($"Open EOS Directory #{++epic}", "File Explorer", (_, _) => Diagnostics.OpenDirectoryInFileExplorer(directory))); } + if (selection.Platform is Platform.Ubisoft) foreach (string directory in directories) { - directory.GetUplayR1Components(out string api32, out string api32_o, out string api64, out string api64_o, out string config, + directory.GetUplayR1Components(out string api32, out string api32_o, out string api64, + out string api64_o, out string config, out string log); - if (api32.FileExists() || api32_o.FileExists() || api64.FileExists() || api64_o.FileExists() || config.FileExists() || log.FileExists()) + if (api32.FileExists() || api32_o.FileExists() || api64.FileExists() || api64_o.FileExists() || + config.FileExists() || log.FileExists()) _ = items.Add(new ContextMenuItem($"Open Uplay R1 Directory #{++r1}", "File Explorer", (_, _) => Diagnostics.OpenDirectoryInFileExplorer(directory))); - directory.GetUplayR2Components(out string old_api32, out string old_api64, out api32, out api32_o, out api64, out api64_o, out config, + 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.FileExists() || old_api64.FileExists() || api32.FileExists() || api32_o.FileExists() || api64.FileExists() - || api64_o.FileExists() || config.FileExists() || log.FileExists()) + if (old_api32.FileExists() || old_api64.FileExists() || api32.FileExists() || + api32_o.FileExists() || api64.FileExists() + || api64_o.FileExists() || config.FileExists() || log.FileExists()) _ = items.Add(new ContextMenuItem($"Open Uplay R2 Directory #{++r2}", "File Explorer", (_, _) => Diagnostics.OpenDirectoryInFileExplorer(directory))); } } + if (id != "PL") { if (selection?.Platform is Platform.Steam || dlcParentSelection?.Platform is Platform.Steam) @@ -838,19 +933,23 @@ internal sealed partial class SelectForm : CustomForm _ = items.Add(new ContextMenuItem("Open SteamDB", "SteamDB", (_, _) => Diagnostics.OpenUrlInInternetBrowser("https://steamdb.info/app/" + id))); } + if (selection is not null) switch (selection.Platform) { case Platform.Steam: _ = items.Add(new ContextMenuItem("Open Steam Store", "Steam Store", (_, _) => Diagnostics.OpenUrlInInternetBrowser(selection.Product))); - _ = items.Add(new ContextMenuItem("Open Steam Community", ("Sub_" + id, selection.SubIcon), "Steam Community", - (_, _) => Diagnostics.OpenUrlInInternetBrowser("https://steamcommunity.com/app/" + id))); + _ = items.Add(new ContextMenuItem("Open Steam Community", ("Sub_" + id, selection.SubIcon), + "Steam Community", + (_, _) => Diagnostics.OpenUrlInInternetBrowser("https://steamcommunity.com/app/" + + id))); break; case Platform.Epic: _ = items.Add(new ToolStripSeparator()); _ = items.Add(new ContextMenuItem("Open ScreamDB", "ScreamDB", - (_, _) => Diagnostics.OpenUrlInInternetBrowser("https://scream-db.web.app/offers/" + id))); + (_, _) => Diagnostics.OpenUrlInInternetBrowser("https://scream-db.web.app/offers/" + + id))); _ = items.Add(new ContextMenuItem("Open Epic Games Store", "Epic Games", (_, _) => Diagnostics.OpenUrlInInternetBrowser(selection.Product))); break; @@ -858,12 +957,15 @@ internal sealed partial class SelectForm : CustomForm _ = items.Add(new ToolStripSeparator()); _ = items.Add(new ContextMenuItem("Open Ubisoft Store", "Ubisoft Store", (_, _) => Diagnostics.OpenUrlInInternetBrowser( - "https://store.ubi.com/us/" + selection.Name.Replace(" ", "-").ToLowerInvariant()))); + "https://store.ubi.com/us/" + + selection.Name.Replace(" ", "-").ToLowerInvariant()))); break; } } + if (selection?.Website is not null) - _ = items.Add(new ContextMenuItem("Open Official Website", ("Web_" + id, IconGrabber.GetDomainFaviconUrl(selection.Website)), + _ = items.Add(new ContextMenuItem("Open Official Website", + ("Web_" + id, IconGrabber.GetDomainFaviconUrl(selection.Website)), (_, _) => Diagnostics.OpenUrlInInternetBrowser(selection.Website))); contextMenuStrip.Show(selectionTreeView, location); contextMenuStrip.Refresh(); @@ -871,7 +973,7 @@ internal sealed partial class SelectForm : CustomForm private void OnLoad(object sender, EventArgs _) { - retry: + retry: try { HideProgressBar(); @@ -934,6 +1036,7 @@ internal sealed partial class SelectForm : CustomForm selection.Enabled = shouldEnable; OnTreeViewNodeCheckedChanged(null, new(selection.TreeNode, TreeViewAction.ByMouse)); } + allCheckBox.CheckedChanged -= OnAllCheckBoxChanged; allCheckBox.Checked = shouldEnable; allCheckBox.CheckedChanged += OnAllCheckBoxChanged; @@ -953,19 +1056,23 @@ internal sealed partial class SelectForm : CustomForm private bool AreSelectionsDefault() => EnumerateTreeNodes(selectionTreeView.Nodes).All(node - => node.Parent is null || node.Tag is not Platform and not DLCType || (node.Text == "Unknown" ? !node.Checked : node.Checked)); + => node.Parent is null || node.Tag is not Platform and not DLCType || + (node.Text == "Unknown" ? !node.Checked : node.Checked)); - private bool CanSaveDlc() => installButton.Enabled && (ProgramData.ReadDlcChoices().Any() || !AreSelectionsDefault()); + private bool CanSaveDlc() => + installButton.Enabled && (ProgramData.ReadDlcChoices().Any() || !AreSelectionsDefault()); private void OnSaveDlc(object sender, EventArgs e) { List<(Platform platform, string gameId, string dlcId)> choices = ProgramData.ReadDlcChoices().ToList(); foreach (SelectionDLC dlc in SelectionDLC.All.Keys) if ((dlc.Name == "Unknown" ? dlc.Enabled : !dlc.Enabled) - && !choices.Any(c => c.platform == dlc.Selection.Platform && c.gameId == dlc.Selection.Id && c.dlcId == dlc.Id)) + && !choices.Any(c => + c.platform == dlc.Selection.Platform && c.gameId == dlc.Selection.Id && c.dlcId == dlc.Id)) choices.Add((dlc.Selection.Platform, dlc.Selection.Id, dlc.Id)); else - _ = choices.RemoveAll(n => n.platform == dlc.Selection.Platform && n.gameId == dlc.Selection.Id && n.dlcId == dlc.Id); + _ = choices.RemoveAll(n => + n.platform == dlc.Selection.Platform && n.gameId == dlc.Selection.Id && n.dlcId == dlc.Id); ProgramData.WriteDlcChoices(choices); loadButton.Enabled = CanLoadDlc(); saveButton.Enabled = CanSaveDlc(); @@ -978,7 +1085,8 @@ internal sealed partial class SelectForm : CustomForm List<(Platform platform, string gameId, string dlcId)> choices = ProgramData.ReadDlcChoices().ToList(); foreach (SelectionDLC dlc in SelectionDLC.All.Keys) { - dlc.Enabled = choices.Any(c => c.platform == dlc.Selection?.Platform && c.gameId == dlc.Selection?.Id && c.dlcId == dlc.Id) + dlc.Enabled = choices.Any(c => + c.platform == dlc.Selection?.Platform && c.gameId == dlc.Selection?.Id && c.dlcId == dlc.Id) ? dlc.Name == "Unknown" : dlc.Name != "Unknown"; OnTreeViewNodeCheckedChanged(null, new(dlc.TreeNode, TreeViewAction.ByMouse)); @@ -994,23 +1102,29 @@ internal sealed partial class SelectForm : CustomForm dlc.Enabled = dlc.Name != "Unknown"; OnTreeViewNodeCheckedChanged(null, new(dlc.TreeNode, TreeViewAction.ByMouse)); } + resetButton.Enabled = CanResetDlc(); } - private static bool AreKoaloaderSelectionsDefault() => Selection.All.Keys.All(selection => selection.Koaloader && selection.KoaloaderProxy is null); + private static bool AreKoaloaderSelectionsDefault() => + Selection.All.Keys.All(selection => selection.Koaloader && selection.KoaloaderProxy is null); - private static bool CanSaveKoaloader() => ProgramData.ReadKoaloaderChoices().Any() || !AreKoaloaderSelectionsDefault(); + private static bool CanSaveKoaloader() => + ProgramData.ReadKoaloaderChoices().Any() || !AreKoaloaderSelectionsDefault(); private void OnSaveKoaloader(object sender, EventArgs e) { - List<(Platform platform, string id, string proxy, bool enabled)> choices = ProgramData.ReadKoaloaderChoices().ToList(); + List<(Platform platform, string id, string proxy, bool enabled)> choices = + ProgramData.ReadKoaloaderChoices().ToList(); foreach (Selection selection in Selection.All.Keys) { _ = choices.RemoveAll(c => c.platform == selection.Platform && c.id == selection.Id); if (selection.KoaloaderProxy is not null and not Selection.DefaultKoaloaderProxy || !selection.Koaloader) - choices.Add((selection.Platform, selection.Id, selection.KoaloaderProxy == Selection.DefaultKoaloaderProxy ? null : selection.KoaloaderProxy, + choices.Add((selection.Platform, selection.Id, + selection.KoaloaderProxy == Selection.DefaultKoaloaderProxy ? null : selection.KoaloaderProxy, selection.Koaloader)); } + ProgramData.WriteKoaloaderProxyChoices(choices); saveKoaloaderButton.Enabled = CanSaveKoaloader(); loadKoaloaderButton.Enabled = CanLoadKoaloader(); @@ -1020,7 +1134,8 @@ internal sealed partial class SelectForm : CustomForm private void OnLoadKoaloader(object sender, EventArgs e) { - List<(Platform platform, string id, string proxy, bool enabled)> choices = ProgramData.ReadKoaloaderChoices().ToList(); + List<(Platform platform, string id, string proxy, bool enabled)> choices = + ProgramData.ReadKoaloaderChoices().ToList(); foreach (Selection selection in Selection.All.Keys) if (choices.Any(c => c.platform == selection.Platform && c.id == selection.Id)) { @@ -1045,6 +1160,7 @@ internal sealed partial class SelectForm : CustomForm selection.Koaloader = true; selection.KoaloaderProxy = null; } + ProgramData.WriteKoaloaderProxyChoices(choices); loadKoaloaderButton.Enabled = CanLoadKoaloader(); OnKoaloaderChanged(); @@ -1059,6 +1175,7 @@ internal sealed partial class SelectForm : CustomForm selection.Koaloader = true; selection.KoaloaderProxy = null; } + OnKoaloaderChanged(); } @@ -1092,13 +1209,19 @@ internal sealed partial class SelectForm : CustomForm using DialogForm form = new(this); _ = form.Show(SystemIcons.Information, "Blocks the program from caching and displaying games protected by anti-cheats." - + "\nYou disable this option and install DLC unlockers to protected games at your own risk!" + "\n\nBlocked games: " - + (string.IsNullOrWhiteSpace(blockedGames.ToString()) ? "(none)" : blockedGames) + "\n\nBlocked game sub-directories: " - + (string.IsNullOrWhiteSpace(blockedDirectories.ToString()) ? "(none)" : blockedDirectories) + "\n\nBlocked game sub-directory exceptions: " - + (string.IsNullOrWhiteSpace(blockedDirectoryExceptions.ToString()) ? "(none)" : blockedDirectoryExceptions), + + "\nYou disable this option and install DLC unlockers to protected games at your own risk!" + + "\n\nBlocked games: " + + (string.IsNullOrWhiteSpace(blockedGames.ToString()) ? "(none)" : blockedGames) + + "\n\nBlocked game sub-directories: " + + (string.IsNullOrWhiteSpace(blockedDirectories.ToString()) ? "(none)" : blockedDirectories) + + "\n\nBlocked game sub-directory exceptions: " + + (string.IsNullOrWhiteSpace(blockedDirectoryExceptions.ToString()) + ? "(none)" + : blockedDirectoryExceptions), customFormText: "Block Protected Games"); } private void OnSortCheckBoxChanged(object sender, EventArgs e) - => selectionTreeView.TreeViewNodeSorter = sortCheckBox.Checked ? PlatformIdComparer.NodeText : PlatformIdComparer.NodeName; + => selectionTreeView.TreeViewNodeSorter = + sortCheckBox.Checked ? PlatformIdComparer.NodeText : PlatformIdComparer.NodeName; } \ No newline at end of file diff --git a/CreamInstaller/Forms/UpdateForm.cs b/CreamInstaller/Forms/UpdateForm.cs index 666724f..a36f151 100644 --- a/CreamInstaller/Forms/UpdateForm.cs +++ b/CreamInstaller/Forms/UpdateForm.cs @@ -50,16 +50,21 @@ internal sealed partial class UpdateForm : CustomForm updateButton.Click -= OnUpdateCancel; progressLabel.Text = "Checking for updates . . ."; changelogTreeView.Visible = false; - changelogTreeView.Location = progressLabel.Location with { Y = progressLabel.Location.Y + progressLabel.Size.Height + 13 }; + changelogTreeView.Location = progressLabel.Location with + { + Y = progressLabel.Location.Y + progressLabel.Size.Height + 13 + }; Refresh(); #if !DEBUG Version currentVersion = new(Program.Version); #endif List releases = null; - string response = await HttpClientManager.EnsureGet($"https://api.github.com/repos/{Program.RepositoryOwner}/{Program.RepositoryName}/releases"); + string response = + await HttpClientManager.EnsureGet( + $"https://api.github.com/repos/{Program.RepositoryOwner}/{Program.RepositoryName}/releases"); if (response is not null) releases = JsonConvert.DeserializeObject>(response) - ?.Where(release => !release.Draft && !release.Prerelease && release.Asset is not null).ToList(); + ?.Where(release => !release.Draft && !release.Prerelease && release.Asset is not null).ToList(); latestRelease = releases?.FirstOrDefault(); #if DEBUG if (latestRelease?.Version is not { } latestVersion) @@ -101,7 +106,7 @@ internal sealed partial class UpdateForm : CustomForm private void OnLoad(object sender, EventArgs _) { - retry: + retry: try { UpdaterPath.DeleteFile(); @@ -125,7 +130,8 @@ internal sealed partial class UpdateForm : CustomForm updateButton.Text = "Cancel"; updateButton.Click -= OnUpdate; updateButton.Click += OnUpdateCancel; - changelogTreeView.Location = progressBar.Location with { Y = progressBar.Location.Y + progressBar.Size.Height + 6 }; + changelogTreeView.Location = + progressBar.Location with { Y = progressBar.Location.Y + progressBar.Size.Height + 6 }; Refresh(); Progress progress = new(); IProgress iProgress = progress; @@ -144,7 +150,8 @@ internal sealed partial class UpdateForm : CustomForm { if (cancellation is null || Program.Canceled) throw new TaskCanceledException(); - using HttpResponseMessage response = await HttpClientManager.HttpClient.GetAsync(latestRelease.Asset.BrowserDownloadUrl, + using HttpResponseMessage response = await HttpClientManager.HttpClient.GetAsync( + latestRelease.Asset.BrowserDownloadUrl, HttpCompletionOption.ResponseHeadersRead, cancellation.Token); _ = response.EnsureSuccessStatusCode(); if (cancellation is null || Program.Canceled) @@ -155,7 +162,8 @@ 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(); @@ -166,6 +174,7 @@ internal sealed partial class UpdateForm : CustomForm continue; iProgress.Report(report); } + iProgress.Report((int)(bytesRead / bytes * 100)); if (cancellation is null || Program.Canceled) throw new TaskCanceledException(); @@ -179,6 +188,7 @@ internal sealed partial class UpdateForm : CustomForm retry = ex.HandleException(this, Program.Name + " encountered an exception while updating"); success = false; } + cancellation?.Dispose(); cancellation = null; await update.DisposeAsync(); @@ -209,13 +219,15 @@ internal sealed partial class UpdateForm : CustomForm Process process = new(); ProcessStartInfo startInfo = new() { - WorkingDirectory = ProgramData.DirectoryPath, FileName = "cmd.exe", Arguments = $"/C START \"UPDATER\" /B {Path.GetFileName(UpdaterPath)}", + WorkingDirectory = ProgramData.DirectoryPath, FileName = "cmd.exe", + Arguments = $"/C START \"UPDATER\" /B {Path.GetFileName(UpdaterPath)}", CreateNoWindow = true }; process.StartInfo = startInfo; _ = process.Start(); return; } + if (!retry) StartProgram(); else diff --git a/CreamInstaller/Platforms/Epic/EpicLibrary.cs b/CreamInstaller/Platforms/Epic/EpicLibrary.cs index 93e8b2d..f0f2ceb 100644 --- a/CreamInstaller/Platforms/Epic/EpicLibrary.cs +++ b/CreamInstaller/Platforms/Epic/EpicLibrary.cs @@ -17,8 +17,11 @@ internal static class EpicLibrary { get { - epicManifestsPath ??= Registry.GetValue(@"HKEY_CURRENT_USER\Software\Epic Games\EOS", "ModSdkMetadataDir", null) as string; - epicManifestsPath ??= Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Epic Games\EpicGamesLauncher", "AppDataPath", null) as string; + epicManifestsPath ??= + Registry.GetValue(@"HKEY_CURRENT_USER\Software\Epic Games\EOS", "ModSdkMetadataDir", null) as string; + epicManifestsPath ??= + Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Epic Games\EpicGamesLauncher", "AppDataPath", + null) as string; if (epicManifestsPath is not null && epicManifestsPath.EndsWith(@"\Data", StringComparison.Ordinal)) epicManifestsPath += @"\Manifests"; return epicManifestsPath.ResolvePath(); @@ -39,8 +42,9 @@ internal static class EpicLibrary try { Manifest manifest = JsonConvert.DeserializeObject(json); - if (manifest is not null && (manifest.InstallLocation = manifest.InstallLocation.ResolvePath()) is not null - && games.All(g => g.CatalogNamespace != manifest.CatalogNamespace)) + if (manifest is not null && (manifest.InstallLocation = manifest.InstallLocation.ResolvePath()) + is not null + && games.All(g => g.CatalogNamespace != manifest.CatalogNamespace)) games.Add(manifest); } catch @@ -48,6 +52,7 @@ internal static class EpicLibrary // ignored } } + if (Program.Canceled) return games; await HeroicLibrary.GetGames(games); diff --git a/CreamInstaller/Platforms/Epic/EpicStore.cs b/CreamInstaller/Platforms/Epic/EpicStore.cs index 6b15426..763498f 100644 --- a/CreamInstaller/Platforms/Epic/EpicStore.cs +++ b/CreamInstaller/Platforms/Epic/EpicStore.cs @@ -14,7 +14,8 @@ internal static class EpicStore { private const int Cooldown = 600; - internal static async Task> QueryCatalog(string categoryNamespace) + internal static async Task> + QueryCatalog(string categoryNamespace) { List<(string id, string name, string product, string icon, string developer)> dlcIds = []; string cacheFile = ProgramData.AppInfoPath + @$"\{categoryNamespace}.json"; @@ -41,13 +42,16 @@ internal static class EpicStore { cacheFile.DeleteFile(); } + if (response is null) return dlcIds; List searchStore = [..response.Data.Catalog.SearchStore.Elements]; foreach (Element element in searchStore) { string title = element.Title; - string product = element.CatalogNs is not null && element.CatalogNs.Mappings.Length > 0 ? element.CatalogNs.Mappings.First().PageSlug : null; + string product = element.CatalogNs is not null && element.CatalogNs.Mappings.Length > 0 + ? element.CatalogNs.Mappings.First().PageSlug + : null; string icon = null; for (int i = 0; i < element.KeyImages?.Length; i++) { @@ -57,14 +61,18 @@ internal static class EpicStore icon = keyImage.Url.ToString(); break; } + foreach (Item item in element.Items) dlcIds.Populate(item.Id, title, product, icon, null, element.Items.Length == 1); } + List catalogOffers = [..response.Data.Catalog.CatalogOffers.Elements]; foreach (Element element in catalogOffers) { string title = element.Title; - string product = element.CatalogNs is not null && element.CatalogNs.Mappings.Length > 0 ? element.CatalogNs.Mappings.First().PageSlug : null; + string product = element.CatalogNs is not null && element.CatalogNs.Mappings.Length > 0 + ? element.CatalogNs.Mappings.First().PageSlug + : null; string icon = null; for (int i = 0; i < element.KeyImages?.Length; i++) { @@ -74,13 +82,17 @@ internal static class EpicStore icon = keyImage.Url.ToString(); break; } + foreach (Item item in element.Items) dlcIds.Populate(item.Id, title, product, icon, item.Developer, element.Items.Length == 1); } + return dlcIds; } - private static void Populate(this List<(string id, string name, string product, string icon, string developer)> dlcIds, string id, string title, + private static void Populate( + this List<(string id, string name, string product, string icon, string developer)> dlcIds, string id, + string title, string product, string icon, string developer, bool canOverwrite = false) { if (id == null) @@ -97,6 +109,7 @@ internal static class EpicStore : (app.id, app.name ?? title, app.product ?? product, app.icon ?? icon, app.developer ?? developer); break; } + if (!found) dlcIds.Add((id, title, product, icon, developer)); } @@ -113,7 +126,8 @@ internal static class EpicStore HttpClient client = HttpClientManager.HttpClient; if (client is null) return null; - HttpResponseMessage httpResponse = await client.PostAsync(new Uri("https://graphql.epicgames.com/graphql"), content); + HttpResponseMessage httpResponse = + await client.PostAsync(new Uri("https://graphql.epicgames.com/graphql"), content); _ = httpResponse.EnsureSuccessStatusCode(); string response = await httpResponse.Content.ReadAsStringAsync(); return JsonConvert.DeserializeObject(response); diff --git a/CreamInstaller/Platforms/Epic/Heroic/HeroicLibrary.cs b/CreamInstaller/Platforms/Epic/Heroic/HeroicLibrary.cs index c39bba2..d69087d 100644 --- a/CreamInstaller/Platforms/Epic/Heroic/HeroicLibrary.cs +++ b/CreamInstaller/Platforms/Epic/Heroic/HeroicLibrary.cs @@ -10,7 +10,8 @@ namespace CreamInstaller.Platforms.Epic.Heroic; internal static class HeroicLibrary { internal static readonly string HeroicLibraryPath - = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\heroic\store_cache\legendary_library.json"; + = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + + @"\heroic\store_cache\legendary_library.json"; internal static async Task GetGames(List games) => await Task.Run(() => @@ -28,11 +29,13 @@ internal static class HeroicLibrary try { HeroicAppData appData = token.ToObject(); - if (appData is null || string.IsNullOrWhiteSpace(appData.Install.InstallPath = appData.Install.InstallPath.ResolvePath())) + if (appData is null || string.IsNullOrWhiteSpace(appData.Install.InstallPath = + appData.Install.InstallPath.ResolvePath())) continue; Manifest manifest = new() { - DisplayName = appData.Title, CatalogNamespace = appData.Namespace, InstallLocation = appData.Install.InstallPath + DisplayName = appData.Title, CatalogNamespace = appData.Namespace, + InstallLocation = appData.Install.InstallPath }; if (games.All(g => g.CatalogNamespace != manifest.CatalogNamespace)) games.Add(manifest); diff --git a/CreamInstaller/Platforms/Paradox/ParadoxLauncher.cs b/CreamInstaller/Platforms/Paradox/ParadoxLauncher.cs index bfc97f1..a96df40 100644 --- a/CreamInstaller/Platforms/Paradox/ParadoxLauncher.cs +++ b/CreamInstaller/Platforms/Paradox/ParadoxLauncher.cs @@ -13,7 +13,12 @@ namespace CreamInstaller.Platforms.Paradox; internal static class ParadoxLauncher { - public enum RepairResult { Failure = -1, Unnecessary = 0, Success } + public enum RepairResult + { + Failure = -1, + Unnecessary = 0, + Success + } private static string installPath; @@ -21,7 +26,8 @@ internal static class ParadoxLauncher { get { - installPath ??= Registry.GetValue(@"HKEY_CURRENT_USER\Software\Paradox Interactive\Paradox Launcher v2", "LauncherInstallation", null) as string; + installPath ??= Registry.GetValue(@"HKEY_CURRENT_USER\Software\Paradox Interactive\Paradox Launcher v2", + "LauncherInstallation", null) as string; return installPath.ResolvePath(); } } @@ -32,11 +38,13 @@ internal static class ParadoxLauncher if (paradoxLauncher is null) return; paradoxLauncher.ExtraSelections.Clear(); - foreach (Selection selection in Selection.AllEnabled.Where(s => !s.Equals(paradoxLauncher) && s.Publisher == "Paradox Interactive")) + foreach (Selection selection in Selection.AllEnabled.Where(s => + !s.Equals(paradoxLauncher) && s.Publisher == "Paradox Interactive")) _ = paradoxLauncher.ExtraSelections.Add(selection); if (paradoxLauncher.ExtraSelections.Count > 0) return; - foreach (Selection selection in Selection.All.Keys.Where(s => !s.Equals(paradoxLauncher) && s.Publisher == "Paradox Interactive")) + foreach (Selection selection in Selection.All.Keys.Where(s => + !s.Equals(paradoxLauncher) && s.Publisher == "Paradox Interactive")) _ = paradoxLauncher.ExtraSelections.Add(selection); } @@ -51,7 +59,8 @@ internal static class ParadoxLauncher using DialogForm dialogForm = new(form); return dialogForm.Show(SystemIcons.Warning, "WARNING: There are no scanned games with DLC that can be added to the Paradox Launcher!" - + "\n\nInstalling DLC unlockers for the Paradox Launcher alone can cause existing configurations to be deleted!", "Ignore", "Cancel", + + "\n\nInstalling DLC unlockers for the Paradox Launcher alone can cause existing configurations to be deleted!", + "Ignore", "Cancel", "Paradox Launcher") != DialogResult.OK; } @@ -64,6 +73,7 @@ internal static class ParadoxLauncher Program.Canceled = false; dialogText = new(); } + using DialogForm dialogForm = new(form); bool smokeInstalled = false; byte[] steamOriginalSdk32 = null; @@ -73,36 +83,46 @@ internal static class ParadoxLauncher byte[] epicOriginalSdk64 = null; 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, + 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)) + if (steamOriginalSdk32 is null && api32.FileExists() && + !api32.IsResourceFile(ResourceIdentifier.Steamworks32)) steamOriginalSdk32 = api32.ReadFileBytes(true); - if (steamOriginalSdk64 is null && api64.FileExists() && !api64.IsResourceFile(ResourceIdentifier.Steamworks64)) + if (steamOriginalSdk64 is null && api64.FileExists() && + !api64.IsResourceFile(ResourceIdentifier.Steamworks64)) steamOriginalSdk64 = api64.ReadFileBytes(true); - directory.GetScreamApiComponents(out api32, out api32_o, out api64, out api64_o, out config, out string log); + 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)) + if (epicOriginalSdk32 is null && api32.FileExists() && + !api32.IsResourceFile(ResourceIdentifier.EpicOnlineServices32)) epicOriginalSdk32 = api32.ReadFileBytes(true); - if (epicOriginalSdk64 is null && api64.FileExists() && !api64.IsResourceFile(ResourceIdentifier.EpicOnlineServices64)) + if (epicOriginalSdk64 is null && api64.FileExists() && + !api64.IsResourceFile(ResourceIdentifier.EpicOnlineServices64)) epicOriginalSdk64 = api64.ReadFileBytes(true); } - if (steamOriginalSdk32 is not null || steamOriginalSdk64 is not null || epicOriginalSdk32 is not null || epicOriginalSdk64 is not null) + + 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.TakeWhile(_ => !Program.Canceled)) { - directory.GetSmokeApiComponents(out string api32, out _, out string api64, out _, out _, out _, out _, out _, out _); + directory.GetSmokeApiComponents(out string api32, out _, out string api64, out _, out _, out _, out _, + out _, out _); if (steamOriginalSdk32 is not null && api32.IsResourceFile(ResourceIdentifier.Steamworks32)) { steamOriginalSdk32.WriteResource(api32); @@ -112,6 +132,7 @@ internal static class ParadoxLauncher dialogText.AppendLine("Corrected Steamworks: " + api32); neededRepair = true; } + if (steamOriginalSdk64 is not null && api64.IsResourceFile(ResourceIdentifier.Steamworks64)) { steamOriginalSdk64.WriteResource(api64); @@ -121,6 +142,7 @@ internal static class ParadoxLauncher dialogText.AppendLine("Corrected Steamworks: " + api64); neededRepair = true; } + if (smokeInstalled) await SmokeAPI.Install(directory, selection, generateConfig: false); directory.GetScreamApiComponents(out api32, out _, out api64, out _, out _, out _); @@ -133,6 +155,7 @@ internal static class ParadoxLauncher dialogText.AppendLine("Corrected Epic Online Services: " + api32); neededRepair = true; } + if (epicOriginalSdk64 is not null && api64.IsResourceFile(ResourceIdentifier.EpicOnlineServices64)) { epicOriginalSdk64.WriteResource(api64); @@ -142,9 +165,11 @@ internal static class ParadoxLauncher dialogText.AppendLine("Corrected Epic Online Services: " + api64); neededRepair = true; } + if (screamInstalled) await ScreamAPI.Install(directory, selection, generateConfig: false); } + if (!Program.Canceled) { if (neededRepair) @@ -156,28 +181,36 @@ internal static class ParadoxLauncher dialogText.AppendLine("\nParadox Launcher successfully repaired!"); _ = dialogForm.Show(form.Icon, dialogText.ToString(), customFormText: "Paradox Launcher"); } + return RepairResult.Success; } + 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"); + _ = 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"); + : 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.") + ? 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.") : 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 55c2188..c5559b2 100644 --- a/CreamInstaller/Platforms/Steam/SteamCMD.cs +++ b/CreamInstaller/Platforms/Steam/SteamCMD.cs @@ -23,7 +23,8 @@ internal static class SteamCMD private static readonly string FilePath = DirectoryPath + @"\steamcmd.exe"; - private static readonly ConcurrentDictionary AttemptCount = new(); // the more app_updates, the longer SteamCMD should wait for app_info_print + private static readonly ConcurrentDictionary + AttemptCount = new(); // the more app_updates, the longer SteamCMD should wait for app_info_print private static readonly int[] Locks = new int[ProcessLimit]; @@ -42,13 +43,13 @@ 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) => await Task.Run(() => { - wait_for_lock: + wait_for_lock: if (Program.Canceled) return ""; for (int i = 0; i < Locks.Length; i++) @@ -62,13 +63,17 @@ internal static class SteamCMD _ = AttemptCount.TryGetValue(appId, out int count); AttemptCount[appId] = ++count; } + if (Program.Canceled) return ""; ProcessStartInfo processStartInfo = new() { - FileName = FilePath, RedirectStandardOutput = true, RedirectStandardInput = true, RedirectStandardError = true, - UseShellExecute = false, Arguments = appId is null ? "+quit" : GetArguments(appId), CreateNoWindow = true, - StandardInputEncoding = Encoding.UTF8, StandardOutputEncoding = Encoding.UTF8, StandardErrorEncoding = Encoding.UTF8 + FileName = FilePath, RedirectStandardOutput = true, RedirectStandardInput = true, + RedirectStandardError = true, + UseShellExecute = false, Arguments = appId is null ? "+quit" : GetArguments(appId), + CreateNoWindow = true, + StandardInputEncoding = Encoding.UTF8, StandardOutputEncoding = Encoding.UTF8, + StandardErrorEncoding = Encoding.UTF8 }; Process process = Process.Start(processStartInfo); StringBuilder output = new(); @@ -83,6 +88,7 @@ internal static class SteamCMD process.Close(); break; } + int c = process.StandardOutput.Read(); if (c != -1) { @@ -92,13 +98,15 @@ internal static class SteamCMD appInfoStarted = true; _ = appInfoStarted ? appInfo.Append(ch) : output.Append(ch); } + DateTime now = DateTime.UtcNow; TimeSpan timeDiff = now - lastOutput; if (!(timeDiff.TotalSeconds > 0.1)) continue; process.Kill(true); process.Close(); - if (appId != null && output.ToString().Contains($"No app info for AppID {appId} found, requesting...")) + if (appId != null && + output.ToString().Contains($"No app info for AppID {appId} found, requesting...")) { AttemptCount[appId]++; processStartInfo.Arguments = GetArguments(appId); @@ -110,9 +118,11 @@ internal static class SteamCMD else break; } + _ = Interlocked.Decrement(ref Locks[i]); return appInfo.ToString(); } + Thread.Sleep(200); goto wait_for_lock; }); @@ -122,14 +132,16 @@ internal static class SteamCMD await Cleanup(); if (!FilePath.FileExists()) { - retryDownload: + retryDownload: HttpClient httpClient = HttpClientManager.HttpClient; if (httpClient is null) return false; while (!Program.Canceled) try { - byte[] file = await httpClient.GetByteArrayAsync(new Uri("https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip")); + byte[] file = + await httpClient.GetByteArrayAsync( + new Uri("https://steamcdn-a.akamaihd.net/client/installer/steamcmd.zip")); _ = file.WriteResource(ArchivePath); ArchivePath.ExtractZip(DirectoryPath); ArchivePath.DeleteFile(); @@ -142,9 +154,11 @@ internal static class SteamCMD return false; } } + if (DllPath.FileExists()) return true; - FileSystemWatcher watcher = new(DirectoryPath) { Filter = "*", IncludeSubdirectories = true, EnableRaisingEvents = true }; + FileSystemWatcher watcher = new(DirectoryPath) + { Filter = "*", IncludeSubdirectories = true, EnableRaisingEvents = true }; if (DllPath.FileExists()) progress.Report(-15); // update (not used at the moment) else @@ -176,7 +190,8 @@ internal static class SteamCMD file.DeleteFile(); foreach (string file in DirectoryPath.EnumerateDirectory("*.ntfs_transaction_failed")) file.DeleteFile(); - AppCachePath.DeleteDirectory(); // this is definitely needed, so SteamCMD gets the latest information for us + AppCachePath + .DeleteDirectory(); // this is definitely needed, so SteamCMD gets the latest information for us DumpsPath.DeleteDirectory(); LogsPath.DeleteDirectory(); SteamAppsPath.DeleteDirectory(); // this is just a useless folder created from +app_update 4 @@ -196,10 +211,12 @@ internal static class SteamCMD if (attempts > 10) { #if DEBUG - DebugForm.Current.Log("Failed to query SteamCMD after 10 tries: " + appId + " (" + branch + ")", LogTextBox.Warning); + DebugForm.Current.Log("Failed to query SteamCMD after 10 tries: " + appId + " (" + branch + ")", + LogTextBox.Warning); #endif break; } + string appUpdateFile = $@"{AppInfoPath}\{appId}.vdf"; string output = appUpdateFile.ReadFile(); if (output is null) @@ -216,27 +233,34 @@ internal static class SteamCMD else { #if DEBUG - DebugForm.Current.Log("SteamCMD query failed on attempt #" + attempts + " for " + appId + " (" + branch + "): Bad output", + DebugForm.Current.Log( + "SteamCMD query failed on attempt #" + attempts + " for " + appId + " (" + branch + + "): Bad output", LogTextBox.Warning); #endif continue; } } + if (!ValveDataFile.TryDeserialize(output, out VProperty appInfo) || appInfo.Value is VValue) { appUpdateFile.DeleteFile(); #if DEBUG - DebugForm.Current.Log("SteamCMD query failed on attempt #" + attempts + " for " + appId + " (" + branch + "): Deserialization failed", + DebugForm.Current.Log( + "SteamCMD query failed on attempt #" + attempts + " for " + appId + " (" + branch + + "): Deserialization failed", LogTextBox.Warning); #endif continue; } + if (!appInfo.Value.Children().Any()) return appInfo; VToken type = appInfo.Value.GetChild("common")?.GetChild("type"); if (type is not null && type.ToString() != "Game") return appInfo; - string buildid = appInfo.Value.GetChild("depots")?.GetChild("branches")?.GetChild(branch)?.GetChild("buildid")?.ToString(); + string buildid = appInfo.Value.GetChild("depots")?.GetChild("branches")?.GetChild(branch) + ?.GetChild("buildid")?.ToString(); if (buildid is null && type is not null) return appInfo; if (type is not null && (!int.TryParse(buildid, out int gamebuildId) || gamebuildId >= buildId)) @@ -246,9 +270,12 @@ internal static class SteamCMD dlcAppUpdateFile.DeleteFile(); appUpdateFile.DeleteFile(); #if DEBUG - DebugForm.Current.Log("SteamCMD query skipped on attempt #" + attempts + " for " + appId + " (" + branch + "): Outdated cache", LogTextBox.Warning); + DebugForm.Current.Log( + "SteamCMD query skipped on attempt #" + attempts + " for " + appId + " (" + branch + + "): Outdated cache", LogTextBox.Warning); #endif } + return null; } @@ -267,15 +294,18 @@ internal static class SteamCMD if (int.TryParse(id, out int appId) && appId > 0) _ = dlcIds.Add("" + appId); } + VToken depots = appInfo.Value.GetChild("depots"); if (depots is null) return dlcIds; - foreach (VToken vToken in depots.Where(p => p is VProperty property && int.TryParse(property.Key, out int _))) + foreach (VToken vToken in depots.Where( + p => p is VProperty property && int.TryParse(property.Key, out int _))) { VProperty property = (VProperty)vToken; if (int.TryParse(property.Value.GetChild("dlcappid")?.ToString(), out int appId) && appId > 0) _ = dlcIds.Add("" + appId); } + return dlcIds; }); diff --git a/CreamInstaller/Platforms/Steam/SteamLibrary.cs b/CreamInstaller/Platforms/Steam/SteamLibrary.cs index f26469d..c87e0af 100644 --- a/CreamInstaller/Platforms/Steam/SteamLibrary.cs +++ b/CreamInstaller/Platforms/Steam/SteamLibrary.cs @@ -16,12 +16,14 @@ internal static class SteamLibrary get { installPath ??= Registry.GetValue(@"HKEY_CURRENT_USER\Software\Valve\Steam", "SteamPath", null) as string; - installPath ??= Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Valve\Steam", "InstallPath", null) as string; + installPath ??= + Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Valve\Steam", "InstallPath", null) as string; return installPath.ResolvePath(); } } - internal static async Task> GetGames() + internal static async Task> + GetGames() => await Task.Run(async () => { List<(string appId, string name, string branch, int buildId, string gameDirectory)> games = new(); @@ -30,10 +32,12 @@ 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))) + 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))) games.Add(game); } + return games; }); @@ -54,11 +58,13 @@ internal static class SteamLibrary string installdir = result.Value.GetChild("installdir")?.ToString(); string name = result.Value.GetChild("name")?.ToString(); string buildId = result.Value.GetChild("buildid")?.ToString(); - if (string.IsNullOrWhiteSpace(appId) || string.IsNullOrWhiteSpace(installdir) || string.IsNullOrWhiteSpace(name) - || string.IsNullOrWhiteSpace(buildId)) + if (string.IsNullOrWhiteSpace(appId) || string.IsNullOrWhiteSpace(installdir) || + string.IsNullOrWhiteSpace(name) + || string.IsNullOrWhiteSpace(buildId)) continue; string gameDirectory = (libraryDirectory + @"\common\" + installdir).ResolvePath(); - if (gameDirectory is null || !int.TryParse(appId, out int _) || !int.TryParse(buildId, out int buildIdInt) || games.Any(g => g.appId == appId)) + if (gameDirectory is null || !int.TryParse(appId, out int _) || + !int.TryParse(buildId, out int buildIdInt) || games.Any(g => g.appId == appId)) continue; VToken userConfig = result.Value.GetChild("UserConfig"); string branch = userConfig?.GetChild("BetaKey")?.ToString(); @@ -69,10 +75,12 @@ internal static class SteamLibrary branch = mountedConfig?.GetChild("BetaKey")?.ToString(); branch ??= mountedConfig?.GetChild("betakey")?.ToString(); } + if (string.IsNullOrWhiteSpace(branch)) branch = "public"; games.Add((appId, name, branch, buildIdInt, gameDirectory)); } + return games; }); @@ -90,9 +98,11 @@ internal static class SteamLibrary return libraryDirectories; _ = libraryDirectories.Add(libraryFolder); string libraryFolders = libraryFolder + @"\libraryfolders.vdf"; - if (!libraryFolders.FileExists() || !ValveDataFile.TryDeserialize(libraryFolders.ReadFile(), out VProperty result)) + if (!libraryFolders.FileExists() || + !ValveDataFile.TryDeserialize(libraryFolders.ReadFile(), out VProperty result)) return libraryDirectories; - foreach (VToken vToken in result.Value.Where(p => p is VProperty property && int.TryParse(property.Key, out int _))) + foreach (VToken vToken in result.Value.Where(p => + p is VProperty property && int.TryParse(property.Key, out int _))) { VProperty property = (VProperty)vToken; string path = property.Value.GetChild("path")?.ToString(); @@ -102,6 +112,7 @@ internal static class SteamLibrary if (path.DirectoryExists()) _ = libraryDirectories.Add(path); } + return libraryDirectories; }); } \ No newline at end of file diff --git a/CreamInstaller/Platforms/Steam/SteamStore.cs b/CreamInstaller/Platforms/Steam/SteamStore.cs index b695ba3..2f00978 100644 --- a/CreamInstaller/Platforms/Steam/SteamStore.cs +++ b/CreamInstaller/Platforms/Steam/SteamStore.cs @@ -24,7 +24,9 @@ internal static class SteamStore HashSet dlcIds = new(); if (appData.DLC is null) return dlcIds; - foreach (string dlcId in from appId in appData.DLC where appId > 0 select appId.ToString(CultureInfo.InvariantCulture)) + foreach (string dlcId in from appId in appData.DLC + where appId > 0 + select appId.ToString(CultureInfo.InvariantCulture)) _ = dlcIds.Add(dlcId); return dlcIds; }); @@ -38,10 +40,12 @@ internal static class SteamStore bool cachedExists = cacheFile.FileExists(); if (!cachedExists || ProgramData.CheckCooldown(appId, isDlc ? CooldownDlc : CooldownGame)) { - string response = await HttpClientManager.EnsureGet($"https://store.steampowered.com/api/appdetails?appids={appId}"); + string response = + await HttpClientManager.EnsureGet($"https://store.steampowered.com/api/appdetails?appids={appId}"); if (response is not null) { - Dictionary apps = JsonConvert.DeserializeObject>(response); + Dictionary apps = + JsonConvert.DeserializeObject>(response); if (apps is not null) foreach (KeyValuePair app in apps) try @@ -54,12 +58,15 @@ internal static class SteamStore { #if DEBUG DebugForm.Current.Log( - "Steam store query failed on attempt #" + attempts + " for " + appId + (isDlc ? " (DLC)" : "") - + ": Query unsuccessful (" + app.Value.ToString(Formatting.None) + ")", LogTextBox.Warning); + "Steam store query failed on attempt #" + attempts + " for " + appId + + (isDlc ? " (DLC)" : "") + + ": Query unsuccessful (" + app.Value.ToString(Formatting.None) + ")", + LogTextBox.Warning); #endif if (data is null) return null; } + if (data is not null) { try @@ -70,8 +77,9 @@ internal static class SteamStore #if DEBUG (Exception e) { - DebugForm.Current.Log("Steam store query failed on attempt #" + attempts + " for " + appId + (isDlc ? " (DLC)" : "") - + ": Unsuccessful serialization (" + e.Message + ")"); + DebugForm.Current.Log("Steam store query failed on attempt #" + attempts + + " for " + appId + (isDlc ? " (DLC)" : "") + + ": Unsuccessful serialization (" + e.Message + ")"); } #else { @@ -81,22 +89,27 @@ internal static class SteamStore return data; } #if DEBUG - DebugForm.Current.Log("Steam store query failed on attempt #" + attempts + " for " + appId + (isDlc ? " (DLC)" : "") - + ": Response data null (" + app.Value.ToString(Formatting.None) + ")"); + DebugForm.Current.Log("Steam store query failed on attempt #" + attempts + " for " + + appId + (isDlc ? " (DLC)" : "") + + ": 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) + ")"); + DebugForm.Current.Log("Steam store query failed on attempt #" + attempts + " for " + + appId + (isDlc ? " (DLC)" : "") + + ": Response details null (" + + app.Value.ToString(Formatting.None) + ")"); #endif } catch #if DEBUG (Exception e) { - DebugForm.Current.Log("Steam store query failed on attempt #" + attempts + " for " + appId + (isDlc ? " (DLC)" : "") - + ": Unsuccessful deserialization (" + e.Message + ")"); + DebugForm.Current.Log("Steam store query failed on attempt #" + attempts + " for " + + appId + (isDlc ? " (DLC)" : "") + + ": Unsuccessful deserialization (" + e.Message + ")"); } #else { @@ -105,18 +118,22 @@ internal static class SteamStore #endif #if DEBUG else - DebugForm.Current.Log("Steam store query failed on attempt #" + attempts + " for " + appId + (isDlc ? " (DLC)" : "") - + ": Response deserialization null"); + DebugForm.Current.Log("Steam store query failed on attempt #" + attempts + " for " + appId + + (isDlc ? " (DLC)" : "") + + ": Response deserialization null"); #endif } else { #if DEBUG - DebugForm.Current.Log("Steam store query failed on attempt #" + attempts + " for " + appId + (isDlc ? " (DLC)" : "") + ": Response null", + DebugForm.Current.Log( + "Steam store query failed on attempt #" + attempts + " for " + appId + (isDlc ? " (DLC)" : "") + + ": Response null", LogTextBox.Warning); #endif } } + if (cachedExists) try { @@ -126,6 +143,7 @@ internal static class SteamStore { cacheFile.DeleteFile(); } + if (isDlc) break; if (attempts > 10) @@ -135,8 +153,10 @@ internal static class SteamStore #endif break; } + Thread.Sleep(1000); } + return null; } } \ No newline at end of file diff --git a/CreamInstaller/Platforms/Steam/ValveDataFile.cs b/CreamInstaller/Platforms/Steam/ValveDataFile.cs index d980102..51b9f57 100644 --- a/CreamInstaller/Platforms/Steam/ValveDataFile.cs +++ b/CreamInstaller/Platforms/Steam/ValveDataFile.cs @@ -17,6 +17,7 @@ internal static class ValveDataFile { // ignored } + return false; } @@ -30,6 +31,7 @@ internal static class ValveDataFile { // ignored } + return null; } } \ No newline at end of file diff --git a/CreamInstaller/Platforms/Ubisoft/UbisoftLibrary.cs b/CreamInstaller/Platforms/Ubisoft/UbisoftLibrary.cs index 1189274..23216fe 100644 --- a/CreamInstaller/Platforms/Ubisoft/UbisoftLibrary.cs +++ b/CreamInstaller/Platforms/Ubisoft/UbisoftLibrary.cs @@ -34,6 +34,7 @@ internal static class UbisoftLibrary if (installDir is not null && games.All(g => g.gameId != gameId)) games.Add((gameId, new DirectoryInfo(installDir).Name, installDir)); } + return games; }); } \ No newline at end of file diff --git a/CreamInstaller/Program.cs b/CreamInstaller/Program.cs index cb3474f..ecd25b9 100644 --- a/CreamInstaller/Program.cs +++ b/CreamInstaller/Program.cs @@ -13,7 +13,11 @@ internal static class Program { internal static readonly string Name = Application.CompanyName; private static readonly string Description = Application.ProductName; - internal static readonly string Version = Application.ProductVersion[..(Application.ProductVersion.IndexOf('+') is var index && index != -1 ? index : Application.ProductVersion.Length)]; + + internal static readonly string Version = Application.ProductVersion[ + ..(Application.ProductVersion.IndexOf('+') is var index && index != -1 + ? index + : Application.ProductVersion.Length)]; internal const string RepositoryOwner = "pointfeev"; internal static readonly string RepositoryName = Name; @@ -37,8 +41,9 @@ internal static class Program internal static readonly string[] ProtectedGameDirectoryExceptions = []; 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())); + => BlockProtectedGames && (ProtectedGames.Contains(name) || directory is not null && + !ProtectedGameDirectoryExceptions.Contains(name) + && ProtectedGameDirectories.Any(path => (directory + path).DirectoryExists())); [STAThread] private static void Main() @@ -52,8 +57,9 @@ internal static class Program Application.ApplicationExit += OnApplicationExit; Application.ThreadException += (_, e) => e.Exception.HandleFatalException(); Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); - AppDomain.CurrentDomain.UnhandledException += (_, e) => (e.ExceptionObject as Exception)?.HandleFatalException(); - retry: + AppDomain.CurrentDomain.UnhandledException += + (_, e) => (e.ExceptionObject as Exception)?.HandleFatalException(); + retry: try { HttpClientManager.Setup(); @@ -71,6 +77,7 @@ internal static class Program return; } } + mutex.Close(); } diff --git a/CreamInstaller/Resources/Koaloader.cs b/CreamInstaller/Resources/Koaloader.cs index f561109..0ec04ec 100644 --- a/CreamInstaller/Resources/Koaloader.cs +++ b/CreamInstaller/Resources/Koaloader.cs @@ -15,11 +15,16 @@ internal static class Koaloader { internal static readonly List<(string unlocker, string dll)> AutoLoadDLLs = [ - ("Koaloader", "Unlocker.dll"), ("Koaloader", "Unlocker32.dll"), ("Koaloader", "Unlocker64.dll"), ("Lyptus", "Lyptus.dll"), - ("Lyptus", "Lyptus32.dll"), ("Lyptus", "Lyptus64.dll"), ("SmokeAPI", "SmokeAPI.dll"), ("SmokeAPI", "SmokeAPI32.dll"), - ("SmokeAPI", "SmokeAPI64.dll"), ("ScreamAPI", "ScreamAPI.dll"), ("ScreamAPI", "ScreamAPI32.dll"), ("ScreamAPI", "ScreamAPI64.dll"), - ("Uplay R1 Unlocker", "UplayR1Unlocker.dll"), ("Uplay R1 Unlocker", "UplayR1Unlocker32.dll"), ("Uplay R1 Unlocker", "UplayR1Unlocker64.dll"), - ("Uplay R2 Unlocker", "UplayR2Unlocker.dll"), ("Uplay R2 Unlocker", "UplayR2Unlocker32.dll"), ("Uplay R2 Unlocker", "UplayR2Unlocker64.dll") + ("Koaloader", "Unlocker.dll"), ("Koaloader", "Unlocker32.dll"), ("Koaloader", "Unlocker64.dll"), + ("Lyptus", "Lyptus.dll"), + ("Lyptus", "Lyptus32.dll"), ("Lyptus", "Lyptus64.dll"), ("SmokeAPI", "SmokeAPI.dll"), + ("SmokeAPI", "SmokeAPI32.dll"), + ("SmokeAPI", "SmokeAPI64.dll"), ("ScreamAPI", "ScreamAPI.dll"), ("ScreamAPI", "ScreamAPI32.dll"), + ("ScreamAPI", "ScreamAPI64.dll"), + ("Uplay R1 Unlocker", "UplayR1Unlocker.dll"), ("Uplay R1 Unlocker", "UplayR1Unlocker32.dll"), + ("Uplay R1 Unlocker", "UplayR1Unlocker64.dll"), + ("Uplay R2 Unlocker", "UplayR2Unlocker.dll"), ("Uplay R2 Unlocker", "UplayR2Unlocker32.dll"), + ("Uplay R2 Unlocker", "UplayR2Unlocker64.dll") ]; internal static IEnumerable GetKoaloaderProxies(this string directory) @@ -38,7 +43,8 @@ internal static class Koaloader private static void WriteProxy(this string path, string proxyName, BinaryType binaryType) { - foreach (string resourceIdentifier in EmbeddedResources.Where(r => r.StartsWith("Koaloader", StringComparison.Ordinal))) + foreach (string resourceIdentifier in EmbeddedResources.Where(r => + r.StartsWith("Koaloader", StringComparison.Ordinal))) { resourceIdentifier.GetProxyInfoFromIdentifier(out string _proxyName, out BinaryType _binaryType); if (_proxyName != proxyName || _binaryType != binaryType) @@ -48,7 +54,8 @@ internal static class Koaloader } } - internal static void GetProxyInfoFromIdentifier(this string resourceIdentifier, out string proxyName, out BinaryType binaryType) + internal static void GetProxyInfoFromIdentifier(this string resourceIdentifier, out string proxyName, + out BinaryType binaryType) { string baseIdentifier = resourceIdentifier[(resourceIdentifier.IndexOf('.') + 1)..]; baseIdentifier = baseIdentifier[..baseIdentifier.IndexOf('.')]; @@ -65,14 +72,18 @@ internal static class Koaloader if (!config.FileExists()) { old_config.MoveFile(config!); - installForm?.UpdateUser($"Converted old configuration: {Path.GetFileName(old_config)} -> {Path.GetFileName(config)}", LogTextBox.Action, false); + installForm?.UpdateUser( + $"Converted old configuration: {Path.GetFileName(old_config)} -> {Path.GetFileName(config)}", + LogTextBox.Action, false); } else { old_config.DeleteFile(); - installForm?.UpdateUser($"Deleted old configuration: {Path.GetFileName(old_config)}", LogTextBox.Action, false); + installForm?.UpdateUser($"Deleted old configuration: {Path.GetFileName(old_config)}", LogTextBox.Action, + false); } } + SortedList targets = new(PlatformIdComparer.String); SortedList modules = new(PlatformIdComparer.String); if (targets.Count > 0 || modules.Count > 0) @@ -88,11 +99,13 @@ internal static class Koaloader else if (config.FileExists()) { config.DeleteFile(); - installForm?.UpdateUser($"Deleted unnecessary configuration: {Path.GetFileName(config)}", LogTextBox.Action, false); + installForm?.UpdateUser($"Deleted unnecessary configuration: {Path.GetFileName(config)}", LogTextBox.Action, + false); } } - private static void WriteConfig(StreamWriter writer, SortedList targets, SortedList modules, InstallForm installForm = null) + private static void WriteConfig(StreamWriter writer, SortedList targets, + SortedList modules, InstallForm installForm = null) { writer.WriteLine("{"); writer.WriteLine(" \"logging\": false,"); @@ -108,10 +121,12 @@ internal static class Koaloader writer.WriteLine($" \"{path}\"{(pair.Equals(lastTarget) ? "" : ",")}"); installForm?.UpdateUser($"Added target to Koaloader.json with path {path}", LogTextBox.Action, false); } + writer.WriteLine(" ]"); } else writer.WriteLine(" \"targets\": []"); + if (modules.Count > 0) { writer.WriteLine(" \"modules\": ["); @@ -125,39 +140,48 @@ internal static class Koaloader writer.WriteLine(" }" + (pair.Equals(lastModule) ? "" : ",")); installForm?.UpdateUser($"Added module to Koaloader.json with path {path}", LogTextBox.Action, false); } + writer.WriteLine(" ]"); } else writer.WriteLine(" \"modules\": []"); + writer.WriteLine("}"); } - internal static async Task Uninstall(string directory, string rootDirectory = null, InstallForm installForm = null, bool deleteConfig = true) + internal static async Task Uninstall(string directory, string rootDirectory = null, InstallForm installForm = null, + bool deleteConfig = true) => await Task.Run(async () => { 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(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())) + + foreach ((string unlocker, string path) in AutoLoadDLLs + .Select(pair => (pair.unlocker, path: directory + @"\" + pair.dll)) + .Where(pair => pair.path.FileExists() && pair.path.IsResourceFile())) { path.DeleteFile(true); installForm?.UpdateUser($"Deleted {unlocker}: {Path.GetFileName(path)}", LogTextBox.Action, false); } + if (deleteConfig && old_config.FileExists()) { old_config.DeleteFile(); - installForm?.UpdateUser($"Deleted configuration: {Path.GetFileName(old_config)}", LogTextBox.Action, false); + installForm?.UpdateUser($"Deleted configuration: {Path.GetFileName(old_config)}", LogTextBox.Action, + false); } + if (deleteConfig && config.FileExists()) { config.DeleteFile(); installForm?.UpdateUser($"Deleted configuration: {Path.GetFileName(config)}", LogTextBox.Action, false); } + await SmokeAPI.Uninstall(directory, installForm, deleteConfig); await ScreamAPI.Uninstall(directory, installForm, deleteConfig); await UplayR1.Uninstall(directory, installForm, deleteConfig); @@ -166,21 +190,27 @@ internal static class Koaloader await Uninstall(rootDirectory, null, installForm, deleteConfig); }); - internal static async Task Install(string directory, BinaryType binaryType, Selection selection, string rootDirectory = null, + internal static async Task Install(string directory, BinaryType binaryType, Selection selection, + string rootDirectory = null, InstallForm installForm = null, bool generateConfig = true) => await Task.Run(() => { string proxy = selection.KoaloaderProxy ?? Selection.DefaultKoaloaderProxy; string path = directory + @"\" + proxy + ".dll"; - foreach (string _path in directory.GetKoaloaderProxies().Where(p => p != path && p.FileExists() && p.IsResourceFile(ResourceIdentifier.Koaloader))) + foreach (string _path in directory.GetKoaloaderProxies().Where(p => + p != path && p.FileExists() && p.IsResourceFile(ResourceIdentifier.Koaloader))) { _path.DeleteFile(true); installForm?.UpdateUser($"Deleted Koaloader: {Path.GetFileName(_path)}", LogTextBox.Action, false); } + if (path.FileExists() && !path.IsResourceFile(ResourceIdentifier.Koaloader)) - throw new CustomMessageException("A non-Koaloader DLL named " + proxy + ".dll already exists in this directory!"); + throw new CustomMessageException("A non-Koaloader DLL named " + proxy + + ".dll already exists in this directory!"); path.WriteProxy(proxy, binaryType); - installForm?.UpdateUser($"Wrote {(binaryType == BinaryType.BIT32 ? "32-bit" : "64-bit")} Koaloader: {Path.GetFileName(path)}", LogTextBox.Action, + installForm?.UpdateUser( + $"Wrote {(binaryType == BinaryType.BIT32 ? "32-bit" : "64-bit")} Koaloader: {Path.GetFileName(path)}", + LogTextBox.Action, false); bool bit32 = false, bit64 = false; foreach (string executable in directory.EnumerateDirectory("*.exe")) @@ -195,9 +225,11 @@ internal static class Koaloader bit64 = true; break; } + if (bit32 && bit64) break; } + if (selection.Platform is Platform.Steam or Platform.Paradox) { if (bit32) @@ -208,15 +240,20 @@ internal static class Koaloader if (path.FileExists()) { path.DeleteFile(); - installForm?.UpdateUser($"Deleted SmokeAPI from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, false); + installForm?.UpdateUser( + $"Deleted SmokeAPI from non-root directory: {Path.GetFileName(path)}", + LogTextBox.Action, false); } + path = rootDirectory + @"\SmokeAPI32.dll"; } + "SmokeAPI.steam_api.dll".WriteManifestResource(path); installForm?.UpdateUser( $"Wrote SmokeAPI{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}", LogTextBox.Action, false); } + if (bit64) { path = directory + @"\SmokeAPI64.dll"; @@ -225,17 +262,23 @@ internal static class Koaloader if (path.FileExists()) { path.DeleteFile(); - installForm?.UpdateUser($"Deleted SmokeAPI from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, false); + installForm?.UpdateUser( + $"Deleted SmokeAPI from non-root directory: {Path.GetFileName(path)}", + LogTextBox.Action, false); } + path = rootDirectory + @"\SmokeAPI64.dll"; } + "SmokeAPI.steam_api64.dll".WriteManifestResource(path); installForm?.UpdateUser( $"Wrote SmokeAPI{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}", LogTextBox.Action, false); } + SmokeAPI.CheckConfig(rootDirectory ?? directory, selection, installForm); } + switch (selection.Platform) { case Platform.Epic or Platform.Paradox: @@ -248,15 +291,20 @@ internal static class Koaloader if (path.FileExists()) { path.DeleteFile(); - installForm?.UpdateUser($"Deleted ScreamAPI from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, false); + installForm?.UpdateUser( + $"Deleted ScreamAPI from non-root directory: {Path.GetFileName(path)}", + LogTextBox.Action, false); } + path = rootDirectory + @"\ScreamAPI32.dll"; } + "ScreamAPI.EOSSDK-Win32-Shipping.dll".WriteManifestResource(path); installForm?.UpdateUser( $"Wrote ScreamAPI{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}", LogTextBox.Action, false); } + if (bit64) { path = directory + @"\ScreamAPI64.dll"; @@ -265,15 +313,20 @@ internal static class Koaloader if (path.FileExists()) { path.DeleteFile(); - installForm?.UpdateUser($"Deleted ScreamAPI from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, false); + installForm?.UpdateUser( + $"Deleted ScreamAPI from non-root directory: {Path.GetFileName(path)}", + LogTextBox.Action, false); } + path = rootDirectory + @"\ScreamAPI64.dll"; } + "ScreamAPI.EOSSDK-Win64-Shipping.dll".WriteManifestResource(path); installForm?.UpdateUser( $"Wrote ScreamAPI{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}", LogTextBox.Action, false); } + ScreamAPI.CheckConfig(rootDirectory ?? directory, selection, installForm); break; } @@ -287,16 +340,21 @@ internal static class Koaloader if (path.FileExists()) { path.DeleteFile(); - installForm?.UpdateUser($"Deleted Uplay R1 Unlocker from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, + installForm?.UpdateUser( + $"Deleted Uplay R1 Unlocker from non-root directory: {Path.GetFileName(path)}", + LogTextBox.Action, false); } + path = rootDirectory + @"\UplayR1Unlocker32.dll"; } + "UplayR1.uplay_r1_loader.dll".WriteManifestResource(path); installForm?.UpdateUser( $"Wrote Uplay R1 Unlocker{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}", LogTextBox.Action, false); } + if (bit64) { path = directory + @"\UplayR1Unlocker64.dll"; @@ -305,16 +363,21 @@ internal static class Koaloader if (path.FileExists()) { path.DeleteFile(); - installForm?.UpdateUser($"Deleted Uplay R1 Unlocker from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, + installForm?.UpdateUser( + $"Deleted Uplay R1 Unlocker from non-root directory: {Path.GetFileName(path)}", + LogTextBox.Action, false); } + path = rootDirectory + @"\UplayR1Unlocker64.dll"; } + "UplayR1.uplay_r1_loader64.dll".WriteManifestResource(path); installForm?.UpdateUser( $"Wrote Uplay R1 Unlocker{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}", LogTextBox.Action, false); } + UplayR1.CheckConfig(rootDirectory ?? directory, selection, installForm); if (bit32) { @@ -324,16 +387,21 @@ internal static class Koaloader if (path.FileExists()) { path.DeleteFile(); - installForm?.UpdateUser($"Deleted Uplay R2 Unlocker from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, + installForm?.UpdateUser( + $"Deleted Uplay R2 Unlocker from non-root directory: {Path.GetFileName(path)}", + LogTextBox.Action, false); } + path = rootDirectory + @"\UplayR2Unlocker32.dll"; } + "UplayR2.upc_r2_loader.dll".WriteManifestResource(path); installForm?.UpdateUser( $"Wrote Uplay R2 Unlocker{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}", LogTextBox.Action, false); } + if (bit64) { path = directory + @"\UplayR2Unlocker64.dll"; @@ -342,20 +410,26 @@ internal static class Koaloader if (path.FileExists()) { path.DeleteFile(); - installForm?.UpdateUser($"Deleted Uplay R2 Unlocker from non-root directory: {Path.GetFileName(path)}", LogTextBox.Action, + installForm?.UpdateUser( + $"Deleted Uplay R2 Unlocker from non-root directory: {Path.GetFileName(path)}", + LogTextBox.Action, false); } + path = rootDirectory + @"\UplayR2Unlocker64.dll"; } + "UplayR2.upc_r2_loader64.dll".WriteManifestResource(path); installForm?.UpdateUser( $"Wrote Uplay R2 Unlocker{(rootDirectory is not null && directory != rootDirectory ? " to root directory" : "")}: {Path.GetFileName(path)}", LogTextBox.Action, false); } + UplayR2.CheckConfig(rootDirectory ?? directory, selection, installForm); break; } } + if (generateConfig) CheckConfig(directory, installForm); }); diff --git a/CreamInstaller/Resources/Resources.cs b/CreamInstaller/Resources/Resources.cs index da33021..68233a4 100644 --- a/CreamInstaller/Resources/Resources.cs +++ b/CreamInstaller/Resources/Resources.cs @@ -432,7 +432,8 @@ internal static class Resources return embeddedResources; string[] names = Assembly.GetExecutingAssembly().GetManifestResourceNames(); embeddedResources = []; - foreach (string resourceName in names.Where(n => n.StartsWith("CreamInstaller.Resources.", StringComparison.Ordinal))) + foreach (string resourceName in names.Where(n => + n.StartsWith("CreamInstaller.Resources.", StringComparison.Ordinal))) _ = embeddedResources.Add(resourceName[25..]); return embeddedResources; } @@ -443,14 +444,16 @@ internal static class Resources while (!Program.Canceled) try { - using Stream resource = Assembly.GetExecutingAssembly().GetManifestResourceStream("CreamInstaller.Resources." + resourceIdentifier); + using Stream resource = Assembly.GetExecutingAssembly() + .GetManifestResourceStream("CreamInstaller.Resources." + resourceIdentifier); using FileStream file = new(filePath, FileMode.Create, FileAccess.Write); resource?.CopyTo(file); break; } catch (Exception e) { - if (filePath.IOWarn("Failed to write a crucial manifest resource (" + resourceIdentifier + ")", e) is not DialogResult.OK) + if (filePath.IOWarn("Failed to write a crucial manifest resource (" + resourceIdentifier + ")", e) is + not DialogResult.OK) break; } } @@ -469,22 +472,26 @@ internal static class Resources 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); + internal static bool TryGetFileBinaryType(this string path, out BinaryType binaryType) => + NativeImports.GetBinaryType(path, out binaryType); - internal static async Task> GetExecutableDirectories(this string rootDirectory, bool filterCommon = false, + internal static async Task> GetExecutableDirectories( + this string rootDirectory, bool filterCommon = false, Func validFunc = null) => await Task.Run(async () => (await rootDirectory.GetExecutables(filterCommon, validFunc) - ?? (filterCommon || validFunc is not null ? await rootDirectory.GetExecutables() : null))?.Select(e => + ?? (filterCommon || validFunc is not null ? await rootDirectory.GetExecutables() : null))?.Select(e => { e.path = Path.GetDirectoryName(e.path); return e; }).DistinctBy(e => e.path).ToList() ?? []); - internal static async Task> GetExecutables(this string rootDirectory, bool filterCommon = false, + internal static async Task> GetExecutables(this string rootDirectory, + bool filterCommon = false, Func validFunc = null) => await Task.Run(() => { @@ -495,18 +502,26 @@ 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) + 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) executables.Add((path, binaryType)); } + foreach (string path in rootDirectory.EnumerateDirectory("*.exe", true)) { 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) + 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) executables.Add((path, binaryType)); } + return executables.Count > 0 ? executables : null; }); @@ -514,12 +529,16 @@ 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) + internal static async Task> GetDllDirectoriesFromGameDirectory(this string gameDirectory, + Platform platform) => await Task.Run(() => { HashSet dllDirectories = []; @@ -532,43 +551,54 @@ internal static class Resources string subDirectory = directory.ResolvePath(); if (subDirectory is null || dllDirectories.Contains(subDirectory)) continue; - bool koaloaderInstalled = Koaloader.AutoLoadDLLs.Select(pair => (pair.unlocker, path: directory + @"\" + pair.dll)) - .Any(pair => pair.path.FileExists() && pair.path.IsResourceFile()); + bool koaloaderInstalled = Koaloader.AutoLoadDLLs + .Select(pair => (pair.unlocker, path: directory + @"\" + pair.dll)) + .Any(pair => pair.path.FileExists() && pair.path.IsResourceFile()); if (platform is Platform.Steam or Platform.Paradox) { - subDirectory.GetSmokeApiComponents(out string api, out string api_o, out string api64, out string api64_o, out string old_config, + subDirectory.GetSmokeApiComponents(out string api, out string api_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 (api.FileExists() || api_o.FileExists() || api64.FileExists() || api64_o.FileExists() - || (old_config.FileExists() || config.FileExists() || old_log.FileExists() || log.FileExists() || cache.FileExists()) - && !koaloaderInstalled) + || (old_config.FileExists() || config.FileExists() || old_log.FileExists() || + log.FileExists() || cache.FileExists()) + && !koaloaderInstalled) _ = dllDirectories.Add(subDirectory); } + if (platform is Platform.Epic or Platform.Paradox) { - subDirectory.GetScreamApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config, + subDirectory.GetScreamApiComponents(out string api32, out string api32_o, out string api64, + out string api64_o, out string config, out string log); if (api32.FileExists() || api32_o.FileExists() || api64.FileExists() || api64_o.FileExists() - || (config.FileExists() || log.FileExists()) && !koaloaderInstalled) + || (config.FileExists() || log.FileExists()) && !koaloaderInstalled) _ = dllDirectories.Add(subDirectory); } + if (platform is Platform.Ubisoft) { - subDirectory.GetUplayR1Components(out string api32, out string api32_o, out string api64, out string api64_o, out string config, + subDirectory.GetUplayR1Components(out string api32, out string api32_o, out string api64, + out string api64_o, out string config, out string log); if (api32.FileExists() || api32_o.FileExists() || api64.FileExists() || api64_o.FileExists() - || (config.FileExists() || log.FileExists()) && !koaloaderInstalled) + || (config.FileExists() || log.FileExists()) && !koaloaderInstalled) _ = dllDirectories.Add(subDirectory); - subDirectory.GetUplayR2Components(out string old_api32, out string old_api64, out api32, out api32_o, out api64, out api64_o, out config, + subDirectory.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.FileExists() || old_api64.FileExists() || api32.FileExists() || api32_o.FileExists() || api64.FileExists() - || api64_o.FileExists() || (config.FileExists() || log.FileExists()) && !koaloaderInstalled) + if (old_api32.FileExists() || old_api64.FileExists() || api32.FileExists() || + api32_o.FileExists() || api64.FileExists() + || api64_o.FileExists() || (config.FileExists() || log.FileExists()) && !koaloaderInstalled) _ = dllDirectories.Add(subDirectory); } } + return dllDirectories.Count > 0 ? dllDirectories : null; }); - internal static void GetCreamApiComponents(this string directory, out string api32, out string api32_o, out string api64, out string api64_o, + internal static void GetCreamApiComponents(this string directory, out string api32, out string api32_o, + out string api64, out string api64_o, out string config) { api32 = directory + @"\steam_api.dll"; @@ -588,14 +618,27 @@ internal static class Resources internal static bool IsResourceFile(this string filePath, ResourceIdentifier identifier) => filePath.ComputeMD5() is { } hash && ResourceMD5s[identifier].Contains(hash); - internal static bool IsResourceFile(this string filePath) => filePath.ComputeMD5() is { } hash && ResourceMD5s.Values.Any(hashes => hashes.Contains(hash)); + internal static bool IsResourceFile(this string filePath) => filePath.ComputeMD5() is { } hash && + ResourceMD5s.Values.Any( + hashes => hashes.Contains(hash)); - internal enum BinaryType { Unknown = -1, BIT32 = 0, BIT64 = 6 } + internal enum BinaryType + { + Unknown = -1, + BIT32 = 0, + BIT64 = 6 + } internal enum ResourceIdentifier { - Koaloader, Steamworks32, Steamworks64, - EpicOnlineServices32, EpicOnlineServices64, Uplay32, - Uplay64, Upc32, Upc64 + Koaloader, + Steamworks32, + Steamworks64, + EpicOnlineServices32, + EpicOnlineServices64, + Uplay32, + Uplay64, + Upc32, + Upc64 } } \ No newline at end of file diff --git a/CreamInstaller/Resources/ScreamAPI.cs b/CreamInstaller/Resources/ScreamAPI.cs index e1651c6..866c57e 100644 --- a/CreamInstaller/Resources/ScreamAPI.cs +++ b/CreamInstaller/Resources/ScreamAPI.cs @@ -11,7 +11,8 @@ namespace CreamInstaller.Resources; internal static class ScreamAPI { - internal static void GetScreamApiComponents(this string directory, out string api32, out string api32_o, out string api64, out string api64_o, + internal static void GetScreamApiComponents(this string directory, out string api32, out string api32_o, + out string api64, out string api64_o, out string config, out string log) { api32 = directory + @"\EOSSDK-Win32-Shipping.dll"; @@ -25,7 +26,8 @@ internal static class ScreamAPI internal static void CheckConfig(string directory, Selection selection, InstallForm installForm = null) { directory.GetScreamApiComponents(out _, out _, out _, out _, out string config, out _); - HashSet overrideCatalogItems = selection.DLC.Where(dlc => dlc.Type is DLCType.Epic && !dlc.Enabled).ToHashSet(); + HashSet overrideCatalogItems = + selection.DLC.Where(dlc => dlc.Type is DLCType.Epic && !dlc.Enabled).ToHashSet(); int entitlementCount = 0; HashSet injectedEntitlements = []; foreach (SelectionDLC dlc in selection.DLC.Where(dlc => dlc.Type is DLCType.EpicEntitlement)) @@ -34,6 +36,7 @@ internal static class ScreamAPI _ = injectedEntitlements.Add(dlc); entitlementCount++; } + foreach (Selection extraSelection in selection.ExtraSelections) { foreach (SelectionDLC extraDlc in extraSelection.DLC.Where(dlc => dlc.Type is DLCType.Epic && !dlc.Enabled)) @@ -45,6 +48,7 @@ internal static class ScreamAPI entitlementCount++; } } + if (injectedEntitlements.Count == entitlementCount) injectedEntitlements.Clear(); if (overrideCatalogItems.Count > 0 || injectedEntitlements.Count > 0) @@ -53,15 +57,18 @@ internal static class ScreamAPI installForm.UpdateUser("Generating ScreamAPI configuration for " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);*/ config.CreateFile(true, installForm)?.Close(); StreamWriter writer = new(config, true, Encoding.UTF8); - WriteConfig(writer, new(overrideCatalogItems.ToDictionary(dlc => dlc.Id, dlc => dlc), PlatformIdComparer.String), - new(injectedEntitlements.ToDictionary(dlc => dlc.Id, dlc => dlc), PlatformIdComparer.String), installForm); + WriteConfig(writer, + new(overrideCatalogItems.ToDictionary(dlc => dlc.Id, dlc => dlc), PlatformIdComparer.String), + new(injectedEntitlements.ToDictionary(dlc => dlc.Id, dlc => dlc), PlatformIdComparer.String), + installForm); writer.Flush(); writer.Close(); } else if (config.FileExists()) { config.DeleteFile(); - installForm?.UpdateUser($"Deleted unnecessary configuration: {Path.GetFileName(config)}", LogTextBox.Action, false); + installForm?.UpdateUser($"Deleted unnecessary configuration: {Path.GetFileName(config)}", LogTextBox.Action, + false); } } @@ -83,13 +90,17 @@ internal static class ScreamAPI { SelectionDLC selectionDlc = pair.Value; writer.WriteLine($" \"{selectionDlc.Id}\"{(pair.Equals(lastOverrideCatalogItem) ? "" : ",")}"); - installForm?.UpdateUser($"Added locked catalog item to ScreamAPI.json with id {selectionDlc.Id} ({selectionDlc.Name})", LogTextBox.Action, + installForm?.UpdateUser( + $"Added locked catalog item to ScreamAPI.json with id {selectionDlc.Id} ({selectionDlc.Name})", + LogTextBox.Action, false); } + writer.WriteLine(" ]"); } else writer.WriteLine(" \"override\": []"); + writer.WriteLine(" },"); writer.WriteLine(" \"entitlements\": {"); if (injectedEntitlements.Count > 0) @@ -102,9 +113,12 @@ internal static class ScreamAPI { SelectionDLC selectionDlc = pair.Value; writer.WriteLine($" \"{selectionDlc.Id}\"{(pair.Equals(lastEntitlement) ? "" : ",")}"); - installForm?.UpdateUser($"Added injected entitlement to ScreamAPI.json with id {selectionDlc.Id} ({selectionDlc.Name})", LogTextBox.Action, + installForm?.UpdateUser( + $"Added injected entitlement to ScreamAPI.json with id {selectionDlc.Id} ({selectionDlc.Name})", + LogTextBox.Action, false); } + writer.WriteLine(" ]"); } else @@ -113,6 +127,7 @@ internal static class ScreamAPI writer.WriteLine(" \"auto_inject\": true,"); writer.WriteLine(" \"inject\": []"); } + writer.WriteLine(" }"); writer.WriteLine("}"); } @@ -120,7 +135,8 @@ internal static class ScreamAPI internal static async Task Uninstall(string directory, InstallForm installForm = null, bool deleteOthers = true) => await Task.Run(() => { - directory.GetScreamApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config, out string log); + directory.GetScreamApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, + out string config, out string log); if (api32_o.FileExists()) { if (api32.FileExists()) @@ -128,9 +144,12 @@ internal static class ScreamAPI api32.DeleteFile(true); installForm?.UpdateUser($"Deleted ScreamAPI: {Path.GetFileName(api32)}", LogTextBox.Action, false); } + api32_o.MoveFile(api32!); - installForm?.UpdateUser($"Restored EOS: {Path.GetFileName(api32_o)} -> {Path.GetFileName(api32)}", LogTextBox.Action, false); + installForm?.UpdateUser($"Restored EOS: {Path.GetFileName(api32_o)} -> {Path.GetFileName(api32)}", + LogTextBox.Action, false); } + if (api64_o.FileExists()) { if (api64.FileExists()) @@ -138,9 +157,12 @@ internal static class ScreamAPI api64.DeleteFile(true); installForm?.UpdateUser($"Deleted ScreamAPI: {Path.GetFileName(api64)}", LogTextBox.Action, false); } + api64_o.MoveFile(api64!); - installForm?.UpdateUser($"Restored EOS: {Path.GetFileName(api64_o)} -> {Path.GetFileName(api64)}", LogTextBox.Action, false); + installForm?.UpdateUser($"Restored EOS: {Path.GetFileName(api64_o)} -> {Path.GetFileName(api64)}", + LogTextBox.Action, false); } + if (!deleteOthers) return; if (config.FileExists()) @@ -148,6 +170,7 @@ internal static class ScreamAPI config.DeleteFile(); installForm?.UpdateUser($"Deleted configuration: {Path.GetFileName(config)}", LogTextBox.Action, false); } + if (log.FileExists()) { log.DeleteFile(); @@ -155,30 +178,38 @@ internal static class ScreamAPI } }); - internal static async Task Install(string directory, Selection selection, InstallForm installForm = null, bool generateConfig = true) + internal static async Task Install(string directory, Selection selection, InstallForm installForm = null, + bool generateConfig = true) => await Task.Run(() => { - directory.GetScreamApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out _, out _); + 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!, true); - installForm?.UpdateUser($"Renamed EOS: {Path.GetFileName(api32)} -> {Path.GetFileName(api32_o)}", LogTextBox.Action, false); + installForm?.UpdateUser($"Renamed EOS: {Path.GetFileName(api32)} -> {Path.GetFileName(api32_o)}", + LogTextBox.Action, false); } + if (api32_o.FileExists()) { "ScreamAPI.EOSSDK-Win32-Shipping.dll".WriteManifestResource(api32); installForm?.UpdateUser($"Wrote ScreamAPI: {Path.GetFileName(api32)}", LogTextBox.Action, false); } + if (api64.FileExists() && !api64_o.FileExists()) { api64.MoveFile(api64_o!, true); - installForm?.UpdateUser($"Renamed EOS: {Path.GetFileName(api64)} -> {Path.GetFileName(api64_o)}", LogTextBox.Action, false); + installForm?.UpdateUser($"Renamed EOS: {Path.GetFileName(api64)} -> {Path.GetFileName(api64_o)}", + LogTextBox.Action, false); } + if (api64_o.FileExists()) { "ScreamAPI.EOSSDK-Win64-Shipping.dll".WriteManifestResource(api64); installForm?.UpdateUser($"Wrote ScreamAPI: {Path.GetFileName(api64)}", LogTextBox.Action, false); } + if (generateConfig) CheckConfig(directory, selection, installForm); }); diff --git a/CreamInstaller/Resources/SmokeAPI.cs b/CreamInstaller/Resources/SmokeAPI.cs index 556585c..03633be 100644 --- a/CreamInstaller/Resources/SmokeAPI.cs +++ b/CreamInstaller/Resources/SmokeAPI.cs @@ -11,7 +11,8 @@ namespace CreamInstaller.Resources; internal static class SmokeAPI { - internal static void GetSmokeApiComponents(this string directory, out string api32, out string api32_o, out string api64, out string api64_o, + internal static void GetSmokeApiComponents(this string directory, 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) { api32 = directory + @"\steam_api.dll"; @@ -27,35 +28,46 @@ internal static class SmokeAPI internal static void CheckConfig(string directory, Selection selection, InstallForm installForm = null) { - directory.GetSmokeApiComponents(out _, out _, out _, out _, out string old_config, out string config, out _, out _, out _); + directory.GetSmokeApiComponents(out _, out _, out _, out _, out string old_config, out string config, out _, + out _, out _); HashSet overrideDlc = selection.DLC.Where(dlc => !dlc.Enabled).ToHashSet(); - foreach (SelectionDLC extraDlc in selection.ExtraSelections.SelectMany(extraSelection => extraSelection.DLC.Where(dlc => !dlc.Enabled))) + foreach (SelectionDLC extraDlc in selection.ExtraSelections.SelectMany(extraSelection => + extraSelection.DLC.Where(dlc => !dlc.Enabled))) _ = overrideDlc.Add(extraDlc); HashSet injectDlc = []; if (selection.DLC.Count() > 64) - foreach (SelectionDLC hiddenDlc in selection.DLC.Where(dlc => dlc.Enabled && dlc.Type is DLCType.SteamHidden)) + foreach (SelectionDLC hiddenDlc in selection.DLC.Where( + dlc => dlc.Enabled && dlc.Type is DLCType.SteamHidden)) _ = injectDlc.Add(hiddenDlc); List injectDlc)>> extraApps = []; - foreach (Selection extraSelection in selection.ExtraSelections.Where(extraSelection => extraSelection.DLC.Count() > 64)) + foreach (Selection extraSelection in selection.ExtraSelections.Where(extraSelection => + extraSelection.DLC.Count() > 64)) { SortedList extraInjectDlc = new(PlatformIdComparer.String); - foreach (SelectionDLC extraDlc in extraSelection.DLC.Where(extraDlc => extraDlc.Enabled && extraDlc.Type is DLCType.SteamHidden)) + foreach (SelectionDLC extraDlc in extraSelection.DLC.Where(extraDlc => + extraDlc.Enabled && extraDlc.Type is DLCType.SteamHidden)) extraInjectDlc.Add(extraDlc.Id, extraDlc); if (extraInjectDlc.Count > 0) extraApps.Add(new(extraSelection.Id, (extraSelection.Name, extraInjectDlc))); } + if (old_config.FileExists()) { old_config.DeleteFile(); - installForm?.UpdateUser($"Deleted old configuration: {Path.GetFileName(old_config)}", LogTextBox.Action, false); + installForm?.UpdateUser($"Deleted old configuration: {Path.GetFileName(old_config)}", LogTextBox.Action, + false); } - if (selection.ExtraSelections.Any(extraSelection => extraSelection.DLC.Any()) || overrideDlc.Count > 0 || injectDlc.Count > 0) + + if (selection.ExtraSelections.Any(extraSelection => extraSelection.DLC.Any()) || overrideDlc.Count > 0 || + injectDlc.Count > 0) { /*if (installForm is not null) installForm.UpdateUser("Generating SmokeAPI configuration for " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);*/ config.CreateFile(true, installForm)?.Close(); StreamWriter writer = new(config, true, Encoding.UTF8); - WriteConfig(writer, selection.Id, new(extraApps.ToDictionary(extraApp => extraApp.Key, extraApp => extraApp.Value), PlatformIdComparer.String), + WriteConfig(writer, selection.Id, + new(extraApps.ToDictionary(extraApp => extraApp.Key, extraApp => extraApp.Value), + PlatformIdComparer.String), new(overrideDlc.ToDictionary(dlc => dlc.Id, dlc => dlc), PlatformIdComparer.String), new(injectDlc.ToDictionary(dlc => dlc.Id, dlc => dlc), PlatformIdComparer.String), installForm); writer.Flush(); @@ -64,12 +76,15 @@ internal static class SmokeAPI else if (config.FileExists()) { config.DeleteFile(); - installForm?.UpdateUser($"Deleted unnecessary configuration: {Path.GetFileName(config)}", LogTextBox.Action, false); + installForm?.UpdateUser($"Deleted unnecessary configuration: {Path.GetFileName(config)}", LogTextBox.Action, + false); } } - private static void WriteConfig(StreamWriter writer, string appId, SortedList injectDlc)> extraApps, - SortedList overrideDlc, SortedList injectDlc, InstallForm installForm = null) + private static void WriteConfig(StreamWriter writer, string appId, + SortedList injectDlc)> extraApps, + SortedList overrideDlc, SortedList injectDlc, + InstallForm installForm = null) { writer.WriteLine("{"); writer.WriteLine(" \"$version\": 2,"); @@ -85,13 +100,17 @@ internal static class SmokeAPI { SelectionDLC selectionDlc = pair.Value; writer.WriteLine($" \"{selectionDlc.Id}\": \"locked\"{(pair.Equals(lastOverrideDlc) ? "" : ",")}"); - installForm?.UpdateUser($"Added locked DLC to SmokeAPI.config.json with appid {selectionDlc.Id} ({selectionDlc.Name})", LogTextBox.Action, + installForm?.UpdateUser( + $"Added locked DLC to SmokeAPI.config.json with appid {selectionDlc.Id} ({selectionDlc.Name})", + LogTextBox.Action, false); } + writer.WriteLine(" },"); } else writer.WriteLine(" \"override_dlc_status\": {},"); + writer.WriteLine(" \"auto_inject_inventory\": true,"); writer.WriteLine(" \"extra_inventory_items\": [],"); if (injectDlc.Count > 0 || extraApps.Count > 0) @@ -105,17 +124,24 @@ internal static class SmokeAPI foreach (KeyValuePair pair in injectDlc) { SelectionDLC selectionDlc = pair.Value; - writer.WriteLine($" \"{selectionDlc.Id}\": \"{selectionDlc.Name}\"{(pair.Equals(lastInjectDlc) ? "" : ",")}"); - installForm?.UpdateUser($"Added extra DLC to SmokeAPI.config.json with appid {selectionDlc.Id} ({selectionDlc.Name})", LogTextBox.Action, + writer.WriteLine( + $" \"{selectionDlc.Id}\": \"{selectionDlc.Name}\"{(pair.Equals(lastInjectDlc) ? "" : ",")}"); + installForm?.UpdateUser( + $"Added extra DLC to SmokeAPI.config.json with appid {selectionDlc.Id} ({selectionDlc.Name})", + LogTextBox.Action, false); } + writer.WriteLine(" }"); writer.WriteLine(extraApps.Count > 0 ? " }," : " }"); } + if (extraApps.Count > 0) { - KeyValuePair injectDlc)> lastExtraApp = extraApps.Last(); - foreach (KeyValuePair injectDlc)> pair in extraApps) + KeyValuePair injectDlc)> lastExtraApp = + extraApps.Last(); + foreach (KeyValuePair injectDlc)> pair in + extraApps) { string extraAppId = pair.Key; (string _ /*extraAppName*/, SortedList extraInjectDlc) = pair.Value; @@ -126,18 +152,23 @@ internal static class SmokeAPI foreach (KeyValuePair extraPair in extraInjectDlc) { SelectionDLC selectionDlc = extraPair.Value; - writer.WriteLine($" \"{selectionDlc.Id}\": \"{selectionDlc.Name}\"{(extraPair.Equals(lastExtraAppDlc) ? "" : ",")}"); - installForm?.UpdateUser($"Added extra DLC to SmokeAPI.config.json with appid {selectionDlc.Id} ({selectionDlc.Name})", + writer.WriteLine( + $" \"{selectionDlc.Id}\": \"{selectionDlc.Name}\"{(extraPair.Equals(lastExtraAppDlc) ? "" : ",")}"); + installForm?.UpdateUser( + $"Added extra DLC to SmokeAPI.config.json with appid {selectionDlc.Id} ({selectionDlc.Name})", LogTextBox.Action, false); } + writer.WriteLine(" }"); writer.WriteLine(pair.Equals(lastExtraApp) ? " }" : " },"); } } + writer.WriteLine(" },"); } else writer.WriteLine(" \"extra_dlcs\": {},"); + writer.WriteLine(" \"store_config\": null"); writer.WriteLine("}"); } @@ -149,9 +180,12 @@ internal static class SmokeAPI if (oldConfig.FileExists()) { oldConfig.DeleteFile(); - installForm?.UpdateUser($"Deleted old CreamAPI configuration: {Path.GetFileName(oldConfig)}", LogTextBox.Action, false); + installForm?.UpdateUser($"Deleted old CreamAPI configuration: {Path.GetFileName(oldConfig)}", + LogTextBox.Action, false); } - directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string old_config, + + 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 (api32_o.FileExists()) { @@ -160,9 +194,13 @@ internal static class SmokeAPI api32.DeleteFile(true); installForm?.UpdateUser($"Deleted SmokeAPI: {Path.GetFileName(api32)}", LogTextBox.Action, false); } + api32_o.MoveFile(api32!); - installForm?.UpdateUser($"Restored Steamworks: {Path.GetFileName(api32_o)} -> {Path.GetFileName(api32)}", LogTextBox.Action, false); + installForm?.UpdateUser( + $"Restored Steamworks: {Path.GetFileName(api32_o)} -> {Path.GetFileName(api32)}", LogTextBox.Action, + false); } + if (api64_o.FileExists()) { if (api64.FileExists()) @@ -170,31 +208,40 @@ internal static class SmokeAPI api64.DeleteFile(true); installForm?.UpdateUser($"Deleted SmokeAPI: {Path.GetFileName(api64)}", LogTextBox.Action, false); } + api64_o.MoveFile(api64!); - installForm?.UpdateUser($"Restored Steamworks: {Path.GetFileName(api64_o)} -> {Path.GetFileName(api64)}", LogTextBox.Action, false); + installForm?.UpdateUser( + $"Restored Steamworks: {Path.GetFileName(api64_o)} -> {Path.GetFileName(api64)}", LogTextBox.Action, + false); } + if (!deleteOthers) return; if (old_config.FileExists()) { old_config.DeleteFile(); - installForm?.UpdateUser($"Deleted configuration: {Path.GetFileName(old_config)}", LogTextBox.Action, false); + installForm?.UpdateUser($"Deleted configuration: {Path.GetFileName(old_config)}", LogTextBox.Action, + false); } + if (config.FileExists()) { config.DeleteFile(); installForm?.UpdateUser($"Deleted configuration: {Path.GetFileName(config)}", LogTextBox.Action, false); } + if (cache.FileExists()) { cache.DeleteFile(); installForm?.UpdateUser($"Deleted cache: {Path.GetFileName(cache)}", LogTextBox.Action, false); } + if (old_log.FileExists()) { old_log.DeleteFile(); installForm?.UpdateUser($"Deleted log: {Path.GetFileName(old_log)}", LogTextBox.Action, false); } + if (log.FileExists()) { log.DeleteFile(); @@ -202,36 +249,46 @@ internal static class SmokeAPI } }); - internal static async Task Install(string directory, Selection selection, InstallForm installForm = null, bool generateConfig = true) + internal static async Task Install(string directory, Selection selection, InstallForm installForm = null, + bool generateConfig = true) => await Task.Run(() => { directory.GetCreamApiComponents(out _, out _, out _, out _, out string oldConfig); if (oldConfig.FileExists()) { oldConfig.DeleteFile(); - installForm?.UpdateUser($"Deleted old CreamAPI configuration: {Path.GetFileName(oldConfig)}", LogTextBox.Action, false); + installForm?.UpdateUser($"Deleted old CreamAPI configuration: {Path.GetFileName(oldConfig)}", + LogTextBox.Action, false); } - directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out _, out _, out _, out _, out _); + + 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!, true); - installForm?.UpdateUser($"Renamed Steamworks: {Path.GetFileName(api32)} -> {Path.GetFileName(api32_o)}", LogTextBox.Action, false); + installForm?.UpdateUser($"Renamed Steamworks: {Path.GetFileName(api32)} -> {Path.GetFileName(api32_o)}", + LogTextBox.Action, false); } + if (api32_o.FileExists()) { "SmokeAPI.steam_api.dll".WriteManifestResource(api32); installForm?.UpdateUser($"Wrote SmokeAPI: {Path.GetFileName(api32)}", LogTextBox.Action, false); } + if (api64.FileExists() && !api64_o.FileExists()) { api64.MoveFile(api64_o!, true); - installForm?.UpdateUser($"Renamed Steamworks: {Path.GetFileName(api64)} -> {Path.GetFileName(api64_o)}", LogTextBox.Action, false); + installForm?.UpdateUser($"Renamed Steamworks: {Path.GetFileName(api64)} -> {Path.GetFileName(api64_o)}", + LogTextBox.Action, false); } + if (api64_o.FileExists()) { "SmokeAPI.steam_api64.dll".WriteManifestResource(api64); installForm?.UpdateUser($"Wrote SmokeAPI: {Path.GetFileName(api64)}", LogTextBox.Action, false); } + if (generateConfig) CheckConfig(directory, selection, installForm); }); diff --git a/CreamInstaller/Resources/UplayR1.cs b/CreamInstaller/Resources/UplayR1.cs index cf0ac69..bae4f6c 100644 --- a/CreamInstaller/Resources/UplayR1.cs +++ b/CreamInstaller/Resources/UplayR1.cs @@ -11,7 +11,8 @@ namespace CreamInstaller.Resources; internal static class UplayR1 { - internal static void GetUplayR1Components(this string directory, out string api32, out string api32_o, out string api64, out string api64_o, + internal static void GetUplayR1Components(this string directory, out string api32, out string api32_o, + out string api64, out string api64_o, out string config, out string log) { api32 = directory + @"\uplay_r1_loader.dll"; @@ -26,7 +27,8 @@ internal static class UplayR1 { directory.GetUplayR1Components(out _, out _, out _, out _, out string config, out _); HashSet blacklistDlc = selection.DLC.Where(dlc => !dlc.Enabled).ToHashSet(); - foreach (SelectionDLC extraDlc in selection.ExtraSelections.SelectMany(extraSelection => extraSelection.DLC.Where(dlc => !dlc.Enabled))) + foreach (SelectionDLC extraDlc in selection.ExtraSelections.SelectMany(extraSelection => + extraSelection.DLC.Where(dlc => !dlc.Enabled))) _ = blacklistDlc.Add(extraDlc); if (blacklistDlc.Count > 0) { @@ -34,18 +36,21 @@ internal static class UplayR1 installForm.UpdateUser("Generating Uplay R1 Unlocker configuration for " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);*/ config.CreateFile(true, installForm)?.Close(); StreamWriter writer = new(config, true, Encoding.UTF8); - WriteConfig(writer, new(blacklistDlc.ToDictionary(dlc => dlc.Id, dlc => dlc), PlatformIdComparer.String), installForm); + WriteConfig(writer, new(blacklistDlc.ToDictionary(dlc => dlc.Id, dlc => dlc), PlatformIdComparer.String), + installForm); writer.Flush(); writer.Close(); } else if (config.FileExists()) { config.DeleteFile(); - installForm?.UpdateUser($"Deleted unnecessary configuration: {Path.GetFileName(config)}", LogTextBox.Action, false); + installForm?.UpdateUser($"Deleted unnecessary configuration: {Path.GetFileName(config)}", LogTextBox.Action, + false); } } - private static void WriteConfig(StreamWriter writer, SortedList blacklistDlc, InstallForm installForm = null) + private static void WriteConfig(StreamWriter writer, SortedList blacklistDlc, + InstallForm installForm = null) { writer.WriteLine("{"); writer.WriteLine(" \"logging\": false,"); @@ -59,40 +64,53 @@ internal static class UplayR1 { SelectionDLC selectionDlc = pair.Value; writer.WriteLine($" {selectionDlc.Id}{(pair.Equals(lastBlacklistDlc) ? "" : ",")}"); - installForm?.UpdateUser($"Added blacklist DLC to UplayR1Unlocker.jsonc with appid {selectionDlc.Id} ({selectionDlc.Name})", LogTextBox.Action, + installForm?.UpdateUser( + $"Added blacklist DLC to UplayR1Unlocker.jsonc with appid {selectionDlc.Id} ({selectionDlc.Name})", + LogTextBox.Action, false); } + writer.WriteLine(" ],"); } else writer.WriteLine(" \"blacklist\": [],"); + writer.WriteLine("}"); } internal static async Task Uninstall(string directory, InstallForm installForm = null, bool deleteOthers = true) => await Task.Run(() => { - directory.GetUplayR1Components(out string api32, out string api32_o, out string api64, out string api64_o, out string config, out string log); + directory.GetUplayR1Components(out string api32, out string api32_o, out string api64, out string api64_o, + out string config, out string log); if (api32_o.FileExists()) { if (api32.FileExists()) { api32.DeleteFile(true); - installForm?.UpdateUser($"Deleted Uplay R1 Unlocker: {Path.GetFileName(api32)}", LogTextBox.Action, false); + installForm?.UpdateUser($"Deleted Uplay R1 Unlocker: {Path.GetFileName(api32)}", LogTextBox.Action, + false); } + api32_o.MoveFile(api32!); - installForm?.UpdateUser($"Restored Uplay R1: {Path.GetFileName(api32_o)} -> {Path.GetFileName(api32)}", LogTextBox.Action, false); + installForm?.UpdateUser($"Restored Uplay R1: {Path.GetFileName(api32_o)} -> {Path.GetFileName(api32)}", + LogTextBox.Action, false); } + if (api64_o.FileExists()) { if (api64.FileExists()) { api64.DeleteFile(true); - installForm?.UpdateUser($"Deleted Uplay R1 Unlocker: {Path.GetFileName(api64)}", LogTextBox.Action, false); + installForm?.UpdateUser($"Deleted Uplay R1 Unlocker: {Path.GetFileName(api64)}", LogTextBox.Action, + false); } + api64_o.MoveFile(api64!); - installForm?.UpdateUser($"Restored Uplay R1: {Path.GetFileName(api64_o)} -> {Path.GetFileName(api64)}", LogTextBox.Action, false); + installForm?.UpdateUser($"Restored Uplay R1: {Path.GetFileName(api64_o)} -> {Path.GetFileName(api64)}", + LogTextBox.Action, false); } + if (!deleteOthers) return; if (config.FileExists()) @@ -100,36 +118,47 @@ internal static class UplayR1 config.DeleteFile(); installForm?.UpdateUser($"Deleted configuration: {Path.GetFileName(config)}", LogTextBox.Action, false); } + if (!log.FileExists()) return; log.DeleteFile(); installForm?.UpdateUser($"Deleted log: {Path.GetFileName(log)}", LogTextBox.Action, false); }); - internal static async Task Install(string directory, Selection selection, InstallForm installForm = null, bool generateConfig = true) + internal static async Task Install(string directory, Selection selection, InstallForm installForm = null, + bool generateConfig = true) => await Task.Run(() => { - directory.GetUplayR1Components(out string api32, out string api32_o, out string api64, out string api64_o, out _, out _); + 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!, true); - installForm?.UpdateUser($"Renamed Uplay R1: {Path.GetFileName(api32)} -> {Path.GetFileName(api32_o)}", LogTextBox.Action, false); + installForm?.UpdateUser($"Renamed Uplay R1: {Path.GetFileName(api32)} -> {Path.GetFileName(api32_o)}", + LogTextBox.Action, false); } + if (api32_o.FileExists()) { "UplayR1.uplay_r1_loader.dll".WriteManifestResource(api32); - installForm?.UpdateUser($"Wrote Uplay R1 Unlocker: {Path.GetFileName(api32)}", LogTextBox.Action, false); + installForm?.UpdateUser($"Wrote Uplay R1 Unlocker: {Path.GetFileName(api32)}", LogTextBox.Action, + false); } + if (api64.FileExists() && !api64_o.FileExists()) { api64.MoveFile(api64_o!, true); - installForm?.UpdateUser($"Renamed Uplay R1: {Path.GetFileName(api64)} -> {Path.GetFileName(api64_o)}", LogTextBox.Action, false); + installForm?.UpdateUser($"Renamed Uplay R1: {Path.GetFileName(api64)} -> {Path.GetFileName(api64_o)}", + LogTextBox.Action, false); } + if (api64_o.FileExists()) { "UplayR1.uplay_r1_loader64.dll".WriteManifestResource(api64); - installForm?.UpdateUser($"Wrote Uplay R1 Unlocker: {Path.GetFileName(api64)}", LogTextBox.Action, false); + installForm?.UpdateUser($"Wrote Uplay R1 Unlocker: {Path.GetFileName(api64)}", LogTextBox.Action, + false); } + if (generateConfig) CheckConfig(directory, selection, installForm); }); diff --git a/CreamInstaller/Resources/UplayR2.cs b/CreamInstaller/Resources/UplayR2.cs index 206419f..7c1de5c 100644 --- a/CreamInstaller/Resources/UplayR2.cs +++ b/CreamInstaller/Resources/UplayR2.cs @@ -11,7 +11,8 @@ namespace CreamInstaller.Resources; internal static class UplayR2 { - internal static void GetUplayR2Components(this string directory, out string old_api32, out string old_api64, out string api32, out string api32_o, + internal static void GetUplayR2Components(this string directory, out string old_api32, out string old_api64, + out string api32, out string api32_o, out string api64, out string api64_o, out string config, out string log) { old_api32 = directory + @"\uplay_r2_loader.dll"; @@ -28,7 +29,8 @@ internal static class UplayR2 { directory.GetUplayR2Components(out _, out _, out _, out _, out _, out _, out string config, out _); HashSet blacklistDlc = selection.DLC.Where(dlc => !dlc.Enabled).ToHashSet(); - foreach (SelectionDLC extraDlc in selection.ExtraSelections.SelectMany(extraSelection => extraSelection.DLC.Where(dlc => !dlc.Enabled))) + foreach (SelectionDLC extraDlc in selection.ExtraSelections.SelectMany(extraSelection => + extraSelection.DLC.Where(dlc => !dlc.Enabled))) _ = blacklistDlc.Add(extraDlc); if (blacklistDlc.Count > 0) { @@ -36,18 +38,21 @@ internal static class UplayR2 installForm.UpdateUser("Generating Uplay R2 Unlocker configuration for " + selection.Name + $" in directory \"{directory}\" . . . ", LogTextBox.Operation);*/ config.CreateFile(true, installForm)?.Close(); StreamWriter writer = new(config, true, Encoding.UTF8); - WriteConfig(writer, new(blacklistDlc.ToDictionary(dlc => dlc.Id, dlc => dlc), PlatformIdComparer.String), installForm); + WriteConfig(writer, new(blacklistDlc.ToDictionary(dlc => dlc.Id, dlc => dlc), PlatformIdComparer.String), + installForm); writer.Flush(); writer.Close(); } else if (config.FileExists()) { config.DeleteFile(); - installForm?.UpdateUser($"Deleted unnecessary configuration: {Path.GetFileName(config)}", LogTextBox.Action, false); + installForm?.UpdateUser($"Deleted unnecessary configuration: {Path.GetFileName(config)}", LogTextBox.Action, + false); } } - private static void WriteConfig(StreamWriter writer, SortedList blacklistDlc, InstallForm installForm = null) + private static void WriteConfig(StreamWriter writer, SortedList blacklistDlc, + InstallForm installForm = null) { writer.WriteLine("{"); writer.WriteLine(" \"logging\": false,"); @@ -63,20 +68,25 @@ internal static class UplayR2 { SelectionDLC selectionDlc = pair.Value; writer.WriteLine($" {selectionDlc.Id}{(pair.Equals(lastBlacklistDlc) ? "" : ",")}"); - installForm?.UpdateUser($"Added blacklist DLC to UplayR2Unlocker.jsonc with appid {selectionDlc.Id} ({selectionDlc.Name})", LogTextBox.Action, + installForm?.UpdateUser( + $"Added blacklist DLC to UplayR2Unlocker.jsonc with appid {selectionDlc.Id} ({selectionDlc.Name})", + LogTextBox.Action, false); } + writer.WriteLine(" ],"); } else writer.WriteLine(" \"blacklist\": [],"); + writer.WriteLine("}"); } internal static async Task Uninstall(string directory, InstallForm installForm = null, bool deleteOthers = true) => await Task.Run(() => { - directory.GetUplayR2Components(out string old_api32, out string old_api64, out string api32, out string api32_o, out string api64, + directory.GetUplayR2Components(out string old_api32, out string old_api64, out string api32, + out string api32_o, out string api64, out string api64_o, out string config, out string log); if (api32_o.FileExists()) { @@ -84,22 +94,30 @@ internal static class UplayR2 if (api.FileExists()) { api.DeleteFile(true); - installForm?.UpdateUser($"Deleted Uplay R2 Unlocker: {Path.GetFileName(api)}", LogTextBox.Action, false); + installForm?.UpdateUser($"Deleted Uplay R2 Unlocker: {Path.GetFileName(api)}", LogTextBox.Action, + false); } + api32_o.MoveFile(api!); - installForm?.UpdateUser($"Restored Uplay R2: {Path.GetFileName(api32_o)} -> {Path.GetFileName(api)}", LogTextBox.Action, false); + installForm?.UpdateUser($"Restored Uplay R2: {Path.GetFileName(api32_o)} -> {Path.GetFileName(api)}", + LogTextBox.Action, false); } + if (api64_o.FileExists()) { string api = old_api64.FileExists() ? old_api64 : api64; if (api.FileExists()) { api.DeleteFile(true); - installForm?.UpdateUser($"Deleted Uplay R2 Unlocker: {Path.GetFileName(api)}", LogTextBox.Action, false); + installForm?.UpdateUser($"Deleted Uplay R2 Unlocker: {Path.GetFileName(api)}", LogTextBox.Action, + false); } + api64_o.MoveFile(api!); - installForm?.UpdateUser($"Restored Uplay R2: {Path.GetFileName(api64_o)} -> {Path.GetFileName(api)}", LogTextBox.Action, false); + installForm?.UpdateUser($"Restored Uplay R2: {Path.GetFileName(api64_o)} -> {Path.GetFileName(api)}", + LogTextBox.Action, false); } + if (!deleteOthers) return; if (config.FileExists()) @@ -107,39 +125,48 @@ internal static class UplayR2 config.DeleteFile(); installForm?.UpdateUser($"Deleted configuration: {Path.GetFileName(config)}", LogTextBox.Action, false); } + if (!log.FileExists()) return; log.DeleteFile(); installForm?.UpdateUser($"Deleted log: {Path.GetFileName(log)}", LogTextBox.Action, false); }); - internal static async Task Install(string directory, Selection selection, InstallForm installForm = null, bool generateConfig = true) + internal static async Task Install(string directory, Selection selection, InstallForm installForm = null, + bool generateConfig = true) => await Task.Run(() => { - directory.GetUplayR2Components(out string old_api32, out string old_api64, out string api32, out string api32_o, out string api64, + directory.GetUplayR2Components(out string old_api32, out string old_api64, out string api32, + out string api32_o, out string api64, out string api64_o, out _, out _); string api = old_api32.FileExists() ? old_api32 : api32; if (api.FileExists() && !api32_o.FileExists()) { api.MoveFile(api32_o!, true); - installForm?.UpdateUser($"Renamed Uplay R2: {Path.GetFileName(api)} -> {Path.GetFileName(api32_o)}", LogTextBox.Action, false); + installForm?.UpdateUser($"Renamed Uplay R2: {Path.GetFileName(api)} -> {Path.GetFileName(api32_o)}", + LogTextBox.Action, false); } + if (api32_o.FileExists()) { "UplayR2.upc_r2_loader.dll".WriteManifestResource(api); installForm?.UpdateUser($"Wrote Uplay R2 Unlocker: {Path.GetFileName(api)}", LogTextBox.Action, false); } + api = old_api64.FileExists() ? old_api64 : api64; if (api.FileExists() && !api64_o.FileExists()) { api.MoveFile(api64_o!, true); - installForm?.UpdateUser($"Renamed Uplay R2: {Path.GetFileName(api)} -> {Path.GetFileName(api64_o)}", LogTextBox.Action, false); + installForm?.UpdateUser($"Renamed Uplay R2: {Path.GetFileName(api)} -> {Path.GetFileName(api64_o)}", + LogTextBox.Action, false); } + if (api64_o.FileExists()) { "UplayR2.upc_r2_loader64.dll".WriteManifestResource(api); installForm?.UpdateUser($"Wrote Uplay R2 Unlocker: {Path.GetFileName(api)}", LogTextBox.Action, false); } + if (generateConfig) CheckConfig(directory, selection, installForm); }); diff --git a/CreamInstaller/Selection.cs b/CreamInstaller/Selection.cs index bae8c9b..b67282c 100644 --- a/CreamInstaller/Selection.cs +++ b/CreamInstaller/Selection.cs @@ -11,8 +11,11 @@ namespace CreamInstaller; public enum Platform { - None = 0, Paradox, Steam, - Epic, Ubisoft + None = 0, + Paradox, + Steam, + Epic, + Ubisoft } internal sealed class Selection : IEquatable @@ -57,15 +60,23 @@ internal sealed class Selection : IEquatable internal static IEnumerable AllEnabled => All.Keys.Where(s => s.Enabled); - internal bool Enabled { get => TreeNode.Checked; set => TreeNode.Checked = value; } + internal bool Enabled + { + get => TreeNode.Checked; + set => TreeNode.Checked = value; + } internal IEnumerable DLC => SelectionDLC.All.Keys.Where(dlc => Equals(dlc.Selection, this)); - public bool Equals(Selection other) => other is not null && (ReferenceEquals(this, other) || Id == other.Id && Platform == other.Platform); + 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, + internal static Selection GetOrCreate(Platform platform, string id, string name, string rootDirectory, + HashSet dllDirectories, List<(string directory, BinaryType binaryType)> executableDirectories) - => FromId(platform, id) ?? new Selection(platform, id, name, rootDirectory, dllDirectories, executableDirectories); + => FromId(platform, id) ?? + new Selection(platform, id, name, rootDirectory, dllDirectories, executableDirectories); internal void Remove() { @@ -82,16 +93,19 @@ internal sealed class Selection : IEquatable Remove(); return; } + if (Program.IsGameBlocked(Name, RootDirectory)) { Remove(); return; } + if (!RootDirectory.DirectoryExists()) { Remove(); return; } + _ = DllDirectories.RemoveWhere(directory => !directory.DirectoryExists()); if (DllDirectories.Count < 1) Remove(); @@ -103,7 +117,8 @@ internal sealed class Selection : IEquatable selection.Validate(programsToScan); } - internal static Selection FromId(Platform platform, string gameId) => All.Keys.FirstOrDefault(s => s.Platform == platform && s.Id == gameId); + internal static Selection FromId(Platform platform, string gameId) => + All.Keys.FirstOrDefault(s => s.Platform == platform && s.Id == gameId); public override bool Equals(object obj) => ReferenceEquals(this, obj) || obj is Selection other && Equals(other); diff --git a/CreamInstaller/SelectionDLC.cs b/CreamInstaller/SelectionDLC.cs index 5692eb4..4548728 100644 --- a/CreamInstaller/SelectionDLC.cs +++ b/CreamInstaller/SelectionDLC.cs @@ -7,8 +7,11 @@ namespace CreamInstaller; public enum DLCType { - None = 0, Steam, SteamHidden, - Epic, EpicEntitlement + None = 0, + Steam, + SteamHidden, + Epic, + EpicEntitlement } internal sealed class SelectionDLC : IEquatable @@ -32,7 +35,11 @@ internal sealed class SelectionDLC : IEquatable TreeNode = new() { Tag = Type, Name = Id, Text = Name }; } - internal bool Enabled { get => TreeNode.Checked; set => TreeNode.Checked = value; } + internal bool Enabled + { + get => TreeNode.Checked; + set => TreeNode.Checked = value; + } internal Selection Selection { @@ -57,7 +64,8 @@ internal sealed class SelectionDLC : IEquatable } public bool Equals(SelectionDLC other) - => other is not null && (ReferenceEquals(this, other) || Type == other.Type && Selection?.Id == other.Selection?.Id && Id == other.Id); + => other is not null && (ReferenceEquals(this, other) || + Type == other.Type && Selection?.Id == other.Selection?.Id && Id == other.Id); internal static SelectionDLC GetOrCreate(DLCType type, string gameId, string id, string name) => FromId(type, gameId, id) ?? new SelectionDLC(type, id, name); diff --git a/CreamInstaller/Utility/Diagnostics.cs b/CreamInstaller/Utility/Diagnostics.cs index 38203e2..c9cdb25 100644 --- a/CreamInstaller/Utility/Diagnostics.cs +++ b/CreamInstaller/Utility/Diagnostics.cs @@ -34,13 +34,17 @@ internal static class Diagnostics OpenFileInWindowsNotepad(path); } - private static void OpenFileInNotepadPlusPlus(string npp, string path) => Process.Start(new ProcessStartInfo { FileName = npp, Arguments = path }); + private static void OpenFileInNotepadPlusPlus(string npp, string path) => + Process.Start(new ProcessStartInfo { FileName = npp, Arguments = path }); - private static void OpenFileInWindowsNotepad(string path) => Process.Start(new ProcessStartInfo { FileName = "notepad.exe", Arguments = path }); + private static void OpenFileInWindowsNotepad(string path) => Process.Start(new ProcessStartInfo + { FileName = "notepad.exe", Arguments = path }); - internal static void OpenDirectoryInFileExplorer(string path) => Process.Start(new ProcessStartInfo { FileName = "explorer.exe", Arguments = path }); + internal static void OpenDirectoryInFileExplorer(string path) => Process.Start(new ProcessStartInfo + { FileName = "explorer.exe", Arguments = path }); - internal static void OpenUrlInInternetBrowser(string url) => Process.Start(new ProcessStartInfo { FileName = url, UseShellExecute = true }); + internal static void OpenUrlInInternetBrowser(string url) => + Process.Start(new ProcessStartInfo { FileName = url, UseShellExecute = true }); internal static string ResolvePath(this string path) { diff --git a/CreamInstaller/Utility/ExceptionHandler.cs b/CreamInstaller/Utility/ExceptionHandler.cs index 2943ce1..b8eb889 100644 --- a/CreamInstaller/Utility/ExceptionHandler.cs +++ b/CreamInstaller/Utility/ExceptionHandler.cs @@ -30,20 +30,27 @@ internal static class ExceptionHandler int ciNum = line.LastIndexOf(@"CreamInstaller\", StringComparison.Ordinal); int lineNum = line.LastIndexOf(":line ", StringComparison.Ordinal); if (atNum != -1) - _ = output.Append("\n " + (inNum != -1 ? line[atNum..(inNum - 1)] : line[atNum..]) + (inNum != -1 - ? "\n " + (ciNum != -1 - ? "in " + (lineNum != -1 ? line[ciNum..lineNum] + "\n on " + line[(lineNum + 1)..] : line[ciNum..]) - : line[inNum..]) - : null)); + _ = output.Append("\n " + (inNum != -1 ? line[atNum..(inNum - 1)] : line[atNum..]) + + (inNum != -1 + ? "\n " + (ciNum != -1 + ? "in " + (lineNum != -1 + ? line[ciNum..lineNum] + "\n on " + + line[(lineNum + 1)..] + : line[ciNum..]) + : line[inNum..]) + : null)); } } + e = e.InnerException; stackDepth++; } + return output.ToString(); } - internal static bool HandleException(this Exception e, Form form = null, string caption = null, string acceptButtonText = "Retry", + internal static bool HandleException(this Exception e, Form form = null, string caption = null, + string acceptButtonText = "Retry", string cancelButtonText = "Cancel") { caption ??= Program.Name + " encountered an exception"; @@ -51,12 +58,14 @@ internal static class ExceptionHandler if (string.IsNullOrWhiteSpace(outputString)) outputString = e?.ToString() ?? "Unknown exception"; using DialogForm dialogForm = new(form ?? Form.ActiveForm); - return dialogForm.Show(SystemIcons.Error, outputString, acceptButtonText, cancelButtonText, caption) is DialogResult.OK; + return dialogForm.Show(SystemIcons.Error, outputString, acceptButtonText, cancelButtonText, caption) is + DialogResult.OK; } internal static void HandleFatalException(this Exception e) { - e.HandleException(caption: Program.Name + " encountered a fatal exception", acceptButtonText: "OK", cancelButtonText: null); + e.HandleException(caption: Program.Name + " encountered a fatal exception", acceptButtonText: "OK", + cancelButtonText: null); Application.Exit(); } } diff --git a/CreamInstaller/Utility/HttpClientManager.cs b/CreamInstaller/Utility/HttpClientManager.cs index 7109170..32aec57 100644 --- a/CreamInstaller/Utility/HttpClientManager.cs +++ b/CreamInstaller/Utility/HttpClientManager.cs @@ -29,8 +29,10 @@ internal static class HttpClientManager try { using HttpRequestMessage request = new(HttpMethod.Get, url); - using HttpResponseMessage response = await HttpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); - if (response.StatusCode is HttpStatusCode.NotModified && HttpContentCache.TryGetValue(url, out string content)) + using HttpResponseMessage response = + await HttpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); + if (response.StatusCode is HttpStatusCode.NotModified && + HttpContentCache.TryGetValue(url, out string content)) return content; _ = response.EnsureSuccessStatusCode(); content = await response.Content.ReadAsStringAsync(); diff --git a/CreamInstaller/Utility/IconGrabber.cs b/CreamInstaller/Utility/IconGrabber.cs index 6a8a863..63f6c5c 100644 --- a/CreamInstaller/Utility/IconGrabber.cs +++ b/CreamInstaller/Utility/IconGrabber.cs @@ -5,7 +5,8 @@ namespace CreamInstaller.Utility; internal static class IconGrabber { - internal const string SteamAppImagesPath = "https://cdn.cloudflare.steamstatic.com/steamcommunity/public/images/apps/"; + internal const string SteamAppImagesPath = + "https://cdn.cloudflare.steamstatic.com/steamcommunity/public/images/apps/"; private const string GoogleFaviconsApiUrl = "https://www.google.com/s2/favicons"; @@ -15,13 +16,16 @@ internal static class IconGrabber return Icon.FromHandle(dialogIconBitmap.GetHicon()); } - internal static string GetDomainFaviconUrl(string domain, int size = 16) => GoogleFaviconsApiUrl + $"?domain={domain}&sz={size}"; + internal static string GetDomainFaviconUrl(string domain, int size = 16) => + GoogleFaviconsApiUrl + $"?domain={domain}&sz={size}"; - internal static Image GetFileIconImage(this string path) => path.FileExists() ? Icon.ExtractAssociatedIcon(path)?.ToBitmap() : null; + internal static Image GetFileIconImage(this string path) => + path.FileExists() ? Icon.ExtractAssociatedIcon(path)?.ToBitmap() : null; internal static Image GetNotepadImage() => GetFileIconImage(Diagnostics.GetNotepadPath()); internal static Image GetCommandPromptImage() => GetFileIconImage(Environment.SystemDirectory + @"\cmd.exe"); - internal static Image GetFileExplorerImage() => GetFileIconImage(Environment.GetFolderPath(Environment.SpecialFolder.Windows) + @"\explorer.exe"); + internal static Image GetFileExplorerImage() => + GetFileIconImage(Environment.GetFolderPath(Environment.SpecialFolder.Windows) + @"\explorer.exe"); } \ No newline at end of file diff --git a/CreamInstaller/Utility/NativeImports.cs b/CreamInstaller/Utility/NativeImports.cs index d957684..5e102f1 100644 --- a/CreamInstaller/Utility/NativeImports.cs +++ b/CreamInstaller/Utility/NativeImports.cs @@ -15,8 +15,10 @@ internal static partial class NativeImports [LibraryImport("kernel32.dll", SetLastError = true), DefaultDllImportSearchPaths(DllImportSearchPath.System32)] [return: MarshalAs(UnmanagedType.Bool)] - internal static partial bool GetBinaryType([MarshalAs(UnmanagedType.LPStr)] string lpApplicationName, out BinaryType lpBinaryType); + internal static partial bool GetBinaryType([MarshalAs(UnmanagedType.LPStr)] string lpApplicationName, + out BinaryType lpBinaryType); [LibraryImport("user32.dll", SetLastError = true), DefaultDllImportSearchPaths(DllImportSearchPath.System32)] - internal static partial void SetWindowPos(nint hWnd, nint hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags); + internal static partial void SetWindowPos(nint hWnd, nint hWndInsertAfter, int x, int y, int cx, int cy, + uint uFlags); } \ No newline at end of file diff --git a/CreamInstaller/Utility/ProgramData.cs b/CreamInstaller/Utility/ProgramData.cs index a14b5ae..2c38cf3 100644 --- a/CreamInstaller/Utility/ProgramData.cs +++ b/CreamInstaller/Utility/ProgramData.cs @@ -10,8 +10,11 @@ namespace CreamInstaller.Utility; internal static class ProgramData { - private static readonly string DirectoryPathOld = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\CreamInstaller"; - internal static readonly string DirectoryPath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + @"\CreamInstaller"; + private static readonly string DirectoryPathOld = + Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\CreamInstaller"; + + internal static readonly string DirectoryPath = + Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + @"\CreamInstaller"; internal static readonly string AppInfoPath = DirectoryPath + @"\appinfo"; private static readonly string AppInfoVersionPath = AppInfoPath + @"\version.txt"; @@ -33,13 +36,17 @@ internal static class ProgramData DirectoryPath.DeleteDirectory(); DirectoryPathOld.MoveDirectory(DirectoryPath, true, form); } + DirectoryPath.CreateDirectory(); - if (!AppInfoVersionPath.FileExists() || !Version.TryParse(AppInfoVersionPath.ReadFile(), out Version version) || version < MinimumAppInfoVersion) + if (!AppInfoVersionPath.FileExists() || + !Version.TryParse(AppInfoVersionPath.ReadFile(), out Version version) || + version < MinimumAppInfoVersion) { AppInfoPath.DeleteDirectory(); AppInfoPath.CreateDirectory(); AppInfoVersionPath.WriteFile(Program.Version); } + CooldownPath.CreateDirectory(); if (OldProgramChoicesPath.FileExists()) OldProgramChoicesPath.DeleteFile(); @@ -71,6 +78,7 @@ internal static class ProgramData { // ignored } + return null; } @@ -93,7 +101,8 @@ internal static class ProgramData if (ProgramChoicesPath.FileExists()) try { - if (JsonConvert.DeserializeObject(ProgramChoicesPath.ReadFile(), typeof(List<(Platform platform, string id)>)) is + if (JsonConvert.DeserializeObject(ProgramChoicesPath.ReadFile(), + typeof(List<(Platform platform, string id)>)) is List<(Platform platform, string id)> choices) return choices; } @@ -101,6 +110,7 @@ internal static class ProgramData { // ignored } + return Enumerable.Empty<(Platform platform, string id)>(); } @@ -124,7 +134,8 @@ internal static class ProgramData if (DlcChoicesPath.FileExists()) try { - if (JsonConvert.DeserializeObject(DlcChoicesPath.ReadFile(), typeof(IEnumerable<(Platform platform, string gameId, string dlcId)>)) is + if (JsonConvert.DeserializeObject(DlcChoicesPath.ReadFile(), + typeof(IEnumerable<(Platform platform, string gameId, string dlcId)>)) is IEnumerable<(Platform platform, string gameId, string dlcId)> choices) return choices; } @@ -132,6 +143,7 @@ internal static class ProgramData { // ignored } + return Enumerable.Empty<(Platform platform, string gameId, string dlcId)>(); } @@ -164,10 +176,12 @@ internal static class ProgramData { // ignored } + return Enumerable.Empty<(Platform platform, string id, string proxy, bool enabled)>(); } - internal static void WriteKoaloaderProxyChoices(IEnumerable<(Platform platform, string id, string proxy, bool enabled)> choices) + internal static void WriteKoaloaderProxyChoices( + IEnumerable<(Platform platform, string id, string proxy, bool enabled)> choices) { try { diff --git a/CreamInstaller/Utility/SafeIO.cs b/CreamInstaller/Utility/SafeIO.cs index d4bbf14..79b568b 100644 --- a/CreamInstaller/Utility/SafeIO.cs +++ b/CreamInstaller/Utility/SafeIO.cs @@ -26,12 +26,14 @@ internal static class SafeIO } catch (Exception e) { - if (!crucial || directoryPath.DirectoryExists() || directoryPath.IOWarn("Failed to create a crucial directory", e, form) is not DialogResult.OK) + if (!crucial || directoryPath.DirectoryExists() || + directoryPath.IOWarn("Failed to create a crucial directory", e, form) is not DialogResult.OK) break; } } - internal static void MoveDirectory(this string directoryPath, string newDirectoryPath, bool crucial = false, Form form = null) + internal static void MoveDirectory(this string directoryPath, string newDirectoryPath, bool crucial = false, + Form form = null) { if (!directoryPath.DirectoryExists()) return; @@ -43,7 +45,8 @@ internal static class SafeIO } catch (Exception e) { - if (!crucial || !directoryPath.DirectoryExists() || directoryPath.IOWarn("Failed to move a crucial directory", e, form) is not DialogResult.OK) + if (!crucial || !directoryPath.DirectoryExists() || + directoryPath.IOWarn("Failed to move a crucial directory", e, form) is not DialogResult.OK) break; } } @@ -61,12 +64,14 @@ internal static class SafeIO 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; } } - internal static IEnumerable EnumerateDirectory(this string directoryPath, string filePattern, bool subdirectories = false, bool crucial = false, + internal static IEnumerable EnumerateDirectory(this string directoryPath, string filePattern, + bool subdirectories = false, bool crucial = false, Form form = null) { if (!directoryPath.DirectoryExists()) @@ -75,19 +80,23 @@ internal static class SafeIO try { return subdirectories - ? Directory.EnumerateFiles(directoryPath, filePattern, new EnumerationOptions { RecurseSubdirectories = true }) + ? Directory.EnumerateFiles(directoryPath, filePattern, + new EnumerationOptions { RecurseSubdirectories = true }) : Directory.EnumerateFiles(directoryPath, filePattern); } 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(); } - internal static IEnumerable EnumerateSubdirectories(this string directoryPath, string directoryPattern, bool subdirectories = false, + internal static IEnumerable EnumerateSubdirectories(this string directoryPath, string directoryPattern, + bool subdirectories = false, bool crucial = false, Form form = null) { if (!directoryPath.DirectoryExists()) @@ -96,15 +105,18 @@ internal static class SafeIO try { return subdirectories - ? Directory.EnumerateDirectories(directoryPath, directoryPattern, new EnumerationOptions { RecurseSubdirectories = true }) + ? Directory.EnumerateDirectories(directoryPath, directoryPattern, + new EnumerationOptions { RecurseSubdirectories = true }) : Directory.EnumerateDirectories(directoryPath, directoryPattern); } 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(); } @@ -122,6 +134,7 @@ internal static class SafeIO if (!crucial || filePath.IOWarn("Failed to create a crucial file", e, form) is not DialogResult.OK) break; } + return null; } @@ -137,7 +150,8 @@ internal static class SafeIO } catch (Exception e) { - if (!crucial || !filePath.FileExists() || filePath.IOWarn("Failed to move a crucial file", e, form) is not DialogResult.OK) + if (!crucial || !filePath.FileExists() || + filePath.IOWarn("Failed to move a crucial file", e, form) is not DialogResult.OK) break; } } @@ -154,7 +168,8 @@ internal static class SafeIO } catch (Exception e) { - if (!crucial || !filePath.FileExists() || filePath.IOWarn("Failed to delete a crucial file", e, form) is not DialogResult.OK) + if (!crucial || !filePath.FileExists() || + filePath.IOWarn("Failed to delete a crucial file", e, form) is not DialogResult.OK) break; } } @@ -170,9 +185,11 @@ internal static class SafeIO } catch (Exception e) { - if (!crucial || !filePath.FileExists() || filePath.IOWarn("Failed to read a crucial file", e, form) is not DialogResult.OK) + if (!crucial || !filePath.FileExists() || + filePath.IOWarn("Failed to read a crucial file", e, form) is not DialogResult.OK) break; } + return null; } @@ -187,9 +204,11 @@ internal static class SafeIO } catch (Exception e) { - if (!crucial || !filePath.FileExists() || filePath.IOWarn("Failed to read a crucial file", e, form) is not DialogResult.OK) + if (!crucial || !filePath.FileExists() || + filePath.IOWarn("Failed to read a crucial file", e, form) is not DialogResult.OK) break; } + return null; } @@ -208,7 +227,8 @@ internal static class SafeIO } } - internal static void ExtractZip(this string archivePath, string destinationPath, bool crucial = false, Form form = null) + internal static void ExtractZip(this string archivePath, string destinationPath, bool crucial = false, + Form form = null) { if (!archivePath.FileExists()) return; @@ -220,7 +240,8 @@ internal static class SafeIO } catch (Exception e) { - if (!crucial || !archivePath.FileExists() || archivePath.IOWarn("Failed to extract a crucial zip file", e, form) is not DialogResult.OK) + if (!crucial || !archivePath.FileExists() || + archivePath.IOWarn("Failed to extract a crucial zip file", e, form) is not DialogResult.OK) break; } } @@ -255,6 +276,7 @@ internal static class SafeIO description += e.FormatException(); break; } + DialogResult result = dialogForm.Show(SystemIcons.Warning, description, "Retry", "Cancel"); if (result is not DialogResult.OK) Program.Canceled = true;