diff --git a/LightlessSync/FileCache/CacheMonitor.cs b/LightlessSync/FileCache/CacheMonitor.cs index 486e11e..b3f273c 100644 --- a/LightlessSync/FileCache/CacheMonitor.cs +++ b/LightlessSync/FileCache/CacheMonitor.cs @@ -469,7 +469,53 @@ public sealed class CacheMonitor : DisposableMediatorSubscriberBase FileCacheSize = totalSize; - var maxCacheInBytes = (long)(_configService.Current.MaxLocalCacheInGiB * 1024d * 1024d * 1024d); + if (Directory.Exists(_configService.Current.CacheFolder + "/downscaled")) + { + var filesDownscaled = Directory.EnumerateFiles(_configService.Current.CacheFolder + "/downscaled").Select(f => new FileInfo(f)).OrderBy(f => f.LastAccessTime).ToList(); + + long totalSizeDownscaled = 0; + + foreach (var f in filesDownscaled) + { + token.ThrowIfCancellationRequested(); + + try + { + long size = 0; + + if (!isWine) + { + try + { + size = _fileCompactor.GetFileSizeOnDisk(f); + } + catch (Exception ex) + { + Logger.LogTrace(ex, "GetFileSizeOnDisk failed for {file}, using fallback length", f.FullName); + size = f.Length; + } + } + else + { + size = f.Length; + } + + totalSizeDownscaled += size; + } + catch (Exception ex) + { + Logger.LogTrace(ex, "Error getting size for {file}", f.FullName); + } + } + + FileCacheSize = (totalSize + totalSizeDownscaled); + } + else + { + FileCacheSize = totalSize; + } + + var maxCacheInBytes = (long)(_configService.Current.MaxLocalCacheInGiB * 1024d * 1024d * 1024d); if (FileCacheSize < maxCacheInBytes) return; diff --git a/LightlessSync/FileCache/FileCompactor.cs b/LightlessSync/FileCache/FileCompactor.cs index 3edf96a..53377b6 100644 --- a/LightlessSync/FileCache/FileCompactor.cs +++ b/LightlessSync/FileCache/FileCompactor.cs @@ -4,6 +4,7 @@ using LightlessSync.Services.Compactor; using Microsoft.Extensions.Logging; using Microsoft.Win32.SafeHandles; using System.Collections.Concurrent; +using System.ComponentModel; using System.Diagnostics; using System.Runtime.InteropServices; using System.Threading.Channels; @@ -16,6 +17,7 @@ public sealed class FileCompactor : IDisposable public const uint FSCTL_DELETE_EXTERNAL_BACKING = 0x90314U; public const ulong WOF_PROVIDER_FILE = 2UL; public const int _maxRetries = 3; + private readonly bool _isWindows; private readonly ConcurrentDictionary _pendingCompactions; private readonly ILogger _logger; @@ -61,6 +63,7 @@ public sealed class FileCompactor : IDisposable _logger = logger; _lightlessConfigService = lightlessConfigService; _dalamudUtilService = dalamudUtilService; + _isWindows = OperatingSystem.IsWindows(); _compactionQueue = Channel.CreateUnbounded(new UnboundedChannelOptions { @@ -197,11 +200,10 @@ public sealed class FileCompactor : IDisposable { try { - bool isWindowsProc = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); var (_, linuxPath) = ResolvePathsForBtrfs(fileInfo.FullName); var (ok, output, err, code) = - isWindowsProc + _isWindows ? RunProcessShell($"stat -c='%b' {QuoteSingle(linuxPath)}", workingDir: null, 10000) : RunProcessDirect("stat", ["-c='%b'", linuxPath], workingDir: null, 10000); @@ -228,16 +230,28 @@ public sealed class FileCompactor : IDisposable try { var blockSize = GetBlockSizeForPath(fileInfo.FullName, _logger, _dalamudUtilService.IsWine); - var losize = GetCompressedFileSizeW(fileInfo.FullName, out uint hosize); - var size = (long)hosize << 32 | losize; - return (flowControl: false, value: ((size + blockSize - 1) / blockSize) * blockSize); + if (blockSize <= 0) + throw new InvalidOperationException($"Invalid block size {blockSize} for {fileInfo.FullName}"); + + uint lo = GetCompressedFileSizeW(fileInfo.FullName, out uint hi); + + if (lo == 0xFFFFFFFF) + { + int err = Marshal.GetLastWin32Error(); + if (err != 0) + throw new Win32Exception(err); + } + + long size = ((long)hi << 32) | lo; + long rounded = ((size + blockSize - 1) / blockSize) * blockSize; + + return (flowControl: false, value: rounded); } catch (Exception ex) { _logger.LogDebug(ex, "Failed stat size for {file}, fallback to Length", fileInfo.FullName); + return (flowControl: true, value: default); } - - return (flowControl: true, value: default); } /// @@ -685,7 +699,6 @@ public sealed class FileCompactor : IDisposable try { var (winPath, linuxPath) = ResolvePathsForBtrfs(path); - bool isWindowsProc = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); if (IsBtrfsCompressedFile(linuxPath)) { @@ -700,7 +713,7 @@ public sealed class FileCompactor : IDisposable } (bool ok, string stdout, string stderr, int code) = - isWindowsProc + _isWindows ? RunProcessShell($"btrfs filesystem defragment -clzo -- {QuoteSingle(linuxPath)}") : RunProcessDirect("btrfs", ["filesystem", "defragment", "-clzo", "--", linuxPath]); @@ -1029,9 +1042,7 @@ public sealed class FileCompactor : IDisposable /// private (string windowsPath, string linuxPath) ResolvePathsForBtrfs(string path) { - bool isWindowsProc = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - - if (!isWindowsProc) + if (!_isWindows) return (path, path); var (ok, outp, _, _) = RunProcessShell($"winepath -u {QuoteSingle(path)}", workingDir: null, 5000); @@ -1050,7 +1061,7 @@ public sealed class FileCompactor : IDisposable { try { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (_isWindows) { using var _ = new FileStream(winePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); } diff --git a/LightlessSync/UI/UISharedService.cs b/LightlessSync/UI/UISharedService.cs index 1d1c6b0..95132ec 100644 --- a/LightlessSync/UI/UISharedService.cs +++ b/LightlessSync/UI/UISharedService.cs @@ -179,9 +179,9 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase int i = 0; double dblSByte = bytes; - while (dblSByte >= 1000 && i < suffix.Length - 1) + while (dblSByte >= 1024 && i < suffix.Length - 1) { - dblSByte /= 1000.0; + dblSByte /= 1024.0; i++; } diff --git a/LightlessSync/Utils/FileSystemHelper.cs b/LightlessSync/Utils/FileSystemHelper.cs index d63b3b9..f7b3c45 100644 --- a/LightlessSync/Utils/FileSystemHelper.cs +++ b/LightlessSync/Utils/FileSystemHelper.cs @@ -32,7 +32,7 @@ namespace LightlessSync.Utils { string rootPath; - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && (!IsProbablyWine() || !isWine)) + if (OperatingSystem.IsWindows() && (!IsProbablyWine() || !isWine)) { var info = new FileInfo(filePath); var dir = info.Directory ?? new DirectoryInfo(filePath); @@ -50,7 +50,7 @@ namespace LightlessSync.Utils FilesystemType detected; - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && (!IsProbablyWine() || !isWine)) + if (OperatingSystem.IsWindows() && (!IsProbablyWine() || !isWine)) { var root = new DriveInfo(rootPath); var format = root.DriveFormat?.ToUpperInvariant() ?? string.Empty; @@ -214,7 +214,7 @@ namespace LightlessSync.Utils if (_blockSizeCache.TryGetValue(root, out int cached)) return cached; - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && !isWine) + if (OperatingSystem.IsWindows() && !isWine) { int result = GetDiskFreeSpaceW(root, out uint sectorsPerCluster,