winamp/Src/external_dependencies/openmpt-trunk/include/ancient/src/CRMDecompressor.cpp

246 lines
6.6 KiB
C++

/* Copyright (C) Teemu Suutari */
#include "CRMDecompressor.hpp"
#include "HuffmanDecoder.hpp"
#include "DLTADecode.hpp"
#include "InputStream.hpp"
#include "OutputStream.hpp"
#include "common/OverflowCheck.hpp"
#include "common/Common.hpp"
namespace ancient::internal
{
bool CRMDecompressor::detectHeader(uint32_t hdr) noexcept
{
switch (hdr)
{
case FourCC("CrM!"):
case FourCC("CrM2"):
case FourCC("Crm!"):
case FourCC("Crm2"):
return true;
default:
return false;
}
}
bool CRMDecompressor::detectHeaderXPK(uint32_t hdr) noexcept
{
return hdr==FourCC("CRM2") || hdr==FourCC("CRMS");
}
std::shared_ptr<Decompressor> CRMDecompressor::create(const Buffer &packedData,bool exactSizeKnown,bool verify)
{
return std::make_shared<CRMDecompressor>(packedData,0,verify);
}
std::shared_ptr<XPKDecompressor> CRMDecompressor::create(uint32_t hdr,uint32_t recursionLevel,const Buffer &packedData,std::shared_ptr<XPKDecompressor::State> &state,bool verify)
{
return std::make_shared<CRMDecompressor>(hdr,recursionLevel,packedData,state,verify);
}
CRMDecompressor::CRMDecompressor(const Buffer &packedData,uint32_t recursionLevel,bool verify) :
XPKDecompressor(recursionLevel),
_packedData(packedData)
{
uint32_t hdr=packedData.readBE32(0);
if (!detectHeader(hdr) || packedData.size()<20)
throw Decompressor::InvalidFormatError();
_rawSize=packedData.readBE32(6);
_packedSize=packedData.readBE32(10);
if (!_rawSize || !_packedSize ||
_rawSize>getMaxRawSize() || _packedSize>getMaxPackedSize() ||
OverflowCheck::sum(_packedSize,14U)>packedData.size()) throw Decompressor::InvalidFormatError();
if (((hdr>>8)&0xff)=='m') _isSampled=true;
if ((hdr&0xff)=='2') _isLZH=true;
}
CRMDecompressor::CRMDecompressor(uint32_t hdr,uint32_t recursionLevel,const Buffer &packedData,std::shared_ptr<XPKDecompressor::State> &state,bool verify) :
CRMDecompressor(packedData,recursionLevel,verify)
{
_isXPKDelta=(hdr==FourCC("CRMS"));
}
CRMDecompressor::~CRMDecompressor()
{
// nothing needed
}
const std::string &CRMDecompressor::getName() const noexcept
{
static std::string names[4]={
"CrM!: Crunch-Mania standard-mode",
"Crm!: Crunch-Mania standard-mode, sampled",
"CrM2: Crunch-Mania LZH-mode",
"Crm2: Crunch-Mania LZH-mode, sampled"};
return names[(_isLZH?2:0)+(_isSampled?1:0)];
}
const std::string &CRMDecompressor::getSubName() const noexcept
{
// the XPK-id is not used in decompressing process,
// but there is a real id inside the stream
// This means we can have frankenstein configurations,
// although in practice we don't
static std::string names[2]={
"XPK-CRM2: Crunch-Mania LZH-mode",
"XPK-CRMS: Crunch-Mania LZH-mode, sampled"};
return names[(_isXPKDelta?1:0)];
}
size_t CRMDecompressor::getPackedSize() const noexcept
{
return _packedSize+14;
}
size_t CRMDecompressor::getRawSize() const noexcept
{
return _rawSize;
}
void CRMDecompressor::decompressImpl(Buffer &rawData,bool verify)
{
if (rawData.size()<_rawSize) throw Decompressor::DecompressionError();
BackwardInputStream inputStream(_packedData,14,_packedSize+14-6);
LSBBitReader<BackwardInputStream> bitReader(inputStream);
{
// There are empty bits?!? at the start of the stream. take them out
size_t bufOffset=_packedSize+14-6;
uint32_t originalBitsContent=_packedData.readBE32(bufOffset);
uint16_t originalShift=_packedData.readBE16(bufOffset+4);
uint8_t bufBitsLength=originalShift+16;
uint32_t bufBitsContent=originalBitsContent>>(16-originalShift);
bitReader.reset(bufBitsContent,bufBitsLength);
}
auto readBits=[&](uint32_t count)->uint32_t
{
return bitReader.readBits8(count);
};
auto readBit=[&]()->uint32_t
{
return bitReader.readBits8(1);
};
BackwardOutputStream outputStream(rawData,0,_rawSize);
if (_isLZH)
{
typedef HuffmanDecoder<uint32_t> CRMHuffmanDecoder;
auto readHuffmanTable=[&](CRMHuffmanDecoder &dec,uint32_t codeLength)
{
uint32_t maxDepth=readBits(4);
if (!maxDepth) throw Decompressor::DecompressionError();
uint32_t lengthTable[15];
for (uint32_t i=0;i<maxDepth;i++)
lengthTable[i]=readBits(std::min(i+1,codeLength));
uint32_t code=0;
for (uint32_t depth=1;depth<=maxDepth;depth++)
{
for (uint32_t i=0;i<lengthTable[depth-1];i++)
{
uint32_t value=readBits(codeLength);
dec.insert(HuffmanCode<uint32_t>{depth,code>>(maxDepth-depth),value});
code+=1<<(maxDepth-depth);
}
}
};
do {
CRMHuffmanDecoder lengthDecoder,distanceDecoder;
readHuffmanTable(lengthDecoder,9);
readHuffmanTable(distanceDecoder,4);
uint32_t items=readBits(16)+1;
for (uint32_t i=0;i<items;i++)
{
uint32_t count=lengthDecoder.decode(readBit);
if (count&0x100)
{
outputStream.writeByte(count);
} else {
count+=3;
uint32_t distanceBits=distanceDecoder.decode(readBit);
uint32_t distance;
if (!distanceBits)
{
distance=readBits(1)+1;
} else {
distance=(readBits(distanceBits)|(1<<distanceBits))+1;
}
outputStream.copy(distance,count);
}
}
} while (readBit());
} else {
HuffmanDecoder<uint8_t> lengthDecoder
{
HuffmanCode<uint8_t>{1,0b000,0},
HuffmanCode<uint8_t>{2,0b010,1},
HuffmanCode<uint8_t>{3,0b110,2},
HuffmanCode<uint8_t>{3,0b111,3}
};
HuffmanDecoder<uint8_t> distanceDecoder
{
HuffmanCode<uint8_t>{1,0b00,0},
HuffmanCode<uint8_t>{2,0b10,1},
HuffmanCode<uint8_t>{2,0b11,2}
};
while (!outputStream.eof())
{
if (readBit())
{
outputStream.writeByte(readBits(8));
} else {
uint8_t lengthIndex=lengthDecoder.decode(readBit);
static const uint8_t lengthBits[4]={1,2,4,8};
static const uint32_t lengthAdditions[4]={2,4,8,24};
uint32_t count=readBits(lengthBits[lengthIndex])+lengthAdditions[lengthIndex];
if (count==23)
{
if (readBit())
{
count=readBits(5)+15;
} else {
count=readBits(14)+15;
}
for (uint32_t i=0;i<count;i++)
outputStream.writeByte(readBits(8));
} else {
if (count>23) count--;
uint8_t distanceIndex=distanceDecoder.decode(readBit);
static const uint8_t distanceBits[3]={9,5,14};
static const uint32_t distanceAdditions[3]={32,0,544};
uint32_t distance=readBits(distanceBits[distanceIndex])+distanceAdditions[distanceIndex];
outputStream.copy(distance,count);
}
}
}
}
if (!outputStream.eof()) throw Decompressor::DecompressionError();
if (_isSampled)
DLTADecode::decode(rawData,rawData,0,_rawSize);
}
void CRMDecompressor::decompressImpl(Buffer &rawData,const Buffer &previousData,bool verify)
{
if (rawData.size()!=_rawSize) throw Decompressor::DecompressionError();
return decompressImpl(rawData,verify);
}
}