diff --git a/BizHawk.Emulation.DiscSystem/DiscHasher.cs b/BizHawk.Emulation.DiscSystem/DiscHasher.cs index 82ef7d93f8..e553effbe9 100644 --- a/BizHawk.Emulation.DiscSystem/DiscHasher.cs +++ b/BizHawk.Emulation.DiscSystem/DiscHasher.cs @@ -13,55 +13,6 @@ namespace BizHawk.Emulation.DiscSystem Disc disc; - class SpecialCRC32 - { - private static readonly uint[] CRC32Table; - - static SpecialCRC32() - { - CRC32Table = new uint[256]; - for (uint i = 0; i < 256; ++i) - { - uint crc = i; - for (int j = 8; j > 0; --j) - { - if ((crc & 1) == 1) - crc = ((crc >> 1) ^ 0xEDB88320); - else - crc >>= 1; - } - CRC32Table[i] = crc; - } - } - - uint current = 0xFFFFFFFF; - public unsafe void Add(byte[] data, int offset, int size) - { - if (offset + size > data.Length) - throw new ArgumentOutOfRangeException(); - if(offset<0) - throw new ArgumentOutOfRangeException(); - fixed(byte* pData = data) - for (int i = 0; i < size; i++) - { - byte b = pData[offset + i]; - current = CRC32Table[(current ^ b) & 0xFF] ^ (current>>8); - } - } - - byte[] smallbuf = new byte[8]; - public void Add(int data) - { - smallbuf[0] = (byte)((data) & 0xFF); - smallbuf[1] = (byte)((data >> 8) & 0xFF); - smallbuf[2] = (byte)((data >> 16) & 0xFF); - smallbuf[3] = (byte)((data >> 24) & 0xFF); - Add(smallbuf, 0, 4); - } - - public uint Result { get { return current ^ 0xFFFFFFFF; } } - } - /// /// calculates the hash for quick PSX Disc identification /// @@ -73,7 +24,7 @@ namespace BizHawk.Emulation.DiscSystem //the TOC isn't needed! //but it will help detect dumps with mangled TOCs which are all too common // - //a special CRC32 is used to help us match redump's DB elsewhere + //a possibly special CRC32 is used to help us match redump's DB elsewhere SpecialCRC32 crc = new SpecialCRC32(); byte[] buffer2352 = new byte[2352]; @@ -144,5 +95,151 @@ namespace BizHawk.Emulation.DiscSystem } return "no data track found"; } + + /// + /// A stateful special CRC32 calculator + /// This may be absolutely standard and not special at all. I don't know, there were some differences between it and other CRC code I found in bizhawk + /// + public class SpecialCRC32 + { + private static readonly uint[] CRC32Table; + + static SpecialCRC32() + { + CRC32Table = new uint[256]; + for (uint i = 0; i < 256; ++i) + { + uint crc = i; + for (int j = 8; j > 0; --j) + { + if ((crc & 1) == 1) + crc = ((crc >> 1) ^ 0xEDB88320); + else + crc >>= 1; + } + CRC32Table[i] = crc; + } + } + + uint current = 0xFFFFFFFF; + public unsafe void Add(byte[] data, int offset, int size) + { + if (offset + size > data.Length) + throw new ArgumentOutOfRangeException(); + if (offset < 0) + throw new ArgumentOutOfRangeException(); + fixed (byte* pData = data) + for (int i = 0; i < size; i++) + { + byte b = pData[offset + i]; + current = CRC32Table[(current ^ b) & 0xFF] ^ (current >> 8); + } + } + + byte[] smallbuf = new byte[8]; + public void Add(int data) + { + smallbuf[0] = (byte)((data) & 0xFF); + smallbuf[1] = (byte)((data >> 8) & 0xFF); + smallbuf[2] = (byte)((data >> 16) & 0xFF); + smallbuf[3] = (byte)((data >> 24) & 0xFF); + Add(smallbuf, 0, 4); + } + + /// + /// The negated output (the typical result of the CRC calculation) + /// + public uint Result { get { return current ^ 0xFFFFFFFF; } } + + /// + /// The raw non-negated output + /// + public uint Current { get { return current; } set { current = value; } } + + uint gf2_matrix_times(uint[] mat, uint vec) + { + int matIdx = 0; + uint sum = 0; + while (vec != 0) + { + if ((vec & 1) != 0) + sum ^= mat[matIdx]; + vec >>= 1; + matIdx++; + } + return sum; + } + + void gf2_matrix_square(uint[] square, uint[] mat) + { + int n; + for (n = 0; n < 32; n++) + square[n] = gf2_matrix_times(mat, mat[n]); + } + + /// + /// Incorporates a pre-calculated CRC with the given length by combining crcs + /// It's a bit flaky, so be careful, but it works + /// + public void Incorporate(uint crc, int len) + { + current = crc32_combine(current, crc, len); + } + + //tables used by crc32_combine + uint[] even, odd; + + //algorithm from zlib's crc32_combine. read http://www.leapsecond.com/tools/crcomb.c for more + uint crc32_combine(uint crc1, uint crc2, int len2) + { + if (even == null) even = new uint[32]; // even-power-of-two zeros operator + if (odd == null) odd = new uint[32]; // odd-power-of-two zeros operator + + // degenerate case + if (len2 == 0) + return crc1; + + // put operator for one zero bit in odd + odd[0] = 0xedb88320; //CRC-32 polynomial + uint row = 1; + for (int n = 1; n < 32; n++) + { + odd[n] = row; + row <<= 1; + } + + //put operator for two zero bits in even + gf2_matrix_square(even, odd); + + //put operator for four zero bits in odd + gf2_matrix_square(odd, even); + + //apply len2 zeros to crc1 (first square will put the operator for one zero byte, eight zero bits, in even) + do + { + //apply zeros operator for this bit of len2 + gf2_matrix_square(even, odd); + if ((len2 & 1) != 0) + crc1 = gf2_matrix_times(even, crc1); + len2 >>= 1; + + //if no more bits set, then done + if (len2 == 0) + break; + + //another iteration of the loop with odd and even swapped + gf2_matrix_square(odd, even); + if ((len2 & 1) != 0) + crc1 = gf2_matrix_times(odd, crc1); + len2 >>= 1; + + //if no more bits set, then done + } while (len2 != 0); + + //return combined crc + crc1 ^= crc2; + return crc1; + } + } } } \ No newline at end of file