Load_sfx.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. /*
  2. * Load_sfx.cpp
  3. * ------------
  4. * Purpose: SFX / MMS (SoundFX / MultiMedia Sound) module loader
  5. * Notes : Mostly based on the Soundtracker loader, some effect behavior is based on Flod's implementation.
  6. * Authors: Devin Acker
  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 "Loaders.h"
  12. #include "Tables.h"
  13. OPENMPT_NAMESPACE_BEGIN
  14. // File Header
  15. struct SFXFileHeader
  16. {
  17. uint8be numOrders;
  18. uint8be restartPos;
  19. uint8be orderList[128];
  20. };
  21. MPT_BINARY_STRUCT(SFXFileHeader, 130)
  22. // Sample Header
  23. struct SFXSampleHeader
  24. {
  25. char name[22];
  26. char dummy[2]; // Supposedly sample length, but almost always incorrect
  27. uint8be finetune;
  28. uint8be volume;
  29. uint16be loopStart;
  30. uint16be loopLength;
  31. // Convert an MOD sample header to OpenMPT's internal sample header.
  32. void ConvertToMPT(ModSample &mptSmp, uint32 length) const
  33. {
  34. mptSmp.Initialize(MOD_TYPE_MOD);
  35. mptSmp.nLength = length;
  36. mptSmp.nFineTune = MOD2XMFineTune(finetune);
  37. mptSmp.nVolume = 4u * std::min(volume.get(), uint8(64));
  38. SmpLength lStart = loopStart;
  39. SmpLength lLength = loopLength * 2u;
  40. if(mptSmp.nLength)
  41. {
  42. mptSmp.nLoopStart = lStart;
  43. mptSmp.nLoopEnd = lStart + lLength;
  44. if(mptSmp.nLoopStart >= mptSmp.nLength)
  45. {
  46. mptSmp.nLoopStart = mptSmp.nLength - 1;
  47. }
  48. if(mptSmp.nLoopEnd > mptSmp.nLength)
  49. {
  50. mptSmp.nLoopEnd = mptSmp.nLength;
  51. }
  52. if(mptSmp.nLoopStart > mptSmp.nLoopEnd || mptSmp.nLoopEnd < 4 || mptSmp.nLoopEnd - mptSmp.nLoopStart < 4)
  53. {
  54. mptSmp.nLoopStart = 0;
  55. mptSmp.nLoopEnd = 0;
  56. }
  57. if(mptSmp.nLoopEnd > mptSmp.nLoopStart)
  58. {
  59. mptSmp.uFlags.set(CHN_LOOP);
  60. }
  61. }
  62. }
  63. };
  64. MPT_BINARY_STRUCT(SFXSampleHeader, 30)
  65. static uint8 ClampSlideParam(uint8 value, uint8 lowNote, uint8 highNote)
  66. {
  67. uint16 lowPeriod, highPeriod;
  68. if(lowNote < highNote &&
  69. lowNote >= 24 + NOTE_MIN &&
  70. highNote >= 24 + NOTE_MIN &&
  71. lowNote < std::size(ProTrackerPeriodTable) + 24 + NOTE_MIN &&
  72. highNote < std::size(ProTrackerPeriodTable) + 24 + NOTE_MIN)
  73. {
  74. lowPeriod = ProTrackerPeriodTable[lowNote - 24 - NOTE_MIN];
  75. highPeriod = ProTrackerPeriodTable[highNote - 24 - NOTE_MIN];
  76. // with a fixed speed of 6 ticks/row, and excluding the first row,
  77. // 1xx/2xx param has a max value of (low-high)/5 to avoid sliding too far
  78. return std::min(value, static_cast<uint8>((lowPeriod - highPeriod) / 5));
  79. }
  80. return 0;
  81. }
  82. static bool ValidateHeader(const SFXFileHeader &fileHeader)
  83. {
  84. if(fileHeader.numOrders > 128)
  85. {
  86. return false;
  87. }
  88. return true;
  89. }
  90. CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderSFX(MemoryFileReader file, const uint64 *pfilesize)
  91. {
  92. SAMPLEINDEX numSamples = 0;
  93. if(numSamples == 0)
  94. {
  95. file.Rewind();
  96. if(!file.CanRead(0x40))
  97. {
  98. return ProbeWantMoreData;
  99. }
  100. if(file.Seek(0x3c) && file.ReadMagic("SONG"))
  101. {
  102. numSamples = 15;
  103. }
  104. }
  105. if(numSamples == 0)
  106. {
  107. file.Rewind();
  108. if(!file.CanRead(0x80))
  109. {
  110. return ProbeWantMoreData;
  111. }
  112. if(file.Seek(0x7C) && file.ReadMagic("SO31"))
  113. {
  114. numSamples = 31;
  115. }
  116. }
  117. if(numSamples == 0)
  118. {
  119. return ProbeFailure;
  120. }
  121. file.Rewind();
  122. for(SAMPLEINDEX smp = 0; smp < numSamples; smp++)
  123. {
  124. if(file.ReadUint32BE() > 131072)
  125. {
  126. return ProbeFailure;
  127. }
  128. }
  129. file.Skip(4);
  130. if(!file.CanRead(2))
  131. {
  132. return ProbeWantMoreData;
  133. }
  134. uint16 speed = file.ReadUint16BE();
  135. if(speed < 178)
  136. {
  137. return ProbeFailure;
  138. }
  139. if(!file.CanRead(sizeof(SFXSampleHeader) * numSamples))
  140. {
  141. return ProbeWantMoreData;
  142. }
  143. file.Skip(sizeof(SFXSampleHeader) * numSamples);
  144. SFXFileHeader fileHeader;
  145. if(!file.ReadStruct(fileHeader))
  146. {
  147. return ProbeWantMoreData;
  148. }
  149. if(!ValidateHeader(fileHeader))
  150. {
  151. return ProbeFailure;
  152. }
  153. MPT_UNREFERENCED_PARAMETER(pfilesize);
  154. return ProbeSuccess;
  155. }
  156. bool CSoundFile::ReadSFX(FileReader &file, ModLoadingFlags loadFlags)
  157. {
  158. if(file.Seek(0x3C), file.ReadMagic("SONG"))
  159. {
  160. InitializeGlobals(MOD_TYPE_SFX);
  161. m_nSamples = 15;
  162. } else if(file.Seek(0x7C), file.ReadMagic("SO31"))
  163. {
  164. InitializeGlobals(MOD_TYPE_SFX);
  165. m_nSamples = 31;
  166. } else
  167. {
  168. return false;
  169. }
  170. uint32 sampleLen[31];
  171. file.Rewind();
  172. for(SAMPLEINDEX smp = 0; smp < m_nSamples; smp++)
  173. {
  174. sampleLen[smp] = file.ReadUint32BE();
  175. if(sampleLen[smp] > 131072)
  176. return false;
  177. }
  178. m_nChannels = 4;
  179. m_nInstruments = 0;
  180. m_nDefaultSpeed = 6;
  181. m_nMinPeriod = 14 * 4;
  182. m_nMaxPeriod = 3424 * 4;
  183. m_nSamplePreAmp = 64;
  184. // Setup channel pan positions and volume
  185. SetupMODPanning(true);
  186. file.Skip(4);
  187. uint16 speed = file.ReadUint16BE();
  188. if(speed < 178)
  189. return false;
  190. m_nDefaultTempo = TEMPO((14565.0 * 122.0) / speed);
  191. file.Skip(14);
  192. uint32 invalidChars = 0;
  193. for(SAMPLEINDEX smp = 1; smp <= m_nSamples; smp++)
  194. {
  195. SFXSampleHeader sampleHeader;
  196. file.ReadStruct(sampleHeader);
  197. sampleHeader.ConvertToMPT(Samples[smp], sampleLen[smp - 1]);
  198. // Get rid of weird characters in sample names.
  199. for(char &c : sampleHeader.name)
  200. {
  201. if(c > 0 && c < ' ')
  202. {
  203. c = ' ';
  204. invalidChars++;
  205. }
  206. }
  207. if(invalidChars >= 128)
  208. return false;
  209. m_szNames[smp] = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name);
  210. }
  211. // Broken conversions of the "Operation Stealth" soundtrack (BOND23 / BOND32)
  212. // There is a converter that shifts all note values except FFFD (empty note) to the left by 1 bit,
  213. // but it should not do that for FFFE (STP) notes - as a consequence, they turn into pattern breaks (FFFC).
  214. const bool fixPatternBreaks = (m_szNames[1] == "BASSE2.AMI") || (m_szNames[1] == "PRA1.AMI");
  215. SFXFileHeader fileHeader;
  216. if(!file.ReadStruct(fileHeader))
  217. {
  218. return false;
  219. }
  220. if(!ValidateHeader(fileHeader))
  221. {
  222. return false;
  223. }
  224. if(loadFlags == onlyVerifyHeader)
  225. {
  226. return true;
  227. }
  228. PATTERNINDEX numPatterns = 0;
  229. for(ORDERINDEX ord = 0; ord < fileHeader.numOrders; ord++)
  230. {
  231. numPatterns = std::max(numPatterns, static_cast<PATTERNINDEX>(fileHeader.orderList[ord] + 1));
  232. }
  233. if(fileHeader.restartPos < fileHeader.numOrders)
  234. Order().SetRestartPos(fileHeader.restartPos);
  235. else
  236. Order().SetRestartPos(0);
  237. ReadOrderFromArray(Order(), fileHeader.orderList, fileHeader.numOrders);
  238. // SFX v2 / MMS modules have 4 extra bytes here for some reason
  239. if(m_nSamples == 31)
  240. file.Skip(4);
  241. uint8 lastNote[4] = {0};
  242. uint8 slideTo[4] = {0};
  243. uint8 slideRate[4] = {0};
  244. uint8 version = 0;
  245. // Reading patterns
  246. if(loadFlags & loadPatternData)
  247. Patterns.ResizeArray(numPatterns);
  248. for(PATTERNINDEX pat = 0; pat < numPatterns; pat++)
  249. {
  250. if(!(loadFlags & loadPatternData) || !Patterns.Insert(pat, 64))
  251. {
  252. file.Skip(64 * 4 * 4);
  253. continue;
  254. }
  255. for(ROWINDEX row = 0; row < 64; row++)
  256. {
  257. PatternRow rowBase = Patterns[pat].GetpModCommand(row, 0);
  258. for(CHANNELINDEX chn = 0; chn < 4; chn++)
  259. {
  260. ModCommand &m = rowBase[chn];
  261. auto data = file.ReadArray<uint8, 4>();
  262. if(data[0] == 0xFF)
  263. {
  264. lastNote[chn] = slideRate[chn] = 0;
  265. if(fixPatternBreaks && data[1] == 0xFC)
  266. data[1] = 0xFE;
  267. switch(data[1])
  268. {
  269. case 0xFE: // STP (note cut)
  270. m.command = CMD_VOLUME;
  271. continue;
  272. case 0xFD: // PIC (null)
  273. continue;
  274. case 0xFC: // BRK (pattern break)
  275. m.command = CMD_PATTERNBREAK;
  276. version = 9;
  277. continue;
  278. }
  279. }
  280. ReadMODPatternEntry(data, m);
  281. if(m.note != NOTE_NONE)
  282. {
  283. lastNote[chn] = m.note;
  284. slideRate[chn] = 0;
  285. if(m.note < NOTE_MIDDLEC - 12)
  286. {
  287. version = std::max(version, uint8(8));
  288. }
  289. }
  290. if(m.command || m.param)
  291. {
  292. switch(m.command)
  293. {
  294. case 0x1: // Arpeggio
  295. m.command = CMD_ARPEGGIO;
  296. break;
  297. case 0x2: // Portamento (like Ultimate Soundtracker)
  298. if(m.param & 0xF0)
  299. {
  300. m.command = CMD_PORTAMENTODOWN;
  301. m.param >>= 4;
  302. } else if(m.param & 0xF)
  303. {
  304. m.command = CMD_PORTAMENTOUP;
  305. m.param &= 0x0F;
  306. } else
  307. {
  308. m.command = m.param = 0;
  309. }
  310. break;
  311. case 0x3: // Enable LED filter
  312. // Give precedence to 7xy/8xy slides
  313. if(slideRate[chn])
  314. {
  315. m.command = m.param = 0;
  316. break;
  317. }
  318. m.command = CMD_MODCMDEX;
  319. m.param = 0;
  320. break;
  321. case 0x4: // Disable LED filter
  322. // Give precedence to 7xy/8xy slides
  323. if(slideRate[chn])
  324. {
  325. m.command = m.param = 0;
  326. break;
  327. }
  328. m.command = CMD_MODCMDEX;
  329. m.param = 1;
  330. break;
  331. case 0x5: // Increase volume
  332. if(m.instr)
  333. {
  334. m.command = CMD_VOLUME;
  335. m.param = std::min(ModCommand::PARAM(0x3F), static_cast<ModCommand::PARAM>((Samples[m.instr].nVolume / 4u) + m.param));
  336. // Give precedence to 7xy/8xy slides (and move this to the volume column)
  337. if(slideRate[chn])
  338. {
  339. m.volcmd = VOLCMD_VOLUME;
  340. m.vol = m.param;
  341. m.command = m.param = 0;
  342. break;
  343. }
  344. } else
  345. {
  346. m.command = m.param = 0;
  347. }
  348. break;
  349. case 0x6: // Decrease volume
  350. if(m.instr)
  351. {
  352. m.command = CMD_VOLUME;
  353. if((Samples[m.instr].nVolume / 4u) >= m.param)
  354. m.param = static_cast<ModCommand::PARAM>(Samples[m.instr].nVolume / 4u) - m.param;
  355. else
  356. m.param = 0;
  357. // Give precedence to 7xy/8xy slides (and move this to the volume column)
  358. if(slideRate[chn])
  359. {
  360. m.volcmd = VOLCMD_VOLUME;
  361. m.vol = m.param;
  362. m.command = m.param = 0;
  363. break;
  364. }
  365. } else
  366. {
  367. m.command = m.param = 0;
  368. }
  369. break;
  370. case 0x7: // 7xy: Slide down x semitones at speed y
  371. slideTo[chn] = lastNote[chn] - (m.param >> 4);
  372. m.command = CMD_PORTAMENTODOWN;
  373. slideRate[chn] = m.param & 0xF;
  374. m.param = ClampSlideParam(slideRate[chn], slideTo[chn], lastNote[chn]);
  375. break;
  376. case 0x8: // 8xy: Slide up x semitones at speed y
  377. slideTo[chn] = lastNote[chn] + (m.param >> 4);
  378. m.command = CMD_PORTAMENTOUP;
  379. slideRate[chn] = m.param & 0xF;
  380. m.param = ClampSlideParam(slideRate[chn], lastNote[chn], slideTo[chn]);
  381. break;
  382. case 0x9: // 9xy: Auto slide
  383. version = std::max(version, uint8(8));
  384. [[fallthrough]];
  385. default:
  386. m.command = CMD_NONE;
  387. break;
  388. }
  389. }
  390. // Continue 7xy/8xy slides if needed
  391. if(m.command == CMD_NONE && slideRate[chn])
  392. {
  393. if(slideTo[chn])
  394. {
  395. m.note = lastNote[chn] = slideTo[chn];
  396. m.param = slideRate[chn];
  397. slideTo[chn] = 0;
  398. }
  399. m.command = CMD_TONEPORTAMENTO;
  400. }
  401. }
  402. }
  403. }
  404. // Reading samples
  405. if(loadFlags & loadSampleData)
  406. {
  407. for(SAMPLEINDEX smp = 1; smp <= m_nSamples; smp++) if(Samples[smp].nLength)
  408. {
  409. SampleIO(
  410. SampleIO::_8bit,
  411. SampleIO::mono,
  412. SampleIO::littleEndian,
  413. SampleIO::signedPCM)
  414. .ReadSample(Samples[smp], file);
  415. }
  416. }
  417. m_modFormat.formatName = m_nSamples == 15 ? MPT_UFORMAT("SoundFX 1.{}")(version) : U_("SoundFX 2.0 / MultiMedia Sound");
  418. m_modFormat.type = m_nSamples == 15 ? UL_("sfx") : UL_("sfx2");
  419. m_modFormat.charset = mpt::Charset::Amiga_no_C1;
  420. return true;
  421. }
  422. OPENMPT_NAMESPACE_END