Unpletter initial implementation.
This commit is contained in:
commit
9378eadf71
9 changed files with 995 additions and 0 deletions
5
unpletter/README.md
Normal file
5
unpletter/README.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
Unpletter is a C and C++ version of the unpacker for the Pletter compression scheme.
|
||||
|
||||
By Sylvain Glaize, 2024.
|
||||
|
||||
Distributed as the same license as the Pletter packer. See license.txt on the root folder.
|
||||
145
unpletter/unpletter.cpp
Normal file
145
unpletter/unpletter.cpp
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
#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;
|
||||
}
|
||||
146
unpletter/unpletter_c.c
Normal file
146
unpletter/unpletter_c.c
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef unsigned char data_type;
|
||||
|
||||
struct Uncompressor {
|
||||
unsigned char* compressedData;
|
||||
int dataPosition;
|
||||
int varPosition;
|
||||
int bitForVarPosition;
|
||||
};
|
||||
|
||||
data_type getByte(struct Uncompressor* uncompressor)
|
||||
{
|
||||
return uncompressor->compressedData[uncompressor->dataPosition++];
|
||||
}
|
||||
|
||||
data_type getBit(struct Uncompressor* uncompressor)
|
||||
{
|
||||
if (uncompressor->bitForVarPosition == 7)
|
||||
{
|
||||
uncompressor->varPosition = uncompressor->dataPosition;
|
||||
uncompressor->dataPosition = uncompressor->varPosition + 1;
|
||||
}
|
||||
|
||||
data_type bit = (uncompressor->compressedData[uncompressor->varPosition] >> uncompressor->bitForVarPosition) & 1;
|
||||
uncompressor->bitForVarPosition--;
|
||||
|
||||
if (uncompressor->bitForVarPosition == -1)
|
||||
{
|
||||
uncompressor->bitForVarPosition = 7;
|
||||
}
|
||||
return bit;
|
||||
}
|
||||
|
||||
data_type getInterlacedEliasGamma(struct Uncompressor* uncompressor)
|
||||
{
|
||||
data_type value = 1;
|
||||
while (getBit(uncompressor))
|
||||
{
|
||||
value = (value << 1) | getBit(uncompressor);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void uncompress(struct Uncompressor* uncompressor, size_t in_data_length)
|
||||
{
|
||||
unsigned char* output = (unsigned char*) malloc(in_data_length * 3);// * 3 is an arbitrary number...
|
||||
size_t output_position = 0;
|
||||
|
||||
int q_value = (getBit(uncompressor) << 2 | getBit(uncompressor) << 1 | getBit(uncompressor)) + 1;
|
||||
|
||||
data_type first_byte = getByte(uncompressor);
|
||||
output[output_position++] = first_byte;
|
||||
|
||||
while (uncompressor->dataPosition < in_data_length)
|
||||
{
|
||||
if (getBit(uncompressor))
|
||||
{
|
||||
data_type length = getInterlacedEliasGamma(uncompressor) + 1;
|
||||
|
||||
if (length == 255)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
int offset = getByte(uncompressor);
|
||||
if (offset & 0x80)
|
||||
{
|
||||
offset = offset & 0x7F;
|
||||
switch (q_value)
|
||||
{
|
||||
case 6:
|
||||
offset = offset | (getBit(uncompressor) << 12);
|
||||
case 5:
|
||||
offset = offset | (getBit(uncompressor) << 11);
|
||||
case 4:
|
||||
offset = offset | (getBit(uncompressor) << 10);
|
||||
case 3:
|
||||
offset = offset | (getBit(uncompressor) << 9);
|
||||
case 2:
|
||||
offset = offset | (getBit(uncompressor) << 8);
|
||||
offset = offset | (getBit(uncompressor) << 7);
|
||||
case 1:
|
||||
break;
|
||||
default:
|
||||
printf("Invalid mode: %d\n", q_value);
|
||||
break;
|
||||
}
|
||||
offset += 128;
|
||||
}
|
||||
offset += 1;
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
output[output_position] = output[output_position - offset];
|
||||
output_position += 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
output[output_position++] = getByte(uncompressor);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < output_position; i++)
|
||||
{
|
||||
printf("%c", output[i]);
|
||||
}
|
||||
free(output);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
if (argc < 2)
|
||||
{
|
||||
printf("Usage: %s <compressed file>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
FILE* file = fopen(argv[1], "rb");
|
||||
if (!file)
|
||||
{
|
||||
printf("Error reading file!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
long length = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
unsigned char* compressedData = (unsigned char*) malloc(length);
|
||||
if (!fread(compressedData, length, 1, file))
|
||||
{
|
||||
printf("Error reading file!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct Uncompressor uncompressor = {compressedData, 0, 0, 7};
|
||||
uncompress(&uncompressor, length);
|
||||
|
||||
free(compressedData);
|
||||
fclose(file);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue