Skip to content

Commit

Permalink
Add assembly framework and architecture checks for xna launcher
Browse files Browse the repository at this point in the history
  • Loading branch information
bbepis committed Jan 22, 2022
1 parent e21677f commit 3f172db
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 3 deletions.
24 changes: 24 additions & 0 deletions BepInEx.NetLauncher/NetPreloader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using BepInEx.Preloader.Core.Logging;
using BepInEx.Preloader.Core.Patching;
using HarmonyLib;
using Mono.Cecil;
using MonoMod.Utils;

namespace BepInEx.NetLauncher;
Expand Down Expand Up @@ -78,6 +79,29 @@ public static void Start(string[] args)

Log.Log(LogLevel.Info, $"CLR runtime version: {Environment.Version}");


AssemblyBuildInfo executableInfo, launcherInfo;

using (var executableAssembly = AssemblyDefinition.ReadAssembly(executablePath))
executableInfo = AssemblyBuildInfo.DetermineInfo(executableAssembly);

using (var launcherAssembly = AssemblyDefinition.ReadAssembly(typeof(NetPreloader).Assembly.Location))
launcherInfo = AssemblyBuildInfo.DetermineInfo(launcherAssembly);

// we don't particularly care about AnyCPU here since the fallback bitness is almost never the case
if (executableInfo.Is64Bit != launcherInfo.Is64Bit)
{
Log.LogError($"Game executable is {(executableInfo.Is64Bit ? "64" : "32")}-bit while BepInEx has been compiled as {(launcherInfo.Is64Bit ? "64" : "32")}-bit. Expect crashes");
}

if (executableInfo.NetFrameworkVersion != launcherInfo.NetFrameworkVersion)
{
Log.LogWarning($"Game executable has been compiled as .NET Framework {executableInfo.NetFrameworkVersion}, while BepInEx has been compiled as .NET Framework {launcherInfo.NetFrameworkVersion}. There may be issues within the game caused by this");
}

Log.LogInfo($"Game executable build architecture: {executableInfo}");
Log.LogInfo($"BepInEx launcher build architecture: {launcherInfo}");

Log.Log(LogLevel.Message, "Preloader started");

Assembly entrypointAssembly;
Expand Down
142 changes: 142 additions & 0 deletions BepInEx.Preloader.Core/AssemblyBuildInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
using System;
using System.Linq;
using Mono.Cecil;

namespace BepInEx.Preloader.Core
{
public class AssemblyBuildInfo
{
public Version NetFrameworkVersion { get; private set; }

public bool IsAnyCpu { get; set; }

public bool Is64Bit { get; set; }

private static Version GetNet4Version(AssemblyDefinition assemblyDefinition)
{
var targetFrameworkAttribute = assemblyDefinition.CustomAttributes.FirstOrDefault(x =>
x.AttributeType.FullName == "System.Runtime.Versioning.TargetFrameworkAttribute");

if (targetFrameworkAttribute == null)
return null;

if (targetFrameworkAttribute.ConstructorArguments.Count < 1)
return null;

if (targetFrameworkAttribute.ConstructorArguments[0].Type.Name != "String")
return null;

string versionInfo = (string)targetFrameworkAttribute.ConstructorArguments[0].Value;

var values = versionInfo.Split(',');

foreach (var value in values)
{
if (!value.StartsWith("Version=v"))
continue;

try
{
return new Version(value.Substring("Version=v".Length));
}
catch {}
}

return null;
}

public static AssemblyBuildInfo DetermineInfo(AssemblyDefinition assemblyDefinition)
{
var buildInfo = new AssemblyBuildInfo();

// framework version

var runtime = assemblyDefinition.MainModule.Runtime;

if (runtime == TargetRuntime.Net_1_0)
{
buildInfo.NetFrameworkVersion = new Version(1, 0);
}
else if (runtime == TargetRuntime.Net_1_1)
{
buildInfo.NetFrameworkVersion = new Version(1, 1);
}
else if (runtime == TargetRuntime.Net_2_0)
{
// Assume 3.5 here. The code to determine versions between 2.0 and 3.5 is not worth the amount of non-unity games that use it, if any

buildInfo.NetFrameworkVersion = new Version(3, 5);
}
else
{
buildInfo.NetFrameworkVersion = GetNet4Version(assemblyDefinition) ?? new Version(4, 0);
}

// bitness

/*
AnyCPU 64-bit preferred
MainModule.Architecture: I386
MainModule.Attributes: ILOnly
AnyCPU 32-bit preferred
MainModule.Architecture: I386
MainModule.Attributes: ILOnly, Required32Bit, Preferred32Bit
x86
MainModule.Architecture: I386
MainModule.Attributes: ILOnly, Required32Bit
x64
MainModule.Architecture: AMD64
MainModule.Attributes: ILOnly
*/

var architecture = assemblyDefinition.MainModule.Architecture;
var attributes = assemblyDefinition.MainModule.Attributes;

if (architecture == TargetArchitecture.AMD64)
{
buildInfo.Is64Bit = true;
buildInfo.IsAnyCpu = false;
}
else if (architecture == TargetArchitecture.I386 && HasFlag(attributes, ModuleAttributes.Preferred32Bit | ModuleAttributes.Required32Bit))
{
buildInfo.Is64Bit = false;
buildInfo.IsAnyCpu = true;
}
else if (architecture == TargetArchitecture.I386 && HasFlag(attributes, ModuleAttributes.Required32Bit))
{
buildInfo.Is64Bit = false;
buildInfo.IsAnyCpu = false;
}
else if (architecture == TargetArchitecture.I386)
{
buildInfo.Is64Bit = true;
buildInfo.IsAnyCpu = true;
}
else
{
throw new Exception("Unable to determine assembly architecture");
}

return buildInfo;
}

private static bool HasFlag(ModuleAttributes value, ModuleAttributes flag)
{
return (value & flag) == flag;
}

/// <inheritdoc />
public override string ToString()
{
if (IsAnyCpu)
{
return $".NET Framework {NetFrameworkVersion}, AnyCPU ({(Is64Bit ? "64" : "32")}-bit preferred)";
}

return $".NET Framework {NetFrameworkVersion}, {(Is64Bit ? "x64" : "x86")}";
}
}
}
6 changes: 4 additions & 2 deletions BepInEx.Preloader.Core/Logging/ChainloaderLogHelper.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Linq;
using System;
using System.Linq;
using BepInEx.Logging;
using BepInEx.Shared;
using MonoMod.Utils;
Expand All @@ -17,14 +18,15 @@ public static void PrintLogInfo(ManualLogSource log)

//See BuildInfoAttribute for more information about this section.
var attributes = typeof(BuildInfoAttribute).Assembly.GetCustomAttributes(typeof(BuildInfoAttribute), false);

if (attributes.Length > 0)
{
var attribute = (BuildInfoAttribute) attributes[0];
log.Log(LogLevel.Message, attribute.Info);
}

Logger.Log(LogLevel.Info, $"System platform: {PlatformHelper.Current}");
Logger.Log(LogLevel.Info, $"Process bitness: {(PlatformUtils.ProcessIs64Bit ? "64-bit (x64)" : "32-bit (x86)")}");
}

public static void RewritePreloaderLogs()
Expand Down
4 changes: 3 additions & 1 deletion BepInEx.Preloader.Core/PlatformUtils.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
Expand All @@ -16,6 +16,8 @@ internal static class PlatformUtils
CharSet = CharSet.Ansi)]
private static extern IntPtr uname_osx(ref utsname_osx utsname);

public static readonly bool ProcessIs64Bit = IntPtr.Size >= 8;

/// <summary>
/// Recreation of MonoMod's PlatformHelper.DeterminePlatform method, but with libc calls instead of creating processes.
/// </summary>
Expand Down

0 comments on commit 3f172db

Please sign in to comment.