Add XL_PLATFORM env var, Util.GetHostPlatform()

- New env var XL_PLATFORM allows launcher to inform Dalamud of the current platform (one of Windows, macOS, or Linux).
- New method Util.GetHostPlatform() provides a best guess for the current platform. This method will respect XL_PLATFORM if set, but will otherwise resort to heuristic checks.
This commit is contained in:
Kaz Wolfe 2023-09-12 18:22:20 -07:00
parent 0fca2c80c3
commit a7aacb15e4
No known key found for this signature in database
GPG key ID: 258813F53A16EBB4

View file

@ -1,10 +1,8 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Net.Http;
using System.Numerics; using System.Numerics;
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -16,11 +14,10 @@ using Dalamud.Data;
using Dalamud.Game; using Dalamud.Game;
using Dalamud.Game.ClientState.Objects.SubKinds; using Dalamud.Game.ClientState.Objects.SubKinds;
using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Interface;
using Dalamud.Interface.Colors; using Dalamud.Interface.Colors;
using Dalamud.Interface.Utility; using Dalamud.Interface.Utility;
using Dalamud.Logging.Internal; using Dalamud.Logging.Internal;
using Dalamud.Networking.Http; using Dalamud.Memory;
using ImGuiNET; using ImGuiNET;
using Lumina.Excel.GeneratedSheets; using Lumina.Excel.GeneratedSheets;
using Serilog; using Serilog;
@ -38,7 +35,7 @@ public static class Util
private static ulong moduleStartAddr; private static ulong moduleStartAddr;
private static ulong moduleEndAddr; private static ulong moduleEndAddr;
/// <summary> /// <summary>
/// Gets the assembly version of Dalamud. /// Gets the assembly version of Dalamud.
/// </summary> /// </summary>
@ -498,6 +495,7 @@ public static class Util
public static bool IsWine() public static bool IsWine()
{ {
if (EnvironmentConfiguration.XlWineOnLinux) return true; if (EnvironmentConfiguration.XlWineOnLinux) return true;
if (Environment.GetEnvironmentVariable("XL_PLATFORM") is not null and not "Windows") return true;
var ntdll = NativeFunctions.GetModuleHandleW("ntdll.dll"); var ntdll = NativeFunctions.GetModuleHandleW("ntdll.dll");
@ -506,17 +504,46 @@ public static class Util
// by most Linux users (else FFXIV will want a macOS license), so we will additionally check some lesser-known // by most Linux users (else FFXIV will want a macOS license), so we will additionally check some lesser-known
// exports as well. // exports as well.
return AnyProcExists( return AnyProcExists(
ntdll, ntdll,
"wine_get_version", "wine_get_version",
"wine_get_build_id", "wine_get_build_id",
"wine_get_host_version", "wine_get_host_version",
"wine_server_call", "wine_server_call",
"wine_unix_to_nt_file_name"); "wine_unix_to_nt_file_name");
bool AnyProcExists(nint handle, params string[] procs) => bool AnyProcExists(nint handle, params string[] procs) =>
procs.Any(p => NativeFunctions.GetProcAddress(handle, p) != nint.Zero); procs.Any(p => NativeFunctions.GetProcAddress(handle, p) != nint.Zero);
} }
/// <summary>
/// Gets the best guess for the current host's platform based on the <c>XL_PLATFORM</c> environment variable or
/// heuristics.
/// </summary>
/// <remarks>
/// This method is not perfectly reliable if <c>XL_PLATFORM</c> is unset. For example, macOS users running with
/// exports hidden will be marked as Linux users. Better heuristic checks for macOS are needed in order to fix this.
/// </remarks>
/// <returns>Returns the <see cref="OSPlatform"/> that Dalamud is currently running on.</returns>
public static OSPlatform GetHostPlatform()
{
switch (Environment.GetEnvironmentVariable("XL_PLATFORM"))
{
case "Windows": return OSPlatform.Windows;
case "MacOS": return OSPlatform.OSX;
case "Linux": return OSPlatform.Linux;
}
if (IsWine())
{
GetWineHostVersion(out var platform, out _);
if (platform == "Darwin") return OSPlatform.OSX; // only happens on macOS without export hides (mac license)
return OSPlatform.Linux;
}
return OSPlatform.Windows;
}
/// <summary> /// <summary>
/// Heuristically determine if the Windows version is higher than Windows 11's first build. /// Heuristically determine if the Windows version is higher than Windows 11's first build.
/// </summary> /// </summary>
@ -683,7 +710,7 @@ public static class Util
ImGui.SetClipboardText(actor.Address.ToInt64().ToString("X")); ImGui.SetClipboardText(actor.Address.ToInt64().ToString("X"));
} }
} }
private static unsafe void ShowValue(ulong addr, IEnumerable<string> path, Type type, object value) private static unsafe void ShowValue(ulong addr, IEnumerable<string> path, Type type, object value)
{ {
if (type.IsPointer) if (type.IsPointer)
@ -738,4 +765,25 @@ public static class Util
} }
} }
} }
private static unsafe bool GetWineHostVersion(out string? platform, out string? version)
{
platform = null;
version = null;
var ntdll = NativeFunctions.GetModuleHandleW("ntdll.dll");
var methodPtr = NativeFunctions.GetProcAddress(ntdll, "wine_get_host_version");
if (methodPtr == nint.Zero) return false;
var methodDelegate = (delegate* unmanaged[Fastcall]<out char*, out char*, void>)methodPtr;
methodDelegate(out var platformPtr, out var versionPtr);
if (platformPtr == null) return false;
platform = MemoryHelper.ReadStringNullTerminated((nint)platformPtr);
if (versionPtr != null) version = MemoryHelper.ReadStringNullTerminated((nint)versionPtr);
return true;
}
} }