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

/*
 * Channels: [1...32]
 * Patterns: [1...256]
 * Rows    : [1...256]
 * Orders  : [1...65535]
 * Samples : [1...255]
 * Instr.  : [1...255]
 * Tempo   : [4...255], T frames per 2.5 seconds.
 * Speed   : [1...255], S frames per row.
 * Notes   : [C-0...C-4...B-9]
 * Pitch   : Amiga periods mode, base is 428 units, period is divided by 2 each octave up.
 *           Slides as x/1 or x/4 units per frame.
 * Volume  : [1...255] , linear.
 * Panning : [0...127].
 * Sampling: Frequency is used directly.
 *
 * Undocumented:
 * - Block II is not always present (Sample mode instead of Intruments mode?)
 *
 * Bug in 3.2: All vibrato types seem to behave as type square.
 *
 * Note triggering & sample substitution:
 * -------------------------------------
 *
 * A new note nr with an instrument nr starts a note with the specified pitch and instrument.
 *
 * A new note nr without an instrument nr starts a note with the last used instrument nr.
 *
 * An instrument nr given without a note nr:
 *   - sets default volume/panning from new instrument.
 *   - restores sustain but does NOT reset fadeout.
 *   - resets auto vibrato position.
 *   - resets envelopes positions. (Visualized in instrument while playing).
 *   - finetune, vibrato/tremolo positions are not affected.
 *
 * Instrument mapping to no sample behave as empty sample with volume 0.
 *
 * Unstored samples are treated as empty samples with default volume 0.
 *
 * Note triggering does not apply when portamento effect is present.
 *
 * Retrig E9y has standard MOD behaviour.
 * Retrig Ixy. y = 0 is memory, x = 0 is only no volume change.
 *   Retrig is like E9y. Volume change x only applies for non-0 ticks.
 *
 * Vibrato & Tremolo position is update every frame.
 *   New notes do NOT reset vibrato position.
 *
 * Tremor resets counter every row.
 *   K00 is memory
 *   K0y behaves as K1y
 *   Kx0 behaves as always on
 *
 * Effects:
 * -------
 *
 * Problems!!!!!
 * - Effect 9, set envelope is not supported.
 * - Effect E8, set sample status is not supported.
 * - Envelope, volume, panning and fadeout are attached in sub-sample of instrument.
 * - Envelopes are stored independently of sample/instrument.
 */

#define NoteDelta (Note_Central-12*4)

#define TAG(x) (*(const uint32_t*) Tag_##x)

static const uint8_t Tag_DMDL[] = "DMDL";
static const uint8_t Tag_IN[] = "IN\0\0";
static const uint8_t Tag_ME[] = "ME\0\0";
static const uint8_t Tag_PA[] = "PA\0\0";
static const uint8_t Tag_PN[] = "PN\0\0";
static const uint8_t Tag_TR[] = "TR\0\0";
static const uint8_t Tag_II[] = "II\0\0";
static const uint8_t Tag_VE[] = "VE\0\0";
static const uint8_t Tag_PE[] = "PE\0\0";
static const uint8_t Tag_FE[] = "FE\0\0";
static const uint8_t Tag_IS[] = "IS\0\0";
static const uint8_t Tag_SA[] = "SA\0\0";
static const uint8_t Typestr_MDL[] = "DigiTrakker";
static const char Invalid_Block[] = "Invalid block";
static const char Unexpected_Block[] = "Block not expected in that position";

typedef struct
{
	uint32_t    Channel;
	uint8_t*    pFrom;
	uint8_t*    pTo;
	uint32_t    row;
	uint8_t	    Note;
	uint8_t	    Inst;
	uint8_t	    Volume;
	uint8_t	    Effect;
	uint8_t	    Value;
	uint8_t	    Effect2;
	uint8_t	    Value2;
} ChannelInfo;

typedef struct
{
	uint8_t note;
	uint8_t inst;
	uint8_t volume;
	uint8_t eff;
	uint8_t val1;
	uint8_t val2;
} LRow;

typedef struct
{
	uint32_t	size;
	uint8_t*    triples;
	uint32_t	fileoffset;
	LRow        row[256];
} LTrack;

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 inline void volume_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 corse slide
		value = (value & 0x0f) << 2;
		// zF0 must force memory to 0, not read it
		if (!value) cmd = cmd_volume_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_volume_memslide;
		cmd |= flag_cmd_slide_frame0;
	}
	else
	{
		// zxx normal slide
		cmd |= flag_cmd_slide_frameN0;
	}
	Pattern_AddEffect_NoteVolume(pSong, cmd, value);
}

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

	switch(effect)
	{
		case 1: // Pitch: Slide up
		{
			if (!value)
			{
				// 100 slide at last slide type and value
				Pattern_AddEffect_Pitch(pSong, cmd_upmem1, 0);
			}
			else
			{
				pitch_slide(pSong, cmd_upmem1, value);
			}
		}
		break;
		case 2: // Pitch: Slide down
		{
			if (!value)
			{
				// 200 slide at last slide type and value
				Pattern_AddEffect_Pitch(pSong, cmd_downmem1, 0);
			}
			else
			{
				pitch_slide(pSong, cmd_downmem1, value);
			}
		}
		break;
		case 3: // Pitch: Portamento
		{
			if (!value)
			{
				// 300 portamento with same speed as before
				Pattern_AddEffect_Pitch(pSong, cmd_portamento, 0);
			}
			else
			{
				Pattern_AddEffect_Pitch(pSong, cmd_portamento + flag_cmd_slide_frameN0, value << 2);
			}
		}
		break;
		case 4: // Pitch: Vibrato
		{
			uint32_t speed = value >> 4;
			value &= 0xf;
			// Vibrate between [-2*y,+2*y] even on 0-frame
			value <<= 3; // << 2 for range conversion and << 1 for depth
			if (speed) Pattern_AddEffect_Pitch(pSong, cmd_vibrato_speed, speed << 2);
			Pattern_AddEffect_Pitch(pSong, cmd_vibrato_depth | flag_cmd_vibrato_frame0N0, value);
		}
		break;
		case 5: // Pitch: Arpeggio
		{
			uint32_t val;

			val = value >> 4;
			value &= 0xf;

			Pattern_AddEffect_Arpeggio(pSong, value, val, cmd_arpeggio_type_NHL);
		}
		break;
		case 7: // Global Tempo: Set
		{
			if (value >= 4)
				Pattern_AddGlbEffect_Tempo(pSong, cmd_set, value);
			else
				SysLog_BadEffectValue(pSong, 0, oeffect, ovalue);
		}
		break;
		case 8: // Panning: Set (highest bit is ignored)
		{
			value = (value & 0x7f) << 1;
			if (value >= 128) value++;
			Pattern_AddEffect_Panning(pSong, cmd_set, value);
		}
		break;
		case 9: // Set envelope
		{
			SysLog_SkipEffect(pSong, 0, oeffect, ovalue);
		}
		break;
		case 0xB: // Global: Jump
		{
			Pattern_AddGlbEffect_Position(pSong, gcmd_pos_jump, value);
		}
		break;
		case 0xC: // Global Volume: Set
		{
			Pattern_AddGlbEffect_Volume(pSong, cmd_set, value);
		}
		break;
		case 0xD: // Global: Break
		{
			uint32_t ten = value >> 4;
			value &= 0xf;
			if ((value > 9) || (ten > 8))
				SysLog_BadEffectValue(pSong, 0, oeffect, ovalue);
			else
			{
				value += ten * 10;
				Pattern_AddGlbEffect_Position(pSong, gcmd_pos_break, value);
			}
		}
		break;
		case 0xE: // +- protracker effects E
		{
			effect = value >> 4;
			value &= 0xf;

			switch(effect)
			{
				case 1: // E1, Panning: Slide Left, Memory
				{
					effect = cmd_downmem1 | flag_cmd_slide_frameN0;
					Pattern_AddEffect_Panning(pSong, effect, value << 1);
				}
				break;
				case 2: // E2, Panning: Slide Right, Memory
				{
					effect = cmd_upmem1 | flag_cmd_slide_frameN0;
					Pattern_AddEffect_Panning(pSong, effect, value << 1);
				}
				break;
				case 4: // E4, Pitch: Vibrato type
				{
					switch(value)
					{
						case 1: value = vibrato_type_ramp; break;
						case 2: value = vibrato_type_square; break;
						default: value = vibrato_type_sin; break;
					}
					value |= vibrato_type_inverted | vibrato_type_noretrig;
					Pattern_AddEffect_Pitch(pSong, cmd_vibrato_type, value);
				}
				break;
				case 5: // E5, Note: Set fine tune in 1/8 semitone
				{
					int32_t svalue = value << 28;

					// only if a note is defined at the same time
					if (note) Pattern_AddEffect_Instrument(pSong, cmd_sample_set_finetune, svalue >> 23);
				}
				break;
				case 6: // E6, Global: Loop in column
				{
					Pattern_AddGlbEffect_Position(pSong, gcmd_pos_loop | value, channel);
				}
				break;
				case 7: // E7 Volume: Vibrato type
				{
					switch(value)
					{
						case 1: value = vibrato_type_ramp; break;
						case 2: value = vibrato_type_square; break;
						default: value = vibrato_type_sin; break;
					}
					value |= vibrato_type_noretrig;
					Pattern_AddEffect_NoteVolume(pSong, cmd_vibrato_type, value);
				}
				break;
				case 8: // E8, Sample loop status
				{
					switch(value)
					{
						case 0: Pattern_AddEffect_Instrument(pSong, cmd_sample_loop_off, 0); break;
						case 1: Pattern_AddEffect_Instrument(pSong, cmd_sample_loop_normal, 0); break;
						case 3: Pattern_AddEffect_Instrument(pSong, cmd_sample_loop_bidi, 0); break;
						default: SysLog_SkipEffect(pSong, 0, oeffect, ovalue);
					}
				}
				break;
				case 9: // E9, Note: Retrig note
				{
					Pattern_AddEffect_RetrigCounter(pSong, cmd_retrig_count_type_MOD, value);
					Pattern_AddEffect_Instrument(pSong, cmd_sample_note_retrig, 8);
				}
				break;
				case 0xa: // EA, Global Volume: Slide Up, Memory
				{
					Pattern_AddGlbEffect_Volume(pSong, cmd_upmem1+flag_cmd_slide_frameN0, value);
				}
				break;
				case 0xb: // EB, Global Volume: Slide Down, Memory
				{
					Pattern_AddGlbEffect_Volume(pSong, cmd_downmem1+flag_cmd_slide_frameN0, value);
				}
				break;
				case 0xc: // EC, Note: Cut note after x ticks
				{
					Pattern_AddEffect_NoteVolume(pSong, cmd_volume_cut, value);
				}
				break;
				case 0xd: // ED, Note: Delay for x ticks
				{
					Pattern_AddEffect_Instrument(pSong, cmd_sample_note_delay, value);
				}
				break;
				case 0xe: // EE, Global: Delay row for x rows
				{
					Pattern_AddGlbEffect_Frames(pSong, gcmd_frames_repeat, value);
				}
				break;
				case 0xF: // EF, Sample set offset (should not arrive here, double-command)
				{
					SysLog_SkipEffect(pSong, 0, oeffect, ovalue);
				}
				break;
				default:
				{
					// Unknown effect
					SysLog_BadEffect(pSong, 0, oeffect, ovalue);
				}
			}
		}
		break;
		case 0xF: // Global: Set speed
		{
			if (value)
				Pattern_AddGlbEffect_Frames(pSong, gcmd_frames_set, value);
			else
				SysLog_BadEffectValue(pSong, 0, oeffect, ovalue);
		}
		break;
		case 0x10: // G, Volume: Slide up
		{
			if (!value)
			{
				// G00 slide at last slide type and value
				Pattern_AddEffect_NoteVolume(pSong, cmd_upmem1, 0);
			}
			else
			{
				volume_slide(pSong, cmd_upmem1, value);
			}
		}
		break;
		case 0x20: // H, Volume: Slide down
		{
			if (!value)
			{
				// H00 slide at last slide type and value
				Pattern_AddEffect_NoteVolume(pSong, cmd_downmem1, 0);
			}
			else
			{
				volume_slide(pSong, cmd_downmem1, value);
			}
		}
		break;
		case 0x30: // I, Note: Retrig note every y ticks with volume change type x, Memory
		{
			uint32_t eff = 0;

			eff = value >> 4;
			value &= 0xf;

			// Memory only if y = 0
			if (!eff) eff = 8;
			eff |= cmd_retrig_volume_skip_frame0;
			Pattern_AddEffect_RetrigCounter(pSong, cmd_retrig_count_type_MOD, value);
			Pattern_AddEffect_Instrument(pSong, cmd_sample_note_retrig, eff);
		}
		break;
		case 0x40: // J, Volume: Vibrato (alias Tremolo)
		{
			uint32_t speed = value >> 4;
			value &= 0xf;
			// Vibrate between [-8*y,+8*y] even on 0-frame
			value <<= 3; // << 0 for range conversion and << 3 for depth
			if (speed) Pattern_AddEffect_NoteVolume(pSong, cmd_vibrato_speed, speed << 2);
			Pattern_AddEffect_NoteVolume(pSong, cmd_vibrato_depth | flag_cmd_vibrato_frame0N0, value);
		}
		break;
		case 0x50: // K, Volume: Tremor
		{
			uint32_t val;

			val = value >> 4;
			value &= 0xf;
			Pattern_AddEffect_NoteVolume(pSong, cmd_volume_tremor_MDL, Effect_Tremor(value, val));
		}
		break;
		default:
		{
			if (oeffect) SysLog_BadEffect(pSong, 0, oeffect, ovalue);
		}
	}
}

static const _kernel_oserror* Pattern_Decode
   ( SongHdr* pSong
   , uint32_t i
   , Pattern* pPattern
   , const uint16_t* pTrackNr
   , const LTrack* pLTracks
   , uint32_t MaxTrackNr
   )
{
	const _kernel_oserror* err = NULL;
	int             j, k, Note;
	const LTrack*   pLTrack;
	const LRow*     pLRow;

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

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

		for (k = 0; k < pSong->Channels; k++)
		{
			if (pTrackNr[k] > MaxTrackNr)
			{
				SysLog_FileCorrupted(SysLog_High, pSong, 0, "Pattern %d, invalid track %d for channel %d", i, pTrackNr[k], k);
			}
			else if (pTrackNr[k] != 0)
			{
				pLTrack = &pLTracks[pTrackNr[k] - 1];
				pLRow = &pLTrack->row[j];

				if ((pLRow->note != Note_Fade) && (pLRow->note >= 120))
				{
					SysLog_BadNote(pSong, 0, k, pLRow->note);
					Note = 0;
				}
				else Note = pLRow->note;

				err = Loaders_StartChannel(pSong, k, Note, pLRow->inst);
				if (err) return err;
				if (pLRow->note == Note_Fade)
					Pattern_AddEffect_Instrument(pSong, cmd_sample_note_action_sustainoff, 0);

				Note = (Note < Note_CmdMin) ? Note : 0;
				if (pLRow->volume)
					Pattern_AddEffect_NoteVolume(pSong, cmd_set, pLRow->volume);

				if (((pLRow->eff & 0xf) == 0xe) && ((pLRow->val1 & 0xf0) == 0xf0))
				{
					// Note: Set sample offset, uses the two values
					// clear byte 0
					Pattern_AddEffect_Instrument(pSong, cmd_sample_set_offset, 0x000 | 0);
					// set byte 1
					Pattern_AddEffect_Instrument(pSong, cmd_sample_set_offset, 0x100 | pLRow->val2);
					// set byte 2
					Pattern_AddEffect_Instrument(pSong, cmd_sample_set_offset, 0x200 | (pLRow->val1 & 0xf));

					// only if a note is defined at the same time
					if (Note) Pattern_AddEffect_Instrument(pSong, cmd_sample_set_offset, 0xf00);
				}
				else
				{
					convert_effect(pSong, k, pLRow->eff & 0xf, pLRow->val1, Note);
					// Second effect use a different encoding (effects G-K) for 1-6 so do not shift them!!!
					if ((pLRow->eff & 0xf0) < 0x70)
						convert_effect(pSong, k, pLRow->eff &0xf0, pLRow->val2, Note);
					else
						convert_effect(pSong, k, pLRow->eff >> 4, pLRow->val2, Note);
				}

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

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

	return Loaders_EndPattern(pSong, pPattern);
}

#define ReadBits(v, n)\
if (bitnum < n)\
{\
	err = FileLoad_ReadByte(pSong, &tst);\
	if (err != NULL) goto smp_err;\
	if (--csize < 0) goto smp_err;\
	bitbuf |= (tst << bitnum);\
	bitnum += 8;\
}\
v = bitbuf & ((1 << n) - 1);\
bitbuf >>= n;\
bitnum -= n;\

static const _kernel_oserror* Read_Sample(SongHdr* pSong, Sample* pSample, uint32_t maxsize)
{
	const _kernel_oserror* err = NULL;
	int size = pSample->Size;
	uint32_t bitbuf = 0, tst;
	int bitnum = 0, csize = 0;
	int8_t dlt = 0, byte, sign;
	uint8_t* pc;
	uint8_t* pcend;

	if (pSample->Type & Smp_Type_16bit)
		size <<= 1;

	// allocate memory with a few extra bytes
	err = Loaders_Alloc(pSong, &pSample->Ptr, size + 4);
	if (err) return err;

	pc = pSample->Ptr;
	pcend = pc + size;

	err = FileLoad_ReadInt(pSong, &tst, 4);
	if (err) goto smp_err;
	if (tst > maxsize)
		csize = maxsize;
	else
		csize = tst;

	while(pc < pcend)
	{
		if (pSample->Type & Smp_Type_16bit)
		{
			ReadBits(byte, 8);
			*pc++ = byte;
		}
		ReadBits(sign, 1);
		ReadBits(tst, 1);
		if (tst)
		{
			ReadBits(byte, 3);
		}
		else
		{
			byte = 8;
			ReadBits(tst, 1);
			while(!tst)
			{
				byte += 16;
				ReadBits(tst, 1);
			}
			ReadBits(tst, 4);
			byte += tst;
		}
		if (sign) byte = ~byte;
		dlt += byte;
		*pc++ = dlt;
	}

smp_err:
	size = pc - (uint8_t*) pSample->Ptr;

	if (pSample->Type & Smp_Type_16bit)
		size >>= 1;

	if (pSample->Size != size)
	{
		SysLog_FileCorrupted
			( ((pSample->Size - size) > 50) ? SysLog_High : SysLog_Medium
			, pSong
			, FileLoad_GetPos(pSong)
		    , "%sSample size %d reduced to %d"
		    , (pSample->Type & Smp_Type_16bit) ? "16-bit " : ""
		    , pSample->Size
		    , size
		    );
		pSample->Size = size;
	}

	if ((csize < 0) || (csize > 3))
	{
		SysLog_FileCorrupted
			( SysLog_High
			, pSong
			, FileLoad_GetPos(pSong)
		    , "%d unused bytes in packed sample"
		    , csize
		    );
	}
	if (csize) FileLoad_Skip(pSong, csize);

	return NULL;
}

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

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

	return err;
}

typedef struct _packed
{
	uint8_t    id;
	uint8_t    points[15][2];
	uint8_t    sustain;
	uint8_t    loop;
} LEnv;

// PVE [1 Byte : Count][Count * [[1 Byte : Id][15*2 Bytes: points][1 Byte : sustain]]
static const _kernel_oserror* Apply_VolumeEnvelope(SongHdr* pSong, Instrument* pInstr, unsigned int nr, const uint8_t* pVE)
{
	const _kernel_oserror* err = NULL;
	unsigned int max = *pVE++;
	unsigned int i, j, pos;
	LEnv* pLEnv;

	for (i = 0; i < max; i++, pVE += 33)
	{
		pLEnv = (LEnv*) pVE;

		if (pLEnv->id == nr)
		{
			Envelope* pEnv = &pInstr->VolumeEnvelope;
			if (pLEnv->sustain & 0x10) pEnv->Flags |= Envelope_Flag_Sustain;
			if (pLEnv->sustain & 0x20) pEnv->Flags |= Envelope_Flag_Loop;
			pEnv->LoopStart = pLEnv->loop & 0xf;
			pEnv->LoopEnd = pLEnv->loop >> 4;
			pEnv->SustainStart = pEnv->SustainEnd = pLEnv->sustain & 0xf;
			err = Loaders_Alloc(pSong, (void**) &pEnv->pTable, 15<<2);
			if (err) return err;
			pEnv->Points = 0;

			pos = 0;
			for (j = 0; j < 15; j++)
			{
				if (!pLEnv->points[j][0])
					break;
				pEnv->Points += 1;
				if (j) pos += pLEnv->points[j][0];
				pEnv->pTable[j] = pos + (pLEnv->points[j][1] << 18);
			}
			break;
		}
	}

	return NULL;
}

// PPE [1 Byte : Count][Count * [[1 Byte : Id][15*2 Bytes: points][1 Byte : sustain]]
static const _kernel_oserror* Apply_PanningEnvelope(SongHdr* pSong, Instrument* pInstr, unsigned int nr, const uint8_t* pPE)
{
	const _kernel_oserror* err = NULL;
	unsigned int max = *pPE++;
	unsigned int i, j, pos;
	LEnv* pLEnv;

	for (i = 0; i < max; i++, pPE += 33)
	{
		pLEnv = (LEnv*) pPE;

		if (pLEnv->id == nr)
		{
			Envelope* pEnv = &pInstr->PanningEnvelope;
			if (pLEnv->sustain & 0x10) pEnv->Flags |= Envelope_Flag_Sustain;
			if (pLEnv->sustain & 0x20) pEnv->Flags |= Envelope_Flag_Loop;
			pEnv->LoopStart = pLEnv->loop & 0xf;
			pEnv->LoopEnd = pLEnv->loop >> 4;
			pEnv->SustainStart = pEnv->SustainEnd = pLEnv->sustain & 0xf;
			err = Loaders_Alloc(pSong, (void**) &pEnv->pTable, 15<<2);
			if (err) return err;
			pEnv->Points = 0;
			pos = 0;
			for (j = 0; j < 15; j++)
			{
				if (!pLEnv->points[j][0])
					break;
				pEnv->Points += 1;
				if (j) pos += pLEnv->points[j][0];
				pEnv->pTable[j] = pos + (pLEnv->points[j][1] << 18);
			}
			break;
		}
	}

	return NULL;
}

// PVE [1 Byte : Count][Count * [[1 Byte : Id][15*2 Bytes: points][1 Byte : sustain]]
static const _kernel_oserror* Apply_PitchEnvelope(SongHdr* pSong, Instrument* pInstr, unsigned int nr, const uint8_t* pFE)
{
	const _kernel_oserror* err = NULL;
	unsigned int max = *pFE++;
	unsigned int i, j, pos;
	int val;
	LEnv* pLEnv;

	for (i = 0; i < max; i++, pFE += 33)
	{
		pLEnv = (LEnv*) pFE;

		if (pLEnv->id == nr)
		{
			Envelope* pEnv = &pInstr->PitchEnvelope;
			if (pLEnv->sustain & 0x10) pEnv->Flags |= Envelope_Flag_Sustain;
			if (pLEnv->sustain & 0x20) pEnv->Flags |= Envelope_Flag_Loop;
			pEnv->LoopStart = pLEnv->loop & 0xf;
			pEnv->LoopEnd = pLEnv->loop >> 4;
			pEnv->SustainStart = pEnv->SustainEnd = pLEnv->sustain & 0xf;
			err = Loaders_Alloc(pSong, (void**) &pEnv->pTable, 15<<2);
			if (err) return err;
			pEnv->Points = 0;

			pos = 0;
			for (j = 0; j < 15; j++)
			{
				if (!pLEnv->points[j][0])
					break;
				pEnv->Points += 1;
				if (j) pos += pLEnv->points[j][0];
				val = (pLEnv->points[j][1] -32) << 24;
				pEnv->pTable[j] = pos + (val >> 1);
			}
			break;
		}
	}

	return NULL;
}

const _kernel_oserror* Loader_MDL(SongHdr* pSong)
{
	const _kernel_oserror* err = NULL;
	SubSong*        pSubSong = Song_GetSubSong(pSong, 0);
	int             i, j, k;
	Pattern*        pPattern;
	Instrument*     pInst;
	Sample*         pSmp;
	uint32_t        val;
	LTrack*         pLTracks = NULL;
	LTrack*         pLTrack;
	uint32_t        FileSize, Tag, TagSize;
	uint32_t        FileOffset;
	uint16_t*       pTrackNrs = NULL;
	uint16_t*       pTrackNr;
	uint32_t        MaxTrackNr;
	uint8_t*        pVE = NULL;
	uint8_t*        pPE = NULL;
	uint8_t*        pFE = NULL;

	// is it a DSMF file?
	err = FileLoad_SetPos(pSong, 0);
	if (err) goto loader_err;
	err = FileLoad_ReadInt(pSong, &val, 4);
	if (err) goto loader_err;
	if (val != TAG(DMDL))
	{
		err = Loaders_NotThisType;
		goto loader_err;
	}

	FileSize = FileLoad_GetSize(pSong);

	err = FileLoad_ReadByte(pSong, &val);
	if (err) goto loader_err;
	pSong->Version = 100*(val >> 4) + 10*(val & 0xf);
	if (!pSong->Version) pSong->Version = 1;

	pSong->Flags = Song_Flag_LimitTremorToRow
	             | Song_Flag_RepeatRowNotN0Frames;
	pSong->MinPitch = NoteDelta;
	pSong->MaxPitch = NoteDelta + 12 * 10 - 1;
	pSong->MinTempo = 4;
	pSong->MaxTempo = 255;
	pSong->MinSpeed = 1;
	pSong->MaxSpeed = 255;
	// Instrument mapping to no sample behave as empty sample with volume 0
	pSong->pSampleNoMap->DefaultVolume = 0;
	pSong->pSampleNoMap->Type = 0;

	// ... file type ptr
	pSong->pType = Typestr_MDL;
	pSubSong->Defaults.VibratoType = vibrato_type_sin | vibrato_type_noretrig | vibrato_type_inverted;
	pSubSong->Defaults.TremoloType = vibrato_type_sin | vibrato_type_noretrig;

	// Read TAGS
	while (FileLoad_GetPos(pSong) < FileSize)
	{
		// Read TAG id & size
		err = Read_TagInfo(pSong, &Tag, &TagSize);
		if (err) goto loader_err;

		FileOffset = FileLoad_GetPos(pSong);

		if (Tag == TAG(IN))
		{
			ChannelDefault* pDef;

			if (pSubSong->SeqLen > 0)
			{
				err = Loaders_Error(pSong, FileOffset - 6, Unexpected_Block);
				goto loader_err;
			}

			if (TagSize < 91)
			{
				err = Loaders_Error(pSong, FileOffset - 6, Invalid_Block);
				goto loader_err;
			}

			err = FileLoad_ReadString(pSong, &pSong->pName, 32, false);
			if (!err) err = FileLoad_ReadString(pSong, &pSong->pAuthor, 20, false);
			if (!err) err = FileLoad_ReadInt(pSong, &pSubSong->SeqLen, 2);
			if (!err) err = FileLoad_ReadInt(pSong, &pSubSong->RestartPos, 2);
			if (!err) err = FileLoad_ReadByte(pSong, &pSubSong->Defaults.Volume); // [0-255]
			if (!err) err = FileLoad_ReadByte(pSong, &pSubSong->Defaults.Speed); // [1-255]
			if (!err) err = FileLoad_ReadByte(pSong, &pSubSong->Defaults.Tempo); // [4-255]
			if (err) goto loader_err;

			if (!pSubSong->SeqLen)
			{
				err = Loaders_InvalidSong(pSong, FileOffset + 52, &pSubSong->SeqLen, pSubSong->SeqLen);
				goto loader_err;
			}

			for (i = 0, pDef = pSubSong->Defaults.ChDef; i < 32; i++, pDef++)
			{
				err = FileLoad_ReadByte(pSong, &val);
				if (err) goto loader_err;
				if (val & 0x80)
					pDef->Mute = 1;
				else
					pSong->Channels = i + 1;
				val = (val & 0x7f) << 1;
				if (val >= 128) val++;
				pDef->Volume = 256;
				pDef->Panning = val;
			}

			if (TagSize < 91 + pSubSong->SeqLen + 8*pSong->Channels)
			{
				err = Loaders_Error(pSong, FileOffset - 6, Invalid_Block);
				goto loader_err;
			}

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

			for (i = 0; i < pSubSong->SeqLen; i++)
			{
				err = FileLoad_ReadByte(pSong, &val);
				if (err) goto loader_err;
				pSubSong->pSeqs[i] = val;
			}

			// Ignore channel names
		}
		else if (Tag == TAG(ME))
		{
			pSong->CommentsLen = TagSize;
			err = FileLoad_ReadString(pSong, &pSong->pComments, pSong->CommentsLen, false);
			if (err) goto loader_err;
		}
		else if (Tag == TAG(PN))
		{
			// Ignore pattern names
		}
		else if (Tag == TAG(PA))
		{
			if (pSong->Patterns > 0)
			{
				err = Loaders_Error(pSong, FileOffset - 6, Unexpected_Block);
				goto loader_err;
			}

			err = FileLoad_ReadByte(pSong, &pSong->Patterns);
			if (err) goto loader_err;

			if (!pSong->Patterns || (pSong->Patterns > 256)) // cf. sequence
			{
				err = Loaders_InvalidSong(pSong, FileOffset, &pSong->Patterns, pSong->Patterns);
				goto loader_err;
			}

			// Reserve space for trackmap
			err = Loaders_Alloc(pSong, (void**) &pTrackNrs, 2*32*pSong->Patterns);
			if (err) goto loader_err;
			memset(pTrackNrs, 0, 2*32*pSong->Patterns);

			for ( i = 0, pPattern = pSong->pPatterns, pTrackNr = pTrackNrs
			    ; i < pSong->Patterns
			    ; i++, pPattern++, pTrackNr += 32)
			{
				uint32_t channels;

				if (pSong->Version < 100)
				{
					channels = 32;
					pPattern->Rows = 64;
				}
				else
				{
					err = FileLoad_ReadByte(pSong, &channels);
					if (!err) err = FileLoad_ReadByte(pSong, &pPattern->Rows);
					if (!err) err = FileLoad_Skip(pSong, 16); // pattern name
					if (err) goto loader_err;
					pPattern->Rows++;
				}

				if (channels > 32)
				{
					err = Loaders_Error(pSong, -18, "Invalid nr of channels %d for pattern %d", channels, i);
					goto loader_err;
				}

				if (FileOffset + TagSize < FileLoad_GetPos(pSong) + 2*channels)
				{
					err = Loaders_Error(pSong, -1, "Block PA to short");
					goto loader_err;
				}

				if (channels)
				{
					err = FileLoad_Read(pSong, pTrackNr, 2*channels);
					if (err) goto loader_err;
				}
			}

			// Treat as soon as possible to free memory
			if (pLTracks != NULL)
			{
				for (i = 0; i < pSong->Patterns; i++)
				{
					err = Pattern_Decode(pSong, i, &pSong->pPatterns[i], pTrackNrs + 32*i, pLTracks, MaxTrackNr);
					if (err) goto loader_err;
				}

				Loaders_Free(pSong, pLTracks);
				Loaders_Free(pSong, pTrackNrs);
				pLTracks = NULL;
				pTrackNrs = NULL;
			}
		}
		else if (Tag == TAG(TR))
		{
			uint32_t flags, row;
			int trsize;

			if (pLTracks != NULL)
			{
				err = Loaders_Error(pSong, FileOffset - 6, Unexpected_Block);
				goto loader_err;
			}

			err = FileLoad_ReadInt(pSong, &MaxTrackNr, 2);
			if (err) goto loader_err;

			err = Loaders_Alloc(pSong, (void**) &pLTracks, MaxTrackNr*sizeof(*pLTracks));
			if (err) goto loader_err;
			memset(pLTracks, 0, MaxTrackNr*sizeof(*pLTracks));

			for (i = 0, pLTrack = pLTracks; i < MaxTrackNr; i++, pLTrack++)
			{
				// read track size
				err = FileLoad_ReadInt(pSong, &val, 2);
				if (err) goto loader_err;
				trsize = val;

				// read track
				pLTrack->fileoffset = FileLoad_GetPos(pSong);
				for (row = 0; trsize > 0;)
				{
					if (row >= 256)
					{
						err = Loaders_Error(pSong, -1, "Encoded pattern exceeds 256 rows");
						goto loader_err;
					}

					err = FileLoad_ReadByte(pSong, &flags);
					if (err) goto loader_err;
					trsize--;

					switch(flags & 3)
					{
						case 0:
						{
							if (row + 1 + (flags >> 2) >= 256)
							{
								err = Loaders_Error(pSong, -1, "Encoded pattern attempts to move past row 255");
								goto loader_err;
							}
							row += 1 + (flags >> 2);
						}
						break;
						case 1:
						{
							if (row == 0)
							{
								err = Loaders_Error(pSong, -1, "Encoded pattern starts with a repeat on first line");
								goto loader_err;
							}
							else if (row + 1 + (flags >> 2) >= 256)
							{
								err = Loaders_Error(pSong, -1, "Encoded pattern repeats past row 255");
								goto loader_err;
							}

							for (k = (flags >> 2); k >= 0; k--)
								pLTrack->row[row + k] = pLTrack->row[row - 1];
							row += 1 + (flags >> 2);
						}
						break;
						case 2:
						{
							if (row <= (flags >> 2))
							{
								err = Loaders_Error(pSong, -1, "Encoded pattern attempts to copy a row which is not defined yet");
								goto loader_err;
							}

							pLTrack->row[row] = pLTrack->row[flags >> 2];
							row++;
						}
						break;
						default:
						{
							if (flags & 0x4)
							{
								if (trsize > 0)
								{
									err = FileLoad_ReadByte(pSong, &val);
									if (err) goto loader_err;
									if (val == 255)
										pLTrack->row[row].note = Note_Fade;
									else if (val)
										pLTrack->row[row].note = val + NoteDelta - 1;
								}
								trsize--;
							}
							if (flags & 0x8)
							{
								if (trsize > 0)
								{
									err = FileLoad_ReadByte(pSong, &val);
									if (err) goto loader_err;
									pLTrack->row[row].inst = val;
								}
								trsize--;
							}
							if (flags & 0x10)
							{
								if (trsize > 0)
								{
									err = FileLoad_ReadByte(pSong, &val);
									if (err) goto loader_err;
									pLTrack->row[row].volume = val;
								}
								trsize--;
							}
							if (flags & 0x20)
							{
								if (trsize > 0)
								{
									err = FileLoad_ReadByte(pSong, &val);
									if (err) goto loader_err;
									pLTrack->row[row].eff = val;
								}
								trsize--;
							}
							if (flags & 0x40)
							{
								if (trsize > 0)
								{
									err = FileLoad_ReadByte(pSong, &val);
									if (err) goto loader_err;
									pLTrack->row[row].val1 = val;
								}
								trsize--;
							}
							if (flags & 0x80)
							{
								if (trsize > 0)
								{
									err = FileLoad_ReadByte(pSong, &val);
									if (err) goto loader_err;
									pLTrack->row[row].val2 = val;
								}
								trsize--;
							}
							row++;
						}
					}
				}
				if (trsize < 0)
				{
					err = Loaders_Error(pSong, -1, "Got past end of encoded pattern by %d bytes", -trsize);
					goto loader_err;
				}
				else if (trsize > 0)
				{
					err = Loaders_Error(pSong, -1, "Completed pattern decoding with %d extra bytes", trsize);
					goto loader_err;
				}
			}

			// Treat as soon as possible to free memory
			if (pTrackNrs != NULL)
			{
				for (i = 0; i < pSong->Patterns; i++)
				{
					err = Pattern_Decode(pSong, i, &pSong->pPatterns[i], pTrackNrs + 32*i, pLTracks, MaxTrackNr);
					if (err) goto loader_err;
				}

				Loaders_Free(pSong, pLTracks);
				Loaders_Free(pSong, pTrackNrs);
				pLTracks = NULL;
				pTrackNrs = NULL;
			}
		}
		else if (Tag == TAG(VE))
		{
			uint32_t stored;

			if (pVE != NULL)
			{
				err = Loaders_Error(pSong, FileOffset - 6, Unexpected_Block);
				goto loader_err;
			}

			err = Loaders_Alloc(pSong, (void**) &pVE, TagSize);
			if (!err) err = FileLoad_Read(pSong, pVE, TagSize);
			if (err) goto loader_err;

			stored = *pVE;
			if (TagSize < (1 + 33*stored))
			{
				SysLog_FileCorrupted(SysLog_High, pSong, FileOffset, "Block VE truncated");
				*pVE = (TagSize  - 1) / 33;
			}
		}
		else if (Tag == TAG(PE))
		{
			uint32_t stored;

			if (pPE != NULL)
			{
				err = Loaders_Error(pSong, FileOffset - 6, Unexpected_Block);
				goto loader_err;
			}

			err = Loaders_Alloc(pSong, (void**) &pPE, TagSize);
			if (!err) err = FileLoad_Read(pSong, pPE, TagSize);
			if (err) goto loader_err;

			stored = *pPE;
			if (TagSize < (1 + 33*stored))
			{
				SysLog_FileCorrupted(SysLog_High, pSong, FileOffset, "Block PE truncated");
				*pPE = (TagSize  - 1) / 33;
			}
		}
		else if (Tag == TAG(FE))
		{
			uint32_t stored;

			if (pFE != NULL)
			{
				err = Loaders_Error(pSong, FileOffset - 6, Unexpected_Block);
				goto loader_err;
			}

			err = Loaders_Alloc(pSong, (void**) &pFE, TagSize);
			if (!err) err = FileLoad_Read(pSong, pFE, TagSize);
			if (err) goto loader_err;

			stored = *pFE;
			if (TagSize < (1 + 33*stored))
			{
				SysLog_FileCorrupted(SysLog_High, pSong, FileOffset, "Block FE truncated");
				*pFE = (TagSize  - 1) / 33;
			}
		}
		else if (Tag == TAG(II))
		{
			uint32_t insts, samples, note;

			if (pSong->Instruments > 0)
			{
				err = Loaders_Error(pSong, FileOffset - 6, Unexpected_Block);
				goto loader_err;
			}

			err = FileLoad_ReadByte(pSong, &insts);
			if (err) goto loader_err;

			if (!insts)
			{
				err = Loaders_InvalidSong(pSong, FileOffset, &pSong->Instruments, insts);
				goto loader_err;
			}

			// Set instrument mode
			pSong->Flags |= Song_Flag_Instruments;

			// Read instruments definitions
			for(i = 0; i < insts; i++)
			{
				if (FileOffset + TagSize < FileLoad_GetPos(pSong) + 33)
				{
					SysLog_FileCorrupted(SysLog_High, pSong, FileOffset, "Block II cannot contain %d instruments", insts);
					break;
				}
				// read instrument nr
				err = FileLoad_ReadByte(pSong, &val);
				if (err) goto loader_err;
				if (!val)
				{
					err = Loaders_Error(pSong, -1, "Invalid inst nr %d", val);
					goto loader_err;
				}
				pInst = &pSong->pInstruments[val - 1];
				if (pSong->Instruments < val)
					pSong->Instruments = val;

				if (pInst->pNotesMap != NULL)
				{
					err = Loaders_Error(pSong, -1, "Instrument nr %d is already defined", val);
					goto loader_err;
				}
				err = Loaders_AllocMapTable(pSong, &pInst->pNotesMap);
				if (err) goto loader_err;
				// read nr of samples
				err = FileLoad_ReadByte(pSong, &samples);
				if (err) goto loader_err;
				// read instrument name
				err = FileLoad_ReadString(pSong, &pInst->pName, 32, false);
				if (err) goto loader_err;

				note = NoteDelta;

				if (samples > 1)
					SysLog_Log(SysLog_Low, "Multiple samples in instrument %d, some information may be lost", val);

				// Read samples definitions
				for(j = 0; j < samples; j++)
				{
					if (FileOffset + TagSize < FileLoad_GetPos(pSong) + 14)
					{
						SysLog_FileCorrupted(SysLog_High, pSong, FileOffset, "Block II cannot contain %d instruments", insts);
						break;
					}
					// read sample nr
					err = FileLoad_ReadByte(pSong, &val);
					if (err) goto loader_err;
					if (!val)
					{
						err = Loaders_Error(pSong, -1, "Invalid sample nr %d", val);
						goto loader_err;
					}
					pSmp = &pSong->pSamples[val - 1];

					// read sample max note
					err = FileLoad_ReadByte(pSong, &val);
					if (err) goto loader_err;
					if (val > 119)
					{
						err = Loaders_Error(pSong, -1, "Inst %d, Smp %d: Invalid note nr %d", i, j, val);
						goto loader_err;
					}
					val += NoteDelta;

					for (; (note <= val) && (note <= Note_Max); note++)
					{
						NoteMap* pn = pInst->pNotesMap + note;
						pn->Volume = 255;
						pn->SampleNr = 1 + (pSmp - pSong->pSamples);
						pn->Pitch = note << 8;
					}

					// read sample volume
					err = FileLoad_ReadByte(pSong, &pSmp->DefaultVolume);
					if (err) goto loader_err;
					// read sample volume envelope
					err = FileLoad_ReadByte(pSong, &val);
					if (err) goto loader_err;
					if (!j)
					{
						pInst->VolumeEnvelope.dummy[0] = val & 0x3f;
						pInst->VolumeEnvelope.Flags = (val & 0x80) ? Envelope_Flag_On : 0;
					}
					if (!(val & 0x40)) pSmp->DefaultVolume = 256;
					// read sample panning
					err = FileLoad_ReadByte(pSong, &pSmp->Panning);
					if (err) goto loader_err;
					pSmp->Panning <<= 1;
					if (pSmp->Panning > 127) pSmp->Panning++;
					// read sample panning envelope
					err = FileLoad_ReadByte(pSong, &val);
					if (err) goto loader_err;
					if (!j)
					{
						pInst->PanningEnvelope.dummy[0] = val & 0x3f;
						pInst->PanningEnvelope.Flags = (val & 0x80) ? Envelope_Flag_On : 0;
					}
					if (val & 0x40) pSmp->Type |= Smp_Type_Set_Panning;
					// read sample fadeout
					err = FileLoad_ReadInt(pSong, &val, 2);
					if (err) goto loader_err;
					pInst->FadeoutVolume = val;
					// read sample vibrato: speed, depth, sweep, type
					err = FileLoad_ReadByte(pSong, &val);
					if (err) goto loader_err;
					pInst->Vibrato.Speed = val;
					err = FileLoad_ReadByte(pSong, &val);
					if (err) goto loader_err;
					pInst->Vibrato.Depth = val << 1; // Twice finer as XM
					err = FileLoad_ReadByte(pSong, &val);
					if (err) goto loader_err;
					pInst->Vibrato.Rate = val;
					err = FileLoad_ReadByte(pSong, &val);
					if (err) goto loader_err;
					switch(val)
					{
						case 1: pInst->Vibrato.Type = vibrato_type_ramp; break;
						case 2: pInst->Vibrato.Type = vibrato_type_square; break;
						default: pInst->Vibrato.Type = vibrato_type_sin; break;
					}
					pInst->Vibrato.Type |= vibrato_type_sweep;
					// read dummy
					err = FileLoad_ReadByte(pSong, &val);
					if (err) goto loader_err;
					// read sample pitch envelope
					err = FileLoad_ReadByte(pSong, &val);
					if (err) goto loader_err;
					if (pFE && !j)
					{
						pInst->PitchEnvelope.dummy[0] = val & 0x3f;
						pInst->PitchEnvelope.Flags = (val & 0x80) ? Envelope_Flag_On : 0;
					}
				}
			}
		}
		else if (Tag == TAG(IS))
		{
			uint32_t samples, sample;
			err = FileLoad_ReadByte(pSong, &samples);
			if (err) goto loader_err;

			if (!samples)
			{
				err = Loaders_InvalidSong(pSong, FileOffset, &pSong->Samples, samples);
				goto loader_err;
			}

			// Read sample definitions
			for(i = 0; i < samples; i++)
			{
				if (  ((pSong->Version <  100) && (FileOffset + TagSize < FileLoad_GetPos(pSong) + 57))
				   || ((pSong->Version >= 100) && (FileOffset + TagSize < FileLoad_GetPos(pSong) + 59))
				   )
				{
					SysLog_FileCorrupted(SysLog_High, pSong, FileOffset, "Block IS cannot contain %d samples", samples);
					break;
				}

				// read sample nr
				err = FileLoad_ReadByte(pSong, &sample);
				if (err) goto loader_err;
				if (!sample)
				{
					err = Loaders_Error(pSong, -1, "Invalid sample nr %d", sample);
					goto loader_err;
				}
				pSmp = &pSong->pSamples[sample - 1];
				if (pSong->Samples < sample)
					pSong->Samples = sample;

				// read sample name
				err = FileLoad_ReadString(pSong, &pSmp->pName, 32, false);
				if (err) goto loader_err;
				// read sample filename
				err = FileLoad_ReadString(pSong, &pSmp->pFilename, 8, false);
				if (err) goto loader_err;
				// read sample frequency
				if (pSong->Version < 100)
					err = FileLoad_ReadInt(pSong, &val, 2);
				else
					err = FileLoad_ReadInt(pSong, &val, 4);
				if (err) goto loader_err;
				pSmp->Frequency = val;
				// read sample size
				err = FileLoad_ReadInt(pSong, &pSmp->Size, 4);
				if (err) goto loader_err;
				// read sample loop start
				err = FileLoad_ReadInt(pSong, &pSmp->LoopStart, 4);
				if (err) goto loader_err;
				// read sample loop size
				err = FileLoad_ReadInt(pSong, &pSmp->LoopEnd, 4);
				if (err) goto loader_err;
				if (pSmp->LoopEnd > 0)
				{
					pSmp->LoopEnd += pSmp->LoopStart;
					pSmp->Type |= Smp_Type_Loop;
				}
				// read dummy
				err = FileLoad_ReadByte(pSong, &val);
				// read flags
				if (!err) err = FileLoad_ReadByte(pSong, &val);
				if (err) goto loader_err;
				if (val & 0x01)
				{
					pSmp->Type |= Smp_Type_16bit;
					pSmp->Size >>= 1;
					pSmp->LoopStart >>= 1;
					pSmp->LoopEnd >>= 1;
				}
				if (val & 0x02) pSmp->Type |= Smp_Type_Loop_Bidi;
				pSmp->ScaleVolume = val >> 2; // use as tmp to store compression
				if ((val & 0xFC) > 0x8)
				{
					err = Loaders_Error(pSong, - 1, "Packing type %d not supported", val >> 2);
					goto loader_err;
				}
			}
		}
		else if (Tag == TAG(SA))
		{
			for (i = 0, pSmp = pSong->pSamples; i < pSong->Samples; i++, pSmp++)
			{
				if (pSmp->Size > 0)
				{
					switch(pSmp->ScaleVolume)
					{
						case 0:
						{
							err = FileLoad_ReadSample(pSong, pSmp);
							if (err) goto loader_err;
						}
						break;
						default:
						{
							err = Read_Sample(pSong, pSmp, FileOffset + TagSize - FileLoad_GetPos(pSong));
							if (err) goto loader_err;
						}
					}
				}
				pSmp->ScaleVolume = 256; // Restore default
			}
		}
		else
		{
			SysLog_FileCorrupted(SysLog_Medium, pSong, FileOffset - 6, Unexpected_Block);
		}

		FileLoad_SetPos(pSong, FileOffset + TagSize);
	}

	// Attach envelopes to instruments
	for (i = 0, pInst = pSong->pInstruments; i < pSong->Instruments; i++, pInst++)
	{
		if (pVE)
		{
			err = Apply_VolumeEnvelope(pSong, pInst, pInst->VolumeEnvelope.dummy[0], pVE);
			if (err) goto loader_err;
		}
		if (pPE)
		{
			err = Apply_PanningEnvelope(pSong, pInst, pInst->PanningEnvelope.dummy[0], pPE);
			if (err) goto loader_err;
		}
		if (pFE)
		{
			err = Apply_PitchEnvelope(pSong, pInst, pInst->PitchEnvelope.dummy[0], pFE);
			if (err) goto loader_err;
		}
	}

loader_err:
	Loaders_Free(pSong, pLTracks);
	Loaders_Free(pSong, pTrackNrs);
	Loaders_Free(pSong, pVE);
	Loaders_Free(pSong, pPE);
	Loaders_Free(pSong, pFE);

	return err;
}
