146 lines
3.6 KiB
C++
146 lines
3.6 KiB
C++
|
#include <fstream>
|
||
|
#include <iostream>
|
||
|
#include <vector>
|
||
|
|
||
|
using data_type = unsigned char;
|
||
|
|
||
|
class Uncompressor
|
||
|
{
|
||
|
public:
|
||
|
explicit Uncompressor(const std::vector<unsigned char>& inData)
|
||
|
: compressedData(inData)
|
||
|
{
|
||
|
std::vector<unsigned char> output;
|
||
|
|
||
|
int q_value = (getBit() << 2 | getBit() << 1 | getBit()) + 1;
|
||
|
|
||
|
data_type first_byte = getByte();
|
||
|
output.push_back(first_byte);
|
||
|
dataPosition = 2;
|
||
|
|
||
|
while (dataPosition < inData.size())
|
||
|
{
|
||
|
if (getBit())// Back reference
|
||
|
{
|
||
|
data_type length = getInterlacedEliasGamma() + 1;
|
||
|
|
||
|
if (length == 255)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
int offset = getByte();
|
||
|
if (offset & 0x80)
|
||
|
{
|
||
|
offset = offset & 0x7F;
|
||
|
switch(q_value)
|
||
|
{
|
||
|
case 6:
|
||
|
offset = offset | (getBit() << 12);
|
||
|
case 5:
|
||
|
offset = offset | (getBit() << 11);
|
||
|
case 4:
|
||
|
offset = offset | (getBit() << 10);
|
||
|
case 3:
|
||
|
offset = offset | (getBit() << 9);
|
||
|
case 2:
|
||
|
offset = offset | (getBit() << 8);
|
||
|
offset = offset | (getBit() << 7);
|
||
|
case 1:
|
||
|
break;
|
||
|
default:
|
||
|
std::cout << "Invalid mode: " << q_value << std::endl;
|
||
|
break;
|
||
|
}
|
||
|
offset += 128;
|
||
|
}
|
||
|
offset += 1;
|
||
|
|
||
|
for (int i = 0; i < length; i++)
|
||
|
{
|
||
|
output.push_back(output[output.size() - offset]);
|
||
|
}
|
||
|
}
|
||
|
else// Literal data
|
||
|
{
|
||
|
output.push_back(getByte());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (unsigned char byte: output)
|
||
|
{
|
||
|
std::cout << byte;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
int dataPosition = 0;
|
||
|
int varPosition = 0;
|
||
|
int bitForVarPosition = 7;
|
||
|
|
||
|
const std::vector<unsigned char>& compressedData;
|
||
|
|
||
|
data_type getByte()
|
||
|
{
|
||
|
return compressedData[dataPosition++];
|
||
|
}
|
||
|
|
||
|
data_type getBit()
|
||
|
{
|
||
|
if (bitForVarPosition == 7)
|
||
|
{
|
||
|
varPosition = dataPosition;
|
||
|
dataPosition = varPosition + 1;
|
||
|
}
|
||
|
|
||
|
data_type bit = (compressedData[varPosition] >> bitForVarPosition) & 1;
|
||
|
bitForVarPosition--;
|
||
|
|
||
|
if (bitForVarPosition == -1)
|
||
|
{
|
||
|
bitForVarPosition = 7;
|
||
|
}
|
||
|
return bit;
|
||
|
}
|
||
|
|
||
|
data_type getInterlacedEliasGamma()
|
||
|
{
|
||
|
data_type value = 1;
|
||
|
while (getBit())
|
||
|
{
|
||
|
value = (value << 1) | getBit();
|
||
|
}
|
||
|
return value;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
int main(int argc, char* argv[])
|
||
|
{
|
||
|
if (argc < 2)
|
||
|
{
|
||
|
std::cout << "Usage: " << argv[0] << " <compressed file>" << std::endl;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
std::ifstream file(argv[1], std::ios::binary | std::ios::ate);
|
||
|
if (!file)
|
||
|
{
|
||
|
std::cout << "Error reading file!" << std::endl;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
std::streamsize length = file.tellg();
|
||
|
file.seekg(0, std::ios::beg);
|
||
|
|
||
|
std::vector<unsigned char> compressedData(length);
|
||
|
if (!file.read(reinterpret_cast<char*>(compressedData.data()), length))
|
||
|
{
|
||
|
std::cout << "Error reading file!" << std::endl;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
Uncompressor data(compressedData);
|
||
|
|
||
|
return 0;
|
||
|
}
|