NdsForge.NET 1.0.1
Read, validate, edit, compare, and build Nintendo DS and DSi images from .NET
Loading...
Searching...
No Matches
NdsLegacyCrc32.cs
1using System.Buffers.Binary;
2
3namespace NdsForge;
4
6internal static class NdsLegacyCrc32
7{
9 private static readonly uint[] Table = CreateTable();
10
14 public static uint Calculate(ReadOnlySpan<byte> data) => ~Update(data, uint.MaxValue);
15
25 public static void ReplacePreservingCrc(
26 Span<byte> data,
27 ref int logicalLength,
28 int patchOffset,
29 ReadOnlySpan<byte> patch,
30 int? fixOffset = null)
31 {
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)
35 {
36 throw new ArgumentOutOfRangeException(nameof(patchOffset), "The legacy CRC patch or correction word is outside the image buffer.");
37 }
38
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--)
47 {
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;
52 }
53
54 state[..4].CopyTo(data[correctionOffset..]);
55 logicalLength = Math.Max(logicalLength, checked(correctionOffset + 4));
56 }
57
62 private static uint Update(ReadOnlySpan<byte> data, uint state)
63 {
64 foreach (byte value in data)
65 {
66 state = (state >> 8) ^ Table[(state ^ value) & 0xFF];
67 }
68
69 return state;
70 }
71
79 private static uint UpdateVirtual(ReadOnlySpan<byte> data, int offset, int length, int logicalLength, uint state)
80 {
81 for (int index = 0; index < length; index++)
82 {
83 int position = offset + index;
84 byte value = position < logicalLength ? data[position] : byte.MaxValue;
85 state = (state >> 8) ^ Table[(state ^ value) & 0xFF];
86 }
87
88 return state;
89 }
90
95 private static byte FindHighByte(byte highByte, out uint value)
96 {
97 for (int index = 0; index < Table.Length; index++)
98 {
99 if ((byte)(Table[index] >> 24) == highByte)
100 {
101 value = Table[index];
102 return (byte)index;
103 }
104 }
105
106 throw new InvalidOperationException("The CRC32 reverse table is incomplete.");
107 }
108
111 private static uint[] CreateTable()
112 {
113 var table = new uint[256];
114 for (uint index = 0; index < table.Length; index++)
115 {
116 uint value = index;
117 for (int bit = 0; bit < 8; bit++)
118 {
119 value = (value >> 1) ^ ((value & 1) != 0 ? 0xEDB8_8320u : 0u);
120 }
121
122 table[index] = value;
123 }
124
125 return table;
126 }
127}