diff --git a/Patcher/Patcher.sln b/Patcher/Patcher.sln
new file mode 100644
index 0000000..9fd647d
--- /dev/null
+++ b/Patcher/Patcher.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.31515.178
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PatchGenerator", "Patcher\PatchGenerator.csproj", "{DDB70566-994E-4884-8555-C005B238039B}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PatcherUtils", "PatcherUtils\PatcherUtils.csproj", "{A9819B34-8111-4344-B2B3-3DE5D7A43A45}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {DDB70566-994E-4884-8555-C005B238039B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DDB70566-994E-4884-8555-C005B238039B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DDB70566-994E-4884-8555-C005B238039B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DDB70566-994E-4884-8555-C005B238039B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A9819B34-8111-4344-B2B3-3DE5D7A43A45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A9819B34-8111-4344-B2B3-3DE5D7A43A45}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A9819B34-8111-4344-B2B3-3DE5D7A43A45}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A9819B34-8111-4344-B2B3-3DE5D7A43A45}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {A9AA1062-33C6-49F2-880C-067D44041C4A}
+ EndGlobalSection
+EndGlobal
diff --git a/Patcher/Patcher/App.xaml b/Patcher/Patcher/App.xaml
new file mode 100644
index 0000000..601179f
--- /dev/null
+++ b/Patcher/Patcher/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/Patcher/Patcher/App.xaml.cs b/Patcher/Patcher/App.xaml.cs
new file mode 100644
index 0000000..eb4c3a1
--- /dev/null
+++ b/Patcher/Patcher/App.xaml.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Data;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows;
+
+namespace PatchGenerator
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ }
+}
diff --git a/Patcher/Patcher/AssemblyInfo.cs b/Patcher/Patcher/AssemblyInfo.cs
new file mode 100644
index 0000000..8b5504e
--- /dev/null
+++ b/Patcher/Patcher/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/Patcher/Patcher/MainWindow.xaml b/Patcher/Patcher/MainWindow.xaml
new file mode 100644
index 0000000..b01a1a2
--- /dev/null
+++ b/Patcher/Patcher/MainWindow.xaml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Patcher/Patcher/MainWindow.xaml.cs b/Patcher/Patcher/MainWindow.xaml.cs
new file mode 100644
index 0000000..d1c8168
--- /dev/null
+++ b/Patcher/Patcher/MainWindow.xaml.cs
@@ -0,0 +1,173 @@
+using System.Diagnostics;
+using System.IO;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Media;
+using PatcherUtils;
+
+namespace PatchGenerator
+{
+ ///
+ /// Interaction logic for MainWindow.xaml
+ ///
+ public partial class MainWindow : Window
+ {
+ private string compareFolder = "";
+ private string targetFolder = "";
+ private readonly string patchFolder = "Aki_Data/Patcher/".FromCwd();
+ private Stopwatch stopwatch = new Stopwatch();
+
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+
+ private string GetStopWatchTime()
+ {
+ return $"Hours: {stopwatch.Elapsed.Hours} - Mins: {stopwatch.Elapsed.Minutes} - Secs: {stopwatch.Elapsed.Seconds} - MilliSecs: {stopwatch.Elapsed.Milliseconds}";
+ }
+
+ private static bool FileDropCheck(DragEventArgs args, ref string str)
+ {
+ if (!args.Data.GetDataPresent(DataFormats.FileDrop))
+ {
+ return false;
+ }
+
+ string[] paths = (string[])args.Data.GetData(DataFormats.FileDrop);
+
+ if (paths.Length != 1) return false;
+
+ if (!Directory.Exists(paths[0]))
+ {
+ return false;
+ }
+
+ str = paths[0];
+
+ return true;
+ }
+
+ private void CompareLabel_Drop(object sender, DragEventArgs e)
+ {
+ if (FileDropCheck(e, ref compareFolder))
+ {
+ CompareLabel.Content = $"Compare Folder:\n{compareFolder}";
+ CompareLabel.BorderBrush = Brushes.DarkCyan;
+ }
+ else
+ {
+ MessageBox.Show("Dropped File/s could not be used. Make sure you only drop one folder.");
+ }
+ }
+
+ private void TargetLabel_Drop(object sender, DragEventArgs e)
+ {
+ if(FileDropCheck(e, ref targetFolder))
+ {
+ TargetLabel.Content = $"Target Folder:\n{targetFolder}";
+ TargetLabel.BorderBrush = Brushes.DarkCyan;
+ }
+ else
+ {
+ MessageBox.Show("Dropped File/s could not be used. Make sure you only drop one folder.");
+ }
+ }
+
+ private void GeneratePatches()
+ {
+ //create temp data
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ GenProgressBar.IsIndeterminate = true;
+ GenProgressMessageLabel.Content = "Extracting temp data ...";
+ });
+
+ LazyOperations.PrepTempDir();
+
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ GenProgressBar.IsIndeterminate = false;
+ });
+
+
+ //generate patches
+
+ FileCompare bc = new FileCompare(targetFolder, compareFolder, patchFolder);
+
+ bc.ProgressChanged += Bc_ProgressChanged;
+
+ if (!bc.CompareAll())
+ {
+ MessageBox.Show("Failed to generate diffs.", ":(", MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+
+ //TODO - Build patcher
+
+ //TODO - compress to file (should add a name textbox or something)
+
+ //Cleanup temp data
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ GenProgressBar.Value = 100;
+ GenProgressMessageLabel.Content = $"Done";
+ });
+
+ if (!LazyOperations.CleanupTempDir())
+ {
+ MessageBox.Show($"Looks like some temp files could not be removed. You can safely delete this folder:\n\n{LazyOperations.TempDir}");
+ }
+ }
+
+ private void Bc_ProgressChanged(object Sender, int Progress, int Total, int Percent, string Message = "", params LineItem[] AdditionalLineItems)
+ {
+
+ string additionalInfo = "";
+ foreach(LineItem item in AdditionalLineItems)
+ {
+ additionalInfo += $"{item.ItemText}: {item.ItemValue}\n";
+ }
+
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ GenProgressBar.Value = Percent;
+
+ if (!string.IsNullOrWhiteSpace(Message))
+ {
+ GenProgressMessageLabel.Content = Message;
+ }
+
+ GenProgressInfoLabel.Content = $"[{Progress}/{Total}]";
+
+ AdditionalInfoBlock.Text = additionalInfo;
+ });
+ }
+
+ private void GenButton_Click(object sender, RoutedEventArgs e)
+ {
+ GenButton.IsEnabled = false;
+
+ Task.Run(() =>
+ {
+ stopwatch.Reset();
+ stopwatch.Start();
+
+ try
+ {
+ GeneratePatches();
+ }
+ finally
+ {
+ stopwatch.Stop();
+
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ GenButton.IsEnabled = true;
+ GenProgressMessageLabel.Content = "";
+ GenProgressInfoLabel.Content = $"Patches Generated in: {GetStopWatchTime()}";
+ });
+ }
+ });
+ }
+ }
+}
diff --git a/Patcher/Patcher/PatchGenerator.csproj b/Patcher/Patcher/PatchGenerator.csproj
new file mode 100644
index 0000000..d97f97a
--- /dev/null
+++ b/Patcher/Patcher/PatchGenerator.csproj
@@ -0,0 +1,33 @@
+
+
+
+ WinExe
+ net5.0-windows
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ References\Aki.ByteBanger.dll
+
+
+ References\Aki.Common.dll
+
+
+ References\ComponentAce.Compression.Libs.zlib.dll
+
+
+
+
diff --git a/Patcher/Patcher/References/Aki.ByteBanger.dll b/Patcher/Patcher/References/Aki.ByteBanger.dll
new file mode 100644
index 0000000..b9fd681
Binary files /dev/null and b/Patcher/Patcher/References/Aki.ByteBanger.dll differ
diff --git a/Patcher/Patcher/References/Aki.Common.dll b/Patcher/Patcher/References/Aki.Common.dll
new file mode 100644
index 0000000..3be1ac3
Binary files /dev/null and b/Patcher/Patcher/References/Aki.Common.dll differ
diff --git a/Patcher/Patcher/References/ComponentAce.Compression.Libs.zlib.dll b/Patcher/Patcher/References/ComponentAce.Compression.Libs.zlib.dll
new file mode 100644
index 0000000..52b6775
Binary files /dev/null and b/Patcher/Patcher/References/ComponentAce.Compression.Libs.zlib.dll differ
diff --git a/Patcher/Patcher/Resources/7za.exe b/Patcher/Patcher/Resources/7za.exe
new file mode 100644
index 0000000..2bdd57d
Binary files /dev/null and b/Patcher/Patcher/Resources/7za.exe differ
diff --git a/Patcher/PatcherUtils/FileCompare.cs b/Patcher/PatcherUtils/FileCompare.cs
new file mode 100644
index 0000000..72ffb5d
--- /dev/null
+++ b/Patcher/PatcherUtils/FileCompare.cs
@@ -0,0 +1,185 @@
+// NOTES:
+// - redo search pattern;
+// - compare both directories against eachother, not just one to the other
+// - add ability to handle missing directories
+
+using System.IO;
+using Aki.Common.Utils;
+using Aki.ByteBanger;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace PatcherUtils
+{
+ public class FileCompare
+ {
+ public string PatchBase;
+ public string TargetBase;
+ public string CompareBase;
+ private int fileCount;
+ private int fileIt;
+
+ private int diffCount = 0;
+ private int newCount = 0;
+ private int delCount = 0;
+ private int matchCount = 0;
+
+ private List TargetPaths;
+ private List ComparePaths;
+ private List AdditionalInfo = new List();
+
+ ///
+ /// Provides patch generation progress changes
+ ///
+ public event ProgressChangedHandler ProgressChanged;
+ protected virtual void RaiseProgressChanged(int progress, int total, string Message = "", params LineItem[] AdditionalLineItems)
+ {
+ int percent = (int)Math.Floor((double)progress / total * 100);
+
+ ProgressChanged?.Invoke(this, progress, total, percent, Message, AdditionalLineItems);
+ }
+
+ ///
+ /// Compare a target file to an assumed compareable file.
+ ///
+ /// The known target path
+ /// The assumed comparable file path
+ /// True if a comparison was made | False if a comparison could not be made
+ private bool Compare(string targetFile, string assumedCompareFile)
+ {
+ string patchFilePath = targetFile.Replace(TargetBase, PatchBase);
+ //we know our target file exists
+ byte[] targetData = VFS.ReadFile(targetFile);
+
+ if(!File.Exists(assumedCompareFile))
+ {
+ //save the data we won't have in our target as new
+ VFS.WriteFile($"{patchFilePath}.new", Zlib.Compress(targetData, ZlibCompression.Maximum));
+ newCount++;
+ return true;
+ }
+
+ //now our compare file is known to exist
+ byte[] compareData = VFS.ReadFile(assumedCompareFile);
+
+ // get diffs
+ DiffResult result = PatchUtil.Diff(compareData, targetData);
+
+ switch (result.Result)
+ {
+ case DiffResultType.Success:
+ VFS.WriteFile($"{patchFilePath}.bpf", result.PatchInfo.ToBytes());
+ diffCount++;
+ return true;
+
+ case DiffResultType.FilesMatch:
+ matchCount++;
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ ///
+ /// Compares the base folders and generates patch files.
+ ///
+ /// True if patches were generated successfully | False if patch generation failed
+ public bool CompareAll()
+ {
+ DirectoryInfo targetDirInfo = new DirectoryInfo(TargetBase);
+ DirectoryInfo compareDirInfo = new DirectoryInfo(CompareBase);
+
+ AdditionalInfo.Add(new LineItem("Diff Patch", "0"));
+ AdditionalInfo.Add(new LineItem("New Patch", "0"));
+ AdditionalInfo.Add(new LineItem("Del Patch", "0"));
+ AdditionalInfo.Add(new LineItem("Files Match", "0"));
+
+ if (!targetDirInfo.Exists || !compareDirInfo.Exists)
+ {
+ Console.WriteLine("Target or Compare folder does not exist");
+ return false;
+ }
+
+ //Get all the files recursively
+ TargetPaths = new List(targetDirInfo.GetFiles("*.*", SearchOption.AllDirectories));
+ ComparePaths = new List(compareDirInfo.GetFiles("*.*", SearchOption.AllDirectories));
+
+ RaiseProgressChanged(0, fileCount, "Generating diffs...");
+
+ /* Comparing Target files -> Compare files
+ * - Exists = Diff (.bfd file)
+ * - Doesn't Exist = New (.new file)
+ *
+ * Once everything has been compared from one side, any remaining paths in our ComparePaths
+ * are things that don't exist in our target and can be deleted (.del file)
+ */
+
+ for (int x = 0; x < TargetPaths.Count; x++)
+ {
+ FileInfo file = TargetPaths[x];
+
+ string assumedComparePath = file.DirectoryName.Replace(TargetBase, CompareBase);
+
+ if (!Compare(file.FullName, VFS.Combine(assumedComparePath, file.Name)))
+ {
+ return false;
+ }
+
+ //remove any existing files from our ComparePaths
+ FileInfo assumedFile = new FileInfo(VFS.Combine(assumedComparePath, file.Name));
+ if (assumedFile.Exists && ComparePaths.Exists(x => x.FullName == assumedFile.FullName))
+ {
+ ComparePaths.Remove(ComparePaths.Where(x => x.FullName == assumedFile.FullName).FirstOrDefault());
+ }
+
+
+ AdditionalInfo[0].ItemValue = diffCount.ToString();
+ AdditionalInfo[1].ItemValue = newCount.ToString();
+ AdditionalInfo[3].ItemValue = matchCount.ToString();
+
+ fileIt++;
+ RaiseProgressChanged(fileIt, fileCount, "", AdditionalInfo.ToArray());
+ }
+
+
+ if (ComparePaths.Count == 0)
+ {
+ //if there are no files to delete, just return true
+ return true;
+ }
+
+ //progress reset for files that need to be deleted
+ RaiseProgressChanged(0, ComparePaths.Count, "Processing .del files...");
+ fileIt = 0;
+ fileCount = ComparePaths.Count;
+
+ //the paths remaining in ComparePaths don't exist in our target and need to be removed during patching.
+ foreach (FileInfo file in ComparePaths)
+ {
+ //add del files replace root dir with patch base
+ string patchFilePath = file.FullName.Replace(CompareBase, PatchBase);
+ VFS.WriteFile($"{patchFilePath}.del", new byte[0]);
+
+ delCount++;
+ AdditionalInfo[2].ItemValue = delCount.ToString();
+
+ fileIt++;
+ RaiseProgressChanged(fileIt, fileCount, "", AdditionalInfo.ToArray());
+ }
+
+ return true;
+ }
+
+ public FileCompare(string TargetBase, string CompareBase, string PatchBase)
+ {
+ this.TargetBase = TargetBase;
+ this.CompareBase = CompareBase;
+ this.PatchBase = PatchBase;
+
+ fileCount = VFS.GetFilesCount(TargetBase);
+ fileIt = 0;
+ }
+ }
+}
diff --git a/Patcher/PatcherUtils/FilePatcher.cs b/Patcher/PatcherUtils/FilePatcher.cs
new file mode 100644
index 0000000..8b96cbe
--- /dev/null
+++ b/Patcher/PatcherUtils/FilePatcher.cs
@@ -0,0 +1,149 @@
+using System;
+using System.IO;
+using Aki.Common.Utils;
+using Aki.ByteBanger;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace PatcherUtils
+{
+ public class FilePatcher
+ {
+ public string TargetBase;
+ public string PatchBase;
+ private int fileCount;
+ private int fileIt;
+
+ private int diffCount;
+ private int newCount;
+ private int delCount;
+
+ private List AdditionalInfo;
+
+
+ public event ProgressChangedHandler ProgressChanged;
+
+ protected virtual void RaiseProgressChanged(int progress, int total, string Message = "", params LineItem[] AdditionalLineItems)
+ {
+ int percent = (int)Math.Floor((double)progress / total * 100);
+
+ ProgressChanged?.Invoke(this, progress, total, percent, Message, AdditionalLineItems);
+ }
+
+ public bool Patch(string targetfile, string patchfile)
+ {
+ byte[] target = VFS.ReadFile(targetfile);
+ byte[] patch = VFS.ReadFile(patchfile);
+
+ PatchResult result = PatchUtil.Patch(target, PatchInfo.FromBytes(patch));
+
+ switch (result.Result)
+ {
+ case PatchResultType.Success:
+ VFS.WriteFile(targetfile, result.PatchedData);
+ return true;
+
+ case PatchResultType.AlreadyPatched:
+ case PatchResultType.InputChecksumMismatch:
+ case PatchResultType.InputLengthMismatch:
+ return true;
+
+ case PatchResultType.OutputChecksumMismatch:
+ default:
+ return false;
+ }
+ }
+
+ private bool PatchAll(string targetpath, string patchpath)
+ {
+ DirectoryInfo di = new DirectoryInfo(patchpath);
+
+ RaiseProgressChanged(0, fileCount, "Patching client...");
+
+ foreach (FileInfo file in di.GetFiles())
+ {
+ FileInfo target;
+
+ switch (file.Extension)
+ {
+ // patch
+ case ".bpf":
+ {
+ target = new FileInfo(VFS.Combine(targetpath, file.Name.Replace(".bpf", "")));
+
+ if (!Patch(target.FullName, file.FullName))
+ {
+ // patch failed
+ return false;
+ }
+
+ diffCount--;
+ }
+ break;
+
+ // add new files
+ case ".new":
+ {
+ target = new FileInfo(VFS.Combine(targetpath, file.Name.Replace(".new", "")));
+ VFS.WriteFile(target.FullName, Zlib.Decompress(VFS.ReadFile(file.FullName)));
+ newCount--;
+ }
+ break;
+
+ // delete old files
+ case ".del":
+ {
+ target = new FileInfo(VFS.Combine(targetpath, file.Name.Replace(".del", "")));
+ target.IsReadOnly = false;
+ target.Delete();
+ delCount--;
+ }
+ break;
+ }
+
+ AdditionalInfo[0].ItemValue = diffCount.ToString();
+ AdditionalInfo[1].ItemValue = newCount.ToString();
+ AdditionalInfo[2].ItemValue = delCount.ToString();
+
+ ++fileIt;
+ RaiseProgressChanged(fileIt, fileCount, "", AdditionalInfo.ToArray());
+ }
+
+ foreach (DirectoryInfo directory in di.GetDirectories())
+ {
+ PatchAll(VFS.Combine(targetpath, directory.Name), directory.FullName);
+ }
+
+ di.Refresh();
+
+ if (di.GetFiles().Length == 0 && di.GetDirectories().Length == 0)
+ {
+ // remove empty folders
+ di.Delete();
+ }
+
+ return true;
+ }
+
+ public bool Run()
+ {
+ fileCount = VFS.GetFilesCount(PatchBase);
+
+ FileInfo[] files = new DirectoryInfo(PatchBase).GetFiles("*.*", SearchOption.AllDirectories);
+
+ diffCount = files.Where(x => x.Extension == ".bpf").Count();
+ newCount = files.Where(x => x.Extension == ".new").Count();
+ delCount = files.Where(x => x.Extension == ".del").Count();
+
+ AdditionalInfo = new List()
+ {
+ new LineItem("Patches Remaining", diffCount.ToString()),
+ new LineItem("New Files to Inflate", newCount.ToString()),
+ new LineItem("Files to Delete", delCount.ToString())
+ };
+
+ fileIt = 0;
+ return PatchAll(TargetBase, PatchBase);
+ }
+ }
+}
diff --git a/Patcher/PatcherUtils/LazyOperations.cs b/Patcher/PatcherUtils/LazyOperations.cs
new file mode 100644
index 0000000..275915d
--- /dev/null
+++ b/Patcher/PatcherUtils/LazyOperations.cs
@@ -0,0 +1,95 @@
+using System.IO;
+using System.Reflection;
+
+namespace PatcherUtils
+{
+ public class LazyOperations
+ {
+ ///
+ /// A directory to store temporary data.
+ ///
+ public static string TempDir = "PATCHER_TEMP".FromCwd();
+
+ private static string SevenZExe = "7za.exe";
+ ///
+ /// The path to the 7za.exe file in the
+ ///
+ public static string SevenZExePath = $"{TempDir}\\{SevenZExe}";
+
+ private static string PatcherClient = "patcher.exe";
+ ///
+ /// The path to the patcher.exe file in the
+ ///
+ public static string PatcherClientPath = $"{TempDir}\\{PatcherClient}";
+
+ ///
+ /// Streams embedded resources out of the assembly
+ ///
+ ///
+ ///
+ /// The resource will not be streamed out if the already exists
+ private static void StreamResourceOut(string ResourceName, string OutputFilePath)
+ {
+ FileInfo outputFile = new FileInfo(OutputFilePath);
+
+ if (outputFile.Exists) return;
+
+ if (!outputFile.Directory.Exists)
+ {
+ Directory.CreateDirectory(outputFile.Directory.FullName);
+ }
+
+ using (FileStream fs = File.Create(OutputFilePath))
+ using (Stream s = Assembly.GetExecutingAssembly().GetManifestResourceStream(ResourceName))
+ {
+ s.CopyTo(fs);
+ }
+ }
+
+ ///
+ /// Checks the resources in the assembly and streams them to the temp directory for later use.
+ ///
+ public static void PrepTempDir()
+ {
+ foreach(string resource in Assembly.GetExecutingAssembly().GetManifestResourceNames())
+ {
+ switch(resource)
+ {
+ case string a when a.EndsWith(SevenZExe):
+ {
+ StreamResourceOut(resource, SevenZExePath);
+ break;
+ }
+ case string a when a.EndsWith(PatcherClient):
+ {
+ StreamResourceOut(resource, PatcherClientPath);
+ break;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Deletes the recursively
+ ///
+ /// Returns true if the temp directory was deleted.
+ public static bool CleanupTempDir()
+ {
+ DirectoryInfo dir = new DirectoryInfo(TempDir);
+
+ if(dir.Exists)
+ {
+ dir.Delete(true);
+ }
+
+ dir.Refresh();
+
+ if(dir.Exists)
+ {
+ return false;
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/Patcher/PatcherUtils/LineItem.cs b/Patcher/PatcherUtils/LineItem.cs
new file mode 100644
index 0000000..93dac23
--- /dev/null
+++ b/Patcher/PatcherUtils/LineItem.cs
@@ -0,0 +1,15 @@
+namespace PatcherUtils
+{
+ public class LineItem
+ {
+ public string ItemText;
+ public string ItemValue;
+ public bool HasValue => ItemValue != "";
+
+ public LineItem(string ItemText, string ItemValue = "")
+ {
+ this.ItemText = ItemText;
+ this.ItemValue = ItemValue;
+ }
+ }
+}
diff --git a/Patcher/PatcherUtils/PatcherUtils.csproj b/Patcher/PatcherUtils/PatcherUtils.csproj
new file mode 100644
index 0000000..2c04ff7
--- /dev/null
+++ b/Patcher/PatcherUtils/PatcherUtils.csproj
@@ -0,0 +1,19 @@
+
+
+
+ net5.0
+
+
+
+
+ ..\Patcher\References\Aki.ByteBanger.dll
+
+
+ ..\Patcher\References\Aki.Common.dll
+
+
+ ..\Patcher\References\ComponentAce.Compression.Libs.zlib.dll
+
+
+
+
diff --git a/Patcher/PatcherUtils/ProgressChangedHandler.cs b/Patcher/PatcherUtils/ProgressChangedHandler.cs
new file mode 100644
index 0000000..073fa99
--- /dev/null
+++ b/Patcher/PatcherUtils/ProgressChangedHandler.cs
@@ -0,0 +1,13 @@
+namespace PatcherUtils
+{
+ ///
+ /// delegate
+ ///
+ /// The object calling the handler
+ /// The current number of items processed
+ /// The total number of items
+ /// The percentage of items processed
+ /// An optional message to display above the progress bar
+ /// Additional information to display below the progress bar.
+ public delegate void ProgressChangedHandler(object Sender, int Progress, int Total, int Percent, string Message = "", params LineItem[] AdditionalLineItems);
+}
diff --git a/Patcher/PatcherUtils/RandomExtensions.cs b/Patcher/PatcherUtils/RandomExtensions.cs
new file mode 100644
index 0000000..865954d
--- /dev/null
+++ b/Patcher/PatcherUtils/RandomExtensions.cs
@@ -0,0 +1,13 @@
+using System;
+using System.IO;
+
+namespace PatcherUtils
+{
+ public static class RandomExtensions
+ {
+ public static string FromCwd(this string s)
+ {
+ return Path.Combine(Environment.CurrentDirectory, s);
+ }
+ }
+}