121 lines
4.2 KiB
C
121 lines
4.2 KiB
C
|
|
#ifndef LZX_H
|
|||
|
|
#define LZX_H
|
|||
|
|
|
|||
|
|
#pragma once
|
|||
|
|
|
|||
|
|
#include <QByteArray>
|
|||
|
|
#include <QDebug>
|
|||
|
|
#include <cstdint>
|
|||
|
|
#include <vector>
|
|||
|
|
|
|||
|
|
namespace LZX {
|
|||
|
|
|
|||
|
|
/*!
|
|||
|
|
* \brief Decompresses LZX-compressed data.
|
|||
|
|
*
|
|||
|
|
* This function decodes a compressed QByteArray until there are no more
|
|||
|
|
* bits available. (In a real LZX stream there is an end-of-stream marker;
|
|||
|
|
* here we simply stop if a required field cannot be read.)
|
|||
|
|
*
|
|||
|
|
* \param compressedData The input LZX–compressed data.
|
|||
|
|
* \param windowBits The log₂ of the sliding window size (default 15 → 32K).
|
|||
|
|
* \return A QByteArray containing the decompressed data.
|
|||
|
|
*/
|
|||
|
|
static QByteArray DecompressLZX(const QByteArray &compressedData, uint32_t windowBits = 15)
|
|||
|
|
{
|
|||
|
|
if (compressedData.isEmpty())
|
|||
|
|
return QByteArray();
|
|||
|
|
|
|||
|
|
// Calculate sliding window size.
|
|||
|
|
const uint32_t windowSize = 1u << windowBits;
|
|||
|
|
std::vector<uint8_t> window(windowSize, 0);
|
|||
|
|
uint32_t windowPos = 0;
|
|||
|
|
|
|||
|
|
// Use a dynamic output buffer.
|
|||
|
|
QByteArray outArray;
|
|||
|
|
// Reserve an initial capacity.
|
|||
|
|
outArray.reserve(1024);
|
|||
|
|
|
|||
|
|
// --- Bitstream state ---
|
|||
|
|
const uint8_t *inData = reinterpret_cast<const uint8_t*>(compressedData.constData());
|
|||
|
|
size_t inSize = compressedData.size();
|
|||
|
|
size_t inPos = 0;
|
|||
|
|
uint32_t bitBuffer = 0;
|
|||
|
|
int bitsInBuffer = 0;
|
|||
|
|
|
|||
|
|
// Lambda: Ensure at least 'count' bits are available.
|
|||
|
|
auto ensureBits = [&](int count) -> bool {
|
|||
|
|
while (bitsInBuffer < count) {
|
|||
|
|
if (inPos < inSize) {
|
|||
|
|
bitBuffer = (bitBuffer << 8) | inData[inPos++];
|
|||
|
|
bitsInBuffer += 8;
|
|||
|
|
} else {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return true;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Lambda: Get (and remove) 'count' bits from the bit buffer.
|
|||
|
|
auto getBits = [&](int count) -> uint32_t {
|
|||
|
|
if (!ensureBits(count))
|
|||
|
|
return 0;
|
|||
|
|
uint32_t result = (bitBuffer >> (bitsInBuffer - count)) & ((1u << count) - 1);
|
|||
|
|
bitsInBuffer -= count;
|
|||
|
|
return result;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// --- Main decompression loop ---
|
|||
|
|
// In this simplified placeholder format:
|
|||
|
|
// - A flag bit of 1 means a literal byte follows (8 bits).
|
|||
|
|
// - A flag bit of 0 means a match follows: first 4 bits for match length (plus base 2)
|
|||
|
|
// then windowBits bits for the match offset (relative to the current sliding window).
|
|||
|
|
while (true) {
|
|||
|
|
// Try to read a flag bit; if not available, we assume the stream is complete.
|
|||
|
|
if (!ensureBits(1))
|
|||
|
|
break;
|
|||
|
|
uint32_t flag = getBits(1);
|
|||
|
|
if (flag == 1) {
|
|||
|
|
// Literal: next 8 bits form a literal byte.
|
|||
|
|
if (!ensureBits(8)) {
|
|||
|
|
qWarning() << "Unexpected end of input while reading literal.";
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
uint8_t literal = static_cast<uint8_t>(getBits(8));
|
|||
|
|
outArray.append(static_cast<char>(literal));
|
|||
|
|
// Update the sliding window.
|
|||
|
|
window[windowPos] = literal;
|
|||
|
|
windowPos = (windowPos + 1) % windowSize;
|
|||
|
|
} else {
|
|||
|
|
// Match: first read a 4-bit match length (with a base of 2).
|
|||
|
|
if (!ensureBits(4)) {
|
|||
|
|
qWarning() << "Unexpected end of input while reading match length.";
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
uint32_t matchLength = getBits(4) + 2;
|
|||
|
|
// Then read the match offset (fixed number of bits equals windowBits).
|
|||
|
|
if (!ensureBits(windowBits)) {
|
|||
|
|
qWarning() << "Unexpected end of input while reading match offset.";
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
uint32_t matchOffset = getBits(windowBits);
|
|||
|
|
// Compute the source position in the sliding window.
|
|||
|
|
uint32_t copyPos = (windowPos + windowSize - matchOffset) % windowSize;
|
|||
|
|
// Copy matchLength bytes from the sliding window.
|
|||
|
|
for (uint32_t i = 0; i < matchLength; i++) {
|
|||
|
|
uint8_t byte = window[(copyPos + i) % windowSize];
|
|||
|
|
outArray.append(static_cast<char>(byte));
|
|||
|
|
// Update the sliding window with the decompressed byte.
|
|||
|
|
window[windowPos] = byte;
|
|||
|
|
windowPos = (windowPos + 1) % windowSize;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return outArray;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
} // namespace LZX
|
|||
|
|
|
|||
|
|
#endif // LZX_H
|