diff --git a/DATFromDir/CRC32.cs b/DATFromDir/CRC32.cs
new file mode 100644
index 00000000..4cd9741c
--- /dev/null
+++ b/DATFromDir/CRC32.cs
@@ -0,0 +1,119 @@
+// Copyright (c) Damien Guard. All rights reserved.
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+// Originally published at http://damieng.com/blog/2006/08/08/calculating_crc32_in_c_and_net
+
+using System;
+using System.Collections.Generic;
+using System.Security.Cryptography;
+
+namespace DamienG.Security.Cryptography
+{
+ ///
+ /// Implements a 32-bit CRC hash algorithm compatible with Zip etc.
+ ///
+ ///
+ /// Crc32 should only be used for backward compatibility with older file formats
+ /// and algorithms. It is not secure enough for new applications.
+ /// If you need to call multiple times for the same data either use the HashAlgorithm
+ /// interface or remember that the result of one Compute call needs to be ~ (XOR) before
+ /// being passed in as the seed for the next Compute call.
+ ///
+ public sealed class Crc32 : HashAlgorithm
+ {
+ public const UInt32 DefaultPolynomial = 0xedb88320u;
+ public const UInt32 DefaultSeed = 0xffffffffu;
+
+ static UInt32[] defaultTable;
+
+ readonly UInt32 seed;
+ readonly UInt32[] table;
+ UInt32 hash;
+
+ public Crc32()
+ : this(DefaultPolynomial, DefaultSeed)
+ {
+ }
+
+ public Crc32(UInt32 polynomial, UInt32 seed)
+ {
+ table = InitializeTable(polynomial);
+ this.seed = hash = seed;
+ }
+
+ public override void Initialize()
+ {
+ hash = seed;
+ }
+
+ protected override void HashCore(byte[] array, int ibStart, int cbSize)
+ {
+ hash = CalculateHash(table, hash, array, ibStart, cbSize);
+ }
+
+ protected override byte[] HashFinal()
+ {
+ var hashBuffer = UInt32ToBigEndianBytes(~hash);
+ HashValue = hashBuffer;
+ return hashBuffer;
+ }
+
+ public override int HashSize { get { return 32; } }
+
+ public static UInt32 Compute(byte[] buffer)
+ {
+ return Compute(DefaultSeed, buffer);
+ }
+
+ public static UInt32 Compute(UInt32 seed, byte[] buffer)
+ {
+ return Compute(DefaultPolynomial, seed, buffer);
+ }
+
+ public static UInt32 Compute(UInt32 polynomial, UInt32 seed, byte[] buffer)
+ {
+ return ~CalculateHash(InitializeTable(polynomial), seed, buffer, 0, buffer.Length);
+ }
+
+ static UInt32[] InitializeTable(UInt32 polynomial)
+ {
+ if (polynomial == DefaultPolynomial && defaultTable != null)
+ return defaultTable;
+
+ var createTable = new UInt32[256];
+ for (var i = 0; i < 256; i++)
+ {
+ var entry = (UInt32)i;
+ for (var 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;
+ }
+
+ static UInt32 CalculateHash(UInt32[] table, UInt32 seed, IList buffer, int start, int size)
+ {
+ var crc = seed;
+ for (var i = start; i < size - start; i++)
+ crc = (crc >> 8) ^ table[buffer[i] ^ crc & 0xff];
+ return crc;
+ }
+
+ static byte[] UInt32ToBigEndianBytes(UInt32 uint32)
+ {
+ var result = BitConverter.GetBytes(uint32);
+
+ if (BitConverter.IsLittleEndian)
+ Array.Reverse(result);
+
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/DATFromDir/DATFromDir.cs b/DATFromDir/DATFromDir.cs
index 6b3683fa..bd2e9d54 100644
--- a/DATFromDir/DATFromDir.cs
+++ b/DATFromDir/DATFromDir.cs
@@ -8,6 +8,8 @@ using System.Text;
using System.Threading.Tasks;
using System.Web;
+using DamienG.Security.Cryptography;
+
namespace SabreTools
{
class DATFromDir
@@ -37,7 +39,7 @@ namespace SabreTools
};
// Get an output array going that has the right mappings (parent, name, size, hash)
- List> roms = new List>();
+ List> roms = new List>();
// This is where the main loop would go
foreach (string item in Directory.GetFiles(_basePath, "*", SearchOption.AllDirectories))
@@ -51,7 +53,9 @@ namespace SabreTools
bool encounteredErrors = zip.StandardError.ReadToEnd().Contains("ERROR");
- // Get a list of files including size and hash
+ // Get a list of files including size and hashes
+ Crc32 crc = new Crc32();
+ MD5 md5 = MD5.Create();
SHA1 sha1 = SHA1.Create();
// If the file was an archive and was extracted successfully, check it
@@ -59,41 +63,83 @@ namespace SabreTools
{
foreach (string entry in Directory.GetFiles(_tempDir, "*", SearchOption.AllDirectories))
{
- FileStream fs = File.OpenRead(entry);
- string tempHash = BitConverter.ToString(sha1.ComputeHash(fs)).Replace("-", "");
- fs.Close();
+ string fileCRC = String.Empty;
+ string fileMD5 = String.Empty;
+ string fileSHA1 = String.Empty;
- roms.Add(new Tuple(
+ try
+ {
+ using (FileStream fs = File.Open(entry, FileMode.Open))
+ {
+ foreach (byte b in crc.ComputeHash(fs))
+ {
+ fileCRC += b.ToString("x2").ToLower();
+ }
+ }
+ using (FileStream fs = File.Open(entry, FileMode.Open))
+ {
+ fileMD5 = BitConverter.ToString(md5.ComputeHash(fs)).Replace("-", "");
+ }
+ using (FileStream fs = File.Open(entry, FileMode.Open))
+ {
+ fileSHA1 = BitConverter.ToString(sha1.ComputeHash(fs)).Replace("-", "");
+ }
+ }
+ catch (IOException)
+ {
+ continue;
+ }
+
+ roms.Add(new Tuple(
Path.GetFileNameWithoutExtension(item),
entry.Remove(0, _tempDir.Length),
(new FileInfo(entry)).Length,
- tempHash));
+ fileCRC,
+ fileMD5,
+ fileSHA1));
Console.WriteLine("File parsed: " + entry.Remove(0, _tempDir.Length));
}
}
// Otherwise, just get the info on the file itself
- else if (!Directory.Exists(item))
+ else if (!Directory.Exists(item) && File.Exists(item))
{
- FileStream fs;
+ string fileCRC = String.Empty;
+ string fileMD5 = String.Empty;
+ string fileSHA1 = String.Empty;
+
try
{
- fs = File.OpenRead(item);
+ using (FileStream fs = File.Open(item, FileMode.Open))
+ {
+ foreach (byte b in crc.ComputeHash(fs))
+ {
+ fileCRC += b.ToString("x2").ToLower();
+ }
+ }
+ using (FileStream fs = File.Open(item, FileMode.Open))
+ {
+ fileMD5 = BitConverter.ToString(md5.ComputeHash(fs)).Replace("-", "");
+ }
+ using (FileStream fs = File.Open(item, FileMode.Open))
+ {
+ fileSHA1 = BitConverter.ToString(sha1.ComputeHash(fs)).Replace("-", "");
+ }
}
catch (IOException)
{
continue;
}
- string tempHash = BitConverter.ToString(sha1.ComputeHash(fs)).Replace("-", "");
- fs.Close();
string actualpath = Path.GetDirectoryName(item.Remove(0, _basePath.Length)).Replace('/', '\\').Split('\\')[0];
- roms.Add(new Tuple(
+ roms.Add(new Tuple(
(actualpath == "" ? "Default" : actualpath),
item.Remove(0, _basePath.Length).Remove(0, actualpath.Length + (actualpath != "" ? 1 : 0)),
(new FileInfo(item)).Length,
- tempHash));
+ fileCRC,
+ fileMD5,
+ fileSHA1));
Console.WriteLine("File parsed: " + item.Remove(0, _basePath.Length));
}
@@ -106,16 +152,12 @@ namespace SabreTools
}
// Order the roms by name of parent, then name of rom
- roms.Sort(delegate (Tuple A, Tuple B)
+ roms.Sort(delegate (Tuple A, Tuple B)
{
if (A.Item1 == B.Item1)
{
if (A.Item2 == B.Item2)
{
- if (A.Item3 == B.Item3)
- {
- return String.Compare(A.Item4, B.Item4);
- }
return (int)(A.Item3 - B.Item3);
}
return String.Compare(A.Item2, B.Item2);
@@ -152,7 +194,7 @@ namespace SabreTools
// Write out each of the machines and roms
string lastgame = "";
- foreach (Tuple rom in roms)
+ foreach (Tuple rom in roms)
{
string state = "";
if (lastgame != "" && lastgame != rom.Item1)
@@ -168,7 +210,9 @@ namespace SabreTools
state += "\t\t\n";
lastgame = rom.Item1;
diff --git a/DATFromDir/DATFromDir.csproj b/DATFromDir/DATFromDir.csproj
index 14d3469f..b3f1fb3e 100644
--- a/DATFromDir/DATFromDir.csproj
+++ b/DATFromDir/DATFromDir.csproj
@@ -44,6 +44,7 @@
+