1
0

ContainerMMCMP.cpp 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. /*
  2. * ContainerMMCMP.cpp
  3. * ------------------
  4. * Purpose: Handling of MMCMP compressed modules
  5. * Notes : (currently none)
  6. * Authors: Olivier Lapicque
  7. * OpenMPT Devs
  8. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  9. */
  10. #include "stdafx.h"
  11. #include "../common/FileReader.h"
  12. #include "Container.h"
  13. #include "Sndfile.h"
  14. #include "BitReader.h"
  15. OPENMPT_NAMESPACE_BEGIN
  16. #if !defined(MPT_WITH_ANCIENT)
  17. #ifdef MPT_ALL_LOGGING
  18. #define MMCMP_LOG
  19. #endif
  20. struct MMCMPFileHeader
  21. {
  22. char id[8]; // "ziRCONia"
  23. uint16le hdrsize; // size of all the remaining header data
  24. uint16le version;
  25. uint16le nblocks;
  26. uint32le filesize;
  27. uint32le blktable;
  28. uint8le glb_comp;
  29. uint8le fmt_comp;
  30. bool Validate() const
  31. {
  32. if(std::memcmp(id, "ziRCONia", 8) != 0)
  33. return false;
  34. if(hdrsize != 14)
  35. return false;
  36. if(nblocks == 0)
  37. return false;
  38. if(filesize == 0)
  39. return false;
  40. if(filesize >= 0x80000000)
  41. return false;
  42. if(blktable < sizeof(MMCMPFileHeader))
  43. return false;
  44. return true;
  45. }
  46. };
  47. MPT_BINARY_STRUCT(MMCMPFileHeader, 24)
  48. struct MMCMPBlock
  49. {
  50. uint32le unpk_size;
  51. uint32le pk_size;
  52. uint32le xor_chk;
  53. uint16le sub_blk;
  54. uint16le flags;
  55. uint16le tt_entries;
  56. uint16le num_bits;
  57. };
  58. MPT_BINARY_STRUCT(MMCMPBlock, 20)
  59. struct MMCMPSubBlock
  60. {
  61. uint32le position;
  62. uint32le size;
  63. bool Validate(std::vector<char> &unpackedData, const uint32 unpackedSize) const
  64. {
  65. if(position >= unpackedSize)
  66. return false;
  67. if(size > unpackedSize)
  68. return false;
  69. if(size > unpackedSize - position)
  70. return false;
  71. if(size == 0)
  72. return false;
  73. if(unpackedData.size() < position + size)
  74. unpackedData.resize(position + size);
  75. return true;
  76. }
  77. };
  78. MPT_BINARY_STRUCT(MMCMPSubBlock, 8)
  79. enum MMCMPFlags : uint16
  80. {
  81. MMCMP_COMP = 0x0001,
  82. MMCMP_DELTA = 0x0002,
  83. MMCMP_16BIT = 0x0004,
  84. MMCMP_STEREO = 0x0100,
  85. MMCMP_ABS16 = 0x0200,
  86. MMCMP_ENDIAN = 0x0400,
  87. };
  88. static constexpr uint8 MMCMP8BitCommands[8] =
  89. {
  90. 0x01, 0x03, 0x07, 0x0F, 0x1E, 0x3C, 0x78, 0xF8
  91. };
  92. static constexpr uint8 MMCMP8BitFetch[8] =
  93. {
  94. 3, 3, 3, 3, 2, 1, 0, 0
  95. };
  96. static constexpr uint16 MMCMP16BitCommands[16] =
  97. {
  98. 0x01, 0x03, 0x07, 0x0F, 0x1E, 0x3C, 0x78, 0xF0,
  99. 0x1F0, 0x3F0, 0x7F0, 0xFF0, 0x1FF0, 0x3FF0, 0x7FF0, 0xFFF0
  100. };
  101. static constexpr uint8 MMCMP16BitFetch[16] =
  102. {
  103. 4, 4, 4, 4, 3, 2, 1, 0,
  104. 0, 0, 0, 0, 0, 0, 0, 0
  105. };
  106. CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMMCMP(MemoryFileReader file, const uint64 *pfilesize)
  107. {
  108. MMCMPFileHeader mfh;
  109. if(!file.ReadStruct(mfh))
  110. return ProbeWantMoreData;
  111. if(!mfh.Validate())
  112. return ProbeFailure;
  113. MPT_UNREFERENCED_PARAMETER(pfilesize);
  114. return ProbeSuccess;
  115. }
  116. bool UnpackMMCMP(std::vector<ContainerItem> &containerItems, FileReader &file, ContainerLoadingFlags loadFlags)
  117. {
  118. file.Rewind();
  119. containerItems.clear();
  120. MMCMPFileHeader mfh;
  121. if(!file.ReadStruct(mfh))
  122. return false;
  123. if(!mfh.Validate())
  124. return false;
  125. if(loadFlags == ContainerOnlyVerifyHeader)
  126. return true;
  127. if(!file.LengthIsAtLeast(mfh.blktable))
  128. return false;
  129. if(!file.LengthIsAtLeast(mfh.blktable + 4 * mfh.nblocks))
  130. return false;
  131. containerItems.emplace_back();
  132. containerItems.back().data_cache = std::make_unique<std::vector<char> >();
  133. auto &unpackedData = *(containerItems.back().data_cache);
  134. // Generally it's not so simple to establish an upper limit for the uncompressed data size (blocks can be reused, etc.),
  135. // so we just reserve a realistic amount of memory.
  136. const uint32 unpackedSize = mfh.filesize;
  137. unpackedData.reserve(std::min(unpackedSize, std::min(mpt::saturate_cast<uint32>(file.GetLength()), uint32_max / 20u) * 20u));
  138. // 8-bit deltas
  139. uint8 ptable[256] = { 0 };
  140. std::vector<MMCMPSubBlock> subblks;
  141. for(uint32 nBlock = 0; nBlock < mfh.nblocks; nBlock++)
  142. {
  143. if(!file.Seek(mfh.blktable + 4 * nBlock))
  144. return false;
  145. if(!file.CanRead(4))
  146. return false;
  147. uint32 blkPos = file.ReadUint32LE();
  148. if(!file.Seek(blkPos))
  149. return false;
  150. MMCMPBlock blk;
  151. if(!file.ReadStruct(blk))
  152. return false;
  153. if(!file.ReadVector(subblks, blk.sub_blk))
  154. return false;
  155. const MMCMPSubBlock *psubblk = blk.sub_blk > 0 ? subblks.data() : nullptr;
  156. if(blkPos + sizeof(MMCMPBlock) + blk.sub_blk * sizeof(MMCMPSubBlock) >= file.GetLength())
  157. return false;
  158. uint32 memPos = blkPos + sizeof(MMCMPBlock) + blk.sub_blk * sizeof(MMCMPSubBlock);
  159. #ifdef MMCMP_LOG
  160. MPT_LOG_GLOBAL(LogDebug, "MMCMP", MPT_UFORMAT("block {}: flags={} sub_blocks={}")(nBlock, mpt::ufmt::HEX0<4>(static_cast<uint16>(blk.flags)), static_cast<uint16>(blk.sub_blk)));
  161. MPT_LOG_GLOBAL(LogDebug, "MMCMP", MPT_UFORMAT(" pksize={} unpksize={}")(static_cast<uint32>(blk.pk_size), static_cast<uint32>(blk.unpk_size)));
  162. MPT_LOG_GLOBAL(LogDebug, "MMCMP", MPT_UFORMAT(" tt_entries={} num_bits={}")(static_cast<uint16>(blk.tt_entries), static_cast<uint16>(blk.num_bits)));
  163. #endif
  164. if(!(blk.flags & MMCMP_COMP))
  165. {
  166. // Data is not packed
  167. for(uint32 i = 0; i < blk.sub_blk; i++)
  168. {
  169. if(!psubblk)
  170. return false;
  171. if(!psubblk->Validate(unpackedData, unpackedSize))
  172. return false;
  173. #ifdef MMCMP_LOG
  174. MPT_LOG_GLOBAL(LogDebug, "MMCMP", MPT_UFORMAT(" Unpacked sub-block {}: offset {}, size={}")(i, static_cast<uint32>(psubblk->position), static_cast<uint32>(psubblk->size)));
  175. #endif
  176. if(!file.Seek(memPos))
  177. return false;
  178. if(file.ReadRaw(mpt::span(&(unpackedData[psubblk->position]), psubblk->size)).size() != psubblk->size)
  179. return false;
  180. psubblk++;
  181. }
  182. } else if(blk.flags & MMCMP_16BIT)
  183. {
  184. // Data is 16-bit packed
  185. uint32 subblk = 0;
  186. if(!psubblk)
  187. return false;
  188. if(!psubblk[subblk].Validate(unpackedData, unpackedSize))
  189. return false;
  190. char *pDest = &(unpackedData[psubblk[subblk].position]);
  191. uint32 dwSize = psubblk[subblk].size & ~1u;
  192. if(!dwSize)
  193. return false;
  194. uint32 dwPos = 0;
  195. uint32 numbits = blk.num_bits;
  196. uint32 oldval = 0;
  197. #ifdef MMCMP_LOG
  198. MPT_LOG_GLOBAL(LogDebug, "MMCMP", MPT_UFORMAT(" 16-bit block: pos={} size={} {} {}")(psubblk->position, psubblk->size, (blk.flags & MMCMP_DELTA) ? U_("DELTA ") : U_(""), (blk.flags & MMCMP_ABS16) ? U_("ABS16 ") : U_("")));
  199. #endif
  200. if(!file.Seek(memPos + blk.tt_entries)) return false;
  201. if(!file.CanRead(blk.pk_size - blk.tt_entries)) return false;
  202. BitReader bitFile{ file.GetChunk(blk.pk_size - blk.tt_entries) };
  203. try
  204. {
  205. while (subblk < blk.sub_blk)
  206. {
  207. uint32 newval = 0x10000;
  208. uint32 d = bitFile.ReadBits(numbits + 1);
  209. uint32 command = MMCMP16BitCommands[numbits & 0x0F];
  210. if(d >= command)
  211. {
  212. uint32 nFetch = MMCMP16BitFetch[numbits & 0x0F];
  213. uint32 newbits = bitFile.ReadBits(nFetch) + ((d - command) << nFetch);
  214. if(newbits != numbits)
  215. {
  216. numbits = newbits & 0x0F;
  217. } else if((d = bitFile.ReadBits(4)) == 0x0F)
  218. {
  219. if(bitFile.ReadBits(1))
  220. break;
  221. newval = 0xFFFF;
  222. } else
  223. {
  224. newval = 0xFFF0 + d;
  225. }
  226. } else
  227. {
  228. newval = d;
  229. }
  230. if(newval < 0x10000)
  231. {
  232. newval = (newval & 1) ? (uint32)(-(int32)((newval + 1) >> 1)) : (uint32)(newval >> 1);
  233. if(blk.flags & MMCMP_DELTA)
  234. {
  235. newval += oldval;
  236. oldval = newval;
  237. } else if(!(blk.flags & MMCMP_ABS16))
  238. {
  239. newval ^= 0x8000;
  240. }
  241. if(blk.flags & MMCMP_ENDIAN)
  242. {
  243. pDest[dwPos + 0] = static_cast<uint8>(newval >> 8);
  244. pDest[dwPos + 1] = static_cast<uint8>(newval & 0xFF);
  245. } else
  246. {
  247. pDest[dwPos + 0] = static_cast<uint8>(newval & 0xFF);
  248. pDest[dwPos + 1] = static_cast<uint8>(newval >> 8);
  249. }
  250. dwPos += 2;
  251. }
  252. if(dwPos >= dwSize)
  253. {
  254. subblk++;
  255. dwPos = 0;
  256. if(!(subblk < blk.sub_blk))
  257. break;
  258. if(!psubblk[subblk].Validate(unpackedData, unpackedSize))
  259. return false;
  260. dwSize = psubblk[subblk].size & ~1u;
  261. if(!dwSize)
  262. return false;
  263. pDest = &(unpackedData[psubblk[subblk].position]);
  264. }
  265. }
  266. } catch(const BitReader::eof &)
  267. {
  268. }
  269. } else
  270. {
  271. // Data is 8-bit packed
  272. uint32 subblk = 0;
  273. if(!psubblk)
  274. return false;
  275. if(!psubblk[subblk].Validate(unpackedData, unpackedSize))
  276. return false;
  277. char *pDest = &(unpackedData[psubblk[subblk].position]);
  278. uint32 dwSize = psubblk[subblk].size;
  279. uint32 dwPos = 0;
  280. uint32 numbits = blk.num_bits;
  281. uint32 oldval = 0;
  282. if(blk.tt_entries > sizeof(ptable)
  283. || !file.Seek(memPos)
  284. || file.ReadRaw(mpt::span(ptable, blk.tt_entries)).size() < blk.tt_entries)
  285. return false;
  286. if(!file.CanRead(blk.pk_size - blk.tt_entries)) return false;
  287. BitReader bitFile{ file.GetChunk(blk.pk_size - blk.tt_entries) };
  288. try
  289. {
  290. while (subblk < blk.sub_blk)
  291. {
  292. uint32 newval = 0x100;
  293. uint32 d = bitFile.ReadBits(numbits + 1);
  294. uint32 command = MMCMP8BitCommands[numbits & 0x07];
  295. if(d >= command)
  296. {
  297. uint32 nFetch = MMCMP8BitFetch[numbits & 0x07];
  298. uint32 newbits = bitFile.ReadBits(nFetch) + ((d - command) << nFetch);
  299. if(newbits != numbits)
  300. {
  301. numbits = newbits & 0x07;
  302. } else if((d = bitFile.ReadBits(3)) == 7)
  303. {
  304. if(bitFile.ReadBits(1))
  305. break;
  306. newval = 0xFF;
  307. } else
  308. {
  309. newval = 0xF8 + d;
  310. }
  311. } else
  312. {
  313. newval = d;
  314. }
  315. if(newval < sizeof(ptable))
  316. {
  317. int n = ptable[newval];
  318. if(blk.flags & MMCMP_DELTA)
  319. {
  320. n += oldval;
  321. oldval = n;
  322. }
  323. pDest[dwPos++] = static_cast<uint8>(n);
  324. }
  325. if(dwPos >= dwSize)
  326. {
  327. subblk++;
  328. dwPos = 0;
  329. if(!(subblk < blk.sub_blk))
  330. break;
  331. if(!psubblk[subblk].Validate(unpackedData, unpackedSize))
  332. return false;
  333. dwSize = psubblk[subblk].size;
  334. pDest = &(unpackedData[psubblk[subblk].position]);
  335. }
  336. }
  337. } catch(const BitReader::eof &)
  338. {
  339. }
  340. }
  341. }
  342. containerItems.back().file = FileReader(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(unpackedData)));
  343. return true;
  344. }
  345. #endif // !MPT_WITH_ANCIENT
  346. OPENMPT_NAMESPACE_END