123 lines
3.2 KiB
C++
123 lines
3.2 KiB
C++
/* Copyright (C) Teemu Suutari */
|
|
|
|
#include "ARTMDecompressor.hpp"
|
|
#include "RangeDecoder.hpp"
|
|
#include "InputStream.hpp"
|
|
#include "OutputStream.hpp"
|
|
#include "common/Common.hpp"
|
|
|
|
|
|
namespace ancient::internal
|
|
{
|
|
|
|
bool ARTMDecompressor::detectHeaderXPK(uint32_t hdr) noexcept
|
|
{
|
|
return hdr==FourCC("ARTM");
|
|
}
|
|
|
|
std::shared_ptr<XPKDecompressor> ARTMDecompressor::create(uint32_t hdr,uint32_t recursionLevel,const Buffer &packedData,std::shared_ptr<XPKDecompressor::State> &state,bool verify)
|
|
{
|
|
return std::make_shared<ARTMDecompressor>(hdr,recursionLevel,packedData,state,verify);
|
|
}
|
|
|
|
ARTMDecompressor::ARTMDecompressor(uint32_t hdr,uint32_t recursionLevel,const Buffer &packedData,std::shared_ptr<XPKDecompressor::State> &state,bool verify) :
|
|
XPKDecompressor(recursionLevel),
|
|
_packedData(packedData)
|
|
{
|
|
if (!detectHeaderXPK(hdr)) throw Decompressor::InvalidFormatError();
|
|
if (packedData.size()<2) throw Decompressor::InvalidFormatError();
|
|
}
|
|
|
|
ARTMDecompressor::~ARTMDecompressor()
|
|
{
|
|
// nothing needed
|
|
}
|
|
|
|
const std::string &ARTMDecompressor::getSubName() const noexcept
|
|
{
|
|
static std::string name="XPK-ARTM: Arithmetic encoding compressor";
|
|
return name;
|
|
}
|
|
|
|
// There really is not much to see here.
|
|
// Except maybe for the fact that there is extra symbol defined but never used.
|
|
// It is used in the original implementation as a terminator. In here it is considered as an error.
|
|
// Logically it would decode into null-character (practically it would be instant buffer overflow)
|
|
void ARTMDecompressor::decompressImpl(Buffer &rawData,const Buffer &previousData,bool verify)
|
|
{
|
|
class BitReader : public RangeDecoder::BitReader
|
|
{
|
|
public:
|
|
BitReader(ForwardInputStream &stream) :
|
|
_reader(stream)
|
|
{
|
|
// nothing needed
|
|
}
|
|
|
|
virtual ~BitReader()
|
|
{
|
|
// nothing needed
|
|
}
|
|
|
|
virtual uint32_t readBit() override final
|
|
{
|
|
return _reader.readBits8(1);
|
|
}
|
|
|
|
private:
|
|
LSBBitReader<ForwardInputStream> _reader;
|
|
};
|
|
|
|
|
|
ForwardInputStream inputStream(_packedData,0,_packedData.size(),true);
|
|
ForwardOutputStream outputStream(rawData,0,rawData.size());
|
|
BitReader bitReader(inputStream);
|
|
|
|
uint16_t initialValue=0;
|
|
for (uint32_t i=0;i<16;i++) initialValue=(initialValue<<1)|bitReader.readBit();
|
|
RangeDecoder decoder(bitReader,initialValue);
|
|
|
|
uint8_t characters[256];
|
|
uint16_t frequencies[256];
|
|
uint16_t frequencySums[256];
|
|
|
|
for (uint32_t i=0;i<256;i++)
|
|
{
|
|
characters[i]=i;
|
|
frequencies[i]=1;
|
|
frequencySums[i]=256-i;
|
|
}
|
|
uint16_t frequencyTotal=257;
|
|
|
|
while (!outputStream.eof())
|
|
{
|
|
uint16_t value=decoder.decode(frequencyTotal);
|
|
uint16_t symbol;
|
|
for (symbol=0;symbol<256;symbol++)
|
|
if (frequencySums[symbol]<=value) break;
|
|
if (symbol==256) throw Decompressor::DecompressionError();
|
|
decoder.scale(frequencySums[symbol],frequencySums[symbol]+frequencies[symbol],frequencyTotal);
|
|
outputStream.writeByte(characters[symbol]);
|
|
|
|
if (frequencyTotal==0x3fffU)
|
|
{
|
|
frequencyTotal=1;
|
|
for (int32_t i=255;i>=0;i--)
|
|
{
|
|
frequencySums[i]=frequencyTotal;
|
|
frequencyTotal+=frequencies[i]=(frequencies[i]+1)>>1;
|
|
}
|
|
}
|
|
|
|
uint16_t i;
|
|
for (i=symbol;i&&frequencies[i-1]==frequencies[i];i--);
|
|
if (i!=symbol)
|
|
std::swap(characters[symbol],characters[i]);
|
|
frequencies[i]++;
|
|
while (i--) frequencySums[i]++;
|
|
frequencyTotal++;
|
|
}
|
|
}
|
|
|
|
}
|