1 /*
2  * Load_it.cpp
3  * -----------
4  * Purpose: IT (Impulse Tracker) module loader / saver
5  * Notes  : Also handles MPTM loading / saving, as the formats are almost identical.
6  * Authors: Olivier Lapicque
7  *          OpenMPT Devs
8  * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
9  */
10 
11 
12 #include "stdafx.h"
13 #include "Loaders.h"
14 #include "tuningcollection.h"
15 #include "mod_specifications.h"
16 #ifdef MODPLUG_TRACKER
17 #include "../mptrack/Moddoc.h"
18 #include "../mptrack/TrackerSettings.h"
19 #endif // MODPLUG_TRACKER
20 #ifdef MPT_EXTERNAL_SAMPLES
21 #include "../common/mptPathString.h"
22 #endif // MPT_EXTERNAL_SAMPLES
23 #include "../common/serialization_utils.h"
24 #ifndef MODPLUG_NO_FILESAVE
25 #include "../common/mptFileIO.h"
26 #endif // MODPLUG_NO_FILESAVE
27 #include "plugins/PlugInterface.h"
28 #include <sstream>
29 #include "../common/version.h"
30 #include "ITTools.h"
31 #include "mpt/io/base.hpp"
32 #include "mpt/io/io.hpp"
33 #include "mpt/io/io_stdstream.hpp"
34 
35 
36 OPENMPT_NAMESPACE_BEGIN
37 
38 
39 const uint16 verMptFileVer = 0x891;
40 const uint16 verMptFileVerLoadLimit = 0x1000; // If cwtv-field is greater or equal to this value,
41 											  // the MPTM file will not be loaded.
42 
43 /*
44 MPTM version history for cwtv-field in "IT" header (only for MPTM files!):
45 0x890(1.18.02.00) -> 0x891(1.19.00.00): Pattern-specific time signatures
46 										Fixed behaviour of Pattern Loop command for rows > 255 (r617)
47 0x88F(1.18.01.00) -> 0x890(1.18.02.00): Removed volume command velocity :xy, added delay-cut command :xy.
48 0x88E(1.17.02.50) -> 0x88F(1.18.01.00): Numerous changes
49 0x88D(1.17.02.49) -> 0x88E(1.17.02.50): Changed ID to that of IT and undone the orderlist change done in
50 				       0x88A->0x88B. Now extended orderlist is saved as extension.
51 0x88C(1.17.02.48) -> 0x88D(1.17.02.49): Some tuning related changes - that part fails to read on older versions.
52 0x88B -> 0x88C: Changed type in which tuning number is printed to file: size_t -> uint16.
53 0x88A -> 0x88B: Changed order-to-pattern-index table type from uint8-array to vector<uint32>.
54 */
55 
56 
57 #ifndef MODPLUG_NO_FILESAVE
58 
AreNonDefaultTuningsUsed(const CSoundFile & sf)59 static bool AreNonDefaultTuningsUsed(const CSoundFile& sf)
60 {
61 	const INSTRUMENTINDEX numIns = sf.GetNumInstruments();
62 	for(INSTRUMENTINDEX i = 1; i <= numIns; i++)
63 	{
64 		if(sf.Instruments[i] != nullptr && sf.Instruments[i]->pTuning != nullptr)
65 			return true;
66 	}
67 	return false;
68 }
69 
WriteTuningCollection(std::ostream & oStrm,const CTuningCollection & tc)70 static void WriteTuningCollection(std::ostream& oStrm, const CTuningCollection& tc)
71 {
72 	tc.Serialize(oStrm, U_("Tune specific tunings"));
73 }
74 
WriteTuningMap(std::ostream & oStrm,const CSoundFile & sf)75 static void WriteTuningMap(std::ostream& oStrm, const CSoundFile& sf)
76 {
77 	if(sf.GetNumInstruments() > 0)
78 	{
79 		//Writing instrument tuning data: first creating
80 		//tuning name <-> tuning id number map,
81 		//and then writing the tuning id for every instrument.
82 		//For example if there are 6 instruments and
83 		//first half use tuning 'T1', and the other half
84 		//tuning 'T2', the output would be something like
85 		//T1 1 T2 2 1 1 1 2 2 2
86 
87 		//Creating the tuning address <-> tuning id number map.
88 		std::map<CTuning*, uint16> tNameToShort_Map;
89 
90 		unsigned short figMap = 0;
91 		for(INSTRUMENTINDEX i = 1; i <= sf.GetNumInstruments(); i++)
92 		{
93 			CTuning *pTuning = nullptr;
94 			if(sf.Instruments[i] != nullptr)
95 			{
96 				pTuning = sf.Instruments[i]->pTuning;
97 			}
98 			auto iter = tNameToShort_Map.find(pTuning);
99 			if(iter != tNameToShort_Map.end())
100 				continue; //Tuning already mapped.
101 
102 			tNameToShort_Map[pTuning] = figMap;
103 			figMap++;
104 		}
105 
106 		//...and write the map with tuning names replacing
107 		//the addresses.
108 		const uint16 tuningMapSize = static_cast<uint16>(tNameToShort_Map.size());
109 		mpt::IO::WriteIntLE<uint16>(oStrm, tuningMapSize);
110 		for(auto &iter : tNameToShort_Map)
111 		{
112 			if(iter.first)
113 				mpt::IO::WriteSizedStringLE<uint8>(oStrm, mpt::ToCharset(mpt::Charset::UTF8, iter.first->GetName()));
114 			else //Case: Using original IT tuning.
115 				mpt::IO::WriteSizedStringLE<uint8>(oStrm, "->MPT_ORIGINAL_IT<-");
116 
117 			mpt::IO::WriteIntLE<uint16>(oStrm, iter.second);
118 		}
119 
120 		//Writing tuning data for instruments.
121 		for(INSTRUMENTINDEX i = 1; i <= sf.GetNumInstruments(); i++)
122 		{
123 			CTuning *pTuning = nullptr;
124 			if(sf.Instruments[i] != nullptr)
125 			{
126 				pTuning = sf.Instruments[i]->pTuning;
127 			}
128 			auto iter = tNameToShort_Map.find(pTuning);
129 			if(iter == tNameToShort_Map.end()) //Should never happen
130 			{
131 				sf.AddToLog(LogError, U_("Error: 210807_1"));
132 				return;
133 			}
134 			mpt::IO::WriteIntLE<uint16>(oStrm, iter->second);
135 		}
136 	}
137 }
138 
139 #endif // MODPLUG_NO_FILESAVE
140 
141 
ReadTuningCollection(std::istream & iStrm,CTuningCollection & tc,const std::size_t dummy,mpt::Charset defaultCharset)142 static void ReadTuningCollection(std::istream &iStrm, CTuningCollection &tc, const std::size_t dummy, mpt::Charset defaultCharset)
143 {
144 	MPT_UNREFERENCED_PARAMETER(dummy);
145 	mpt::ustring name;
146 	tc.Deserialize(iStrm, name, defaultCharset);
147 }
148 
149 
150 template<class TUNNUMTYPE, class STRSIZETYPE>
ReadTuningMapTemplate(std::istream & iStrm,std::map<uint16,mpt::ustring> & shortToTNameMap,mpt::Charset charset,const size_t maxNum=500)151 static bool ReadTuningMapTemplate(std::istream& iStrm, std::map<uint16, mpt::ustring> &shortToTNameMap, mpt::Charset charset, const size_t maxNum = 500)
152 {
153 	TUNNUMTYPE numTuning = 0;
154 	mpt::IO::ReadIntLE<TUNNUMTYPE>(iStrm, numTuning);
155 	if(numTuning > maxNum)
156 		return true;
157 
158 	for(size_t i = 0; i < numTuning; i++)
159 	{
160 		std::string temp;
161 		uint16 ui = 0;
162 		if(!mpt::IO::ReadSizedStringLE<STRSIZETYPE>(iStrm, temp, 255))
163 			return true;
164 
165 		mpt::IO::ReadIntLE<uint16>(iStrm, ui);
166 		shortToTNameMap[ui] = mpt::ToUnicode(charset, temp);
167 	}
168 	if(iStrm.good())
169 		return false;
170 	else
171 		return true;
172 }
173 
174 
ReadTuningMapImpl(std::istream & iStrm,CSoundFile & csf,mpt::Charset charset,const size_t=0,bool old=false)175 static void ReadTuningMapImpl(std::istream& iStrm, CSoundFile& csf, mpt::Charset charset, const size_t = 0, bool old = false)
176 {
177 	std::map<uint16, mpt::ustring> shortToTNameMap;
178 	if(old)
179 	{
180 		ReadTuningMapTemplate<uint32, uint32>(iStrm, shortToTNameMap, charset);
181 	} else
182 	{
183 		ReadTuningMapTemplate<uint16, uint8>(iStrm, shortToTNameMap, charset);
184 	}
185 
186 	// Read & set tunings for instruments
187 	std::vector<mpt::ustring> notFoundTunings;
188 	for(INSTRUMENTINDEX i = 1; i<=csf.GetNumInstruments(); i++)
189 	{
190 		uint16 ui = 0;
191 		mpt::IO::ReadIntLE<uint16>(iStrm, ui);
192 		auto iter = shortToTNameMap.find(ui);
193 		if(csf.Instruments[i] && iter != shortToTNameMap.end())
194 		{
195 			const mpt::ustring str = iter->second;
196 
197 			if(str == U_("->MPT_ORIGINAL_IT<-"))
198 			{
199 				csf.Instruments[i]->pTuning = nullptr;
200 				continue;
201 			}
202 
203 			csf.Instruments[i]->pTuning = csf.GetTuneSpecificTunings().GetTuning(str);
204 			if(csf.Instruments[i]->pTuning)
205 				continue;
206 
207 #ifdef MODPLUG_TRACKER
208 			CTuning *localTuning = TrackerSettings::Instance().oldLocalTunings->GetTuning(str);
209 			if(localTuning)
210 			{
211 				std::unique_ptr<CTuning> pNewTuning = std::unique_ptr<CTuning>(new CTuning(*localTuning));
212 				CTuning *pT = csf.GetTuneSpecificTunings().AddTuning(std::move(pNewTuning));
213 				if(pT)
214 				{
215 					csf.AddToLog(LogInformation, U_("Local tunings are deprecated and no longer supported. Tuning '") + str + U_("' found in Local tunings has been copied to Tune-specific tunings and will be saved in the module file."));
216 					csf.Instruments[i]->pTuning = pT;
217 					if(csf.GetpModDoc() != nullptr)
218 					{
219 						csf.GetpModDoc()->SetModified();
220 					}
221 					continue;
222 				} else
223 				{
224 					csf.AddToLog(LogError, U_("Copying Local tuning '") + str + U_("' to Tune-specific tunings failed."));
225 				}
226 			}
227 #endif
228 
229 			if(str == U_("12TET [[fs15 1.17.02.49]]") || str == U_("12TET"))
230 			{
231 				std::unique_ptr<CTuning> pNewTuning = csf.CreateTuning12TET(str);
232 				CTuning *pT = csf.GetTuneSpecificTunings().AddTuning(std::move(pNewTuning));
233 				if(pT)
234 				{
235 					#ifdef MODPLUG_TRACKER
236 						csf.AddToLog(LogInformation, U_("Built-in tunings will no longer be used. Tuning '") + str + U_("' has been copied to Tune-specific tunings and will be saved in the module file."));
237 						csf.Instruments[i]->pTuning = pT;
238 						if(csf.GetpModDoc() != nullptr)
239 						{
240 							csf.GetpModDoc()->SetModified();
241 						}
242 					#endif
243 					continue;
244 				} else
245 				{
246 					#ifdef MODPLUG_TRACKER
247 						csf.AddToLog(LogError, U_("Copying Built-in tuning '") + str + U_("' to Tune-specific tunings failed."));
248 					#endif
249 				}
250 			}
251 
252 			// Checking if not found tuning already noticed.
253 			if(!mpt::contains(notFoundTunings, str))
254 			{
255 				notFoundTunings.push_back(str);
256 				csf.AddToLog(LogWarning, U_("Tuning '") + str + U_("' used by the module was not found."));
257 #ifdef MODPLUG_TRACKER
258 				if(csf.GetpModDoc() != nullptr)
259 				{
260 					csf.GetpModDoc()->SetModified(); // The tuning is changed so the modified flag is set.
261 				}
262 #endif // MODPLUG_TRACKER
263 
264 			}
265 			csf.Instruments[i]->pTuning = csf.GetDefaultTuning();
266 
267 		} else
268 		{
269 			//This 'else' happens probably only in case of corrupted file.
270 			if(csf.Instruments[i])
271 				csf.Instruments[i]->pTuning = csf.GetDefaultTuning();
272 		}
273 
274 	}
275 	//End read&set instrument tunings
276 }
277 
278 
ReadTuningMap(std::istream & iStrm,CSoundFile & csf,const size_t dummy,mpt::Charset charset)279 static void ReadTuningMap(std::istream& iStrm, CSoundFile& csf, const size_t dummy, mpt::Charset charset)
280 {
281 	ReadTuningMapImpl(iStrm, csf, charset, dummy, false);
282 }
283 
284 
285 //////////////////////////////////////////////////////////
286 // Impulse Tracker IT file support
287 
288 
ITInstrToMPT(FileReader & file,ModInstrument & ins,uint16 trkvers)289 size_t CSoundFile::ITInstrToMPT(FileReader &file, ModInstrument &ins, uint16 trkvers)
290 {
291 	if(trkvers < 0x0200)
292 	{
293 		// Load old format (IT 1.xx) instrument (early IT 2.xx modules may have cmwt set to 1.00 for backwards compatibility)
294 		ITOldInstrument instrumentHeader;
295 		if(!file.ReadStruct(instrumentHeader))
296 		{
297 			return 0;
298 		} else
299 		{
300 			instrumentHeader.ConvertToMPT(ins);
301 			return sizeof(ITOldInstrument);
302 		}
303 	} else
304 	{
305 		const FileReader::off_t offset = file.GetPosition();
306 
307 		// Try loading extended instrument... instSize will differ between normal and extended instruments.
308 		ITInstrumentEx instrumentHeader;
309 		file.ReadStructPartial(instrumentHeader);
310 		size_t instSize = instrumentHeader.ConvertToMPT(ins, GetType());
311 		file.Seek(offset + instSize);
312 
313 		// Try reading modular instrument data.
314 		// Yes, it is completely idiotic that we have both this and LoadExtendedInstrumentProperties.
315 		// This is only required for files saved with *really* old OpenMPT versions (pre-1.17-RC1).
316 		// This chunk was also written in later versions (probably to maintain compatibility with
317 		// those ancient versions), but this also means that redundant information is stored in the file.
318 		// Starting from OpenMPT 1.25.02.07, this chunk is no longer written.
319 		if(file.ReadMagic("MSNI"))
320 		{
321 			//...the next piece of data must be the total size of the modular data
322 			FileReader modularData = file.ReadChunk(file.ReadUint32LE());
323 			instSize += 8 + modularData.GetLength();
324 			if(modularData.ReadMagic("GULP"))
325 			{
326 				ins.nMixPlug = modularData.ReadUint8();
327 				if(ins.nMixPlug > MAX_MIXPLUGINS) ins.nMixPlug = 0;
328 			}
329 		}
330 
331 		return instSize;
332 	}
333 }
334 
335 
CopyPatternName(CPattern & pattern,FileReader & file)336 static void CopyPatternName(CPattern &pattern, FileReader &file)
337 {
338 	char name[MAX_PATTERNNAME] = "";
339 	file.ReadString<mpt::String::maybeNullTerminated>(name, MAX_PATTERNNAME);
340 	pattern.SetName(name);
341 }
342 
343 
344 // Get version of Schism Tracker that was used to create an IT/S3M file.
GetSchismTrackerVersion(uint16 cwtv,uint32 reserved)345 mpt::ustring CSoundFile::GetSchismTrackerVersion(uint16 cwtv, uint32 reserved)
346 {
347 	// Schism Tracker version information in a nutshell:
348 	// < 0x020: a proper version (files saved by such versions are likely very rare)
349 	// = 0x020: any version between the 0.2a release (2005-04-29?) and 2007-04-17
350 	// = 0x050: anywhere from 2007-04-17 to 2009-10-31
351 	// > 0x050: the number of days since 2009-10-31
352 	// = 0xFFF: any version starting from 2020-10-28 (exact version stored in reserved value)
353 
354 	cwtv &= 0xFFF;
355 	if(cwtv > 0x050)
356 	{
357 		int32 date = SchismTrackerEpoch + (cwtv < 0xFFF ? cwtv - 0x050 : reserved);
358 		int32 y = static_cast<int32>((Util::mul32to64(10000, date) + 14780) / 3652425);
359 		int32 ddd = date - (365 * y + y / 4 - y / 100 + y / 400);
360 		if(ddd < 0)
361 		{
362 			y--;
363 			ddd = date - (365 * y + y / 4 - y / 100 + y / 400);
364 		}
365 		int32 mi = (100 * ddd + 52) / 3060;
366 		return MPT_UFORMAT("Schism Tracker {}-{}-{}")(
367 			mpt::ufmt::dec0<4>(y + (mi + 2) / 12),
368 			mpt::ufmt::dec0<2>((mi + 2) % 12 + 1),
369 			mpt::ufmt::dec0<2>(ddd - (mi * 306 + 5) / 10 + 1));
370 	} else
371 	{
372 		return MPT_UFORMAT("Schism Tracker 0.{}")(mpt::ufmt::hex0<2>(cwtv));
373 	}
374 }
375 
376 
ValidateHeader(const ITFileHeader & fileHeader)377 static bool ValidateHeader(const ITFileHeader &fileHeader)
378 {
379 	if((std::memcmp(fileHeader.id, "IMPM", 4) && std::memcmp(fileHeader.id, "tpm.", 4))
380 		|| fileHeader.insnum > 0xFF
381 		|| fileHeader.smpnum >= MAX_SAMPLES
382 		)
383 	{
384 		return false;
385 	}
386 	return true;
387 }
388 
389 
GetHeaderMinimumAdditionalSize(const ITFileHeader & fileHeader)390 static uint64 GetHeaderMinimumAdditionalSize(const ITFileHeader &fileHeader)
391 {
392 	return fileHeader.ordnum + (fileHeader.insnum + fileHeader.smpnum + fileHeader.patnum) * 4;
393 }
394 
395 
ProbeFileHeaderIT(MemoryFileReader file,const uint64 * pfilesize)396 CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderIT(MemoryFileReader file, const uint64 *pfilesize)
397 {
398 	ITFileHeader fileHeader;
399 	if(!file.ReadStruct(fileHeader))
400 	{
401 		return ProbeWantMoreData;
402 	}
403 	if(!ValidateHeader(fileHeader))
404 	{
405 		return ProbeFailure;
406 	}
407 	return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader));
408 }
409 
410 
ReadIT(FileReader & file,ModLoadingFlags loadFlags)411 bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags)
412 {
413 	file.Rewind();
414 
415 	ITFileHeader fileHeader;
416 	if(!file.ReadStruct(fileHeader))
417 	{
418 		return false;
419 	}
420 	if(!ValidateHeader(fileHeader))
421 	{
422 		return false;
423 	}
424 	if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader))))
425 	{
426 		return false;
427 	}
428 	if(loadFlags == onlyVerifyHeader)
429 	{
430 		return true;
431 	}
432 
433 	InitializeGlobals(MOD_TYPE_IT);
434 
435 	bool interpretModPlugMade = false;
436 	mpt::ustring madeWithTracker;
437 
438 	// OpenMPT crap at the end of file
439 	size_t mptStartPos = 0;
440 
441 	if(!memcmp(fileHeader.id, "tpm.", 4))
442 	{
443 		// Legacy MPTM files (old 1.17.02.4x releases)
444 		SetType(MOD_TYPE_MPT);
445 		file.Seek(file.GetLength() - 4);
446 		mptStartPos = file.ReadUint32LE();
447 	} else
448 	{
449 		if(fileHeader.cwtv > 0x888 && fileHeader.cwtv <= 0xFFF)
450 		{
451 			file.Seek(file.GetLength() - 4);
452 			mptStartPos = file.ReadUint32LE();
453 			if(mptStartPos >= 0x100 && mptStartPos < file.GetLength())
454 			{
455 				if(file.Seek(mptStartPos) && file.ReadMagic("228"))
456 				{
457 					SetType(MOD_TYPE_MPT);
458 					if(fileHeader.cwtv >= verMptFileVerLoadLimit)
459 					{
460 						AddToLog(LogError, U_("The file informed that it is incompatible with this version of OpenMPT. Loading was terminated."));
461 						return false;
462 					} else if(fileHeader.cwtv > verMptFileVer)
463 					{
464 						AddToLog(LogInformation, U_("The loaded file was made with a more recent OpenMPT version and this version may not be able to load all the features or play the file correctly."));
465 					}
466 				}
467 			}
468 		}
469 
470 		if(GetType() == MOD_TYPE_IT)
471 		{
472 			// Which tracker was used to make this?
473 			if((fileHeader.cwtv & 0xF000) == 0x5000)
474 			{
475 				// OpenMPT Version number (Major.Minor)
476 				// This will only be interpreted as "made with ModPlug" (i.e. disable compatible playback etc) if the "reserved" field is set to "OMPT" - else, compatibility was used.
477 				uint32 mptVersion = (fileHeader.cwtv & 0x0FFF) << 16;
478 				if(!memcmp(&fileHeader.reserved, "OMPT", 4))
479 					interpretModPlugMade = true;
480 				else if(mptVersion >= 0x01'29'00'00)
481 					mptVersion |= fileHeader.reserved & 0xFFFF;
482 				m_dwLastSavedWithVersion = Version(mptVersion);
483 			} else if(fileHeader.cmwt == 0x888 || fileHeader.cwtv == 0x888)
484 			{
485 				// OpenMPT 1.17.02.26 (r122) to 1.18 (raped IT format)
486 				// Exact version number will be determined later.
487 				interpretModPlugMade = true;
488 				m_dwLastSavedWithVersion = MPT_V("1.17.00.00");
489 			} else if(fileHeader.cwtv == 0x0217 && fileHeader.cmwt == 0x0200 && fileHeader.reserved == 0)
490 			{
491 				if(memchr(fileHeader.chnpan, 0xFF, sizeof(fileHeader.chnpan)) != nullptr)
492 				{
493 					// ModPlug Tracker 1.16 (semi-raped IT format) or BeRoTracker (will be determined later)
494 					m_dwLastSavedWithVersion = MPT_V("1.16.00.00");
495 					madeWithTracker = U_("ModPlug Tracker 1.09 - 1.16");
496 				} else
497 				{
498 					// OpenMPT 1.17 disguised as this in compatible mode,
499 					// but never writes 0xFF in the pan map for unused channels (which is an invalid value).
500 					m_dwLastSavedWithVersion = MPT_V("1.17.00.00");
501 					madeWithTracker = U_("OpenMPT 1.17 (compatibility export)");
502 				}
503 				interpretModPlugMade = true;
504 			} else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0202 && fileHeader.reserved == 0)
505 			{
506 				// ModPlug Tracker b3.3 - 1.09, instruments 557 bytes apart
507 				m_dwLastSavedWithVersion = MPT_V("1.09.00.00");
508 				madeWithTracker = U_("ModPlug Tracker b3.3 - 1.09");
509 				interpretModPlugMade = true;
510 			} else if(fileHeader.cwtv == 0x0300 && fileHeader.cmwt == 0x0300 && fileHeader.reserved == 0 && fileHeader.ordnum == 256 && fileHeader.sep == 128 && fileHeader.pwd == 0)
511 			{
512 				// A rare variant used from OpenMPT 1.17.02.20 (r113) to 1.17.02.25 (r121), found e.g. in xTr1m-SD.it
513 				m_dwLastSavedWithVersion = MPT_V("1.17.02.20");
514 				interpretModPlugMade = true;
515 			}
516 		}
517 	}
518 
519 	m_SongFlags.set(SONG_LINEARSLIDES, (fileHeader.flags & ITFileHeader::linearSlides) != 0);
520 	m_SongFlags.set(SONG_ITOLDEFFECTS, (fileHeader.flags & ITFileHeader::itOldEffects) != 0);
521 	m_SongFlags.set(SONG_ITCOMPATGXX, (fileHeader.flags & ITFileHeader::itCompatGxx) != 0);
522 	m_SongFlags.set(SONG_EXFILTERRANGE, (fileHeader.flags & ITFileHeader::extendedFilterRange) != 0);
523 
524 	m_songName = mpt::String::ReadBuf(mpt::String::spacePadded, fileHeader.songname);
525 
526 	// Read row highlights
527 	if((fileHeader.special & ITFileHeader::embedPatternHighlights))
528 	{
529 		// MPT 1.09 and older (and maybe also newer) versions leave this blank (0/0), but have the "special" flag set.
530 		// Newer versions of MPT and OpenMPT 1.17 *always* write 4/16 here.
531 		// Thus, we will just ignore those old versions.
532 		// Note: OpenMPT 1.17.03.02 was the first version to properly make use of the time signature in the IT header.
533 		// This poses a small unsolvable problem:
534 		// - In compatible mode, we cannot distinguish this version from earlier 1.17 releases.
535 		//   Thus we cannot know when to read this field or not (m_dwLastSavedWithVersion will always be 1.17.00.00).
536 		//   Luckily OpenMPT 1.17.03.02 should not be very wide-spread.
537 		// - In normal mode the time signature is always present in the song extensions anyway. So it's okay if we read
538 		//   the signature here and maybe overwrite it later when parsing the song extensions.
539 		if(!m_dwLastSavedWithVersion || m_dwLastSavedWithVersion >= MPT_V("1.17.03.02"))
540 		{
541 			m_nDefaultRowsPerBeat = fileHeader.highlight_minor;
542 			m_nDefaultRowsPerMeasure = fileHeader.highlight_major;
543 		}
544 	}
545 
546 	// Global Volume
547 	m_nDefaultGlobalVolume = fileHeader.globalvol << 1;
548 	if(m_nDefaultGlobalVolume > MAX_GLOBAL_VOLUME)
549 		m_nDefaultGlobalVolume = MAX_GLOBAL_VOLUME;
550 	if(fileHeader.speed)
551 		m_nDefaultSpeed = fileHeader.speed;
552 	m_nDefaultTempo.Set(std::max(uint8(31), static_cast<uint8>(fileHeader.tempo)));
553 	m_nSamplePreAmp = std::min(static_cast<uint8>(fileHeader.mv), uint8(128));
554 
555 	// Reading Channels Pan Positions
556 	for(CHANNELINDEX i = 0; i < 64; i++) if(fileHeader.chnpan[i] != 0xFF)
557 	{
558 		ChnSettings[i].Reset();
559 		ChnSettings[i].nVolume = Clamp<uint8, uint8>(fileHeader.chnvol[i], 0, 64);
560 		if(fileHeader.chnpan[i] & 0x80) ChnSettings[i].dwFlags.set(CHN_MUTE);
561 		uint8 n = fileHeader.chnpan[i] & 0x7F;
562 		if(n <= 64) ChnSettings[i].nPan = n * 4;
563 		if(n == 100) ChnSettings[i].dwFlags.set(CHN_SURROUND);
564 	}
565 
566 	// Reading orders
567 	file.Seek(sizeof(ITFileHeader));
568 	if(GetType() == MOD_TYPE_MPT && fileHeader.cwtv > 0x88A && fileHeader.cwtv <= 0x88D)
569 	{
570 		// Deprecated format used for MPTm files created with OpenMPT 1.17.02.46 - 1.17.02.48.
571 		uint16 version = file.ReadUint16LE();
572 		if(version != 0)
573 			return false;
574 		uint32 numOrd = file.ReadUint32LE();
575 		if(numOrd > ModSpecs::mptm.ordersMax || !ReadOrderFromFile<uint32le>(Order(), file, numOrd))
576 			return false;
577 	} else
578 	{
579 		ReadOrderFromFile<uint8>(Order(), file, fileHeader.ordnum, 0xFF, 0xFE);
580 	}
581 
582 	// Reading instrument, sample and pattern offsets
583 	std::vector<uint32le> insPos, smpPos, patPos;
584 	if(!file.ReadVector(insPos, fileHeader.insnum)
585 		|| !file.ReadVector(smpPos, fileHeader.smpnum)
586 		|| !file.ReadVector(patPos, fileHeader.patnum))
587 	{
588 		return false;
589 	}
590 
591 	// Find the first parapointer.
592 	// This is used for finding out whether the edit history is actually stored in the file or not,
593 	// as some early versions of Schism Tracker set the history flag, but didn't save anything.
594 	// We will consider the history invalid if it ends after the first parapointer.
595 	uint32 minPtr = std::numeric_limits<decltype(minPtr)>::max();
596 	for(uint32 pos : insPos)
597 	{
598 		if(pos > 0 && pos < minPtr)
599 			minPtr = pos;
600 	}
601 	for(uint32 pos : smpPos)
602 	{
603 		if(pos > 0 && pos < minPtr)
604 			minPtr = pos;
605 	}
606 	for(uint32 pos : patPos)
607 	{
608 		if(pos > 0 && pos < minPtr)
609 			minPtr = pos;
610 	}
611 	if(fileHeader.special & ITFileHeader::embedSongMessage)
612 	{
613 		minPtr = std::min(minPtr, fileHeader.msgoffset.get());
614 	}
615 
616 	const bool possiblyUNMO3 = fileHeader.cmwt == 0x0214 && (fileHeader.cwtv == 0x0214 || fileHeader.cwtv == 0)
617 		&& fileHeader.highlight_major == 0 && fileHeader.highlight_minor == 0
618 		&& fileHeader.pwd == 0 && fileHeader.reserved == 0
619 		&& (fileHeader.flags & (ITFileHeader::useMIDIPitchController | ITFileHeader::reqEmbeddedMIDIConfig)) == 0;
620 
621 	if(possiblyUNMO3 && fileHeader.insnum == 0 && fileHeader.smpnum > 0 && file.GetPosition() + 4 * smpPos.size() + 2 <= minPtr)
622 	{
623 		// UNMO3 < v2.4.0.1 reserves some space for instrument parapointers even in sample mode.
624 		// This makes reading MIDI macros and plugin information impossible.
625 		// Note: While UNMO3 and CheeseTracker header fingerprints are almost identical, we cannot mis-detect CheeseTracker here,
626 		// as it always sets the instrument mode flag and writes non-zero row highlights.
627 		bool oldUNMO3 = true;
628 		for(uint16 i = 0; i < fileHeader.smpnum; i++)
629 		{
630 			if(file.ReadUint32LE() != 0)
631 			{
632 				oldUNMO3 = false;
633 				file.SkipBack(4 + i * 4);
634 				break;
635 			}
636 		}
637 		if(oldUNMO3)
638 		{
639 			madeWithTracker = U_("UNMO3 <= 2.4");
640 		}
641 	}
642 
643 	if(possiblyUNMO3 && fileHeader.cwtv == 0)
644 	{
645 		madeWithTracker = U_("UNMO3 v0/1");
646 	}
647 
648 	// Reading IT Edit History Info
649 	// This is only supposed to be present if bit 1 of the special flags is set.
650 	// However, old versions of Schism and probably other trackers always set this bit
651 	// even if they don't write the edit history count. So we have to filter this out...
652 	// This is done by looking at the parapointers. If the history data ends after
653 	// the first parapointer, we assume that it's actually no history data.
654 	if(fileHeader.special & ITFileHeader::embedEditHistory)
655 	{
656 		const uint16 nflt = file.ReadUint16LE();
657 
658 		if(file.CanRead(nflt * sizeof(ITHistoryStruct)) && file.GetPosition() + nflt * sizeof(ITHistoryStruct) <= minPtr)
659 		{
660 			m_FileHistory.resize(nflt);
661 			for(auto &mptHistory : m_FileHistory)
662 			{
663 				ITHistoryStruct itHistory;
664 				file.ReadStruct(itHistory);
665 				itHistory.ConvertToMPT(mptHistory);
666 			}
667 
668 			if(possiblyUNMO3 && nflt == 0)
669 			{
670 				if(fileHeader.special & ITFileHeader::embedPatternHighlights)
671 					madeWithTracker = U_("UNMO3 <= 2.4.0.1");  // Set together with MIDI macro embed flag
672 				else
673 					madeWithTracker = U_("UNMO3");  // Either 2.4.0.2+ or no MIDI macros embedded
674 			}
675 		} else
676 		{
677 			// Oops, we were not supposed to read this.
678 			file.SkipBack(2);
679 		}
680 	} else if(possiblyUNMO3 && fileHeader.special <= 1)
681 	{
682 		// UNMO3 < v2.4.0.1 will set the edit history special bit iff the MIDI macro embed bit is also set,
683 		// but it always writes the two extra bytes for the edit history length (zeroes).
684 		// If MIDI macros are embedded, we are fine and end up in the first case of the if statement (read edit history).
685 		// Otherwise we end up here and might have to read the edit history length.
686 		if(file.ReadUint16LE() == 0)
687 		{
688 			madeWithTracker = U_("UNMO3 <= 2.4");
689 		} else
690 		{
691 			// These were not zero bytes, but potentially belong to the upcoming MIDI config - need to skip back.
692 			// I think the only application that could end up here is CheeseTracker, if it allows to write 0 for both row highlight values.
693 			// IT 2.14 itself will always write the edit history.
694 			file.SkipBack(2);
695 		}
696 	}
697 
698 	// Reading MIDI Output & Macros
699 	bool hasMidiConfig = (fileHeader.flags & ITFileHeader::reqEmbeddedMIDIConfig) || (fileHeader.special & ITFileHeader::embedMIDIConfiguration);
700 	if(hasMidiConfig && file.ReadStruct<MIDIMacroConfigData>(m_MidiCfg))
701 	{
702 		m_MidiCfg.Sanitize();
703 	}
704 
705 	// Ignore MIDI data. Fixes some files like denonde.it that were made with old versions of Impulse Tracker (which didn't support Zxx filters) and have Zxx effects in the patterns.
706 	if(fileHeader.cwtv < 0x0214)
707 	{
708 		m_MidiCfg.ClearZxxMacros();
709 	}
710 
711 	// Read pattern names: "PNAM"
712 	FileReader patNames;
713 	if(file.ReadMagic("PNAM"))
714 	{
715 		patNames = file.ReadChunk(file.ReadUint32LE());
716 	}
717 
718 	m_nChannels = 1;
719 	// Read channel names: "CNAM"
720 	if(file.ReadMagic("CNAM"))
721 	{
722 		FileReader chnNames = file.ReadChunk(file.ReadUint32LE());
723 		const CHANNELINDEX readChns = std::min(MAX_BASECHANNELS, static_cast<CHANNELINDEX>(chnNames.GetLength() / MAX_CHANNELNAME));
724 		m_nChannels = readChns;
725 
726 		for(CHANNELINDEX i = 0; i < readChns; i++)
727 		{
728 			chnNames.ReadString<mpt::String::maybeNullTerminated>(ChnSettings[i].szName, MAX_CHANNELNAME);
729 		}
730 	}
731 
732 	// Read mix plugins information
733 	FileReader pluginChunk = file.ReadChunk((minPtr >= file.GetPosition()) ? minPtr - file.GetPosition() : file.BytesLeft());
734 	const bool isBeRoTracker = LoadMixPlugins(pluginChunk);
735 
736 	// Read Song Message
737 	if((fileHeader.special & ITFileHeader::embedSongMessage) && fileHeader.msglength > 0 && file.Seek(fileHeader.msgoffset))
738 	{
739 		// Generally, IT files should use CR for line endings. However, ChibiTracker uses LF. One could do...
740 		// if(itHeader.cwtv == 0x0214 && itHeader.cmwt == 0x0214 && itHeader.reserved == ITFileHeader::chibiMagic) --> Chibi detected.
741 		// But we'll just use autodetection here:
742 		m_songMessage.Read(file, fileHeader.msglength, SongMessage::leAutodetect);
743 	}
744 
745 	// Reading Instruments
746 	m_nInstruments = 0;
747 	if(fileHeader.flags & ITFileHeader::instrumentMode)
748 	{
749 		m_nInstruments = std::min(static_cast<INSTRUMENTINDEX>(fileHeader.insnum), static_cast<INSTRUMENTINDEX>(MAX_INSTRUMENTS - 1));
750 	}
751 	for(INSTRUMENTINDEX i = 0; i < GetNumInstruments(); i++)
752 	{
753 		if(insPos[i] > 0 && file.Seek(insPos[i]) && file.CanRead(fileHeader.cmwt < 0x200 ? sizeof(ITOldInstrument) : sizeof(ITInstrument)))
754 		{
755 			ModInstrument *instrument = AllocateInstrument(i + 1);
756 			if(instrument != nullptr)
757 			{
758 				ITInstrToMPT(file, *instrument, fileHeader.cmwt);
759 				// MIDI Pitch Wheel Depth is a global setting in IT. Apply it to all instruments.
760 				instrument->midiPWD = fileHeader.pwd;
761 			}
762 		}
763 	}
764 
765 	// In order to properly compute the position, in file, of eventual extended settings
766 	// such as "attack" we need to keep the "real" size of the last sample as those extra
767 	// setting will follow this sample in the file
768 	FileReader::off_t lastSampleOffset = 0;
769 	if(fileHeader.smpnum > 0)
770 	{
771 		lastSampleOffset = smpPos[fileHeader.smpnum - 1] + sizeof(ITSample);
772 	}
773 
774 	bool possibleXMconversion = false;
775 
776 	// Reading Samples
777 	m_nSamples = std::min(static_cast<SAMPLEINDEX>(fileHeader.smpnum), static_cast<SAMPLEINDEX>(MAX_SAMPLES - 1));
778 	bool lastSampleCompressed = false;
779 	for(SAMPLEINDEX i = 0; i < GetNumSamples(); i++)
780 	{
781 		ITSample sampleHeader;
782 		if(smpPos[i] > 0 && file.Seek(smpPos[i]) && file.ReadStruct(sampleHeader))
783 		{
784 			// IT does not check for the IMPS magic, and some bad XM->IT converter out there doesn't write the magic bytes for empty sample slots.
785 			ModSample &sample = Samples[i + 1];
786 			size_t sampleOffset = sampleHeader.ConvertToMPT(sample);
787 
788 			m_szNames[i + 1] = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name);
789 
790 			if(!file.Seek(sampleOffset))
791 				continue;
792 
793 			lastSampleCompressed = false;
794 			if(sample.uFlags[CHN_ADLIB])
795 			{
796 				// FM instrument in MPTM
797 				OPLPatch patch;
798 				if(file.ReadArray(patch))
799 				{
800 					sample.SetAdlib(true, patch);
801 				}
802 			} else if(!sample.uFlags[SMP_KEEPONDISK])
803 			{
804 				SampleIO sampleIO = sampleHeader.GetSampleFormat(fileHeader.cwtv);
805 				if(loadFlags & loadSampleData)
806 				{
807 					sampleIO.ReadSample(sample, file);
808 				} else
809 				{
810 					if(sampleIO.IsVariableLengthEncoded())
811 						lastSampleCompressed = true;
812 					else
813 						file.Skip(sampleIO.CalculateEncodedSize(sample.nLength));
814 				}
815 				if(sampleIO.GetEncoding() == SampleIO::unsignedPCM && sample.nLength != 0)
816 				{
817 					// There is some XM to IT converter (don't know which one) and it identifies as IT 2.04.
818 					// The only safe way to distinguish it from an IT-saved file are the unsigned samples.
819 					possibleXMconversion = true;
820 				}
821 			} else
822 			{
823 				// External sample in MPTM file
824 				size_t strLen;
825 				file.ReadVarInt(strLen);
826 				if((loadFlags & loadSampleData) && strLen)
827 				{
828 					std::string filenameU8;
829 					file.ReadString<mpt::String::maybeNullTerminated>(filenameU8, strLen);
830 #if defined(MPT_EXTERNAL_SAMPLES)
831 					SetSamplePath(i + 1, mpt::PathString::FromUTF8(filenameU8));
832 #elif !defined(LIBOPENMPT_BUILD_TEST)
833 					AddToLog(LogWarning, MPT_UFORMAT("Loading external sample {} ('{}') failed: External samples are not supported.")(i + 1, mpt::ToUnicode(mpt::Charset::UTF8, filenameU8)));
834 #endif  // MPT_EXTERNAL_SAMPLES
835 				} else
836 				{
837 					file.Skip(strLen);
838 				}
839 			}
840 			lastSampleOffset = std::max(lastSampleOffset, file.GetPosition());
841 		}
842 	}
843 	m_nSamples = std::max(SAMPLEINDEX(1), GetNumSamples());
844 
845 	if(possibleXMconversion && fileHeader.cwtv == 0x0204 && fileHeader.cmwt == 0x0200 && fileHeader.special == 0 && fileHeader.reserved == 0
846 		&& (fileHeader.flags & ~ITFileHeader::linearSlides) == (ITFileHeader::useStereoPlayback | ITFileHeader::instrumentMode | ITFileHeader::itOldEffects)
847 		&& fileHeader.globalvol == 128 && fileHeader.mv == 48 && fileHeader.sep == 128 && fileHeader.pwd == 0 && fileHeader.msglength == 0)
848 	{
849 		for(uint8 pan : fileHeader.chnpan)
850 		{
851 			if(pan != 0x20 && pan != 0xA0)
852 				possibleXMconversion = false;
853 		}
854 		for(uint8 vol : fileHeader.chnvol)
855 		{
856 			if(vol != 0x40)
857 				possibleXMconversion = false;
858 		}
859 		for(size_t i = 20; i < std::size(fileHeader.songname); i++)
860 		{
861 			if(fileHeader.songname[i] != 0)
862 				possibleXMconversion = false;
863 		}
864 		if(possibleXMconversion)
865 			madeWithTracker = U_("XM Conversion");
866 	}
867 
868 	m_nMinPeriod = 0;
869 	m_nMaxPeriod = int32_max;
870 
871 	PATTERNINDEX numPats = std::min(static_cast<PATTERNINDEX>(patPos.size()), GetModSpecifications().patternsMax);
872 
873 	if(numPats != patPos.size())
874 	{
875 		// Hack: Notify user here if file contains more patterns than what can be read.
876 		AddToLog(LogWarning, MPT_UFORMAT("The module contains {} patterns but only {} patterns can be loaded in this OpenMPT version.")(patPos.size(), numPats));
877 	}
878 
879 	if(!(loadFlags & loadPatternData))
880 	{
881 		numPats = 0;
882 	}
883 
884 	// Checking for number of used channels, which is not explicitely specified in the file.
885 	for(PATTERNINDEX pat = 0; pat < numPats; pat++)
886 	{
887 		if(patPos[pat] == 0 || !file.Seek(patPos[pat]))
888 			continue;
889 
890 		uint16 len = file.ReadUint16LE();
891 		ROWINDEX numRows = file.ReadUint16LE();
892 
893 		if(numRows < 1
894 			|| numRows > MAX_PATTERN_ROWS
895 			|| !file.Skip(4))
896 			continue;
897 
898 		FileReader patternData = file.ReadChunk(len);
899 		ROWINDEX row = 0;
900 		std::vector<uint8> chnMask(GetNumChannels());
901 
902 		while(row < numRows && patternData.CanRead(1))
903 		{
904 			uint8 b = patternData.ReadUint8();
905 			if(!b)
906 			{
907 				row++;
908 				continue;
909 			}
910 
911 			CHANNELINDEX ch = (b & IT_bitmask_patternChanField_c);   // 0x7f We have some data grab a byte keeping only 7 bits
912 			if(ch)
913 			{
914 				ch = (ch - 1);// & IT_bitmask_patternChanMask_c;   // 0x3f mask of the byte again, keeping only 6 bits
915 			}
916 
917 			if(ch >= chnMask.size())
918 			{
919 				chnMask.resize(ch + 1, 0);
920 			}
921 
922 			if(b & IT_bitmask_patternChanEnabled_c)            // 0x80 check if the upper bit is enabled.
923 			{
924 				chnMask[ch] = patternData.ReadUint8();       // set the channel mask for this channel.
925 			}
926 			// Channel used
927 			if(chnMask[ch] & 0x0F)         // if this channel is used set m_nChannels
928 			{
929 				if(ch >= GetNumChannels() && ch < MAX_BASECHANNELS)
930 				{
931 					m_nChannels = ch + 1;
932 				}
933 			}
934 			// Now we actually update the pattern-row entry the note,instrument etc.
935 			// Note
936 			if(chnMask[ch] & 1)
937 				patternData.Skip(1);
938 			// Instrument
939 			if(chnMask[ch] & 2)
940 				patternData.Skip(1);
941 			// Volume
942 			if(chnMask[ch] & 4)
943 				patternData.Skip(1);
944 			// Effect
945 			if(chnMask[ch] & 8)
946 				patternData.Skip(2);
947 		}
948 		lastSampleOffset = std::max(lastSampleOffset, file.GetPosition());
949 	}
950 
951 	// Compute extra instruments settings position
952 	if(lastSampleOffset > 0)
953 	{
954 		file.Seek(lastSampleOffset);
955 		if(lastSampleCompressed)
956 		{
957 			// If the last sample was compressed, we do not know where it ends.
958 			// Hence, in case we decided not to decode the sample data, we now
959 			// have to emulate this until we reach EOF or some instrument / song properties.
960 			while(file.CanRead(4))
961 			{
962 				if(file.ReadMagic("XTPM") || file.ReadMagic("STPM"))
963 				{
964 					uint32 id = file.ReadUint32LE();
965 					file.SkipBack(8);
966 					// Our chunk IDs should only contain ASCII characters
967 					if(!(id & 0x80808080) && (id & 0x60606060))
968 					{
969 						break;
970 					}
971 				}
972 				file.Skip(file.ReadUint16LE());
973 			}
974 		}
975 	}
976 
977 	// Load instrument and song extensions.
978 	interpretModPlugMade |= LoadExtendedInstrumentProperties(file);
979 	if(interpretModPlugMade && !isBeRoTracker)
980 	{
981 		m_playBehaviour.reset();
982 		m_nMixLevels = MixLevels::Original;
983 	}
984 	// Need to do this before reading the patterns because m_nChannels might be modified by LoadExtendedSongProperties. *sigh*
985 	LoadExtendedSongProperties(file, false, &interpretModPlugMade);
986 
987 	// Reading Patterns
988 	Patterns.ResizeArray(numPats);
989 	for(PATTERNINDEX pat = 0; pat < numPats; pat++)
990 	{
991 		if(patPos[pat] == 0 || !file.Seek(patPos[pat]))
992 		{
993 			// Empty 64-row pattern
994 			if(!Patterns.Insert(pat, 64))
995 			{
996 				AddToLog(LogWarning, MPT_UFORMAT("Allocating patterns failed starting from pattern {}")(pat));
997 				break;
998 			}
999 			// Now (after the Insert() call), we can read the pattern name.
1000 			CopyPatternName(Patterns[pat], patNames);
1001 			continue;
1002 		}
1003 
1004 		uint16 len = file.ReadUint16LE();
1005 		ROWINDEX numRows = file.ReadUint16LE();
1006 
1007 		if(!file.Skip(4)
1008 			|| !Patterns.Insert(pat, numRows))
1009 			continue;
1010 
1011 		FileReader patternData = file.ReadChunk(len);
1012 
1013 		// Now (after the Insert() call), we can read the pattern name.
1014 		CopyPatternName(Patterns[pat], patNames);
1015 
1016 		std::vector<uint8> chnMask(GetNumChannels());
1017 		std::vector<ModCommand> lastValue(GetNumChannels(), ModCommand::Empty());
1018 
1019 		auto patData = Patterns[pat].begin();
1020 		ROWINDEX row = 0;
1021 		while(row < numRows && patternData.CanRead(1))
1022 		{
1023 			uint8 b = patternData.ReadUint8();
1024 			if(!b)
1025 			{
1026 				row++;
1027 				patData += GetNumChannels();
1028 				continue;
1029 			}
1030 
1031 			CHANNELINDEX ch = b & IT_bitmask_patternChanField_c; // 0x7f
1032 
1033 			if(ch)
1034 			{
1035 				ch = (ch - 1); //& IT_bitmask_patternChanMask_c; // 0x3f
1036 			}
1037 
1038 			if(ch >= chnMask.size())
1039 			{
1040 				chnMask.resize(ch + 1, 0);
1041 				lastValue.resize(ch + 1, ModCommand::Empty());
1042 				MPT_ASSERT(chnMask.size() <= GetNumChannels());
1043 			}
1044 
1045 			if(b & IT_bitmask_patternChanEnabled_c)  // 0x80
1046 			{
1047 				chnMask[ch] = patternData.ReadUint8();
1048 			}
1049 
1050 			// Now we grab the data for this particular row/channel.
1051 			ModCommand dummy = ModCommand::Empty();
1052 			ModCommand &m = ch < m_nChannels ? patData[ch] : dummy;
1053 
1054 			if(chnMask[ch] & 0x10)
1055 			{
1056 				m.note = lastValue[ch].note;
1057 			}
1058 			if(chnMask[ch] & 0x20)
1059 			{
1060 				m.instr = lastValue[ch].instr;
1061 			}
1062 			if(chnMask[ch] & 0x40)
1063 			{
1064 				m.volcmd = lastValue[ch].volcmd;
1065 				m.vol = lastValue[ch].vol;
1066 			}
1067 			if(chnMask[ch] & 0x80)
1068 			{
1069 				m.command = lastValue[ch].command;
1070 				m.param = lastValue[ch].param;
1071 			}
1072 			if(chnMask[ch] & 1)	// Note
1073 			{
1074 				uint8 note = patternData.ReadUint8();
1075 				if(note < 0x80)
1076 					note += NOTE_MIN;
1077 				if(!(GetType() & MOD_TYPE_MPT))
1078 				{
1079 					if(note > NOTE_MAX && note < 0xFD) note = NOTE_FADE;
1080 					else if(note == 0xFD) note = NOTE_NONE;
1081 				}
1082 				m.note = note;
1083 				lastValue[ch].note = note;
1084 			}
1085 			if(chnMask[ch] & 2)
1086 			{
1087 				uint8 instr = patternData.ReadUint8();
1088 				m.instr = instr;
1089 				lastValue[ch].instr = instr;
1090 			}
1091 			if(chnMask[ch] & 4)
1092 			{
1093 				uint8 vol = patternData.ReadUint8();
1094 				// 0-64: Set Volume
1095 				if(vol <= 64) { m.volcmd = VOLCMD_VOLUME; m.vol = vol; } else
1096 				// 128-192: Set Panning
1097 				if(vol >= 128 && vol <= 192) { m.volcmd = VOLCMD_PANNING; m.vol = vol - 128; } else
1098 				// 65-74: Fine Volume Up
1099 				if(vol < 75) { m.volcmd = VOLCMD_FINEVOLUP; m.vol = vol - 65; } else
1100 				// 75-84: Fine Volume Down
1101 				if(vol < 85) { m.volcmd = VOLCMD_FINEVOLDOWN; m.vol = vol - 75; } else
1102 				// 85-94: Volume Slide Up
1103 				if(vol < 95) { m.volcmd = VOLCMD_VOLSLIDEUP; m.vol = vol - 85; } else
1104 				// 95-104: Volume Slide Down
1105 				if(vol < 105) { m.volcmd = VOLCMD_VOLSLIDEDOWN; m.vol = vol - 95; } else
1106 				// 105-114: Pitch Slide Up
1107 				if(vol < 115) { m.volcmd = VOLCMD_PORTADOWN; m.vol = vol - 105; } else
1108 				// 115-124: Pitch Slide Down
1109 				if(vol < 125) { m.volcmd = VOLCMD_PORTAUP; m.vol = vol - 115; } else
1110 				// 193-202: Portamento To
1111 				if(vol >= 193 && vol <= 202) { m.volcmd = VOLCMD_TONEPORTAMENTO; m.vol = vol - 193; } else
1112 				// 203-212: Vibrato depth
1113 				if(vol >= 203 && vol <= 212)
1114 				{
1115 					m.volcmd = VOLCMD_VIBRATODEPTH;
1116 					m.vol = vol - 203;
1117 					// Old versions of ModPlug saved this as vibrato speed instead, so let's fix that.
1118 					if(m.vol && m_dwLastSavedWithVersion && m_dwLastSavedWithVersion <= MPT_V("1.17.02.54"))
1119 						m.volcmd = VOLCMD_VIBRATOSPEED;
1120 				} else
1121 				// 213-222: Unused (was velocity)
1122 				// 223-232: Offset
1123 				if(vol >= 223 && vol <= 232) { m.volcmd = VOLCMD_OFFSET; m.vol = vol - 223; }
1124 				lastValue[ch].volcmd = m.volcmd;
1125 				lastValue[ch].vol = m.vol;
1126 			}
1127 			// Reading command/param
1128 			if(chnMask[ch] & 8)
1129 			{
1130 				const auto [command, param] = patternData.ReadArray<uint8, 2>();
1131 				m.command = command;
1132 				m.param = param;
1133 				S3MConvert(m, true);
1134 				// In some IT-compatible trackers, it is possible to input a parameter without a command.
1135 				// In this case, we still need to update the last value memory. OpenMPT didn't do this until v1.25.01.07.
1136 				// Example: ckbounce.it
1137 				lastValue[ch].command = m.command;
1138 				lastValue[ch].param = m.param;
1139 			}
1140 		}
1141 	}
1142 
1143 	if(!m_dwLastSavedWithVersion && fileHeader.cwtv == 0x0888)
1144 	{
1145 		// Up to OpenMPT 1.17.02.45 (r165), it was possible that the "last saved with" field was 0
1146 		// when saving a file in OpenMPT for the first time.
1147 		m_dwLastSavedWithVersion = MPT_V("1.17.00.00");
1148 	}
1149 
1150 	if(m_dwLastSavedWithVersion && madeWithTracker.empty())
1151 	{
1152 		madeWithTracker = U_("OpenMPT ") + mpt::ufmt::val(m_dwLastSavedWithVersion);
1153 		if(memcmp(&fileHeader.reserved, "OMPT", 4) && (fileHeader.cwtv & 0xF000) == 0x5000)
1154 		{
1155 			madeWithTracker += U_(" (compatibility export)");
1156 		} else if(m_dwLastSavedWithVersion.IsTestVersion())
1157 		{
1158 			madeWithTracker += U_(" (test build)");
1159 		}
1160 	} else
1161 	{
1162 		const int32 schismDateVersion = SchismTrackerEpoch + ((fileHeader.cwtv == 0x1FFF) ? fileHeader.reserved : (fileHeader.cwtv - 0x1050));
1163 		switch(fileHeader.cwtv >> 12)
1164 		{
1165 		case 0:
1166 			if(isBeRoTracker)
1167 			{
1168 				// Old versions
1169 				madeWithTracker = U_("BeRoTracker");
1170 			} else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0200 && fileHeader.flags == 9 && fileHeader.special == 0
1171 				&& fileHeader.highlight_major == 0 && fileHeader.highlight_minor == 0
1172 				&& fileHeader.insnum == 0 && fileHeader.patnum + 1 == fileHeader.ordnum
1173 				&& fileHeader.globalvol == 128 && fileHeader.mv == 100 && fileHeader.speed == 1 && fileHeader.sep == 128 && fileHeader.pwd == 0
1174 				&& fileHeader.msglength == 0 && fileHeader.msgoffset == 0 && fileHeader.reserved == 0)
1175 			{
1176 				madeWithTracker = U_("OpenSPC conversion");
1177 			} else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0200 && fileHeader.highlight_major == 0 && fileHeader.highlight_minor == 0 && fileHeader.reserved == 0)
1178 			{
1179 				// ModPlug Tracker 1.00a5, instruments 560 bytes apart
1180 				m_dwLastSavedWithVersion = MPT_V("1.00.00.A5");
1181 				madeWithTracker = U_("ModPlug Tracker 1.00a5");
1182 				interpretModPlugMade = true;
1183 			} else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0214 && !memcmp(&fileHeader.reserved, "CHBI", 4))
1184 			{
1185 				madeWithTracker = U_("ChibiTracker");
1186 				m_playBehaviour.reset(kITShortSampleRetrig);
1187 			} else if(fileHeader.cwtv == 0x0214 && fileHeader.cmwt == 0x0214 && fileHeader.special <= 1 && fileHeader.pwd == 0 && fileHeader.reserved == 0
1188 				&& (fileHeader.flags & (ITFileHeader::vol0Optimisations | ITFileHeader::instrumentMode | ITFileHeader::useMIDIPitchController | ITFileHeader::reqEmbeddedMIDIConfig | ITFileHeader::extendedFilterRange)) == ITFileHeader::instrumentMode
1189 				&& m_nSamples > 0 && (Samples[1].filename == "XXXXXXXX.YYY"))
1190 			{
1191 				madeWithTracker = U_("CheeseTracker");
1192 			} else if(fileHeader.cwtv == 0 && madeWithTracker.empty())
1193 			{
1194 				madeWithTracker = U_("Unknown");
1195 			} else if(fileHeader.cmwt < 0x0300 && madeWithTracker.empty())
1196 			{
1197 				if(fileHeader.cmwt > 0x0214)
1198 				{
1199 					madeWithTracker = U_("Impulse Tracker 2.15");
1200 				} else if(fileHeader.cwtv > 0x0214)
1201 				{
1202 					// Patched update of IT 2.14 (0x0215 - 0x0217 == p1 - p3)
1203 					// p4 (as found on modland) adds the ITVSOUND driver, but doesn't seem to change
1204 					// anything as far as file saving is concerned.
1205 					madeWithTracker = MPT_UFORMAT("Impulse Tracker 2.14p{}")(fileHeader.cwtv - 0x0214);
1206 				} else
1207 				{
1208 					madeWithTracker = MPT_UFORMAT("Impulse Tracker {}.{}")((fileHeader.cwtv & 0x0F00) >> 8, mpt::ufmt::hex0<2>((fileHeader.cwtv & 0xFF)));
1209 				}
1210 				if(m_FileHistory.empty() && fileHeader.reserved != 0)
1211 				{
1212 					// Starting from  version 2.07, IT stores the total edit time of a module in the "reserved" field
1213 					uint32 editTime = DecodeITEditTimer(fileHeader.cwtv, fileHeader.reserved);
1214 
1215 					FileHistory hist;
1216 					hist.openTime = static_cast<uint32>(editTime * (HISTORY_TIMER_PRECISION / 18.2));
1217 					m_FileHistory.push_back(hist);
1218 				}
1219 			}
1220 			break;
1221 		case 1:
1222 			madeWithTracker = GetSchismTrackerVersion(fileHeader.cwtv, fileHeader.reserved);
1223 			// Hertz in linear mode: Added 2015-01-29, https://github.com/schismtracker/schismtracker/commit/671b30311082a0e7df041fca25f989b5d2478f69
1224 			if(schismDateVersion < SchismVersionFromDate<2015, 01, 29>::date && m_SongFlags[SONG_LINEARSLIDES])
1225 				m_playBehaviour.reset(kPeriodsAreHertz);
1226 			// Hertz in Amiga mode: Added 2021-05-02, https://github.com/schismtracker/schismtracker/commit/c656a6cbd5aaf81198a7580faf81cb7960cb6afa
1227 			else if(schismDateVersion < SchismVersionFromDate<2021, 05, 02>::date && !m_SongFlags[SONG_LINEARSLIDES])
1228 				m_playBehaviour.reset(kPeriodsAreHertz);
1229 			// Qxx with short samples: Added 2016-05-13, https://github.com/schismtracker/schismtracker/commit/e7b1461fe751554309fd403713c2a1ef322105ca
1230 			if(schismDateVersion < SchismVersionFromDate<2016, 05, 13>::date)
1231 				m_playBehaviour.reset(kITShortSampleRetrig);
1232 			// Instrument pan doesn't override channel pan: Added 2021-05-02, https://github.com/schismtracker/schismtracker/commit/a34ec86dc819915debc9e06f4727b77bf2dd29ee
1233 			if(schismDateVersion < SchismVersionFromDate<2021, 05, 02>::date)
1234 				m_playBehaviour.reset(kITDoNotOverrideChannelPan);
1235 			// Notes set instrument panning, not instrument numbers: Added 2021-05-02, https://github.com/schismtracker/schismtracker/commit/648f5116f984815c69e11d018b32dfec53c6b97a
1236 			if(schismDateVersion < SchismVersionFromDate<2021, 05, 02>::date)
1237 				m_playBehaviour.reset(kITPanningReset);
1238 			// Imprecise calculation of ping-pong loop wraparound: Added 2021-11-01, https://github.com/schismtracker/schismtracker/commit/22cbb9b676e9c2c9feb7a6a17deca7a17ac138cc
1239 			if(schismDateVersion < SchismVersionFromDate<2021, 11, 01>::date)
1240 				m_playBehaviour.set(kImprecisePingPongLoops);
1241 			// Pitch/Pan Separation can be overridden by panning commands: Added 2021-11-01, https://github.com/schismtracker/schismtracker/commit/6e9f1207015cae0fe1b829fff7bb867e02ec6dea
1242 			if(schismDateVersion < SchismVersionFromDate<2021, 11, 01>::date)
1243 				m_playBehaviour.reset(kITPitchPanSeparation);
1244 			break;
1245 		case 4:
1246 			madeWithTracker = MPT_UFORMAT("pyIT {}.{}")((fileHeader.cwtv & 0x0F00) >> 8, mpt::ufmt::hex0<2>(fileHeader.cwtv & 0xFF));
1247 			break;
1248 		case 6:
1249 			madeWithTracker = U_("BeRoTracker");
1250 			break;
1251 		case 7:
1252 			if(fileHeader.cwtv == 0x7FFF && fileHeader.cmwt == 0x0215)
1253 				madeWithTracker = U_("munch.py");
1254 			else
1255 				madeWithTracker = MPT_UFORMAT("ITMCK {}.{}.{}")((fileHeader.cwtv >> 8) & 0x0F, (fileHeader.cwtv >> 4) & 0x0F, fileHeader.cwtv & 0x0F);
1256 			break;
1257 		case 0xD:
1258 			madeWithTracker = U_("spc2it");
1259 			break;
1260 		}
1261 	}
1262 
1263 	if(GetType() == MOD_TYPE_MPT)
1264 	{
1265 		// START - mpt specific:
1266 		if(fileHeader.cwtv > 0x0889 && file.Seek(mptStartPos))
1267 		{
1268 			LoadMPTMProperties(file, fileHeader.cwtv);
1269 		}
1270 	}
1271 
1272 	m_modFormat.formatName = (GetType() == MOD_TYPE_MPT) ? U_("OpenMPT MPTM") : MPT_UFORMAT("Impulse Tracker {}.{}")(fileHeader.cmwt >> 8, mpt::ufmt::hex0<2>(fileHeader.cmwt & 0xFF));
1273 	m_modFormat.type = (GetType() == MOD_TYPE_MPT) ? U_("mptm") : U_("it");
1274 	m_modFormat.madeWithTracker = std::move(madeWithTracker);
1275 	m_modFormat.charset = m_dwLastSavedWithVersion ? mpt::Charset::Windows1252 : mpt::Charset::CP437;
1276 
1277 	return true;
1278 }
1279 
1280 
LoadMPTMProperties(FileReader & file,uint16 cwtv)1281 void CSoundFile::LoadMPTMProperties(FileReader &file, uint16 cwtv)
1282 {
1283 	std::istringstream iStrm(mpt::buffer_cast<std::string>(file.GetRawDataAsByteVector()));
1284 
1285 	if(cwtv >= 0x88D)
1286 	{
1287 		srlztn::SsbRead ssb(iStrm);
1288 		ssb.BeginRead("mptm", Version::Current().GetRawVersion());
1289 		int8 useUTF8Tuning = 0;
1290 		ssb.ReadItem(useUTF8Tuning, "UTF8Tuning");
1291 		mpt::Charset TuningCharset = useUTF8Tuning ? mpt::Charset::UTF8 : GetCharsetInternal();
1292 		ssb.ReadItem(GetTuneSpecificTunings(), "0", [TuningCharset](std::istream &iStrm, CTuningCollection &tc, const std::size_t dummy){ return ReadTuningCollection(iStrm, tc, dummy, TuningCharset); });
1293 		ssb.ReadItem(*this, "1", [TuningCharset](std::istream& iStrm, CSoundFile& csf, const std::size_t dummy){ return ReadTuningMap(iStrm, csf, dummy, TuningCharset); });
1294 		ssb.ReadItem(Order, "2", &ReadModSequenceOld);
1295 		ssb.ReadItem(Patterns, FileIdPatterns, &ReadModPatterns);
1296 		mpt::Charset sequenceDefaultCharset = GetCharsetInternal();
1297 		ssb.ReadItem(Order, FileIdSequences, [sequenceDefaultCharset](std::istream &iStrm, ModSequenceSet &seq, std::size_t nSize){ return ReadModSequences(iStrm, seq, nSize, sequenceDefaultCharset); });
1298 
1299 		if(ssb.GetStatus() & srlztn::SNT_FAILURE)
1300 		{
1301 			AddToLog(LogError, U_("Unknown error occurred while deserializing file."));
1302 		}
1303 	} else
1304 	{
1305 		// Loading for older files.
1306 		mpt::ustring name;
1307 		if(GetTuneSpecificTunings().Deserialize(iStrm, name, GetCharsetInternal()) != Tuning::SerializationResult::Success)
1308 		{
1309 			AddToLog(LogError, U_("Loading tune specific tunings failed."));
1310 		} else
1311 		{
1312 			ReadTuningMapImpl(iStrm, *this, GetCharsetInternal(), 0, cwtv < 0x88C);
1313 		}
1314 	}
1315 }
1316 
1317 
1318 #ifndef MODPLUG_NO_FILESAVE
1319 
1320 // Save edit history. Pass a null pointer for *f to retrieve the number of bytes that would be written.
SaveITEditHistory(const CSoundFile & sndFile,std::ostream * file)1321 static uint32 SaveITEditHistory(const CSoundFile &sndFile, std::ostream *file)
1322 {
1323 	size_t num = sndFile.GetFileHistory().size();
1324 #ifdef MODPLUG_TRACKER
1325 	const CModDoc *pModDoc = sndFile.GetpModDoc();
1326 	num += (pModDoc != nullptr) ? 1 : 0;	// + 1 for this session
1327 #endif // MODPLUG_TRACKER
1328 
1329 	uint16 fnum = mpt::saturate_cast<uint16>(num);	// Number of entries that are actually going to be written
1330 	const uint32 bytesWritten = 2 + fnum * 8;		// Number of bytes that are actually going to be written
1331 
1332 	if(!file)
1333 	{
1334 		return bytesWritten;
1335 	}
1336 	std::ostream & f = *file;
1337 
1338 	// Write number of history entries
1339 	mpt::IO::WriteIntLE(f, fnum);
1340 
1341 	// Write history data
1342 	const size_t start = (num > uint16_max) ? num - uint16_max : 0;
1343 	for(size_t n = start; n < num; n++)
1344 	{
1345 		FileHistory mptHistory;
1346 
1347 #ifdef MODPLUG_TRACKER
1348 		if(n < sndFile.GetFileHistory().size())
1349 #endif // MODPLUG_TRACKER
1350 		{
1351 			// Previous timestamps
1352 			mptHistory = sndFile.GetFileHistory()[n];
1353 #ifdef MODPLUG_TRACKER
1354 		} else if(pModDoc != nullptr)
1355 		{
1356 			// Current ("new") timestamp
1357 			const time_t creationTime = pModDoc->GetCreationTime();
1358 
1359 			MemsetZero(mptHistory.loadDate);
1360 			//localtime_s(&loadDate, &creationTime);
1361 			const tm* const p = localtime(&creationTime);
1362 			if (p != nullptr)
1363 				mptHistory.loadDate = *p;
1364 			else
1365 				sndFile.AddToLog(LogError, U_("Unable to retrieve current time."));
1366 
1367 			mptHistory.openTime = (uint32)(difftime(time(nullptr), creationTime) * HISTORY_TIMER_PRECISION);
1368 #endif // MODPLUG_TRACKER
1369 		}
1370 
1371 		ITHistoryStruct itHistory;
1372 		itHistory.ConvertToIT(mptHistory);
1373 		mpt::IO::Write(f, itHistory);
1374 	}
1375 
1376 	return bytesWritten;
1377 }
1378 
1379 
SaveIT(std::ostream & f,const mpt::PathString & filename,bool compatibilityExport)1380 bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool compatibilityExport)
1381 {
1382 
1383 	const CModSpecifications &specs = (GetType() == MOD_TYPE_MPT ? ModSpecs::mptm : (compatibilityExport ? ModSpecs::it : ModSpecs::itEx));
1384 
1385 	uint32 dwChnNamLen;
1386 	ITFileHeader itHeader;
1387 	uint64 dwPos = 0;
1388 	uint32 dwHdrPos = 0, dwExtra = 0;
1389 
1390 	// Writing Header
1391 	MemsetZero(itHeader);
1392 	dwChnNamLen = 0;
1393 	memcpy(itHeader.id, "IMPM", 4);
1394 	mpt::String::WriteBuf(mpt::String::nullTerminated, itHeader.songname) = m_songName;
1395 
1396 	itHeader.highlight_minor = (uint8)std::min(m_nDefaultRowsPerBeat, ROWINDEX(uint8_max));
1397 	itHeader.highlight_major = (uint8)std::min(m_nDefaultRowsPerMeasure, ROWINDEX(uint8_max));
1398 
1399 	if(GetType() == MOD_TYPE_MPT)
1400 	{
1401 		itHeader.ordnum = Order().GetLengthTailTrimmed();
1402 		if(Order().NeedsExtraDatafield() && itHeader.ordnum > 256)
1403 		{
1404 			// If there are more order items, write them elsewhere.
1405 			itHeader.ordnum = 256;
1406 		}
1407 	} else
1408 	{
1409 		// An additional "---" pattern is appended so Impulse Tracker won't ignore the last order item.
1410 		// Interestingly, this can exceed IT's 256 order limit. Also, IT will always save at least two orders.
1411 		itHeader.ordnum = std::min(Order().GetLengthTailTrimmed(), specs.ordersMax) + 1;
1412 		if(itHeader.ordnum < 2)
1413 			itHeader.ordnum = 2;
1414 	}
1415 
1416 	itHeader.insnum = std::min(m_nInstruments, specs.instrumentsMax);
1417 	itHeader.smpnum = std::min(m_nSamples, specs.samplesMax);
1418 	itHeader.patnum = std::min(Patterns.GetNumPatterns(), specs.patternsMax);
1419 
1420 	// Parapointers
1421 	std::vector<uint32le> patpos(itHeader.patnum);
1422 	std::vector<uint32le> smppos(itHeader.smpnum);
1423 	std::vector<uint32le> inspos(itHeader.insnum);
1424 
1425 	//VERSION
1426 	if(GetType() == MOD_TYPE_MPT)
1427 	{
1428 		// MPTM
1429 		itHeader.cwtv = verMptFileVer;	// Used in OMPT-hack versioning.
1430 		itHeader.cmwt = 0x888;
1431 	} else
1432 	{
1433 		// IT
1434 		const uint32 mptVersion = Version::Current().GetRawVersion();
1435 		itHeader.cwtv = 0x5000 | static_cast<uint16>((mptVersion >> 16) & 0x0FFF); // format: txyy (t = tracker ID, x = version major, yy = version minor), e.g. 0x5117 (OpenMPT = 5, 117 = v1.17)
1436 		itHeader.cmwt = 0x0214;	// Common compatible tracker :)
1437 		// Hack from schism tracker:
1438 		for(INSTRUMENTINDEX nIns = 1; nIns <= GetNumInstruments(); nIns++)
1439 		{
1440 			if(Instruments[nIns] && Instruments[nIns]->PitchEnv.dwFlags[ENV_FILTER])
1441 			{
1442 				itHeader.cmwt = 0x0216;
1443 				break;
1444 			}
1445 		}
1446 
1447 		if(compatibilityExport)
1448 			itHeader.reserved = mptVersion & 0xFFFF;
1449 		else
1450 			memcpy(&itHeader.reserved, "OMPT", 4);
1451 	}
1452 
1453 	itHeader.flags = ITFileHeader::useStereoPlayback | ITFileHeader::useMIDIPitchController;
1454 	itHeader.special = ITFileHeader::embedEditHistory | ITFileHeader::embedPatternHighlights;
1455 	if(m_nInstruments) itHeader.flags |= ITFileHeader::instrumentMode;
1456 	if(m_SongFlags[SONG_LINEARSLIDES]) itHeader.flags |= ITFileHeader::linearSlides;
1457 	if(m_SongFlags[SONG_ITOLDEFFECTS]) itHeader.flags |= ITFileHeader::itOldEffects;
1458 	if(m_SongFlags[SONG_ITCOMPATGXX]) itHeader.flags |= ITFileHeader::itCompatGxx;
1459 	if(m_SongFlags[SONG_EXFILTERRANGE] && !compatibilityExport) itHeader.flags |= ITFileHeader::extendedFilterRange;
1460 
1461 	itHeader.globalvol = static_cast<uint8>(m_nDefaultGlobalVolume / 2u);
1462 	itHeader.mv = static_cast<uint8>(std::min(m_nSamplePreAmp, uint32(128)));
1463 	itHeader.speed = mpt::saturate_cast<uint8>(m_nDefaultSpeed);
1464  	itHeader.tempo = mpt::saturate_cast<uint8>(m_nDefaultTempo.GetInt()); // We save the real tempo in an extension below if it exceeds 255.
1465 	itHeader.sep = 128; // pan separation
1466 	// IT doesn't have a per-instrument Pitch Wheel Depth setting, so we just store the first non-zero PWD setting in the header.
1467 	for(INSTRUMENTINDEX ins = 1; ins <= GetNumInstruments(); ins++)
1468 	{
1469 		if(Instruments[ins] != nullptr && Instruments[ins]->midiPWD != 0)
1470 		{
1471 			itHeader.pwd = static_cast<uint8>(std::abs(Instruments[ins]->midiPWD));
1472 			break;
1473 		}
1474 	}
1475 
1476 	dwHdrPos = sizeof(itHeader) + itHeader.ordnum;
1477 	// Channel Pan and Volume
1478 	memset(itHeader.chnpan, 0xA0, 64);
1479 	memset(itHeader.chnvol, 64, 64);
1480 
1481 	for(CHANNELINDEX ich = 0; ich < std::min(m_nChannels, CHANNELINDEX(64)); ich++) // Header only has room for settings for 64 chans...
1482 	{
1483 		itHeader.chnpan[ich] = (uint8)(ChnSettings[ich].nPan >> 2);
1484 		if (ChnSettings[ich].dwFlags[CHN_SURROUND]) itHeader.chnpan[ich] = 100;
1485 		itHeader.chnvol[ich] = (uint8)(ChnSettings[ich].nVolume);
1486 #ifdef MODPLUG_TRACKER
1487 		if(TrackerSettings::Instance().MiscSaveChannelMuteStatus)
1488 #endif
1489 		if (ChnSettings[ich].dwFlags[CHN_MUTE]) itHeader.chnpan[ich] |= 0x80;
1490 	}
1491 
1492 	// Channel names
1493 	if(!compatibilityExport)
1494 	{
1495 		for(CHANNELINDEX i = 0; i < m_nChannels; i++)
1496 		{
1497 			if(ChnSettings[i].szName[0])
1498 			{
1499 				dwChnNamLen = (i + 1) * MAX_CHANNELNAME;
1500 			}
1501 		}
1502 		if(dwChnNamLen) dwExtra += dwChnNamLen + 8;
1503 	}
1504 
1505 	if(!m_MidiCfg.IsMacroDefaultSetupUsed())
1506 	{
1507 		itHeader.flags |= ITFileHeader::reqEmbeddedMIDIConfig;
1508 		itHeader.special |= ITFileHeader::embedMIDIConfiguration;
1509 		dwExtra += sizeof(MIDIMacroConfigData);
1510 	}
1511 
1512 	// Pattern Names
1513 	const PATTERNINDEX numNamedPats = compatibilityExport ? 0 : Patterns.GetNumNamedPatterns();
1514 	if(numNamedPats > 0)
1515 	{
1516 		dwExtra += (numNamedPats * MAX_PATTERNNAME) + 8;
1517 	}
1518 
1519 	// Mix Plugins. Just calculate the size of this extra block for now.
1520 	if(!compatibilityExport)
1521 	{
1522 		dwExtra += SaveMixPlugins(nullptr, true);
1523 	}
1524 
1525 	// Edit History. Just calculate the size of this extra block for now.
1526 	dwExtra += SaveITEditHistory(*this, nullptr);
1527 
1528 	// Comments
1529 	uint16 msglength = 0;
1530 	if(!m_songMessage.empty())
1531 	{
1532 		itHeader.special |= ITFileHeader::embedSongMessage;
1533 		itHeader.msglength = msglength = mpt::saturate_cast<uint16>(m_songMessage.length() + 1u);
1534 		itHeader.msgoffset = dwHdrPos + dwExtra + (itHeader.insnum + itHeader.smpnum + itHeader.patnum) * 4;
1535 	}
1536 
1537 	// Write file header
1538 	mpt::IO::Write(f, itHeader);
1539 
1540 	Order().WriteAsByte(f, itHeader.ordnum);
1541 	mpt::IO::Write(f, inspos);
1542 	mpt::IO::Write(f, smppos);
1543 	mpt::IO::Write(f, patpos);
1544 
1545 	// Writing edit history information
1546 	SaveITEditHistory(*this, &f);
1547 
1548 	// Writing midi cfg
1549 	if(itHeader.flags & ITFileHeader::reqEmbeddedMIDIConfig)
1550 	{
1551 		mpt::IO::Write(f, static_cast<MIDIMacroConfigData &>(m_MidiCfg));
1552 	}
1553 
1554 	// Writing pattern names
1555 	if(numNamedPats)
1556 	{
1557 		mpt::IO::WriteRaw(f, "PNAM", 4);
1558 		mpt::IO::WriteIntLE<uint32>(f, numNamedPats * MAX_PATTERNNAME);
1559 
1560 		for(PATTERNINDEX pat = 0; pat < numNamedPats; pat++)
1561 		{
1562 			char name[MAX_PATTERNNAME];
1563 			mpt::String::WriteBuf(mpt::String::maybeNullTerminated, name) = Patterns[pat].GetName();
1564 			mpt::IO::Write(f, name);
1565 		}
1566 	}
1567 
1568 	// Writing channel names
1569 	if(dwChnNamLen && !compatibilityExport)
1570 	{
1571 		mpt::IO::WriteRaw(f, "CNAM", 4);
1572 		mpt::IO::WriteIntLE<uint32>(f, dwChnNamLen);
1573 		uint32 nChnNames = dwChnNamLen / MAX_CHANNELNAME;
1574 		for(uint32 inam = 0; inam < nChnNames; inam++)
1575 		{
1576 			char name[MAX_CHANNELNAME];
1577 			mpt::String::WriteBuf(mpt::String::maybeNullTerminated, name) = ChnSettings[inam].szName;
1578 			mpt::IO::Write(f, name);
1579 		}
1580 	}
1581 
1582 	// Writing mix plugins info
1583 	if(!compatibilityExport)
1584 	{
1585 		SaveMixPlugins(&f, false);
1586 	}
1587 
1588 	// Writing song message
1589 	dwPos = dwHdrPos + dwExtra + (itHeader.insnum + itHeader.smpnum + itHeader.patnum) * 4;
1590 	if(itHeader.special & ITFileHeader::embedSongMessage)
1591 	{
1592 		dwPos += msglength;
1593 		mpt::IO::WriteRaw(f, m_songMessage.c_str(), msglength);
1594 	}
1595 
1596 	// Writing instruments
1597 	const ModInstrument dummyInstr;
1598 	for(INSTRUMENTINDEX nins = 1; nins <= itHeader.insnum; nins++)
1599 	{
1600 		ITInstrumentEx iti;
1601 		uint32 instSize;
1602 
1603 		const ModInstrument &instr = (Instruments[nins] != nullptr) ? *Instruments[nins] : dummyInstr;
1604 		instSize = iti.ConvertToIT(instr, compatibilityExport, *this);
1605 
1606 		// Writing instrument
1607 		inspos[nins - 1] = static_cast<uint32>(dwPos);
1608 		dwPos += instSize;
1609 		mpt::IO::WritePartial(f, iti, instSize);
1610 	}
1611 
1612 	// Writing dummy sample headers (until we know the correct sample data offset)
1613 	ITSample itss;
1614 	MemsetZero(itss);
1615 	for(SAMPLEINDEX smp = 0; smp < itHeader.smpnum; smp++)
1616 	{
1617 		smppos[smp] = static_cast<uint32>(dwPos);
1618 		dwPos += sizeof(ITSample);
1619 		mpt::IO::Write(f, itss);
1620 	}
1621 
1622 	// Writing Patterns
1623 	bool needsMptPatSave = false;
1624 	for(PATTERNINDEX pat = 0; pat < itHeader.patnum; pat++)
1625 	{
1626 		uint32 dwPatPos = static_cast<uint32>(dwPos);
1627 		if (!Patterns.IsValidPat(pat)) continue;
1628 
1629 		if(Patterns[pat].GetOverrideSignature())
1630 			needsMptPatSave = true;
1631 
1632 		// Check for empty pattern
1633 		if(Patterns[pat].GetNumRows() == 64 && Patterns.IsPatternEmpty(pat))
1634 		{
1635 			patpos[pat] = 0;
1636 			continue;
1637 		}
1638 
1639 		patpos[pat] = static_cast<uint32>(dwPos);
1640 
1641 		// Write pattern header
1642 		ROWINDEX writeRows = mpt::saturate_cast<uint16>(Patterns[pat].GetNumRows());
1643 		uint16 writeSize = 0;
1644 		uint16le patinfo[4];
1645 		patinfo[0] = 0;
1646 		patinfo[1] = static_cast<uint16>(writeRows);
1647 		patinfo[2] = 0;
1648 		patinfo[3] = 0;
1649 
1650 		mpt::IO::Write(f, patinfo);
1651 		dwPos += 8;
1652 
1653 		struct ChnState { ModCommand lastCmd; uint8 mask = 0xFF; };
1654 		const CHANNELINDEX maxChannels = std::min(specs.channelsMax, GetNumChannels());
1655 		std::vector<ChnState> chnStates(maxChannels);
1656 		// Maximum 7 bytes per cell, plus end of row marker, so this buffer is always large enough to cover one row.
1657 		std::vector<uint8> buf(7 * maxChannels + 1);
1658 
1659 		for(ROWINDEX row = 0; row < writeRows; row++)
1660 		{
1661 			uint32 len = 0;
1662 			const ModCommand *m = Patterns[pat].GetpModCommand(row, 0);
1663 
1664 			for(CHANNELINDEX ch = 0; ch < maxChannels; ch++, m++)
1665 			{
1666 				// Skip mptm-specific notes.
1667 				if(m->IsPcNote())
1668 				{
1669 					needsMptPatSave = true;
1670 					continue;
1671 				}
1672 
1673 				auto &chnState = chnStates[ch];
1674 				uint8 b = 0;
1675 				uint8 command = m->command;
1676 				uint8 param = m->param;
1677 				uint8 vol = 0xFF;
1678 				uint8 note = m->note;
1679 				if (note != NOTE_NONE) b |= 1;
1680 				if (m->IsNote()) note -= NOTE_MIN;
1681 				if (note == NOTE_FADE && GetType() != MOD_TYPE_MPT) note = 0xF6;
1682 				if (m->instr) b |= 2;
1683 				if (m->volcmd != VOLCMD_NONE)
1684 				{
1685 					vol = std::min(m->vol, uint8(9));
1686 					switch(m->volcmd)
1687 					{
1688 					case VOLCMD_VOLUME:         vol = std::min(m->vol, uint8(64)); break;
1689 					case VOLCMD_PANNING:        vol = std::min(m->vol, uint8(64)) + 128; break;
1690 					case VOLCMD_VOLSLIDEUP:     vol += 85; break;
1691 					case VOLCMD_VOLSLIDEDOWN:   vol += 95; break;
1692 					case VOLCMD_FINEVOLUP:      vol += 65; break;
1693 					case VOLCMD_FINEVOLDOWN:    vol += 75; break;
1694 					case VOLCMD_VIBRATODEPTH:   vol += 203; break;
1695 					case VOLCMD_TONEPORTAMENTO: vol += 193; break;
1696 					case VOLCMD_PORTADOWN:      vol += 105; break;
1697 					case VOLCMD_PORTAUP:        vol += 115; break;
1698 					case VOLCMD_VIBRATOSPEED:
1699 						if(command == CMD_NONE)
1700 						{
1701 							// Move unsupported command if possible
1702 							command = CMD_VIBRATO;
1703 							param = std::min(m->vol, uint8(15)) << 4;
1704 							vol = 0xFF;
1705 						} else
1706 						{
1707 							vol = 203;
1708 						}
1709 						break;
1710 					case VOLCMD_OFFSET:
1711 						if(!compatibilityExport)
1712 							vol += 223;
1713 						else
1714 							vol = 0xFF;
1715 						break;
1716 					default: vol = 0xFF;
1717 					}
1718 				}
1719 				if (vol != 0xFF) b |= 4;
1720 				if (command != CMD_NONE)
1721 				{
1722 					S3MSaveConvert(command, param, true, compatibilityExport);
1723 					if (command) b |= 8;
1724 				}
1725 				// Packing information
1726 				if (b)
1727 				{
1728 					// Same note ?
1729 					if (b & 1)
1730 					{
1731 						if ((note == chnState.lastCmd.note) && (chnState.lastCmd.volcmd & 1))
1732 						{
1733 							b &= ~1;
1734 							b |= 0x10;
1735 						} else
1736 						{
1737 							chnState.lastCmd.note = note;
1738 							chnState.lastCmd.volcmd |= 1;
1739 						}
1740 					}
1741 					// Same instrument ?
1742 					if (b & 2)
1743 					{
1744 						if ((m->instr == chnState.lastCmd.instr) && (chnState.lastCmd.volcmd & 2))
1745 						{
1746 							b &= ~2;
1747 							b |= 0x20;
1748 						} else
1749 						{
1750 							chnState.lastCmd.instr = m->instr;
1751 							chnState.lastCmd.volcmd |= 2;
1752 						}
1753 					}
1754 					// Same volume column byte ?
1755 					if (b & 4)
1756 					{
1757 						if ((vol == chnState.lastCmd.vol) && (chnState.lastCmd.volcmd & 4))
1758 						{
1759 							b &= ~4;
1760 							b |= 0x40;
1761 						} else
1762 						{
1763 							chnState.lastCmd.vol = vol;
1764 							chnState.lastCmd.volcmd |= 4;
1765 						}
1766 					}
1767 					// Same command / param ?
1768 					if (b & 8)
1769 					{
1770 						if ((command == chnState.lastCmd.command) && (param == chnState.lastCmd.param) && (chnState.lastCmd.volcmd & 8))
1771 						{
1772 							b &= ~8;
1773 							b |= 0x80;
1774 						} else
1775 						{
1776 							chnState.lastCmd.command = command;
1777 							chnState.lastCmd.param = param;
1778 							chnState.lastCmd.volcmd |= 8;
1779 						}
1780 					}
1781 					if (b != chnState.mask)
1782 					{
1783 						chnState.mask = b;
1784 						buf[len++] = static_cast<uint8>((ch + 1) | IT_bitmask_patternChanEnabled_c);
1785 						buf[len++] = b;
1786 					} else
1787 					{
1788 						buf[len++] = static_cast<uint8>(ch + 1);
1789 					}
1790 					if (b & 1) buf[len++] = note;
1791 					if (b & 2) buf[len++] = m->instr;
1792 					if (b & 4) buf[len++] = vol;
1793 					if (b & 8)
1794 					{
1795 						buf[len++] = command;
1796 						buf[len++] = param;
1797 					}
1798 				}
1799 			}
1800 			buf[len++] = 0;
1801 			if(writeSize > uint16_max - len)
1802 			{
1803 				AddToLog(LogWarning, MPT_UFORMAT("Warning: File format limit was reached. Some pattern data may not get written to file. (pattern {})")(pat));
1804 				break;
1805 			} else
1806 			{
1807 				dwPos += len;
1808 				writeSize += (uint16)len;
1809 				mpt::IO::WriteRaw(f, buf.data(), len);
1810 			}
1811 		}
1812 
1813 		mpt::IO::SeekAbsolute(f, dwPatPos);
1814 		patinfo[0] = writeSize;
1815 		mpt::IO::Write(f, patinfo);
1816 		mpt::IO::SeekAbsolute(f, dwPos);
1817 	}
1818 	// Writing Sample Data
1819 	for(SAMPLEINDEX smp = 1; smp <= itHeader.smpnum; smp++)
1820 	{
1821 		const ModSample &sample = Samples[smp];
1822 #ifdef MODPLUG_TRACKER
1823 		uint32 type = GetType() == MOD_TYPE_IT ? 1 : 4;
1824 		if(compatibilityExport) type = 2;
1825 		bool compress = ((((sample.GetNumChannels() > 1) ? TrackerSettings::Instance().MiscITCompressionStereo : TrackerSettings::Instance().MiscITCompressionMono) & type) != 0);
1826 #else
1827 		bool compress = false;
1828 #endif // MODPLUG_TRACKER
1829 		// Old MPT, DUMB and probably other libraries will only consider the IT2.15 compression flag if the header version also indicates IT2.15.
1830 		// MilkyTracker <= 0.90.85 assumes IT2.15 compression with cmwt == 0x215, ignoring the delta flag completely.
1831 		itss.ConvertToIT(sample, GetType(), compress, itHeader.cmwt >= 0x215, GetType() == MOD_TYPE_MPT);
1832 		const bool isExternal = itss.cvt == ITSample::cvtExternalSample;
1833 
1834 		mpt::String::WriteBuf(mpt::String::nullTerminated, itss.name) = m_szNames[smp];
1835 
1836 		itss.samplepointer = static_cast<uint32>(dwPos);
1837 		if(dwPos > uint32_max)
1838 		{
1839 			// Sample position does not fit into sample pointer!
1840 			AddToLog(LogWarning, MPT_UFORMAT("Cannot save sample {}: File size exceeds 4 GB.")(smp));
1841 			itss.samplepointer = 0;
1842 			itss.length = 0;
1843 		}
1844 		SmpLength smpLength = itss.length;	// Possibly truncated to 2^32 samples
1845 		mpt::IO::SeekAbsolute(f, smppos[smp - 1]);
1846 		mpt::IO::Write(f, itss);
1847 		if(dwPos > uint32_max)
1848 		{
1849 			continue;
1850 		}
1851 		// TODO this actually wraps around at 2 GB, so we either need to use the 64-bit seek API or warn earlier!
1852 		mpt::IO::SeekAbsolute(f, dwPos);
1853 		if(!isExternal)
1854 		{
1855 			if(sample.nLength > smpLength && smpLength != 0)
1856 			{
1857 				// Sample length does not fit into IT header!
1858 				AddToLog(LogWarning, MPT_UFORMAT("Truncating sample {}: Length exceeds exceeds 4 gigasamples.")(smp));
1859 			}
1860 			dwPos += itss.GetSampleFormat().WriteSample(f, sample, smpLength);
1861 		} else
1862 		{
1863 #ifdef MPT_EXTERNAL_SAMPLES
1864 			const std::string filenameU8 = GetSamplePath(smp).AbsolutePathToRelative(filename.GetPath()).ToUTF8();
1865 			const size_t strSize = filenameU8.size();
1866 			size_t intBytes = 0;
1867 			if(mpt::IO::WriteVarInt(f, strSize, &intBytes))
1868 			{
1869 				dwPos += intBytes + strSize;
1870 				mpt::IO::WriteRaw(f, filenameU8.data(), strSize);
1871 			}
1872 #else
1873 			MPT_UNREFERENCED_PARAMETER(filename);
1874 #endif // MPT_EXTERNAL_SAMPLES
1875 		}
1876 	}
1877 
1878 	//Save hacked-on extra info
1879 	if(!compatibilityExport)
1880 	{
1881 		if(GetNumInstruments())
1882 		{
1883 			SaveExtendedInstrumentProperties(itHeader.insnum, f);
1884 		}
1885 		SaveExtendedSongProperties(f);
1886 	}
1887 
1888 	// Updating offsets
1889 	mpt::IO::SeekAbsolute(f, dwHdrPos);
1890 	mpt::IO::Write(f, inspos);
1891 	mpt::IO::Write(f, smppos);
1892 	mpt::IO::Write(f, patpos);
1893 
1894 	if(GetType() == MOD_TYPE_IT)
1895 	{
1896 		return true;
1897 	}
1898 
1899 	//hack
1900 	//BEGIN: MPT SPECIFIC:
1901 
1902 	bool success = true;
1903 
1904 	mpt::IO::SeekEnd(f);
1905 
1906 	const mpt::IO::Offset MPTStartPos = mpt::IO::TellWrite(f);
1907 
1908 	srlztn::SsbWrite ssb(f);
1909 	ssb.BeginWrite("mptm", Version::Current().GetRawVersion());
1910 
1911 	if(GetTuneSpecificTunings().GetNumTunings() > 0 || AreNonDefaultTuningsUsed(*this))
1912 		ssb.WriteItem(int8(1), "UTF8Tuning");
1913 	if(GetTuneSpecificTunings().GetNumTunings() > 0)
1914 		ssb.WriteItem(GetTuneSpecificTunings(), "0", &WriteTuningCollection);
1915 	if(AreNonDefaultTuningsUsed(*this))
1916 		ssb.WriteItem(*this, "1", &WriteTuningMap);
1917 	if(Order().NeedsExtraDatafield())
1918 		ssb.WriteItem(Order, "2", &WriteModSequenceOld);
1919 	if(needsMptPatSave)
1920 		ssb.WriteItem(Patterns, FileIdPatterns, &WriteModPatterns);
1921 	ssb.WriteItem(Order, FileIdSequences, &WriteModSequences);
1922 
1923 	ssb.FinishWrite();
1924 
1925 	if(ssb.GetStatus() & srlztn::SNT_FAILURE)
1926 	{
1927 		AddToLog(LogError, U_("Error occurred in writing MPTM extensions."));
1928 	}
1929 
1930 	//Last 4 bytes should tell where the hack mpt things begin.
1931 	if(!f.good())
1932 	{
1933 		f.clear();
1934 		success = false;
1935 	}
1936 	mpt::IO::WriteIntLE<uint32>(f, static_cast<uint32>(MPTStartPos));
1937 
1938 	mpt::IO::SeekEnd(f);
1939 
1940 	//END  : MPT SPECIFIC
1941 
1942 	//NO WRITING HERE ANYMORE.
1943 
1944 	return success;
1945 }
1946 
1947 
1948 #endif // MODPLUG_NO_FILESAVE
1949 
1950 
1951 #ifndef MODPLUG_NO_FILESAVE
1952 
SaveMixPlugins(std::ostream * file,bool updatePlugData)1953 uint32 CSoundFile::SaveMixPlugins(std::ostream *file, bool updatePlugData)
1954 {
1955 #ifndef NO_PLUGINS
1956 	uint32 totalSize = 0;
1957 
1958 	for(PLUGINDEX i = 0; i < MAX_MIXPLUGINS; i++)
1959 	{
1960 		const SNDMIXPLUGIN &plugin = m_MixPlugins[i];
1961 		if(plugin.IsValidPlugin())
1962 		{
1963 			uint32 chunkSize = sizeof(SNDMIXPLUGININFO) + 4; // plugininfo+4 (datalen)
1964 			if(plugin.pMixPlugin && updatePlugData)
1965 			{
1966 				plugin.pMixPlugin->SaveAllParameters();
1967 			}
1968 
1969 			const uint32 extraDataSize =
1970 				4 + sizeof(float32) + // 4 for ID and size of dryRatio
1971 				4 + sizeof(int32);    // Default Program
1972 			// For each extra entity, add 4 for ID, plus 4 for size of entity, plus size of entity
1973 
1974 			chunkSize += extraDataSize + 4; // +4 is for size field itself
1975 
1976 			const uint32 plugDataSize = std::min(mpt::saturate_cast<uint32>(plugin.pluginData.size()), uint32_max - chunkSize);
1977 			chunkSize += plugDataSize;
1978 
1979 			if(file)
1980 			{
1981 				std::ostream &f = *file;
1982 				// Chunk ID (= plugin ID)
1983 				char id[4] = { 'F', 'X', '0', '0' };
1984 				if(i >= 100) id[1] = '0' + (i / 100u);
1985 				id[2] += (i / 10u) % 10u;
1986 				id[3] += (i % 10u);
1987 				mpt::IO::WriteRaw(f, id, 4);
1988 
1989 				// Write chunk size, plugin info and plugin data chunk
1990 				mpt::IO::WriteIntLE<uint32>(f, chunkSize);
1991 				mpt::IO::Write(f, m_MixPlugins[i].Info);
1992 				mpt::IO::WriteIntLE<uint32>(f, plugDataSize);
1993 				if(plugDataSize)
1994 				{
1995 					mpt::IO::WriteRaw(f, m_MixPlugins[i].pluginData.data(), plugDataSize);
1996 				}
1997 
1998 				mpt::IO::WriteIntLE<uint32>(f, extraDataSize);
1999 
2000 				// Dry/Wet ratio
2001 				mpt::IO::WriteRaw(f, "DWRT", 4);
2002 				// DWRT chunk does not include a size, so better make sure we always write 4 bytes here.
2003 				static_assert(sizeof(IEEE754binary32LE) == 4);
2004 				mpt::IO::Write(f, IEEE754binary32LE(m_MixPlugins[i].fDryRatio));
2005 
2006 				// Default program
2007 				mpt::IO::WriteRaw(f, "PROG", 4);
2008 				// PROG chunk does not include a size, so better make sure we always write 4 bytes here.
2009 				static_assert(sizeof(m_MixPlugins[i].defaultProgram) == sizeof(int32));
2010 				mpt::IO::WriteIntLE<int32>(f, m_MixPlugins[i].defaultProgram);
2011 
2012 				// Please, if you add any more chunks here, don't repeat history (see above) and *do* add a size field for your chunk, mmmkay?
2013 			}
2014 			totalSize += chunkSize + 8;
2015 		}
2016 	}
2017 	std::vector<uint32le> chinfo(GetNumChannels());
2018 	uint32 numChInfo = 0;
2019 	for(CHANNELINDEX j = 0; j < GetNumChannels(); j++)
2020 	{
2021 		if((chinfo[j] = ChnSettings[j].nMixPlugin) != 0)
2022 		{
2023 			numChInfo = j + 1;
2024 		}
2025 	}
2026 	if(numChInfo)
2027 	{
2028 		if(file)
2029 		{
2030 			std::ostream &f = *file;
2031 			mpt::IO::WriteRaw(f, "CHFX", 4);
2032 			mpt::IO::WriteIntLE<uint32>(f, numChInfo * 4);
2033 			chinfo.resize(numChInfo);
2034 			mpt::IO::Write(f, chinfo);
2035 		}
2036 		totalSize += numChInfo * 4 + 8;
2037 	}
2038 	return totalSize;
2039 #else
2040 	MPT_UNREFERENCED_PARAMETER(file);
2041 	MPT_UNREFERENCED_PARAMETER(updatePlugData);
2042 	return 0;
2043 #endif // NO_PLUGINS
2044 }
2045 
2046 #endif // MODPLUG_NO_FILESAVE
2047 
2048 
LoadMixPlugins(FileReader & file)2049 bool CSoundFile::LoadMixPlugins(FileReader &file)
2050 {
2051 	bool isBeRoTracker = false;
2052 	while(file.CanRead(9))
2053 	{
2054 		char code[4];
2055 		file.ReadArray(code);
2056 		const uint32 chunkSize = file.ReadUint32LE();
2057 		if(!memcmp(code, "IMPI", 4)     // IT instrument, we definitely read too far
2058 		   || !memcmp(code, "IMPS", 4)  // IT sample, ditto
2059 		   || !memcmp(code, "XTPM", 4)  // Instrument extensions, ditto
2060 		   || !memcmp(code, "STPM", 4)  // Song extensions, ditto
2061 		   || !file.CanRead(chunkSize))
2062 		{
2063 			file.SkipBack(8);
2064 			return isBeRoTracker;
2065 		}
2066 		FileReader chunk = file.ReadChunk(chunkSize);
2067 
2068 		// Channel FX
2069 		if(!memcmp(code, "CHFX", 4))
2070 		{
2071 			for(auto &chn : ChnSettings)
2072 			{
2073 				chn.nMixPlugin = static_cast<PLUGINDEX>(chunk.ReadUint32LE());
2074 			}
2075 #ifndef NO_PLUGINS
2076 		}
2077 		// Plugin Data FX00, ... FX99, F100, ... F255
2078 #define MPT_ISDIGIT(x) (code[(x)] >= '0' && code[(x)] <= '9')
2079 		else if(code[0] == 'F' && (code[1] == 'X' || MPT_ISDIGIT(1)) && MPT_ISDIGIT(2) && MPT_ISDIGIT(3))
2080 #undef MPT_ISDIGIT
2081 		{
2082 			PLUGINDEX plug = (code[2] - '0') * 10 + (code[3] - '0');	//calculate plug-in number.
2083 			if(code[1] != 'X') plug += (code[1] - '0') * 100;
2084 
2085 			if(plug < MAX_MIXPLUGINS)
2086 			{
2087 				ReadMixPluginChunk(chunk, m_MixPlugins[plug]);
2088 			}
2089 #endif // NO_PLUGINS
2090 		} else if(!memcmp(code, "MODU", 4))
2091 		{
2092 			isBeRoTracker = true;
2093 			m_dwLastSavedWithVersion = Version();	// Reset MPT detection for old files that have a similar fingerprint
2094 		}
2095 	}
2096 	return isBeRoTracker;
2097 }
2098 
2099 
2100 #ifndef NO_PLUGINS
ReadMixPluginChunk(FileReader & file,SNDMIXPLUGIN & plugin)2101 void CSoundFile::ReadMixPluginChunk(FileReader &file, SNDMIXPLUGIN &plugin)
2102 {
2103 	// MPT's standard plugin data. Size not specified in file.. grrr..
2104 	file.ReadStruct(plugin.Info);
2105 	mpt::String::SetNullTerminator(plugin.Info.szName.buf);
2106 	mpt::String::SetNullTerminator(plugin.Info.szLibraryName.buf);
2107 	plugin.editorX = plugin.editorY = int32_min;
2108 
2109 	// Plugin user data
2110 	FileReader pluginDataChunk = file.ReadChunk(file.ReadUint32LE());
2111 	plugin.pluginData.resize(mpt::saturate_cast<size_t>(pluginDataChunk.BytesLeft()));
2112 	pluginDataChunk.ReadRaw(mpt::as_span(plugin.pluginData));
2113 
2114 	if(FileReader modularData = file.ReadChunk(file.ReadUint32LE()); modularData.IsValid())
2115 	{
2116 		while(modularData.CanRead(5))
2117 		{
2118 			// do we recognize this chunk?
2119 			char code[4];
2120 			modularData.ReadArray(code);
2121 			uint32 dataSize = 0;
2122 			if(!memcmp(code, "DWRT", 4) || !memcmp(code, "PROG", 4))
2123 			{
2124 				// Legacy system with fixed size chunks
2125 				dataSize = 4;
2126 			} else
2127 			{
2128 				dataSize = modularData.ReadUint32LE();
2129 			}
2130 			FileReader dataChunk = modularData.ReadChunk(dataSize);
2131 
2132 			if(!memcmp(code, "DWRT", 4))
2133 			{
2134 				plugin.fDryRatio = std::clamp(dataChunk.ReadFloatLE(), 0.0f, 1.0f);
2135 				if(!std::isnormal(plugin.fDryRatio))
2136 					plugin.fDryRatio = 0.0f;
2137 			} else if(!memcmp(code, "PROG", 4))
2138 			{
2139 				plugin.defaultProgram = dataChunk.ReadUint32LE();
2140 			} else if(!memcmp(code, "MCRO", 4))
2141 			{
2142 				// Read plugin-specific macros
2143 				//dataChunk.ReadStructPartial(plugin.macros, dataChunk.GetLength());
2144 			}
2145 		}
2146 	}
2147 }
2148 #endif // NO_PLUGINS
2149 
2150 
2151 #ifndef MODPLUG_NO_FILESAVE
2152 
SaveExtendedSongProperties(std::ostream & f) const2153 void CSoundFile::SaveExtendedSongProperties(std::ostream &f) const
2154 {
2155 	const CModSpecifications &specs = GetModSpecifications();
2156 	// Extra song data - Yet Another Hack.
2157 	mpt::IO::WriteIntLE<uint32>(f, MagicBE("MPTS"));
2158 
2159 #define WRITEMODULARHEADER(code, fsize) \
2160 	{ \
2161 		mpt::IO::WriteIntLE<uint32>(f, code); \
2162 		MPT_ASSERT(mpt::in_range<uint16>(fsize)); \
2163 		const uint16 _size = fsize; \
2164 		mpt::IO::WriteIntLE<uint16>(f, _size); \
2165 	}
2166 #define WRITEMODULAR(code, field) \
2167 	{ \
2168 		WRITEMODULARHEADER(code, sizeof(field)) \
2169 		mpt::IO::WriteIntLE(f, field); \
2170 	}
2171 
2172 	if(m_nDefaultTempo.GetInt() > 255)
2173 	{
2174 		uint32 tempo = m_nDefaultTempo.GetInt();
2175 		WRITEMODULAR(MagicBE("DT.."), tempo);
2176 	}
2177 	if(m_nDefaultTempo.GetFract() != 0 && specs.hasFractionalTempo)
2178 	{
2179 		uint32 tempo = m_nDefaultTempo.GetFract();
2180 		WRITEMODULAR(MagicLE("DTFR"), tempo);
2181 	}
2182 
2183 	if(m_nDefaultRowsPerBeat > 255 || m_nDefaultRowsPerMeasure > 255 || GetType() == MOD_TYPE_XM)
2184 	{
2185 		WRITEMODULAR(MagicBE("RPB."), m_nDefaultRowsPerBeat);
2186 		WRITEMODULAR(MagicBE("RPM."), m_nDefaultRowsPerMeasure);
2187 	}
2188 
2189 	if(GetType() != MOD_TYPE_XM)
2190 	{
2191 		WRITEMODULAR(MagicBE("C..."), m_nChannels);
2192 	}
2193 
2194 	if((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && GetNumChannels() > 64)
2195 	{
2196 		// IT header has only room for 64 channels. Save the settings that do not fit to the header here as an extension.
2197 		WRITEMODULARHEADER(MagicBE("ChnS"), (GetNumChannels() - 64) * 2);
2198 		for(CHANNELINDEX chn = 64; chn < GetNumChannels(); chn++)
2199 		{
2200 			uint8 panvol[2];
2201 			panvol[0] = (uint8)(ChnSettings[chn].nPan >> 2);
2202 			if (ChnSettings[chn].dwFlags[CHN_SURROUND]) panvol[0] = 100;
2203 			if (ChnSettings[chn].dwFlags[CHN_MUTE]) panvol[0] |= 0x80;
2204 			panvol[1] = (uint8)ChnSettings[chn].nVolume;
2205 			mpt::IO::Write(f, panvol);
2206 		}
2207 	}
2208 
2209 	{
2210 		WRITEMODULARHEADER(MagicBE("TM.."), 1);
2211 		uint8 mode = static_cast<uint8>(m_nTempoMode);
2212 		mpt::IO::WriteIntLE(f, mode);
2213 	}
2214 
2215 	const int32 tmpMixLevels = static_cast<int32>(m_nMixLevels);
2216 	WRITEMODULAR(MagicBE("PMM."), tmpMixLevels);
2217 
2218 	if(m_dwCreatedWithVersion)
2219 	{
2220 		WRITEMODULAR(MagicBE("CWV."), m_dwCreatedWithVersion.GetRawVersion());
2221 	}
2222 
2223 	WRITEMODULAR(MagicBE("LSWV"), Version::Current().GetRawVersion());
2224 	WRITEMODULAR(MagicBE("SPA."), m_nSamplePreAmp);
2225 	WRITEMODULAR(MagicBE("VSTV"), m_nVSTiVolume);
2226 
2227 	if(GetType() == MOD_TYPE_XM && m_nDefaultGlobalVolume != MAX_GLOBAL_VOLUME)
2228 	{
2229 		WRITEMODULAR(MagicBE("DGV."), m_nDefaultGlobalVolume);
2230 	}
2231 
2232 	if(GetType() != MOD_TYPE_XM && Order().GetRestartPos() != 0)
2233 	{
2234 		WRITEMODULAR(MagicBE("RP.."), Order().GetRestartPos());
2235 	}
2236 
2237 	if(m_nResampling != SRCMODE_DEFAULT && specs.hasDefaultResampling)
2238 	{
2239 		WRITEMODULAR(MagicLE("RSMP"), static_cast<uint32>(m_nResampling));
2240 	}
2241 
2242 	// Sample cues
2243 	if(GetType() == MOD_TYPE_MPT)
2244 	{
2245 		for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
2246 		{
2247 			const ModSample &sample = Samples[smp];
2248 			if(sample.nLength && sample.HasCustomCuePoints())
2249 			{
2250 				// Write one chunk for every sample.
2251 				// Rationale: chunks are limited to 65536 bytes, which can easily be reached
2252 				// with the amount of samples that OpenMPT supports.
2253 				WRITEMODULARHEADER(MagicLE("CUES"), static_cast<uint16>(2 + std::size(sample.cues) * 4));
2254 				mpt::IO::WriteIntLE<uint16>(f, smp);
2255 				for(auto cue : sample.cues)
2256 				{
2257 					mpt::IO::WriteIntLE<uint32>(f, cue);
2258 				}
2259 			}
2260 		}
2261 	}
2262 
2263 	// Tempo Swing Factors
2264 	if(!m_tempoSwing.empty())
2265 	{
2266 		std::ostringstream oStrm;
2267 		TempoSwing::Serialize(oStrm, m_tempoSwing);
2268 		std::string data = oStrm.str();
2269 		uint16 length = mpt::saturate_cast<uint16>(data.size());
2270 		WRITEMODULARHEADER(MagicLE("SWNG"), length);
2271 		mpt::IO::WriteRaw(f, data.data(), length);
2272 	}
2273 
2274 	// Playback compatibility flags
2275 	{
2276 		uint8 bits[(kMaxPlayBehaviours + 7) / 8u];
2277 		MemsetZero(bits);
2278 		size_t maxBit = 0;
2279 		for(size_t i = 0; i < kMaxPlayBehaviours; i++)
2280 		{
2281 			if(m_playBehaviour[i])
2282 			{
2283 				bits[i >> 3] |= 1 << (i & 0x07);
2284 				maxBit = i + 8;
2285 			}
2286 		}
2287 		uint16 numBytes = static_cast<uint16>(maxBit / 8u);
2288 		WRITEMODULARHEADER(MagicBE("MSF."), numBytes);
2289 		mpt::IO::WriteRaw(f, bits, numBytes);
2290 	}
2291 
2292 	if(!m_songArtist.empty() && specs.hasArtistName)
2293 	{
2294 		std::string songArtistU8 = mpt::ToCharset(mpt::Charset::UTF8, m_songArtist);
2295 		uint16 length = mpt::saturate_cast<uint16>(songArtistU8.length());
2296 		WRITEMODULARHEADER(MagicLE("AUTH"), length);
2297 		mpt::IO::WriteRaw(f, songArtistU8.c_str(), length);
2298 	}
2299 
2300 #ifdef MODPLUG_TRACKER
2301 	// MIDI mapping directives
2302 	if(GetMIDIMapper().GetCount() > 0)
2303 	{
2304 		const size_t objectsize = GetMIDIMapper().Serialize();
2305 		if(!mpt::in_range<uint16>(objectsize))
2306 		{
2307 			AddToLog(LogWarning, U_("Too many MIDI Mapping directives to save; data won't be written."));
2308 		} else
2309 		{
2310 			WRITEMODULARHEADER(MagicBE("MIMA"), static_cast<uint16>(objectsize));
2311 			GetMIDIMapper().Serialize(&f);
2312 		}
2313 	}
2314 
2315 	// Channel colors
2316 	{
2317 		CHANNELINDEX numChannels = 0;
2318 		for(CHANNELINDEX i = 0; i < m_nChannels; i++)
2319 		{
2320 			if(ChnSettings[i].color != ModChannelSettings::INVALID_COLOR)
2321 			{
2322 				numChannels = i + 1;
2323 			}
2324 		}
2325 		if(numChannels > 0)
2326 		{
2327 			WRITEMODULARHEADER(MagicLE("CCOL"), numChannels * 4);
2328 			for(CHANNELINDEX i = 0; i < numChannels; i++)
2329 			{
2330 				uint32 color = ChnSettings[i].color;
2331 				if(color != ModChannelSettings::INVALID_COLOR)
2332 					color &= 0x00FFFFFF;
2333 				std::array<uint8, 4> rgb{static_cast<uint8>(color), static_cast<uint8>(color >> 8), static_cast<uint8>(color >> 16), static_cast<uint8>(color >> 24)};
2334 				mpt::IO::Write(f, rgb);
2335 			}
2336 		}
2337 	}
2338 #endif
2339 
2340 #undef WRITEMODULAR
2341 #undef WRITEMODULARHEADER
2342 	return;
2343 }
2344 
2345 #endif // MODPLUG_NO_FILESAVE
2346 
2347 
2348 template<typename T>
ReadField(FileReader & chunk,std::size_t size,T & field)2349 void ReadField(FileReader &chunk, std::size_t size, T &field)
2350 {
2351 	field = chunk.ReadSizedIntLE<T>(size);
2352 }
2353 
2354 
2355 template<typename T>
ReadFieldCast(FileReader & chunk,std::size_t size,T & field)2356 void ReadFieldCast(FileReader &chunk, std::size_t size, T &field)
2357 {
2358 	static_assert(sizeof(T) <= sizeof(int32));
2359 	field = static_cast<T>(chunk.ReadSizedIntLE<int32>(size));
2360 }
2361 
2362 
LoadExtendedSongProperties(FileReader & file,bool ignoreChannelCount,bool * pInterpretMptMade)2363 void CSoundFile::LoadExtendedSongProperties(FileReader &file, bool ignoreChannelCount, bool *pInterpretMptMade)
2364 {
2365 	if(!file.ReadMagic("STPM"))	// 'MPTS'
2366 	{
2367 		return;
2368 	}
2369 
2370 	// Found MPTS, interpret the file MPT made.
2371 	if(pInterpretMptMade != nullptr)
2372 		*pInterpretMptMade = true;
2373 
2374 	// HACK: Reset mod flags to default values here, as they are not always written.
2375 	m_playBehaviour.reset();
2376 
2377 	while(file.CanRead(7))
2378 	{
2379 		const uint32 code = file.ReadUint32LE();
2380 		const uint16 size = file.ReadUint16LE();
2381 
2382 		// Start of MPTM extensions, non-ASCII ID or truncated field
2383 		if(code == MagicLE("228\x04"))
2384 		{
2385 			file.SkipBack(6);
2386 			break;
2387 		} else if((code & 0x80808080) || !(code & 0x60606060) || !file.CanRead(size))
2388 		{
2389 			break;
2390 		}
2391 
2392 		FileReader chunk = file.ReadChunk(size);
2393 
2394 		switch (code)					// interpret field code
2395 		{
2396 			case MagicBE("DT.."): { uint32 tempo; ReadField(chunk, size, tempo); m_nDefaultTempo.Set(tempo, m_nDefaultTempo.GetFract()); break; }
2397 			case MagicLE("DTFR"): { uint32 tempoFract; ReadField(chunk, size, tempoFract); m_nDefaultTempo.Set(m_nDefaultTempo.GetInt(), tempoFract); break; }
2398 			case MagicBE("RPB."): ReadField(chunk, size, m_nDefaultRowsPerBeat); break;
2399 			case MagicBE("RPM."): ReadField(chunk, size, m_nDefaultRowsPerMeasure); break;
2400 				// FIXME: If there are only PC events on the last few channels in an MPTM MO3, they won't be imported!
2401 			case MagicBE("C..."): if(!ignoreChannelCount) { CHANNELINDEX chn = 0; ReadField(chunk, size, chn); m_nChannels = Clamp(chn, m_nChannels, MAX_BASECHANNELS); } break;
2402 			case MagicBE("TM.."): ReadFieldCast(chunk, size, m_nTempoMode); break;
2403 			case MagicBE("PMM."): ReadFieldCast(chunk, size, m_nMixLevels); break;
2404 			case MagicBE("CWV."): { uint32 ver = 0; ReadField(chunk, size, ver); m_dwCreatedWithVersion = Version(ver); break; }
2405 			case MagicBE("LSWV"): { uint32 ver = 0; ReadField(chunk, size, ver); if(ver != 0) { m_dwLastSavedWithVersion = Version(ver); } break; }
2406 			case MagicBE("SPA."): ReadField(chunk, size, m_nSamplePreAmp); break;
2407 			case MagicBE("VSTV"): ReadField(chunk, size, m_nVSTiVolume); break;
2408 			case MagicBE("DGV."): ReadField(chunk, size, m_nDefaultGlobalVolume); break;
2409 			case MagicBE("RP.."): if(GetType() != MOD_TYPE_XM) { ORDERINDEX restartPos; ReadField(chunk, size, restartPos); Order().SetRestartPos(restartPos); } break;
2410 			case MagicLE("RSMP"):
2411 				ReadFieldCast(chunk, size, m_nResampling);
2412 				if(!Resampling::IsKnownMode(m_nResampling)) m_nResampling = SRCMODE_DEFAULT;
2413 				break;
2414 #ifdef MODPLUG_TRACKER
2415 			case MagicBE("MIMA"): GetMIDIMapper().Deserialize(chunk); break;
2416 
2417 			case MagicLE("CCOL"):
2418 				// Channel colors
2419 				{
2420 					const CHANNELINDEX numChannels = std::min(MAX_BASECHANNELS, static_cast<CHANNELINDEX>(size / 4u));
2421 					for(CHANNELINDEX i = 0; i < numChannels; i++)
2422 					{
2423 						auto rgb = chunk.ReadArray<uint8, 4>();
2424 						if(rgb[3])
2425 							ChnSettings[i].color = ModChannelSettings::INVALID_COLOR;
2426 						else
2427 							ChnSettings[i].color = rgb[0] | (rgb[1] << 8) | (rgb[2] << 16);
2428 					}
2429 				}
2430 				break;
2431 #endif
2432 			case MagicLE("AUTH"):
2433 				{
2434 					std::string artist;
2435 					chunk.ReadString<mpt::String::spacePadded>(artist, chunk.GetLength());
2436 					m_songArtist = mpt::ToUnicode(mpt::Charset::UTF8, artist);
2437 				}
2438 				break;
2439 			case MagicBE("ChnS"):
2440 				// Channel settings for channels 65+
2441 				if(size <= (MAX_BASECHANNELS - 64) * 2 && (size % 2u) == 0)
2442 				{
2443 					static_assert(mpt::array_size<decltype(ChnSettings)>::size >= 64);
2444 					const CHANNELINDEX loopLimit = std::min(uint16(64 + size / 2), uint16(std::size(ChnSettings)));
2445 
2446 					for(CHANNELINDEX chn = 64; chn < loopLimit; chn++)
2447 					{
2448 						auto [pan, vol] = chunk.ReadArray<uint8, 2>();
2449 						if(pan != 0xFF)
2450 						{
2451 							ChnSettings[chn].nVolume = vol;
2452 							ChnSettings[chn].nPan = 128;
2453 							ChnSettings[chn].dwFlags.reset();
2454 							if(pan & 0x80) ChnSettings[chn].dwFlags.set(CHN_MUTE);
2455 							pan &= 0x7F;
2456 							if(pan <= 64) ChnSettings[chn].nPan = pan << 2;
2457 							if(pan == 100) ChnSettings[chn].dwFlags.set(CHN_SURROUND);
2458 						}
2459 					}
2460 				}
2461 				break;
2462 
2463 			case MagicLE("CUES"):
2464 				// Sample cues
2465 				if(size > 2)
2466 				{
2467 					SAMPLEINDEX smp = chunk.ReadUint16LE();
2468 					if(smp > 0 && smp <= GetNumSamples())
2469 					{
2470 						ModSample &sample = Samples[smp];
2471 						for(auto &cue : sample.cues)
2472 						{
2473 							cue = chunk.ReadUint32LE();
2474 						}
2475 					}
2476 				}
2477 				break;
2478 
2479 			case MagicLE("SWNG"):
2480 				// Tempo Swing Factors
2481 				if(size > 2)
2482 				{
2483 					std::istringstream iStrm(mpt::buffer_cast<std::string>(chunk.ReadRawDataAsByteVector()));
2484 					TempoSwing::Deserialize(iStrm, m_tempoSwing, chunk.GetLength());
2485 				}
2486 				break;
2487 
2488 			case MagicBE("MSF."):
2489 				// Playback compatibility flags
2490 				{
2491 					size_t bit = 0;
2492 					m_playBehaviour.reset();
2493 					while(chunk.CanRead(1) && bit < m_playBehaviour.size())
2494 					{
2495 						uint8 b = chunk.ReadUint8();
2496 						for(uint8 i = 0; i < 8; i++, bit++)
2497 						{
2498 							if((b & (1 << i)) && bit < m_playBehaviour.size())
2499 							{
2500 								m_playBehaviour.set(bit);
2501 							}
2502 						}
2503 					}
2504 				}
2505 				break;
2506 		}
2507 	}
2508 
2509 	// Validate read values.
2510 	Limit(m_nDefaultTempo, GetModSpecifications().GetTempoMin(), GetModSpecifications().GetTempoMax());
2511 	if(m_nTempoMode >= TempoMode::NumModes)
2512 		m_nTempoMode = TempoMode::Classic;
2513 	if(m_nMixLevels >= MixLevels::NumMixLevels)
2514 		m_nMixLevels = MixLevels::Original;
2515 	//m_dwCreatedWithVersion
2516 	//m_dwLastSavedWithVersion
2517 	//m_nSamplePreAmp
2518 	//m_nVSTiVolume
2519 	//m_nDefaultGlobalVolume
2520 	LimitMax(m_nDefaultGlobalVolume, MAX_GLOBAL_VOLUME);
2521 	//m_nRestartPos
2522 	//m_ModFlags
2523 	LimitMax(m_nDefaultRowsPerBeat, MAX_ROWS_PER_BEAT);
2524 	LimitMax(m_nDefaultRowsPerMeasure, MAX_ROWS_PER_BEAT);
2525 }
2526 
2527 
2528 OPENMPT_NAMESPACE_END
2529