- Fixed parsing of DLC appid 0
This commit is contained in:
pointfeev 2022-12-20 19:25:08 -05:00
parent 99bd3e2425
commit 8572c5f03b
10 changed files with 322 additions and 322 deletions

View file

@ -13,6 +13,32 @@ internal class ContextMenuItem : ToolStripMenuItem
{
private static readonly ConcurrentDictionary<string, Image> images = new();
private readonly EventHandler OnClickEvent;
internal ContextMenuItem(string text, EventHandler onClick = null)
{
Text = text;
OnClickEvent = onClick;
}
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) => _ = TryImageIdentifierInfo(this, imageIdentifierInfo);
internal ContextMenuItem(string text, (string id, string iconUrl) imageIdentifierInfo,
string imageIdentifierFallback, EventHandler onClick = null)
: this(text, onClick) => _ = TryImageIdentifierInfo(this, imageIdentifierInfo,
async () => await TryImageIdentifier(
this, imageIdentifierFallback));
internal ContextMenuItem(string text, (string id, string iconUrl) imageIdentifierInfo,
(string id, string iconUrl) imageIdentifierInfoFallback, EventHandler onClick = null)
: this(text, onClick) => _ = TryImageIdentifierInfo(this, imageIdentifierInfo,
async () => await TryImageIdentifierInfo(
this, imageIdentifierInfoFallback));
private static async Task TryImageIdentifier(ContextMenuItem item, string imageIdentifier) => await Task.Run(
async () =>
{
@ -101,36 +127,10 @@ internal class ContextMenuItem : ToolStripMenuItem
}
});
private readonly EventHandler OnClickEvent;
protected override void OnClick(EventArgs e)
{
base.OnClick(e);
if (OnClickEvent is null) return;
OnClickEvent.Invoke(this, e);
}
internal ContextMenuItem(string text, EventHandler onClick = null)
{
Text = text;
OnClickEvent = onClick;
}
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) => _ = TryImageIdentifierInfo(this, imageIdentifierInfo);
internal ContextMenuItem(string text, (string id, string iconUrl) imageIdentifierInfo,
string imageIdentifierFallback, EventHandler onClick = null)
: this(text, onClick) => _ = TryImageIdentifierInfo(this, imageIdentifierInfo,
async () => await TryImageIdentifier(
this, imageIdentifierFallback));
internal ContextMenuItem(string text, (string id, string iconUrl) imageIdentifierInfo,
(string id, string iconUrl) imageIdentifierInfoFallback, EventHandler onClick = null)
: this(text, onClick) => _ = TryImageIdentifierInfo(this, imageIdentifierInfo,
async () => await TryImageIdentifierInfo(
this, imageIdentifierInfoFallback));
}

View file

@ -15,8 +15,8 @@ internal class CustomForm : Form
internal const short SWP_NOMOVE = 0x0002;
internal const short SWP_NOSIZE = 0x0001;
internal static readonly IntPtr HWND_NOTOPMOST = new(-2);
internal static readonly IntPtr HWND_TOPMOST = new(-1);
internal static readonly nint HWND_NOTOPMOST = new(-2);
internal static readonly nint HWND_TOPMOST = new(-1);
internal CustomForm()
{
@ -96,7 +96,7 @@ internal class CustomForm : Form
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
internal static extern void SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy,
internal static extern void SetWindowPos(nint hWnd, nint hWndInsertAfter, int x, int y, int cx, int cy,
uint uFlags);
internal void BringToFrontWithoutActivation()

View file

@ -8,15 +8,15 @@ namespace CreamInstaller.Components;
internal static class PlatformIdComparer
{
private static StringComparer stringComparer;
internal static StringComparer String => stringComparer ??= new StringComparer();
private static NodeComparer nodeComparer;
internal static NodeComparer Node => nodeComparer ??= new NodeComparer();
private static NodeNameComparer nodeNameComparer;
internal static NodeNameComparer NodeName => nodeNameComparer ??= new NodeNameComparer();
private static NodeTextComparer nodeTextComparer;
internal static StringComparer String => stringComparer ??= new StringComparer();
internal static NodeComparer Node => nodeComparer ??= new NodeComparer();
internal static NodeNameComparer NodeName => nodeNameComparer ??= new NodeNameComparer();
internal static NodeTextComparer NodeText => nodeTextComparer ??= new NodeTextComparer();
}

View file

@ -4,7 +4,7 @@
<TargetFramework>net7.0-windows</TargetFramework>
<UseWindowsForms>True</UseWindowsForms>
<ApplicationIcon>Resources\ini.ico</ApplicationIcon>
<Version>4.3.0.0</Version>
<Version>4.3.0.1</Version>
<Copyright>2021, pointfeev (https://github.com/pointfeev)</Copyright>
<Company>CreamInstaller</Company>
<Product>Automatic DLC Unlocker Installer &amp; Configuration Generator</Product>

View file

@ -9,6 +9,8 @@ namespace CreamInstaller.Platforms.Epic.GraphQL;
internal class Request
{
internal Request(string @namespace) => Vars = new Variables(@namespace);
[JsonProperty(PropertyName = "query")]
private string Query => @"query searchOffers($namespace: String!) {
Catalog {
@ -58,8 +60,6 @@ internal class Request
[JsonProperty(PropertyName = "variables")]
private Variables Vars { get; set; }
internal Request(string @namespace) => Vars = new Variables(@namespace);
private class Headers
{
[JsonProperty(PropertyName = "Content-Type")]
@ -68,9 +68,9 @@ internal class Request
private class Variables
{
internal Variables(string @namespace) => Namespace = @namespace;
[JsonProperty(PropertyName = "namespace")]
private string Namespace { get; set; }
internal Variables(string @namespace) => Namespace = @namespace;
}
}

View file

@ -217,7 +217,7 @@ internal static class SteamCMD
goto restart;
}
}
if (Program.Canceled || output is null) return null;
if (Program.Canceled) return null;
if (!ValveDataFile.TryDeserialize(output, out VProperty appInfo) || appInfo.Value is VValue)
{
File.Delete(appUpdateFile);
@ -225,62 +225,61 @@ internal static class SteamCMD
}
if (appInfo is null || appInfo.Value?.Children()?.ToList()?.Count == 0) return appInfo;
VToken type = appInfo.Value?.GetChild("common")?.GetChild("type");
if (type is null || type.ToString() == "Game")
{
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 null || (int.TryParse(buildid, out int gamebuildId) && gamebuildId < buildId))
{
List<string> dlcAppIds = await ParseDlcAppIds(appInfo);
foreach (string dlcAppUpdateFile in dlcAppIds.Select(id => $@"{AppInfoPath}\{id}.vdf"))
if (File.Exists(dlcAppUpdateFile))
File.Delete(dlcAppUpdateFile);
if (File.Exists(appUpdateFile)) File.Delete(appUpdateFile);
goto restart;
}
}
return appInfo;
if (type is not null && type.ToString() != "Game")
return appInfo;
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))
return appInfo;
List<string> dlcAppIds = await ParseDlcAppIds(appInfo);
foreach (string dlcAppUpdateFile in dlcAppIds.Select(id => $@"{AppInfoPath}\{id}.vdf"))
if (File.Exists(dlcAppUpdateFile))
File.Delete(dlcAppUpdateFile);
if (File.Exists(appUpdateFile)) File.Delete(appUpdateFile);
goto restart;
}
internal static async Task<List<string>> ParseDlcAppIds(VProperty appInfo) => await Task.Run(() =>
{
List<string> dlcIds = new();
#pragma warning disable IDE0150 // Prefer 'null' check over type check
if (Program.Canceled || appInfo is not VProperty) return dlcIds;
#pragma warning restore IDE0150 // Prefer 'null' check over type check
if (Program.Canceled || appInfo is null) return dlcIds;
VToken extended = appInfo.Value.GetChild("extended");
if (extended is not null)
#pragma warning disable IDE0220 // Add explicit cast
foreach (VProperty property in extended.Where(p => p is VProperty && (p as VProperty).Key == "listofdlc"))
foreach (VToken vToken in extended.Where(p => p is VProperty { Key: "listofdlc" }))
{
VProperty property = (VProperty)vToken;
foreach (string id in property.Value.ToString().Split(","))
if (int.TryParse(id, out int appId) && !dlcIds.Contains("" + appId))
if (int.TryParse(id, out int appId) && appId > 0 && !dlcIds.Contains("" + appId))
dlcIds.Add("" + appId);
}
VToken depots = appInfo.Value.GetChild("depots");
if (depots is not null)
foreach (VProperty property in depots.Where(
p => p is VProperty && int.TryParse((p as VProperty).Key, out int _)))
if (int.TryParse(property.Value.GetChild("dlcappid")?.ToString(), out int appId)
&& !dlcIds.Contains("" + appId))
dlcIds.Add("" + appId);
#pragma warning restore IDE0220 // Add explicit cast
if (depots is null)
return dlcIds;
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.Contains("" + appId))
dlcIds.Add("" + appId);
}
return dlcIds;
});
internal static async Task Kill()
private static async Task Kill()
{
List<Task> tasks = new();
foreach (Process process in Process.GetProcessesByName("steamcmd"))
tasks.Add(Task.Run(() =>
{
try
{
process.Kill(true);
process.WaitForExit();
process.Close();
}
catch { }
}));
List<Task> tasks = Process.GetProcessesByName("steamcmd")
.Select(process => Task.Run(() =>
{
try
{
process.Kill(true);
process.WaitForExit();
process.Close();
}
catch { }
}))
.ToList();
foreach (Task task in tasks) await task;
}

View file

@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using CreamInstaller.Forms;
@ -21,8 +22,7 @@ internal static class SteamStore
{
List<string> dlcIds = new();
if (appData.dlc is null) return dlcIds;
foreach (int appId in appData.dlc)
dlcIds.Add(appId.ToString());
dlcIds.AddRange(from appId in appData.dlc where appId > 0 select appId.ToString());
return dlcIds;
});

View file

@ -21,39 +21,44 @@ public enum DlcType
internal class ProgramSelection
{
internal bool Enabled;
internal bool Koaloader;
internal const string DefaultKoaloaderProxy = "version";
internal string KoaloaderProxy;
internal Platform Platform;
internal string Id = "0";
internal string Name = "Program";
internal string ProductUrl;
internal string IconUrl;
internal string SubIconUrl;
internal string Publisher;
internal string WebsiteUrl;
internal string RootDirectory;
internal List<(string directory, BinaryType binaryType)> ExecutableDirectories;
internal List<string> DllDirectories;
internal static readonly List<ProgramSelection> All = new();
internal readonly SortedList<string, (DlcType type, string name, string icon)> AllDlc
= new(PlatformIdComparer.String);
internal readonly SortedList<string, (DlcType type, string name, string icon)> SelectedDlc
= new(PlatformIdComparer.String);
internal readonly List<(string id, string name, SortedList<string, (DlcType type, string name, string icon)> dlc)>
ExtraDlc = new(); // for Paradox Launcher
internal readonly List<(string id, string name, SortedList<string, (DlcType type, string name, string icon)> dlc)>
ExtraSelectedDlc = new(); // for Paradox Launcher
internal readonly SortedList<string, (DlcType type, string name, string icon)> SelectedDlc
= new(PlatformIdComparer.String);
internal List<string> DllDirectories;
internal bool Enabled;
internal List<(string directory, BinaryType binaryType)> ExecutableDirectories;
internal string IconUrl;
internal string Id = "0";
internal bool Koaloader;
internal string KoaloaderProxy;
internal string Name = "Program";
internal Platform Platform;
internal string ProductUrl;
internal string Publisher;
internal string RootDirectory;
internal string SubIconUrl;
internal string WebsiteUrl;
internal ProgramSelection() => All.Add(this);
internal bool AreDllsLocked
{
get
@ -117,6 +122,10 @@ internal class ProgramSelection
}
}
internal static List<ProgramSelection> AllSafe => All.ToList();
internal static List<ProgramSelection> AllEnabled => AllSafe.FindAll(s => s.Enabled);
private void Toggle(string dlcAppId, (DlcType type, string name, string icon) dlcApp, bool enabled)
{
if (enabled) SelectedDlc[dlcAppId] = dlcApp;
@ -138,8 +147,6 @@ internal class ProgramSelection
Enabled = SelectedDlc.Any() || ExtraSelectedDlc.Any();
}
internal ProgramSelection() => All.Add(this);
internal void Validate()
{
if (Program.IsGameBlocked(Name, RootDirectory))
@ -171,12 +178,6 @@ internal class ProgramSelection
internal static void ValidateAll(List<(Platform platform, string id, string name)> programsToScan)
=> AllSafe.ForEach(selection => selection.Validate(programsToScan));
internal static readonly List<ProgramSelection> All = new();
internal static List<ProgramSelection> AllSafe => All.ToList();
internal static List<ProgramSelection> AllEnabled => AllSafe.FindAll(s => s.Enabled);
internal static ProgramSelection FromPlatformId(Platform platform, string gameId)
=> AllSafe.Find(s => s.Platform == platform && s.Id == gameId);

View file

@ -15,212 +15,6 @@ internal static class Resources
{
internal static List<string> embeddedResources;
internal static List<string> EmbeddedResources
{
get
{
if (embeddedResources is null)
{
string[] names = Assembly.GetExecutingAssembly().GetManifestResourceNames();
embeddedResources = new List<string>();
foreach (string resourceName in names.Where(n => n.StartsWith("CreamInstaller.Resources.")))
embeddedResources.Add(resourceName[25..]);
}
return embeddedResources;
}
}
internal static void Write(this string resourceIdentifier, string filePath)
{
using Stream resource = Assembly.GetExecutingAssembly()
.GetManifestResourceStream("CreamInstaller.Resources." + resourceIdentifier);
using FileStream file = new(filePath, FileMode.Create, FileAccess.Write);
resource.CopyTo(file);
}
internal static void Write(this byte[] resource, string filePath)
{
using FileStream fileStream = new(filePath, FileMode.Create, FileAccess.Write);
fileStream.Write(resource);
}
internal static bool IsFilePathLocked(this string filePath)
{
try
{
File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None).Close();
}
catch (FileNotFoundException)
{
return false;
}
catch (IOException)
{
return true;
}
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 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, validFunc)
?? (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<List<(string path, BinaryType binaryType)>> GetExecutables(
this string rootDirectory, bool filterCommon = false, Func<string, bool> validFunc = null) => await Task.Run(
() =>
{
List<(string path, BinaryType binaryType)> executables = new();
if (Program.Canceled || !Directory.Exists(rootDirectory)) return null;
foreach (string path in Directory.EnumerateFiles(rootDirectory, "*.exe",
new EnumerationOptions { RecurseSubdirectories = true }))
{
if (Program.Canceled) return null;
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));
Thread.Sleep(1);
}
foreach (string path in Directory.EnumerateFiles(rootDirectory, "*.exe",
new EnumerationOptions { RecurseSubdirectories = true }))
{
if (Program.Canceled) return null;
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));
Thread.Sleep(1);
}
return !executables.Any() ? null : executables;
});
internal static bool IsCommonIncorrectExecutable(this string rootDirectory, string path)
{
string subPath = path[rootDirectory.Length..].ToUpperInvariant().BeautifyPath();
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");
}
internal static async Task<List<string>> GetDllDirectoriesFromGameDirectory(
this string gameDirectory, Platform platform) => await Task.Run(() =>
{
List<string> dllDirectories = new();
if (Program.Canceled || !Directory.Exists(gameDirectory)) return null;
foreach (string directory in Directory
.EnumerateDirectories(gameDirectory, "*",
new EnumerationOptions { RecurseSubdirectories = true })
.Append(gameDirectory))
{
if (Program.Canceled) return null;
string subDirectory = directory.BeautifyPath();
if (!dllDirectories.Contains(subDirectory))
{
bool koaloaderInstalled = Koaloader.AutoLoadDlls
.Select(pair => (pair.unlocker, path: directory + @"\" + pair.dll))
.Any(pair => File.Exists(pair.path) && 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 config, out string cache);
if (File.Exists(api)
|| File.Exists(api_o)
|| File.Exists(api64)
|| File.Exists(api64_o)
|| (File.Exists(config) && !koaloaderInstalled)
|| (File.Exists(cache) && !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);
if (File.Exists(api32)
|| File.Exists(api32_o)
|| File.Exists(api64)
|| File.Exists(api64_o)
|| (File.Exists(config) && !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);
if (File.Exists(api32)
|| File.Exists(api32_o)
|| File.Exists(api64)
|| File.Exists(api64_o)
|| (File.Exists(config) && !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);
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) && !koaloaderInstalled))
dllDirectories.Add(subDirectory);
}
}
}
return !dllDirectories.Any() ? null : dllDirectories;
});
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";
api32_o = directory + @"\steam_api_o.dll";
api64 = directory + @"\steam_api64.dll";
api64_o = directory + @"\steam_api64_o.dll";
config = directory + @"\cream_api.ini";
}
internal enum ResourceIdentifier
{
Koaloader, Steamworks32, Steamworks64,
EpicOnlineServices32, EpicOnlineServices64, Uplay32,
Uplay64, Upc32, Upc64
}
internal static readonly Dictionary<ResourceIdentifier, IReadOnlyList<string>> ResourceMD5s = new()
{
{
@ -534,6 +328,198 @@ internal static class Resources
}
};
internal static List<string> EmbeddedResources
{
get
{
if (embeddedResources is null)
{
string[] names = Assembly.GetExecutingAssembly().GetManifestResourceNames();
embeddedResources = new List<string>();
foreach (string resourceName in names.Where(n => n.StartsWith("CreamInstaller.Resources.")))
embeddedResources.Add(resourceName[25..]);
}
return embeddedResources;
}
}
internal static void Write(this string resourceIdentifier, string filePath)
{
using Stream resource = Assembly.GetExecutingAssembly()
.GetManifestResourceStream("CreamInstaller.Resources." + resourceIdentifier);
using FileStream file = new(filePath, FileMode.Create, FileAccess.Write);
resource.CopyTo(file);
}
internal static void Write(this byte[] resource, string filePath)
{
using FileStream fileStream = new(filePath, FileMode.Create, FileAccess.Write);
fileStream.Write(resource);
}
internal static bool IsFilePathLocked(this string filePath)
{
try
{
File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None).Close();
}
catch (FileNotFoundException)
{
return false;
}
catch (IOException)
{
return true;
}
return false;
}
[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 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, validFunc)
?? (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<List<(string path, BinaryType binaryType)>> GetExecutables(
this string rootDirectory, bool filterCommon = false, Func<string, bool> validFunc = null) => await Task.Run(
() =>
{
List<(string path, BinaryType binaryType)> executables = new();
if (Program.Canceled || !Directory.Exists(rootDirectory)) return null;
foreach (string path in Directory.EnumerateFiles(rootDirectory, "*.exe",
new EnumerationOptions { RecurseSubdirectories = 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.BIT64)
executables.Add((path, binaryType));
Thread.Sleep(1);
}
foreach (string path in Directory.EnumerateFiles(rootDirectory, "*.exe",
new EnumerationOptions { RecurseSubdirectories = 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)
executables.Add((path, binaryType));
Thread.Sleep(1);
}
return !executables.Any() ? null : executables;
});
internal static bool IsCommonIncorrectExecutable(this string rootDirectory, string path)
{
string subPath = path[rootDirectory.Length..].ToUpperInvariant().BeautifyPath();
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");
}
internal static async Task<List<string>> GetDllDirectoriesFromGameDirectory(
this string gameDirectory, Platform platform) => await Task.Run(() =>
{
List<string> dllDirectories = new();
if (Program.Canceled || !Directory.Exists(gameDirectory)) return null;
foreach (string directory in Directory
.EnumerateDirectories(gameDirectory, "*",
new EnumerationOptions { RecurseSubdirectories = true })
.Append(gameDirectory))
{
if (Program.Canceled) return null;
string subDirectory = directory.BeautifyPath();
if (!dllDirectories.Contains(subDirectory))
{
bool koaloaderInstalled = Koaloader.AutoLoadDlls
.Select(pair => (pair.unlocker, path: directory + @"\" + pair.dll))
.Any(pair => File.Exists(pair.path) && 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 config, out string cache);
if (File.Exists(api)
|| File.Exists(api_o)
|| File.Exists(api64)
|| File.Exists(api64_o)
|| (File.Exists(config) && !koaloaderInstalled)
|| (File.Exists(cache) && !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);
if (File.Exists(api32)
|| File.Exists(api32_o)
|| File.Exists(api64)
|| File.Exists(api64_o)
|| (File.Exists(config) && !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);
if (File.Exists(api32)
|| File.Exists(api32_o)
|| File.Exists(api64)
|| File.Exists(api64_o)
|| (File.Exists(config) && !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);
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) && !koaloaderInstalled))
dllDirectories.Add(subDirectory);
}
}
}
return !dllDirectories.Any() ? null : dllDirectories;
});
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";
api32_o = directory + @"\steam_api_o.dll";
api64 = directory + @"\steam_api64.dll";
api64_o = directory + @"\steam_api64_o.dll";
config = directory + @"\cream_api.ini";
}
internal static string ComputeMD5(this string filePath)
{
if (!File.Exists(filePath)) return null;
@ -551,4 +537,18 @@ internal static class Resources
internal static bool IsResourceFile(this string filePath) => filePath.ComputeMD5() is string hash
&& ResourceMD5s.Values.Any(
hashes => hashes.Contains(hash));
internal enum BinaryType
{
Unknown = -1, BIT32 = 0, DOS = 1,
WOW = 2, PIF = 3, POSIX = 4,
OS216 = 5, BIT64 = 6
}
internal enum ResourceIdentifier
{
Koaloader, Steamworks32, Steamworks64,
EpicOnlineServices32, EpicOnlineServices64, Uplay32,
Uplay64, Upc32, Upc64
}
}

View file

@ -6,17 +6,17 @@ namespace CreamInstaller.Utility;
internal static class IconGrabber
{
internal const string SteamAppImagesPath
= "https://cdn.cloudflare.steamstatic.com/steamcommunity/public/images/apps/";
internal const string GoogleFaviconsApiUrl = "https://www.google.com/s2/favicons";
internal static Icon ToIcon(this Image image)
{
using Bitmap dialogIconBitmap = new(image, new Size(image.Width, image.Height));
return Icon.FromHandle(dialogIconBitmap.GetHicon());
}
internal const string SteamAppImagesPath
= "https://cdn.cloudflare.steamstatic.com/steamcommunity/public/images/apps/";
internal const string GoogleFaviconsApiUrl = "https://www.google.com/s2/favicons";
internal static string GetDomainFaviconUrl(string domain, int size = 16)
=> GoogleFaviconsApiUrl + $"?domain={domain}&sz={size}";