1 /*
2  * Load_ams.cpp
3  * ------------
4  * Purpose: AMS (Extreme's Tracker / Velvet Studio) module loader
5  * Notes  : Extreme was renamed to Velvet Development at some point,
6  *          and thus they also renamed their tracker from
7  *          "Extreme's Tracker" to "Velvet Studio".
8  *          While the two programs look rather similiar, the structure of both
9  *          programs' "AMS" format is significantly different in some places -
10  *          Velvet Studio is a rather advanced tracker in comparison to Extreme's Tracker.
11  *          The source code of Velvet Studio has been released into the
12  *          public domain in 2013: https://github.com/Patosc/VelvetStudio/commits/master
13  * Authors: OpenMPT Devs
14  * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
15  */
16 
17 
18 #include "stdafx.h"
19 #include "Loaders.h"
20 
21 
22 OPENMPT_NAMESPACE_BEGIN
23 
24 
25 // Read AMS or AMS2 (newVersion = true) pattern. At least this part of the format is more or less identical between the two trackers...
ReadAMSPattern(CPattern & pattern,bool newVersion,FileReader & patternChunk)26 static void ReadAMSPattern(CPattern &pattern, bool newVersion, FileReader &patternChunk)
27 {
28 	enum
29 	{
30 		emptyRow		= 0xFF,	// No commands on row
31 		endOfRowMask	= 0x80,	// If set, no more commands on this row
32 		noteMask		= 0x40,	// If set, no note+instr in this command
33 		channelMask		= 0x1F,	// Mask for extracting channel
34 
35 		// Note flags
36 		readNextCmd		= 0x80,	// One more command follows
37 		noteDataMask	= 0x7F,	// Extract note
38 
39 		// Command flags
40 		volCommand		= 0x40,	// Effect is compressed volume command
41 		commandMask		= 0x3F,	// Command or volume mask
42 	};
43 
44 	// Effect translation table for extended (non-Protracker) effects
45 	static constexpr ModCommand::COMMAND effTrans[] =
46 	{
47 		CMD_S3MCMDEX,		// Forward / Backward
48 		CMD_PORTAMENTOUP,	// Extra fine slide up
49 		CMD_PORTAMENTODOWN,	// Extra fine slide up
50 		CMD_RETRIG,			// Retrigger
51 		CMD_NONE,
52 		CMD_TONEPORTAVOL,	// Toneporta with fine volume slide
53 		CMD_VIBRATOVOL,		// Vibrato with fine volume slide
54 		CMD_NONE,
55 		CMD_PANNINGSLIDE,
56 		CMD_NONE,
57 		CMD_VOLUMESLIDE,	// Two times finder volume slide than Axx
58 		CMD_NONE,
59 		CMD_CHANNELVOLUME,	// Channel volume (0...127)
60 		CMD_PATTERNBREAK,	// Long pattern break (in hex)
61 		CMD_S3MCMDEX,		// Fine slide commands
62 		CMD_NONE,			// Fractional BPM
63 		CMD_KEYOFF,			// Key off at tick xx
64 		CMD_PORTAMENTOUP,	// Porta up, but uses all octaves (?)
65 		CMD_PORTAMENTODOWN,	// Porta down, but uses all octaves (?)
66 		CMD_NONE,
67 		CMD_NONE,
68 		CMD_NONE,
69 		CMD_NONE,
70 		CMD_NONE,
71 		CMD_NONE,
72 		CMD_NONE,
73 		CMD_GLOBALVOLSLIDE,	// Global volume slide
74 		CMD_NONE,
75 		CMD_GLOBALVOLUME,	// Global volume (0... 127)
76 	};
77 
78 	ModCommand dummy;
79 	for(ROWINDEX row = 0; row < pattern.GetNumRows(); row++)
80 	{
81 		PatternRow baseRow = pattern.GetRow(row);
82 		while(patternChunk.CanRead(1))
83 		{
84 			const uint8 flags = patternChunk.ReadUint8();
85 			if(flags == emptyRow)
86 			{
87 				break;
88 			}
89 
90 			const CHANNELINDEX chn = (flags & channelMask);
91 			ModCommand &m = chn < pattern.GetNumChannels() ? baseRow[chn] : dummy;
92 			bool moreCommands = true;
93 			if(!(flags & noteMask))
94 			{
95 				// Read note + instr
96 				uint8 note = patternChunk.ReadUint8();
97 				moreCommands = (note & readNextCmd) != 0;
98 				note &= noteDataMask;
99 
100 				if(note == 1)
101 				{
102 					m.note = NOTE_KEYOFF;
103 				} else if(note >= 2 && note <= 121 && newVersion)
104 				{
105 					m.note = note - 2 + NOTE_MIN;
106 				} else if(note >= 12 && note <= 108 && !newVersion)
107 				{
108 					m.note = note + 12 + NOTE_MIN;
109 				}
110 				m.instr = patternChunk.ReadUint8();
111 			}
112 
113 			while(moreCommands)
114 			{
115 				// Read one more effect command
116 				ModCommand origCmd = m;
117 				const uint8 command = patternChunk.ReadUint8(), effect = (command & commandMask);
118 				moreCommands = (command & readNextCmd) != 0;
119 
120 				if(command & volCommand)
121 				{
122 					m.volcmd = VOLCMD_VOLUME;
123 					m.vol = effect;
124 				} else
125 				{
126 					m.param = patternChunk.ReadUint8();
127 
128 					if(effect < 0x10)
129 					{
130 						// PT commands
131 						m.command = effect;
132 						CSoundFile::ConvertModCommand(m);
133 
134 						// Post-fix some commands
135 						switch(m.command)
136 						{
137 						case CMD_PANNING8:
138 							// 4-Bit panning
139 							m.command = CMD_PANNING8;
140 							m.param = (m.param & 0x0F) * 0x11;
141 							break;
142 
143 						case CMD_VOLUME:
144 							m.command = CMD_NONE;
145 							m.volcmd = VOLCMD_VOLUME;
146 							m.vol = static_cast<ModCommand::VOL>(std::min((m.param + 1) / 2, 64));
147 							break;
148 
149 						case CMD_MODCMDEX:
150 							if(m.param == 0x80)
151 							{
152 								// Break sample loop (cut after loop)
153 								m.command = CMD_NONE;
154 							} else
155 							{
156 								m.ExtendedMODtoS3MEffect();
157 							}
158 							break;
159 						}
160 					} else if(effect < 0x10 + mpt::array_size<decltype(effTrans)>::size)
161 					{
162 						// Extended commands
163 						m.command = effTrans[effect - 0x10];
164 
165 						// Post-fix some commands
166 						switch(effect)
167 						{
168 						case 0x10:
169 							// Play sample forwards / backwards
170 							if(m.param <= 0x01)
171 							{
172 								m.param |= 0x9E;
173 							} else
174 							{
175 								m.command = CMD_NONE;
176 							}
177 							break;
178 
179 						case 0x11:
180 						case 0x12:
181 							// Extra fine slides
182 							m.param = static_cast<ModCommand::PARAM>(std::min(uint8(0x0F), m.param) | 0xE0);
183 							break;
184 
185 						case 0x15:
186 						case 0x16:
187 							// Fine slides
188 							m.param = static_cast<ModCommand::PARAM>((std::min(0x10, m.param + 1) / 2) | 0xF0);
189 							break;
190 
191 						case 0x1E:
192 							// More fine slides
193 							switch(m.param >> 4)
194 							{
195 							case 0x1:
196 								// Fine porta up
197 								m.command = CMD_PORTAMENTOUP;
198 								m.param |= 0xF0;
199 								break;
200 							case 0x2:
201 								// Fine porta down
202 								m.command = CMD_PORTAMENTODOWN;
203 								m.param |= 0xF0;
204 								break;
205 							case 0xA:
206 								// Extra fine volume slide up
207 								m.command = CMD_VOLUMESLIDE;
208 								m.param = ((((m.param & 0x0F) + 1) / 2) << 4) | 0x0F;
209 								break;
210 							case 0xB:
211 								// Extra fine volume slide down
212 								m.command = CMD_VOLUMESLIDE;
213 								m.param = (((m.param & 0x0F) + 1) / 2) | 0xF0;
214 								break;
215 							default:
216 								m.command = CMD_NONE;
217 								break;
218 							}
219 							break;
220 
221 						case 0x1C:
222 							// Adjust channel volume range
223 							m.param = static_cast<ModCommand::PARAM>(std::min((m.param + 1) / 2, 64));
224 							break;
225 						}
226 					}
227 
228 					// Try merging commands first
229 					ModCommand::CombineEffects(m.command, m.param, origCmd.command, origCmd.param);
230 
231 					if(ModCommand::GetEffectWeight(origCmd.command) > ModCommand::GetEffectWeight(m.command))
232 					{
233 						if(m.volcmd == VOLCMD_NONE && ModCommand::ConvertVolEffect(m.command, m.param, true))
234 						{
235 							// Volume column to the rescue!
236 							m.volcmd = m.command;
237 							m.vol = m.param;
238 						}
239 
240 						m.command = origCmd.command;
241 						m.param = origCmd.param;
242 					}
243 				}
244 			}
245 
246 			if(flags & endOfRowMask)
247 			{
248 				// End of row
249 				break;
250 			}
251 		}
252 	}
253 }
254 
255 
256 /////////////////////////////////////////////////////////////////////
257 // AMS (Extreme's Tracker) 1.x loader
258 
259 // AMS File Header
260 struct AMSFileHeader
261 {
262 	uint8le  versionLow;
263 	uint8le  versionHigh;
264 	uint8le  channelConfig;
265 	uint8le  numSamps;
266 	uint16le numPats;
267 	uint16le numOrds;
268 	uint8le  midiChannels;
269 	uint16le extraSize;
270 };
271 
272 MPT_BINARY_STRUCT(AMSFileHeader, 11)
273 
274 
275 // AMS Sample Header
276 struct AMSSampleHeader
277 {
278 	enum SampleFlags
279 	{
280 		smp16BitOld	= 0x04,	// AMS 1.0 (at least according to docs, I yet have to find such a file)
281 		smp16Bit	= 0x80,	// AMS 1.1+
282 		smpPacked	= 0x03,
283 	};
284 
285 	uint32le length;
286 	uint32le loopStart;
287 	uint32le loopEnd;
288 	uint8le  panFinetune;	// High nibble = pan position, low nibble = finetune value
289 	uint16le sampleRate;
290 	uint8le  volume;		// 0...127
291 	uint8le  flags;			// See SampleFlags
292 
293 	// Convert sample header to OpenMPT's internal format.
ConvertToMPTAMSSampleHeader294 	void ConvertToMPT(ModSample &mptSmp) const
295 	{
296 		mptSmp.Initialize();
297 
298 		mptSmp.nLength = length;
299 		mptSmp.nLoopStart = std::min(loopStart, length);
300 		mptSmp.nLoopEnd = std::min(loopEnd, length);
301 
302 		mptSmp.nVolume = (std::min(uint8(127), volume.get()) * 256 + 64) / 127;
303 		if(panFinetune & 0xF0)
304 		{
305 			mptSmp.nPan = (panFinetune & 0xF0);
306 			mptSmp.uFlags = CHN_PANNING;
307 		}
308 
309 		mptSmp.nC5Speed = 2 * sampleRate;
310 		if(sampleRate == 0)
311 		{
312 			mptSmp.nC5Speed = 2 * 8363;
313 		}
314 
315 		uint32 newC4speed = ModSample::TransposeToFrequency(0, MOD2XMFineTune(panFinetune & 0x0F));
316 		mptSmp.nC5Speed = (mptSmp.nC5Speed * newC4speed) / 8363;
317 
318 		if(mptSmp.nLoopStart < mptSmp.nLoopEnd)
319 		{
320 			mptSmp.uFlags.set(CHN_LOOP);
321 		}
322 
323 		if(flags & (smp16Bit | smp16BitOld))
324 		{
325 			mptSmp.uFlags.set(CHN_16BIT);
326 		}
327 	}
328 };
329 
330 MPT_BINARY_STRUCT(AMSSampleHeader, 17)
331 
332 
ValidateHeader(const AMSFileHeader & fileHeader)333 static bool ValidateHeader(const AMSFileHeader &fileHeader)
334 {
335 	if(fileHeader.versionHigh != 0x01)
336 	{
337 		return false;
338 	}
339 	return true;
340 }
341 
342 
GetHeaderMinimumAdditionalSize(const AMSFileHeader & fileHeader)343 static uint64 GetHeaderMinimumAdditionalSize(const AMSFileHeader &fileHeader)
344 {
345 	return fileHeader.extraSize + 3u + fileHeader.numSamps * (1u + sizeof(AMSSampleHeader)) + fileHeader.numOrds * 2u + fileHeader.numPats * 4u;
346 }
347 
348 
ProbeFileHeaderAMS(MemoryFileReader file,const uint64 * pfilesize)349 CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderAMS(MemoryFileReader file, const uint64 *pfilesize)
350 {
351 	if(!file.CanRead(7))
352 	{
353 		return ProbeWantMoreData;
354 	}
355 	if(!file.ReadMagic("Extreme"))
356 	{
357 		return ProbeFailure;
358 	}
359 	AMSFileHeader fileHeader;
360 	if(!file.ReadStruct(fileHeader))
361 	{
362 		return ProbeWantMoreData;
363 	}
364 	if(!ValidateHeader(fileHeader))
365 	{
366 		return ProbeFailure;
367 	}
368 	return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader));
369 }
370 
371 
ReadAMS(FileReader & file,ModLoadingFlags loadFlags)372 bool CSoundFile::ReadAMS(FileReader &file, ModLoadingFlags loadFlags)
373 {
374 	file.Rewind();
375 
376 	if(!file.ReadMagic("Extreme"))
377 	{
378 		return false;
379 	}
380 	AMSFileHeader fileHeader;
381 	if(!file.ReadStruct(fileHeader))
382 	{
383 		return false;
384 	}
385 	if(!ValidateHeader(fileHeader))
386 	{
387 		return false;
388 	}
389 	if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader))))
390 	{
391 		return false;
392 	}
393 	if(!file.Skip(fileHeader.extraSize))
394 	{
395 		return false;
396 	}
397 	if(loadFlags == onlyVerifyHeader)
398 	{
399 		return true;
400 	}
401 
402 	InitializeGlobals(MOD_TYPE_AMS);
403 
404 	m_SongFlags = SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS;
405 	m_nChannels = (fileHeader.channelConfig & 0x1F) + 1;
406 	m_nSamples = fileHeader.numSamps;
407 	SetupMODPanning(true);
408 
409 	m_modFormat.formatName = U_("Extreme's Tracker");
410 	m_modFormat.type = U_("ams");
411 	m_modFormat.madeWithTracker = MPT_UFORMAT("Extreme's Tracker {}.{}")(fileHeader.versionHigh, fileHeader.versionLow);
412 	m_modFormat.charset = mpt::Charset::CP437;
413 
414 	std::vector<bool> packSample(fileHeader.numSamps);
415 
416 	static_assert(MAX_SAMPLES > 255);
417 	for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
418 	{
419 		AMSSampleHeader sampleHeader;
420 		file.ReadStruct(sampleHeader);
421 		sampleHeader.ConvertToMPT(Samples[smp]);
422 		packSample[smp - 1] = (sampleHeader.flags & AMSSampleHeader::smpPacked) != 0;
423 	}
424 
425 	// Texts
426 	file.ReadSizedString<uint8le, mpt::String::spacePadded>(m_songName);
427 
428 	// Read sample names
429 	for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
430 	{
431 		file.ReadSizedString<uint8le, mpt::String::spacePadded>(m_szNames[smp]);
432 	}
433 
434 	// Read channel names
435 	for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++)
436 	{
437 		ChnSettings[chn].Reset();
438 		file.ReadSizedString<uint8le, mpt::String::spacePadded>(ChnSettings[chn].szName);
439 	}
440 
441 	// Read pattern names and create patterns
442 	Patterns.ResizeArray(fileHeader.numPats);
443 	for(PATTERNINDEX pat = 0; pat < fileHeader.numPats; pat++)
444 	{
445 		char name[11];
446 		const bool ok = file.ReadSizedString<uint8le, mpt::String::spacePadded>(name);
447 		// Create pattern now, so name won't be reset later.
448 		if(Patterns.Insert(pat, 64) && ok)
449 		{
450 			Patterns[pat].SetName(name);
451 		}
452 	}
453 
454 	// Read packed song message
455 	const uint16 packedLength = file.ReadUint16LE();
456 	if(packedLength && file.CanRead(packedLength))
457 	{
458 		std::vector<uint8> textIn;
459 		file.ReadVector(textIn, packedLength);
460 		std::string textOut;
461 		textOut.reserve(packedLength);
462 
463 		for(auto c : textIn)
464 		{
465 			if(c & 0x80)
466 			{
467 				textOut.insert(textOut.end(), (c & 0x7F), ' ');
468 			} else
469 			{
470 				textOut.push_back(c);
471 			}
472 		}
473 
474 		textOut = mpt::ToCharset(mpt::Charset::CP437, mpt::Charset::CP437AMS, textOut);
475 
476 		// Packed text doesn't include any line breaks!
477 		m_songMessage.ReadFixedLineLength(mpt::byte_cast<const std::byte*>(textOut.c_str()), textOut.length(), 76, 0);
478 	}
479 
480 	// Read Order List
481 	ReadOrderFromFile<uint16le>(Order(), file, fileHeader.numOrds);
482 
483 	// Read patterns
484 	for(PATTERNINDEX pat = 0; pat < fileHeader.numPats && file.CanRead(4); pat++)
485 	{
486 		uint32 patLength = file.ReadUint32LE();
487 		FileReader patternChunk = file.ReadChunk(patLength);
488 
489 		if((loadFlags & loadPatternData) && Patterns.IsValidPat(pat))
490 		{
491 			ReadAMSPattern(Patterns[pat], false, patternChunk);
492 		}
493 	}
494 
495 	if(loadFlags & loadSampleData)
496 	{
497 		// Read Samples
498 		for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
499 		{
500 			SampleIO(
501 				Samples[smp].uFlags[CHN_16BIT] ? SampleIO::_16bit : SampleIO::_8bit,
502 				SampleIO::mono,
503 				SampleIO::littleEndian,
504 				packSample[smp - 1] ? SampleIO::AMS : SampleIO::signedPCM)
505 				.ReadSample(Samples[smp], file);
506 		}
507 	}
508 
509 	return true;
510 }
511 
512 
513 /////////////////////////////////////////////////////////////////////
514 // AMS (Velvet Studio) 2.0 - 2.02 loader
515 
516 // AMS2 File Header
517 struct AMS2FileHeader
518 {
519 	enum FileFlags
520 	{
521 		linearSlides	= 0x40,
522 	};
523 
524 	uint8le  versionLow;		// Version of format (Hi = MainVer, Low = SubVer e.g. 0202 = 2.02)
525 	uint8le  versionHigh;		// ditto
526 	uint8le  numIns;			// Nr of Instruments (0-255)
527 	uint16le numPats;			// Nr of Patterns (1-1024)
528 	uint16le numOrds;			// Nr of Positions (1-65535)
529 	// Rest of header differs between format revision 2.01 and 2.02
530 };
531 
532 MPT_BINARY_STRUCT(AMS2FileHeader, 7)
533 
534 
535 // AMS2 Instument Envelope
536 struct AMS2Envelope
537 {
538 	uint8 speed;		// Envelope speed (currently not supported, always the same as current BPM)
539 	uint8 sustainPoint;	// Envelope sustain point
540 	uint8 loopStart;	// Envelope loop Start
541 	uint8 loopEnd;		// Envelope loop End
542 	uint8 numPoints;	// Envelope length
543 
544 	// Read envelope and do partial conversion.
ConvertToMPTAMS2Envelope545 	void ConvertToMPT(InstrumentEnvelope &mptEnv, FileReader &file)
546 	{
547 		file.ReadStruct(*this);
548 
549 		// Read envelope points
550 		uint8 data[64][3];
551 		file.ReadStructPartial(data, numPoints * 3);
552 
553 		if(numPoints <= 1)
554 		{
555 			// This is not an envelope.
556 			return;
557 		}
558 
559 		static_assert(MAX_ENVPOINTS >= std::size(data));
560 		mptEnv.resize(std::min(numPoints, mpt::saturate_cast<uint8>(std::size(data))));
561 		mptEnv.nLoopStart = loopStart;
562 		mptEnv.nLoopEnd = loopEnd;
563 		mptEnv.nSustainStart = mptEnv.nSustainEnd = sustainPoint;
564 
565 		for(uint32 i = 0; i < mptEnv.size(); i++)
566 		{
567 			if(i != 0)
568 			{
569 				mptEnv[i].tick = mptEnv[i - 1].tick + static_cast<uint16>(std::max(1, data[i][0] | ((data[i][1] & 0x01) << 8)));
570 			}
571 			mptEnv[i].value = data[i][2];
572 		}
573 	}
574 };
575 
576 MPT_BINARY_STRUCT(AMS2Envelope, 5)
577 
578 
579 // AMS2 Instrument Data
580 struct AMS2Instrument
581 {
582 	enum EnvelopeFlags
583 	{
584 		envLoop		= 0x01,
585 		envSustain	= 0x02,
586 		envEnabled	= 0x04,
587 
588 		// Flag shift amounts
589 		volEnvShift	= 0,
590 		panEnvShift	= 1,
591 		vibEnvShift	= 2,
592 
593 		vibAmpMask	= 0x3000,
594 		vibAmpShift	= 12,
595 		fadeOutMask	= 0xFFF,
596 	};
597 
598 	uint8le  shadowInstr;	// Shadow Instrument. If non-zero, the value=the shadowed inst.
599 	uint16le vibampFadeout;	// Vib.Amplify + Volume fadeout in one variable!
600 	uint16le envFlags;		// See EnvelopeFlags
601 
ApplyFlagsAMS2Instrument602 	void ApplyFlags(InstrumentEnvelope &mptEnv, EnvelopeFlags shift) const
603 	{
604 		const int flags = envFlags >> (shift * 3);
605 		mptEnv.dwFlags.set(ENV_ENABLED, (flags & envEnabled) != 0);
606 		mptEnv.dwFlags.set(ENV_LOOP, (flags & envLoop) != 0);
607 		mptEnv.dwFlags.set(ENV_SUSTAIN, (flags & envSustain) != 0);
608 
609 		// "Break envelope" should stop the envelope loop when encountering a note-off... We can only use the sustain loop to emulate this behaviour.
610 		if(!(flags & envSustain) && (flags & envLoop) != 0 && (flags & (1 << (9 - shift * 2))) != 0)
611 		{
612 			mptEnv.nSustainStart = mptEnv.nLoopStart;
613 			mptEnv.nSustainEnd = mptEnv.nLoopEnd;
614 			mptEnv.dwFlags.set(ENV_SUSTAIN);
615 			mptEnv.dwFlags.reset(ENV_LOOP);
616 		}
617 	}
618 
619 };
620 
621 MPT_BINARY_STRUCT(AMS2Instrument, 5)
622 
623 
624 // AMS2 Sample Header
625 struct AMS2SampleHeader
626 {
627 	enum SampleFlags
628 	{
629 		smpPacked	= 0x03,
630 		smp16Bit	= 0x04,
631 		smpLoop		= 0x08,
632 		smpBidiLoop	= 0x10,
633 		smpReverse	= 0x40,
634 	};
635 
636 	uint32le length;
637 	uint32le loopStart;
638 	uint32le loopEnd;
639 	uint16le sampledRate;		// Whyyyy?
640 	uint8le  panFinetune;		// High nibble = pan position, low nibble = finetune value
641 	uint16le c4speed;			// Why is all of this so redundant?
642 	int8le   relativeTone;		// q.e.d.
643 	uint8le  volume;			// 0...127
644 	uint8le  flags;			// See SampleFlags
645 
646 	// Convert sample header to OpenMPT's internal format.
ConvertToMPTAMS2SampleHeader647 	void ConvertToMPT(ModSample &mptSmp) const
648 	{
649 		mptSmp.Initialize();
650 
651 		mptSmp.nLength = length;
652 		mptSmp.nLoopStart = std::min(loopStart, length);
653 		mptSmp.nLoopEnd = std::min(loopEnd, length);
654 
655 		mptSmp.nC5Speed = c4speed * 2;
656 		if(c4speed == 0)
657 		{
658 			mptSmp.nC5Speed = 8363 * 2;
659 		}
660 		// Why, oh why, does this format need a c5speed and transpose/finetune at the same time...
661 		uint32 newC4speed = ModSample::TransposeToFrequency(relativeTone, MOD2XMFineTune(panFinetune & 0x0F));
662 		mptSmp.nC5Speed = (mptSmp.nC5Speed * newC4speed) / 8363;
663 
664 		mptSmp.nVolume = (std::min(volume.get(), uint8(127)) * 256 + 64) / 127;
665 		if(panFinetune & 0xF0)
666 		{
667 			mptSmp.nPan = (panFinetune & 0xF0);
668 			mptSmp.uFlags = CHN_PANNING;
669 		}
670 
671 		if(flags & smp16Bit) mptSmp.uFlags.set(CHN_16BIT);
672 		if((flags & smpLoop) && mptSmp.nLoopStart < mptSmp.nLoopEnd)
673 		{
674 			mptSmp.uFlags.set(CHN_LOOP);
675 			if(flags & smpBidiLoop) mptSmp.uFlags.set(CHN_PINGPONGLOOP);
676 			if(flags & smpReverse) mptSmp.uFlags.set(CHN_REVERSE);
677 		}
678 	}
679 };
680 
681 MPT_BINARY_STRUCT(AMS2SampleHeader, 20)
682 
683 
684 // AMS2 Song Description Header
685 struct AMS2Description
686 {
687 	uint32le packedLen;		// Including header
688 	uint32le unpackedLen;
689 	uint8le  packRoutine;	// 01
690 	uint8le  preProcessing;	// None!
691 	uint8le  packingMethod;	// RLE
692 };
693 
694 MPT_BINARY_STRUCT(AMS2Description, 11)
695 
696 
ValidateHeader(const AMS2FileHeader & fileHeader)697 static bool ValidateHeader(const AMS2FileHeader &fileHeader)
698 {
699 	if(fileHeader.versionHigh != 2 || fileHeader.versionLow > 2)
700 	{
701 		return false;
702 	}
703 	return true;
704 }
705 
706 
GetHeaderMinimumAdditionalSize(const AMS2FileHeader & fileHeader)707 static uint64 GetHeaderMinimumAdditionalSize(const AMS2FileHeader &fileHeader)
708 {
709 	return 36u + sizeof(AMS2Description) + fileHeader.numIns * 2u + fileHeader.numOrds * 2u + fileHeader.numPats * 4u;
710 }
711 
712 
ProbeFileHeaderAMS2(MemoryFileReader file,const uint64 * pfilesize)713 CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderAMS2(MemoryFileReader file, const uint64 *pfilesize)
714 {
715 	if(!file.CanRead(7))
716 	{
717 		return ProbeWantMoreData;
718 	}
719 	if(!file.ReadMagic("AMShdr\x1A"))
720 	{
721 		return ProbeFailure;
722 	}
723 	if(!file.CanRead(1))
724 	{
725 		return ProbeWantMoreData;
726 	}
727 	const uint8 songNameLength = file.ReadUint8();
728 	if(!file.Skip(songNameLength))
729 	{
730 		return ProbeWantMoreData;
731 	}
732 	AMS2FileHeader fileHeader;
733 	if(!file.ReadStruct(fileHeader))
734 	{
735 		return ProbeWantMoreData;
736 	}
737 	if(!ValidateHeader(fileHeader))
738 	{
739 		return ProbeFailure;
740 	}
741 	return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader));
742 }
743 
744 
ReadAMS2(FileReader & file,ModLoadingFlags loadFlags)745 bool CSoundFile::ReadAMS2(FileReader &file, ModLoadingFlags loadFlags)
746 {
747 	file.Rewind();
748 
749 	if(!file.ReadMagic("AMShdr\x1A"))
750 	{
751 		return false;
752 	}
753 	std::string songName;
754 	if(!file.ReadSizedString<uint8le, mpt::String::spacePadded>(songName))
755 	{
756 		return false;
757 	}
758 	AMS2FileHeader fileHeader;
759 	if(!file.ReadStruct(fileHeader))
760 	{
761 		return false;
762 	}
763 	if(!ValidateHeader(fileHeader))
764 	{
765 		return false;
766 	}
767 	if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader))))
768 	{
769 		return false;
770 	}
771 	if(loadFlags == onlyVerifyHeader)
772 	{
773 		return true;
774 	}
775 
776 	InitializeGlobals(MOD_TYPE_AMS);
777 
778 	m_songName = songName;
779 
780 	m_nInstruments = fileHeader.numIns;
781 	m_nChannels = 32;
782 	SetupMODPanning(true);
783 
784 	m_modFormat.formatName = U_("Velvet Studio");
785 	m_modFormat.type = U_("ams");
786 	m_modFormat.madeWithTracker = MPT_UFORMAT("Velvet Studio {}.{}")(fileHeader.versionHigh.get(), mpt::ufmt::dec0<2>(fileHeader.versionLow.get()));
787 	m_modFormat.charset = mpt::Charset::CP437;
788 
789 	uint16 headerFlags;
790 	if(fileHeader.versionLow >= 2)
791 	{
792 		uint16 tempo = std::max(uint16(32 << 8), file.ReadUint16LE());	// 8.8 tempo
793 		m_nDefaultTempo.SetRaw((tempo * TEMPO::fractFact) >> 8);
794 		m_nDefaultSpeed = std::max(uint8(1), file.ReadUint8());
795 		file.Skip(3);	// Default values for pattern editor
796 		headerFlags = file.ReadUint16LE();
797 	} else
798 	{
799 		m_nDefaultTempo.Set(std::max(uint8(32), file.ReadUint8()));
800 		m_nDefaultSpeed = std::max(uint8(1), file.ReadUint8());
801 		headerFlags = file.ReadUint8();
802 	}
803 
804 	m_SongFlags = SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS | ((headerFlags & AMS2FileHeader::linearSlides) ? SONG_LINEARSLIDES : SongFlags(0));
805 
806 	// Instruments
807 	std::vector<SAMPLEINDEX> firstSample;	// First sample of instrument
808 	std::vector<uint16> sampleSettings;		// Shadow sample map... Lo byte = Instrument, Hi byte, lo nibble = Sample index in instrument, Hi byte, hi nibble = Sample pack status
809 	enum
810 	{
811 		instrIndexMask		= 0xFF,		// Shadow instrument
812 		sampleIndexMask		= 0x7F00,	// Sample index in instrument
813 		sampleIndexShift	= 8,
814 		packStatusMask		= 0x8000,	// If bit is set, sample is packed
815 	};
816 
817 	static_assert(MAX_INSTRUMENTS > 255);
818 	for(INSTRUMENTINDEX ins = 1; ins <= m_nInstruments; ins++)
819 	{
820 		ModInstrument *instrument = AllocateInstrument(ins);
821 		if(instrument == nullptr
822 			|| !file.ReadSizedString<uint8le, mpt::String::spacePadded>(instrument->name))
823 		{
824 			break;
825 		}
826 
827 		uint8 numSamples = file.ReadUint8();
828 		uint8 sampleAssignment[120];
829 		MemsetZero(sampleAssignment);	// Only really needed for v2.0, where the lowest and highest octave aren't cleared.
830 
831 		if(numSamples == 0
832 			|| (fileHeader.versionLow > 0 && !file.ReadArray(sampleAssignment))	// v2.01+: 120 Notes
833 			|| (fileHeader.versionLow == 0 && !file.ReadRaw(mpt::span(sampleAssignment + 12, 96)).size()))	// v2.0: 96 Notes
834 		{
835 			continue;
836 		}
837 
838 		static_assert(mpt::array_size<decltype(instrument->Keyboard)>::size >= std::size(sampleAssignment));
839 		for(size_t i = 0; i < 120; i++)
840 		{
841 			instrument->Keyboard[i] = sampleAssignment[i] + GetNumSamples() + 1;
842 		}
843 
844 		AMS2Envelope volEnv, panEnv, vibratoEnv;
845 		volEnv.ConvertToMPT(instrument->VolEnv, file);
846 		panEnv.ConvertToMPT(instrument->PanEnv, file);
847 		vibratoEnv.ConvertToMPT(instrument->PitchEnv, file);
848 
849 		AMS2Instrument instrHeader;
850 		file.ReadStruct(instrHeader);
851 		instrument->nFadeOut = (instrHeader.vibampFadeout & AMS2Instrument::fadeOutMask);
852 		const int16 vibAmp = 1 << ((instrHeader.vibampFadeout & AMS2Instrument::vibAmpMask) >> AMS2Instrument::vibAmpShift);
853 
854 		instrHeader.ApplyFlags(instrument->VolEnv, AMS2Instrument::volEnvShift);
855 		instrHeader.ApplyFlags(instrument->PanEnv, AMS2Instrument::panEnvShift);
856 		instrHeader.ApplyFlags(instrument->PitchEnv, AMS2Instrument::vibEnvShift);
857 
858 		// Scale envelopes to correct range
859 		for(auto &p : instrument->VolEnv)
860 		{
861 			p.value = std::min(uint8(ENVELOPE_MAX), static_cast<uint8>((p.value * ENVELOPE_MAX + 64u) / 127u));
862 		}
863 		for(auto &p : instrument->PanEnv)
864 		{
865 			p.value = std::min(uint8(ENVELOPE_MAX), static_cast<uint8>((p.value * ENVELOPE_MAX + 128u) / 255u));
866 		}
867 		for(auto &p : instrument->PitchEnv)
868 		{
869 #ifdef MODPLUG_TRACKER
870 			p.value = std::min(uint8(ENVELOPE_MAX), static_cast<uint8>(32 + Util::muldivrfloor(static_cast<int8>(p.value - 128), vibAmp, 255)));
871 #else
872 			// Try to keep as much precision as possible... divide by 8 since that's the highest possible vibAmp factor.
873 			p.value = static_cast<uint8>(128 + Util::muldivrfloor(static_cast<int8>(p.value - 128), vibAmp, 8));
874 #endif
875 		}
876 
877 		// Sample headers - we will have to read them even for shadow samples, and we will have to load them several times,
878 		// as it is possible that shadow samples use different sample settings like base frequency or panning.
879 		const SAMPLEINDEX firstSmp = GetNumSamples() + 1;
880 		for(SAMPLEINDEX smp = 0; smp < numSamples; smp++)
881 		{
882 			if(firstSmp + smp >= MAX_SAMPLES)
883 			{
884 				file.Skip(sizeof(AMS2SampleHeader));
885 				break;
886 			}
887 			file.ReadSizedString<uint8le, mpt::String::spacePadded>(m_szNames[firstSmp + smp]);
888 
889 			AMS2SampleHeader sampleHeader;
890 			file.ReadStruct(sampleHeader);
891 			sampleHeader.ConvertToMPT(Samples[firstSmp + smp]);
892 
893 			uint16 settings = (instrHeader.shadowInstr & instrIndexMask)
894 				| ((smp << sampleIndexShift) & sampleIndexMask)
895 				| ((sampleHeader.flags & AMS2SampleHeader::smpPacked) ? packStatusMask : 0);
896 			sampleSettings.push_back(settings);
897 		}
898 
899 		firstSample.push_back(firstSmp);
900 		m_nSamples = static_cast<SAMPLEINDEX>(std::min(MAX_SAMPLES - 1, GetNumSamples() + numSamples));
901 	}
902 
903 	// Text
904 
905 	// Read composer name
906 	if(std::string composer; file.ReadSizedString<uint8le, mpt::String::spacePadded>(composer))
907 	{
908 		m_songArtist = mpt::ToUnicode(mpt::Charset::CP437AMS2, composer);
909 	}
910 
911 	// Channel names
912 	for(CHANNELINDEX chn = 0; chn < 32; chn++)
913 	{
914 		ChnSettings[chn].Reset();
915 		file.ReadSizedString<uint8le, mpt::String::spacePadded>(ChnSettings[chn].szName);
916 	}
917 
918 	// RLE-Packed description text
919 	AMS2Description descriptionHeader;
920 	if(!file.ReadStruct(descriptionHeader))
921 	{
922 		return true;
923 	}
924 	if(descriptionHeader.packedLen > sizeof(descriptionHeader) && file.CanRead(descriptionHeader.packedLen - sizeof(descriptionHeader)))
925 	{
926 		const uint32 textLength = descriptionHeader.packedLen - static_cast<uint32>(sizeof(descriptionHeader));
927 		std::vector<uint8> textIn;
928 		file.ReadVector(textIn, textLength);
929 		// In the best case, every byte triplet can decode to 255 bytes, which is a ratio of exactly 1:85
930 		const uint32 maxLength = std::min(textLength, Util::MaxValueOfType(textLength) / 85u) * 85u;
931 		std::string textOut;
932 		textOut.reserve(std::min(maxLength, descriptionHeader.unpackedLen.get()));
933 
934 		size_t readLen = 0;
935 		while(readLen < textLength)
936 		{
937 			uint8 c = textIn[readLen++];
938 			if(c == 0xFF && textLength - readLen >= 2)
939 			{
940 				c = textIn[readLen++];
941 				uint32 count = textIn[readLen++];
942 				textOut.insert(textOut.end(), count, c);
943 			} else
944 			{
945 				textOut.push_back(c);
946 			}
947 		}
948 		textOut = mpt::ToCharset(mpt::Charset::CP437, mpt::Charset::CP437AMS2, textOut);
949 		// Packed text doesn't include any line breaks!
950 		m_songMessage.ReadFixedLineLength(mpt::byte_cast<const std::byte*>(textOut.c_str()), textOut.length(), 74, 0);
951 	}
952 
953 	// Read Order List
954 	ReadOrderFromFile<uint16le>(Order(), file, fileHeader.numOrds);
955 
956 	// Read Patterns
957 	if(loadFlags & loadPatternData)
958 		Patterns.ResizeArray(fileHeader.numPats);
959 	for(PATTERNINDEX pat = 0; pat < fileHeader.numPats && file.CanRead(4); pat++)
960 	{
961 		uint32 patLength = file.ReadUint32LE();
962 		FileReader patternChunk = file.ReadChunk(patLength);
963 
964 		if(loadFlags & loadPatternData)
965 		{
966 			const ROWINDEX numRows = patternChunk.ReadUint8() + 1;
967 			// We don't need to know the number of channels or commands.
968 			patternChunk.Skip(1);
969 
970 			if(!Patterns.Insert(pat, numRows))
971 			{
972 				continue;
973 			}
974 
975 			char patternName[11];
976 			if(patternChunk.ReadSizedString<uint8le, mpt::String::spacePadded>(patternName))
977 				Patterns[pat].SetName(patternName);
978 
979 			ReadAMSPattern(Patterns[pat], true, patternChunk);
980 		}
981 	}
982 
983 	if(!(loadFlags & loadSampleData))
984 	{
985 		return true;
986 	}
987 
988 	// Read Samples
989 	for(SAMPLEINDEX smp = 0; smp < GetNumSamples(); smp++)
990 	{
991 		if((sampleSettings[smp] & instrIndexMask) == 0)
992 		{
993 			// Only load samples that aren't part of a shadow instrument
994 			SampleIO(
995 				(Samples[smp + 1].uFlags & CHN_16BIT) ? SampleIO::_16bit : SampleIO::_8bit,
996 				SampleIO::mono,
997 				SampleIO::littleEndian,
998 				(sampleSettings[smp] & packStatusMask) ? SampleIO::AMS : SampleIO::signedPCM)
999 				.ReadSample(Samples[smp + 1], file);
1000 		}
1001 	}
1002 
1003 	// Copy shadow samples
1004 	for(SAMPLEINDEX smp = 0; smp < GetNumSamples(); smp++)
1005 	{
1006 		INSTRUMENTINDEX sourceInstr = (sampleSettings[smp] & instrIndexMask);
1007 		if(sourceInstr == 0
1008 			|| --sourceInstr >= firstSample.size())
1009 		{
1010 			continue;
1011 		}
1012 
1013 		SAMPLEINDEX sourceSample = ((sampleSettings[smp] & sampleIndexMask) >> sampleIndexShift) + firstSample[sourceInstr];
1014 		if(sourceSample > GetNumSamples() || !Samples[sourceSample].HasSampleData())
1015 		{
1016 			continue;
1017 		}
1018 
1019 		// Copy over original sample
1020 		ModSample &sample = Samples[smp + 1];
1021 		ModSample &source = Samples[sourceSample];
1022 		sample.uFlags.set(CHN_16BIT, source.uFlags[CHN_16BIT]);
1023 		sample.nLength = source.nLength;
1024 		if(sample.AllocateSample())
1025 		{
1026 			memcpy(sample.sampleb(), source.sampleb(), source.GetSampleSizeInBytes());
1027 		}
1028 	}
1029 
1030 	return true;
1031 }
1032 
1033 
1034 /////////////////////////////////////////////////////////////////////
1035 // AMS Sample unpacking
1036 
AMSUnpack(const int8 * const source,size_t sourceSize,void * const dest,const size_t destSize,char packCharacter)1037 void AMSUnpack(const int8 * const source, size_t sourceSize, void * const dest, const size_t destSize, char packCharacter)
1038 {
1039 	std::vector<int8> tempBuf(destSize, 0);
1040 	size_t depackSize = destSize;
1041 
1042 	// Unpack Loop
1043 	{
1044 		const int8 *in = source;
1045 		int8 *out = tempBuf.data();
1046 
1047 		size_t i = sourceSize, j = destSize;
1048 		while(i != 0 && j != 0)
1049 		{
1050 			int8 ch = *(in++);
1051 			if(--i != 0 && ch == packCharacter)
1052 			{
1053 				uint8 repCount = *(in++);
1054 				repCount = static_cast<uint8>(std::min(static_cast<size_t>(repCount), j));
1055 				if(--i != 0 && repCount)
1056 				{
1057 					ch = *(in++);
1058 					i--;
1059 					while(repCount-- != 0)
1060 					{
1061 						*(out++) = ch;
1062 						j--;
1063 					}
1064 				} else
1065 				{
1066 					*(out++) = packCharacter;
1067 					j--;
1068 				}
1069 			} else
1070 			{
1071 				*(out++) = ch;
1072 				j--;
1073 			}
1074 		}
1075 		// j should only be non-zero for truncated samples
1076 		depackSize -= j;
1077 	}
1078 
1079 	// Bit Unpack Loop
1080 	{
1081 		int8 *out = tempBuf.data();
1082 		uint16 bitcount = 0x80;
1083 		size_t k = 0;
1084 		uint8 *dst = static_cast<uint8 *>(dest);
1085 		for(size_t i = 0; i < depackSize; i++)
1086 		{
1087 			uint8 al = *out++;
1088 			uint16 dh = 0;
1089 			for(uint16 count = 0; count < 8; count++)
1090 			{
1091 				uint16 bl = al & bitcount;
1092 				bl = ((bl | (bl << 8)) >> ((dh + 8 - count) & 7)) & 0xFF;
1093 				bitcount = ((bitcount | (bitcount << 8)) >> 1) & 0xFF;
1094 				dst[k++] |= bl;
1095 				if(k >= destSize)
1096 				{
1097 					k = 0;
1098 					dh++;
1099 				}
1100 			}
1101 			bitcount = ((bitcount | (bitcount << 8)) >> dh) & 0xFF;
1102 		}
1103 	}
1104 
1105 	// Delta Unpack
1106 	{
1107 		int8 old = 0;
1108 		int8 *out = static_cast<int8 *>(dest);
1109 		for(size_t i = depackSize; i != 0; i--)
1110 		{
1111 			int pos = *reinterpret_cast<uint8 *>(out);
1112 			if(pos != 128 && (pos & 0x80) != 0)
1113 			{
1114 				pos = -(pos & 0x7F);
1115 			}
1116 			old -= static_cast<int8>(pos);
1117 			*(out++) = old;
1118 		}
1119 	}
1120 }
1121 
1122 
1123 OPENMPT_NAMESPACE_END
1124