1 /*
2  * SampleFormats.cpp
3  * -----------------
4  * Purpose: Code for loading various more or less common sample and instrument formats.
5  * Notes  : (currently none)
6  * Authors: OpenMPT Devs
7  * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
8  */
9 
10 
11 #include "stdafx.h"
12 #include "Sndfile.h"
13 #include "mod_specifications.h"
14 #ifdef MODPLUG_TRACKER
15 #include "../mptrack/Moddoc.h"
16 #include "Dlsbank.h"
17 #endif // MODPLUG_TRACKER
18 #include "../soundlib/AudioCriticalSection.h"
19 #ifndef MODPLUG_NO_FILESAVE
20 #include "mpt/io/base.hpp"
21 #include "mpt/io/io.hpp"
22 #include "mpt/io/io_stdstream.hpp"
23 #include "../common/mptFileIO.h"
24 #endif // !MODPLUG_NO_FILESAVE
25 #include "../common/misc_util.h"
26 #include "openmpt/base/Endian.hpp"
27 #include "Tagging.h"
28 #include "ITTools.h"
29 #include "XMTools.h"
30 #include "S3MTools.h"
31 #include "WAVTools.h"
32 #include "../common/version.h"
33 #include "Loaders.h"
34 #include "../common/FileReader.h"
35 #include "../soundlib/ModSampleCopy.h"
36 #include <functional>
37 #include <map>
38 
39 
40 OPENMPT_NAMESPACE_BEGIN
41 
42 
43 using namespace mpt::uuid_literals;
44 
45 
ReadSampleFromFile(SAMPLEINDEX nSample,FileReader & file,bool mayNormalize,bool includeInstrumentFormats)46 bool CSoundFile::ReadSampleFromFile(SAMPLEINDEX nSample, FileReader &file, bool mayNormalize, bool includeInstrumentFormats)
47 {
48 	if(!nSample || nSample >= MAX_SAMPLES) return false;
49 	if(!ReadWAVSample(nSample, file, mayNormalize)
50 		&& !(includeInstrumentFormats && ReadXISample(nSample, file))
51 		&& !(includeInstrumentFormats && ReadITISample(nSample, file))
52 		&& !ReadW64Sample(nSample, file)
53 		&& !ReadCAFSample(nSample, file)
54 		&& !ReadAIFFSample(nSample, file, mayNormalize)
55 		&& !ReadITSSample(nSample, file)
56 		&& !(includeInstrumentFormats && ReadPATSample(nSample, file))
57 		&& !ReadIFFSample(nSample, file)
58 		&& !ReadS3ISample(nSample, file)
59 		&& !ReadSBISample(nSample, file)
60 		&& !ReadAUSample(nSample, file, mayNormalize)
61 		&& !ReadBRRSample(nSample, file)
62 		&& !ReadFLACSample(nSample, file)
63 		&& !ReadOpusSample(nSample, file)
64 		&& !ReadVorbisSample(nSample, file)
65 		&& !ReadMP3Sample(nSample, file, false)
66 		&& !ReadMediaFoundationSample(nSample, file)
67 		)
68 	{
69 		return false;
70 	}
71 
72 	if(nSample > GetNumSamples())
73 	{
74 		m_nSamples = nSample;
75 	}
76 	if(Samples[nSample].uFlags[CHN_ADLIB])
77 	{
78 		InitOPL();
79 	}
80 	return true;
81 }
82 
83 
ReadInstrumentFromFile(INSTRUMENTINDEX nInstr,FileReader & file,bool mayNormalize)84 bool CSoundFile::ReadInstrumentFromFile(INSTRUMENTINDEX nInstr, FileReader &file, bool mayNormalize)
85 {
86 	if ((!nInstr) || (nInstr >= MAX_INSTRUMENTS)) return false;
87 	if(!ReadITIInstrument(nInstr, file)
88 		&& !ReadXIInstrument(nInstr, file)
89 		&& !ReadPATInstrument(nInstr, file)
90 		&& !ReadSFZInstrument(nInstr, file)
91 		// Generic read
92 		&& !ReadSampleAsInstrument(nInstr, file, mayNormalize))
93 	{
94 		bool ok = false;
95 #ifdef MODPLUG_TRACKER
96 		CDLSBank bank;
97 		if(bank.Open(file))
98 		{
99 			ok = bank.ExtractInstrument(*this, nInstr, 0, 0);
100 		}
101 #endif // MODPLUG_TRACKER
102 		if(!ok) return false;
103 	}
104 
105 	if(nInstr > GetNumInstruments()) m_nInstruments = nInstr;
106 	return true;
107 }
108 
109 
ReadSampleAsInstrument(INSTRUMENTINDEX nInstr,FileReader & file,bool mayNormalize)110 bool CSoundFile::ReadSampleAsInstrument(INSTRUMENTINDEX nInstr, FileReader &file, bool mayNormalize)
111 {
112 	// Scanning free sample
113 	SAMPLEINDEX nSample = GetNextFreeSample(nInstr); // may also return samples which are only referenced by the current instrument
114 	if(nSample == SAMPLEINDEX_INVALID)
115 	{
116 		return false;
117 	}
118 
119 	// Loading Instrument
120 	ModInstrument *pIns = new (std::nothrow) ModInstrument(nSample);
121 	if(pIns == nullptr)
122 	{
123 		return false;
124 	}
125 	if(!ReadSampleFromFile(nSample, file, mayNormalize, false))
126 	{
127 		delete pIns;
128 		return false;
129 	}
130 
131 	// Remove all samples which are only referenced by the old instrument, except for the one we just loaded our new sample into.
132 	RemoveInstrumentSamples(nInstr, nSample);
133 
134 	// Replace the instrument
135 	DestroyInstrument(nInstr, doNoDeleteAssociatedSamples);
136 	Instruments[nInstr] = pIns;
137 
138 #if defined(MPT_ENABLE_FILEIO) && defined(MPT_EXTERNAL_SAMPLES)
139 	SetSamplePath(nSample, file.GetOptionalFileName().value_or(P_("")));
140 #endif
141 
142 	return true;
143 }
144 
145 
DestroyInstrument(INSTRUMENTINDEX nInstr,deleteInstrumentSamples removeSamples)146 bool CSoundFile::DestroyInstrument(INSTRUMENTINDEX nInstr, deleteInstrumentSamples removeSamples)
147 {
148 	if(nInstr == 0 || nInstr >= MAX_INSTRUMENTS || !Instruments[nInstr]) return true;
149 
150 	if(removeSamples == deleteAssociatedSamples)
151 	{
152 		RemoveInstrumentSamples(nInstr);
153 	}
154 
155 	CriticalSection cs;
156 
157 	ModInstrument *pIns = Instruments[nInstr];
158 	Instruments[nInstr] = nullptr;
159 	for(auto &chn : m_PlayState.Chn)
160 	{
161 		if(chn.pModInstrument == pIns)
162 			chn.pModInstrument = nullptr;
163 	}
164 	delete pIns;
165 	return true;
166 }
167 
168 
169 // Remove all unused samples from the given nInstr and keep keepSample if provided
RemoveInstrumentSamples(INSTRUMENTINDEX nInstr,SAMPLEINDEX keepSample)170 bool CSoundFile::RemoveInstrumentSamples(INSTRUMENTINDEX nInstr, SAMPLEINDEX keepSample)
171 {
172 	if(Instruments[nInstr] == nullptr)
173 	{
174 		return false;
175 	}
176 
177 	std::vector<bool> keepSamples(GetNumSamples() + 1, true);
178 
179 	// Check which samples are used by the instrument we are going to nuke.
180 	auto referencedSamples = Instruments[nInstr]->GetSamples();
181 	for(auto sample : referencedSamples)
182 	{
183 		if(sample <= GetNumSamples())
184 		{
185 			keepSamples[sample] = false;
186 		}
187 	}
188 
189 	// If we want to keep a specific sample, do so.
190 	if(keepSample != SAMPLEINDEX_INVALID)
191 	{
192 		if(keepSample <= GetNumSamples())
193 		{
194 			keepSamples[keepSample] = true;
195 		}
196 	}
197 
198 	// Check if any of those samples are referenced by other instruments as well, in which case we want to keep them of course.
199 	for(INSTRUMENTINDEX nIns = 1; nIns <= GetNumInstruments(); nIns++) if (Instruments[nIns] != nullptr && nIns != nInstr)
200 	{
201 		Instruments[nIns]->GetSamples(keepSamples);
202 	}
203 
204 	// Now nuke the selected samples.
205 	RemoveSelectedSamples(keepSamples);
206 	return true;
207 }
208 
209 ////////////////////////////////////////////////////////////////////////////////
210 //
211 // I/O From another song
212 //
213 
ReadInstrumentFromSong(INSTRUMENTINDEX targetInstr,const CSoundFile & srcSong,INSTRUMENTINDEX sourceInstr)214 bool CSoundFile::ReadInstrumentFromSong(INSTRUMENTINDEX targetInstr, const CSoundFile &srcSong, INSTRUMENTINDEX sourceInstr)
215 {
216 	if ((!sourceInstr) || (sourceInstr > srcSong.GetNumInstruments())
217 		|| (targetInstr >= MAX_INSTRUMENTS) || (!srcSong.Instruments[sourceInstr]))
218 	{
219 		return false;
220 	}
221 	if (m_nInstruments < targetInstr) m_nInstruments = targetInstr;
222 
223 	ModInstrument *pIns = new (std::nothrow) ModInstrument();
224 	if(pIns == nullptr)
225 	{
226 		return false;
227 	}
228 
229 	DestroyInstrument(targetInstr, deleteAssociatedSamples);
230 
231 	Instruments[targetInstr] = pIns;
232 	*pIns = *srcSong.Instruments[sourceInstr];
233 
234 	std::vector<SAMPLEINDEX> sourceSample;	// Sample index in source song
235 	std::vector<SAMPLEINDEX> targetSample;	// Sample index in target song
236 	SAMPLEINDEX targetIndex = 0;		// Next index for inserting sample
237 
238 	for(auto &sample : pIns->Keyboard)
239 	{
240 		const SAMPLEINDEX sourceIndex = sample;
241 		if(sourceIndex > 0 && sourceIndex <= srcSong.GetNumSamples())
242 		{
243 			const auto entry = std::find(sourceSample.cbegin(), sourceSample.cend(), sourceIndex);
244 			if(entry == sourceSample.end())
245 			{
246 				// Didn't consider this sample yet, so add it to our map.
247 				targetIndex = GetNextFreeSample(targetInstr, targetIndex + 1);
248 				if(targetIndex <= GetModSpecifications().samplesMax)
249 				{
250 					sourceSample.push_back(sourceIndex);
251 					targetSample.push_back(targetIndex);
252 					sample = targetIndex;
253 				} else
254 				{
255 					sample = 0;
256 				}
257 			} else
258 			{
259 				// Sample reference has already been created, so only need to update the sample map.
260 				sample = *(entry - sourceSample.begin() + targetSample.begin());
261 			}
262 		} else
263 		{
264 			// Invalid or no source sample
265 			sample = 0;
266 		}
267 	}
268 
269 #ifdef MODPLUG_TRACKER
270 	if(pIns->filename.empty() && srcSong.GetpModDoc() != nullptr && &srcSong != this)
271 	{
272 		pIns->filename = srcSong.GetpModDoc()->GetPathNameMpt().GetFullFileName().ToLocale();
273 	}
274 #endif
275 	pIns->Convert(srcSong.GetType(), GetType());
276 
277 	// Copy all referenced samples over
278 	for(size_t i = 0; i < targetSample.size(); i++)
279 	{
280 		ReadSampleFromSong(targetSample[i], srcSong, sourceSample[i]);
281 	}
282 
283 	return true;
284 }
285 
286 
ReadSampleFromSong(SAMPLEINDEX targetSample,const CSoundFile & srcSong,SAMPLEINDEX sourceSample)287 bool CSoundFile::ReadSampleFromSong(SAMPLEINDEX targetSample, const CSoundFile &srcSong, SAMPLEINDEX sourceSample)
288 {
289 	if(!sourceSample
290 		|| sourceSample > srcSong.GetNumSamples()
291 		|| (targetSample >= GetModSpecifications().samplesMax && targetSample > GetNumSamples()))
292 	{
293 		return false;
294 	}
295 
296 	DestroySampleThreadsafe(targetSample);
297 
298 	const ModSample &sourceSmp = srcSong.GetSample(sourceSample);
299 	ModSample &targetSmp = GetSample(targetSample);
300 
301 	if(GetNumSamples() < targetSample) m_nSamples = targetSample;
302 	targetSmp = sourceSmp;
303 	m_szNames[targetSample] = srcSong.m_szNames[sourceSample];
304 
305 	if(sourceSmp.HasSampleData())
306 	{
307 		if(targetSmp.CopyWaveform(sourceSmp))
308 			targetSmp.PrecomputeLoops(*this, false);
309 		// Remember on-disk path (for MPTM files), but don't implicitely enable on-disk storage
310 		// (we really don't want this for e.g. duplicating samples or splitting stereo samples)
311 #ifdef MPT_EXTERNAL_SAMPLES
312 		SetSamplePath(targetSample, srcSong.GetSamplePath(sourceSample));
313 #endif
314 		targetSmp.uFlags.reset(SMP_KEEPONDISK);
315 	}
316 
317 #ifdef MODPLUG_TRACKER
318 	if((targetSmp.filename.empty()) && srcSong.GetpModDoc() != nullptr && &srcSong != this)
319 	{
320 		targetSmp.filename = mpt::ToCharset(GetCharsetInternal(), srcSong.GetpModDoc()->GetTitle());
321 	}
322 #endif
323 
324 	if(targetSmp.uFlags[CHN_ADLIB] && !SupportsOPL())
325 	{
326 		AddToLog(LogInformation, U_("OPL instruments are not supported by this format."));
327 	}
328 	targetSmp.Convert(srcSong.GetType(), GetType());
329 	if(targetSmp.uFlags[CHN_ADLIB])
330 	{
331 		InitOPL();
332 	}
333 	return true;
334 }
335 
336 
337 ////////////////////////////////////////////////////////////////////////
338 // IMA ADPCM Support for WAV files
339 
340 
IMAADPCMUnpack16(int16 * target,SmpLength sampleLen,FileReader file,uint16 blockAlign,uint32 numChannels)341 static bool IMAADPCMUnpack16(int16 *target, SmpLength sampleLen, FileReader file, uint16 blockAlign, uint32 numChannels)
342 {
343 	static constexpr int8 IMAIndexTab[8] =  { -1, -1, -1, -1, 2, 4, 6, 8 };
344 	static constexpr int16 IMAUnpackTable[90] =
345 	{
346 		7,     8,     9,     10,    11,    12,    13,    14,
347 		16,    17,    19,    21,    23,    25,    28,    31,
348 		34,    37,    41,    45,    50,    55,    60,    66,
349 		73,    80,    88,    97,    107,   118,   130,   143,
350 		157,   173,   190,   209,   230,   253,   279,   307,
351 		337,   371,   408,   449,   494,   544,   598,   658,
352 		724,   796,   876,   963,   1060,  1166,  1282,  1411,
353 		1552,  1707,  1878,  2066,  2272,  2499,  2749,  3024,
354 		3327,  3660,  4026,  4428,  4871,  5358,  5894,  6484,
355 		7132,  7845,  8630,  9493,  10442, 11487, 12635, 13899,
356 		15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
357 		32767, 0
358 	};
359 
360 	if(target == nullptr || blockAlign < 4u * numChannels)
361 		return false;
362 
363 	SmpLength samplePos = 0;
364 	sampleLen *= numChannels;
365 	while(file.CanRead(4u * numChannels) && samplePos < sampleLen)
366 	{
367 		FileReader block = file.ReadChunk(blockAlign);
368 		FileReader::PinnedView blockView = block.GetPinnedView();
369 		const std::byte *data = blockView.data();
370 		const uint32 blockSize = static_cast<uint32>(blockView.size());
371 
372 		for(uint32 chn = 0; chn < numChannels; chn++)
373 		{
374 			// Block header
375 			int32 value = block.ReadInt16LE();
376 			int32 nIndex = block.ReadUint8();
377 			Limit(nIndex, 0, 89);
378 			block.Skip(1);
379 
380 			SmpLength smpPos = samplePos + chn;
381 			uint32 dataPos = (numChannels + chn) * 4;
382 			// Block data
383 			while(smpPos <= (sampleLen - 8) && dataPos <= (blockSize - 4))
384 			{
385 				for(uint32 i = 0; i < 8; i++)
386 				{
387 					uint8 delta = mpt::byte_cast<uint8>(data[dataPos]);
388 					if(i & 1)
389 					{
390 						delta >>= 4;
391 						dataPos++;
392 					} else
393 					{
394 						delta &= 0x0F;
395 					}
396 					int32 v = IMAUnpackTable[nIndex] >> 3;
397 					if (delta & 1) v += IMAUnpackTable[nIndex] >> 2;
398 					if (delta & 2) v += IMAUnpackTable[nIndex] >> 1;
399 					if (delta & 4) v += IMAUnpackTable[nIndex];
400 					if (delta & 8) value -= v; else value += v;
401 					nIndex += IMAIndexTab[delta & 7];
402 					Limit(nIndex, 0, 88);
403 					Limit(value, -32768, 32767);
404 					target[smpPos] = static_cast<int16>(value);
405 					smpPos += numChannels;
406 				}
407 				dataPos += (numChannels - 1) * 4u;
408 			}
409 		}
410 		samplePos += ((blockSize - (numChannels * 4u)) * 2u);
411 	}
412 
413 	return true;
414 }
415 
416 
417 ////////////////////////////////////////////////////////////////////////////////
418 // WAV Open
419 
ReadWAVSample(SAMPLEINDEX nSample,FileReader & file,bool mayNormalize,FileReader * wsmpChunk)420 bool CSoundFile::ReadWAVSample(SAMPLEINDEX nSample, FileReader &file, bool mayNormalize, FileReader *wsmpChunk)
421 {
422 	WAVReader wavFile(file);
423 
424 	static constexpr WAVFormatChunk::SampleFormats SupportedFormats[] = {WAVFormatChunk::fmtPCM, WAVFormatChunk::fmtFloat, WAVFormatChunk::fmtIMA_ADPCM, WAVFormatChunk::fmtMP3, WAVFormatChunk::fmtALaw, WAVFormatChunk::fmtULaw};
425 	if(!wavFile.IsValid()
426 	   || wavFile.GetNumChannels() == 0
427 	   || wavFile.GetNumChannels() > 2
428 	   || (wavFile.GetBitsPerSample() == 0 && wavFile.GetSampleFormat() != WAVFormatChunk::fmtMP3)
429 	   || (wavFile.GetBitsPerSample() < 32 && wavFile.GetSampleFormat() == WAVFormatChunk::fmtFloat)
430 	   || (wavFile.GetBitsPerSample() > 64)
431 	   || !mpt::contains(SupportedFormats, wavFile.GetSampleFormat()))
432 	{
433 		return false;
434 	}
435 
436 	DestroySampleThreadsafe(nSample);
437 	m_szNames[nSample] = "";
438 	ModSample &sample = Samples[nSample];
439 	sample.Initialize();
440 	sample.nLength = wavFile.GetSampleLength();
441 	sample.nC5Speed = wavFile.GetSampleRate();
442 	wavFile.ApplySampleSettings(sample, GetCharsetInternal(), m_szNames[nSample]);
443 
444 	FileReader sampleChunk = wavFile.GetSampleData();
445 
446 	SampleIO sampleIO(
447 		SampleIO::_8bit,
448 		(wavFile.GetNumChannels() > 1) ? SampleIO::stereoInterleaved : SampleIO::mono,
449 		SampleIO::littleEndian,
450 		SampleIO::signedPCM);
451 
452 	if(wavFile.GetSampleFormat() == WAVFormatChunk::fmtIMA_ADPCM && wavFile.GetNumChannels() <= 2)
453 	{
454 		// IMA ADPCM 4:1
455 		LimitMax(sample.nLength, MAX_SAMPLE_LENGTH);
456 		sample.uFlags.set(CHN_16BIT);
457 		sample.uFlags.set(CHN_STEREO, wavFile.GetNumChannels() == 2);
458 		if(!sample.AllocateSample())
459 		{
460 			return false;
461 		}
462 		IMAADPCMUnpack16(sample.sample16(), sample.nLength, sampleChunk, wavFile.GetBlockAlign(), wavFile.GetNumChannels());
463 		sample.PrecomputeLoops(*this, false);
464 	} else if(wavFile.GetSampleFormat() == WAVFormatChunk::fmtMP3)
465 	{
466 		// MP3 in WAV
467 		bool loadedMP3 = ReadMP3Sample(nSample, sampleChunk, false, true) || ReadMediaFoundationSample(nSample, sampleChunk, true);
468 		if(!loadedMP3)
469 		{
470 			return false;
471 		}
472 	} else if(!wavFile.IsExtensibleFormat() && wavFile.MayBeCoolEdit16_8() && wavFile.GetSampleFormat() == WAVFormatChunk::fmtPCM && wavFile.GetBitsPerSample() == 32 && wavFile.GetBlockAlign() == wavFile.GetNumChannels() * 4)
473 	{
474 		// Syntrillium Cool Edit hack to store IEEE 32bit floating point
475 		// Format is described as 32bit integer PCM contained in 32bit blocks and an WAVEFORMATEX extension size of 2 which contains a single 16 bit little endian value of 1.
476 		//  (This is parsed in WAVTools.cpp and returned via MayBeCoolEdit16_8()).
477 		// The data actually stored in this case is little endian 32bit floating point PCM with 2**15 full scale.
478 		// Cool Edit calls this format "16.8 float".
479 		sampleIO |= SampleIO::_32bit;
480 		sampleIO |= SampleIO::floatPCM15;
481 		sampleIO.ReadSample(sample, sampleChunk);
482 	} else if(!wavFile.IsExtensibleFormat() && wavFile.GetSampleFormat() == WAVFormatChunk::fmtPCM && wavFile.GetBitsPerSample() == 24 && wavFile.GetBlockAlign() == wavFile.GetNumChannels() * 4)
483 	{
484 		// Syntrillium Cool Edit hack to store IEEE 32bit floating point
485 		// Format is described as 24bit integer PCM contained in 32bit blocks.
486 		// The data actually stored in this case is little endian 32bit floating point PCM with 2**23 full scale.
487 		// Cool Edit calls this format "24.0 float".
488 		sampleIO |= SampleIO::_32bit;
489 		sampleIO |= SampleIO::floatPCM23;
490 		sampleIO.ReadSample(sample, sampleChunk);
491 	} else if(wavFile.GetSampleFormat() == WAVFormatChunk::fmtALaw || wavFile.GetSampleFormat() == WAVFormatChunk::fmtULaw)
492 	{
493 		// a-law / u-law
494 		sampleIO |= SampleIO::_16bit;
495 		sampleIO |= wavFile.GetSampleFormat() == WAVFormatChunk::fmtALaw ? SampleIO::aLaw : SampleIO::uLaw;
496 		sampleIO.ReadSample(sample, sampleChunk);
497 	} else
498 	{
499 		// PCM / Float
500 		SampleIO::Bitdepth bitDepth;
501 		switch((wavFile.GetBitsPerSample() - 1) / 8u)
502 		{
503 		default:
504 		case 0: bitDepth = SampleIO::_8bit; break;
505 		case 1: bitDepth = SampleIO::_16bit; break;
506 		case 2: bitDepth = SampleIO::_24bit; break;
507 		case 3: bitDepth = SampleIO::_32bit; break;
508 		case 7: bitDepth = SampleIO::_64bit; break;
509 		}
510 
511 		sampleIO |= bitDepth;
512 		if(wavFile.GetBitsPerSample() <= 8)
513 			sampleIO |= SampleIO::unsignedPCM;
514 
515 		if(wavFile.GetSampleFormat() == WAVFormatChunk::fmtFloat)
516 			sampleIO |= SampleIO::floatPCM;
517 
518 		if(mayNormalize)
519 			sampleIO.MayNormalize();
520 
521 		sampleIO.ReadSample(sample, sampleChunk);
522 	}
523 
524 	if(wsmpChunk != nullptr)
525 	{
526 		// DLS WSMP chunk
527 		*wsmpChunk = wavFile.GetWsmpChunk();
528 	}
529 
530 	sample.Convert(MOD_TYPE_IT, GetType());
531 	sample.PrecomputeLoops(*this, false);
532 
533 	return true;
534 }
535 
536 
537 ///////////////////////////////////////////////////////////////
538 // Save WAV
539 
540 
541 #ifndef MODPLUG_NO_FILESAVE
SaveWAVSample(SAMPLEINDEX nSample,std::ostream & f) const542 bool CSoundFile::SaveWAVSample(SAMPLEINDEX nSample, std::ostream &f) const
543 {
544 	const ModSample &sample = Samples[nSample];
545 	if(sample.uFlags[CHN_ADLIB])
546 		return false;
547 
548 	mpt::IO::OFile<std::ostream> ff(f);
549 	WAVWriter file(ff);
550 
551 	file.WriteFormat(sample.GetSampleRate(GetType()), sample.GetElementarySampleSize() * 8, sample.GetNumChannels(), WAVFormatChunk::fmtPCM);
552 
553 	// Write sample data
554 	file.StartChunk(RIFFChunk::iddata);
555 	file.Skip(SampleIO(
556 		sample.uFlags[CHN_16BIT] ? SampleIO::_16bit : SampleIO::_8bit,
557 		sample.uFlags[CHN_STEREO] ? SampleIO::stereoInterleaved : SampleIO::mono,
558 		SampleIO::littleEndian,
559 		sample.uFlags[CHN_16BIT] ? SampleIO::signedPCM : SampleIO::unsignedPCM)
560 		.WriteSample(f, sample));
561 
562 	file.WriteLoopInformation(sample);
563 	file.WriteExtraInformation(sample, GetType());
564 	if(sample.HasCustomCuePoints())
565 	{
566 		file.WriteCueInformation(sample);
567 	}
568 
569 	FileTags tags;
570 	tags.SetEncoder();
571 	tags.title = mpt::ToUnicode(GetCharsetInternal(), m_szNames[nSample]);
572 	file.WriteMetatags(tags);
573 	file.Finalize();
574 
575 	return true;
576 }
577 
578 #endif // MODPLUG_NO_FILESAVE
579 
580 
581 
582 /////////////////
583 // Sony Wave64 //
584 
585 
586 struct Wave64FileHeader
587 {
588 	mpt::GUIDms GuidRIFF;
589 	uint64le    FileSize;
590 	mpt::GUIDms GuidWAVE;
591 };
592 
593 MPT_BINARY_STRUCT(Wave64FileHeader, 40)
594 
595 
596 struct Wave64ChunkHeader
597 {
598 	mpt::GUIDms GuidChunk;
599 	uint64le    Size;
600 };
601 
602 MPT_BINARY_STRUCT(Wave64ChunkHeader, 24)
603 
604 
605 struct Wave64Chunk
606 {
607 	Wave64ChunkHeader header;
608 
GetLengthWave64Chunk609 	FileReader::off_t GetLength() const
610 	{
611 		uint64 length = header.Size;
612 		if(length < sizeof(Wave64ChunkHeader))
613 		{
614 			length = 0;
615 		} else
616 		{
617 			length -= sizeof(Wave64ChunkHeader);
618 		}
619 		return mpt::saturate_cast<FileReader::off_t>(length);
620 	}
621 
GetIDWave64Chunk622 	mpt::UUID GetID() const
623 	{
624 		return mpt::UUID(header.GuidChunk);
625 	}
626 };
627 
628 MPT_BINARY_STRUCT(Wave64Chunk, 24)
629 
630 
Wave64TagFromLISTINFO(mpt::ustring & dst,uint16 codePage,const FileReader::ChunkList<RIFFChunk> & infoChunk,RIFFChunk::ChunkIdentifiers id)631 static void Wave64TagFromLISTINFO(mpt::ustring & dst, uint16 codePage, const FileReader::ChunkList<RIFFChunk> & infoChunk, RIFFChunk::ChunkIdentifiers id)
632 {
633 	if(!infoChunk.ChunkExists(id))
634 	{
635 		return;
636 	}
637 	FileReader textChunk = infoChunk.GetChunk(id);
638 	if(!textChunk.IsValid())
639 	{
640 		return;
641 	}
642 	std::string str;
643 	textChunk.ReadString<mpt::String::maybeNullTerminated>(str, textChunk.GetLength());
644 	str = mpt::replace(str, std::string("\r\n"), std::string("\n"));
645 	str = mpt::replace(str, std::string("\r"), std::string("\n"));
646 	dst = mpt::ToUnicode(codePage, mpt::Charset::Windows1252, str);
647 }
648 
649 
ReadW64Sample(SAMPLEINDEX nSample,FileReader & file,bool mayNormalize)650 bool CSoundFile::ReadW64Sample(SAMPLEINDEX nSample, FileReader &file, bool mayNormalize)
651 {
652 	file.Rewind();
653 
654 	constexpr mpt::UUID guidRIFF       = "66666972-912E-11CF-A5D6-28DB04C10000"_uuid;
655 	constexpr mpt::UUID guidWAVE       = "65766177-ACF3-11D3-8CD1-00C04F8EDB8A"_uuid;
656 
657 	constexpr mpt::UUID guidLIST       = "7473696C-912F-11CF-A5D6-28DB04C10000"_uuid;
658 	constexpr mpt::UUID guidFMT        = "20746D66-ACF3-11D3-8CD1-00C04F8EDB8A"_uuid;
659 	//constexpr mpt::UUID guidFACT       = "74636166-ACF3-11D3-8CD1-00C04F8EDB8A"_uuid;
660 	constexpr mpt::UUID guidDATA       = "61746164-ACF3-11D3-8CD1-00C04F8EDB8A"_uuid;
661 	//constexpr mpt::UUID guidLEVL       = "6C76656C-ACF3-11D3-8CD1-00C04F8EDB8A"_uuid;
662 	//constexpr mpt::UUID guidJUNK       = "6b6E756A-ACF3-11D3-8CD1-00C04f8EDB8A"_uuid;
663 	//constexpr mpt::UUID guidBEXT       = "74786562-ACF3-11D3-8CD1-00C04F8EDB8A"_uuid;
664 	//constexpr mpt::UUID guiMARKER      = "ABF76256-392D-11D2-86C7-00C04F8EDB8A"_uuid;
665 	//constexpr mpt::UUID guiSUMMARYLIST = "925F94BC-525A-11D2-86DC-00C04F8EDB8A"_uuid;
666 
667 	constexpr mpt::UUID guidCSET       = "54455343-ACF3-11D3-8CD1-00C04F8EDB8A"_uuid;
668 
669 	Wave64FileHeader fileHeader;
670 	if(!file.ReadStruct(fileHeader))
671 	{
672 		return false;
673 	}
674 	if(mpt::UUID(fileHeader.GuidRIFF) != guidRIFF)
675 	{
676 		return false;
677 	}
678 	if(mpt::UUID(fileHeader.GuidWAVE) != guidWAVE)
679 	{
680 		return false;
681 	}
682 	if(fileHeader.FileSize != file.GetLength())
683 	{
684 		return false;
685 	}
686 
687 	FileReader chunkFile = file;
688 	auto chunkList = chunkFile.ReadChunks<Wave64Chunk>(8);
689 
690 	if(!chunkList.ChunkExists(guidFMT))
691 	{
692 		return false;
693 	}
694 	FileReader formatChunk = chunkList.GetChunk(guidFMT);
695 	WAVFormatChunk format;
696 	if(!formatChunk.ReadStruct(format))
697 	{
698 		return false;
699 	}
700 	uint16 sampleFormat = format.format;
701 	if(format.format == WAVFormatChunk::fmtExtensible)
702 	{
703 		WAVFormatChunkExtension formatExt;
704 		if(!formatChunk.ReadStruct(formatExt))
705 		{
706 			return false;
707 		}
708 		sampleFormat = static_cast<uint16>(mpt::UUID(formatExt.subFormat).GetData1());
709 	}
710 	if(format.sampleRate == 0)
711 	{
712 		return false;
713 	}
714 	if(format.numChannels == 0)
715 	{
716 		return false;
717 	}
718 	if(format.numChannels > 2)
719 	{
720 		return false;
721 	}
722 	if(sampleFormat != WAVFormatChunk::fmtPCM && sampleFormat != WAVFormatChunk::fmtFloat)
723 	{
724 		return false;
725 	}
726 	if(sampleFormat == WAVFormatChunk::fmtFloat && format.bitsPerSample != 32 && format.bitsPerSample != 64)
727 	{
728 		return false;
729 	}
730 	if(sampleFormat == WAVFormatChunk::fmtPCM && format.bitsPerSample > 64)
731 	{
732 		return false;
733 	}
734 
735 	SampleIO::Bitdepth bitDepth;
736 	switch((format.bitsPerSample - 1) / 8u)
737 	{
738 	default:
739 	case 0: bitDepth = SampleIO::_8bit ; break;
740 	case 1: bitDepth = SampleIO::_16bit; break;
741 	case 2: bitDepth = SampleIO::_24bit; break;
742 	case 3: bitDepth = SampleIO::_32bit; break;
743 	case 7: bitDepth = SampleIO::_64bit; break;
744 	}
745 	SampleIO sampleIO(
746 		bitDepth,
747 		(format.numChannels > 1) ? SampleIO::stereoInterleaved : SampleIO::mono,
748 		SampleIO::littleEndian,
749 		(sampleFormat == WAVFormatChunk::fmtFloat) ? SampleIO::floatPCM : SampleIO::signedPCM);
750 	if(format.bitsPerSample <= 8)
751 	{
752 		sampleIO |= SampleIO::unsignedPCM;
753 	}
754 	if(mayNormalize)
755 	{
756 		sampleIO.MayNormalize();
757 	}
758 
759 	FileTags tags;
760 
761 	uint16 codePage = 28591; // mpt::Charset::ISO8859_1
762 	FileReader csetChunk = chunkList.GetChunk(guidCSET);
763 	if(csetChunk.IsValid())
764 	{
765 		if(csetChunk.CanRead(2))
766 		{
767 			codePage = csetChunk.ReadUint16LE();
768 		}
769 	}
770 
771 	if(chunkList.ChunkExists(guidLIST))
772 	{
773 		FileReader listChunk = chunkList.GetChunk(guidLIST);
774 		if(listChunk.ReadMagic("INFO"))
775 		{
776 			auto infoChunk = listChunk.ReadChunks<RIFFChunk>(2);
777 			Wave64TagFromLISTINFO(tags.title, codePage, infoChunk, RIFFChunk::idINAM);
778 			Wave64TagFromLISTINFO(tags.encoder, codePage, infoChunk, RIFFChunk::idISFT);
779 			//Wave64TagFromLISTINFO(void, codePage, infoChunk, RIFFChunk::idICOP);
780 			Wave64TagFromLISTINFO(tags.artist, codePage, infoChunk, RIFFChunk::idIART);
781 			Wave64TagFromLISTINFO(tags.album, codePage, infoChunk, RIFFChunk::idIPRD);
782 			Wave64TagFromLISTINFO(tags.comments, codePage, infoChunk, RIFFChunk::idICMT);
783 			//Wave64TagFromLISTINFO(void, codePage, infoChunk, RIFFChunk::idIENG);
784 			//Wave64TagFromLISTINFO(void, codePage, infoChunk, RIFFChunk::idISBJ);
785 			Wave64TagFromLISTINFO(tags.genre, codePage, infoChunk, RIFFChunk::idIGNR);
786 			//Wave64TagFromLISTINFO(void, codePage, infoChunk, RIFFChunk::idICRD);
787 			Wave64TagFromLISTINFO(tags.year, codePage, infoChunk, RIFFChunk::idYEAR);
788 			Wave64TagFromLISTINFO(tags.trackno, codePage, infoChunk, RIFFChunk::idTRCK);
789 			Wave64TagFromLISTINFO(tags.url, codePage, infoChunk, RIFFChunk::idTURL);
790 			//Wave64TagFromLISTINFO(tags.bpm, codePage, infoChunk, void);
791 		}
792 	}
793 
794 	if(!chunkList.ChunkExists(guidDATA))
795 	{
796 		return false;
797 	}
798 	FileReader audioData = chunkList.GetChunk(guidDATA);
799 
800 	SmpLength length = mpt::saturate_cast<SmpLength>(audioData.GetLength() / (sampleIO.GetEncodedBitsPerSample()/8));
801 
802 	ModSample &mptSample = Samples[nSample];
803 	DestroySampleThreadsafe(nSample);
804 	mptSample.Initialize();
805 	mptSample.nLength = length;
806 	mptSample.nC5Speed = format.sampleRate;
807 
808 	sampleIO.ReadSample(mptSample, audioData);
809 
810 	m_szNames[nSample] = mpt::ToCharset(GetCharsetInternal(), GetSampleNameFromTags(tags));
811 
812 	mptSample.Convert(MOD_TYPE_IT, GetType());
813 	mptSample.PrecomputeLoops(*this, false);
814 
815 	return true;
816 
817 }
818 
819 
820 
821 #ifndef MODPLUG_NO_FILESAVE
822 
823 ///////////////////////////////////////////////////////////////
824 // Save RAW
825 
SaveRAWSample(SAMPLEINDEX nSample,std::ostream & f) const826 bool CSoundFile::SaveRAWSample(SAMPLEINDEX nSample, std::ostream &f) const
827 {
828 	const ModSample &sample = Samples[nSample];
829 	SampleIO(
830 		sample.uFlags[CHN_16BIT] ? SampleIO::_16bit : SampleIO::_8bit,
831 		sample.uFlags[CHN_STEREO] ? SampleIO::stereoInterleaved : SampleIO::mono,
832 		SampleIO::littleEndian,
833 		SampleIO::signedPCM)
834 		.WriteSample(f, sample);
835 
836 	return true;
837 }
838 
839 #endif // MODPLUG_NO_FILESAVE
840 
841 /////////////////////////////////////////////////////////////
842 // GUS Patches
843 
844 struct GF1PatchFileHeader
845 {
846 	char     magic[8];       // "GF1PATCH"
847 	char     version[4];     // "100", or "110"
848 	char     id[10];         // "ID#000002"
849 	char     copyright[60];  // Copyright
850 	uint8le  numInstr;       // Number of instruments in patch
851 	uint8le  voices;         // Number of voices, usually 14
852 	uint8le  channels;       // Number of wav channels that can be played concurently to the patch
853 	uint16le numSamples;     // Total number of waveforms for all the .PAT
854 	uint16le volume;         // Master volume
855 	uint32le dataSize;
856 	char     reserved2[36];
857 };
858 
859 MPT_BINARY_STRUCT(GF1PatchFileHeader, 129)
860 
861 
862 struct GF1Instrument
863 {
864 	uint16le id;        // Instrument id: 0-65535
865 	char     name[16];  // Name of instrument. Gravis doesn't seem to use it
866 	uint32le size;      // Number of bytes for the instrument with header. (To skip to next instrument)
867 	uint8    layers;    // Number of layers in instrument: 1-4
868 	char     reserved[40];
869 };
870 
871 MPT_BINARY_STRUCT(GF1Instrument, 63)
872 
873 
874 struct GF1SampleHeader
875 {
876 	char     name[7];        // null terminated string. name of the wave.
877 	uint8le  fractions;      // Start loop point fraction in 4 bits + End loop point fraction in the 4 other bits.
878 	uint32le length;         // total size of wavesample. limited to 65535 now by the drivers, not the card.
879 	uint32le loopstart;      // start loop position in the wavesample
880 	uint32le loopend;        // end loop position in the wavesample
881 	uint16le freq;           // Rate at which the wavesample has been sampled
882 	uint32le low_freq;       // check note.h for the correspondance.
883 	uint32le high_freq;      // check note.h for the correspondance.
884 	uint32le root_freq;      // check note.h for the correspondance.
885 	int16le  finetune;       // fine tune. -512 to +512, EXCLUDING 0 cause it is a multiplier. 512 is one octave off, and 1 is a neutral value
886 	uint8le  balance;        // Balance: 0-15. 0=full left, 15 = full right
887 	uint8le  env_rate[6];    // attack rates
888 	uint8le  env_volume[6];  // attack volumes
889 	uint8le  tremolo_sweep, tremolo_rate, tremolo_depth;
890 	uint8le  vibrato_sweep, vibrato_rate, vibrato_depth;
891 	uint8le  flags;
892 	int16le  scale_frequency;  // Note
893 	uint16le scale_factor;     // 0...2048 (1024 is normal) or 0...2
894 	char     reserved[36];
895 };
896 
897 MPT_BINARY_STRUCT(GF1SampleHeader, 96)
898 
899 // -- GF1 Envelopes --
900 //
901 // It can be represented like this (the envelope is totally bogus, it is
902 // just to show the concept):
903 //
904 //  |
905 //  |           /----`               | |
906 //  |   /------/      `\         | | | | |
907 //  |  /                 \       | | | | |
908 //  | /                    \     | | | | |
909 //  |/                       \   | | | | |
910 //  ---------------------------- | | | | | |
911 //  <---> attack rate 0          0 1 2 3 4 5 amplitudes
912 //       <----> attack rate 1
913 //           <> attack rate 2
914 //           <--> attack rate 3
915 //               <> attack rate 4
916 //               <-----> attack rate 5
917 //
918 // -- GF1 Flags --
919 //
920 // bit 0: 8/16 bit
921 // bit 1: Signed/Unsigned
922 // bit 2: off/on looping
923 // bit 3: off/on bidirectionnal looping
924 // bit 4: off/on backward looping
925 // bit 5: off/on sustaining (3rd point in env.)
926 // bit 6: off/on envelopes
927 // bit 7: off/on clamped release (6th point, env)
928 
929 
930 struct GF1Layer
931 {
932 	uint8le  previous;		// If !=0 the wavesample to use is from the previous layer. The waveheader is still needed
933 	uint8le  id;			// Layer id: 0-3
934 	uint32le size;			// data size in bytes in the layer, without the header. to skip to next layer for example:
935 	uint8le  samples;		// number of wavesamples
936 	char     reserved[40];
937 };
938 
939 MPT_BINARY_STRUCT(GF1Layer, 47)
940 
941 
PatchFreqToNote(uint32 nFreq)942 static double PatchFreqToNote(uint32 nFreq)
943 {
944 	return std::log(nFreq / 2044.0) * (12.0 * 1.44269504088896340736);	// 1.0/std::log(2.0)
945 }
946 
947 
PatchFreqToNoteInt(uint32 nFreq)948 static int32 PatchFreqToNoteInt(uint32 nFreq)
949 {
950 	return mpt::saturate_round<int32>(PatchFreqToNote(nFreq));
951 }
952 
953 
PatchToSample(CSoundFile * that,SAMPLEINDEX nSample,GF1SampleHeader & sampleHeader,FileReader & file)954 static void PatchToSample(CSoundFile *that, SAMPLEINDEX nSample, GF1SampleHeader &sampleHeader, FileReader &file)
955 {
956 	ModSample &sample = that->GetSample(nSample);
957 
958 	file.ReadStruct(sampleHeader);
959 
960 	sample.Initialize();
961 	if(sampleHeader.flags & 4) sample.uFlags.set(CHN_LOOP);
962 	if(sampleHeader.flags & 8) sample.uFlags.set(CHN_PINGPONGLOOP);
963 	if(sampleHeader.flags & 16) sample.uFlags.set(CHN_REVERSE);
964 	sample.nLength = sampleHeader.length;
965 	sample.nLoopStart = sampleHeader.loopstart;
966 	sample.nLoopEnd = sampleHeader.loopend;
967 	sample.nC5Speed = sampleHeader.freq;
968 	sample.nPan = (sampleHeader.balance * 256 + 8) / 15;
969 	if(sample.nPan > 256) sample.nPan = 128;
970 	else sample.uFlags.set(CHN_PANNING);
971 	sample.nVibType = VIB_SINE;
972 	sample.nVibSweep = sampleHeader.vibrato_sweep;
973 	sample.nVibDepth = sampleHeader.vibrato_depth;
974 	sample.nVibRate = sampleHeader.vibrato_rate / 4;
975 	if(sampleHeader.scale_factor)
976 	{
977 		sample.Transpose((84.0 - PatchFreqToNote(sampleHeader.root_freq)) / 12.0);
978 	}
979 
980 	SampleIO sampleIO(
981 		SampleIO::_8bit,
982 		SampleIO::mono,
983 		SampleIO::littleEndian,
984 		(sampleHeader.flags & 2) ? SampleIO::unsignedPCM : SampleIO::signedPCM);
985 
986 	if(sampleHeader.flags & 1)
987 	{
988 		sampleIO |= SampleIO::_16bit;
989 		sample.nLength /= 2;
990 		sample.nLoopStart /= 2;
991 		sample.nLoopEnd /= 2;
992 	}
993 	sampleIO.ReadSample(sample, file);
994 	sample.Convert(MOD_TYPE_IT, that->GetType());
995 	sample.PrecomputeLoops(*that, false);
996 
997 	that->m_szNames[nSample] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.name);
998 }
999 
1000 
ReadPATSample(SAMPLEINDEX nSample,FileReader & file)1001 bool CSoundFile::ReadPATSample(SAMPLEINDEX nSample, FileReader &file)
1002 {
1003 	file.Rewind();
1004 	GF1PatchFileHeader fileHeader;
1005 	GF1Instrument instrHeader;	// We only support one instrument
1006 	GF1Layer layerHeader;
1007 	if(!file.ReadStruct(fileHeader)
1008 		|| memcmp(fileHeader.magic, "GF1PATCH", 8)
1009 		|| (memcmp(fileHeader.version, "110\0", 4) && memcmp(fileHeader.version, "100\0", 4))
1010 		|| memcmp(fileHeader.id, "ID#000002\0", 10)
1011 		|| !fileHeader.numInstr || !fileHeader.numSamples
1012 		|| !file.ReadStruct(instrHeader)
1013 		//|| !instrHeader.layers	// DOO.PAT has 0 layers
1014 		|| !file.ReadStruct(layerHeader)
1015 		|| !layerHeader.samples)
1016 	{
1017 		return false;
1018 	}
1019 
1020 	DestroySampleThreadsafe(nSample);
1021 	GF1SampleHeader sampleHeader;
1022 	PatchToSample(this, nSample, sampleHeader, file);
1023 
1024 	if(instrHeader.name[0] > ' ')
1025 	{
1026 		m_szNames[nSample] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, instrHeader.name);
1027 	}
1028 	return true;
1029 }
1030 
1031 
1032 // PAT Instrument
ReadPATInstrument(INSTRUMENTINDEX nInstr,FileReader & file)1033 bool CSoundFile::ReadPATInstrument(INSTRUMENTINDEX nInstr, FileReader &file)
1034 {
1035 	file.Rewind();
1036 	GF1PatchFileHeader fileHeader;
1037 	GF1Instrument instrHeader;	// We only support one instrument
1038 	GF1Layer layerHeader;
1039 	if(!file.ReadStruct(fileHeader)
1040 		|| memcmp(fileHeader.magic, "GF1PATCH", 8)
1041 		|| (memcmp(fileHeader.version, "110\0", 4) && memcmp(fileHeader.version, "100\0", 4))
1042 		|| memcmp(fileHeader.id, "ID#000002\0", 10)
1043 		|| !fileHeader.numInstr || !fileHeader.numSamples
1044 		|| !file.ReadStruct(instrHeader)
1045 		//|| !instrHeader.layers	// DOO.PAT has 0 layers
1046 		|| !file.ReadStruct(layerHeader)
1047 		|| !layerHeader.samples)
1048 	{
1049 		return false;
1050 	}
1051 
1052 	ModInstrument *pIns = new (std::nothrow) ModInstrument();
1053 	if(pIns == nullptr)
1054 	{
1055 		return false;
1056 	}
1057 
1058 	DestroyInstrument(nInstr, deleteAssociatedSamples);
1059 	if (nInstr > m_nInstruments) m_nInstruments = nInstr;
1060 	Instruments[nInstr] = pIns;
1061 
1062 	pIns->name = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, instrHeader.name);
1063 	pIns->nFadeOut = 2048;
1064 	if(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))
1065 	{
1066 		pIns->nNNA = NewNoteAction::NoteOff;
1067 		pIns->nDNA = DuplicateNoteAction::NoteFade;
1068 	}
1069 
1070 	SAMPLEINDEX nextSample = 0;
1071 	int32 nMinSmpNote = 0xFF;
1072 	SAMPLEINDEX nMinSmp = 0;
1073 	for(uint8 smp = 0; smp < layerHeader.samples; smp++)
1074 	{
1075 		// Find a free sample
1076 		nextSample = GetNextFreeSample(nInstr, nextSample + 1);
1077 		if(nextSample == SAMPLEINDEX_INVALID) break;
1078 		if(m_nSamples < nextSample) m_nSamples = nextSample;
1079 		if(!nMinSmp) nMinSmp = nextSample;
1080 		// Load it
1081 		GF1SampleHeader sampleHeader;
1082 		PatchToSample(this, nextSample, sampleHeader, file);
1083 		int32 nMinNote = (sampleHeader.low_freq > 100) ? PatchFreqToNoteInt(sampleHeader.low_freq) : 0;
1084 		int32 nMaxNote = (sampleHeader.high_freq > 100) ? PatchFreqToNoteInt(sampleHeader.high_freq) : static_cast<uint8>(NOTE_MAX);
1085 		int32 nBaseNote = (sampleHeader.root_freq > 100) ? PatchFreqToNoteInt(sampleHeader.root_freq) : -1;
1086 		if(!sampleHeader.scale_factor && layerHeader.samples == 1) { nMinNote = 0; nMaxNote = NOTE_MAX; }
1087 		// Fill Note Map
1088 		for(int32 k = 0; k < NOTE_MAX; k++)
1089 		{
1090 			if(k == nBaseNote || (!pIns->Keyboard[k] && k >= nMinNote && k <= nMaxNote))
1091 			{
1092 				if(!sampleHeader.scale_factor)
1093 					pIns->NoteMap[k] = NOTE_MIDDLEC;
1094 
1095 				pIns->Keyboard[k] = nextSample;
1096 				if(k < nMinSmpNote)
1097 				{
1098 					nMinSmpNote = k;
1099 					nMinSmp = nextSample;
1100 				}
1101 			}
1102 		}
1103 	}
1104 	if(nMinSmp)
1105 	{
1106 		// Fill note map and missing samples
1107 		for(uint8 k = 0; k < NOTE_MAX; k++)
1108 		{
1109 			if(!pIns->NoteMap[k]) pIns->NoteMap[k] = k + 1;
1110 			if(!pIns->Keyboard[k])
1111 			{
1112 				pIns->Keyboard[k] = nMinSmp;
1113 			} else
1114 			{
1115 				nMinSmp = pIns->Keyboard[k];
1116 			}
1117 		}
1118 	}
1119 
1120 	pIns->Sanitize(MOD_TYPE_IT);
1121 	pIns->Convert(MOD_TYPE_IT, GetType());
1122 	return true;
1123 }
1124 
1125 
1126 /////////////////////////////////////////////////////////////
1127 // S3I Samples
1128 
1129 
ReadS3ISample(SAMPLEINDEX nSample,FileReader & file)1130 bool CSoundFile::ReadS3ISample(SAMPLEINDEX nSample, FileReader &file)
1131 {
1132 	file.Rewind();
1133 
1134 	S3MSampleHeader sampleHeader;
1135 	if(!file.ReadStruct(sampleHeader)
1136 		|| (sampleHeader.sampleType != S3MSampleHeader::typePCM && sampleHeader.sampleType != S3MSampleHeader::typeAdMel)
1137 		|| (memcmp(sampleHeader.magic, "SCRS", 4) && memcmp(sampleHeader.magic, "SCRI", 4))
1138 		|| !file.Seek(sampleHeader.GetSampleOffset()))
1139 	{
1140 		return false;
1141 	}
1142 
1143 	DestroySampleThreadsafe(nSample);
1144 
1145 	ModSample &sample = Samples[nSample];
1146 	sampleHeader.ConvertToMPT(sample);
1147 	m_szNames[nSample] = mpt::String::ReadBuf(mpt::String::nullTerminated, sampleHeader.name);
1148 
1149 	if(sampleHeader.sampleType < S3MSampleHeader::typeAdMel)
1150 		sampleHeader.GetSampleFormat(false).ReadSample(sample, file);
1151 	else if(SupportsOPL())
1152 		InitOPL();
1153 	else
1154 		AddToLog(LogInformation, U_("OPL instruments are not supported by this format."));
1155 
1156 	sample.Convert(MOD_TYPE_S3M, GetType());
1157 	sample.PrecomputeLoops(*this, false);
1158 	return true;
1159 }
1160 
1161 #ifndef MODPLUG_NO_FILESAVE
1162 
SaveS3ISample(SAMPLEINDEX smp,std::ostream & f) const1163 bool CSoundFile::SaveS3ISample(SAMPLEINDEX smp, std::ostream &f) const
1164 {
1165 	const ModSample &sample = Samples[smp];
1166 	S3MSampleHeader sampleHeader;
1167 	MemsetZero(sampleHeader);
1168 	SmpLength length = sampleHeader.ConvertToS3M(sample);
1169 	mpt::String::WriteBuf(mpt::String::nullTerminated, sampleHeader.name) = m_szNames[smp];
1170 	mpt::String::WriteBuf(mpt::String::maybeNullTerminated, sampleHeader.reserved2) = mpt::ToCharset(mpt::Charset::UTF8, Version::Current().GetOpenMPTVersionString());
1171 	if(length)
1172 		sampleHeader.dataPointer[1] = sizeof(S3MSampleHeader) >> 4;
1173 	mpt::IO::Write(f, sampleHeader);
1174 	if(length)
1175 		sampleHeader.GetSampleFormat(false).WriteSample(f, sample, length);
1176 
1177 	return true;
1178 }
1179 
1180 #endif // MODPLUG_NO_FILESAVE
1181 
1182 
1183 /////////////////////////////////////////////////////////////
1184 // SBI OPL patch files
1185 
ReadSBISample(SAMPLEINDEX sample,FileReader & file)1186 bool CSoundFile::ReadSBISample(SAMPLEINDEX sample, FileReader &file)
1187 {
1188 	file.Rewind();
1189 	if(!file.ReadMagic("SBI\x1A")
1190 		|| !file.CanRead(32 + sizeof(OPLPatch))
1191 		|| file.CanRead(64))	// Arbitrary threshold to reject files that are unlikely to be SBI files
1192 		return false;
1193 
1194 	if(!SupportsOPL())
1195 	{
1196 		AddToLog(LogInformation, U_("OPL instruments are not supported by this format."));
1197 		return true;
1198 	}
1199 
1200 	DestroySampleThreadsafe(sample);
1201 	InitOPL();
1202 
1203 	ModSample &mptSmp = Samples[sample];
1204 	mptSmp.Initialize(MOD_TYPE_S3M);
1205 	file.ReadString<mpt::String::nullTerminated>(m_szNames[sample], 32);
1206 	OPLPatch patch;
1207 	file.ReadArray(patch);
1208 	mptSmp.SetAdlib(true, patch);
1209 
1210 	mptSmp.Convert(MOD_TYPE_S3M, GetType());
1211 	return true;
1212 }
1213 
1214 
1215 
1216 /////////////////////////////////////////////////////////////
1217 // XI Instruments
1218 
1219 
ReadXIInstrument(INSTRUMENTINDEX nInstr,FileReader & file)1220 bool CSoundFile::ReadXIInstrument(INSTRUMENTINDEX nInstr, FileReader &file)
1221 {
1222 	file.Rewind();
1223 
1224 	XIInstrumentHeader fileHeader;
1225 	if(!file.ReadStruct(fileHeader)
1226 		|| memcmp(fileHeader.signature, "Extended Instrument: ", 21)
1227 		|| fileHeader.version != XIInstrumentHeader::fileVersion
1228 		|| fileHeader.eof != 0x1A)
1229 	{
1230 		return false;
1231 	}
1232 
1233 	ModInstrument *pIns = new (std::nothrow) ModInstrument();
1234 	if(pIns == nullptr)
1235 	{
1236 		return false;
1237 	}
1238 
1239 	DestroyInstrument(nInstr, deleteAssociatedSamples);
1240 	if(nInstr > m_nInstruments)
1241 	{
1242 		m_nInstruments = nInstr;
1243 	}
1244 	Instruments[nInstr] = pIns;
1245 
1246 	fileHeader.ConvertToMPT(*pIns);
1247 
1248 	// Translate sample map and find available sample slots
1249 	std::vector<SAMPLEINDEX> sampleMap(fileHeader.numSamples);
1250 	SAMPLEINDEX maxSmp = 0;
1251 
1252 	for(size_t i = 0 + 12; i < 96 + 12; i++)
1253 	{
1254 		if(pIns->Keyboard[i] >= fileHeader.numSamples)
1255 		{
1256 			continue;
1257 		}
1258 
1259 		if(sampleMap[pIns->Keyboard[i]] == 0)
1260 		{
1261 			// Find slot for this sample
1262 			maxSmp = GetNextFreeSample(nInstr, maxSmp + 1);
1263 			if(maxSmp != SAMPLEINDEX_INVALID)
1264 			{
1265 				sampleMap[pIns->Keyboard[i]] = maxSmp;
1266 			}
1267 		}
1268 		pIns->Keyboard[i] = sampleMap[pIns->Keyboard[i]];
1269 	}
1270 
1271 	if(m_nSamples < maxSmp)
1272 	{
1273 		m_nSamples = maxSmp;
1274 	}
1275 
1276 	std::vector<SampleIO> sampleFlags(fileHeader.numSamples);
1277 
1278 	// Read sample headers
1279 	for(SAMPLEINDEX i = 0; i < fileHeader.numSamples; i++)
1280 	{
1281 		XMSample sampleHeader;
1282 		if(!file.ReadStruct(sampleHeader)
1283 			|| !sampleMap[i])
1284 		{
1285 			continue;
1286 		}
1287 
1288 		ModSample &mptSample = Samples[sampleMap[i]];
1289 		sampleHeader.ConvertToMPT(mptSample);
1290 		fileHeader.instrument.ApplyAutoVibratoToMPT(mptSample);
1291 		mptSample.Convert(MOD_TYPE_XM, GetType());
1292 		if(GetType() != MOD_TYPE_XM && fileHeader.numSamples == 1)
1293 		{
1294 			// No need to pan that single sample, thank you...
1295 			mptSample.uFlags &= ~CHN_PANNING;
1296 		}
1297 
1298 		mptSample.filename = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name);
1299 		m_szNames[sampleMap[i]] = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name);
1300 
1301 		sampleFlags[i] = sampleHeader.GetSampleFormat();
1302 	}
1303 
1304 	// Read sample data
1305 	for(SAMPLEINDEX i = 0; i < fileHeader.numSamples; i++)
1306 	{
1307 		if(sampleMap[i])
1308 		{
1309 			sampleFlags[i].ReadSample(Samples[sampleMap[i]], file);
1310 			Samples[sampleMap[i]].PrecomputeLoops(*this, false);
1311 		}
1312 	}
1313 
1314 	// Read MPT crap
1315 	ReadExtendedInstrumentProperties(pIns, file);
1316 	pIns->Convert(MOD_TYPE_XM, GetType());
1317 	pIns->Sanitize(GetType());
1318 	return true;
1319 }
1320 
1321 
1322 #ifndef MODPLUG_NO_FILESAVE
1323 
SaveXIInstrument(INSTRUMENTINDEX nInstr,std::ostream & f) const1324 bool CSoundFile::SaveXIInstrument(INSTRUMENTINDEX nInstr, std::ostream &f) const
1325 {
1326 	ModInstrument *pIns = Instruments[nInstr];
1327 	if(pIns == nullptr)
1328 	{
1329 		return false;
1330 	}
1331 
1332 	// Create file header
1333 	XIInstrumentHeader header;
1334 	header.ConvertToXM(*pIns, false);
1335 
1336 	const std::vector<SAMPLEINDEX> samples = header.instrument.GetSampleList(*pIns, false);
1337 	if(samples.size() > 0 && samples[0] <= GetNumSamples())
1338 	{
1339 		// Copy over auto-vibrato settings of first sample
1340 		header.instrument.ApplyAutoVibratoToXM(Samples[samples[0]], GetType());
1341 	}
1342 
1343 	mpt::IO::Write(f, header);
1344 
1345 	std::vector<SampleIO> sampleFlags(samples.size());
1346 
1347 	// XI Sample Headers
1348 	for(SAMPLEINDEX i = 0; i < samples.size(); i++)
1349 	{
1350 		XMSample xmSample;
1351 		if(samples[i] <= GetNumSamples())
1352 		{
1353 			xmSample.ConvertToXM(Samples[samples[i]], GetType(), false);
1354 		} else
1355 		{
1356 			MemsetZero(xmSample);
1357 		}
1358 		sampleFlags[i] = xmSample.GetSampleFormat();
1359 
1360 		mpt::String::WriteBuf(mpt::String::spacePadded, xmSample.name) = m_szNames[samples[i]];
1361 
1362 		mpt::IO::Write(f, xmSample);
1363 	}
1364 
1365 	// XI Sample Data
1366 	for(SAMPLEINDEX i = 0; i < samples.size(); i++)
1367 	{
1368 		if(samples[i] <= GetNumSamples())
1369 		{
1370 			sampleFlags[i].WriteSample(f, Samples[samples[i]]);
1371 		}
1372 	}
1373 
1374 	// Write 'MPTX' extension tag
1375 	mpt::IO::WriteText(f, "XTPM");
1376 	WriteInstrumentHeaderStructOrField(pIns, f);	// Write full extended header.
1377 
1378 	return true;
1379 }
1380 
1381 #endif // MODPLUG_NO_FILESAVE
1382 
1383 
1384 // Read first sample from XI file into a sample slot
ReadXISample(SAMPLEINDEX nSample,FileReader & file)1385 bool CSoundFile::ReadXISample(SAMPLEINDEX nSample, FileReader &file)
1386 {
1387 	file.Rewind();
1388 
1389 	XIInstrumentHeader fileHeader;
1390 	if(!file.ReadStruct(fileHeader)
1391 		|| !file.CanRead(sizeof(XMSample))
1392 		|| memcmp(fileHeader.signature, "Extended Instrument: ", 21)
1393 		|| fileHeader.version != XIInstrumentHeader::fileVersion
1394 		|| fileHeader.eof != 0x1A
1395 		|| fileHeader.numSamples == 0)
1396 	{
1397 		return false;
1398 	}
1399 
1400 	if(m_nSamples < nSample)
1401 	{
1402 		m_nSamples = nSample;
1403 	}
1404 
1405 	uint16 numSamples = fileHeader.numSamples;
1406 	FileReader::off_t samplePos = sizeof(XIInstrumentHeader) + numSamples * sizeof(XMSample);
1407 	// Preferrably read the middle-C sample
1408 	auto sample = fileHeader.instrument.sampleMap[48];
1409 	if(sample >= fileHeader.numSamples)
1410 		sample = 0;
1411 	XMSample sampleHeader;
1412 	while(sample--)
1413 	{
1414 		file.ReadStruct(sampleHeader);
1415 		samplePos += sampleHeader.length;
1416 	}
1417 	file.ReadStruct(sampleHeader);
1418 	// Gotta skip 'em all!
1419 	file.Seek(samplePos);
1420 
1421 	DestroySampleThreadsafe(nSample);
1422 
1423 	ModSample &mptSample = Samples[nSample];
1424 	sampleHeader.ConvertToMPT(mptSample);
1425 	if(GetType() != MOD_TYPE_XM)
1426 	{
1427 		// No need to pan that single sample, thank you...
1428 		mptSample.uFlags.reset(CHN_PANNING);
1429 	}
1430 	fileHeader.instrument.ApplyAutoVibratoToMPT(mptSample);
1431 	mptSample.Convert(MOD_TYPE_XM, GetType());
1432 
1433 	mptSample.filename = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name);
1434 	m_szNames[nSample] = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name);
1435 
1436 	// Read sample data
1437 	sampleHeader.GetSampleFormat().ReadSample(Samples[nSample], file);
1438 	Samples[nSample].PrecomputeLoops(*this, false);
1439 
1440 	return true;
1441 }
1442 
1443 
1444 ///////////////
1445 // Apple CAF //
1446 
1447 
1448 struct CAFFileHeader
1449 {
1450 	uint32be mFileType;
1451 	uint16be mFileVersion;
1452 	uint16be mFileFlags;
1453 };
1454 
1455 MPT_BINARY_STRUCT(CAFFileHeader, 8)
1456 
1457 
1458 struct CAFChunkHeader
1459 {
1460 	uint32be mChunkType;
1461 	int64be  mChunkSize;
1462 };
1463 
1464 MPT_BINARY_STRUCT(CAFChunkHeader, 12)
1465 
1466 
1467 struct CAFChunk
1468 {
1469 	enum ChunkIdentifiers
1470 	{
1471 		iddesc = MagicBE("desc"),
1472 		iddata = MagicBE("data"),
1473 		idstrg = MagicBE("strg"),
1474 		idinfo = MagicBE("info")
1475 	};
1476 
1477 	CAFChunkHeader header;
1478 
GetLengthCAFChunk1479 	FileReader::off_t GetLength() const
1480 	{
1481 		int64 length = header.mChunkSize;
1482 		if(length == -1)
1483 		{
1484 			length = std::numeric_limits<int64>::max(); // spec
1485 		}
1486 		if(length < 0)
1487 		{
1488 			length = std::numeric_limits<int64>::max(); // heuristic
1489 		}
1490 		return mpt::saturate_cast<FileReader::off_t>(length);
1491 	}
1492 
GetIDCAFChunk1493 	ChunkIdentifiers GetID() const
1494 	{
1495 		return static_cast<ChunkIdentifiers>(header.mChunkType.get());
1496 	}
1497 };
1498 
1499 MPT_BINARY_STRUCT(CAFChunk, 12)
1500 
1501 
1502 enum {
1503 	CAFkAudioFormatLinearPCM      = MagicBE("lpcm"),
1504 	CAFkAudioFormatAppleIMA4      = MagicBE("ima4"),
1505 	CAFkAudioFormatMPEG4AAC       = MagicBE("aac "),
1506 	CAFkAudioFormatMACE3          = MagicBE("MAC3"),
1507 	CAFkAudioFormatMACE6          = MagicBE("MAC6"),
1508 	CAFkAudioFormatULaw           = MagicBE("ulaw"),
1509 	CAFkAudioFormatALaw           = MagicBE("alaw"),
1510 	CAFkAudioFormatMPEGLayer1     = MagicBE(".mp1"),
1511 	CAFkAudioFormatMPEGLayer2     = MagicBE(".mp2"),
1512 	CAFkAudioFormatMPEGLayer3     = MagicBE(".mp3"),
1513 	CAFkAudioFormatAppleLossless  = MagicBE("alac")
1514 };
1515 
1516 
1517 enum {
1518 	CAFkCAFLinearPCMFormatFlagIsFloat         = (1L << 0),
1519 	CAFkCAFLinearPCMFormatFlagIsLittleEndian  = (1L << 1)
1520 };
1521 
1522 
1523 struct CAFAudioFormat
1524 {
1525 	float64be mSampleRate;
1526 	uint32be  mFormatID;
1527 	uint32be  mFormatFlags;
1528 	uint32be  mBytesPerPacket;
1529 	uint32be  mFramesPerPacket;
1530 	uint32be  mChannelsPerFrame;
1531 	uint32be  mBitsPerChannel;
1532 };
1533 
1534 MPT_BINARY_STRUCT(CAFAudioFormat, 32)
1535 
1536 
CAFSetTagFromInfoKey(mpt::ustring & dst,const std::map<std::string,std::string> & infoMap,const std::string & key)1537 static void CAFSetTagFromInfoKey(mpt::ustring & dst, const std::map<std::string,std::string> & infoMap, const std::string & key)
1538 {
1539 	auto item = infoMap.find(key);
1540 	if(item == infoMap.end())
1541 	{
1542 		return;
1543 	}
1544 	if(item->second.empty())
1545 	{
1546 		return;
1547 	}
1548 	dst = mpt::ToUnicode(mpt::Charset::UTF8, item->second);
1549 }
1550 
1551 
ReadCAFSample(SAMPLEINDEX nSample,FileReader & file,bool mayNormalize)1552 bool CSoundFile::ReadCAFSample(SAMPLEINDEX nSample, FileReader &file, bool mayNormalize)
1553 {
1554 	file.Rewind();
1555 
1556 	CAFFileHeader fileHeader;
1557 	if(!file.ReadStruct(fileHeader))
1558 	{
1559 		return false;
1560 	}
1561 	if(fileHeader.mFileType != MagicBE("caff"))
1562 	{
1563 		return false;
1564 	}
1565 	if(fileHeader.mFileVersion != 1)
1566 	{
1567 		return false;
1568 	}
1569 
1570 	auto chunkList = file.ReadChunks<CAFChunk>(0);
1571 
1572 	CAFAudioFormat audioFormat;
1573 	if(!chunkList.GetChunk(CAFChunk::iddesc).ReadStruct(audioFormat))
1574 	{
1575 		return false;
1576 	}
1577 	if(audioFormat.mSampleRate <= 0.0)
1578 	{
1579 		return false;
1580 	}
1581 	if(audioFormat.mChannelsPerFrame == 0)
1582 	{
1583 		return false;
1584 	}
1585 	if(audioFormat.mChannelsPerFrame > 2)
1586 	{
1587 		return false;
1588 	}
1589 
1590 	if(!mpt::in_range<uint32>(mpt::saturate_round<int64>(audioFormat.mSampleRate)))
1591 	{
1592 		return false;
1593 	}
1594 	uint32 sampleRate = static_cast<uint32>(mpt::saturate_round<int64>(audioFormat.mSampleRate));
1595 	if(sampleRate <= 0)
1596 	{
1597 		return false;
1598 	}
1599 
1600 	SampleIO sampleIO;
1601 	if(audioFormat.mFormatID == CAFkAudioFormatLinearPCM)
1602 	{
1603 		if(audioFormat.mFramesPerPacket != 1)
1604 		{
1605 			return false;
1606 		}
1607 		if(audioFormat.mBytesPerPacket == 0)
1608 		{
1609 			return false;
1610 		}
1611 		if(audioFormat.mBitsPerChannel == 0)
1612 		{
1613 			return false;
1614 		}
1615 		if(audioFormat.mFormatFlags & CAFkCAFLinearPCMFormatFlagIsFloat)
1616 		{
1617 			if(audioFormat.mBitsPerChannel != 32 && audioFormat.mBitsPerChannel != 64)
1618 			{
1619 				return false;
1620 			}
1621 			if(audioFormat.mBytesPerPacket != audioFormat.mChannelsPerFrame * audioFormat.mBitsPerChannel/8)
1622 			{
1623 				return false;
1624 			}
1625 		}
1626 		if(audioFormat.mBytesPerPacket % audioFormat.mChannelsPerFrame != 0)
1627 		{
1628 			return false;
1629 		}
1630 		if(audioFormat.mBytesPerPacket / audioFormat.mChannelsPerFrame != 1
1631 			&& audioFormat.mBytesPerPacket / audioFormat.mChannelsPerFrame != 2
1632 			&& audioFormat.mBytesPerPacket / audioFormat.mChannelsPerFrame != 3
1633 			&& audioFormat.mBytesPerPacket / audioFormat.mChannelsPerFrame != 4
1634 			&& audioFormat.mBytesPerPacket / audioFormat.mChannelsPerFrame != 8
1635 			)
1636 		{
1637 			return false;
1638 		}
1639 		SampleIO::Channels channels = (audioFormat.mChannelsPerFrame == 2) ? SampleIO::stereoInterleaved : SampleIO::mono;
1640 		SampleIO::Endianness endianness = (audioFormat.mFormatFlags & CAFkCAFLinearPCMFormatFlagIsLittleEndian) ? SampleIO::littleEndian : SampleIO::bigEndian;
1641 		SampleIO::Encoding encoding = (audioFormat.mFormatFlags & CAFkCAFLinearPCMFormatFlagIsFloat) ? SampleIO::floatPCM : SampleIO::signedPCM;
1642 		SampleIO::Bitdepth bitdepth = static_cast<SampleIO::Bitdepth>((audioFormat.mBytesPerPacket / audioFormat.mChannelsPerFrame) * 8);
1643 		sampleIO = SampleIO(bitdepth, channels, endianness, encoding);
1644 	} else
1645 	{
1646 		return false;
1647 	}
1648 
1649 	if(mayNormalize)
1650 	{
1651 		sampleIO.MayNormalize();
1652 	}
1653 
1654 	/*
1655 	std::map<uint32, std::string> stringMap; // UTF-8
1656 	if(chunkList.ChunkExists(CAFChunk::idstrg))
1657 	{
1658 		FileReader stringsChunk = chunkList.GetChunk(CAFChunk::idstrg);
1659 		uint32 numEntries = stringsChunk.ReadUint32BE();
1660 		if(stringsChunk.Skip(12 * numEntries))
1661 		{
1662 			FileReader stringData = stringsChunk.ReadChunk(stringsChunk.BytesLeft());
1663 			stringsChunk.Seek(4);
1664 			for(uint32 entry = 0; entry < numEntries && stringsChunk.CanRead(12); entry++)
1665 			{
1666 				uint32 stringID = stringsChunk.ReadUint32BE();
1667 				int64 offset = stringsChunk.ReadIntBE<int64>();
1668 				if(offset >= 0 && mpt::in_range<FileReader::off_t>(offset))
1669 				{
1670 					stringData.Seek(mpt::saturate_cast<FileReader::off_t>(offset));
1671 					std::string str;
1672 					if(stringData.ReadNullString(str))
1673 					{
1674 						stringMap[stringID] = str;
1675 					}
1676 				}
1677 			}
1678 		}
1679 	}
1680 	*/
1681 
1682 	std::map<std::string, std::string> infoMap; // UTF-8
1683 	if(chunkList.ChunkExists(CAFChunk::idinfo))
1684 	{
1685 		FileReader informationChunk = chunkList.GetChunk(CAFChunk::idinfo);
1686 		uint32 numEntries = informationChunk.ReadUint32BE();
1687 		for(uint32 entry = 0; entry < numEntries && informationChunk.CanRead(2); entry++)
1688 		{
1689 			std::string key;
1690 			std::string value;
1691 			if(!informationChunk.ReadNullString(key))
1692 			{
1693 				break;
1694 			}
1695 			if(!informationChunk.ReadNullString(value))
1696 			{
1697 				break;
1698 			}
1699 			if(!key.empty() && !value.empty())
1700 			{
1701 				infoMap[key] = value;
1702 			}
1703 		}
1704 	}
1705 	FileTags tags;
1706 	CAFSetTagFromInfoKey(tags.bpm, infoMap, "tempo");
1707 	//CAFSetTagFromInfoKey(void, infoMap, "key signature");
1708 	//CAFSetTagFromInfoKey(void, infoMap, "time signature");
1709 	CAFSetTagFromInfoKey(tags.artist, infoMap, "artist");
1710 	CAFSetTagFromInfoKey(tags.album, infoMap, "album");
1711 	CAFSetTagFromInfoKey(tags.trackno, infoMap, "track number");
1712 	CAFSetTagFromInfoKey(tags.year, infoMap, "year");
1713 	//CAFSetTagFromInfoKey(void, infoMap, "composer");
1714 	//CAFSetTagFromInfoKey(void, infoMap, "lyricist");
1715 	CAFSetTagFromInfoKey(tags.genre, infoMap, "genre");
1716 	CAFSetTagFromInfoKey(tags.title, infoMap, "title");
1717 	//CAFSetTagFromInfoKey(void, infoMap, "recorded date");
1718 	CAFSetTagFromInfoKey(tags.comments, infoMap, "comments");
1719 	//CAFSetTagFromInfoKey(void, infoMap, "copyright");
1720 	//CAFSetTagFromInfoKey(void, infoMap, "source encoder");
1721 	CAFSetTagFromInfoKey(tags.encoder, infoMap, "encoding application");
1722 	//CAFSetTagFromInfoKey(void, infoMap, "nominal bit rate");
1723 	//CAFSetTagFromInfoKey(void, infoMap, "channel layout");
1724 	//CAFSetTagFromInfoKey(tags.url, infoMap, void);
1725 
1726 	if(!chunkList.ChunkExists(CAFChunk::iddata))
1727 	{
1728 		return false;
1729 	}
1730 	FileReader dataChunk = chunkList.GetChunk(CAFChunk::iddata);
1731 	dataChunk.Skip(4);  // edit count
1732 	FileReader audioData = dataChunk.ReadChunk(dataChunk.BytesLeft());
1733 
1734 	SmpLength length = mpt::saturate_cast<SmpLength>((audioData.GetLength() / audioFormat.mBytesPerPacket) * audioFormat.mFramesPerPacket);
1735 
1736 	ModSample &mptSample = Samples[nSample];
1737 	DestroySampleThreadsafe(nSample);
1738 	mptSample.Initialize();
1739 	mptSample.nLength = length;
1740 	mptSample.nC5Speed = sampleRate;
1741 
1742 	sampleIO.ReadSample(mptSample, audioData);
1743 
1744 	m_szNames[nSample] = mpt::ToCharset(GetCharsetInternal(), GetSampleNameFromTags(tags));
1745 
1746 	mptSample.Convert(MOD_TYPE_IT, GetType());
1747 	mptSample.PrecomputeLoops(*this, false);
1748 
1749 	return true;
1750 
1751 }
1752 
1753 
1754 
1755 /////////////////////////////////////////////////////////////////////////////////////////
1756 // AIFF File I/O
1757 
1758 // AIFF header
1759 struct AIFFHeader
1760 {
1761 	char     magic[4];	// FORM
1762 	uint32be length;	// Size of the file, not including magic and length
1763 	char     type[4];	// AIFF or AIFC
1764 };
1765 
1766 MPT_BINARY_STRUCT(AIFFHeader, 12)
1767 
1768 
1769 // General IFF Chunk header
1770 struct AIFFChunk
1771 {
1772 	// 32-Bit chunk identifiers
1773 	enum ChunkIdentifiers
1774 	{
1775 		idCOMM	= MagicBE("COMM"),
1776 		idSSND	= MagicBE("SSND"),
1777 		idINST	= MagicBE("INST"),
1778 		idMARK	= MagicBE("MARK"),
1779 		idNAME	= MagicBE("NAME"),
1780 	};
1781 
1782 	uint32be id;		// See ChunkIdentifiers
1783 	uint32be length;	// Chunk size without header
1784 
GetLengthAIFFChunk1785 	size_t GetLength() const
1786 	{
1787 		return length;
1788 	}
1789 
GetIDAIFFChunk1790 	ChunkIdentifiers GetID() const
1791 	{
1792 		return static_cast<ChunkIdentifiers>(id.get());
1793 	}
1794 };
1795 
1796 MPT_BINARY_STRUCT(AIFFChunk, 8)
1797 
1798 
1799 // "Common" chunk (in AIFC, a compression ID and compression name follows this header, but apart from that it's identical)
1800 struct AIFFCommonChunk
1801 {
1802 	uint16be numChannels;
1803 	uint32be numSampleFrames;
1804 	uint16be sampleSize;
1805 	uint8be  sampleRate[10];		// Sample rate in 80-Bit floating point
1806 
1807 	// Convert sample rate to integer
GetSampleRateAIFFCommonChunk1808 	uint32 GetSampleRate() const
1809 	{
1810 		uint32 mantissa = (sampleRate[2] << 24) | (sampleRate[3] << 16) | (sampleRate[4] << 8) | (sampleRate[5] << 0);
1811 		uint32 last = 0;
1812 		uint8 exp = 30 - sampleRate[1];
1813 
1814 		while(exp--)
1815 		{
1816 			last = mantissa;
1817 			mantissa >>= 1;
1818 		}
1819 		if(last & 1) mantissa++;
1820 		return mantissa;
1821 	}
1822 };
1823 
1824 MPT_BINARY_STRUCT(AIFFCommonChunk, 18)
1825 
1826 
1827 // Sound chunk
1828 struct AIFFSoundChunk
1829 {
1830 	uint32be offset;
1831 	uint32be blockSize;
1832 };
1833 
1834 MPT_BINARY_STRUCT(AIFFSoundChunk, 8)
1835 
1836 
1837 // Marker
1838 struct AIFFMarker
1839 {
1840 	uint16be id;
1841 	uint32be position;		// Position in sample
1842 	uint8be  nameLength;	// Not counting eventually existing padding byte in name string
1843 };
1844 
1845 MPT_BINARY_STRUCT(AIFFMarker, 7)
1846 
1847 
1848 // Instrument loop
1849 struct AIFFInstrumentLoop
1850 {
1851 	enum PlayModes
1852 	{
1853 		noLoop		= 0,
1854 		loopNormal	= 1,
1855 		loopBidi	= 2,
1856 	};
1857 
1858 	uint16be playMode;
1859 	uint16be beginLoop;	// Marker index
1860 	uint16be endLoop;	// Marker index
1861 };
1862 
1863 MPT_BINARY_STRUCT(AIFFInstrumentLoop, 6)
1864 
1865 
1866 struct AIFFInstrumentChunk
1867 {
1868 	uint8be  baseNote;
1869 	uint8be  detune;
1870 	uint8be  lowNote;
1871 	uint8be  highNote;
1872 	uint8be  lowVelocity;
1873 	uint8be  highVelocity;
1874 	uint16be gain;
1875 	AIFFInstrumentLoop sustainLoop;
1876 	AIFFInstrumentLoop releaseLoop;
1877 };
1878 
1879 MPT_BINARY_STRUCT(AIFFInstrumentChunk, 20)
1880 
1881 
ReadAIFFSample(SAMPLEINDEX nSample,FileReader & file,bool mayNormalize)1882 bool CSoundFile::ReadAIFFSample(SAMPLEINDEX nSample, FileReader &file, bool mayNormalize)
1883 {
1884 	file.Rewind();
1885 
1886 	// Verify header
1887 	AIFFHeader fileHeader;
1888 	if(!file.ReadStruct(fileHeader)
1889 		|| memcmp(fileHeader.magic, "FORM", 4)
1890 		|| (memcmp(fileHeader.type, "AIFF", 4) && memcmp(fileHeader.type, "AIFC", 4)))
1891 	{
1892 		return false;
1893 	}
1894 
1895 	auto chunks = file.ReadChunks<AIFFChunk>(2);
1896 
1897 	// Read COMM chunk
1898 	FileReader commChunk(chunks.GetChunk(AIFFChunk::idCOMM));
1899 	AIFFCommonChunk sampleInfo;
1900 	if(!commChunk.ReadStruct(sampleInfo))
1901 	{
1902 		return false;
1903 	}
1904 
1905 	// Is this a proper sample?
1906 	if(sampleInfo.numSampleFrames == 0
1907 		|| sampleInfo.numChannels < 1 || sampleInfo.numChannels > 2
1908 		|| sampleInfo.sampleSize < 1 || sampleInfo.sampleSize > 64)
1909 	{
1910 		return false;
1911 	}
1912 
1913 	// Read compression type in AIFF-C files.
1914 	uint8 compression[4] = { 'N', 'O', 'N', 'E' };
1915 	SampleIO::Endianness endian = SampleIO::bigEndian;
1916 	if(!memcmp(fileHeader.type, "AIFC", 4))
1917 	{
1918 		if(!commChunk.ReadArray(compression))
1919 		{
1920 			return false;
1921 		}
1922 		if(!memcmp(compression, "twos", 4))
1923 		{
1924 			endian = SampleIO::littleEndian;
1925 		}
1926 	}
1927 
1928 	// Read SSND chunk
1929 	FileReader soundChunk(chunks.GetChunk(AIFFChunk::idSSND));
1930 	AIFFSoundChunk sampleHeader;
1931 	if(!soundChunk.ReadStruct(sampleHeader))
1932 	{
1933 		return false;
1934 	}
1935 
1936 	SampleIO::Bitdepth bitDepth;
1937 	switch((sampleInfo.sampleSize - 1) / 8)
1938 	{
1939 	default:
1940 	case 0: bitDepth = SampleIO::_8bit; break;
1941 	case 1: bitDepth = SampleIO::_16bit; break;
1942 	case 2: bitDepth = SampleIO::_24bit; break;
1943 	case 3: bitDepth = SampleIO::_32bit; break;
1944 	case 7: bitDepth = SampleIO::_64bit; break;
1945 	}
1946 
1947 	SampleIO sampleIO(bitDepth,
1948 		(sampleInfo.numChannels == 2) ? SampleIO::stereoInterleaved : SampleIO::mono,
1949 		endian,
1950 		SampleIO::signedPCM);
1951 
1952 	if(!memcmp(compression, "fl32", 4) || !memcmp(compression, "FL32", 4) || !memcmp(compression, "fl64", 4) || !memcmp(compression, "FL64", 4))
1953 	{
1954 		sampleIO |= SampleIO::floatPCM;
1955 	} else if(!memcmp(compression, "alaw", 4) || !memcmp(compression, "ALAW", 4))
1956 	{
1957 		sampleIO |= SampleIO::aLaw;
1958 		sampleIO |= SampleIO::_16bit;
1959 	} else if(!memcmp(compression, "ulaw", 4) || !memcmp(compression, "ULAW", 4))
1960 	{
1961 		sampleIO |= SampleIO::uLaw;
1962 		sampleIO |= SampleIO::_16bit;
1963 	} else if(!memcmp(compression, "raw ", 4))
1964 	{
1965 		sampleIO |= SampleIO::unsignedPCM;
1966 	}
1967 
1968 	if(mayNormalize)
1969 	{
1970 		sampleIO.MayNormalize();
1971 	}
1972 
1973 	if(soundChunk.CanRead(sampleHeader.offset))
1974 	{
1975 		soundChunk.Skip(sampleHeader.offset);
1976 	}
1977 
1978 	ModSample &mptSample = Samples[nSample];
1979 	DestroySampleThreadsafe(nSample);
1980 	mptSample.Initialize();
1981 	mptSample.nLength = sampleInfo.numSampleFrames;
1982 	mptSample.nC5Speed = sampleInfo.GetSampleRate();
1983 
1984 	sampleIO.ReadSample(mptSample, soundChunk);
1985 
1986 	// Read MARK and INST chunk to extract sample loops
1987 	FileReader markerChunk(chunks.GetChunk(AIFFChunk::idMARK));
1988 	AIFFInstrumentChunk instrHeader;
1989 	if(markerChunk.IsValid() && chunks.GetChunk(AIFFChunk::idINST).ReadStruct(instrHeader))
1990 	{
1991 		uint16 numMarkers = markerChunk.ReadUint16BE();
1992 
1993 		std::vector<AIFFMarker> markers;
1994 		markers.reserve(numMarkers);
1995 		for(size_t i = 0; i < numMarkers; i++)
1996 		{
1997 			AIFFMarker marker;
1998 			if(!markerChunk.ReadStruct(marker))
1999 			{
2000 				break;
2001 			}
2002 			markers.push_back(marker);
2003 			markerChunk.Skip(marker.nameLength + ((marker.nameLength % 2u) == 0 ? 1 : 0));
2004 		}
2005 
2006 		if(instrHeader.sustainLoop.playMode != AIFFInstrumentLoop::noLoop)
2007 		{
2008 			mptSample.uFlags.set(CHN_SUSTAINLOOP);
2009 			mptSample.uFlags.set(CHN_PINGPONGSUSTAIN, instrHeader.sustainLoop.playMode == AIFFInstrumentLoop::loopBidi);
2010 		}
2011 
2012 		if(instrHeader.releaseLoop.playMode != AIFFInstrumentLoop::noLoop)
2013 		{
2014 			mptSample.uFlags.set(CHN_LOOP);
2015 			mptSample.uFlags.set(CHN_PINGPONGLOOP, instrHeader.releaseLoop.playMode == AIFFInstrumentLoop::loopBidi);
2016 		}
2017 
2018 		// Read markers
2019 		for(const auto &m : markers)
2020 		{
2021 			if(m.id == instrHeader.sustainLoop.beginLoop)
2022 				mptSample.nSustainStart = m.position;
2023 			if(m.id == instrHeader.sustainLoop.endLoop)
2024 				mptSample.nSustainEnd = m.position;
2025 			if(m.id == instrHeader.releaseLoop.beginLoop)
2026 				mptSample.nLoopStart = m.position;
2027 			if(m.id == instrHeader.releaseLoop.endLoop)
2028 				mptSample.nLoopEnd = m.position;
2029 		}
2030 		mptSample.SanitizeLoops();
2031 	}
2032 
2033 	// Extract sample name
2034 	FileReader nameChunk(chunks.GetChunk(AIFFChunk::idNAME));
2035 	if(nameChunk.IsValid())
2036 	{
2037 		nameChunk.ReadString<mpt::String::spacePadded>(m_szNames[nSample], nameChunk.GetLength());
2038 	} else
2039 	{
2040 		m_szNames[nSample] = "";
2041 	}
2042 
2043 	mptSample.Convert(MOD_TYPE_IT, GetType());
2044 	mptSample.PrecomputeLoops(*this, false);
2045 	return true;
2046 }
2047 
2048 
AUIsAnnotationLineWithField(const std::string & line)2049 static bool AUIsAnnotationLineWithField(const std::string &line)
2050 {
2051 	std::size_t pos = line.find('=');
2052 	if(pos == std::string::npos)
2053 	{
2054 		return false;
2055 	}
2056 	if(pos == 0)
2057 	{
2058 		return false;
2059 	}
2060 	const auto field = std::string_view(line).substr(0, pos);
2061 	// Scan for invalid chars
2062 	for(auto c : field)
2063 	{
2064 		if(!mpt::is_in_range(c, 'a', 'z') && !mpt::is_in_range(c, 'A', 'Z') && !mpt::is_in_range(c, '0', '9') && c != '-' && c != '_')
2065 		{
2066 			return false;
2067 		}
2068 	}
2069 	return true;
2070 }
2071 
AUTrimFieldFromAnnotationLine(const std::string & line)2072 static std::string AUTrimFieldFromAnnotationLine(const std::string &line)
2073 {
2074 	if(!AUIsAnnotationLineWithField(line))
2075 	{
2076 		return line;
2077 	}
2078 	std::size_t pos = line.find('=');
2079 	return line.substr(pos + 1);
2080 }
2081 
AUGetAnnotationFieldFromLine(const std::string & line)2082 static std::string AUGetAnnotationFieldFromLine(const std::string &line)
2083 {
2084 	if(!AUIsAnnotationLineWithField(line))
2085 	{
2086 		return std::string();
2087 	}
2088 	std::size_t pos = line.find('=');
2089 	return line.substr(0, pos);
2090 }
2091 
ReadAUSample(SAMPLEINDEX nSample,FileReader & file,bool mayNormalize)2092 bool CSoundFile::ReadAUSample(SAMPLEINDEX nSample, FileReader &file, bool mayNormalize)
2093 {
2094 	file.Rewind();
2095 
2096 	// Verify header
2097 	const auto magic = file.ReadArray<char, 4>();
2098 	const bool bigEndian = !std::memcmp(magic.data(), ".snd", 4);
2099 	const bool littleEndian = !std::memcmp(magic.data(), "dns.", 4);
2100 	if(!bigEndian && !littleEndian)
2101 		return false;
2102 
2103 	auto readUint32 = std::bind(bigEndian ? &FileReader::ReadUint32BE : &FileReader::ReadUint32LE, file);
2104 
2105 	uint32 dataOffset = readUint32();  // must be divisible by 8 according to spec, however, there are files that ignore this requirement
2106 	uint32 dataSize = readUint32();
2107 	uint32 encoding = readUint32();
2108 	uint32 sampleRate = readUint32();
2109 	uint32 channels = readUint32();
2110 
2111 	// According to spec, a minimum 8 byte annotation field after the header fields is required,
2112 	// however, there are files in the wild that violate this requirement.
2113 	// Thus, check for 24 instead of 32 here.
2114 	if(dataOffset < 24) // data offset points inside header
2115 	{
2116 		return false;
2117 	}
2118 
2119 	if(channels < 1 || channels > 2)
2120 		return false;
2121 
2122 	SampleIO sampleIO(SampleIO::_8bit, channels == 1 ? SampleIO::mono : SampleIO::stereoInterleaved, bigEndian ? SampleIO::bigEndian : SampleIO::littleEndian, SampleIO::signedPCM);
2123 	switch(encoding)
2124 	{
2125 	case 1: sampleIO |= SampleIO::_16bit;         // u-law
2126 		sampleIO |= SampleIO::uLaw; break;
2127 	case 2: break;                                // 8-bit linear PCM
2128 	case 3: sampleIO |= SampleIO::_16bit; break;  // 16-bit linear PCM
2129 	case 4: sampleIO |= SampleIO::_24bit; break;  // 24-bit linear PCM
2130 	case 5: sampleIO |= SampleIO::_32bit; break;  // 32-bit linear PCM
2131 	case 6: sampleIO |= SampleIO::_32bit;         // 32-bit IEEE floating point
2132 		sampleIO |= SampleIO::floatPCM;
2133 		break;
2134 	case 7: sampleIO |= SampleIO::_64bit;         // 64-bit IEEE floating point
2135 		sampleIO |= SampleIO::floatPCM;
2136 		break;
2137 	case 27: sampleIO |= SampleIO::_16bit;        // a-law
2138 		sampleIO |= SampleIO::aLaw; break;
2139 	default: return false;
2140 	}
2141 
2142 	if(!file.LengthIsAtLeast(dataOffset))
2143 	{
2144 		return false;
2145 	}
2146 
2147 	FileTags tags;
2148 
2149 	// This reads annotation metadata as written by OpenMPT, sox, ffmpeg.
2150 	// Additionally, we fall back to just reading the whole field as a single comment.
2151 	// We only read up to the first \0 byte.
2152 	file.Seek(24);
2153 	std::string annotation;
2154 	file.ReadString<mpt::String::maybeNullTerminated>(annotation, dataOffset - 24);
2155 	annotation = mpt::replace(annotation, std::string("\r\n"), std::string("\n"));
2156 	annotation = mpt::replace(annotation, std::string("\r"), std::string("\n"));
2157 	mpt::Charset charset = mpt::IsUTF8(annotation) ? mpt::Charset::UTF8 : mpt::Charset::ISO8859_1;
2158 	const auto lines = mpt::String::Split<std::string>(annotation, "\n");
2159 	bool hasFields = false;
2160 	for(const auto &line : lines)
2161 	{
2162 		if(AUIsAnnotationLineWithField(line))
2163 		{
2164 			hasFields = true;
2165 			break;
2166 		}
2167 	}
2168 	if(hasFields)
2169 	{
2170 		std::map<std::string, std::vector<std::string>> linesPerField;
2171 		std::string lastField = "comment";
2172 		for(const auto &line : lines)
2173 		{
2174 			if(AUIsAnnotationLineWithField(line))
2175 			{
2176 				lastField = mpt::ToLowerCaseAscii(mpt::trim(AUGetAnnotationFieldFromLine(line)));
2177 			}
2178 			linesPerField[lastField].push_back(AUTrimFieldFromAnnotationLine(line));
2179 		}
2180 		tags.title    = mpt::ToUnicode(charset, mpt::String::Combine(linesPerField["title"  ], std::string("\n")));
2181 		tags.artist   = mpt::ToUnicode(charset, mpt::String::Combine(linesPerField["artist" ], std::string("\n")));
2182 		tags.album    = mpt::ToUnicode(charset, mpt::String::Combine(linesPerField["album"  ], std::string("\n")));
2183 		tags.trackno  = mpt::ToUnicode(charset, mpt::String::Combine(linesPerField["track"  ], std::string("\n")));
2184 		tags.genre    = mpt::ToUnicode(charset, mpt::String::Combine(linesPerField["genre"  ], std::string("\n")));
2185 		tags.comments = mpt::ToUnicode(charset, mpt::String::Combine(linesPerField["comment"], std::string("\n")));
2186 	} else
2187 	{
2188 		// Most applications tend to write their own name here,
2189 		// thus there is little use in interpreting the string as a title.
2190 		annotation = mpt::trim_right(annotation, std::string("\r\n"));
2191 		tags.comments = mpt::ToUnicode(charset, annotation);
2192 	}
2193 
2194 	file.Seek(dataOffset);
2195 
2196 	ModSample &mptSample = Samples[nSample];
2197 	DestroySampleThreadsafe(nSample);
2198 	mptSample.Initialize();
2199 	SmpLength length = mpt::saturate_cast<SmpLength>(file.BytesLeft());
2200 	if(dataSize != 0xFFFFFFFF)
2201 		LimitMax(length, dataSize);
2202 	mptSample.nLength = (length * 8u) / (sampleIO.GetEncodedBitsPerSample() * channels);
2203 	mptSample.nC5Speed = sampleRate;
2204 	m_szNames[nSample] = mpt::ToCharset(GetCharsetInternal(), GetSampleNameFromTags(tags));
2205 
2206 	if(mayNormalize)
2207 	{
2208 		sampleIO.MayNormalize();
2209 	}
2210 
2211 	sampleIO.ReadSample(mptSample, file);
2212 
2213 	mptSample.Convert(MOD_TYPE_IT, GetType());
2214 	mptSample.PrecomputeLoops(*this, false);
2215 	return true;
2216 }
2217 
2218 
2219 /////////////////////////////////////////////////////////////////////////////////////////
2220 // ITS Samples
2221 
2222 
ReadITSSample(SAMPLEINDEX nSample,FileReader & file,bool rewind)2223 bool CSoundFile::ReadITSSample(SAMPLEINDEX nSample, FileReader &file, bool rewind)
2224 {
2225 	if(rewind)
2226 	{
2227 		file.Rewind();
2228 	}
2229 
2230 	ITSample sampleHeader;
2231 	if(!file.ReadStruct(sampleHeader)
2232 		|| memcmp(sampleHeader.id, "IMPS", 4))
2233 	{
2234 		return false;
2235 	}
2236 	DestroySampleThreadsafe(nSample);
2237 
2238 	ModSample &sample = Samples[nSample];
2239 	file.Seek(sampleHeader.ConvertToMPT(sample));
2240 	m_szNames[nSample] = mpt::String::ReadBuf(mpt::String::spacePaddedNull, sampleHeader.name);
2241 
2242 	if(sample.uFlags[CHN_ADLIB])
2243 	{
2244 		OPLPatch patch;
2245 		file.ReadArray(patch);
2246 		sample.SetAdlib(true, patch);
2247 		InitOPL();
2248 		if(!SupportsOPL())
2249 		{
2250 			AddToLog(LogInformation, U_("OPL instruments are not supported by this format."));
2251 		}
2252 	} else if(!sample.uFlags[SMP_KEEPONDISK])
2253 	{
2254 		sampleHeader.GetSampleFormat().ReadSample(sample, file);
2255 	} else
2256 	{
2257 		// External sample
2258 		size_t strLen;
2259 		file.ReadVarInt(strLen);
2260 #ifdef MPT_EXTERNAL_SAMPLES
2261 		std::string filenameU8;
2262 		file.ReadString<mpt::String::maybeNullTerminated>(filenameU8, strLen);
2263 		mpt::PathString filename = mpt::PathString::FromUTF8(filenameU8);
2264 
2265 		if(!filename.empty())
2266 		{
2267 			if(file.GetOptionalFileName())
2268 			{
2269 				filename = filename.RelativePathToAbsolute(file.GetOptionalFileName()->GetPath());
2270 			}
2271 			if(!LoadExternalSample(nSample, filename))
2272 			{
2273 				AddToLog(LogWarning, U_("Unable to load sample: ") + filename.ToUnicode());
2274 			}
2275 		} else
2276 		{
2277 			sample.uFlags.reset(SMP_KEEPONDISK);
2278 		}
2279 #else
2280 		file.Skip(strLen);
2281 #endif // MPT_EXTERNAL_SAMPLES
2282 	}
2283 
2284 	sample.Convert(MOD_TYPE_IT, GetType());
2285 	sample.PrecomputeLoops(*this, false);
2286 	return true;
2287 }
2288 
2289 
ReadITISample(SAMPLEINDEX nSample,FileReader & file)2290 bool CSoundFile::ReadITISample(SAMPLEINDEX nSample, FileReader &file)
2291 {
2292 	ITInstrument instrumentHeader;
2293 
2294 	file.Rewind();
2295 	if(!file.ReadStruct(instrumentHeader)
2296 		|| memcmp(instrumentHeader.id, "IMPI", 4))
2297 	{
2298 		return false;
2299 	}
2300 	file.Rewind();
2301 	ModInstrument dummy;
2302 	ITInstrToMPT(file, dummy, instrumentHeader.trkvers);
2303 	// Old SchismTracker versions set nos=0
2304 	const SAMPLEINDEX nsamples = std::max(static_cast<SAMPLEINDEX>(instrumentHeader.nos), *std::max_element(std::begin(dummy.Keyboard), std::end(dummy.Keyboard)));
2305 	if(!nsamples)
2306 		return false;
2307 
2308 	// Preferrably read the middle-C sample
2309 	auto sample = dummy.Keyboard[NOTE_MIDDLEC - NOTE_MIN];
2310 	if(sample > 0)
2311 		sample--;
2312 	else
2313 		sample = 0;
2314 	file.Seek(file.GetPosition() + sample * sizeof(ITSample));
2315 	return ReadITSSample(nSample, file, false);
2316 }
2317 
2318 
ReadITIInstrument(INSTRUMENTINDEX nInstr,FileReader & file)2319 bool CSoundFile::ReadITIInstrument(INSTRUMENTINDEX nInstr, FileReader &file)
2320 {
2321 	ITInstrument instrumentHeader;
2322 	SAMPLEINDEX smp = 0;
2323 
2324 	file.Rewind();
2325 	if(!file.ReadStruct(instrumentHeader)
2326 		|| memcmp(instrumentHeader.id, "IMPI", 4))
2327 	{
2328 		return false;
2329 	}
2330 	if(nInstr > GetNumInstruments()) m_nInstruments = nInstr;
2331 
2332 	ModInstrument *pIns = new (std::nothrow) ModInstrument();
2333 	if(pIns == nullptr)
2334 	{
2335 		return false;
2336 	}
2337 
2338 	DestroyInstrument(nInstr, deleteAssociatedSamples);
2339 
2340 	Instruments[nInstr] = pIns;
2341 	file.Rewind();
2342 	ITInstrToMPT(file, *pIns, instrumentHeader.trkvers);
2343 	// Old SchismTracker versions set nos=0
2344 	const SAMPLEINDEX nsamples = std::max(static_cast<SAMPLEINDEX>(instrumentHeader.nos), *std::max_element(std::begin(pIns->Keyboard), std::end(pIns->Keyboard)));
2345 
2346 	// In order to properly compute the position, in file, of eventual extended settings
2347 	// such as "attack" we need to keep the "real" size of the last sample as those extra
2348 	// setting will follow this sample in the file
2349 	FileReader::off_t extraOffset = file.GetPosition();
2350 
2351 	// Reading Samples
2352 	std::vector<SAMPLEINDEX> samplemap(nsamples, 0);
2353 	for(SAMPLEINDEX i = 0; i < nsamples; i++)
2354 	{
2355 		smp = GetNextFreeSample(nInstr, smp + 1);
2356 		if(smp == SAMPLEINDEX_INVALID) break;
2357 		samplemap[i] = smp;
2358 		const FileReader::off_t offset = file.GetPosition();
2359 		if(!ReadITSSample(smp, file, false))
2360 			smp--;
2361 		extraOffset = std::max(extraOffset, file.GetPosition());
2362 		file.Seek(offset + sizeof(ITSample));
2363 	}
2364 	if(GetNumSamples() < smp) m_nSamples = smp;
2365 
2366 	// Adjust sample assignment
2367 	for(auto &sample : pIns->Keyboard)
2368 	{
2369 		if(sample > 0 && sample <= nsamples)
2370 		{
2371 			sample = samplemap[sample - 1];
2372 		}
2373 	}
2374 
2375 	if(file.Seek(extraOffset))
2376 	{
2377 		// Read MPT crap
2378 		ReadExtendedInstrumentProperties(pIns, file);
2379 	}
2380 
2381 	pIns->Convert(MOD_TYPE_IT, GetType());
2382 	pIns->Sanitize(GetType());
2383 
2384 	return true;
2385 }
2386 
2387 
2388 #ifndef MODPLUG_NO_FILESAVE
2389 
SaveITIInstrument(INSTRUMENTINDEX nInstr,std::ostream & f,const mpt::PathString & filename,bool compress,bool allowExternal) const2390 bool CSoundFile::SaveITIInstrument(INSTRUMENTINDEX nInstr, std::ostream &f, const mpt::PathString &filename, bool compress, bool allowExternal) const
2391 {
2392 	ITInstrument iti;
2393 	ModInstrument *pIns = Instruments[nInstr];
2394 
2395 	if((!pIns) || (filename.empty() && allowExternal)) return false;
2396 
2397 	auto instSize = iti.ConvertToIT(*pIns, false, *this);
2398 
2399 	// Create sample assignment table
2400 	std::vector<SAMPLEINDEX> smptable;
2401 	std::vector<uint8> smpmap(GetNumSamples(), 0);
2402 	for(size_t i = 0; i < NOTE_MAX; i++)
2403 	{
2404 		const SAMPLEINDEX smp = pIns->Keyboard[i];
2405 		if(smp && smp <= GetNumSamples())
2406 		{
2407 			if(!smpmap[smp - 1])
2408 			{
2409 				// We haven't considered this sample yet.
2410 				smptable.push_back(smp);
2411 				smpmap[smp - 1] = static_cast<uint8>(smptable.size());
2412 			}
2413 			iti.keyboard[i * 2 + 1] = smpmap[smp - 1];
2414 		} else
2415 		{
2416 			iti.keyboard[i * 2 + 1] = 0;
2417 		}
2418 	}
2419 	iti.nos = static_cast<uint8>(smptable.size());
2420 	smpmap.clear();
2421 
2422 	uint32 filePos = instSize;
2423 	mpt::IO::WritePartial(f, iti, instSize);
2424 
2425 	filePos += mpt::saturate_cast<uint32>(smptable.size() * sizeof(ITSample));
2426 
2427 	// Writing sample headers + data
2428 	std::vector<SampleIO> sampleFlags;
2429 	for(auto smp : smptable)
2430 	{
2431 		ITSample itss;
2432 		itss.ConvertToIT(Samples[smp], GetType(), compress, compress, allowExternal);
2433 		const bool isExternal = itss.cvt == ITSample::cvtExternalSample;
2434 
2435 		mpt::String::WriteBuf(mpt::String::nullTerminated, itss.name) = m_szNames[smp];
2436 
2437 		itss.samplepointer = filePos;
2438 		mpt::IO::Write(f, itss);
2439 
2440 		// Write sample
2441 		auto curPos = mpt::IO::TellWrite(f);
2442 		mpt::IO::SeekAbsolute(f, filePos);
2443 		if(!isExternal)
2444 		{
2445 			filePos += mpt::saturate_cast<uint32>(itss.GetSampleFormat(0x0214).WriteSample(f, Samples[smp]));
2446 		} else
2447 		{
2448 #ifdef MPT_EXTERNAL_SAMPLES
2449 			const std::string filenameU8 = GetSamplePath(smp).AbsolutePathToRelative(filename.GetPath()).ToUTF8();
2450 			const size_t strSize = filenameU8.size();
2451 			size_t intBytes = 0;
2452 			if(mpt::IO::WriteVarInt(f, strSize, &intBytes))
2453 			{
2454 				filePos += mpt::saturate_cast<uint32>(intBytes + strSize);
2455 				mpt::IO::WriteRaw(f, filenameU8.data(), strSize);
2456 			}
2457 #endif // MPT_EXTERNAL_SAMPLES
2458 		}
2459 		mpt::IO::SeekAbsolute(f, curPos);
2460 	}
2461 
2462 	mpt::IO::SeekEnd(f);
2463 	// Write 'MPTX' extension tag
2464 	mpt::IO::WriteRaw(f, "XTPM", 4);
2465 	WriteInstrumentHeaderStructOrField(pIns, f);	// Write full extended header.
2466 
2467 	return true;
2468 }
2469 
2470 #endif // MODPLUG_NO_FILESAVE
2471 
2472 
2473 ///////////////////////////////////////////////////////////////////////////////////////////////////
2474 // 8SVX / 16SVX / MAUD Samples
2475 
2476 // IFF File Header
2477 struct IFFHeader
2478 {
2479 	char     form[4];   // "FORM"
2480 	uint32be size;
2481 	char     magic[4];  // "8SVX", "16SV", "MAUD"
2482 };
2483 
2484 MPT_BINARY_STRUCT(IFFHeader, 12)
2485 
2486 
2487 // General IFF Chunk header
2488 struct IFFChunk
2489 {
2490 	// 32-Bit chunk identifiers
2491 	enum ChunkIdentifiers
2492 	{
2493 		// 8SVX / 16SV
2494 		idVHDR = MagicBE("VHDR"),
2495 		idBODY = MagicBE("BODY"),
2496 		idCHAN = MagicBE("CHAN"),
2497 
2498 		// MAUD
2499 		idMHDR = MagicBE("MHDR"),
2500 		idMDAT = MagicBE("MDAT"),
2501 
2502 		idNAME = MagicBE("NAME"),
2503 	};
2504 
2505 	uint32be id;      // See ChunkIdentifiers
2506 	uint32be length;  // Chunk size without header
2507 
GetLengthIFFChunk2508 	size_t GetLength() const
2509 	{
2510 		if(length == 0)  // Broken files
2511 			return std::numeric_limits<size_t>::max();
2512 		return length;
2513 	}
2514 
GetIDIFFChunk2515 	ChunkIdentifiers GetID() const
2516 	{
2517 		return static_cast<ChunkIdentifiers>(id.get());
2518 	}
2519 };
2520 
2521 MPT_BINARY_STRUCT(IFFChunk, 8)
2522 
2523 
2524 struct IFFSampleHeader
2525 {
2526 	uint32be oneShotHiSamples;   // Samples in the high octave 1-shot part
2527 	uint32be repeatHiSamples;    // Samples in the high octave repeat part
2528 	uint32be samplesPerHiCycle;  // Samples/cycle in high octave, else 0
2529 	uint16be samplesPerSec;      // Data sampling rate
2530 	uint8be  octave;             // Octaves of waveforms
2531 	uint8be  compression;        // Data compression technique used
2532 	uint32be volume;
2533 };
2534 
2535 MPT_BINARY_STRUCT(IFFSampleHeader, 20)
2536 
2537 
ReadIFFSample(SAMPLEINDEX nSample,FileReader & file)2538 bool CSoundFile::ReadIFFSample(SAMPLEINDEX nSample, FileReader &file)
2539 {
2540 	file.Rewind();
2541 
2542 	IFFHeader fileHeader;
2543 	if(!file.ReadStruct(fileHeader)
2544 	   || memcmp(fileHeader.form, "FORM", 4)
2545 	   || (memcmp(fileHeader.magic, "8SVX", 4) && memcmp(fileHeader.magic, "16SV", 4) && memcmp(fileHeader.magic, "MAUD", 4)))
2546 	{
2547 		return false;
2548 	}
2549 
2550 	const auto chunks = file.ReadChunks<IFFChunk>(2);
2551 	FileReader sampleData;
2552 
2553 	SampleIO sampleIO(SampleIO::_8bit, SampleIO::mono, SampleIO::bigEndian, SampleIO::signedPCM);
2554 	uint32 numSamples = 0, sampleRate = 0, loopStart = 0, loopLength = 0, volume = 0;
2555 
2556 	if(!memcmp(fileHeader.magic, "MAUD", 4))
2557 	{
2558 		FileReader mhdrChunk = chunks.GetChunk(IFFChunk::idMHDR);
2559 		sampleData = chunks.GetChunk(IFFChunk::idMDAT);
2560 		if(!mhdrChunk.LengthIs(32)
2561 		   || !sampleData.IsValid())
2562 		{
2563 			return false;
2564 		}
2565 
2566 		numSamples = mhdrChunk.ReadUint32BE();
2567 		const uint16 bitsPerSample = mhdrChunk.ReadUint16BE();
2568 		mhdrChunk.Skip(2);  // bits per sample after decompression
2569 		sampleRate = mhdrChunk.ReadUint32BE();
2570 		const auto [clockDivide, channelInformation, numChannels, compressionType] = mhdrChunk.ReadArray<uint16be, 4>();
2571 		if(!clockDivide)
2572 			return false;
2573 		else
2574 			sampleRate /= clockDivide;
2575 
2576 		if(numChannels != (channelInformation + 1))
2577 			return false;
2578 		if(numChannels == 2)
2579 			sampleIO |= SampleIO::stereoInterleaved;
2580 
2581 		if(bitsPerSample == 8 && compressionType == 0)
2582 			sampleIO |= SampleIO::unsignedPCM;
2583 		else if(bitsPerSample == 8 && compressionType == 2)
2584 			sampleIO |= SampleIO::aLaw;
2585 		else if(bitsPerSample == 8 && compressionType == 3)
2586 			sampleIO |= SampleIO::uLaw;
2587 		else if(bitsPerSample == 16 && compressionType == 0)
2588 			sampleIO |= SampleIO::_16bit;
2589 		else
2590 			return false;
2591 	} else
2592 	{
2593 		FileReader vhdrChunk = chunks.GetChunk(IFFChunk::idVHDR);
2594 		FileReader chanChunk = chunks.GetChunk(IFFChunk::idCHAN);
2595 		sampleData           = chunks.GetChunk(IFFChunk::idBODY);
2596 		IFFSampleHeader sampleHeader;
2597 		if(!sampleData.IsValid()
2598 			|| !vhdrChunk.IsValid()
2599 			|| !vhdrChunk.ReadStruct(sampleHeader))
2600 		{
2601 			return false;
2602 		}
2603 
2604 		const uint8 bytesPerSample = memcmp(fileHeader.magic, "8SVX", 4) ? 2 : 1;
2605 		const uint8 numChannels    = chanChunk.ReadUint32BE() == 6 ? 2 : 1;
2606 		const uint8 bytesPerFrame  = bytesPerSample * numChannels;
2607 
2608 		// While this is an Amiga format, the 16SV version appears to be only used on PC, and only with little-endian sample data.
2609 		if(bytesPerSample == 2)
2610 			sampleIO = SampleIO(SampleIO::_16bit, SampleIO::mono, SampleIO::littleEndian, SampleIO::signedPCM);
2611 		if(numChannels == 2)
2612 			sampleIO |= SampleIO::stereoSplit;
2613 
2614 		loopStart  = sampleHeader.oneShotHiSamples / bytesPerFrame;
2615 		loopLength = sampleHeader.repeatHiSamples / bytesPerFrame;
2616 		sampleRate = sampleHeader.samplesPerSec;
2617 		volume     = sampleHeader.volume;
2618 		numSamples = mpt::saturate_cast<SmpLength>(sampleData.GetLength() / bytesPerFrame);
2619 	}
2620 
2621 	DestroySampleThreadsafe(nSample);
2622 	ModSample &sample = Samples[nSample];
2623 	sample.Initialize();
2624 	sample.nLength    = numSamples;
2625 	sample.nLoopStart = loopStart;
2626 	sample.nLoopEnd   = sample.nLoopStart + loopLength;
2627 	if((sample.nLoopStart + 4 < sample.nLoopEnd) && (sample.nLoopEnd <= sample.nLength))
2628 		sample.uFlags.set(CHN_LOOP);
2629 
2630 	sample.nC5Speed = sampleRate;
2631 	if(!sample.nC5Speed)
2632 		sample.nC5Speed = 22050;
2633 
2634 	sample.nVolume = static_cast<uint16>(volume / 256);
2635 	if(!sample.nVolume || sample.nVolume > 256)
2636 		sample.nVolume = 256;
2637 
2638 	sample.Convert(MOD_TYPE_IT, GetType());
2639 
2640 	FileReader nameChunk = chunks.GetChunk(IFFChunk::idNAME);
2641 	if(nameChunk.IsValid())
2642 		nameChunk.ReadString<mpt::String::maybeNullTerminated>(m_szNames[nSample], nameChunk.GetLength());
2643 	else
2644 		m_szNames[nSample] = "";
2645 
2646 	sampleIO.ReadSample(sample, sampleData);
2647 	sample.PrecomputeLoops(*this, false);
2648 
2649 	return true;
2650 }
2651 
2652 
2653 OPENMPT_NAMESPACE_END
2654