diff --git a/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs b/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs index 8216526f9..5d1c1c183 100644 --- a/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs +++ b/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs @@ -17,97 +17,117 @@ namespace XIVLauncher.Common.Unix.Compatibility; public class CompatibilityTools { - private DirectoryInfo toolDirectory; + private DirectoryInfo wineDirectory; + private DirectoryInfo dxvkDirectory; private StreamWriter logWriter; -#if WINE_XIV_ARCH_LINUX - private const string WINE_XIV_RELEASE_URL = "https://github.com/goatcorp/wine-xiv-git/releases/download/8.5.r4.g4211bac7/wine-xiv-staging-fsync-git-arch-8.5.r4.g4211bac7.tar.xz"; -#elif WINE_XIV_FEDORA_LINUX - private const string WINE_XIV_RELEASE_URL = "https://github.com/goatcorp/wine-xiv-git/releases/download/8.5.r4.g4211bac7/wine-xiv-staging-fsync-git-fedora-8.5.r4.g4211bac7.tar.xz"; -#else - private const string WINE_XIV_RELEASE_URL = "https://github.com/goatcorp/wine-xiv-git/releases/download/8.5.r4.g4211bac7/wine-xiv-staging-fsync-git-ubuntu-8.5.r4.g4211bac7.tar.xz"; -#endif - private const string WINE_XIV_RELEASE_NAME = "wine-xiv-staging-fsync-git-8.5.r4.g4211bac7"; - public bool IsToolReady { get; private set; } - public WineSettings Settings { get; private set; } + public WineSettings WineSettings { get; private set; } + + public DxvkSettings DxvkSettings { get; private set; } - private string WineBinPath => Settings.StartupType == WineStartupType.Managed ? - Path.Combine(toolDirectory.FullName, WINE_XIV_RELEASE_NAME, "bin") : - Settings.CustomBinPath; - private string Wine64Path => Path.Combine(WineBinPath, "wine64"); - private string WineServerPath => Path.Combine(WineBinPath, "wineserver"); + private string WineDLLOverrides; - public bool IsToolDownloaded => File.Exists(Wine64Path) && Settings.Prefix.Exists; + private FileInfo LogFile; - private readonly Dxvk.DxvkHudType hudType; - private readonly bool gamemodeOn; - private readonly string dxvkAsyncOn; + public DirectoryInfo Prefix { get; private set; } - public CompatibilityTools(WineSettings wineSettings, Dxvk.DxvkHudType hudType, bool? gamemodeOn, bool? dxvkAsyncOn, DirectoryInfo toolsFolder) + public bool IsToolDownloaded => File.Exists(WineSettings.RunCommand) && Prefix.Exists; + + public bool IsFlatpak; // Not currently used. + + + public CompatibilityTools(WineSettings wineSettings, DxvkSettings dxvkSettings, DirectoryInfo prefix, DirectoryInfo toolsFolder, FileInfo logfile, bool isFlatpak) { - this.Settings = wineSettings; - this.hudType = hudType; - this.gamemodeOn = gamemodeOn ?? false; - this.dxvkAsyncOn = (dxvkAsyncOn ?? false) ? "1" : "0"; + WineSettings = wineSettings; + DxvkSettings = dxvkSettings; + Prefix = prefix; + WineDLLOverrides = $"msquic=,mscoree=n,b;d3d9,d3d11,d3d10core,dxgi={(dxvkSettings.Enabled ? "n,b" : "b")}"; + wineDirectory = new DirectoryInfo(Path.Combine(toolsFolder.FullName, "wine")); + dxvkDirectory = new DirectoryInfo(Path.Combine(toolsFolder.FullName, "dxvk")); + LogFile = logfile; - this.toolDirectory = new DirectoryInfo(Path.Combine(toolsFolder.FullName, "beta")); - this.dxvkDirectory = new DirectoryInfo(Path.Combine(toolsFolder.FullName, "dxvk")); + logWriter = new StreamWriter(LogFile.FullName); - this.logWriter = new StreamWriter(wineSettings.LogFile.FullName); + IsFlatpak = isFlatpak; - if (wineSettings.StartupType == WineStartupType.Managed) - { - if (!this.toolDirectory.Exists) - this.toolDirectory.Create(); + if (!wineDirectory.Exists) + wineDirectory.Create(); - if (!this.dxvkDirectory.Exists) - this.dxvkDirectory.Create(); - } + if (!dxvkDirectory.Exists) + dxvkDirectory.Create(); - if (!wineSettings.Prefix.Exists) - wineSettings.Prefix.Create(); + if (!Prefix.Exists) + Prefix.Create(); } public async Task EnsureTool(DirectoryInfo tempPath) { - if (!File.Exists(Wine64Path)) - { - Log.Information("Compatibility tool does not exist, downloading"); - await DownloadTool(tempPath).ConfigureAwait(false); - } - + // Check to make sure wine is valid + await EnsureWine(); + if (!File.Exists(WineSettings.RunCommand)) + throw new FileNotFoundException("No wine or wine64 binary could be found."); EnsurePrefix(); - await Dxvk.InstallDxvk(Settings.Prefix, dxvkDirectory).ConfigureAwait(false); + + // Check to make sure dxvk is valid + if (DxvkSettings.Enabled) + await EnsureDxvk(); IsToolReady = true; + Log.Information($"Using wine at path {WineSettings.RunCommand}"); + } + + private async Task EnsureWine() + { + if (!WineSettings.IsManaged) return; + + await DownloadTool(wineDirectory.FullName, WineSettings.Folder, WineSettings.DownloadUrl); + + // Use wine if wine64 isn't found. This is mostly for WoW64 wine builds. } - private async Task DownloadTool(DirectoryInfo tempPath) + private async Task EnsureDxvk() { - using var client = new HttpClient(); - var tempFilePath = Path.Combine(tempPath.FullName, $"{Guid.NewGuid()}"); + await DownloadTool(dxvkDirectory.FullName, DxvkSettings.Folder, DxvkSettings.DownloadUrl); + + var prefixinstall = Path.Combine(Prefix.FullName, "drive_c", "windows", "system32"); + var files = new DirectoryInfo(Path.Combine(dxvkDirectory.FullName, DxvkSettings.Folder, "x64")).GetFiles(); - await File.WriteAllBytesAsync(tempFilePath, await client.GetByteArrayAsync(WINE_XIV_RELEASE_URL).ConfigureAwait(false)).ConfigureAwait(false); + foreach (FileInfo fileName in files) + fileName.CopyTo(Path.Combine(prefixinstall, fileName.Name), true); + } - PlatformHelpers.Untar(tempFilePath, this.toolDirectory.FullName); + private async Task DownloadTool(string toolDirectory, string toolFolder, string downloadUrl) + { + if (IsDirectoryEmpty(Path.Combine(toolDirectory, toolFolder))) + { + if (string.IsNullOrEmpty(downloadUrl)) + { + Log.Error($"Attempted to download {toolFolder} without a download URL."); + throw new InvalidOperationException($"{toolFolder} does not exist, and no download URL was provided for it."); + } + Log.Information($"{toolFolder} does not exist. Downloading..."); + using var client = new HttpClient(); + var tempPath = Path.GetTempFileName(); - Log.Information("Compatibility tool successfully extracted to {Path}", this.toolDirectory.FullName); + File.WriteAllBytes(tempPath, await client.GetByteArrayAsync(downloadUrl)); + PlatformHelpers.Untar(tempPath, toolDirectory); - File.Delete(tempFilePath); + File.Delete(tempPath); + } } private void ResetPrefix() { - Settings.Prefix.Refresh(); + Prefix.Refresh(); - if (Settings.Prefix.Exists) - Settings.Prefix.Delete(true); + if (Prefix.Exists) + Prefix.Delete(true); - Settings.Prefix.Create(); + Prefix.Create(); EnsurePrefix(); } @@ -118,24 +138,24 @@ public void EnsurePrefix() public Process RunInPrefix(string command, string workingDirectory = "", IDictionary environment = null, bool redirectOutput = false, bool writeLog = false, bool wineD3D = false) { - var psi = new ProcessStartInfo(Wine64Path); - psi.Arguments = command; + var psi = new ProcessStartInfo(WineSettings.RunCommand); + psi.Arguments = command.Trim(); - Log.Verbose("Running in prefix: {FileName} {Arguments}", psi.FileName, command); + Log.Verbose("Running in prefix: {FileName} {Arguments}", psi.FileName, psi.Arguments); return RunInPrefix(psi, workingDirectory, environment, redirectOutput, writeLog, wineD3D); } public Process RunInPrefix(string[] args, string workingDirectory = "", IDictionary environment = null, bool redirectOutput = false, bool writeLog = false, bool wineD3D = false) { - var psi = new ProcessStartInfo(Wine64Path); + var psi = new ProcessStartInfo(WineSettings.RunCommand); foreach (var arg in args) psi.ArgumentList.Add(arg); - + Log.Verbose("Running in prefix: {FileName} {Arguments}", psi.FileName, psi.ArgumentList.Aggregate(string.Empty, (a, b) => a + " " + b)); return RunInPrefix(psi, workingDirectory, environment, redirectOutput, writeLog, wineD3D); } - private void MergeDictionaries(StringDictionary a, IDictionary b) + private void MergeDictionaries(IDictionary a, IDictionary b) { if (b is null) return; @@ -143,12 +163,31 @@ private void MergeDictionaries(StringDictionary a, IDictionary b foreach (var keyValuePair in b) { if (a.ContainsKey(keyValuePair.Key)) - a[keyValuePair.Key] = keyValuePair.Value; + { + if (keyValuePair.Key == "LD_PRELOAD") + a[keyValuePair.Key] = MergeLDPreload(a[keyValuePair.Key], keyValuePair.Value); + else + a[keyValuePair.Key] = keyValuePair.Value; + } else a.Add(keyValuePair.Key, keyValuePair.Value); } } + private string MergeLDPreload(string a, string b) + { + var alist = a.Split(':'); + var blist = b.Split(':'); + + var merged = alist.Union(blist); + + var ldpreload = ""; + foreach (var item in merged) + ldpreload += item + ":"; + + return ldpreload.TrimEnd(':'); + } + private Process RunInPrefix(ProcessStartInfo psi, string workingDirectory, IDictionary environment, bool redirectOutput, bool writeLog, bool wineD3D) { psi.RedirectStandardOutput = redirectOutput; @@ -156,48 +195,28 @@ private Process RunInPrefix(ProcessStartInfo psi, string workingDirectory, IDict psi.UseShellExecute = false; psi.WorkingDirectory = workingDirectory; - var wineEnviromentVariables = new Dictionary(); - wineEnviromentVariables.Add("WINEPREFIX", Settings.Prefix.FullName); - wineEnviromentVariables.Add("WINEDLLOVERRIDES", $"msquic=,mscoree=n,b;d3d9,d3d11,d3d10core,dxgi={(wineD3D ? "b" : "n")}"); - - if (!string.IsNullOrEmpty(Settings.DebugVars)) - { - wineEnviromentVariables.Add("WINEDEBUG", Settings.DebugVars); - } - - wineEnviromentVariables.Add("XL_WINEONLINUX", "true"); - string ldPreload = Environment.GetEnvironmentVariable("LD_PRELOAD") ?? ""; - - string dxvkHud = hudType switch - { - Dxvk.DxvkHudType.None => "0", - Dxvk.DxvkHudType.Fps => "fps", - Dxvk.DxvkHudType.Full => "full", - _ => throw new ArgumentOutOfRangeException() - }; - - if (this.gamemodeOn == true && !ldPreload.Contains("libgamemodeauto.so.0")) - { - ldPreload = ldPreload.Equals("") ? "libgamemodeauto.so.0" : ldPreload + ":libgamemodeauto.so.0"; - } - - wineEnviromentVariables.Add("DXVK_HUD", dxvkHud); - wineEnviromentVariables.Add("DXVK_ASYNC", dxvkAsyncOn); - wineEnviromentVariables.Add("WINEESYNC", Settings.EsyncOn); - wineEnviromentVariables.Add("WINEFSYNC", Settings.FsyncOn); - - wineEnviromentVariables.Add("LD_PRELOAD", ldPreload); - - MergeDictionaries(psi.EnvironmentVariables, wineEnviromentVariables); - MergeDictionaries(psi.EnvironmentVariables, environment); + var wineEnvironmentVariables = new Dictionary(); + if (wineD3D) + wineEnvironmentVariables.Add("WINEDLLOVERRIDES", "msquic=,mscoree=n,b;d3d9,d3d11,d3d10core,dxgi=b"); + else + wineEnvironmentVariables.Add("WINEDLLOVERRIDES", WineDLLOverrides); + wineEnvironmentVariables.Add("XL_WINEONLINUX", "true"); + var ldPreload = Environment.GetEnvironmentVariable("LD_PRELOAD"); + if (ldPreload is not null) + wineEnvironmentVariables.Add("LD_PRELOAD", ldPreload); + + MergeDictionaries(psi.Environment, WineSettings.Environment); + MergeDictionaries(psi.Environment, DxvkSettings.Environment); + MergeDictionaries(psi.Environment, wineEnvironmentVariables); + MergeDictionaries(psi.Environment, environment); #if FLATPAK_NOTRIGHTNOW psi.FileName = "flatpak-spawn"; psi.ArgumentList.Insert(0, "--host"); - psi.ArgumentList.Insert(1, Wine64Path); + psi.ArgumentList.Insert(1, WineSettings.RunCommand); - foreach (KeyValuePair envVar in wineEnviromentVariables) + foreach (KeyValuePair envVar in wineEnvironmentVariables) { psi.ArgumentList.Insert(1, $"--env={envVar.Key}={envVar.Value}"); } @@ -258,13 +277,53 @@ public Int32 GetUnixProcessId(Int32 winePid) var wineDbg = RunInPrefix("winedbg --command \"info procmap\"", redirectOutput: true); var output = wineDbg.StandardOutput.ReadToEnd(); if (output.Contains("syntax error\n")) - return 0; + { + var processName = GetProcessName(winePid); + return GetUnixProcessIdByName(processName); + } var matchingLines = output.Split('\n', StringSplitOptions.RemoveEmptyEntries).Skip(1).Where( l => int.Parse(l.Substring(1, 8), System.Globalization.NumberStyles.HexNumber) == winePid); var unixPids = matchingLines.Select(l => int.Parse(l.Substring(10, 8), System.Globalization.NumberStyles.HexNumber)).ToArray(); return unixPids.FirstOrDefault(); } + private string GetProcessName(Int32 winePid) + { + var wineDbg = RunInPrefix("winedbg --command \"info proc\"", redirectOutput: true); + var output = wineDbg.StandardOutput.ReadToEnd(); + var matchingLines = output.Split('\n', StringSplitOptions.RemoveEmptyEntries).Skip(1).Where( + l => int.Parse(l.Substring(1, 8), System.Globalization.NumberStyles.HexNumber) == winePid); + var processNames = matchingLines.Select( l => l.Substring(20).Trim('\'')).ToArray(); + return processNames.FirstOrDefault(); + } + + private Int32 GetUnixProcessIdByName(string executableName) + { + int closest = 0; + int early = 0; + var currentProcess = Process.GetCurrentProcess(); // Gets XIVLauncher.Core's process + bool nonunique = false; + foreach (var process in Process.GetProcessesByName(executableName)) + { + if (process.Id < currentProcess.Id) + { + early = process.Id; + continue; // Process was launched before XIVLauncher.Core + } + // Assume that the closest PID to XIVLauncher.Core's is the correct one. But log an error if more than one is found. + if ((closest - currentProcess.Id) > (process.Id - currentProcess.Id) || closest == 0) + { + if (closest != 0) nonunique = true; + closest = process.Id; + } + if (nonunique) Log.Error($"More than one {executableName} found! Selecting the most likely match with process id {closest}."); + } + // Deal with rare edge-case where pid rollover causes the ffxiv pid to be lower than XLCore's. + if (closest == 0 && early != 0) closest = early; + if (closest != 0) Log.Verbose($"Process for {executableName} found using fallback method: {closest}. XLCore pid: {currentProcess.Id}"); + return closest; + } + public string UnixToWinePath(string unixPath) { var launchArguments = new string[] { "winepath", "--windows", unixPath }; @@ -282,12 +341,18 @@ public void AddRegistryKey(string key, string value, string data) public void Kill() { - var psi = new ProcessStartInfo(WineServerPath) + var psi = new ProcessStartInfo(WineSettings.WineServer) { Arguments = "-k" }; - psi.EnvironmentVariables.Add("WINEPREFIX", Settings.Prefix.FullName); + psi.Environment.Add("WINEPREFIX", Prefix.FullName); Process.Start(psi); } + + private bool IsDirectoryEmpty(string folder) + { + if (!Directory.Exists(folder)) return true; + return !Directory.EnumerateFileSystemEntries(folder).Any(); + } } \ No newline at end of file diff --git a/src/XIVLauncher.Common.Unix/Compatibility/Dxvk.cs b/src/XIVLauncher.Common.Unix/Compatibility/Dxvk.cs deleted file mode 100644 index 50fa98d9e..000000000 --- a/src/XIVLauncher.Common.Unix/Compatibility/Dxvk.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System.IO; -using System.Net.Http; -using System.Threading.Tasks; -using Serilog; -using XIVLauncher.Common.Util; - -namespace XIVLauncher.Common.Unix.Compatibility; - -public static class Dxvk -{ - private const string DXVK_DOWNLOAD = "https://github.com/Sporif/dxvk-async/releases/download/1.10.1/dxvk-async-1.10.1.tar.gz"; - private const string DXVK_NAME = "dxvk-async-1.10.1"; - - public static async Task InstallDxvk(DirectoryInfo prefix, DirectoryInfo installDirectory) - { - var dxvkPath = Path.Combine(installDirectory.FullName, DXVK_NAME, "x64"); - - if (!Directory.Exists(dxvkPath)) - { - Log.Information("DXVK does not exist, downloading"); - await DownloadDxvk(installDirectory).ConfigureAwait(false); - } - - var system32 = Path.Combine(prefix.FullName, "drive_c", "windows", "system32"); - var files = Directory.GetFiles(dxvkPath); - - foreach (string fileName in files) - { - File.Copy(fileName, Path.Combine(system32, Path.GetFileName(fileName)), true); - } - } - - private static async Task DownloadDxvk(DirectoryInfo installDirectory) - { - using var client = new HttpClient(); - var tempPath = Path.GetTempFileName(); - - File.WriteAllBytes(tempPath, await client.GetByteArrayAsync(DXVK_DOWNLOAD)); - PlatformHelpers.Untar(tempPath, installDirectory.FullName); - - File.Delete(tempPath); - } - - public enum DxvkHudType - { - [SettingsDescription("None", "Show nothing")] - None, - - [SettingsDescription("FPS", "Only show FPS")] - Fps, - - [SettingsDescription("Full", "Show everything")] - Full, - } -} \ No newline at end of file diff --git a/src/XIVLauncher.Common.Unix/Compatibility/DxvkSettings.cs b/src/XIVLauncher.Common.Unix/Compatibility/DxvkSettings.cs new file mode 100644 index 000000000..6495611ad --- /dev/null +++ b/src/XIVLauncher.Common.Unix/Compatibility/DxvkSettings.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using Serilog; + +namespace XIVLauncher.Common.Unix.Compatibility; + +public class DxvkSettings +{ + public string Folder { get; } + + public string DownloadUrl { get; } + + public Dictionary Environment { get; } + + public bool Enabled { get; } + + public DxvkSettings(string folder, string url, string rootFolder, Dictionary env = null, bool enabled = true) + { + Folder = folder; + DownloadUrl = url; + Environment = env ?? new Dictionary(); + Enabled = enabled; + } +} \ No newline at end of file diff --git a/src/XIVLauncher.Common.Unix/Compatibility/WineSettings.cs b/src/XIVLauncher.Common.Unix/Compatibility/WineSettings.cs index 9b122f7aa..0f9f6c7a0 100644 --- a/src/XIVLauncher.Common.Unix/Compatibility/WineSettings.cs +++ b/src/XIVLauncher.Common.Unix/Compatibility/WineSettings.cs @@ -1,37 +1,56 @@ -using System.IO; +using System.Collections.Generic; +using System.IO; +using Serilog; namespace XIVLauncher.Common.Unix.Compatibility; -public enum WineStartupType +public class WineSettings { - [SettingsDescription("Managed by XIVLauncher", "The game installation and wine setup is managed by XIVLauncher - you can leave it up to us.")] - Managed, + private string RunCommandFolder; + + public string RunCommand + { + get + { + if (File.Exists(Path.Combine(RunCommandFolder, "wine64"))) + return Path.Combine(RunCommandFolder, "wine64"); + + if (File.Exists(Path.Combine(RunCommandFolder, "wine"))) + return Path.Combine(RunCommandFolder, "wine"); + + return string.Empty; + } + } - [SettingsDescription("Custom", "Point XIVLauncher to a custom location containing wine binaries to run the game with.")] - Custom, -} + public string WineServer { get; } -public class WineSettings -{ - public WineStartupType StartupType { get; private set; } - public string CustomBinPath { get; private set; } + public string Folder { get; } - public string EsyncOn { get; private set; } - public string FsyncOn { get; private set; } + public string DownloadUrl { get; } - public string DebugVars { get; private set; } - public FileInfo LogFile { get; private set; } + public bool IsManaged { get; } - public DirectoryInfo Prefix { get; private set; } + public Dictionary Environment { get; } - public WineSettings(WineStartupType? startupType, string customBinPath, string debugVars, FileInfo logFile, DirectoryInfo prefix, bool? esyncOn, bool? fsyncOn) + public WineSettings(string customWinePath, string managedFolder, string managedDownloadUrl, string rootFolder, Dictionary env = null) { - this.StartupType = startupType ?? WineStartupType.Custom; - this.CustomBinPath = customBinPath; - this.EsyncOn = (esyncOn ?? false) ? "1" : "0"; - this.FsyncOn = (fsyncOn ?? false) ? "1" : "0"; - this.DebugVars = debugVars; - this.LogFile = logFile; - this.Prefix = prefix; + Folder = managedFolder; + DownloadUrl = managedDownloadUrl; + Environment = env ?? new Dictionary(); + + // Use customWinePath to pass in the custom wine bin/ path. If it's empty, we construct the RunCommand from the managedFolder. + if (string.IsNullOrEmpty(customWinePath)) + { + var wineBinPath = Path.Combine(Path.Combine(rootFolder, "compatibilitytool", "wine"), managedFolder, "bin"); + RunCommandFolder = wineBinPath; + WineServer = Path.Combine(wineBinPath, "wineserver"); + IsManaged = true; + } + else + { + RunCommandFolder = customWinePath; + WineServer = Path.Combine(customWinePath, "wineserver"); + IsManaged = false; + } } } \ No newline at end of file diff --git a/src/XIVLauncher.Common.Unix/UnixDalamudRunner.cs b/src/XIVLauncher.Common.Unix/UnixDalamudRunner.cs index ef1b85457..3314946e9 100644 --- a/src/XIVLauncher.Common.Unix/UnixDalamudRunner.cs +++ b/src/XIVLauncher.Common.Unix/UnixDalamudRunner.cs @@ -74,12 +74,25 @@ public UnixDalamudRunner(CompatibilityTools compatibility, DirectoryInfo dotnetR launchArguments.Add(gameArgs); var dalamudProcess = compatibility.RunInPrefix(string.Join(" ", launchArguments), environment: environment, redirectOutput: true, writeLog: true); - var output = dalamudProcess.StandardOutput.ReadLine(); - if (output == null) - throw new DalamudRunnerException("An internal Dalamud error has occured"); + DalamudConsoleOutput dalamudConsoleOutput = null; - Console.WriteLine(output); + while (dalamudConsoleOutput == null) + { + var output = dalamudProcess.StandardOutput.ReadLine(); + if (output == null) + throw new DalamudRunnerException("An internal Dalamud error has occured"); + Console.WriteLine(output); + + try + { + dalamudConsoleOutput = JsonConvert.DeserializeObject(output); + } + catch (Exception ex) + { + Log.Warning(ex, $"Couldn't parse Dalamud output: {output}"); + } + } new Thread(() => { @@ -89,17 +102,15 @@ public UnixDalamudRunner(CompatibilityTools compatibility, DirectoryInfo dotnetR if (output != null) Console.WriteLine(output); } - }).Start(); try { - var dalamudConsoleOutput = JsonConvert.DeserializeObject(output); var unixPid = compatibility.GetUnixProcessId(dalamudConsoleOutput.Pid); if (unixPid == 0) { - Log.Error("Could not retrive Unix process ID, this feature currently requires a patched wine version"); + Log.Error("Could not retrieve Unix process ID"); return null; } @@ -107,9 +118,9 @@ public UnixDalamudRunner(CompatibilityTools compatibility, DirectoryInfo dotnetR Log.Verbose($"Got game process handle {gameProcess.Handle} with Unix pid {gameProcess.Id} and Wine pid {dalamudConsoleOutput.Pid}"); return gameProcess; } - catch (JsonReaderException ex) + catch (Exception ex) { - Log.Error(ex, $"Couldn't parse Dalamud output: {output}"); + Log.Error(ex, $"Could not retrieve game Process information"); return null; } }