diff --git a/MPF.Library/Data/Enumerations.cs b/MPF.Library/Data/Enumerations.cs
index 4aa51422..72c216d6 100644
--- a/MPF.Library/Data/Enumerations.cs
+++ b/MPF.Library/Data/Enumerations.cs
@@ -2,6 +2,24 @@
namespace MPF.Data
{
+ ///
+ /// Available hashing types
+ ///
+ [Flags]
+ public enum Hash
+ {
+ CRC = 1 << 0,
+ MD5 = 1 << 1,
+ SHA1 = 1 << 2,
+ SHA256 = 1 << 3,
+ SHA384 = 1 << 4,
+ SHA512 = 1 << 5,
+
+ // Special combinations
+ Standard = CRC | MD5 | SHA1,
+ All = CRC | MD5 | SHA1 | SHA256 | SHA384 | SHA512,
+ }
+
///
/// Drive type for dumping
///
diff --git a/MPF.Library/Utilities/Hasher.cs b/MPF.Library/Utilities/Hasher.cs
new file mode 100644
index 00000000..133674c8
--- /dev/null
+++ b/MPF.Library/Utilities/Hasher.cs
@@ -0,0 +1,125 @@
+using System;
+using System.Linq;
+using System.Security.Cryptography;
+using MPF.Data;
+using OptimizedCRC;
+
+namespace MPF.Utilities
+{
+ ///
+ /// Async hashing class wraper
+ ///
+ public class Hasher
+ {
+ public Hash HashType { get; private set; }
+ private IDisposable _hasher;
+
+ public Hasher(Hash hashType)
+ {
+ this.HashType = hashType;
+ GetHasher();
+ }
+
+ ///
+ /// Generate the correct hashing class based on the hash type
+ ///
+ private void GetHasher()
+ {
+ switch (HashType)
+ {
+ case Hash.CRC:
+ _hasher = new OptimizedCRC.OptimizedCRC();
+ break;
+
+ case Hash.MD5:
+ _hasher = MD5.Create();
+ break;
+
+ case Hash.SHA1:
+ _hasher = SHA1.Create();
+ break;
+
+ case Hash.SHA256:
+ _hasher = SHA256.Create();
+ break;
+
+ case Hash.SHA384:
+ _hasher = SHA384.Create();
+ break;
+
+ case Hash.SHA512:
+ _hasher = SHA512.Create();
+ break;
+ }
+ }
+
+ public void Dispose()
+ {
+ _hasher.Dispose();
+ }
+
+ ///
+ /// Process a buffer of some length with the internal hash algorithm
+ ///
+ public void Process(byte[] buffer, int size)
+ {
+ switch (HashType)
+ {
+ case Hash.CRC:
+ (_hasher as OptimizedCRC.OptimizedCRC).Update(buffer, 0, size);
+ break;
+
+ case Hash.MD5:
+ case Hash.SHA1:
+ case Hash.SHA256:
+ case Hash.SHA384:
+ case Hash.SHA512:
+ (_hasher as HashAlgorithm).TransformBlock(buffer, 0, size, null, 0);
+ break;
+ }
+ }
+
+ ///
+ /// Finalize the internal hash algorigthm
+ ///
+ public void Finalize()
+ {
+ byte[] emptyBuffer = new byte[0];
+ switch (HashType)
+ {
+ case Hash.CRC:
+ (_hasher as OptimizedCRC.OptimizedCRC).Update(emptyBuffer, 0, 0);
+ break;
+
+ case Hash.MD5:
+ case Hash.SHA1:
+ case Hash.SHA256:
+ case Hash.SHA384:
+ case Hash.SHA512:
+ (_hasher as HashAlgorithm).TransformFinalBlock(emptyBuffer, 0, 0);
+ break;
+ }
+ }
+
+ ///
+ /// Get internal hash as a byte array
+ ///
+ public byte[] GetHash()
+ {
+ switch (HashType)
+ {
+ case Hash.CRC:
+ return BitConverter.GetBytes((_hasher as OptimizedCRC.OptimizedCRC).Value).Reverse().ToArray();
+
+ case Hash.MD5:
+ case Hash.SHA1:
+ case Hash.SHA256:
+ case Hash.SHA384:
+ case Hash.SHA512:
+ return (_hasher as HashAlgorithm).Hash;
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/MPF.Library/Utilities/OptimizedCRC.cs b/MPF.Library/Utilities/OptimizedCRC.cs
new file mode 100644
index 00000000..160a7757
--- /dev/null
+++ b/MPF.Library/Utilities/OptimizedCRC.cs
@@ -0,0 +1,153 @@
+/*
+
+ Copyright (c) 2012-2015 Eugene Larchenko (spct@mail.ru)
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+
+*/
+
+using System;
+
+namespace OptimizedCRC
+{
+ internal class OptimizedCRC : IDisposable
+ {
+ private const uint kCrcPoly = 0xEDB88320;
+ private const uint kInitial = 0xFFFFFFFF;
+ private const int CRC_NUM_TABLES = 8;
+ private static readonly uint[] Table;
+
+ static OptimizedCRC()
+ {
+ unchecked
+ {
+ Table = new uint[256 * CRC_NUM_TABLES];
+ int i;
+ for (i = 0; i < 256; i++)
+ {
+ uint r = (uint)i;
+ for (int j = 0; j < 8; j++)
+ {
+ r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1));
+ }
+ Table[i] = r;
+ }
+ for (; i < 256 * CRC_NUM_TABLES; i++)
+ {
+ uint r = Table[i - 256];
+ Table[i] = Table[r & 0xFF] ^ (r >> 8);
+ }
+ }
+ }
+
+ public uint UnsignedValue;
+
+ public OptimizedCRC()
+ {
+ Init();
+ }
+
+ ///
+ /// Reset CRC
+ ///
+ public void Init()
+ {
+ UnsignedValue = kInitial;
+ }
+
+ public int Value
+ {
+ get { return (int)~UnsignedValue; }
+ }
+
+ public void Update(byte[] data, int offset, int count)
+ {
+ new ArraySegment(data, offset, count); // check arguments
+ if (count == 0)
+ {
+ return;
+ }
+
+ var table = OptimizedCRC.Table;
+
+ uint crc = UnsignedValue;
+
+ for (; (offset & 7) != 0 && count != 0; count--)
+ {
+ crc = (crc >> 8) ^ table[(byte)crc ^ data[offset++]];
+ }
+
+ if (count >= 8)
+ {
+ /*
+ * Idea from 7-zip project sources (http://7-zip.org/sdk.html)
+ */
+
+ int end = (count - 8) & ~7;
+ count -= end;
+ end += offset;
+
+ while (offset != end)
+ {
+ crc ^= (uint)(data[offset] + (data[offset + 1] << 8) + (data[offset + 2] << 16) + (data[offset + 3] << 24));
+ uint high = (uint)(data[offset + 4] + (data[offset + 5] << 8) + (data[offset + 6] << 16) + (data[offset + 7] << 24));
+ offset += 8;
+
+ crc = table[(byte)crc + 0x700]
+ ^ table[(byte)(crc >>= 8) + 0x600]
+ ^ table[(byte)(crc >>= 8) + 0x500]
+ ^ table[/*(byte)*/(crc >> 8) + 0x400]
+ ^ table[(byte)(high) + 0x300]
+ ^ table[(byte)(high >>= 8) + 0x200]
+ ^ table[(byte)(high >>= 8) + 0x100]
+ ^ table[/*(byte)*/(high >> 8) + 0x000];
+ }
+ }
+
+ while (count-- != 0)
+ {
+ crc = (crc >> 8) ^ table[(byte)crc ^ data[offset++]];
+ }
+
+ UnsignedValue = crc;
+ }
+
+ static public int Compute(byte[] data, int offset, int count)
+ {
+ var crc = new OptimizedCRC();
+ crc.Update(data, offset, count);
+ return crc.Value;
+ }
+
+ static public int Compute(byte[] data)
+ {
+ return Compute(data, 0, data.Length);
+ }
+
+ static public int Compute(ArraySegment block)
+ {
+ return Compute(block.Array, block.Offset, block.Count);
+ }
+
+ public void Dispose()
+ {
+ UnsignedValue = 0;
+ }
+ }
+}
\ No newline at end of file