From dbc8988119f400c0a99a715e92ced44d067165fc Mon Sep 17 00:00:00 2001 From: jz-k Date: Fri, 6 Sep 2024 08:29:56 -0400 Subject: [PATCH] Check hashed contents of memories and saves before saving archives (#324) * update AssetsService to check content hash before saving compressed screenshots or save states * prettier filenames for achives, includes date stamp again --- src/services/AssetsService.cs | 62 +++++++++++++++++++++++++++++++---- 1 file changed, 56 insertions(+), 6 deletions(-) diff --git a/src/services/AssetsService.cs b/src/services/AssetsService.cs index 3e010c19..7bf604e4 100644 --- a/src/services/AssetsService.cs +++ b/src/services/AssetsService.cs @@ -1,4 +1,6 @@ using System.IO.Compression; +using System.Security.Cryptography; +using System.Text; using Newtonsoft.Json; using Pannella.Helpers; @@ -6,7 +8,8 @@ namespace Pannella.Services; public class AssetsService { - private const string BLACKLIST = "https://raw.githubusercontent.com/mattpannella/pupdate/main/blacklist.json"; + private const string BLACKLIST = + "https://raw.githubusercontent.com/mattpannella/pupdate/main/blacklist.json"; private readonly bool useLocalBlacklist; private List blacklist; @@ -46,7 +49,11 @@ public static void BackupMemories(string directory, string backupLocation) BackupDirectory(directory, "Memories", backupLocation); } - private static void BackupDirectory(string rootDirectory, string folderName, string backupLocation) + public static void BackupDirectory( + string rootDirectory, + string folderName, + string backupLocation + ) { if (string.IsNullOrEmpty(rootDirectory)) { @@ -60,22 +67,65 @@ private static void BackupDirectory(string rootDirectory, string folderName, str Console.WriteLine($"Compressing and backing up {folderName} directory..."); string savesPath = Path.Combine(rootDirectory, folderName); - string fileName = $"{folderName}_Backup_{DateTime.Now:yyyy-MM-dd_HH.mm.ss}.zip"; - string archiveName = Path.Combine(backupLocation, fileName); if (Directory.Exists(savesPath)) { + string directoryHash = ComputeDirectoryHash(savesPath); + string truncatedHash = directoryHash.Substring(0, 8); + string dateStamp = DateTime.Now.ToString("yyyy-MM-dd_HH.mm.ss"); + string fileName = $"{folderName}_Backup_{dateStamp}-version{truncatedHash}.zip"; + string archiveName = Path.Combine(backupLocation, fileName); + if (!Directory.Exists(backupLocation)) { Directory.CreateDirectory(backupLocation); } - ZipFile.CreateFromDirectory(savesPath, archiveName); - Console.WriteLine("Complete."); + bool isDuplicateBackup = Directory + .GetFiles(backupLocation, $"{folderName}_Backup_*-version{truncatedHash}.zip") + .Any(); + + if (!isDuplicateBackup) + { + ZipFile.CreateFromDirectory(savesPath, archiveName); + Console.WriteLine("Complete."); + } + else + { + Console.WriteLine($"Backup with the same contents already exists, skipping..."); + } } else { Console.WriteLine($"No {folderName} directory found, skipping backup..."); } } + + private static string ComputeDirectoryHash(string directoryPath) + { + using (var sha256 = SHA256.Create()) + { + var allFiles = Directory + .GetFiles(directoryPath, "*.*", SearchOption.AllDirectories) + .OrderBy(p => p) + .ToList(); + + var hashBuilder = new StringBuilder(); + + foreach (var filePath in allFiles) + { + byte[] fileBytes = File.ReadAllBytes(filePath); + byte[] hashBytes = sha256.ComputeHash(fileBytes); + + hashBuilder.Append( + BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant() + ); + } + + byte[] finalHashBytes = sha256.ComputeHash( + Encoding.UTF8.GetBytes(hashBuilder.ToString()) + ); + return BitConverter.ToString(finalHashBytes).Replace("-", "").ToLowerInvariant(); + } + } }