Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/ClusterM/nes-containers.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexey 'Cluster' Avdyukhin <clusterrr@clusterrr.com>2022-11-08 09:53:44 +0300
committerAlexey 'Cluster' Avdyukhin <clusterrr@clusterrr.com>2022-11-08 09:53:44 +0300
commit0acfff00771d0f944db0c91ebd366e0cb3d01591 (patch)
treeb4e4dc362615d1b735b8897100d4c83d1274747c
parent530e8fa3a1ff5eb0614e7a54d5b2df66fd8eed26 (diff)
Padding fix, checksum fix
-rw-r--r--Crc32.cs65
-rw-r--r--Crc32Calculator.cs60
-rw-r--r--NesFile.cs47
-rw-r--r--UnifFile.cs52
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);
- }
-}
diff --git a/NesFile.cs b/NesFile.cs
index 94f18b7..ad45117 100644
--- a/NesFile.cs
+++ b/NesFile.cs
@@ -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)