1 /*
2  * Load_med.cpp
3  * ------------
4  * Purpose: OctaMED / MED Soundstudio module loader
5  * Notes  : Support for synthesized instruments is still missing.
6  * Authors: OpenMPT Devs
7  * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
8  */
9 
10 #include "stdafx.h"
11 #include "Loaders.h"
12 
13 #ifdef MPT_WITH_VST
14 #include "../mptrack/Vstplug.h"
15 #include "plugins/PluginManager.h"
16 #endif  // MPT_WITH_VST
17 #include "mpt/io/base.hpp"
18 #include "mpt/io/io.hpp"
19 #include "mpt/io/io_span.hpp"
20 #include "mpt/io/io_stdstream.hpp"
21 
22 #include <map>
23 
24 OPENMPT_NAMESPACE_BEGIN
25 
26 struct MMD0FileHeader
27 {
28 	char     mmd[3];               // "MMD" for the first song in file, "MCN" for the rest
29 	uint8be  version;              // '0'-'3'
30 	uint32be modLength;            // Size of file
31 	uint32be songOffset;           // Position in file for the first song
32 	uint16be playerSettings1[2];   // Internal variables for the play routine
33 	uint32be blockArrOffset;       // Position in file for blocks (patterns)
34 	uint8be  flags;
35 	uint8be  reserved1[3];
36 	uint32be sampleArrOffset;      // Position in file for samples (should be identical between songs)
37 	uint32be reserved2;
38 	uint32be expDataOffset;        // Absolute offset in file for ExpData (0 if not present)
39 	uint32be reserved3;
40 	char     playerSettings2[11];  // Internal variables for the play routine
41 	uint8be  extraSongs;           // Number of songs - 1
42 };
43 
44 MPT_BINARY_STRUCT(MMD0FileHeader, 52)
45 
46 
47 struct MMD0Sample
48 {
49 	uint16be loopStart;
50 	uint16be loopLength;
51 	uint8be  midiChannel;
52 	uint8be  midiPreset;
53 	uint8be  sampleVolume;
54 	int8be   sampleTranspose;
55 };
56 
57 MPT_BINARY_STRUCT(MMD0Sample, 8)
58 
59 
60 // Song header for MMD0/MMD1
61 struct MMD0Song
62 {
63 	uint8be sequence[256];
64 };
65 
66 MPT_BINARY_STRUCT(MMD0Song, 256)
67 
68 
69 // Song header for MMD2/MMD3
70 struct MMD2Song
71 {
72 	enum Flags3
73 	{
74 		FLAG3_STEREO  = 0x01,  // Mixing in stereo
75 		FLAG3_FREEPAN = 0x02,  // Mixing flag: free pan
76 	};
77 
78 	uint32be playSeqTableOffset;
79 	uint32be sectionTableOffset;
80 	uint32be trackVolsOffset;
81 	uint16be numTracks;
82 	uint16be numPlaySeqs;
83 	uint32be trackPanOffset;  // 0: all centered (according to docs, MED Soundstudio uses Amiga hard-panning instead)
84 	uint32be flags3;
85 	uint16be volAdjust;       // Volume adjust (%)
86 	uint16be mixChannels;     // Mixing channels, 0 means 4
87 	uint8be  mixEchoType;     // 0 = nothing, 1 = normal, 2 = cross
88 	uint8be  mixEchoDepth;    // 1 - 6, 0 = default
89 	uint16be mixEchoLength;   // Echo length in milliseconds
90 	int8be   mixStereoSep;    // Stereo separation
91 	char     pad0[223];
92 };
93 
94 MPT_BINARY_STRUCT(MMD2Song, 256)
95 
96 
97 // Common song header
98 struct MMDSong
99 {
100 	enum Flags
101 	{
102 		FLAG_FILTERON   = 0x01,  // The hardware audio filter is on
103 		FLAG_JUMPINGON  = 0x02,  // Mouse pointer jumping on
104 		FLAG_JUMP8TH    = 0x04,  // ump every 8th line (not in OctaMED Pro)
105 		FLAG_INSTRSATT  = 0x08,  // sng+samples indicator (not useful in MMDs)
106 		FLAG_VOLHEX     = 0x10,  // volumes are HEX
107 		FLAG_STSLIDE    = 0x20,  // use ST/NT/PT compatible sliding
108 		FLAG_8CHANNEL   = 0x40,  // this is OctaMED 5-8 channel song
109 		FLAG_SLOWHQ     = 0x80,  // HQ V2-4 compatibility mode
110 	};
111 
112 	enum Flags2
113 	{
114 		FLAG2_BMASK = 0x1F,  // (bits 0-4) BPM beat length (in lines)
115 		FLAG2_BPM   = 0x20,  // BPM mode on
116 		FLAG2_MIX   = 0x80,  // Module uses mixing
117 	};
118 
119 	uint16be numBlocks;   // Number of blocks in current song
120 	uint16be songLength;  // MMD0: Number of sequence numbers in the play sequence list, MMD2: Number of sections
121 	char song[256];
GetMMD0SongMMDSong122 	MMD0Song GetMMD0Song() const
123 	{
124 		static_assert(sizeof(MMD0Song) == sizeof(song));
125 		return mpt::bit_cast<MMD0Song>(song);
126 	}
GetMMD2SongMMDSong127 	MMD2Song GetMMD2Song() const
128 	{
129 		static_assert(sizeof(MMD2Song) == sizeof(song));
130 		return mpt::bit_cast<MMD2Song>(song);
131 	}
132 	uint16be defaultTempo;
133 	int8be   playTranspose;  // The global play transpose value for current song
134 	uint8be  flags;
135 	uint8be  flags2;
136 	uint8be  tempo2;        // Timing pulses per line (ticks)
137 	uint8be  trackVol[16];  // 1...64 in MMD0/MMD1, reserved in MMD2
138 	uint8be  masterVol;     // 1...64
139 	uint8be  numSamples;
140 };
141 
142 MPT_BINARY_STRUCT(MMDSong, 284)
143 
144 
145 struct MMD2PlaySeq
146 {
147 	char     name[32];
148 	uint32be commandTableOffset;
149 	uint32be reserved;
150 	uint16be length;  // Number of entries
151 };
152 
153 MPT_BINARY_STRUCT(MMD2PlaySeq, 42)
154 
155 
156 struct MMD0PatternHeader
157 {
158 	uint8be numTracks;
159 	uint8be numRows;
160 };
161 
162 MPT_BINARY_STRUCT(MMD0PatternHeader, 2)
163 
164 
165 struct MMD1PatternHeader
166 {
167 	uint16be numTracks;
168 	uint16be numRows;
169 	uint32be blockInfoOffset;
170 };
171 
172 MPT_BINARY_STRUCT(MMD1PatternHeader, 8)
173 
174 
175 struct MMDPlaySeqCommand
176 {
177 	enum Command
178 	{
179 		kStop = 1,
180 		kJump = 2,
181 	};
182 
183 	uint16be offset;   // Offset within current play sequence, 0xFFFF = end of list
184 	uint8be  command;  // Stop = 1, Jump = 2
185 	uint8be  extraSize;
186 };
187 
188 MPT_BINARY_STRUCT(MMDPlaySeqCommand, 4)
189 
190 
191 struct MMDBlockInfo
192 {
193 	uint32be highlightMaskOffset;
194 	uint32be nameOffset;
195 	uint32be nameLength;
196 	uint32be pageTableOffset;    // File offset of command page table
197 	uint32be cmdExtTableOffset;  // File offset of command extension table (second parameter)
198 	uint32be reserved[4];
199 };
200 
201 MPT_BINARY_STRUCT(MMDBlockInfo, 36)
202 
203 
204 struct MMDInstrHeader
205 {
206 	enum Types
207 	{
208 		VSTI      = -4,
209 		HIGHLIFE  = -3,
210 		HYBRID    = -2,
211 		SYNTHETIC = -1,
212 		SAMPLE    =  0,  // an ordinary 1-octave sample (or MIDI)
213 		IFF5OCT   =  1,  // 5 octaves
214 		IFF3OCT   =  2,  // 3 octaves
215 		// The following ones are recognized by OctaMED Pro only
216 		IFF2OCT   = 3,  // 2 octaves
217 		IFF4OCT   = 4,  // 4 octaves
218 		IFF6OCT   = 5,  // 6 octaves
219 		IFF7OCT   = 6,  // 7 octaves
220 		// OctaMED Pro V5 + later
221 		EXTSAMPLE = 7,  // two extra-low octaves
222 
223 		TYPEMASK  = 0x0F,
224 
225 		S_16          = 0x10,
226 		STEREO        = 0x20,
227 		DELTA         = 0x40,
228 		PACKED        = 0x80,  // MMDPackedSampleHeader follows
229 		OBSOLETE_MD16 = 0x18,
230 	};
231 
232 	uint32be length;
233 	int16be  type;
234 };
235 
236 MPT_BINARY_STRUCT(MMDInstrHeader, 6)
237 
238 
239 struct MMDPackedSampleHeader
240 {
241 	uint16be packType;    // Only 1 = ADPCM is supported
242 	uint16be subType;     // Packing subtype
243 	                      // ADPCM subtype
244 	                      // 1: g723_40
245 	                      // 2: g721
246 	                      // 3: g723_24
247 	uint8be commonFlags;  // flags common to all packtypes (none defined so far)
248 	uint8be packerFlags;  // flags for the specific packtype
249 	uint32be leftChLen;   // packed length of left channel in bytes
250 	uint32be rightChLen;  // packed length of right channel in bytes (ONLY PRESENT IN STEREO SAMPLES)
251 };
252 
253 MPT_BINARY_STRUCT(MMDPackedSampleHeader, 14)
254 
255 
256 struct MMDInstrExt
257 {
258 	enum
259 	{
260 		SSFLG_LOOP     = 0x01, // Loop On / Off
261 		SSFLG_EXTPSET  = 0x02, // Ext.Preset
262 		SSFLG_DISABLED = 0x04, // Disabled
263 		SSFLG_PINGPONG = 0x08, // Ping-pong looping
264 	};
265 
266 	uint8be  hold;   // 0...127
267 	uint8be  decay;  // 0...127
268 	uint8be  suppressMidiOff;
269 	int8be   finetune;
270 	// Below fields saved by >= V5
271 	uint8be  defaultPitch;
272 	uint8be  instrFlags;
273 	uint16be longMidiPreset;  // Legacy MIDI program mode that doesn't use banks but a combination of two program change commands
274 	// Below fields saved by >= V5.02
275 	uint8be  outputDevice;
276 	uint8be  reserved;
277 	// Below fields saved by >= V7
278 	uint32be loopStart;
279 	uint32be loopLength;
280 	// Not sure which version starts saving those but they are saved by MED Soundstudio for Windows
281 	uint8    volume;      // 0...127
282 	uint8    outputPort;  // Index into user-configurable device list (NOT WinAPI port index)
283 	uint16le midiBank;
284 };
285 
286 MPT_BINARY_STRUCT(MMDInstrExt, 22)
287 
288 
289 struct MMDInstrInfo
290 {
291 	char name[40];
292 };
293 
294 MPT_BINARY_STRUCT(MMDInstrInfo, 40)
295 
296 
297 struct MMD0Exp
298 {
299 	uint32be nextModOffset;
300 	uint32be instrExtOffset;
301 	uint16be instrExtEntries;
302 	uint16be instrExtEntrySize;
303 	uint32be annoText;
304 	uint32be annoLength;
305 	uint32be instrInfoOffset;
306 	uint16be instrInfoEntries;
307 	uint16be instrInfoEntrySize;
308 	uint32be jumpMask;
309 	uint32be rgbTable;
310 	uint8be  channelSplit[4];
311 	uint32be notationInfoOffset;
312 	uint32be songNameOffset;
313 	uint32be songNameLength;
314 	uint32be midiDumpOffset;
315 	uint32be mmdInfoOffset;
316 	uint32be arexxOffset;
317 	uint32be midiCmd3xOffset;
318 	uint32be trackInfoOffset;   // Pointer to song->numtracks pointers to tag lists
319 	uint32be effectInfoOffset;  // Pointers to group pointers
320 	uint32be tagEnd;
321 };
322 
323 MPT_BINARY_STRUCT(MMD0Exp, 80)
324 
325 
326 struct MMDTag
327 {
328 	enum TagType
329 	{
330 		// Generic MMD tags
331 		MMDTAG_END      = 0x00000000,
332 		MMDTAG_PTR      = 0x80000000,  // Data needs relocation
333 		MMDTAG_MUSTKNOW = 0x40000000,  // Loader must fail if this isn't recognized
334 		MMDTAG_MUSTWARN = 0x20000000,  // Loader must warn if this isn't recognized
335 		MMDTAG_MASK     = 0x1FFFFFFF,
336 
337 		// ExpData tags
338 		// # of effect groups, including the global group (will override settings in MMDSong struct), default = 1
339 		MMDTAG_EXP_NUMFXGROUPS = 1,
340 		MMDTAG_TRK_FXGROUP     = 3,
341 
342 		MMDTAG_TRK_NAME    = 1,  // trackinfo tags
343 		MMDTAG_TRK_NAMELEN = 2,  // namelen includes zero term.
344 
345 		// effectinfo tags
346 		MMDTAG_FX_ECHOTYPE   = 1,
347 		MMDTAG_FX_ECHOLEN    = 2,
348 		MMDTAG_FX_ECHODEPTH  = 3,
349 		MMDTAG_FX_STEREOSEP  = 4,
350 		MMDTAG_FX_GROUPNAME  = 5,  // the Global Effects group shouldn't have name saved!
351 		MMDTAG_FX_GRPNAMELEN = 6,  // namelen includes zero term.
352 	};
353 
354 	uint32be type;
355 	uint32be data;
356 };
357 
358 MPT_BINARY_STRUCT(MMDTag, 8)
359 
360 
361 struct MMDDump
362 {
363 	uint32be length;
364 	uint32be dataPointer;
365 	uint16be extLength;  // If >= 20: name follows as char[20]
366 };
367 
368 MPT_BINARY_STRUCT(MMDDump, 10)
369 
370 
MMDTempoToBPM(uint32 tempo,bool is8Ch,bool bpmMode,uint8 rowsPerBeat)371 static TEMPO MMDTempoToBPM(uint32 tempo, bool is8Ch, bool bpmMode, uint8 rowsPerBeat)
372 {
373 	if(bpmMode && !is8Ch)
374 	{
375 		// You would have thought that we could use modern tempo mode here.
376 		// Alas, the number of ticks per row still influences the tempo. :(
377 		return TEMPO((tempo * rowsPerBeat) / 4.0);
378 	}
379 	if(is8Ch && tempo > 0)
380 	{
381 		LimitMax(tempo, 10u);
382 		// MED Soundstudio uses these tempos when importing old files
383 		static constexpr uint8 tempos[10] = {179, 164, 152, 141, 131, 123, 116, 110, 104, 99};
384 		return TEMPO(tempos[tempo - 1], 0);
385 	} else if(tempo > 0 && tempo <= 10)
386 	{
387 		// SoundTracker compatible tempo
388 		return TEMPO((6.0 * 1773447.0 / 14500.0) / tempo);
389 	}
390 
391 	return TEMPO(tempo / 0.264);
392 }
393 
394 
ConvertMEDEffect(ModCommand & m,bool is8ch,bool bpmMode,uint8 rowsPerBeat,bool volHex)395 static void ConvertMEDEffect(ModCommand &m, bool is8ch, bool bpmMode, uint8 rowsPerBeat, bool volHex)
396 {
397 	switch(m.command)
398 	{
399 	case 0x04:  // Vibrato (twice as deep as in ProTracker)
400 		m.command = CMD_VIBRATO;
401 		m.param = (std::min<uint8>(m.param >> 3, 0x0F) << 4) | std::min<uint8>((m.param & 0x0F) * 2, 0x0F);
402 		break;
403 	case 0x08:  // Hold and decay
404 		m.command = CMD_NONE;
405 		break;
406 	case 0x09:  // Set secondary speed
407 		if(m.param > 0 && m.param <= 20)
408 			m.command = CMD_SPEED;
409 		else
410 			m.command = CMD_NONE;
411 		break;
412 	case 0x0C:  // Set Volume
413 		m.command = CMD_VOLUME;
414 		if(!volHex && m.param < 0x99)
415 			m.param = (m.param >> 4) * 10 + (m.param & 0x0F);
416 		else if(volHex)
417 			m.param = ((m.param & 0x7F) + 1) / 2;
418 		else
419 			m.command = CMD_NONE;
420 		break;
421 	case 0x0D:
422 		m.command = CMD_VOLUMESLIDE;
423 		break;
424 	case 0x0E:  // Synth jump
425 		m.command = CMD_NONE;
426 		break;
427 	case 0x0F:  // Misc
428 		if(m.param == 0)
429 		{
430 			m.command = CMD_PATTERNBREAK;
431 		} else if(m.param <= 0xF0)
432 		{
433 			m.command = CMD_TEMPO;
434 			if(m.param < 0x03)  // This appears to be a bug in OctaMED which is not emulated in MED Soundstudio on Windows.
435 				m.param = 0x70;
436 			else
437 				m.param = mpt::saturate_round<ModCommand::PARAM>(MMDTempoToBPM(m.param, is8ch, bpmMode, rowsPerBeat).ToDouble());
438 #ifdef MODPLUG_TRACKER
439 			if(m.param < 0x20)
440 				m.param = 0x20;
441 #endif  // MODPLUG_TRACKER
442 		} else switch(m.command)
443 		{
444 			case 0xF1:  // Play note twice
445 				m.command = CMD_MODCMDEX;
446 				m.param = 0x93;
447 				break;
448 			case 0xF2:  // Delay note
449 				m.command = CMD_MODCMDEX;
450 				m.param = 0xD3;
451 				break;
452 			case 0xF3:  // Play note three times
453 				m.command = CMD_MODCMDEX;
454 				m.param = 0x92;
455 				break;
456 			case 0xF8:  // Turn filter off
457 			case 0xF9:  // Turn filter on
458 				m.command = CMD_MODCMDEX;
459 				m.param = 0xF9 - m.param;
460 				break;
461 			case 0xFA:  // MIDI pedal on
462 			case 0xFB:  // MIDI pedal off
463 			case 0xFD:  // Set pitch
464 			case 0xFE:  // End of song
465 				m.command = CMD_NONE;
466 				break;
467 			case 0xFF:  // Turn note off
468 				m.note = NOTE_NOTECUT;
469 				m.command = CMD_NONE;
470 				break;
471 			default:
472 				m.command = CMD_NONE;
473 				break;
474 		}
475 		break;
476 	case 0x10:  // MIDI message
477 		m.command = CMD_MIDI;
478 		m.param |= 0x80;
479 		break;
480 	case 0x11:  // Slide pitch up
481 		m.command = CMD_MODCMDEX;
482 		m.param = 0x10 | std::min<uint8>(m.param, 0x0F);
483 		break;
484 	case 0x12:  // Slide pitch down
485 		m.command = CMD_MODCMDEX;
486 		m.param = 0x20 | std::min<uint8>(m.param, 0x0F);
487 		break;
488 	case 0x14:  // Vibrato (ProTracker compatible depth, but faster)
489 		m.command = CMD_VIBRATO;
490 		m.param = (std::min<uint8>((m.param >> 4) + 1, 0x0F) << 4) | (m.param & 0x0F);
491 		break;
492 	case 0x15:  // Set finetune
493 		m.command = CMD_MODCMDEX;
494 		m.param = 0x50 | (m.param & 0x0F);
495 		break;
496 	case 0x16:  // Loop
497 		m.command = CMD_MODCMDEX;
498 		m.param = 0x60 | std::min<uint8>(m.param, 0x0F);
499 		break;
500 	case 0x18:  // Stop note
501 		m.command = CMD_MODCMDEX;
502 		m.param = 0xC0 | std::min<uint8>(m.param, 0x0F);
503 		break;
504 	case 0x19:  // Sample Offset
505 		m.command = CMD_OFFSET;
506 		break;
507 	case 0x1A:  // Slide volume up once
508 		m.command = CMD_MODCMDEX;
509 		m.param = 0xA0 | std::min<uint8>(m.param, 0x0F);
510 		break;
511 	case 0x1B:  // Slide volume down once
512 		m.command = CMD_MODCMDEX;
513 		m.param = 0xB0 | std::min<uint8>(m.param, 0x0F);
514 		break;
515 	case 0x1C:  // MIDI program
516 		if(m.param > 0 && m.param <= 128)
517 		{
518 			m.command = CMD_MIDI;
519 			m.param--;
520 		} else
521 		{
522 			m.command = CMD_NONE;
523 		}
524 		break;
525 	case 0x1D:  // Pattern break (in hex)
526 		m.command = CMD_PATTERNBREAK;
527 		break;
528 	case 0x1E:  // Repeat row
529 		m.command = CMD_MODCMDEX;
530 		m.param = 0xE0 | std::min<uint8>(m.param, 0x0F);
531 		break;
532 	case 0x1F:  // Note delay and retrigger
533 	{
534 		if(m.param & 0xF0)
535 		{
536 			m.command = CMD_MODCMDEX;
537 			m.param = 0xD0 | (m.param >> 4);
538 		} else if(m.param & 0x0F)
539 		{
540 			m.command = CMD_MODCMDEX;
541 			m.param = 0x90 | m.param;
542 		} else
543 		{
544 			m.command = CMD_NONE;
545 		}
546 		break;
547 	}
548 	case 0x20:  // Reverse sample + skip samples
549 		if(m.param == 0 && m.vol == 0)
550 		{
551 			m.command = CMD_S3MCMDEX;
552 			m.param = 0x9F;
553 		} else
554 		{
555 			// Skip given number of samples
556 			m.command = CMD_NONE;
557 		}
558 		break;
559 	case 0x29:  // Relative sample offset
560 		if(m.vol > 0)
561 		{
562 			m.command = CMD_OFFSETPERCENTAGE;
563 			m.param = mpt::saturate_cast<ModCommand::PARAM>(Util::muldiv_unsigned(m.param, 0x100, m.vol));
564 		} else
565 		{
566 			m.command = CMD_NONE;
567 		}
568 		break;
569 	case 0x2E:  // Set panning
570 		if(m.param <= 0x10 || m.param >= 0xF0)
571 		{
572 			m.command = CMD_PANNING8;
573 			m.param = mpt::saturate_cast<ModCommand::PARAM>(((m.param ^ 0x80) - 0x70) * 8);
574 		} else
575 		{
576 			m.command = CMD_NONE;
577 		}
578 		break;
579 	default:
580 		if(m.command < 0x10)
581 			CSoundFile::ConvertModCommand(m);
582 		else
583 			m.command = CMD_NONE;
584 		break;
585 	}
586 }
587 
588 #ifdef MPT_WITH_VST
ReadMEDStringUTF16BE(FileReader & file)589 static std::wstring ReadMEDStringUTF16BE(FileReader &file)
590 {
591 	FileReader chunk = file.ReadChunk(file.ReadUint32BE());
592 	std::wstring s(chunk.GetLength() / 2u, L'\0');
593 	for(auto &c : s)
594 	{
595 		c = chunk.ReadUint16BE();
596 	}
597 	return s;
598 }
599 #endif  // MPT_WITH_VST
600 
601 
MEDReadNextSong(FileReader & file,MMD0FileHeader & fileHeader,MMD0Exp & expData,MMDSong & songHeader)602 static void MEDReadNextSong(FileReader &file, MMD0FileHeader &fileHeader, MMD0Exp &expData, MMDSong &songHeader)
603 {
604 	file.ReadStruct(fileHeader);
605 	file.Seek(fileHeader.songOffset + 63 * sizeof(MMD0Sample));
606 	file.ReadStruct(songHeader);
607 	if(fileHeader.expDataOffset && file.Seek(fileHeader.expDataOffset))
608 		file.ReadStruct(expData);
609 	else
610 		expData = {};
611 }
612 
613 
MEDScanNumChannels(FileReader & file,const uint8 version)614 static std::pair<CHANNELINDEX, SEQUENCEINDEX> MEDScanNumChannels(FileReader &file, const uint8 version)
615 {
616 	MMD0FileHeader fileHeader;
617 	MMD0Exp expData;
618 	MMDSong songHeader;
619 
620 	file.Rewind();
621 	uint32 songOffset = 0;
622 	MEDReadNextSong(file, fileHeader, expData, songHeader);
623 
624 	SEQUENCEINDEX numSongs = std::min(MAX_SEQUENCES, mpt::saturate_cast<SEQUENCEINDEX>(fileHeader.expDataOffset ? fileHeader.extraSongs + 1 : 1));
625 	CHANNELINDEX numChannels = 4;
626 	// Scan patterns for max number of channels
627 	for(SEQUENCEINDEX song = 0; song < numSongs; song++)
628 	{
629 		const PATTERNINDEX numPatterns = songHeader.numBlocks;
630 		if(songHeader.numSamples > 63 || numPatterns > 0x7FFF)
631 			return {};
632 
633 		for(PATTERNINDEX pat = 0; pat < numPatterns; pat++)
634 		{
635 			if(!file.Seek(fileHeader.blockArrOffset + pat * 4u)
636 			   || !file.Seek(file.ReadUint32BE()))
637 			{
638 				continue;
639 			}
640 			numChannels = std::max(numChannels, static_cast<CHANNELINDEX>(version < 1 ? file.ReadUint8() : file.ReadUint16BE()));
641 		}
642 
643 		// If song offsets are going backwards, reject the file
644 		if(expData.nextModOffset <= songOffset || !file.Seek(expData.nextModOffset))
645 		{
646 			numSongs = song + 1;
647 			break;
648 		}
649 		songOffset = expData.nextModOffset;
650 		MEDReadNextSong(file, fileHeader, expData, songHeader);
651 	}
652 	return {numChannels, numSongs};
653 }
654 
655 
ValidateHeader(const MMD0FileHeader & fileHeader)656 static bool ValidateHeader(const MMD0FileHeader &fileHeader)
657 {
658 	if(std::memcmp(fileHeader.mmd, "MMD", 3)
659 	   || fileHeader.version < '0' || fileHeader.version > '3'
660 	   || fileHeader.songOffset < sizeof(MMD0FileHeader)
661 	   || fileHeader.songOffset > uint32_max - 63 * sizeof(MMD0Sample) - sizeof(MMDSong)
662 	   || fileHeader.blockArrOffset < sizeof(MMD0FileHeader)
663 	   || (fileHeader.sampleArrOffset > 0 && fileHeader.sampleArrOffset < sizeof(MMD0FileHeader))
664 	   || fileHeader.expDataOffset > uint32_max - sizeof(MMD0Exp))
665 	{
666 		return false;
667 	}
668 	return true;
669 }
670 
671 
GetHeaderMinimumAdditionalSize(const MMD0FileHeader & fileHeader)672 static uint64 GetHeaderMinimumAdditionalSize(const MMD0FileHeader &fileHeader)
673 {
674 	return std::max<uint64>({ fileHeader.songOffset + 63 * sizeof(MMD0Sample) + sizeof(MMDSong),
675 		fileHeader.blockArrOffset,
676 		fileHeader.sampleArrOffset ? fileHeader.sampleArrOffset : sizeof(MMD0FileHeader),
677 		fileHeader.expDataOffset + sizeof(MMD0Exp) }) - sizeof(MMD0FileHeader);
678 }
679 
680 
ProbeFileHeaderMED(MemoryFileReader file,const uint64 * pfilesize)681 CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderMED(MemoryFileReader file, const uint64 *pfilesize)
682 {
683 	MMD0FileHeader fileHeader;
684 	if(!file.ReadStruct(fileHeader))
685 		return ProbeWantMoreData;
686 	if(!ValidateHeader(fileHeader))
687 		return ProbeFailure;
688 	return ProbeAdditionalSize(file, pfilesize, GetHeaderMinimumAdditionalSize(fileHeader));
689 }
690 
691 
ReadMED(FileReader & file,ModLoadingFlags loadFlags)692 bool CSoundFile::ReadMED(FileReader &file, ModLoadingFlags loadFlags)
693 {
694 	file.Rewind();
695 	MMD0FileHeader fileHeader;
696 	if(!file.ReadStruct(fileHeader))
697 		return false;
698 	if(!ValidateHeader(fileHeader))
699 		return false;
700 	if(!file.CanRead(mpt::saturate_cast<FileReader::off_t>(GetHeaderMinimumAdditionalSize(fileHeader))))
701 		return false;
702 	if(loadFlags == onlyVerifyHeader)
703 		return true;
704 
705 	InitializeGlobals(MOD_TYPE_MED);
706 	InitializeChannels();
707 	const uint8 version = fileHeader.version - '0';
708 
709 	file.Seek(fileHeader.songOffset);
710 	FileReader sampleHeaderChunk = file.ReadChunk(63 * sizeof(MMD0Sample));
711 
712 	MMDSong songHeader;
713 	file.ReadStruct(songHeader);
714 
715 	if(songHeader.numSamples > 63 || songHeader.numBlocks > 0x7FFF)
716 		return false;
717 
718 	MMD0Exp expData{};
719 	if(fileHeader.expDataOffset && file.Seek(fileHeader.expDataOffset))
720 	{
721 		file.ReadStruct(expData);
722 	}
723 
724 	const auto [numChannels, numSongs] = MEDScanNumChannels(file, version);
725 	if(numChannels < 1 || numChannels > MAX_BASECHANNELS)
726 		return false;
727 	m_nChannels = numChannels;
728 
729 	// Start with the instruments, as those are shared between songs
730 
731 	std::vector<uint32be> instrOffsets;
732 	if(fileHeader.sampleArrOffset)
733 	{
734 		file.Seek(fileHeader.sampleArrOffset);
735 		file.ReadVector(instrOffsets, songHeader.numSamples);
736 	} else if(songHeader.numSamples > 0)
737 	{
738 		return false;
739 	}
740 	m_nInstruments = m_nSamples = songHeader.numSamples;
741 
742 	// In MMD0 / MMD1, octave wrapping is not done for synth instruments
743 	// - It's required e.g. for automatic terminated to.mmd0 and you got to let the music.mmd1
744 	// - starkelsesirap.mmd0 (synth instruments) on the other hand don't need it
745 	// In MMD2 / MMD3, the mix flag is used instead.
746 	const bool hardwareMixSamples = (version < 2) || (version >= 2 && !(songHeader.flags2 & MMDSong::FLAG2_MIX));
747 
748 	bool needInstruments = false;
749 	bool anySynthInstrs = false;
750 #ifdef MPT_WITH_VST
751 	PLUGINDEX numPlugins = 0;
752 #endif  // MPT_WITH_VST
753 	for(SAMPLEINDEX ins = 1, smp = 1; ins <= m_nInstruments; ins++)
754 	{
755 		if(!AllocateInstrument(ins, smp))
756 			return false;
757 		ModInstrument &instr = *Instruments[ins];
758 
759 		MMDInstrHeader instrHeader{};
760 		FileReader sampleChunk;
761 		if(instrOffsets[ins - 1] != 0 && file.Seek(instrOffsets[ins - 1]))
762 		{
763 			file.ReadStruct(instrHeader);
764 			sampleChunk = file.ReadChunk(instrHeader.length);
765 		}
766 		const bool isSynth = instrHeader.type < 0;
767 		const size_t maskedType = static_cast<size_t>(instrHeader.type & MMDInstrHeader::TYPEMASK);
768 
769 #ifdef MPT_WITH_VST
770 		if(instrHeader.type == MMDInstrHeader::VSTI)
771 		{
772 			needInstruments = true;
773 			sampleChunk.Skip(6); // 00 00 <size of following data>
774 			const std::wstring type = ReadMEDStringUTF16BE(sampleChunk);
775 			const std::wstring name = ReadMEDStringUTF16BE(sampleChunk);
776 			if(type == L"VST")
777 			{
778 				auto &mixPlug = m_MixPlugins[numPlugins];
779 				mixPlug = {};
780 				mixPlug.Info.dwPluginId1 = Vst::kEffectMagic;
781 				mixPlug.Info.gain = 10;
782 				mixPlug.Info.szName = mpt::ToCharset(mpt::Charset::Locale, name);
783 				mixPlug.Info.szLibraryName = mpt::ToCharset(mpt::Charset::UTF8, name);
784 				instr.nMixPlug = numPlugins + 1;
785 				instr.nMidiChannel = MidiFirstChannel;
786 				instr.Transpose(-24);
787 				instr.AssignSample(0);
788 				// TODO: Figure out patch and routing data
789 
790 				numPlugins++;
791 			}
792 		} else
793 #endif  // MPT_WITH_VST
794 		if(isSynth)
795 		{
796 			// TODO: Figure out synth instruments
797 			anySynthInstrs = true;
798 			instr.AssignSample(0);
799 		}
800 
801 		uint8 numSamples = 1;
802 		static constexpr uint8 SamplesPerType[] = {1, 5, 3, 2, 4, 6, 7};
803 		if(!isSynth && maskedType < std::size(SamplesPerType))
804 			numSamples = SamplesPerType[maskedType];
805 		if(numSamples > 1)
806 		{
807 			static_assert(MAX_SAMPLES > 63 * 9, "Check IFFOCT multisample code");
808 			m_nSamples += numSamples - 1;
809 			needInstruments = true;
810 			static constexpr uint8 OctSampleMap[][8] =
811 			{
812 				{1, 1, 0, 0, 0, 0, 0, 0},  // 2
813 				{2, 2, 1, 1, 0, 0, 0, 0},  // 3
814 				{3, 3, 2, 2, 1, 0, 0, 0},  // 4
815 				{4, 3, 2, 1, 1, 0, 0, 0},  // 5
816 				{5, 4, 3, 2, 1, 0, 0, 0},  // 6
817 				{6, 5, 4, 3, 2, 1, 0, 0},  // 7
818 			};
819 
820 			static constexpr int8 OctTransposeMap[][8] =
821 			{
822 				{ 0, 0, -12, -12, -24, -36, -48, -60},  // 2
823 				{ 0, 0, -12, -12, -24, -36, -48, -60},  // 3
824 				{ 0, 0, -12, -12, -24, -36, -48, -60},  // 4
825 				{12, 0, -12, -24, -24, -36, -48, -60},  // 5
826 				{12, 0, -12, -24, -36, -48, -48, -60},  // 6
827 				{12, 0, -12, -24, -36, -48, -60, -72},  // 7
828 			};
829 
830 			// TODO: Move octaves so that they align better (C-4 = lowest, we don't have access to the highest four octaves)
831 			for(int octave = 4; octave < 10; octave++)
832 			{
833 				for(int note = 0; note < 12; note++)
834 				{
835 					instr.Keyboard[12 * octave + note] = smp + OctSampleMap[numSamples - 2][octave - 4];
836 					instr.NoteMap[12 * octave + note] += OctTransposeMap[numSamples - 2][octave - 4];
837 				}
838 			}
839 		} else if(maskedType == MMDInstrHeader::EXTSAMPLE)
840 		{
841 			needInstruments = true;
842 			instr.Transpose(-24);
843 		} else if(!isSynth && hardwareMixSamples)
844 		{
845 			for(int octave = 7; octave < 10; octave++)
846 			{
847 				for(int note = 0; note < 12; note++)
848 				{
849 					instr.NoteMap[12 * octave + note] -= static_cast<uint8>((octave - 6) * 12);
850 				}
851 			}
852 		}
853 
854 		MMD0Sample sampleHeader;
855 		sampleHeaderChunk.ReadStruct(sampleHeader);
856 
857 		// midiChannel = 0xFF == midi instrument but with invalid channel, midiChannel = 0x00 == sample-based instrument?
858 		if(sampleHeader.midiChannel > 0 && sampleHeader.midiChannel <= 16)
859 		{
860 			instr.nMidiChannel = sampleHeader.midiChannel - 1 + MidiFirstChannel;
861 			needInstruments = true;
862 
863 #ifdef MPT_WITH_VST
864 			if(!isSynth)
865 			{
866 				auto &mixPlug = m_MixPlugins[numPlugins];
867 				mixPlug = {};
868 				mixPlug.Info.dwPluginId1 = PLUGMAGIC('V', 's', 't', 'P');
869 				mixPlug.Info.dwPluginId2 = PLUGMAGIC('M', 'M', 'I', 'D');
870 				mixPlug.Info.gain = 10;
871 				mixPlug.Info.szName = "MIDI Input Output";
872 				mixPlug.Info.szLibraryName = "MIDI Input Output";
873 
874 				instr.nMixPlug = numPlugins + 1;
875 				instr.Transpose(-24);
876 
877 				numPlugins++;
878 			}
879 #endif  // MPT_WITH_VST
880 		}
881 		if(sampleHeader.midiPreset > 0 && sampleHeader.midiPreset <= 128)
882 		{
883 			instr.nMidiProgram = sampleHeader.midiPreset;
884 		}
885 
886 		for(SAMPLEINDEX i = 0; i < numSamples; i++)
887 		{
888 			ModSample &mptSmp = Samples[smp + i];
889 			mptSmp.Initialize(MOD_TYPE_MED);
890 			mptSmp.nVolume = 4u * std::min<uint8>(sampleHeader.sampleVolume, 64u);
891 			mptSmp.RelativeTone = sampleHeader.sampleTranspose;
892 		}
893 
894 		if(isSynth || !(loadFlags & loadSampleData))
895 		{
896 			smp += numSamples;
897 			continue;
898 		}
899 
900 		SampleIO sampleIO(
901 			SampleIO::_8bit,
902 			SampleIO::mono,
903 			SampleIO::bigEndian,
904 			SampleIO::signedPCM);
905 
906 		const bool hasLoop = sampleHeader.loopLength > 1;
907 		SmpLength loopStart = sampleHeader.loopStart * 2;
908 		SmpLength loopEnd = loopStart + sampleHeader.loopLength * 2;
909 
910 		SmpLength length = mpt::saturate_cast<SmpLength>(sampleChunk.GetLength());
911 		if(instrHeader.type & MMDInstrHeader::S_16)
912 		{
913 			sampleIO |= SampleIO::_16bit;
914 			length /= 2;
915 		}
916 		if (instrHeader.type & MMDInstrHeader::STEREO)
917 		{
918 			sampleIO |= SampleIO::stereoSplit;
919 			length /= 2;
920 		}
921 		if(instrHeader.type & MMDInstrHeader::DELTA)
922 		{
923 			sampleIO |= SampleIO::deltaPCM;
924 		}
925 
926 		if(numSamples > 1)
927 			length = length / ((1u << numSamples) - 1);
928 
929 		for(SAMPLEINDEX i = 0; i < numSamples; i++)
930 		{
931 			ModSample &mptSmp = Samples[smp + i];
932 
933 			mptSmp.nLength = length;
934 			sampleIO.ReadSample(mptSmp, sampleChunk);
935 
936 			if(hasLoop)
937 			{
938 				mptSmp.nLoopStart = loopStart;
939 				mptSmp.nLoopEnd = loopEnd;
940 				mptSmp.uFlags.set(CHN_LOOP);
941 			}
942 
943 			length *= 2;
944 			loopStart *= 2;
945 			loopEnd *= 2;
946 		}
947 
948 		smp += numSamples;
949 	}
950 
951 	if(expData.instrExtOffset != 0 && expData.instrExtEntries != 0 && file.Seek(expData.instrExtOffset))
952 	{
953 		const uint16 entries = std::min<uint16>(expData.instrExtEntries, songHeader.numSamples);
954 		const uint16 size = expData.instrExtEntrySize;
955 		for(uint16 i = 0; i < entries; i++)
956 		{
957 			MMDInstrExt instrExt;
958 			file.ReadStructPartial(instrExt, size);
959 
960 			ModInstrument &ins = *Instruments[i + 1];
961 			if(instrExt.hold)
962 			{
963 				ins.VolEnv.assign({
964 					EnvelopeNode{0u, ENVELOPE_MAX},
965 					EnvelopeNode{static_cast<EnvelopeNode::tick_t>(instrExt.hold - 1), ENVELOPE_MAX},
966 					EnvelopeNode{static_cast<EnvelopeNode::tick_t>(instrExt.hold + (instrExt.decay ? 64u / instrExt.decay : 0u)), ENVELOPE_MIN},
967 				});
968 				if(instrExt.hold == 1)
969 					ins.VolEnv.erase(ins.VolEnv.begin());
970 				ins.nFadeOut = instrExt.decay ? (instrExt.decay * 512) : 32767;
971 				ins.VolEnv.dwFlags.set(ENV_ENABLED);
972 				needInstruments = true;
973 			}
974 			if(size > offsetof(MMDInstrExt, volume))
975 				ins.nGlobalVol = (instrExt.volume + 1u) / 2u;
976 			if(size > offsetof(MMDInstrExt, midiBank))
977 				ins.wMidiBank = instrExt.midiBank;
978 #ifdef MPT_WITH_VST
979 			if(ins.nMixPlug > 0)
980 			{
981 				PLUGINDEX plug = ins.nMixPlug - 1;
982 				auto &mixPlug = m_MixPlugins[plug];
983 				if(mixPlug.Info.dwPluginId2 == PLUGMAGIC('M', 'M', 'I', 'D'))
984 				{
985 					float dev = (instrExt.outputDevice + 1) / 65536.0f;  // Magic code from MidiInOut.h :(
986 					mixPlug.pluginData.resize(3 * sizeof(uint32));
987 					auto memFile = std::make_pair(mpt::as_span(mixPlug.pluginData), mpt::IO::Offset(0));
988 					mpt::IO::WriteIntLE<uint32>(memFile, 0);          // Plugin data type
989 					mpt::IO::Write(memFile, IEEE754binary32LE{0});    // Input device
990 					mpt::IO::Write(memFile, IEEE754binary32LE{dev});  // Output device
991 
992 					// Check if we already have another plugin referencing this output device
993 					for(PLUGINDEX p = 0; p < plug; p++)
994 					{
995 						const auto &otherPlug = m_MixPlugins[p];
996 						if(otherPlug.Info.dwPluginId1 == mixPlug.Info.dwPluginId1
997 							&& otherPlug.Info.dwPluginId2 == mixPlug.Info.dwPluginId2
998 							&& otherPlug.pluginData == mixPlug.pluginData)
999 						{
1000 							ins.nMixPlug = p + 1;
1001 							mixPlug = {};
1002 							break;
1003 						}
1004 					}
1005 				}
1006 			}
1007 #endif  // MPT_WITH_VST
1008 
1009 			ModSample &sample = Samples[ins.Keyboard[NOTE_MIDDLEC]];
1010 			sample.nFineTune = MOD2XMFineTune(instrExt.finetune);
1011 
1012 			if(size > offsetof(MMDInstrExt, loopLength))
1013 			{
1014 				sample.nLoopStart = instrExt.loopStart;
1015 				sample.nLoopEnd = instrExt.loopStart + instrExt.loopLength;
1016 			}
1017 			if(size > offsetof(MMDInstrExt, instrFlags))
1018 			{
1019 				sample.uFlags.set(CHN_LOOP, (instrExt.instrFlags & MMDInstrExt::SSFLG_LOOP) != 0);
1020 				sample.uFlags.set(CHN_PINGPONGLOOP, (instrExt.instrFlags & MMDInstrExt::SSFLG_PINGPONG) != 0);
1021 				if(instrExt.instrFlags & MMDInstrExt::SSFLG_DISABLED)
1022 					sample.nGlobalVol = 0;
1023 			}
1024 		}
1025 	}
1026 	if(expData.instrInfoOffset != 0 && expData.instrInfoEntries != 0 && file.Seek(expData.instrInfoOffset))
1027 	{
1028 		const uint16 entries = std::min<uint16>(expData.instrInfoEntries, songHeader.numSamples);
1029 		const uint16 size = expData.instrInfoEntrySize;
1030 		for(uint16 i = 0; i < entries; i++)
1031 		{
1032 			MMDInstrInfo instrInfo;
1033 			file.ReadStructPartial(instrInfo, size);
1034 			Instruments[i + 1]->name = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, instrInfo.name);
1035 			for(auto smp : Instruments[i + 1]->GetSamples())
1036 			{
1037 				m_szNames[smp] = Instruments[i + 1]->name;
1038 			}
1039 		}
1040 	}
1041 
1042 	// Setup a program change macro for command 1C (even if MIDI plugin is disabled, as otherwise these commands may act as filter commands)
1043 	m_MidiCfg.ClearZxxMacros();
1044 	strcpy(m_MidiCfg.szMidiSFXExt[0], "Cc z");
1045 
1046 	file.Rewind();
1047 	PATTERNINDEX basePattern = 0;
1048 	for(SEQUENCEINDEX song = 0; song < numSongs; song++)
1049 	{
1050 		MEDReadNextSong(file, fileHeader, expData, songHeader);
1051 
1052 		if(song != 0)
1053 		{
1054 			if(Order.AddSequence() == SEQUENCEINDEX_INVALID)
1055 				return false;
1056 		}
1057 
1058 		ModSequence &order = Order(song);
1059 
1060 		std::map<ORDERINDEX, ORDERINDEX> jumpTargets;
1061 		order.clear();
1062 		uint32 preamp = 32;
1063 		if(version < 2)
1064 		{
1065 			if(songHeader.songLength > 256 || m_nChannels > 16)
1066 				return false;
1067 			ReadOrderFromArray(order, songHeader.GetMMD0Song().sequence, songHeader.songLength);
1068 			for(auto &ord : order)
1069 			{
1070 				ord += basePattern;
1071 			}
1072 
1073 			SetupMODPanning(true);
1074 			for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++)
1075 			{
1076 				ChnSettings[chn].nVolume = std::min<uint8>(songHeader.trackVol[chn], 64);
1077 			}
1078 		} else
1079 		{
1080 			const MMD2Song header = songHeader.GetMMD2Song();
1081 			if(header.numTracks < 1 || header.numTracks > 64 || m_nChannels > 64)
1082 				return false;
1083 
1084 			const bool freePan = (header.flags3 & MMD2Song::FLAG3_FREEPAN);
1085 			if(header.volAdjust)
1086 				preamp = Util::muldivr_unsigned(preamp, std::min<uint16>(header.volAdjust, 800), 100);
1087 			if (freePan)
1088 				preamp /= 2;
1089 
1090 			if(file.Seek(header.trackVolsOffset))
1091 			{
1092 				for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++)
1093 				{
1094 					ChnSettings[chn].nVolume = std::min<uint8>(file.ReadUint8(), 64);
1095 				}
1096 			}
1097 			if(header.trackPanOffset && file.Seek(header.trackPanOffset))
1098 			{
1099 				for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++)
1100 				{
1101 					ChnSettings[chn].nPan = (Clamp<int8, int8>(file.ReadInt8(), -16, 16) + 16) * 8;
1102 				}
1103 			} else
1104 			{
1105 				SetupMODPanning(true);
1106 			}
1107 
1108 			std::vector<uint16be> sections;
1109 			if(!file.Seek(header.sectionTableOffset)
1110 			   || !file.CanRead(songHeader.songLength * 2)
1111 			   || !file.ReadVector(sections, songHeader.songLength))
1112 				continue;
1113 
1114 			for(uint16 section : sections)
1115 			{
1116 				if(section > header.numPlaySeqs)
1117 					continue;
1118 
1119 				file.Seek(header.playSeqTableOffset + section * 4);
1120 				if(!file.Seek(file.ReadUint32BE()) || !file.CanRead(sizeof(MMD2PlaySeq)))
1121 					continue;
1122 
1123 				MMD2PlaySeq playSeq;
1124 				file.ReadStruct(playSeq);
1125 
1126 				if(!order.empty())
1127 					order.push_back(order.GetIgnoreIndex());
1128 
1129 				size_t readOrders = playSeq.length;
1130 				if(!file.CanRead(readOrders))
1131 					LimitMax(readOrders, file.BytesLeft());
1132 				LimitMax(readOrders, ORDERINDEX_MAX);
1133 
1134 				size_t orderStart = order.size();
1135 				order.reserve(orderStart + readOrders);
1136 				for(size_t ord = 0; ord < readOrders; ord++)
1137 				{
1138 					PATTERNINDEX pat = file.ReadUint16BE();
1139 					if(pat < 0x8000)
1140 					{
1141 						order.push_back(basePattern + pat);
1142 					}
1143 				}
1144 				if(playSeq.name[0])
1145 					order.SetName(mpt::ToUnicode(mpt::Charset::ISO8859_1, mpt::String::ReadAutoBuf(playSeq.name)));
1146 
1147 				// Play commands (jump / stop)
1148 				if(playSeq.commandTableOffset > 0 && file.Seek(playSeq.commandTableOffset))
1149 				{
1150 					MMDPlaySeqCommand command;
1151 					while(file.ReadStruct(command))
1152 					{
1153 						FileReader chunk = file.ReadChunk(command.extraSize);
1154 						ORDERINDEX ord = mpt::saturate_cast<ORDERINDEX>(orderStart + command.offset);
1155 						if(command.offset == 0xFFFF || ord >= order.size())
1156 							break;
1157 						if(command.command == MMDPlaySeqCommand::kStop)
1158 						{
1159 							order[ord] = order.GetInvalidPatIndex();
1160 						} else if(command.command == MMDPlaySeqCommand::kJump)
1161 						{
1162 							jumpTargets[ord] = chunk.ReadUint16BE();
1163 							order[ord] = order.GetIgnoreIndex();
1164 						}
1165 					}
1166 				}
1167 			}
1168 		}
1169 
1170 		const bool volHex = (songHeader.flags & MMDSong::FLAG_VOLHEX) != 0;
1171 		const bool is8Ch = (songHeader.flags & MMDSong::FLAG_8CHANNEL) != 0;
1172 		const bool bpmMode = (songHeader.flags2 & MMDSong::FLAG2_BPM) != 0;
1173 		const uint8 rowsPerBeat = 1 + (songHeader.flags2 & MMDSong::FLAG2_BMASK);
1174 		m_nDefaultTempo = MMDTempoToBPM(songHeader.defaultTempo, is8Ch, bpmMode, rowsPerBeat);
1175 		m_nDefaultSpeed = Clamp<uint8, uint8>(songHeader.tempo2, 1, 32);
1176 		if(bpmMode)
1177 		{
1178 			m_nDefaultRowsPerBeat = rowsPerBeat;
1179 			m_nDefaultRowsPerMeasure = m_nDefaultRowsPerBeat * 4u;
1180 		}
1181 
1182 		if(songHeader.masterVol)
1183 			m_nDefaultGlobalVolume = std::min<uint8>(songHeader.masterVol, 64) * 4;
1184 		m_nSamplePreAmp = m_nVSTiVolume = preamp;
1185 
1186 		// For MED, this affects both volume and pitch slides
1187 		m_SongFlags.set(SONG_FASTVOLSLIDES, !(songHeader.flags & MMDSong::FLAG_STSLIDE));
1188 
1189 		if(expData.songNameOffset && file.Seek(expData.songNameOffset))
1190 		{
1191 			file.ReadString<mpt::String::maybeNullTerminated>(m_songName, expData.songNameLength);
1192 			if(numSongs > 1)
1193 				order.SetName(mpt::ToUnicode(mpt::Charset::ISO8859_1, m_songName));
1194 		}
1195 		if(expData.annoLength > 1 && file.Seek(expData.annoText))
1196 		{
1197 			m_songMessage.Read(file, expData.annoLength - 1, SongMessage::leAutodetect);
1198 		}
1199 
1200 #ifdef MPT_WITH_VST
1201 		// Read MIDI messages
1202 		if(expData.midiDumpOffset && file.Seek(expData.midiDumpOffset) && file.CanRead(8))
1203 		{
1204 			uint16 numDumps = std::min(file.ReadUint16BE(), static_cast<uint16>(std::size(m_MidiCfg.szMidiZXXExt)));
1205 			file.Skip(6);
1206 			if(file.CanRead(numDumps * 4))
1207 			{
1208 				std::vector<uint32be> dumpPointers;
1209 				file.ReadVector(dumpPointers, numDumps);
1210 				for(uint16 dump = 0; dump < numDumps; dump++)
1211 				{
1212 					if(!file.Seek(dumpPointers[dump]) || !file.CanRead(sizeof(MMDDump)))
1213 						continue;
1214 					MMDDump dumpHeader;
1215 					file.ReadStruct(dumpHeader);
1216 					if(!file.Seek(dumpHeader.dataPointer) || !file.CanRead(dumpHeader.length))
1217 						continue;
1218 					auto &macro = m_MidiCfg.szMidiZXXExt[dump];
1219 					auto length = std::min(static_cast<size_t>(dumpHeader.length), std::size(macro) / 2u);
1220 					for(size_t i = 0; i < length; i++)
1221 					{
1222 						const uint8 byte = file.ReadUint8(), high = byte >> 4, low = byte & 0x0F;
1223 						macro[i * 2] = high + (high < 0x0A ? '0' : 'A' - 0x0A);
1224 						macro[i * 2 + 1] = low + (low < 0x0A ? '0' : 'A' - 0x0A);
1225 					}
1226 				}
1227 			}
1228 		}
1229 #endif  // MPT_WITH_VST
1230 
1231 		if(expData.mmdInfoOffset && file.Seek(expData.mmdInfoOffset) && file.CanRead(12))
1232 		{
1233 			file.Skip(6);  // Next info file (unused) + reserved
1234 			if(file.ReadUint16BE() == 1)  // ASCII text
1235 			{
1236 				uint32 length = file.ReadUint32BE();
1237 				if(length && file.CanRead(length))
1238 				{
1239 					const auto oldMsg = std::move(m_songMessage);
1240 					m_songMessage.Read(file, length, SongMessage::leAutodetect);
1241 					if(!oldMsg.empty())
1242 						m_songMessage.SetRaw(oldMsg + std::string(2, SongMessage::InternalLineEnding) + m_songMessage);
1243 				}
1244 			}
1245 		}
1246 
1247 		// Track Names
1248 		if(version >= 2 && expData.trackInfoOffset)
1249 		{
1250 			for(CHANNELINDEX chn = 0; chn < m_nChannels; chn++)
1251 			{
1252 				if(file.Seek(expData.trackInfoOffset + chn * 4)
1253 				   && file.Seek(file.ReadUint32BE()))
1254 				{
1255 					uint32 nameOffset = 0, nameLength = 0;
1256 					while(file.CanRead(sizeof(MMDTag)))
1257 					{
1258 						MMDTag tag;
1259 						file.ReadStruct(tag);
1260 						if(tag.type == MMDTag::MMDTAG_END)
1261 							break;
1262 						switch(tag.type & MMDTag::MMDTAG_MASK)
1263 						{
1264 						case MMDTag::MMDTAG_TRK_NAME: nameOffset = tag.data; break;
1265 						case MMDTag::MMDTAG_TRK_NAMELEN: nameLength = tag.data; break;
1266 						}
1267 					}
1268 					if(nameOffset > 0 && nameLength > 0 && file.Seek(nameOffset))
1269 					{
1270 						file.ReadString<mpt::String::maybeNullTerminated>(ChnSettings[chn].szName, nameLength);
1271 					}
1272 				}
1273 			}
1274 		}
1275 
1276 		PATTERNINDEX numPatterns = songHeader.numBlocks;
1277 		Patterns.ResizeArray(basePattern + numPatterns);
1278 		for(PATTERNINDEX pat = 0; pat < numPatterns; pat++)
1279 		{
1280 			if(!(loadFlags & loadPatternData)
1281 			   || !file.Seek(fileHeader.blockArrOffset + pat * 4u)
1282 			   || !file.Seek(file.ReadUint32BE()))
1283 			{
1284 				continue;
1285 			}
1286 
1287 			CHANNELINDEX numTracks;
1288 			ROWINDEX numRows;
1289 			std::string patName;
1290 			int transpose;
1291 			FileReader cmdExt;
1292 
1293 			if(version < 1)
1294 			{
1295 				transpose = NOTE_MIN + 47;
1296 				MMD0PatternHeader patHeader;
1297 				file.ReadStruct(patHeader);
1298 				numTracks = patHeader.numTracks;
1299 				numRows = patHeader.numRows + 1;
1300 			} else
1301 			{
1302 				transpose = NOTE_MIN + (version <= 2 ? 47 : 23) + songHeader.playTranspose;
1303 				MMD1PatternHeader patHeader;
1304 				file.ReadStruct(patHeader);
1305 				numTracks = patHeader.numTracks;
1306 				numRows = patHeader.numRows + 1;
1307 				if(patHeader.blockInfoOffset)
1308 				{
1309 					auto offset = file.GetPosition();
1310 					file.Seek(patHeader.blockInfoOffset);
1311 					MMDBlockInfo blockInfo;
1312 					file.ReadStruct(blockInfo);
1313 					if(file.Seek(blockInfo.nameOffset))
1314 					{
1315 						// We have now chased four pointers to get this far... lovely format.
1316 						file.ReadString<mpt::String::maybeNullTerminated>(patName, blockInfo.nameLength);
1317 					}
1318 					if(blockInfo.cmdExtTableOffset
1319 					   && file.Seek(blockInfo.cmdExtTableOffset)
1320 					   && file.Seek(file.ReadUint32BE()))
1321 					{
1322 						cmdExt = file.ReadChunk(numTracks * numRows);
1323 					}
1324 
1325 					file.Seek(offset);
1326 				}
1327 			}
1328 
1329 			if(!Patterns.Insert(basePattern + pat, numRows))
1330 				continue;
1331 
1332 			CPattern &pattern = Patterns[basePattern + pat];
1333 			pattern.SetName(patName);
1334 			LimitMax(numTracks, m_nChannels);
1335 
1336 			for(ROWINDEX row = 0; row < numRows; row++)
1337 			{
1338 				ModCommand *m = pattern.GetpModCommand(row, 0);
1339 				for(CHANNELINDEX chn = 0; chn < numTracks; chn++, m++)
1340 				{
1341 					int note = NOTE_NONE;
1342 					if(version < 1)
1343 					{
1344 						const auto [noteInstr, instrCmd, param] = file.ReadArray<uint8, 3>();
1345 
1346 						if(noteInstr & 0x3F)
1347 							note = (noteInstr & 0x3F) + transpose;
1348 
1349 						m->instr = (instrCmd >> 4) | ((noteInstr & 0x80) >> 3) | ((noteInstr & 0x40) >> 1);
1350 
1351 						m->command = instrCmd & 0x0F;
1352 						m->param = param;
1353 					} else
1354 					{
1355 						const auto [noteVal, instr, command, param1] = file.ReadArray<uint8, 4>();
1356 						m->vol = cmdExt.ReadUint8();
1357 
1358 						if(noteVal & 0x7F)
1359 							note = (noteVal & 0x7F) + transpose;
1360 						else if(noteVal == 0x80)
1361 							m->note = NOTE_NOTECUT;
1362 
1363 						m->instr = instr & 0x3F;
1364 						m->command = command;
1365 						m->param = param1;
1366 					}
1367 					// Octave wrapping for 4-channel modules (TODO: this should not be set because of synth instruments)
1368 					if(hardwareMixSamples && note >= NOTE_MIDDLEC + 2 * 12)
1369 						needInstruments = true;
1370 
1371 					if(note >= NOTE_MIN && note <= NOTE_MAX)
1372 						m->note = static_cast<ModCommand::NOTE>(note);
1373 					ConvertMEDEffect(*m, is8Ch, bpmMode, rowsPerBeat, volHex);
1374 				}
1375 			}
1376 		}
1377 
1378 		// Fix jump order commands
1379 		for(const auto & [from, to] : jumpTargets)
1380 		{
1381 			PATTERNINDEX pat;
1382 			if(from > 0 && order.IsValidPat(from - 1))
1383 			{
1384 				pat = order.EnsureUnique(from - 1);
1385 			} else
1386 			{
1387 				if(to == from + 1)  // No action required
1388 					continue;
1389 				pat = Patterns.InsertAny(1);
1390 				if(pat == PATTERNINDEX_INVALID)
1391 					continue;
1392 				order[from] = pat;
1393 			}
1394 			Patterns[pat].WriteEffect(EffectWriter(CMD_POSITIONJUMP, mpt::saturate_cast<ModCommand::PARAM>(to)).Row(Patterns[pat].GetNumRows() - 1).RetryPreviousRow());
1395 			if(pat >= numPatterns)
1396 				numPatterns = pat + 1;
1397 		}
1398 
1399 		basePattern += numPatterns;
1400 
1401 		if(!expData.nextModOffset || !file.Seek(expData.nextModOffset))
1402 			break;
1403 	}
1404 	Order.SetSequence(0);
1405 
1406 	if(!needInstruments)
1407 	{
1408 		for(INSTRUMENTINDEX ins = 1; ins <= m_nInstruments; ins++)
1409 		{
1410 			delete Instruments[ins];
1411 			Instruments[ins] = nullptr;
1412 		}
1413 		m_nInstruments = 0;
1414 	}
1415 
1416 	if(anySynthInstrs)
1417 		AddToLog(LogWarning, U_("Synthesized MED instruments are not supported."));
1418 
1419 	const mpt::uchar *madeWithTracker = MPT_ULITERAL("");
1420 	switch(version)
1421 	{
1422 	case 0: madeWithTracker = m_nChannels > 4 ? MPT_ULITERAL("OctaMED v2.10 (MMD0)") : MPT_ULITERAL("MED v2 (MMD0)"); break;
1423 	case 1: madeWithTracker = MPT_ULITERAL("OctaMED v4 (MMD1)"); break;
1424 	case 2: madeWithTracker = MPT_ULITERAL("OctaMED v5 (MMD2)"); break;
1425 	case 3: madeWithTracker = MPT_ULITERAL("OctaMED Soundstudio (MMD3)"); break;
1426 	}
1427 
1428 	m_modFormat.formatName = MPT_UFORMAT("OctaMED (MMD{})")(version);
1429 	m_modFormat.type = MPT_USTRING("med");
1430 	m_modFormat.madeWithTracker = madeWithTracker;
1431 	m_modFormat.charset = mpt::Charset::ISO8859_1;
1432 
1433 	return true;
1434 }
1435 
1436 OPENMPT_NAMESPACE_END
1437