IMPDecompressor.cpp 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. /* Copyright (C) Teemu Suutari */
  2. #include "IMPDecompressor.hpp"
  3. #include "HuffmanDecoder.hpp"
  4. #include "InputStream.hpp"
  5. #include "OutputStream.hpp"
  6. #include "common/Common.hpp"
  7. #include "common/OverflowCheck.hpp"
  8. namespace ancient::internal
  9. {
  10. static bool readIMPHeader(uint32_t hdr,uint32_t &addition) noexcept
  11. {
  12. switch (hdr)
  13. {
  14. case FourCC("ATN!"):
  15. case FourCC("EDAM"):
  16. case FourCC("IMP!"):
  17. case FourCC("M.H."):
  18. addition=7;
  19. return true;
  20. case FourCC("BDPI"):
  21. addition=0x6e8;
  22. return true;
  23. case FourCC("CHFI"):
  24. addition=0xfe4;
  25. return true;
  26. case FourCC("RDC9"): // Files do not contain checksum
  27. // I haven't got these files to be sure what is the addition
  28. case FourCC("Dupa"):
  29. case FourCC("FLT!"):
  30. case FourCC("PARA"):
  31. addition=0; // disable checksum for now
  32. return true;
  33. default:
  34. return false;
  35. }
  36. }
  37. bool IMPDecompressor::detectHeader(uint32_t hdr) noexcept
  38. {
  39. uint32_t dummy;
  40. return readIMPHeader(hdr,dummy);
  41. }
  42. bool IMPDecompressor::detectHeaderXPK(uint32_t hdr) noexcept
  43. {
  44. return hdr==FourCC("IMPL");
  45. }
  46. std::shared_ptr<Decompressor> IMPDecompressor::create(const Buffer &packedData,bool exactSizeKnown,bool verify)
  47. {
  48. return std::make_shared<IMPDecompressor>(packedData,verify);
  49. }
  50. std::shared_ptr<XPKDecompressor> IMPDecompressor::create(uint32_t hdr,uint32_t recursionLevel,const Buffer &packedData,std::shared_ptr<XPKDecompressor::State> &state,bool verify)
  51. {
  52. return std::make_shared<IMPDecompressor>(hdr,recursionLevel,packedData,state,verify);
  53. }
  54. IMPDecompressor::IMPDecompressor(const Buffer &packedData,bool verify) :
  55. _packedData(packedData)
  56. {
  57. uint32_t hdr=packedData.readBE32(0);
  58. uint32_t checksumAddition;
  59. if (!readIMPHeader(hdr,checksumAddition) || packedData.size()<0x32) throw InvalidFormatError();
  60. _rawSize=packedData.readBE32(4);
  61. _endOffset=packedData.readBE32(8);
  62. if ((_endOffset&1) || _endOffset<0xc || _endOffset+0x32>packedData.size() ||
  63. !_rawSize || !_endOffset ||
  64. _rawSize>getMaxRawSize() || _endOffset>getMaxPackedSize()) throw InvalidFormatError();
  65. uint32_t checksum=packedData.readBE32(_endOffset+0x2e);
  66. if (verify && checksumAddition)
  67. {
  68. // size is divisible by 2
  69. uint32_t sum=checksumAddition;
  70. for (uint32_t i=0;i<_endOffset+0x2e;i+=2)
  71. {
  72. // TODO: slow, optimize
  73. uint16_t tmp=_packedData.readBE16(i);
  74. sum+=uint32_t(tmp);
  75. }
  76. if (checksum!=sum) throw InvalidFormatError();
  77. }
  78. }
  79. IMPDecompressor::IMPDecompressor(uint32_t hdr,uint32_t recursionLevel,const Buffer &packedData,std::shared_ptr<XPKDecompressor::State> &state,bool verify) :
  80. XPKDecompressor(recursionLevel),
  81. _packedData(packedData)
  82. {
  83. if (!detectHeaderXPK(hdr) || packedData.size()<0x2e) throw InvalidFormatError();
  84. _rawSize=packedData.readBE32(4);
  85. _endOffset=packedData.readBE32(8);
  86. if ((_endOffset&1) || _endOffset<0xc || OverflowCheck::sum(_endOffset,0x2eU)>packedData.size()) throw InvalidFormatError();
  87. _isXPK=true;
  88. }
  89. IMPDecompressor::~IMPDecompressor()
  90. {
  91. // nothing needed
  92. }
  93. const std::string &IMPDecompressor::getName() const noexcept
  94. {
  95. static std::string name="IMP: File Imploder";
  96. return name;
  97. }
  98. const std::string &IMPDecompressor::getSubName() const noexcept
  99. {
  100. static std::string name="XPK-IMPL: File Imploder";
  101. return name;
  102. }
  103. size_t IMPDecompressor::getPackedSize() const noexcept
  104. {
  105. return _endOffset+0x32;
  106. }
  107. size_t IMPDecompressor::getRawSize() const noexcept
  108. {
  109. return _rawSize;
  110. }
  111. void IMPDecompressor::decompressImpl(Buffer &rawData,bool verify)
  112. {
  113. if (rawData.size()<_rawSize) throw DecompressionError();
  114. class IMPInputStream
  115. {
  116. public:
  117. IMPInputStream(const Buffer &buffer,size_t startOffset,size_t endOffset) :
  118. _bufPtr(buffer.data()),
  119. _currentOffset(endOffset),
  120. _endOffset(startOffset),
  121. _refOffset(endOffset)
  122. {
  123. if (_currentOffset<_endOffset || _currentOffset>buffer.size() || _endOffset>buffer.size()) throw Decompressor::DecompressionError();
  124. uint8_t markerByte=buffer.read8(_currentOffset+16);
  125. if (!(markerByte&0x80))
  126. {
  127. if (_currentOffset==_endOffset) throw Decompressor::DecompressionError();
  128. _currentOffset--;
  129. }
  130. }
  131. ~IMPInputStream()
  132. {
  133. // nothing needed
  134. }
  135. uint8_t readByte()
  136. {
  137. // streamreader with funny ordering
  138. auto sourceOffset=[&](size_t i)->size_t
  139. {
  140. if (i>=12)
  141. {
  142. return i;
  143. } else {
  144. if (i<4)
  145. {
  146. return i+_refOffset+8;
  147. } else if (i<8) {
  148. return i+_refOffset;
  149. } else {
  150. return i+_refOffset-8;
  151. }
  152. }
  153. };
  154. if (_currentOffset<=_endOffset) throw Decompressor::DecompressionError();
  155. return _bufPtr[sourceOffset(--_currentOffset)];
  156. }
  157. bool eof() const { return _currentOffset==_endOffset; }
  158. private:
  159. const uint8_t *_bufPtr;
  160. size_t _currentOffset;
  161. size_t _endOffset;
  162. size_t _refOffset;
  163. };
  164. IMPInputStream inputStream(_packedData,0,_endOffset);
  165. MSBBitReader<IMPInputStream> bitReader(inputStream);
  166. auto readBits=[&](uint32_t count)->uint32_t
  167. {
  168. return bitReader.readBits8(count);
  169. };
  170. auto readBit=[&]()->uint32_t
  171. {
  172. return bitReader.readBits8(1);
  173. };
  174. auto readByte=[&]()->uint8_t
  175. {
  176. return inputStream.readByte();
  177. };
  178. // the anchor-bit does not seem always to be at the correct place
  179. {
  180. uint8_t halfByte=_packedData.read8(_endOffset+17);
  181. for (uint32_t i=0;i<7;i++)
  182. if (halfByte&(1<<i))
  183. {
  184. bitReader.reset(halfByte>>(i+1),7-i);
  185. break;
  186. }
  187. }
  188. BackwardOutputStream outputStream(rawData,0,_rawSize);
  189. // tables
  190. uint16_t distanceValues[2][4];
  191. for (uint32_t i=0;i<8;i++)
  192. distanceValues[i>>2][i&3]=_packedData.readBE16(_endOffset+18+i*2);
  193. uint8_t distanceBits[3][4];
  194. for (uint32_t i=0;i<12;i++)
  195. distanceBits[i>>2][i&3]=_packedData.read8(_endOffset+34+i);
  196. // length, distance & literal counts are all intertwined
  197. HuffmanDecoder<uint8_t> lldDecoder
  198. {
  199. HuffmanCode<uint8_t>{1,0b00000,0},
  200. HuffmanCode<uint8_t>{2,0b00010,1},
  201. HuffmanCode<uint8_t>{3,0b00110,2},
  202. HuffmanCode<uint8_t>{4,0b01110,3},
  203. HuffmanCode<uint8_t>{5,0b11110,4},
  204. HuffmanCode<uint8_t>{5,0b11111,5}
  205. };
  206. HuffmanDecoder<uint8_t> lldDecoder2
  207. {
  208. HuffmanCode<uint8_t>{1,0b00,0},
  209. HuffmanCode<uint8_t>{2,0b10,1},
  210. HuffmanCode<uint8_t>{2,0b11,2}
  211. };
  212. // finally loop
  213. uint32_t litLength=_packedData.readBE32(_endOffset+12);
  214. for (;;)
  215. {
  216. for (uint32_t i=0;i<litLength;i++) outputStream.writeByte(readByte());
  217. if (outputStream.eof()) break;
  218. // now the intertwined Huffman table reads.
  219. uint32_t i0=lldDecoder.decode(readBit);
  220. uint32_t selector=(i0<4)?i0:3;
  221. uint32_t count=i0+2;
  222. if (count==6)
  223. {
  224. count+=readBits(3);
  225. } else if (count==7) {
  226. count=readByte();
  227. // why this is error? (Well, it just is)
  228. if (!count) throw DecompressionError();
  229. }
  230. static const uint8_t literalLengths[4]={6,10,10,18};
  231. static const uint8_t literalBits[3][4]={
  232. {1,1,1,1},
  233. {2,3,3,4},
  234. {4,5,7,14}};
  235. uint32_t i1=lldDecoder2.decode(readBit);
  236. litLength=i1+i1;
  237. if (litLength==4)
  238. {
  239. litLength=literalLengths[selector];
  240. }
  241. litLength+=readBits(literalBits[i1][selector]);
  242. uint32_t i2=lldDecoder2.decode(readBit);
  243. uint32_t distance=1+((i2)?distanceValues[i2-1][selector]:0)+readBits(distanceBits[i2][selector]);
  244. outputStream.copy(distance,count);
  245. }
  246. }
  247. void IMPDecompressor::decompressImpl(Buffer &rawData,const Buffer &previousData,bool verify)
  248. {
  249. if (_rawSize!=rawData.size()) throw DecompressionError();
  250. return decompressImpl(rawData,verify);
  251. }
  252. }