/* 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 CRMDecompressor::create(const Buffer &packedData,bool exactSizeKnown,bool verify) { return std::make_shared(packedData,0,verify); } std::shared_ptr CRMDecompressor::create(uint32_t hdr,uint32_t recursionLevel,const Buffer &packedData,std::shared_ptr &state,bool verify) { return std::make_shared(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 &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 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 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{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 lengthDecoder { HuffmanCode{1,0b000,0}, HuffmanCode{2,0b010,1}, HuffmanCode{3,0b110,2}, HuffmanCode{3,0b111,3} }; HuffmanDecoder distanceDecoder { HuffmanCode{1,0b00,0}, HuffmanCode{2,0b10,1}, HuffmanCode{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;i23) 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); } }