1
0

ContainerPP20.cpp 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. /*
  2. * ContainerPP20.cpp
  3. * -----------------
  4. * Purpose: Handling of PowerPack PP20 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. struct PPBITBUFFER
  18. {
  19. uint32 bitcount = 0;
  20. uint32 bitbuffer = 0;
  21. const uint8 *pStart = nullptr;
  22. const uint8 *pSrc = nullptr;
  23. uint32 GetBits(uint32 n);
  24. };
  25. uint32 PPBITBUFFER::GetBits(uint32 n)
  26. {
  27. uint32 result = 0;
  28. for(uint32 i = 0; i < n; i++)
  29. {
  30. if(!bitcount)
  31. {
  32. bitcount = 8;
  33. if(pSrc != pStart)
  34. pSrc--;
  35. bitbuffer = *pSrc;
  36. }
  37. result = (result << 1) | (bitbuffer & 1);
  38. bitbuffer >>= 1;
  39. bitcount--;
  40. }
  41. return result;
  42. }
  43. static bool PP20_DoUnpack(const uint8 *pSrc, uint32 srcLen, uint8 *pDst, uint32 dstLen)
  44. {
  45. const std::array<uint8, 4> modeTable{pSrc[0], pSrc[1], pSrc[2], pSrc[3]};
  46. PPBITBUFFER BitBuffer;
  47. BitBuffer.pStart = pSrc;
  48. BitBuffer.pSrc = pSrc + srcLen - 4;
  49. BitBuffer.GetBits(pSrc[srcLen - 1]);
  50. uint32 bytesLeft = dstLen;
  51. while(bytesLeft > 0)
  52. {
  53. if(!BitBuffer.GetBits(1))
  54. {
  55. uint32 count = 1, countAdd;
  56. do
  57. {
  58. countAdd = BitBuffer.GetBits(2);
  59. count += countAdd;
  60. } while(countAdd == 3);
  61. LimitMax(count, bytesLeft);
  62. for(uint32 i = 0; i < count; i++)
  63. {
  64. pDst[--bytesLeft] = (uint8)BitBuffer.GetBits(8);
  65. }
  66. if(!bytesLeft)
  67. break;
  68. }
  69. {
  70. uint32 modeIndex = BitBuffer.GetBits(2);
  71. MPT_CHECKER_ASSUME(modeIndex < 4);
  72. uint32 count = modeIndex + 2, offset;
  73. if(modeIndex == 3)
  74. {
  75. offset = BitBuffer.GetBits((BitBuffer.GetBits(1)) ? modeTable[modeIndex] : 7);
  76. uint32 countAdd = 7;
  77. do
  78. {
  79. countAdd = BitBuffer.GetBits(3);
  80. count += countAdd;
  81. } while(countAdd == 7);
  82. } else
  83. {
  84. offset = BitBuffer.GetBits(modeTable[modeIndex]);
  85. }
  86. LimitMax(count, bytesLeft);
  87. for(uint32 i = 0; i < count; i++)
  88. {
  89. pDst[bytesLeft - 1] = (bytesLeft + offset < dstLen) ? pDst[bytesLeft + offset] : 0;
  90. --bytesLeft;
  91. }
  92. }
  93. }
  94. return true;
  95. }
  96. struct PP20header
  97. {
  98. char magic[4]; // "PP20"
  99. uint8 efficiency[4];
  100. };
  101. MPT_BINARY_STRUCT(PP20header, 8)
  102. static bool ValidateHeader(const PP20header &hdr)
  103. {
  104. if(std::memcmp(hdr.magic, "PP20", 4) != 0)
  105. {
  106. return false;
  107. }
  108. if(hdr.efficiency[0] < 9 || hdr.efficiency[0] > 15
  109. || hdr.efficiency[1] < 9 || hdr.efficiency[1] > 15
  110. || hdr.efficiency[2] < 9 || hdr.efficiency[2] > 15
  111. || hdr.efficiency[3] < 9 || hdr.efficiency[3] > 15)
  112. {
  113. return false;
  114. }
  115. return true;
  116. }
  117. CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPP20(MemoryFileReader file, const uint64 *pfilesize)
  118. {
  119. PP20header hdr;
  120. if(!file.ReadStruct(hdr))
  121. {
  122. return ProbeWantMoreData;
  123. }
  124. if(!ValidateHeader(hdr))
  125. {
  126. return ProbeFailure;
  127. }
  128. MPT_UNREFERENCED_PARAMETER(pfilesize);
  129. return ProbeSuccess;
  130. }
  131. bool UnpackPP20(std::vector<ContainerItem> &containerItems, FileReader &file, ContainerLoadingFlags loadFlags)
  132. {
  133. file.Rewind();
  134. containerItems.clear();
  135. PP20header hdr;
  136. if(!file.ReadStruct(hdr))
  137. {
  138. return false;
  139. }
  140. if(!ValidateHeader(hdr))
  141. {
  142. return false;
  143. }
  144. if(loadFlags == ContainerOnlyVerifyHeader)
  145. {
  146. return true;
  147. }
  148. if(!file.CanRead(4))
  149. {
  150. return false;
  151. }
  152. containerItems.emplace_back();
  153. containerItems.back().data_cache = std::make_unique<std::vector<char> >();
  154. std::vector<char> & unpackedData = *(containerItems.back().data_cache);
  155. FileReader::off_t length = file.GetLength();
  156. if(!mpt::in_range<uint32>(length)) return false;
  157. // Length word must be aligned
  158. if((length % 2u) != 0)
  159. return false;
  160. file.Seek(length - 4);
  161. uint32 dstLen = file.ReadUint24BE();
  162. if(dstLen == 0)
  163. return false;
  164. try
  165. {
  166. unpackedData.resize(dstLen);
  167. } catch(mpt::out_of_memory e)
  168. {
  169. mpt::delete_out_of_memory(e);
  170. return false;
  171. }
  172. file.Seek(4);
  173. bool result = PP20_DoUnpack(file.GetRawData<uint8>().data(), static_cast<uint32>(length - 4), mpt::byte_cast<uint8 *>(unpackedData.data()), dstLen);
  174. if(result)
  175. {
  176. containerItems.back().file = FileReader(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(unpackedData)));
  177. }
  178. return result;
  179. }
  180. #endif // !MPT_WITH_ANCIENT
  181. OPENMPT_NAMESPACE_END