using System.IO; using SpdUp.Core; namespace SpdUp.Services; /// /// Cleans DirectX shader cache, NVIDIA shader cache, and temp files /// to reduce micro-stuttering in games. /// public static class ShaderCacheCleanerService { public static ShaderCleanResult CleanAll() { long totalBytes = 0; int totalFiles = 0; totalBytes += CleanDirectory(GetDirectXShaderCachePath(), ref totalFiles); totalBytes += CleanDirectory(GetNvidiaShaderCachePath(), ref totalFiles); totalBytes += CleanDirectory(GetWindowsTempPath(), ref totalFiles); totalBytes += CleanDirectory(GetLocalTempPath(), ref totalFiles); Logger.Info($"Shader cache clean: {totalFiles} files, {totalBytes / 1048576.0:F1} MB freed."); return new ShaderCleanResult(totalFiles, totalBytes); } public static ShaderCleanResult CleanDirectXCache() { long totalBytes = 0; int totalFiles = 0; totalBytes += CleanDirectory(GetDirectXShaderCachePath(), ref totalFiles); return new ShaderCleanResult(totalFiles, totalBytes); } public static ShaderCleanResult CleanNvidiaCache() { long totalBytes = 0; int totalFiles = 0; totalBytes += CleanDirectory(GetNvidiaShaderCachePath(), ref totalFiles); return new ShaderCleanResult(totalFiles, totalBytes); } public static ShaderCleanResult CleanTempFiles() { long totalBytes = 0; int totalFiles = 0; totalBytes += CleanDirectory(GetWindowsTempPath(), ref totalFiles); totalBytes += CleanDirectory(GetLocalTempPath(), ref totalFiles); return new ShaderCleanResult(totalFiles, totalBytes); } // ── Paths ────────────────────────────────────────────────────────── private static string GetDirectXShaderCachePath() => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "D3DSCache"); private static string GetNvidiaShaderCachePath() => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "NVIDIA", "DXCache"); private static string GetWindowsTempPath() => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "Temp"); private static string GetLocalTempPath() => Path.GetTempPath(); // ── Helpers ──────────────────────────────────────────────────────── private static long CleanDirectory(string path, ref int fileCount) { if (!Directory.Exists(path)) return 0; long bytes = 0; try { foreach (var file in Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories)) { try { var fi = new FileInfo(file); bytes += fi.Length; fi.Delete(); fileCount++; } catch { /* locked / in use – skip */ } } // Remove empty dirs foreach (var dir in Directory.EnumerateDirectories(path, "*", SearchOption.AllDirectories) .OrderByDescending(d => d.Length)) { try { if (!Directory.EnumerateFileSystemEntries(dir).Any()) Directory.Delete(dir); } catch { } } } catch (Exception ex) { Logger.Warn($"Error cleaning {path}: {ex.Message}"); } return bytes; } } public record struct ShaderCleanResult(int FilesDeleted, long BytesFreed) { public double MBFreed => BytesFreed / 1048576.0; }