#include "pkware.h"
#include <algorithm>
#include <array>
#include <cstring>
namespace whiteout::storages::mpq {
namespace {
static constexpr std::array<u8, 16> kLenBits = {3, 2, 3, 3, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 7, 7};
static constexpr std::array<u8, 16> kLenBase = {0, 4, 8, 16, 32, 64, 128, 0,
1, 2, 3, 4, 5, 6, 7, 8};
static constexpr std::array<u8, 66> kDistBits = {
2, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8};
static constexpr std::array<u8, 256> kChBitsAsc = {
0x0B, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x08, 0x07, 0x0C, 0x0C, 0x07, 0x0C, 0x0C,
0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
0x04, 0x0A, 0x08, 0x0C, 0x0A, 0x0C, 0x0A, 0x08, 0x07, 0x07, 0x08, 0x09, 0x07, 0x06, 0x07, 0x08,
0x07, 0x06, 0x07, 0x07, 0x07, 0x07, 0x08, 0x07, 0x07, 0x08, 0x08, 0x0C, 0x0B, 0x07, 0x09, 0x0B,
0x0C, 0x06, 0x07, 0x06, 0x06, 0x05, 0x07, 0x08, 0x08, 0x06, 0x0B, 0x09, 0x06, 0x07, 0x06, 0x06,
0x07, 0x0B, 0x06, 0x06, 0x06, 0x07, 0x09, 0x08, 0x09, 0x09, 0x0B, 0x08, 0x0B, 0x08, 0x0C, 0x08,
0x0C, 0x05, 0x06, 0x06, 0x06, 0x05, 0x06, 0x06, 0x06, 0x05, 0x0B, 0x07, 0x05, 0x06, 0x05, 0x05,
0x06, 0x0A, 0x05, 0x05, 0x05, 0x05, 0x08, 0x07, 0x08, 0x08, 0x0A, 0x0B, 0x0B, 0x0C, 0x0C, 0x0C,
0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D,
0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D,
0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D,
0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
0x0D, 0x0C, 0x0D, 0x0D, 0x0D, 0x0C, 0x0D, 0x0D, 0x0D, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0C, 0x0D,
0x0D, 0x0D, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D};
static constexpr std::array<u16, 256> kChCodeAsc = {
0x0490, 0x0FE0, 0x07E0, 0x0BE0, 0x03E0, 0x0DE0, 0x05E0, 0x09E0, 0x01E0, 0x00B8, 0x0062, 0x0EE0,
0x06E0, 0x0022, 0x0AE0, 0x02E0, 0x0CE0, 0x04E0, 0x08E0, 0x00E0, 0x0F60, 0x0760, 0x0B60, 0x0360,
0x0D60, 0x0560, 0x1240, 0x0960, 0x0160, 0x0E60, 0x0660, 0x0A60, 0x000F, 0x0250, 0x0038, 0x0260,
0x0050, 0x0C60, 0x0390, 0x00D8, 0x0042, 0x0002, 0x0058, 0x01B0, 0x007C, 0x0029, 0x003C, 0x0098,
0x005C, 0x0009, 0x001C, 0x006C, 0x002C, 0x004C, 0x0018, 0x000C, 0x0074, 0x00E8, 0x0068, 0x0460,
0x0090, 0x0034, 0x00B0, 0x0710, 0x0860, 0x0031, 0x0054, 0x0011, 0x0021, 0x0017, 0x0014, 0x00A8,
0x0028, 0x0001, 0x0310, 0x0130, 0x003E, 0x0064, 0x001E, 0x002E, 0x0024, 0x0510, 0x000E, 0x0036,
0x0016, 0x0044, 0x0030, 0x00C8, 0x01D0, 0x00D0, 0x0110, 0x0048, 0x0610, 0x0150, 0x0060, 0x0088,
0x0FA0, 0x0007, 0x0026, 0x0006, 0x003A, 0x001B, 0x001A, 0x002A, 0x000A, 0x000B, 0x0210, 0x0004,
0x0013, 0x0032, 0x0003, 0x001D, 0x0012, 0x0190, 0x000D, 0x0015, 0x0005, 0x0019, 0x0008, 0x0078,
0x00F0, 0x0070, 0x0290, 0x0410, 0x0010, 0x07A0, 0x0BA0, 0x03A0, 0x0240, 0x1C40, 0x0C40, 0x1440,
0x0440, 0x1840, 0x0840, 0x1040, 0x0040, 0x1F40, 0x0F40, 0x1740, 0x0740, 0x1B40, 0x0B40, 0x1340,
0x0340, 0x1D40, 0x0D40, 0x1540, 0x0540, 0x1940, 0x0940, 0x1140, 0x0140, 0x1E40, 0x0E40, 0x1640,
0x0640, 0x1A40, 0x0A40, 0x1240, 0x0240, 0x1C40, 0x0C40, 0x1440, 0x0440, 0x1840, 0x0840, 0x1040,
0x0040, 0x1F40, 0x0F40, 0x1740, 0x0740, 0x1B40, 0x0B40, 0x1340, 0x0DA0, 0x05A0, 0x09A0, 0x01A0,
0x0EA0, 0x06A0, 0x0AA0, 0x02A0, 0x0CA0, 0x04A0, 0x08A0, 0x00A0, 0x0F20, 0x0720, 0x0B20, 0x0320,
0x0D20, 0x0520, 0x0920, 0x0120, 0x0E20, 0x0620, 0x0A20, 0x0220, 0x0C20, 0x0420, 0x0820, 0x0020,
0x0FC0, 0x07C0, 0x0BC0, 0x03C0, 0x0DC0, 0x05C0, 0x09C0, 0x01C0, 0x0EC0, 0x06C0, 0x0AC0, 0x02C0,
0x0CC0, 0x04C0, 0x08C0, 0x00C0, 0x0F40, 0x0740, 0x0B40, 0x0340, 0x0C00, 0x0D40, 0x1A00, 0x0600,
0x1E00, 0x0540, 0x0E00, 0x1600, 0x0200, 0x0940, 0x1C00, 0x0400, 0x1400, 0x0140, 0x0800, 0x1800,
0x1000, 0x0A00, 0x0F00, 0x1200, 0x0700, 0x0B00, 0x0300, 0x0D00, 0x0500, 0x0900, 0x0100, 0x1100,
0x1900, 0x1500, 0x1D00, 0x0000};
static constexpr std::array<u8, 16> kLenCode = {0x05, 0x03, 0x01, 0x06, 0x0A, 0x02, 0x0C, 0x14,
0x04, 0x18, 0x08, 0x30, 0x10, 0x20, 0x40, 0x00};
static constexpr i32 ASC_FAST_BITS = 13;
static constexpr i32 ASC_FAST_SIZE = 1 << ASC_FAST_BITS;
struct AscLutEntry {
u16 symbol; u8 bits; };
constexpr std::array<AscLutEntry, ASC_FAST_SIZE> buildAscLut() {
std::array<AscLutEntry, ASC_FAST_SIZE> table{};
for (i32 i = 0; i < ASC_FAST_SIZE; ++i) {
table[i] = {0xFFFF, 0};
}
for (u32 sym = 0; sym < 256; ++sym) {
u32 const codeLen = kChBitsAsc[sym];
if (codeLen == 0)
continue;
u16 const code = kChCodeAsc[sym];
u32 const padBits = ASC_FAST_BITS - codeLen;
u32 const numEntries = 1u << padBits;
for (u32 p = 0; p < numEntries; ++p) {
u32 const idx = code | (p << codeLen);
if (idx < static_cast<u32>(ASC_FAST_SIZE)) {
table[idx] = {static_cast<u16>(sym), static_cast<u8>(codeLen)};
}
}
}
return table;
}
static constexpr auto kAscLut = buildAscLut();
struct ExplodeBitReader {
const u8* data;
size_t dataSize;
size_t bytePos = 0;
u32 bitBuf = 0;
u32 bitsAvail = 0;
ExplodeBitReader(const u8* d, size_t s) : data(d), dataSize(s) {}
bool ensureBits(u32 count) {
while (bitsAvail < count) {
if (bytePos >= dataSize)
return false;
bitBuf |= static_cast<u32>(data[bytePos++]) << bitsAvail;
bitsAvail += 8;
}
return true;
}
u32 peekBits(u32 count) const {
return bitBuf & ((1u << count) - 1);
}
void dropBits(u32 count) {
bitBuf >>= count;
bitsAvail -= count;
}
u32 readBits(u32 count) {
ensureBits(count);
u32 const val = peekBits(count);
dropBits(count);
return val;
}
};
u32 decodeLengthValue(ExplodeBitReader& br) {
if (!br.ensureBits(8))
return 0;
u32 const code = br.peekBits(8);
u32 lengthCode;
if (code < 16) {
lengthCode = 0;
for (u32 i = 0; i < 16; ++i) {
if (!br.ensureBits(kLenBits[i]))
return 0;
u32 const val = br.peekBits(kLenBits[i]);
if (val == kLenCode[i]) {
br.dropBits(kLenBits[i]);
lengthCode = i;
break;
}
}
} else {
lengthCode = code & 0x0F;
br.dropBits(4);
}
u32 length = kLenBase[lengthCode];
if (kLenBits[lengthCode] > 0) {
u32 const extra = br.readBits(kLenBits[lengthCode]);
length += extra;
}
return length + 2; }
}
std::vector<u8> pkwareExplode(std::span<const u8> src, size_t expectedSize) {
if (src.size() < 4)
return {};
u8 const compressionType = src[0];
u8 const dictSizeBits = src[1];
if (compressionType > 1)
return {};
if (dictSizeBits < 4 || dictSizeBits > 6)
return {};
[[maybe_unused]] u32 const dictSize = 1u << dictSizeBits;
ExplodeBitReader br(src.data() + 2, src.size() - 2);
std::vector<u8> output;
output.reserve(expectedSize);
while (output.size() < expectedSize) {
if (!br.ensureBits(1))
break;
if (br.readBits(1) == 1) {
if (compressionType == 1) {
if (!br.ensureBits(ASC_FAST_BITS))
break;
u32 const val = br.peekBits(ASC_FAST_BITS);
const auto& entry = kAscLut[val];
if (entry.symbol == 0xFFFF)
break;
br.dropBits(entry.bits);
output.push_back(static_cast<u8>(entry.symbol));
} else {
if (!br.ensureBits(8))
break;
output.push_back(static_cast<u8>(br.readBits(8)));
}
} else {
if (!br.ensureBits(dictSizeBits + 6))
break;
u32 const distLow = br.readBits(dictSizeBits);
u32 distHigh = 0;
if (!br.ensureBits(6))
break;
u32 const distCode = br.peekBits(6);
br.dropBits(6);
if (distCode < 64) {
u32 const extraDist = kDistBits[distCode];
if (extraDist > 0 && !br.ensureBits(extraDist))
break;
u32 const extra = (extraDist > 0) ? br.readBits(extraDist) : 0;
distHigh = (distCode << 2) | extra; }
u32 const distance = (distHigh << dictSizeBits) | distLow;
u32 const length = decodeLengthValue(br);
if (length == 0)
break;
if (distance == 0 && length == 2)
break;
if (distance >= output.size())
break;
size_t const srcPos = output.size() - distance - 1;
for (u32 i = 0; i < length && output.size() < expectedSize; ++i) {
output.push_back(output[srcPos + i]);
}
}
}
if (output.size() != expectedSize) {
}
return output;
}
std::vector<u8> pkwareImplode(std::span<const u8> src) {
if (src.empty())
return {};
std::vector<u8> output;
output.reserve(src.size() + src.size() / 8 + 4);
output.push_back(0x00); output.push_back(0x06);
u32 bitBuf = 0;
u32 bitsUsed = 0;
auto flushByte = [&]() {
output.push_back(static_cast<u8>(bitBuf & 0xFF));
bitBuf >>= 8;
bitsUsed -= 8;
};
auto writeBits = [&](u32 value, u32 count) {
bitBuf |= (value << bitsUsed);
bitsUsed += count;
while (bitsUsed >= 8)
flushByte();
};
for (u8 const byte : src) {
writeBits(1, 1); writeBits(byte, 8); }
writeBits(0, 1); writeBits(0, 6);
writeBits(0, 6);
writeBits(0x05, 3);
if (bitsUsed > 0) {
output.push_back(static_cast<u8>(bitBuf & 0xFF));
}
return output;
}
}