1using System.Buffers.Binary;
6internal static class NdsLegacyCrc32
9 private static readonly uint[] Table = CreateTable();
14 public static uint Calculate(ReadOnlySpan<byte> data) => ~Update(data, uint.MaxValue);
25 public static void ReplacePreservingCrc(
27 ref
int logicalLength,
29 ReadOnlySpan<byte> patch,
30 int? fixOffset =
null)
32 int correctionOffset = fixOffset ?? checked(patchOffset + patch.Length);
33 if (patchOffset < 0 || correctionOffset < patchOffset + patch.Length ||
34 patch.Length > data.Length - patchOffset || 4 > data.Length - correctionOffset)
36 throw new ArgumentOutOfRangeException(nameof(patchOffset),
"The legacy CRC patch or correction word is outside the image buffer.");
39 Span<byte> state = stackalloc
byte[8];
40 uint before = UpdateVirtual(data, patchOffset, correctionOffset - patchOffset, logicalLength, uint.MaxValue);
41 BinaryPrimitives.WriteUInt32LittleEndian(state, before);
42 patch.CopyTo(data[patchOffset..]);
43 logicalLength = Math.Max(logicalLength, checked(patchOffset + patch.Length));
44 uint after = UpdateVirtual(data, patchOffset, correctionOffset - patchOffset + 4, logicalLength, uint.MaxValue);
45 BinaryPrimitives.WriteUInt32LittleEndian(state[4..], after);
46 for (
int index = 4; index >= 1; index--)
48 byte tableIndex = FindHighByte(state[index + 3], out uint tableValue);
49 uint word = BinaryPrimitives.ReadUInt32LittleEndian(state[index..]);
50 BinaryPrimitives.WriteUInt32LittleEndian(state[index..], word ^ tableValue);
51 state[index - 1] ^= tableIndex;
54 state[..4].CopyTo(data[correctionOffset..]);
55 logicalLength = Math.Max(logicalLength, checked(correctionOffset + 4));
62 private static uint Update(ReadOnlySpan<byte> data, uint state)
64 foreach (
byte value
in data)
66 state = (state >> 8) ^ Table[(state ^ value) & 0xFF];
79 private static uint UpdateVirtual(ReadOnlySpan<byte> data,
int offset,
int length,
int logicalLength, uint state)
81 for (
int index = 0; index < length; index++)
83 int position = offset + index;
84 byte value = position < logicalLength ? data[position] :
byte.MaxValue;
85 state = (state >> 8) ^ Table[(state ^ value) & 0xFF];
95 private static byte FindHighByte(
byte highByte, out uint value)
97 for (
int index = 0; index < Table.Length; index++)
99 if ((
byte)(Table[index] >> 24) == highByte)
101 value = Table[index];
106 throw new InvalidOperationException(
"The CRC32 reverse table is incomplete.");
111 private static uint[] CreateTable()
113 var table =
new uint[256];
114 for (uint index = 0; index < table.Length; index++)
117 for (
int bit = 0; bit < 8; bit++)
119 value = (value >> 1) ^ ((value & 1) != 0 ? 0xEDB8_8320u : 0u);
122 table[index] = value;