namespace CVRManager.Services; public static class FileOps { public static void CopyTree(string src, string dst, Func skip, Action log, CancellationToken ct) { Directory.CreateDirectory(dst); var count = 0; foreach (var entry in new DirectoryInfo(src).EnumerateFileSystemInfos()) { ct.ThrowIfCancellationRequested(); if (skip(entry.FullName)) continue; var target = Path.Combine(dst, entry.Name); if (entry is DirectoryInfo d) { if (d.Attributes.HasFlag(FileAttributes.ReparsePoint)) continue; CopyTree(d.FullName, target, skip, log, ct); } else { File.Copy(entry.FullName, target, true); if (++count % 200 == 0) log($"Copying {Path.GetFileName(src)}..."); } } } public static void DeleteDirectorySafe(string path) { if (!Directory.Exists(path)) return; foreach (var d in new DirectoryInfo(path).EnumerateDirectories("*", SearchOption.AllDirectories).ToList()) if (d.Exists && d.Attributes.HasFlag(FileAttributes.ReparsePoint)) d.Delete(false); Directory.Delete(path, true); } public static string Slug(string name) { var s = new string(name.Select(c => char.IsLetterOrDigit(c) ? c : '-').ToArray()).Trim('-'); return s.Length == 0 ? "install" : s; } public static string FormatSize(long bytes) => bytes >= 1L << 30 ? $"{bytes / (double)(1L << 30):0.0} GB" : $"{bytes / (double)(1L << 20):0} MB"; }