Skip to content

Commit

Permalink
Implement better platform identification and descriptions in preloade…
Browse files Browse the repository at this point in the history
…r log
  • Loading branch information
bbepis committed Jan 24, 2022
1 parent 7669b6d commit a02c330
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 8 deletions.
2 changes: 2 additions & 0 deletions BepInEx.NetLauncher/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ internal static void PreloaderMain(string[] args)

internal static void OuterMain(string[] args, string filename)
{
PlatformUtils.SetPlatform();

Paths.SetExecutablePath(filename);

AppDomain.CurrentDomain.AssemblyResolve += LocalResolve;
Expand Down
114 changes: 113 additions & 1 deletion BepInEx.Preloader.Core/Logging/ChainloaderLogHelper.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BepInEx.Logging;
using BepInEx.Shared;
using MonoMod.Utils;
Expand All @@ -25,10 +27,120 @@ public static void PrintLogInfo(ManualLogSource log)
log.Log(LogLevel.Message, attribute.Info);
}

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

private static string GetPlatformString()
{
var builder = new StringBuilder();

var osVersion = Environment.OSVersion.Version;

// NOTE: this logic needs to be different for .NET 5.
// We don't use it and I don't think we will for a long time (possibly ever), but upgrading will break Environment.OSVersion
// https://docs.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/5.0/environment-osversion-returns-correct-version#change-description

// Some additional notes
// On .NET Framework and .NET Core platforms before 5, Environment.OSVersion does not work as you would expect.

// On Windows, it returns at maximum 6.3 (Windows 8) if you don't specify that your application is specifically compatible with Windows 10, due to compatibility layer stuff.
// So we have to call RtlGetVersion which bypasses that and gets the values for us. This is done in PlatformUtils

// On macOS it returns the Darwin kernel version. I've included a mapping of most versions, but there's definitely some missing versions

// Not sure what it does on Linux. I think it returns the kernel version there too, but we already get the utsname structure from SetPlatform() regardless

if (PlatformHelper.Is(Platform.Windows))
{
osVersion = PlatformUtils.WindowsVersion;

builder.Append("Windows ");

// https://stackoverflow.com/a/2819962

if (osVersion.Major >= 10 && osVersion.Build >= 22000)
builder.Append("11");
else if (osVersion.Major >= 10)
builder.Append("10");
else if (osVersion.Major == 6 && osVersion.Minor == 3)
builder.Append("8.1");
else if (osVersion.Major == 6 && osVersion.Minor == 2)
builder.Append("8");
else if (osVersion.Major == 6 && osVersion.Minor == 1)
builder.Append("7");
else if (osVersion.Major == 6 && osVersion.Minor == 0)
builder.Append("Vista");
else if (osVersion.Major <= 5)
builder.Append("XP");
}
else if (PlatformHelper.Is(Platform.MacOS))
{
builder.Append("macOS ");


var osxVersion = osVersion.ToString(3);

if (MacOSVersions.TryGetValue(osxVersion, out var macOsVersion))
{
builder.Append(macOsVersion);
}
else
{
builder.AppendFormat("Unknown (kernel {0})", osVersion);
}
}
else if (PlatformHelper.Is(Platform.Linux))
{
builder.Append("Linux");

if (PlatformUtils.LinuxKernelVersion != null)
{
builder.AppendFormat(" (kernel {0})", PlatformUtils.LinuxKernelVersion);
}
}

builder.Append(PlatformHelper.Is(Platform.Bits64) ? " 64-bit" : " 32-bit");

if (PlatformHelper.Is(Platform.Android))
{
builder.Append(" Android");
}

if (PlatformHelper.Is(Platform.ARM))
{
builder.Append(" ARM");

if (PlatformHelper.Is(Platform.Bits64))
builder.Append("64");
}

return builder.ToString();
}

private static Dictionary<string, string> MacOSVersions { get; } = new()
{
// https://en.wikipedia.org/wiki/Darwin_%28operating_system%29#Release_history
["16.0.0"] = "10.12",
["16.5.0"] = "10.12.4",
["16.6.0"] = "10.12.6",
["17.5.0"] = "10.13.4",
["17.6.0"] = "10.13.5",
["17.7.0"] = "10.13.6",
["18.2.0"] = "10.14.1",
["19.2.0"] = "10.15.2",
["19.3.0"] = "10.15.3",
["19.5.0"] = "10.15.5.1",
["20.1.0"] = "11.0",
["20.2.0"] = "11.1",
["20.3.0"] = "11.2",
["20.4.0"] = "11.3",
["20.5.0"] = "11.4",
["21.0.1"] = "12.0",
["21.1.0"] = "12.0.1",
["21.2.0"] = "12.1",
};

public static void RewritePreloaderLogs()
{
if (PreloaderConsoleListener.LogEvents == null || PreloaderConsoleListener.LogEvents.Count == 0)
Expand Down
54 changes: 47 additions & 7 deletions BepInEx.Preloader.Core/PlatformUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,19 @@ internal static class PlatformUtils
CharSet = CharSet.Ansi)]
private static extern IntPtr uname_osx(ref utsname_osx utsname);

[DllImport("ntdll.dll", SetLastError = true)]
private static extern bool RtlGetVersion(ref WindowsOSVersionInfoExW versionInfo);


private static bool Is(this Platform current, Platform expected) => (current & expected) == expected;


public static readonly bool ProcessIs64Bit = IntPtr.Size >= 8;
public static Version WindowsVersion { get; set; }

public static string LinuxArchitecture { get; set; }
public static string LinuxKernelVersion { get; set; }


/// <summary>
/// Recreation of MonoMod's PlatformHelper.DeterminePlatform method, but with libc calls instead of creating processes.
Expand All @@ -40,27 +52,36 @@ public static void SetPlatform()
current = Platform.Windows;
else if (platID.Contains("mac") || platID.Contains("osx"))
current = Platform.MacOS;
else if (platID.Contains("lin") || platID.Contains("unix")) current = Platform.Linux;
else if (platID.Contains("lin") || platID.Contains("unix"))
current = Platform.Linux;

if (Is(current, Platform.Linux) && Directory.Exists("/data") && File.Exists("/system/build.prop"))
if (current.Is(Platform.Linux) && Directory.Exists("/data") && File.Exists("/system/build.prop"))
current = Platform.Android;
else if (Is(current, Platform.Unix) && Directory.Exists("/System/Library/AccessibilityBundles"))
else if (current.Is(Platform.Unix) && Directory.Exists("/System/Library/AccessibilityBundles"))
current = Platform.iOS;

if (current.Is(Platform.Windows))
{
var windowsVersionInfo = new WindowsOSVersionInfoExW();
RtlGetVersion(ref windowsVersionInfo);

WindowsVersion = new Version((int)windowsVersionInfo.dwMajorVersion, (int)windowsVersionInfo.dwMinorVersion, 0, (int)windowsVersionInfo.dwBuildNumber);
}

// Is64BitOperatingSystem has been added in .NET Framework 4.0
var m_get_Is64BitOperatingSystem =
typeof(Environment).GetProperty("Is64BitOperatingSystem")?.GetGetMethod();
if (m_get_Is64BitOperatingSystem != null)
current |= (bool) m_get_Is64BitOperatingSystem.Invoke(null, new object[0]) ? Platform.Bits64 : 0;
else
current |= IntPtr.Size >= 8 ? Platform.Bits64 : 0;

if ((Is(current, Platform.MacOS) || Is(current, Platform.Linux)) && Type.GetType("Mono.Runtime") != null)
if ((current.Is(Platform.MacOS) || current.Is(Platform.Linux)) && Type.GetType("Mono.Runtime") != null)
{
string arch;
IntPtr result;

if (Is(current, Platform.MacOS))
if (current.Is(Platform.MacOS))
{
var utsname_osx = new utsname_osx();
result = uname_osx(ref utsname_osx);
Expand All @@ -72,6 +93,9 @@ public static void SetPlatform()
var utsname_linux = new utsname_linux();
result = uname_linux(ref utsname_linux);
arch = utsname_linux.machine;

LinuxArchitecture = utsname_linux.machine;
LinuxKernelVersion = utsname_linux.version;
}

if (result == IntPtr.Zero && (arch.StartsWith("aarch") || arch.StartsWith("arm")))
Expand All @@ -88,7 +112,23 @@ public static void SetPlatform()
PlatformHelper.Current = current;
}

private static bool Is(Platform current, Platform expected) => (current & expected) == expected;
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Unicode)]
public struct WindowsOSVersionInfoExW
{
private static uint size = (uint)Marshal.SizeOf(typeof(WindowsOSVersionInfoExW));
public uint dwOSVersionInfoSize = size;
public uint dwMajorVersion;
public uint dwMinorVersion;
public uint dwBuildNumber;
public uint dwPlatformId;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string szCSDVersion;
public ushort wServicePackMajor;
public ushort wServicePackMinor;
public ushort wSuiteMask;
public byte wProductType;
public byte wReserved;
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct utsname_osx
Expand Down

0 comments on commit a02c330

Please sign in to comment.