From fd518f8e3f6123835733e6714f5840eae108bacf Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Sun, 20 Aug 2023 16:14:42 -0700 Subject: [PATCH 1/5] fix: Don't check for Wine Registry anymore - Renames `IsLinux` to `IsWine` to better reflect that this will return true for macOS as well. - Fixes a bug caused by misbehaving apps wanting to be helpful to Linux users - Also makes Wine checking far more resilient in cases where XL_WINEONLINUX isn't set. --- Dalamud/Utility/Util.cs | 42 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/Dalamud/Utility/Util.cs b/Dalamud/Utility/Util.cs index 5f2e4d5bf..63fc9faee 100644 --- a/Dalamud/Utility/Util.cs +++ b/Dalamud/Utility/Util.cs @@ -8,6 +8,7 @@ using System.Net.Http; using System.Numerics; using System.Reflection; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text; using Dalamud.Configuration.Internal; @@ -22,7 +23,6 @@ using Dalamud.Logging.Internal; using Dalamud.Networking.Http; using ImGuiNET; using Lumina.Excel.GeneratedSheets; -using Microsoft.Win32; using Serilog; namespace Dalamud.Utility; @@ -491,32 +491,30 @@ public static class Util } /// - /// Heuristically determine if Dalamud is running on Linux/WINE. + /// Determine if Dalamud is currently running within a Wine context (e.g. either on macOS or Linux). This method + /// will not return information about the host operating system. /// - /// Whether or not Dalamud is running on Linux/WINE. - public static bool IsLinux() + /// Returns true if Wine is detected, false otherwise. + public static bool IsWine() { - bool Check1() - { - return EnvironmentConfiguration.XlWineOnLinux; - } + if (EnvironmentConfiguration.XlWineOnLinux) return true; - bool Check2() - { - var hModule = NativeFunctions.GetModuleHandleW("ntdll.dll"); - var proc1 = NativeFunctions.GetProcAddress(hModule, "wine_get_version"); - var proc2 = NativeFunctions.GetProcAddress(hModule, "wine_get_build_id"); + var ntdll = NativeFunctions.GetModuleHandleW("ntdll.dll"); - return proc1 != IntPtr.Zero || proc2 != IntPtr.Zero; - } + // Test to see if any Wine specific exports exist. If they do, then we are running on Wine. + // The exports "wine_get_version", "wine_get_build_id", and "wine_get_host_version" will tend to be hidden + // 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", + "wine_get_host_version", + "wine_server_call", + "wine_unix_to_nt_file_name"); - bool Check3() - { - return Registry.CurrentUser.OpenSubKey(@"Software\Wine") != null || - Registry.LocalMachine.OpenSubKey(@"Software\Wine") != null; - } - - return Check1() || Check2() || Check3(); + bool AnyProcExists(nint handle, params string[] procs) => + procs.Any(p => NativeFunctions.GetProcAddress(handle, p) != nint.Zero); } /// From 2acba2b81f93cdd6dd766c9140389c9ec12ded08 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Tue, 12 Sep 2023 17:06:22 -0700 Subject: [PATCH 2/5] Update Dalamud.Boot to use extra import checks - Ports extra import checks to Dalamud Boot - Renames is_running_on_linux to is_running_on_wine - Update relevant logging string for VEH --- Dalamud.Boot/dllmain.cpp | 4 ++-- Dalamud.Boot/utils.cpp | 6 +++++- Dalamud.Boot/utils.h | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Dalamud.Boot/dllmain.cpp b/Dalamud.Boot/dllmain.cpp index 9a741a47f..94f1c7d0f 100644 --- a/Dalamud.Boot/dllmain.cpp +++ b/Dalamud.Boot/dllmain.cpp @@ -133,8 +133,8 @@ DWORD WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue) { // ============================== VEH ======================================== // logging::I("Initializing VEH..."); - if (utils::is_running_on_linux()) { - logging::I("=> VEH was disabled, running on linux"); + if (utils::is_running_on_wine()) { + logging::I("=> VEH was disabled, running on wine"); } else if (g_startInfo.BootVehEnabled) { if (veh::add_handler(g_startInfo.BootVehFull, g_startInfo.WorkingDirectory)) logging::I("=> Done!"); diff --git a/Dalamud.Boot/utils.cpp b/Dalamud.Boot/utils.cpp index 79205eb8d..b45795045 100644 --- a/Dalamud.Boot/utils.cpp +++ b/Dalamud.Boot/utils.cpp @@ -578,7 +578,7 @@ std::vector utils::get_env_list(const wchar_t* pcszName) { return res; } -bool utils::is_running_on_linux() { +bool utils::is_running_on_wine() { if (get_env(L"XL_WINEONLINUX")) return true; HMODULE hntdll = GetModuleHandleW(L"ntdll.dll"); @@ -588,6 +588,10 @@ bool utils::is_running_on_linux() { return true; if (GetProcAddress(hntdll, "wine_get_host_version")) return true; + if (GetProcAddress(hntdll, "wine_server_call")) + return true; + if (GetProcAddress(hntdll, "wine_unix_to_nt_file_name")) + return true; return false; } diff --git a/Dalamud.Boot/utils.h b/Dalamud.Boot/utils.h index 5d5c90dde..5e3caa4d6 100644 --- a/Dalamud.Boot/utils.h +++ b/Dalamud.Boot/utils.h @@ -264,7 +264,7 @@ namespace utils { return get_env_list(unicode::convert(pcszName).c_str()); } - bool is_running_on_linux(); + bool is_running_on_wine(); std::filesystem::path get_module_path(HMODULE hModule); From 0fca2c80c30457f9634e1d12be437ae51dc02b49 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Tue, 12 Sep 2023 18:14:54 -0700 Subject: [PATCH 3/5] Fix a build error, oops --- Dalamud/EntryPoint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dalamud/EntryPoint.cs b/Dalamud/EntryPoint.cs index 1975505a8..7ad794e42 100644 --- a/Dalamud/EntryPoint.cs +++ b/Dalamud/EntryPoint.cs @@ -166,7 +166,7 @@ public sealed class EntryPoint // This is due to GitHub not supporting TLS 1.0, so we enable all TLS versions globally ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls; - if (!Util.IsLinux()) + if (!Util.IsWine()) InitSymbolHandler(info); var dalamud = new Dalamud(info, configuration, mainThreadContinueEvent); From a7aacb15e4603a367e2f980578271a9a639d8852 Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Tue, 12 Sep 2023 18:22:20 -0700 Subject: [PATCH 4/5] 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; + } } From 088cf8c3922022d5fb576f05b876d7263032226f Mon Sep 17 00:00:00 2001 From: Kaz Wolfe Date: Wed, 13 Sep 2023 00:23:17 -0700 Subject: [PATCH 5/5] Remove fancy Darwin checking code --- Dalamud/Utility/Util.cs | 38 +++++++------------------------------- 1 file changed, 7 insertions(+), 31 deletions(-) diff --git a/Dalamud/Utility/Util.cs b/Dalamud/Utility/Util.cs index 5be4e4fc4..78edda3dd 100644 --- a/Dalamud/Utility/Util.cs +++ b/Dalamud/Utility/Util.cs @@ -520,8 +520,8 @@ public static class Util /// 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. + /// macOS users running without XL_PLATFORM being set will be reported as Linux users. Due to the way our + /// Wines work, there isn't a great (consistent) way to split the two apart if we're not told. /// /// Returns the that Dalamud is currently running on. public static OSPlatform GetHostPlatform() @@ -532,16 +532,13 @@ public static class Util 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; - } + // n.b. we had some fancy code here to check if the Wine host version returned "Darwin" but apparently + // *all* our Wines report Darwin if exports aren't hidden. As such, it is effectively impossible (without some + // (very cursed and inaccurate heuristics) to determine if we're on macOS or Linux unless we're explicitly told + // by our launcher. See commit a7aacb15e4603a367e2f980578271a9a639d8852 for the old check. - return OSPlatform.Windows; + return IsWine() ? OSPlatform.Linux : OSPlatform.Windows; } /// @@ -765,25 +762,4 @@ 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; - } }