mysimulation/server/tso.files/Utils/DiffGenerator.cs
Tony Bark 8fec258215 Added FSO.Files for use with the API server
Don't ask me. FreeSO is the prime example of dependency hell.
2024-05-01 04:38:12 -04:00

130 lines
4.3 KiB
C#
Executable file

using System;
using System.Collections.Generic;
using System.IO;
using xxHashSharp;
namespace FSO.Files.Utils
{
public class DiffGenerator
{
public HashSet<string> SourceFiles; //aka before
public HashSet<string> DestFiles; //aka after
public HashSet<string> AddFiles;
public HashSet<string> RemovedFiles;
public HashSet<string> SameFiles;
public string SourcePath;
public string DestPath;
public static List<FileDiff> GetDiffs(string sourcePath, string destPath)
{
var sourceFiles = new HashSet<string>();
RecursiveDirectoryScan(sourcePath, sourceFiles, sourcePath);
var destFiles = new HashSet<string>();
RecursiveDirectoryScan(destPath, destFiles, destPath);
var addFiles = new HashSet<string>(destFiles);
addFiles.ExceptWith(sourceFiles);
var removedFiles = new HashSet<string>(sourceFiles);
removedFiles.ExceptWith(destFiles);
var sameFiles = new HashSet<string>(sourceFiles);
sameFiles.IntersectWith(destFiles);
var diffs = new List<FileDiff>();
foreach (var removed in removedFiles)
{
var bytes = GetFileBytes(Path.Combine(sourcePath, removed));
var hash = xxHash.CalculateHash(bytes).ToString("x8");
diffs.Add(new FileDiff(FileDiffType.Remove, removed, hash, null));
}
foreach (var added in addFiles)
{
var bytes = GetFileBytes(Path.Combine(destPath, added));
var hash = xxHash.CalculateHash(bytes).ToString("x8");
diffs.Add(new FileDiff(FileDiffType.Add, added, null, hash));
}
foreach (var same in sameFiles)
{
var bytesBefore = GetFileBytes(Path.Combine(sourcePath, same));
var bytesAfter = GetFileBytes(Path.Combine(destPath, same));
var hashBefore = xxHash.CalculateHash(bytesBefore).ToString("x8");
var hashAfter = xxHash.CalculateHash(bytesAfter).ToString("x8");
diffs.Add(new FileDiff(
(hashBefore == hashAfter) ? FileDiffType.Unchanged : FileDiffType.Modify,
same, hashBefore, hashAfter));
}
return diffs;
}
private static byte[] GetFileBytes(string path)
{
using (var stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
var memory = new MemoryStream();
stream.CopyTo(memory);
var bytes = memory.ToArray();
return bytes;
}
}
private static void RecursiveDirectoryScan(string folder, HashSet<string> fileNames, string basePath)
{
var files = Directory.GetFiles(folder);
foreach (var file in files)
{
fileNames.Add(GetRelativePath(basePath, file));
}
var dirs = Directory.GetDirectories(folder);
foreach (var dir in dirs)
{
RecursiveDirectoryScan(dir, fileNames, basePath);
}
}
private static string GetRelativePath(string relativeTo, string path)
{
if (!(relativeTo.EndsWith("/") || relativeTo.EndsWith("\\"))) relativeTo += "/";
var uri = new Uri(relativeTo);
var rel = Uri.UnescapeDataString(uri.MakeRelativeUri(new Uri(path)).ToString()).Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
if (rel.Contains(Path.DirectorySeparatorChar.ToString()) == false)
{
rel = $".{ Path.DirectorySeparatorChar }{ rel }";
}
return rel;
}
}
public class FileDiff
{
public FileDiffType DiffType;
public string Path;
public string BeforeHash;
public string AfterHash;
public FileDiff(FileDiffType type, string path, string beforeHash, string afterHash)
{
DiffType = type;
Path = path;
BeforeHash = beforeHash;
AfterHash = afterHash;
}
}
public enum FileDiffType
{
Add, //after hash only
Modify,
Remove, //before hash only
Unchanged
}
}