1 /*
2  * Load_mdl.cpp
3  * ------------
4  * Purpose: Digitrakker (MDL) module loader
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 "Loaders.h"
13 
14 OPENMPT_NAMESPACE_BEGIN
15 
16 // MDL file header
17 struct MDLFileHeader
18 {
19 	char  id[4];	// "DMDL"
20 	uint8 version;
21 };
22 
23 MPT_BINARY_STRUCT(MDLFileHeader, 5)
24 
25 
26 // RIFF-style Chunk
27 struct MDLChunk
28 {
29 	// 16-Bit chunk identifiers
30 	enum ChunkIdentifiers
31 	{
32 		idInfo			= MagicLE("IN"),
33 		idMessage		= MagicLE("ME"),
34 		idPats			= MagicLE("PA"),
35 		idPatNames		= MagicLE("PN"),
36 		idTracks		= MagicLE("TR"),
37 		idInstrs		= MagicLE("II"),
38 		idVolEnvs		= MagicLE("VE"),
39 		idPanEnvs		= MagicLE("PE"),
40 		idFreqEnvs		= MagicLE("FE"),
41 		idSampleInfo	= MagicLE("IS"),
42 		ifSampleData	= MagicLE("SA"),
43 	};
44 
45 	uint16le id;
46 	uint32le length;
47 
GetLengthMDLChunk48 	size_t GetLength() const
49 	{
50 		return length;
51 	}
52 
GetIDMDLChunk53 	ChunkIdentifiers GetID() const
54 	{
55 		return static_cast<ChunkIdentifiers>(id.get());
56 	}
57 };
58 
59 MPT_BINARY_STRUCT(MDLChunk, 6)
60 
61 
62 struct MDLInfoBlock
63 {
64 	char     title[32];
65 	char     composer[20];
66 	uint16le numOrders;
67 	uint16le restartPos;
68 	uint8le  globalVol;	// 1...255
69 	uint8le  speed;		// 1...255
70 	uint8le  tempo;		// 4...255
71 	uint8le  chnSetup[32];
72 };
73 
74 MPT_BINARY_STRUCT(MDLInfoBlock, 91)
75 
76 
77 // Sample header in II block
78 struct MDLSampleHeader
79 {
80 	uint8le  smpNum;
81 	uint8le  lastNote;
82 	uint8le  volume;
83 	uint8le  volEnvFlags;	// 6 bits env #, 2 bits flags
84 	uint8le  panning;
85 	uint8le  panEnvFlags;
86 	uint16le fadeout;
87 	uint8le  vibSpeed;
88 	uint8le  vibDepth;
89 	uint8le  vibSweep;
90 	uint8le  vibType;
91 	uint8le  reserved;		// zero
92 	uint8le  freqEnvFlags;
93 };
94 
95 MPT_BINARY_STRUCT(MDLSampleHeader, 14)
96 
97 
98 struct MDLEnvelope
99 {
100 	uint8 envNum;
101 	struct
102 	{
103 		uint8 x;	// Delta value from last point, 0 means no more points defined
104 		uint8 y;	// 0...63
105 	} nodes[15];
106 	uint8 flags;
107 	uint8 loop;		// Lower 4 bits = start, upper 4 bits = end
108 
ConvertToMPTMDLEnvelope109 	void ConvertToMPT(InstrumentEnvelope &mptEnv) const
110 	{
111 		mptEnv.dwFlags.reset();
112 		mptEnv.clear();
113 		mptEnv.reserve(15);
114 		int16 tick = -nodes[0].x;
115 		for(uint8 n = 0; n < 15; n++)
116 		{
117 			if(!nodes[n].x)
118 				break;
119 			tick += nodes[n].x;
120 			mptEnv.push_back(EnvelopeNode(tick, std::min(nodes[n].y, uint8(64)))); // actually 0-63
121 		}
122 
123 		mptEnv.nLoopStart = (loop & 0x0F);
124 		mptEnv.nLoopEnd = (loop >> 4);
125 		mptEnv.nSustainStart = mptEnv.nSustainEnd = (flags & 0x0F);
126 
127 		if(flags & 0x10) mptEnv.dwFlags.set(ENV_SUSTAIN);
128 		if(flags & 0x20) mptEnv.dwFlags.set(ENV_LOOP);
129 	}
130 };
131 
132 MPT_BINARY_STRUCT(MDLEnvelope, 33)
133 
134 
135 struct MDLPatternHeader
136 {
137 	uint8le channels;
138 	uint8le lastRow;
139 	char    name[16];
140 };
141 
142 MPT_BINARY_STRUCT(MDLPatternHeader, 18)
143 
144 
145 enum
146 {
147 	MDLNOTE_NOTE	= 1 << 0,
148 	MDLNOTE_SAMPLE	= 1 << 1,
149 	MDLNOTE_VOLUME	= 1 << 2,
150 	MDLNOTE_EFFECTS	= 1 << 3,
151 	MDLNOTE_PARAM1	= 1 << 4,
152 	MDLNOTE_PARAM2	= 1 << 5,
153 };
154 
155 
156 static constexpr VibratoType MDLVibratoType[] = { VIB_SINE, VIB_RAMP_DOWN, VIB_SQUARE, VIB_SINE };
157 
158 static constexpr ModCommand::COMMAND MDLEffTrans[] =
159 {
160 	/* 0 */ CMD_NONE,
161 	/* 1st column only */
162 	/* 1 */ CMD_PORTAMENTOUP,
163 	/* 2 */ CMD_PORTAMENTODOWN,
164 	/* 3 */ CMD_TONEPORTAMENTO,
165 	/* 4 */ CMD_VIBRATO,
166 	/* 5 */ CMD_ARPEGGIO,
167 	/* 6 */ CMD_NONE,
168 	/* Either column */
169 	/* 7 */ CMD_TEMPO,
170 	/* 8 */ CMD_PANNING8,
171 	/* 9 */ CMD_SETENVPOSITION,
172 	/* A */ CMD_NONE,
173 	/* B */ CMD_POSITIONJUMP,
174 	/* C */ CMD_GLOBALVOLUME,
175 	/* D */ CMD_PATTERNBREAK,
176 	/* E */ CMD_S3MCMDEX,
177 	/* F */ CMD_SPEED,
178 	/* 2nd column only */
179 	/* G */ CMD_VOLUMESLIDE, // up
180 	/* H */ CMD_VOLUMESLIDE, // down
181 	/* I */ CMD_RETRIG,
182 	/* J */ CMD_TREMOLO,
183 	/* K */ CMD_TREMOR,
184 	/* L */ CMD_NONE,
185 };
186 
187 
188 // receive an MDL effect, give back a 'normal' one.
ConvertMDLCommand(uint8 & cmd,uint8 & param)189 static void ConvertMDLCommand(uint8 &cmd, uint8 &param)
190 {
191 	if(cmd >= std::size(MDLEffTrans))
192 		return;
193 
194 	uint8 origCmd = cmd;
195 	cmd = MDLEffTrans[cmd];
196 
197 	switch(origCmd)
198 	{
199 #ifdef MODPLUG_TRACKER
200 	case 0x07: // Tempo
201 		// MDL supports any nonzero tempo value, but OpenMPT doesn't
202 		param = std::max(param, uint8(0x20));
203 		break;
204 #endif // MODPLUG_TRACKER
205 	case 0x08: // Panning
206 		param = (param & 0x7F) * 2u;
207 		break;
208 	case 0x0C:	// Global volume
209 		param = (param + 1) / 2u;
210 		break;
211 	case 0x0D: // Pattern Break
212 		// Convert from BCD
213 		param = 10 * (param >> 4) + (param & 0x0F);
214 		break;
215 	case 0x0E: // Special
216 		switch(param >> 4)
217 		{
218 		case 0x0: // unused
219 		case 0x3: // unused
220 		case 0x8: // Set Samplestatus (loop type)
221 			cmd = CMD_NONE;
222 			break;
223 		case 0x1: // Pan Slide Left
224 			cmd = CMD_PANNINGSLIDE;
225 			param = (std::min(static_cast<uint8>(param & 0x0F), uint8(0x0E)) << 4) | 0x0F;
226 			break;
227 		case 0x2: // Pan Slide Right
228 			cmd = CMD_PANNINGSLIDE;
229 			param = 0xF0 | std::min(static_cast<uint8>(param & 0x0F), uint8(0x0E));
230 			break;
231 		case 0x4: // Vibrato Waveform
232 			param = 0x30 | (param & 0x0F);
233 			break;
234 		case 0x5:  // Set Finetune
235 			cmd = CMD_FINETUNE;
236 			param = (param << 4) ^ 0x80;
237 			break;
238 		case 0x6: // Pattern Loop
239 			param = 0xB0 | (param & 0x0F);
240 			break;
241 		case 0x7: // Tremolo Waveform
242 			param = 0x40 | (param & 0x0F);
243 			break;
244 		case 0x9: // Retrig
245 			cmd = CMD_RETRIG;
246 			param &= 0x0F;
247 			break;
248 		case 0xA: // Global vol slide up
249 			cmd = CMD_GLOBALVOLSLIDE;
250 			param = 0xF0 & (((param & 0x0F) + 1) << 3);
251 			break;
252 		case 0xB: // Global vol slide down
253 			cmd = CMD_GLOBALVOLSLIDE;
254 			param = ((param & 0x0F) + 1) >> 1;
255 			break;
256 		case 0xC: // Note cut
257 		case 0xD: // Note delay
258 		case 0xE: // Pattern delay
259 			// Nothing to change here
260 			break;
261 		case 0xF: // Offset -- further mangled later.
262 			cmd = CMD_OFFSET;
263 			break;
264 		}
265 		break;
266 	case 0x10: // Volslide up
267 		if(param < 0xE0)
268 		{
269 			// 00...DF regular slide - four times more precise than in XM
270 			param >>= 2;
271 			if(param > 0x0F)
272 				param = 0x0F;
273 			param <<= 4;
274 		} else if(param < 0xF0)
275 		{
276 			// E0...EF extra fine slide (on first tick, 4 times finer)
277 			param = (((param & 0x0F) << 2) | 0x0F);
278 		} else
279 		{
280 			// F0...FF regular fine slide (on first tick) - like in XM
281 			param = ((param << 4) | 0x0F);
282 		}
283 		break;
284 	case 0x11: // Volslide down
285 		if(param < 0xE0)
286 		{
287 			// 00...DF regular slide - four times more precise than in XM
288 			param >>= 2;
289 			if(param > 0x0F)
290 				param = 0x0F;
291 		} else if(param < 0xF0)
292 		{
293 			// E0...EF extra fine slide (on first tick, 4 times finer)
294 			param = (((param & 0x0F) >> 2) | 0xF0);
295 		} else
296 		{
297 			// F0...FF regular fine slide (on first tick) - like in XM
298 		}
299 		break;
300 	}
301 }
302 
303 
304 // Returns true if command was lost
ImportMDLCommands(ModCommand & m,uint8 vol,uint8 e1,uint8 e2,uint8 p1,uint8 p2)305 static bool ImportMDLCommands(ModCommand &m, uint8 vol, uint8 e1, uint8 e2, uint8 p1, uint8 p2)
306 {
307 	// Map second effect values 1-6 to effects G-L
308 	if(e2 >= 1 && e2 <= 6)
309 		e2 += 15;
310 
311 	ConvertMDLCommand(e1, p1);
312 	ConvertMDLCommand(e2, p2);
313 	/* From the Digitrakker documentation:
314 		* EFx -xx - Set Sample Offset
315 		This  is a  double-command.  It starts the
316 		sample at adress xxx*256.
317 		Example: C-5 01 -- EF1 -23 ->starts sample
318 		01 at address 12300 (in hex).
319 	Kind of screwy, but I guess it's better than the mess required to do it with IT (which effectively
320 	requires 3 rows in order to set the offset past 0xff00). If we had access to the entire track, we
321 	*might* be able to shove the high offset SAy into surrounding rows (or 2x MPTM #xx), but it wouldn't
322 	always be possible, it'd make the loader a lot uglier, and generally would be more trouble than
323 	it'd be worth to implement.
324 
325 	What's more is, if there's another effect in the second column, it's ALSO processed in addition to the
326 	offset, and the second data byte is shared between the two effects. */
327 	uint32 offset = uint32_max;
328 	uint8 otherCmd = CMD_NONE;
329 	if(e1 == CMD_OFFSET)
330 	{
331 		// EFy -xx => offset yxx00
332 		offset = ((p1 & 0x0F) << 8) | p2;
333 		p1 = (p1 & 0x0F) ? 0xFF : p2;
334 		if(e2 == CMD_OFFSET)
335 			e2 = CMD_NONE;
336 		else
337 			otherCmd = e2;
338 	} else if (e2 == CMD_OFFSET)
339 	{
340 		// --- EFy => offset y0000
341 		offset = (p2 & 0x0F) << 8;
342 		p2 = (p2 & 0x0F) ? 0xFF : 0;
343 		otherCmd = e1;
344 	}
345 
346 	if(offset != uint32_max && offset > 0xFF && ModCommand::GetEffectWeight(otherCmd) < ModCommand::GetEffectWeight(CMD_OFFSET))
347 	{
348 		m.command = CMD_OFFSET;
349 		m.param = static_cast<ModCommand::PARAM>(offset & 0xFF);
350 		m.volcmd = VOLCMD_OFFSET;
351 		m.vol = static_cast<ModCommand::VOL>(offset >> 8);
352 		return otherCmd != CMD_NONE || vol != 0;
353 	}
354 
355 	if(vol)
356 	{
357 		m.volcmd = VOLCMD_VOLUME;
358 		m.vol = (vol + 2) / 4u;
359 	}
360 
361 	// If we have Dxx + G00, or Dxx + H00, combine them into Lxx/Kxx.
362 	ModCommand::CombineEffects(e1, p1, e2, p2);
363 
364 	bool lostCommand = false;
365 	// Try to fit the "best" effect into e2.
366 	if(e1 == CMD_NONE)
367 	{
368 		// Easy
369 	} else if(e2 == CMD_NONE)
370 	{
371 		// Almost as easy
372 		e2 = e1;
373 		p2 = p1;
374 		e1 = CMD_NONE;
375 	} else if(e1 == e2 && e1 != CMD_S3MCMDEX)
376 	{
377 		// Digitrakker processes the effects left-to-right, so if both effects are the same, the
378 		// second essentially overrides the first.
379 		e1 = CMD_NONE;
380 	} else if(!vol)
381 	{
382 		lostCommand |= (ModCommand::TwoRegularCommandsToMPT(e1, p1, e2, p2).first != CMD_NONE);
383 		m.volcmd = e1;
384 		m.vol = p1;
385 	} else
386 	{
387 		if(ModCommand::GetEffectWeight((ModCommand::COMMAND)e1) > ModCommand::GetEffectWeight((ModCommand::COMMAND)e2))
388 		{
389 			std::swap(e1, e2);
390 			std::swap(p1, p2);
391 		}
392 	}
393 
394 	m.command = e2;
395 	m.param = p2;
396 	return lostCommand;
397 }
398 
399 
MDLReadEnvelopes(FileReader file,std::vector<MDLEnvelope> & envelopes)400 static void MDLReadEnvelopes(FileReader file, std::vector<MDLEnvelope> &envelopes)
401 {
402 	if(!file.CanRead(1))
403 		return;
404 
405 	envelopes.resize(64);
406 	uint8 numEnvs = file.ReadUint8();
407 	while(numEnvs--)
408 	{
409 		MDLEnvelope mdlEnv;
410 		if(!file.ReadStruct(mdlEnv) || mdlEnv.envNum > 63)
411 			continue;
412 		envelopes[mdlEnv.envNum] = mdlEnv;
413 	}
414 }
415 
416 
CopyEnvelope(InstrumentEnvelope & mptEnv,uint8 flags,std::vector<MDLEnvelope> & envelopes)417 static void CopyEnvelope(InstrumentEnvelope &mptEnv, uint8 flags, std::vector<MDLEnvelope> &envelopes)
418 {
419 	uint8 envNum = flags & 0x3F;
420 	if(envNum < envelopes.size())
421 		envelopes[envNum].ConvertToMPT(mptEnv);
422 	mptEnv.dwFlags.set(ENV_ENABLED, (flags & 0x80) && !mptEnv.empty());
423 }
424 
425 
ValidateHeader(const MDLFileHeader & fileHeader)426 static bool ValidateHeader(const MDLFileHeader &fileHeader)
427 {
428 	if(std::memcmp(fileHeader.id, "DMDL", 4)
429 		|| fileHeader.version >= 0x20)
430 	{
431 		return false;
432 	}
433 	return true;
434 }
435 
436 
ProbeFileHeaderMDL(MemoryFileReader file,const uint64 * pfilesize)437 CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMDL(MemoryFileReader file, const uint64 *pfilesize)
438 {
439 	MDLFileHeader fileHeader;
440 	if(!file.ReadStruct(fileHeader))
441 	{
442 		return ProbeWantMoreData;
443 	}
444 	if(!ValidateHeader(fileHeader))
445 	{
446 		return ProbeFailure;
447 	}
448 	MPT_UNREFERENCED_PARAMETER(pfilesize);
449 	return ProbeSuccess;
450 }
451 
452 
ReadMDL(FileReader & file,ModLoadingFlags loadFlags)453 bool CSoundFile::ReadMDL(FileReader &file, ModLoadingFlags loadFlags)
454 {
455 	file.Rewind();
456 	MDLFileHeader fileHeader;
457 	if(!file.ReadStruct(fileHeader))
458 	{
459 		return false;
460 	}
461 	if(!ValidateHeader(fileHeader))
462 	{
463 		return false;
464 	}
465 	if(loadFlags == onlyVerifyHeader)
466 	{
467 		return true;
468 	}
469 
470 	ChunkReader chunkFile(file);
471 	ChunkReader::ChunkList<MDLChunk> chunks = chunkFile.ReadChunks<MDLChunk>(0);
472 
473 	// Read global info
474 	FileReader chunk = chunks.GetChunk(MDLChunk::idInfo);
475 	MDLInfoBlock info;
476 	if(!chunk.IsValid() || !chunk.ReadStruct(info))
477 	{
478 		return false;
479 	}
480 
481 	InitializeGlobals(MOD_TYPE_MDL);
482 	m_SongFlags = SONG_ITCOMPATGXX;
483 	m_playBehaviour.set(kPerChannelGlobalVolSlide);
484 	m_playBehaviour.set(kApplyOffsetWithoutNote);
485 	m_playBehaviour.reset(kITVibratoTremoloPanbrello);
486 	m_playBehaviour.reset(kITSCxStopsSample);	// Gate effect in underbeat.mdl
487 
488 	m_modFormat.formatName = U_("Digitrakker");
489 	m_modFormat.type = U_("mdl");
490 	m_modFormat.madeWithTracker = U_("Digitrakker ") + (
491 		(fileHeader.version == 0x11) ? U_("3") // really could be 2.99b - close enough
492 		: (fileHeader.version == 0x10) ? U_("2.3")
493 		: (fileHeader.version == 0x00) ? U_("2.0 - 2.2b") // there was no 1.x release
494 		: U_(""));
495 	m_modFormat.charset = mpt::Charset::CP437;
496 
497 	m_songName = mpt::String::ReadBuf(mpt::String::spacePadded, info.title);
498 	m_songArtist = mpt::ToUnicode(mpt::Charset::CP437, mpt::String::ReadBuf(mpt::String::spacePadded, info.composer));
499 
500 	m_nDefaultGlobalVolume = info.globalVol + 1;
501 	m_nDefaultSpeed = Clamp<uint8, uint8>(info.speed, 1, 255);
502 	m_nDefaultTempo.Set(Clamp<uint8, uint8>(info.tempo, 4, 255));
503 
504 	ReadOrderFromFile<uint8>(Order(), chunk, info.numOrders);
505 	Order().SetRestartPos(info.restartPos);
506 
507 	m_nChannels = 0;
508 	for(CHANNELINDEX c = 0; c < 32; c++)
509 	{
510 		ChnSettings[c].Reset();
511 		ChnSettings[c].nPan = (info.chnSetup[c] & 0x7F) * 2u;
512 		if(ChnSettings[c].nPan == 254)
513 			ChnSettings[c].nPan = 256;
514 		if(info.chnSetup[c] & 0x80)
515 			ChnSettings[c].dwFlags.set(CHN_MUTE);
516 		else
517 			m_nChannels = c + 1;
518 		chunk.ReadString<mpt::String::spacePadded>(ChnSettings[c].szName, 8);
519 	}
520 
521 	// Read song message
522 	chunk = chunks.GetChunk(MDLChunk::idMessage);
523 	m_songMessage.Read(chunk, chunk.GetLength(), SongMessage::leCR);
524 
525 	// Read sample info and data
526 	chunk = chunks.GetChunk(MDLChunk::idSampleInfo);
527 	if(chunk.IsValid())
528 	{
529 		FileReader dataChunk = chunks.GetChunk(MDLChunk::ifSampleData);
530 
531 		uint8 numSamples = chunk.ReadUint8();
532 		for(uint8 smp = 0; smp < numSamples; smp++)
533 		{
534 			const SAMPLEINDEX sampleIndex = chunk.ReadUint8();
535 			if(sampleIndex == 0 || sampleIndex >= MAX_SAMPLES || !chunk.CanRead(32 + 8 + 2 + 12 + 2))
536 				break;
537 
538 			if(sampleIndex > GetNumSamples())
539 				m_nSamples = sampleIndex;
540 
541 			ModSample &sample = Samples[sampleIndex];
542 			sample.Initialize();
543 			sample.Set16BitCuePoints();
544 
545 			chunk.ReadString<mpt::String::spacePadded>(m_szNames[sampleIndex], 32);
546 			chunk.ReadString<mpt::String::spacePadded>(sample.filename, 8);
547 
548 			uint32 c4speed;
549 			if(fileHeader.version < 0x10)
550 				c4speed = chunk.ReadUint16LE();
551 			else
552 				c4speed = chunk.ReadUint32LE();
553 			sample.nC5Speed = c4speed * 2u;
554 			sample.nLength = chunk.ReadUint32LE();
555 			sample.nLoopStart = chunk.ReadUint32LE();
556 			sample.nLoopEnd = chunk.ReadUint32LE();
557 			if(sample.nLoopEnd != 0)
558 			{
559 				sample.uFlags.set(CHN_LOOP);
560 				sample.nLoopEnd += sample.nLoopStart;
561 			}
562 			uint8 volume = chunk.ReadUint8();
563 			if(fileHeader.version < 0x10)
564 				sample.nVolume = volume;
565 			uint8 flags = chunk.ReadUint8();
566 
567 			if(flags & 0x01)
568 			{
569 				sample.uFlags.set(CHN_16BIT);
570 				sample.nLength /= 2u;
571 				sample.nLoopStart /= 2u;
572 				sample.nLoopEnd /= 2u;
573 			}
574 
575 			sample.uFlags.set(CHN_PINGPONGLOOP, (flags & 0x02) != 0);
576 
577 			SampleIO sampleIO(
578 				(flags & 0x01) ? SampleIO::_16bit : SampleIO::_8bit,
579 				SampleIO::mono,
580 				SampleIO::littleEndian,
581 				(flags & 0x0C) ? SampleIO::MDL : SampleIO::signedPCM);
582 
583 			if(loadFlags & loadSampleData)
584 			{
585 				sampleIO.ReadSample(sample, dataChunk);
586 			}
587 		}
588 	}
589 
590 	chunk = chunks.GetChunk(MDLChunk::idInstrs);
591 	if(chunk.IsValid())
592 	{
593 		std::vector<MDLEnvelope> volEnvs, panEnvs, pitchEnvs;
594 		MDLReadEnvelopes(chunks.GetChunk(MDLChunk::idVolEnvs), volEnvs);
595 		MDLReadEnvelopes(chunks.GetChunk(MDLChunk::idPanEnvs), panEnvs);
596 		MDLReadEnvelopes(chunks.GetChunk(MDLChunk::idFreqEnvs), pitchEnvs);
597 
598 		uint8 numInstruments = chunk.ReadUint8();
599 		for(uint8 i = 0; i < numInstruments; i++)
600 		{
601 			const auto [ins, numSamples] = chunk.ReadArray<uint8, 2>();
602 			uint8 firstNote = 0;
603 			ModInstrument *mptIns = nullptr;
604 			if(ins == 0
605 				|| !chunk.CanRead(32 + sizeof(MDLSampleHeader) * numSamples)
606 				|| (mptIns = AllocateInstrument(ins)) == nullptr)
607 			{
608 				chunk.Skip(32 + sizeof(MDLSampleHeader) * numSamples);
609 				continue;
610 			}
611 
612 			chunk.ReadString<mpt::String::spacePadded>(mptIns->name, 32);
613 			for(uint8 smp = 0; smp < numSamples; smp++)
614 			{
615 				MDLSampleHeader sampleHeader;
616 				chunk.ReadStruct(sampleHeader);
617 				if(sampleHeader.smpNum == 0 || sampleHeader.smpNum > GetNumSamples())
618 					continue;
619 
620 				LimitMax(sampleHeader.lastNote, static_cast<uint8>(std::size(mptIns->Keyboard)));
621 				for(uint8 n = firstNote; n <= sampleHeader.lastNote; n++)
622 				{
623 					mptIns->Keyboard[n] = sampleHeader.smpNum;
624 				}
625 				firstNote = sampleHeader.lastNote + 1;
626 
627 				CopyEnvelope(mptIns->VolEnv, sampleHeader.volEnvFlags, volEnvs);
628 				CopyEnvelope(mptIns->PanEnv, sampleHeader.panEnvFlags, panEnvs);
629 				CopyEnvelope(mptIns->PitchEnv, sampleHeader.freqEnvFlags, pitchEnvs);
630 				mptIns->nFadeOut = (sampleHeader.fadeout + 1u) / 2u;
631 #ifdef MODPLUG_TRACKER
632 				if((mptIns->VolEnv.dwFlags & (ENV_ENABLED | ENV_LOOP)) == ENV_ENABLED)
633 				{
634 					// Fade-out is only supposed to happen on key-off, not at the end of a volume envelope.
635 					// Fake it by putting a loop at the end.
636 					mptIns->VolEnv.nLoopStart = mptIns->VolEnv.nLoopEnd = static_cast<uint8>(mptIns->VolEnv.size() - 1);
637 					mptIns->VolEnv.dwFlags.set(ENV_LOOP);
638 				}
639 				for(auto &p : mptIns->PitchEnv)
640 				{
641 					// Scale pitch envelope
642 					p.value = (p.value * 6u) / 16u;
643 				}
644 #endif // MODPLUG_TRACKER
645 
646 				// Samples were already initialized above. Let's hope they are not going to be re-used with different volume / panning / vibrato...
647 				ModSample &mptSmp = Samples[sampleHeader.smpNum];
648 
649 				// This flag literally enables and disables the default volume of a sample. If you disable this flag,
650 				// the sample volume of a previously sample is re-used, even if you put an instrument number next to the note.
651 				if(sampleHeader.volEnvFlags & 0x40)
652 					mptSmp.nVolume = sampleHeader.volume;
653 				else
654 					mptSmp.uFlags.set(SMP_NODEFAULTVOLUME);
655 				mptSmp.nPan = std::min(static_cast<uint16>(sampleHeader.panning * 2), uint16(254));
656 				mptSmp.nVibType = MDLVibratoType[sampleHeader.vibType & 3];
657 				mptSmp.nVibSweep = sampleHeader.vibSweep;
658 				mptSmp.nVibDepth = (sampleHeader.vibDepth + 3u) / 4u;
659 				mptSmp.nVibRate = sampleHeader.vibSpeed;
660 				// Convert to IT-like vibrato sweep
661 				if(mptSmp.nVibSweep != 0)
662 					mptSmp.nVibSweep = mpt::saturate_cast<decltype(mptSmp.nVibSweep)>(Util::muldivr_unsigned(mptSmp.nVibDepth, 256, mptSmp.nVibSweep));
663 				else
664 					mptSmp.nVibSweep = 255;
665 				if(sampleHeader.panEnvFlags & 0x40)
666 					mptSmp.uFlags.set(CHN_PANNING);
667 			}
668 		}
669 	}
670 
671 	// Read pattern tracks
672 	std::vector<FileReader> tracks;
673 	if((loadFlags & loadPatternData) && (chunk = chunks.GetChunk(MDLChunk::idTracks)).IsValid())
674 	{
675 		uint32 numTracks = chunk.ReadUint16LE();
676 		tracks.resize(numTracks + 1);
677 		for(uint32 i = 1; i <= numTracks; i++)
678 		{
679 			tracks[i] = chunk.ReadChunk(chunk.ReadUint16LE());
680 		}
681 	}
682 
683 	// Read actual patterns
684 	if((loadFlags & loadPatternData) && (chunk = chunks.GetChunk(MDLChunk::idPats)).IsValid())
685 	{
686 		PATTERNINDEX numPats = chunk.ReadUint8();
687 
688 		// In case any muted channels contain data, be sure that we import them as well.
689 		for(PATTERNINDEX pat = 0; pat < numPats; pat++)
690 		{
691 			CHANNELINDEX numChans = 32;
692 			if(fileHeader.version >= 0x10)
693 			{
694 				MDLPatternHeader patHead;
695 				chunk.ReadStruct(patHead);
696 				if(patHead.channels > m_nChannels && patHead.channels <= 32)
697 					m_nChannels = patHead.channels;
698 				numChans = patHead.channels;
699 			}
700 			for(CHANNELINDEX chn = 0; chn < numChans; chn++)
701 			{
702 				if(chunk.ReadUint16LE() > 0 && chn >= m_nChannels && chn < 32)
703 					m_nChannels = chn + 1;
704 			}
705 		}
706 		chunk.Seek(1);
707 
708 		Patterns.ResizeArray(numPats);
709 		for(PATTERNINDEX pat = 0; pat < numPats; pat++)
710 		{
711 			CHANNELINDEX numChans = 32;
712 			ROWINDEX numRows = 64;
713 			std::string name;
714 			if(fileHeader.version >= 0x10)
715 			{
716 				MDLPatternHeader patHead;
717 				chunk.ReadStruct(patHead);
718 				numChans = patHead.channels;
719 				numRows = patHead.lastRow + 1;
720 				name = mpt::String::ReadBuf(mpt::String::spacePadded, patHead.name);
721 			}
722 
723 			if(!Patterns.Insert(pat, numRows))
724 			{
725 				chunk.Skip(2 * numChans);
726 				continue;
727 			}
728 			Patterns[pat].SetName(name);
729 
730 			for(CHANNELINDEX chn = 0; chn < numChans; chn++)
731 			{
732 				uint16 trkNum = chunk.ReadUint16LE();
733 				if(!trkNum || trkNum >= tracks.size() || chn >= m_nChannels)
734 					continue;
735 
736 				FileReader &track = tracks[trkNum];
737 				track.Rewind();
738 				ROWINDEX row = 0;
739 				while(row < numRows && track.CanRead(1))
740 				{
741 					ModCommand *m = Patterns[pat].GetpModCommand(row, chn);
742 					uint8 b = track.ReadUint8();
743 					uint8 x = (b >> 2), y = (b & 3);
744 					switch(y)
745 					{
746 					case 0:
747 						// (x + 1) empty notes follow
748 						row += x + 1;
749 						break;
750 					case 1:
751 						// Repeat previous note (x + 1) times
752 						if(row > 0)
753 						{
754 							ModCommand &orig = *Patterns[pat].GetpModCommand(row - 1, chn);
755 							do
756 							{
757 								*m = orig;
758 								m += m_nChannels;
759 								row++;
760 							} while (row < numRows && x--);
761 						}
762 						break;
763 					case 2:
764 						// Copy note from row x
765 						if(row > x)
766 						{
767 							*m = *Patterns[pat].GetpModCommand(x, chn);
768 						}
769 						row++;
770 						break;
771 					case 3:
772 						// New note data
773 						if(x & MDLNOTE_NOTE)
774 						{
775 							b = track.ReadUint8();
776 							m->note = (b > 120) ? static_cast<ModCommand::NOTE>(NOTE_KEYOFF) : static_cast<ModCommand::NOTE>(b);
777 						}
778 						if(x & MDLNOTE_SAMPLE)
779 						{
780 							m->instr = track.ReadUint8();
781 						}
782 						{
783 							uint8 vol = 0, e1 = 0, e2 = 0, p1 = 0, p2 = 0;
784 							if(x & MDLNOTE_VOLUME)
785 							{
786 								vol = track.ReadUint8();
787 							}
788 							if(x & MDLNOTE_EFFECTS)
789 							{
790 								b = track.ReadUint8();
791 								e1 = (b & 0x0F);
792 								e2 = (b >> 4);
793 							}
794 							if(x & MDLNOTE_PARAM1)
795 								p1 = track.ReadUint8();
796 							if(x & MDLNOTE_PARAM2)
797 								p2 = track.ReadUint8();
798 							ImportMDLCommands(*m, vol, e1, e2, p1, p2);
799 						}
800 
801 						row++;
802 						break;
803 					}
804 				}
805 			}
806 		}
807 	}
808 
809 	if((loadFlags & loadPatternData) && (chunk = chunks.GetChunk(MDLChunk::idPatNames)).IsValid())
810 	{
811 		PATTERNINDEX i = 0;
812 		while(i < Patterns.Size() && chunk.CanRead(16))
813 		{
814 			char name[17];
815 			chunk.ReadString<mpt::String::spacePadded>(name, 16);
816 			Patterns[i].SetName(name);
817 		}
818 	}
819 
820 	return true;
821 }
822 
823 
824 OPENMPT_NAMESPACE_END
825