This repository has been archived by the owner on Feb 16, 2024. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5 from marzent/xlcommon2
Adding XIVLauncher2.Common
- Loading branch information
Showing
172 changed files
with
15,773 additions
and
20 deletions.
There are no files selected for viewing
281 changes: 281 additions & 0 deletions
281
src/XIVLauncher2.Common.Unix/Compatibility/CompatibilityTools.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,281 @@ | ||
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 XIVLauncher2.Common.Util; | ||
|
||
#if FLATPAK | ||
#warning THIS IS A FLATPAK BUILD!!! | ||
#endif | ||
|
||
namespace XIVLauncher2.Common.Unix.Compatibility; | ||
|
||
public class CompatibilityTools | ||
{ | ||
private DirectoryInfo toolDirectory; | ||
private DirectoryInfo dxvkDirectory; | ||
|
||
private StreamWriter logWriter; | ||
|
||
#if WINE_XIV_ARCH_LINUX | ||
private const string WINE_XIV_RELEASE_URL = "https://github.com/goatcorp/wine-xiv-git/releases/download/7.10.r3.g560db77d/wine-xiv-staging-fsync-git-arch-7.10.r3.g560db77d.tar.xz"; | ||
#elif WINE_XIV_FEDORA_LINUX | ||
private const string WINE_XIV_RELEASE_URL = "https://github.com/goatcorp/wine-xiv-git/releases/download/7.10.r3.g560db77d/wine-xiv-staging-fsync-git-fedora-7.10.r3.g560db77d.tar.xz"; | ||
#else | ||
private const string WINE_XIV_RELEASE_URL = "https://github.com/goatcorp/wine-xiv-git/releases/download/7.10.r3.g560db77d/wine-xiv-staging-fsync-git-ubuntu-7.10.r3.g560db77d.tar.xz"; | ||
#endif | ||
private const string WINE_XIV_RELEASE_NAME = "wine-xiv-staging-fsync-git-7.10.r3.g560db77d"; | ||
|
||
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 DxvkSettings DxvkSettings { get; private set; } | ||
|
||
private readonly bool gamemodeOn; | ||
|
||
public CompatibilityTools(WineSettings wineSettings, DxvkSettings dxvkSettings, bool? gamemodeOn, DirectoryInfo toolsFolder) | ||
{ | ||
this.Settings = wineSettings; | ||
this.DxvkSettings = dxvkSettings; | ||
this.gamemodeOn = gamemodeOn ?? false; | ||
this.toolDirectory = new DirectoryInfo(Path.Combine(toolsFolder.FullName, "beta")); | ||
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.dxvkDirectory.Exists) | ||
this.dxvkDirectory.Create(); | ||
} | ||
} | ||
|
||
public async Task EnsureTool(DirectoryInfo tempPath) | ||
{ | ||
if (!File.Exists(Wine64Path)) | ||
{ | ||
Log.Information("Compatibility tool does not exist, downloading"); | ||
await DownloadTool(tempPath).ConfigureAwait(false); | ||
} | ||
|
||
EnsurePrefix(); | ||
await Dxvk.InstallDxvk(Settings.Prefix, dxvkDirectory, DxvkSettings).ConfigureAwait(false); | ||
|
||
IsToolReady = true; | ||
} | ||
|
||
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); | ||
|
||
PlatformHelpers.Untar(tempFilePath, this.toolDirectory.FullName); | ||
|
||
Log.Information("Compatibility tool successfully extracted to {Path}", this.toolDirectory.FullName); | ||
|
||
File.Delete(tempFilePath); | ||
} | ||
|
||
private void ResetPrefix() | ||
{ | ||
Settings.Prefix.Refresh(); | ||
|
||
if (Settings.Prefix.Exists) | ||
Settings.Prefix.Delete(true); | ||
|
||
Settings.Prefix.Create(); | ||
EnsurePrefix(); | ||
} | ||
|
||
public void EnsurePrefix() | ||
{ | ||
RunInPrefix("cmd /c dir %userprofile%/Documents > nul").WaitForExit(); | ||
} | ||
|
||
public Process RunInPrefix(string command, string workingDirectory = "", IDictionary<string, string> environment = null, bool redirectOutput = false, bool writeLog = false, bool wineD3D = false) | ||
{ | ||
var psi = new ProcessStartInfo(Wine64Path); | ||
psi.Arguments = command; | ||
|
||
Log.Verbose("Running in prefix: {FileName} {Arguments}", psi.FileName, command); | ||
return RunInPrefix(psi, workingDirectory, environment, redirectOutput, writeLog, wineD3D); | ||
} | ||
|
||
public Process RunInPrefix(string[] args, string workingDirectory = "", IDictionary<string, string> environment = null, bool redirectOutput = false, bool writeLog = false, bool wineD3D = false) | ||
{ | ||
var psi = new ProcessStartInfo(Wine64Path); | ||
foreach (var arg in args) | ||
psi.ArgumentList.Add(arg); | ||
|
||
Log.Verbose("Running in prefix: {FileName} {Arguments}", psi.FileName, psi.ArgumentList.Aggregate(string.Empty, (a, b) => a + " " + b)); | ||
return RunInPrefix(psi, workingDirectory, environment, redirectOutput, writeLog, wineD3D); | ||
} | ||
|
||
private void MergeDictionaries(StringDictionary a, IDictionary<string, string> b) | ||
{ | ||
if (b is null) | ||
return; | ||
|
||
foreach (var keyValuePair in b) | ||
{ | ||
if (a.ContainsKey(keyValuePair.Key)) | ||
a[keyValuePair.Key] = keyValuePair.Value; | ||
else | ||
a.Add(keyValuePair.Key, keyValuePair.Value); | ||
} | ||
} | ||
|
||
private Process RunInPrefix(ProcessStartInfo psi, string workingDirectory, IDictionary<string, string> environment, bool redirectOutput, bool writeLog, bool wineD3D) | ||
{ | ||
psi.RedirectStandardOutput = redirectOutput; | ||
psi.RedirectStandardError = writeLog; | ||
psi.UseShellExecute = false; | ||
psi.WorkingDirectory = workingDirectory; | ||
|
||
var wineEnviromentVariables = new Dictionary<string, string>(); | ||
wineEnviromentVariables.Add("WINEPREFIX", Settings.Prefix.FullName); | ||
wineEnviromentVariables.Add("WINEDLLOVERRIDES", $"msquic=,mscoree=n,b;d3d9,d3d11,d3d10core,dxgi={(DxvkSettings.Enabled && !wineD3D ? "n" : "b")}"); | ||
|
||
if (!string.IsNullOrEmpty(Settings.DebugVars)) | ||
{ | ||
wineEnviromentVariables.Add("WINEDEBUG", Settings.DebugVars); | ||
} | ||
|
||
wineEnviromentVariables.Add("XL_WINEONLINUX", "true"); | ||
string ldPreload = Environment.GetEnvironmentVariable("LD_PRELOAD") ?? ""; | ||
|
||
if (gamemodeOn && !ldPreload.Contains("libgamemodeauto.so.0")) | ||
{ | ||
ldPreload = ldPreload.Equals("") ? "libgamemodeauto.so.0" : ldPreload + ":libgamemodeauto.so.0"; | ||
} | ||
|
||
foreach (KeyValuePair<string, string> dxvkVar in DxvkSettings.DxvkVars) | ||
wineEnviromentVariables.Add(dxvkVar.Key, dxvkVar.Value); | ||
|
||
wineEnviromentVariables.Add("WINEESYNC", Settings.EsyncOn); | ||
wineEnviromentVariables.Add("WINEFSYNC", Settings.FsyncOn); | ||
|
||
wineEnviromentVariables.Add("LD_PRELOAD", ldPreload); | ||
|
||
MergeDictionaries(psi.EnvironmentVariables, wineEnviromentVariables); | ||
MergeDictionaries(psi.EnvironmentVariables, environment); | ||
|
||
#if FLATPAK_NOTRIGHTNOW | ||
psi.FileName = "flatpak-spawn"; | ||
|
||
psi.ArgumentList.Insert(0, "--host"); | ||
psi.ArgumentList.Insert(1, Wine64Path); | ||
|
||
foreach (KeyValuePair<string, string> envVar in wineEnviromentVariables) | ||
{ | ||
psi.ArgumentList.Insert(1, $"--env={envVar.Key}={envVar.Value}"); | ||
} | ||
|
||
if (environment != null) | ||
{ | ||
foreach (KeyValuePair<string, string> envVar in environment) | ||
{ | ||
psi.ArgumentList.Insert(1, $"--env=\"{envVar.Key}\"=\"{envVar.Value}\""); | ||
} | ||
} | ||
#endif | ||
|
||
Process helperProcess = new(); | ||
helperProcess.StartInfo = psi; | ||
helperProcess.ErrorDataReceived += new DataReceivedEventHandler((_, errLine) => | ||
{ | ||
if (String.IsNullOrEmpty(errLine.Data)) | ||
return; | ||
try | ||
{ | ||
logWriter.WriteLine(errLine.Data); | ||
Console.Error.WriteLine(errLine.Data); | ||
} | ||
catch (Exception ex) when (ex is ArgumentOutOfRangeException || | ||
ex is OverflowException || | ||
ex is IndexOutOfRangeException) | ||
{ | ||
// very long wine log lines get chopped off after a (seemingly) arbitrary limit resulting in strings that are not null terminated | ||
//logWriter.WriteLine("Error writing Wine log line:"); | ||
//logWriter.WriteLine(ex.Message); | ||
} | ||
}); | ||
|
||
helperProcess.Start(); | ||
if (writeLog) | ||
helperProcess.BeginErrorReadLine(); | ||
|
||
return helperProcess; | ||
} | ||
|
||
public Int32[] GetProcessIds(string executableName) | ||
{ | ||
var wineDbg = RunInPrefix("winedbg --command \"info proc\"", redirectOutput: true); | ||
var output = wineDbg.StandardOutput.ReadToEnd(); | ||
var matchingLines = output.Split('\n', StringSplitOptions.RemoveEmptyEntries).Where(l => l.Contains(executableName)); | ||
return matchingLines.Select(l => int.Parse(l.Substring(1, 8), System.Globalization.NumberStyles.HexNumber)).ToArray(); | ||
} | ||
|
||
public Int32 GetProcessId(string executableName) | ||
{ | ||
return GetProcessIds(executableName).FirstOrDefault(); | ||
} | ||
|
||
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 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(); | ||
} | ||
|
||
public string UnixToWinePath(string unixPath) | ||
{ | ||
var launchArguments = new string[] { "winepath", "--windows", unixPath }; | ||
var winePath = RunInPrefix(launchArguments, redirectOutput: true); | ||
var output = winePath.StandardOutput.ReadToEnd(); | ||
return output.Split('\n', StringSplitOptions.RemoveEmptyEntries).LastOrDefault(); | ||
} | ||
|
||
public void AddRegistryKey(string key, string value, string data) | ||
{ | ||
var args = new string[] { "reg", "add", key, "/v", value, "/d", data, "/f" }; | ||
var wineProcess = RunInPrefix(args); | ||
wineProcess.WaitForExit(); | ||
} | ||
|
||
public void Kill() | ||
{ | ||
var psi = new ProcessStartInfo(WineServerPath) | ||
{ | ||
Arguments = "-k" | ||
}; | ||
psi.EnvironmentVariables.Add("WINEPREFIX", Settings.Prefix.FullName); | ||
|
||
Process.Start(psi); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
using System.IO; | ||
using System.Net.Http; | ||
using System.Threading.Tasks; | ||
using Serilog; | ||
using XIVLauncher2.Common.Util; | ||
|
||
namespace XIVLauncher2.Common.Unix.Compatibility; | ||
|
||
public static class Dxvk | ||
{ | ||
public static async Task InstallDxvk(DirectoryInfo prefix, DirectoryInfo installDirectory, DxvkSettings dxvkSettings) | ||
{ | ||
var dxvkPath = Path.Combine(installDirectory.FullName, dxvkSettings.FolderName, "x64"); | ||
|
||
if (!Directory.Exists(dxvkPath)) | ||
{ | ||
Log.Information("DXVK does not exist, downloading"); | ||
await DownloadDxvk(installDirectory, dxvkSettings.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); | ||
} | ||
} | ||
|
||
private static async Task DownloadDxvk(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 enum DxvkHudType | ||
{ | ||
[SettingsDescription("None", "Show nothing")] | ||
None, | ||
|
||
[SettingsDescription("FPS", "Only show FPS")] | ||
Fps, | ||
|
||
[SettingsDescription("DXVK Hud Custom", "Use a custom DXVK_HUD string")] | ||
Custom, | ||
|
||
[SettingsDescription("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 enum DxvkVersion | ||
{ | ||
[SettingsDescription("1.10.1", "The version of DXVK used with XIVLauncher.Core 1.0.2. Safe to use.")] | ||
v1_10_1, | ||
|
||
[SettingsDescription("1.10.2", "Older version of 1.10 branch of DXVK. Safe to use.")] | ||
v1_10_2, | ||
|
||
[SettingsDescription("1.10.3 (default)", "Current version of 1.10 branch of DXVK.")] | ||
v1_10_3, | ||
|
||
[SettingsDescription("2.0 (might break Dalamud, GShade)", "Newest version of DXVK. May be faster, but not stable yet.")] | ||
v2_0, | ||
} | ||
} |
Oops, something went wrong.