327 lines
7.1 KiB
C++
327 lines
7.1 KiB
C++
/* Copyright (C) Teemu Suutari */
|
|
|
|
#include <array>
|
|
|
|
#include "LZCBDecompressor.hpp"
|
|
#include "RangeDecoder.hpp"
|
|
#include "InputStream.hpp"
|
|
#include "OutputStream.hpp"
|
|
#include "common/Common.hpp"
|
|
|
|
|
|
namespace ancient::internal
|
|
{
|
|
|
|
template<size_t T>
|
|
class FrequencyTree
|
|
{
|
|
public:
|
|
FrequencyTree()
|
|
{
|
|
for (uint32_t i=0;i<_size;i++)
|
|
_tree[i]=0;
|
|
}
|
|
|
|
~FrequencyTree()
|
|
{
|
|
// nothing needed
|
|
}
|
|
|
|
uint16_t decode(uint16_t value,uint16_t &low,uint16_t &freq) const
|
|
{
|
|
if (value>=_tree[_size-1])
|
|
throw Decompressor::DecompressionError();
|
|
uint16_t symbol=0;
|
|
low=0;
|
|
for (uint32_t i=_levels-2;;i--)
|
|
{
|
|
uint16_t tmp=_tree[_levelOffsets[i]+symbol];
|
|
if (uint32_t(symbol+1)<_levelSizes[i] && value>=tmp)
|
|
{
|
|
symbol++;
|
|
low+=tmp;
|
|
value-=tmp;
|
|
}
|
|
if (!i) break;
|
|
symbol<<=1;
|
|
}
|
|
freq=_tree[symbol];
|
|
return symbol;
|
|
}
|
|
|
|
bool exists(uint16_t symbol) const
|
|
{
|
|
return _tree[symbol];
|
|
}
|
|
|
|
void increment(uint16_t symbol)
|
|
{
|
|
for (uint16_t i=0;i<_levels;i++)
|
|
{
|
|
_tree[_levelOffsets[i]+symbol]++;
|
|
symbol>>=1;
|
|
}
|
|
}
|
|
|
|
void halve()
|
|
{
|
|
// non-standard way
|
|
for (uint32_t i=0;i<T;i++)
|
|
_tree[i]>>=1;
|
|
for (uint32_t i=T;i<_size;i++)
|
|
_tree[i]=0;
|
|
for (uint32_t i=0,length=T;i<_levels-1;i++,length=(length+1)>>1)
|
|
{
|
|
for (uint32_t j=0;j<length;j++)
|
|
_tree[_levelOffsets[i+1]+(j>>1)]+=_tree[_levelOffsets[i]+j];
|
|
}
|
|
}
|
|
|
|
uint32_t getTotal() const
|
|
{
|
|
return _tree[_size-1];
|
|
}
|
|
|
|
private:
|
|
static constexpr uint32_t levelSize(uint32_t level)
|
|
{
|
|
uint32_t ret=T;
|
|
for (uint32_t i=0;i<level;i++)
|
|
{
|
|
ret=(ret+1)>>1;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static constexpr uint32_t levels()
|
|
{
|
|
uint32_t ret=0;
|
|
while (levelSize(ret)!=1) ret++;
|
|
return ret+1;
|
|
}
|
|
|
|
static constexpr uint32_t size()
|
|
{
|
|
uint32_t ret=0;
|
|
for (uint32_t i=0;i<levels();i++)
|
|
ret+=levelSize(i);
|
|
return ret;
|
|
}
|
|
|
|
static constexpr uint32_t levelOffset(uint32_t level)
|
|
{
|
|
uint32_t ret=0;
|
|
for (uint32_t i=0;i<level;i++)
|
|
ret+=levelSize(i);
|
|
return ret;
|
|
}
|
|
|
|
template<uint32_t... I>
|
|
static constexpr auto makeLevelOffsetSequence(std::integer_sequence<uint32_t,I...>)
|
|
{
|
|
return std::integer_sequence<uint32_t,levelOffset(I)...>{};
|
|
}
|
|
|
|
template<uint32_t... I>
|
|
static constexpr auto makeLevelSizeSequence(std::integer_sequence<uint32_t,I...>)
|
|
{
|
|
return std::integer_sequence<uint32_t,levelSize(I)...>{};
|
|
}
|
|
|
|
template<uint32_t... I>
|
|
static constexpr std::array<uint32_t,sizeof...(I)> makeArray(std::integer_sequence<uint32_t,I...>)
|
|
{
|
|
return std::array<uint32_t,sizeof...(I)>{{I...}};
|
|
}
|
|
|
|
static constexpr uint32_t _size=size();
|
|
static constexpr uint32_t _levels=levels();
|
|
static constexpr std::array<uint32_t,_levels> _levelOffsets=makeArray(makeLevelOffsetSequence(std::make_integer_sequence<uint32_t,levels()>{}));
|
|
static constexpr std::array<uint32_t,_levels> _levelSizes=makeArray(makeLevelSizeSequence(std::make_integer_sequence<uint32_t,levels()>{}));
|
|
|
|
uint16_t _tree[size()];
|
|
};
|
|
|
|
template<size_t T>
|
|
class FrequencyDecoder
|
|
{
|
|
public:
|
|
FrequencyDecoder(RangeDecoder &decoder) :
|
|
_decoder(decoder)
|
|
{
|
|
// nothing needed
|
|
}
|
|
|
|
~FrequencyDecoder()
|
|
{
|
|
// nothing needed
|
|
}
|
|
|
|
template<typename F>
|
|
uint16_t decode(F readFunc)
|
|
{
|
|
uint16_t freq=0,symbol,value=_decoder.decode(_threshold+_tree.getTotal());
|
|
if (value>=_threshold)
|
|
{
|
|
uint16_t low;
|
|
symbol=_tree.decode(value-_threshold,low,freq);
|
|
_decoder.scale(_threshold+low,_threshold+low+freq,_threshold+_tree.getTotal());
|
|
if (freq==1 && _threshold>1)
|
|
_threshold--;
|
|
} else {
|
|
_decoder.scale(0,_threshold,_threshold+_tree.getTotal());
|
|
symbol=readFunc();
|
|
// A bug in the encoder
|
|
if (!symbol && _tree.exists(symbol)) symbol=T;
|
|
_threshold++;
|
|
}
|
|
_tree.increment(symbol);
|
|
if (_threshold+_tree.getTotal()>=0x3ffdU)
|
|
{
|
|
_tree.halve();
|
|
_threshold=(_threshold>>1)+1;
|
|
}
|
|
return symbol;
|
|
}
|
|
|
|
private:
|
|
RangeDecoder &_decoder;
|
|
FrequencyTree<T+1> _tree;
|
|
uint16_t _threshold=1;
|
|
};
|
|
|
|
bool LZCBDecompressor::detectHeaderXPK(uint32_t hdr) noexcept
|
|
{
|
|
return hdr==FourCC("LZCB");
|
|
}
|
|
|
|
std::shared_ptr<XPKDecompressor> LZCBDecompressor::create(uint32_t hdr,uint32_t recursionLevel,const Buffer &packedData,std::shared_ptr<XPKDecompressor::State> &state,bool verify)
|
|
{
|
|
return std::make_shared<LZCBDecompressor>(hdr,recursionLevel,packedData,state,verify);
|
|
}
|
|
|
|
LZCBDecompressor::LZCBDecompressor(uint32_t hdr,uint32_t recursionLevel,const Buffer &packedData,std::shared_ptr<XPKDecompressor::State> &state,bool verify) :
|
|
XPKDecompressor(recursionLevel),
|
|
_packedData(packedData)
|
|
{
|
|
if (packedData.size()<2) throw Decompressor::InvalidFormatError();
|
|
}
|
|
|
|
LZCBDecompressor::~LZCBDecompressor()
|
|
{
|
|
// nothing needed
|
|
}
|
|
|
|
const std::string &LZCBDecompressor::getSubName() const noexcept
|
|
{
|
|
static std::string name="XPK-LZCB: LZ-compressor";
|
|
return name;
|
|
}
|
|
|
|
void LZCBDecompressor::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.readBitsBE32(1);
|
|
}
|
|
|
|
uint32_t readBits(uint32_t bitCount)
|
|
{
|
|
return _reader.readBitsBE32(bitCount);
|
|
}
|
|
|
|
private:
|
|
MSBBitReader<ForwardInputStream> _reader;
|
|
};
|
|
|
|
ForwardInputStream inputStream(_packedData,0,_packedData.size(),true);
|
|
ForwardOutputStream outputStream(rawData,0,rawData.size());
|
|
BitReader bitReader(inputStream);
|
|
|
|
RangeDecoder rangeDecoder(bitReader,bitReader.readBits(16));
|
|
|
|
// Ugly duplicates
|
|
auto readByte=[&]()->uint16_t
|
|
{
|
|
uint16_t ret=rangeDecoder.decode(0x100U);
|
|
rangeDecoder.scale(ret,ret+1,0x100U);
|
|
return ret;
|
|
};
|
|
|
|
auto readCount=[&]()->uint16_t
|
|
{
|
|
uint16_t ret=rangeDecoder.decode(0x101U);
|
|
rangeDecoder.scale(ret,ret+1,0x101U);
|
|
return ret;
|
|
};
|
|
|
|
FrequencyDecoder<256> baseLiteralDecoder(rangeDecoder);
|
|
FrequencyDecoder<257> repeatCountDecoder(rangeDecoder);
|
|
FrequencyDecoder<257> literalCountDecoder(rangeDecoder);
|
|
FrequencyDecoder<256> distanceDecoder(rangeDecoder);
|
|
|
|
std::unique_ptr<FrequencyDecoder<256>> literalDecoders[256];
|
|
|
|
uint8_t ch=uint8_t(baseLiteralDecoder.decode(readByte));
|
|
outputStream.writeByte(ch);
|
|
bool lastIsLiteral=true;
|
|
while (!outputStream.eof())
|
|
{
|
|
uint32_t count=repeatCountDecoder.decode(readCount);
|
|
if (count)
|
|
{
|
|
if (count==0x100U)
|
|
{
|
|
uint32_t tmp;
|
|
do
|
|
{
|
|
tmp=readByte();
|
|
count+=tmp;
|
|
} while (tmp==0xffU);
|
|
}
|
|
count+=lastIsLiteral?5:4;
|
|
|
|
uint32_t distance=distanceDecoder.decode(readByte)<<8;
|
|
distance|=readByte();
|
|
|
|
ch=outputStream.copy(distance,count);
|
|
lastIsLiteral=false;
|
|
} else {
|
|
uint16_t literalCount;
|
|
do
|
|
{
|
|
literalCount=literalCountDecoder.decode(readCount);
|
|
if (!literalCount) throw Decompressor::DecompressionError();
|
|
|
|
for (uint32_t i=0;i<literalCount;i++)
|
|
{
|
|
auto &literalDecoder=literalDecoders[ch];
|
|
if (!literalDecoder) literalDecoder=std::make_unique<FrequencyDecoder<256>>(rangeDecoder);
|
|
ch=uint8_t(literalDecoder->decode([&]()
|
|
{
|
|
return baseLiteralDecoder.decode(readByte);
|
|
}));
|
|
outputStream.writeByte(ch);
|
|
}
|
|
} while (literalCount==0x100U);
|
|
lastIsLiteral=true;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|