diff --git a/src/.vs/ChecksumValidator/project-colors.json b/src/.vs/ChecksumValidator/project-colors.json
new file mode 100644
index 0000000..509e419
--- /dev/null
+++ b/src/.vs/ChecksumValidator/project-colors.json
@@ -0,0 +1,11 @@
+{
+ "Version": 1,
+ "ProjectMap": {
+ "67aa0267-46db-4857-bfc1-87c9ab535542": {
+ "ProjectGuid": "67aa0267-46db-4857-bfc1-87c9ab535542",
+ "DisplayName": "Gardian.Utilities.ChecksumValidator",
+ "ColorIndex": 0
+ }
+ },
+ "NextColorIndex": 1
+}
\ No newline at end of file
diff --git a/src/Backup/ChecksumValidator.sln b/src/Backup/ChecksumValidator.sln
new file mode 100644
index 0000000..f613ea8
--- /dev/null
+++ b/src/Backup/ChecksumValidator.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Gardian.Utilities.ChecksumValidator", "Gardian.Utilities.ChecksumValidator\Gardian.Utilities.ChecksumValidator.csproj", "{67AA0267-46DB-4857-BFC1-87C9AB535542}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {67AA0267-46DB-4857-BFC1-87C9AB535542}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {67AA0267-46DB-4857-BFC1-87C9AB535542}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {67AA0267-46DB-4857-BFC1-87C9AB535542}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {67AA0267-46DB-4857-BFC1-87C9AB535542}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/src/Backup/Gardian.Utilities.ChecksumValidator/CRC32.cs b/src/Backup/Gardian.Utilities.ChecksumValidator/CRC32.cs
new file mode 100644
index 0000000..004a7d6
--- /dev/null
+++ b/src/Backup/Gardian.Utilities.ChecksumValidator/CRC32.cs
@@ -0,0 +1,145 @@
+// Author: Damien Guard, (c) 2006
+// (http://damieng.com/blog/2006/08/08/Calculating_CRC32_in_C_and_NET)
+
+using System;
+using System.Security.Cryptography;
+
+///
+///
+public sealed class CRC32 : HashAlgorithm
+{
+
+ ///
+ /// Default polynomial used for CRC32 computation.
+ ///
+ [CLSCompliant(false)]
+ public const UInt32 DefaultPolynomial = 0xedb88320;
+
+ ///
+ /// Default CRC32 computation seed.
+ ///
+ [CLSCompliant(false)]
+ public const UInt32 DefaultSeed = 0xffffffff;
+
+ private UInt32 _hash;
+ private readonly UInt32 _seed;
+ private readonly UInt32[] _table;
+ private static UInt32[] DefaultTable;
+
+ ///
+ ///
+ public CRC32()
+ {
+ this._table = InitializeTable(DefaultPolynomial);
+ this._seed = DefaultSeed;
+ Initialize();
+ }
+
+ ///
+ ///
+ [CLSCompliant(false)]
+ public CRC32(UInt32 polynomial, UInt32 seed)
+ {
+ this._table = InitializeTable(polynomial);
+ this._seed = seed;
+ Initialize();
+ }
+
+ ///
+ ///
+ public override void Initialize()
+ {
+ this._hash = this._seed;
+ }
+
+ ///
+ ///
+ protected override void HashCore(byte[] buffer, int start, int length)
+ {
+ this._hash = CalculateHash(this._table, this._hash, buffer, start, length);
+ }
+
+ ///
+ ///
+ protected override byte[] HashFinal()
+ {
+ byte[] hashBuffer = UInt32ToBigEndianBytes(~this._hash);
+ this.HashValue = hashBuffer;
+ return hashBuffer;
+ }
+
+ ///
+ ///
+ public override int HashSize
+ {
+ get { return 32; }
+ }
+
+ ///
+ ///
+ [CLSCompliant(false)]
+ public static UInt32 Compute(byte[] buffer)
+ {
+ return ~CalculateHash(InitializeTable(DefaultPolynomial), DefaultSeed, buffer, 0, buffer.Length);
+ }
+
+ ///
+ ///
+ [CLSCompliant(false)]
+ public static UInt32 Compute(UInt32 seed, byte[] buffer)
+ {
+ return ~CalculateHash(InitializeTable(DefaultPolynomial), seed, buffer, 0, buffer.Length);
+ }
+
+ ///
+ ///
+ [CLSCompliant(false)]
+ public static UInt32 Compute(UInt32 polynomial, UInt32 seed, byte[] buffer)
+ {
+ return ~CalculateHash(InitializeTable(polynomial), seed, buffer, 0, buffer.Length);
+ }
+
+ private static UInt32[] InitializeTable(UInt32 polynomial)
+ {
+ if (polynomial == DefaultPolynomial && DefaultTable != null)
+ return DefaultTable;
+
+ var createTable = new UInt32[256];
+ for (int i = 0; i < 256; i++)
+ {
+ var entry = (UInt32)i;
+ for (int j = 0; j < 8; j++)
+ if ((entry & 1) == 1)
+ entry = (entry >> 1) ^ polynomial;
+ else
+ entry = entry >> 1;
+ createTable[i] = entry;
+ }
+
+ if (polynomial == DefaultPolynomial)
+ DefaultTable = createTable;
+
+ return createTable;
+ }
+
+ private static UInt32 CalculateHash(UInt32[] table, UInt32 seed, byte[] buffer, int start, int size)
+ {
+ UInt32 crc = seed;
+ for (int i = start; i < size; i++)
+ unchecked
+ {
+ crc = (crc >> 8) ^ table[buffer[i] ^ crc & 0xff];
+ }
+ return crc;
+ }
+
+ private static byte[] UInt32ToBigEndianBytes(UInt32 x)
+ {
+ return new[] {
+ (byte)((x >> 24) & 0xff),
+ (byte)((x >> 16) & 0xff),
+ (byte)((x >> 8) & 0xff),
+ (byte)(x & 0xff)
+ };
+ }
+}
\ No newline at end of file
diff --git a/src/Backup/Gardian.Utilities.ChecksumValidator/Checksum.cs b/src/Backup/Gardian.Utilities.ChecksumValidator/Checksum.cs
new file mode 100644
index 0000000..991ea77
--- /dev/null
+++ b/src/Backup/Gardian.Utilities.ChecksumValidator/Checksum.cs
@@ -0,0 +1,184 @@
+using System;
+using System.Globalization;
+using System.IO;
+using System.Security.Cryptography;
+using System.Text;
+using System.Threading;
+
+namespace Gardian.Utilities.ChecksumValidator
+{
+
+ ///
+ /// Checksum computation implementation.
+ ///
+ internal static class Checksum
+ {
+
+ //**************************************************
+ //* Public interface
+ //**************************************************
+
+ //-------------------------------------------------
+ ///
+ /// Compute checksum for the specified source file,
+ /// return it as hex string ("F3BA87...").
+ ///
+ ///
+ /// This method will be run on a worker thread
+ /// (from thread pool) via async invocation.
+ ///
+ public static string ComputeChecksum(string sourceFile, ChecksumMethod method, Action progressNotifier)
+ {
+ System.Threading.Thread.Sleep(100);
+ using (var hashAlgorithm = CreateHashAlgorithm(method))
+ using (var source = new TrackingStream(File.OpenRead(sourceFile), progressNotifier))
+ {
+ var hash = hashAlgorithm.ComputeHash(source);
+
+ var msg = new StringBuilder(128);
+ foreach (var byteValue in hash)
+ {
+ msg.AppendFormat(byteValue.ToString("X2", CultureInfo.InvariantCulture));
+ }
+ return msg.ToString();
+ }
+ }
+
+
+ //-------------------------------------------------
+ ///
+ /// Flag that requests cancellation of the current computation
+ /// (if set to true and a computation is ongoing).
+ ///
+ ///
+ /// Field marked as volatile because it will be accessed
+ /// by multiple threads (UI thread to set it, worker
+ /// thread to check it).
+ ///
+ public static volatile bool Cancel;
+
+
+
+
+ //**************************************************
+ //* Private
+ //**************************************************
+
+ //-------------------------------------------------
+ ///
+ /// Find hash algorithm that corresponds to the
+ /// requested method
+ ///
+ /// Thrown
+ /// when requested method is not recognized.
+ private static HashAlgorithm CreateHashAlgorithm(ChecksumMethod method)
+ {
+ switch (method)
+ {
+ case ChecksumMethod.SHA1: return new SHA1CryptoServiceProvider();
+ case ChecksumMethod.MD5: return new MD5CryptoServiceProvider();
+ case ChecksumMethod.CRC32: return new CRC32();
+ default: throw new NotSupportedException(string.Concat("Requested checksum method ", method, " is not supported"));
+ }
+ }
+
+
+ //-------------------------------------------------
+ ///
+ /// Custom stream for hash algorithm that only implements
+ /// Read & Dispose methods (every other method will
+ /// throw NotSupportedException). This class implements
+ /// a bridge pattern (both Read and Dispose are forwarded
+ /// to the underlying "tracked" stream). The only extra
+ /// functionality is that if Read operation read another
+ /// megabyte of data since last progress notifier invocation,
+ /// progress percentage is recomputed and progress notifier
+ /// is called with this value. This allows us to track
+ /// progress of hash computation even though HashAlgorithm
+ /// does not support this.
+ ///
+ private sealed class TrackingStream : Stream
+ {
+ public TrackingStream(Stream trackedStream, Action progressNotifier)
+ {
+ if (trackedStream == null) { throw new ArgumentNullException("trackedStream"); }
+ this._trackedStream = trackedStream;
+ this._progressNotifier = progressNotifier;
+ }
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+ if (disposing)
+ {
+ this._trackedStream.Dispose();
+ }
+ }
+ public override void Flush()
+ {
+ throw new NotImplementedException();
+ }
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ throw new NotImplementedException();
+ }
+ public override void SetLength(long value)
+ {
+ throw new NotImplementedException();
+ }
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ var ret = this._trackedStream.Read(buffer, offset, count);
+
+ if (Checksum.Cancel)
+ {
+ throw new ThreadInterruptedException();
+ }
+ if (this._progressNotifier != null)
+ {
+ var position = this._trackedStream.Position;
+ const long mb = 2L << 19;
+ var megabyte = position / mb;
+ if (megabyte > this._lastMegabyte)
+ {
+ this._lastMegabyte = megabyte;
+ var percentage = ((decimal)position / this._trackedStream.Length);
+ this._progressNotifier(percentage);
+ }
+ }
+ return ret;
+ }
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ throw new NotImplementedException();
+ }
+ public override bool CanRead
+ {
+ get { throw new NotImplementedException(); }
+ }
+ public override bool CanSeek
+ {
+ get { throw new NotImplementedException(); }
+ }
+ public override bool CanWrite
+ {
+ get { throw new NotImplementedException(); }
+ }
+ public override long Length
+ {
+ get { throw new NotImplementedException(); }
+ }
+ public override long Position
+ {
+ get { throw new NotImplementedException(); }
+ set { throw new NotImplementedException(); }
+ }
+
+ private long _lastMegabyte;
+ private readonly Action _progressNotifier;
+ private readonly Stream _trackedStream;
+
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/Backup/Gardian.Utilities.ChecksumValidator/ChecksumMethod.cs b/src/Backup/Gardian.Utilities.ChecksumValidator/ChecksumMethod.cs
new file mode 100644
index 0000000..53f5e8f
--- /dev/null
+++ b/src/Backup/Gardian.Utilities.ChecksumValidator/ChecksumMethod.cs
@@ -0,0 +1,17 @@
+namespace Gardian.Utilities.ChecksumValidator
+{
+
+ ///
+ /// Define supported checksum (hash) methods.
+ ///
+ internal enum ChecksumMethod
+ {
+ // ReSharper disable InconsistentNaming
+ None = 0,
+ SHA1,
+ MD5,
+ CRC32,
+ // ReSharper restore InconsistentNaming
+ }
+
+}
diff --git a/src/Backup/Gardian.Utilities.ChecksumValidator/ColorPulser.cs b/src/Backup/Gardian.Utilities.ChecksumValidator/ColorPulser.cs
new file mode 100644
index 0000000..c8b72d7
--- /dev/null
+++ b/src/Backup/Gardian.Utilities.ChecksumValidator/ColorPulser.cs
@@ -0,0 +1,146 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace Gardian.Utilities.ChecksumValidator
+{
+
+ ///
+ ///
+ internal sealed class ColorPulser
+ {
+
+ //**************************************************
+ //* Construction & destruction
+ //**************************************************
+
+ //-------------------------------------------------
+ ///
+ /// Create new instance of color pulser, initializing
+ /// the color-transitions table.
+ ///
+ public ColorPulser(Timer timer, List