1
0

Load_s3m.cpp 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090
  1. /*
  2. * Load_s3m.cpp
  3. * ------------
  4. * Purpose: S3M (ScreamTracker 3) module loader / saver
  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. #include "S3MTools.h"
  12. #include "ITTools.h"
  13. #ifndef MODPLUG_NO_FILESAVE
  14. #include "mpt/io/base.hpp"
  15. #include "mpt/io/io.hpp"
  16. #include "mpt/io/io_stdstream.hpp"
  17. #include "../common/mptFileIO.h"
  18. #ifdef MODPLUG_TRACKER
  19. #include "../mptrack/TrackerSettings.h"
  20. #endif // MODPLUG_TRACKER
  21. #endif // MODPLUG_NO_FILESAVE
  22. #include "../common/version.h"
  23. OPENMPT_NAMESPACE_BEGIN
  24. void CSoundFile::S3MConvert(ModCommand &m, bool fromIT)
  25. {
  26. switch(m.command | 0x40)
  27. {
  28. case '@': m.command = (m.param ? CMD_DUMMY : CMD_NONE); break;
  29. case 'A': m.command = CMD_SPEED; break;
  30. case 'B': m.command = CMD_POSITIONJUMP; break;
  31. case 'C': m.command = CMD_PATTERNBREAK; if (!fromIT) m.param = (m.param >> 4) * 10 + (m.param & 0x0F); break;
  32. case 'D': m.command = CMD_VOLUMESLIDE; break;
  33. case 'E': m.command = CMD_PORTAMENTODOWN; break;
  34. case 'F': m.command = CMD_PORTAMENTOUP; break;
  35. case 'G': m.command = CMD_TONEPORTAMENTO; break;
  36. case 'H': m.command = CMD_VIBRATO; break;
  37. case 'I': m.command = CMD_TREMOR; break;
  38. case 'J': m.command = CMD_ARPEGGIO; break;
  39. case 'K': m.command = CMD_VIBRATOVOL; break;
  40. case 'L': m.command = CMD_TONEPORTAVOL; break;
  41. case 'M': m.command = CMD_CHANNELVOLUME; break;
  42. case 'N': m.command = CMD_CHANNELVOLSLIDE; break;
  43. case 'O': m.command = CMD_OFFSET; break;
  44. case 'P': m.command = CMD_PANNINGSLIDE; break;
  45. case 'Q': m.command = CMD_RETRIG; break;
  46. case 'R': m.command = CMD_TREMOLO; break;
  47. case 'S': m.command = CMD_S3MCMDEX; break;
  48. case 'T': m.command = CMD_TEMPO; break;
  49. case 'U': m.command = CMD_FINEVIBRATO; break;
  50. case 'V': m.command = CMD_GLOBALVOLUME; break;
  51. case 'W': m.command = CMD_GLOBALVOLSLIDE; break;
  52. case 'X': m.command = CMD_PANNING8; break;
  53. case 'Y': m.command = CMD_PANBRELLO; break;
  54. case 'Z': m.command = CMD_MIDI; break;
  55. case '\\': m.command = fromIT ? CMD_SMOOTHMIDI : CMD_MIDI; break;
  56. // Chars under 0x40 don't save properly, so the following commands don't map to their pattern editor representations
  57. case ']': m.command = fromIT ? CMD_DELAYCUT : CMD_NONE; break;
  58. case '[': m.command = fromIT ? CMD_XPARAM : CMD_NONE; break;
  59. case '^': m.command = fromIT ? CMD_FINETUNE : CMD_NONE; break;
  60. case '_': m.command = fromIT ? CMD_FINETUNE_SMOOTH : CMD_NONE; break;
  61. // BeRoTracker extensions
  62. case '1' + 0x41: m.command = fromIT ? CMD_KEYOFF : CMD_NONE; break;
  63. case '2' + 0x41: m.command = fromIT ? CMD_SETENVPOSITION : CMD_NONE; break;
  64. default: m.command = CMD_NONE;
  65. }
  66. }
  67. #ifndef MODPLUG_NO_FILESAVE
  68. void CSoundFile::S3MSaveConvert(uint8 &command, uint8 &param, bool toIT, bool compatibilityExport) const
  69. {
  70. const bool extendedIT = !compatibilityExport && toIT;
  71. switch(command)
  72. {
  73. case CMD_DUMMY: command = (param ? '@' : 0); break;
  74. case CMD_SPEED: command = 'A'; break;
  75. case CMD_POSITIONJUMP: command = 'B'; break;
  76. case CMD_PATTERNBREAK: command = 'C'; if(!toIT) param = ((param / 10) << 4) + (param % 10); break;
  77. case CMD_VOLUMESLIDE: command = 'D'; break;
  78. case CMD_PORTAMENTODOWN: command = 'E'; if (param >= 0xE0 && (GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM))) param = 0xDF; break;
  79. case CMD_PORTAMENTOUP: command = 'F'; if (param >= 0xE0 && (GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM))) param = 0xDF; break;
  80. case CMD_TONEPORTAMENTO: command = 'G'; break;
  81. case CMD_VIBRATO: command = 'H'; break;
  82. case CMD_TREMOR: command = 'I'; break;
  83. case CMD_ARPEGGIO: command = 'J'; break;
  84. case CMD_VIBRATOVOL: command = 'K'; break;
  85. case CMD_TONEPORTAVOL: command = 'L'; break;
  86. case CMD_CHANNELVOLUME: command = 'M'; break;
  87. case CMD_CHANNELVOLSLIDE: command = 'N'; break;
  88. case CMD_OFFSETPERCENTAGE:
  89. case CMD_OFFSET: command = 'O'; break;
  90. case CMD_PANNINGSLIDE: command = 'P'; break;
  91. case CMD_RETRIG: command = 'Q'; break;
  92. case CMD_TREMOLO: command = 'R'; break;
  93. case CMD_S3MCMDEX: command = 'S'; break;
  94. case CMD_TEMPO: command = 'T'; break;
  95. case CMD_FINEVIBRATO: command = 'U'; break;
  96. case CMD_GLOBALVOLUME: command = 'V'; break;
  97. case CMD_GLOBALVOLSLIDE: command = 'W'; break;
  98. case CMD_PANNING8:
  99. command = 'X';
  100. if(toIT && !(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_XM | MOD_TYPE_MOD)))
  101. {
  102. if (param == 0xA4) { command = 'S'; param = 0x91; }
  103. else if (param == 0x80) { param = 0xFF; }
  104. else if (param < 0x80) { param <<= 1; }
  105. else command = 0;
  106. } else if (!toIT && (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT | MOD_TYPE_XM | MOD_TYPE_MOD)))
  107. {
  108. param >>= 1;
  109. }
  110. break;
  111. case CMD_PANBRELLO: command = 'Y'; break;
  112. case CMD_MIDI: command = 'Z'; break;
  113. case CMD_SMOOTHMIDI:
  114. if(extendedIT)
  115. command = '\\';
  116. else
  117. command = 'Z';
  118. break;
  119. case CMD_XFINEPORTAUPDOWN:
  120. switch(param & 0xF0)
  121. {
  122. case 0x10: command = 'F'; param = (param & 0x0F) | 0xE0; break;
  123. case 0x20: command = 'E'; param = (param & 0x0F) | 0xE0; break;
  124. case 0x90: command = 'S'; break;
  125. default: command = 0;
  126. }
  127. break;
  128. case CMD_MODCMDEX:
  129. {
  130. ModCommand m;
  131. m.command = CMD_MODCMDEX;
  132. m.param = param;
  133. m.ExtendedMODtoS3MEffect();
  134. command = m.command;
  135. param = m.param;
  136. S3MSaveConvert(command, param, toIT, compatibilityExport);
  137. }
  138. return;
  139. // Chars under 0x40 don't save properly, so map : to ] and # to [.
  140. case CMD_DELAYCUT:
  141. command = extendedIT ? ']' : 0;
  142. break;
  143. case CMD_XPARAM:
  144. command = extendedIT ? '[' : 0;
  145. break;
  146. case CMD_FINETUNE:
  147. command = extendedIT ? '^' : 0;
  148. break;
  149. case CMD_FINETUNE_SMOOTH:
  150. command = extendedIT ? '_' : 0;
  151. break;
  152. default:
  153. command = 0;
  154. }
  155. if(command == 0)
  156. {
  157. param = 0;
  158. }
  159. command &= ~0x40;
  160. }
  161. #endif // MODPLUG_NO_FILESAVE
  162. static bool ValidateHeader(const S3MFileHeader &fileHeader)
  163. {
  164. if(std::memcmp(fileHeader.magic, "SCRM", 4)
  165. || fileHeader.fileType != S3MFileHeader::idS3MType
  166. || (fileHeader.formatVersion != S3MFileHeader::oldVersion && fileHeader.formatVersion != S3MFileHeader::newVersion)
  167. )
  168. {
  169. return false;
  170. }
  171. return true;
  172. }
  173. static uint64 GetHeaderMinimumAdditionalSize(const S3MFileHeader &fileHeader)
  174. {
  175. return fileHeader.ordNum + (fileHeader.smpNum + fileHeader.patNum) * 2;
  176. }
  177. CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderS3M(MemoryFileReader file, const uint64 *pfilesize)
  178. {
  179. S3MFileHeader fileHeader;
  180. if(!file.ReadStruct(fileHeader))
  181. {
  182. return ProbeWantMoreData;
  183. }
  184. if(!ValidateHeader(fileHeader))
  185. {
  186. return ProbeFailure;
  187. }
  188. return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader));
  189. }
  190. bool CSoundFile::ReadS3M(FileReader &file, ModLoadingFlags loadFlags)
  191. {
  192. file.Rewind();
  193. // Is it a valid S3M file?
  194. S3MFileHeader fileHeader;
  195. if(!file.ReadStruct(fileHeader))
  196. {
  197. return false;
  198. }
  199. if(!ValidateHeader(fileHeader))
  200. {
  201. return false;
  202. }
  203. if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader))))
  204. {
  205. return false;
  206. }
  207. if(loadFlags == onlyVerifyHeader)
  208. {
  209. return true;
  210. }
  211. InitializeGlobals(MOD_TYPE_S3M);
  212. m_nMinPeriod = 64;
  213. m_nMaxPeriod = 32767;
  214. // ST3 ignored Zxx commands, so if we find that a file was made with ST3, we should erase all MIDI macros.
  215. bool keepMidiMacros = false;
  216. mpt::ustring madeWithTracker;
  217. bool formatTrackerStr = false;
  218. bool nonCompatTracker = false;
  219. bool isST3 = false;
  220. bool isSchism = false;
  221. const int32 schismDateVersion = SchismTrackerEpoch + ((fileHeader.cwtv == 0x4FFF) ? fileHeader.reserved2 : (fileHeader.cwtv - 0x4050));
  222. switch(fileHeader.cwtv & S3MFileHeader::trackerMask)
  223. {
  224. case S3MFileHeader::trkAkord & S3MFileHeader::trackerMask:
  225. if(fileHeader.cwtv == S3MFileHeader::trkAkord)
  226. madeWithTracker = U_("Akord");
  227. break;
  228. case S3MFileHeader::trkScreamTracker:
  229. if(fileHeader.cwtv == S3MFileHeader::trkST3_20 && fileHeader.special == 0 && (fileHeader.ordNum & 0x0F) == 0 && fileHeader.ultraClicks == 0 && (fileHeader.flags & ~0x50) == 0)
  230. {
  231. // MPT and OpenMPT before 1.17.03.02 - Simply keep default (filter) MIDI macros
  232. if((fileHeader.masterVolume & 0x80) != 0)
  233. {
  234. m_dwLastSavedWithVersion = MPT_V("1.16.00.00");
  235. madeWithTracker = U_("ModPlug Tracker / OpenMPT 1.17");
  236. } else
  237. {
  238. // MPT 1.0 alpha5 doesn't set the stereo flag, but MPT 1.0 beta1 does.
  239. m_dwLastSavedWithVersion = MPT_V("1.00.00.00");
  240. madeWithTracker = U_("ModPlug Tracker 1.0 alpha");
  241. }
  242. keepMidiMacros = true;
  243. nonCompatTracker = true;
  244. m_playBehaviour.set(kST3LimitPeriod);
  245. } else if(fileHeader.cwtv == S3MFileHeader::trkST3_20 && fileHeader.special == 0 && fileHeader.ultraClicks == 0 && fileHeader.flags == 0 && fileHeader.usePanningTable == 0)
  246. {
  247. madeWithTracker = U_("Velvet Studio");
  248. } else
  249. {
  250. // ST3.20 should only ever write ultra-click values 16, 24 and 32 (corresponding to 8, 12 and 16 in the GUI), ST3.01/3.03 should only write 0,
  251. // though several ST3.01/3.03 files with ultra-click values of 16 have been found as well.
  252. // However, we won't fingerprint these values here as it's unlikely that there is any other tracker out there disguising as ST3 and using a strange ultra-click value.
  253. // Also, re-saving a file with a strange ultra-click value in ST3 doesn't fix this value unless the user manually changes it, or if it's below 16.
  254. madeWithTracker = U_("Scream Tracker");
  255. formatTrackerStr = true;
  256. isST3 = true;
  257. }
  258. break;
  259. case S3MFileHeader::trkImagoOrpheus:
  260. madeWithTracker = U_("Imago Orpheus");
  261. formatTrackerStr = true;
  262. nonCompatTracker = true;
  263. break;
  264. case S3MFileHeader::trkImpulseTracker:
  265. if(fileHeader.cwtv <= S3MFileHeader::trkIT2_14)
  266. {
  267. madeWithTracker = U_("Impulse Tracker");
  268. formatTrackerStr = true;
  269. } else
  270. {
  271. madeWithTracker = MPT_UFORMAT("Impulse Tracker 2.14p{}")(fileHeader.cwtv - S3MFileHeader::trkIT2_14);
  272. }
  273. if(fileHeader.cwtv >= S3MFileHeader::trkIT2_07 && fileHeader.reserved3 != 0)
  274. {
  275. // Starting from version 2.07, IT stores the total edit time of a module in the "reserved" field
  276. uint32 editTime = DecodeITEditTimer(fileHeader.cwtv, fileHeader.reserved3);
  277. FileHistory hist;
  278. hist.openTime = static_cast<uint32>(editTime * (HISTORY_TIMER_PRECISION / 18.2));
  279. m_FileHistory.push_back(hist);
  280. }
  281. nonCompatTracker = true;
  282. m_playBehaviour.set(kPeriodsAreHertz);
  283. m_playBehaviour.set(kITRetrigger);
  284. m_playBehaviour.set(kITShortSampleRetrig);
  285. m_playBehaviour.set(kST3SampleSwap); // Not exactly like ST3, but close enough
  286. m_nMinPeriod = 1;
  287. break;
  288. case S3MFileHeader::trkSchismTracker:
  289. if(fileHeader.cwtv == S3MFileHeader::trkBeRoTrackerOld)
  290. {
  291. madeWithTracker = U_("BeRoTracker");
  292. m_playBehaviour.set(kST3LimitPeriod);
  293. } else
  294. {
  295. madeWithTracker = GetSchismTrackerVersion(fileHeader.cwtv, fileHeader.reserved2);
  296. m_nMinPeriod = 1;
  297. isSchism = true;
  298. if(schismDateVersion >= SchismVersionFromDate<2021, 05, 02>::date)
  299. m_playBehaviour.set(kPeriodsAreHertz);
  300. if(schismDateVersion >= SchismVersionFromDate<2016, 05, 13>::date)
  301. m_playBehaviour.set(kITShortSampleRetrig);
  302. }
  303. nonCompatTracker = true;
  304. break;
  305. case S3MFileHeader::trkOpenMPT:
  306. {
  307. uint32 mptVersion = (fileHeader.cwtv & S3MFileHeader::versionMask) << 16;
  308. if(mptVersion >= 0x01'29'00'00)
  309. mptVersion |= fileHeader.reserved2;
  310. m_dwLastSavedWithVersion = Version(mptVersion);
  311. madeWithTracker = U_("OpenMPT ") + mpt::ufmt::val(m_dwLastSavedWithVersion);
  312. }
  313. break;
  314. case S3MFileHeader::trkBeRoTracker:
  315. madeWithTracker = U_("BeRoTracker");
  316. m_playBehaviour.set(kST3LimitPeriod);
  317. break;
  318. case S3MFileHeader::trkCreamTracker:
  319. madeWithTracker = U_("CreamTracker");
  320. break;
  321. default:
  322. if(fileHeader.cwtv == S3MFileHeader::trkCamoto)
  323. madeWithTracker = U_("Camoto");
  324. break;
  325. }
  326. if(formatTrackerStr)
  327. {
  328. madeWithTracker = MPT_UFORMAT("{} {}.{}")(madeWithTracker, (fileHeader.cwtv & 0xF00) >> 8, mpt::ufmt::hex0<2>(fileHeader.cwtv & 0xFF));
  329. }
  330. m_modFormat.formatName = U_("Scream Tracker 3");
  331. m_modFormat.type = U_("s3m");
  332. m_modFormat.madeWithTracker = std::move(madeWithTracker);
  333. m_modFormat.charset = m_dwLastSavedWithVersion ? mpt::Charset::Windows1252 : mpt::Charset::CP437;
  334. if(nonCompatTracker)
  335. {
  336. m_playBehaviour.reset(kST3NoMutedChannels);
  337. m_playBehaviour.reset(kST3EffectMemory);
  338. m_playBehaviour.reset(kST3PortaSampleChange);
  339. m_playBehaviour.reset(kST3VibratoMemory);
  340. m_playBehaviour.reset(KST3PortaAfterArpeggio);
  341. m_playBehaviour.reset(kST3OffsetWithoutInstrument);
  342. m_playBehaviour.reset(kApplyUpperPeriodLimit);
  343. }
  344. if((fileHeader.cwtv & S3MFileHeader::trackerMask) > S3MFileHeader::trkScreamTracker)
  345. {
  346. if((fileHeader.cwtv & S3MFileHeader::trackerMask) != S3MFileHeader::trkImpulseTracker || fileHeader.cwtv >= S3MFileHeader::trkIT2_14)
  347. {
  348. // Keep MIDI macros if this is not an old IT version (BABYLON.S3M by Necros has Zxx commands and was saved with IT 2.05)
  349. keepMidiMacros = true;
  350. }
  351. }
  352. m_MidiCfg.Reset();
  353. if(!keepMidiMacros)
  354. {
  355. // Remove macros so they don't interfere with tunes made in trackers that don't support Zxx
  356. m_MidiCfg.ClearZxxMacros();
  357. }
  358. m_songName = mpt::String::ReadBuf(mpt::String::nullTerminated, fileHeader.name);
  359. if(fileHeader.flags & S3MFileHeader::amigaLimits) m_SongFlags.set(SONG_AMIGALIMITS);
  360. if(fileHeader.flags & S3MFileHeader::st2Vibrato) m_SongFlags.set(SONG_S3MOLDVIBRATO);
  361. if(fileHeader.cwtv == S3MFileHeader::trkST3_00 || (fileHeader.flags & S3MFileHeader::fastVolumeSlides) != 0)
  362. {
  363. m_SongFlags.set(SONG_FASTVOLSLIDES);
  364. }
  365. // Speed
  366. m_nDefaultSpeed = fileHeader.speed;
  367. if(m_nDefaultSpeed == 0 || (m_nDefaultSpeed == 255 && isST3))
  368. {
  369. // Even though ST3 accepts the command AFF as expected, it mysteriously fails to load a default speed of 255...
  370. m_nDefaultSpeed = 6;
  371. }
  372. // Tempo
  373. m_nDefaultTempo.Set(fileHeader.tempo);
  374. if(fileHeader.tempo < 33)
  375. {
  376. // ST3 also fails to load an otherwise valid default tempo of 32...
  377. m_nDefaultTempo.Set(isST3 ? 125 : 32);
  378. }
  379. // Global Volume
  380. m_nDefaultGlobalVolume = std::min(fileHeader.globalVol.get(), uint8(64)) * 4u;
  381. // The following check is probably not very reliable, but it fixes a few tunes, e.g.
  382. // DARKNESS.S3M by Purple Motion (ST 3.00) and "Image of Variance" by C.C.Catch (ST 3.01):
  383. if(m_nDefaultGlobalVolume == 0 && fileHeader.cwtv < S3MFileHeader::trkST3_20)
  384. {
  385. m_nDefaultGlobalVolume = MAX_GLOBAL_VOLUME;
  386. }
  387. if(fileHeader.formatVersion == S3MFileHeader::oldVersion && fileHeader.masterVolume < 8)
  388. m_nSamplePreAmp = std::min((fileHeader.masterVolume + 1) * 0x10, 0x7F);
  389. // These changes were probably only supposed to be done for older format revisions, where supposedly 0x10 was the stereo flag.
  390. // However, this version check is missing in ST3, so any mono file with a master volume of 18 will be converted to a stereo file with master volume 32.
  391. else if(fileHeader.masterVolume == 2 || fileHeader.masterVolume == (2 | 0x10))
  392. m_nSamplePreAmp = 0x20;
  393. else if(!(fileHeader.masterVolume & 0x7F))
  394. m_nSamplePreAmp = 48;
  395. else
  396. m_nSamplePreAmp = std::max(fileHeader.masterVolume & 0x7F, 0x10); // Bit 7 = Stereo (we always use stereo)
  397. const bool isStereo = (fileHeader.masterVolume & 0x80) != 0 || m_dwLastSavedWithVersion;
  398. if(!isStereo)
  399. m_nSamplePreAmp = Util::muldivr_unsigned(m_nSamplePreAmp, 8, 11);
  400. // Approximately as loud as in DOSBox and a real SoundBlaster 16
  401. m_nVSTiVolume = 36;
  402. if(isSchism && schismDateVersion < SchismVersionFromDate<2018, 11, 12>::date)
  403. m_nVSTiVolume = 64;
  404. // Channel setup
  405. m_nChannels = 4;
  406. std::bitset<32> isAdlibChannel;
  407. for(CHANNELINDEX i = 0; i < 32; i++)
  408. {
  409. ChnSettings[i].Reset();
  410. uint8 ctype = fileHeader.channels[i] & ~0x80;
  411. if(fileHeader.channels[i] != 0xFF)
  412. {
  413. m_nChannels = i + 1;
  414. if(isStereo)
  415. ChnSettings[i].nPan = (ctype & 8) ? 0xCC : 0x33; // 200 : 56
  416. }
  417. if(fileHeader.channels[i] & 0x80)
  418. {
  419. ChnSettings[i].dwFlags = CHN_MUTE;
  420. }
  421. if(ctype >= 16 && ctype <= 29)
  422. {
  423. // Adlib channel - except for OpenMPT 1.19 and older, which would write wrong channel types for PCM channels 16-32.
  424. // However, MPT/OpenMPT always wrote the extra panning table, so there is no need to consider this here.
  425. ChnSettings[i].nPan = 128;
  426. isAdlibChannel[i] = true;
  427. }
  428. }
  429. if(m_nChannels < 1)
  430. {
  431. m_nChannels = 1;
  432. }
  433. ReadOrderFromFile<uint8>(Order(), file, fileHeader.ordNum, 0xFF, 0xFE);
  434. // Read sample header offsets
  435. std::vector<uint16le> sampleOffsets;
  436. file.ReadVector(sampleOffsets, fileHeader.smpNum);
  437. // Read pattern offsets
  438. std::vector<uint16le> patternOffsets;
  439. file.ReadVector(patternOffsets, fileHeader.patNum);
  440. // Read extended channel panning
  441. if(fileHeader.usePanningTable == S3MFileHeader::idPanning)
  442. {
  443. uint8 pan[32];
  444. file.ReadArray(pan);
  445. for(CHANNELINDEX i = 0; i < 32; i++)
  446. {
  447. if((pan[i] & 0x20) != 0 && (!isST3 || !isAdlibChannel[i]))
  448. {
  449. ChnSettings[i].nPan = (static_cast<uint16>(pan[i] & 0x0F) * 256 + 8) / 15;
  450. }
  451. }
  452. }
  453. // Reading sample headers
  454. m_nSamples = std::min(static_cast<SAMPLEINDEX>(fileHeader.smpNum), static_cast<SAMPLEINDEX>(MAX_SAMPLES - 1));
  455. bool anySamples = false;
  456. uint16 gusAddresses = 0;
  457. for(SAMPLEINDEX smp = 0; smp < m_nSamples; smp++)
  458. {
  459. S3MSampleHeader sampleHeader;
  460. if(!file.Seek(sampleOffsets[smp] * 16) || !file.ReadStruct(sampleHeader))
  461. {
  462. continue;
  463. }
  464. sampleHeader.ConvertToMPT(Samples[smp + 1], isST3);
  465. m_szNames[smp + 1] = mpt::String::ReadBuf(mpt::String::nullTerminated, sampleHeader.name);
  466. if(sampleHeader.sampleType < S3MSampleHeader::typeAdMel)
  467. {
  468. const uint32 sampleOffset = sampleHeader.GetSampleOffset();
  469. if((loadFlags & loadSampleData) && sampleHeader.length != 0 && file.Seek(sampleOffset))
  470. {
  471. sampleHeader.GetSampleFormat((fileHeader.formatVersion == S3MFileHeader::oldVersion)).ReadSample(Samples[smp + 1], file);
  472. anySamples = true;
  473. }
  474. gusAddresses |= sampleHeader.gusAddress;
  475. }
  476. }
  477. if(isST3 && anySamples && !gusAddresses && fileHeader.cwtv != S3MFileHeader::trkST3_00)
  478. {
  479. // All Scream Tracker versions except for some probably early revisions of Scream Tracker 3.00 write GUS addresses. GUS support might not have existed at that point (1992).
  480. // Hence if a file claims to be written with ST3 (but not ST3.00), but has no GUS addresses, we deduce that it must be written by some other software (e.g. some PSM -> S3M conversions)
  481. isST3 = false;
  482. MPT_UNUSED(isST3);
  483. m_modFormat.madeWithTracker = U_("Unknown");
  484. } else if(isST3)
  485. {
  486. // Saving an S3M file in ST3 with the Gravis Ultrasound driver loaded will write a unique GUS memory address for each non-empty sample slot (and 0 for unused slots).
  487. // Re-saving that file in ST3 with the SoundBlaster driver loaded will reset the GUS address for all samples to 0 (unused) or 1 (used).
  488. // The first used sample will also have an address of 1 with the GUS driver.
  489. // So this is a safe way of telling if the file was last saved with the GUS driver loaded or not if there's more than one sample.
  490. const bool useGUS = gusAddresses > 1;
  491. m_playBehaviour.set(kST3PortaSampleChange, useGUS);
  492. m_playBehaviour.set(kST3SampleSwap, !useGUS);
  493. m_playBehaviour.set(kITShortSampleRetrig, !useGUS); // Only half the truth but close enough for now
  494. m_modFormat.madeWithTracker += useGUS ? UL_(" (GUS)") : UL_(" (SB)");
  495. // ST3's GUS driver doesn't use this value. Ignoring it fixes the balance between FM and PCM samples (e.g. in Rotagilla by Manwe)
  496. if(useGUS)
  497. m_nSamplePreAmp = 48;
  498. }
  499. // Try to find out if Zxx commands are supposed to be panning commands (PixPlay).
  500. // Actually I am only aware of one module that uses this panning style, namely "Crawling Despair" by $volkraq
  501. // and I have no idea what PixPlay is, so this code is solely based on the sample text of that module.
  502. // We won't convert if there are not enough Zxx commands, too "high" Zxx commands
  503. // or there are only "left" or "right" pannings (we assume that stereo should be somewhat balanced),
  504. // and modules not made with an old version of ST3 were probably made in a tracker that supports panning anyway.
  505. bool pixPlayPanning = (fileHeader.cwtv < S3MFileHeader::trkST3_20);
  506. int zxxCountRight = 0, zxxCountLeft = 0;
  507. // Reading patterns
  508. if(!(loadFlags & loadPatternData))
  509. {
  510. return true;
  511. }
  512. // Order list cannot contain pattern indices > 255, so do not even try to load higher patterns
  513. const PATTERNINDEX readPatterns = std::min(static_cast<PATTERNINDEX>(fileHeader.patNum), static_cast<PATTERNINDEX>(uint8_max));
  514. Patterns.ResizeArray(readPatterns);
  515. for(PATTERNINDEX pat = 0; pat < readPatterns; pat++)
  516. {
  517. // A zero parapointer indicates an empty pattern.
  518. if(!Patterns.Insert(pat, 64) || patternOffsets[pat] == 0 || !file.Seek(patternOffsets[pat] * 16))
  519. {
  520. continue;
  521. }
  522. // Skip pattern length indication.
  523. // Some modules, for example http://aminet.net/mods/8voic/s3m_hunt.lha seem to have a wrong pattern length -
  524. // If you strictly adhere the pattern length, you won't read some patterns (e.g. 17) correctly in that module.
  525. // It's most likely a broken copy because there are other versions of the track which don't have this issue.
  526. // Still, we don't really need this information, so we just ignore it.
  527. file.Skip(2);
  528. // Read pattern data
  529. ROWINDEX row = 0;
  530. PatternRow rowBase = Patterns[pat].GetRow(0);
  531. while(row < 64)
  532. {
  533. uint8 info = file.ReadUint8();
  534. if(info == s3mEndOfRow)
  535. {
  536. // End of row
  537. if(++row < 64)
  538. {
  539. rowBase = Patterns[pat].GetRow(row);
  540. }
  541. continue;
  542. }
  543. CHANNELINDEX channel = (info & s3mChannelMask);
  544. ModCommand dummy;
  545. ModCommand &m = (channel < GetNumChannels()) ? rowBase[channel] : dummy;
  546. if(info & s3mNotePresent)
  547. {
  548. const auto [note, instr] = file.ReadArray<uint8, 2>();
  549. if(note < 0xF0)
  550. m.note = static_cast<ModCommand::NOTE>(Clamp((note & 0x0F) + 12 * (note >> 4) + 12 + NOTE_MIN, NOTE_MIN, NOTE_MAX));
  551. else if(note == s3mNoteOff)
  552. m.note = NOTE_NOTECUT;
  553. else if(note == s3mNoteNone)
  554. m.note = NOTE_NONE;
  555. m.instr = instr;
  556. }
  557. if(info & s3mVolumePresent)
  558. {
  559. uint8 volume = file.ReadUint8();
  560. if(volume >= 128 && volume <= 192)
  561. {
  562. m.volcmd = VOLCMD_PANNING;
  563. m.vol = volume - 128;
  564. } else
  565. {
  566. m.volcmd = VOLCMD_VOLUME;
  567. m.vol = std::min(volume, uint8(64));
  568. }
  569. }
  570. if(info & s3mEffectPresent)
  571. {
  572. const auto [command, param] = file.ReadArray<uint8, 2>();
  573. m.command = command;
  574. m.param = param;
  575. S3MConvert(m, false);
  576. if(m.command == CMD_S3MCMDEX && (m.param & 0xF0) == 0xA0 && fileHeader.cwtv < S3MFileHeader::trkST3_20)
  577. {
  578. // Convert old SAx panning to S8x (should only be found in PANIC.S3M by Purple Motion)
  579. m.param = 0x80 | ((m.param & 0x0F) ^ 8);
  580. } else if(m.command == CMD_MIDI)
  581. {
  582. // PixPlay panning test
  583. if(m.param > 0x0F)
  584. {
  585. // PixPlay has Z00 to Z0F panning, so we ignore this.
  586. pixPlayPanning = false;
  587. } else
  588. {
  589. if(m.param < 0x08)
  590. zxxCountLeft++;
  591. else if(m.param > 0x08)
  592. zxxCountRight++;
  593. }
  594. } else if(m.command == CMD_OFFSET && m.param == 0 && fileHeader.cwtv <= S3MFileHeader::trkST3_01)
  595. {
  596. // Offset command didn't have effect memory in ST3.01; fixed in ST3.03
  597. m.command = CMD_DUMMY;
  598. }
  599. }
  600. }
  601. }
  602. if(pixPlayPanning && zxxCountLeft + zxxCountRight >= m_nChannels && (-zxxCountLeft + zxxCountRight) < static_cast<int>(m_nChannels))
  603. {
  604. // There are enough Zxx commands, so let's assume this was made to be played with PixPlay
  605. Patterns.ForEachModCommand([](ModCommand &m)
  606. {
  607. if(m.command == CMD_MIDI)
  608. {
  609. m.command = CMD_S3MCMDEX;
  610. m.param |= 0x80;
  611. }
  612. });
  613. }
  614. return true;
  615. }
  616. #ifndef MODPLUG_NO_FILESAVE
  617. bool CSoundFile::SaveS3M(std::ostream &f) const
  618. {
  619. static constexpr uint8 filler[16] =
  620. {
  621. 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
  622. 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
  623. };
  624. if(m_nChannels == 0)
  625. {
  626. return false;
  627. }
  628. const bool saveMuteStatus =
  629. #ifdef MODPLUG_TRACKER
  630. TrackerSettings::Instance().MiscSaveChannelMuteStatus;
  631. #else
  632. true;
  633. #endif
  634. S3MFileHeader fileHeader;
  635. MemsetZero(fileHeader);
  636. mpt::String::WriteBuf(mpt::String::nullTerminated, fileHeader.name) = m_songName;
  637. fileHeader.dosEof = S3MFileHeader::idEOF;
  638. fileHeader.fileType = S3MFileHeader::idS3MType;
  639. // Orders
  640. ORDERINDEX writeOrders = Order().GetLengthTailTrimmed();
  641. if(writeOrders < 2)
  642. {
  643. writeOrders = 2;
  644. } else if((writeOrders % 2u) != 0)
  645. {
  646. // Number of orders should be even
  647. writeOrders++;
  648. }
  649. LimitMax(writeOrders, static_cast<ORDERINDEX>(256));
  650. fileHeader.ordNum = static_cast<uint16>(writeOrders);
  651. // Samples
  652. SAMPLEINDEX writeSamples = static_cast<SAMPLEINDEX>(GetNumInstruments());
  653. if(writeSamples == 0)
  654. {
  655. writeSamples = GetNumSamples();
  656. }
  657. writeSamples = Clamp(writeSamples, static_cast<SAMPLEINDEX>(1), static_cast<SAMPLEINDEX>(99));
  658. fileHeader.smpNum = static_cast<uint16>(writeSamples);
  659. // Patterns
  660. PATTERNINDEX writePatterns = std::min(Patterns.GetNumPatterns(), PATTERNINDEX(100));
  661. fileHeader.patNum = static_cast<uint16>(writePatterns);
  662. // Flags
  663. if(m_SongFlags[SONG_FASTVOLSLIDES])
  664. {
  665. fileHeader.flags |= S3MFileHeader::fastVolumeSlides;
  666. }
  667. if(m_nMaxPeriod < 20000 || m_SongFlags[SONG_AMIGALIMITS])
  668. {
  669. fileHeader.flags |= S3MFileHeader::amigaLimits;
  670. }
  671. if(m_SongFlags[SONG_S3MOLDVIBRATO])
  672. {
  673. fileHeader.flags |= S3MFileHeader::st2Vibrato;
  674. }
  675. // Version info following: ST3.20 = 0x1320
  676. // Most significant nibble = Tracker ID, see S3MFileHeader::S3MTrackerVersions
  677. // Following: One nibble = Major version, one byte = Minor version (hex)
  678. const uint32 mptVersion = Version::Current().GetRawVersion();
  679. fileHeader.cwtv = S3MFileHeader::trkOpenMPT | static_cast<uint16>((mptVersion >> 16) & S3MFileHeader::versionMask);
  680. fileHeader.reserved2 = static_cast<uint16>(mptVersion);
  681. fileHeader.formatVersion = S3MFileHeader::newVersion;
  682. memcpy(fileHeader.magic, "SCRM", 4);
  683. // Song Variables
  684. fileHeader.globalVol = static_cast<uint8>(std::min(m_nDefaultGlobalVolume / 4u, uint32(64)));
  685. fileHeader.speed = static_cast<uint8>(Clamp(m_nDefaultSpeed, 1u, 254u));
  686. fileHeader.tempo = static_cast<uint8>(Clamp(m_nDefaultTempo.GetInt(), 33u, 255u));
  687. fileHeader.masterVolume = static_cast<uint8>(Clamp(m_nSamplePreAmp, 16u, 127u) | 0x80);
  688. fileHeader.ultraClicks = 16;
  689. fileHeader.usePanningTable = S3MFileHeader::idPanning;
  690. mpt::IO::Write(f, fileHeader);
  691. Order().WriteAsByte(f, writeOrders);
  692. // Comment about parapointers stolen from Schism Tracker:
  693. // The sample data parapointers are 24+4 bits, whereas pattern data and sample headers are only 16+4
  694. // bits -- so while the sample data can be written up to 268 MB within the file (starting at 0xffffff0),
  695. // the pattern data and sample headers are restricted to the first 1 MB (starting at 0xffff0). In effect,
  696. // this practically requires the sample data to be written last in the file, as it is entirely possible
  697. // (and quite easy, even) to write more than 1 MB of sample data in a file.
  698. // The "practical standard order" listed in TECH.DOC is sample headers, patterns, then sample data.
  699. // Calculate offset of first sample header...
  700. mpt::IO::Offset sampleHeaderOffset = mpt::IO::TellWrite(f) + (writeSamples + writePatterns) * 2 + 32;
  701. // ...which must be a multiple of 16, because parapointers omit the lowest 4 bits.
  702. sampleHeaderOffset = (sampleHeaderOffset + 15) & ~15;
  703. std::vector<uint16le> sampleOffsets(writeSamples);
  704. for(SAMPLEINDEX smp = 0; smp < writeSamples; smp++)
  705. {
  706. static_assert((sizeof(S3MSampleHeader) % 16) == 0);
  707. sampleOffsets[smp] = static_cast<uint16>((sampleHeaderOffset + smp * sizeof(S3MSampleHeader)) / 16);
  708. }
  709. mpt::IO::Write(f, sampleOffsets);
  710. mpt::IO::Offset patternPointerOffset = mpt::IO::TellWrite(f);
  711. mpt::IO::Offset firstPatternOffset = sampleHeaderOffset + writeSamples * sizeof(S3MSampleHeader);
  712. std::vector<uint16le> patternOffsets(writePatterns);
  713. // Need to calculate the real offsets later.
  714. mpt::IO::Write(f, patternOffsets);
  715. // Write channel panning
  716. uint8 chnPan[32];
  717. for(CHANNELINDEX chn = 0; chn < 32; chn++)
  718. {
  719. if(chn < GetNumChannels())
  720. chnPan[chn] = static_cast<uint8>(((ChnSettings[chn].nPan * 15 + 128) / 256) | 0x20);
  721. else
  722. chnPan[chn] = 0x08;
  723. }
  724. mpt::IO::Write(f, chnPan);
  725. // Do we need to fill up the file with some padding bytes for 16-Byte alignment?
  726. mpt::IO::Offset curPos = mpt::IO::TellWrite(f);
  727. if(curPos < sampleHeaderOffset)
  728. {
  729. MPT_ASSERT(sampleHeaderOffset - curPos < 16);
  730. mpt::IO::WriteRaw(f, filler, static_cast<std::size_t>(sampleHeaderOffset - curPos));
  731. }
  732. // Don't write sample headers for now, we are lacking the sample offset data.
  733. mpt::IO::SeekAbsolute(f, firstPatternOffset);
  734. // Write patterns
  735. enum class S3MChannelType : uint8 { kUnused = 0, kPCM = 1, kAdlib = 2 };
  736. FlagSet<S3MChannelType> channelType[32] = { S3MChannelType::kUnused };
  737. bool globalCmdOnMutedChn = false;
  738. for(PATTERNINDEX pat = 0; pat < writePatterns; pat++)
  739. {
  740. if(Patterns.IsPatternEmpty(pat))
  741. {
  742. patternOffsets[pat] = 0;
  743. continue;
  744. }
  745. mpt::IO::Offset patOffset = mpt::IO::TellWrite(f);
  746. if(patOffset > 0xFFFF0)
  747. {
  748. AddToLog(LogError, MPT_UFORMAT("Too much pattern data! Writing patterns failed starting from pattern {}.")(pat));
  749. break;
  750. }
  751. MPT_ASSERT((patOffset % 16) == 0);
  752. patternOffsets[pat] = static_cast<uint16>(patOffset / 16);
  753. std::vector<uint8> buffer;
  754. buffer.reserve(5 * 1024);
  755. // Reserve space for length bytes
  756. buffer.resize(2, 0);
  757. if(Patterns.IsValidPat(pat))
  758. {
  759. for(ROWINDEX row = 0; row < 64; row++)
  760. {
  761. if(row >= Patterns[pat].GetNumRows())
  762. {
  763. // Invent empty row
  764. buffer.push_back(s3mEndOfRow);
  765. continue;
  766. }
  767. const PatternRow rowBase = Patterns[pat].GetRow(row);
  768. CHANNELINDEX writeChannels = std::min(CHANNELINDEX(32), GetNumChannels());
  769. for(CHANNELINDEX chn = 0; chn < writeChannels; chn++)
  770. {
  771. const ModCommand &m = rowBase[chn];
  772. uint8 info = static_cast<uint8>(chn);
  773. uint8 note = m.note;
  774. ModCommand::VOLCMD volcmd = m.volcmd;
  775. uint8 vol = m.vol;
  776. uint8 command = m.command;
  777. uint8 param = m.param;
  778. if(note != NOTE_NONE || m.instr != 0)
  779. {
  780. info |= s3mNotePresent;
  781. if(note == NOTE_NONE)
  782. {
  783. note = s3mNoteNone;
  784. } else if(ModCommand::IsSpecialNote(note))
  785. {
  786. // Note Cut
  787. note = s3mNoteOff;
  788. } else if(note < 12 + NOTE_MIN)
  789. {
  790. // Too low
  791. note = 0;
  792. } else if(note <= NOTE_MAX)
  793. {
  794. note -= (12 + NOTE_MIN);
  795. note = (note % 12) + ((note / 12) << 4);
  796. }
  797. if(m.instr > 0 && m.instr <= GetNumSamples())
  798. {
  799. const ModSample &smp = Samples[m.instr];
  800. if(smp.uFlags[CHN_ADLIB])
  801. channelType[chn].set(S3MChannelType::kAdlib);
  802. else if(smp.HasSampleData())
  803. channelType[chn].set(S3MChannelType::kPCM);
  804. }
  805. }
  806. if(command == CMD_VOLUME)
  807. {
  808. command = CMD_NONE;
  809. volcmd = VOLCMD_VOLUME;
  810. vol = std::min(param, uint8(64));
  811. }
  812. if(volcmd == VOLCMD_VOLUME)
  813. {
  814. info |= s3mVolumePresent;
  815. } else if(volcmd == VOLCMD_PANNING)
  816. {
  817. info |= s3mVolumePresent;
  818. vol |= 0x80;
  819. }
  820. if(command != CMD_NONE)
  821. {
  822. S3MSaveConvert(command, param, false, true);
  823. if(command || param)
  824. {
  825. info |= s3mEffectPresent;
  826. if(saveMuteStatus && ChnSettings[chn].dwFlags[CHN_MUTE] && m.IsGlobalCommand())
  827. {
  828. globalCmdOnMutedChn = true;
  829. }
  830. }
  831. }
  832. if(info & s3mAnyPresent)
  833. {
  834. buffer.push_back(info);
  835. if(info & s3mNotePresent)
  836. {
  837. buffer.push_back(note);
  838. buffer.push_back(m.instr);
  839. }
  840. if(info & s3mVolumePresent)
  841. {
  842. buffer.push_back(vol);
  843. }
  844. if(info & s3mEffectPresent)
  845. {
  846. buffer.push_back(command);
  847. buffer.push_back(param);
  848. }
  849. }
  850. }
  851. buffer.push_back(s3mEndOfRow);
  852. }
  853. } else
  854. {
  855. // Invent empty pattern
  856. buffer.insert(buffer.end(), 64, s3mEndOfRow);
  857. }
  858. uint16 length = mpt::saturate_cast<uint16>(buffer.size());
  859. buffer[0] = static_cast<uint8>(length & 0xFF);
  860. buffer[1] = static_cast<uint8>((length >> 8) & 0xFF);
  861. if((buffer.size() % 16u) != 0)
  862. {
  863. // Add padding bytes
  864. buffer.insert(buffer.end(), 16 - (buffer.size() % 16u), 0);
  865. }
  866. mpt::IO::Write(f, buffer);
  867. }
  868. if(globalCmdOnMutedChn)
  869. {
  870. //AddToLog(LogWarning, U_("Global commands on muted channels are interpreted only by some S3M players."));
  871. }
  872. mpt::IO::Offset sampleDataOffset = mpt::IO::TellWrite(f);
  873. // Write samples
  874. std::vector<S3MSampleHeader> sampleHeader(writeSamples);
  875. for(SAMPLEINDEX smp = 0; smp < writeSamples; smp++)
  876. {
  877. SAMPLEINDEX realSmp = smp + 1;
  878. if(GetNumInstruments() != 0 && Instruments[smp] != nullptr)
  879. {
  880. // Find some valid sample associated with this instrument.
  881. for(SAMPLEINDEX keySmp : Instruments[smp]->Keyboard)
  882. {
  883. if(keySmp > 0 && keySmp <= GetNumSamples())
  884. {
  885. realSmp = keySmp;
  886. break;
  887. }
  888. }
  889. }
  890. if(realSmp > GetNumSamples())
  891. {
  892. continue;
  893. }
  894. const SmpLength smpLength = sampleHeader[smp].ConvertToS3M(Samples[realSmp]);
  895. mpt::String::WriteBuf(mpt::String::nullTerminated, sampleHeader[smp].name) = m_szNames[realSmp];
  896. if(smpLength != 0)
  897. {
  898. // Write sample data
  899. if(sampleDataOffset > 0xFFFFFF0)
  900. {
  901. AddToLog(LogError, MPT_UFORMAT("Too much sample data! Writing samples failed starting from sample {}.")(realSmp));
  902. break;
  903. }
  904. sampleHeader[smp].dataPointer[1] = static_cast<uint8>((sampleDataOffset >> 4) & 0xFF);
  905. sampleHeader[smp].dataPointer[2] = static_cast<uint8>((sampleDataOffset >> 12) & 0xFF);
  906. sampleHeader[smp].dataPointer[0] = static_cast<uint8>((sampleDataOffset >> 20) & 0xFF);
  907. size_t writtenLength = sampleHeader[smp].GetSampleFormat(false).WriteSample(f, Samples[realSmp], smpLength);
  908. sampleDataOffset += writtenLength;
  909. if((writtenLength % 16u) != 0)
  910. {
  911. size_t fillSize = 16 - (writtenLength % 16u);
  912. mpt::IO::WriteRaw(f, filler, fillSize);
  913. sampleDataOffset += fillSize;
  914. }
  915. }
  916. }
  917. // Channel Table
  918. uint8 sampleCh = 0, adlibCh = 0;
  919. for(CHANNELINDEX chn = 0; chn < 32; chn++)
  920. {
  921. if(chn < GetNumChannels())
  922. {
  923. if(channelType[chn][S3MChannelType::kPCM] && channelType[chn][S3MChannelType::kAdlib])
  924. {
  925. AddToLog(LogWarning, MPT_UFORMAT("Pattern channel {} constains both samples and OPL instruments, which is not supported by Scream Tracker 3.")(chn + 1));
  926. }
  927. // ST3 only supports 16 PCM channels, so if channels 17-32 are used,
  928. // they must be mapped to the same "internal channels" as channels 1-16.
  929. // The channel indices determine in which order channels are evaluated in ST3.
  930. // First, the "left" channels (0...7) are evaluated, then the "right" channels (8...15).
  931. // Previously, an alternating LRLR scheme was written, which would lead to a different
  932. // effect processing in ST3 than LLL...RRR, but since OpenMPT doesn't care about the
  933. // channel order and always parses them left to right as they appear in the pattern,
  934. // we should just write in the LLL...RRR manner.
  935. uint8 ch = sampleCh % 16u; // If there are neither PCM nor AdLib instruments on this channel, just fall back a regular sample-based channel for maximum compatibility.
  936. if(channelType[chn][S3MChannelType::kPCM])
  937. ch = (sampleCh++) % 16u;
  938. else if(channelType[chn][S3MChannelType::kAdlib])
  939. ch = 16 + ((adlibCh++) % 9u);
  940. if(saveMuteStatus && ChnSettings[chn].dwFlags[CHN_MUTE])
  941. {
  942. ch |= 0x80;
  943. }
  944. fileHeader.channels[chn] = ch;
  945. } else
  946. {
  947. fileHeader.channels[chn] = 0xFF;
  948. }
  949. }
  950. if(sampleCh > 16)
  951. {
  952. AddToLog(LogWarning, MPT_UFORMAT("This module has more than 16 ({}) sample channels, which is not supported by Scream Tracker 3.")(sampleCh));
  953. }
  954. if(adlibCh > 9)
  955. {
  956. AddToLog(LogWarning, MPT_UFORMAT("This module has more than 9 ({}) OPL channels, which is not supported by Scream Tracker 3.")(adlibCh));
  957. }
  958. mpt::IO::SeekAbsolute(f, 0);
  959. mpt::IO::Write(f, fileHeader);
  960. // Now we know where the patterns are.
  961. if(writePatterns != 0)
  962. {
  963. mpt::IO::SeekAbsolute(f, patternPointerOffset);
  964. mpt::IO::Write(f, patternOffsets);
  965. }
  966. // And we can finally write the sample headers.
  967. if(writeSamples != 0)
  968. {
  969. mpt::IO::SeekAbsolute(f, sampleHeaderOffset);
  970. mpt::IO::Write(f, sampleHeader);
  971. }
  972. return true;
  973. }
  974. #endif // MODPLUG_NO_FILESAVE
  975. OPENMPT_NAMESPACE_END