1 /*
2  * Load_stp.cpp
3  * ------------
4  * Purpose: STP (Soundtracker Pro II) module loader
5  * Notes  : A few exotic effects aren't supported.
6  *          Multiple sample loops are supported, but only the first 10 can be used as cue points
7  *          (with 16xx and 18xx).
8  *          Fractional speed values and combined auto effects are handled whenever possible,
9  *          but some effects may be omitted (and there may be tempo accuracy issues).
10  * Authors: Devin Acker
11  *          OpenMPT Devs
12  * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
13  *
14  * Wisdom from the Soundtracker Pro II manual:
15  * "To create shorter patterns, simply create shorter patterns."
16  */
17 
18 #include "stdafx.h"
19 #include "Loaders.h"
20 
21 OPENMPT_NAMESPACE_BEGIN
22 
23 // File header
24 struct STPFileHeader
25 {
26 	char     magic[4];
27 	uint16be version;
28 	uint8be  numOrders;
29 	uint8be  patternLength;
30 	uint8be  orderList[128];
31 	uint16be speed;
32 	uint16be speedFrac;
33 	uint16be timerCount;
34 	uint16be flags;
35 	uint32be reserved;
36 	uint16be midiCount; // always 50
37 	uint8be  midi[50];
38 	uint16be numSamples;
39 	uint16be sampleStructSize;
40 };
41 
42 MPT_BINARY_STRUCT(STPFileHeader, 204)
43 
44 
45 // Sample header (common part between all versions)
46 struct STPSampleHeader
47 {
48 	uint32be length;
49 	uint8be  volume;
50 	uint8be  reserved1;
51 	uint32be loopStart;
52 	uint32be loopLength;
53 	uint16be defaultCommand;	// Default command to put next to note when editing patterns; not relevant for playback
54 	// The following 4 bytes are reserved in version 0 and 1.
55 	uint16be defaultPeriod;
56 	uint8be  finetune;
57 	uint8be  reserved2;
58 
ConvertToMPTSTPSampleHeader59 	void ConvertToMPT(ModSample &mptSmp) const
60 	{
61 		mptSmp.nLength = length;
62 		mptSmp.nVolume = 4u * std::min(volume.get(), uint8(64));
63 
64 		mptSmp.nLoopStart = loopStart;
65 		mptSmp.nLoopEnd = loopStart + loopLength;
66 
67 		if(mptSmp.nLoopStart >= mptSmp.nLength)
68 		{
69 			mptSmp.nLoopStart = mptSmp.nLength - 1;
70 		}
71 		if(mptSmp.nLoopEnd > mptSmp.nLength)
72 		{
73 			mptSmp.nLoopEnd = mptSmp.nLength;
74 		}
75 
76 		if(mptSmp.nLoopStart > mptSmp.nLoopEnd)
77 		{
78 			mptSmp.nLoopStart = 0;
79 			mptSmp.nLoopEnd = 0;
80 		} else if(mptSmp.nLoopEnd > mptSmp.nLoopStart)
81 		{
82 			mptSmp.uFlags.set(CHN_LOOP);
83 			mptSmp.cues[0] = mptSmp.nLoopStart;
84 		}
85 	}
86 };
87 
88 MPT_BINARY_STRUCT(STPSampleHeader, 20)
89 
90 
91 struct STPLoopInfo
92 {
93 	SmpLength loopStart;
94 	SmpLength loopLength;
95 	SAMPLEINDEX looped;
96 	SAMPLEINDEX nonLooped;
97 };
98 
99 typedef std::vector<STPLoopInfo> STPLoopList;
100 
101 
ConvertTempo(uint16 ciaSpeed)102 static TEMPO ConvertTempo(uint16 ciaSpeed)
103 {
104 	// 3546 is the resulting CIA timer value when using 4F7D (tempo 125 bpm) command in STProII
105 	return TEMPO((125.0 * 3546.0) / ciaSpeed);
106 }
107 
108 
ConvertLoopSlice(ModSample & src,ModSample & dest,SmpLength start,SmpLength len,bool loop)109 static void ConvertLoopSlice(ModSample &src, ModSample &dest, SmpLength start, SmpLength len, bool loop)
110 {
111 	if(!src.HasSampleData()
112 		|| start >= src.nLength
113 		|| src.nLength - start < len)
114 	{
115 		return;
116 	}
117 
118 	dest.FreeSample();
119 	dest = src;
120 	dest.nLength = len;
121 	dest.pData.pSample = nullptr;
122 
123 	if(!dest.AllocateSample())
124 	{
125 		return;
126 	}
127 
128 	// only preserve cue points if the target sample length is the same
129 	if(len != src.nLength)
130 		MemsetZero(dest.cues);
131 
132 	std::memcpy(dest.sampleb(), src.sampleb() + start, len);
133 	dest.uFlags.set(CHN_LOOP, loop);
134 	if(loop)
135 	{
136 		dest.nLoopStart = 0;
137 		dest.nLoopEnd = len;
138 	} else
139 	{
140 		dest.nLoopStart = 0;
141 		dest.nLoopEnd = 0;
142 	}
143 }
144 
ConvertLoopSequence(ModSample & smp,STPLoopList & loopList)145 static void ConvertLoopSequence(ModSample &smp, STPLoopList &loopList)
146 {
147 	// This should only modify a sample if it has more than one loop
148 	// (otherwise, it behaves like a normal sample loop)
149 	if(!smp.HasSampleData() || loopList.size() < 2) return;
150 
151 	ModSample newSmp = smp;
152 	newSmp.nLength = 0;
153 	newSmp.pData.pSample = nullptr;
154 
155 	size_t numLoops = loopList.size();
156 
157 	// Get the total length of the sample after combining all looped sections
158 	for(size_t i = 0; i < numLoops; i++)
159 	{
160 		STPLoopInfo &info = loopList[i];
161 
162 		// If adding this loop would cause the sample length to exceed maximum,
163 		// then limit and bail out
164 		if(info.loopStart >= smp.nLength
165 			|| smp.nLength - info.loopStart < info.loopLength
166 			|| newSmp.nLength > MAX_SAMPLE_LENGTH - info.loopLength)
167 		{
168 			numLoops = i;
169 			break;
170 		}
171 
172 		newSmp.nLength += info.loopLength;
173 	}
174 
175 	if(!newSmp.AllocateSample())
176 	{
177 		return;
178 	}
179 
180 	// start copying the looped sample data parts
181 	SmpLength start = 0;
182 
183 	for(size_t i = 0; i < numLoops; i++)
184 	{
185 		STPLoopInfo &info = loopList[i];
186 
187 		memcpy(newSmp.sampleb() + start, smp.sampleb() + info.loopStart, info.loopLength);
188 
189 		// update loop info based on position in edited sample
190 		info.loopStart = start;
191 		if(i > 0 && i <= std::size(newSmp.cues))
192 		{
193 			newSmp.cues[i - 1] = start;
194 		}
195 		start += info.loopLength;
196 	}
197 
198 	// replace old sample with new one
199 	smp.FreeSample();
200 	smp = newSmp;
201 
202 	smp.nLoopStart = 0;
203 	smp.nLoopEnd = smp.nLength;
204 	smp.uFlags.set(CHN_LOOP);
205 }
206 
207 
ValidateHeader(const STPFileHeader & fileHeader)208 static bool ValidateHeader(const STPFileHeader &fileHeader)
209 {
210 	if(std::memcmp(fileHeader.magic, "STP3", 4)
211 		|| fileHeader.version > 2
212 		|| fileHeader.numOrders > 128
213 		|| fileHeader.numSamples >= MAX_SAMPLES
214 		|| fileHeader.timerCount == 0
215 		|| fileHeader.midiCount != 50)
216 	{
217 		return false;
218 	}
219 	return true;
220 }
221 
222 
ProbeFileHeaderSTP(MemoryFileReader file,const uint64 * pfilesize)223 CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderSTP(MemoryFileReader file, const uint64 *pfilesize)
224 {
225 	STPFileHeader fileHeader;
226 	if(!file.ReadStruct(fileHeader))
227 	{
228 		return ProbeWantMoreData;
229 	}
230 	if(!ValidateHeader(fileHeader))
231 	{
232 		return ProbeFailure;
233 	}
234 	MPT_UNREFERENCED_PARAMETER(pfilesize);
235 	return ProbeSuccess;
236 }
237 
238 
ReadSTP(FileReader & file,ModLoadingFlags loadFlags)239 bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags)
240 {
241 	file.Rewind();
242 
243 	STPFileHeader fileHeader;
244 	if(!file.ReadStruct(fileHeader))
245 	{
246 		return false;
247 	}
248 	if(!ValidateHeader(fileHeader))
249 	{
250 		return false;
251 	}
252 	if(loadFlags == onlyVerifyHeader)
253 	{
254 		return true;
255 	}
256 
257 	InitializeGlobals(MOD_TYPE_STP);
258 
259 	m_modFormat.formatName = MPT_UFORMAT("Soundtracker Pro II v{}")(fileHeader.version);
260 	m_modFormat.type = U_("stp");
261 	m_modFormat.charset = mpt::Charset::ISO8859_1;
262 
263 	m_nChannels = 4;
264 	m_nSamples = 0;
265 
266 	m_nDefaultSpeed = fileHeader.speed;
267 	m_nDefaultTempo = ConvertTempo(fileHeader.timerCount);
268 
269 	m_nMinPeriod = 14 * 4;
270 	m_nMaxPeriod = 3424 * 4;
271 
272 	ReadOrderFromArray(Order(), fileHeader.orderList, fileHeader.numOrders);
273 
274 	std::vector<STPLoopList> loopInfo;
275 	// Non-looped versions of samples with loops (when needed)
276 	std::vector<SAMPLEINDEX> nonLooped;
277 
278 	// Load sample headers
279 	SAMPLEINDEX samplesInFile = 0;
280 
281 	for(SAMPLEINDEX smp = 0; smp < fileHeader.numSamples; smp++)
282 	{
283 		SAMPLEINDEX actualSmp = file.ReadUint16BE();
284 		if(actualSmp == 0 || actualSmp >= MAX_SAMPLES)
285 			return false;
286 		uint32 chunkSize = fileHeader.sampleStructSize;
287 		if(fileHeader.version == 2)
288 			chunkSize = file.ReadUint32BE() - 2;
289 		FileReader chunk = file.ReadChunk(chunkSize);
290 
291 		samplesInFile = m_nSamples = std::max(m_nSamples, actualSmp);
292 
293 		ModSample &mptSmp = Samples[actualSmp];
294 		mptSmp.Initialize(MOD_TYPE_MOD);
295 
296 		if(fileHeader.version < 2)
297 		{
298 			// Read path
299 			chunk.ReadString<mpt::String::maybeNullTerminated>(mptSmp.filename, 31);
300 			// Ignore flags, they are all not relevant for us
301 			chunk.Skip(1);
302 			// Read filename / sample text
303 			chunk.ReadString<mpt::String::maybeNullTerminated>(m_szNames[actualSmp], 30);
304 		} else
305 		{
306 			std::string str;
307 			// Read path
308 			chunk.ReadNullString(str, 257);
309 			mptSmp.filename = str;
310 			// Ignore flags, they are all not relevant for us
311 			chunk.Skip(1);
312 			// Read filename / sample text
313 			chunk.ReadNullString(str, 31);
314 			m_szNames[actualSmp] = str;
315 			// Seek to even boundary
316 			if(chunk.GetPosition() % 2u)
317 				chunk.Skip(1);
318 		}
319 
320 		STPSampleHeader sampleHeader;
321 		chunk.ReadStruct(sampleHeader);
322 		sampleHeader.ConvertToMPT(mptSmp);
323 
324 		if(fileHeader.version == 2)
325 		{
326 			mptSmp.nFineTune = static_cast<int8>(sampleHeader.finetune << 3);
327 		}
328 
329 		if(fileHeader.version >= 1)
330 		{
331 			nonLooped.resize(samplesInFile);
332 			loopInfo.resize(samplesInFile);
333 			STPLoopList &loopList = loopInfo[actualSmp - 1];
334 			loopList.clear();
335 
336 			const uint16 numLoops = file.ReadUint16BE();
337 			if(!file.CanRead(numLoops * 8u))
338 				return false;
339 			loopList.reserve(numLoops);
340 
341 			STPLoopInfo loop;
342 			loop.looped = loop.nonLooped = 0;
343 
344 			if(numLoops == 0 && mptSmp.uFlags[CHN_LOOP])
345 			{
346 				loop.loopStart  = mptSmp.nLoopStart;
347 				loop.loopLength = mptSmp.nLoopEnd - mptSmp.nLoopStart;
348 				loopList.push_back(loop);
349 			} else for(uint16 i = 0; i < numLoops; i++)
350 			{
351 				loop.loopStart  = file.ReadUint32BE();
352 				loop.loopLength = file.ReadUint32BE();
353 				loopList.push_back(loop);
354 			}
355 		}
356 	}
357 
358 	// Load patterns
359 	uint16 numPatterns = 128;
360 	if(fileHeader.version == 0)
361 		numPatterns = file.ReadUint16BE();
362 
363 	uint16 patternLength = fileHeader.patternLength;
364 	CHANNELINDEX channels = 4;
365 	if(fileHeader.version > 0)
366 	{
367 		// Scan for total number of channels
368 		FileReader::off_t patOffset = file.GetPosition();
369 		for(uint16 pat = 0; pat < numPatterns; pat++)
370 		{
371 			PATTERNINDEX actualPat = file.ReadUint16BE();
372 			if(actualPat == 0xFFFF)
373 				break;
374 
375 			patternLength = file.ReadUint16BE();
376 			channels = file.ReadUint16BE();
377 			if(channels > MAX_BASECHANNELS)
378 				return false;
379 			m_nChannels = std::max(m_nChannels, channels);
380 
381 			file.Skip(channels * patternLength * 4u);
382 		}
383 		file.Seek(patOffset);
384 	}
385 
386 	struct ChannelMemory
387 	{
388 		uint8 autoFinePorta, autoPortaUp, autoPortaDown, autoVolSlide, autoVibrato;
389 		uint8 vibratoMem, autoTremolo, autoTonePorta, tonePortaMem;
390 	};
391 	std::vector<ChannelMemory> channelMemory(m_nChannels);
392 	uint8 globalVolSlide = 0;
393 	uint8 speedFrac = static_cast<uint8>(fileHeader.speedFrac);
394 
395 	for(uint16 pat = 0; pat < numPatterns; pat++)
396 	{
397 		PATTERNINDEX actualPat = pat;
398 
399 		if(fileHeader.version > 0)
400 		{
401 			actualPat = file.ReadUint16BE();
402 			if(actualPat == 0xFFFF)
403 				break;
404 
405 			patternLength = file.ReadUint16BE();
406 			channels = file.ReadUint16BE();
407 		}
408 
409 		if(!file.CanRead(channels * patternLength * 4u))
410 			break;
411 
412 		if(!(loadFlags & loadPatternData) || !Patterns.Insert(actualPat, patternLength))
413 		{
414 			file.Skip(channels * patternLength * 4u);
415 			continue;
416 		}
417 
418 		for(ROWINDEX row = 0; row < patternLength; row++)
419 		{
420 			auto rowBase = Patterns[actualPat].GetRow(row);
421 
422 			bool didGlobalVolSlide = false;
423 
424 			// if a fractional speed value is in use then determine if we should stick a fine pattern delay somewhere
425 			bool shouldDelay;
426 			switch(speedFrac & 3)
427 			{
428 			default: shouldDelay = false; break;
429 			// 1/4
430 			case 1: shouldDelay = (row & 3) == 0; break;
431 			// 1/2
432 			case 2: shouldDelay = (row & 1) == 0; break;
433 			// 3/4
434 			case 3: shouldDelay = (row & 3) != 3; break;
435 			}
436 
437 			for(CHANNELINDEX chn = 0; chn < channels; chn++)
438 			{
439 				ChannelMemory &chnMem = channelMemory[chn];
440 				ModCommand &m = rowBase[chn];
441 				const auto [instr, note, command, param] = file.ReadArray<uint8, 4>();
442 
443 				m.instr   = instr;
444 				m.note    = note;
445 				m.param   = param;
446 
447 				if(m.note)
448 				{
449 					m.note += 24 + NOTE_MIN;
450 					chnMem = ChannelMemory();
451 				}
452 
453 				// this is a nibble-swapped param value used for auto fine volside
454 				// and auto global fine volside
455 				uint8 swapped = (m.param >> 4) | (m.param << 4);
456 
457 				if((command & 0xF0) == 0xF0)
458 				{
459 					// 12-bit CIA tempo
460 					uint16 ciaTempo = (static_cast<uint16>(command & 0x0F) << 8) | m.param;
461 					if(ciaTempo)
462 					{
463 						m.param = mpt::saturate_round<ModCommand::PARAM>(ConvertTempo(ciaTempo).ToDouble());
464 						m.command = CMD_TEMPO;
465 					} else
466 					{
467 						m.command = CMD_NONE;
468 					}
469 				} else switch(command)
470 				{
471 				case 0x00: // arpeggio
472 					if(m.param)
473 						m.command = CMD_ARPEGGIO;
474 					else
475 						m.command = CMD_NONE;
476 					break;
477 
478 				case 0x01: // portamento up
479 					m.command = CMD_PORTAMENTOUP;
480 					break;
481 
482 				case 0x02: // portamento down
483 					m.command = CMD_PORTAMENTODOWN;
484 					break;
485 
486 				case 0x03: // auto fine portamento up
487 					chnMem.autoFinePorta = 0x10 | std::min(m.param, ModCommand::PARAM(15));
488 					chnMem.autoPortaUp = 0;
489 					chnMem.autoPortaDown = 0;
490 					chnMem.autoTonePorta = 0;
491 
492 					m.command = CMD_NONE;
493 					break;
494 
495 				case 0x04: // auto fine portamento down
496 					chnMem.autoFinePorta = 0x20 | std::min(m.param, ModCommand::PARAM(15));
497 					chnMem.autoPortaUp = 0;
498 					chnMem.autoPortaDown = 0;
499 					chnMem.autoTonePorta = 0;
500 
501 					m.command = CMD_NONE;
502 					break;
503 
504 				case 0x05: // auto portamento up
505 					chnMem.autoFinePorta = 0;
506 					chnMem.autoPortaUp = m.param;
507 					chnMem.autoPortaDown = 0;
508 					chnMem.autoTonePorta = 0;
509 
510 					m.command = CMD_NONE;
511 					break;
512 
513 				case 0x06: // auto portamento down
514 					chnMem.autoFinePorta = 0;
515 					chnMem.autoPortaUp = 0;
516 					chnMem.autoPortaDown = m.param;
517 					chnMem.autoTonePorta = 0;
518 
519 					m.command = CMD_NONE;
520 					break;
521 
522 				case 0x07: // set global volume
523 					m.command = CMD_GLOBALVOLUME;
524 					globalVolSlide = 0;
525 					break;
526 
527 				case 0x08: // auto global fine volume slide
528 					globalVolSlide = swapped;
529 					m.command = CMD_NONE;
530 					break;
531 
532 				case 0x09: // fine portamento up
533 					m.command = CMD_MODCMDEX;
534 					m.param = 0x10 | std::min(m.param, ModCommand::PARAM(15));
535 					break;
536 
537 				case 0x0A: // fine portamento down
538 					m.command = CMD_MODCMDEX;
539 					m.param = 0x20 | std::min(m.param, ModCommand::PARAM(15));
540 					break;
541 
542 				case 0x0B: // auto fine volume slide
543 					chnMem.autoVolSlide = swapped;
544 					m.command = CMD_NONE;
545 					break;
546 
547 				case 0x0C: // set volume
548 					m.volcmd = VOLCMD_VOLUME;
549 					m.vol = m.param;
550 					chnMem.autoVolSlide = 0;
551 					m.command = CMD_NONE;
552 					break;
553 
554 				case 0x0D: // volume slide (param is swapped compared to .mod)
555 					if(m.param & 0xF0)
556 					{
557 						m.volcmd = VOLCMD_VOLSLIDEDOWN;
558 						m.vol = m.param >> 4;
559 					} else if(m.param & 0x0F)
560 					{
561 						m.volcmd = VOLCMD_VOLSLIDEUP;
562 						m.vol = m.param & 0xF;
563 					}
564 					chnMem.autoVolSlide = 0;
565 					m.command = CMD_NONE;
566 					break;
567 
568 				case 0x0E: // set filter (also uses opposite value compared to .mod)
569 					m.command = CMD_MODCMDEX;
570 					m.param = 1 ^ (m.param ? 1 : 0);
571 					break;
572 
573 				case 0x0F: // set speed
574 					m.command = CMD_SPEED;
575 					speedFrac = m.param & 0x0F;
576 					m.param >>= 4;
577 					break;
578 
579 				case 0x10: // auto vibrato
580 					chnMem.autoVibrato = m.param;
581 					chnMem.vibratoMem = 0;
582 					m.command = CMD_NONE;
583 					break;
584 
585 				case 0x11: // auto tremolo
586 					if(m.param & 0xF)
587 						chnMem.autoTremolo = m.param;
588 					else
589 						chnMem.autoTremolo = 0;
590 					m.command = CMD_NONE;
591 					break;
592 
593 				case 0x12: // pattern break
594 					m.command = CMD_PATTERNBREAK;
595 					break;
596 
597 				case 0x13: // auto tone portamento
598 					chnMem.autoFinePorta = 0;
599 					chnMem.autoPortaUp = 0;
600 					chnMem.autoPortaDown = 0;
601 					chnMem.autoTonePorta = m.param;
602 
603 					chnMem.tonePortaMem = 0;
604 					m.command = CMD_NONE;
605 					break;
606 
607 				case 0x14: // position jump
608 					m.command = CMD_POSITIONJUMP;
609 					break;
610 
611 				case 0x16: // start loop sequence
612 					if(m.instr && m.instr <= loopInfo.size())
613 					{
614 						STPLoopList &loopList = loopInfo[m.instr - 1];
615 
616 						m.param--;
617 						if(m.param < std::min(std::size(ModSample().cues), loopList.size()))
618 						{
619 							m.volcmd = VOLCMD_OFFSET;
620 							m.vol = m.param;
621 						}
622 					}
623 
624 					m.command = CMD_NONE;
625 					break;
626 
627 				case 0x17: // play only loop nn
628 					if(m.instr && m.instr <= loopInfo.size())
629 					{
630 						STPLoopList &loopList = loopInfo[m.instr - 1];
631 
632 						m.param--;
633 						if(m.param < loopList.size())
634 						{
635 							if(!loopList[m.param].looped && CanAddMoreSamples())
636 								loopList[m.param].looped = ++m_nSamples;
637 							m.instr = static_cast<ModCommand::INSTR>(loopList[m.param].looped);
638 						}
639 					}
640 
641 					m.command = CMD_NONE;
642 					break;
643 
644 				case 0x18: // play sequence without loop
645 					if(m.instr && m.instr <= loopInfo.size())
646 					{
647 						STPLoopList &loopList = loopInfo[m.instr - 1];
648 
649 						m.param--;
650 						if(m.param < std::min(std::size(ModSample().cues), loopList.size()))
651 						{
652 							m.volcmd = VOLCMD_OFFSET;
653 							m.vol = m.param;
654 						}
655 						// switch to non-looped version of sample and create it if needed
656 						if(!nonLooped[m.instr - 1] && CanAddMoreSamples())
657 							nonLooped[m.instr - 1] = ++m_nSamples;
658 						m.instr = static_cast<ModCommand::INSTR>(nonLooped[m.instr - 1]);
659 					}
660 
661 					m.command = CMD_NONE;
662 					break;
663 
664 				case 0x19: // play only loop nn without loop
665 					if(m.instr && m.instr <= loopInfo.size())
666 					{
667 						STPLoopList &loopList = loopInfo[m.instr - 1];
668 
669 						m.param--;
670 						if(m.param < loopList.size())
671 						{
672 							if(!loopList[m.param].nonLooped && CanAddMoreSamples())
673 								loopList[m.param].nonLooped = ++m_nSamples;
674 							m.instr = static_cast<ModCommand::INSTR>(loopList[m.param].nonLooped);
675 						}
676 					}
677 
678 					m.command = CMD_NONE;
679 					break;
680 
681 				case 0x1D: // fine volume slide (nibble order also swapped)
682 					m.command = CMD_VOLUMESLIDE;
683 					m.param = swapped;
684 					if(m.param & 0xF0) // slide down
685 						m.param |= 0x0F;
686 					else if(m.param & 0x0F)
687 						m.param |= 0xF0;
688 					break;
689 
690 				case 0x20: // "delayed fade"
691 					// just behave like either a normal fade or a notecut
692 					// depending on the speed
693 					if(m.param & 0xF0)
694 					{
695 						chnMem.autoVolSlide = m.param >> 4;
696 						m.command = CMD_NONE;
697 					} else
698 					{
699 						m.command = CMD_MODCMDEX;
700 						m.param = 0xC0 | (m.param & 0xF);
701 					}
702 					break;
703 
704 				case 0x21: // note delay
705 					m.command = CMD_MODCMDEX;
706 					m.param = 0xD0 | std::min(m.param, ModCommand::PARAM(15));
707 					break;
708 
709 				case 0x22: // retrigger note
710 					m.command = CMD_MODCMDEX;
711 					m.param = 0x90 | std::min(m.param, ModCommand::PARAM(15));
712 					break;
713 
714 				case 0x49: // set sample offset
715 					m.command = CMD_OFFSET;
716 					break;
717 
718 				case 0x4E: // other protracker commands (pattern loop / delay)
719 					if((m.param & 0xF0) == 0x60 || (m.param & 0xF0) == 0xE0)
720 						m.command = CMD_MODCMDEX;
721 					else
722 						m.command = CMD_NONE;
723 					break;
724 
725 				case 0x4F: // set speed/tempo
726 					if(m.param < 0x20)
727 					{
728 						m.command = CMD_SPEED;
729 						speedFrac = 0;
730 					} else
731 					{
732 						m.command = CMD_TEMPO;
733 					}
734 					break;
735 
736 				default:
737 					m.command = CMD_NONE;
738 					break;
739 				}
740 
741 				bool didVolSlide = false;
742 
743 				// try to put volume slide in volume command
744 				if(chnMem.autoVolSlide && m.volcmd == VOLCMD_NONE)
745 				{
746 					if(chnMem.autoVolSlide & 0xF0)
747 					{
748 						m.volcmd = VOLCMD_FINEVOLUP;
749 						m.vol = chnMem.autoVolSlide >> 4;
750 					} else
751 					{
752 						m.volcmd = VOLCMD_FINEVOLDOWN;
753 						m.vol = chnMem.autoVolSlide & 0xF;
754 					}
755 					didVolSlide = true;
756 				}
757 
758 				// try to place/combine all remaining running effects.
759 				if(m.command == CMD_NONE)
760 				{
761 					if(chnMem.autoPortaUp)
762 					{
763 						m.command = CMD_PORTAMENTOUP;
764 						m.param = chnMem.autoPortaUp;
765 
766 					} else if(chnMem.autoPortaDown)
767 					{
768 						m.command = CMD_PORTAMENTODOWN;
769 						m.param = chnMem.autoPortaDown;
770 					} else if(chnMem.autoFinePorta)
771 					{
772 						m.command = CMD_MODCMDEX;
773 						m.param = chnMem.autoFinePorta;
774 
775 					} else if(chnMem.autoTonePorta)
776 					{
777 						m.command = CMD_TONEPORTAMENTO;
778 						m.param = chnMem.tonePortaMem = chnMem.autoTonePorta;
779 
780 					} else if(chnMem.autoVibrato)
781 					{
782 						m.command = CMD_VIBRATO;
783 						m.param = chnMem.vibratoMem = chnMem.autoVibrato;
784 
785 					} else if(!didVolSlide && chnMem.autoVolSlide)
786 					{
787 						m.command = CMD_VOLUMESLIDE;
788 						m.param = chnMem.autoVolSlide;
789 						// convert to a "fine" value by setting the other nibble to 0xF
790 						if(m.param & 0x0F)
791 							m.param |= 0xF0;
792 						else if(m.param & 0xF0)
793 							m.param |= 0x0F;
794 						didVolSlide = true;
795 
796 					} else if(chnMem.autoTremolo)
797 					{
798 						m.command = CMD_TREMOLO;
799 						m.param = chnMem.autoTremolo;
800 
801 					} else if(shouldDelay)
802 					{
803 						// insert a fine pattern delay here
804 						m.command = CMD_S3MCMDEX;
805 						m.param = 0x61;
806 						shouldDelay = false;
807 
808 					} else if(!didGlobalVolSlide && globalVolSlide)
809 					{
810 						m.command = CMD_GLOBALVOLSLIDE;
811 						m.param = globalVolSlide;
812 						// convert to a "fine" value by setting the other nibble to 0xF
813 						if(m.param & 0x0F)
814 							m.param |= 0xF0;
815 						else if(m.param & 0xF0)
816 							m.param |= 0x0F;
817 
818 						didGlobalVolSlide = true;
819 					}
820 				}
821 			}
822 
823 			// TODO: create/use extra channels for global volslide/delay if needed
824 		}
825 	}
826 
827 	// after we know how many channels there really are...
828 	m_nSamplePreAmp = 256 / m_nChannels;
829 	// Setup channel pan positions and volume
830 	SetupMODPanning(true);
831 
832 	// Skip over scripts and drumpad info
833 	if(fileHeader.version > 0)
834 	{
835 		while(file.CanRead(2))
836 		{
837 			uint16 scriptNum = file.ReadUint16BE();
838 			if(scriptNum == 0xFFFF)
839 				break;
840 
841 			file.Skip(2);
842 			uint32 length = file.ReadUint32BE();
843 			file.Skip(length);
844 		}
845 
846 		// Skip drumpad stuff
847 		file.Skip(17 * 2);
848 	}
849 
850 	// Reading samples
851 	if(loadFlags & loadSampleData)
852 	{
853 		for(SAMPLEINDEX smp = 1; smp <= samplesInFile; smp++) if(Samples[smp].nLength)
854 		{
855 			SampleIO(
856 				SampleIO::_8bit,
857 				SampleIO::mono,
858 				SampleIO::littleEndian,
859 				SampleIO::signedPCM)
860 				.ReadSample(Samples[smp], file);
861 
862 			if(smp > loopInfo.size())
863 				continue;
864 
865 			ConvertLoopSequence(Samples[smp], loopInfo[smp - 1]);
866 
867 			// make a non-looping duplicate of this sample if needed
868 			if(nonLooped[smp - 1])
869 			{
870 				ConvertLoopSlice(Samples[smp], Samples[nonLooped[smp - 1]], 0, Samples[smp].nLength, false);
871 			}
872 
873 			for(const auto &info : loopInfo[smp - 1])
874 			{
875 				// make duplicate samples for this individual section if needed
876 				if(info.looped)
877 				{
878 					ConvertLoopSlice(Samples[smp], Samples[info.looped], info.loopStart, info.loopLength, true);
879 				}
880 				if(info.nonLooped)
881 				{
882 					ConvertLoopSlice(Samples[smp], Samples[info.nonLooped], info.loopStart, info.loopLength, false);
883 				}
884 			}
885 		}
886 	}
887 
888 	return true;
889 }
890 
891 OPENMPT_NAMESPACE_END
892