- Executable bitness is now detected automatically per-directory (prioritizing 64-bit before 32-bit)
- Executables which or not 32-bit nor 64-bit are now omitted
- Saved pre-v4.1.0.0 proxy choices will be automatically converted to the new format ("Koaloader.version_64.version.dll" -> "version")
- Executable directories' determined bitness can be visualized with the context menu (or during installation)
This commit is contained in:
pointfeev 2022-08-27 22:04:42 -04:00
parent 0fe30cd966
commit 50c5f4fb24
12 changed files with 149 additions and 105 deletions

View file

@ -8,6 +8,8 @@ using System.Linq;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
using static CreamInstaller.Resources.Resources;
using TreeView = System.Windows.Forms.TreeView;
namespace CreamInstaller.Components;
@ -157,7 +159,7 @@ internal class CustomTreeView : TreeView
comboBoxFont ??= new(font.FontFamily, 6, font.Style, font.Unit, font.GdiCharSet, font.GdiVerticalFont);
ComboBoxState comboBoxState = Enabled ? ComboBoxState.Normal : ComboBoxState.Disabled;
text = selection.KoaloaderProxy.GetKoaloaderProxyDisplay();
text = selection.KoaloaderProxy + ".dll";
size = TextRenderer.MeasureText(graphics, text, comboBoxFont) + new Size(6, 0);
int padding = 2;
bounds = new(bounds.X + bounds.Width, bounds.Y + padding / 2, size.Width, bounds.Height - padding);
@ -210,50 +212,25 @@ internal class CustomTreeView : TreeView
_ = comboBoxBounds.Remove(pair.Key);
else if (pair.Value.Contains(clickPoint))
{
List<string> proxies = Resources.Resources.EmbeddedResources.FindAll(r => r.StartsWith("Koaloader"));
List<string> proxies = EmbeddedResources.FindAll(r => r.StartsWith("Koaloader"))
.Select(p =>
{
p.GetProxyInfoFromIdentifier(out string proxyName, out _);
return proxyName;
}).Distinct().ToList();
comboBoxDropDown ??= new();
comboBoxDropDown.ShowItemToolTips = false;
comboBoxDropDown.Items.Clear();
_ = comboBoxDropDown.Items.Add(new ToolStripButton("32-bit", null, (s, e) =>
foreach (string proxy in proxies)
{
comboBoxDropDown.Items.Clear();
foreach (string proxy in proxies)
_ = comboBoxDropDown.Items.Add(new ToolStripButton(proxy + ".dll", null, (s, e) =>
{
string text = proxy.GetKoaloaderProxyDisplay();
if (text.Contains("32-bit"))
{
_ = comboBoxDropDown.Items.Add(new ToolStripButton(text, null, new EventHandler((sender, e) =>
{
pair.Key.KoaloaderProxy = proxy;
ProgramData.UpdateKoaloaderProxyChoices();
Invalidate();
}))
{ Font = comboBoxFont });
}
}
comboBoxDropDown.Show(new(comboBoxDropDown.Left, comboBoxDropDown.Top));
})
{ Font = comboBoxFont });
_ = comboBoxDropDown.Items.Add(new ToolStripButton("64-bit", null, (s, e) =>
{
comboBoxDropDown.Items.Clear();
foreach (string proxy in proxies)
{
string text = proxy.GetKoaloaderProxyDisplay();
if (text.Contains("64-bit"))
{
_ = comboBoxDropDown.Items.Add(new ToolStripButton(text, null, new EventHandler((sender, e) =>
{
pair.Key.KoaloaderProxy = proxy;
ProgramData.UpdateKoaloaderProxyChoices();
Invalidate();
}))
{ Font = comboBoxFont });
}
}
comboBoxDropDown.Show(new(comboBoxDropDown.Left, comboBoxDropDown.Top));
})
{ Font = comboBoxFont });
pair.Key.KoaloaderProxy = proxy;
ProgramData.UpdateKoaloaderProxyChoices();
Invalidate();
})
{ Font = comboBoxFont });
}
comboBoxDropDown.Show(this, PointToScreen(new(pair.Value.Left, pair.Value.Bottom - 1)));
invalidate = true;
break;

View file

@ -5,7 +5,7 @@
<UseWindowsForms>True</UseWindowsForms>
<ApplicationIcon>Resources\ini.ico</ApplicationIcon>
<IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>
<Version>4.0.2.1</Version>
<Version>4.1.0.0</Version>
<PackageIcon>Resources\ini.ico</PackageIcon>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<Copyright>2021, pointfeev (https://github.com/pointfeev)</Copyright>
@ -151,6 +151,7 @@
<PackageReference Include="HtmlAgilityPack" Version="1.11.45" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Onova" Version="2.6.2" />
<PackageReference Include="System.Reflection.Metadata" Version="6.0.1" />
<PackageReference Include="System.ServiceModel.Primitives" Version="4.10.0" />
</ItemGroup>
<ItemGroup>

View file

@ -9,6 +9,8 @@ using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using static CreamInstaller.Resources.Resources;
namespace CreamInstaller.Epic;
internal static class EpicLibrary
@ -27,7 +29,7 @@ internal static class EpicLibrary
}
}
internal static async Task<List<string>> GetExecutableDirectories(string gameDirectory) =>
internal static async Task<List<(string directory, BinaryType binaryType)>> GetExecutableDirectories(string gameDirectory) =>
await Task.Run(async () => await gameDirectory.GetExecutableDirectories(filterCommon: true));
internal static async Task<List<Manifest>> GetGames() => await Task.Run(() =>

View file

@ -12,6 +12,7 @@ using System.Threading.Tasks;
using System.Windows.Forms;
using static CreamInstaller.Paradox.ParadoxLauncher;
using static CreamInstaller.Resources.Resources;
namespace CreamInstaller;
@ -64,13 +65,16 @@ internal partial class InstallForm : CustomForm
UpdateUser($"Repairing Paradox Launcher . . . ", InstallationLog.Operation);
_ = await Repair(this, selection);
}
IEnumerable<string> invalidDirectories = (await selection.RootDirectory.GetExecutables()).Where(d => !selection.ExecutableDirectories.Contains(d));
if (!selection.ExecutableDirectories.Contains(selection.RootDirectory)) invalidDirectories = invalidDirectories.Append(selection.RootDirectory);
IEnumerable<string> invalidDirectories = (await selection.RootDirectory.GetExecutables())
.Where(d => !selection.ExecutableDirectories.Any(s => d.path.Contains(s.directory)))
.Select(d => Path.GetDirectoryName(d.path));
if (!selection.ExecutableDirectories.Any(s => s.directory == selection.RootDirectory))
invalidDirectories = invalidDirectories.Append(selection.RootDirectory);
invalidDirectories = invalidDirectories.Distinct();
foreach (string directory in invalidDirectories)
{
directory.GetKoaloaderComponents(out List<string> proxies, out string config);
if (proxies.Any(proxy => File.Exists(proxy) && proxy.IsResourceFile(Resources.Resources.ResourceIdentifier.Koaloader))
if (proxies.Any(proxy => File.Exists(proxy) && proxy.IsResourceFile(ResourceIdentifier.Koaloader))
|| Koaloader.AutoLoadDlls.Any(pair => File.Exists(directory + @"\" + pair.dll))
|| File.Exists(config))
{
@ -81,10 +85,10 @@ internal partial class InstallForm : CustomForm
}
if (Uninstalling || !selection.Koaloader)
{
foreach (string directory in selection.ExecutableDirectories)
foreach ((string directory, BinaryType binaryType) in selection.ExecutableDirectories)
{
directory.GetKoaloaderComponents(out List<string> proxies, out string config);
if (proxies.Any(proxy => File.Exists(proxy) && proxy.IsResourceFile(Resources.Resources.ResourceIdentifier.Koaloader))
if (proxies.Any(proxy => File.Exists(proxy) && proxy.IsResourceFile(ResourceIdentifier.Koaloader))
|| Koaloader.AutoLoadDlls.Any(pair => File.Exists(directory + @"\" + pair.dll))
|| File.Exists(config))
{
@ -162,10 +166,10 @@ internal partial class InstallForm : CustomForm
}
if (selection.Koaloader && !Uninstalling)
{
foreach (string directory in selection.ExecutableDirectories)
foreach ((string directory, BinaryType binaryType) in selection.ExecutableDirectories)
{
UpdateUser("Installing Koaloader to " + selection.Name + $" in directory \"{directory}\" . . . ", InstallationLog.Operation);
await Koaloader.Install(directory, selection, this);
await Koaloader.Install(directory, binaryType, selection, this);
}
}
UpdateProgress(100);

View file

@ -21,6 +21,8 @@ using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using static CreamInstaller.Resources.Resources;
namespace CreamInstaller;
internal partial class SelectForm : CustomForm
@ -728,14 +730,13 @@ internal partial class SelectForm : CustomForm
contextMenuStrip.Items.Add(new ToolStripSeparator());
contextMenuStrip.Items.Add(new ContextMenuItem("Open Root Directory", "File Explorer",
new EventHandler((sender, e) => Diagnostics.OpenDirectoryInFileExplorer(selection.RootDirectory))));
List<string> directories = selection.ExecutableDirectories.ToList();
int executables = 0;
foreach (string directory in directories)
foreach ((string directory, BinaryType binaryType) in selection.ExecutableDirectories.ToList())
{
contextMenuStrip.Items.Add(new ContextMenuItem($"Open Executable Directory #{++executables}", "File Explorer",
contextMenuStrip.Items.Add(new ContextMenuItem($"Open Executable Directory #{++executables} ({(binaryType == BinaryType.BIT32 ? "32" : "64")}-bit)", "File Explorer",
new EventHandler((sender, e) => Diagnostics.OpenDirectoryInFileExplorer(directory))));
}
directories = selection.DllDirectories.ToList();
List<string> directories = selection.DllDirectories.ToList();
int steam = 0, epic = 0, r1 = 0, r2 = 0;
if (selection.Platform is Platform.Steam or Platform.Paradox)
foreach (string directory in directories)

View file

@ -27,8 +27,8 @@ internal static class ParadoxLauncher
}
}
internal static async Task<List<string>> GetExecutableDirectories(string gameDirectory) =>
await Task.Run(async () => await gameDirectory.GetExecutableDirectories(validFunc: d => !Path.GetFileName(d).Contains("bootstrapper")));
internal static async Task<List<(string directory, BinaryType binaryType)>> GetExecutableDirectories(string gameDirectory) =>
await Task.Run(async () => await gameDirectory.GetExecutableDirectories(validFunc: path => !Path.GetFileName(path).Contains("bootstrapper")));
private static void PopulateDlc(ProgramSelection paradoxLauncher = null)
{

View file

@ -5,6 +5,8 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using static CreamInstaller.Resources.Resources;
namespace CreamInstaller;
public enum Platform
@ -43,7 +45,7 @@ internal class ProgramSelection
internal string WebsiteUrl;
internal string RootDirectory;
internal List<string> ExecutableDirectories;
internal List<(string directory, BinaryType binaryType)> ExecutableDirectories;
internal List<string> DllDirectories;
internal readonly SortedList<string, (DlcType type, string name, string icon)> AllDlc = new(PlatformIdComparer.String);

View file

@ -7,6 +7,8 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static CreamInstaller.Resources.Resources;
namespace CreamInstaller.Resources;
internal static class Koaloader
@ -18,7 +20,7 @@ internal static class Koaloader
)
{
proxies = new();
foreach (string proxy in Resources.EmbeddedResources.Select(proxy =>
foreach (string proxy in EmbeddedResources.Select(proxy =>
{
proxy = proxy[(proxy.IndexOf('.') + 1)..];
return proxy[(proxy.IndexOf('.') + 1)..];
@ -26,13 +28,26 @@ internal static class Koaloader
config = directory + @"\Koaloader.json";
}
internal static string GetKoaloaderProxyDisplay(this string proxy)
internal static void WriteProxy(this string path, string proxyName, BinaryType binaryType)
{
string proxyDisplay = proxy[(proxy.IndexOf('.') + 1)..];
proxyDisplay = proxyDisplay[..proxyDisplay.IndexOf('.')];
string name = proxyDisplay[..proxyDisplay.LastIndexOf('_')];
string bitness = proxyDisplay[(proxyDisplay.LastIndexOf('_') + 1)..];
return $"{name}.dll ({bitness}-bit)";
foreach (string resourceIdentifier in EmbeddedResources.FindAll(r => r.StartsWith("Koaloader")))
{
resourceIdentifier.GetProxyInfoFromIdentifier(out string _proxyName, out BinaryType _binaryType);
if (_proxyName == proxyName && _binaryType == binaryType)
{
resourceIdentifier.Write(path);
break;
}
}
}
internal static void GetProxyInfoFromIdentifier(this string resourceIdentifier, out string proxyName, out BinaryType binaryType)
{
string baseIdentifier = resourceIdentifier[(resourceIdentifier.IndexOf('.') + 1)..];
baseIdentifier = baseIdentifier[..baseIdentifier.IndexOf('.')];
proxyName = baseIdentifier[..baseIdentifier.LastIndexOf('_')];
string bitness = baseIdentifier[(baseIdentifier.LastIndexOf('_') + 1)..];
binaryType = bitness == "32" ? BinaryType.BIT32 : bitness == "64" ? BinaryType.BIT64 : BinaryType.Unknown;
}
internal static readonly List<(string unlocker, string dll)> AutoLoadDlls = new()
@ -137,22 +152,19 @@ internal static class Koaloader
await UplayR2.Uninstall(directory, installForm, deleteConfig);
});
internal static async Task Install(string directory, ProgramSelection selection, InstallForm installForm = null, bool generateConfig = true) => await Task.Run(() =>
internal static async Task Install(string directory, BinaryType binaryType, ProgramSelection selection, InstallForm installForm = null, bool generateConfig = true) => await Task.Run(() =>
{
directory.GetKoaloaderComponents(out List<string> proxies, out string config);
string proxy = selection.KoaloaderProxy;
proxy = proxy[(proxy.IndexOf('.') + 1)..];
proxy = proxy[(proxy.IndexOf('.') + 1)..];
string path = directory + @"\" + proxy;
foreach (string proxyPath in proxies.Where(proxyPath => proxyPath != path && File.Exists(proxyPath) && proxyPath.IsResourceFile(Resources.ResourceIdentifier.Koaloader)))
string path = directory + @"\" + selection.KoaloaderProxy + ".dll";
foreach (string _path in proxies.Where(p => p != path && File.Exists(p) && p.IsResourceFile(ResourceIdentifier.Koaloader)))
{
File.Delete(proxyPath);
File.Delete(_path);
if (installForm is not null)
installForm.UpdateUser($"Deleted Koaloader: {Path.GetFileName(proxyPath)}", InstallationLog.Action, info: false);
installForm.UpdateUser($"Deleted Koaloader: {Path.GetFileName(_path)}", InstallationLog.Action, info: false);
}
selection.KoaloaderProxy.Write(path);
path.WriteProxy(selection.KoaloaderProxy, binaryType);
if (installForm is not null)
installForm.UpdateUser($"Wrote Koaloader: {Path.GetFileName(path)}", InstallationLog.Action, info: false);
installForm.UpdateUser($"Wrote {(binaryType == BinaryType.BIT32 ? "32-bit" : "64-bit")} Koaloader: {Path.GetFileName(path)}", InstallationLog.Action, info: false);
if (selection.Platform is Platform.Steam or Platform.Paradox)
{
bool did32 = false, did64 = false;

View file

@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
@ -29,9 +30,9 @@ internal static class Resources
}
}
internal static void Write(this string resourceName, string filePath)
internal static void Write(this string resourceIdentifier, string filePath)
{
using Stream resource = Assembly.GetExecutingAssembly().GetManifestResourceStream("CreamInstaller.Resources." + resourceName);
using Stream resource = Assembly.GetExecutingAssembly().GetManifestResourceStream("CreamInstaller.Resources." + resourceIdentifier);
using FileStream file = new(filePath, FileMode.Create, FileAccess.Write);
resource.CopyTo(file);
}
@ -59,6 +60,23 @@ internal static class Resources
return false;
}
internal enum BinaryType
{
Unknown = -1,
BIT32 = 0,
DOS = 1,
WOW = 2,
PIF = 3,
POSIX = 4,
OS216 = 5,
BIT64 = 6,
}
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
private static extern bool GetBinaryType(string lpApplicationName, out BinaryType lpBinaryType);
internal static bool TryGetFileBinaryType(this string path, out BinaryType binaryType) => GetBinaryType(path, out binaryType);
internal static bool IsCommonIncorrectExecutable(this string rootDirectory, string path)
{
string subPath = path[rootDirectory.Length..].ToUpperInvariant().BeautifyPath();
@ -66,35 +84,53 @@ internal static class Resources
|| subPath.Contains("CRASH") && (subPath.Contains("PAD") || subPath.Contains("REPORT"));
}
internal static async Task<List<string>> GetExecutables(this string rootDirectory, string subDirectory = null, bool filterCommon = false, Func<string, bool> validFunc = null) => await Task.Run(async () =>
{
List<string> executables = new();
if (Program.Canceled || !Directory.Exists(subDirectory ?? rootDirectory)) return null;
Thread.Sleep(0);
foreach (string path in Directory.GetFiles(subDirectory ?? rootDirectory, "*.exe"))
internal static async Task<List<(string path, BinaryType binaryType)>> GetExecutables(this string rootDirectory, string subDirectory = null, bool filterCommon = false, Func<string, bool> validFunc = null) =>
await Task.Run(async () =>
{
List<(string path, BinaryType binaryType)> executables = new();
if (Program.Canceled || !Directory.Exists(subDirectory ?? rootDirectory)) return null;
Thread.Sleep(0);
if ((!filterCommon || !rootDirectory.IsCommonIncorrectExecutable(path)) && (validFunc is null || validFunc(path)))
executables.Add(path);
}
string[] directories = Directory.GetDirectories(subDirectory ?? rootDirectory);
foreach (string directory in directories)
{
if (Program.Canceled) return null;
Thread.Sleep(0);
try
string[] files = Directory.GetFiles(subDirectory ?? rootDirectory, "*.exe");
foreach (string path in files)
{
List<string> moreExecutables = await rootDirectory.GetExecutables(directory, filterCommon, validFunc);
if (moreExecutables is not null)
executables.AddRange(moreExecutables);
Thread.Sleep(0);
if (!executables.Any(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));
}
catch { }
}
return !executables.Any() ? null : executables;
});
foreach (string path in files)
{
Thread.Sleep(0);
if (!executables.Any(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));
}
string[] directories = Directory.GetDirectories(subDirectory ?? rootDirectory);
foreach (string directory in directories)
{
if (Program.Canceled) return null;
Thread.Sleep(0);
try
{
List<(string path, BinaryType binaryType)> moreExecutables = await rootDirectory.GetExecutables(directory, filterCommon, validFunc);
if (moreExecutables is not null)
executables.AddRange(moreExecutables);
}
catch { }
}
return !executables.Any() ? null : executables;
});
internal static async Task<List<string>> GetExecutableDirectories(this string rootDirectory, bool filterCommon = false, Func<string, bool> validFunc = null) => await Task.Run(async () =>
(await rootDirectory.GetExecutables(filterCommon: filterCommon, validFunc: validFunc) ?? await rootDirectory.GetExecutables()).Select(e => e = Path.GetDirectoryName(e)).Distinct().ToList());
internal static async Task<List<(string directory, BinaryType binaryType)>> GetExecutableDirectories(this string rootDirectory, bool filterCommon = false, Func<string, bool> validFunc = null) =>
await Task.Run(async () => (await rootDirectory.GetExecutables(filterCommon: filterCommon, validFunc: validFunc) ?? await rootDirectory.GetExecutables()).Select(e =>
{
e.path = Path.GetDirectoryName(e.path);
return e;
}).DistinctBy(e => e.path).ToList());
internal static void GetCreamApiComponents(
this string directory,

View file

@ -11,6 +11,8 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static CreamInstaller.Resources.Resources;
namespace CreamInstaller.Steam;
internal static class SteamLibrary
@ -26,7 +28,7 @@ internal static class SteamLibrary
}
}
internal static async Task<List<string>> GetExecutableDirectories(string gameDirectory) =>
internal static async Task<List<(string directory, BinaryType binaryType)>> GetExecutableDirectories(string gameDirectory) =>
await Task.Run(async () => await gameDirectory.GetExecutableDirectories(filterCommon: true));
internal static async Task<List<(string appId, string name, string branch, int buildId, string gameDirectory)>> GetGames() => await Task.Run(async () =>

View file

@ -8,6 +8,8 @@ using System.IO;
using System.Linq;
using System.Threading.Tasks;
using static CreamInstaller.Resources.Resources;
namespace CreamInstaller.Ubisoft;
internal static class UbisoftLibrary
@ -23,7 +25,7 @@ internal static class UbisoftLibrary
}
}
internal static async Task<List<string>> GetExecutableDirectories(string gameDirectory) =>
internal static async Task<List<(string directory, BinaryType binaryType)>> GetExecutableDirectories(string gameDirectory) =>
await Task.Run(async () => await gameDirectory.GetExecutableDirectories(filterCommon: true));
internal static async Task<List<(string gameId, string name, string gameDirectory)>> GetGames() => await Task.Run(() =>

View file

@ -1,4 +1,6 @@
using Newtonsoft.Json;
using CreamInstaller.Resources;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
@ -132,14 +134,17 @@ internal static class ProgramData
internal static void UpdateKoaloaderProxyChoices()
{
string defaultProxy = Resources.Resources.EmbeddedResources.Find(r => r.Contains("version_64"));
string defaultProxy = "version";
List<(Platform platform, string id, string proxy)> choices = ReadKoaloaderProxyChoices() ?? new();
foreach ((Platform platform, string id, string proxy) choice in choices.ToList())
if (ProgramSelection.FromPlatformId(choice.platform, choice.id) is ProgramSelection selection)
{
string proxy = choice.proxy;
if (proxy.Contains('.')) // convert pre-v4.1.0.0 choices
proxy.GetProxyInfoFromIdentifier(out proxy, out _);
if (selection.KoaloaderProxy is null)
selection.KoaloaderProxy = choice.proxy;
else if (selection.KoaloaderProxy != choice.proxy && choices.Remove(choice))
selection.KoaloaderProxy = proxy;
else if (selection.KoaloaderProxy != proxy && choices.Remove(choice))
choices.Add((selection.Platform, selection.Id, selection.KoaloaderProxy));
}
foreach (ProgramSelection selection in ProgramSelection.AllSafe)