diff options
author | Alexey 'Cluster' Avdyukhin <clusterrr@clusterrr.com> | 2022-11-08 09:53:44 +0300 |
---|---|---|
committer | Alexey 'Cluster' Avdyukhin <clusterrr@clusterrr.com> | 2022-11-08 09:53:44 +0300 |
commit | 0acfff00771d0f944db0c91ebd366e0cb3d01591 (patch) | |
tree | b4e4dc362615d1b735b8897100d4c83d1274747c | |
parent | 530e8fa3a1ff5eb0614e7a54d5b2df66fd8eed26 (diff) |
Padding fix, checksum fix
-rw-r--r-- | Crc32.cs | 65 | ||||
-rw-r--r-- | Crc32Calculator.cs | 60 | ||||
-rw-r--r-- | NesFile.cs | 47 | ||||
-rw-r--r-- | UnifFile.cs | 52 |
4 files changed, 145 insertions, 79 deletions
diff --git a/Crc32.cs b/Crc32.cs new file mode 100644 index 0000000..33acf83 --- /dev/null +++ b/Crc32.cs @@ -0,0 +1,65 @@ +using System; +using System.Security.Cryptography; + +namespace com.clusterrr.Famicom.Containers +{ + /// <summary> + /// CRC32 calculator + /// </summary> + internal class Crc32 : HashAlgorithm + { + private readonly uint[] table = new uint[256]; + private uint crc = 0xFFFFFFFF; + + public Crc32() + { + // Calculate table + uint poly = 0xedb88320; + uint temp; + for (uint i = 0; i < table.Length; ++i) + { + temp = i; + for (int j = 8; j > 0; --j) + { + if ((temp & 1) == 1) + { + temp = (uint)((temp >> 1) ^ poly); + } + else + { + temp >>= 1; + } + } + table[i] = temp; + } + } + + public override void Initialize() + { + } + + public override bool CanReuseTransform => false; + + protected override void HashCore(byte[] array, int ibStart, int cbSize) + { + while (cbSize > 0) + { + var pos = ibStart; + byte index = (byte)(((crc) & 0xff) ^ array[pos]); + crc = (crc >> 8) ^ table[index]; + ibStart++; + cbSize--; + } + } + + protected override byte[] HashFinal() + { + return BitConverter.GetBytes(~crc); + } + + public override bool CanTransformMultipleBlocks => true; + public override byte[] Hash => BitConverter.GetBytes(~crc); + public override int HashSize => 32; + + } +} diff --git a/Crc32Calculator.cs b/Crc32Calculator.cs deleted file mode 100644 index 3c6e0c7..0000000 --- a/Crc32Calculator.cs +++ /dev/null @@ -1,60 +0,0 @@ -namespace com.clusterrr.Famicom.Containers
-{
- /// <summary>
- /// CRC32 calculator
- /// </summary>
- internal static class Crc32Calculator
- {
- static readonly uint[] table = new uint[256];
-
- static Crc32Calculator()
- {
- // Calculate table
- uint poly = 0xedb88320;
- uint temp;
- for (uint i = 0; i < table.Length; ++i)
- {
- temp = i;
- for (int j = 8; j > 0; --j)
- {
- if ((temp & 1) == 1)
- {
- temp = (uint)((temp >> 1) ^ poly);
- }
- else
- {
- temp >>= 1;
- }
- }
- table[i] = temp;
- }
- }
-
- /// <summary>
- /// Calculate CRC32 checksum
- /// </summary>
- /// <param name="data">Input data</param>
- /// <param name="offset">Data offset</param>
- /// <param name="length">Length</param>
- /// <returns>CRC32 checksum</returns>
- public static uint CalculateCRC32(byte[] data, int offset, int length)
- {
- // Calculate CRC32
- uint crc = 0xffffffff;
- for (int i = offset; length > 0; i++, length--)
- {
- byte index = (byte)(((crc) & 0xff) ^ data[i]);
- crc = (crc >> 8) ^ table[index];
- }
- return ~crc;
- }
-
- /// <summary>
- /// Calculate CRC32 checksum
- /// </summary>
- /// <param name="data">Input data</param>
- /// <returns>CRC32 checksum</returns>
- public static uint CalculateCRC32(byte[] data)
- => CalculateCRC32(data, 0, data.Length);
- }
-}
@@ -752,7 +752,9 @@ namespace com.clusterrr.Famicom.Containers if (trainer.Length < 512) data.AddRange(Enumerable.Repeat<byte>(0xFF, (int)512 - trainer.Length));
}
data.AddRange(prg);
+ data.AddRange(Enumerable.Repeat<byte>(byte.MaxValue, (int)prgSizePadded - prg.Length));
data.AddRange(chr);
+ data.AddRange(Enumerable.Repeat<byte>(byte.MaxValue, (int)chrSizePadded - chr.Length));
}
else if (Version == iNesVersion.NES20)
{
@@ -846,12 +848,12 @@ namespace com.clusterrr.Famicom.Containers if (trainer.Length > 0)
{
data.AddRange(trainer);
- if (trainer.Length < 512) data.AddRange(Enumerable.Repeat<byte>(0xFF, (int)512 - trainer.Length));
+ if (trainer.Length < 512) data.AddRange(Enumerable.Repeat(byte.MaxValue, (int)512 - trainer.Length));
}
data.AddRange(prg);
- data.AddRange(Enumerable.Repeat<byte>(0xFF, (int)prgSizePadded - prg.Length));
+ data.AddRange(Enumerable.Repeat(byte.MaxValue, (int)prgSizePadded - prg.Length));
data.AddRange(chr);
- data.AddRange(Enumerable.Repeat<byte>(0xFF, (int)chrSizePadded - chr.Length));
+ data.AddRange(Enumerable.Repeat(byte.MaxValue, (int)chrSizePadded - chr.Length));
if (MiscellaneousROMsCount > 0 || miscellaneousROM.Length > 0)
{
if (MiscellaneousROMsCount == 0)
@@ -929,15 +931,46 @@ namespace com.clusterrr.Famicom.Containers /// <returns>MD5 checksum for all PRG and CHR data</returns>
public byte[] CalculateMD5()
{
- var md5 = MD5.Create();
- var alldata = Enumerable.Concat(prg ?? Array.Empty<byte>(), chr ?? Array.Empty<byte>()).ToArray();
- return md5.ComputeHash(alldata);
+ int prgSizeUpPow2 = 1;
+ int chrSizeUpPow2 = 1;
+ while (prgSizeUpPow2 < PRG.Length) prgSizeUpPow2 *= 2;
+ if (CHR.Length == 0)
+ chrSizeUpPow2 = 0;
+ else
+ while (chrSizeUpPow2 < CHR.Length) chrSizeUpPow2 *= 2;
+ using (var md5 = MD5.Create())
+ {
+ md5.TransformBlock(prg, 0, prg.Length, null, 0);
+ md5.TransformBlock(Enumerable.Repeat<byte>(byte.MaxValue, prgSizeUpPow2 - prg.Length).ToArray(), 0, prgSizeUpPow2 - prg.Length, null, 0);
+ md5.TransformBlock(chr, 0, chr.Length, null, 0);
+ md5.TransformBlock(Enumerable.Repeat<byte>(byte.MaxValue, chrSizeUpPow2 - chr.Length).ToArray(), 0, chrSizeUpPow2 - chr.Length, null, 0);
+ md5.TransformFinalBlock(new byte[0], 0, 0);
+ return md5.Hash;
+ }
}
/// <summary>
/// Calculate CRC32 checksum of ROM (CHR+PRG without header)
/// </summary>
/// <returns>CRC32 checksum for all PRG and CHR data</returns>
- public uint CalculateCRC32() => Crc32Calculator.CalculateCRC32(Enumerable.Concat(prg ?? Array.Empty<byte>(), chr ?? Array.Empty<byte>()).ToArray());
+ public uint CalculateCRC32()
+ {
+ int prgSizeUpPow2 = 1;
+ int chrSizeUpPow2 = 1;
+ while (prgSizeUpPow2 < PRG.Length) prgSizeUpPow2 *= 2;
+ if (CHR.Length == 0)
+ chrSizeUpPow2 = 0;
+ else
+ while (chrSizeUpPow2 < CHR.Length) chrSizeUpPow2 *= 2;
+ using (var crc32 = new Crc32())
+ {
+ crc32.TransformBlock(prg, 0, prg.Length, null, 0);
+ crc32.TransformBlock(Enumerable.Repeat<byte>(byte.MaxValue, prgSizeUpPow2 - prg.Length).ToArray(), 0, prgSizeUpPow2 - prg.Length, null, 0);
+ crc32.TransformBlock(chr, 0, chr.Length, null, 0);
+ crc32.TransformBlock(Enumerable.Repeat<byte>(byte.MaxValue, chrSizeUpPow2 - chr.Length).ToArray(), 0, chrSizeUpPow2 - chr.Length, null, 0);
+ crc32.TransformFinalBlock(new byte[0], 0, 0);
+ return BitConverter.ToUInt32(crc32.Hash, 0);
+ }
+ }
}
}
diff --git a/UnifFile.cs b/UnifFile.cs index a39e58d..0a0f33c 100644 --- a/UnifFile.cs +++ b/UnifFile.cs @@ -610,14 +610,16 @@ namespace com.clusterrr.Famicom.Containers foreach (var kv in this.Where(kv => kv.Key.StartsWith(PREFIX_PRG)))
{
var num = kv.Key[3];
- var crc32 = Crc32Calculator.CalculateCRC32(kv.Value);
- this[$"PCK{num}"] = BitConverter.GetBytes(crc32);
+ using var crc32 = new Crc32();
+ var crc32sum = crc32.ComputeHash(kv.Value);
+ this[$"PCK{num}"] = crc32sum;
}
foreach (var kv in this.Where(kv => kv.Key.StartsWith(PREFIX_CHR)))
{
var num = kv.Key[3];
- var crc32 = Crc32Calculator.CalculateCRC32(kv.Value);
- this[$"CCK{num}"] = BitConverter.GetBytes(crc32);
+ using var crc32 = new Crc32();
+ var crc32sum = crc32.ComputeHash(kv.Value);
+ this[$"CCK{num}"] = crc32sum;
}
}
@@ -627,10 +629,22 @@ namespace com.clusterrr.Famicom.Containers /// <returns>MD5 checksum for all PRG and CHR data</returns>
public byte[] CalculateMD5()
{
- var md5 = MD5.Create();
- var alldata = Enumerable.Concat(fields.Where(k => k.Key.StartsWith(PREFIX_PRG)).OrderBy(k => k.Key).SelectMany(i => i.Value),
- fields.Where(k => k.Key.StartsWith(PREFIX_CHR)).OrderBy(k => k.Key).SelectMany(i => i.Value)).ToArray();
- return md5.ComputeHash(alldata);
+ using var md5 = MD5.Create();
+ foreach(var kv in fields.Where(k => k.Key.StartsWith(PREFIX_PRG)).OrderBy(k => k.Key)
+ .Concat(fields.Where(k => k.Key.StartsWith(PREFIX_CHR)).OrderBy(k => k.Key)))
+ {
+ var v = kv.Value;
+ int sizeUpPow2 = 0;
+ if (v.Length > 0)
+ {
+ sizeUpPow2 = 1;
+ while (sizeUpPow2 < v.Length) sizeUpPow2 *= 2;
+ }
+ md5.TransformBlock(v, 0, v.Length, null, 0);
+ md5.TransformBlock(Enumerable.Repeat<byte>(byte.MaxValue, sizeUpPow2 - v.Length).ToArray(), 0, sizeUpPow2 - v.Length, null, 0);
+ }
+ md5.TransformFinalBlock(new byte[0], 0, 0);
+ return md5.Hash;
}
/// <summary>
@@ -638,10 +652,24 @@ namespace com.clusterrr.Famicom.Containers /// </summary>
/// <returns>CRC32 checksum for all PRG and CHR data</returns>
public uint CalculateCRC32()
- => Crc32Calculator.CalculateCRC32(
- Enumerable.Concat(fields.Where(k => k.Key.StartsWith(PREFIX_PRG)).OrderBy(k => k.Key).SelectMany(i => i.Value),
- fields.Where(k => k.Key.StartsWith(PREFIX_CHR)).OrderBy(k => k.Key).SelectMany(i => i.Value)).ToArray()
- );
+ {
+ using var crc32 = new Crc32();
+ foreach (var kv in fields.Where(k => k.Key.StartsWith(PREFIX_PRG)).OrderBy(k => k.Key)
+ .Concat(fields.Where(k => k.Key.StartsWith(PREFIX_CHR)).OrderBy(k => k.Key)))
+ {
+ var v = kv.Value;
+ int sizeUpPow2 = 0;
+ if (v.Length > 0)
+ {
+ sizeUpPow2 = 1;
+ while (sizeUpPow2 < v.Length) sizeUpPow2 *= 2;
+ }
+ crc32.TransformBlock(v, 0, v.Length, null, 0);
+ crc32.TransformBlock(Enumerable.Repeat<byte>(byte.MaxValue, sizeUpPow2 - v.Length).ToArray(), 0, sizeUpPow2 - v.Length, null, 0);
+ }
+ crc32.TransformFinalBlock(new byte[0], 0, 0);
+ return BitConverter.ToUInt32(crc32.Hash, 0);
+ }
/// <summary>
/// Default game controller(s)
|