1
0

Load_ult.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. /*
  2. * Load_ult.cpp
  3. * ------------
  4. * Purpose: ULT (UltraTracker) module loader
  5. * Notes : (currently none)
  6. * Authors: Storlek (Original author - http://schismtracker.org/ - code ported with permission)
  7. * Johannes Schultz (OpenMPT Port, tweaks)
  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. OPENMPT_NAMESPACE_BEGIN
  13. struct UltFileHeader
  14. {
  15. char signature[14]; // "MAS_UTrack_V00"
  16. uint8 version; // '1'...'4'
  17. char songName[32]; // Song Name, not guaranteed to be null-terminated
  18. uint8 messageLength; // Number of Lines
  19. };
  20. MPT_BINARY_STRUCT(UltFileHeader, 48)
  21. struct UltSample
  22. {
  23. enum UltSampleFlags
  24. {
  25. ULT_16BIT = 4,
  26. ULT_LOOP = 8,
  27. ULT_PINGPONGLOOP = 16,
  28. };
  29. char name[32];
  30. char filename[12];
  31. uint32le loopStart;
  32. uint32le loopEnd;
  33. uint32le sizeStart;
  34. uint32le sizeEnd;
  35. uint8le volume; // 0-255, apparently prior to 1.4 this was logarithmic?
  36. uint8le flags; // above
  37. uint16le speed; // only exists for 1.4+
  38. int16le finetune;
  39. // Convert an ULT sample header to OpenMPT's internal sample header.
  40. void ConvertToMPT(ModSample &mptSmp) const
  41. {
  42. mptSmp.Initialize();
  43. mptSmp.Set16BitCuePoints();
  44. mptSmp.filename = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, filename);
  45. if(sizeEnd <= sizeStart)
  46. {
  47. return;
  48. }
  49. mptSmp.nLength = sizeEnd - sizeStart;
  50. mptSmp.nSustainStart = loopStart;
  51. mptSmp.nSustainEnd = std::min(static_cast<SmpLength>(loopEnd), mptSmp.nLength);
  52. mptSmp.nVolume = volume;
  53. mptSmp.nC5Speed = speed;
  54. if(finetune)
  55. {
  56. mptSmp.Transpose(finetune / (12.0 * 32768.0));
  57. }
  58. if(flags & ULT_LOOP)
  59. mptSmp.uFlags.set(CHN_SUSTAINLOOP);
  60. if(flags & ULT_PINGPONGLOOP)
  61. mptSmp.uFlags.set(CHN_PINGPONGSUSTAIN);
  62. if(flags & ULT_16BIT)
  63. {
  64. mptSmp.uFlags.set(CHN_16BIT);
  65. mptSmp.nSustainStart /= 2;
  66. mptSmp.nSustainEnd /= 2;
  67. }
  68. }
  69. };
  70. MPT_BINARY_STRUCT(UltSample, 66)
  71. /* Unhandled effects:
  72. 5x1 - do not loop sample (x is unused)
  73. E0x - set vibrato strength (2 is normal)
  74. The logarithmic volume scale used in older format versions here, or pretty
  75. much anywhere for that matter. I don't even think Ultra Tracker tries to
  76. convert them. */
  77. static void TranslateULTCommands(uint8 &effect, uint8 &param, uint8 version)
  78. {
  79. static constexpr uint8 ultEffTrans[] =
  80. {
  81. CMD_ARPEGGIO,
  82. CMD_PORTAMENTOUP,
  83. CMD_PORTAMENTODOWN,
  84. CMD_TONEPORTAMENTO,
  85. CMD_VIBRATO,
  86. CMD_NONE,
  87. CMD_NONE,
  88. CMD_TREMOLO,
  89. CMD_NONE,
  90. CMD_OFFSET,
  91. CMD_VOLUMESLIDE,
  92. CMD_PANNING8,
  93. CMD_VOLUME,
  94. CMD_PATTERNBREAK,
  95. CMD_NONE, // extended effects, processed separately
  96. CMD_SPEED,
  97. };
  98. uint8 e = effect & 0x0F;
  99. effect = ultEffTrans[e];
  100. switch(e)
  101. {
  102. case 0x00:
  103. if(!param || version < '3')
  104. effect = CMD_NONE;
  105. break;
  106. case 0x05:
  107. // play backwards
  108. if((param & 0x0F) == 0x02 || (param & 0xF0) == 0x20)
  109. {
  110. effect = CMD_S3MCMDEX;
  111. param = 0x9F;
  112. }
  113. if(((param & 0x0F) == 0x0C || (param & 0xF0) == 0xC0) && version >= '3')
  114. {
  115. effect = CMD_KEYOFF;
  116. param = 0;
  117. }
  118. break;
  119. case 0x07:
  120. if(version < '4')
  121. effect = CMD_NONE;
  122. break;
  123. case 0x0A:
  124. if(param & 0xF0)
  125. param &= 0xF0;
  126. break;
  127. case 0x0B:
  128. param = (param & 0x0F) * 0x11;
  129. break;
  130. case 0x0C: // volume
  131. param /= 4u;
  132. break;
  133. case 0x0D: // pattern break
  134. param = 10 * (param >> 4) + (param & 0x0F);
  135. break;
  136. case 0x0E: // special
  137. switch(param >> 4)
  138. {
  139. case 0x01:
  140. effect = CMD_PORTAMENTOUP;
  141. param = 0xF0 | (param & 0x0F);
  142. break;
  143. case 0x02:
  144. effect = CMD_PORTAMENTODOWN;
  145. param = 0xF0 | (param & 0x0F);
  146. break;
  147. case 0x08:
  148. if(version >= '4')
  149. {
  150. effect = CMD_S3MCMDEX;
  151. param = 0x60 | (param & 0x0F);
  152. }
  153. break;
  154. case 0x09:
  155. effect = CMD_RETRIG;
  156. param &= 0x0F;
  157. break;
  158. case 0x0A:
  159. effect = CMD_VOLUMESLIDE;
  160. param = ((param & 0x0F) << 4) | 0x0F;
  161. break;
  162. case 0x0B:
  163. effect = CMD_VOLUMESLIDE;
  164. param = 0xF0 | (param & 0x0F);
  165. break;
  166. case 0x0C: case 0x0D:
  167. effect = CMD_S3MCMDEX;
  168. break;
  169. }
  170. break;
  171. case 0x0F:
  172. if(param > 0x2F)
  173. effect = CMD_TEMPO;
  174. break;
  175. }
  176. }
  177. static int ReadULTEvent(ModCommand &m, FileReader &file, uint8 version)
  178. {
  179. uint8 repeat = 1;
  180. uint8 b = file.ReadUint8();
  181. if(b == 0xFC) // repeat event
  182. {
  183. repeat = file.ReadUint8();
  184. b = file.ReadUint8();
  185. }
  186. m.note = (b > 0 && b < 61) ? (b + 35 + NOTE_MIN) : NOTE_NONE;
  187. const auto [instr, cmd, para1, para2] = file.ReadArray<uint8, 4>();
  188. m.instr = instr;
  189. uint8 cmd1 = cmd & 0x0F;
  190. uint8 cmd2 = cmd >> 4;
  191. uint8 param1 = para1;
  192. uint8 param2 = para2;
  193. TranslateULTCommands(cmd1, param1, version);
  194. TranslateULTCommands(cmd2, param2, version);
  195. // sample offset -- this is even more special than digitrakker's
  196. if(cmd1 == CMD_OFFSET && cmd2 == CMD_OFFSET)
  197. {
  198. uint32 offset = ((param2 << 8) | param1) >> 6;
  199. m.command = CMD_OFFSET;
  200. m.param = static_cast<ModCommand::PARAM>(offset);
  201. if(offset > 0xFF)
  202. {
  203. m.volcmd = VOLCMD_OFFSET;
  204. m.vol = static_cast<ModCommand::VOL>(offset >> 8);
  205. }
  206. return repeat;
  207. } else if(cmd1 == CMD_OFFSET)
  208. {
  209. uint32 offset = param1 * 4;
  210. param1 = mpt::saturate_cast<uint8>(offset);
  211. if(offset > 0xFF && ModCommand::GetEffectWeight(cmd2) < ModCommand::GetEffectType(CMD_OFFSET))
  212. {
  213. m.command = CMD_OFFSET;
  214. m.param = static_cast<ModCommand::PARAM>(offset);
  215. m.volcmd = VOLCMD_OFFSET;
  216. m.vol = static_cast<ModCommand::VOL>(offset >> 8);
  217. return repeat;
  218. }
  219. } else if(cmd2 == CMD_OFFSET)
  220. {
  221. uint32 offset = param2 * 4;
  222. param2 = mpt::saturate_cast<uint8>(offset);
  223. if(offset > 0xFF && ModCommand::GetEffectWeight(cmd1) < ModCommand::GetEffectType(CMD_OFFSET))
  224. {
  225. m.command = CMD_OFFSET;
  226. m.param = static_cast<ModCommand::PARAM>(offset);
  227. m.volcmd = VOLCMD_OFFSET;
  228. m.vol = static_cast<ModCommand::VOL>(offset >> 8);
  229. return repeat;
  230. }
  231. } else if(cmd1 == cmd2)
  232. {
  233. // don't try to figure out how ultratracker does this, it's quite random
  234. cmd2 = CMD_NONE;
  235. }
  236. if(cmd2 == CMD_VOLUME || (cmd2 == CMD_NONE && cmd1 != CMD_VOLUME))
  237. {
  238. // swap commands
  239. std::swap(cmd1, cmd2);
  240. std::swap(param1, param2);
  241. }
  242. // Combine slide commands, if possible
  243. ModCommand::CombineEffects(cmd2, param2, cmd1, param1);
  244. ModCommand::TwoRegularCommandsToMPT(cmd1, param1, cmd2, param2);
  245. m.volcmd = cmd1;
  246. m.vol = param1;
  247. m.command = cmd2;
  248. m.param = param2;
  249. return repeat;
  250. }
  251. // Functor for postfixing ULT patterns (this is easier than just remembering everything WHILE we're reading the pattern events)
  252. struct PostFixUltCommands
  253. {
  254. PostFixUltCommands(CHANNELINDEX numChannels)
  255. {
  256. this->numChannels = numChannels;
  257. curChannel = 0;
  258. writeT125 = false;
  259. isPortaActive.resize(numChannels, false);
  260. }
  261. void operator()(ModCommand &m)
  262. {
  263. // Attempt to fix portamentos.
  264. // UltraTracker will slide until the destination note is reached or 300 is encountered.
  265. // Stop porta?
  266. if(m.command == CMD_TONEPORTAMENTO && m.param == 0)
  267. {
  268. isPortaActive[curChannel] = false;
  269. m.command = CMD_NONE;
  270. }
  271. if(m.volcmd == VOLCMD_TONEPORTAMENTO && m.vol == 0)
  272. {
  273. isPortaActive[curChannel] = false;
  274. m.volcmd = VOLCMD_NONE;
  275. }
  276. // Apply porta?
  277. if(m.note == NOTE_NONE && isPortaActive[curChannel])
  278. {
  279. if(m.command == CMD_NONE && m.volcmd != VOLCMD_TONEPORTAMENTO)
  280. {
  281. m.command = CMD_TONEPORTAMENTO;
  282. m.param = 0;
  283. } else if(m.volcmd == VOLCMD_NONE && m.command != CMD_TONEPORTAMENTO)
  284. {
  285. m.volcmd = VOLCMD_TONEPORTAMENTO;
  286. m.vol = 0;
  287. }
  288. } else // new note -> stop porta (or initialize again)
  289. {
  290. isPortaActive[curChannel] = (m.command == CMD_TONEPORTAMENTO || m.volcmd == VOLCMD_TONEPORTAMENTO);
  291. }
  292. // attempt to fix F00 (reset to tempo 125, speed 6)
  293. if(writeT125 && m.command == CMD_NONE)
  294. {
  295. m.command = CMD_TEMPO;
  296. m.param = 125;
  297. }
  298. if(m.command == CMD_SPEED && m.param == 0)
  299. {
  300. m.param = 6;
  301. writeT125 = true;
  302. }
  303. if(m.command == CMD_TEMPO) // don't try to fix this anymore if the tempo has already changed.
  304. {
  305. writeT125 = false;
  306. }
  307. curChannel = (curChannel + 1) % numChannels;
  308. }
  309. std::vector<bool> isPortaActive;
  310. CHANNELINDEX numChannels, curChannel;
  311. bool writeT125;
  312. };
  313. static bool ValidateHeader(const UltFileHeader &fileHeader)
  314. {
  315. if(fileHeader.version < '1'
  316. || fileHeader.version > '4'
  317. || std::memcmp(fileHeader.signature, "MAS_UTrack_V00", sizeof(fileHeader.signature))
  318. )
  319. {
  320. return false;
  321. }
  322. return true;
  323. }
  324. static uint64 GetHeaderMinimumAdditionalSize(const UltFileHeader &fileHeader)
  325. {
  326. return fileHeader.messageLength * 32u + 3u + 256u;
  327. }
  328. CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderULT(MemoryFileReader file, const uint64 *pfilesize)
  329. {
  330. UltFileHeader fileHeader;
  331. if(!file.ReadStruct(fileHeader))
  332. {
  333. return ProbeWantMoreData;
  334. }
  335. if(!ValidateHeader(fileHeader))
  336. {
  337. return ProbeFailure;
  338. }
  339. return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader));
  340. }
  341. bool CSoundFile::ReadULT(FileReader &file, ModLoadingFlags loadFlags)
  342. {
  343. file.Rewind();
  344. UltFileHeader fileHeader;
  345. if(!file.ReadStruct(fileHeader))
  346. {
  347. return false;
  348. }
  349. if(!ValidateHeader(fileHeader))
  350. {
  351. return false;
  352. }
  353. if(loadFlags == onlyVerifyHeader)
  354. {
  355. return true;
  356. }
  357. if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader))))
  358. {
  359. return false;
  360. }
  361. InitializeGlobals(MOD_TYPE_ULT);
  362. m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songName);
  363. const mpt::uchar *versions[] = {UL_("<1.4"), UL_("1.4"), UL_("1.5"), UL_("1.6")};
  364. m_modFormat.formatName = U_("UltraTracker");
  365. m_modFormat.type = U_("ult");
  366. m_modFormat.madeWithTracker = U_("UltraTracker ") + versions[fileHeader.version - '1'];
  367. m_modFormat.charset = mpt::Charset::CP437;
  368. m_SongFlags = SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS; // this will be converted to IT format by MPT.
  369. // Read "messageLength" lines, each containing 32 characters.
  370. m_songMessage.ReadFixedLineLength(file, fileHeader.messageLength * 32, 32, 0);
  371. if(SAMPLEINDEX numSamples = file.ReadUint8(); numSamples < MAX_SAMPLES)
  372. m_nSamples = numSamples;
  373. else
  374. return false;
  375. for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
  376. {
  377. UltSample sampleHeader;
  378. // Annoying: v4 added a field before the end of the struct
  379. if(fileHeader.version >= '4')
  380. {
  381. file.ReadStruct(sampleHeader);
  382. } else
  383. {
  384. file.ReadStructPartial(sampleHeader, 64);
  385. sampleHeader.finetune = sampleHeader.speed;
  386. sampleHeader.speed = 8363;
  387. }
  388. sampleHeader.ConvertToMPT(Samples[smp]);
  389. m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.name);
  390. }
  391. ReadOrderFromFile<uint8>(Order(), file, 256, 0xFF, 0xFE);
  392. if(CHANNELINDEX numChannels = file.ReadUint8() + 1u; numChannels <= MAX_BASECHANNELS)
  393. m_nChannels = numChannels;
  394. else
  395. return false;
  396. PATTERNINDEX numPats = file.ReadUint8() + 1;
  397. for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++)
  398. {
  399. ChnSettings[chn].Reset();
  400. if(fileHeader.version >= '3')
  401. ChnSettings[chn].nPan = ((file.ReadUint8() & 0x0F) << 4) + 8;
  402. else
  403. ChnSettings[chn].nPan = (chn & 1) ? 192 : 64;
  404. }
  405. Patterns.ResizeArray(numPats);
  406. for(PATTERNINDEX pat = 0; pat < numPats; pat++)
  407. {
  408. if(!Patterns.Insert(pat, 64))
  409. return false;
  410. }
  411. for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++)
  412. {
  413. ModCommand evnote;
  414. for(PATTERNINDEX pat = 0; pat < numPats && file.CanRead(5); pat++)
  415. {
  416. ModCommand *note = Patterns[pat].GetpModCommand(0, chn);
  417. ROWINDEX row = 0;
  418. while(row < 64)
  419. {
  420. int repeat = ReadULTEvent(evnote, file, fileHeader.version);
  421. if(repeat + row > 64)
  422. repeat = 64 - row;
  423. if(repeat == 0) break;
  424. while(repeat--)
  425. {
  426. *note = evnote;
  427. note += GetNumChannels();
  428. row++;
  429. }
  430. }
  431. }
  432. }
  433. // Post-fix some effects.
  434. Patterns.ForEachModCommand(PostFixUltCommands(GetNumChannels()));
  435. if(loadFlags & loadSampleData)
  436. {
  437. for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
  438. {
  439. SampleIO(
  440. Samples[smp].uFlags[CHN_16BIT] ? SampleIO::_16bit : SampleIO::_8bit,
  441. SampleIO::mono,
  442. SampleIO::littleEndian,
  443. SampleIO::signedPCM)
  444. .ReadSample(Samples[smp], file);
  445. }
  446. }
  447. return true;
  448. }
  449. OPENMPT_NAMESPACE_END