#include "Song.h"
#include "Effects.h"
#include "Instrument.h"
#include "Loaders.h"
#include "FSysLog.h"
#include <string.h>

/*
 * Channels: [1...64]
 * Patterns: [1...256]
 * Rows    : [1...65535]
 * Orders  : [1...256]
 * Samples : [1...256]
 * Instr.  : [1...255]
 * Tempo   : [32...255], T frames per 2.5 seconds. Also uses BPM and LPB.
 * Speed   : [1...31], S frames per row.
 * Notes   : [C-0...C-4...B-7]
 * Pitch   : Semitones mode, value is upped by 12 each octave up.
 *           Slides in x/16, x/64 of x/256 units per row.
 * Volume  : Note: [0...128] , linear.
 *           Channel [0...65536] (200%)
 *           Main [0...32768]
 * Panning : [-127...127] + Surround (-128).
 * Sampling: Frequency is used directly. Same for relative tones.
 *
 * Notes:
 * Compensate for Ch. volume & NotesMap volume range (200%)
 * by adjusting sample scale volume shift otherwise the final
 * ouput varies to much from song to song (~[25%-400%]
 * instead of the usual [50%-200%]).
 *
 * Not supported:
 * - Resonant filters: highpass variant, attack & decay..
 * - Channel filters (delay, reverb, ...).
 * - VST instruments.
 * - Synchronised samples.
 */

#define MT2_FLAG_PACKEDPATTERNS 0x01
#define MT2_FLAG_AUTOMATION     0x02
#define MT2_FLAG_DRUMSAUTO      0x08
#define MT2_FLAG_MASTERAUTO     0x10

#define MT2_INST_FLAG_FILTER 0x02
#define MT2_INST_FLAG_VST    0x04

typedef struct
{
	uint32_t    TAG;
	uint32_t    UserId;
	uint8_t     FormatVers[2];
	uint8_t     Tracker[32];
	uint8_t     Name[64];
	uint16_t    Orders;
	uint16_t    RestartPos;
	uint16_t    Patterns;
	uint16_t    Channels;
	uint16_t    SamplesPerTick;
	uint8_t     Speed;
	uint8_t     LinesPerBeat;
	uint8_t     Flags[4];
	uint16_t    Instruments;
	uint16_t    Samples;
	uint8_t     Order[256];
	uint16_t    DrumsLength;
} LHdr;

typedef struct
{
	uint16_t    Patterns;
	uint16_t    Samples[8];
	uint8_t     Order[256];
} LDrumsHdr;

typedef struct
{
	uint16_t    Volume;
	uint8_t     HasFX;
	uint8_t     Output;
	uint16_t    FXId;
	uint16_t    FXParams[64][8];
} LChannelInfo;

typedef struct
{
	uint32_t	Pattern;
	uint32_t	Channel;
	uint32_t	Type;
	uint32_t	Points;
	uint32_t	Values[64];
} LAutomaton;

typedef struct
{
	uint8_t     Groups[2];
	uint8_t     NotesMap[96];
	uint8_t     VibratoType;
	uint8_t     VibratoSpeed;
	uint8_t     VibratoDepth;
	uint8_t     VibratoSweep;
	uint16_t    Fadeout;
	uint8_t     NewNoteAction;
	uint8_t     DuplicateCheck;
	// if vers >= 201
	uint16_t    Flags;
	// if vers >= 202
	uint32_t    EnvelopeFlags;
} LInst;

// 0-7 -> Channel filter params
// 8-15 ->
#define MT2_AUTO_VOLUME           8
#define MT2_AUTO_PANNING          9 // Unused in 2.61
#define MT2_AUTO_INST_CUTOFF     10
#define MT2_AUTO_INST_RESONANCE  11
#define MT2_AUTO_INST_ATTACK     12
#define MT2_AUTO_INST_DECAY      13

typedef struct
{
	uint8_t     Flags;
	uint8_t     Points;
	uint8_t     Sustain;
	uint8_t     LoopStart;
	uint8_t     LoopEnd;
	uint8_t     Reserved[3];
	uint8_t     Values[64];
} LEnvelope;

typedef struct
{
	uint8_t     SynthID;
	uint8_t     EffectID;
	uint16_t    Cutoff;
	uint8_t     Resonance;
	uint8_t     Attack;
	uint8_t     Decay;
	uint8_t     Channel;
	int8_t      Device;
	uint8_t     Volume;
	int8_t      Pitch;
	int8_t      Transpose;
	uint8_t     MidiChannel;
	uint8_t     MidiProgram;
	uint8_t     Program;
	uint8_t     Reserved[17];
} LInst2;

typedef struct
{
	uint32_t    Length;
	uint32_t    Frequency;
	uint8_t     Depth;
	uint8_t     Channels;
	uint8_t     Flags;
	uint8_t     LoopType;
	uint32_t    LoopStart;
	uint32_t    LoopEnd;
	uint16_t    DefaultVolume; // [0-128]
	uint8_t     Panning;
	uint8_t     SampledNote;
	uint16_t    SamplesPerBeat;
} LSmp;

typedef struct
{
	uint8_t     SampleNr;
	uint8_t     ScaleVolume; // [0 .. 128 .. 255]
	int8_t      FinePitch;   // x/128 semitone
	uint8_t     Reserved[5];
} LGroup;

typedef struct
{
	uint32_t    Rows;
	uint32_t    Size;
	uint8_t*    pData;
} LPattern;

#define NoteDelta (Note_Central-12*4-1)

static const uint8_t Tag_MT20[] = "MT20";
//static const uint8_t Tag_BPM [] = "BPM+";
//static const uint8_t Tag_TFXM[] = "TFXM";
static const uint8_t Tag_TRKS[] = "TRKS";
//static const uint8_t Tag_TRKL[] = "TRKL"; // Channels names
//static const uint8_t Tag_TRKO[] = "TRKO";
//static const uint8_t Tag_PATN[] = "PATN"; // Patterns names
static const uint8_t Tag_MSG [] = "MSG\0";
//static const uint8_t Tag_PICT[] = "PICT";
//static const uint8_t Tag_SUM [] = "SUM\0";
//static const uint8_t Tag_TMAP[] = "TMAP";
//static const uint8_t Tag_MIDI[] = "MIDI";
//static const uint8_t Tag_TREQ[] = "TREQ";
static const uint8_t Tag_VST2[] = "VST2";

static const uint8_t Typestr_MT2[] = "MadTracker";
#define TAG(x) (*(const uint32_t*) Tag_##x)

static const _kernel_oserror* Read_TagInfo(SongHdr* pSong, uint32_t* pTag, uint32_t* pTagSize)
{
	const _kernel_oserror* err = NULL;

	err = FileLoad_ReadInt(pSong, pTag, 4);
	if (err) return err;
	err = FileLoad_ReadInt(pSong, pTagSize, 4);

	return err;
}

static inline void pitch_slide(SongHdr* pSong, uint32_t cmd, uint32_t value)
{
	// Note: only value 0x00 is memory, 0xF0 or 0xE0 are slides with value 0
	if (value >= 0xf0)
	{
		// zFx fine slide
		value = (value & 0x0f) << 2;
		// zF0 must force memory to 0, not read it
		if (!value) cmd = cmd_pitch_memslide;
		cmd |= flag_cmd_slide_frame0;
	}
	else if (value >= 0xe0)
	{
		// zEx extra fine slide
		value &= 0x0f;
		// zE0 must force memory to 0, not read it
		if (!value) cmd = cmd_pitch_memslide;
		cmd |= flag_cmd_slide_frame0;
	}
	else
	{
		// zxx normal slide
		value <<= 2;
		cmd |= flag_cmd_slide_frameN0;
	}
	Pattern_AddEffect_Pitch(pSong, cmd, value);
}

static void convert_effect(SongHdr* pSong, const LoaderChannelData* pConfig, uint32_t effect, uint32_t value)
{
#ifdef USELOG
	uint32_t oeffect = effect;
	uint32_t ovalue = value;
#endif

	switch(effect)
	{
		case 0: // XM effect
		{
			effect = value >> 8;
			value &= 0xff;
			if (effect == 0xf) // Global: Set speed/tempo
			{
				if (value)
				{
					if (value <= pSong->MaxSpeed)
					{
						// effect_f_test_speed
						Pattern_AddGlbEffect_Frames(pSong, gcmd_frames_set, value);
					}
					else
					{
						// effect_f_tempo
						Pattern_AddGlbEffect_Tempo(pSong, cmd_set, value << 3);
					}
				}
			}
			else
				convert_effect_XM(pSong, pConfig->Channel, effect, value, pConfig->Note);
		}
		break;
		case 1: // Pitch: Slide up
		{
			effect = cmd_upmem1 | flag_cmd_slide_frameN0;

			value = (value + 0xf) >> 4;
			if (value >= 0x400) value = 0x3ff;
			pitch_slide(pSong, effect, value);
		}
		break;
		case 2: // Pitch: Slide down
		{
			effect = cmd_downmem2 | flag_cmd_slide_frameN0;

			value = (value + 0xf) >> 4;
			if (value >= 0x400) value = 0x3ff;
			pitch_slide(pSong, effect, value);
		}
		break;
		case 3: // Pitch: Portamento
		{
			value = (value + 0xf) >> 4;
			if (value >= 0x400) value = 0x3ff;
			Pattern_AddEffect_Pitch(pSong, cmd_portamento + flag_cmd_slide_frameN0, value);
		}
		break;
		case 4: // Pitch: Vibrato at speed x and depth y, Memory
		{
			uint32_t speed = value >> 8;
			value &= 0xff;
			// Vibrate between [-2*y,+2*y]
			value = (value + 7) >> 3; // >> 4 for range conversion and << 1 for depth
			if (speed) Pattern_AddEffect_Pitch(pSong, cmd_vibrato_speed, speed);
			Pattern_AddEffect_Pitch(pSong, cmd_vibrato_depth | flag_cmd_vibrato_frameN0, value);
		}
		break;
		case 7: // Volume: Vibrato (alias Tremolo), Memory
		{
			uint32_t speed = value >> 8;
			value &= 0xff;
			// Vibrate between [-2*y,+2*y]
			value <<= 3; // << 1 for range conversion and << 2 for depth
			if (speed) Pattern_AddEffect_NoteVolume(pSong, cmd_vibrato_speed, speed);
			Pattern_AddEffect_NoteVolume(pSong, cmd_vibrato_depth | flag_cmd_vibrato_frameN0, value);
		}
		break;
		case 8: // Polarity, Panning: Set [0, 255]
		{
			value &= 0xff;
			if (value)
			{
				if (value >= 128) value++;
				Pattern_AddEffect_Panning(pSong, cmd_set, value);
			}
		}
		break;
		case 9: // Note: Set sample offset
		{
			if (value)
			{
				// define offset
				Pattern_AddEffect_Instrument(pSong, cmd_sample_set_offset, 0x000 | 0);
				// set offset byte 1
				Pattern_AddEffect_Instrument(pSong, cmd_sample_set_offset, 0x100 | (value & 0xff));
				// set offset byte 2
				Pattern_AddEffect_Instrument(pSong, cmd_sample_set_offset, 0x200 | (value >> 8));
			}

			// only if a note is defined at the same time
			if (pConfig->Note) Pattern_AddEffect_Instrument(pSong, cmd_sample_set_offset, 0xf00);
		}
		break;
		case 0xc: // Volume: Set [0, 0xFFFF]
		{
			value >>= 7;
			if (value > 256) value = 256;
			Pattern_AddEffect_NoteVolume(pSong, cmd_set, value);
		}
		break;
		case 0xf: // BPM, LPB, Speed BBLS
		{
			uint32_t bpm = value >> 8;
			uint32_t lpb = (value >> 4) & 0xf;
			uint32_t speed = value & 0xf;

			if (bpm && lpb && speed)
			{
				uint32_t tempo = bpm * lpb * speed * 20 / 60;
				if (tempo <= 0xfff)
					Pattern_AddGlbEffect_Tempo(pSong, cmd_set, tempo);
				else
					Pattern_AddGlbEffect_Tempo(pSong, gcmd_tempo_setlarge, tempo >> 4);
				Pattern_AddGlbEffect_Frames(pSong, gcmd_frames_set, speed);
			}
		}
		break;
		case 0x10: // IT effect
		{
			effect = value >> 8;
			value &= 0xff;
			if (effect == 0x14) // T, Global: Tempo
			{
				if (!value)
				{
					// T00 repeat last slide (Should be last T)
					Pattern_AddGlbEffect_Tempo(pSong, cmd_last_slide, 0);
				}
				else if (value >= 0x20)
				{
					// set
					Pattern_AddGlbEffect_Tempo(pSong, cmd_set, value << 3);
					// should reset last slide to 0
				}
				else if (value >= 0x10)
				{
					// slide up, Memory
					if (value) Pattern_AddGlbEffect_Tempo(pSong, cmd_upmem1+flag_cmd_slide_frameN0, (value - 0x10) << 3);
					// else should reset last slide to 0
				}
				else
				{
					// slide down, Memory
					Pattern_AddGlbEffect_Tempo(pSong, cmd_downmem1+flag_cmd_slide_frameN0, value << 3);
				}
			}
			else
				convert_effect_IT(pSong, pConfig, effect, value);
		}
		break;
		case 0x1d: // Gapper effect XXYY, stop sample XX ticks, play YY effects
		{
			uint32_t off = (value >> 4) & 0xf;
			value &= 0xf;
			Pattern_AddEffect_Instrument(pSong, cmd_sample_gapper, Effect_Tremor(off, value));
		}
		break;
		case 0x20: // Cufoff, Resonance CCRR
		{
			uint32_t cutoff = value >> 8;
			value &= 0xff;
			if (cutoff) Pattern_AddEffect_Pitch(pSong, cmd_filter_setcutoff, cutoff >> 1);
			if (value) Pattern_AddEffect_Pitch(pSong, cmd_filter_setresonance, value >> 1);
			if (cutoff || value) pSong->Flags |= Song_Flag_Filters;
		}
		break;
		case 0x21: // Attack, Decay AADD
		{
			SysLog_SkipEffect(pSong, 0, oeffect, ovalue);
		}
		break;
		case 0x22: // CRAD
		{
			uint32_t cutoff = value >> 12;
			uint32_t ad = value & 0xff;
			cutoff |= cutoff << 4;
			value = (value >> 8) & 0xf;
			value |= value << 4;
			if (cutoff) Pattern_AddEffect_Pitch(pSong, cmd_filter_setcutoff, cutoff << 3);
			if (value) Pattern_AddEffect_Pitch(pSong, cmd_filter_setresonance, value << 3);
			if (cutoff || value) pSong->Flags |= Song_Flag_Filters;
			if (ad) SysLog_SkipEffect(pSong, 0, oeffect, ovalue);
		}
		break;
		case 0x24: // Reverse
		{
			// Not limited to new notes
			Pattern_AddEffect_Instrument(pSong, cmd_sample_reverse_play, 0);
		}
		break;
		case 0x80: // Channel volume [0-0xFFFF]
		{
			value >>= 8;
			if (value > 256) value = 256;
			Pattern_AddEffect_ChannelVolume(pSong, cmd_set, value);
		}
		break;
		case 0x90: // Sync offset PPSS
		{
			SysLog_SkipEffect(pSong, 0, oeffect, ovalue);
		}
		break;
		case 0x9d: // Offset + Delay OODD
		{
			uint32_t offset = value >> 8;
			value &= 0xff;
			if (offset)
			{
				// define offset
				Pattern_AddEffect_Instrument(pSong, cmd_sample_set_offset, 0x000 | offset);

				// only if a note is defined at the same time
				if (pConfig->Note) Pattern_AddEffect_Instrument(pSong, cmd_sample_set_offset, 0xf00);
			}

			if (value) Pattern_AddEffect_Instrument(pSong, cmd_sample_note_delay, value);
		}
		break;
		case 0xf9: // Set track output
		case 0xff: // Visual effect
		{
			; // Ignore
		}
		break;
		default:
		{
			SysLog_SkipEffect(pSong, 0, oeffect, ovalue);
		}
	}
}

typedef struct
{
	uint8_t Note;
	uint8_t Instrument;
	uint8_t VolumeCommand;
	uint8_t Panning;
	uint8_t Effect;
	uint8_t Value;
	uint8_t Value2;
	uint8_t Dummy;
} PatternInfo;

typedef struct
{
	uint8_t Effect;
	uint8_t Value;
	uint8_t Value2;
	uint8_t SValue; // For S00 command
	uint8_t SFValue; // For SFx command
} ChannelInfo;

static bool AutomatonValue
	( const LAutomaton* pLAutomatons
	, uint32_t Automatons
	, uint32_t Pattern
	, uint32_t Channel
	, uint32_t Type
	, uint32_t Row
	, uint32_t* pValue
	)
{
	const LAutomaton* pAuto = pLAutomatons;

	// frac is index from table, use bissection
	int32_t min = 0;
	int32_t max = Automatons - 1;
	int32_t cur;

	do
	{
		cur = (min + max) /2;
		pAuto = pLAutomatons + cur;
		if (pAuto->Pattern < Pattern)
			min = cur;
		else if (pAuto->Pattern == Pattern)
		{
			if (pAuto->Channel < Channel)
				min = cur;
			else if (pAuto->Channel == Channel)
			{
				if (pAuto->Type < Type)
					min = cur;
				else if (pAuto->Type == Type)
					min = max = cur;
				else
					max = cur;
			}
			else max = cur;
		}
		else max = cur;

		cur = max - min;
		cur--; // real value may be between consecutive values
	}
	while (cur > 0);

	if ((pAuto->Pattern == Pattern)
	&&  (pAuto->Channel == Channel)
	&&  (pAuto->Type == Type))
	{
		uint32_t posa = 0;
		uint32_t vala = pAuto->Values[0] >> 16;
		for (int i = 0; i < pAuto->Points; i++)
		{
			uint32_t valb = pAuto->Values[i] >> 16;
			uint32_t posb = (i == 0) ? 0 : (pAuto->Values[i] << 16);
			posb >>= 16;
			if (Row == posb)
			{
				*pValue = valb;
				return true;
			}
			else if (Row < posb)
			{
				int32_t val = valb;
				val -= vala;
				val *= (int32_t) (Row - posa);
				val /= (int32_t) (posb - posa);
				val += vala;
				*pValue = val;
				return true;
			}
			posa = posb;
			vala = valb;
		}
		return false;
	}

	return false;
}

static const _kernel_oserror* Pattern_MT2
	( SongHdr* pSong
	, uint32_t i // our own pattern nr
	, uint32_t Li // MT2 one
	, Pattern* pPattern
	, bool packed
	, const LPattern* pLPattern
	, const LPattern* pDrums
	, const LAutomaton* pLAutomatons
	, uint32_t Automatons
	, LoaderChannelData* pConfig
	)
{
	const _kernel_oserror* err = NULL;
	uint32_t        val;
	int             row;
	const uint8_t*  pPos2;
	const uint8_t*  pPos = pLPattern->pData;
	const uint8_t*  pEnd = pPos + pLPattern->Size;
	const uint8_t*  pDrumsPos = pDrums ? pDrums->pData : NULL;
	int             channel;
	uint32_t        Note, Inst, Info, Repeats = 0;
	ChannelInfo*    pChInfo;
	PatternInfo*    pPattInfo = NULL;
	PatternInfo*    pPatt;
	PatternInfo*    pRepPatt = NULL;
	ChannelInfo     ChInfo[64];
	uint32_t        Value;

	pPattern->Rows = pLPattern->Rows;

	err = Loaders_Alloc(pSong, (void**) &pPattInfo, sizeof(PatternInfo)*pSong->Channels*pPattern->Rows);
	if (err) return err;
	memset(pPattInfo, 0, sizeof(PatternInfo)*pSong->Channels*pPattern->Rows);

	if (pLPattern->Size)
	{
		if (packed)
		{
			// Info is stored channels per channel instead of row by row
			for (channel = 0; channel < pSong->Channels; channel++)
			{
				for (row = 0; row < pPattern->Rows; row++)
				{
					pPatt = pPattInfo + row * pSong->Channels + channel;
					if (Repeats == 0)
					{
						pRepPatt = pPatt;

						if (pPos < pEnd)
						{
							// read flag
							Info = *pPos++;

							if (Info == 255)
							{
								if ((pPos + 2) > pEnd)
								{
									SysLog_FileCorrupted(SysLog_Medium, pSong, 0, "Pattern %d truncated", i);
									break;
								}
								Repeats = *pPos++;
								Info = *pPos++;
							}

							// will we remain within pattern limits while reading channel info?
							pPos2 = pPos;
							if (Info & 0x01) pPos2++;
							if (Info & 0x02) pPos2++;
							if (Info & 0x04) pPos2++;
							if (Info & 0x08) pPos2++;
							if (Info & 0x10) pPos2++;
							if (Info & 0x20) pPos2++;
							if (Info & 0x40) pPos2++;
							if (pPos2 > pEnd)
							{
								SysLog_FileCorrupted(SysLog_Medium, pSong, 0, "Pattern %d truncated", i);
								pPos = pEnd;
								break;
							}

							// Read channel values
							pPatt->Note = (Info & 0x01) ? *pPos++ : 0;
							pPatt->Instrument = (Info & 0x02) ? *pPos++ : 0;
							pPatt->VolumeCommand = (Info & 0x04) ? *pPos++ : 0;
							pPatt->Panning = (Info & 0x08) ? (*pPos++ + 128) : 0;
							pPatt->Effect = (Info & 0x10) ? *pPos++ : 0;
							pPatt->Value = (Info & 0x20) ? *pPos++ : 0;
							pPatt->Value2 = (Info & 0x40) ? *pPos++ : 0;
						}
						else
						{
							SysLog_FileCorrupted(SysLog_Medium, pSong, 0, "Pattern %d truncated", i);
							pPos = pEnd;
							break;
						}
					}
					else
					{
						*pPatt = *pRepPatt;
						Repeats--;
					}
				}
			}
		}
		else
		{
			for (row = 0; row < pPattern->Rows; row++)
			{
				for (channel = 0; channel < pSong->Channels; channel++)
				{
					pPatt = pPattInfo + row * pSong->Channels + channel;

					if (pPos < pEnd)
					{
						Info = 0x7f;

						// will we remain within pattern limits while reading channel info?
						pPos2 = pPos;
						if (Info & 0x01) pPos2++;
						if (Info & 0x02) pPos2++;
						if (Info & 0x04) pPos2++;
						if (Info & 0x08) pPos2++;
						if (Info & 0x10) pPos2++;
						if (Info & 0x20) pPos2++;
						if (Info & 0x40) pPos2++;
						if (pPos2 > pEnd)
						{
							SysLog_FileCorrupted(SysLog_Medium, pSong, 0, "Pattern %d truncated", i);
							pPos = pEnd;
							break;
						}

						// Read channel values
						pPatt->Note = (Info & 0x01) ? *pPos++ : 0;
						pPatt->Instrument = (Info & 0x02) ? *pPos++ : 0;
						pPatt->VolumeCommand = (Info & 0x04) ? *pPos++ : 0;
						pPatt->Panning = (Info & 0x08) ? (*pPos++ + 128) : 0;
						pPatt->Effect = (Info & 0x10) ? *pPos++ : 0;
						pPatt->Value = (Info & 0x20) ? *pPos++ : 0;
						pPatt->Value2 = (Info & 0x40) ? *pPos++ : 0;
					}
					else
					{
						SysLog_FileCorrupted(SysLog_Medium, pSong, -1, "Pattern %d truncated", i);
						pPos = pEnd;
						break;
					}
				}
			}
		}
	}

	err = Loaders_StartPattern(pSong, i);
	if (err) return err;

	for (channel = 0; channel < pSong->Channels; channel++)
	{
		pChInfo = &ChInfo[channel];
		pChInfo->Effect = 0;
		pChInfo->Value = 0;
		pChInfo->Value2 = 0;
		pChInfo->SValue = 0;
		pChInfo->SFValue = 0;
	}

	for (row = 0, pPatt = pPattInfo; row < pPattern->Rows; row++)
	{
		err = Loaders_StartRow(pSong, row);
		if (err) return err;

		if (AutomatonValue(pLAutomatons, Automatons, Li, 256, MT2_AUTO_VOLUME, row, &Value))
		{
			// Volume range [0-32767]
			Value >>= 7;
			if (Value > 256) Value = 256;
			Pattern_AddGlbEffect_Volume(pSong, cmd_set, Value);
		}

		for (channel = 0; channel < pSong->Channels; channel++, pPatt++)
		{
			pChInfo = &ChInfo[channel];

			// Treat note/Instrument
			if (pPatt->Note)
			{
				if (pPatt->Note == 97)
					Note = Note_OffXM;
				else if (pPatt->Note > 97)
				{
					SysLog_BadNote(pSong, 0, channel, pPatt->Note);
					Note = 0;
				}
				else
					Note = pPatt->Note + NoteDelta;
			}
			else Note = 0;

			Inst = pPatt->Instrument;
			err = Loaders_StartChannel(pSong, channel, Note, Inst);
			if (err) return err;

			if (AutomatonValue(pLAutomatons, Automatons, Li, channel, MT2_AUTO_VOLUME, row, &Value))
			{
				// Volume range [0-65535]
				Pattern_AddEffect_ChannelVolume(pSong, cmd_set, Value >> 8);
			}

			if (AutomatonValue(pLAutomatons, Automatons, Li, channel, MT2_AUTO_INST_CUTOFF, row, &Value))
			{
				// Cutoff range [100Hz - 11KHz]
				Value = Convert_FreqToCutoff(Value);
				Pattern_AddEffect_Pitch(pSong, cmd_filter_setcutoff, Value);
				pSong->Flags |= Song_Flag_Filters;
			}

			if (AutomatonValue(pLAutomatons, Automatons, Li, channel, MT2_AUTO_INST_RESONANCE, row, &Value))
			{
				// Resonance range [0-128]
				if (Value > 127) Value = 127;
				Pattern_AddEffect_Pitch(pSong, cmd_filter_setresonance, Value);
				pSong->Flags |= Song_Flag_Filters;
			}

			// Treat volume column before effect
			val = pPatt->VolumeCommand;

			if (val < 0x10)
				; // Ignore
			else if (val <= 0x90)
			{
				// [0x10-0x90] volume
				Pattern_AddEffect_NoteVolume(pSong, cmd_set, (val - 0x10) << 1);
			}
			else if (val < 0xA0)
			{
				SysLog_BadEffect(pSong, 0, 256, val);
			}
			else if (val <= 0xAF)
			{
				// [0xA0-0xAF] volume slide down
				Pattern_AddEffect_NoteVolume(pSong
					, cmd_downmem2+flag_cmd_slide_frameN0
					, (val - 0xA0) << 2);
			}
			else if (val <= 0xBF)
			{
				// [0xB0-0xBF] volume slide up
				Pattern_AddEffect_NoteVolume(pSong
					, cmd_upmem2+flag_cmd_slide_frameN0
					, (val - 0xB0) << 2);
			}
			else if (val <= 0xCF)
			{
				// [0xC0-0xCF] fine slide down
				Pattern_AddEffect_NoteVolume(pSong
					, cmd_downmem2+flag_cmd_slide_frame0
					, (val - 0xC0) << 2);
			}
			else if (val <= 0xDF)
			{
				// [0xD0-0xDF] fine slide up
				Pattern_AddEffect_NoteVolume(pSong
					, cmd_upmem2+flag_cmd_slide_frame0
					, (val - 0xD0) << 2);
			}
			else
			{
				SysLog_BadEffect(pSong, 0, 256, val);
			}

			// Treat panning column before effect
			if (pPatt->Panning)
			{
				Pattern_AddEffect_Panning(pSong, cmd_set, pPatt->Panning);
			}

/*
			??? effect
			if (Info & 0x88)
			{
				// Damn S00 memory!
				// Try to minimise the damage
				if (pChInfo->Effect == 0x13)
				{
					if (!pChInfo->Value)
						pChInfo->Value = pChInfo->SValue;
					else
						pChInfo->SValue = pChInfo->Value;
					// SFx, Midi Macro selector
					if ((pChInfo->Value >> 4) == 0xf)
						pChInfo->SFValue = pChInfo->Value & 0xf;
				}

				pConfig->Channel = channel;
				pConfig->Note = (Note < Note_CmdMin) ? Note : 0;
				convert_effect(pSong, pConfig, pChInfo->Effect, pChInfo->Value);

				// Treat Zxx Midi Macro here
				if (pChInfo->Effect == 0x1a)
				{
					if (pChInfo->Value >= 128)
					{
						val = pConfig->Zxx[pChInfo->Value - 128];
						if ((val & 0xff) == 0xff) val = (val & 0xf00);
					}
					else
					{
						val = pConfig->SFx[pChInfo->SFValue];
						if ((val & 0xff) == 0xff) val = (val & 0xf00) + pChInfo->Value;
					}
					switch (val >> 8)
					{
						case 1: // Cutoff
							Pattern_AddEffect_Pitch(pSong, cmd_filter_setcutoff, val & 0xff);
							if ((val & 0xff) < 0x7f) pSong->Flags |= Song_Flag_Filters;
						break;
						case 2: // Resonance
							Pattern_AddEffect_Pitch(pSong, cmd_filter_setresonance, val & 0xff);
							if (val & 0xff) pSong->Flags |= Song_Flag_Filters;
						break;
						default:
							SysLog_BadEffectValue(pSong, 0, pChInfo->Effect, pChInfo->Value);
					}
				}
			}
*/
			convert_effect(pSong, pConfig, pPatt->Effect, (pPatt->Value2 << 8) + pPatt->Value);

			err = Loaders_EndChannel(pSong);
			if (err) return err;
		}

		if (pDrums && (row < pDrums->Rows))
		{
			for (channel = 0; channel < 8; channel++, pDrumsPos += 4)
			{
				Note = (pDrumsPos[0] & 0x80) ? Note_Central : 0;
				Inst = Note ? (pSong->Instruments + channel + 1) : 0;
				err = Loaders_StartChannel(pSong, channel + pSong->Channels, Note, Inst);
				if (err) return err;

				if (Note && (pDrumsPos[0] & 0x0f))
					Pattern_AddEffect_Instrument(pSong, cmd_sample_note_delay, pDrumsPos[0] & 0x0f);
				Pattern_AddEffect_NoteVolume(pSong, cmd_set, pDrumsPos[1] << 1);

				err = Loaders_EndChannel(pSong);
				if (err) return err;
			}
		}

		err = Loaders_EndRow(pSong);
		if (err) return err;
	}

	Loaders_Free(pSong, pPattInfo);

	return Loaders_EndPattern(pSong, pPattern);
}

const _kernel_oserror* Loader_MT2(SongHdr* pSong)
{
	const _kernel_oserror* err = NULL;
	SubSong*        pSubSong = Song_GetSubSong(pSong, 0);
	LHdr*           pLHdr = NULL;
	LInst*          pLInst = NULL;
	LInst2*         pLInst2 = NULL;
	LDrumsHdr*      pLDrumsHdr = NULL;
	LSmp*           pLSmp = NULL;
	LEnvelope       lEnv;
	LPattern*       pLPatterns = NULL;
	LPattern*       pLDrums = NULL;
	LGroup*         pGroups = NULL;
	LAutomaton*     pLAutomatons = NULL;
	Pattern*        pPattern;
	Sample*         pSmp;
	Instrument*     pInstr;
	ChannelDefault* pDef;
	uint8_t*        pc;
	uint32_t        val, val2, Tag, TagSize;
	int             i, j, k;
	LoaderChannelData   ChConfig;
	uint32_t        Groups = 0;
	uint32_t        NVst = 0;
	uint32_t        Automatons = 0;
	uint32_t        MaxAutomatons = 128;

	// First read all information required for memory allocation
	err = Loaders_Alloc(pSong, (void**) &pLHdr, sizeof(*pLHdr));
	if (err) goto loader_err;
	err = Loaders_Alloc(pSong, (void**) &pLDrumsHdr, sizeof(*pLDrumsHdr));
	if (err) goto loader_err;
	err = Loaders_Alloc(pSong, (void**) &pLInst, sizeof(*pLInst));
	if (err) goto loader_err;
	err = Loaders_Alloc(pSong, (void**) &pLInst2, sizeof(*pLInst2));
	if (err) goto loader_err;
	err = Loaders_Alloc(pSong, (void**) &pLSmp, sizeof(*pLSmp));
	if (err) goto loader_err;
	err = Loaders_Alloc(pSong, (void**) &pLAutomatons, sizeof(*pLAutomatons)*MaxAutomatons);

	//which type do we try to load?
	err = FileLoad_SetPos(pSong, 0);
	if (err) goto loader_err;
	err = FileLoad_Read(pSong, pLHdr, sizeof(*pLHdr));
	if (err) goto loader_err;

	// is MT2 ?
	if (pLHdr->TAG != TAG(MT20))
	{
		err = Loaders_NotThisType;
		goto loader_err;
	}
	pSong->Version = (pLHdr->FormatVers[0] & 0xf) + 10*((pLHdr->FormatVers[0] & 0xf0)>>4) + 100*pLHdr->FormatVers[1];

	pSong->Flags = Song_Flag_Linear
	             | Song_Flag_Instruments
	             | Song_Flag_InstNoNote_Playing
	             | Song_Flag_XMFadeout
	             | Song_Flag_KeyoffLosesPrevNote;
	pSong->MaxNotesPerChannel = 4; // Limited number of notes per channel
	pSong->MinPitch = NoteDelta;
	pSong->MaxPitch = NoteDelta + 118 -1; // cf XM

	// fill the info
	err = Loaders_String(pSong, &pSong->pName, &pLHdr->Name[0], sizeof(pLHdr->Name), false);
	if (err) goto loader_err;

	// ... file type ptr
	pSong->pType = Typestr_MT2;
	pSubSong->RestartPos = pLHdr->RestartPos;
	pSubSong->SeqLen = pLHdr->Orders;
	if (!pSubSong->SeqLen)
	{
		err = Loaders_InvalidSong(pSong, 106, &pSubSong->SeqLen, pSubSong->SeqLen);
		goto loader_err;
	}
	pSong->Patterns = pLHdr->Patterns;
	if (!pSong->Patterns || (pSong->Patterns > 256)) // cf. sequence
	{
		err = Loaders_InvalidSong(pSong, 110, &pSong->Patterns, pSong->Patterns);
		goto loader_err;
	}
	pSong->Channels = pLHdr->Channels;
	if (!pSong->Channels || (pSong->Channels > 64))
	{
		err = Loaders_InvalidSong(pSong, 112, &pSong->Channels, pSong->Channels);
		goto loader_err;
	}
	pSong->Instruments = pLHdr->Instruments;
	if (!pSong->Instruments || (pSong->Instruments > 255)) // cf. Inst in pattern
	{
		err = Loaders_InvalidSong(pSong, 122, &pSong->Instruments, pSong->Instruments);
		goto loader_err;
	}
	pSong->Samples = pLHdr->Samples; // cf. notemap in instrument
	if (!pSong->Samples || (pSong->Samples > 255))
	{
		err = Loaders_InvalidSong(pSong, 124, &pSong->Samples, pSong->Samples);
		goto loader_err;
	}

	if (!pLHdr->Speed) pLHdr->Speed = 6;
	if (!pLHdr->LinesPerBeat) pLHdr->LinesPerBeat = 4;
	pSubSong->Defaults.Speed = pLHdr->Speed;
	pSong->TempoBase = Tempo_Base_20Hz;
	pSong->MinTempo = 1*20;
	pSong->MaxTempo = 255*15*15*20/60;
	if (pLHdr->SamplesPerTick)
		pSubSong->Defaults.Tempo = 20*44100/pLHdr->SamplesPerTick;
	else
		pSubSong->Defaults.Tempo = 125*20;

	// fill the sequence
	err = Loaders_AllocSeq(pSong, pSubSong);
	if (err) goto loader_err;

	for (i = 0; i < pSubSong->SeqLen; i++)
		pSubSong->pSeqs[i] = i; // We rewrite patterns due to drums & automatons

	// set midi config
	memset(&ChConfig, 0, sizeof(ChConfig));
	ChConfig.SFx[0] = 0x1ff;
	for (i = 0; i < 16; i++)
		ChConfig.Zxx[i] = 0x200 + 8*i;

	// Drums header
	if (pLHdr->DrumsLength)
	{
		if (pLHdr->Channels + 8 > song_max_channels)
		{
			err = Loaders_InvalidSong(pSong, 112, &pSong->Channels, pSong->Channels);
			goto loader_err;
		}
		err = FileLoad_Read(pSong, pLDrumsHdr, 274);
		if (err) goto loader_err;

		err = Loaders_Alloc(pSong, (void**) &pLDrums, sizeof(*pLDrums) * pLDrumsHdr->Patterns);
		if (err) goto loader_err;
		memset(pLDrums, 0, sizeof(*pLDrums) * pLDrumsHdr->Patterns);
	}
	else
	{
		pLDrumsHdr->Patterns = 0;
	}

	// Additional Data
	uint32_t len;
	uint32_t pos;
	err = FileLoad_ReadInt(pSong, &len, 4);
	pos = FileLoad_GetPos(pSong);
	len += pos;
	if (err) goto loader_err;
	while (pos < len)
	{
		err = Read_TagInfo(pSong, &Tag, &TagSize);
		if (err) goto loader_err;
		if (Tag == TAG(TRKS))
		{
			// Main Volume, Channel params (volume, filter effect & params)
			err = FileLoad_ReadInt(pSong, &val, 2);
			if (err) goto loader_err;
			val >>= 7;
			if (val > 256) val = 256;
			pSubSong->Defaults.Volume = val;
			// read channel volume settings and effects (includes drum channels)
			for (i = 0, pDef = pSubSong->Defaults.ChDef; i < pSong->Channels; i++, pDef++)
			{
				err = FileLoad_ReadInt(pSong, &val, 2);
				if (err) goto loader_err;
				pDef->Volume = val >> 8;
				err = FileLoad_Skip(pSong, 1028);
				if (err) goto loader_err;
			}
		}
		else if (Tag == TAG(MSG))
		{
			// fill comments
			pSong->CommentsLen = TagSize;
			err = FileLoad_ReadString(pSong, &pSong->pComments, pSong->CommentsLen, false);
			if (err)
			{
				SysLog_FileCorrupted(SysLog_Low, pSong, FileLoad_GetPos(pSong), "Could not read comments");
				pSong->CommentsLen = 0;
				Loaders_Free(pSong, pSong->pComments);
				pSong->pComments = NULL;
			}
		}
		else if (Tag == TAG(VST2))
		{
			err = FileLoad_ReadInt(pSong, &NVst, 4);
		}
		err = FileLoad_SetPos(pSong, pos + TagSize + 8);
		if (err) goto loader_err;
		pos = FileLoad_GetPos(pSong);
	}
	// Move to end of extra
	err = FileLoad_SetPos(pSong, len);
	if (err) goto loader_err;

	// Fill the patterns
	err = Loaders_Alloc(pSong, (void**) &pLPatterns, sizeof(*pLPatterns) * pLHdr->Patterns);
	if (err) goto loader_err;
	memset(pLPatterns, 0, sizeof(*pLPatterns) * pLHdr->Patterns);

	for (i = 0; i < pLHdr->Patterns; i++)
	{
		LPattern* pLPattern = &pLPatterns[i];

		// read pattern length
		err = FileLoad_ReadInt(pSong, &pLPattern->Rows, 2);
		if (err) goto loader_err;
		if (!pLPattern->Rows || (pLPattern->Rows > song_max_rows))
		{
			err = Loaders_InvalidSong(pSong, -2, &pSong->pPatterns[i].Rows, pLPattern->Rows);
			goto loader_err;
		}
		err = FileLoad_ReadInt(pSong, &pLPattern->Size, 4);
		if (err) goto loader_err;
		if (pLPattern->Size)
		{
			if (pLPattern->Size & 1) pLPattern->Size++; // Must be even
			err = Loaders_Alloc(pSong, (void**) &pLPattern->pData, pLPattern->Size);
			if (err) goto loader_err;
			err = FileLoad_Read(pSong, pLPattern->pData, pLPattern->Size);
			if (err) goto loader_err;
		}
	}

	// fill Drum patterns
	for (i = 0; i < pLDrumsHdr->Patterns; i++)
	{
		LPattern* pLDrum = &pLDrums[i];
		// read pattern length
		err = FileLoad_ReadInt(pSong, &pLDrum->Rows, 2);
		if (err) goto loader_err;
		pLDrum->Size = pLDrum->Rows*32;

		err = Loaders_Alloc(pSong, (void**) &pLDrum->pData, pLDrum->Size);
		if (err) goto loader_err;
		err = FileLoad_Read(pSong, pLDrum->pData, pLDrum->Size);
		if (err) goto loader_err;
	}

	// fill Automation
	if (pLHdr->Flags[0] & MT2_FLAG_AUTOMATION)
	{
		// Automation x channels + 0/8 drum channels + master + x VST
		int chmax = pLHdr->Channels;
		if (pLDrumsHdr->Patterns) chmax += 8;
		int ymax = chmax + 1 + NVst;

		for (i = 0; i < pLHdr->Patterns; i++)
		{
			for (int y = 0; y < ymax; y++)
			{
				uint32_t flags;
				if (pSong->Version >= 203)
				{
					err = FileLoad_ReadInt(pSong, &flags, 4);
					err = FileLoad_Skip(pSong, 4);
					if (err) goto loader_err;
				}
				else
				{
					err = FileLoad_ReadInt(pSong, &flags, 2);
					err = FileLoad_Skip(pSong, 2);
					if (err) goto loader_err;
				}
				int id = 0;
				while (flags)
				{
					if (flags & 1)
					{
						if (y <= chmax)
						{
							LAutomaton* pAuto = pLAutomatons + Automatons;
							pAuto->Pattern = i;
							pAuto->Channel = (y < chmax) ? y : 256;
							pAuto->Type = id;
							// nr points + 64 points envelope
							err = FileLoad_Read(pSong, &pAuto->Points, 4 + 4*64);

							if (y == chmax)
								SysLog_Log(SysLog_Medium, "Patt %d Automaton %d Points %d", i, id, pAuto->Points);
							else
								SysLog_Log(SysLog_Medium, "Patt %d Channel %d Automaton %d Points %d", i, y, id, pAuto->Points);

							if (pAuto->Points > 64)
							{
								err = Loaders_Error(pSong, -4*65, "Invalid nr (%d) of points in automaton", pAuto->Points);
								goto loader_err;
							}
							else if (pAuto->Points)
							{
								Automatons++;
								if (Automatons >= MaxAutomatons)
								{
									int size = sizeof(*pLAutomatons)*MaxAutomatons;
									// double current size
									err = Loaders_Resize(pSong, (void**) &pLAutomatons, size, size);
									if (err) goto loader_err;
									MaxAutomatons <<= 1;
								}
							}
						}
						else // VST?
						{
							// nr points + 64 points envelope
							err = FileLoad_Skip(pSong, 4 + 4*64);
							if (err) goto loader_err;
							SysLog_Log(SysLog_Medium, "Patt %d VST %d Automaton %d", i, y - chmax, id);
						}
					}
					flags >>= 1;
					id++;
				}
			}
		}
	}

	// read instruments info
	pSong->Instruments = 0;
	for (i = 0, pInstr = pSong->pInstruments; i < 255; i++, pInstr++)
	{
		pos = FileLoad_GetPos(pSong) + 36;

		// read name
		err = FileLoad_ReadString(pSong, &pInstr->pName, 32, false);
		if (err) goto loader_err;

		err = FileLoad_ReadInt(pSong, &TagSize, 4);
		if (err) goto loader_err;

		if (pInstr->pName[0] || TagSize)
			pSong->Instruments = i + 1;

		if (TagSize == 32) TagSize = 108 + 4 * sizeof(LEnvelope);
		if (TagSize)
		{
			int size = sizeof(*pLInst);
			if (pSong->Version < 202)
			{
				size -= 4;
				pLInst->EnvelopeFlags = 3;
			}
			else
				TagSize += 4; // < 202 correct size, after we need + 4
			if (pSong->Version < 201)
			{
				size -= 2;
				pLInst->Flags = 0;
			}
			err = FileLoad_Read(pSong, pLInst, size);
			if (err) goto loader_err;

			// read note table
			err = Loaders_AllocMapTable(pSong, &pInstr->pNotesMap);
			if (err) goto loader_err;

			NoteMap* pn;
			for (j = 0, pn = pInstr->pNotesMap + NoteDelta; j < 96; j++, pn++)
			{
				if (pLInst->NotesMap[j] < pLInst->Groups[0])
				{
					pn->Pitch = (j + NoteDelta) << 8;
					pn->SampleNr = 1 + Groups + pLInst->NotesMap[j]; // Group nr
				}
			}
			Groups += pLInst->Groups[0];

			// read volume fadeout
			pInstr->FadeoutVolume = pLInst->Fadeout << 1;
			// read auto-vibrato
			switch (pLInst->VibratoType)
			{
				case 1: pInstr->Vibrato.Type = vibrato_type_square; break;
				case 2: pInstr->Vibrato.Type = vibrato_type_ramp; break;
				case 3: pInstr->Vibrato.Type = vibrato_type_ramp | vibrato_type_inverted; break;
				default: pInstr->Vibrato.Type = vibrato_type_sin;
			}
			pInstr->Vibrato.Speed = pLInst->VibratoSpeed;
			pInstr->Vibrato.Depth = pLInst->VibratoDepth << 2;
			pInstr->Vibrato.Type |= vibrato_type_sweep;
			pInstr->Vibrato.Rate = pLInst->VibratoSweep;
			// read New Note Action
			switch (pLInst->NewNoteAction)
			{
				case 1: pInstr->NewNoteAction = note_action_continue; break;
				case 2: pInstr->NewNoteAction = note_action_offxm; break;
				case 3: pInstr->NewNoteAction = note_action_fade; break;
				default: pInstr->NewNoteAction = note_action_cut;
			}
			if (pInstr->NewNoteAction != note_action_cut) pSong->Flags |= Song_Flag_Virtual;

			// read Duplicate Check Type
			val = pLInst->DuplicateCheck & 0xf;
			if (val > 3) pInstr->DuplicateCheckType = 0;
			else pInstr->DuplicateCheckType = val;
			// read Duplicate Note Action
			switch(pLInst->DuplicateCheck >> 4)
			{
				case 1: pInstr->DuplicateNoteAction = note_action_continue; break;
				case 2: pInstr->DuplicateNoteAction = note_action_offxm; break;
				case 3: pInstr->DuplicateNoteAction = note_action_fade; break;
				default: pInstr->DuplicateNoteAction = note_action_cut; break;
			}

			// Read Volume envelope?
			if (pLInst->EnvelopeFlags & 1)
			{
				err = FileLoad_Read(pSong, &lEnv, sizeof(lEnv));
				if (err) goto loader_err;
				Envelope* pEnv = &pInstr->VolumeEnvelope;

				val = lEnv.Flags;
				val2 = 0;
				if (val & 1) val2 |= Envelope_Flag_On;
				if (val & 2) val2 |= Envelope_Flag_Sustain;
				if (val & 4) val2 |= Envelope_Flag_Loop;
				pEnv->Flags = val2;
				if (lEnv.Points > 16) lEnv.Points = 0;
				pEnv->Points = lEnv.Points;
				pEnv->LoopStart = lEnv.LoopStart;
				pEnv->LoopEnd = lEnv.LoopEnd;
				pEnv->SustainStart =
				pEnv->SustainEnd = lEnv.Sustain;

				// read envelope points
				err = Loaders_Alloc(pSong, (void**) &pEnv->pTable, pEnv->Points << 2);
				if (err) goto loader_err;
				for (j = 0, pc = lEnv.Values; j < pEnv->Points; j++, pc += 4)
				{
					uint32_t val = (pc[2] + (pc[3] << 8)) << 2;

					if (val > 256) val = 256;
					pEnv->pTable[j] = pc[0] + (pc[1] << 8) + (val << 16);
				}
			}

			// Read Panning envelope?
			if (pLInst->EnvelopeFlags & 2)
			{
				err = FileLoad_Read(pSong, &lEnv, sizeof(lEnv));
				if (err) goto loader_err;
				Envelope* pEnv = &pInstr->PanningEnvelope;

				val = lEnv.Flags;
				val2 = 0;
				if (val & 1) val2 |= Envelope_Flag_On;
				if (val & 2) val2 |= Envelope_Flag_Sustain;
				if (val & 4) val2 |= Envelope_Flag_Loop;
				pEnv->Flags = val2;
				if (lEnv.Points > 16) lEnv.Points = 0;
				pEnv->Points = lEnv.Points;
				pEnv->LoopStart = lEnv.LoopStart;
				pEnv->LoopEnd = lEnv.LoopEnd;
				pEnv->SustainStart =
				pEnv->SustainEnd = lEnv.Sustain;

				// read envelope points
				err = Loaders_Alloc(pSong, (void**) &pEnv->pTable, pEnv->Points << 2);
				if (err) goto loader_err;
				for (j = 0, pc = lEnv.Values; j < pEnv->Points; j++, pc += 4)
				{
					int val = pc[2];

					if (val > 64) val = 64;
					pEnv->pTable[j] = pc[0] + (pc[1] << 8) + (val << 18);
				}
			}

			// Read Pitch envelope?
			if (pLInst->EnvelopeFlags & 4)
			{
				err = FileLoad_Read(pSong, &lEnv, sizeof(lEnv));
				if (err) goto loader_err;

/* Doc of latest version, 2.61, still has not implementation of pitch envelopes
				Envelope* pEnv = &pInstr->PitchEnvelope;

				val = lEnv.Flags;
				val2 = 0;
				if (val & 1) val2 |= Envelope_Flag_On;
				if (val & 2) val2 |= Envelope_Flag_Sustain;
				if (val & 4) val2 |= Envelope_Flag_Loop;
				pEnv->Flags = val2;
				if (lEnv.Points > 16) lEnv.Points = 0;
				pEnv->Points = lEnv.Points;
				pEnv->LoopStart = lEnv.LoopStart;
				pEnv->LoopEnd = lEnv.LoopEnd;
				pEnv->SustainStart =
				pEnv->SustainEnd = lEnv.Sustain;

				// read envelope points
				err = Loaders_Alloc(pSong, (void**) &pEnv->pTable, pEnv->Points << 2);
				if (err) goto loader_err;
				for (j = 0, pc = lEnv.Values; j < pEnv->Points; j++, pc += 4)
				{
					int val = pc[2] << 24;

					pEnv->pTable[j] = pc[0] + (pc[1] << 8) + (val >> 1);
				}
*/
			}

			// Read Filter envelope?
			if (pLInst->EnvelopeFlags & 4)
			{
				err = FileLoad_Read(pSong, &lEnv, sizeof(lEnv));
				if (err) goto loader_err;
				Envelope* pEnv = &pInstr->FilterEnvelope;

				val = lEnv.Flags;
				val2 = 0;
				if (val & 1) val2 |= Envelope_Flag_On;
				if (val & 2) val2 |= Envelope_Flag_Sustain;
				if (val & 4) val2 |= Envelope_Flag_Loop;
				pEnv->Flags = val2;
				if (lEnv.Points > 16) lEnv.Points = 0;
				pEnv->Points = lEnv.Points;
				pEnv->LoopStart = lEnv.LoopStart;
				pEnv->LoopEnd = lEnv.LoopEnd;
				pEnv->SustainStart =
				pEnv->SustainEnd = lEnv.Sustain;

				// read envelope points
				err = Loaders_Alloc(pSong, (void**) &pEnv->pTable, pEnv->Points << 2);
				if (err) goto loader_err;
				for (j = 0, pc = lEnv.Values; j < pEnv->Points; j++, pc += 4)
				{
					int32_t val = pc[2] << 2;

					if (val > 256) val = 256;
					pEnv->pTable[j] = pc[0] + (pc[1] << 8) + (val << 16);
				}
			}

			if (pLInst->Flags)
			{
				err = FileLoad_Read(pSong, pLInst2, sizeof(*pLInst2));
				if (err) goto loader_err;
				if (pLInst->Flags & MT2_INST_FLAG_VST)
				{
					SysLog_FileCorrupted
						( SysLog_High, pSong, -32
						, "VST Instrument %d, Synth %d"
						, i + 1, pLInst2->SynthID
						);
					Loaders_String(pSong, &pInstr->pName, (uint8_t*) "[VST Synth]", 12, false);
				}
				if (pLInst->Flags & MT2_INST_FLAG_FILTER)
				{
					if (pLInst2->EffectID == 0)
					{
						// read LowPass Resonance Filter Info
						pInstr->FilterCutoff = 0x80 + Convert_FreqToCutoff(pLInst2->Cutoff);
						if (pLInst2->Resonance > 127) pLInst2->Resonance = 127;
						pInstr->FilterResonance = 0x80 + pLInst2->Resonance;
						if (((pInstr->FilterCutoff & 0x80) && (pInstr->FilterCutoff != 0xff))
						||  ((pInstr->FilterResonance & 0x80) && (pInstr->FilterResonance != 0x80)))
							pSong->Flags |= Song_Flag_Filters;
					}
					else
					{
						SysLog_Log
							( SysLog_Medium
							, "Instrument %d Flags: %x, Effect: %d"
							, i + 1, pLInst->Flags, pLInst2->EffectID
							);
					}
				}
				if (pLInst->Flags & ~6)
					SysLog_Log
						( SysLog_Medium
						, "Instrument %d Flags: %x, Synth: %d, Effect: %d"
						, i + 1, pLInst->Flags, pLInst2->SynthID, pLInst2->EffectID
						);
			}

			err = FileLoad_SetPos(pSong, pos + TagSize);
			if (err) goto loader_err;
		}
	}

	// Check space for drum instruments but add them later
	if (pLDrumsHdr->Patterns > 0)
	{
		if (pSong->Instruments > (255 - 8))
		{
			err = Loaders_InvalidSong(pSong, 122, &pSong->Instruments, pSong->Instruments);
			goto loader_err;
		}
	}

	// Build real patterns, only after we have final number of instruments
	// because we will use the eight next instruments for drum samples
	pSong->Patterns = pSubSong->SeqLen;
	for (i = 0, pPattern = pSong->pPatterns; i < pSubSong->SeqLen; i++, pPattern++)
	{
		LPattern* pLPattern = NULL;
		LPattern* pLDrum = NULL;

		if (pLHdr->Order[i] < pLHdr->Patterns)
			pLPattern = &pLPatterns[pLHdr->Order[i]];
		if (pLDrumsHdr->Order[i] < pLDrumsHdr->Patterns)
			pLDrum = &pLDrums[pLDrumsHdr->Order[i]];

		err = Pattern_MT2
				(pSong
				, i
				, pLHdr->Order[i]
				, pPattern
				, (pLHdr->Flags[0] & MT2_FLAG_PACKEDPATTERNS) != 0
				, pLPattern
				, pLDrum
				, pLAutomatons
				, Automatons
				, &ChConfig
				);
		if (err) goto loader_err;
	}
	if (pLPatterns)
	{
		for (i = 0; i < pLHdr->Patterns; i++)
			Loaders_Free(pSong, pLPatterns[i].pData);
		Loaders_Free(pSong, pLPatterns);
		pLPatterns = NULL;
	}
	if (pLDrums)
	{
		for (i = 0; i < pLDrumsHdr->Patterns; i++)
			Loaders_Free(pSong, pLDrums[i].pData);
		Loaders_Free(pSong, pLDrums);
		pLDrums = NULL;
	}

	// read samples info
	pSong->Samples = 0;
	pSmp = pSong->pSamples;
	for (i = 0; i < 256; i++, pSmp++)
	{
		pos = FileLoad_GetPos(pSong) + 36;

		// read name
		err = FileLoad_ReadString(pSong, &pSmp->pName, 32, false);
		if (err) goto loader_err;

		err = FileLoad_ReadInt(pSong, &TagSize, 4);
		if (err) goto loader_err;

		// we can only handle 255 samples
		if ((i < 255) && (pSmp->pName[0] || TagSize))
			pSong->Samples = i + 1;

		if ((i < 255) && TagSize)
		{
			err = FileLoad_Read(pSong, pLSmp, sizeof(*pLSmp));
			if (err) goto loader_err;

			pSmp->Type = Smp_Type_Set_Panning;

			// read volume
			val = pLSmp->DefaultVolume >> 7;
			if (val <= 256) pSmp->DefaultVolume = val;
			pSmp->ScaleVolume = 256;

			// read panning
			val = (pLSmp->Panning + 128) & 0xff;
			if (!val) pSmp->Panning = Panning_Surround;
			else pSmp->Panning = val;

			// read sizes
			pSmp->Size = pLSmp->Length;
			pSmp->LoopStart = pLSmp->LoopStart;
			pSmp->LoopEnd = pLSmp->LoopEnd;
			pSmp->Frequency = pLSmp->Frequency;
			pSmp->RelTone = -(pLSmp->SampledNote + NoteDelta - Note_Central) << 8;

			// read sample type
			// sample stored in file?
if (pLSmp->Flags & ~0x5) SysLog_Log(SysLog_Medium, "Sample %d: Flags %x", i + 1, pLSmp->Flags);
			if (!(pLSmp->Flags & 0x05))
			{
				// sample stored in file
				if (pLSmp->Depth != 1) pSmp->Type |= Smp_Type_16bit;
				if (pLSmp->Channels != 1) pSmp->Type |= Smp_Type_Stereo;
				if (pLSmp->LoopType) pSmp->Type |= Smp_Type_Loop;
				if (pLSmp->LoopType == 2) pSmp->Type |= Smp_Type_Loop_Bidi;
				if (pSmp->Type & Smp_Type_16bit)
					pSmp->Size >>= 1;
				if (pSmp->Type & Smp_Type_Stereo)
					pSmp->Size >>= 1;
			}
			else
			{
				// sample not stored in file
				pSmp->Size = 0;
				pSmp->Frequency = 0;
			}
		}
		else
		{
			// sample not defined
			pSmp->Size = 0;
			pSmp->Frequency = 8363;
		}
		err = FileLoad_SetPos(pSong, pos + TagSize);
		if (err) goto loader_err;
	}

	// read groups
	err = Loaders_Alloc(pSong, (void**) &pGroups, Groups*sizeof(*pGroups));
	if (err) goto loader_err;
	err = FileLoad_Read(pSong, pGroups, Groups*sizeof(*pGroups));
	// normal volume is 128 but can go to 256, so try to normalise a little bit
	if (err) goto loader_err;
	{
		int maxScale = 0;

		for (i = 0; i < Groups; i++)
			maxScale += pGroups[i].ScaleVolume;

		maxScale /= Groups;
		if (maxScale < 128) maxScale = 128;
		// If tend to go largely beyond normal volume, reduce shift
		pSong->VolumeShift >>= 1;
		pSong->VolumeShift = (pSong->VolumeShift * 256) / maxScale;
	}

	// update instruments with group info
	for (i = 0, pInstr = pSong->pInstruments; i < pSong->Instruments; i++, pInstr++)
	{
		if (pInstr->pNotesMap)
		{
			NoteMap* pn;
			for (j = 0, pn = pInstr->pNotesMap + NoteDelta; j < 96; j++, pn++)
			{
				val = pn->SampleNr;
				if (val && (val <= Groups))
				{
					const LGroup* pGroup = pGroups + val - 1;
					pn->Volume = pGroup->ScaleVolume;
					pn->SampleNr = 1 + pGroup->SampleNr;
					pn->Pitch += pGroup->FinePitch << 1;
				}
				else
					pn->SampleNr = 0;
			}
		}
	}

	// read samples data
	pSmp = pSong->pSamples;
	for (i = 0; i < pSong->Samples; i++, pSmp++)
	{
		// Stored in file
		if (pSmp->Frequency)
		{
			err = FileLoad_ReadSample(pSong, pSmp);
			if (err) goto loader_err;

			// Remove Stereo info because samples are not interlaced
			pSmp->Type &= ~Smp_Type_Stereo;

			// convert sample from diff to absolute
			if (pSmp->Type & Smp_Type_Stereo)
			{
				if (pSmp->Type & Smp_Type_16bit)
				{
					int16_t* pc;

					val = 0;
					for (k = pSmp->Size<<1, pc = pSmp->Ptr; k; k--, pc++)
					{
						val += *pc;
						*pc = val;
					}
				}
				else
				{
					signed char* pc;

					val = 0;
					for (k = pSmp->Size<<1, pc = pSmp->Ptr; k; k--, pc++)
					{
						val += *pc;
						*pc = val;
					}
				}
			}
			else
			{
				if (pSmp->Type & Smp_Type_16bit)
				{
					int16_t* pc;

					val = 0;
					for (k = pSmp->Size, pc = pSmp->Ptr; k; k--, pc++)
					{
						val += *pc;
						*pc = val;
					}
				}
				else
				{
					signed char* pc;

					val = 0;
					for (k = pSmp->Size, pc = pSmp->Ptr; k; k--, pc++)
					{
						val += *pc;
						*pc = val;
					}
				}
			}
		}
		else
		{
			err = FileLoad_ReadInt(pSong, &val, 4);
			if (err) goto loader_err;
			err = FileLoad_Skip(pSong, 12 + val);
			if (err) goto loader_err;
		}
	}

	// Add drum instruments
	if (pLDrumsHdr->Patterns)
	{
		for (i = 0, pInstr = pSong->pInstruments + pSong->Instruments; i < 8; i++, pInstr++)
		{
			// build note table
			err = Loaders_AllocMapTable(pSong, &pInstr->pNotesMap);
			if (err) goto loader_err;

			NoteMap* pn;
			for (j = 0, pn = pInstr->pNotesMap + NoteDelta; j < 96; j++, pn++)
			{
				pn->Pitch = (j + NoteDelta) << 8;
				pn->SampleNr = 1 + pLDrumsHdr->Samples[i];
				pn->Volume = 128;
			}

			// set volume fadeout to max
			pInstr->FadeoutVolume = 0xffff;
		}
		pSong->Instruments += 8;
		pSong->Channels += 8;
	}

	// normal channel volume is 128 but can go to 256, so try to normalise a little bit
	{
		int maxScale = 0;
		for (i = 0, pDef = pSubSong->Defaults.ChDef; i < pSong->Channels; i++, pDef++)
			maxScale += pDef->Volume;
		maxScale /= pSong->Channels;

		if (maxScale < 128) maxScale = 128;
		// If tend to go largely beyond normal volume, reduce shift
		pSong->VolumeShift = (pSong->VolumeShift * 256) / maxScale;
	}

loader_err:
	if (pLPatterns)
	{
		for (i = 0; i < pLHdr->Patterns; i++)
			Loaders_Free(pSong, pLPatterns[i].pData);
		Loaders_Free(pSong, pLPatterns);
	}
	if (pLDrums)
	{
		for (i = 0; i < pLDrumsHdr->Patterns; i++)
			Loaders_Free(pSong, pLDrums[i].pData);
		Loaders_Free(pSong, pLDrums);
	}
	Loaders_Free(pSong, pLHdr);
	Loaders_Free(pSong, pLDrumsHdr);
	Loaders_Free(pSong, pLInst);
	Loaders_Free(pSong, pLInst2);
	Loaders_Free(pSong, pLSmp);
	Loaders_Free(pSong, pGroups);
	Loaders_Free(pSong, pLAutomatons);

	return err;
}
