diff --git a/LightlessSync/FileCache/FileCompactor.cs b/LightlessSync/FileCache/FileCompactor.cs index 2e32a3e..a8170f4 100644 --- a/LightlessSync/FileCache/FileCompactor.cs +++ b/LightlessSync/FileCache/FileCompactor.cs @@ -1,6 +1,5 @@ using LightlessSync.LightlessConfiguration; using LightlessSync.Services; -using LightlessSync.Utils; using Microsoft.Extensions.Logging; using System.Collections.Concurrent; using System.Diagnostics; @@ -191,6 +190,16 @@ public sealed class FileCompactor : IDisposable } } + private static string ConvertWinePathToLinux(string winePath) + { + if (winePath.StartsWith("Z:\\", StringComparison.OrdinalIgnoreCase)) + return "/" + winePath.Substring(3).Replace('\\', '/'); + if (winePath.StartsWith("C:\\", StringComparison.OrdinalIgnoreCase)) + return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), + winePath.Substring(3).Replace('\\', '/')).Replace('\\', '/'); + return winePath.Replace('\\', '/'); + } + [DllImport("kernel32.dll")] private static extern int DeviceIoControl(IntPtr hDevice, uint dwIoControlCode, IntPtr lpInBuffer, uint nInBufferSize, IntPtr lpOutBuffer, uint nOutBufferSize, out IntPtr lpBytesReturned, out IntPtr lpOverlapped); @@ -347,18 +356,42 @@ public sealed class FileCompactor : IDisposable var mountOptions = GetMountOptionsForPath(path); if (mountOptions.Contains("compress", StringComparison.OrdinalIgnoreCase)) { - _logger.LogWarning("Cannot safely decompress {file}: filesystem mounted with compression ({opts}). Remount with 'compress=no' before running decompression.", path, mountOptions); + _logger.LogWarning( + "Cannot safely decompress {file}: filesystem mounted with compression ({opts}). Remount with 'compress=no' before running decompression.", + path, mountOptions); return; } - _logger.LogDebug("Rewriting {file} to remove btrfs compression...", path); - - var psi = new ProcessStartInfo("btrfs", $"filesystem defragment -- \"{path}\"") + string realPath = path; + bool isWine = _dalamudUtilService?.IsWine ?? false; + if (isWine) { + if (path.StartsWith("Z:\\", StringComparison.OrdinalIgnoreCase)) + { + realPath = "/" + path.Substring(3).Replace('\\', '/'); + } + else if (path.StartsWith("C:\\", StringComparison.OrdinalIgnoreCase)) + { + // fallback for Wine's C:\ mapping + realPath = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.Personal), + path.Substring(3).Replace('\\', '/') + ).Replace('\\', '/'); + } + + _logger.LogTrace("Detected Wine environment. Converted path for decompression: {realPath}", realPath); + } + + string command = $"btrfs filesystem defragment -- \"{realPath}\""; + var psi = new ProcessStartInfo + { + FileName = isWine ? "/bin/bash" : "btrfs", + Arguments = isWine ? $"-c \"{command}\"" : $"filesystem defragment -- \"{realPath}\"", RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, - CreateNoWindow = true + CreateNoWindow = true, + WorkingDirectory = "/" }; using var proc = Process.Start(psi); @@ -368,7 +401,7 @@ public sealed class FileCompactor : IDisposable return; } - //End stream of process to read the files + // 4️⃣ Read process output var stdout = proc.StandardOutput.ReadToEnd(); var stderr = proc.StandardError.ReadToEnd(); proc.WaitForExit(); @@ -495,12 +528,36 @@ public sealed class FileCompactor : IDisposable { try { - var psi = new ProcessStartInfo("filefrag", $"-v \"{path}\"") + bool isWine = _dalamudUtilService?.IsWine ?? false; + string realPath = path; + + if (isWine) { + if (path.StartsWith("Z:\\", StringComparison.OrdinalIgnoreCase)) + { + realPath = "/" + path.Substring(3).Replace('\\', '/'); + } + else if (path.StartsWith("C:\\", StringComparison.OrdinalIgnoreCase)) + { + realPath = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.Personal), + path.Substring(3).Replace('\\', '/') + ).Replace('\\', '/'); + } + + _logger.LogTrace("Detected Wine environment. Converted path for filefrag: {realPath}", realPath); + } + + string command = $"filefrag -v -- \"{realPath}\""; + var psi = new ProcessStartInfo + { + FileName = isWine ? "/bin/bash" : "filefrag", + Arguments = isWine ? $"-c \"{command}\"" : $"-v -- \"{realPath}\"", RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, - CreateNoWindow = true + CreateNoWindow = true, + WorkingDirectory = "/" }; using var proc = Process.Start(psi); @@ -511,10 +568,17 @@ public sealed class FileCompactor : IDisposable } string output = proc.StandardOutput.ReadToEnd(); + string stderr = proc.StandardError.ReadToEnd(); proc.WaitForExit(); - bool compressed = output.Contains("flags: compressed", StringComparison.OrdinalIgnoreCase); + if (proc.ExitCode != 0) + { + _logger.LogDebug("filefrag exited with {code} for {file}. stderr: {stderr}", + proc.ExitCode, path, stderr); + return false; + } + bool compressed = output.Contains("flags: compressed", StringComparison.OrdinalIgnoreCase); _logger.LogTrace("Btrfs compression check for {file}: {compressed}", path, compressed); return compressed; } @@ -567,7 +631,20 @@ public sealed class FileCompactor : IDisposable { try { - var psi = new ProcessStartInfo("btrfs", $"filesystem defragment -czstd -- \"{path}\"") + string realPath = path; + if (_dalamudUtilService.IsWine) + { + realPath = ConvertWinePathToLinux(path); + _logger.LogTrace("Detected Wine environment, remapped path: {realPath}", realPath); + } + + if (!File.Exists("/usr/bin/btrfs") && !File.Exists("/bin/btrfs")) + { + _logger.LogWarning("Skipping Btrfs compression — btrfs binary not found"); + return false; + } + + var psi = new ProcessStartInfo("btrfs", $"filesystem defragment -czstd -- \"{realPath}\"") { RedirectStandardOutput = true, RedirectStandardError = true, @@ -578,7 +655,7 @@ public sealed class FileCompactor : IDisposable using var proc = Process.Start(psi); if (proc == null) { - _logger.LogWarning("Failed to start btrfs process for {file}", path); + _logger.LogWarning("Failed to start btrfs process for {file}", realPath); return false; } @@ -588,7 +665,7 @@ public sealed class FileCompactor : IDisposable if (proc.ExitCode != 0) { - _logger.LogWarning("btrfs defrag returned {code} for {file}: {err}", proc.ExitCode, path, stderr); + _logger.LogWarning("btrfs defrag returned {code} for {file}: {err}", proc.ExitCode, realPath, stderr); return false; } @@ -597,7 +674,7 @@ public sealed class FileCompactor : IDisposable } catch (Exception ex) { - _logger.LogWarning(ex, "Error running btrfs defragment for {file}", path); + _logger.LogWarning(ex, "Error running btrfs defragment for {file}", realPath); return false; } }