diff --git a/lib/FFXIVQuickLauncher b/lib/FFXIVQuickLauncher index 2844c3df..ffef16dd 160000 --- a/lib/FFXIVQuickLauncher +++ b/lib/FFXIVQuickLauncher @@ -1 +1 @@ -Subproject commit 2844c3df359080755bf4475aff30066c2e5cc29a +Subproject commit ffef16ddf067641cd400740204dd06e5bc55bff8 diff --git a/src/XIVLauncher.Core/Components/MainPage/MainPage.cs b/src/XIVLauncher.Core/Components/MainPage/MainPage.cs index dcd0f9f8..f508f07f 100644 --- a/src/XIVLauncher.Core/Components/MainPage/MainPage.cs +++ b/src/XIVLauncher.Core/Components/MainPage/MainPage.cs @@ -19,6 +19,7 @@ using XIVLauncher.Core.Accounts; using XIVLauncher.Common.Game.Exceptions; using XIVLauncher.Core.Support; +using XIVLauncher.Core.UnixCompatibility; namespace XIVLauncher.Core.Components.MainPage; @@ -738,15 +739,15 @@ public async Task StartGameAndAddon(Launcher.LoginResult loginResult, b } else if (Environment.OSVersion.Platform == PlatformID.Unix) { - if (App.Settings.WineStartupType == WineStartupType.Custom) + if (App.Settings.WineType == WineType.Custom) { if (App.Settings.WineBinaryPath == null) throw new Exception("Custom wine binary path wasn't set."); else if (!Directory.Exists(App.Settings.WineBinaryPath)) throw new Exception("Custom wine binary path is invalid: no such directory.\n" + "Check path carefully for typos: " + App.Settings.WineBinaryPath); - else if (!File.Exists(Path.Combine(App.Settings.WineBinaryPath,"wine64"))) - throw new Exception("Custom wine binary path is invalid: no wine64 found at that location.\n" + + else if (!File.Exists(Path.Combine(App.Settings.WineBinaryPath, "wine64")) && !File.Exists(Path.Combine(App.Settings.WineBinaryPath, "wine"))) + throw new Exception("Custom wine binary path is invalid: no wine or wine64 found at that location.\n" + "Check path carefully for typos: " + App.Settings.WineBinaryPath); } @@ -756,12 +757,10 @@ public async Task StartGameAndAddon(Launcher.LoginResult loginResult, b var _ = Task.Run(async () => { var tempPath = App.Storage.GetFolder("temp"); - var winver = (App.Settings.SetWin7 ?? true) ? "win7" : "win10"; await Program.CompatibilityTools.EnsureTool(tempPath).ConfigureAwait(false); - Program.CompatibilityTools.RunInPrefix($"winecfg /v {winver}"); - var gameFixApply = new GameFixApply(App.Settings.GamePath, App.Settings.GameConfigPath, Program.CompatibilityTools.Settings.Prefix, tempPath); + var gameFixApply = new GameFixApply(App.Settings.GamePath, App.Settings.GameConfigPath, Program.CompatibilityTools.Prefix, tempPath); gameFixApply.UpdateProgress += (text, hasProgress, progress) => { App.LoadingPage.Line1 = "Applying game-specific fixes..."; diff --git a/src/XIVLauncher.Core/Components/SettingsPage/SettingsPage.cs b/src/XIVLauncher.Core/Components/SettingsPage/SettingsPage.cs index cbf9a074..3773a743 100644 --- a/src/XIVLauncher.Core/Components/SettingsPage/SettingsPage.cs +++ b/src/XIVLauncher.Core/Components/SettingsPage/SettingsPage.cs @@ -11,6 +11,7 @@ public class SettingsPage : Page new SettingsTabGame(), new SettingsTabPatching(), new SettingsTabWine(), + new SettingsTabDxvk(), new SettingsTabDalamud(), new SettingsTabAutoStart(), new SettingsTabAbout(), diff --git a/src/XIVLauncher.Core/Components/SettingsPage/SettingsTab.cs b/src/XIVLauncher.Core/Components/SettingsPage/SettingsTab.cs index e50ebeda..5e86faab 100644 --- a/src/XIVLauncher.Core/Components/SettingsPage/SettingsTab.cs +++ b/src/XIVLauncher.Core/Components/SettingsPage/SettingsTab.cs @@ -16,9 +16,10 @@ public override void Draw() foreach (SettingsEntry settingsEntry in Entries) { if (settingsEntry.IsVisible) + { settingsEntry.Draw(); - - ImGui.Dummy(new Vector2(10) * ImGuiHelpers.GlobalScale); + ImGui.Dummy(new Vector2(10) * ImGuiHelpers.GlobalScale); + } } base.Draw(); diff --git a/src/XIVLauncher.Core/Components/SettingsPage/Tabs/SettingsTabDebug.cs b/src/XIVLauncher.Core/Components/SettingsPage/Tabs/SettingsTabDebug.cs index 53225022..dd12f0c6 100644 --- a/src/XIVLauncher.Core/Components/SettingsPage/Tabs/SettingsTabDebug.cs +++ b/src/XIVLauncher.Core/Components/SettingsPage/Tabs/SettingsTabDebug.cs @@ -1,5 +1,7 @@ using System.Collections; using ImGuiNET; +using XIVLauncher.Common; +using XIVLauncher.Core.UnixCompatibility; namespace XIVLauncher.Core.Components.SettingsPage.Tabs; @@ -12,7 +14,10 @@ public override void Draw() { ImGui.TextUnformatted("Generic Information"); ImGui.Separator(); - ImGui.TextUnformatted($"Operating System: {Environment.OSVersion}"); + if (Distro.Platform == Platform.Linux) + ImGui.TextUnformatted($"Operating System: {Distro.Name} - {Environment.OSVersion}"); + else + ImGui.TextUnformatted($"Operating System: {Environment.OSVersion}"); ImGui.TextUnformatted($"Runtime Version: {Environment.Version}"); if (Program.IsSteamDeckHardware) @@ -21,9 +26,8 @@ public override void Draw() if (Program.IsSteamDeckGamingMode) ImGui.Text("Steam Deck Gaming Mode Detected"); -#if FLATPAK + if (Distro.IsFlatpak) ImGui.Text("Running as a Flatpak"); -#endif ImGui.Spacing(); diff --git a/src/XIVLauncher.Core/Components/SettingsPage/Tabs/SettingsTabDxvk.cs b/src/XIVLauncher.Core/Components/SettingsPage/Tabs/SettingsTabDxvk.cs new file mode 100644 index 00000000..77773fe7 --- /dev/null +++ b/src/XIVLauncher.Core/Components/SettingsPage/Tabs/SettingsTabDxvk.cs @@ -0,0 +1,99 @@ +using System.IO; +using System.Numerics; +using System.Runtime.InteropServices; +using ImGuiNET; +using XIVLauncher.Common.Unix.Compatibility; +using XIVLauncher.Common.Util; +using XIVLauncher.Core.UnixCompatibility; + +namespace XIVLauncher.Core.Components.SettingsPage.Tabs; + +public class SettingsTabDxvk : SettingsTab +{ + private SettingsEntry dxvkVersionSetting; + private SettingsEntry wineD3DUseVk; + private SettingsEntry hudSetting; + + public SettingsTabDxvk() + { + Entries = new SettingsEntry[] + { + dxvkVersionSetting = new SettingsEntry("DXVK Version", "Choose which version of DXVK to use.", () => Program.Config.DxvkVersion ?? DxvkVersion.v1_10_3, type => Program.Config.DxvkVersion = type) + { + CheckWarning = type => + { + if (new [] {DxvkVersion.v2_1, DxvkVersion.v2_2}.Contains(type)) + return "May not work with older graphics cards. AMD users may need to use env variable RADV_PERFTEST=gpl"; + return null; + }, + }, + new SettingsEntry("Enable DXVK ASYNC", "Enable DXVK ASYNC patch.", () => Program.Config.DxvkAsyncEnabled ?? true, b => Program.Config.DxvkAsyncEnabled = b) + { + CheckVisibility = () => (new [] {DxvkVersion.v1_10_3, DxvkVersion.v2_0}.Contains(dxvkVersionSetting.Value)), + CheckWarning = b => + { + if (!b && dxvkVersionSetting.Value == DxvkVersion.v2_0) + return "AMD users may need to use env variable RADV_PERFTEST=gpl"; + return null; + }, + }, + hudSetting = new SettingsEntry("DXVK Overlay", "DXVK Hud is included with Dxvk. It doesn't work if Dxvk is disabled.\nMangoHud must be installed separately. Flatpak XIVLauncher needs flatpak MangoHud.", () => Program.Config.HudType, x => Program.Config.HudType = x) + { + CheckVisibility = () => dxvkVersionSetting.Value != DxvkVersion.Disabled, + CheckValidity = x => + { + if ((x == HudType.MangoHud || x == HudType.MangoHudCustom || x == HudType.MangoHudFull) + && (HudManager.FindMangoHud() is null)) + return "MangoHud not detected."; + + return null; + }, + CheckWarning = x => + { + if ((x == HudType.MangoHud || x == HudType.MangoHudCustom || x == HudType.MangoHudFull) + && (HudManager.FindMangoHud() is not null)) + return "WARNING! Using MangoHud, Dalamud AND ReShade all at the same time may result in crashes. Using any two should be safe."; + + return null; + } + }, + new SettingsEntry("DXVK Hud Custom String", "Set a custom string for the built in DXVK Hud. Warning: If it's invalid, the game may hang.", () => Program.Config.DxvkHudCustom, s => Program.Config.DxvkHudCustom = s) + { + CheckVisibility = () => hudSetting.Value == HudType.Custom && dxvkVersionSetting.Value != DxvkVersion.Disabled, + CheckWarning = s => + { + if(!HudManager.CheckDxvkHudString(s)) + return "That's not a valid hud string"; + return null; + }, + }, + new SettingsEntry("MangoHud Custom Path", "Set a custom path for MangoHud config file.", () => Program.Config.MangoHudCustom, s => Program.Config.MangoHudCustom = s) + { + CheckVisibility = () => hudSetting.Value == HudType.MangoHudCustom && !(dxvkVersionSetting.Value == DxvkVersion.Disabled && !wineD3DUseVk.Value), + CheckWarning = s => + { + if(!File.Exists(s)) + return "That's not a valid file."; + return null; + }, + }, + new NumericSettingsEntry("Frame Rate Limit", "Set a frame rate limit, and DXVK will try not exceed it. Use 0 for unlimited.", () => Program.Config.DxvkFrameRate ?? 0, i => Program.Config.DxvkFrameRate = i, 0, 1000) + { + CheckVisibility = () => dxvkVersionSetting.Value != DxvkVersion.Disabled, + }, + }; + } + + public override SettingsEntry[] Entries { get; } + + public override bool IsUnixExclusive => true; + + public override string Title => "DXVK"; + + public override void Save() + { + base.Save(); + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + Program.CreateCompatToolsInstance(); + } +} diff --git a/src/XIVLauncher.Core/Components/SettingsPage/Tabs/SettingsTabTroubleshooting.cs b/src/XIVLauncher.Core/Components/SettingsPage/Tabs/SettingsTabTroubleshooting.cs index c3f4f153..db8ad99c 100644 --- a/src/XIVLauncher.Core/Components/SettingsPage/Tabs/SettingsTabTroubleshooting.cs +++ b/src/XIVLauncher.Core/Components/SettingsPage/Tabs/SettingsTabTroubleshooting.cs @@ -25,12 +25,6 @@ public override void Draw() ImGui.Separator(); - ImGui.Text("\nReset settings to default."); - if (ImGui.Button("Clear Settings")) - { - Program.ClearSettings(true); - } - ImGui.Text("\nClear the Wine Prefix - delete the ~/.xlcore/wineprefix folder"); if (ImGui.Button("Clear Prefix")) { @@ -55,6 +49,12 @@ public override void Draw() Program.ClearLogs(true); } + ImGui.Text("\nReset settings to default."); + if (ImGui.Button("Clear Settings")) + { + Program.ClearSettings(true); + } + ImGui.Text("\nDo all of the above."); if (ImGui.Button("Clear Everything")) { diff --git a/src/XIVLauncher.Core/Components/SettingsPage/Tabs/SettingsTabWine.cs b/src/XIVLauncher.Core/Components/SettingsPage/Tabs/SettingsTabWine.cs index 435006e3..ff3e81d6 100644 --- a/src/XIVLauncher.Core/Components/SettingsPage/Tabs/SettingsTabWine.cs +++ b/src/XIVLauncher.Core/Components/SettingsPage/Tabs/SettingsTabWine.cs @@ -3,25 +3,31 @@ using ImGuiNET; using XIVLauncher.Common.Unix.Compatibility; using XIVLauncher.Common.Util; +using XIVLauncher.Core.UnixCompatibility; namespace XIVLauncher.Core.Components.SettingsPage.Tabs; public class SettingsTabWine : SettingsTab { - private SettingsEntry startupTypeSetting; + private SettingsEntry startupTypeSetting; public SettingsTabWine() { Entries = new SettingsEntry[] { - startupTypeSetting = new SettingsEntry("Wine Version", "Choose how XIVLauncher will start and manage your wine installation.", - () => Program.Config.WineStartupType ?? WineStartupType.Managed, x => Program.Config.WineStartupType = x), + startupTypeSetting = new SettingsEntry("Installation Type", "Choose how XIVLauncher will start and manage your game installation.", + () => Program.Config.WineType ?? WineType.Managed, x => Program.Config.WineType = x), + new SettingsEntry("Wine Version", "Choose a patched wine version.", () => Program.Config.WineVersion ?? WineVersion.Wine7_10, x => Program.Config.WineVersion = x) + { + CheckVisibility = () => startupTypeSetting.Value == WineType.Managed + }, + new SettingsEntry("Wine Binary Path", "Set the path XIVLauncher will use to run applications via wine.\nIt should be an absolute path to a folder containing wine64 and wineserver binaries.", () => Program.Config.WineBinaryPath, s => Program.Config.WineBinaryPath = s) { - CheckVisibility = () => startupTypeSetting.Value == WineStartupType.Custom + CheckVisibility = () => startupTypeSetting.Value == WineType.Custom }, new SettingsEntry("Enable Feral's GameMode", "Enable launching with Feral Interactive's GameMode CPU optimizations.", () => Program.Config.GameModeEnabled ?? true, b => Program.Config.GameModeEnabled = b) @@ -36,8 +42,8 @@ public SettingsTabWine() } }, - new SettingsEntry("Enable DXVK ASYNC", "Enable DXVK ASYNC patch.", () => Program.Config.DxvkAsyncEnabled ?? true, b => Program.Config.DxvkAsyncEnabled = b), new SettingsEntry("Enable ESync", "Enable eventfd-based synchronization.", () => Program.Config.ESyncEnabled ?? true, b => Program.Config.ESyncEnabled = b), + new SettingsEntry("Enable FSync", "Enable fast user mutex (futex2).", () => Program.Config.FSyncEnabled ?? true, b => Program.Config.FSyncEnabled = b) { CheckVisibility = () => RuntimeInformation.IsOSPlatform(OSPlatform.Linux), @@ -49,10 +55,6 @@ public SettingsTabWine() return null; } }, - - new SettingsEntry("Set Windows version to 7", "Default for Wine 8.1+ is Windows 10, but this causes issues with some Dalamud plugins. Windows 7 is recommended for now.", () => Program.Config.SetWin7 ?? true, b => Program.Config.SetWin7 = b), - - new SettingsEntry("DXVK Overlay", "Configure how much of the DXVK overlay is to be shown.", () => Program.Config.DxvkHudType, type => Program.Config.DxvkHudType = type), new SettingsEntry("WINEDEBUG Variables", "Configure debug logging for wine. Useful for troubleshooting.", () => Program.Config.WineDebugVars ?? string.Empty, s => Program.Config.WineDebugVars = s) }; } @@ -67,6 +69,10 @@ public override void Draw() { base.Draw(); + ImGui.Separator(); + + ImGui.Dummy(new Vector2(10) * ImGuiHelpers.GlobalScale); + if (!Program.CompatibilityTools.IsToolDownloaded) { ImGui.BeginDisabled(); @@ -77,7 +83,7 @@ public override void Draw() if (ImGui.Button("Open prefix")) { - PlatformHelpers.OpenBrowser(Program.CompatibilityTools.Settings.Prefix.FullName); + PlatformHelpers.OpenBrowser(Program.CompatibilityTools.Prefix.FullName); } ImGui.SameLine(); @@ -94,6 +100,31 @@ public override void Draw() Program.CompatibilityTools.RunInPrefix("explorer"); } + ImGui.SameLine(); + + if (ImGui.Button("Open Wine explorer (use WineD3D")) + { + Program.CompatibilityTools.RunInPrefix("explorer", wineD3D: true); + + } + + ImGui.Dummy(new Vector2(10) * ImGuiHelpers.GlobalScale); + + + if (ImGui.Button("Set Wine to Windows 7")) + { + Program.CompatibilityTools.RunInPrefix($"winecfg /v win7", redirectOutput: true, writeLog: true); + } + + ImGui.SameLine(); + + if (ImGui.Button("Set Wine to Windows 10")) + { + Program.CompatibilityTools.RunInPrefix($"winecfg /v win10", redirectOutput: true, writeLog: true); + } + + ImGui.Dummy(new Vector2(10) * ImGuiHelpers.GlobalScale); + if (ImGui.Button("Kill all wine processes")) { Program.CompatibilityTools.Kill(); diff --git a/src/XIVLauncher.Core/Configuration/ILauncherConfig.cs b/src/XIVLauncher.Core/Configuration/ILauncherConfig.cs index c7e72e02..53edaf9f 100644 --- a/src/XIVLauncher.Core/Configuration/ILauncherConfig.cs +++ b/src/XIVLauncher.Core/Configuration/ILauncherConfig.cs @@ -3,6 +3,7 @@ using XIVLauncher.Common.Dalamud; using XIVLauncher.Common.Game.Patch.Acquisition; using XIVLauncher.Common.Unix.Compatibility; +using XIVLauncher.Core.UnixCompatibility; namespace XIVLauncher.Core.Configuration; @@ -62,7 +63,11 @@ public interface ILauncherConfig #region Linux - public WineStartupType? WineStartupType { get; set; } + public WineType? WineType { get; set; } + + public WineVersion? WineVersion { get; set; } + + public DxvkVersion? DxvkVersion { get; set; } public string? WineBinaryPath { get; set; } @@ -74,7 +79,13 @@ public interface ILauncherConfig public bool? FSyncEnabled { get; set; } - public Dxvk.DxvkHudType DxvkHudType { get; set; } + public HudType HudType { get; set; } + + public string? DxvkHudCustom { get; set; } + + public string? MangoHudCustom { get; set; } + + public int? DxvkFrameRate { get; set; } public string? WineDebugVars { get; set; } @@ -82,8 +93,6 @@ public interface ILauncherConfig public bool? FixIM { get; set; } - public bool? SetWin7 { get; set; } - #endregion #region Dalamud diff --git a/src/XIVLauncher.Core/Distro.cs b/src/XIVLauncher.Core/Distro.cs new file mode 100644 index 00000000..7f929491 --- /dev/null +++ b/src/XIVLauncher.Core/Distro.cs @@ -0,0 +1,132 @@ +using System.Numerics; +using System.IO; +using System.Collections.Generic; +using XIVLauncher.Common; +using System.Runtime.InteropServices; + +namespace XIVLauncher.Core; + +public enum DistroPackage +{ + ubuntu, + + fedora, + + arch, + + none, +} + +public static class Distro +{ + public static DistroPackage Package { get; private set; } + + public static string Name { get; private set; } + + public static bool IsFlatpak { get; private set; } + + public static Platform Platform { get; private set; } + + public static void Initialize() + { + var os = System.Environment.OSVersion; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + Package = DistroPackage.none; + Name = os.VersionString; + IsFlatpak = false; + Platform = Platform.Win32; + return; + } + + // There's no wine releases for MacOS or FreeBSD, and I'm not sure this will even compile on either + // platform, but here's some code just in case. Can modify this as needed if it's useful in the future. + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + Platform = Platform.Mac; + Name = os.VersionString; + IsFlatpak = false; + Package = DistroPackage.none; + return; + } + + if (RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD)) + { + Platform = Platform.Mac; // Don't have an option for this atm. + Name = os.VersionString; + IsFlatpak = false; + Package = DistroPackage.none; + return; + } + + Platform = Platform.Linux; + try + { + if (!File.Exists("/etc/os-release")) + { + Package = DistroPackage.ubuntu; + Name = "Unknown distribution"; + IsFlatpak = false; + return; + } + var osRelease = File.ReadAllLines("/etc/os-release"); + var osInfo = new Dictionary(); + foreach (var line in osRelease) + { + var keyValue = line.Split('=', 2); + if (keyValue.Length == 1) + osInfo.Add(keyValue[0], ""); + else + osInfo.Add(keyValue[0], keyValue[1]); + } + + var name = (osInfo.ContainsKey("NAME") ? osInfo["NAME"] : "").Trim('"'); + var pretty = (osInfo.ContainsKey("PRETTY_NAME") ? osInfo["PRETTY_NAME"] : "").Trim('"'); + Name = pretty == "" ? (name == "" ? "Unknown distribution" : name) : pretty; + + if (CheckFlatpak(osInfo)) + { + IsFlatpak = true; + Package = DistroPackage.ubuntu; + return; + } + + Package = CheckDistro(osInfo); + IsFlatpak = false; + return; + } + catch + { + // If there's any kind of error opening the file or even finding it, just go with default. + Package = DistroPackage.ubuntu; + Name = "Unknown distribution"; + IsFlatpak = false; + } + } + + private static bool CheckFlatpak(Dictionary osInfo) + { + if (osInfo.ContainsKey("ID")) + if (osInfo["ID"] == "org.freedesktop.platform") + return true; + return false; + } + + private static DistroPackage CheckDistro(Dictionary osInfo) + { + foreach (var kvp in osInfo) + { + if (kvp.Value.ToLower().Contains("fedora")) + return DistroPackage.fedora; + if (kvp.Value.ToLower().Contains("tumbleweed")) + return DistroPackage.fedora; + if (kvp.Value.ToLower().Contains("ubuntu")) + return DistroPackage.ubuntu; + if (kvp.Value.ToLower().Contains("debian")) + return DistroPackage.ubuntu; + if (kvp.Value.ToLower().Contains("arch")) + return DistroPackage.arch; + } + return DistroPackage.ubuntu; + } +} \ No newline at end of file diff --git a/src/XIVLauncher.Core/Program.cs b/src/XIVLauncher.Core/Program.cs index 4a689730..d8051247 100644 --- a/src/XIVLauncher.Core/Program.cs +++ b/src/XIVLauncher.Core/Program.cs @@ -22,6 +22,7 @@ using XIVLauncher.Core.Components.LoadingPage; using XIVLauncher.Core.Configuration; using XIVLauncher.Core.Configuration.Parsers; +using XIVLauncher.Core.UnixCompatibility; namespace XIVLauncher.Core; @@ -115,12 +116,16 @@ private static void LoadConfig(Storage storage) Config.GlobalScale ??= 1.0f; Config.GameModeEnabled ??= false; + Config.DxvkVersion ??= DxvkVersion.v1_10_3; Config.DxvkAsyncEnabled ??= true; + Config.DxvkFrameRate ??= 0; Config.ESyncEnabled ??= true; Config.FSyncEnabled ??= false; - Config.SetWin7 ??= true; + Config.DxvkHudCustom ??= "fps,frametimes,gpuload,version"; + Config.MangoHudCustom ??= Path.Combine(Environment.GetEnvironmentVariable("HOME"), ".config", "MangoHud", "MangoHud.conf"); - Config.WineStartupType ??= WineStartupType.Managed; + Config.WineType ??= WineType.Managed; + Config.WineVersion ??= WineVersion.Wine7_10; Config.WineBinaryPath ??= "/usr/bin"; Config.WineDebugVars ??= "-all"; @@ -187,6 +192,8 @@ private static void Main(string[] args) default: throw new PlatformNotSupportedException(); } + Distro.Initialize(); + Log.Information("Running on {DistroName}. {wineInfo}", Distro.Name, (Distro.Platform == Platform.Linux) ? $"Using {Distro.Package} package for managed wine downloads." : string.Empty); if (!Config.IsIgnoringSteam ?? true) { try @@ -246,15 +253,13 @@ private static void Main(string[] args) var needUpdate = false; -#if FLATPAK - if (Config.DoVersionCheck ?? false) + if (Config.DoVersionCheck ?? false && Distro.IsFlatpak) { var versionCheckResult = UpdateCheck.CheckForUpdate().GetAwaiter().GetResult(); if (versionCheckResult.Success) needUpdate = versionCheckResult.NeedUpdate; - } -#endif + } needUpdate = CoreEnvironmentSettings.IsUpgrade ? true : needUpdate; @@ -319,11 +324,10 @@ public static void CreateCompatToolsInstance() { var wineLogFile = new FileInfo(Path.Combine(storage.GetFolder("logs").FullName, "wine.log")); var winePrefix = storage.GetFolder("wineprefix"); - var wineSettings = new WineSettings(Config.WineStartupType, Config.WineBinaryPath, Config.WineDebugVars, wineLogFile, winePrefix, Config.ESyncEnabled, Config.FSyncEnabled); var toolsFolder = storage.GetFolder("compatibilitytool"); - Directory.CreateDirectory(Path.Combine(toolsFolder.FullName, "dxvk")); - Directory.CreateDirectory(Path.Combine(toolsFolder.FullName, "beta")); - CompatibilityTools = new CompatibilityTools(wineSettings, Config.DxvkHudType, Config.GameModeEnabled, Config.DxvkAsyncEnabled, toolsFolder); + var wine = WineManager.GetSettings(); + var dxvk = DxvkManager.GetSettings(); + CompatibilityTools = new CompatibilityTools(wine, dxvk, winePrefix, toolsFolder, wineLogFile, Distro.IsFlatpak); } public static void ShowWindow() @@ -411,8 +415,8 @@ public static void ClearPlugins(bool tsbutton = false) public static void ClearTools(bool tsbutton = false) { storage.GetFolder("compatibilitytool").Delete(true); - storage.GetFolder("compatibilitytool/beta"); - storage.GetFolder("compatibilitytool/dxvk"); + storage.GetFolder(Path.Combine("compatibilitytool", "wine")); + storage.GetFolder(Path.Combine("compatibilitytool", "dxvk")); if (tsbutton) CreateCompatToolsInstance(); } diff --git a/src/XIVLauncher.Core/UnixCompatibility/DxvkManager.cs b/src/XIVLauncher.Core/UnixCompatibility/DxvkManager.cs new file mode 100644 index 00000000..7620c43f --- /dev/null +++ b/src/XIVLauncher.Core/UnixCompatibility/DxvkManager.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Serilog; +using XIVLauncher.Common; +using XIVLauncher.Common.Unix.Compatibility; + +namespace XIVLauncher.Core.UnixCompatibility; + +public enum DxvkVersion +{ + [SettingsDescription("1.10.3 (default)", "Current version of 1.10 branch of DXVK.")] + v1_10_3, + + [SettingsDescription("2.0", "Newer version of DXVK. Last version with Async patch")] + v2_0, + + [SettingsDescription("2.1 (No Async)", "Newer version of DXVK, using graphics pipeline library. No Async patch.")] + v2_1, + + [SettingsDescription("2.2 (No Async)", "Newest version of DXVK, using graphics pipeline library. No Async patch.")] + v2_2, + + [SettingsDescription("Disabled", "Disable Dxvk, use WineD3D with OpenGL instead.")] + Disabled, +} + +public static class DxvkManager +{ + public static DxvkSettings GetSettings() + { + var isDxvk = true; + var folder = ""; + var url = ""; + var rootfolder = Program.storage.Root.FullName; + var dxvkfolder = Path.Combine(rootfolder, "compatibilitytool", "dxvk"); + var async = (Program.Config.DxvkAsyncEnabled ?? true) ? "1" : "0"; + var framerate = Program.Config.DxvkFrameRate ?? 0; + var env = new Dictionary + { + { "DXVK_LOG_PATH", Path.Combine(rootfolder, "logs") }, + { "DXVK_CONFIG_FILE", Path.Combine(dxvkfolder, "dxvk.conf") }, + }; + if (framerate != 0) + env.Add("DXVK_FRAME_RATE", framerate.ToString()); + switch (Program.Config.DxvkVersion) + { + case DxvkVersion.v1_10_3: + folder = "dxvk-async-1.10.3"; + url = "https://github.com/Sporif/dxvk-async/releases/download/1.10.3/dxvk-async-1.10.3.tar.gz"; + env.Add("DXVK_ASYNC", async); + break; + + case DxvkVersion.v2_0: + folder = "dxvk-async-2.0"; + url = "https://github.com/Sporif/dxvk-async/releases/download/2.0/dxvk-async-2.0.tar.gz"; + env.Add("DXVK_ASYNC", async); + break; + + case DxvkVersion.v2_1: + folder = "dxvk-2.1"; + url = "https://github.com/doitsujin/dxvk/releases/download/v2.1/dxvk-2.1.tar.gz"; + break; + + case DxvkVersion.v2_2: + folder = "dxvk-2.2"; + url = "https://github.com/doitsujin/dxvk/releases/download/v2.2/dxvk-2.2.tar.gz"; + break; + + + case DxvkVersion.Disabled: + env.Add("PROTON_USE_WINED3D", "1"); + env.Add("MANGHUD_DLSYM", "1"); + isDxvk = false; + break; + + default: + throw new ArgumentOutOfRangeException("Bad value for DxvkVersion"); + } + + if (isDxvk) + { + var dxvkCachePath = new DirectoryInfo(Path.Combine(dxvkfolder, "cache")); + if (!dxvkCachePath.Exists) dxvkCachePath.Create(); + env.Add("DXVK_STATE_CACHE_PATH", Path.Combine(dxvkCachePath.FullName, folder)); + } + + var hud = HudManager.GetSettings(); + foreach (var kvp in hud) + { + if (env.ContainsKey(kvp.Key)) + env[kvp.Key] = kvp.Value; + else + env.Add(kvp.Key, kvp.Value); + } + + var settings = new DxvkSettings(folder, url, Program.storage.Root.FullName, env, isDxvk); + return settings; + } +} + diff --git a/src/XIVLauncher.Core/UnixCompatibility/HudManager.cs b/src/XIVLauncher.Core/UnixCompatibility/HudManager.cs new file mode 100644 index 00000000..9637b114 --- /dev/null +++ b/src/XIVLauncher.Core/UnixCompatibility/HudManager.cs @@ -0,0 +1,144 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Serilog; +using XIVLauncher.Common; +using XIVLauncher.Common.Unix.Compatibility; + +namespace XIVLauncher.Core.UnixCompatibility; + +public enum HudType +{ + [SettingsDescription("None", "Show nothing")] + None, + + [SettingsDescription("DXVK Hud FPS", "Only show FPS")] + Fps, + + [SettingsDescription("DXVK Hud Custom", "Use a custom DXVK_HUD string")] + Custom, + + [SettingsDescription("DXVK Hud Full", "Show everything")] + Full, + + [SettingsDescription("MangoHud Default", "Uses no config file.")] + MangoHud, + + [SettingsDescription("MangoHud Custom", "Specify a custom config file")] + MangoHudCustom, + + [SettingsDescription("MangoHud Full", "Show (almost) everything")] + MangoHudFull, +} + +public static class HudManager +{ + 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 static Dictionary GetSettings() + { + var rootfolder = Program.storage.Root.FullName; + var env = new Dictionary(); + var hudType = Program.Config.HudType; + if (FindMangoHud() is null && new [] {HudType.MangoHud, HudType.MangoHudCustom, HudType.MangoHudFull}.Contains(hudType)) + { + hudType = HudType.None; + Program.Config.HudType = HudType.None; + } + var dxvkHudCustom = Program.Config.DxvkHudCustom ?? "fps,frametimes,gpuload,version"; + var mangoHudConfig = string.IsNullOrEmpty(Program.Config.MangoHudCustom) ? null : new FileInfo(Program.Config.MangoHudCustom); + switch (hudType) + { + case HudType.Fps: + env.Add("DXVK_HUD","fps"); + env.Add("MANGOHUD","0"); + break; + + case HudType.Custom: + if (!CheckDxvkHudString(Program.Config.DxvkHudCustom)) + dxvkHudCustom = "fps,frametimes,gpuload,version"; + env.Add("DXVK_HUD", Program.Config.DxvkHudCustom); + env.Add("MANGOHUD","0"); + break; + + case HudType.Full: + env.Add("DXVK_HUD","full"); + env.Add("MANGOHUD","0"); + break; + + case HudType.MangoHud: + env.Add("DXVK_HUD","0"); + env.Add("MANGOHUD","1"); + env.Add("MANGOHUD_CONFIG", ""); + break; + + case HudType.MangoHudCustom: + env.Add("DXVK_HUD","0"); + env.Add("MANGOHUD","1"); + + if (mangoHudConfig is null) + { + var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + var conf1 = Path.Combine(rootfolder, "MangoHud.conf"); + var conf2 = Path.Combine(home, ".config", "MangoHud", "wine-ffxiv_dx11.conf"); + var conf3 = Path.Combine(home, ".config", "MangoHud", "MangoHud.conf"); + if (File.Exists(conf1)) + mangoHudConfig = new FileInfo(conf1); + else if (File.Exists(conf2)) + mangoHudConfig = new FileInfo(conf2); + else if (File.Exists(conf3)) + mangoHudConfig = new FileInfo(conf3); + } + + if (mangoHudConfig is not null && mangoHudConfig.Exists) + env.Add("MANGOHUD_CONFIGFILE", mangoHudConfig.FullName); + else + env.Add("MANGOHUD_CONFIG", ""); + break; + + case HudType.MangoHudFull: + env.Add("DXVK_HUD","0"); + env.Add("MANGOHUD","1"); + env.Add("MANGOHUD_CONFIG","full"); + break; + + case HudType.None: + break; + + default: + throw new ArgumentOutOfRangeException(); + } + + return env; + } + + 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)); + } + + public static string? FindMangoHud() + { + 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 null; + } +} \ No newline at end of file diff --git a/src/XIVLauncher.Core/UnixCompatibility/WineManager.cs b/src/XIVLauncher.Core/UnixCompatibility/WineManager.cs new file mode 100644 index 00000000..553656ac --- /dev/null +++ b/src/XIVLauncher.Core/UnixCompatibility/WineManager.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Diagnostics; +using System.IO; +using System.Linq; +using Serilog; +using XIVLauncher.Common; +using XIVLauncher.Common.Unix.Compatibility; + +namespace XIVLauncher.Core.UnixCompatibility; + +public enum WineType +{ + [SettingsDescription("Managed by XIVLauncher", "Choose a patched version of wine made specifically for XIVLauncher")] + Managed, + + [SettingsDescription("Custom", "Point XIVLauncher to a custom location containing wine binaries to run the game with.")] + Custom, +} + +public enum WineVersion +{ + [SettingsDescription("Wine-xiv 7.10 (Default)", "A patched version of Wine, based on 7.10. The current default.")] + Wine7_10, + + [SettingsDescription("Wine-xiv 8.5", "A newer patched version of Wine-staging 8.5. May be faster, but less stable.")] + Wine8_5, +} + +public static class WineManager +{ + public static WineSettings GetSettings() + { + var winepath = ""; + var folder = ""; + var url = ""; + var version = Program.Config.WineVersion ?? WineVersion.Wine7_10; + var package = Distro.Package.ToString(); + switch (Program.Config.WineType ?? WineType.Managed) + { + case WineType.Custom: + winepath = Program.Config.WineBinaryPath ?? "/usr/bin"; + break; + + case WineType.Managed: + break; + + default: + throw new ArgumentOutOfRangeException("Bad value for WineType"); + } + + switch (version) + { + case WineVersion.Wine8_5: + folder = "wine-xiv-staging-fsync-git-8.5.r4.g4211bac7"; + url = $"https://github.com/goatcorp/wine-xiv-git/releases/download/8.5.r4.g4211bac7/wine-xiv-staging-fsync-git-{package}-8.5.r4.g4211bac7.tar.xz"; + break; + + case WineVersion.Wine7_10: + folder = "wine-xiv-staging-fsync-git-7.10.r3.g560db77d"; + url = $"https://github.com/goatcorp/wine-xiv-git/releases/download/7.10.r3.g560db77d/wine-xiv-staging-fsync-git-{package}-7.10.r3.g560db77d.tar.xz"; + break; + + default: + throw new ArgumentOutOfRangeException("Bad value for WineVersion"); + } + + var env = new Dictionary(); + if (Program.Config.GameModeEnabled ?? false) + env.Add("LD_PRELOAD", "libgamemodeauto.so.0"); + if (!string.IsNullOrEmpty(Program.Config.WineDebugVars)) + env.Add("WINEDEBUG", Program.Config.WineDebugVars); + if (Program.Config.ESyncEnabled ?? true) env.Add("WINEESYNC", "1"); + if (Program.Config.FSyncEnabled ?? false) env.Add("WINEFSYNC", "1"); + env.Add("WINEPREFIX", Path.Combine(Program.storage.Root.FullName, "wineprefix")); + + return new WineSettings(winepath, folder, url, Program.storage.Root.FullName, env); + } +} + + + +