1
0

Load_itp.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. /*
  2. * Load_itp.cpp
  3. * ------------
  4. * Purpose: Impulse Tracker Project (ITP) module loader
  5. * Notes : Despite its name, ITP is not a format supported by Impulse Tracker.
  6. * In fact, it's a format invented by the OpenMPT team to allow people to work
  7. * with the IT format, but keeping the instrument files with big samples separate
  8. * from the pattern data, to keep the work files small and handy.
  9. * The design of the format is quite flawed, though, so it was superseded by
  10. * extra functionality in the MPTM format in OpenMPT 1.24.
  11. * Authors: OpenMPT Devs
  12. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  13. */
  14. #include "stdafx.h"
  15. #include "../common/version.h"
  16. #include "Loaders.h"
  17. #include "ITTools.h"
  18. #ifdef MODPLUG_TRACKER
  19. // For loading external instruments
  20. #include "../mptrack/Moddoc.h"
  21. #endif // MODPLUG_TRACKER
  22. #ifdef MPT_EXTERNAL_SAMPLES
  23. #include "../common/mptFileIO.h"
  24. #endif // MPT_EXTERNAL_SAMPLES
  25. OPENMPT_NAMESPACE_BEGIN
  26. // Version changelog:
  27. // v1.03: - Relative unicode instrument paths instead of absolute ANSI paths
  28. // - Per-path variable string length
  29. // - Embedded samples are IT-compressed
  30. // (rev. 3249)
  31. // v1.02: Explicitly updated format to use new instrument flags representation (rev. 483)
  32. // v1.01: Added option to embed instrument headers
  33. struct ITPModCommand
  34. {
  35. uint8 note;
  36. uint8 instr;
  37. uint8 volcmd;
  38. uint8 command;
  39. uint8 vol;
  40. uint8 param;
  41. operator ModCommand() const
  42. {
  43. static constexpr VolumeCommand ITPVolCmds[] =
  44. {
  45. VOLCMD_NONE, VOLCMD_VOLUME, VOLCMD_PANNING, VOLCMD_VOLSLIDEUP,
  46. VOLCMD_VOLSLIDEDOWN, VOLCMD_FINEVOLUP, VOLCMD_FINEVOLDOWN, VOLCMD_VIBRATOSPEED,
  47. VOLCMD_VIBRATODEPTH, VOLCMD_PANSLIDELEFT, VOLCMD_PANSLIDERIGHT, VOLCMD_TONEPORTAMENTO,
  48. VOLCMD_PORTAUP, VOLCMD_PORTADOWN, VOLCMD_PLAYCONTROL, VOLCMD_OFFSET,
  49. };
  50. static constexpr EffectCommand ITPCommands[] =
  51. {
  52. CMD_NONE, CMD_ARPEGGIO, CMD_PORTAMENTOUP, CMD_PORTAMENTODOWN,
  53. CMD_TONEPORTAMENTO, CMD_VIBRATO, CMD_TONEPORTAVOL, CMD_VIBRATOVOL,
  54. CMD_TREMOLO, CMD_PANNING8, CMD_OFFSET, CMD_VOLUMESLIDE,
  55. CMD_POSITIONJUMP, CMD_VOLUME, CMD_PATTERNBREAK, CMD_RETRIG,
  56. CMD_SPEED, CMD_TEMPO, CMD_TREMOR, CMD_MODCMDEX,
  57. CMD_S3MCMDEX, CMD_CHANNELVOLUME, CMD_CHANNELVOLSLIDE, CMD_GLOBALVOLUME,
  58. CMD_GLOBALVOLSLIDE, CMD_KEYOFF, CMD_FINEVIBRATO, CMD_PANBRELLO,
  59. CMD_XFINEPORTAUPDOWN, CMD_PANNINGSLIDE, CMD_SETENVPOSITION, CMD_MIDI,
  60. CMD_SMOOTHMIDI, CMD_DELAYCUT, CMD_XPARAM,
  61. };
  62. ModCommand result;
  63. result.note = (ModCommand::IsNote(note) || ModCommand::IsSpecialNote(note)) ? static_cast<ModCommand::NOTE>(note) : static_cast<ModCommand::NOTE>(NOTE_NONE);
  64. result.instr = instr;
  65. result.volcmd = (volcmd < std::size(ITPVolCmds)) ? ITPVolCmds[volcmd] : VOLCMD_NONE;
  66. result.command = (command < std::size(ITPCommands)) ? ITPCommands[command] : CMD_NONE;
  67. result.vol = vol;
  68. result.param = param;
  69. return result;
  70. }
  71. };
  72. MPT_BINARY_STRUCT(ITPModCommand, 6)
  73. struct ITPHeader
  74. {
  75. uint32le magic;
  76. uint32le version;
  77. };
  78. MPT_BINARY_STRUCT(ITPHeader, 8)
  79. static bool ValidateHeader(const ITPHeader &hdr)
  80. {
  81. if(hdr.magic != MagicBE(".itp"))
  82. {
  83. return false;
  84. }
  85. if(hdr.version < 0x00000100 || hdr.version > 0x00000103)
  86. {
  87. return false;
  88. }
  89. return true;
  90. }
  91. static uint64 GetHeaderMinimumAdditionalSize(const ITPHeader &hdr)
  92. {
  93. return 76 + (hdr.version <= 0x102 ? 4 : 0);
  94. }
  95. CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderITP(MemoryFileReader file, const uint64 *pfilesize)
  96. {
  97. ITPHeader hdr;
  98. if(!file.ReadStruct(hdr))
  99. {
  100. return ProbeWantMoreData;
  101. }
  102. if(!ValidateHeader(hdr))
  103. {
  104. return ProbeFailure;
  105. }
  106. return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(hdr));
  107. }
  108. bool CSoundFile::ReadITP(FileReader &file, ModLoadingFlags loadFlags)
  109. {
  110. #if !defined(MPT_EXTERNAL_SAMPLES) && !defined(MPT_FUZZ_TRACKER)
  111. // Doesn't really make sense to support this format when there's no support for external files...
  112. MPT_UNREFERENCED_PARAMETER(file);
  113. MPT_UNREFERENCED_PARAMETER(loadFlags);
  114. return false;
  115. #else // !MPT_EXTERNAL_SAMPLES && !MPT_FUZZ_TRACKER
  116. enum ITPSongFlags
  117. {
  118. ITP_EMBEDMIDICFG = 0x00001, // Embed macros in file
  119. ITP_ITOLDEFFECTS = 0x00004, // Old Impulse Tracker effect implementations
  120. ITP_ITCOMPATGXX = 0x00008, // IT "Compatible Gxx" (IT's flag to behave more like other trackers w/r/t portamento effects)
  121. ITP_LINEARSLIDES = 0x00010, // Linear slides vs. Amiga slides
  122. ITP_EXFILTERRANGE = 0x08000, // Cutoff Filter has double frequency range (up to ~10Khz)
  123. ITP_ITPROJECT = 0x20000, // Is a project file
  124. ITP_ITPEMBEDIH = 0x40000, // Embed instrument headers in project file
  125. };
  126. file.Rewind();
  127. ITPHeader hdr;
  128. if(!file.ReadStruct(hdr))
  129. {
  130. return false;
  131. }
  132. if(!ValidateHeader(hdr))
  133. {
  134. return false;
  135. }
  136. if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(hdr))))
  137. {
  138. return false;
  139. }
  140. if(loadFlags == onlyVerifyHeader)
  141. {
  142. return true;
  143. }
  144. const uint32 version = hdr.version;
  145. InitializeGlobals(MOD_TYPE_IT);
  146. m_playBehaviour.reset();
  147. file.ReadSizedString<uint32le, mpt::String::maybeNullTerminated>(m_songName);
  148. // Song comments
  149. m_songMessage.Read(file, file.ReadUint32LE(), SongMessage::leCR);
  150. // Song global config
  151. const uint32 songFlags = file.ReadUint32LE();
  152. if(!(songFlags & ITP_ITPROJECT))
  153. {
  154. return false;
  155. }
  156. m_SongFlags.set(SONG_IMPORTED);
  157. if(songFlags & ITP_ITOLDEFFECTS)
  158. m_SongFlags.set(SONG_ITOLDEFFECTS);
  159. if(songFlags & ITP_ITCOMPATGXX)
  160. m_SongFlags.set(SONG_ITCOMPATGXX);
  161. if(songFlags & ITP_LINEARSLIDES)
  162. m_SongFlags.set(SONG_LINEARSLIDES);
  163. if(songFlags & ITP_EXFILTERRANGE)
  164. m_SongFlags.set(SONG_EXFILTERRANGE);
  165. m_nDefaultGlobalVolume = file.ReadUint32LE();
  166. m_nSamplePreAmp = file.ReadUint32LE();
  167. m_nDefaultSpeed = std::max(uint32(1), file.ReadUint32LE());
  168. m_nDefaultTempo.Set(std::max(uint32(32), file.ReadUint32LE()));
  169. m_nChannels = static_cast<CHANNELINDEX>(file.ReadUint32LE());
  170. if(m_nChannels == 0 || m_nChannels > MAX_BASECHANNELS)
  171. {
  172. return false;
  173. }
  174. // channel name string length (=MAX_CHANNELNAME)
  175. uint32 size = file.ReadUint32LE();
  176. // Channels' data
  177. for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++)
  178. {
  179. ChnSettings[chn].nPan = std::min(static_cast<uint16>(file.ReadUint32LE()), uint16(256));
  180. ChnSettings[chn].dwFlags.reset();
  181. uint32 flags = file.ReadUint32LE();
  182. if(flags & 0x100) ChnSettings[chn].dwFlags.set(CHN_MUTE);
  183. if(flags & 0x800) ChnSettings[chn].dwFlags.set(CHN_SURROUND);
  184. ChnSettings[chn].nVolume = std::min(static_cast<uint16>(file.ReadUint32LE()), uint16(64));
  185. file.ReadString<mpt::String::maybeNullTerminated>(ChnSettings[chn].szName, size);
  186. }
  187. // Song mix plugins
  188. {
  189. FileReader plugChunk = file.ReadChunk(file.ReadUint32LE());
  190. LoadMixPlugins(plugChunk);
  191. }
  192. // MIDI Macro config
  193. file.ReadStructPartial<MIDIMacroConfigData>(m_MidiCfg, file.ReadUint32LE());
  194. m_MidiCfg.Sanitize();
  195. // Song Instruments
  196. m_nInstruments = static_cast<INSTRUMENTINDEX>(file.ReadUint32LE());
  197. if(m_nInstruments >= MAX_INSTRUMENTS)
  198. {
  199. m_nInstruments = 0;
  200. return false;
  201. }
  202. // Instruments' paths
  203. if(version <= 0x102)
  204. {
  205. size = file.ReadUint32LE(); // path string length
  206. }
  207. std::vector<mpt::PathString> instrPaths(GetNumInstruments());
  208. for(INSTRUMENTINDEX ins = 0; ins < GetNumInstruments(); ins++)
  209. {
  210. if(version > 0x102)
  211. {
  212. size = file.ReadUint32LE(); // path string length
  213. }
  214. std::string path;
  215. file.ReadString<mpt::String::maybeNullTerminated>(path, size);
  216. #ifdef MODPLUG_TRACKER
  217. if(version <= 0x102)
  218. {
  219. instrPaths[ins] = mpt::PathString::FromLocaleSilent(path);
  220. } else
  221. #endif // MODPLUG_TRACKER
  222. {
  223. instrPaths[ins] = mpt::PathString::FromUTF8(path);
  224. }
  225. #ifdef MODPLUG_TRACKER
  226. if(const auto fileName = file.GetOptionalFileName(); fileName.has_value())
  227. {
  228. instrPaths[ins] = instrPaths[ins].RelativePathToAbsolute(fileName->GetPath());
  229. } else if(GetpModDoc() != nullptr)
  230. {
  231. instrPaths[ins] = instrPaths[ins].RelativePathToAbsolute(GetpModDoc()->GetPathNameMpt().GetPath());
  232. }
  233. #endif // MODPLUG_TRACKER
  234. }
  235. // Song Orders
  236. size = file.ReadUint32LE();
  237. ReadOrderFromFile<uint8>(Order(), file, size, 0xFF, 0xFE);
  238. // Song Patterns
  239. const PATTERNINDEX numPats = static_cast<PATTERNINDEX>(file.ReadUint32LE());
  240. const PATTERNINDEX numNamedPats = static_cast<PATTERNINDEX>(file.ReadUint32LE());
  241. size_t patNameLen = file.ReadUint32LE(); // Size of each pattern name
  242. FileReader pattNames = file.ReadChunk(numNamedPats * patNameLen);
  243. // modcommand data length
  244. size = file.ReadUint32LE();
  245. if(size != sizeof(ITPModCommand))
  246. {
  247. return false;
  248. }
  249. if(loadFlags & loadPatternData)
  250. Patterns.ResizeArray(numPats);
  251. for(PATTERNINDEX pat = 0; pat < numPats; pat++)
  252. {
  253. const ROWINDEX numRows = file.ReadUint32LE();
  254. FileReader patternChunk = file.ReadChunk(numRows * size * GetNumChannels());
  255. // Allocate pattern
  256. if(!(loadFlags & loadPatternData) || !Patterns.Insert(pat, numRows))
  257. {
  258. pattNames.Skip(patNameLen);
  259. continue;
  260. }
  261. if(pat < numNamedPats)
  262. {
  263. char patName[32];
  264. if(pattNames.ReadString<mpt::String::maybeNullTerminated>(patName, patNameLen))
  265. Patterns[pat].SetName(patName);
  266. }
  267. // Pattern data
  268. size_t numCommands = GetNumChannels() * numRows;
  269. if(patternChunk.CanRead(sizeof(ITPModCommand) * numCommands))
  270. {
  271. ModCommand *target = Patterns[pat].GetpModCommand(0, 0);
  272. while(numCommands-- != 0)
  273. {
  274. ITPModCommand data;
  275. patternChunk.ReadStruct(data);
  276. *(target++) = data;
  277. }
  278. }
  279. }
  280. // Load embedded samples
  281. // Read original number of samples
  282. m_nSamples = static_cast<SAMPLEINDEX>(file.ReadUint32LE());
  283. LimitMax(m_nSamples, SAMPLEINDEX(MAX_SAMPLES - 1));
  284. // Read number of embedded samples - at most as many as there are real samples in a valid file
  285. uint32 embeddedSamples = file.ReadUint32LE();
  286. if(embeddedSamples > m_nSamples)
  287. {
  288. return false;
  289. }
  290. // Read samples
  291. for(uint32 smp = 0; smp < embeddedSamples && file.CanRead(8 + sizeof(ITSample)); smp++)
  292. {
  293. uint32 realSample = file.ReadUint32LE();
  294. ITSample sampleHeader;
  295. file.ReadStruct(sampleHeader);
  296. FileReader sampleData = file.ReadChunk(file.ReadUint32LE());
  297. if((loadFlags & loadSampleData)
  298. && realSample >= 1 && realSample <= GetNumSamples()
  299. && Samples[realSample].pData.pSample == nullptr
  300. && !memcmp(sampleHeader.id, "IMPS", 4))
  301. {
  302. sampleHeader.ConvertToMPT(Samples[realSample]);
  303. m_szNames[realSample] = mpt::String::ReadBuf(mpt::String::nullTerminated, sampleHeader.name);
  304. // Read sample data
  305. sampleHeader.GetSampleFormat().ReadSample(Samples[realSample], sampleData);
  306. }
  307. }
  308. // Load instruments
  309. for(INSTRUMENTINDEX ins = 0; ins < GetNumInstruments(); ins++)
  310. {
  311. if(instrPaths[ins].empty())
  312. continue;
  313. #ifdef MPT_EXTERNAL_SAMPLES
  314. InputFile f(instrPaths[ins], SettingCacheCompleteFileBeforeLoading());
  315. FileReader instrFile = GetFileReader(f);
  316. if(!ReadInstrumentFromFile(ins + 1, instrFile, true))
  317. {
  318. AddToLog(LogWarning, U_("Unable to open instrument: ") + instrPaths[ins].ToUnicode());
  319. }
  320. #else
  321. AddToLog(LogWarning, MPT_UFORMAT("Loading external instrument {} ('{}') failed: External instruments are not supported.")(ins + 1, instrPaths[ins].ToUnicode()));
  322. #endif // MPT_EXTERNAL_SAMPLES
  323. }
  324. // Extra info data
  325. uint32 code = file.ReadUint32LE();
  326. // Embed instruments' header [v1.01]
  327. if(version >= 0x101 && (songFlags & ITP_ITPEMBEDIH) && code == MagicBE("EBIH"))
  328. {
  329. code = file.ReadUint32LE();
  330. INSTRUMENTINDEX ins = 1;
  331. while(ins <= GetNumInstruments() && file.CanRead(4))
  332. {
  333. if(code == MagicBE("MPTS"))
  334. {
  335. break;
  336. } else if(code == MagicBE("SEP@") || code == MagicBE("MPTX"))
  337. {
  338. // jump code - switch to next instrument
  339. ins++;
  340. } else
  341. {
  342. ReadExtendedInstrumentProperty(Instruments[ins], code, file);
  343. }
  344. code = file.ReadUint32LE();
  345. }
  346. }
  347. for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
  348. {
  349. Samples[smp].SetDefaultCuePoints();
  350. }
  351. // Song extensions
  352. if(code == MagicBE("MPTS"))
  353. {
  354. file.SkipBack(4);
  355. LoadExtendedSongProperties(file, true);
  356. }
  357. m_nMaxPeriod = 0xF000;
  358. m_nMinPeriod = 8;
  359. // Before OpenMPT 1.20.01.09, the MIDI macros were always read from the file, even if the "embed" flag was not set.
  360. if(m_dwLastSavedWithVersion >= MPT_V("1.20.01.09") && !(songFlags & ITP_EMBEDMIDICFG))
  361. {
  362. m_MidiCfg.Reset();
  363. }
  364. m_modFormat.formatName = U_("Impulse Tracker Project");
  365. m_modFormat.type = U_("itp");
  366. m_modFormat.madeWithTracker = U_("OpenMPT ") + mpt::ufmt::val(m_dwLastSavedWithVersion);
  367. m_modFormat.charset = mpt::Charset::Windows1252;
  368. return true;
  369. #endif // MPT_EXTERNAL_SAMPLES
  370. }
  371. OPENMPT_NAMESPACE_END