Skip to content

Commit a02c330

Browse files
committed
Implement better platform identification and descriptions in preloader log
1 parent 7669b6d commit a02c330

File tree

3 files changed

+162
-8
lines changed

3 files changed

+162
-8
lines changed

BepInEx.NetLauncher/Program.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ internal static void PreloaderMain(string[] args)
3131

3232
internal static void OuterMain(string[] args, string filename)
3333
{
34+
PlatformUtils.SetPlatform();
35+
3436
Paths.SetExecutablePath(filename);
3537

3638
AppDomain.CurrentDomain.AssemblyResolve += LocalResolve;

BepInEx.Preloader.Core/Logging/ChainloaderLogHelper.cs

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Linq;
4+
using System.Text;
35
using BepInEx.Logging;
46
using BepInEx.Shared;
57
using MonoMod.Utils;
@@ -25,10 +27,120 @@ public static void PrintLogInfo(ManualLogSource log)
2527
log.Log(LogLevel.Message, attribute.Info);
2628
}
2729

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

34+
private static string GetPlatformString()
35+
{
36+
var builder = new StringBuilder();
37+
38+
var osVersion = Environment.OSVersion.Version;
39+
40+
// NOTE: this logic needs to be different for .NET 5.
41+
// We don't use it and I don't think we will for a long time (possibly ever), but upgrading will break Environment.OSVersion
42+
// https://docs.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/5.0/environment-osversion-returns-correct-version#change-description
43+
44+
// Some additional notes
45+
// On .NET Framework and .NET Core platforms before 5, Environment.OSVersion does not work as you would expect.
46+
47+
// 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.
48+
// So we have to call RtlGetVersion which bypasses that and gets the values for us. This is done in PlatformUtils
49+
50+
// On macOS it returns the Darwin kernel version. I've included a mapping of most versions, but there's definitely some missing versions
51+
52+
// 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
53+
54+
if (PlatformHelper.Is(Platform.Windows))
55+
{
56+
osVersion = PlatformUtils.WindowsVersion;
57+
58+
builder.Append("Windows ");
59+
60+
// https://stackoverflow.com/a/2819962
61+
62+
if (osVersion.Major >= 10 && osVersion.Build >= 22000)
63+
builder.Append("11");
64+
else if (osVersion.Major >= 10)
65+
builder.Append("10");
66+
else if (osVersion.Major == 6 && osVersion.Minor == 3)
67+
builder.Append("8.1");
68+
else if (osVersion.Major == 6 && osVersion.Minor == 2)
69+
builder.Append("8");
70+
else if (osVersion.Major == 6 && osVersion.Minor == 1)
71+
builder.Append("7");
72+
else if (osVersion.Major == 6 && osVersion.Minor == 0)
73+
builder.Append("Vista");
74+
else if (osVersion.Major <= 5)
75+
builder.Append("XP");
76+
}
77+
else if (PlatformHelper.Is(Platform.MacOS))
78+
{
79+
builder.Append("macOS ");
80+
81+
82+
var osxVersion = osVersion.ToString(3);
83+
84+
if (MacOSVersions.TryGetValue(osxVersion, out var macOsVersion))
85+
{
86+
builder.Append(macOsVersion);
87+
}
88+
else
89+
{
90+
builder.AppendFormat("Unknown (kernel {0})", osVersion);
91+
}
92+
}
93+
else if (PlatformHelper.Is(Platform.Linux))
94+
{
95+
builder.Append("Linux");
96+
97+
if (PlatformUtils.LinuxKernelVersion != null)
98+
{
99+
builder.AppendFormat(" (kernel {0})", PlatformUtils.LinuxKernelVersion);
100+
}
101+
}
102+
103+
builder.Append(PlatformHelper.Is(Platform.Bits64) ? " 64-bit" : " 32-bit");
104+
105+
if (PlatformHelper.Is(Platform.Android))
106+
{
107+
builder.Append(" Android");
108+
}
109+
110+
if (PlatformHelper.Is(Platform.ARM))
111+
{
112+
builder.Append(" ARM");
113+
114+
if (PlatformHelper.Is(Platform.Bits64))
115+
builder.Append("64");
116+
}
117+
118+
return builder.ToString();
119+
}
120+
121+
private static Dictionary<string, string> MacOSVersions { get; } = new()
122+
{
123+
// https://en.wikipedia.org/wiki/Darwin_%28operating_system%29#Release_history
124+
["16.0.0"] = "10.12",
125+
["16.5.0"] = "10.12.4",
126+
["16.6.0"] = "10.12.6",
127+
["17.5.0"] = "10.13.4",
128+
["17.6.0"] = "10.13.5",
129+
["17.7.0"] = "10.13.6",
130+
["18.2.0"] = "10.14.1",
131+
["19.2.0"] = "10.15.2",
132+
["19.3.0"] = "10.15.3",
133+
["19.5.0"] = "10.15.5.1",
134+
["20.1.0"] = "11.0",
135+
["20.2.0"] = "11.1",
136+
["20.3.0"] = "11.2",
137+
["20.4.0"] = "11.3",
138+
["20.5.0"] = "11.4",
139+
["21.0.1"] = "12.0",
140+
["21.1.0"] = "12.0.1",
141+
["21.2.0"] = "12.1",
142+
};
143+
32144
public static void RewritePreloaderLogs()
33145
{
34146
if (PreloaderConsoleListener.LogEvents == null || PreloaderConsoleListener.LogEvents.Count == 0)

BepInEx.Preloader.Core/PlatformUtils.cs

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,19 @@ internal static class PlatformUtils
1616
CharSet = CharSet.Ansi)]
1717
private static extern IntPtr uname_osx(ref utsname_osx utsname);
1818

19+
[DllImport("ntdll.dll", SetLastError = true)]
20+
private static extern bool RtlGetVersion(ref WindowsOSVersionInfoExW versionInfo);
21+
22+
23+
private static bool Is(this Platform current, Platform expected) => (current & expected) == expected;
24+
25+
1926
public static readonly bool ProcessIs64Bit = IntPtr.Size >= 8;
27+
public static Version WindowsVersion { get; set; }
28+
29+
public static string LinuxArchitecture { get; set; }
30+
public static string LinuxKernelVersion { get; set; }
31+
2032

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

45-
if (Is(current, Platform.Linux) && Directory.Exists("/data") && File.Exists("/system/build.prop"))
58+
if (current.Is(Platform.Linux) && Directory.Exists("/data") && File.Exists("/system/build.prop"))
4659
current = Platform.Android;
47-
else if (Is(current, Platform.Unix) && Directory.Exists("/System/Library/AccessibilityBundles"))
60+
else if (current.Is(Platform.Unix) && Directory.Exists("/System/Library/AccessibilityBundles"))
4861
current = Platform.iOS;
4962

63+
if (current.Is(Platform.Windows))
64+
{
65+
var windowsVersionInfo = new WindowsOSVersionInfoExW();
66+
RtlGetVersion(ref windowsVersionInfo);
67+
68+
WindowsVersion = new Version((int)windowsVersionInfo.dwMajorVersion, (int)windowsVersionInfo.dwMinorVersion, 0, (int)windowsVersionInfo.dwBuildNumber);
69+
}
70+
5071
// Is64BitOperatingSystem has been added in .NET Framework 4.0
5172
var m_get_Is64BitOperatingSystem =
5273
typeof(Environment).GetProperty("Is64BitOperatingSystem")?.GetGetMethod();
5374
if (m_get_Is64BitOperatingSystem != null)
5475
current |= (bool) m_get_Is64BitOperatingSystem.Invoke(null, new object[0]) ? Platform.Bits64 : 0;
5576
else
5677
current |= IntPtr.Size >= 8 ? Platform.Bits64 : 0;
57-
58-
if ((Is(current, Platform.MacOS) || Is(current, Platform.Linux)) && Type.GetType("Mono.Runtime") != null)
78+
79+
if ((current.Is(Platform.MacOS) || current.Is(Platform.Linux)) && Type.GetType("Mono.Runtime") != null)
5980
{
6081
string arch;
6182
IntPtr result;
6283

63-
if (Is(current, Platform.MacOS))
84+
if (current.Is(Platform.MacOS))
6485
{
6586
var utsname_osx = new utsname_osx();
6687
result = uname_osx(ref utsname_osx);
@@ -72,6 +93,9 @@ public static void SetPlatform()
7293
var utsname_linux = new utsname_linux();
7394
result = uname_linux(ref utsname_linux);
7495
arch = utsname_linux.machine;
96+
97+
LinuxArchitecture = utsname_linux.machine;
98+
LinuxKernelVersion = utsname_linux.version;
7599
}
76100

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

91-
private static bool Is(Platform current, Platform expected) => (current & expected) == expected;
115+
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Unicode)]
116+
public struct WindowsOSVersionInfoExW
117+
{
118+
private static uint size = (uint)Marshal.SizeOf(typeof(WindowsOSVersionInfoExW));
119+
public uint dwOSVersionInfoSize = size;
120+
public uint dwMajorVersion;
121+
public uint dwMinorVersion;
122+
public uint dwBuildNumber;
123+
public uint dwPlatformId;
124+
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
125+
public string szCSDVersion;
126+
public ushort wServicePackMajor;
127+
public ushort wServicePackMinor;
128+
public ushort wSuiteMask;
129+
public byte wProductType;
130+
public byte wReserved;
131+
}
92132

93133
[StructLayout(LayoutKind.Sequential, Pack = 1)]
94134
public struct utsname_osx

0 commit comments

Comments
 (0)