mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 10:17:22 +01:00
Implement dalamud-platform launch argument (#1452)
* implement dalamud platform launch arg * implement cross-platform gamePath fallback * refactor platform detection heuristic * add cross platform dalamud runtime detection
This commit is contained in:
parent
9815cf1d88
commit
7cac19ce81
11 changed files with 299 additions and 101 deletions
|
|
@ -109,6 +109,7 @@ void from_json(const nlohmann::json& json, DalamudStartInfo& config) {
|
|||
config.PluginDirectory = json.value("PluginDirectory", config.PluginDirectory);
|
||||
config.AssetDirectory = json.value("AssetDirectory", config.AssetDirectory);
|
||||
config.Language = json.value("Language", config.Language);
|
||||
config.Platform = json.value("Platform", config.Platform);
|
||||
config.GameVersion = json.value("GameVersion", config.GameVersion);
|
||||
config.TroubleshootingPackData = json.value("TroubleshootingPackData", std::string{});
|
||||
config.DelayInitializeMs = json.value("DelayInitializeMs", config.DelayInitializeMs);
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ struct DalamudStartInfo {
|
|||
std::string PluginDirectory;
|
||||
std::string AssetDirectory;
|
||||
ClientLanguage Language = ClientLanguage::English;
|
||||
std::string Platform;
|
||||
std::string GameVersion;
|
||||
std::string TroubleshootingPackData;
|
||||
int DelayInitializeMs = 0;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include "pch.h"
|
||||
#include "DalamudStartInfo.h"
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
|
|
@ -584,6 +585,10 @@ std::vector<std::string> utils::get_env_list(const wchar_t* pcszName) {
|
|||
return res;
|
||||
}
|
||||
|
||||
bool utils::is_running_on_wine() {
|
||||
return g_startInfo.Platform != "WINDOWS";
|
||||
}
|
||||
|
||||
std::filesystem::path utils::get_module_path(HMODULE hModule) {
|
||||
std::wstring buf(MAX_PATH, L'\0');
|
||||
while (true) {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using System.Runtime.InteropServices;
|
||||
using Dalamud.Common.Game;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
|
|
@ -15,7 +16,7 @@ public record DalamudStartInfo
|
|||
/// </summary>
|
||||
public DalamudStartInfo()
|
||||
{
|
||||
// ignored
|
||||
this.Platform = OSPlatform.Create("UNKNOWN");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -58,6 +59,12 @@ public record DalamudStartInfo
|
|||
/// </summary>
|
||||
public ClientLanguage Language { get; set; } = ClientLanguage.English;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the underlying platform<72>Dalamud runs on.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(OSPlatformConverter))]
|
||||
public OSPlatform Platform { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the current game version code.
|
||||
/// </summary>
|
||||
|
|
|
|||
78
Dalamud.Common/OSPlatformConverter.cs
Normal file
78
Dalamud.Common/OSPlatformConverter.cs
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
using System.Runtime.InteropServices;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Dalamud.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="OSPlatform"/> to and from a string (e.g. <c>"FreeBSD"</c>).
|
||||
/// </summary>
|
||||
public sealed class OSPlatformConverter : JsonConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Writes the JSON representation of the object.
|
||||
/// </summary>
|
||||
/// <param name="writer">The <see cref="JsonWriter"/> to write to.</param>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <param name="serializer">The calling serializer.</param>
|
||||
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
writer.WriteNull();
|
||||
}
|
||||
else if (value is OSPlatform)
|
||||
{
|
||||
writer.WriteValue(value.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new JsonSerializationException("Expected OSPlatform object value");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the JSON representation of the object.
|
||||
/// </summary>
|
||||
/// <param name="reader">The <see cref="JsonReader"/> to read from.</param>
|
||||
/// <param name="objectType">Type of the object.</param>
|
||||
/// <param name="existingValue">The existing property value of the JSON that is being converted.</param>
|
||||
/// <param name="serializer">The calling serializer.</param>
|
||||
/// <returns>The object value.</returns>
|
||||
public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
|
||||
{
|
||||
if (reader.TokenType == JsonToken.Null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (reader.TokenType == JsonToken.String)
|
||||
{
|
||||
try
|
||||
{
|
||||
return OSPlatform.Create((string)reader.Value!);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new JsonSerializationException($"Error parsing OSPlatform string: {reader.Value}", ex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new JsonSerializationException($"Unexpected token or value when parsing OSPlatform. Token: {reader.TokenType}, Value: {reader.Value}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this instance can convert the specified object type.
|
||||
/// </summary>
|
||||
/// <param name="objectType">Type of the object.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return objectType == typeof(OSPlatform);
|
||||
}
|
||||
}
|
||||
|
|
@ -263,6 +263,35 @@ namespace Dalamud.Injector
|
|||
}
|
||||
}
|
||||
|
||||
private static OSPlatform DetectPlatformHeuristic()
|
||||
{
|
||||
var ntdll = NativeFunctions.GetModuleHandleW("ntdll.dll");
|
||||
var wineServerCallPtr = NativeFunctions.GetProcAddress(ntdll, "wine_server_call");
|
||||
var wineGetHostVersionPtr = NativeFunctions.GetProcAddress(ntdll, "wine_get_host_version");
|
||||
var winePlatform = GetWinePlatform(wineGetHostVersionPtr);
|
||||
var isWine = wineServerCallPtr != nint.Zero;
|
||||
|
||||
static unsafe string? GetWinePlatform(nint wineGetHostVersionPtr)
|
||||
{
|
||||
if (wineGetHostVersionPtr == nint.Zero) return null;
|
||||
|
||||
var methodDelegate = (delegate* unmanaged[Fastcall]<out char*, out char*, void>)wineGetHostVersionPtr;
|
||||
methodDelegate(out var platformPtr, out var _);
|
||||
|
||||
if (platformPtr == null) return null;
|
||||
|
||||
return Marshal.PtrToStringAnsi((nint)platformPtr);
|
||||
}
|
||||
|
||||
if (!isWine)
|
||||
return OSPlatform.Windows;
|
||||
|
||||
if (winePlatform == "Darwin")
|
||||
return OSPlatform.OSX;
|
||||
|
||||
return OSPlatform.Linux;
|
||||
}
|
||||
|
||||
private static DalamudStartInfo ExtractAndInitializeStartInfoFromArguments(DalamudStartInfo? startInfo, List<string> args)
|
||||
{
|
||||
int len;
|
||||
|
|
@ -278,6 +307,7 @@ namespace Dalamud.Injector
|
|||
var logName = startInfo.LogName;
|
||||
var logPath = startInfo.LogPath;
|
||||
var languageStr = startInfo.Language.ToString().ToLowerInvariant();
|
||||
var platformStr = startInfo.Platform.ToString().ToLowerInvariant();
|
||||
var unhandledExceptionStr = startInfo.UnhandledException.ToString().ToLowerInvariant();
|
||||
var troubleshootingData = "{\"empty\": true, \"description\": \"No troubleshooting data supplied.\"}";
|
||||
|
||||
|
|
@ -307,6 +337,10 @@ namespace Dalamud.Injector
|
|||
{
|
||||
languageStr = args[i][key.Length..].ToLowerInvariant();
|
||||
}
|
||||
else if (args[i].StartsWith(key = "--dalamud-platform="))
|
||||
{
|
||||
platformStr = args[i][key.Length..].ToLowerInvariant();
|
||||
}
|
||||
else if (args[i].StartsWith(key = "--dalamud-tspack-b64="))
|
||||
{
|
||||
troubleshootingData = Encoding.UTF8.GetString(Convert.FromBase64String(args[i][key.Length..]));
|
||||
|
|
@ -378,11 +412,35 @@ namespace Dalamud.Injector
|
|||
throw new CommandLineException($"\"{languageStr}\" is not a valid supported language.");
|
||||
}
|
||||
|
||||
OSPlatform platform;
|
||||
if (platformStr[0..(len = Math.Min(platformStr.Length, (key = "win").Length))] == key[0..len]) // covers both win32 and Windows
|
||||
{
|
||||
platform = OSPlatform.Windows;
|
||||
}
|
||||
else if (platformStr[0..(len = Math.Min(platformStr.Length, (key = "linux").Length))] == key[0..len])
|
||||
{
|
||||
platform = OSPlatform.Linux;
|
||||
}
|
||||
else if (platformStr[0..(len = Math.Min(platformStr.Length, (key = "macos").Length))] == key[0..len])
|
||||
{
|
||||
platform = OSPlatform.OSX;
|
||||
}
|
||||
else if (platformStr[0..(len = Math.Min(platformStr.Length, (key = "osx").Length))] == key[0..len])
|
||||
{
|
||||
platform = OSPlatform.OSX;
|
||||
}
|
||||
else
|
||||
{
|
||||
platform = DetectPlatformHeuristic();
|
||||
Log.Warning("Heuristically determined host system platform as {platform}", platform);
|
||||
}
|
||||
|
||||
startInfo.WorkingDirectory = workingDirectory;
|
||||
startInfo.ConfigurationPath = configurationPath;
|
||||
startInfo.PluginDirectory = pluginDirectory;
|
||||
startInfo.AssetDirectory = assetDirectory;
|
||||
startInfo.Language = clientLanguage;
|
||||
startInfo.Platform = platform;
|
||||
startInfo.DelayInitializeMs = delayInitializeMs;
|
||||
startInfo.GameVersion = null;
|
||||
startInfo.TroubleshootingPackData = troubleshootingData;
|
||||
|
|
@ -465,7 +523,7 @@ namespace Dalamud.Injector
|
|||
}
|
||||
|
||||
Console.WriteLine("Specifying dalamud start info: [--dalamud-working-directory=path] [--dalamud-configuration-path=path]");
|
||||
Console.WriteLine(" [--dalamud-plugin-directory=path]");
|
||||
Console.WriteLine(" [--dalamud-plugin-directory=path] [--dalamud-platform=win32|linux|macOS]");
|
||||
Console.WriteLine(" [--dalamud-asset-directory=path] [--dalamud-delay-initialize=0(ms)]");
|
||||
Console.WriteLine(" [--dalamud-client-language=0-3|j(apanese)|e(nglish)|d|g(erman)|f(rench)]");
|
||||
|
||||
|
|
@ -732,15 +790,42 @@ namespace Dalamud.Injector
|
|||
{
|
||||
try
|
||||
{
|
||||
var appDataDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||
var xivlauncherDir = Path.Combine(appDataDir, "XIVLauncher");
|
||||
var launcherConfigPath = Path.Combine(xivlauncherDir, "launcherConfigV3.json");
|
||||
gamePath = Path.Combine(JsonSerializer.CreateDefault().Deserialize<Dictionary<string, string>>(new JsonTextReader(new StringReader(File.ReadAllText(launcherConfigPath))))["GamePath"], "game", "ffxiv_dx11.exe");
|
||||
Log.Information("Using game installation path configuration from from XIVLauncher: {0}", gamePath);
|
||||
if (dalamudStartInfo.Platform == OSPlatform.Windows)
|
||||
{
|
||||
var appDataDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||
var xivlauncherDir = Path.Combine(appDataDir, "XIVLauncher");
|
||||
var launcherConfigPath = Path.Combine(xivlauncherDir, "launcherConfigV3.json");
|
||||
gamePath = Path.Combine(
|
||||
JsonSerializer.CreateDefault()
|
||||
.Deserialize<Dictionary<string, string>>(
|
||||
new JsonTextReader(new StringReader(File.ReadAllText(launcherConfigPath))))["GamePath"],
|
||||
"game",
|
||||
"ffxiv_dx11.exe");
|
||||
Log.Information("Using game installation path configuration from from XIVLauncher: {0}", gamePath);
|
||||
}
|
||||
else if (dalamudStartInfo.Platform == OSPlatform.Linux)
|
||||
{
|
||||
var homeDir = $"Z:\\home\\{Environment.UserName}";
|
||||
var xivlauncherDir = Path.Combine(homeDir, ".xlcore");
|
||||
var launcherConfigPath = Path.Combine(xivlauncherDir, "launcher.ini");
|
||||
var config = File.ReadAllLines(launcherConfigPath)
|
||||
.Where(line => line.Contains('='))
|
||||
.ToDictionary(line => line.Split('=')[0], line => line.Split('=')[1]);
|
||||
gamePath = Path.Combine("Z:" + config["GamePath"].Replace('/', '\\'), "game", "ffxiv_dx11.exe");
|
||||
Log.Information("Using game installation path configuration from from XIVLauncher Core: {0}", gamePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
var homeDir = $"Z:\\Users\\{Environment.UserName}";
|
||||
var xomlauncherDir = Path.Combine(homeDir, "Library", "Application Support", "XIV on Mac");
|
||||
// we could try to parse the binary plist file here if we really wanted to...
|
||||
gamePath = Path.Combine(xomlauncherDir, "ffxiv", "game", "ffxiv_dx11.exe");
|
||||
Log.Information("Using default game installation path from XOM: {0}", gamePath);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Log.Error("Failed to read launcherConfigV3.json to get the set-up game path, please specify one using -g");
|
||||
Log.Error("Failed to read launcher config to get the set-up game path, please specify one using -g");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -795,20 +880,6 @@ namespace Dalamud.Injector
|
|||
if (encryptArguments)
|
||||
{
|
||||
var rawTickCount = (uint)Environment.TickCount;
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
[System.Runtime.InteropServices.DllImport("c")]
|
||||
#pragma warning disable SA1300
|
||||
static extern ulong clock_gettime_nsec_np(int clockId);
|
||||
#pragma warning restore SA1300
|
||||
|
||||
const int CLOCK_MONOTONIC_RAW = 4;
|
||||
var rawTickCountFixed = clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW) / 1000000;
|
||||
Log.Information("ArgumentBuilder::DeriveKey() fixing up rawTickCount from {0} to {1} on macOS", rawTickCount, rawTickCountFixed);
|
||||
rawTickCount = (uint)rawTickCountFixed;
|
||||
}
|
||||
|
||||
var ticks = rawTickCount & 0xFFFF_FFFFu;
|
||||
var key = ticks & 0xFFFF_0000u;
|
||||
gameArguments.Insert(0, $"T={ticks}");
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Dalamud.Injector
|
||||
|
|
@ -910,5 +911,46 @@ namespace Dalamud.Injector
|
|||
uint dwDesiredAccess,
|
||||
[MarshalAs(UnmanagedType.Bool)] bool bInheritHandle,
|
||||
DuplicateOptions dwOptions);
|
||||
|
||||
/// <summary>
|
||||
/// See https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandlew.
|
||||
/// Retrieves a module handle for the specified module. The module must have been loaded by the calling process. To
|
||||
/// avoid the race conditions described in the Remarks section, use the GetModuleHandleEx function.
|
||||
/// </summary>
|
||||
/// <param name="lpModuleName">
|
||||
/// The name of the loaded module (either a .dll or .exe file). If the file name extension is omitted, the default
|
||||
/// library extension .dll is appended. The file name string can include a trailing point character (.) to indicate
|
||||
/// that the module name has no extension. The string does not have to specify a path. When specifying a path, be sure
|
||||
/// to use backslashes (\), not forward slashes (/). The name is compared (case independently) to the names of modules
|
||||
/// currently mapped into the address space of the calling process. If this parameter is NULL, GetModuleHandle returns
|
||||
/// a handle to the file used to create the calling process (.exe file). The GetModuleHandle function does not retrieve
|
||||
/// handles for modules that were loaded using the LOAD_LIBRARY_AS_DATAFILE flag.For more information, see LoadLibraryEx.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// If the function succeeds, the return value is a handle to the specified module. If the function fails, the return
|
||||
/// value is NULL.To get extended error information, call GetLastError.
|
||||
/// </returns>
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
|
||||
public static extern IntPtr GetModuleHandleW(string lpModuleName);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the address of an exported function or variable from the specified dynamic-link library (DLL).
|
||||
/// </summary>
|
||||
/// <param name="hModule">
|
||||
/// A handle to the DLL module that contains the function or variable. The LoadLibrary, LoadLibraryEx, LoadPackagedLibrary,
|
||||
/// or GetModuleHandle function returns this handle. The GetProcAddress function does not retrieve addresses from modules
|
||||
/// that were loaded using the LOAD_LIBRARY_AS_DATAFILE flag.For more information, see LoadLibraryEx.
|
||||
/// </param>
|
||||
/// <param name="procName">
|
||||
/// The function or variable name, or the function's ordinal value. If this parameter is an ordinal value, it must be
|
||||
/// in the low-order word; the high-order word must be zero.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// If the function succeeds, the return value is the address of the exported function or variable. If the function
|
||||
/// fails, the return value is NULL.To get extended error information, call GetLastError.
|
||||
/// </returns>
|
||||
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
|
||||
[SuppressMessage("Globalization", "CA2101:Specify marshaling for P/Invoke string arguments", Justification = "Ansi only")]
|
||||
public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,11 +5,6 @@ namespace Dalamud.Configuration.Internal;
|
|||
/// </summary>
|
||||
internal class EnvironmentConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the XL_WINEONLINUX setting has been enabled.
|
||||
/// </summary>
|
||||
public static bool XlWineOnLinux { get; } = GetEnvironmentVariable("XL_WINEONLINUX");
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the DALAMUD_NOT_HAVE_PLUGINS setting has been enabled.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -182,6 +182,10 @@ public sealed class EntryPoint
|
|||
// Apply common fixes for culture issues
|
||||
CultureFixes.Apply();
|
||||
|
||||
// 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;
|
||||
|
||||
// Currently VEH is not fully functional on WINE
|
||||
if (!Util.IsWine())
|
||||
InitSymbolHandler(info);
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ using Dalamud.Game.ClientState.Objects.SubKinds;
|
|||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using Dalamud.Interface.Colors;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Logging.Internal;
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
using Dalamud.Support;
|
||||
using ImGuiNET;
|
||||
|
|
@ -500,55 +501,14 @@ public static class Util
|
|||
/// 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.
|
||||
/// </summary>
|
||||
/// <returns>Returns true if Wine is detected, false otherwise.</returns>
|
||||
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");
|
||||
|
||||
// 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 AnyProcExists(nint handle, params string[] procs) =>
|
||||
procs.Any(p => NativeFunctions.GetProcAddress(handle, p) != nint.Zero);
|
||||
}
|
||||
/// <returns>Returns true if running on Wine, false otherwise.</returns>
|
||||
public static bool IsWine() => Service<Dalamud>.Get().StartInfo.Platform != OSPlatform.Windows;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the best guess for the current host's platform based on the <c>XL_PLATFORM</c> environment variable or
|
||||
/// heuristics.
|
||||
/// Gets the current host's platform based on the injector launch arguments or heuristics.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// macOS users running without <c>XL_PLATFORM</c> 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.
|
||||
/// </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;
|
||||
}
|
||||
|
||||
// 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 IsWine() ? OSPlatform.Linux : OSPlatform.Windows;
|
||||
}
|
||||
public static OSPlatform GetHostPlatform() => Service<Dalamud>.Get().StartInfo.Platform;
|
||||
|
||||
/// <summary>
|
||||
/// Heuristically determine if the Windows version is higher than Windows 11's first build.
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
#include <Windows.h>
|
||||
#include <Lmcons.h>
|
||||
#include <Shlobj.h>
|
||||
#include "CoreCLR.h"
|
||||
#include "..\..\Dalamud.Boot\logging.h"
|
||||
|
|
@ -27,6 +28,64 @@ void ConsoleTeardown()
|
|||
|
||||
std::optional<CoreCLR> g_clr;
|
||||
|
||||
static wchar_t* GetRuntimePath()
|
||||
{
|
||||
int result;
|
||||
std::wstring buffer;
|
||||
wchar_t* runtime_path;
|
||||
wchar_t* _appdata;
|
||||
DWORD username_len = UNLEN + 1;
|
||||
wchar_t username[UNLEN + 1];
|
||||
|
||||
buffer.resize(0);
|
||||
result = GetEnvironmentVariableW(L"DALAMUD_RUNTIME", &buffer[0], 0);
|
||||
|
||||
if (result)
|
||||
{
|
||||
buffer.resize(result); // The first pass returns the required length
|
||||
result = GetEnvironmentVariableW(L"DALAMUD_RUNTIME", &buffer[0], result);
|
||||
return _wcsdup(buffer.c_str());
|
||||
}
|
||||
|
||||
// Detect Windows first
|
||||
result = SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_DEFAULT, nullptr, &_appdata);
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
logging::E("Unable to get RoamingAppData path (err={})", result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
std::filesystem::path fs_app_data(_appdata);
|
||||
runtime_path = _wcsdup(fs_app_data.append("XIVLauncher").append("runtime").c_str());
|
||||
if (std::filesystem::exists(runtime_path))
|
||||
return runtime_path;
|
||||
free(runtime_path);
|
||||
|
||||
// Next XLCore on Linux
|
||||
result = GetUserNameW(username, &username_len);
|
||||
if (result != 0)
|
||||
{
|
||||
logging::E("Unable to get user name (err={})", result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
std::filesystem::path homeDir = L"Z:\\home\\" + std::wstring(username);
|
||||
runtime_path = _wcsdup(homeDir.append(".xlcore").append("runtime").c_str());
|
||||
if (std::filesystem::exists(runtime_path))
|
||||
return runtime_path;
|
||||
free(runtime_path);
|
||||
|
||||
// Finally XOM
|
||||
homeDir = L"Z:\\Users\\" + std::wstring(username);
|
||||
runtime_path = _wcsdup(homeDir.append("Library").append("Application Suppor").append("XIV on Mac").append("runtime").c_str());
|
||||
if (std::filesystem::exists(runtime_path))
|
||||
return runtime_path;
|
||||
free(runtime_path);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
HRESULT InitializeClrAndGetEntryPoint(
|
||||
void* calling_module,
|
||||
bool enable_etw,
|
||||
|
|
@ -62,31 +121,12 @@ HRESULT InitializeClrAndGetEntryPoint(
|
|||
|
||||
SetEnvironmentVariable(L"COMPlus_ETWEnabled", enable_etw ? L"1" : L"0");
|
||||
|
||||
wchar_t* dotnet_path;
|
||||
wchar_t* _appdata;
|
||||
wchar_t* dotnet_path = GetRuntimePath();
|
||||
|
||||
std::wstring buffer;
|
||||
buffer.resize(0);
|
||||
result = GetEnvironmentVariableW(L"DALAMUD_RUNTIME", &buffer[0], 0);
|
||||
|
||||
if (result)
|
||||
if (!dotnet_path || !std::filesystem::exists(dotnet_path))
|
||||
{
|
||||
buffer.resize(result); // The first pass returns the required length
|
||||
result = GetEnvironmentVariableW(L"DALAMUD_RUNTIME", &buffer[0], result);
|
||||
dotnet_path = _wcsdup(buffer.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
result = SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_DEFAULT, nullptr, &_appdata);
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
logging::E("Unable to get RoamingAppData path (err={})", result);
|
||||
return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
|
||||
}
|
||||
|
||||
std::filesystem::path fs_app_data(_appdata);
|
||||
dotnet_path = _wcsdup(fs_app_data.append("XIVLauncher").append("runtime").c_str());
|
||||
logging::E("Error: Unable to find .NET runtime path");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// =========================================================================== //
|
||||
|
|
@ -95,12 +135,6 @@ HRESULT InitializeClrAndGetEntryPoint(
|
|||
logging::I("with config_path: {}", runtimeconfig_path);
|
||||
logging::I("with module_path: {}", module_path);
|
||||
|
||||
if (!std::filesystem::exists(dotnet_path))
|
||||
{
|
||||
logging::E("Error: Unable to find .NET runtime path");
|
||||
return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
|
||||
}
|
||||
|
||||
get_hostfxr_parameters init_parameters
|
||||
{
|
||||
sizeof(get_hostfxr_parameters),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue