1
0

ContainerXPK.cpp 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. /*
  2. * ContainerXPK.cpp
  3. * ----------------
  4. * Purpose: Handling of XPK 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 <stdexcept>
  15. OPENMPT_NAMESPACE_BEGIN
  16. #if !defined(MPT_WITH_ANCIENT)
  17. #ifdef MPT_ALL_LOGGING
  18. #define MMCMP_LOG
  19. #endif
  20. struct XPKFILEHEADER
  21. {
  22. char XPKF[4];
  23. uint32be SrcLen;
  24. char SQSH[4];
  25. uint32be DstLen;
  26. char Name[16];
  27. uint32be Reserved;
  28. };
  29. MPT_BINARY_STRUCT(XPKFILEHEADER, 36)
  30. struct XPK_error : public std::range_error
  31. {
  32. XPK_error() : std::range_error("invalid XPK data") { }
  33. };
  34. struct XPK_BufferBounds
  35. {
  36. const uint8 *pSrcBeg;
  37. std::size_t SrcSize;
  38. inline uint8 SrcRead(std::size_t index)
  39. {
  40. if(index >= SrcSize) throw XPK_error();
  41. return pSrcBeg[index];
  42. }
  43. };
  44. static int32 bfextu(std::size_t p, int32 bo, int32 bc, XPK_BufferBounds &bufs)
  45. {
  46. uint32 r;
  47. p += bo / 8;
  48. r = bufs.SrcRead(p); p++;
  49. r <<= 8;
  50. r |= bufs.SrcRead(p); p++;
  51. r <<= 8;
  52. r |= bufs.SrcRead(p);
  53. r <<= bo % 8;
  54. r &= 0xffffff;
  55. r >>= 24 - bc;
  56. return r;
  57. }
  58. static int32 bfexts(std::size_t p, int32 bo, int32 bc, XPK_BufferBounds &bufs)
  59. {
  60. uint32 r;
  61. p += bo / 8;
  62. r = bufs.SrcRead(p); p++;
  63. r <<= 8;
  64. r |= bufs.SrcRead(p); p++;
  65. r <<= 8;
  66. r |= bufs.SrcRead(p);
  67. r <<= (bo % 8) + 8;
  68. return mpt::rshift_signed(static_cast<int32>(r), 32 - bc);
  69. }
  70. static uint8 XPK_ReadTable(int32 index)
  71. {
  72. static constexpr uint8 xpk_table[] = {
  73. 2,3,4,5,6,7,8,0,3,2,4,5,6,7,8,0,4,3,5,2,6,7,8,0,5,4,6,2,3,7,8,0,6,5,7,2,3,4,8,0,7,6,8,2,3,4,5,0,8,7,6,2,3,4,5,0
  74. };
  75. if(index < 0) throw XPK_error();
  76. if(static_cast<std::size_t>(index) >= std::size(xpk_table)) throw XPK_error();
  77. return xpk_table[index];
  78. }
  79. static bool XPK_DoUnpack(const uint8 *src_, uint32 srcLen, std::vector<char> &unpackedData, int32 len)
  80. {
  81. if(len <= 0) return false;
  82. int32 d0,d1,d2,d3,d4,d5,d6,a2,a5;
  83. int32 cp, cup1, type;
  84. std::size_t c;
  85. std::size_t src;
  86. std::size_t phist = 0;
  87. unpackedData.reserve(std::min(static_cast<uint32>(len), std::min(srcLen, uint32_max / 20u) * 20u));
  88. XPK_BufferBounds bufs;
  89. bufs.pSrcBeg = src_;
  90. bufs.SrcSize = srcLen;
  91. src = 0;
  92. c = src;
  93. while(len > 0)
  94. {
  95. type = bufs.SrcRead(c+0);
  96. cp = (bufs.SrcRead(c+4)<<8) | (bufs.SrcRead(c+5)); // packed
  97. cup1 = (bufs.SrcRead(c+6)<<8) | (bufs.SrcRead(c+7)); // unpacked
  98. //Log(" packed=%6d unpacked=%6d bytes left=%d dst=%08X(%d)\n", cp, cup1, len, dst, dst);
  99. c += 8;
  100. src = c+2;
  101. if (type == 0)
  102. {
  103. // RAW chunk
  104. if(cp < 0 || cp > len) throw XPK_error();
  105. for(int32 i = 0; i < cp; ++i)
  106. {
  107. unpackedData.push_back(bufs.SrcRead(c + i));
  108. }
  109. c+=cp;
  110. len -= cp;
  111. continue;
  112. }
  113. if (type != 1)
  114. {
  115. #ifdef MMCMP_LOG
  116. MPT_LOG_GLOBAL(LogDebug, "XPK", MPT_UFORMAT("Invalid XPK type! ({} bytes left)")(len));
  117. #endif
  118. break;
  119. }
  120. LimitMax(cup1, len);
  121. len -= cup1;
  122. cp = (cp + 3) & 0xfffc;
  123. c += cp;
  124. d0 = d1 = d2 = a2 = 0;
  125. d3 = bufs.SrcRead(src); src++;
  126. unpackedData.push_back(static_cast<char>(d3));
  127. cup1--;
  128. while (cup1 > 0)
  129. {
  130. if (d1 >= 8) goto l6dc;
  131. if (bfextu(src,d0,1,bufs)) goto l75a;
  132. d0 += 1;
  133. d5 = 0;
  134. d6 = 8;
  135. goto l734;
  136. l6dc:
  137. if (bfextu(src,d0,1,bufs)) goto l726;
  138. d0 += 1;
  139. if (! bfextu(src,d0,1,bufs)) goto l75a;
  140. d0 += 1;
  141. if (bfextu(src,d0,1,bufs)) goto l6f6;
  142. d6 = 2;
  143. goto l708;
  144. l6f6:
  145. d0 += 1;
  146. if (!bfextu(src,d0,1,bufs)) goto l706;
  147. d6 = bfextu(src,d0,3,bufs);
  148. d0 += 3;
  149. goto l70a;
  150. l706:
  151. d6 = 3;
  152. l708:
  153. d0 += 1;
  154. l70a:
  155. d6 = XPK_ReadTable((8*a2) + d6 -17);
  156. if (d6 != 8) goto l730;
  157. l718:
  158. if (d2 >= 20)
  159. {
  160. d5 = 1;
  161. goto l732;
  162. }
  163. d5 = 0;
  164. goto l734;
  165. l726:
  166. d0 += 1;
  167. d6 = 8;
  168. if (d6 == a2) goto l718;
  169. d6 = a2;
  170. l730:
  171. d5 = 4;
  172. l732:
  173. d2 += 8;
  174. l734:
  175. while ((d5 >= 0) && (cup1 > 0))
  176. {
  177. d4 = bfexts(src,d0,d6,bufs);
  178. d0 += d6;
  179. d3 -= d4;
  180. unpackedData.push_back(static_cast<char>(d3));
  181. cup1--;
  182. d5--;
  183. }
  184. if (d1 != 31) d1++;
  185. a2 = d6;
  186. l74c:
  187. d6 = d2;
  188. d6 >>= 3;
  189. d2 -= d6;
  190. }
  191. }
  192. return !unpackedData.empty();
  193. l75a:
  194. d0 += 1;
  195. if (bfextu(src,d0,1,bufs)) goto l766;
  196. d4 = 2;
  197. goto l79e;
  198. l766:
  199. d0 += 1;
  200. if (bfextu(src,d0,1,bufs)) goto l772;
  201. d4 = 4;
  202. goto l79e;
  203. l772:
  204. d0 += 1;
  205. if (bfextu(src,d0,1,bufs)) goto l77e;
  206. d4 = 6;
  207. goto l79e;
  208. l77e:
  209. d0 += 1;
  210. if (bfextu(src,d0,1,bufs)) goto l792;
  211. d0 += 1;
  212. d6 = bfextu(src,d0,3,bufs);
  213. d0 += 3;
  214. d6 += 8;
  215. goto l7a8;
  216. l792:
  217. d0 += 1;
  218. d6 = bfextu(src,d0,5,bufs);
  219. d0 += 5;
  220. d4 = 16;
  221. goto l7a6;
  222. l79e:
  223. d0 += 1;
  224. d6 = bfextu(src,d0,1,bufs);
  225. d0 += 1;
  226. l7a6:
  227. d6 += d4;
  228. l7a8:
  229. if(bfextu(src, d0, 1, bufs))
  230. {
  231. d5 = 12;
  232. a5 = -0x100;
  233. } else
  234. {
  235. d0 += 1;
  236. if(bfextu(src, d0, 1, bufs))
  237. {
  238. d5 = 14;
  239. a5 = -0x1100;
  240. } else
  241. {
  242. d5 = 8;
  243. a5 = 0;
  244. }
  245. }
  246. d0 += 1;
  247. d4 = bfextu(src,d0,d5,bufs);
  248. d0 += d5;
  249. d6 -= 3;
  250. if (d6 >= 0)
  251. {
  252. if (d6 > 0) d1 -= 1;
  253. d1 -= 1;
  254. if (d1 < 0) d1 = 0;
  255. }
  256. d6 += 2;
  257. phist = unpackedData.size() + a5 - d4 - 1;
  258. if(phist >= unpackedData.size())
  259. throw XPK_error();
  260. while ((d6 >= 0) && (cup1 > 0))
  261. {
  262. d3 = unpackedData[phist];
  263. phist++;
  264. unpackedData.push_back(static_cast<char>(d3));
  265. cup1--;
  266. d6--;
  267. }
  268. goto l74c;
  269. }
  270. static bool ValidateHeader(const XPKFILEHEADER &header)
  271. {
  272. if(std::memcmp(header.XPKF, "XPKF", 4) != 0)
  273. {
  274. return false;
  275. }
  276. if(std::memcmp(header.SQSH, "SQSH", 4) != 0)
  277. {
  278. return false;
  279. }
  280. if(header.SrcLen == 0)
  281. {
  282. return false;
  283. }
  284. if(header.DstLen == 0)
  285. {
  286. return false;
  287. }
  288. static_assert(sizeof(XPKFILEHEADER) >= 8);
  289. if(header.SrcLen < (sizeof(XPKFILEHEADER) - 8))
  290. {
  291. return false;
  292. }
  293. return true;
  294. }
  295. static bool ValidateHeaderFileSize(const XPKFILEHEADER &header, uint64 filesize)
  296. {
  297. if(filesize < header.SrcLen - 8)
  298. {
  299. return false;
  300. }
  301. return true;
  302. }
  303. CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderXPK(MemoryFileReader file, const uint64 *pfilesize)
  304. {
  305. XPKFILEHEADER header;
  306. if(!file.ReadStruct(header))
  307. {
  308. return ProbeWantMoreData;
  309. }
  310. if(!ValidateHeader(header))
  311. {
  312. return ProbeFailure;
  313. }
  314. if(pfilesize)
  315. {
  316. if(!ValidateHeaderFileSize(header, *pfilesize))
  317. {
  318. return ProbeFailure;
  319. }
  320. }
  321. return ProbeSuccess;
  322. }
  323. bool UnpackXPK(std::vector<ContainerItem> &containerItems, FileReader &file, ContainerLoadingFlags loadFlags)
  324. {
  325. file.Rewind();
  326. containerItems.clear();
  327. XPKFILEHEADER header;
  328. if(!file.ReadStruct(header))
  329. {
  330. return false;
  331. }
  332. if(!ValidateHeader(header))
  333. {
  334. return false;
  335. }
  336. if(loadFlags == ContainerOnlyVerifyHeader)
  337. {
  338. return true;
  339. }
  340. if(!file.CanRead(header.SrcLen - (sizeof(XPKFILEHEADER) - 8)))
  341. {
  342. return false;
  343. }
  344. containerItems.emplace_back();
  345. containerItems.back().data_cache = std::make_unique<std::vector<char> >();
  346. std::vector<char> & unpackedData = *(containerItems.back().data_cache);
  347. #ifdef MMCMP_LOG
  348. MPT_LOG_GLOBAL(LogDebug, "XPK", MPT_UFORMAT("XPK detected (SrcLen={} DstLen={}) filesize={}")(static_cast<uint32>(header.SrcLen), static_cast<uint32>(header.DstLen), file.GetLength()));
  349. #endif
  350. bool result = false;
  351. try
  352. {
  353. result = XPK_DoUnpack(file.GetRawData<uint8>().data(), header.SrcLen - (sizeof(XPKFILEHEADER) - 8), unpackedData, header.DstLen);
  354. } catch(mpt::out_of_memory e)
  355. {
  356. mpt::delete_out_of_memory(e);
  357. return false;
  358. } catch(const XPK_error &)
  359. {
  360. return false;
  361. }
  362. if(result)
  363. {
  364. containerItems.back().file = FileReader(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(unpackedData)));
  365. }
  366. return result;
  367. }
  368. #endif // !MPT_WITH_ANCIENT
  369. OPENMPT_NAMESPACE_END