From 325f6ad1177062434d9268086f64eab431454d06 Mon Sep 17 00:00:00 2001 From: pointfeev Date: Tue, 23 Aug 2022 21:24:57 -0400 Subject: [PATCH] refactoring & koaloader testing --- CreamInstaller/Forms/InstallForm.cs | 133 +++++++++----- CreamInstaller/Forms/SelectForm.cs | 5 +- CreamInstaller/ProgramSelection.cs | 2 + CreamInstaller/Resources/Koaloader.cs | 239 ++++++++++++++++++++++++++ CreamInstaller/Resources/Resources.cs | 23 +++ CreamInstaller/Steam/SteamLibrary.cs | 2 + 6 files changed, 355 insertions(+), 49 deletions(-) create mode 100644 CreamInstaller/Resources/Koaloader.cs diff --git a/CreamInstaller/Forms/InstallForm.cs b/CreamInstaller/Forms/InstallForm.cs index 96398d2..122af58 100644 --- a/CreamInstaller/Forms/InstallForm.cs +++ b/CreamInstaller/Forms/InstallForm.cs @@ -58,67 +58,106 @@ internal partial class InstallForm : CustomForm private async Task OperateFor(ProgramSelection selection) { UpdateProgress(0); - int count = selection.DllDirectories.Count; - int cur = 0; if (selection.Id == "PL") { UpdateUser($"Repairing Paradox Launcher . . . ", InstallationLog.Operation); _ = await Repair(this, selection); } - foreach (string directory in selection.DllDirectories) + if (selection.Koaloader && !Uninstalling) { - if (selection.Platform is Platform.Steam || selection.SelectedDlc.Any(d => d.Value.type is DlcType.Steam or DlcType.SteamHidden) - || selection.Platform is Platform.Paradox || selection.ExtraSelectedDlc.Any(item => item.dlc.Any(dlc => dlc.Value.type is DlcType.Steam or DlcType.SteamHidden))) + foreach (string directory in await selection.RootDirectory.GetExecutableDirectories()) { - directory.GetSmokeApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config, out string cache); - if (File.Exists(api32) || File.Exists(api32_o) || File.Exists(api64) || File.Exists(api64_o) || File.Exists(config) || File.Exists(cache)) + UpdateUser("Installing Koaloader to " + selection.Name + $" in directory \"{directory}\" . . . ", InstallationLog.Operation); + await Koaloader.Install(directory, selection, this); + } + } + else + { + if (Uninstalling) + { + foreach (string directory in await selection.RootDirectory.GetExecutableDirectories()) { - UpdateUser($"{(Uninstalling ? "Uninstalling" : "Installing")} SmokeAPI" + - $" {(Uninstalling ? "from" : "for")} " + selection.Name + $" in directory \"{directory}\" . . . ", InstallationLog.Operation); - if (Uninstalling) - await SmokeAPI.Uninstall(directory, this); - else - await SmokeAPI.Install(directory, selection, this); + directory.GetKoaloaderComponents(out List proxies, out string config); + bool proxyExists = false; + foreach (string proxy in proxies) + if (File.Exists(proxy) && proxy.IsResourceFile(Resources.Resources.ResourceIdentifier.Koaloader)) + { + proxyExists = true; + break; + } + bool dllExists = false; + foreach ((string unlocker, string dll) in Koaloader.AutoLoadDlls) + { + string path = directory + @"\" + dll; + if (File.Exists(path)) + { + dllExists = true; + break; + } + } + if (proxyExists || dllExists || File.Exists(config)) + { + UpdateUser("Uninstalling Koaloader from " + selection.Name + $" in directory \"{directory}\" . . . ", InstallationLog.Operation); + await Koaloader.Uninstall(directory, selection, this); + } } } - if (selection.Platform is Platform.Epic || selection.SelectedDlc.Any(d => d.Value.type is DlcType.EpicCatalogItem or DlcType.EpicEntitlement) - || selection.Platform is Platform.Paradox || selection.ExtraSelectedDlc.Any(item => item.dlc.Any(dlc => dlc.Value.type is DlcType.EpicCatalogItem or DlcType.EpicEntitlement))) + int count = selection.DllDirectories.Count, cur = 0; + foreach (string directory in selection.DllDirectories) { - directory.GetScreamApiComponents(out string api32, out string api32_o, out string api64, out string api64_o, out string config); - if (File.Exists(api32) || File.Exists(api32_o) || File.Exists(api64) || File.Exists(api64_o) || File.Exists(config)) + if (selection.Platform is Platform.Steam || selection.SelectedDlc.Any(d => d.Value.type is DlcType.Steam or DlcType.SteamHidden) + || selection.Platform is Platform.Paradox || selection.ExtraSelectedDlc.Any(item => item.dlc.Any(dlc => dlc.Value.type is DlcType.Steam or DlcType.SteamHidden))) { - UpdateUser($"{(Uninstalling ? "Uninstalling" : "Installing")} ScreamAPI" + - $" {(Uninstalling ? "from" : "for")} " + selection.Name + $" in directory \"{directory}\" . . . ", InstallationLog.Operation); - if (Uninstalling) - await ScreamAPI.Uninstall(directory, this); - else - await ScreamAPI.Install(directory, selection, this); + directory.GetSmokeApiComponents(out _, out string api32_o, out _, out string api64_o, out string config, out string cache); + if (File.Exists(api32_o) || File.Exists(api64_o) || File.Exists(config) || File.Exists(cache)) + { + UpdateUser($"{(Uninstalling ? "Uninstalling" : "Installing")} SmokeAPI" + + $" {(Uninstalling ? "from" : "for")} " + selection.Name + $" in directory \"{directory}\" . . . ", InstallationLog.Operation); + if (Uninstalling) + await SmokeAPI.Uninstall(directory, this); + else + await SmokeAPI.Install(directory, selection, this); + } } + if (selection.Platform is Platform.Epic || selection.SelectedDlc.Any(d => d.Value.type is DlcType.EpicCatalogItem or DlcType.EpicEntitlement) + || selection.Platform is Platform.Paradox || selection.ExtraSelectedDlc.Any(item => item.dlc.Any(dlc => dlc.Value.type is DlcType.EpicCatalogItem or DlcType.EpicEntitlement))) + { + directory.GetScreamApiComponents(out _, out string api32_o, out _, out string api64_o, out string config); + if (File.Exists(api32_o) || File.Exists(api64_o) || File.Exists(config)) + { + UpdateUser($"{(Uninstalling ? "Uninstalling" : "Installing")} ScreamAPI" + + $" {(Uninstalling ? "from" : "for")} " + selection.Name + $" in directory \"{directory}\" . . . ", InstallationLog.Operation); + if (Uninstalling) + await ScreamAPI.Uninstall(directory, this); + else + await ScreamAPI.Install(directory, selection, this); + } + } + if (selection.Platform is Platform.Ubisoft) + { + directory.GetUplayR1Components(out _, out string api32_o, out _, out string api64_o, out string config); + if (File.Exists(api32_o) || File.Exists(api64_o) || File.Exists(config)) + { + UpdateUser($"{(Uninstalling ? "Uninstalling" : "Installing")} Uplay R1 Unlocker" + + $" {(Uninstalling ? "from" : "for")} " + selection.Name + $" in directory \"{directory}\" . . . ", InstallationLog.Operation); + if (Uninstalling) + await UplayR1.Uninstall(directory, this); + else + await UplayR1.Install(directory, selection, this); + } + directory.GetUplayR2Components(out _, out _, out _, out api32_o, out _, out api64_o, out config); + if (File.Exists(api32_o) || File.Exists(api64_o) || File.Exists(config)) + { + UpdateUser($"{(Uninstalling ? "Uninstalling" : "Installing")} Uplay R2 Unlocker" + + $" {(Uninstalling ? "from" : "for")} " + selection.Name + $" in directory \"{directory}\" . . . ", InstallationLog.Operation); + if (Uninstalling) + await UplayR2.Uninstall(directory, this); + else + await UplayR2.Install(directory, selection, this); + } + } + UpdateProgress(++cur / count * 100); } - 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); - if (File.Exists(api32) || File.Exists(api32_o) || File.Exists(api64) || File.Exists(api64_o) || File.Exists(config)) - { - UpdateUser($"{(Uninstalling ? "Uninstalling" : "Installing")} Uplay R1 Unlocker" + - $" {(Uninstalling ? "from" : "for")} " + selection.Name + $" in directory \"{directory}\" . . . ", InstallationLog.Operation); - if (Uninstalling) - 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); - if (File.Exists(old_api32) || File.Exists(old_api64) || File.Exists(api32) || File.Exists(api32_o) || File.Exists(api64) || File.Exists(api64_o) || File.Exists(config)) - { - UpdateUser($"{(Uninstalling ? "Uninstalling" : "Installing")} Uplay R2 Unlocker" + - $" {(Uninstalling ? "from" : "for")} " + selection.Name + $" in directory \"{directory}\" . . . ", InstallationLog.Operation); - if (Uninstalling) - await UplayR2.Uninstall(directory, this); - else - await UplayR2.Install(directory, selection, this); - } - } - UpdateProgress(++cur / count * 100); } UpdateProgress(100); } diff --git a/CreamInstaller/Forms/SelectForm.cs b/CreamInstaller/Forms/SelectForm.cs index 58c3efe..24a3a6c 100644 --- a/CreamInstaller/Forms/SelectForm.cs +++ b/CreamInstaller/Forms/SelectForm.cs @@ -134,11 +134,11 @@ internal partial class SelectForm : CustomForm _ = selectionTreeView.Nodes.Add(programNode); } } + int totalGames = 0, gamesChecked = 0; if (ProgramsToScan.Any(c => c.platform is Platform.Steam)) { List<(string appId, string name, string branch, int buildId, string gameDirectory)> steamGames = await SteamLibrary.GetGames(); - int totalGames = steamGames.Count; - int gamesChecked = 0; + totalGames = steamGames.Count; foreach ((string appId, string name, string branch, int buildId, string gameDirectory) in steamGames) { if (Program.Canceled) return; @@ -437,6 +437,7 @@ internal partial class SelectForm : CustomForm if (Program.Canceled) return; await task; } + gamesChecked = totalGames; } private List<(Platform platform, string id, string name)> ProgramsToScan; diff --git a/CreamInstaller/ProgramSelection.cs b/CreamInstaller/ProgramSelection.cs index e2316d3..97d0413 100644 --- a/CreamInstaller/ProgramSelection.cs +++ b/CreamInstaller/ProgramSelection.cs @@ -43,6 +43,8 @@ internal class ProgramSelection internal string RootDirectory; internal List DllDirectories; + internal bool Koaloader = true; + internal readonly SortedList AllDlc = new(PlatformIdComparer.String); internal readonly SortedList SelectedDlc = new(PlatformIdComparer.String); diff --git a/CreamInstaller/Resources/Koaloader.cs b/CreamInstaller/Resources/Koaloader.cs new file mode 100644 index 0000000..d427e94 --- /dev/null +++ b/CreamInstaller/Resources/Koaloader.cs @@ -0,0 +1,239 @@ +using ABI.System.Collections.Generic; + +using CreamInstaller.Components; +using CreamInstaller.Utility; + +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Windows.Media.Playback; +using Windows.Networking.Connectivity; + +namespace CreamInstaller.Resources; + +internal static class Koaloader +{ + internal static void GetKoaloaderComponents( + this string directory, + out List proxies, + out string config + ) + { + proxies = new(); + foreach (string proxy in Resources.EmbeddedResources) + { + string proxyPath = proxy[(proxy.IndexOf('.') + 1)..]; + proxyPath = proxyPath[(proxyPath.IndexOf('.') + 1)..]; + proxies.Add(directory + @"\" + proxyPath); + } + config = directory + @"\Koaloader.json"; + } + + internal static readonly List<(string unlocker, string dll)> AutoLoadDlls = new() + { + ("SmokeAPI", "SmokeAPI32.dll"), ("SmokeAPI", "SmokeAPI64.dll"), + ("ScreamAPI", "ScreamAPI32.dll"), ("ScreamAPI", "ScreamAPI64.dll"), + ("Uplay R2 Unlocker", "UplayR2Unlocker32.dll"), ("Uplay R2 Unlocker", "UplayR2Unlocker64.dll"), + ("Uplay R2 Unlocker", "UplayR2Unlocker32.dll"), ("Uplay R2 Unlocker", "UplayR2Unlocker64.dll") + }; + + internal static void WriteConfig(StreamWriter writer, SortedList targets, SortedList modules, InstallForm installForm = null) + { + writer.WriteLine("{"); + writer.WriteLine(" \"logging\": false,"); + writer.WriteLine(" \"enabled\": true,"); + writer.WriteLine(" \"auto_load\": " + (modules.Any() ? "false" : "true") + ","); + if (targets.Any()) + { + writer.WriteLine(" \"targets\": ["); + System.Collections.Generic.KeyValuePair lastTarget = targets.Last(); + foreach (System.Collections.Generic.KeyValuePair pair in targets) + { + string path = pair.Value; + writer.WriteLine($" \"{path}\"{(pair.Equals(lastTarget) ? "" : ",")}"); + if (installForm is not null) + installForm.UpdateUser($"Added target to Koaloader.json with path {path}", InstallationLog.Action, info: false); + } + writer.WriteLine(" ]"); + } + else + writer.WriteLine(" \"targets\": []"); + if (modules.Any()) + { + writer.WriteLine(" \"modules\": ["); + System.Collections.Generic.KeyValuePair lastModule = modules.Last(); + foreach (System.Collections.Generic.KeyValuePair pair in modules) + { + string path = pair.Value; + writer.WriteLine(" {"); + writer.WriteLine($" \"path\": \"" + path + "\","); + writer.WriteLine($" \"required\": true"); + writer.WriteLine(" }" + (pair.Equals(lastModule) ? "" : ",")); + if (installForm is not null) + installForm.UpdateUser($"Added module to Koaloader.json with path {path}", InstallationLog.Action, info: false); + } + writer.WriteLine(" ]"); + } + else + writer.WriteLine(" \"modules\": []"); + writer.WriteLine("}"); + } + + internal static async Task Uninstall(string directory, ProgramSelection selection, InstallForm installForm = null, bool deleteConfig = true) => await Task.Run(() => + { + directory.GetKoaloaderComponents(out List proxies, out string config); + foreach (string proxy in proxies) + { + if (File.Exists(proxy) && proxy.IsResourceFile(Resources.ResourceIdentifier.Koaloader)) + { + File.Delete(proxy); + if (installForm is not null) + installForm.UpdateUser($"Deleted Koaloader: {Path.GetFileName(proxy)}", InstallationLog.Action, info: false); + } + } + foreach ((string unlocker, string dll) in AutoLoadDlls) + { + string path = directory + @"\" + dll; + if (File.Exists(path)) + { + File.Delete(path); + if (installForm is not null) + installForm.UpdateUser($"Deleted {unlocker}: {Path.GetFileName(path)}", InstallationLog.Action, info: false); + } + } + if (deleteConfig && File.Exists(config)) + { + File.Delete(config); + if (installForm is not null) + installForm.UpdateUser($"Deleted configuration: {Path.GetFileName(config)}", InstallationLog.Action, info: false); + } + }); + + internal static async Task Install(string directory, ProgramSelection selection, InstallForm installForm = null, bool generateConfig = true) => await Task.Run(() => + { + directory.GetKoaloaderComponents(out List proxies, out string config); + string path = directory + @"\version.dll"; + "Koaloader.version_64.version.dll".Write(path); + if (installForm is not null) + installForm.UpdateUser($"Wrote Koaloader: {Path.GetFileName(path)}", InstallationLog.Action, info: false); + if (selection.Platform is Platform.Steam or Platform.Paradox) + { + bool did32 = false, did64 = false; + foreach (string dllDirectory in selection.DllDirectories) + { + dllDirectory.GetSmokeApiComponents(out string api32, out _, out string api64, out _, out _, out _); + if (!did32 && File.Exists(api32)) + { + did32 = true; + path = directory + @"\SmokeAPI32.dll"; + "SmokeAPI.steam_api.dll".Write(path); + if (installForm is not null) + installForm.UpdateUser($"Wrote SmokeAPI: {Path.GetFileName(path)}", InstallationLog.Action, info: false); + } + if (!did64 && File.Exists(api64)) + { + did64 = true; + path = directory + @"\SmokeAPI64.dll"; + "SmokeAPI.steam_api64.dll".Write(path); + if (installForm is not null) + installForm.UpdateUser($"Wrote SmokeAPI: {Path.GetFileName(path)}", InstallationLog.Action, info: false); + } + if (did32 && did64) + break; + } + } + if (selection.Platform is Platform.Epic or Platform.Paradox) + { + bool did32 = false, did64 = false; + foreach (string dllDirectory in selection.DllDirectories) + { + dllDirectory.GetScreamApiComponents(out string api32, out _, out string api64, out _, out _); + if (!did32 && File.Exists(api32)) + { + did32 = true; + path = directory + @"\ScreamAPI32.dll"; + "ScreamAPI.EOSSDK-Win32-Shipping.dll".Write(path); + if (installForm is not null) + installForm.UpdateUser($"Wrote ScreamAPI: {Path.GetFileName(path)}", InstallationLog.Action, info: false); + } + if (!did64 && File.Exists(api64)) + { + did64 = true; + path = directory + @"\ScreamAPI64.dll"; + "ScreamAPI.EOSSDK-Win64-Shipping.dll".Write(path); + if (installForm is not null) + installForm.UpdateUser($"Wrote ScreamAPI: {Path.GetFileName(path)}", InstallationLog.Action, info: false); + } + if (did32 && did64) + break; + } + } + if (selection.Platform is Platform.Ubisoft) + { + bool did32r1 = false, did64r1 = false, did32r2 = false, did64r2 = false; + foreach (string dllDirectory in selection.DllDirectories) + { + dllDirectory.GetUplayR1Components(out string api32, out _, out string api64, out _, out _); + if (!did32r1 && File.Exists(api32)) + { + did32r1 = true; + path = directory + @"\UplayR1Unlocker32.dll"; + "UplayR1.uplay_r1_loader.dll".Write(path); + if (installForm is not null) + installForm.UpdateUser($"Wrote Uplay R1 Unlocker: {Path.GetFileName(path)}", InstallationLog.Action, info: false); + } + if (!did64r1 && File.Exists(api64)) + { + did64r1 = true; + path = directory + @"\UplayR1Unlocker64.dll"; + "UplayR1.uplay_r1_loader64.dll".Write(path); + if (installForm is not null) + installForm.UpdateUser($"Wrote Uplay R1 Unlocker: {Path.GetFileName(path)}", InstallationLog.Action, info: false); + } + dllDirectory.GetUplayR2Components(out string old_api32, out string old_api64, out api32, out _, out api64, out _, out _); + if (!did32r2 && (File.Exists(old_api32) || File.Exists(old_api32))) + { + did32r2 = true; + path = directory + @"\UplayR2Unlocker32.dll"; + "UplayR2.upc_r2_loader.dll".Write(path); + if (installForm is not null) + installForm.UpdateUser($"Wrote Uplay R2 Unlocker: {Path.GetFileName(path)}", InstallationLog.Action, info: false); + } + if (!did64r2 && (File.Exists(old_api64) || File.Exists(api64))) + { + did64r2 = true; + path = directory + @"\UplayR2Unlocker64.dll"; + "UplayR2.upc_r2_loader64.dll".Write(path); + if (installForm is not null) + installForm.UpdateUser($"Wrote Uplay R2 Unlocker: {Path.GetFileName(path)}", InstallationLog.Action, info: false); + } + if (did32r1 && did64r1 && did32r2 && did64r2) + break; + } + } + if (generateConfig) + { + SortedList targets = new(PlatformIdComparer.String); + SortedList modules = new(PlatformIdComparer.String); + if (targets.Any() || modules.Any()) + { + if (installForm is not null) + installForm.UpdateUser("Generating Koaloader configuration for " + selection.Name + $" in directory \"{directory}\" . . . ", InstallationLog.Operation); + File.Create(config).Close(); + StreamWriter writer = new(config, true, Encoding.UTF8); + WriteConfig(writer, targets, modules, installForm); + writer.Flush(); + writer.Close(); + } + else if (File.Exists(config)) + { + File.Delete(config); + if (installForm is not null) + installForm.UpdateUser($"Deleted unnecessary configuration: {Path.GetFileName(config)}", InstallationLog.Action, info: false); + } + } + }); +} diff --git a/CreamInstaller/Resources/Resources.cs b/CreamInstaller/Resources/Resources.cs index 2bfd539..590dcc0 100644 --- a/CreamInstaller/Resources/Resources.cs +++ b/CreamInstaller/Resources/Resources.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; +using System.Drawing; using System.IO; using System.Linq; using System.Reflection; using System.Security.Cryptography; +using System.Threading.Tasks; namespace CreamInstaller.Resources; @@ -417,4 +419,25 @@ internal static class Resources internal static bool IsResourceFile(this string filePath, ResourceIdentifier identifier) => filePath.ComputeMD5() is string hash && ResourceMD5s[identifier].Contains(hash); internal static bool IsResourceFile(this string filePath) => filePath.ComputeMD5() is string hash && ResourceMD5s.Values.Any(hashes => hashes.Contains(hash)); + + internal static async Task> GetExecutableDirectories(this string rootDirectory) => await Task.Run(async () => + { + List executableDirectories = new(); + if (Program.Canceled || !Directory.Exists(rootDirectory)) return null; + if (Directory.GetFiles(rootDirectory, "*.exe").Any()) + executableDirectories.Add(rootDirectory); + string[] directories = Directory.GetDirectories(rootDirectory); + foreach (string _directory in directories) + { + if (Program.Canceled) return null; + try + { + List moreExecutableDirectories = await _directory.GetExecutableDirectories(); + if (moreExecutableDirectories is not null) + executableDirectories.AddRange(moreExecutableDirectories); + } + catch { } + } + return !executableDirectories.Any() ? null : executableDirectories; + }); } diff --git a/CreamInstaller/Steam/SteamLibrary.cs b/CreamInstaller/Steam/SteamLibrary.cs index 77dc24d..f660f69 100644 --- a/CreamInstaller/Steam/SteamLibrary.cs +++ b/CreamInstaller/Steam/SteamLibrary.cs @@ -110,6 +110,7 @@ internal static class SteamLibrary gameDirectories.Add(libraryFolder); string libraryFolders = libraryFolder + @"\libraryfolders.vdf"; if (File.Exists(libraryFolders) && ValveDataFile.TryDeserialize(File.ReadAllText(libraryFolders, Encoding.UTF8), out VProperty result)) +#pragma warning disable IDE0220 // Add explicit cast foreach (VProperty property in result.Value.Where(p => p is VProperty && int.TryParse((p as VProperty).Key, out int _))) { string path = property.Value.GetChild("path")?.ToString(); @@ -117,6 +118,7 @@ internal static class SteamLibrary path += @"\steamapps"; if (Directory.Exists(path) && !gameDirectories.Contains(path)) gameDirectories.Add(path); } +#pragma warning restore IDE0220 // Add explicit cast } } return gameDirectories;