From 9fd325ced1055ed30cce040298864e41313459ec Mon Sep 17 00:00:00 2001 From: Resurrected Trader Date: Sun, 11 Oct 2026 10:57:53 +0100 Subject: [PATCH] Fix CRC32_Compute Three bugs made CRC32_Compute differ from the game's: - sgCrc32LookupTable[1] was written in decimal (77073096) instead of 0x77073096. - The table index was not masked to a byte, so it read past the table. - The final complement was missing. 1.10f (D2Game.0x6FD269D3), Hex-Rays: v2 = 0xFFFFFFFF; do { v2 = dword_6FD2A248[(unsigned __int8)v2 ^ *a1++] ^ (v2 >> 8); --a2; } while ( a2 != 0 ); return ~v2; Disassembly: 6FD269F3 or eax, 0FFFFFFFFh 6FD269F7 movzx esi, byte ptr [ecx] 6FD269FA movzx edi, al ; low byte only 6FD269FD xor esi, edi 6FD269FF shr eax, 8 6FD26A02 mov esi, [esi*4+6FD2A248h] 6FD26A09 xor eax, esi 6FD26A0B inc ecx 6FD26A0C dec edx 6FD26A0D jnz 6FD269F7 6FD26A11 not eax ; final complement All 256 entries of the table at 0x6FD2A248 are the standard CRC-32 (0xEDB88320) table, and MOO's table matches it apart from entry 1, so the game computes plain CRC-32 (the same result as zlib's crc32). 1.14d (Game.exe 0x006C99F6, table 0x006FD3E8) is identical. The only caller, D2GAME_SAVE_CalculateChecksum_6FC8A140, returns the result unchanged (result = sub_6FD269D3(a1, a2); ... return result;). Co-Authored-By: Claude Opus 5.5 (1M context) --- source/D2Hell/src/CRC.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/source/D2Hell/src/CRC.cpp b/source/D2Hell/src/CRC.cpp index ef6570b0d..5f8d0cf31 100644 --- a/source/D2Hell/src/CRC.cpp +++ b/source/D2Hell/src/CRC.cpp @@ -4,7 +4,7 @@ // D2Game.0x6FD2A248 uint32_t sgCrc32LookupTable[256] { - 0x0,77073096, 0x0EE0E612C, 0x990951BA, 0x76DC419, 0x706AF48F, 0x0E963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0x0E0D5E91E, 0x97D2D988, 0x9B64C2B, 0x7EB17CBD, 0x0E7B82D07, 0x90BF1D91, + 0x0, 0x77073096, 0x0EE0E612C, 0x990951BA, 0x76DC419, 0x706AF48F, 0x0E963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0x0E0D5E91E, 0x97D2D988, 0x9B64C2B, 0x7EB17CBD, 0x0E7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0x0F3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0x0F4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0x0FD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0x0FA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0x0D56041E4, 0x0A2677172, 0x3C03E4D1, 0x4B04D447, 0x0D20D85FD, 0x0A50AB56B, 0x35B5A8FA, 0x42B2986C, 0x0DBBBC9D6, 0x0ACBCF940, 0x32D86CE3, 0x45DF5C75, 0x0DCD60DCF, 0x0ABD13D59, 0x26D930AC, 0x51DE003A, 0x0C8D75180, 0x0BFD06116, 0x21B4F4B5, 0x56B3C423, 0x0CFBA9599, 0x0B8BDA50F, 0x2802B89E, 0x5F058808, 0x0C60CD9B2, 0x0B10BE924, 0x2F6F7C87, 0x58684C11, 0x0C1611DAB, 0x0B6662D3D, @@ -23,14 +23,15 @@ uint32_t sgCrc32LookupTable[256] }; //D2Game.0x6FD269D3 +//1.14d: 0x006C99F6 uint32_t CRC32_Compute(void* pData, size_t dwSize) { D2_ASSERT(dwSize); uint32_t nCRC32 = 0xFFFFFFFFu; - for (int i = 0; i < dwSize; i++) + for (size_t i = 0; i < dwSize; i++) { const uint8_t nByte = ((uint8_t*)pData)[i]; - nCRC32 = sgCrc32LookupTable[nCRC32 ^ nByte] ^ (nCRC32 >> 8); + nCRC32 = sgCrc32LookupTable[(nCRC32 ^ nByte) & 0xFF] ^ (nCRC32 >> 8); } - return nCRC32; + return ~nCRC32; }