diff --git a/.github/workflows/autobuild.yml b/.github/workflows/autobuild.yml new file mode 100644 index 0000000..174200b --- /dev/null +++ b/.github/workflows/autobuild.yml @@ -0,0 +1,37 @@ +name: Autobuild + +on: + push: + tags: + - '*' + workflow_dispatch: + pull_request: + types: [ main ] + +jobs: + build: + runs-on: windows-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.0.x' + + - name: Restore dependencies + run: dotnet restore CreamInstaller.sln + + - name: Build Release + run: dotnet build CreamInstaller.sln --configuration Release --no-restore + + - name: Publish single-file + run: dotnet publish CreamInstaller.sln -c Release -r win-x64 -p:PublishSingleFile=true --self-contained true --output ./publish + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: CreamInstaller-release + path: ./publish/CreamInstaller.exe diff --git a/CreamInstaller/Components/CustomForm.cs b/CreamInstaller/Components/CustomForm.cs index d36e628..06b4d2e 100644 --- a/CreamInstaller/Components/CustomForm.cs +++ b/CreamInstaller/Components/CustomForm.cs @@ -52,7 +52,7 @@ internal class CustomForm : Form "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 version of [CreamAPI](https://cs.rin.ru/forum/viewtopic.php?f=29&t=70576) by [deadmau5](https://cs.rin.ru/forum/viewtopic.php?f=29&t=70576). It also utilizes the latest versions of [Koaloader]({acidicoala}/Koaloader), [ScreamAPI]({acidicoala}/ScreamAPI), [Uplay R1\n" + + $"The program utilizes the latest version of [CreamAPI](https://cs.rin.ru/forum/viewtopic.php?f=29&t=70576) by [deadmau5](https://cs.rin.ru/forum/viewtopic.php?f=29&t=70576). It also utilizes the latest versions of [SmokeAPI]({acidicoala}/SmokeAPI), [Koaloader]({acidicoala}/Koaloader), [ScreamAPI]({acidicoala}/ScreamAPI), [Uplay R1\n" + $"Unlocker]({acidicoala}/UplayR1Unlocker) and [Uplay R2 Unlocker]({acidicoala}/UplayR2Unlocker), all by [acidicoala]({acidicoala}). All unlockers are downloaded and embedded into the program itself; no further\n" + "downloads necessary on your part!\n\n" + "USAGE:\n" + " 1. Choose which programs and/or games the program should scan for DLC.\n" diff --git a/CreamInstaller/Components/PlatformIdComparer.cs b/CreamInstaller/Components/PlatformIdComparer.cs index 8e3292c..2c87d13 100644 --- a/CreamInstaller/Components/PlatformIdComparer.cs +++ b/CreamInstaller/Components/PlatformIdComparer.cs @@ -22,7 +22,7 @@ internal sealed class StringComparer : IComparer { public int Compare(string a, string b) => !int.TryParse(a, out _) && !int.TryParse(b, out _) - ? string.Compare(a, b, StringComparison.CurrentCulture) + ? string.Compare(a, b, StringComparison.Ordinal) : !int.TryParse(a, out int A) ? 1 : !int.TryParse(b, out int B) diff --git a/CreamInstaller/CreamInstaller.csproj b/CreamInstaller/CreamInstaller.csproj index ed83620..1c28dd9 100644 --- a/CreamInstaller/CreamInstaller.csproj +++ b/CreamInstaller/CreamInstaller.csproj @@ -4,7 +4,7 @@ net8.0-windows10.0.22621.0 True Resources\program.ico - 5.0.1.0 + 5.0.1.3 2021, pointfeev (https://github.com/pointfeev) CreamInstaller Automatic DLC Unlocker Installer & Configuration Generator diff --git a/CreamInstaller/Forms/InstallForm.cs b/CreamInstaller/Forms/InstallForm.cs index 31fdc20..44a5566 100644 --- a/CreamInstaller/Forms/InstallForm.cs +++ b/CreamInstaller/Forms/InstallForm.cs @@ -68,6 +68,9 @@ internal sealed partial class InstallForm : CustomForm bool useCreamApiProxy = selection.UseProxy && !Program.UseSmokeAPI && (selection.Platform is Platform.Steam || selection.Platform is Platform.Paradox && selection.ExtraSelections.Any(s => s.Platform is Platform.Steam)); + bool useSmokeApiProxy = selection.UseProxy && Program.UseSmokeAPI && + (selection.Platform is Platform.Steam || selection.Platform is Platform.Paradox && + selection.ExtraSelections.Any(s => s.Platform is Platform.Steam)); UpdateUser( $"{(uninstalling ? "Uninstalling" : "Installing")}" + $" {(uninstalling ? "from" : "for")} " + @@ -84,7 +87,7 @@ internal sealed partial class InstallForm : CustomForm if (Program.Canceled) return; - directory.GetKoaloaderComponents(out string old_config, out string config); + directory.GetKoaloaderComponents(out string old_config, out string config, out _); if (directory.GetKoaloaderProxies().Any(proxy => proxy.FileExists() && proxy.IsResourceFile(ResourceIdentifier.Koaloader)) || directory != selection.RootDirectory && @@ -97,19 +100,36 @@ internal sealed partial class InstallForm : CustomForm await Koaloader.Uninstall(directory, selection.RootDirectory, this); } - directory.GetCreamApiComponents(out _, out _, out _, out _, out config); - if (directory.GetCreamApiProxies().Any(proxy => - proxy.FileExists() && (proxy.IsResourceFile(ResourceIdentifier.Steamworks32) || - proxy.IsResourceFile(ResourceIdentifier.Steamworks64)))) + if (!Program.UseSmokeAPI) { - UpdateUser( - "Uninstalling CreamAPI in proxy mode from " + selection.Name + - $" in incorrect directory \"{directory}\" . . . ", LogTextBox.Operation); - await CreamAPI.ProxyUninstall(directory, this); + directory.GetCreamApiComponents(out _, out _, out _, out _, out config); + if (directory.GetCreamApiProxies().Any(proxy => + proxy.FileExists() && (proxy.IsResourceFile(ResourceIdentifier.Steamworks32) || + proxy.IsResourceFile(ResourceIdentifier.Steamworks64)))) + { + UpdateUser( + "Uninstalling CreamAPI in proxy mode from " + selection.Name + + $" in incorrect directory \"{directory}\" . . . ", LogTextBox.Operation); + await CreamAPI.ProxyUninstall(directory, this); + } + } + else + { + directory.GetSmokeApiComponents(out _, out _, out _, out _, out old_config, out config, out _, + out _, out _); + if (directory.GetSmokeApiProxies().Any(proxy => + proxy.FileExists() && (proxy.IsResourceFile(ResourceIdentifier.Steamworks32) || + proxy.IsResourceFile(ResourceIdentifier.Steamworks64)))) + { + UpdateUser( + "Uninstalling SmokeAPI in proxy mode from " + selection.Name + + $" in incorrect directory \"{directory}\" . . . ", LogTextBox.Operation); + await SmokeAPI.ProxyUninstall(directory, this); + } } } - if (uninstalling || !useKoaloader || !useCreamApiProxy) + if (uninstalling || !useKoaloader || !useCreamApiProxy || !useSmokeApiProxy) foreach ((string directory, _) in selection.ExecutableDirectories) { if (Program.Canceled) @@ -117,7 +137,7 @@ internal sealed partial class InstallForm : CustomForm if (uninstalling || !useKoaloader) { - directory.GetKoaloaderComponents(out string old_config, out string config); + directory.GetKoaloaderComponents(out string old_config, out string config, out _); if (directory.GetKoaloaderProxies().Any(proxy => proxy.FileExists() && proxy.IsResourceFile(ResourceIdentifier.Koaloader)) || Koaloader.AutoLoadDLLs.Any(pair => (directory + @"\" + pair.dll).FileExists()) || @@ -130,23 +150,44 @@ internal sealed partial class InstallForm : CustomForm } } - if (uninstalling || !useCreamApiProxy) + if (!Program.UseSmokeAPI) { - directory.GetCreamApiComponents(out _, out _, out _, out _, out string config); - if (directory.GetCreamApiProxies().Any(proxy => - proxy.FileExists() && (proxy.IsResourceFile(ResourceIdentifier.Steamworks32) || - proxy.IsResourceFile(ResourceIdentifier.Steamworks64))) || - config.FileExists()) + if (uninstalling || !useCreamApiProxy) { - UpdateUser( - "Uninstalling CreamAPI in proxy mode from " + selection.Name + - $" in directory \"{directory}\" . . . ", LogTextBox.Operation); - await CreamAPI.ProxyUninstall(directory, this); + directory.GetCreamApiComponents(out _, out _, out _, out _, out string config); + if (directory.GetCreamApiProxies().Any(proxy => + proxy.FileExists() && (proxy.IsResourceFile(ResourceIdentifier.Steamworks32) || + proxy.IsResourceFile(ResourceIdentifier.Steamworks64))) || + config.FileExists()) + { + UpdateUser( + "Uninstalling CreamAPI in proxy mode from " + selection.Name + + $" in directory \"{directory}\" . . . ", LogTextBox.Operation); + await CreamAPI.ProxyUninstall(directory, this); + } + } + } + else + { + if (uninstalling || !useSmokeApiProxy) + { + directory.GetSmokeApiComponents(out _, out _, out _, out _, out string old_config, out string config, out _, + out _, out _); + if (directory.GetSmokeApiProxies().Any(proxy => + proxy.FileExists() && (proxy.IsResourceFile(ResourceIdentifier.Steamworks32) || + proxy.IsResourceFile(ResourceIdentifier.Steamworks64))) || + config.FileExists()) + { + UpdateUser( + "Uninstalling SmokeAPI in proxy mode from " + selection.Name + + $" in directory \"{directory}\" . . . ", LogTextBox.Operation); + await SmokeAPI.ProxyUninstall(directory, this); + } } } } - bool uninstallingForProxy = uninstalling || useKoaloader || useCreamApiProxy; + bool uninstallingForProxy = uninstalling || useKoaloader || useCreamApiProxy || useSmokeApiProxy; int count = selection.DllDirectories.Count, cur = 0; foreach (string directory in selection.DllDirectories) { @@ -199,7 +240,7 @@ internal sealed partial class InstallForm : CustomForm 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); + out string api64_o, out string old_config, out string config, out string old_log, out string log); if (uninstallingForProxy ? api32_o.FileExists() || api64_o.FileExists() || config.FileExists() || log.FileExists() : api32.FileExists() || api64.FileExists()) @@ -253,13 +294,13 @@ internal sealed partial class InstallForm : CustomForm UpdateProgress(++cur / count * 100); } - if ((useCreamApiProxy || useKoaloader) && !uninstalling) + if ((useCreamApiProxy || useSmokeApiProxy || useKoaloader) && !uninstalling) foreach ((string directory, BinaryType binaryType) in selection.ExecutableDirectories) { if (Program.Canceled) return; - if (useCreamApiProxy) + if (useCreamApiProxy && !Program.UseSmokeAPI) { UpdateUser( "Installing CreamAPI in proxy mode for " + selection.Name + @@ -267,6 +308,14 @@ internal sealed partial class InstallForm : CustomForm LogTextBox.Operation); await CreamAPI.ProxyInstall(directory, binaryType, selection, this); } + else if (useSmokeApiProxy && Program.UseSmokeAPI) + { + UpdateUser( + "Installing SmokeAPI in proxy mode for " + selection.Name + + $" in directory \"{directory}\" . . . ", + LogTextBox.Operation); + await SmokeAPI.ProxyInstall(directory, binaryType, selection, this); + } else if (useKoaloader) { UpdateUser("Installing Koaloader for " + selection.Name + $" in directory \"{directory}\" . . . ", diff --git a/CreamInstaller/Forms/SelectForm.Designer.cs b/CreamInstaller/Forms/SelectForm.Designer.cs index 6f018d1..4671df6 100644 --- a/CreamInstaller/Forms/SelectForm.Designer.cs +++ b/CreamInstaller/Forms/SelectForm.Designer.cs @@ -1,8 +1,8 @@ -using System.ComponentModel; +using CreamInstaller.Components; +using CreamInstaller.Resources; +using System.ComponentModel; using System.Windows.Forms; -using CreamInstaller.Components; - namespace CreamInstaller.Forms { partial class SelectForm @@ -28,6 +28,9 @@ namespace CreamInstaller.Forms blockedGamesFlowPanel = new FlowLayoutPanel(); blockedGamesCheckBox = new CheckBox(); blockProtectedHelpButton = new Button(); + useSmokeAPILayoutPanel = new FlowLayoutPanel(); + useSmokeAPICheckBox = new CheckBox(); + useSmokeAPIHelpButton = new Button(); allCheckBoxLayoutPanel = new FlowLayoutPanel(); allCheckBox = new CheckBox(); progressBar = new ProgressBar(); @@ -45,6 +48,7 @@ namespace CreamInstaller.Forms programsGroupBox.SuspendLayout(); proxyFlowPanel.SuspendLayout(); blockedGamesFlowPanel.SuspendLayout(); + useSmokeAPILayoutPanel.SuspendLayout(); allCheckBoxLayoutPanel.SuspendLayout(); saveFlowPanel.SuspendLayout(); SuspendLayout(); @@ -84,6 +88,7 @@ namespace CreamInstaller.Forms programsGroupBox.Controls.Add(proxyFlowPanel); programsGroupBox.Controls.Add(noneFoundLabel); programsGroupBox.Controls.Add(blockedGamesFlowPanel); + programsGroupBox.Controls.Add(useSmokeAPILayoutPanel); programsGroupBox.Controls.Add(allCheckBoxLayoutPanel); programsGroupBox.Controls.Add(selectionTreeView); programsGroupBox.Location = new System.Drawing.Point(12, 12); @@ -172,6 +177,48 @@ namespace CreamInstaller.Forms blockProtectedHelpButton.UseVisualStyleBackColor = true; blockProtectedHelpButton.Click += OnBlockProtectedGamesHelpButtonClicked; // + // useSmokeAPILayoutPanel + // + useSmokeAPILayoutPanel.Anchor = AnchorStyles.Top; + useSmokeAPILayoutPanel.AutoSize = true; + useSmokeAPILayoutPanel.AutoSizeMode = AutoSizeMode.GrowAndShrink; + useSmokeAPILayoutPanel.Controls.Add(useSmokeAPICheckBox); + useSmokeAPILayoutPanel.Controls.Add(useSmokeAPIHelpButton); + useSmokeAPILayoutPanel.Location = new System.Drawing.Point(338, -1); + useSmokeAPILayoutPanel.Margin = new Padding(0); + useSmokeAPILayoutPanel.Name = "useSmokeAPILayoutPanel"; + useSmokeAPILayoutPanel.Size = new System.Drawing.Size(124, 19); + useSmokeAPILayoutPanel.TabIndex = 1006; + useSmokeAPILayoutPanel.WrapContents = false; + // + // useSmokeAPICheckBox + // + useSmokeAPICheckBox.AutoSize = true; + useSmokeAPICheckBox.Checked = true; + useSmokeAPICheckBox.CheckState = CheckState.Checked; + useSmokeAPICheckBox.Enabled = false; + useSmokeAPICheckBox.Location = new System.Drawing.Point(2, 0); + useSmokeAPICheckBox.Margin = new Padding(2, 0, 0, 0); + useSmokeAPICheckBox.Name = "useSmokeAPICheckBox"; + useSmokeAPICheckBox.Size = new System.Drawing.Size(102, 19); + useSmokeAPICheckBox.TabIndex = 1; + useSmokeAPICheckBox.Text = "Use SmokeAPI"; + useSmokeAPICheckBox.UseVisualStyleBackColor = true; + useSmokeAPICheckBox.CheckedChanged += OnUseSmokeAPICheckBoxChanged; + // + // useSmokeAPIHelpButton + // + useSmokeAPIHelpButton.Enabled = false; + useSmokeAPIHelpButton.Font = new System.Drawing.Font("Segoe UI", 7F); + useSmokeAPIHelpButton.Location = new System.Drawing.Point(104, 0); + useSmokeAPIHelpButton.Margin = new Padding(0, 0, 1, 0); + useSmokeAPIHelpButton.Name = "useSmokeAPIHelpButton"; + useSmokeAPIHelpButton.Size = new System.Drawing.Size(19, 19); + useSmokeAPIHelpButton.TabIndex = 2; + useSmokeAPIHelpButton.Text = "?"; + useSmokeAPIHelpButton.UseVisualStyleBackColor = true; + useSmokeAPIHelpButton.Click += OnUseSmokeAPIHelpButtonClicked; + // // allCheckBoxLayoutPanel // allCheckBoxLayoutPanel.Anchor = AnchorStyles.Top | AnchorStyles.Right; @@ -182,7 +229,7 @@ namespace CreamInstaller.Forms allCheckBoxLayoutPanel.Margin = new Padding(0); allCheckBoxLayoutPanel.Name = "allCheckBoxLayoutPanel"; allCheckBoxLayoutPanel.Size = new System.Drawing.Size(42, 19); - allCheckBoxLayoutPanel.TabIndex = 1006; + allCheckBoxLayoutPanel.TabIndex = 1007; allCheckBoxLayoutPanel.WrapContents = false; // // allCheckBox @@ -404,7 +451,10 @@ namespace CreamInstaller.Forms private CustomTreeView selectionTreeView; private CheckBox blockedGamesCheckBox; private Button blockProtectedHelpButton; + private CheckBox useSmokeAPICheckBox; + private Button useSmokeAPIHelpButton; private FlowLayoutPanel blockedGamesFlowPanel; + private FlowLayoutPanel useSmokeAPILayoutPanel; private FlowLayoutPanel allCheckBoxLayoutPanel; private Button uninstallButton; private Label progressLabelGames; diff --git a/CreamInstaller/Forms/SelectForm.cs b/CreamInstaller/Forms/SelectForm.cs index bdbbb1b..481e850 100644 --- a/CreamInstaller/Forms/SelectForm.cs +++ b/CreamInstaller/Forms/SelectForm.cs @@ -109,7 +109,12 @@ internal sealed partial class SelectForm : CustomForm UpdateRemainingDLCs(); }); } - + private static async Task WithTimeout(Task task, int millisecondsTimeout) + { + if (await Task.WhenAny(task, Task.Delay(millisecondsTimeout)) == task) + return await task; + return default; + } private async Task GetApplicablePrograms(IProgress progress, bool uninstallAll = false) { if (!uninstallAll && (programsToScan is null || programsToScan.Count < 1)) @@ -199,7 +204,7 @@ internal sealed partial class SelectForm : CustomForm return; StoreAppData storeAppData = await SteamStore.QueryStoreAPI(appId); _ = Interlocked.Decrement(ref steamGamesToCheck); - CmdAppData cmdAppData = await SteamCMD.GetAppInfo(appId, branch, buildId); + CmdAppData cmdAppData = await WithTimeout(SteamCMD.GetAppInfo(appId, branch, buildId), 20000); if (storeAppData is null && cmdAppData is null) { RemoveFromRemainingGames(name); @@ -548,6 +553,8 @@ internal sealed partial class SelectForm : CustomForm Program.Canceled = false; blockedGamesCheckBox.Enabled = false; blockProtectedHelpButton.Enabled = false; + useSmokeAPICheckBox.Enabled = false; + useSmokeAPIHelpButton.Enabled = false; cancelButton.Enabled = true; scanButton.Enabled = false; noneFoundLabel.Visible = false; @@ -694,6 +701,8 @@ internal sealed partial class SelectForm : CustomForm scanButton.Enabled = true; blockedGamesCheckBox.Enabled = true; blockProtectedHelpButton.Enabled = true; + useSmokeAPICheckBox.Enabled = true; + useSmokeAPIHelpButton.Enabled = true; } private void OnTreeViewNodeCheckedChanged(object sender, TreeViewEventArgs e) @@ -887,8 +896,8 @@ internal sealed partial class SelectForm : CustomForm foreach (string directory in directories) { directory.GetScreamApiComponents(out string api32, out string api32_o, out string api64, - out string api64_o, out string config, - out string log); + out string api64_o, out string old_config, out string config, + out string old_log, out string log); 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", @@ -1095,7 +1104,7 @@ internal sealed partial class SelectForm : CustomForm private static bool CanLoadProxy() => ProgramData.ReadProxyChoices().Any(); - private bool CanLoadSelections() => CanLoadDlc() || CanLoadProxy(); + private static bool CanLoadSelections() => CanLoadDlc() || CanLoadProxy(); private void OnLoadSelections(object sender, EventArgs e) { @@ -1171,7 +1180,7 @@ internal sealed partial class SelectForm : CustomForm saveButton.Enabled = CanSaveSelections(); resetButton.Enabled = CanResetSelections(); proxyAllCheckBox.CheckedChanged -= OnProxyAllCheckBoxChanged; - proxyAllCheckBox.Checked = Selection.All.Keys.All(selection => selection.UseProxy); + proxyAllCheckBox.Checked = Selection.All.Keys.Count != 0 && Selection.All.Keys.All(selection => selection.UseProxy); proxyAllCheckBox.CheckedChanged += OnProxyAllCheckBoxChanged; } @@ -1206,6 +1215,20 @@ internal sealed partial class SelectForm : CustomForm : blockedDirectoryExceptions), customFormText: "Block Protected Games"); } + private void OnUseSmokeAPICheckBoxChanged(object sender, EventArgs e) + { + Program.UseSmokeAPI = useSmokeAPICheckBox.Checked; + OnLoad(forceProvideChoices: false); + } + + private void OnUseSmokeAPIHelpButtonClicked(object sender, EventArgs e) + { + using DialogForm form = new(this); + _ = form.Show(SystemIcons.Information, + "InTest restore SmokeAPI in app. May be unstable." + + "\n\nIf some games don't launch with it - try disable and reinstall unlock", + customFormText: "Use SmokeAPI"); + } private void OnSortCheckBoxChanged(object sender, EventArgs e) => selectionTreeView.TreeViewNodeSorter = diff --git a/CreamInstaller/Forms/SelectForm.resx b/CreamInstaller/Forms/SelectForm.resx index 00c232e..a50cd1b 100644 --- a/CreamInstaller/Forms/SelectForm.resx +++ b/CreamInstaller/Forms/SelectForm.resx @@ -1,7 +1,7 @@