/* Copyright (C) Teemu Suutari */ #include #include "MMCMPDecompressor.hpp" #include "InputStream.hpp" #include "OutputStream.hpp" #include "common/OverflowCheck.hpp" #include "common/Common.hpp" namespace ancient::internal { bool MMCMPDecompressor::detectHeader(uint32_t hdr) noexcept { return hdr==FourCC("ziRC"); } std::shared_ptr MMCMPDecompressor::create(const Buffer &packedData,bool exactSizeKnown,bool verify) { return std::make_shared(packedData,exactSizeKnown,verify); } MMCMPDecompressor::MMCMPDecompressor(const Buffer &packedData,bool exactSizeKnown,bool verify) : _packedData(packedData) { if (!detectHeader(packedData.readBE32(0)) || packedData.readBE32(4U)!=FourCC("ONia") || packedData.readLE16(8U)!=14U || packedData.size()<24U) throw InvalidFormatError(); _version=packedData.readLE16(10U); _blocks=packedData.readLE16(12U); _blocksOffset=packedData.readLE32(18U); _rawSize=packedData.readLE32(14U); if (OverflowCheck::sum(_blocksOffset,uint32_t(_blocks)*4U)>packedData.size()) throw InvalidFormatError(); _packedSize=0; for (uint32_t i=0;i<_blocks;i++) { uint32_t blockAddr=packedData.readLE32(OverflowCheck::sum(_blocksOffset,i*4U)); if (OverflowCheck::sum(blockAddr,20U)>=packedData.size()) throw InvalidFormatError(); uint32_t blockSize=packedData.readLE32(blockAddr+4U)+uint32_t(packedData.readLE16(blockAddr+12U))*8U+20U; _packedSize=std::max(_packedSize,OverflowCheck::sum(blockAddr,blockSize)); } if (_packedSize>packedData.size()) throw InvalidFormatError(); } MMCMPDecompressor::~MMCMPDecompressor() { // nothing needed } const std::string &MMCMPDecompressor::getName() const noexcept { static std::string name="MMCMP: Music Module Compressor"; return name; } size_t MMCMPDecompressor::getPackedSize() const noexcept { return _packedSize; } size_t MMCMPDecompressor::getRawSize() const noexcept { return _rawSize; } void MMCMPDecompressor::decompressImpl(Buffer &rawData,bool verify) { if (rawData.size()<_rawSize) throw DecompressionError(); // MMCMP allows gaps in data. Although not used in practice still we memset before decompressing to be sure std::memset(rawData.data(),0,rawData.size()); uint8_t *rawDataPtr=rawData.data(); for (uint32_t i=0;i<_blocks;i++) { uint32_t blockAddr=_packedData.readLE32(_blocksOffset+i*4U); uint32_t unpackedBlockSize=_packedData.readLE32(blockAddr); uint32_t packedBlockSize=_packedData.readLE32(blockAddr+4U); uint32_t fileChecksum=_packedData.readLE32(blockAddr+8U); uint32_t subBlocks=_packedData.readLE16(blockAddr+12U); uint16_t flags=_packedData.readLE16(blockAddr+14U); uint32_t packTableSize=_packedData.readLE16(blockAddr+16U); if (packTableSize>packedBlockSize) throw DecompressionError(); uint16_t bitCount=_packedData.readLE16(blockAddr+18U); ForwardInputStream inputStream(_packedData,OverflowCheck::sum(blockAddr,subBlocks*8U,20U,packTableSize),OverflowCheck::sum(blockAddr,subBlocks*8U,20U,packedBlockSize)); LSBBitReader bitReader(inputStream); auto readBits=[&](uint32_t count)->uint32_t { return bitReader.readBits8(count); }; uint32_t currentSubBlock=0; uint32_t outputOffset=0,outputSize=0; auto readNextSubBlock=[&]() { if (currentSubBlock>=subBlocks) throw DecompressionError(); outputOffset=_packedData.readLE32(blockAddr+currentSubBlock*8U+20U); outputSize=_packedData.readLE32(blockAddr+currentSubBlock*8U+24U); if (OverflowCheck::sum(outputOffset,outputSize)>_rawSize) throw DecompressionError(); currentSubBlock++; }; uint32_t checksum=0,checksumPartial=0,checksumRot=0; auto writeByte=[&](uint8_t value,bool allowOverrun=false) { while (!outputSize) { if (allowOverrun && currentSubBlock>=subBlocks) return; readNextSubBlock(); } outputSize--; rawDataPtr[outputOffset++]=value; if (verify) { if (_version>=0x1310) { checksum^=value; checksum=(checksum<<1)|(checksum>>31); } else { checksumPartial|=((uint32_t)value)<=8) throw DecompressionError(); uint8_t oldValue[2]={0,0}; uint32_t chIndex=0; const uint8_t *tablePtr=&_packedData[blockAddr+subBlocks*8U+20U]; for (uint32_t j=0;j=valueThresholds[bitCount]) { uint32_t newBitCount=readBits(extraBits[bitCount])+((value-valueThresholds[bitCount])<=packTableSize) throw DecompressionError(); value=tablePtr[value]; if (flags&0x2U) { // delta value+=oldValue[chIndex]; oldValue[chIndex]=value; if (flags&0x100U) chIndex^=1U; // stereo } writeByte(value); j++; } } else { // 16 bit compression // shameless copy-paste from 8-bit variant, with minor changes static const uint16_t valueThresholds[16]={ 0x1U, 0x3U, 0x7U, 0xfU, 0x1eU, 0x3cU, 0x78U, 0xf0U, 0x1f0U, 0x3f0U, 0x7f0U, 0xff0U,0x1ff0U,0x3ff0U,0x7ff0U,0xfff0U }; static const uint8_t extraBits[16]={4,4,4,4, 3,2,1,0, 0,0,0,0, 0,0,0,0}; if (bitCount>=16) throw DecompressionError(); int16_t oldValue[2]={0,0}; uint32_t chIndex=0; for (uint32_t j=0;j=valueThresholds[bitCount]) { uint32_t newBitCount=readBits(extraBits[bitCount])+((value-valueThresholds[bitCount])<>=1; if (flags&0x2U) { // delta value+=oldValue[chIndex]; oldValue[chIndex]=value; if (flags&0x100U) chIndex^=1U; // stereo } else if (!(flags&0x200U)) value^=0x8000U; // abs16 if (flags&0x400U) { // big ending writeByte(value>>8U); writeByte(value,true); } else { // little endian writeByte(value); writeByte(value>>8U,true); } j+=2; } } if (verify && checksum!=fileChecksum) throw VerificationError(); } } }