From a7aacb15e4603a367e2f980578271a9a639d8852 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Tue, 12 Sep 2023 18:22:20 -0700 Subject: [PATCH] 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. --- Dalamud/Utility/Util.cs | 68 +++++++++++++++++++++++++++++++++++------ 1 file changed, 58 insertions(+), 10 deletions(-) diff --git a/Dalamud/Utility/Util.cs b/Dalamud/Utility/Util.cs index 63fc9faee..5be4e4fc4 100644 --- a/Dalamud/Utility/Util.cs +++ b/Dalamud/Utility/Util.cs @@ -1,10 +1,8 @@ -using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Linq; -using System.Net.Http; using System.Numerics; using System.Reflection; using System.Runtime.CompilerServices; @@ -16,11 +14,10 @@ using Dalamud.Data; using Dalamud.Game; using Dalamud.Game.ClientState.Objects.SubKinds; using Dalamud.Game.ClientState.Objects.Types; -using Dalamud.Interface; using Dalamud.Interface.Colors; using Dalamud.Interface.Utility; using Dalamud.Logging.Internal; -using Dalamud.Networking.Http; +using Dalamud.Memory; using ImGuiNET; using Lumina.Excel.GeneratedSheets; using Serilog; @@ -38,7 +35,7 @@ public static class Util private static ulong moduleStartAddr; private static ulong moduleEndAddr; - + /// /// Gets the assembly version of Dalamud. /// @@ -498,6 +495,7 @@ public static class Util public static bool IsWine() { if (EnvironmentConfiguration.XlWineOnLinux) return true; + if (Environment.GetEnvironmentVariable("XL_PLATFORM") is not null and not "Windows") return true; 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 // exports as well. return AnyProcExists( - ntdll, - "wine_get_version", - "wine_get_build_id", + ntdll, + "wine_get_version", + "wine_get_build_id", "wine_get_host_version", - "wine_server_call", + "wine_server_call", "wine_unix_to_nt_file_name"); bool AnyProcExists(nint handle, params string[] procs) => procs.Any(p => NativeFunctions.GetProcAddress(handle, p) != nint.Zero); } + /// + /// Gets the best guess for the current host's platform based on the XL_PLATFORM environment variable or + /// heuristics. + /// + /// + /// This method is not perfectly reliable if XL_PLATFORM 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. + /// + /// Returns the that Dalamud is currently running on. + 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; + } + /// /// Heuristically determine if the Windows version is higher than Windows 11's first build. /// @@ -683,7 +710,7 @@ public static class Util ImGui.SetClipboardText(actor.Address.ToInt64().ToString("X")); } } - + private static unsafe void ShowValue(ulong addr, IEnumerable path, Type type, object value) { 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])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; + } }