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

/*
 * Channels: [1...16] or [1...32]
 * Patterns: [1...] of single channel patterns
 * Rows    : [1...] (can be shortened in sequence)
 * Orders  : [1...255]
 * Samples : [1...255]
 * Instr.  : 0
 * Tempo   : [32?...255], T frames per 2.5 seconds.
 * Speed   : [1...31?], S frames per row.
 * Notes   : [C-0...C-5...B-9]
 * Pitch   : Uses Amiga periods, base is 428 units, period is divided by 2 each octave up.
 *           Slides in x/1 or x/16 of units per frame.
 * Volume  : [0...64], linear.
 * Panning : [-64...64], 100 = Surround.
 * Sampling: 8363 Hz (frequency is turned into a relative tone).
 *
 * Doc tells in version 1.0 only sample loop start is present (2 bytes)
 *   but files I have tells both start and end are present (2* 4 bytes).
 *
 * Undocumented, I think order value 0, found in some files, means an empty track.
 *
 * Doc tells 0x7F is copy last row info. I found it in the middle of other triples
 *   setting instrument and effect for the same row, which makes the doc ridiculous.
 *   Found the original for eye.amf, i.e. eyegaboom.mod and the corresponding
 *   lines countain an instrument + sample offset command and no note, which
 *   means 0x7F is just a marker for such cases.
 *
 * Doc tells 0x83 is channel volume, it's note volume.
 *
 * Undocumented, 0x84 with value 0x80 stands for S3M F00.
 *
 * M2AMF maps both tremolo and tremor to 0x87. We choose tremolo.
 *
 * Extra fine porta 0x96 seems to be 4 times finer than S3M one.
 */

#define NoteDelta (Note_Central-12*5)

static const uint8_t Tag_AMF[] = "AMF\0";
static const uint8_t Typestr_AMF[] = "DSMI Advanced Module Format";

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

typedef struct
{
	uint8_t     Filename[13];
	uint8_t     Flags[2];
	uint8_t     Volume;
	uint32_t    Size;
	uint32_t    LoopStart;
	uint32_t    LoopEnd;
	uint32_t	Dummy; // Used internally to point to sample data
	uint16_t	Frequency;
	uint16_t	Period;
	uint8_t		Name[28];
} LSmp;

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

typedef struct
{
	uint32_t	size;
	uint8_t*    triples;
	uint32_t	fileoffset;
} LTrack;

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
	int32_t svalue = (value << 24);
	svalue >>= 24;

	switch(effect)
	{
		case 0x81: // Global: Set speed, No memory
		{
			if (value)
				Pattern_AddGlbEffect_Frames(pSong, gcmd_frames_set, value);
			else
				SysLog_BadEffectValue(pSong, 0, oeffect, ovalue);
		}
		break;
		case 0x82: // Volume: Slide, Memory?
		{
			if (svalue == 0)
			{
				// repeat last slide
				Pattern_AddEffect_NoteVolume(pSong, cmd_last_slide, 0);
			}
			else if (svalue > 0)
			{
				// slide up
				effect = cmd_upmem1 | flag_cmd_slide_frameN0;
				Pattern_AddEffect_NoteVolume(pSong, effect, svalue << 2);
			}
			else
			{
				// slide down
				effect = cmd_downmem1 | flag_cmd_slide_frameN0;
				Pattern_AddEffect_NoteVolume(pSong, effect, (-svalue) << 2);
			}
		}
		break;
		case 0x83: // Note Volume: Set
		{
			if (value <= 64)
				Pattern_AddEffect_NoteVolume(pSong, cmd_set, value << 2);
			else
				SysLog_BadEffectValue(pSong, 0, oeffect, ovalue);
		}
		break;
		case 0x84: // Pitch: Slide, Memory
		{
			if (value & 0x80)
			{
				if (value == 0x80)
					svalue = 0;
				// slide up
				effect = cmd_upmem1 | flag_cmd_slide_frameN0;
				Pattern_AddEffect_Pitch(pSong, effect, (-svalue) << 2);
			}
			else
			{
				// slide down
				effect = cmd_downmem1 | flag_cmd_slide_frameN0;
				Pattern_AddEffect_Pitch(pSong, effect, svalue << 2);
			}
		}
		break;
		case 0x86: // Pitch: Portamento
		{
			if (!value)
			{
				// 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 0x87: // Volume: Tremor/Tremolo, Memory
		{
			uint32_t speed;

			speed = value >> 4;
			value &= 0xf;
			// Vibrate between [-2*y,+2*y], assuming S3M range
			value <<= 3; // << 2 for range conversion and << 1 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 0x88: // Pitch: Arpeggio, Memory
		{
			uint32_t val;

			val = value >> 4;
			value &= 0xf;
			Pattern_AddEffect_Arpeggio(pSong, value, val, cmd_arpeggio_type_NHL);
		}
		break;
		case 0x89: // Pitch: Vibrato, Memory
		{
			uint32_t speed = value >> 4;
			value &= 0xf;
			// Vibrate between [-2*y,+2*y]
			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 0x8a: // Pitch: Portamento 0 - Volume: Slide, Memory
		{
			convert_effect(pSong, pChInfo, 0x84, 0);
			convert_effect(pSong, pChInfo, 0x82, value);
		}
		break;
		case 0x8b: // Pitch: Vibrato 0 - Volume: Slide, Memory
		{
			convert_effect(pSong, pChInfo, 0x89, 0);
			convert_effect(pSong, pChInfo, 0x82, value);
		}
		break;
		case 0x8c: // 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 0x8d: // Global: Jump
		{
			Pattern_AddGlbEffect_Position(pSong, gcmd_pos_jump, value);
		}
		break;
		case 0x8e: // Sync ?
		{
			SysLog_SkipEffect(pSong, 0, oeffect, ovalue);
		}
		break;
		case 0x8f: // Note: Retrig note every y ticks with volume change type x, Memory
		{
			uint32_t eff;

			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 0x90: // Note: Set sample offset, No Memory
		{
			if (value)
			{
				// define offset
				// clear byte 0
				Pattern_AddEffect_Instrument(pSong, cmd_sample_set_offset, (0<<8) | 0);
				// set byte 1
				Pattern_AddEffect_Instrument(pSong, cmd_sample_set_offset, (1<<8) | value);
			}

			// only if a note is defined at the same time
			if (pChInfo->Note) Pattern_AddEffect_Instrument(pSong, cmd_sample_set_offset, 0xf00);
		}
		break;
		case 0x91: // Volume: Fine Slide, Memory?
		{
			if (svalue == 0)
			{
				// repeat last fine slide
				effect = cmd_pitch_memslide | flag_cmd_slide_frame0;
				Pattern_AddEffect_NoteVolume(pSong, effect, 0);
			}
			else if (svalue > 0)
			{
				// slide up
				effect = cmd_upmem1 | flag_cmd_slide_frame0;
				Pattern_AddEffect_NoteVolume(pSong, effect, svalue << 2);
			}
			else
			{
				// slide down
				effect = cmd_downmem1 | flag_cmd_slide_frame0;
				Pattern_AddEffect_NoteVolume(pSong, effect, (-svalue) << 2);
			}
		}
		break;
		case 0x92: // Pitch: Fine Slide, No Memory
		{
			if (svalue > 0)
			{
				// slide down
				effect = cmd_downmem1 | flag_cmd_slide_frame0;
				Pattern_AddEffect_Pitch(pSong, effect, svalue << 2);
			}
			else
			{
				// slide up
				effect = cmd_upmem1 | flag_cmd_slide_frame0;
				Pattern_AddEffect_Pitch(pSong, effect, (-svalue) << 2);
			}
		}
		break;
		case 0x93: // Note: Delay for x ticks
		{
			Pattern_AddEffect_Instrument(pSong, cmd_sample_note_delay, value);
		}
		break;
		case 0x94: // Note: Cut note after x ticks
		{
			Pattern_AddEffect_NoteVolume(pSong, cmd_volume_cut, value);
		}
		break;
		case 0x95: // Global: Set tempo, No memory
		{
			if (value >= 0x20)
			{
				Pattern_AddGlbEffect_Tempo(pSong, cmd_set, value);
			}
			else
			{
				SysLog_BadEffectValue(pSong, 0, oeffect, ovalue);
			}
		}
		break;
		case 0x96: // Pitch: Extra Fine Slide, No Memory
		{
			if (svalue >= 0)
			{
				// slide down
				effect = cmd_downmem1 | flag_cmd_slide_frame0;
				Pattern_AddEffect_Pitch(pSong, effect, svalue >> 2);
			}
			else
			{
				// slide up
				effect = cmd_upmem1 | flag_cmd_slide_frame0;
				Pattern_AddEffect_Pitch(pSong, effect, (-svalue) >> 2);
			}
		}
		break;
		case 0x97: // Panning: Set
		{
			if (value == 100)
				svalue = Panning_Surround;
			else
			{
				svalue += 64;
				svalue <<= 1;
				if (svalue < 0) svalue = 0;
				if (svalue > 256) svalue = 256;
			}
			Pattern_AddEffect_Panning(pSong, cmd_set, svalue);
		}
		break;
		default:
		{
			SysLog_BadEffect(pSong, 0, oeffect, ovalue);
		}
	}
}

const _kernel_oserror* Loader_AMF(SongHdr* pSong)
{
	const _kernel_oserror* err = NULL;
	SubSong*        pSubSong = Song_GetSubSong(pSong, 0);
	int             i, j, k;
	Pattern*        pPattern;
	Sample*         pSmp;
	uint32_t        val;
	uint32_t        tracks, maxtrack = 0;
	uint32_t*       pRows = NULL;
	uint32_t*       pOrders = NULL;
	uint16_t*       pTrackNrs = NULL;
	LTrack*         pLTracks = NULL;
	ChannelInfo*    pChInfos = NULL;
	uint32_t*       pOrder;
	LTrack*         pLTrack;
	ChannelInfo*    pChInfo;
	ChannelDefault* pDef;
	uint32_t        SamplesPos;
	bool            bShortSamples = false;

	// Allocate memory
	err = Loaders_Alloc(pSong, (void**) &pChInfos, 32*sizeof(ChannelInfo));
	if (err) goto loader_err;

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

	if (val != TAG(AMF))
	{
		err = Loaders_NotThisType;
		goto loader_err;
	}

	pSong->Flags = 0;
	pSong->MinPitch = NoteDelta;
	pSong->MaxPitch = NoteDelta + 12 * 10 - 1;

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

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

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

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

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

	err = FileLoad_ReadInt(pSong, &tracks, 2); // size or patterns map
	if (err) goto loader_err;
	if (!tracks)
	{
		err = Loaders_InvalidSong(pSong, 38, &pSong->Patterns, tracks);
		goto loader_err;
	}

	err = FileLoad_ReadByte(pSong, &pSong->Channels);
	if (err) goto loader_err;
	if (!pSong->Channels || (pSong->Channels > ((pSong->Version >= 130) ? 32 : 16)))
	{
		err = Loaders_InvalidSong(pSong, 40, &pSong->Channels, pSong->Channels);
		goto loader_err;
	}

	if (pSong->Version == 100)
	{
		// Skip channel remap table, I guess initial panning may be wrong
		err = FileLoad_SetPos(pSong, FileLoad_GetPos(pSong) + 16);
		if (err) goto loader_err;
	}
	else if (pSong->Version >= 110)
	{
		// Read panning table
		j = (pSong->Version >= 130) ? 32 : 16;
		for (i = 0, pDef = pSubSong->Defaults.ChDef; i < j; i++, pDef++)
		{
			err = FileLoad_ReadByte(pSong, &val);
			if (err) goto loader_err;
			if (val == 100)
				pDef->Panning = Panning_Surround;
			else
			{
				int32_t sval = (val << 24);
				sval = 128 + (sval >> 23);
				if (sval < 0) sval = 0;
				if (sval > 256) sval = 256;
				pDef->Panning = sval;
			}
		}

		if (pSong->Version >= 130)
		{
			err = FileLoad_ReadByte(pSong, &pSubSong->Defaults.Tempo);
			if (err) goto loader_err;
			err = FileLoad_ReadByte(pSong, &pSubSong->Defaults.Speed);
			if (err) goto loader_err;
		}
	}

	// Read nr rows/track mapping
	val = pSubSong->SeqLen * pSong->Channels;
	err = Loaders_Alloc(pSong, (void**) &pOrders, sizeof(uint32_t) * val);
	if (err) goto loader_err;
	err = Loaders_Alloc(pSong, (void**) &pRows, sizeof(uint32_t) * pSubSong->SeqLen);
	if (err) goto loader_err;

	for (i = 0, pOrder = pOrders; i < pSubSong->SeqLen; i++)
	{
		if (pSong->Version >= 140)
		{
			err = FileLoad_ReadInt(pSong, &pRows[i], 2);
			if (err) goto loader_err;
			if (!pRows[i] || (pRows[i] > song_max_rows))
			{
				err = Loaders_InvalidSong(pSong, -2, &pSong->pPatterns[i].Rows, pRows[i]);
				goto loader_err;
			}
		}
		else pRows[i] = 64;

		for (j = 0; j < pSong->Channels; j++, pOrder++)
		{
			err = FileLoad_ReadInt(pSong, pOrder, 2);
			if (err) goto loader_err;

			if (*pOrder > tracks)
			{
				err = Loaders_Error(pSong, -2, "Invalid order %d", *pOrder);
				goto loader_err;
			}
		}
	}

	SamplesPos = FileLoad_GetPos(pSong);

	// Read samples definitions
	if (pSong->Version < 100)
		bShortSamples = true;
	else
	{
		// Attempt to read with long layout
		for(i = 0, pSmp = pSong->pSamples; i < pSong->Samples; i++, pSmp++)
		{
			// read sample type
			err = FileLoad_ReadByte(pSong, &val);
			if (err) goto loader_err;

			pSmp->Type = Smp_Type_Unsigned;
			// 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, 13, false);
			if (err) goto loader_err;
			// read sample index and store it in panning for now
			err = FileLoad_ReadInt(pSong, &pSmp->Panning, 4);
			if (err) goto loader_err;
			// read sample size
			err = FileLoad_ReadInt(pSong, &pSmp->Size, 4);
			if (err) goto loader_err;
			// read sample rate
			err = FileLoad_ReadInt(pSong, &val, 2);
			if (err) goto loader_err;
			pSmp->Frequency = 8363;
			pSmp->RelTone = Convert_RelTone(pSmp->Frequency, val);
			// read sample volume
			err = FileLoad_ReadByte(pSong, &pSmp->DefaultVolume);
			if (err) goto loader_err;
			if (pSmp->DefaultVolume > 64) pSmp->DefaultVolume = 256;
			else pSmp->DefaultVolume <<= 2;
			// read sample loop start
			err = FileLoad_ReadInt(pSong, &pSmp->LoopStart, 4);
			if (err) goto loader_err;
			// read sample loop end
			err = FileLoad_ReadInt(pSong, &pSmp->LoopEnd, 4);
			if (err) goto loader_err;

			// some 1.00 files have the < 1.00 layout
			if (pSmp->LoopStart >> 24)
			{
				bShortSamples = true;
				break;
			}

			if (pSmp->LoopStart) pSmp->Type |= Smp_Type_Loop;
		}
	}

	// Short layout of the files
	if (bShortSamples)
	{
		err = FileLoad_SetPos(pSong, SamplesPos);
		if (err) goto loader_err;

		// Read short layout
		for(i = 0, pSmp = pSong->pSamples; i < pSong->Samples; i++, pSmp++)
		{
			// read sample type
			err = FileLoad_ReadByte(pSong, &val);
			if (err) goto loader_err;

			pSmp->Type = Smp_Type_Unsigned;
			// 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, 13, false);
			if (err) goto loader_err;
			// read sample index and store it in panning for now
			err = FileLoad_ReadInt(pSong, &pSmp->Panning, 4);
			if (err) goto loader_err;
			// read sample size
			err = FileLoad_ReadInt(pSong, &pSmp->Size, 4);
			if (err) goto loader_err;
			// read sample rate
			err = FileLoad_ReadInt(pSong, &val, 2);
			if (err) goto loader_err;
			pSmp->Frequency = 8363;
			pSmp->RelTone = Convert_RelTone(pSmp->Frequency, val);
			// read sample volume
			err = FileLoad_ReadByte(pSong, &pSmp->DefaultVolume);
			if (err) goto loader_err;
			if (pSmp->DefaultVolume > 64) pSmp->DefaultVolume = 256;
			else pSmp->DefaultVolume <<= 2;
			// read sample loop start
			err = FileLoad_ReadInt(pSong, &pSmp->LoopStart, 2);
			if (err) goto loader_err;
			pSmp->LoopEnd = pSmp->Size;

			if (pSmp->LoopStart) pSmp->Type |= Smp_Type_Loop;
		}
	}

	// Read track numbers
	err = Loaders_Alloc(pSong, (void**) &pTrackNrs, 2 * tracks);
	if (err) goto loader_err;
	err = FileLoad_Read(pSong, pTrackNrs, 2 * tracks);
	if (err) goto loader_err;

	// Get max track
	maxtrack = 0;
	for (i = 0; i < tracks; i++)
	{
		if (!pTrackNrs[i])
		{
			err = Loaders_Error(pSong, 2*(i - tracks), "Track Nr invalid");
			goto loader_err;
		}
		if (maxtrack < pTrackNrs[i])
			maxtrack = pTrackNrs[i];
	}


	// Translate orders into track nr
	for (i = 0; i < (pSubSong->SeqLen * pSong->Channels); i++)
	{
		val = pOrders[i];
		if (val) pOrders[i] = pTrackNrs[pOrders[i] - 1];
	}

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

	// Read track definitions
	for (i = 0, pLTrack = pLTracks; i < maxtrack; i++, pLTrack++)
		pLTrack->triples = NULL;

	for (i = 0, pLTrack = pLTracks; i < maxtrack; i++, pLTrack++)
	{
		// read track size / 3
		err = FileLoad_ReadInt(pSong, &pLTrack->size, 3);
		if (err) goto loader_err;
		// read track
		pLTrack->fileoffset = FileLoad_GetPos(pSong);
		err = Loaders_Alloc(pSong, (void**) &pLTrack->triples, 3 * pLTrack->size);
		if (err) goto loader_err;
		err = FileLoad_Read(pSong, pLTrack->triples, 3 * pLTrack->size);
		if (err) goto loader_err;
	}

	// We will construct 1 track for each order, so real order is simple
	pSong->Patterns = pSubSong->SeqLen;

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

	for (i = 0, pOrder = pOrders, pPattern = pSong->pPatterns; i < pSubSong->SeqLen; i++, pPattern++)
	{
		pSubSong->pSeqs[i] = i;
		pPattern->Rows = pRows[i];

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

        for (k = 0, pChInfo = pChInfos; k < pSong->Channels; k++, pChInfo++, pOrder++)
        {
			if (*pOrder)
			{
				pLTrack = &pLTracks[*pOrder - 1];
				pChInfo->pFrom = pLTrack->triples;
				pChInfo->pTo = pChInfo->pFrom + 3 * pLTrack->size;
				pChInfo->endoffset = pLTrack->fileoffset + (pChInfo->pTo - pChInfo->pFrom);
			}
			else
			{
				pChInfo->pFrom = NULL;
				pChInfo->pTo = NULL;
			}
			pChInfo->Channel = k;
			pChInfo->Note = 0;
			pChInfo->Inst = 0;
			pChInfo->Volume = 0xff;
			pChInfo->Effect = 0xff;
			pChInfo->Effect2 = 0xff;
        }

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

	        for (k = 0, pChInfo = pChInfos; k < pSong->Channels; k++, pChInfo++)
    	    {
				int started = 0;

				for (;pChInfo->pFrom < pChInfo->pTo; pChInfo->pFrom += 3)
				{
					if (pChInfo->pFrom[0] < j)
					{
						err = Loaders_Error
								(pSong, pChInfo->endoffset - (pChInfo->pTo - pChInfo->pFrom)
								, "%03d:%03d:%02d: Bad row nr %02d"
								, i, j, k, pChInfo->pFrom[0]
								);
						goto loader_err;
					}
					if (pChInfo->pFrom[0] > j)
						break;

					if (started == 0)
					{
						// Clear info on this row
						pChInfo->Note = 0;
						pChInfo->Inst = 0;
						pChInfo->Volume = 0xff;
						pChInfo->Effect = 0xff;
						pChInfo->Effect2 = 0xff;
						started = 1;
					}

					if (pChInfo->pFrom[1] < 0x7f)
					{
						pChInfo->Note = NoteDelta + pChInfo->pFrom[1];
						pChInfo->Volume = pChInfo->pFrom[2];
					}
					else if (pChInfo->pFrom[1] == 0x7f)
						; // Just a marker for instrument without a note
					else if (pChInfo->pFrom[1] == 0x80)
						pChInfo->Inst = pChInfo->pFrom[2] + 1;
					else
					{
						if (pChInfo->Effect == 0xff)
						{
							pChInfo->Effect = pChInfo->pFrom[1];
							pChInfo->Value = pChInfo->pFrom[2];
						}
						else if (pChInfo->Effect2 == 0xff)
						{
							pChInfo->Effect2 = pChInfo->pFrom[1];
							pChInfo->Value2 = pChInfo->pFrom[2];
						}
						else
						{
							SysLog_FileCorrupted
								(SysLog_High, pSong, 0, "%03d:%03d:%02d: Multiple effects %d"
								, i, j, k, pChInfo->pFrom[1]
								);
						}
					}
				}

				if (started
				&& (    (pChInfo->Note != 0)
				    ||  (pChInfo->Inst != 0)
				    ||  (pChInfo->Volume <= 64)
				    ||  (pChInfo->Effect != 0xff))
				   )
				{
					err = Loaders_StartChannel(pSong, k, pChInfo->Note, pChInfo->Inst);
					if (err) goto loader_err;
					if (pChInfo->Volume <= 64)
						Pattern_AddEffect_NoteVolume(pSong, cmd_set, pChInfo->Volume << 2);
					if (pChInfo->Effect != 0xff)
						convert_effect(pSong, pChInfo, pChInfo->Effect, pChInfo->Value);
					if (pChInfo->Effect2 != 0xff)
						convert_effect(pSong, pChInfo, pChInfo->Effect2, pChInfo->Value2);
					err = Loaders_EndChannel(pSong);
					if (err) goto loader_err;
				}
    	    }

			err = Loaders_EndRow(pSong);
			if (err) goto loader_err;
		}

		err = Loaders_EndPattern(pSong, pPattern);
		if (err) goto loader_err;
	}

	// Read samples data
	for (i = 0; i < pSong->Samples; i++)
	{
		for (j = 0, pSmp = pSong->pSamples; j < pSong->Samples; j++, pSmp++)
		{
			// Compare to index stored in panning
			if ((i + 1) == pSmp->Panning)
			{
				err = FileLoad_ReadSample(pSong, pSmp);
				if (err) goto loader_err;
			}
		}
	}

loader_err:
	Loaders_Free(pSong, pRows);
	Loaders_Free(pSong, pOrders);
	Loaders_Free(pSong, pTrackNrs);
	Loaders_Free(pSong, pChInfos);

	if (pLTracks)
	{
		for (i = 0, pLTrack = pLTracks; i < maxtrack; i++, pLTrack++)
			Loaders_Free(pSong, pLTrack->triples);
		Loaders_Free(pSong, pLTracks);
	}

	return err;
}
