#include "Song.h"
#include "Effects.h"
#include "Instrument.h"
#include "Loaders.h"
#include "FSysLog.h"

/*
 * Channels: [1...32]
 * Patterns: [1...65535]
 * Rows    : [1...65535]
 * Orders  : [1...256]
 * Samples : [1...255]
 * Instr.  : 0
 * Tempo   : [1...999], T frames per 2.5 seconds.
 * Speed   : [1...31], S frames per row.
 * Notes   : [C-0...C-2...B-5] or [C-0...C-2...B-7]
 * Pitch   : Uses Amiga periods, base is 428 units, period is divided by 2 each octave up.
 *           Slides as x/1 or x/4 units per frame.
 * Volume  : [0...256...&FFF], linear or exponential.
 * Panning : [0...&FFF].
 * Sampling: 8363 Hz (frequency is turned into a relative tone).
 *
 * Envelopes (a succession of commands in fact) are not supported.
 * Effect 0xAC & 0xAD Extra fine pitch slide are pitch slides by 16 times finer,
 * which is not not the usual definition.
 * Sample, Note, Channel and Global volume can be amplified above 100% which we dont support.
 * Unimplemeted: Loops may start and end in different patterns. We keep them in same pattern.
 */

#define NoteDelta (Note_Central-12*4)

static const uint8_t Tag_GTK[]  = "GTK\0";
static const uint8_t Tag_GT2[]  = "GT2\0";
static const uint8_t Tag_TVOL[] = "TVOL";
static const uint8_t Tag_XCOM[] = "XCOM";
static const uint8_t Tag_TCN1[] = "TCN1";
static const uint8_t Tag_SONG[] = "SONG";
static const uint8_t Tag_PATS[] = "PATS";
static const uint8_t Tag_PATD[] = "PATD";
static const uint8_t Tag_PAIN[] = "PAIN";
static const uint8_t Tag_ORCH[] = "ORCH";
static const uint8_t Tag_INST[] = "INST";
static const uint8_t Tag_ASMP[] = "ASMP";
static const uint8_t Tag_SAMP[] = "SAMP";
static const uint8_t Tag_SAM2[] = "SAM2";
//static const uint8_t Tag_VENV[] = "VENV";
//static const uint8_t Tag_TENV[] = "TENV";
//static const uint8_t Tag_PENV[] = "PENV";
static const uint8_t Tag_ENDC[] = "ENDC";

static const uint8_t Typestr_GTK[] = "Graoumf Tracker (GTK)";
static const uint8_t Typestr_GT2[] = "Graoumf Tracker (GT2)";
static const char Invalid_Block[] = "Invalid block";
static const char Unexpected_Block[] = "Block not expected in that position";

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

typedef struct
{
	uint32_t	Channel;
	uint32_t	Note;
} ChannelInfo;

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

	if ((effect < 0x20) || (effect >= 0xa0))
	{
		switch(effect)
		{
			case 0:
				;
			break;
			case 1: // Pitch: Slide up, Memory
			{
				Pattern_AddEffect_Pitch(pSong, cmd_upmem1+flag_cmd_slide_frameN0, value << 2);
			}
			break;
			case 2: // Pitch: Slide down, Memory
			{
				Pattern_AddEffect_Pitch(pSong, cmd_downmem1+flag_cmd_slide_frameN0, value << 2);
			}
			break;
			case 3: // Pitch: Portamento, Memory
			{
				Pattern_AddEffect_Pitch(pSong, cmd_portamento+flag_cmd_slide_frameN0, value << 2);
			}
			break;
			case 4: // Pitch: Vibrato at speed x and depth y, Memory
			{
				uint32_t speed = value >> 4;
				value &= 0xf;
				// Vibrate between [-2*y,+2*y]
				// 4 * ((it vibrato * 2) - 1)
				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_frameN0, value);
			}
			break;
			case 5: // Pitch: Vibrato 0 - Pitch: Portamento
			{
				convert_effect(pSong, pChInfo, 0x4, 0);
				convert_effect(pSong, pChInfo, 0x3, value);
			}
			break;
			case 6: // Pitch: Portamento - Pitch: Vibrato
			{
				convert_effect(pSong, pChInfo, 0x3, 0);
				convert_effect(pSong, pChInfo, 0x4, value);
			}
			break;
			case 7: // Volume: Vibrato (alias Tremolo)
			{
				uint32_t speed = value >> 4;
				value &= 0xf;
				// Vibrate between [-4*y,+4*y]
				value <<= 4; // << 2 for range conversion and << 2 for depth
				if (speed) Pattern_AddEffect_NoteVolume(pSong, cmd_vibrato_speed, speed << 2);
				Pattern_AddEffect_NoteVolume(pSong, cmd_vibrato_depth | flag_cmd_vibrato_frameN0, value);
			}
			break;
			case 8: // Pitch: Detune xy = finetune(x - y) in 1/8 semitone
			{
				int svalue = ((value & 0xf0) >> 4) - (value & 0xf);

				if (pChInfo->Note) Pattern_AddEffect_Instrument(pSong, cmd_sample_set_finetune, svalue << 5);
			}
			break;
			case 9: // Note: Delay for x ticks
			{
				Pattern_AddEffect_Instrument(pSong, cmd_sample_note_delay, value);
			}
			break;
			case 0xa: // Note: Cut note after x ticks
			{
				Pattern_AddEffect_NoteVolume(pSong, cmd_volume_cut, value);
			}
			break;
			case 0xb: // Global: Jump
			{
				Pattern_AddGlbEffect_Position(pSong, gcmd_pos_jump, value);
			}
			break;
			case 0xc: //  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;
				}
				Pattern_AddEffect_Pitch(pSong, cmd_vibrato_type, value);
			}
			break;
			case 0xd: // Global: Break, decimal
			{
				uint32_t ten = value >> 4;
				value &= 0xf;
				if ((value > 9) || (ten > 9))
				{
					SysLog_BadEffectValue(pSong, 0, oeffect, ovalue);
					value = 0; // Invalid value is treated as 0
				}
				else
				{
					value += ten * 10;
				}
				Pattern_AddGlbEffect_Position(pSong, gcmd_pos_break, value);
			}
			break;
			case 0xe: //  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;
				}
				Pattern_AddEffect_NoteVolume(pSong, cmd_vibrato_type, value);
			}
			break;
			case 0xf: // Global: Set speed/tempo
			{
				// if 0 stop (restart) song, test removed cf multiple F on same row
				if (value)
				{
					if (value <= pSong->MaxSpeed)
					{
						// effect_f_test_speed
						Pattern_AddGlbEffect_Frames(pSong, gcmd_frames_set, value);
						// forces tempo to 125
						Pattern_AddGlbEffect_Tempo(pSong, cmd_set, 125);
					}
					else
					{
						// effect_f_tempo
						Pattern_AddGlbEffect_Tempo(pSong, cmd_set, value);
					}
				}
			}
			break;
			case 0x10: // Pitch: Arpeggio
			{
				if (value)
				{
					uint32_t val;

					val = value >> 4;
					value &= 0xf;
					Pattern_AddEffect_Arpeggio(pSong, value, val, cmd_arpeggio_type_NHL);
				}
			}
			break;
			case 0x11: // Pitch: Slide fine up, Memory
			{
				Pattern_AddEffect_Pitch(pSong, cmd_upmem1+flag_cmd_slide_frame0, value << 2);
			}
			break;
			case 0x12: // Pitch: Slide fine down, Memory
			{
				Pattern_AddEffect_Pitch(pSong, cmd_downmem1+flag_cmd_slide_frame0, value << 2);
			}
			break;
			case 0x13: // Note: Retrig note every y ticks with volume change type x, Memory
			{
				uint32_t eff = 0;

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

				// Memory only if xy = 00
				if (value && !eff) eff = 8;
				if (!value && eff) value = 0xff; // should be infinite
				Pattern_AddEffect_RetrigCounter(pSong, cmd_retrig_count_type_S3M, value);
				Pattern_AddEffect_Instrument(pSong, cmd_sample_note_retrig, eff);
			}
			break;
			case 0x14: // Volume: Slide up, Memory
			{
				Pattern_AddEffect_NoteVolume(pSong, cmd_upmem1+flag_cmd_slide_frameN0, value);
			}
			break;
			case 0x15: // Volume: Slide down, Memory
			{
				Pattern_AddEffect_NoteVolume(pSong, cmd_downmem1+flag_cmd_slide_frameN0, value);
			}
			break;
			case 0x16: // Volume: Exp Slide up, Memory (doubled every 0x100?)
			{
				Pattern_AddEffect_NoteVolume(pSong, cmd_volume_upmem1_exp+flag_cmd_slide_frameN0, value);
			}
			break;
			case 0x17: // Volume: Exp Slide down, Memory (halved every 0x100?)
			{
				Pattern_AddEffect_NoteVolume(pSong, cmd_volume_downmem1_exp+flag_cmd_slide_frameN0, value);
			}
			break;
			case 0x18: // Pitch: Portamento 0 - Volume: Slide up, Memory
			{
				convert_effect(pSong, pChInfo, 0x3, 0);
				convert_effect(pSong, pChInfo, 0x14, value);
			}
			break;
			case 0x19: // Pitch: Portamento 0 - Volume: Slide down, Memory
			{
				convert_effect(pSong, pChInfo, 0x3, 0);
				convert_effect(pSong, pChInfo, 0x15, value);
			}
			break;
			case 0x1a: // Pitch: Portamento 0 - Volume: Exp Slide up, Memory
			{
				convert_effect(pSong, pChInfo, 0x3, 0);
				convert_effect(pSong, pChInfo, 0x16, value);
			}
			break;
			case 0x1b: // Pitch: Portamento 0 - Volume: Exp Slide down, Memory
			{
				convert_effect(pSong, pChInfo, 0x3, 0);
				convert_effect(pSong, pChInfo, 0x17, value);
			}
			break;
			case 0x1c: // Pitch: Vibrato 0 - Volume: Slide up, Memory
			{
				convert_effect(pSong, pChInfo, 0x4, 0);
				convert_effect(pSong, pChInfo, 0x14, value);
			}
			break;
			case 0x1d: // Pitch: Vibrato 0 - Volume: Slide down, Memory
			{
				convert_effect(pSong, pChInfo, 0x4, 0);
				convert_effect(pSong, pChInfo, 0x15, value);
			}
			break;
			case 0x1e: // Pitch: Vibrato 0 - Volume: Exp Slide up, Memory
			{
				convert_effect(pSong, pChInfo, 0x4, 0);
				convert_effect(pSong, pChInfo, 0x16, value);
			}
			break;
			case 0x1f: // Pitch: Vibrato 0 - Volume: Exp Slide down, Memory
			{
				convert_effect(pSong, pChInfo, 0x4, 0);
				convert_effect(pSong, pChInfo, 0x17, value);
			}
			break;
			case 0xa0: // Global Volume: Slide up, Memory
			{
				Pattern_AddGlbEffect_Volume(pSong, cmd_upmem1+flag_cmd_slide_frameN0, value);
			}
			break;
			case 0xa1: // Global Volume: Slide down, Memory
			{
				Pattern_AddGlbEffect_Volume(pSong, cmd_downmem1+flag_cmd_slide_frameN0, value);
			}
			break;
			case 0xa4: // Volume: Slide fine up, Memory
			{
				Pattern_AddEffect_NoteVolume(pSong, cmd_upmem1+flag_cmd_slide_frame0, value);
			}
			break;
			case 0xa5: // Volume: Slide fine down, Memory
			{
				Pattern_AddEffect_NoteVolume(pSong, cmd_downmem1+flag_cmd_slide_frame0, value);
			}
			break;
			case 0xa6: // Global Volume: Slide fine up, Memory
			{
				Pattern_AddGlbEffect_Volume(pSong, cmd_upmem1+flag_cmd_slide_frame0, value);
			}
			break;
			case 0xa7: // Global Volume: Slide fine down, Memory
			{
				Pattern_AddGlbEffect_Volume(pSong, cmd_downmem1+flag_cmd_slide_frame0, value);
			}
			break;
			case 0xa8: // Global: Set Speed
			{
				if (value <= pSong->MaxSpeed)
					Pattern_AddGlbEffect_Frames(pSong, gcmd_frames_set, value);
				else
					SysLog_BadEffectValue(pSong, 0, oeffect, ovalue);
			}
			break;
			case 0xa9: // Global: Set Tempo fraction ( x/256 of tempo unit)
			{
				SysLog_SkipEffect(pSong, 0, oeffect, ovalue);
			}
			break;
			case 0xaa: // Global: Delay row for x rows
			{
				Pattern_AddGlbEffect_Frames(pSong, gcmd_frames_repeat, value);
			}
			break;
			case 0xab: // Pitch: Portamento extra fine , Memory
			{
				Pattern_AddEffect_Pitch(pSong, cmd_portamento+flag_cmd_slide_frame0, value);
			}
			break;
			case 0xac: // Pitch: Slide extra fine up, Memory
			{
				// It's 16x finer, we approximate by 4x finer but on frame 0 only
				Pattern_AddEffect_Pitch(pSong, cmd_upmem1+flag_cmd_slide_frame0, value);
			}
			break;
			case 0xad: // Pitch: Slide extra fine down, Memory
			{
				// It's 16x finer, we approximate by 4x finer but on frame 0 only
				Pattern_AddEffect_Pitch(pSong, cmd_downmem1+flag_cmd_slide_frame0, value);
			}
			break;
			case 0xae: // Panning: Slide left, Memory
			{
				Pattern_AddEffect_Panning(pSong, cmd_downmem1+flag_cmd_slide_frameN0, (value + 15) >> 4);
			}
			break;
			case 0xaf: // Panning: Slide right, Memory
			{
				Pattern_AddEffect_Panning(pSong, cmd_upmem1+flag_cmd_slide_frameN0, (value + 15) >> 4);
			}
			break;
			case 0xb0: // Volume: Tremor
			{
				uint32_t val;

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

				Pattern_AddEffect_NoteVolume(pSong, cmd_volume_tremor_S3M, Effect_Tremor(value, val));
			}
			break;
			case 0xb1: // Global: Loop in column
			{
				Pattern_AddGlbEffect_Position(pSong, gcmd_pos_loop | value, pChInfo->Channel);
			}
			break;
			case 0xb2: // Set flags
				;
			break;
			case 0xb3: // Set Volume Envelope Nr
			case 0xb4: // Set Tone Envelope Nr
			case 0xb5: // Set Panning Envelope Nr
			{
				SysLog_SkipEffect(pSong, 0, oeffect, ovalue);
			}
			break;
			case 0xb6: // Set Volume Envelope Nr + Key Off
			case 0xb7: // Set Tone Envelope Nr + Key Off
			case 0xb8: // Set Panning Envelope Nr + Key Off
			{
				convert_effect(pSong, pChInfo, 0x0a, 0);
				SysLog_SkipEffect(pSong, 0, oeffect, ovalue);
			}
			break;
			case 0xb9: // Demo synchro
				;
			break;
			case 0xba: // Note: Set sample offset, fine
			{
				if (value)
				{
					// define offset
					Pattern_AddEffect_Instrument(pSong, cmd_sample_set_offset, 0x000 | ((value && 0xf) << 4));
					// set offset byte 1
					Pattern_AddEffect_Instrument(pSong, cmd_sample_set_offset, 0x100 | (value >> 4));
					// set offset byte 2
					Pattern_AddEffect_Instrument(pSong, cmd_sample_set_offset, 0x200 | 0);
				}

				// only if a note is defined at the same time
				if (pChInfo->Note) Pattern_AddEffect_Instrument(pSong, cmd_sample_set_offset, 0xf00);
			}
			break;
			case 0xbb: // Note: Set sample offset, extra fine
			{
				if (value)
				{
					// define offset
					Pattern_AddEffect_Instrument(pSong, cmd_sample_set_offset, 0x000 | value);
					// set offset byte 1
					Pattern_AddEffect_Instrument(pSong, cmd_sample_set_offset, 0x100 | 0);
					// set offset byte 2
					Pattern_AddEffect_Instrument(pSong, cmd_sample_set_offset, 0x200 | 0);
				}

				// only if a note is defined at the same time
				if (pChInfo->Note) Pattern_AddEffect_Instrument(pSong, cmd_sample_set_offset, 0xf00);
			}
			break;
			case 0xbc: // Note: Increase sample offset
			case 0xbd: // Note: Decrease sample offset
			case 0xbe: // Auto tempo instrument in x lines
			case 0xbf: // Auto pitch instrument in x lines
			{
				SysLog_SkipEffect(pSong, 0, oeffect, ovalue);
			}
			break;
			case 0xc2: // Channel Volume: Set [0x10 is 100%]
			{
				value <<= 4;
				if (value > 256)
				{
					SysLog_SkipEffectValue(pSong, 0, oeffect, ovalue);
					value = 256;
				}
				Pattern_AddEffect_ChannelVolume(pSong, cmd_set, value);
			}
			break;
			case 0xc3: // Channel Volume: Set Exp [0xc0 is 100%]
			{
				// Linear volume = 0x100*2^((x-0xC0)/0x10) + .5
				value = Convert_ToneToFrac((Note_Central<<8) + 12*0x10*(value - 0xC0)) >> 8;
				if (value > 256)
				{
					SysLog_SkipEffectValue(pSong, 0, oeffect, ovalue);
					value = 256;
				}
				Pattern_AddEffect_ChannelVolume(pSong, cmd_set, value);
			}
			break;
			case 0xc4: // Channel Volume: Slide Up
			{
				Pattern_AddEffect_ChannelVolume(pSong, cmd_upmem1+flag_cmd_slide_frameN0, value);
			}
			break;
			case 0xc5: // Channel Volume: Slide Down
			{
				Pattern_AddEffect_ChannelVolume(pSong, cmd_downmem1+flag_cmd_slide_frameN0, value);
			}
			break;
			case 0xc6: // Channel Volume: Slide Up Exp
			{
				Pattern_AddEffect_ChannelVolume(pSong, cmd_volume_upmem1_exp+flag_cmd_slide_frameN0, value);
			}
			break;
			case 0xc7: // Channel Volume: Slide Down Exp
			{
				Pattern_AddEffect_ChannelVolume(pSong, cmd_volume_downmem1_exp+flag_cmd_slide_frameN0, value);
			}
			break;
			case 0xc8: // Channel Volume: Slide Up fine Exp
			{
				Pattern_AddEffect_ChannelVolume(pSong, cmd_volume_upmem1_exp+flag_cmd_slide_frame0, value);
			}
			break;
			case 0xc9: // Channel Volume: Slide Down fine Exp
			{
				Pattern_AddEffect_ChannelVolume(pSong, cmd_volume_downmem1_exp+flag_cmd_slide_frame0, value);
			}
			break;
			default:
			{
				SysLog_BadEffect(pSong, 0, oeffect, ovalue);
			}
		}
	}
	else
	{
		value |= (effect & 0xf) << 8;
		effect >>= 4;

		switch(effect)
		{
			case 0x2: // Volume: Set [0-FFF, 100 is 100%]
			{
				if (value > 256)
				{
					SysLog_SkipEffectValue(pSong, 0, oeffect, ovalue);
					value = 256;
				}
				Pattern_AddEffect_NoteVolume(pSong, cmd_set, value);
			}
			break;
			case 0x3: // Volume: Set Exp [0-FFF]
			{
				// Linear volume = 0x100*2^((x-0x800)/0x100) + .5
				value = Convert_ToneToFrac((Note_Central<<8) + 12*(value - 0x800)) >> 8;
				if (value > 256)
				{
					SysLog_SkipEffectValue(pSong, 0, oeffect, ovalue);
					value = 256;
				}
				Pattern_AddEffect_NoteVolume(pSong, cmd_set, value);
			}
			break;
			case 0x4: // Panning: Set [0-FFF]
			{
				Pattern_AddEffect_Panning(pSong, cmd_set, value >> 4);
			}
			break;
			case 0x5: // Global Volume: Set [0-FFF]
			{
				if (value > 256)
				{
					SysLog_SkipEffectValue(pSong, 0, oeffect, ovalue);
					value = 256;
				}
				Pattern_AddGlbEffect_Volume(pSong, cmd_set, value);
			}
			break;
			case 0x6: // Global Volume: Set Exp [0-FFF]
			{
				// Linear volume = 0x1000*2^((x-0x1000)/0x155) + .5
				value = Convert_ToneToFrac((Note_Central<<8) + (12*0x100*(value - 0x1000)/0x155)) >> 4;
				if (value > 256)
				{
					SysLog_SkipEffectValue(pSong, 0, oeffect, ovalue);
					value = 256;
				}
				Pattern_AddGlbEffect_Volume(pSong, cmd_set, value);
			}
			break;
			case 0x7: // Note: Retrig note every x ticks with a max of yy repeats, Memory
			{
				// Max repeats is not supported

				if (value & 0xff)
					SysLog_SkipEffectValue(pSong, 0, oeffect, ovalue);

				value = (value & 0xf00) >> 8;
				Pattern_AddEffect_RetrigCounter(pSong, cmd_retrig_count_type_S3M, value);
				Pattern_AddEffect_Instrument(pSong, cmd_sample_note_retrig, 8);
			}
			break;
			case 0x8: // Panning: Set - Note: Retrig note every y ticks with volume change type x, Memory
			{
				uint32_t eff = 0;

				eff = value >> 8;
				eff = eff | (eff << 4);
				if (eff >= 128) eff++;
				Pattern_AddEffect_Panning(pSong, cmd_set, eff);

				eff = (value & 0xf0) >> 4;
				value &= 0xf;
				// Memory only if xy = 00
				if (value && !eff) eff = 8;
				if (!value && eff) value = 0xff; // should be infinite
				Pattern_AddEffect_RetrigCounter(pSong, cmd_retrig_count_type_S3M, value);
				Pattern_AddEffect_Instrument(pSong, cmd_sample_note_retrig, eff);
			}
			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 (pChInfo->Note) Pattern_AddEffect_Instrument(pSong, cmd_sample_set_offset, 0xf00);
			}
			break;
			default:
			{
				SysLog_BadEffect(pSong, 0, oeffect, ovalue);
			}
		}
	}
}

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_ReadReverseInt(pSong, pTagSize, 4);

	if (!err
	&&  (*pTagSize < 8))
	{
		err = Loaders_Error(pSong, -8, Invalid_Block);
	}
	else *pTagSize = *pTagSize - 8; // Size includes header

	return err;
}

static const _kernel_oserror* Pattern_GTK(SongHdr* pSong, uint32_t i, Pattern* pPattern, uint32_t channels, const uint8_t* pPos)
{
	const _kernel_oserror* err = NULL;
	int             j, k;
	int             effect, value;
	uint32_t        Note, Inst;
	ChannelInfo     ChInfo;
	ChannelInfo*    pChInfo = &ChInfo;

	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 < channels; k++)
		{
			// Read note
			Note = *pPos++;
			if (Note)
			{
				if ((Note >= 24) && (Note < 84))
					Note += NoteDelta;
				else
				{
					SysLog_BadNote(pSong, 0, k, Note);
					Note = 0;
				}
			}
			// Read instrument
			Inst = *pPos++;

			err = Loaders_StartChannel(pSong, k, Note, Inst);
			if (err) return err;

			pChInfo->Note = (Note < Note_CmdMin) ?  Note : 0;
			pChInfo->Channel = k;

			// Read effect and value
			effect = *pPos++;
			value = *pPos++;
			convert_effect(pSong, pChInfo, effect, value);

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

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

	return Loaders_EndPattern(pSong, pPattern);
}

static const _kernel_oserror* Pattern_GT2(SongHdr* pSong, uint32_t i, Pattern* pPattern, uint32_t channels, const uint8_t* pPos)
{
	const _kernel_oserror* err = NULL;
	int             j, k;
	int             effect, value;
	uint32_t        Note, Inst;
	ChannelInfo     ChInfo;
	ChannelInfo*    pChInfo = &ChInfo;

	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 < channels; k++)
		{
			// Read note
			Note = *pPos++;
			if (Note)
			{
				if ((Note >= 24) && (Note < 128))
				{
					Note += NoteDelta;
					if (Note > 127) Note = 127;
				}
				else
				{
					SysLog_BadNote(pSong, 0, k, Note);
					Note = 0;
				}
			}
			// Read instrument
			Inst = *pPos++;

			err = Loaders_StartChannel(pSong, k, Note, Inst);
			if (err) return err;

			pChInfo->Note = (Note < Note_CmdMin) ?  Note : 0;
			pChInfo->Channel = k;

			// Read effect and value
			effect = *pPos++;
			value = *pPos++;
			convert_effect(pSong, pChInfo, effect, value);

			// Read volume value
			value = *pPos++;
			if (value) Pattern_AddEffect_NoteVolume(pSong, cmd_set, value);

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

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

	return Loaders_EndPattern(pSong, pPattern);
}

const _kernel_oserror* Loader_GT2(SongHdr* pSong)
{
	const _kernel_oserror* err = NULL;
	SubSong*        pSubSong = Song_GetSubSong(pSong, 0);
	uint8_t*        pLTrack = NULL;
	uint32_t        val;
	int             i, j;
	Pattern*        pPattern;
	Sample*         pSmp;
	Instrument*     pInst;
	ChannelDefault* pDef;
	uint32_t        Tag, TagSize;
	uint32_t        FileOffset;
	uint32_t        FileSize = FileLoad_GetSize(pSong);
	uint32_t        curPatt = 0;
	uint32_t        curInst = 1;
	uint32_t        curSmp = 1;
	uint32_t        size;

	// is it a GT2 file?
	err = FileLoad_SetPos(pSong, 0);
	if (err) goto loader_err;
	err = FileLoad_ReadInt(pSong, &Tag, 3);
	if (err) goto loader_err;

	if (Tag != TAG(GT2))
	{
		err = Loaders_NotThisType;
		goto loader_err;
	}

	err = FileLoad_ReadByte(pSong, &val);
	if (err) goto loader_err;
	pSong->Version = val * 100;
	err = FileLoad_ReadReverseInt(pSong, &size, 4);
	if (err) goto loader_err;

	pSong->Flags = Song_Flag_Instruments;
	pSong->MinPitch = NoteDelta + 24;
	pSong->MaxPitch = NoteDelta + 127;
	pSong->MinTempo = 1;
	pSong->MaxTempo = 999;

	// ... file type ptr
	pSong->pType = Typestr_GT2;

	pSong->CommentsLen = 160;
	err = FileLoad_ReadString(pSong, &pSong->pName, 32, false);
	if (!err) err = FileLoad_ReadString(pSong, &pSong->pComments, 160, false);
	if (!err) err = FileLoad_Skip(pSong, 28); // Date and Tracker name
	if (size >= 236)
	{
		if (!err) err = FileLoad_ReadReverseInt(pSong, &pSubSong->Defaults.Speed, 2);
		if (!err) err = FileLoad_ReadReverseInt(pSong, &pSubSong->Defaults.Tempo, 2);
		if (!err) err = FileLoad_ReadReverseInt(pSong, &val, 2); // Master Volume, ignore
		if (!err) err = FileLoad_ReadReverseInt(pSong, &pSong->Channels, 2);
		if (err) goto loader_err;

	    if (!pSong->Channels || (pSong->Channels > 32))
	    {
			err = Loaders_InvalidSong(pSong, 234, &pSong->Channels, pSong->Channels);
			goto loader_err;
	    }

    	if (size < 236 + 2*pSong->Channels)
	    {
			err = Loaders_Error(pSong, 4, Invalid_Block);
			goto loader_err;
    	}

		for (i = 0, pDef = pSubSong->Defaults.ChDef; i < pSong->Channels; i++, pDef++)
		{
			err = FileLoad_ReadReverseInt(pSong, &val, 2);
			if (err) goto loader_err;
			if (val < 0x1000)
				pDef->Panning = val >> 4;
			else
				pDef->Panning = 0x80;
		}
	}
	else
	{
    	if (size < 228)
	    {
			err = Loaders_Error(pSong, 4, Invalid_Block);
			goto loader_err;
    	}
	}

	// 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(TVOL))
		{
			err = FileLoad_ReadReverseInt(pSong, &size, 2);
			if (err) goto loader_err;

			if ((TagSize < 2 + 2*size)
			||  (size > pSong->Channels))
			{
				err = Loaders_Error(pSong, FileOffset - 4, Invalid_Block);
				goto loader_err;
			}

			for (i = 0, pDef = pSubSong->Defaults.ChDef; i < size; i++, pDef++)
			{
				err = FileLoad_ReadReverseInt(pSong, &val, 2);
				if (err) goto loader_err;
				if (val <= 0x1000)
					pDef->Volume = val >> 4;
				else
					pDef->Volume = 256;
			}
		}

		else if (Tag == TAG(XCOM))
			; // Extra comments
		else if (Tag == TAG(TCN1))
			; // Tracker config
		else if (Tag == TAG(SONG))
		{
			if (pSubSong->SeqLen)
			{
				err = Loaders_Error(pSong, FileOffset - 8, Unexpected_Block);
				goto loader_err;
			}

			// Nr of Orders
			err = FileLoad_ReadReverseInt(pSong, &pSubSong->SeqLen, 2);
			if (!err) err = FileLoad_ReadReverseInt(pSong, &pSubSong->RestartPos, 2);
			if (err) goto loader_err;

			if (TagSize != (4 + 2 * pSubSong->SeqLen))
			{
				err = Loaders_InvalidSong(pSong, FileOffset + 8, &pSubSong->SeqLen, pSubSong->SeqLen);
				goto loader_err;
			}

			// Sequence
			err = Loaders_AllocSeq(pSong, pSubSong);
			if (err) goto loader_err;
			for (i = 0; i < pSubSong->SeqLen; i++)
			{
				err = FileLoad_ReadReverseInt(pSong, &val, 2);
				pSubSong->pSeqs[i] = val;
				if (err) goto loader_err;
			}
		}
		else if (Tag == TAG(PATS))
		{
			if (pSong->Patterns)
			{
				err = Loaders_Error(pSong, FileOffset - 8, Unexpected_Block);
				goto loader_err;
			}

			if (TagSize != 0x4)
			{
				err = Loaders_Error(pSong, FileOffset - 8, Invalid_Block);
				goto loader_err;
			}

			// Nr of channels
			err = FileLoad_ReadReverseInt(pSong, &pSong->Channels, 2);
			if (err) goto loader_err;

		    if (!pSong->Channels || (pSong->Channels > 32))
		    {
				err = Loaders_InvalidSong(pSong, FileOffset + 4, &pSong->Channels, pSong->Channels);
				goto loader_err;
		    }

			// Nr of patterns
			err = FileLoad_ReadReverseInt(pSong, &pSong->Patterns, 2);
			if (err) goto loader_err;

			if (!pSong->Patterns
			|| (pSong->Patterns > 1024))
			{
				err = Loaders_InvalidSong(pSong, FileOffset + 6, &pSong->Patterns, pSong->Patterns);
				goto loader_err;
			}
		}
		else if (Tag == TAG(PATD))
		{
			uint32_t channels;

			if (!pSong->Patterns || !pSong->Channels)
			{
				err = Loaders_Error(pSong, FileOffset - 8, Unexpected_Block);
				goto loader_err;
			}

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

			// Read pattern number
			err = FileLoad_ReadReverseInt(pSong, &val, 2);
			if (err) goto loader_err;
			if ((val >= pSong->Patterns)
			||  (val != curPatt))
			{
				err = Loaders_Error(pSong, -2, "Invalid pattern nr (%d)", val);
				goto loader_err;
			}
			pPattern = &pSong->pPatterns[curPatt];

			err = FileLoad_Skip(pSong, 16); // Pattern name
			if (!err) err = FileLoad_ReadReverseInt(pSong, &val, 2);
			if (err) goto loader_err;
			if (val)
			{
//				err = Loaders_Error(pSong, -2, "Invalid pattern (%d) encoding %d", curPatt, val);
//				goto loader_err;
			}
			err = FileLoad_ReadReverseInt(pSong, &pPattern->Rows, 2);
			if (!err) err = FileLoad_ReadReverseInt(pSong, &channels, 2);
			if (err) goto loader_err;

			size = pPattern->Rows * channels * 5;

			if (TagSize < (24 + size))
			{
				err = Loaders_Error(pSong, FileOffset - 8, Invalid_Block);
				goto loader_err;
			}

			if (!pPattern->Rows || (pPattern->Rows > song_max_rows))
			{
				err = Loaders_InvalidSong(pSong, -4, &pPattern->Rows, pPattern->Rows);
				goto loader_err;
			}

			err = Loaders_Alloc(pSong, (void**) &pLTrack, size);
			if (err) goto loader_err;
			err = FileLoad_Read(pSong, pLTrack, size);
			if (err) goto loader_err;

			err = Pattern_GT2(pSong, curPatt, pPattern, channels, pLTrack);
			if (err) goto loader_err;

			Loaders_Free(pSong, pLTrack);
			pLTrack = NULL;
			curPatt++;
		}
		else if (Tag == TAG(PAIN))
			; // Pattern extension?
		else if (Tag == TAG(ORCH))
			; // Nr of Instruments saved (i.e. total minus gaps)
		else if (Tag == TAG(INST))
		{
			if (TagSize < 308)
			{
				err = Loaders_Error(pSong, FileOffset - 4, Invalid_Block);
				goto loader_err;
			}

			// Read instrument number
			err = FileLoad_ReadReverseInt(pSong, &val, 2);
			if (err) goto loader_err;
			if (!val
			||  (val > 255)
			||  (val < curInst))
			{
				err = Loaders_Error(pSong, -2, "Invalid instrument nr (%d)", val);
				goto loader_err;
			}

			pSong->Instruments = val; // Till next bock

			// Complete gaps
			curInst = val;

			pInst = &pSong->pInstruments[curInst-1];
			curInst++;

			// Read instrument definition
			err = FileLoad_ReadString(pSong, &pInst->pName, 28, false);
			if (err) goto loader_err;
			err = FileLoad_ReadReverseInt(pSong, &val, 2);
			if (err) goto loader_err;
			if (val)
			{
				err = Loaders_Error(pSong, -2, "Invalid instrument (%d) encoding %d", curInst, val);
				goto loader_err;
			}

			pInst->Flags = 0;
			// Volume
			uint32_t inst_volume;
			err = FileLoad_ReadReverseInt(pSong, &inst_volume, 2);
			if (err) goto loader_err;
			if (inst_volume > 256)
			{
				if (val < 0x1000)
					SysLog_FileCorrupted(SysLog_Medium, pSong, -2, "Instrument (%d), volume boost (%d) not supported", curInst, val);
				inst_volume = 256;
			}

			// Panning
			err = FileLoad_ReadReverseInt(pSong, &val, 2);
			if (err) goto loader_err;
			if (val <= 0x1000)
			{
				pInst->Flags |= Inst_Flag_Set_Panning;
				pInst->Panning = val >> 4;
			}
/*
			// Envelope numbers
			err = FileLoad_ReadReverseInt(pSong, &VEnvNr[curInst], 2);
			if (!err) err = FileLoad_ReadReverseInt(pSong, &TEnvNr[curInst], 2);
			if (!err) err = FileLoad_ReadReverseInt(pSong, &PEnvNr[curInst], 2);
*/			if (!err) err = FileLoad_Skip(pSong, 16);
			if (err) goto loader_err;

			// Note mapping
			err = Loaders_AllocMapTable(pSong, &pInst->pNotesMap);
			if (err) goto loader_err;

			if (inst_volume) inst_volume--;

			NoteMap* pn;
			for (j = NoteDelta, pn = pInst->pNotesMap + NoteDelta; j < 128; j++, pn++)
			{
				err = FileLoad_ReadByte(pSong, &val);
				pn->Volume = inst_volume;
				pn->SampleNr = val; // Sample
				if (!err) err = FileLoad_ReadByte(pSong, &val);
				if (err) goto loader_err;
				pn->Pitch = (j + ((val << 24) >> 24)) << 8; // Note, transposed
			}
		}
		else if ((Tag == TAG(SAMP))|| (Tag == TAG(ASMP))) // Note ASMP is due to a bug GT 0.8770
		{
			if (!pSong->Instruments)
			{
				err = Loaders_Error(pSong, FileOffset - 8, Unexpected_Block);
				goto loader_err;
			}

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

			// Read sample number
			err = FileLoad_ReadReverseInt(pSong, &val, 2);
			if (err) goto loader_err;
			if (!val
			||  (val > 255)
			||  (val < curSmp))
			{
				err = Loaders_Error(pSong, -2, "Invalid sample nr (%d)", val);
				goto loader_err;
			}

			pSong->Samples = val; // Till next block

			while (curSmp < val)
			{
				pSmp = &pSong->pSamples[curSmp-1];
				pSmp->Size = 0;
				curSmp++;
			}

			pSmp = &pSong->pSamples[curSmp-1];
			curSmp++;

			// Read sample definition
			err = FileLoad_ReadString(pSong, &pSmp->pName, 28, false);
			if (err) goto smp_err;
			err = FileLoad_ReadReverseInt(pSong, &val, 2);
			if (err) goto smp_err;

			pSmp->Type = Smp_Type_BigEndian;
			if (val & 1) pSmp->Type |= Smp_Type_Stereo;
			if (val & 2) pSmp->Type |= Smp_Type_Loop_Bidi;

			// Panning
			err = FileLoad_ReadReverseInt(pSong, &val, 2);
			if (err) goto smp_err;
			if (val <= 0x1000)
			{
				pSmp->Type |= Smp_Type_Set_Panning;
				pSmp->Panning = val >> 4;
			}

			// Type
			err = FileLoad_ReadReverseInt(pSong, &val, 2);
			if (err) goto smp_err;
			if (val == 16)
				pSmp->Type |= Smp_Type_16bit;
			else if (val != 8)
			{
				err = Loaders_Error(pSong, -2, "Invalid sample nr of bits (%d)", val);
				goto loader_err;
			}

			// Frequency
			pSmp->Frequency = 8363;
			err = FileLoad_ReadReverseInt(pSong, &val, 2);
			if (err) goto smp_err;
			pSmp->RelTone = Convert_RelTone(pSmp->Frequency, val);

			err = FileLoad_ReadReverseInt(pSong, &pSmp->Size, 4);
			if (!err) err = FileLoad_ReadReverseInt(pSong, &pSmp->LoopStart, 4);
			if (!err) err = FileLoad_ReadReverseInt(pSong, &pSmp->LoopEnd, 4);
			if (err) goto smp_err;
			if (pSmp->LoopEnd > 2)
			{
				pSmp->Type |= Smp_Type_Loop;
				pSmp->LoopEnd += pSmp->LoopStart;
			}

			if (pSmp->Type & Smp_Type_16bit)
			{
				pSmp->Size >>= 1;
				pSmp->LoopStart >>= 1;
				pSmp->LoopEnd >>= 1;
			}

			// Volume
			err = FileLoad_ReadReverseInt(pSong, &val, 2);
			if (err) goto smp_err;
			if (val <= 256)
				pSmp->DefaultVolume = val;
			else
				pSmp->DefaultVolume = 256;
			// Finetune
			err = FileLoad_ReadReverseInt(pSong, &val, 2);
			if (err) goto smp_err;
			pSmp->FineTune = val << 24;
			// Encoding
			err = FileLoad_ReadReverseInt(pSong, &val, 2);
			if (err) goto smp_err;
			if (val)
			{
				err = Loaders_Error(pSong, -2, "Invalid sample (%d) encoding %d", curSmp, val);
				goto loader_err;
			}

			// Data
			err = FileLoad_ReadSample(pSong, pSmp);
			if (err) goto loader_err;

smp_err:
			// Accept files truncated within the samples
			if (err)
			{
				SysLog_FileCorrupted(SysLog_High, pSong, -8, "Sample data could not be read");
				pSmp->Size = 0;
				err = NULL;
			}
		}
		else if (Tag == TAG(SAM2))
		{
			if (!pSong->Instruments)
			{
				err = Loaders_Error(pSong, FileOffset - 8, Unexpected_Block);
				goto loader_err;
			}

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

			// Read sample number
			err = FileLoad_ReadReverseInt(pSong, &val, 2);
			if (err) goto loader_err;
			if (!val
			||  (val > 255)
			||  (val < curSmp))
			{
				err = Loaders_Error(pSong, -2, "Invalid sample nr (%d)", val);
				goto loader_err;
			}

			pSong->Samples = val; // Till next block

			while (curSmp < val)
			{
				pSmp = &pSong->pSamples[curSmp-1];
				pSmp->Size = 0;
				curSmp++;
			}

			pSmp = &pSong->pSamples[curSmp-1];
			curSmp++;

			pSmp->Type = 0; // NOT Smp_Type_BigEndian;

			// Read sample definition
			err = FileLoad_ReadString(pSong, &pSmp->pName, 28, false);
			if (err) goto smp_err2;

			// Skip 2, constant values
			err = FileLoad_ReadReverseInt(pSong, &val, 2);
			if (err) goto smp_err2;
			if (val & 1) pSmp->Type |= Smp_Type_Stereo;
			if (val & 2) pSmp->Type |= Smp_Type_Loop_Bidi;

			// Type
			err = FileLoad_ReadReverseInt(pSong, &val, 2);
			if (err) goto smp_err2;
			if (val == 16)
				pSmp->Type |= Smp_Type_16bit;
			else if (val != 8)
			{
				err = Loaders_Error(pSong, -2, "Invalid sample nr of bits (%d)", val);
				goto loader_err;
			}

			// Skip 4, constant values
			err = FileLoad_Skip(pSong, 4);
			if (err) goto smp_err2;

			// Panning
			err = FileLoad_ReadReverseInt(pSong, &val, 2);
			if (err) goto smp_err;
			if (val <= 0x1000)
			{
				pSmp->Type |= Smp_Type_Set_Panning;
				pSmp->Panning = val >> 4;
			}
			// Volume
			err = FileLoad_ReadReverseInt(pSong, &val, 2);
			if (err) goto smp_err2;
			if (val <= 256)
				pSmp->DefaultVolume = val;
			else
				pSmp->DefaultVolume = 256;

			// Skip 14, constant values except byte 4 (0 or 1)
			err = FileLoad_Skip(pSong, 14);
			if (err) goto smp_err2;

			// Frequency
			pSmp->Frequency = 8363;
			err = FileLoad_ReadReverseInt(pSong, &val, 2);
			if (err) goto smp_err2;
			pSmp->RelTone = Convert_RelTone(pSmp->Frequency, val);

			err = FileLoad_ReadReverseInt(pSong, &pSmp->Size, 4);
			if (!err) err = FileLoad_ReadReverseInt(pSong, &pSmp->LoopStart, 4);
			if (!err) err = FileLoad_ReadReverseInt(pSong, &pSmp->LoopEnd, 4);
			if (err) goto smp_err2;
			if (pSmp->LoopEnd > 2)
			{
				pSmp->Type |= Smp_Type_Loop;
				pSmp->LoopEnd += pSmp->LoopStart;
			}

			// Skip 8, constant avlues
			err = FileLoad_Skip(pSong, 8);
			if (err) goto smp_err2;

			// Data
			err = FileLoad_ReadSample(pSong, pSmp);
			if (err) goto loader_err;

smp_err2:
			// Accept files truncated within the samples
			if (err)
			{
				SysLog_FileCorrupted(SysLog_High, pSong, -8, "Sample data could not be read");
				pSmp->Size = 0;
				err = NULL;
			}
		}
		else if (Tag == TAG(ENDC))
			;
		else
		{
			SysLog_FileCorrupted(SysLog_Medium, pSong, FileOffset - 8, Unexpected_Block);
		}

		// to prevent overflows
		if ((TagSize >= FileSize)
		||  (FileOffset + TagSize >= FileSize))
			break;

		FileLoad_SetPos(pSong, FileOffset + TagSize);
	}

loader_err:
	Loaders_Free(pSong, pLTrack);

	return err;
}

const _kernel_oserror* Loader_GTK(SongHdr* pSong)
{
	const _kernel_oserror* err = NULL;
	SubSong*    pSubSong = Song_GetSubSong(pSong, 0);
	uint8_t*    pLTrack = NULL;
	uint32_t    val;
	int         i;
	uint32_t    Tag;
	Pattern*    pPattern;
	Sample*     pSmp;
	uint32_t    size;
	uint32_t    pattern_rows;

	// is it a GTK file?
	err = FileLoad_SetPos(pSong, 0);
	if (err) goto loader_err;
	err = FileLoad_ReadInt(pSong, &Tag, 3);
	if (err) goto loader_err;

	if (Tag != TAG(GTK))
	{
		err = Loaders_NotThisType;
		goto loader_err;
	}

	err = FileLoad_ReadByte(pSong, &val);
	if (err) goto loader_err;
	pSong->Version = val * 100;

	pSong->Flags = 0;
	pSong->MinPitch = NoteDelta + 24;
	pSong->MaxPitch = NoteDelta + 83;
	pSong->MinTempo = 1;
	pSong->MaxTempo = 999;

	// ... file type ptr
	pSong->pType = Typestr_GTK;

	pSong->CommentsLen = 160;
	err = FileLoad_ReadString(pSong, &pSong->pName, 32, false);
	if (!err) err = FileLoad_ReadString(pSong, &pSong->pComments, 160, false);
	if (!err) err = FileLoad_ReadReverseInt(pSong, &pSong->Samples, 2);
	if (!err) err = FileLoad_ReadReverseInt(pSong, &pattern_rows, 2);
	if (!err) err = FileLoad_ReadReverseInt(pSong, &pSong->Channels, 2);
	if (!err) err = FileLoad_ReadReverseInt(pSong, &pSubSong->SeqLen, 2);
	if (!err) err = FileLoad_ReadReverseInt(pSong, &pSubSong->RestartPos, 2);
	if (err) goto loader_err;

    if (!pSong->Samples || (pSong->Samples > 255))
    {
		err = Loaders_InvalidSong(pSong, 196, &pSong->Samples, pSong->Samples);
		goto loader_err;
    }

    if (!pattern_rows || (pattern_rows > song_max_rows))
    {
		err = Loaders_Error(pSong, 198, "Invalid nr of pattern rows (%d) in header", pattern_rows);
		goto loader_err;
    }

    if (!pSong->Channels || (pSong->Channels > 32))
    {
		err = Loaders_InvalidSong(pSong, 200, &pSong->Channels, pSong->Channels);
		goto loader_err;
    }

    if (!pSubSong->SeqLen || (pSubSong->SeqLen > 256))
    {
		err = Loaders_InvalidSong(pSong, 202, &pSubSong->SeqLen, pSubSong->SeqLen);
		goto loader_err;
    }

	// Read samples definitions
	for (i = 0, pSmp = pSong->pSamples; i < pSong->Samples; i++, pSmp++)
	{
		if (pSong->Version < 200)
		{
			err = FileLoad_ReadString(pSong, &pSmp->pName, 32, false);
			if (err) goto loader_err;
		}
		else
		{
			err = FileLoad_ReadString(pSong, &pSmp->pName, 28, false);
			if (err) goto loader_err;

			if (pSong->Version > 200)
			{
				err = FileLoad_Skip(pSong, 14);
				if (err) goto loader_err;

				pSmp->Type = Smp_Type_BigEndian;

				// Panning
				err = FileLoad_ReadReverseInt(pSong, &val, 2);
				if (err) goto loader_err;
				if (val <= 0x1000)
				{
					pSmp->Type |= Smp_Type_Set_Panning;
					pSmp->Panning = val >> 4;
				}
			}

			// Type
			err = FileLoad_ReadReverseInt(pSong, &val, 2);
			if (err) goto loader_err;
			if (val == 2)
				pSmp->Type |= Smp_Type_16bit;
			else if (val != 1)
			{
				err = Loaders_Error(pSong, -2, "Invalid sample nr of bits (%d)", val);
				goto loader_err;
			}

			// Frequency
			pSmp->Frequency = 8363;
			err = FileLoad_ReadReverseInt(pSong, &val, 2);
			if (err) goto loader_err;
			pSmp->RelTone = Convert_RelTone(pSmp->Frequency, val);
		}

		err = FileLoad_ReadReverseInt(pSong, &pSmp->Size, 4);
		if (!err) err = FileLoad_ReadReverseInt(pSong, &pSmp->LoopStart, 4);
		if (!err) err = FileLoad_ReadReverseInt(pSong, &pSmp->LoopEnd, 4);
		if (err) goto loader_err;
		if (pSmp->LoopEnd > 2)
		{
			pSmp->Type |= Smp_Type_Loop;
			pSmp->LoopEnd += pSmp->LoopStart;
		}

		if (pSmp->Type & Smp_Type_16bit)
		{
			pSmp->Size >>= 1;
			pSmp->LoopStart >>= 1;
			pSmp->LoopEnd >>= 1;
		}

		// Volume
		err = FileLoad_ReadReverseInt(pSong, &val, 2);
		if (err) goto loader_err;
		if (val <= 256)
			pSmp->DefaultVolume = val;
		else
			pSmp->DefaultVolume = 256;
		// Finetune
		err = FileLoad_ReadReverseInt(pSong, &val, 2);
		if (err) goto loader_err;
		pSmp->FineTune = val << 24;
	}

	// Sequence
	pSong->Patterns = 0;
	err = Loaders_AllocSeq(pSong, pSubSong);
	if (err) goto loader_err;

	for (i = 0; i < pSubSong->SeqLen; i++)
	{
		err = FileLoad_ReadReverseInt(pSong, &val, 2);
		if (err) goto loader_err;
		pSubSong->pSeqs[i] = val;
		if (pSong->Patterns < val) pSong->Patterns = val;
	}
	// Padding to 256
	for (; i < 256; i++)
	{
		err = FileLoad_ReadReverseInt(pSong, &val, 2);
		if (err) goto loader_err;
//		if (pSong->Patterns < val) pSong->Patterns = val;
	}

	pSong->Patterns++;

	if (!pSong->Patterns
	|| (pSong->Patterns > song_max_patterns))
	{
		err = Loaders_InvalidSong(pSong, -1, &pSong->Patterns, pSong->Patterns);
		goto loader_err;
	}

	// Read patterns
	for (i = 0, pPattern = pSong->pPatterns; i < pSong->Patterns; i++, pPattern++)
	{
		pPattern->Rows = pattern_rows;

		if (pSong->Version < 400)
		{
			size = pPattern->Rows * pSong->Channels * 4;

			err = Loaders_Alloc(pSong, (void**) &pLTrack, size);
			if (err) goto loader_err;
			err = FileLoad_Read(pSong, pLTrack, size);
			if (err) goto loader_err;

			err = Pattern_GTK(pSong, i, pPattern, pSong->Channels, pLTrack);
			if (err) goto loader_err;
		}
		else
		{
			size = pPattern->Rows * pSong->Channels * 5;

			err = Loaders_Alloc(pSong, (void**) &pLTrack, size);
			if (err) goto loader_err;
			err = FileLoad_Read(pSong, pLTrack, size);
			if (err) goto loader_err;

			err = Pattern_GT2(pSong, i, pPattern, pSong->Channels, pLTrack);
			if (err) goto loader_err;
		}

		Loaders_Free(pSong, pLTrack);
		pLTrack = NULL;
	}

	// Read samples data
	for (i = 0, pSmp = pSong->pSamples; i < pSong->Samples; i++, pSmp++)
	{
		err = FileLoad_ReadSample(pSong, pSmp);
		if (err) goto loader_err;
	}

loader_err:
	Loaders_Free(pSong, pLTrack);

	return err;
}
