1
0

Load_fmt.cpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. /*
  2. * Load_fmt.cpp
  3. * ------------
  4. * Purpose: Davey W Taylor's FM Tracker module loader
  5. * Notes : (currently none)
  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. struct FMTChannelSetting
  13. {
  14. char name[8];
  15. char settings[11];
  16. };
  17. MPT_BINARY_STRUCT(FMTChannelSetting, 19)
  18. struct FMTFileHeader
  19. {
  20. char magic[11]; // Includes format version number for simplicity
  21. char trackerName[20];
  22. char songName[32];
  23. FMTChannelSetting channels[8];
  24. uint8 lastRow;
  25. uint8 lastOrder;
  26. uint8 lastPattern;
  27. };
  28. MPT_BINARY_STRUCT(FMTFileHeader, 218)
  29. static uint64 GetHeaderMinimumAdditionalSize(const FMTFileHeader &fileHeader)
  30. {
  31. // Order list + pattern delays, pattern mapping + at least one byte per channel
  32. return (fileHeader.lastOrder + 1u) * 2u + (fileHeader.lastPattern + 1u) * 9u;
  33. }
  34. static bool ValidateHeader(const FMTFileHeader &fileHeader)
  35. {
  36. if(memcmp(fileHeader.magic, "FMTracker\x01\x01", 11))
  37. return false;
  38. for(const auto &channel : fileHeader.channels)
  39. {
  40. // Reject anything that resembles OPL3
  41. if((channel.settings[8] & 0xFC) || (channel.settings[9] & 0xFC) || (channel.settings[10] & 0xF0))
  42. return false;
  43. }
  44. return true;
  45. }
  46. CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderFMT(MemoryFileReader file, const uint64 *pfilesize)
  47. {
  48. FMTFileHeader fileHeader;
  49. if(!file.Read(fileHeader))
  50. return ProbeWantMoreData;
  51. if(!ValidateHeader(fileHeader))
  52. return ProbeFailure;
  53. return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader));
  54. }
  55. bool CSoundFile::ReadFMT(FileReader &file, ModLoadingFlags loadFlags)
  56. {
  57. file.Rewind();
  58. FMTFileHeader fileHeader;
  59. if(!file.Read(fileHeader))
  60. return false;
  61. if(!ValidateHeader(fileHeader))
  62. return false;
  63. if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader))))
  64. return false;
  65. if(loadFlags == onlyVerifyHeader)
  66. return true;
  67. InitializeGlobals(MOD_TYPE_S3M);
  68. InitializeChannels();
  69. m_nChannels = 8;
  70. m_nSamples = 8;
  71. m_nDefaultTempo = TEMPO(45.5); // 18.2 Hz timer
  72. m_playBehaviour.set(kOPLNoteStopWith0Hz);
  73. m_SongFlags.set(SONG_IMPORTED);
  74. m_songName = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.songName);
  75. for(CHANNELINDEX chn = 0; chn < 8; chn++)
  76. {
  77. const auto name = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.channels[chn].name);
  78. ChnSettings[chn].szName = name;
  79. ModSample &mptSmp = Samples[chn + 1];
  80. mptSmp.Initialize(MOD_TYPE_S3M);
  81. OPLPatch patch{{}};
  82. memcpy(patch.data(), fileHeader.channels[chn].settings, 11);
  83. mptSmp.SetAdlib(true, patch);
  84. mptSmp.nC5Speed = 8215;
  85. m_szNames[chn + 1] = name;
  86. }
  87. const ORDERINDEX numOrders = fileHeader.lastOrder + 1u;
  88. ReadOrderFromFile<uint8>(Order(), file, numOrders);
  89. std::vector<uint8> delays;
  90. file.ReadVector(delays, numOrders);
  91. for(uint8 delay : delays)
  92. {
  93. if(delay < 1 || delay > 8)
  94. return false;
  95. }
  96. m_nDefaultSpeed = delays[0];
  97. const PATTERNINDEX numPatterns = fileHeader.lastPattern + 1u;
  98. const ROWINDEX numRows = fileHeader.lastRow + 1u;
  99. std::vector<uint8> patternMap;
  100. file.ReadVector(patternMap, numPatterns);
  101. Patterns.ResizeArray(numPatterns);
  102. for(PATTERNINDEX pat = 0; pat < numPatterns; pat++)
  103. {
  104. if(!(loadFlags & loadPatternData) || !Patterns.Insert(patternMap[pat], numRows))
  105. break;
  106. auto &pattern = Patterns[patternMap[pat]];
  107. for(CHANNELINDEX chn = 0; chn < 8; chn++)
  108. {
  109. for(ROWINDEX row = 0; row < pattern.GetNumRows(); row++)
  110. {
  111. uint8 data = file.ReadUint8();
  112. if(data & 0x80)
  113. {
  114. row += data & 0x7F;
  115. } else
  116. {
  117. ModCommand &m = *pattern.GetpModCommand(row, chn);
  118. if(data == 1)
  119. {
  120. m.note = NOTE_NOTECUT;
  121. } else if(data >= 2 && data <= 97)
  122. {
  123. m.note = data + NOTE_MIN + 11u;
  124. m.instr = static_cast<ModCommand::INSTR>(chn + 1u);
  125. }
  126. }
  127. }
  128. }
  129. }
  130. // Write song speed to patterns... due to a quirk in the original playback routine
  131. // (delays is applied before notes are triggered, not afterwards), a pattern's delay
  132. // already applies to the last row of the previous pattern.
  133. // In case you wonder if anyone would ever notice: My own songs written with this tracker
  134. // actively work around this issue and will sound wrong if tempo is changed on the first row.
  135. for(ORDERINDEX ord = 0; ord < numOrders; ord++)
  136. {
  137. if(!Order().IsValidPat(ord))
  138. {
  139. if(PATTERNINDEX pat = Patterns.InsertAny(numRows); pat != PATTERNINDEX_INVALID)
  140. Order()[ord] = pat;
  141. else
  142. continue;
  143. }
  144. auto m = Patterns[Order()[ord]].end() - 1;
  145. auto delay = delays[(ord + 1u) % numOrders];
  146. if(m->param == delay)
  147. continue;
  148. if(m->command == CMD_SPEED)
  149. {
  150. PATTERNINDEX newPat = Order().EnsureUnique(ord);
  151. if(newPat != PATTERNINDEX_INVALID)
  152. m = Patterns[newPat].end() - 1;
  153. }
  154. m->command = CMD_SPEED;
  155. m->param = delay;
  156. }
  157. m_modFormat.formatName = U_("FM Tracker");
  158. m_modFormat.type = U_("fmt");
  159. m_modFormat.madeWithTracker = mpt::ToUnicode(mpt::Charset::CP437, mpt::String::ReadBuf(mpt::String::maybeNullTerminated, fileHeader.trackerName));
  160. m_modFormat.charset = mpt::Charset::CP437;
  161. return true;
  162. }
  163. OPENMPT_NAMESPACE_END