/* Copyright (C) Teemu Suutari */ #include "ZENODecompressor.hpp" #include "InputStream.hpp" #include "OutputStream.hpp" #include "common/Common.hpp" namespace ancient::internal { bool ZENODecompressor::detectHeaderXPK(uint32_t hdr) noexcept { return hdr==FourCC("ZENO"); } std::shared_ptr ZENODecompressor::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); } ZENODecompressor::ZENODecompressor(uint32_t hdr,uint32_t recursionLevel,const Buffer &packedData,std::shared_ptr &state,bool verify) : XPKDecompressor(recursionLevel), _packedData(packedData) { if (!detectHeaderXPK(hdr) || _packedData.size()<6) throw Decompressor::InvalidFormatError(); // first 4 bytes is checksum for password. It needs to be zero if (_packedData.readBE32(0)) throw Decompressor::InvalidFormatError(); _maxBits=_packedData.read8(4); if (_maxBits<9 || _maxBits>20) throw Decompressor::InvalidFormatError(); _startOffset=uint32_t(_packedData.read8(5))+6; if (_startOffset>=_packedData.size()) throw Decompressor::InvalidFormatError(); } ZENODecompressor::~ZENODecompressor() { // nothing needed } const std::string &ZENODecompressor::getSubName() const noexcept { static std::string name="XPK-ZENO: LZW-compressor"; return name; } void ZENODecompressor::decompressImpl(Buffer &rawData,const Buffer &previousData,bool verify) { ForwardInputStream inputStream(_packedData,_startOffset,_packedData.size()); MSBBitReader bitReader(inputStream); auto readBits=[&](uint32_t count)->uint32_t { return bitReader.readBits8(count); }; ForwardOutputStream outputStream(rawData,0,rawData.size()); uint32_t maxCode=1<<_maxBits; uint32_t stackLength=5000; // magic constant auto prefix=std::make_unique(maxCode-258); auto suffix=std::make_unique(maxCode-258); auto stack=std::make_unique(stackLength); uint32_t freeIndex,codeBits,prevCode,newCode; auto init=[&]() { codeBits=9; freeIndex=258; }; init(); prevCode=readBits(9); newCode=prevCode; suffix[freeIndex-258]=0; prefix[freeIndex-258]=0; freeIndex++; outputStream.writeByte(newCode); while (!outputStream.eof()) { if (freeIndex+3>=(1U<=258) { do { if (stackPos+1>=stackLength || tmp>=freeIndex) throw Decompressor::DecompressionError(); stack[stackPos++]=suffix[tmp-258]; tmp=prefix[tmp-258]; } while (tmp>=258); stack[stackPos++]=newCode=tmp; while (stackPos) outputStream.writeByte(stack[--stackPos]); } else { newCode=tmp; outputStream.writeByte(tmp); if (stackPos) outputStream.writeByte(stack[0]); } } if (freeIndex