1
0

Load_plm.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. /*
  2. * Load_plm.cpp
  3. * ------------
  4. * Purpose: PLM (Disorder Tracker 2) module loader
  5. * Notes : (currently none)
  6. * Authors: OpenMPT Devs
  7. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  8. */
  9. #include "stdafx.h"
  10. #include "Loaders.h"
  11. OPENMPT_NAMESPACE_BEGIN
  12. struct PLMFileHeader
  13. {
  14. char magic[4]; // "PLM\x1A"
  15. uint8le headerSize; // Number of bytes in header, including magic bytes
  16. uint8le version; // version code of file format (0x10)
  17. char songName[48];
  18. uint8le numChannels;
  19. uint8le flags; // unused?
  20. uint8le maxVol; // Maximum volume for vol slides, normally 0x40
  21. uint8le amplify; // SoundBlaster amplify, 0x40 = no amplify
  22. uint8le tempo;
  23. uint8le speed;
  24. uint8le panPos[32]; // 0...15
  25. uint8le numSamples;
  26. uint8le numPatterns;
  27. uint16le numOrders;
  28. };
  29. MPT_BINARY_STRUCT(PLMFileHeader, 96)
  30. struct PLMSampleHeader
  31. {
  32. enum SampleFlags
  33. {
  34. smp16Bit = 1,
  35. smpPingPong = 2,
  36. };
  37. char magic[4]; // "PLS\x1A"
  38. uint8le headerSize; // Number of bytes in header, including magic bytes
  39. uint8le version;
  40. char name[32];
  41. char filename[12];
  42. uint8le panning; // 0...15, 255 = no pan
  43. uint8le volume; // 0...64
  44. uint8le flags; // See SampleFlags
  45. uint16le sampleRate;
  46. char unused[4];
  47. uint32le loopStart;
  48. uint32le loopEnd;
  49. uint32le length;
  50. };
  51. MPT_BINARY_STRUCT(PLMSampleHeader, 71)
  52. struct PLMPatternHeader
  53. {
  54. uint32le size;
  55. uint8le numRows;
  56. uint8le numChannels;
  57. uint8le color;
  58. char name[25];
  59. };
  60. MPT_BINARY_STRUCT(PLMPatternHeader, 32)
  61. struct PLMOrderItem
  62. {
  63. uint16le x; // Starting position of pattern
  64. uint8le y; // Number of first channel
  65. uint8le pattern;
  66. };
  67. MPT_BINARY_STRUCT(PLMOrderItem, 4)
  68. static bool ValidateHeader(const PLMFileHeader &fileHeader)
  69. {
  70. if(std::memcmp(fileHeader.magic, "PLM\x1A", 4)
  71. || fileHeader.version != 0x10
  72. || fileHeader.numChannels == 0 || fileHeader.numChannels > 32
  73. || fileHeader.headerSize < sizeof(PLMFileHeader)
  74. )
  75. {
  76. return false;
  77. }
  78. return true;
  79. }
  80. static uint64 GetHeaderMinimumAdditionalSize(const PLMFileHeader &fileHeader)
  81. {
  82. return fileHeader.headerSize - sizeof(PLMFileHeader) + 4 * (fileHeader.numOrders + fileHeader.numPatterns + fileHeader.numSamples);
  83. }
  84. CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderPLM(MemoryFileReader file, const uint64 *pfilesize)
  85. {
  86. PLMFileHeader fileHeader;
  87. if(!file.ReadStruct(fileHeader))
  88. {
  89. return ProbeWantMoreData;
  90. }
  91. if(!ValidateHeader(fileHeader))
  92. {
  93. return ProbeFailure;
  94. }
  95. return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader));
  96. }
  97. bool CSoundFile::ReadPLM(FileReader &file, ModLoadingFlags loadFlags)
  98. {
  99. file.Rewind();
  100. PLMFileHeader fileHeader;
  101. if(!file.ReadStruct(fileHeader))
  102. {
  103. return false;
  104. }
  105. if(!ValidateHeader(fileHeader))
  106. {
  107. return false;
  108. }
  109. if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader))))
  110. {
  111. return false;
  112. }
  113. if(loadFlags == onlyVerifyHeader)
  114. {
  115. return true;
  116. }
  117. if(!file.Seek(fileHeader.headerSize))
  118. {
  119. return false;
  120. }
  121. InitializeGlobals(MOD_TYPE_PLM);
  122. InitializeChannels();
  123. m_SongFlags = SONG_ITOLDEFFECTS;
  124. m_playBehaviour.set(kApplyOffsetWithoutNote);
  125. m_modFormat.formatName = U_("Disorder Tracker 2");
  126. m_modFormat.type = U_("plm");
  127. m_modFormat.charset = mpt::Charset::CP437;
  128. // Some PLMs use ASCIIZ, some space-padding strings...weird. Oh, and the file browser stops at 0 bytes in the name, the main GUI doesn't.
  129. m_songName = mpt::String::ReadBuf(mpt::String::spacePadded, fileHeader.songName);
  130. m_nChannels = fileHeader.numChannels + 1; // Additional channel for writing pattern breaks
  131. m_nSamplePreAmp = fileHeader.amplify;
  132. m_nDefaultTempo.Set(fileHeader.tempo);
  133. m_nDefaultSpeed = fileHeader.speed;
  134. for(CHANNELINDEX chn = 0; chn < fileHeader.numChannels; chn++)
  135. {
  136. ChnSettings[chn].nPan = fileHeader.panPos[chn] * 0x11;
  137. }
  138. m_nSamples = fileHeader.numSamples;
  139. std::vector<PLMOrderItem> order(fileHeader.numOrders);
  140. file.ReadVector(order, fileHeader.numOrders);
  141. std::vector<uint32le> patternPos, samplePos;
  142. file.ReadVector(patternPos, fileHeader.numPatterns);
  143. file.ReadVector(samplePos, fileHeader.numSamples);
  144. for(SAMPLEINDEX smp = 0; smp < fileHeader.numSamples; smp++)
  145. {
  146. ModSample &sample = Samples[smp + 1];
  147. sample.Initialize();
  148. PLMSampleHeader sampleHeader;
  149. if(samplePos[smp] == 0
  150. || !file.Seek(samplePos[smp])
  151. || !file.ReadStruct(sampleHeader))
  152. continue;
  153. m_szNames[smp + 1] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.name);
  154. sample.filename = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.filename);
  155. if(sampleHeader.panning <= 15)
  156. {
  157. sample.uFlags.set(CHN_PANNING);
  158. sample.nPan = sampleHeader.panning * 0x11;
  159. }
  160. sample.nGlobalVol = std::min(sampleHeader.volume.get(), uint8(64));
  161. sample.nC5Speed = sampleHeader.sampleRate;
  162. sample.nLoopStart = sampleHeader.loopStart;
  163. sample.nLoopEnd = sampleHeader.loopEnd;
  164. sample.nLength = sampleHeader.length;
  165. if(sampleHeader.flags & PLMSampleHeader::smp16Bit)
  166. {
  167. sample.nLoopStart /= 2;
  168. sample.nLoopEnd /= 2;
  169. sample.nLength /= 2;
  170. }
  171. if(sample.nLoopEnd > sample.nLoopStart)
  172. {
  173. sample.uFlags.set(CHN_LOOP);
  174. if(sampleHeader.flags & PLMSampleHeader::smpPingPong) sample.uFlags.set(CHN_PINGPONGLOOP);
  175. }
  176. sample.SanitizeLoops();
  177. if(loadFlags & loadSampleData)
  178. {
  179. file.Seek(samplePos[smp] + sampleHeader.headerSize);
  180. SampleIO(
  181. (sampleHeader.flags & PLMSampleHeader::smp16Bit) ? SampleIO::_16bit : SampleIO::_8bit,
  182. SampleIO::mono,
  183. SampleIO::littleEndian,
  184. SampleIO::unsignedPCM)
  185. .ReadSample(sample, file);
  186. }
  187. }
  188. if(!(loadFlags & loadPatternData))
  189. {
  190. return true;
  191. }
  192. // PLM is basically one huge continuous pattern, so we split it up into smaller patterns.
  193. const ROWINDEX rowsPerPat = 64;
  194. uint32 maxPos = 0;
  195. static constexpr ModCommand::COMMAND effTrans[] =
  196. {
  197. CMD_NONE,
  198. CMD_PORTAMENTOUP,
  199. CMD_PORTAMENTODOWN,
  200. CMD_TONEPORTAMENTO,
  201. CMD_VOLUMESLIDE,
  202. CMD_TREMOLO,
  203. CMD_VIBRATO,
  204. CMD_S3MCMDEX, // Tremolo Waveform
  205. CMD_S3MCMDEX, // Vibrato Waveform
  206. CMD_TEMPO,
  207. CMD_SPEED,
  208. CMD_POSITIONJUMP, // Jump to order
  209. CMD_POSITIONJUMP, // Break to end of this order
  210. CMD_OFFSET,
  211. CMD_S3MCMDEX, // GUS Panning
  212. CMD_RETRIG,
  213. CMD_S3MCMDEX, // Note Delay
  214. CMD_S3MCMDEX, // Note Cut
  215. CMD_S3MCMDEX, // Pattern Delay
  216. CMD_FINEVIBRATO,
  217. CMD_VIBRATOVOL,
  218. CMD_TONEPORTAVOL,
  219. CMD_OFFSETPERCENTAGE,
  220. };
  221. Order().clear();
  222. for(const auto &ord : order)
  223. {
  224. if(ord.pattern >= fileHeader.numPatterns
  225. || ord.y > fileHeader.numChannels
  226. || !file.Seek(patternPos[ord.pattern])) continue;
  227. PLMPatternHeader patHeader;
  228. file.ReadStruct(patHeader);
  229. if(!patHeader.numRows) continue;
  230. static_assert(ORDERINDEX_MAX >= (std::numeric_limits<decltype(ord.x)>::max() + 255) / rowsPerPat);
  231. ORDERINDEX curOrd = static_cast<ORDERINDEX>(ord.x / rowsPerPat);
  232. ROWINDEX curRow = static_cast<ROWINDEX>(ord.x % rowsPerPat);
  233. const CHANNELINDEX numChannels = std::min(patHeader.numChannels.get(), static_cast<uint8>(fileHeader.numChannels - ord.y));
  234. const uint32 patternEnd = ord.x + patHeader.numRows;
  235. maxPos = std::max(maxPos, patternEnd);
  236. ModCommand::NOTE lastNote[32] = { 0 };
  237. for(ROWINDEX r = 0; r < patHeader.numRows; r++, curRow++)
  238. {
  239. if(curRow >= rowsPerPat)
  240. {
  241. curRow = 0;
  242. curOrd++;
  243. }
  244. if(curOrd >= Order().size())
  245. {
  246. Order().resize(curOrd + 1);
  247. Order()[curOrd] = Patterns.InsertAny(rowsPerPat);
  248. }
  249. PATTERNINDEX pat = Order()[curOrd];
  250. if(!Patterns.IsValidPat(pat)) break;
  251. ModCommand *m = Patterns[pat].GetpModCommand(curRow, ord.y);
  252. for(CHANNELINDEX c = 0; c < numChannels; c++, m++)
  253. {
  254. const auto [note, instr, volume, command, param] = file.ReadArray<uint8, 5>();
  255. if(note > 0 && note < 0x90)
  256. lastNote[c] = m->note = (note >> 4) * 12 + (note & 0x0F) + 12 + NOTE_MIN;
  257. else
  258. m->note = NOTE_NONE;
  259. m->instr = instr;
  260. m->volcmd = VOLCMD_VOLUME;
  261. if(volume != 0xFF)
  262. m->vol = volume;
  263. else
  264. m->volcmd = VOLCMD_NONE;
  265. if(command < std::size(effTrans))
  266. {
  267. m->command = effTrans[command];
  268. m->param = param;
  269. // Fix some commands
  270. switch(command)
  271. {
  272. case 0x07: // Tremolo waveform
  273. m->param = 0x40 | (m->param & 0x03);
  274. break;
  275. case 0x08: // Vibrato waveform
  276. m->param = 0x30 | (m->param & 0x03);
  277. break;
  278. case 0x0B: // Jump to order
  279. if(m->param < order.size())
  280. {
  281. uint16 target = order[m->param].x;
  282. m->param = static_cast<ModCommand::PARAM>(target / rowsPerPat);
  283. ModCommand *mBreak = Patterns[pat].GetpModCommand(curRow, m_nChannels - 1);
  284. mBreak->command = CMD_PATTERNBREAK;
  285. mBreak->param = static_cast<ModCommand::PARAM>(target % rowsPerPat);
  286. }
  287. break;
  288. case 0x0C: // Jump to end of order
  289. {
  290. m->param = static_cast<ModCommand::PARAM>(patternEnd / rowsPerPat);
  291. ModCommand *mBreak = Patterns[pat].GetpModCommand(curRow, m_nChannels - 1);
  292. mBreak->command = CMD_PATTERNBREAK;
  293. mBreak->param = static_cast<ModCommand::PARAM>(patternEnd % rowsPerPat);
  294. }
  295. break;
  296. case 0x0E: // GUS Panning
  297. m->param = 0x80 | (m->param & 0x0F);
  298. break;
  299. case 0x10: // Delay Note
  300. m->param = 0xD0 | std::min(m->param, ModCommand::PARAM(0x0F));
  301. break;
  302. case 0x11: // Cut Note
  303. m->param = 0xC0 | std::min(m->param, ModCommand::PARAM(0x0F));
  304. break;
  305. case 0x12: // Pattern Delay
  306. m->param = 0xE0 | std::min(m->param, ModCommand::PARAM(0x0F));
  307. break;
  308. case 0x04: // Volume Slide
  309. case 0x14: // Vibrato + Volume Slide
  310. case 0x15: // Tone Portamento + Volume Slide
  311. // If both nibbles of a volume slide are set, act as fine volume slide up
  312. if((m->param & 0xF0) && (m->param & 0x0F) && (m->param & 0xF0) != 0xF0)
  313. {
  314. m->param |= 0x0F;
  315. }
  316. break;
  317. case 0x0D:
  318. case 0x16:
  319. // Offset without note
  320. if(m->note == NOTE_NONE)
  321. {
  322. m->note = lastNote[c];
  323. }
  324. break;
  325. }
  326. }
  327. }
  328. if(patHeader.numChannels > numChannels)
  329. {
  330. file.Skip(5 * (patHeader.numChannels - numChannels));
  331. }
  332. }
  333. }
  334. // Module ends with the last row of the last order item
  335. ROWINDEX endPatSize = maxPos % rowsPerPat;
  336. ORDERINDEX endOrder = static_cast<ORDERINDEX>(maxPos / rowsPerPat);
  337. if(endPatSize > 0 && Order().IsValidPat(endOrder))
  338. {
  339. Patterns[Order()[endOrder]].Resize(endPatSize, false);
  340. }
  341. // If there are still any non-existent patterns in our order list, insert some blank patterns.
  342. PATTERNINDEX blankPat = PATTERNINDEX_INVALID;
  343. for(auto &pat : Order())
  344. {
  345. if(pat == Order.GetInvalidPatIndex())
  346. {
  347. if(blankPat == PATTERNINDEX_INVALID)
  348. {
  349. blankPat = Patterns.InsertAny(rowsPerPat);
  350. }
  351. pat = blankPat;
  352. }
  353. }
  354. return true;
  355. }
  356. OPENMPT_NAMESPACE_END