/* Copyright (C) Teemu Suutari */ #include "SXSCDecompressor.hpp" #include "InputStream.hpp" #include "OutputStream.hpp" #include "DLTADecode.hpp" #include "common/MemoryBuffer.hpp" #include "common/Common.hpp" namespace ancient::internal { SXSCDecompressor::SXSCReader::SXSCReader(ForwardInputStream &stream) : _reader(stream) { // nothing needed } SXSCDecompressor::SXSCReader::~SXSCReader() { // nothing needed } uint32_t SXSCDecompressor::SXSCReader::readBit() { return _reader.readBits8(1); } bool SXSCDecompressor::detectHeaderXPK(uint32_t hdr) noexcept { return hdr==FourCC("SASC")||hdr==FourCC("SHSC"); } std::shared_ptr SXSCDecompressor::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); } SXSCDecompressor::SXSCDecompressor(uint32_t hdr,uint32_t recursionLevel,const Buffer &packedData,std::shared_ptr &state,bool verify) : XPKDecompressor(recursionLevel), _packedData(packedData), _isHSC(hdr==FourCC("SHSC")) { if (!detectHeaderXPK(hdr)) throw Decompressor::InvalidFormatError(); } SXSCDecompressor::~SXSCDecompressor() { // nothing needed } const std::string &SXSCDecompressor::getSubName() const noexcept { static std::string nameASC="XPK-SASC: LZ-compressor with arithmetic and delta encoding"; static std::string nameHSC="XPK-SHSC: Context modeling compressor"; return _isHSC?nameHSC:nameASC; } void SXSCDecompressor::decompressASC(Buffer &rawData,ForwardInputStream &inputStream) { ForwardOutputStream outputStream(rawData,0,rawData.size()); uint16_t bitReaderInitialValue; bitReaderInitialValue=inputStream.readByte()<<8; bitReaderInitialValue|=inputStream.readByte(); SXSCReader bitReader(inputStream); RangeDecoder arithDecoder(bitReader,bitReaderInitialValue); // decoder for literal, copy, end decision // two thresholds -> 3 symbols, last symbol is break with size of 1 uint16_t bitThreshold1[4]={40,40,40,40}; uint16_t bitThreshold2[4]={40,40,40,40}; uint32_t bitPos=0; // generics for the other decoder auto tableElements=[](auto &table)->uint16_t { return (sizeof(table)/sizeof(*table)+1)>>1; }; auto initTable=[](auto &table,uint16_t initialValue) { constexpr uint32_t length=(sizeof(table)/sizeof(*table)+1)>>1; for (uint32_t i=0;i>1; for (uint32_t i=index;i>1)+length) table[i]+=value; if (table[length*2-2]>=max) { for (uint32_t i=0;i1) table[i]>>=1; for (uint32_t j=0,i=length;iuint16_t { constexpr uint32_t length=(sizeof(table)/sizeof(*table)+1)>>1; return table[length*2-2]; }; auto decodeSymbol=[](auto &table,uint16_t &value)->uint16_t { constexpr uint32_t length=(sizeof(table)/sizeof(*table)+1)>>1; uint32_t threshold=0; uint32_t i=length*2-4; while (i>=length) { uint32_t child=(i-length)<<1; if (value-threshold>=table[i]) { threshold+=table[i]; child+=2; } i=child; } if (value-threshold>=table[i]) { threshold+=table[i]; i++; } value=threshold; return i; }; // literal decoder uint16_t litInitial[256*2-1]; uint16_t litDynamic[256*2-1]; uint16_t litThreshold=1; initTable(litInitial,1); initTable(litDynamic,0); // distance / length decoder uint16_t distanceCodes[16*2-1]; uint16_t countInitial[64*2-1]; uint16_t countDynamic[64*2-1]; uint16_t countThreshold=8; initTable(distanceCodes,0); initTable(countInitial,1); initTable(countDynamic,0); updateTable(distanceCodes,6000,0,24); uint32_t distanceIndex=0; auto twoStepArithDecoder=[&](auto &initialTable,auto &dynamicTable,uint16_t &threshold,uint16_t max,uint16_t step,uint16_t updateRange)->uint16_t { uint16_t value=arithDecoder.decode(tableSize(dynamicTable)+threshold); uint16_t ret; if (valueupdateRange?ret-updateRange:0;istep?threshold-step:1; return ret; }; for (;;) { uint16_t bitSize=bitThreshold1[bitPos]+bitThreshold2[bitPos]; uint16_t bitValue=arithDecoder.decode(bitSize+1); if (bitValue==bitSize) break; bool bit=bitValue=6000) { if (!(bitThreshold1[bitPos]>>=1)) bitThreshold1[bitPos]=1; if (!(bitThreshold2[bitPos]>>=1)) bitThreshold2[bitPos]=1; } bitPos=(bitPos<<1&2)|(bit?0:1); if (bit) { // literal outputStream.writeByte(uint8_t(twoStepArithDecoder(litInitial,litDynamic,litThreshold,1000,1,8))); } else { // copy while (outputStream.getOffset()>(1ULL<=2) { uint16_t minRange=1<<(distanceBits-1); uint16_t range=distanceIndex==distanceBits?static_cast(std::min(outputStream.getOffset(),size_t(31200U)))-minRange:minRange; distance=arithDecoder.decode(range); arithDecoder.scale(distance,distance+1,range); distance+=minRange; } distance++; uint32_t count=twoStepArithDecoder(countInitial,countDynamic,countThreshold,6000,8,4); if (count==15) { count=783; } else if (count>=16) { uint16_t value=arithDecoder.decode(16); arithDecoder.scale(value,value+1,16); count=((count-16)<<4)+value+15; } count+=3; outputStream.copy(distance,count); } } if (!outputStream.eof()) throw Decompressor::DecompressionError(); } template class CheckedArray { public: CheckedArray() : _memory(length*sizeof(T)) { // nothing needed } ~CheckedArray() { // nothing needed } T &operator[](size_t i) { if (i>=length) throw Decompressor::DecompressionError(); return _memory.cast()[i]; } const T &operator[](size_t i) const { if (i>=length) throw Decompressor::DecompressionError(); return _memory.cast()[i]; } private: MemoryBuffer _memory; }; // The horror. It needs to follow exactly the original logic, even if that logic is not very good. void SXSCDecompressor::decompressHSC(Buffer &rawData,ForwardInputStream &inputStream) { struct Model { uint8_t context[4]; uint16_t hashPointer; uint16_t expiryPrevious; uint16_t expiryNext; uint16_t frequencyTotal; uint16_t escapeFrequency; uint8_t contextLength; uint8_t characterCount; uint8_t refreshCounter; }; struct Frequency { uint16_t frequency; uint16_t next; uint8_t character; }; struct HashItem { uint16_t data; uint16_t random; }; ForwardOutputStream outputStream(rawData,0,rawData.size()); uint16_t bitReaderInitialValue; bitReaderInitialValue=inputStream.readByte()<<8; bitReaderInitialValue|=inputStream.readByte(); SXSCReader bitReader(inputStream); RangeDecoder arithDecoder(bitReader,bitReaderInitialValue); uint8_t maxContextLength=4; int16_t dropCount=2500; int16_t contextSearchLength=0; CheckedArray models{}; for (uint32_t i=0;i<10000;i++) { auto &m=models[i]; for (uint32_t j=0;j<4;j++) m.context[j]=0; m.hashPointer=0; m.expiryPrevious=i-1; m.expiryNext=i+1; m.frequencyTotal=0; m.escapeFrequency=0; m.contextLength=0xffU; m.characterCount=0; m.refreshCounter=0; } uint8_t currentContext[4]; for (uint32_t i=0;i<4;i++) currentContext[i]=0; uint16_t firstExpiry=0,lastExpiry=9999; uint16_t freeBlockPointer=10000; uint16_t releaseBlock=0; CheckedArray frequencies{}; for (uint32_t i=0;i<32760;i++) { auto &f=frequencies[i]; f.frequency=0; f.next=(i>=10000&&i<32759)?i+1:0xffffU; f.character=0; } CheckedArray hashes{}; for (uint32_t i=0,j=10;i<0x4000U;i++) { auto &h=hashes[i]; h.data=0xffffU; // constants used are 2147483647 % / 16807 int32_t integerPart=j/127773U; int32_t fractionalPart=j%127773U; int32_t tmp=16807*fractionalPart-2836*integerPart; j=tmp<0?tmp+0x7fff'ffff:tmp; h.random=j&0x3fffU; } uint16_t hashStack[5]; for (uint32_t i=0;i<5;i++) hashStack[i]=0; bool characterMask[256]; uint8_t characterMaskStack[256]; for (uint32_t i=0;i<256;i++) { characterMask[i]=false; characterMaskStack[i]=0; } int16_t characterMaskStackPointer=0; uint8_t initialEscapeChar[5]; for (uint32_t i=0;i<5;i++) initialEscapeChar[i]=i?15:16; uint8_t escapeCharacterCounter=0; uint16_t stackPointer=0; uint16_t contextPointer[5]; uint16_t frequencyArrayIndex[5]; for (uint32_t i=0;i<5;i++) { contextPointer[i]=0; frequencyArrayIndex[i]=0; } auto loopBreaker=[](uint16_t i) { if (i>=0x8000U) throw Decompressor::DecompressionError(); }; auto findNext=[&]()->uint16_t { for (int32_t i=contextSearchLength-1;i>=0;i--) { for (uint32_t lb=0,j=hashes[hashStack[i]].data;j!=0xffffU;j=models[j].hashPointer,loopBreaker(lb++)) { if (i==models[j].contextLength) { if ([&]()->bool { for (int32_t k=0;k=value) break; i++; } arithDecoder.scale(i,i+1,size); break; } // madness!!! auto getEscFrequency=[&](uint16_t value,uint16_t i)->uint16_t { auto &model=models[i]; if (model.frequencyTotal==1) return initialEscapeChar[model.contextLength]>=16?2:1; if (model.characterCount==0xffU) return 1; uint16_t tmp=uint16_t(model.characterCount)*2+2; if (model.characterCount && tmp>=model.frequencyTotal) { value=int32_t(value)*tmp/model.frequencyTotal; if (model.characterCount+1==model.frequencyTotal) value+=tmp>>2; } if (!value) value++; return value; }; int16_t freq=0,escapeFreq=0; int16_t currentFrequency=0,frequencyTotal=0; auto decodeCf=[&](uint16_t shift,bool cmCondition)->uint16_t { freq<<=shift; uint16_t i,value=arithDecoder.decode(freq+escapeFreq)>>shift; uint32_t lb=0; for (i=index;i!=0xffffU;i=frequencies[i].next,loopBreaker(lb++)) { auto &frequency=frequencies[i]; if (cmCondition||!characterMask[frequency.character]) { if (frequencyTotal+frequency.frequency<=value) { frequencyTotal+=frequency.frequency; } else { currentFrequency=frequency.frequency<bool { if (chPos==0xffffU) { arithDecoder.scale(freq,freq+escapeFreq,freq+escapeFreq); if (models[index].frequencyTotal==1 && initialEscapeChar[models[index].contextLength]<32) initialEscapeChar[models[index].contextLength]++; uint16_t prevI=0; for (uint16_t lb=0,i=index;i!=0xffffU;prevI=i,i=frequencies[i].next,loopBreaker(lb++)) { auto &frequency=frequencies[i]; if (cmCondition||!characterMask[frequency.character]) { if (characterMaskStackPointer==256) throw Decompressor::DecompressionError(); characterMaskStack[characterMaskStackPointer++]=frequency.character; characterMask[frequency.character]=true; } } contextPointer[insertPos]=index|0x8000U; frequencyArrayIndex[insertPos]=prevI; ch=256; return true; } else { arithDecoder.scale(frequencyTotal,frequencyTotal+currentFrequency,freq+escapeFreq); if (models[index].frequencyTotal==1 && initialEscapeChar[models[index].contextLength]) initialEscapeChar[models[index].contextLength]--; contextPointer[insertPos]=index; frequencyArrayIndex[insertPos]=chPos; ch=frequencies[chPos].character; return false; } }; if (characterMaskStackPointer) { for (uint16_t lb=0,i=index;i!=0xffffU;i=frequencies[i].next,loopBreaker(lb++)) { auto &frequency=frequencies[i]; if (!characterMask[frequency.character]) { freq+=frequency.frequency; if (frequency.frequency<3) escapeFreq++; } } escapeFreq=getEscFrequency(escapeFreq,index); uint16_t chPos=decodeCf(0,false); if (stackPointer==5) throw Decompressor::DecompressionError(); if (!decodeCh(chPos,stackPointer,false)) { if (escapeCharacterCounter==10) throw Decompressor::DecompressionError(); escapeCharacterCounter++; } stackPointer++; } else { freq=models[index].frequencyTotal; escapeFreq=getEscFrequency(models[index].escapeFrequency,index); uint16_t chPos=decodeCf((escapeCharacterCounter>=5)?(freq<5 && escapeCharacterCounter==10)?2:1:0,true); stackPointer=1; if (decodeCh(chPos,0,true)) { escapeCharacterCounter=0; } else { if (escapeCharacterCounter<10) escapeCharacterCounter++; } } if (ch!=256) { if (index!=firstExpiry) { auto &model=models[index]; if (index==lastExpiry) { lastExpiry=model.expiryPrevious; } else { models[model.expiryNext].expiryPrevious=model.expiryPrevious; models[model.expiryPrevious].expiryNext=model.expiryNext; } models[firstExpiry].expiryPrevious=index; model.expiryNext=firstExpiry; firstExpiry=index; } break; } } if (ch==256) break; while (stackPointer) { uint16_t freqIndex=frequencyArrayIndex[--stackPointer]; uint16_t pointer=contextPointer[stackPointer]; auto &model=models[pointer&0x7fffU]; if (pointer&0x8000U) { if (freeBlockPointer==0xffffU) { for (uint16_t i=0;i<=stackPointer;) { // yuck uint32_t lb=0; do { releaseBlock=(releaseBlock!=9999U)?releaseBlock+1:0; loopBreaker(lb++); } while (frequencies[releaseBlock].next==0xffffU); for (i=0;i<=stackPointer;i++) if ((contextPointer[i]&0x7fffU)==releaseBlock) break; } auto &frequencyRB=frequencies[releaseBlock]; auto &modelRB=models[releaseBlock]; uint16_t f=frequencyRB.frequency; for (uint16_t lb=0,i=frequencyRB.next;i!=0xffffU;i=frequencies[i].next,loopBreaker(lb++)) if (frequencies[i].frequencyfrequencies[freqIndex].frequency*2) { model.refreshCounter--; } else if (model.refreshCounter<4) { model.refreshCounter++; } // one ugly scaler if (!model.refreshCounter||model.frequencyTotal>=8000) { model.refreshCounter++; model.escapeFrequency=0; model.frequencyTotal=0; for (uint16_t lb=0,i=pointer&0x7fffU;i!=0xffffU;i=frequencies[i].next,loopBreaker(lb++)) { if (frequencies[i].frequency>1) { uint16_t tmp=frequencies[i].frequency>>=1; model.frequencyTotal+=tmp; if (tmp<3) model.escapeFrequency++; } else { model.escapeFrequency++; model.frequencyTotal++; } } } } uint8_t maxLength=maxContextLength+1; while (maxLength-->minLength) { auto hash=[&](const uint8_t *block,uint32_t length)->uint16_t { uint16_t ret=0; for (uint32_t i=0;i=2); std::unique_ptr tmpBuffer; if (needsTmpBuffer) tmpBuffer=std::make_unique(rawData.size()); if (_isHSC) decompressHSC(needsTmpBuffer?*tmpBuffer:rawData,inputStream); else decompressASC(needsTmpBuffer?*tmpBuffer:rawData,inputStream); // Mono, High byte only // also includes de-interleaving auto deltaDecode16BE=[&]() { size_t length=rawData.size(); const uint8_t *src=tmpBuffer->data(); const uint8_t *midSrc=&src[length>>1]; uint8_t *dest=rawData.data(); uint8_t ctr=0; for (size_t i=0,j=0;jdata(); const uint8_t *midSrc=&src[length>>1]; uint8_t *dest=rawData.data(); uint8_t ctr=0; for (size_t i=0,j=0;j