1
0

Load_okt.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. /*
  2. * Load_okt.cpp
  3. * ------------
  4. * Purpose: OKT (Oktalyzer) 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 OktIffChunk
  14. {
  15. // IFF chunk names
  16. enum ChunkIdentifiers
  17. {
  18. idCMOD = MagicBE("CMOD"),
  19. idSAMP = MagicBE("SAMP"),
  20. idSPEE = MagicBE("SPEE"),
  21. idSLEN = MagicBE("SLEN"),
  22. idPLEN = MagicBE("PLEN"),
  23. idPATT = MagicBE("PATT"),
  24. idPBOD = MagicBE("PBOD"),
  25. idSBOD = MagicBE("SBOD"),
  26. };
  27. uint32be signature; // IFF chunk name
  28. uint32be chunksize; // chunk size without header
  29. };
  30. MPT_BINARY_STRUCT(OktIffChunk, 8)
  31. struct OktSample
  32. {
  33. char name[20];
  34. uint32be length; // length in bytes
  35. uint16be loopStart; // *2 for real value
  36. uint16be loopLength; // ditto
  37. uint16be volume; // default volume
  38. uint16be type; // 7-/8-bit sample
  39. };
  40. MPT_BINARY_STRUCT(OktSample, 32)
  41. // Parse the sample header block
  42. static void ReadOKTSamples(FileReader &chunk, CSoundFile &sndFile)
  43. {
  44. sndFile.m_nSamples = std::min(static_cast<SAMPLEINDEX>(chunk.BytesLeft() / sizeof(OktSample)), static_cast<SAMPLEINDEX>(MAX_SAMPLES - 1));
  45. for(SAMPLEINDEX smp = 1; smp <= sndFile.GetNumSamples(); smp++)
  46. {
  47. ModSample &mptSmp = sndFile.GetSample(smp);
  48. OktSample oktSmp;
  49. chunk.ReadStruct(oktSmp);
  50. mptSmp.Initialize();
  51. sndFile.m_szNames[smp] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, oktSmp.name);
  52. mptSmp.nC5Speed = 8287;
  53. mptSmp.nVolume = std::min(oktSmp.volume.get(), uint16(64)) * 4u;
  54. mptSmp.nLength = oktSmp.length & ~1;
  55. mptSmp.cues[0] = oktSmp.type; // Temporary storage for pattern reader, will be reset later
  56. // Parse loops
  57. const SmpLength loopStart = oktSmp.loopStart * 2;
  58. const SmpLength loopLength = oktSmp.loopLength * 2;
  59. if(loopLength > 2 && loopStart + loopLength <= mptSmp.nLength)
  60. {
  61. mptSmp.uFlags.set(CHN_SUSTAINLOOP);
  62. mptSmp.nSustainStart = loopStart;
  63. mptSmp.nSustainEnd = loopStart + loopLength;
  64. }
  65. }
  66. }
  67. // Parse a pattern block
  68. static void ReadOKTPattern(FileReader &chunk, PATTERNINDEX pat, CSoundFile &sndFile, const std::array<int8, 8> pairedChn)
  69. {
  70. if(!chunk.CanRead(2))
  71. {
  72. // Invent empty pattern
  73. sndFile.Patterns.Insert(pat, 64);
  74. return;
  75. }
  76. ROWINDEX rows = Clamp(static_cast<ROWINDEX>(chunk.ReadUint16BE()), ROWINDEX(1), MAX_PATTERN_ROWS);
  77. if(!sndFile.Patterns.Insert(pat, rows))
  78. {
  79. return;
  80. }
  81. const CHANNELINDEX chns = sndFile.GetNumChannels();
  82. for(ROWINDEX row = 0; row < rows; row++)
  83. {
  84. auto rowCmd = sndFile.Patterns[pat].GetRow(row);
  85. for(CHANNELINDEX chn = 0; chn < chns; chn++)
  86. {
  87. ModCommand &m = rowCmd[chn];
  88. const auto [note, instr, effect, param] = chunk.ReadArray<uint8, 4>();
  89. if(note > 0 && note <= 36)
  90. {
  91. m.note = note + (NOTE_MIDDLEC - 13);
  92. m.instr = instr + 1;
  93. if(m.instr > 0 && m.instr <= sndFile.GetNumSamples())
  94. {
  95. const auto &sample = sndFile.GetSample(m.instr);
  96. // Default volume only works on raw Paula channels
  97. if(pairedChn[chn] && sample.nVolume < 256)
  98. {
  99. m.volcmd = VOLCMD_VOLUME;
  100. m.vol = 64;
  101. }
  102. // If channel and sample type don't match, stop this channel (add 100 to the instrument number to make it understandable what happened during import)
  103. if((sample.cues[0] == 1 && pairedChn[chn] != 0) || (sample.cues[0] == 0 && pairedChn[chn] == 0))
  104. {
  105. m.instr += 100;
  106. }
  107. }
  108. }
  109. switch(effect)
  110. {
  111. case 0: // Nothing
  112. break;
  113. case 1: // 1 Portamento Down (Period)
  114. if(param)
  115. {
  116. m.command = CMD_PORTAMENTOUP;
  117. m.param = param;
  118. }
  119. break;
  120. case 2: // 2 Portamento Up (Period)
  121. if(param)
  122. {
  123. m.command = CMD_PORTAMENTODOWN;
  124. m.param = param;
  125. }
  126. break;
  127. #if 0
  128. /* these aren't like regular arpeggio: "down" means to *subtract* the offset from the note.
  129. For now I'm going to leave these unimplemented. */
  130. case 10: // A Arpeggio 1 (down, orig, up)
  131. case 11: // B Arpeggio 2 (orig, up, orig, down)
  132. if(param)
  133. {
  134. m.command = CMD_ARPEGGIO;
  135. m.param = param;
  136. }
  137. break;
  138. #endif
  139. // This one is close enough to "standard" arpeggio -- I think!
  140. case 12: // C Arpeggio 3 (up, up, orig)
  141. if(param)
  142. {
  143. m.command = CMD_ARPEGGIO;
  144. m.param = param;
  145. }
  146. break;
  147. case 13: // D Slide Down (Notes)
  148. if(param)
  149. {
  150. m.command = CMD_NOTESLIDEDOWN;
  151. m.param = 0x10 | std::min(uint8(0x0F), param);
  152. }
  153. break;
  154. case 30: // U Slide Up (Notes)
  155. if(param)
  156. {
  157. m.command = CMD_NOTESLIDEUP;
  158. m.param = 0x10 | std::min(uint8(0x0F), param);
  159. }
  160. break;
  161. // Fine Slides are only implemented for libopenmpt. For OpenMPT,
  162. // sliding every 5 (non-note) ticks kind of works (at least at
  163. // speed 6), but implementing separate (format-agnostic) fine slide commands would of course be better.
  164. case 21: // L Slide Down Once (Notes)
  165. if(param)
  166. {
  167. m.command = CMD_NOTESLIDEDOWN;
  168. m.param = 0x50 | std::min(uint8(0x0F), param);
  169. }
  170. break;
  171. case 17: // H Slide Up Once (Notes)
  172. if(param)
  173. {
  174. m.command = CMD_NOTESLIDEUP;
  175. m.param = 0x50 | std::min(uint8(0x0F), param);
  176. }
  177. break;
  178. case 15: // F Set Filter <>00:ON
  179. m.command = CMD_MODCMDEX;
  180. m.param = !!param;
  181. break;
  182. case 25: // P Pos Jump
  183. m.command = CMD_POSITIONJUMP;
  184. m.param = param;
  185. break;
  186. case 27: // R Release sample (apparently not listed in the help!)
  187. m.Clear();
  188. m.note = NOTE_KEYOFF;
  189. break;
  190. case 28: // S Speed
  191. if(param < 0x20)
  192. {
  193. m.command = CMD_SPEED;
  194. m.param = param;
  195. }
  196. break;
  197. case 31: // V Volume
  198. // Volume on mixed channels is permanent, on hardware channels it behaves like in regular MODs
  199. if(param & 0x0F)
  200. {
  201. m.command = pairedChn[chn] ? CMD_CHANNELVOLSLIDE : CMD_VOLUMESLIDE;
  202. m.param = param & 0x0F;
  203. }
  204. switch(param >> 4)
  205. {
  206. case 4: // Normal slide down
  207. if(param != 0x40)
  208. break;
  209. // 0x40 is set volume -- fall through
  210. [[fallthrough]];
  211. case 0: case 1: case 2: case 3:
  212. if(pairedChn[chn])
  213. {
  214. m.command = CMD_CHANNELVOLUME;
  215. m.param = param;
  216. } else
  217. {
  218. m.volcmd = VOLCMD_VOLUME;
  219. m.vol = param;
  220. m.command = CMD_NONE;
  221. }
  222. break;
  223. case 5: // Normal slide up
  224. m.param <<= 4;
  225. break;
  226. case 6: // Fine slide down
  227. m.param = 0xF0 | std::min(static_cast<uint8>(m.param), uint8(0x0E));
  228. break;
  229. case 7: // Fine slide up
  230. m.param = (std::min(static_cast<uint8>(m.param), uint8(0x0E)) << 4) | 0x0F;
  231. break;
  232. default:
  233. // Junk.
  234. m.command = CMD_NONE;
  235. break;
  236. }
  237. // Volume is shared between two mixed channels, second channel has priority
  238. if(m.command == CMD_CHANNELVOLUME || m.command == CMD_CHANNELVOLSLIDE)
  239. {
  240. ModCommand &other = rowCmd[chn + pairedChn[chn]];
  241. // Try to preserve effect if there already was one
  242. if(other.ConvertVolEffect(other.command, other.param, true))
  243. {
  244. other.volcmd = other.command;
  245. other.vol = other.param;
  246. }
  247. other.command = m.command;
  248. other.param = m.param;
  249. }
  250. break;
  251. #if 0
  252. case 24: // O Old Volume (???)
  253. m.command = CMD_VOLUMESLIDE;
  254. m.param = 0;
  255. break;
  256. #endif
  257. default:
  258. m.command = CMD_NONE;
  259. break;
  260. }
  261. }
  262. }
  263. }
  264. CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderOKT(MemoryFileReader file, const uint64 *pfilesize)
  265. {
  266. if(!file.CanRead(8))
  267. {
  268. return ProbeWantMoreData;
  269. }
  270. if(!file.ReadMagic("OKTASONG"))
  271. {
  272. return ProbeFailure;
  273. }
  274. OktIffChunk iffHead;
  275. if(!file.ReadStruct(iffHead))
  276. {
  277. return ProbeWantMoreData;
  278. }
  279. if(iffHead.chunksize == 0)
  280. {
  281. return ProbeFailure;
  282. }
  283. if((iffHead.signature & 0x80808080u) != 0) // ASCII?
  284. {
  285. return ProbeFailure;
  286. }
  287. MPT_UNREFERENCED_PARAMETER(pfilesize);
  288. return ProbeSuccess;
  289. }
  290. bool CSoundFile::ReadOKT(FileReader &file, ModLoadingFlags loadFlags)
  291. {
  292. file.Rewind();
  293. if(!file.ReadMagic("OKTASONG"))
  294. {
  295. return false;
  296. }
  297. // prepare some arrays to store offsets etc.
  298. std::vector<FileReader> patternChunks;
  299. std::vector<FileReader> sampleChunks;
  300. std::array<int8, 8> pairedChn{{}};
  301. ORDERINDEX numOrders = 0;
  302. InitializeGlobals(MOD_TYPE_OKT);
  303. m_modFormat.formatName = U_("Oktalyzer");
  304. m_modFormat.type = U_("okt");
  305. m_modFormat.charset = mpt::Charset::Amiga_no_C1;
  306. // Go through IFF chunks...
  307. while(file.CanRead(sizeof(OktIffChunk)))
  308. {
  309. OktIffChunk iffHead;
  310. if(!file.ReadStruct(iffHead))
  311. {
  312. break;
  313. }
  314. FileReader chunk = file.ReadChunk(iffHead.chunksize);
  315. if(!chunk.IsValid())
  316. {
  317. break;
  318. }
  319. switch(iffHead.signature)
  320. {
  321. case OktIffChunk::idCMOD:
  322. // Channel setup table
  323. if(m_nChannels == 0 && chunk.GetLength() >= 8)
  324. {
  325. const auto chnTable = chunk.ReadArray<uint16be, 4>();
  326. for(CHANNELINDEX chn = 0; chn < 4; chn++)
  327. {
  328. if(chnTable[chn])
  329. {
  330. pairedChn[m_nChannels] = 1;
  331. pairedChn[m_nChannels + 1] = -1;
  332. ChnSettings[m_nChannels].Reset();
  333. ChnSettings[m_nChannels++].nPan = (((chn & 3) == 1) || ((chn & 3) == 2)) ? 0xC0 : 0x40;
  334. }
  335. ChnSettings[m_nChannels].Reset();
  336. ChnSettings[m_nChannels++].nPan = (((chn & 3) == 1) || ((chn & 3) == 2)) ? 0xC0 : 0x40;
  337. }
  338. if(loadFlags == onlyVerifyHeader)
  339. {
  340. return true;
  341. }
  342. }
  343. break;
  344. case OktIffChunk::idSAMP:
  345. // Convert sample headers
  346. if(m_nSamples > 0)
  347. {
  348. break;
  349. }
  350. ReadOKTSamples(chunk, *this);
  351. break;
  352. case OktIffChunk::idSPEE:
  353. // Read default speed
  354. if(chunk.GetLength() >= 2)
  355. {
  356. m_nDefaultSpeed = Clamp(chunk.ReadUint16BE(), uint16(1), uint16(255));
  357. }
  358. break;
  359. case OktIffChunk::idSLEN:
  360. // Number of patterns, we don't need this.
  361. break;
  362. case OktIffChunk::idPLEN:
  363. // Read number of valid orders
  364. if(chunk.GetLength() >= 2)
  365. {
  366. numOrders = chunk.ReadUint16BE();
  367. }
  368. break;
  369. case OktIffChunk::idPATT:
  370. // Read the orderlist
  371. ReadOrderFromFile<uint8>(Order(), chunk, chunk.GetLength(), 0xFF, 0xFE);
  372. break;
  373. case OktIffChunk::idPBOD:
  374. // Don't read patterns for now, as the number of channels might be unknown at this point.
  375. if(patternChunks.size() < 256)
  376. {
  377. patternChunks.push_back(chunk);
  378. }
  379. break;
  380. case OktIffChunk::idSBOD:
  381. // Sample data - same as with patterns, as we need to know the sample format / length
  382. if(sampleChunks.size() < MAX_SAMPLES - 1 && chunk.GetLength() > 0)
  383. {
  384. sampleChunks.push_back(chunk);
  385. }
  386. break;
  387. default:
  388. // Non-ASCII chunk ID?
  389. if(iffHead.signature & 0x80808080)
  390. return false;
  391. break;
  392. }
  393. }
  394. // If there wasn't even a CMOD chunk, we can't really load this.
  395. if(m_nChannels == 0)
  396. return false;
  397. m_nDefaultTempo.Set(125);
  398. m_nDefaultGlobalVolume = MAX_GLOBAL_VOLUME;
  399. m_nSamplePreAmp = m_nVSTiVolume = 48;
  400. m_nMinPeriod = 113 * 4;
  401. m_nMaxPeriod = 856 * 4;
  402. // Fix orderlist
  403. Order().resize(numOrders);
  404. // Read patterns
  405. if(loadFlags & loadPatternData)
  406. {
  407. Patterns.ResizeArray(static_cast<PATTERNINDEX>(patternChunks.size()));
  408. for(PATTERNINDEX pat = 0; pat < patternChunks.size(); pat++)
  409. {
  410. ReadOKTPattern(patternChunks[pat], pat, *this, pairedChn);
  411. }
  412. }
  413. // Read samples
  414. size_t fileSmp = 0;
  415. for(SAMPLEINDEX smp = 1; smp < m_nSamples; smp++)
  416. {
  417. if(fileSmp >= sampleChunks.size() || !(loadFlags & loadSampleData))
  418. break;
  419. ModSample &mptSample = Samples[smp];
  420. mptSample.SetDefaultCuePoints();
  421. if(mptSample.nLength == 0)
  422. continue;
  423. // Weird stuff?
  424. LimitMax(mptSample.nLength, mpt::saturate_cast<SmpLength>(sampleChunks[fileSmp].GetLength()));
  425. SampleIO(
  426. SampleIO::_8bit,
  427. SampleIO::mono,
  428. SampleIO::bigEndian,
  429. SampleIO::signedPCM)
  430. .ReadSample(mptSample, sampleChunks[fileSmp]);
  431. fileSmp++;
  432. }
  433. return true;
  434. }
  435. OPENMPT_NAMESPACE_END