Load_digi.cpp 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. /*
  2. * Load_digi.cpp
  3. * -------------
  4. * Purpose: Digi Booster module loader
  5. * Notes : Basically these are like ProTracker MODs with a few extra features such as more channels, longer samples and a few more effects.
  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. // DIGI File Header
  13. struct DIGIFileHeader
  14. {
  15. char signature[20];
  16. char versionStr[4]; // Supposed to be "V1.6" or similar, but other values like "TAP!" have been found as well.
  17. uint8be versionInt; // e.g. 0x16 = 1.6
  18. uint8be numChannels;
  19. uint8be packEnable;
  20. char unknown[19];
  21. uint8be lastPatIndex;
  22. uint8be lastOrdIndex;
  23. uint8be orders[128];
  24. uint32be smpLength[31];
  25. uint32be smpLoopStart[31];
  26. uint32be smpLoopLength[31];
  27. uint8be smpVolume[31];
  28. uint8be smpFinetune[31];
  29. };
  30. MPT_BINARY_STRUCT(DIGIFileHeader, 610)
  31. static void ReadDIGIPatternEntry(FileReader &file, ModCommand &m)
  32. {
  33. CSoundFile::ReadMODPatternEntry(file, m);
  34. CSoundFile::ConvertModCommand(m);
  35. if(m.command == CMD_MODCMDEX)
  36. {
  37. switch(m.param & 0xF0)
  38. {
  39. case 0x30:
  40. // E3x: Play sample backwards (E30 stops sample when it reaches the beginning, any other value plays it from the beginning including regular loop)
  41. // The play direction is also reset if a new note is played on the other channel linked to this channel.
  42. // The behaviour is rather broken when there is no note next to the ommand.
  43. m.command = CMD_DIGIREVERSESAMPLE;
  44. m.param &= 0x0F;
  45. break;
  46. case 0x40:
  47. // E40: Stop playing sample
  48. if(m.param == 0x40)
  49. {
  50. m.note = NOTE_NOTECUT;
  51. m.command = CMD_NONE;
  52. }
  53. break;
  54. case 0x80:
  55. // E8x: High sample offset
  56. m.command = CMD_S3MCMDEX;
  57. m.param = 0xA0 | (m.param & 0x0F);
  58. }
  59. } else if(m.command == CMD_PANNING8)
  60. {
  61. // 8xx "Robot" effect (not supported)
  62. m.command = CMD_NONE;
  63. }
  64. }
  65. static bool ValidateHeader(const DIGIFileHeader &fileHeader)
  66. {
  67. if(std::memcmp(fileHeader.signature, "DIGI Booster module\0", 20)
  68. || !fileHeader.numChannels
  69. || fileHeader.numChannels > 8
  70. || fileHeader.lastOrdIndex > 127)
  71. {
  72. return false;
  73. }
  74. return true;
  75. }
  76. CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderDIGI(MemoryFileReader file, const uint64 *pfilesize)
  77. {
  78. DIGIFileHeader fileHeader;
  79. if(!file.ReadStruct(fileHeader))
  80. {
  81. return ProbeWantMoreData;
  82. }
  83. if(!ValidateHeader(fileHeader))
  84. {
  85. return ProbeFailure;
  86. }
  87. MPT_UNREFERENCED_PARAMETER(pfilesize);
  88. return ProbeSuccess;
  89. }
  90. bool CSoundFile::ReadDIGI(FileReader &file, ModLoadingFlags loadFlags)
  91. {
  92. file.Rewind();
  93. DIGIFileHeader fileHeader;
  94. if(!file.ReadStruct(fileHeader))
  95. {
  96. return false;
  97. }
  98. if(!ValidateHeader(fileHeader))
  99. {
  100. return false;
  101. }
  102. if(loadFlags == onlyVerifyHeader)
  103. {
  104. return true;
  105. }
  106. // Globals
  107. InitializeGlobals(MOD_TYPE_DIGI);
  108. InitializeChannels();
  109. m_nChannels = fileHeader.numChannels;
  110. m_nSamples = 31;
  111. m_nSamplePreAmp = 256 / m_nChannels;
  112. m_modFormat.formatName = U_("DigiBooster");
  113. m_modFormat.type = U_("digi");
  114. m_modFormat.madeWithTracker = MPT_UFORMAT("Digi Booster {}.{}")(fileHeader.versionInt >> 4, fileHeader.versionInt & 0x0F);
  115. m_modFormat.charset = mpt::Charset::Amiga_no_C1;
  116. ReadOrderFromArray(Order(), fileHeader.orders, fileHeader.lastOrdIndex + 1);
  117. // Read sample headers
  118. for(SAMPLEINDEX smp = 0; smp < 31; smp++)
  119. {
  120. ModSample &sample = Samples[smp + 1];
  121. sample.Initialize(MOD_TYPE_MOD);
  122. sample.nLength = fileHeader.smpLength[smp];
  123. sample.nLoopStart = fileHeader.smpLoopStart[smp];
  124. sample.nLoopEnd = sample.nLoopStart + fileHeader.smpLoopLength[smp];
  125. if(fileHeader.smpLoopLength[smp])
  126. {
  127. sample.uFlags.set(CHN_LOOP);
  128. }
  129. sample.SanitizeLoops();
  130. sample.nVolume = std::min(fileHeader.smpVolume[smp].get(), uint8(64)) * 4;
  131. sample.nFineTune = MOD2XMFineTune(fileHeader.smpFinetune[smp]);
  132. }
  133. // Read song + sample names
  134. file.ReadString<mpt::String::maybeNullTerminated>(m_songName, 32);
  135. for(SAMPLEINDEX smp = 1; smp <= 31; smp++)
  136. {
  137. file.ReadString<mpt::String::maybeNullTerminated>(m_szNames[smp], 30);
  138. }
  139. if(loadFlags & loadPatternData)
  140. Patterns.ResizeArray(fileHeader.lastPatIndex + 1);
  141. for(PATTERNINDEX pat = 0; pat <= fileHeader.lastPatIndex; pat++)
  142. {
  143. FileReader patternChunk;
  144. if(fileHeader.packEnable)
  145. {
  146. patternChunk = file.ReadChunk(file.ReadUint16BE());
  147. } else
  148. {
  149. patternChunk = file.ReadChunk(4 * 64 * GetNumChannels());
  150. }
  151. if(!(loadFlags & loadPatternData) || !Patterns.Insert(pat, 64))
  152. {
  153. continue;
  154. }
  155. if(fileHeader.packEnable)
  156. {
  157. uint8 eventMask[64];
  158. patternChunk.ReadArray(eventMask);
  159. // Compressed patterns are stored in row-major order...
  160. for(ROWINDEX row = 0; row < 64; row++)
  161. {
  162. PatternRow patRow = Patterns[pat].GetRow(row);
  163. uint8 bit = 0x80;
  164. for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++, bit >>= 1)
  165. {
  166. if(eventMask[row] & bit)
  167. {
  168. ModCommand &m = patRow[chn];
  169. ReadDIGIPatternEntry(patternChunk, m);
  170. }
  171. }
  172. }
  173. } else
  174. {
  175. // ...but uncompressed patterns are stored in column-major order. WTF!
  176. for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++)
  177. {
  178. for(ROWINDEX row = 0; row < 64; row++)
  179. {
  180. ReadDIGIPatternEntry(patternChunk, *Patterns[pat].GetpModCommand(row, chn));
  181. }
  182. }
  183. }
  184. }
  185. if(loadFlags & loadSampleData)
  186. {
  187. // Reading Samples
  188. const SampleIO sampleIO(
  189. SampleIO::_8bit,
  190. SampleIO::mono,
  191. SampleIO::bigEndian,
  192. SampleIO::signedPCM);
  193. for(SAMPLEINDEX smp = 1; smp <= 31; smp++)
  194. {
  195. sampleIO.ReadSample(Samples[smp], file);
  196. }
  197. }
  198. return true;
  199. }
  200. OPENMPT_NAMESPACE_END