1
0

Load_dsym.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613
  1. /*
  2. * Load_dsym.cpp
  3. * -------------
  4. * Purpose: Digital Symphony module loader
  5. * Notes : Based on information from the DSym_Info file and sigma-delta decompression code from TimPlayer.
  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. #include "BitReader.h"
  12. OPENMPT_NAMESPACE_BEGIN
  13. struct DSymFileHeader
  14. {
  15. char magic[8];
  16. uint8le version; // 0 / 1
  17. uint8le numChannels; // 1...8
  18. uint16le numOrders; // 0...4096
  19. uint16le numTracks; // 0...4096
  20. uint16le infoLenLo;
  21. uint8le infoLenHi;
  22. bool Validate() const
  23. {
  24. return !std::memcmp(magic, "\x02\x01\x13\x13\x14\x12\x01\x0B", 8)
  25. && version <= 1
  26. && numChannels >= 1 && numChannels <= 8
  27. && numOrders <= 4096
  28. && numTracks <= 4096;
  29. }
  30. uint64 GetHeaderMinimumAdditionalSize() const
  31. {
  32. return 72u;
  33. }
  34. };
  35. MPT_BINARY_STRUCT(DSymFileHeader, 17)
  36. static std::vector<std::byte> DecompressDSymLZW(FileReader &file, uint32 size)
  37. {
  38. BitReader bitFile(file);
  39. const auto startPos = bitFile.GetPosition();
  40. // In the best case, 13 bits decode 8192 bytes, a ratio of approximately 1:5042.
  41. // Too much for reserving memory in case of malformed files, just choose an arbitrary but realistic upper limit.
  42. std::vector<std::byte> output;
  43. output.reserve(std::min(size, std::min(mpt::saturate_cast<uint32>(file.BytesLeft()), Util::MaxValueOfType(size) / 50u) * 50u));
  44. static constexpr uint16 lzwBits = 13, MaxNodes = 1 << lzwBits;
  45. static constexpr uint16 ResetDict = 256, EndOfStream = 257;
  46. struct LZWEntry
  47. {
  48. uint16 prev;
  49. std::byte value;
  50. };
  51. std::vector<LZWEntry> dictionary(MaxNodes);
  52. std::vector<std::byte> match(MaxNodes);
  53. // Initialize dictionary
  54. for(int i = 0; i < 256; i++)
  55. {
  56. dictionary[i].prev = MaxNodes;
  57. dictionary[i].value = static_cast<std::byte>(i);
  58. }
  59. uint8 codeSize = 9;
  60. uint16 prevCode = 0;
  61. uint16 nextIndex = 257;
  62. while(true)
  63. {
  64. // Read next code
  65. const auto newCode = static_cast<uint16>(bitFile.ReadBits(codeSize));
  66. if(newCode == EndOfStream || newCode > nextIndex || output.size() >= size)
  67. break;
  68. // Reset dictionary
  69. if(newCode == ResetDict)
  70. {
  71. codeSize = 9;
  72. prevCode = 0;
  73. nextIndex = 257;
  74. continue;
  75. }
  76. // Output
  77. auto code = (newCode < nextIndex) ? newCode : prevCode;
  78. auto writeOffset = MaxNodes;
  79. do
  80. {
  81. match[--writeOffset] = dictionary[code].value;
  82. code = dictionary[code].prev;
  83. } while(code < MaxNodes);
  84. output.insert(output.end(), match.begin() + writeOffset, match.end());
  85. // Handling for KwKwK problem
  86. if(newCode == nextIndex)
  87. output.push_back(match[writeOffset]);
  88. // Add to dictionary
  89. if(nextIndex < MaxNodes)
  90. {
  91. // Special case for FULLEFFECT, NARCOSIS and NEWDANCE, which end with a dictionary size of 512
  92. // right before the end-of-stream token, but the code size is expected to be 9
  93. if(output.size() >= size)
  94. continue;
  95. dictionary[nextIndex].value = match[writeOffset];
  96. dictionary[nextIndex].prev = prevCode;
  97. nextIndex++;
  98. if(nextIndex != MaxNodes && nextIndex == (1u << codeSize))
  99. codeSize++;
  100. }
  101. prevCode = newCode;
  102. }
  103. MPT_ASSERT(output.size() == size);
  104. // Align length to 4 bytes
  105. file.Seek(startPos + ((bitFile.GetPosition() - startPos + 3u) & ~FileReader::off_t(3)));
  106. return output;
  107. }
  108. static std::vector<std::byte> DecompressDSymSigmaDelta(FileReader &file, uint32 size)
  109. {
  110. const uint8 maxRunLength = std::max(file.ReadUint8(), uint8(1));
  111. BitReader bitFile(file);
  112. const auto startPos = bitFile.GetPosition();
  113. // In the best case, sigma-delta compression represents each sample point as one bit.
  114. // As a result, if we have a file length of n, we know that the sample can be at most n*8 sample points long.
  115. LimitMax(size, std::min(mpt::saturate_cast<uint32>(file.BytesLeft()), Util::MaxValueOfType(size) / 8u) * 8u);
  116. std::vector<std::byte> output(size);
  117. uint32 pos = 0;
  118. uint8 runLength = maxRunLength;
  119. uint8 numBits = 8;
  120. uint8 accum = static_cast<uint8>(bitFile.ReadBits(numBits));
  121. output[pos++] = mpt::byte_cast<std::byte>(accum);
  122. while(pos < size)
  123. {
  124. const uint32 value = bitFile.ReadBits(numBits);
  125. // Increase bit width
  126. if(value == 0)
  127. {
  128. if(numBits >= 9)
  129. break;
  130. numBits++;
  131. runLength = maxRunLength;
  132. continue;
  133. }
  134. if(value & 1)
  135. accum -= static_cast<uint8>(value >> 1);
  136. else
  137. accum += static_cast<uint8>(value >> 1);
  138. output[pos++] = mpt::byte_cast<std::byte>(accum);
  139. // Reset run length if high bit is set
  140. if((value >> (numBits - 1u)) != 0)
  141. {
  142. runLength = maxRunLength;
  143. continue;
  144. }
  145. // Decrease bit width
  146. if(--runLength == 0)
  147. {
  148. if(numBits > 1)
  149. numBits--;
  150. runLength = maxRunLength;
  151. }
  152. }
  153. // Align length to 4 bytes
  154. file.Seek(startPos + ((bitFile.GetPosition() - startPos + 3u) & ~FileReader::off_t(3)));
  155. return output;
  156. }
  157. static bool ReadDSymChunk(FileReader &file, std::vector<std::byte> &data, uint32 size)
  158. {
  159. const uint8 packingType = file.ReadUint8();
  160. if(packingType > 1)
  161. return false;
  162. if(packingType)
  163. {
  164. try
  165. {
  166. data = DecompressDSymLZW(file, size);
  167. } catch(const BitReader::eof &)
  168. {
  169. return false;
  170. }
  171. } else
  172. {
  173. if(!file.CanRead(size))
  174. return false;
  175. file.ReadVector(data, size);
  176. }
  177. return data.size() >= size;
  178. }
  179. CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderDSym(MemoryFileReader file, const uint64 *pfilesize)
  180. {
  181. DSymFileHeader fileHeader;
  182. if(!file.ReadStruct(fileHeader))
  183. return ProbeWantMoreData;
  184. if(!fileHeader.Validate())
  185. return ProbeFailure;
  186. return ProbeAdditionalSize(file, pfilesize, fileHeader.GetHeaderMinimumAdditionalSize());
  187. }
  188. bool CSoundFile::ReadDSym(FileReader &file, ModLoadingFlags loadFlags)
  189. {
  190. DSymFileHeader fileHeader;
  191. file.Rewind();
  192. if(!file.ReadStruct(fileHeader) || !fileHeader.Validate())
  193. return false;
  194. if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(fileHeader.GetHeaderMinimumAdditionalSize())))
  195. return false;
  196. if(loadFlags == onlyVerifyHeader)
  197. return true;
  198. InitializeGlobals(MOD_TYPE_MOD);
  199. m_SongFlags.set(SONG_IMPORTED | SONG_AMIGALIMITS);
  200. m_SongFlags.reset(SONG_ISAMIGA);
  201. m_nChannels = fileHeader.numChannels;
  202. m_nSamples = 63;
  203. for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++)
  204. {
  205. InitChannel(chn);
  206. ChnSettings[chn].nPan = (((chn & 3) == 1) || ((chn & 3) == 2)) ? 64 : 192;
  207. }
  208. uint8 sampleNameLength[64] = {};
  209. for(SAMPLEINDEX smp = 1; smp <= m_nSamples; smp++)
  210. {
  211. Samples[smp].Initialize(MOD_TYPE_MOD);
  212. sampleNameLength[smp] = file.ReadUint8();
  213. if(!(sampleNameLength[smp] & 0x80))
  214. Samples[smp].nLength = file.ReadUint24LE() << 1;
  215. }
  216. file.ReadSizedString<uint8le, mpt::String::spacePadded>(m_songName);
  217. const auto allowedCommands = file.ReadArray<uint8, 8>();
  218. std::vector<std::byte> sequenceData;
  219. if(fileHeader.numOrders)
  220. {
  221. const uint32 sequenceSize = fileHeader.numOrders * fileHeader.numChannels * 2u;
  222. if(!ReadDSymChunk(file, sequenceData, sequenceSize))
  223. return false;
  224. }
  225. const auto sequence = mpt::as_span(reinterpret_cast<uint16le *>(sequenceData.data()), sequenceData.size() / 2u);
  226. std::vector<std::byte> trackData;
  227. trackData.reserve(fileHeader.numTracks * 256u);
  228. // For some reason, patterns are stored in 512K chunks
  229. for(uint16 offset = 0; offset < fileHeader.numTracks; offset += 2000)
  230. {
  231. const uint32 chunkSize = std::min(fileHeader.numTracks - offset, 2000) * 256;
  232. std::vector<std::byte> chunk;
  233. if(!ReadDSymChunk(file, chunk, chunkSize))
  234. return false;
  235. trackData.insert(trackData.end(), chunk.begin(), chunk.end());
  236. }
  237. const auto tracks = mpt::byte_cast<mpt::span<uint8>>(mpt::as_span(trackData));
  238. Order().resize(fileHeader.numOrders);
  239. for(ORDERINDEX pat = 0; pat < fileHeader.numOrders; pat++)
  240. {
  241. Order()[pat] = pat;
  242. if(!(loadFlags & loadPatternData) || !Patterns.Insert(pat, 64))
  243. continue;
  244. for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++)
  245. {
  246. const uint16 track = sequence[pat * m_nChannels + chn];
  247. if(track >= fileHeader.numTracks)
  248. continue;
  249. ModCommand *m = Patterns[pat].GetpModCommand(0, chn);
  250. for(ROWINDEX row = 0; row < 64; row++, m += m_nChannels)
  251. {
  252. const auto data = tracks.subspan(track * 256 + row * 4, 4);
  253. m->note = data[0] & 0x3F;
  254. if(m->note)
  255. m->note += 47 + NOTE_MIN;
  256. else
  257. m->note = NOTE_NONE;
  258. m->instr = (data[0] >> 6) | ((data[1] & 0x0F) << 2);
  259. const uint8 command = (data[1] >> 6) | ((data[2] & 0x0F) << 2);
  260. const uint16 param = (data[2] >> 4) | (data[3] << 4);
  261. if(!(allowedCommands[command >> 3u] & (1u << (command & 7u))))
  262. continue;
  263. if(command == 0 && param == 0)
  264. continue;
  265. m->command = command;
  266. m->param = static_cast<uint8>(param);
  267. m->vol = static_cast<ModCommand::VOL>(param >> 8);
  268. switch(command)
  269. {
  270. case 0x00: // 00 xyz Normal play or Arpeggio + Volume Slide Up
  271. case 0x01: // 01 xyy Slide Up + Volume Slide Up
  272. case 0x02: // 01 xyy Slide Up + Volume Slide Up
  273. case 0x20: // 20 xyz Normal play or Arpeggio + Volume Slide Down
  274. case 0x21: // 21 xyy Slide Up + Volume Slide Down
  275. case 0x22: // 22 xyy Slide Down + Volume Slide Down
  276. m->command &= 0x0F;
  277. ConvertModCommand(*m);
  278. if(m->vol)
  279. m->volcmd = (command < 0x20) ? VOLCMD_VOLSLIDEUP : VOLCMD_VOLSLIDEDOWN;
  280. break;
  281. case 0x03: // 03 xyy Tone Portamento
  282. case 0x04: // 04 xyz Vibrato
  283. case 0x05: // 05 xyz Tone Portamento + Volume Slide
  284. case 0x06: // 06 xyz Vibrato + Volume Slide
  285. case 0x07: // 07 xyz Tremolo
  286. case 0x0C: // 0C xyy Set Volume
  287. ConvertModCommand(*m);
  288. break;
  289. case 0x09: // 09 xxx Set Sample Offset
  290. m->command = CMD_OFFSET;
  291. m->param = static_cast<ModCommand::PARAM>(param >> 1);
  292. if(param >= 0x200)
  293. {
  294. m->volcmd = VOLCMD_OFFSET;
  295. m->vol >>= 1;
  296. }
  297. break;
  298. case 0x0A: // 0A xyz Volume Slide + Fine Slide Up
  299. case 0x2A: // 2A xyz Volume Slide + Fine Slide Down
  300. if(param < 0xFF)
  301. {
  302. m->command &= 0x0F;
  303. ConvertModCommand(*m);
  304. } else
  305. {
  306. m->command = CMD_MODCMDEX;
  307. m->param = static_cast<ModCommand::PARAM>(((command < 0x20) ? 0x10 : 0x20) | (param >> 8));
  308. if(param & 0xF0)
  309. {
  310. m->volcmd = VOLCMD_VOLSLIDEUP;
  311. m->vol = static_cast<ModCommand::VOL>((param >> 4) & 0x0F);
  312. } else
  313. {
  314. m->volcmd = VOLCMD_VOLSLIDEDOWN;
  315. m->vol = static_cast<ModCommand::VOL>(param & 0x0F);
  316. }
  317. }
  318. break;
  319. case 0x0B: // 0B xxx Position Jump
  320. case 0x0F: // 0F xxx Set Speed
  321. m->command = (command == 0x0B) ? CMD_POSITIONJUMP : CMD_SPEED;
  322. m->param = mpt::saturate_cast<ModCommand::PARAM>(param);
  323. break;
  324. case 0x0D: // 0D xyy Pattern Break (not BCD-encoded like in MOD)
  325. m->command = CMD_PATTERNBREAK;
  326. if(m->param > 63)
  327. m->param = 0;
  328. break;
  329. case 0x10: // 10 xxy Filter Control (not implemented in Digital Symphony)
  330. case 0x13: // 13 xxy Glissando Control
  331. case 0x14: // 14 xxy Set Vibrato Waveform
  332. case 0x15: // 15 xxy Set Fine Tune
  333. case 0x17: // 17 xxy Set Tremolo Waveform
  334. case 0x1F: // 1F xxy Invert Loop
  335. m->command = CMD_MODCMDEX;
  336. m->param = (command << 4) | (m->param & 0x0F);
  337. break;
  338. case 0x16: // 16 xxx Jump to Loop
  339. case 0x19: // 19 xxx Retrig Note
  340. case 0x1C: // 1C xxx Note Cut
  341. case 0x1D: // 1D xxx Note Delay
  342. case 0x1E: // 1E xxx Pattern Delay
  343. m->command = CMD_MODCMDEX;
  344. m->param = (command << 4) | static_cast<ModCommand::PARAM>(std::min(param, uint16(0x0F)));
  345. break;
  346. case 0x11: // 11 xyy Fine Slide Up + Fine Volume Slide Up
  347. case 0x12: // 12 xyy Fine Slide Down + Fine Volume Slide Up
  348. case 0x1A: // 1A xyy Fine Slide Up + Fine Volume Slide Down
  349. case 0x1B: // 1B xyy Fine Slide Down + Fine Volume Slide Down
  350. m->command = CMD_MODCMDEX;
  351. if(m->param & 0xFF)
  352. {
  353. m->param = static_cast<ModCommand::PARAM>(((command == 0x11 || command == 0x1A) ? 0x10 : 0x20) | (param & 0x0F));
  354. if(param & 0xF00)
  355. m->volcmd = (command >= 0x1A) ? VOLCMD_FINEVOLDOWN : VOLCMD_FINEVOLUP;
  356. } else
  357. {
  358. m->param = static_cast<ModCommand::PARAM>(((command >= 0x1A) ? 0xB0 : 0xA0) | (param >> 8));
  359. }
  360. break;
  361. case 0x2F: // 2F xxx Set Tempo
  362. if(param > 0)
  363. {
  364. m->command = CMD_TEMPO;
  365. m->param = mpt::saturate_cast<ModCommand::PARAM>(std::max(8, param + 4) / 8);
  366. #ifdef MODPLUG_TRACKER
  367. m->param = std::max(m->param, ModCommand::PARAM(0x20));
  368. #endif
  369. } else
  370. {
  371. m->command = CMD_NONE;
  372. }
  373. break;
  374. case 0x2B: // 2B xyy Line Jump
  375. m->command = CMD_PATTERNBREAK;
  376. for(CHANNELINDEX brkChn = 0; brkChn < m_nChannels; brkChn++)
  377. {
  378. ModCommand &cmd = *(m - chn + brkChn);
  379. if(cmd.command != CMD_NONE)
  380. continue;
  381. cmd.command = CMD_POSITIONJUMP;
  382. cmd.param = mpt::saturate_cast<ModCommand::PARAM>(pat);
  383. }
  384. break;
  385. case 0x30: // 30 xxy Set Stereo
  386. m->command = CMD_PANNING8;
  387. if(param & 7)
  388. {
  389. static constexpr uint8 panning[8] = {0x00, 0x00, 0x2B, 0x56, 0x80, 0xAA, 0xD4, 0xFF};
  390. m->param = panning[param & 7];
  391. } else if((param >> 4) != 0x80)
  392. {
  393. m->param = static_cast<ModCommand::PARAM>(param >> 4);
  394. if(m->param < 0x80)
  395. m->param += 0x80;
  396. else
  397. m->param = 0xFF - m->param;
  398. } else
  399. {
  400. m->command = CMD_NONE;
  401. }
  402. break;
  403. case 0x32: // 32 xxx Unset Sample Repeat
  404. m->command = CMD_NONE;
  405. m->param = 0;
  406. if(m->note == NOTE_NONE)
  407. m->note = NOTE_KEYOFF;
  408. else
  409. m->command = CMD_KEYOFF;
  410. break;
  411. case 0x31: // 31 xxx Song Upcall
  412. default:
  413. m->command = CMD_NONE;
  414. break;
  415. }
  416. }
  417. }
  418. }
  419. for(SAMPLEINDEX smp = 1; smp <= m_nSamples; smp++)
  420. {
  421. file.ReadString<mpt::String::maybeNullTerminated>(m_szNames[smp], sampleNameLength[smp] & 0x3F);
  422. if(sampleNameLength[smp] & 0x80)
  423. continue;
  424. ModSample &mptSmp = Samples[smp];
  425. mptSmp.nSustainStart = file.ReadUint24LE() << 1;
  426. if(const auto loopLen = file.ReadUint24LE() << 1; loopLen > 2)
  427. {
  428. mptSmp.nSustainEnd = mptSmp.nSustainStart + loopLen;
  429. mptSmp.uFlags.set(CHN_SUSTAINLOOP);
  430. }
  431. mptSmp.nVolume = std::min(file.ReadUint8(), uint8(64)) * 4u;
  432. mptSmp.nFineTune = MOD2XMFineTune(file.ReadUint8());
  433. mptSmp.Set16BitCuePoints();
  434. if(!mptSmp.nLength)
  435. continue;
  436. const uint8 packingType = file.ReadUint8();
  437. switch(packingType)
  438. {
  439. case 0: // Modified u-Law
  440. if(loadFlags & loadSampleData)
  441. {
  442. std::vector<std::byte> sampleData;
  443. if(!file.CanRead(mptSmp.nLength))
  444. return false;
  445. file.ReadVector(sampleData, mptSmp.nLength);
  446. for(auto &b : sampleData)
  447. {
  448. uint8 v = mpt::byte_cast<uint8>(b);
  449. v = (v << 7) | (static_cast<uint8>(~v) >> 1);
  450. b = mpt::byte_cast<std::byte>(v);
  451. }
  452. FileReader sampleDataFile = FileReader(mpt::as_span(sampleData));
  453. SampleIO(
  454. SampleIO::_16bit,
  455. SampleIO::mono,
  456. SampleIO::littleEndian,
  457. SampleIO::uLaw)
  458. .ReadSample(mptSmp, sampleDataFile);
  459. } else
  460. {
  461. file.Skip(mptSmp.nLength);
  462. }
  463. break;
  464. case 1: // 13-bit LZW applied to linear sample data differences
  465. {
  466. std::vector<std::byte> sampleData;
  467. try
  468. {
  469. sampleData = DecompressDSymLZW(file, mptSmp.nLength);
  470. } catch(const BitReader::eof &)
  471. {
  472. return false;
  473. }
  474. if(!(loadFlags & loadSampleData))
  475. break;
  476. FileReader sampleDataFile = FileReader(mpt::as_span(sampleData));
  477. SampleIO(
  478. SampleIO::_8bit,
  479. SampleIO::mono,
  480. SampleIO::littleEndian,
  481. SampleIO::deltaPCM)
  482. .ReadSample(mptSmp, sampleDataFile);
  483. }
  484. break;
  485. case 2: // 8-bit signed
  486. case 3: // 16-bit signed
  487. if(loadFlags & loadSampleData)
  488. {
  489. SampleIO(
  490. (packingType == 2) ? SampleIO::_8bit : SampleIO::_16bit,
  491. SampleIO::mono,
  492. SampleIO::littleEndian,
  493. SampleIO::signedPCM)
  494. .ReadSample(mptSmp, file);
  495. } else
  496. {
  497. file.Skip(mptSmp.nLength * (packingType - 1));
  498. }
  499. break;
  500. case 4: // Sigma-Delta compression applied to linear sample differences
  501. case 5: // Sigma-Delta compression applied to logarithmic sample differences
  502. {
  503. std::vector<std::byte> sampleData;
  504. try
  505. {
  506. sampleData = DecompressDSymSigmaDelta(file, mptSmp.nLength);
  507. } catch(const BitReader::eof &)
  508. {
  509. return false;
  510. }
  511. if(!(loadFlags & loadSampleData))
  512. break;
  513. if(packingType == 5)
  514. {
  515. static constexpr uint8 xorMask[] = {0x00, 0x7F};
  516. for(auto &b : sampleData)
  517. {
  518. uint8 v = mpt::byte_cast<uint8>(b);
  519. v ^= xorMask[v >> 7];
  520. b = mpt::byte_cast<std::byte>(v);
  521. }
  522. }
  523. FileReader sampleDataFile = FileReader(mpt::as_span(sampleData));
  524. SampleIO(
  525. (packingType == 5) ? SampleIO::_16bit : SampleIO::_8bit,
  526. SampleIO::mono,
  527. SampleIO::littleEndian,
  528. (packingType == 5) ? SampleIO::uLaw : SampleIO::unsignedPCM)
  529. .ReadSample(mptSmp, sampleDataFile);
  530. }
  531. break;
  532. default:
  533. return false;
  534. }
  535. }
  536. if(const uint32 infoLen = fileHeader.infoLenLo | (fileHeader.infoLenHi << 16); infoLen > 0)
  537. {
  538. std::vector<std::byte> infoData;
  539. if(!ReadDSymChunk(file, infoData, infoLen))
  540. return false;
  541. FileReader infoChunk = FileReader(mpt::as_span(infoData));
  542. m_songMessage.Read(infoChunk, infoLen, SongMessage::leLF);
  543. }
  544. m_modFormat.formatName = MPT_UFORMAT("Digital Symphony v{}")(fileHeader.version);
  545. m_modFormat.type = U_("dsym"); // RISC OS doesn't use file extensions but this is a common abbreviation used for this tracker
  546. m_modFormat.madeWithTracker = U_("Digital Symphony");
  547. m_modFormat.charset = mpt::Charset::RISC_OS;
  548. return true;
  549. }
  550. OPENMPT_NAMESPACE_END