diff --git a/ResticLogParser.sln b/ResticLogParser.sln
new file mode 100644
index 0000000..d61b91c
--- /dev/null
+++ b/ResticLogParser.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.13.35931.197 d17.13
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ResticLogParser", "ResticLogParser\ResticLogParser.csproj", "{F688B5E5-B5BD-43D2-96E9-A7286A0EB440}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ResticLogParserTests", "ResticLogParserTests\ResticLogParserTests.csproj", "{B4D864B6-35C1-4890-A89E-4C13152B569C}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {F688B5E5-B5BD-43D2-96E9-A7286A0EB440}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F688B5E5-B5BD-43D2-96E9-A7286A0EB440}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F688B5E5-B5BD-43D2-96E9-A7286A0EB440}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F688B5E5-B5BD-43D2-96E9-A7286A0EB440}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B4D864B6-35C1-4890-A89E-4C13152B569C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B4D864B6-35C1-4890-A89E-4C13152B569C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B4D864B6-35C1-4890-A89E-4C13152B569C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B4D864B6-35C1-4890-A89E-4C13152B569C}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {D90F1BCC-4C6C-48FC-8920-2B87B5019AAB}
+ EndGlobalSection
+EndGlobal
diff --git a/ResticLogParser/Program.cs b/ResticLogParser/Program.cs
new file mode 100644
index 0000000..51df1c3
--- /dev/null
+++ b/ResticLogParser/Program.cs
@@ -0,0 +1,3 @@
+using ResticLogParser.src;
+
+new LogParser(@"C:\Users\warrence\Documents\My Documents\restic\restic-logfile.txt").Run();
\ No newline at end of file
diff --git a/ResticLogParser/ResticLogParser.csproj b/ResticLogParser/ResticLogParser.csproj
new file mode 100644
index 0000000..2150e37
--- /dev/null
+++ b/ResticLogParser/ResticLogParser.csproj
@@ -0,0 +1,10 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
diff --git a/ResticLogParser/src/FileLogEntry.cs b/ResticLogParser/src/FileLogEntry.cs
new file mode 100644
index 0000000..1acec92
--- /dev/null
+++ b/ResticLogParser/src/FileLogEntry.cs
@@ -0,0 +1,36 @@
+namespace ResticLogParser.src
+{
+ internal readonly struct FileLogEntry : IComparable
+ {
+ public FileLogEntry(string fileName, FileLogEntryType type, long addedSize)
+ : this(fileName, type, addedSize, addedSize)
+ {
+ }
+
+ public FileLogEntry(string fileName, FileLogEntryType type, long addedSize, long storedSize)
+ {
+ FileName = fileName;
+ Type = type;
+ AddedSize = addedSize;
+ StoredSize = storedSize;
+ }
+
+ public string FileName { get; init; }
+
+ public FileLogEntryType Type { get; init; }
+
+ public long AddedSize { get; init; }
+
+ public long StoredSize { get; init; }
+
+ public int CompareTo(FileLogEntry other)
+ {
+ return AddedSize.CompareTo(other.AddedSize);
+ }
+
+ public override string ToString()
+ {
+ return $"{Type}, added {AddedSize} bytes, {FileName}";
+ }
+ }
+}
diff --git a/ResticLogParser/src/FileLogEntryType.cs b/ResticLogParser/src/FileLogEntryType.cs
new file mode 100644
index 0000000..54106cb
--- /dev/null
+++ b/ResticLogParser/src/FileLogEntryType.cs
@@ -0,0 +1,8 @@
+namespace ResticLogParser.src
+{
+ internal enum FileLogEntryType
+ {
+ New,
+ Modified
+ }
+}
diff --git a/ResticLogParser/src/FileSize.cs b/ResticLogParser/src/FileSize.cs
new file mode 100644
index 0000000..eee0a56
--- /dev/null
+++ b/ResticLogParser/src/FileSize.cs
@@ -0,0 +1,46 @@
+using System.Globalization;
+
+namespace ResticLogParser.src
+{
+ public class FileSize
+ {
+ public static long GetBytesFromString(string fileSizeText)
+ {
+ string[] split = fileSizeText.Split(" ", StringSplitOptions.TrimEntries);
+ if (split.Length < 2 || split[0].Length < 1 || split[1].Length < 1 || split[1].Length > 3
+ || split[1][split[1].Length - 1] != 'B')
+ {
+ return 0;
+ }
+ var value = double.Parse(split[0], CultureInfo.InvariantCulture.NumberFormat);
+ var power = split[1].Length > 1
+ ? split[1][0] switch
+ {
+ 'K' => 1,
+ 'M' => 2,
+ 'G' => 3,
+ 'T' => 4,
+ _ => 0
+ }
+ : 0;
+
+ int valueBase;
+ if (split[1].Length < 3)
+ {
+ valueBase = 1000;
+ }
+ else if (split[1][1] == 'i')
+ {
+ valueBase = 1024;
+ }
+ else
+ {
+ return 0;
+ }
+
+ var s = Math.Pow(valueBase, power);
+ var t = value * Math.Pow(valueBase, power);
+ return (long)Math.Round(value * Math.Pow(valueBase, power));
+ }
+ }
+}
diff --git a/ResticLogParser/src/LogParser.cs b/ResticLogParser/src/LogParser.cs
new file mode 100644
index 0000000..e481e57
--- /dev/null
+++ b/ResticLogParser/src/LogParser.cs
@@ -0,0 +1,91 @@
+using System.Text.RegularExpressions;
+
+namespace ResticLogParser.src
+{
+ internal class LogParser
+ {
+ // Example match:
+ // new /E/Projects/Unity/SpriteTransfer/Images/Equipment/Belts/Belt 3 Up.png, saved in 0.000s (7.201 KiB added)
+ private Regex _newFileLogEntryRegex = new(@"new\s+(.+), saved in \d+\.\d+s \((\d+(?:\.\d+)? .+) added\)");
+
+ // Example match:
+ // modified /C/Users/warrence/AppData/Local/Log/Desktop.log, saved in 0.009s (1.135 MiB added, 1.135 MiB stored)
+ private Regex _modifiedFileLogEntryRegex =
+ new(@"modified\s+(.+), saved in \d+\.\d+s \((\d+(?:\.\d+)? .+) added, (\d+(?:\.\d+)? .+) stored\)");
+
+ private string _logFileName;
+ private readonly List _files = new();
+
+ public LogParser(string logFileName)
+ {
+ if (string.IsNullOrEmpty(logFileName))
+ {
+ throw new ArgumentNullException(nameof(logFileName));
+ }
+
+ _logFileName = logFileName;
+ }
+
+ public void Run()
+ {
+ Console.WriteLine($"Running restic log parser for '{_logFileName}'...");
+ ParseLogFile();
+ _files.Sort();
+ WriteFileInformation();
+ }
+
+ private void ParseLogFile()
+ {
+ try
+ {
+ using StreamReader reader = new(_logFileName);
+ while (!reader.EndOfStream)
+ {
+ string line = reader.ReadLine()!;
+ TryParseLogEntry(line);
+ }
+ }
+ catch (IOException e)
+ {
+ Console.WriteLine("The file could not be read:");
+ Console.WriteLine(e.Message);
+ }
+ }
+
+ private bool TryParseLogEntry(string logLine)
+ {
+ Match match = _newFileLogEntryRegex.Match(logLine);
+ if (match.Success)
+ {
+ var size = FileSize.GetBytesFromString(match.Groups[2].Value);
+ _files.Add(new FileLogEntry(match.Groups[1].Value, FileLogEntryType.New, size));
+ return true;
+ }
+
+ match = _modifiedFileLogEntryRegex.Match(logLine);
+ if (match.Success)
+ {
+ var addedSize = FileSize.GetBytesFromString(match.Groups[2].Value);
+ var storedSize = FileSize.GetBytesFromString(match.Groups[3].Value);
+ _files.Add(new FileLogEntry(match.Groups[1].Value, FileLogEntryType.Modified, addedSize, storedSize));
+ return true;
+ }
+
+ return false;
+ }
+
+ private void WriteFileInformation()
+ {
+ string outputFileName = Path.Combine(Path.GetDirectoryName(_logFileName)!, "restic-added-data.txt");
+ Console.WriteLine($"Writing output to '{outputFileName}'...");
+
+ using (StreamWriter outputFile = new StreamWriter(outputFileName))
+ {
+ for (int i = _files.Count - 1; i >= 0; i--)
+ {
+ outputFile.WriteLine(_files[i].ToString());
+ }
+ }
+ }
+ }
+}
diff --git a/ResticLogParserTests/FileSizeTests.cs b/ResticLogParserTests/FileSizeTests.cs
new file mode 100644
index 0000000..5133db1
--- /dev/null
+++ b/ResticLogParserTests/FileSizeTests.cs
@@ -0,0 +1,18 @@
+using ResticLogParser.src;
+
+namespace ResticLogParserTests
+{
+ public class FileSizeTests
+ {
+ [TestCase("0 B", 0)]
+ [TestCase("739 B", 739)]
+ [TestCase("17.601 KiB", 18023)]
+ [TestCase("17.601 KB", 17601)]
+ [TestCase("107.135 MiB", 112339190)]
+ [TestCase("107.135 MB", 107135000)]
+ public void Test(string fileSizeText, long expected)
+ {
+ Assert.That(FileSize.GetBytesFromString(fileSizeText), Is.EqualTo(expected));
+ }
+ }
+}
\ No newline at end of file
diff --git a/ResticLogParserTests/ResticLogParserTests.csproj b/ResticLogParserTests/ResticLogParserTests.csproj
new file mode 100644
index 0000000..4be83ed
--- /dev/null
+++ b/ResticLogParserTests/ResticLogParserTests.csproj
@@ -0,0 +1,28 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+