Last active
December 24, 2018 00:19
-
-
Save wildbook/a6375a179d3ad1994af93927e2c20b5b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
namespace BDODecryptFramework | |
{ | |
public static class BdoDecryptCores | |
{ | |
public static int LolReplayCore(byte[] source, int startPos, out byte[] destination, int decompressedSize) | |
{ | |
destination = new byte[decompressedSize]; | |
var index = startPos; | |
var dIndex = 0; | |
var blockGroupHeader = 1u; | |
while (true) | |
{ | |
// The last bit of BlockGroupHeader is 1 | |
// If there's only one 1 left, we want to load the next BlockGroupHeader | |
if (blockGroupHeader == 1) | |
{ | |
// Read next BlockGroupHeader | |
blockGroupHeader = BitConverter.ToUInt32(source, index); | |
index += 4; | |
} | |
if ((blockGroupHeader & 1) == 0) | |
{ | |
// If there's less than 10 bytes left, we're finished with | |
// the while loop, which is most of the decryption/decompression. | |
if (dIndex > decompressedSize - 10) | |
break; | |
// Copy 4 bytes without moving index | |
for (var i = 0; i < 4; i++) | |
destination[dIndex + i] = source[index + i]; | |
// Skip until there's either a bit set, or until we've skipped 4 times | |
for (var step = 0; step < 4; step++) | |
{ | |
// Abort if there's a bit set | |
if ((blockGroupHeader & 1) != 0) | |
break; | |
// Skip one byte in both source and destination | |
index++; | |
dIndex++; | |
// Shift group header one bit right | |
blockGroupHeader >>= 1; | |
} | |
} | |
else | |
{ | |
// Move header one step, as we just parsed the current bit | |
blockGroupHeader >>= 1; | |
// Read 4 bytes | |
// This could be done by using PeekChar instead to avoid | |
// rolling back the bytes we're not using, but this avoids the issue | |
// where there is no way to read 3 bytes to one value without reading | |
// to an array and converting to a number from there. | |
// | |
// (Also, I'm very very tired.) | |
// TODO: Do it the right way instead, preventing issues if there's ever not 4 bytes available | |
var blockHeader = BitConverter.ToUInt32(source, index); | |
// Read first 2 bits | |
var headerType = blockHeader & 0b0011; | |
// We just read 2 bits, so move 2 bits forward | |
blockHeader >>= 2; | |
uint blockLength; | |
uint repeatIndex; | |
switch (headerType) | |
{ | |
// Size is 8 bits (1 byte) | |
// HeaderType = 2 | |
// BlockLength = 0 | |
// RepeatIndex = 6 | |
case 0: | |
// Header size is 1 byte (+ sometimes 1 more later, but we take care of that then) | |
index += 1; | |
// BlockLength = 0 | |
blockLength = 0; | |
// The next 6 bits are RepeatIndex (1 byte - 2 bits for headerType) | |
repeatIndex = blockHeader & 0b0011_1111; | |
break; | |
// Size is 24 / 32 bits (3 / 4 bytes) | |
// Note: This | |
// HeaderType = 2 | |
// BlockLength = 5 (+ 8) | |
// RepeatIndex = 17 | |
// | |
// Note: If 5 bits isn't enough for BlockLength, an | |
// extra byte is read and parsed as size + 1 | |
case 3: | |
// Header size is 3 byte (+ sometimes 1 more later, but we take care of that then) | |
index += 3; | |
// BlockLength = first 5 bits | |
blockLength = blockHeader & 0b0001_1111; | |
// We just read 5 bits, so move 5 bits forward | |
blockHeader >>= 5; | |
// If size == 0, size exceeds 5 bits | |
if (blockLength == 0) | |
{ | |
// Read BlockLength as a single byte | |
blockLength = (blockHeader & 0b1111_1111); | |
// We just read an extra byte, so we move index and blockHeader again | |
index += 1; | |
blockHeader >>= 8; | |
} | |
else | |
{ | |
// Because length == 0 means "too large" there | |
// has to be a way to specify length as 0 as well. | |
// We solve this by adding one to length, meaning | |
// length = 0 is stored as length = 1 instead. | |
// Because of this, we need to -1 here to get the correct length. | |
blockLength--; | |
} | |
// The next 17 bits are RepeatIndex | |
repeatIndex = blockHeader & 0b0001_1111_1111_1111_1111; | |
break; | |
// Size is 16 bits (2 bytes) | |
// HeaderType = 2 | |
// BlockLength = 4 | |
// RepeatIndex = 10 | |
case 2: | |
// Header size is 2 byte | |
index += 2; | |
// BlockLength = first 4 bits | |
blockLength = (blockHeader & 0b1111); | |
// Advance 4 bits | |
blockHeader >>= 4; | |
// The next 10 bits are RepeatIndex | |
repeatIndex = blockHeader & 0b0011_1111_1111; | |
break; | |
// Size is 16 bits (2 bytes) | |
// HeaderType = 2 | |
// BlockLength = 0 | |
// RepeatIndex = 14 | |
case 1: | |
// Header size is 2 byte | |
index += 2; | |
// BlockLength = 0 | |
blockLength = 0; | |
// The next 14 bits are RepeatIndex | |
repeatIndex = blockHeader & 0b0011_1111_1111_1111; | |
break; | |
default: | |
throw new NotSupportedException($"Header type {headerType} is not supported."); | |
} | |
blockLength += 3; | |
for (var currentByte = 0; currentByte < blockLength; currentByte += 3) | |
{ | |
var dest = dIndex + currentByte; | |
var src = dest - (int)repeatIndex; | |
// Copy 4 bytes without moving index | |
for (var i = 0; i < 4; i++) | |
destination[dest + i] = destination[src + i]; | |
} | |
// We just wrote blockLength bytes to output, so move the index as well | |
dIndex += (int)blockLength; | |
} | |
} | |
// If decompressedSize is larger than current output size, copy the last bytes | |
while (dIndex < decompressedSize) | |
{ | |
if (blockGroupHeader == 1) | |
{ | |
// Set header to "32 bits left" | |
blockGroupHeader = 0b1000_0000_0000_0000_0000_0000_0000_0000; | |
var hm = BitConverter.ToUInt32(source, index); | |
// Skip actual header? | |
index += 4; | |
} | |
// Copy one byte over to the end of the output array | |
destination[dIndex++] = source[index++]; | |
// Move one step forward | |
blockGroupHeader >>= 1; | |
} | |
return decompressedSize; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment