From 88a7cc174d75d0cbe3ea495cabf6514838ec7590 Mon Sep 17 00:00:00 2001 From: Rankyn Bass Date: Sun, 23 Jul 2023 19:08:17 -0700 Subject: [PATCH 01/12] Fix UnixDalamudRunner to work around reshade errors --- .../Compatibility/CompatibilityTools.cs | 42 ++++++++++++++++++- .../UnixDalamudRunner.cs | 32 ++++++++++---- 2 files changed, 64 insertions(+), 10 deletions(-) diff --git a/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs b/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs index 8216526f..2ceebaa0 100644 --- a/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs +++ b/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs @@ -258,13 +258,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 }; diff --git a/src/XIVLauncher.Common.Unix/UnixDalamudRunner.cs b/src/XIVLauncher.Common.Unix/UnixDalamudRunner.cs index ef1b8545..b2fde02c 100644 --- a/src/XIVLauncher.Common.Unix/UnixDalamudRunner.cs +++ b/src/XIVLauncher.Common.Unix/UnixDalamudRunner.cs @@ -74,12 +74,28 @@ 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; + int invalidJsonCount = 0; - Console.WriteLine(output); + // Keep checking for valid json output, but only 5 times. If it's still erroring out at that point, give up. + while (dalamudConsoleOutput == null && invalidJsonCount < 5) + { + 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}"); + } + invalidJsonCount++; + } new Thread(() => { @@ -89,17 +105,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 +121,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; } } From c7668691cd7b55aa17876339eb7a2414a29c6f54 Mon Sep 17 00:00:00 2001 From: Rankyn Bass Date: Sun, 23 Jul 2023 20:02:00 -0700 Subject: [PATCH 02/12] Update WineSettings to accept folder & url --- .../Compatibility/CompatibilityTools.cs | 44 ++++++------------- .../Compatibility/WineSettings.cs | 33 ++++++++------ 2 files changed, 34 insertions(+), 43 deletions(-) diff --git a/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs b/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs index 2ceebaa0..4e3dcbc6 100644 --- a/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs +++ b/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs @@ -22,29 +22,16 @@ public class CompatibilityTools 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; } - 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"); - - public bool IsToolDownloaded => File.Exists(Wine64Path) && Settings.Prefix.Exists; + public bool IsToolDownloaded => File.Exists(Settings.WinePath) && Settings.Prefix.Exists; private readonly Dxvk.DxvkHudType hudType; + private readonly bool gamemodeOn; + private readonly string dxvkAsyncOn; public CompatibilityTools(WineSettings wineSettings, Dxvk.DxvkHudType hudType, bool? gamemodeOn, bool? dxvkAsyncOn, DirectoryInfo toolsFolder) @@ -54,19 +41,16 @@ public CompatibilityTools(WineSettings wineSettings, Dxvk.DxvkHudType hudType, b this.gamemodeOn = gamemodeOn ?? false; this.dxvkAsyncOn = (dxvkAsyncOn ?? false) ? "1" : "0"; - this.toolDirectory = new DirectoryInfo(Path.Combine(toolsFolder.FullName, "beta")); + this.toolDirectory = new DirectoryInfo(Path.Combine(toolsFolder.FullName, "wine")); this.dxvkDirectory = new DirectoryInfo(Path.Combine(toolsFolder.FullName, "dxvk")); this.logWriter = new StreamWriter(wineSettings.LogFile.FullName); - if (wineSettings.StartupType == WineStartupType.Managed) - { - if (!this.toolDirectory.Exists) - this.toolDirectory.Create(); + if (!this.toolDirectory.Exists) + this.toolDirectory.Create(); - if (!this.dxvkDirectory.Exists) - this.dxvkDirectory.Create(); - } + if (!this.dxvkDirectory.Exists) + this.dxvkDirectory.Create(); if (!wineSettings.Prefix.Exists) wineSettings.Prefix.Create(); @@ -74,7 +58,7 @@ public CompatibilityTools(WineSettings wineSettings, Dxvk.DxvkHudType hudType, b public async Task EnsureTool(DirectoryInfo tempPath) { - if (!File.Exists(Wine64Path)) + if (!File.Exists(Settings.WinePath)) { Log.Information("Compatibility tool does not exist, downloading"); await DownloadTool(tempPath).ConfigureAwait(false); @@ -91,7 +75,7 @@ private async Task DownloadTool(DirectoryInfo tempPath) using var client = new HttpClient(); var tempFilePath = Path.Combine(tempPath.FullName, $"{Guid.NewGuid()}"); - await File.WriteAllBytesAsync(tempFilePath, await client.GetByteArrayAsync(WINE_XIV_RELEASE_URL).ConfigureAwait(false)).ConfigureAwait(false); + await File.WriteAllBytesAsync(tempFilePath, await client.GetByteArrayAsync(Settings.DownloadUrl).ConfigureAwait(false)).ConfigureAwait(false); PlatformHelpers.Untar(tempFilePath, this.toolDirectory.FullName); @@ -118,7 +102,7 @@ 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); + var psi = new ProcessStartInfo(Settings.WinePath); psi.Arguments = command; Log.Verbose("Running in prefix: {FileName} {Arguments}", psi.FileName, command); @@ -127,7 +111,7 @@ public Process RunInPrefix(string command, string workingDirectory = "", IDictio 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(Settings.WinePath); foreach (var arg in args) psi.ArgumentList.Add(arg); @@ -195,7 +179,7 @@ private Process RunInPrefix(ProcessStartInfo psi, string workingDirectory, IDict psi.FileName = "flatpak-spawn"; psi.ArgumentList.Insert(0, "--host"); - psi.ArgumentList.Insert(1, Wine64Path); + psi.ArgumentList.Insert(1, Settings.WinePath); foreach (KeyValuePair envVar in wineEnviromentVariables) { @@ -322,7 +306,7 @@ public void AddRegistryKey(string key, string value, string data) public void Kill() { - var psi = new ProcessStartInfo(WineServerPath) + var psi = new ProcessStartInfo(Settings.WineServerPath) { Arguments = "-k" }; diff --git a/src/XIVLauncher.Common.Unix/Compatibility/WineSettings.cs b/src/XIVLauncher.Common.Unix/Compatibility/WineSettings.cs index 9b122f7a..57b00c2d 100644 --- a/src/XIVLauncher.Common.Unix/Compatibility/WineSettings.cs +++ b/src/XIVLauncher.Common.Unix/Compatibility/WineSettings.cs @@ -2,32 +2,39 @@ 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, + public bool IsManaged { get; private set; } - [SettingsDescription("Custom", "Point XIVLauncher to a custom location containing wine binaries to run the game with.")] - Custom, -} + public string WineServerPath => Path.Combine(BinPath, "wineserver"); -public class WineSettings -{ - public WineStartupType StartupType { get; private set; } - public string CustomBinPath { get; private set; } + public string WinePath => File.Exists(Path.Combine(BinPath, "wine64")) ? Path.Combine(BinPath, "wine64") : Path.Combine(BinPath, "wine"); + + private string BinPath; + + public string WineRootFolder; + + public string DownloadUrl; public string EsyncOn { get; private set; } + public string FsyncOn { get; private set; } public string DebugVars { get; private set; } + public FileInfo LogFile { get; private set; } public DirectoryInfo Prefix { get; private set; } - public WineSettings(WineStartupType? startupType, string customBinPath, string debugVars, FileInfo logFile, DirectoryInfo prefix, bool? esyncOn, bool? fsyncOn) + public WineSettings(bool isManaged, string customBinPath, string managedFolder, string managedUrl, string storageFolder, string debugVars, FileInfo logFile, DirectoryInfo prefix, bool? esyncOn, bool? fsyncOn) { - this.StartupType = startupType ?? WineStartupType.Custom; - this.CustomBinPath = customBinPath; + // storageFolder is the path to .xlcore folder. managedFolder is the foldername inside the tarball that will be downloaded + // from managedUrl + IsManaged = isManaged; + WineRootFolder = managedFolder; + DownloadUrl = managedUrl; + BinPath = (isManaged) ? Path.Combine(new [] {storageFolder, "compatibilitytool", "wine", managedFolder, "bin"}) : customBinPath; + this.EsyncOn = (esyncOn ?? false) ? "1" : "0"; this.FsyncOn = (fsyncOn ?? false) ? "1" : "0"; this.DebugVars = debugVars; From 23a45a9ca7dd79b698b69348bfa3660105277fe8 Mon Sep 17 00:00:00 2001 From: Rankyn Bass Date: Sun, 23 Jul 2023 21:49:58 -0700 Subject: [PATCH 03/12] Add DxvkSettings class --- .../Compatibility/CompatibilityTools.cs | 35 ++++++------ .../Compatibility/Dxvk.cs | 22 +++++--- .../Compatibility/DxvkSettings.cs | 53 +++++++++++++++++++ 3 files changed, 85 insertions(+), 25 deletions(-) create mode 100644 src/XIVLauncher.Common.Unix/Compatibility/DxvkSettings.cs diff --git a/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs b/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs index 4e3dcbc6..3482045f 100644 --- a/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs +++ b/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs @@ -26,20 +26,20 @@ public class CompatibilityTools public WineSettings Settings { get; private set; } + public DxvkSettings DxvkSettings { get; private set; } + public bool IsToolDownloaded => File.Exists(Settings.WinePath) && Settings.Prefix.Exists; private readonly Dxvk.DxvkHudType hudType; private readonly bool gamemodeOn; - private readonly string dxvkAsyncOn; - - public CompatibilityTools(WineSettings wineSettings, Dxvk.DxvkHudType hudType, bool? gamemodeOn, bool? dxvkAsyncOn, DirectoryInfo toolsFolder) + public CompatibilityTools(WineSettings wineSettings, DxvkSettings dxvkSettings, Dxvk.DxvkHudType hudType, bool? gamemodeOn, DirectoryInfo toolsFolder) { this.Settings = wineSettings; + this.DxvkSettings = dxvkSettings; this.hudType = hudType; this.gamemodeOn = gamemodeOn ?? false; - this.dxvkAsyncOn = (dxvkAsyncOn ?? false) ? "1" : "0"; this.toolDirectory = new DirectoryInfo(Path.Combine(toolsFolder.FullName, "wine")); this.dxvkDirectory = new DirectoryInfo(Path.Combine(toolsFolder.FullName, "dxvk")); @@ -65,7 +65,7 @@ public async Task EnsureTool(DirectoryInfo tempPath) } EnsurePrefix(); - await Dxvk.InstallDxvk(Settings.Prefix, dxvkDirectory).ConfigureAwait(false); + await Dxvk.InstallDxvk(Settings.Prefix, dxvkDirectory, DxvkSettings.Folder, DxvkSettings.DownloadUrl).ConfigureAwait(false); IsToolReady = true; } @@ -140,16 +140,16 @@ 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")}"); + var wineEnvironmentVariables = new Dictionary(); + wineEnvironmentVariables.Add("WINEPREFIX", Settings.Prefix.FullName); + wineEnvironmentVariables.Add("WINEDLLOVERRIDES", $"msquic=,mscoree=n,b;d3d9,d3d11,d3d10core,dxgi={(wineD3D ? "b" : "n")}"); if (!string.IsNullOrEmpty(Settings.DebugVars)) { - wineEnviromentVariables.Add("WINEDEBUG", Settings.DebugVars); + wineEnvironmentVariables.Add("WINEDEBUG", Settings.DebugVars); } - wineEnviromentVariables.Add("XL_WINEONLINUX", "true"); + wineEnvironmentVariables.Add("XL_WINEONLINUX", "true"); string ldPreload = Environment.GetEnvironmentVariable("LD_PRELOAD") ?? ""; string dxvkHud = hudType switch @@ -159,20 +159,21 @@ private Process RunInPrefix(ProcessStartInfo psi, string workingDirectory, IDict Dxvk.DxvkHudType.Full => "full", _ => throw new ArgumentOutOfRangeException() }; + wineEnvironmentVariables.Add("DXVK_HUD", dxvkHud); 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); + foreach (var dxvkVar in DxvkSettings.Environment) + wineEnvironmentVariables.Add(dxvkVar.Key, dxvkVar.Value); + wineEnvironmentVariables.Add("WINEESYNC", Settings.EsyncOn); + wineEnvironmentVariables.Add("WINEFSYNC", Settings.FsyncOn); - wineEnviromentVariables.Add("LD_PRELOAD", ldPreload); + wineEnvironmentVariables.Add("LD_PRELOAD", ldPreload); - MergeDictionaries(psi.EnvironmentVariables, wineEnviromentVariables); + MergeDictionaries(psi.EnvironmentVariables, wineEnvironmentVariables); MergeDictionaries(psi.EnvironmentVariables, environment); #if FLATPAK_NOTRIGHTNOW @@ -181,7 +182,7 @@ private Process RunInPrefix(ProcessStartInfo psi, string workingDirectory, IDict psi.ArgumentList.Insert(0, "--host"); psi.ArgumentList.Insert(1, Settings.WinePath); - foreach (KeyValuePair envVar in wineEnviromentVariables) + foreach (KeyValuePair envVar in wineEnvironmentVariables) { psi.ArgumentList.Insert(1, $"--env={envVar.Key}={envVar.Value}"); } diff --git a/src/XIVLauncher.Common.Unix/Compatibility/Dxvk.cs b/src/XIVLauncher.Common.Unix/Compatibility/Dxvk.cs index 50fa98d9..1d4659f2 100644 --- a/src/XIVLauncher.Common.Unix/Compatibility/Dxvk.cs +++ b/src/XIVLauncher.Common.Unix/Compatibility/Dxvk.cs @@ -8,34 +8,40 @@ 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) + public static async Task InstallDxvk(DirectoryInfo prefix, DirectoryInfo installDirectory, string folder, string downloadUrl) { - var dxvkPath = Path.Combine(installDirectory.FullName, DXVK_NAME, "x64"); + var dxvkPath = Path.Combine(installDirectory.FullName, folder, "x64"); + var dxvkPath32 = Path.Combine(installDirectory.FullName, folder, "x32"); if (!Directory.Exists(dxvkPath)) { Log.Information("DXVK does not exist, downloading"); - await DownloadDxvk(installDirectory).ConfigureAwait(false); + await DownloadDxvk(installDirectory, downloadUrl).ConfigureAwait(false); } var system32 = Path.Combine(prefix.FullName, "drive_c", "windows", "system32"); + var syswow64 = Path.Combine(prefix.FullName, "drive_c", "windows", "syswow64"); var files = Directory.GetFiles(dxvkPath); foreach (string fileName in files) { File.Copy(fileName, Path.Combine(system32, Path.GetFileName(fileName)), true); } + + files = Directory.GetFiles(dxvkPath32); + + foreach (string fileName in files) + { + File.Copy(fileName, Path.Combine(syswow64, Path.GetFileName(fileName)), true); + } } - private static async Task DownloadDxvk(DirectoryInfo installDirectory) + private static async Task DownloadDxvk(DirectoryInfo installDirectory, string downloadUrl) { using var client = new HttpClient(); var tempPath = Path.GetTempFileName(); - File.WriteAllBytes(tempPath, await client.GetByteArrayAsync(DXVK_DOWNLOAD)); + File.WriteAllBytes(tempPath, await client.GetByteArrayAsync(downloadUrl)); PlatformHelpers.Untar(tempPath, installDirectory.FullName); File.Delete(tempPath); diff --git a/src/XIVLauncher.Common.Unix/Compatibility/DxvkSettings.cs b/src/XIVLauncher.Common.Unix/Compatibility/DxvkSettings.cs new file mode 100644 index 00000000..fd2b2d6d --- /dev/null +++ b/src/XIVLauncher.Common.Unix/Compatibility/DxvkSettings.cs @@ -0,0 +1,53 @@ +using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using Serilog; + +namespace XIVLauncher.Common.Unix.Compatibility; + +public class DxvkSettings +{ + public bool Enabled { get; } + + public string Folder { get; } + + public string DownloadUrl { get; } + + public Dictionary Environment { get; } + + private const string ALLOWED_CHARS = "^[0-9a-zA-Z,=.]+$"; + + private const string ALLOWED_WORDS = "^(?:devinfo|fps|frametimes|submissions|drawcalls|pipelines|descriptors|memory|gpuload|version|api|cs|compiler|samplers|scale=(?:[0-9])*(?:.(?:[0-9])+)?)$"; + + public DxvkSettings(string folder, string url, string storageFolder, bool? async, int maxFrameRate, bool enabled = true) + { + Folder = folder; + DownloadUrl = url; + Enabled = enabled; + + var dxvkConfigPath = new DirectoryInfo(Path.Combine(storageFolder, "compatibilitytool", "dxvk")); + Environment = new Dictionary + { + { "DXVK_LOG_PATH", Path.Combine(storageFolder, "logs") }, + { "DXVK_CONFIG_FILE", Path.Combine(dxvkConfigPath.FullName, "dxvk.conf") }, + { "DXVK_FRAME_RATE", (maxFrameRate).ToString() } + }; + if (async is not null) + Environment.Add("DXVK_ASYNC", async.Value ? "1" : "0"); + var dxvkCachePath = new DirectoryInfo(Path.Combine(dxvkConfigPath.FullName, "cache")); + if (!dxvkCachePath.Exists) dxvkCachePath.Create(); + Environment.Add("DXVK_STATE_CACHE_PATH", Path.Combine(dxvkCachePath.FullName, folder)); + } + + public static bool CheckDxvkHudString(string customHud) + { + if (string.IsNullOrWhiteSpace(customHud)) return false; + if (customHud == "1") return true; + if (!Regex.IsMatch(customHud,ALLOWED_CHARS)) return false; + + string[] hudvars = customHud.Split(","); + + return hudvars.All(hudvar => Regex.IsMatch(hudvar, ALLOWED_WORDS)); + } +} \ No newline at end of file From c87718719745aad02897975aec08e32a3d49945e Mon Sep 17 00:00:00 2001 From: Rankyn Bass Date: Mon, 24 Jul 2023 18:52:58 -0700 Subject: [PATCH 04/12] Get DxvkSettings working --- .../Compatibility/CompatibilityTools.cs | 49 +++++++---------- .../Compatibility/DxvkSettings.cs | 52 ++++++++++++++++--- .../{Dxvk.cs => ToolDownloader.cs} | 29 +++++++---- .../Compatibility/WineSettings.cs | 4 +- 4 files changed, 86 insertions(+), 48 deletions(-) rename src/XIVLauncher.Common.Unix/Compatibility/{Dxvk.cs => ToolDownloader.cs} (64%) diff --git a/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs b/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs index 3482045f..1ec182a1 100644 --- a/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs +++ b/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs @@ -17,7 +17,7 @@ namespace XIVLauncher.Common.Unix.Compatibility; public class CompatibilityTools { - private DirectoryInfo toolDirectory; + private DirectoryInfo wineDirectory; private DirectoryInfo dxvkDirectory; private StreamWriter logWriter; @@ -30,24 +30,21 @@ public class CompatibilityTools public bool IsToolDownloaded => File.Exists(Settings.WinePath) && Settings.Prefix.Exists; - private readonly Dxvk.DxvkHudType hudType; - private readonly bool gamemodeOn; - public CompatibilityTools(WineSettings wineSettings, DxvkSettings dxvkSettings, Dxvk.DxvkHudType hudType, bool? gamemodeOn, DirectoryInfo toolsFolder) + public CompatibilityTools(WineSettings wineSettings, DxvkSettings dxvkSettings, bool? gamemodeOn, DirectoryInfo toolsFolder) { this.Settings = wineSettings; this.DxvkSettings = dxvkSettings; - this.hudType = hudType; this.gamemodeOn = gamemodeOn ?? false; - this.toolDirectory = new DirectoryInfo(Path.Combine(toolsFolder.FullName, "wine")); + this.wineDirectory = new DirectoryInfo(Path.Combine(toolsFolder.FullName, "wine")); this.dxvkDirectory = new DirectoryInfo(Path.Combine(toolsFolder.FullName, "dxvk")); this.logWriter = new StreamWriter(wineSettings.LogFile.FullName); - if (!this.toolDirectory.Exists) - this.toolDirectory.Create(); + if (!this.wineDirectory.Exists) + this.wineDirectory.Create(); if (!this.dxvkDirectory.Exists) this.dxvkDirectory.Create(); @@ -61,28 +58,29 @@ public async Task EnsureTool(DirectoryInfo tempPath) if (!File.Exists(Settings.WinePath)) { Log.Information("Compatibility tool does not exist, downloading"); - await DownloadTool(tempPath).ConfigureAwait(false); + await ToolDownloader.InstallWine(wineDirectory, Settings.FolderName, Settings.DownloadUrl).ConfigureAwait(false); } EnsurePrefix(); - await Dxvk.InstallDxvk(Settings.Prefix, dxvkDirectory, DxvkSettings.Folder, DxvkSettings.DownloadUrl).ConfigureAwait(false); + if (DxvkSettings.Enabled) + await ToolDownloader.InstallDxvk(Settings.Prefix, dxvkDirectory, DxvkSettings.FolderName, DxvkSettings.DownloadUrl).ConfigureAwait(false); IsToolReady = true; } - private async Task DownloadTool(DirectoryInfo tempPath) - { - using var client = new HttpClient(); - var tempFilePath = Path.Combine(tempPath.FullName, $"{Guid.NewGuid()}"); + // private async Task DownloadTool(DirectoryInfo tempPath) + // { + // using var client = new HttpClient(); + // var tempFilePath = Path.Combine(tempPath.FullName, $"{Guid.NewGuid()}"); - await File.WriteAllBytesAsync(tempFilePath, await client.GetByteArrayAsync(Settings.DownloadUrl).ConfigureAwait(false)).ConfigureAwait(false); + // await File.WriteAllBytesAsync(tempFilePath, await client.GetByteArrayAsync(Settings.DownloadUrl).ConfigureAwait(false)).ConfigureAwait(false); - PlatformHelpers.Untar(tempFilePath, this.toolDirectory.FullName); + // PlatformHelpers.Untar(tempFilePath, this.wineDirectory.FullName); - Log.Information("Compatibility tool successfully extracted to {Path}", this.toolDirectory.FullName); + // Log.Information("Compatibility tool successfully extracted to {Path}", this.wineDirectory.FullName); - File.Delete(tempFilePath); - } + // File.Delete(tempFilePath); + // } private void ResetPrefix() { @@ -140,9 +138,11 @@ private Process RunInPrefix(ProcessStartInfo psi, string workingDirectory, IDict psi.UseShellExecute = false; psi.WorkingDirectory = workingDirectory; + wineD3D = !DxvkSettings.Enabled || wineD3D; + var wineEnvironmentVariables = new Dictionary(); wineEnvironmentVariables.Add("WINEPREFIX", Settings.Prefix.FullName); - wineEnvironmentVariables.Add("WINEDLLOVERRIDES", $"msquic=,mscoree=n,b;d3d9,d3d11,d3d10core,dxgi={(wineD3D ? "b" : "n")}"); + wineEnvironmentVariables.Add("WINEDLLOVERRIDES", $"msquic=,mscoree=n,b;d3d9,d3d11,d3d10core,dxgi={(wineD3D ? "b" : "n,b")}"); if (!string.IsNullOrEmpty(Settings.DebugVars)) { @@ -152,15 +152,6 @@ private Process RunInPrefix(ProcessStartInfo psi, string workingDirectory, IDict wineEnvironmentVariables.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() - }; - wineEnvironmentVariables.Add("DXVK_HUD", dxvkHud); - if (this.gamemodeOn == true && !ldPreload.Contains("libgamemodeauto.so.0")) { ldPreload = ldPreload.Equals("") ? "libgamemodeauto.so.0" : ldPreload + ":libgamemodeauto.so.0"; diff --git a/src/XIVLauncher.Common.Unix/Compatibility/DxvkSettings.cs b/src/XIVLauncher.Common.Unix/Compatibility/DxvkSettings.cs index fd2b2d6d..d72746d5 100644 --- a/src/XIVLauncher.Common.Unix/Compatibility/DxvkSettings.cs +++ b/src/XIVLauncher.Common.Unix/Compatibility/DxvkSettings.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; +using System.Net.Http; +using System.Threading.Tasks; using Serilog; namespace XIVLauncher.Common.Unix.Compatibility; @@ -10,7 +12,7 @@ public class DxvkSettings { public bool Enabled { get; } - public string Folder { get; } + public string FolderName { get; } public string DownloadUrl { get; } @@ -20,9 +22,9 @@ public class DxvkSettings private const string ALLOWED_WORDS = "^(?:devinfo|fps|frametimes|submissions|drawcalls|pipelines|descriptors|memory|gpuload|version|api|cs|compiler|samplers|scale=(?:[0-9])*(?:.(?:[0-9])+)?)$"; - public DxvkSettings(string folder, string url, string storageFolder, bool? async, int maxFrameRate, bool enabled = true) + public DxvkSettings(string folder, string url, string storageFolder, bool async, int maxFrameRate, bool dxvkHudEnabled, string dxvkHudString, bool mangoHudEnabled, bool mangoHudCustomIsFile, string customMangoHud, bool enabled = true) { - Folder = folder; + FolderName = folder; DownloadUrl = url; Enabled = enabled; @@ -31,16 +33,39 @@ public DxvkSettings(string folder, string url, string storageFolder, bool? async { { "DXVK_LOG_PATH", Path.Combine(storageFolder, "logs") }, { "DXVK_CONFIG_FILE", Path.Combine(dxvkConfigPath.FullName, "dxvk.conf") }, - { "DXVK_FRAME_RATE", (maxFrameRate).ToString() } }; - if (async is not null) - Environment.Add("DXVK_ASYNC", async.Value ? "1" : "0"); + + if (maxFrameRate != 0) + Environment.Add("DXVK_FRAME_RATE", (maxFrameRate).ToString()); + + if (async) + Environment.Add("DXVK_ASYNC", "1"); + var dxvkCachePath = new DirectoryInfo(Path.Combine(dxvkConfigPath.FullName, "cache")); if (!dxvkCachePath.Exists) dxvkCachePath.Create(); Environment.Add("DXVK_STATE_CACHE_PATH", Path.Combine(dxvkCachePath.FullName, folder)); + + if (dxvkHudEnabled) + Environment.Add("DXVK_HUD", DxvkHudStringIsValid(dxvkHudString) ? dxvkHudString : "1"); + + if (mangoHudEnabled && !string.IsNullOrEmpty(GetMangoHudPath())) + { + Environment.Add("MANGOHUD", "1"); + if (mangoHudCustomIsFile) + { + if (File.Exists(customMangoHud)) + Environment.Add("MANGOHUD_CONFIGFILE", customMangoHud); + else + Environment.Add("MANGOHUD_CONFIG", ""); + } + else + { + Environment.Add("MANGOHUD_CONFIG", customMangoHud); + } + } } - public static bool CheckDxvkHudString(string customHud) + public static bool DxvkHudStringIsValid(string customHud) { if (string.IsNullOrWhiteSpace(customHud)) return false; if (customHud == "1") return true; @@ -50,4 +75,17 @@ public static bool CheckDxvkHudString(string customHud) return hudvars.All(hudvar => Regex.IsMatch(hudvar, ALLOWED_WORDS)); } + + public static string GetMangoHudPath() + { + var usrLib = Path.Combine("/usr", "lib", "mangohud", "libMangoHud.so"); // fedora uses this + var usrLib64 = Path.Combine("/usr", "lib64", "mangohud", "libMangoHud.so"); // arch and openSUSE use this + var flatpak = Path.Combine(new string[] { "/usr", "lib", "extensions", "vulkan", "MangoHud", "lib", "x86_64-linux-gnu", "libMangoHud.so"}); + var debuntu = Path.Combine(new string[] { "/usr", "lib", "x86_64-linux-gnu", "mangohud", "libMangoHud.so"}); + if (File.Exists(usrLib64)) return usrLib64; + if (File.Exists(usrLib)) return usrLib; + if (File.Exists(flatpak)) return flatpak; + if (File.Exists(debuntu)) return debuntu; + return string.Empty; + } } \ No newline at end of file diff --git a/src/XIVLauncher.Common.Unix/Compatibility/Dxvk.cs b/src/XIVLauncher.Common.Unix/Compatibility/ToolDownloader.cs similarity index 64% rename from src/XIVLauncher.Common.Unix/Compatibility/Dxvk.cs rename to src/XIVLauncher.Common.Unix/Compatibility/ToolDownloader.cs index 1d4659f2..ed9f3401 100644 --- a/src/XIVLauncher.Common.Unix/Compatibility/Dxvk.cs +++ b/src/XIVLauncher.Common.Unix/Compatibility/ToolDownloader.cs @@ -6,21 +6,18 @@ namespace XIVLauncher.Common.Unix.Compatibility; -public static class Dxvk +public static class ToolDownloader { public static async Task InstallDxvk(DirectoryInfo prefix, DirectoryInfo installDirectory, string folder, string downloadUrl) { var dxvkPath = Path.Combine(installDirectory.FullName, folder, "x64"); - var dxvkPath32 = Path.Combine(installDirectory.FullName, folder, "x32"); - if (!Directory.Exists(dxvkPath)) { Log.Information("DXVK does not exist, downloading"); - await DownloadDxvk(installDirectory, downloadUrl).ConfigureAwait(false); + await DownloadTool(installDirectory, downloadUrl).ConfigureAwait(false); } var system32 = Path.Combine(prefix.FullName, "drive_c", "windows", "system32"); - var syswow64 = Path.Combine(prefix.FullName, "drive_c", "windows", "syswow64"); var files = Directory.GetFiles(dxvkPath); foreach (string fileName in files) @@ -28,15 +25,27 @@ public static async Task InstallDxvk(DirectoryInfo prefix, DirectoryInfo install File.Copy(fileName, Path.Combine(system32, Path.GetFileName(fileName)), true); } - files = Directory.GetFiles(dxvkPath32); - - foreach (string fileName in files) + // 32-bit files for Directx9. + var dxvkPath32 = Path.Combine(installDirectory.FullName, folder, "x32"); + if (Directory.Exists(dxvkPath32)) { - File.Copy(fileName, Path.Combine(syswow64, Path.GetFileName(fileName)), true); + var syswow64 = Path.Combine(prefix.FullName, "drive_c", "windows", "syswow64"); + files = Directory.GetFiles(dxvkPath32); + + foreach (string fileName in files) + { + File.Copy(fileName, Path.Combine(syswow64, Path.GetFileName(fileName)), true); + } } } - private static async Task DownloadDxvk(DirectoryInfo installDirectory, string downloadUrl) + public static async Task InstallWine(DirectoryInfo installDirectory, string folder, string downloadUrl) + { + if (!Directory.Exists(Path.Combine(installDirectory.FullName, folder))) + await DownloadTool(installDirectory, downloadUrl).ConfigureAwait(false); + } + + private static async Task DownloadTool(DirectoryInfo installDirectory, string downloadUrl) { using var client = new HttpClient(); var tempPath = Path.GetTempFileName(); diff --git a/src/XIVLauncher.Common.Unix/Compatibility/WineSettings.cs b/src/XIVLauncher.Common.Unix/Compatibility/WineSettings.cs index 57b00c2d..4e0a31c5 100644 --- a/src/XIVLauncher.Common.Unix/Compatibility/WineSettings.cs +++ b/src/XIVLauncher.Common.Unix/Compatibility/WineSettings.cs @@ -12,7 +12,7 @@ public class WineSettings private string BinPath; - public string WineRootFolder; + public string FolderName; public string DownloadUrl; @@ -31,7 +31,7 @@ public WineSettings(bool isManaged, string customBinPath, string managedFolder, // storageFolder is the path to .xlcore folder. managedFolder is the foldername inside the tarball that will be downloaded // from managedUrl IsManaged = isManaged; - WineRootFolder = managedFolder; + FolderName = managedFolder; DownloadUrl = managedUrl; BinPath = (isManaged) ? Path.Combine(new [] {storageFolder, "compatibilitytool", "wine", managedFolder, "bin"}) : customBinPath; From 569c31e15dfd9cd2e8d93f10cd587e355c19db9e Mon Sep 17 00:00:00 2001 From: Rankyn Bass Date: Mon, 24 Jul 2023 22:37:25 -0700 Subject: [PATCH 05/12] Rework MangoHud check to bool --- .../Compatibility/DxvkSettings.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/XIVLauncher.Common.Unix/Compatibility/DxvkSettings.cs b/src/XIVLauncher.Common.Unix/Compatibility/DxvkSettings.cs index d72746d5..2cad433e 100644 --- a/src/XIVLauncher.Common.Unix/Compatibility/DxvkSettings.cs +++ b/src/XIVLauncher.Common.Unix/Compatibility/DxvkSettings.cs @@ -48,7 +48,7 @@ public DxvkSettings(string folder, string url, string storageFolder, bool async, if (dxvkHudEnabled) Environment.Add("DXVK_HUD", DxvkHudStringIsValid(dxvkHudString) ? dxvkHudString : "1"); - if (mangoHudEnabled && !string.IsNullOrEmpty(GetMangoHudPath())) + if (mangoHudEnabled && MangoHudInstalled()) { Environment.Add("MANGOHUD", "1"); if (mangoHudCustomIsFile) @@ -68,6 +68,7 @@ public DxvkSettings(string folder, string url, string storageFolder, bool async, public static bool DxvkHudStringIsValid(string customHud) { if (string.IsNullOrWhiteSpace(customHud)) return false; + if (customHud == "full") return true; if (customHud == "1") return true; if (!Regex.IsMatch(customHud,ALLOWED_CHARS)) return false; @@ -76,16 +77,14 @@ public static bool DxvkHudStringIsValid(string customHud) return hudvars.All(hudvar => Regex.IsMatch(hudvar, ALLOWED_WORDS)); } - public static string GetMangoHudPath() + public static bool MangoHudInstalled() { var usrLib = Path.Combine("/usr", "lib", "mangohud", "libMangoHud.so"); // fedora uses this var usrLib64 = Path.Combine("/usr", "lib64", "mangohud", "libMangoHud.so"); // arch and openSUSE use this var flatpak = Path.Combine(new string[] { "/usr", "lib", "extensions", "vulkan", "MangoHud", "lib", "x86_64-linux-gnu", "libMangoHud.so"}); var debuntu = Path.Combine(new string[] { "/usr", "lib", "x86_64-linux-gnu", "mangohud", "libMangoHud.so"}); - if (File.Exists(usrLib64)) return usrLib64; - if (File.Exists(usrLib)) return usrLib; - if (File.Exists(flatpak)) return flatpak; - if (File.Exists(debuntu)) return debuntu; - return string.Empty; + if (File.Exists(usrLib64) || File.Exists(usrLib) || File.Exists(flatpak) || File.Exists(debuntu)) + return true; + return false; } } \ No newline at end of file From e6b2a3e6d6c797cafba121d1bd257c01fd2ec571 Mon Sep 17 00:00:00 2001 From: Rankyn Bass Date: Tue, 25 Jul 2023 20:33:52 -0700 Subject: [PATCH 06/12] Code cleanup * Renamed ToolDownloader to UnixHelpers * Moved DxvkHud and MangoHud checkers to UnixHelpers * Removed unneeded "using" statements * Added flatpak and extraEnvironmentVars for future use --- .../Compatibility/CompatibilityTools.cs | 50 +++++++++++++++---- .../Compatibility/DxvkSettings.cs | 35 +------------ .../{ToolDownloader.cs => UnixHelpers.cs} | 44 ++++++++++++---- .../Compatibility/WineSettings.cs | 3 +- 4 files changed, 78 insertions(+), 54 deletions(-) rename src/XIVLauncher.Common.Unix/Compatibility/{ToolDownloader.cs => UnixHelpers.cs} (58%) diff --git a/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs b/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs index 1ec182a1..eb1d5cca 100644 --- a/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs +++ b/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs @@ -1,13 +1,10 @@ using System; using System.Collections.Generic; -using System.Collections.Specialized; using System.Diagnostics; using System.IO; using System.Linq; -using System.Net.Http; using System.Threading.Tasks; using Serilog; -using XIVLauncher.Common.Util; #if FLATPAK #warning THIS IS A FLATPAK BUILD!!! @@ -18,6 +15,7 @@ namespace XIVLauncher.Common.Unix.Compatibility; public class CompatibilityTools { private DirectoryInfo wineDirectory; + private DirectoryInfo dxvkDirectory; private StreamWriter logWriter; @@ -30,14 +28,26 @@ public class CompatibilityTools public bool IsToolDownloaded => File.Exists(Settings.WinePath) && Settings.Prefix.Exists; + public bool IsFlatpak { get; } + private readonly bool gamemodeOn; - public CompatibilityTools(WineSettings wineSettings, DxvkSettings dxvkSettings, bool? gamemodeOn, DirectoryInfo toolsFolder) + private Dictionary extraEnvironmentVars; + + public CompatibilityTools(WineSettings wineSettings, DxvkSettings dxvkSettings, bool? gamemodeOn, DirectoryInfo toolsFolder, bool isFlatpak, Dictionary extraEnvVars = null) { this.Settings = wineSettings; this.DxvkSettings = dxvkSettings; this.gamemodeOn = gamemodeOn ?? false; + // This is currently unused, but might be useful in the future. + this.IsFlatpak = isFlatpak; + this.extraEnvironmentVars = extraEnvVars ?? new Dictionary(); + + // This is also unused. It's here to allow features to be added to XL.Core without requiring immediate + // changes to XL.Common.Unix. It should only ever be used temporarily. + this.extraEnvironmentVars = extraEnvVars ?? new Dictionary(); + this.wineDirectory = new DirectoryInfo(Path.Combine(toolsFolder.FullName, "wine")); this.dxvkDirectory = new DirectoryInfo(Path.Combine(toolsFolder.FullName, "dxvk")); @@ -58,12 +68,12 @@ public async Task EnsureTool(DirectoryInfo tempPath) if (!File.Exists(Settings.WinePath)) { Log.Information("Compatibility tool does not exist, downloading"); - await ToolDownloader.InstallWine(wineDirectory, Settings.FolderName, Settings.DownloadUrl).ConfigureAwait(false); + await UnixHelpers.InstallWine(wineDirectory, Settings.FolderName, Settings.DownloadUrl).ConfigureAwait(false); } EnsurePrefix(); if (DxvkSettings.Enabled) - await ToolDownloader.InstallDxvk(Settings.Prefix, dxvkDirectory, DxvkSettings.FolderName, DxvkSettings.DownloadUrl).ConfigureAwait(false); + await UnixHelpers.InstallDxvk(Settings.Prefix, dxvkDirectory, DxvkSettings.FolderName, DxvkSettings.DownloadUrl).ConfigureAwait(false); IsToolReady = true; } @@ -117,7 +127,7 @@ public Process RunInPrefix(string[] args, string workingDirectory = "", IDiction 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; @@ -125,12 +135,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; @@ -164,8 +193,9 @@ private Process RunInPrefix(ProcessStartInfo psi, string workingDirectory, IDict wineEnvironmentVariables.Add("LD_PRELOAD", ldPreload); - MergeDictionaries(psi.EnvironmentVariables, wineEnvironmentVariables); - MergeDictionaries(psi.EnvironmentVariables, environment); + MergeDictionaries(psi.Environment, wineEnvironmentVariables); + MergeDictionaries(psi.Environment, extraEnvironmentVars); + MergeDictionaries(psi.Environment, environment); #if FLATPAK_NOTRIGHTNOW psi.FileName = "flatpak-spawn"; diff --git a/src/XIVLauncher.Common.Unix/Compatibility/DxvkSettings.cs b/src/XIVLauncher.Common.Unix/Compatibility/DxvkSettings.cs index 2cad433e..cfd421db 100644 --- a/src/XIVLauncher.Common.Unix/Compatibility/DxvkSettings.cs +++ b/src/XIVLauncher.Common.Unix/Compatibility/DxvkSettings.cs @@ -1,9 +1,5 @@ using System.IO; using System.Collections.Generic; -using System.Linq; -using System.Text.RegularExpressions; -using System.Net.Http; -using System.Threading.Tasks; using Serilog; namespace XIVLauncher.Common.Unix.Compatibility; @@ -18,10 +14,6 @@ public class DxvkSettings public Dictionary Environment { get; } - private const string ALLOWED_CHARS = "^[0-9a-zA-Z,=.]+$"; - - private const string ALLOWED_WORDS = "^(?:devinfo|fps|frametimes|submissions|drawcalls|pipelines|descriptors|memory|gpuload|version|api|cs|compiler|samplers|scale=(?:[0-9])*(?:.(?:[0-9])+)?)$"; - public DxvkSettings(string folder, string url, string storageFolder, bool async, int maxFrameRate, bool dxvkHudEnabled, string dxvkHudString, bool mangoHudEnabled, bool mangoHudCustomIsFile, string customMangoHud, bool enabled = true) { FolderName = folder; @@ -46,9 +38,9 @@ public DxvkSettings(string folder, string url, string storageFolder, bool async, Environment.Add("DXVK_STATE_CACHE_PATH", Path.Combine(dxvkCachePath.FullName, folder)); if (dxvkHudEnabled) - Environment.Add("DXVK_HUD", DxvkHudStringIsValid(dxvkHudString) ? dxvkHudString : "1"); + Environment.Add("DXVK_HUD", UnixHelpers.DxvkHudStringIsValid(dxvkHudString) ? dxvkHudString : "1"); - if (mangoHudEnabled && MangoHudInstalled()) + if (mangoHudEnabled && UnixHelpers.MangoHudIsInstalled()) { Environment.Add("MANGOHUD", "1"); if (mangoHudCustomIsFile) @@ -64,27 +56,4 @@ public DxvkSettings(string folder, string url, string storageFolder, bool async, } } } - - public static bool DxvkHudStringIsValid(string customHud) - { - if (string.IsNullOrWhiteSpace(customHud)) return false; - if (customHud == "full") return true; - if (customHud == "1") return true; - if (!Regex.IsMatch(customHud,ALLOWED_CHARS)) return false; - - string[] hudvars = customHud.Split(","); - - return hudvars.All(hudvar => Regex.IsMatch(hudvar, ALLOWED_WORDS)); - } - - public static bool MangoHudInstalled() - { - var usrLib = Path.Combine("/usr", "lib", "mangohud", "libMangoHud.so"); // fedora uses this - var usrLib64 = Path.Combine("/usr", "lib64", "mangohud", "libMangoHud.so"); // arch and openSUSE use this - var flatpak = Path.Combine(new string[] { "/usr", "lib", "extensions", "vulkan", "MangoHud", "lib", "x86_64-linux-gnu", "libMangoHud.so"}); - var debuntu = Path.Combine(new string[] { "/usr", "lib", "x86_64-linux-gnu", "mangohud", "libMangoHud.so"}); - if (File.Exists(usrLib64) || File.Exists(usrLib) || File.Exists(flatpak) || File.Exists(debuntu)) - return true; - return false; - } } \ No newline at end of file diff --git a/src/XIVLauncher.Common.Unix/Compatibility/ToolDownloader.cs b/src/XIVLauncher.Common.Unix/Compatibility/UnixHelpers.cs similarity index 58% rename from src/XIVLauncher.Common.Unix/Compatibility/ToolDownloader.cs rename to src/XIVLauncher.Common.Unix/Compatibility/UnixHelpers.cs index ed9f3401..27d9b171 100644 --- a/src/XIVLauncher.Common.Unix/Compatibility/ToolDownloader.cs +++ b/src/XIVLauncher.Common.Unix/Compatibility/UnixHelpers.cs @@ -1,12 +1,25 @@ using System.IO; using System.Net.Http; using System.Threading.Tasks; +using System.Linq; +using System.Text.RegularExpressions; using Serilog; using XIVLauncher.Common.Util; namespace XIVLauncher.Common.Unix.Compatibility; -public static class ToolDownloader +public enum DistroPackage +{ + ubuntu, + + fedora, + + arch, + + none, +} + +public static class UnixHelpers { public static async Task InstallDxvk(DirectoryInfo prefix, DirectoryInfo installDirectory, string folder, string downloadUrl) { @@ -55,16 +68,29 @@ private static async Task DownloadTool(DirectoryInfo installDirectory, string do File.Delete(tempPath); } - - public enum DxvkHudType + public static bool DxvkHudStringIsValid(string customHud) { - [SettingsDescription("None", "Show nothing")] - None, + var ALLOWED_CHARS = "^[0-9a-zA-Z,=.]+$"; + var ALLOWED_WORDS = "^(?:devinfo|fps|frametimes|submissions|drawcalls|pipelines|descriptors|memory|gpuload|version|api|cs|compiler|samplers|scale=(?:[0-9])*(?:.(?:[0-9])+)?)$"; + + if (string.IsNullOrWhiteSpace(customHud)) return false; + if (customHud == "full") return true; + if (customHud == "1") return true; + if (!Regex.IsMatch(customHud, ALLOWED_CHARS)) return false; - [SettingsDescription("FPS", "Only show FPS")] - Fps, + string[] hudvars = customHud.Split(","); - [SettingsDescription("Full", "Show everything")] - Full, + return hudvars.All(hudvar => Regex.IsMatch(hudvar, ALLOWED_WORDS)); + } + + public static bool MangoHudIsInstalled() + { + var usrLib = Path.Combine("/usr", "lib", "mangohud", "libMangoHud.so"); // fedora uses this + var usrLib64 = Path.Combine("/usr", "lib64", "mangohud", "libMangoHud.so"); // arch and openSUSE use this + var flatpak = Path.Combine(new string[] { "/usr", "lib", "extensions", "vulkan", "MangoHud", "lib", "x86_64-linux-gnu", "libMangoHud.so"}); + var debuntu = Path.Combine(new string[] { "/usr", "lib", "x86_64-linux-gnu", "mangohud", "libMangoHud.so"}); + if (File.Exists(usrLib64) || File.Exists(usrLib) || File.Exists(flatpak) || File.Exists(debuntu)) + return true; + return false; } } \ 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 4e0a31c5..0dcac216 100644 --- a/src/XIVLauncher.Common.Unix/Compatibility/WineSettings.cs +++ b/src/XIVLauncher.Common.Unix/Compatibility/WineSettings.cs @@ -28,8 +28,7 @@ public class WineSettings public WineSettings(bool isManaged, string customBinPath, string managedFolder, string managedUrl, string storageFolder, string debugVars, FileInfo logFile, DirectoryInfo prefix, bool? esyncOn, bool? fsyncOn) { - // storageFolder is the path to .xlcore folder. managedFolder is the foldername inside the tarball that will be downloaded - // from managedUrl + // storageFolder is the path to .xlcore folder. managedFolder is the foldername inside the tarball that will be downloaded from managedUrl. IsManaged = isManaged; FolderName = managedFolder; DownloadUrl = managedUrl; From 16c32f31dbf3c3be2492a72eda41a190b6008afb Mon Sep 17 00:00:00 2001 From: Rankyn Bass Date: Wed, 26 Jul 2023 07:52:43 -0700 Subject: [PATCH 07/12] Remove duplicate line, rename InstallWine to DownloadWine --- .../Compatibility/CompatibilityTools.cs | 10 +++------- .../Compatibility/UnixHelpers.cs | 2 +- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs b/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs index eb1d5cca..2f805257 100644 --- a/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs +++ b/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs @@ -40,14 +40,10 @@ public CompatibilityTools(WineSettings wineSettings, DxvkSettings dxvkSettings, this.DxvkSettings = dxvkSettings; this.gamemodeOn = gamemodeOn ?? false; - // This is currently unused, but might be useful in the future. + // These are currently unused. Here for future use. this.IsFlatpak = isFlatpak; this.extraEnvironmentVars = extraEnvVars ?? new Dictionary(); - // This is also unused. It's here to allow features to be added to XL.Core without requiring immediate - // changes to XL.Common.Unix. It should only ever be used temporarily. - this.extraEnvironmentVars = extraEnvVars ?? new Dictionary(); - this.wineDirectory = new DirectoryInfo(Path.Combine(toolsFolder.FullName, "wine")); this.dxvkDirectory = new DirectoryInfo(Path.Combine(toolsFolder.FullName, "dxvk")); @@ -68,10 +64,10 @@ public async Task EnsureTool(DirectoryInfo tempPath) if (!File.Exists(Settings.WinePath)) { Log.Information("Compatibility tool does not exist, downloading"); - await UnixHelpers.InstallWine(wineDirectory, Settings.FolderName, Settings.DownloadUrl).ConfigureAwait(false); + await UnixHelpers.DownloadWine(wineDirectory, Settings.FolderName, Settings.DownloadUrl).ConfigureAwait(false); } - EnsurePrefix(); + if (DxvkSettings.Enabled) await UnixHelpers.InstallDxvk(Settings.Prefix, dxvkDirectory, DxvkSettings.FolderName, DxvkSettings.DownloadUrl).ConfigureAwait(false); diff --git a/src/XIVLauncher.Common.Unix/Compatibility/UnixHelpers.cs b/src/XIVLauncher.Common.Unix/Compatibility/UnixHelpers.cs index 27d9b171..61159d5d 100644 --- a/src/XIVLauncher.Common.Unix/Compatibility/UnixHelpers.cs +++ b/src/XIVLauncher.Common.Unix/Compatibility/UnixHelpers.cs @@ -52,7 +52,7 @@ public static async Task InstallDxvk(DirectoryInfo prefix, DirectoryInfo install } } - public static async Task InstallWine(DirectoryInfo installDirectory, string folder, string downloadUrl) + public static async Task DownloadWine(DirectoryInfo installDirectory, string folder, string downloadUrl) { if (!Directory.Exists(Path.Combine(installDirectory.FullName, folder))) await DownloadTool(installDirectory, downloadUrl).ConfigureAwait(false); From f20b884c972d27cce2aeaf3de69fde1fed91f853 Mon Sep 17 00:00:00 2001 From: Rankyn Bass Date: Wed, 26 Jul 2023 19:36:57 -0700 Subject: [PATCH 08/12] Add funcs for extraEnvironmentVars --- .../Compatibility/CompatibilityTools.cs | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs b/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs index 2f805257..266772c5 100644 --- a/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs +++ b/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs @@ -144,6 +144,8 @@ private void MergeDictionaries(IDictionary a, IDictionary env) + { + MergeDictionaries(extraEnvironmentVars, env); + } + private Process RunInPrefix(ProcessStartInfo psi, string workingDirectory, IDictionary environment, bool redirectOutput, bool writeLog, bool wineD3D) { psi.RedirectStandardOutput = redirectOutput; @@ -175,22 +187,17 @@ private Process RunInPrefix(ProcessStartInfo psi, string workingDirectory, IDict } wineEnvironmentVariables.Add("XL_WINEONLINUX", "true"); - string ldPreload = Environment.GetEnvironmentVariable("LD_PRELOAD") ?? ""; - if (this.gamemodeOn == true && !ldPreload.Contains("libgamemodeauto.so.0")) - { - ldPreload = ldPreload.Equals("") ? "libgamemodeauto.so.0" : ldPreload + ":libgamemodeauto.so.0"; - } + if (this.gamemodeOn) + wineEnvironmentVariables.Add("LD_PRELOAD", MergeLDPreload("libgamemodeauto.so.0" , Environment.GetEnvironmentVariable("LD_PRELOAD"))); foreach (var dxvkVar in DxvkSettings.Environment) wineEnvironmentVariables.Add(dxvkVar.Key, dxvkVar.Value); wineEnvironmentVariables.Add("WINEESYNC", Settings.EsyncOn); wineEnvironmentVariables.Add("WINEFSYNC", Settings.FsyncOn); - wineEnvironmentVariables.Add("LD_PRELOAD", ldPreload); - MergeDictionaries(psi.Environment, wineEnvironmentVariables); - MergeDictionaries(psi.Environment, extraEnvironmentVars); + MergeDictionaries(psi.Environment, extraEnvironmentVars); // Allow extraEnvironmentVars to override what we set here. MergeDictionaries(psi.Environment, environment); #if FLATPAK_NOTRIGHTNOW From 7d21042d9706b887673020f1e2ec7c09b7d76d7d Mon Sep 17 00:00:00 2001 From: Rankyn Bass Date: Sat, 29 Jul 2023 21:25:41 -0700 Subject: [PATCH 09/12] Add fix for proton-wine 8 --- src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs b/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs index 266772c5..77924015 100644 --- a/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs +++ b/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs @@ -266,7 +266,7 @@ 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")) + if (output.Contains("syntax error\n") || output.Contains("Exception c0000005")) // Proton8 wine changed the error message { var processName = GetProcessName(winePid); return GetUnixProcessIdByName(processName); From 3025d2500b71272bd6cdace718de36ebb20b58c2 Mon Sep 17 00:00:00 2001 From: Rankyn Bass Date: Tue, 1 Aug 2023 20:08:44 -0700 Subject: [PATCH 10/12] Updated with suggestions from marzent: * Moved download funcs to CompatibilityTools * Moved hud funcs to DxvkSettings * Simplified MergeLDPreload * Cleaned up formatting, deleted a commented-out function --- .../Compatibility/CompatibilityTools.cs | 68 ++++++++----- .../Compatibility/DxvkSettings.cs | 32 ++++++- .../Compatibility/UnixHelpers.cs | 96 ------------------- .../Compatibility/WineSettings.cs | 4 +- 4 files changed, 77 insertions(+), 123 deletions(-) delete mode 100644 src/XIVLauncher.Common.Unix/Compatibility/UnixHelpers.cs diff --git a/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs b/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs index 77924015..afe43885 100644 --- a/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs +++ b/src/XIVLauncher.Common.Unix/Compatibility/CompatibilityTools.cs @@ -4,6 +4,8 @@ using System.IO; using System.Linq; using System.Threading.Tasks; +using System.Net.Http; +using XIVLauncher.Common.Util; using Serilog; #if FLATPAK @@ -63,30 +65,59 @@ public async Task EnsureTool(DirectoryInfo tempPath) { if (!File.Exists(Settings.WinePath)) { - Log.Information("Compatibility tool does not exist, downloading"); - await UnixHelpers.DownloadWine(wineDirectory, Settings.FolderName, Settings.DownloadUrl).ConfigureAwait(false); + Log.Information($"Compatibility tool does not exist, downloading {Settings.DownloadUrl}"); + await DownloadTool(wineDirectory, Settings.DownloadUrl).ConfigureAwait(false); } EnsurePrefix(); if (DxvkSettings.Enabled) - await UnixHelpers.InstallDxvk(Settings.Prefix, dxvkDirectory, DxvkSettings.FolderName, DxvkSettings.DownloadUrl).ConfigureAwait(false); + await InstallDxvk().ConfigureAwait(false); IsToolReady = true; } - // private async Task DownloadTool(DirectoryInfo tempPath) - // { - // using var client = new HttpClient(); - // var tempFilePath = Path.Combine(tempPath.FullName, $"{Guid.NewGuid()}"); + private async Task InstallDxvk() + { + var dxvkPath = Path.Combine(dxvkDirectory.FullName, DxvkSettings.FolderName, "x64"); + if (!Directory.Exists(dxvkPath)) + { + Log.Information($"DXVK does not exist, downloading {DxvkSettings.DownloadUrl}"); + await DownloadTool(dxvkDirectory, DxvkSettings.DownloadUrl).ConfigureAwait(false); + } + + var system32 = Path.Combine(Settings.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); + } + + // 32-bit files for Directx9. + var dxvkPath32 = Path.Combine(dxvkDirectory.FullName, DxvkSettings.FolderName, "x32"); + var syswow64 = Path.Combine(Settings.Prefix.FullName, "drive_c", "windows", "syswow64"); + + if (Directory.Exists(dxvkPath32)) + { + files = Directory.GetFiles(dxvkPath32); - // await File.WriteAllBytesAsync(tempFilePath, await client.GetByteArrayAsync(Settings.DownloadUrl).ConfigureAwait(false)).ConfigureAwait(false); + foreach (string fileName in files) + { + File.Copy(fileName, Path.Combine(syswow64, Path.GetFileName(fileName)), true); + } + } + } - // PlatformHelpers.Untar(tempFilePath, this.wineDirectory.FullName); + private async Task DownloadTool(DirectoryInfo installDirectory, string downloadUrl) + { + using var client = new HttpClient(); + var tempPath = Path.GetTempFileName(); - // Log.Information("Compatibility tool successfully extracted to {Path}", this.wineDirectory.FullName); + File.WriteAllBytes(tempPath, await client.GetByteArrayAsync(downloadUrl)); + PlatformHelpers.Untar(tempPath, installDirectory.FullName); - // File.Delete(tempFilePath); - // } + File.Delete(tempPath); + } private void ResetPrefix() { @@ -146,16 +177,7 @@ private string MergeLDPreload(string a, string b) { a ??= ""; 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(':'); + return (a.Trim(':') + ":" + b.Trim(':')).Trim(':'); } public void AddEnvironmentVar(string key, string value) @@ -283,7 +305,7 @@ private string GetProcessName(Int32 winePid) 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(); + var processNames = matchingLines.Select(l => l.Substring(20).Trim('\'')).ToArray(); return processNames.FirstOrDefault(); } diff --git a/src/XIVLauncher.Common.Unix/Compatibility/DxvkSettings.cs b/src/XIVLauncher.Common.Unix/Compatibility/DxvkSettings.cs index cfd421db..78ad0388 100644 --- a/src/XIVLauncher.Common.Unix/Compatibility/DxvkSettings.cs +++ b/src/XIVLauncher.Common.Unix/Compatibility/DxvkSettings.cs @@ -1,5 +1,7 @@ using System.IO; using System.Collections.Generic; +using System.Text.RegularExpressions; +using System.Linq; using Serilog; namespace XIVLauncher.Common.Unix.Compatibility; @@ -38,9 +40,9 @@ public DxvkSettings(string folder, string url, string storageFolder, bool async, Environment.Add("DXVK_STATE_CACHE_PATH", Path.Combine(dxvkCachePath.FullName, folder)); if (dxvkHudEnabled) - Environment.Add("DXVK_HUD", UnixHelpers.DxvkHudStringIsValid(dxvkHudString) ? dxvkHudString : "1"); + Environment.Add("DXVK_HUD", DxvkHudStringIsValid(dxvkHudString) ? dxvkHudString : "1"); - if (mangoHudEnabled && UnixHelpers.MangoHudIsInstalled()) + if (mangoHudEnabled && MangoHudIsInstalled()) { Environment.Add("MANGOHUD", "1"); if (mangoHudCustomIsFile) @@ -56,4 +58,30 @@ public DxvkSettings(string folder, string url, string storageFolder, bool async, } } } + + public static bool DxvkHudStringIsValid(string customHud) + { + var ALLOWED_CHARS = "^[0-9a-zA-Z,=.]+$"; + var ALLOWED_WORDS = "^(?:devinfo|fps|frametimes|submissions|drawcalls|pipelines|descriptors|memory|gpuload|version|api|cs|compiler|samplers|scale=(?:[0-9])*(?:.(?:[0-9])+)?)$"; + + if (string.IsNullOrWhiteSpace(customHud)) return false; + if (customHud == "full") return true; + if (customHud == "1") return true; + if (!Regex.IsMatch(customHud, ALLOWED_CHARS)) return false; + + string[] hudvars = customHud.Split(","); + + return hudvars.All(hudvar => Regex.IsMatch(hudvar, ALLOWED_WORDS)); + } + + public static bool MangoHudIsInstalled() + { + var usrLib = Path.Combine("/", "usr", "lib", "mangohud", "libMangoHud.so"); // fedora uses this + var usrLib64 = Path.Combine("/", "usr", "lib64", "mangohud", "libMangoHud.so"); // arch and openSUSE use this + var flatpak = Path.Combine("/", "usr", "lib", "extensions", "vulkan", "MangoHud", "lib", "x86_64-linux-gnu", "libMangoHud.so"); + var debuntu = Path.Combine("/", "usr", "lib", "x86_64-linux-gnu", "mangohud", "libMangoHud.so"); + if (File.Exists(usrLib64) || File.Exists(usrLib) || File.Exists(flatpak) || File.Exists(debuntu)) + return true; + return false; + } } \ No newline at end of file diff --git a/src/XIVLauncher.Common.Unix/Compatibility/UnixHelpers.cs b/src/XIVLauncher.Common.Unix/Compatibility/UnixHelpers.cs deleted file mode 100644 index 61159d5d..00000000 --- a/src/XIVLauncher.Common.Unix/Compatibility/UnixHelpers.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System.IO; -using System.Net.Http; -using System.Threading.Tasks; -using System.Linq; -using System.Text.RegularExpressions; -using Serilog; -using XIVLauncher.Common.Util; - -namespace XIVLauncher.Common.Unix.Compatibility; - -public enum DistroPackage -{ - ubuntu, - - fedora, - - arch, - - none, -} - -public static class UnixHelpers -{ - public static async Task InstallDxvk(DirectoryInfo prefix, DirectoryInfo installDirectory, string folder, string downloadUrl) - { - var dxvkPath = Path.Combine(installDirectory.FullName, folder, "x64"); - if (!Directory.Exists(dxvkPath)) - { - Log.Information("DXVK does not exist, downloading"); - await DownloadTool(installDirectory, downloadUrl).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); - } - - // 32-bit files for Directx9. - var dxvkPath32 = Path.Combine(installDirectory.FullName, folder, "x32"); - if (Directory.Exists(dxvkPath32)) - { - var syswow64 = Path.Combine(prefix.FullName, "drive_c", "windows", "syswow64"); - files = Directory.GetFiles(dxvkPath32); - - foreach (string fileName in files) - { - File.Copy(fileName, Path.Combine(syswow64, Path.GetFileName(fileName)), true); - } - } - } - - public static async Task DownloadWine(DirectoryInfo installDirectory, string folder, string downloadUrl) - { - if (!Directory.Exists(Path.Combine(installDirectory.FullName, folder))) - await DownloadTool(installDirectory, downloadUrl).ConfigureAwait(false); - } - - private static async Task DownloadTool(DirectoryInfo installDirectory, string downloadUrl) - { - using var client = new HttpClient(); - var tempPath = Path.GetTempFileName(); - - File.WriteAllBytes(tempPath, await client.GetByteArrayAsync(downloadUrl)); - PlatformHelpers.Untar(tempPath, installDirectory.FullName); - - File.Delete(tempPath); - } - public static bool DxvkHudStringIsValid(string customHud) - { - var ALLOWED_CHARS = "^[0-9a-zA-Z,=.]+$"; - var ALLOWED_WORDS = "^(?:devinfo|fps|frametimes|submissions|drawcalls|pipelines|descriptors|memory|gpuload|version|api|cs|compiler|samplers|scale=(?:[0-9])*(?:.(?:[0-9])+)?)$"; - - if (string.IsNullOrWhiteSpace(customHud)) return false; - if (customHud == "full") return true; - if (customHud == "1") return true; - if (!Regex.IsMatch(customHud, ALLOWED_CHARS)) return false; - - string[] hudvars = customHud.Split(","); - - return hudvars.All(hudvar => Regex.IsMatch(hudvar, ALLOWED_WORDS)); - } - - public static bool MangoHudIsInstalled() - { - var usrLib = Path.Combine("/usr", "lib", "mangohud", "libMangoHud.so"); // fedora uses this - var usrLib64 = Path.Combine("/usr", "lib64", "mangohud", "libMangoHud.so"); // arch and openSUSE use this - var flatpak = Path.Combine(new string[] { "/usr", "lib", "extensions", "vulkan", "MangoHud", "lib", "x86_64-linux-gnu", "libMangoHud.so"}); - var debuntu = Path.Combine(new string[] { "/usr", "lib", "x86_64-linux-gnu", "mangohud", "libMangoHud.so"}); - if (File.Exists(usrLib64) || File.Exists(usrLib) || File.Exists(flatpak) || File.Exists(debuntu)) - return true; - return false; - } -} \ 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 0dcac216..ed8d16df 100644 --- a/src/XIVLauncher.Common.Unix/Compatibility/WineSettings.cs +++ b/src/XIVLauncher.Common.Unix/Compatibility/WineSettings.cs @@ -26,13 +26,13 @@ public class WineSettings public DirectoryInfo Prefix { get; private set; } - public WineSettings(bool isManaged, string customBinPath, string managedFolder, string managedUrl, string storageFolder, string debugVars, FileInfo logFile, DirectoryInfo prefix, bool? esyncOn, bool? fsyncOn) + public WineSettings(bool isManaged, string customBinPath, string managedFolder, string managedUrl, DirectoryInfo storageFolder, string debugVars, FileInfo logFile, DirectoryInfo prefix, bool? esyncOn, bool? fsyncOn) { // storageFolder is the path to .xlcore folder. managedFolder is the foldername inside the tarball that will be downloaded from managedUrl. IsManaged = isManaged; FolderName = managedFolder; DownloadUrl = managedUrl; - BinPath = (isManaged) ? Path.Combine(new [] {storageFolder, "compatibilitytool", "wine", managedFolder, "bin"}) : customBinPath; + BinPath = (isManaged) ? Path.Combine(storageFolder.FullName, "compatibilitytool", "wine", managedFolder, "bin") : customBinPath; this.EsyncOn = (esyncOn ?? false) ? "1" : "0"; this.FsyncOn = (fsyncOn ?? false) ? "1" : "0"; From dd1d2a3ebec5faf10605b956a86d01cc0372b0eb Mon Sep 17 00:00:00 2001 From: Rankyn Bass Date: Thu, 3 Aug 2023 07:45:48 -0700 Subject: [PATCH 11/12] Add defaults for mangohud --- src/XIVLauncher.Common.Unix/Compatibility/DxvkSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/XIVLauncher.Common.Unix/Compatibility/DxvkSettings.cs b/src/XIVLauncher.Common.Unix/Compatibility/DxvkSettings.cs index 78ad0388..db657bbc 100644 --- a/src/XIVLauncher.Common.Unix/Compatibility/DxvkSettings.cs +++ b/src/XIVLauncher.Common.Unix/Compatibility/DxvkSettings.cs @@ -16,7 +16,7 @@ public class DxvkSettings public Dictionary Environment { get; } - public DxvkSettings(string folder, string url, string storageFolder, bool async, int maxFrameRate, bool dxvkHudEnabled, string dxvkHudString, bool mangoHudEnabled, bool mangoHudCustomIsFile, string customMangoHud, bool enabled = true) + public DxvkSettings(string folder, string url, string storageFolder, bool async, int maxFrameRate, bool dxvkHudEnabled, string dxvkHudString, bool mangoHudEnabled = false, bool mangoHudCustomIsFile = false, string customMangoHud = "", bool enabled = true) { FolderName = folder; DownloadUrl = url; From 9e21f074303be3ec294b99520057a55379fdaa09 Mon Sep 17 00:00:00 2001 From: Rankyn Bass Date: Sat, 5 Oct 2024 20:33:32 -0700 Subject: [PATCH 12/12] Remove DXVK_CONFIG_FILE override. Better to have it at default location --- src/XIVLauncher.Common.Unix/Compatibility/DxvkSettings.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/XIVLauncher.Common.Unix/Compatibility/DxvkSettings.cs b/src/XIVLauncher.Common.Unix/Compatibility/DxvkSettings.cs index db657bbc..78530c8a 100644 --- a/src/XIVLauncher.Common.Unix/Compatibility/DxvkSettings.cs +++ b/src/XIVLauncher.Common.Unix/Compatibility/DxvkSettings.cs @@ -22,11 +22,9 @@ public DxvkSettings(string folder, string url, string storageFolder, bool async, DownloadUrl = url; Enabled = enabled; - var dxvkConfigPath = new DirectoryInfo(Path.Combine(storageFolder, "compatibilitytool", "dxvk")); Environment = new Dictionary { { "DXVK_LOG_PATH", Path.Combine(storageFolder, "logs") }, - { "DXVK_CONFIG_FILE", Path.Combine(dxvkConfigPath.FullName, "dxvk.conf") }, }; if (maxFrameRate != 0) @@ -35,7 +33,7 @@ public DxvkSettings(string folder, string url, string storageFolder, bool async, if (async) Environment.Add("DXVK_ASYNC", "1"); - var dxvkCachePath = new DirectoryInfo(Path.Combine(dxvkConfigPath.FullName, "cache")); + var dxvkCachePath = new DirectoryInfo(Path.Combine(storageFolder, "compatibilitytool", "dxvk", "cache")); if (!dxvkCachePath.Exists) dxvkCachePath.Create(); Environment.Add("DXVK_STATE_CACHE_PATH", Path.Combine(dxvkCachePath.FullName, folder));