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.PluginDirectory = json.value("PluginDirectory", config.PluginDirectory);
|
||||||
config.AssetDirectory = json.value("AssetDirectory", config.AssetDirectory);
|
config.AssetDirectory = json.value("AssetDirectory", config.AssetDirectory);
|
||||||
config.Language = json.value("Language", config.Language);
|
config.Language = json.value("Language", config.Language);
|
||||||
|
config.Platform = json.value("Platform", config.Platform);
|
||||||
config.GameVersion = json.value("GameVersion", config.GameVersion);
|
config.GameVersion = json.value("GameVersion", config.GameVersion);
|
||||||
config.TroubleshootingPackData = json.value("TroubleshootingPackData", std::string{});
|
config.TroubleshootingPackData = json.value("TroubleshootingPackData", std::string{});
|
||||||
config.DelayInitializeMs = json.value("DelayInitializeMs", config.DelayInitializeMs);
|
config.DelayInitializeMs = json.value("DelayInitializeMs", config.DelayInitializeMs);
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ struct DalamudStartInfo {
|
||||||
std::string PluginDirectory;
|
std::string PluginDirectory;
|
||||||
std::string AssetDirectory;
|
std::string AssetDirectory;
|
||||||
ClientLanguage Language = ClientLanguage::English;
|
ClientLanguage Language = ClientLanguage::English;
|
||||||
|
std::string Platform;
|
||||||
std::string GameVersion;
|
std::string GameVersion;
|
||||||
std::string TroubleshootingPackData;
|
std::string TroubleshootingPackData;
|
||||||
int DelayInitializeMs = 0;
|
int DelayInitializeMs = 0;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
|
#include "DalamudStartInfo.h"
|
||||||
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
|
|
@ -584,6 +585,10 @@ std::vector<std::string> utils::get_env_list(const wchar_t* pcszName) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool utils::is_running_on_wine() {
|
||||||
|
return g_startInfo.Platform != "WINDOWS";
|
||||||
|
}
|
||||||
|
|
||||||
std::filesystem::path utils::get_module_path(HMODULE hModule) {
|
std::filesystem::path utils::get_module_path(HMODULE hModule) {
|
||||||
std::wstring buf(MAX_PATH, L'\0');
|
std::wstring buf(MAX_PATH, L'\0');
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using Dalamud.Common.Game;
|
using Dalamud.Common.Game;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
|
@ -15,7 +16,7 @@ public record DalamudStartInfo
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DalamudStartInfo()
|
public DalamudStartInfo()
|
||||||
{
|
{
|
||||||
// ignored
|
this.Platform = OSPlatform.Create("UNKNOWN");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -58,6 +59,12 @@ public record DalamudStartInfo
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ClientLanguage Language { get; set; } = ClientLanguage.English;
|
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>
|
/// <summary>
|
||||||
/// Gets or sets the current game version code.
|
/// Gets or sets the current game version code.
|
||||||
/// </summary>
|
/// </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)
|
private static DalamudStartInfo ExtractAndInitializeStartInfoFromArguments(DalamudStartInfo? startInfo, List<string> args)
|
||||||
{
|
{
|
||||||
int len;
|
int len;
|
||||||
|
|
@ -278,6 +307,7 @@ namespace Dalamud.Injector
|
||||||
var logName = startInfo.LogName;
|
var logName = startInfo.LogName;
|
||||||
var logPath = startInfo.LogPath;
|
var logPath = startInfo.LogPath;
|
||||||
var languageStr = startInfo.Language.ToString().ToLowerInvariant();
|
var languageStr = startInfo.Language.ToString().ToLowerInvariant();
|
||||||
|
var platformStr = startInfo.Platform.ToString().ToLowerInvariant();
|
||||||
var unhandledExceptionStr = startInfo.UnhandledException.ToString().ToLowerInvariant();
|
var unhandledExceptionStr = startInfo.UnhandledException.ToString().ToLowerInvariant();
|
||||||
var troubleshootingData = "{\"empty\": true, \"description\": \"No troubleshooting data supplied.\"}";
|
var troubleshootingData = "{\"empty\": true, \"description\": \"No troubleshooting data supplied.\"}";
|
||||||
|
|
||||||
|
|
@ -307,6 +337,10 @@ namespace Dalamud.Injector
|
||||||
{
|
{
|
||||||
languageStr = args[i][key.Length..].ToLowerInvariant();
|
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="))
|
else if (args[i].StartsWith(key = "--dalamud-tspack-b64="))
|
||||||
{
|
{
|
||||||
troubleshootingData = Encoding.UTF8.GetString(Convert.FromBase64String(args[i][key.Length..]));
|
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.");
|
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.WorkingDirectory = workingDirectory;
|
||||||
startInfo.ConfigurationPath = configurationPath;
|
startInfo.ConfigurationPath = configurationPath;
|
||||||
startInfo.PluginDirectory = pluginDirectory;
|
startInfo.PluginDirectory = pluginDirectory;
|
||||||
startInfo.AssetDirectory = assetDirectory;
|
startInfo.AssetDirectory = assetDirectory;
|
||||||
startInfo.Language = clientLanguage;
|
startInfo.Language = clientLanguage;
|
||||||
|
startInfo.Platform = platform;
|
||||||
startInfo.DelayInitializeMs = delayInitializeMs;
|
startInfo.DelayInitializeMs = delayInitializeMs;
|
||||||
startInfo.GameVersion = null;
|
startInfo.GameVersion = null;
|
||||||
startInfo.TroubleshootingPackData = troubleshootingData;
|
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("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-asset-directory=path] [--dalamud-delay-initialize=0(ms)]");
|
||||||
Console.WriteLine(" [--dalamud-client-language=0-3|j(apanese)|e(nglish)|d|g(erman)|f(rench)]");
|
Console.WriteLine(" [--dalamud-client-language=0-3|j(apanese)|e(nglish)|d|g(erman)|f(rench)]");
|
||||||
|
|
||||||
|
|
@ -732,15 +790,42 @@ namespace Dalamud.Injector
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var appDataDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
if (dalamudStartInfo.Platform == OSPlatform.Windows)
|
||||||
var xivlauncherDir = Path.Combine(appDataDir, "XIVLauncher");
|
{
|
||||||
var launcherConfigPath = Path.Combine(xivlauncherDir, "launcherConfigV3.json");
|
var appDataDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||||
gamePath = Path.Combine(JsonSerializer.CreateDefault().Deserialize<Dictionary<string, string>>(new JsonTextReader(new StringReader(File.ReadAllText(launcherConfigPath))))["GamePath"], "game", "ffxiv_dx11.exe");
|
var xivlauncherDir = Path.Combine(appDataDir, "XIVLauncher");
|
||||||
Log.Information("Using game installation path configuration from from XIVLauncher: {0}", gamePath);
|
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)
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -795,20 +880,6 @@ namespace Dalamud.Injector
|
||||||
if (encryptArguments)
|
if (encryptArguments)
|
||||||
{
|
{
|
||||||
var rawTickCount = (uint)Environment.TickCount;
|
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 ticks = rawTickCount & 0xFFFF_FFFFu;
|
||||||
var key = ticks & 0xFFFF_0000u;
|
var key = ticks & 0xFFFF_0000u;
|
||||||
gameArguments.Insert(0, $"T={ticks}");
|
gameArguments.Insert(0, $"T={ticks}");
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Dalamud.Injector
|
namespace Dalamud.Injector
|
||||||
|
|
@ -910,5 +911,46 @@ namespace Dalamud.Injector
|
||||||
uint dwDesiredAccess,
|
uint dwDesiredAccess,
|
||||||
[MarshalAs(UnmanagedType.Bool)] bool bInheritHandle,
|
[MarshalAs(UnmanagedType.Bool)] bool bInheritHandle,
|
||||||
DuplicateOptions dwOptions);
|
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>
|
/// </summary>
|
||||||
internal class EnvironmentConfiguration
|
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>
|
/// <summary>
|
||||||
/// Gets a value indicating whether the DALAMUD_NOT_HAVE_PLUGINS setting has been enabled.
|
/// Gets a value indicating whether the DALAMUD_NOT_HAVE_PLUGINS setting has been enabled.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -181,7 +181,11 @@ public sealed class EntryPoint
|
||||||
|
|
||||||
// Apply common fixes for culture issues
|
// Apply common fixes for culture issues
|
||||||
CultureFixes.Apply();
|
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())
|
if (!Util.IsWine())
|
||||||
InitSymbolHandler(info);
|
InitSymbolHandler(info);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||||
using Dalamud.Game.ClientState.Objects.Types;
|
using Dalamud.Game.ClientState.Objects.Types;
|
||||||
using Dalamud.Interface.Colors;
|
using Dalamud.Interface.Colors;
|
||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
|
using Dalamud.Logging.Internal;
|
||||||
using Dalamud.Interface.Utility.Raii;
|
using Dalamud.Interface.Utility.Raii;
|
||||||
using Dalamud.Support;
|
using Dalamud.Support;
|
||||||
using ImGuiNET;
|
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
|
/// 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.
|
/// will not return information about the host operating system.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Returns true if Wine is detected, false otherwise.</returns>
|
/// <returns>Returns true if running on Wine, false otherwise.</returns>
|
||||||
public static bool IsWine()
|
public static bool IsWine() => Service<Dalamud>.Get().StartInfo.Platform != OSPlatform.Windows;
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the best guess for the current host's platform based on the <c>XL_PLATFORM</c> environment variable or
|
/// Gets the current host's platform based on the injector launch arguments or heuristics.
|
||||||
/// heuristics.
|
|
||||||
/// </summary>
|
/// </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>
|
/// <returns>Returns the <see cref="OSPlatform"/> that Dalamud is currently running on.</returns>
|
||||||
public static OSPlatform GetHostPlatform()
|
public static OSPlatform GetHostPlatform() => Service<Dalamud>.Get().StartInfo.Platform;
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <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.
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
#include <Lmcons.h>
|
||||||
#include <Shlobj.h>
|
#include <Shlobj.h>
|
||||||
#include "CoreCLR.h"
|
#include "CoreCLR.h"
|
||||||
#include "..\..\Dalamud.Boot\logging.h"
|
#include "..\..\Dalamud.Boot\logging.h"
|
||||||
|
|
@ -27,6 +28,64 @@ void ConsoleTeardown()
|
||||||
|
|
||||||
std::optional<CoreCLR> g_clr;
|
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(
|
HRESULT InitializeClrAndGetEntryPoint(
|
||||||
void* calling_module,
|
void* calling_module,
|
||||||
bool enable_etw,
|
bool enable_etw,
|
||||||
|
|
@ -62,31 +121,12 @@ HRESULT InitializeClrAndGetEntryPoint(
|
||||||
|
|
||||||
SetEnvironmentVariable(L"COMPlus_ETWEnabled", enable_etw ? L"1" : L"0");
|
SetEnvironmentVariable(L"COMPlus_ETWEnabled", enable_etw ? L"1" : L"0");
|
||||||
|
|
||||||
wchar_t* dotnet_path;
|
wchar_t* dotnet_path = GetRuntimePath();
|
||||||
wchar_t* _appdata;
|
|
||||||
|
|
||||||
std::wstring buffer;
|
if (!dotnet_path || !std::filesystem::exists(dotnet_path))
|
||||||
buffer.resize(0);
|
|
||||||
result = GetEnvironmentVariableW(L"DALAMUD_RUNTIME", &buffer[0], 0);
|
|
||||||
|
|
||||||
if (result)
|
|
||||||
{
|
{
|
||||||
buffer.resize(result); // The first pass returns the required length
|
logging::E("Error: Unable to find .NET runtime path");
|
||||||
result = GetEnvironmentVariableW(L"DALAMUD_RUNTIME", &buffer[0], result);
|
return 1;
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// =========================================================================== //
|
// =========================================================================== //
|
||||||
|
|
@ -95,12 +135,6 @@ HRESULT InitializeClrAndGetEntryPoint(
|
||||||
logging::I("with config_path: {}", runtimeconfig_path);
|
logging::I("with config_path: {}", runtimeconfig_path);
|
||||||
logging::I("with module_path: {}", module_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
|
get_hostfxr_parameters init_parameters
|
||||||
{
|
{
|
||||||
sizeof(get_hostfxr_parameters),
|
sizeof(get_hostfxr_parameters),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue