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

/*
 * Channels: [1...64]
 * Patterns: [1...253]
 * Rows    : [1...65535]
 * Orders  : [1...65535]
 * Samples : [1...255]
 * Instr.  : [1...255]
 * Tempo   : [32...255], T frames per 2.5 seconds.
 * Speed   : [1...31], S frames per row.
 * Notes   : [C-0...C-5...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.
 *           Semitones mode, value is upped by 12 each octave up.
 *           Slides in x/16 or x/64 units per row.
 *           See remark below on pitch limits.
 * Volume  : [0...64] , linear.
 * Panning : [0...64] + Surround.
 * Sampling: 8363 Hz (frequency is turned into a relative tone).
 *
 * Bug in IT 2.14v5: if sample "default pan" is On it always resets panning to 0.
 * Bug in IT 2.14v5: IT envelope carry doesn't move envelope loop limits
 *                   back to sustain loop limits till end of current loop is reached.
 *
 * 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:
 *   if a note is playing with same instrument as before:
 *   - sets volume from sample default (intrument global volume and swing have no effect).
 *   - sustain, fadeout are not affected (such notes are virtual ones).
 *   - panning, finetune, nna, vibrato/tremolo/panbrello/auto-vibrato positions are not affected.
 *   - envelope flags and positions are not affected.
 *   else:
 *   - triggers a new note using the last note nr.
 *   Exception: if the instrument maps to sample 0 (undefined) the playing note is left unchanged.
 *
 * A key off or note cut in the note column, resets the last note nr and so affects
 *   the previous paragraph.
 *
 * Unstored samples are treated as empty samples with default volume set to max.
 *
 * Note triggering does not normally apply when portamento effect is present
 *   EXCEPT IF no note is playing.
 *
 * Retrigger xy effect, y is delay. 00 is memory.
 *   The counter is initialised to delay when a note starts playing (either from retrig or normal note)
 *   and decreases each tick of rows where the effect is present.
 *   When a <= 0 value is reached, a new note is triggered even. Retrigs with y > speed will work
 *   provided that you put enough H commands in the row that follows (they can even be interspaced by
 *   other effects).
 *     Delay of 0 behaves as a delay of 1.
 *     If delay is 3, retrig will happen after 3, 6, 9 ... ticks.
 *     If delay is set to 11 then to 2 on next tick, retrig will happen after 11, 13, 15, i.e change
 *     will take effect only after the retrig.
 *   For some reason retrigger is not processed if no note is playing.
 *
 * Envelopes
 * ---------
 *
 * Envelope plays endpoint befor looping. Ex: loops between point 0 (tick = 0) and 1 (tick = 2),
 *   envelope pos will be 0 1 2 0 1 2 ... not 0 1 0 1 ... like XM.
 *
 * Envelope carry keeps envelope position of previous playing note om certain conditions:
 *   - they use the same instrument
 *   - the previous note is still playing (if NNA cuts previous note, carry does not apply)
 *
 * Effects
 * -------
 *
 * Effects in the volume column are applied before effects in the effects column. This as some
 *   consequences like when the effects in both columns share the same memory.
 *
 * Waveforms (S3x, S4x, S5x), document IT.TXT inverts rampdown and square (F1 on pattern shows
 *   the correct list).
 *
 * Vibrato, is twice as fine in IT, starts in the reverse direction and is applied every frame.
 *   That is unless you set the old effects flag.
 *
 * Fine vibrato Ux0 = vibrato Hx0, they both vibrate at same depth as before.
 *
 * Pitch slides Exx and Fxx are from memory only with value 00, any other value becomes the memory.
 *   Notably, slides by values F0 and E0 behave as a fine/extra fine slide of 0, not as a fine/extra
 *   fine slide from memory.
 *
 * Pitch slides in volume column and in effects column behave differently,
 *   pitch slide Ex, Fx are 4 times larger than Eyy, Fyy.
 *
 * Pitch slide Ex, Fx shares the same memory as Eyy, Fyy, so if yy is in memory, the command E0/F0
 *   will perform a slide of 4 times yy, even when a fine/extra fine slide is in memory
 *   (i.e. when yy = Ex or Fx).
 * Implementation: Ex and Exx are translated into a single slide down command so an E0/E00 which
 *                 uses a memory set from the other column will behave incorrectly. Idem for Fx/Fxx.
 *
 * Pitch slides are not limited into the [C-0...B-9] range, only so as not to to cause overflows.
 *   High (very) pitches are cut.
 *   From tests I would assume in non-linear mode S3M period is limited to [1...65535] and
 *   in linear mode pitch is in 1/256th of semitone relative to C-5 and limited to [-32768, 32767].
 * Implementation: our pitch limits more or less reflects the non-linear limits.
 *
 * Sample frequency is translated while triggering the note into the appropriate pitch shift
 *   to play it as if its frequency is 8363 Hz. So a C-4 at 2 * 8363 Hz, will be translated into
 *   a C-5 at 8363 Hz. This impacts slides in non-linear mode because in this mode slides have
 *   a wide effect at high pitch (low period).
 *
 * Tremolo is twice finer as MOD one.
 *
 * Tremor xy. 00 is Memory. When the effect is present on a row, the counter decreases
 *   till a <= 0 value is reached. At this point the switch between on and off occurs
 *   and the counter is reinitialised. The counter is never reset by other means
 *   (such as a new note or a new row with a tremor effect).
 *   As a consequence 0 values for x and y behave as a 1, changes of x or y
 *   due to a new tremor effect only take effect when the counter reaches 0
 *   and is reinitialised.
 *   For some reason tremor is not processed if no note is playing.
 *   For OldEffects behaviour changes to "counter decreases till a < 0 value is reached".
 *   ontime = x+1, offtime = y+1.
 *
 * Only the first row repeat on a row is processed. As usual during repeats
 *   tick-0 events are ignored and non-0 tick events are processed, BUT
 *   the tick count is reset on each repeat which means for example
 *   that delayed notes will be trigerred in each repeat.
 *   Implementation: Ticks are not reset so we loose that behaviour.
 *
 * Loops. On exit of a loop in a column, the loop start point is moved
 *   to the row after the end of the loop.
 *
 * Command S00 is memory for any Sxy command.
 *   Implementation: we translate all Sx commands so there can't be any last S command.
 *   To minimise the damage we keep track of S commands in the loader and replace
 *   any S00 by that but the info is lost if S00 if the first S command in the pattern.
 *
 * Note cut SCx with x > speed does nothing, combining it with SEx changes nothing. SC0 behaves as SC1.
 * Note cut SDx with x > speed does nothing, combining it with SEx changes nothing. SD0 behaves as SD1.
 * On the other hand SDx is treated on each repeat.
 *
 * KeyOff in sample mode, does nothing.
 * KeyOff in instrument mode, unsets "Sustain" state for the note but sets "Fadeout" state
 *   only if the volume envelope is off or the volume envelope has a non-sustain loop.
 *   Once "Sustain" state is inactive sustain loops (envelopes and sample) are ignored.
 * "Fadeout" state is also set once the end of volume envelope is reached, and that
 *   even if "Sustain" state is active.
 * "Fadeout" state decreases the fadeout volume by the instrument fadeout value each tick.
 *   If said value is 0, no fadeout occurs.
 */

typedef struct
{
	uint32_t    TAG;
	uint8_t     Name[26];
	uint16_t    Dummy;
	uint16_t    Orders;
	uint16_t    Instruments;
	uint16_t    Samples;
	uint16_t    Patterns;
	uint8_t     TrackerVers[2];
	uint8_t     FormatVers[2];
	uint32_t    Flags;
	uint8_t     GlobalVolume;
	uint8_t     MixingVolume;
	uint8_t     Speed;
	uint8_t     Tempo;
	uint8_t     PanningSep;
	uint8_t     PitchWheelDepth;
	uint16_t    MsgLength;
	uint32_t    MsgOffset;
	uint32_t    Reserved;
	uint8_t     ChannelsPanning[64];
	uint8_t     ChannelsVolume[64];
} LHdr;

typedef struct
{
	uint32_t    TAG;
	uint8_t     Filename[12];
	union
	{
		struct
		{
			uint8_t     Dummy;
			uint8_t     NewNoteAction;
			uint8_t     DuplicateCheckType;
			uint8_t     DuplicateCheckAction;
			uint16_t    Fadeout;
			uint8_t     PitchPanSep;
			uint8_t     PitchPanCenter;
			uint8_t     Volume;
			uint8_t     Panning;
			uint8_t     VolumeSwing;
			uint8_t     PanningSwing;
		} V2;
		struct
		{
			uint8_t     Dummy;
			uint8_t     EnvFlags;
			uint8_t     EnvLoopStart;
			uint8_t     EnvLoopEnd;
			uint8_t     EnvSustainStart;
			uint8_t     EnvSustainEnd;
			uint16_t    Dummy2;
			uint16_t    Fadeout;
			uint8_t     NewNoteAction;
			uint8_t     DuplicateCheck;
		} V1;
	} Info;
	uint32_t    Dummy2;
	uint8_t     Name[26];
	uint8_t     FilterCutoff;
	uint8_t     FilterResonance;
	uint8_t     MidiChannel;
	uint8_t     MidiProgram;
	uint16_t    MidiBank;
	uint8_t     NotesMap[120][2];
	union
	{
		struct
		{
			uint8_t     VolEnvFlags;
			uint8_t     VolEnvPoints;
			uint8_t     VolEnvLoopStart;
			uint8_t     VolEnvLoopEnd;
			uint8_t     VolEnvSustainStart;
			uint8_t     VolEnvSustainEnd;
			uint8_t     VolEnvValues[75];
			uint8_t     VolEnvDummy;
			uint8_t     PanEnvFlags;
			uint8_t     PanEnvPoints;
			uint8_t     PanEnvLoopStart;
			uint8_t     PanEnvLoopEnd;
			uint8_t     PanEnvSustainStart;
			uint8_t     PanEnvSustainEnd;
			uint8_t     PanEnvValues[75];
			uint8_t     PanEnvDummy;
			uint8_t     PitchEnvFlags;
			uint8_t     PitchEnvPoints;
			uint8_t     PitchEnvLoopStart;
			uint8_t     PitchEnvLoopEnd;
			uint8_t     PitchEnvSustainStart;
			uint8_t     PitchEnvSustainEnd;
			uint8_t     PitchEnvValues[75];
			uint8_t     PitchEnvDummy;
			uint32_t    Dummy3;
		} V2;
		struct
		{
			uint8_t     VolValues[200];
			uint8_t     VolEnvValues[50];
		} V1;
	} Env;
} LInst;

typedef struct
{
	uint32_t    TAG;
	uint8_t     Filename[12];
	uint8_t     Dummy;
	uint8_t     ScaleVolume;
	uint8_t     Flags;
	uint8_t     Volume;
	uint8_t     Name[26];
	uint8_t     Convert;
	uint8_t     Panning;
	uint32_t    Length;
	uint32_t    LoopStart;
	uint32_t    LoopEnd;
	uint32_t    Frequency;
	uint32_t    SustainStart;
	uint32_t    SustainEnd;
	uint32_t    DataPtr;
	uint8_t     VibratoSpeed;
	uint8_t     VibratoDepth;
	uint8_t     VibratoRate;
	uint8_t     VibratoType;
} LSmp;

#define IT_Flags_Stereo           0x01
#define IT_Flags_Vol0Optimisation 0x02
#define IT_Flags_Instruments      0x04
#define IT_Flags_Linear           0x08
#define IT_Flags_OldEffects       0x10
#define IT_Flags_LinkEFG          0x20
#define IT_Flags_UseMidiPitchCtrl 0x40
#define IT_Flags_EmbedMidiConfig  0x80
#define IT_Flags_ExtFilterRange 0x1000 // ModPlug extension?

#define Song_Flags_NewEffects   Song_Flag_ITEnvelopes


#define NoteDelta (Note_Central-12*5)

static const uint8_t PortamentoTable[10] = {0, 1, 4, 8, 16, 32, 64, 96, 128, 255};

static const uint8_t Tag_IMPM[] = "IMPM";
static const uint8_t Tag_IMPI[] = "IMPI";
//static const uint8_t Tag_IMPS[] = "IMPS";

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

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);
}

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

	switch(effect)
	{
		case 0: // no cmd
		break;
		case 1: // A, Global: Set speed
		{
			if (value)
				Pattern_AddGlbEffect_Frames(pSong, gcmd_frames_set, value);
			else
				SysLog_BadEffectValue(pSong, 0, oeffect, ovalue);
		}
		break;
		case 2: // B, Global: Jump
		{
			Pattern_AddGlbEffect_Position(pSong, gcmd_pos_jump, value);
		}
		break;
		case 3: // C, Global: Break
		{
			Pattern_AddGlbEffect_Position(pSong, gcmd_pos_break, value);
		}
		break;
		case 4: // D, Volume: Slide
		{
			if (value == 0)
			{
				// D00 repeat last slide
				Pattern_AddEffect_NoteVolume(pSong, cmd_last_slide, 0);
			}
			else if (!(value & 0x0f))
			{
				// Dx0 slide up
				effect = cmd_upmem1 | flag_cmd_slide_frameN0;
				value &= 0xf0;
				if (value == 0xf0) effect |= flag_cmd_slide_frame0;

				Pattern_AddEffect_NoteVolume(pSong, effect, value >> 2);
			}
			else if (!(value & 0xf0))
			{
				// D0y slide down
				effect = cmd_downmem1 | flag_cmd_slide_frameN0;
				value &= 0x0f;
				if (value == 0x0f) effect |= flag_cmd_slide_frame0;

				Pattern_AddEffect_NoteVolume(pSong, effect, value << 2);
			}
			else if ((value & 0x0f) == 0x0f)
			{
				// DxF up
				effect = cmd_upmem1 | flag_cmd_slide_frame0;
				value &= 0xf0;

				Pattern_AddEffect_NoteVolume(pSong, effect, value >> 2);
			}
			else if ((value & 0xf0) == 0xf0)
			{
				// DFy down
				effect = cmd_downmem1 | flag_cmd_slide_frame0;
				value &= 0x0f;

				Pattern_AddEffect_NoteVolume(pSong, effect, value << 2);
			}
			else
				SysLog_BadEffectValue(pSong, 0, oeffect, ovalue);
		}
		break;
		case 5: // E, Pitch: Slide down
		{
			if (!value)
			{
				// E00 slide at last slide type and value
				Pattern_AddEffect_Pitch(pSong, cmd_downmem1, 0);
			}
			else
			{
				pitch_slide(pSong, cmd_downmem1, value);

				if (pSong->Flags & Song_Flag_LinkSlideAndPorta)
					Pattern_AddEffect_Pitch(pSong
						, cmd_pitch_memportamento + flag_cmd_slide_frameN0
						, value << 2);
			}
		}
		break;
		case 6: // F, Pitch: Slide up
		{
			if (!value)
			{
				// F00 slide at last slide type and value
				Pattern_AddEffect_Pitch(pSong, cmd_upmem1, 0);
			}
			else
			{
				pitch_slide(pSong, cmd_upmem1, value);

				if (pSong->Flags & Song_Flag_LinkSlideAndPorta)
					Pattern_AddEffect_Pitch(pSong
						, cmd_pitch_memportamento + flag_cmd_slide_frameN0
						, value << 2);
			}
		}
		break;
		case 7: // G, Pitch: Portamento
		{
			if (!value)
			{
				// G00 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);

				if (pSong->Flags & Song_Flag_LinkSlideAndPorta)
					pitch_slide(pSong, cmd_pitch_memslide, value);
			}
		}
		break;
		case 8: // H, Pitch: Vibrato
		{
			uint32_t speed;

			speed = value >> 4;
			value = (value & 0xf) << 2;

			// Vibrate between [-y,+y] (new) or [-2*y,+2*y] (old)
			if (pSong->Flags & Song_Flags_NewEffects)
				effect = cmd_vibrato_depth | flag_cmd_vibrato_frame0N0;
			else
			{
				effect = cmd_vibrato_depth | flag_cmd_vibrato_frameN0;
				value <<= 1;
			}

			if (speed) Pattern_AddEffect_Pitch(pSong, cmd_vibrato_speed, speed << 2);
			Pattern_AddEffect_Pitch(pSong, effect, value);
		}
		break;
		case 9: // I, Volume: Tremor
		{
			uint32_t val;
			uint32_t cmd;

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

			if (pSong->Flags & Song_Flags_NewEffects)
				cmd = cmd_volume_tremor_IT;
			else
				cmd = cmd_volume_tremor_ITOld;

			Pattern_AddEffect_NoteVolume(pSong, cmd, Effect_Tremor(value, val));
		}
		break;
		case 0xa: // J, Pitch: Arpeggio
		{
			uint32_t val;

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

			Pattern_AddEffect_Arpeggio(pSong, value, val, cmd_arpeggio_type_NHL);
		}
		break;
		case 0xb: // K, Pitch: Vibrato 0 - Volume: Slide
		{
			convert_effect_IT(pSong, pConfig, 0x8, 0);
			convert_effect_IT(pSong, pConfig, 0x4, value);
		}
		break;
		case 0xc: // L, Pitch: Portamento 0 - Volume: Slide
		{
			convert_effect_IT(pSong, pConfig, 0x7, 0);
			convert_effect_IT(pSong, pConfig, 0x4, value);
		}
		break;
		case 0xd: // M, Channel Volume: Set
		{
			if (value <= 64)
				Pattern_AddEffect_ChannelVolume(pSong, cmd_set, value << 2);
			else
				SysLog_BadEffectValue(pSong, 0, oeffect, ovalue);
		}
		break;
		case 0xe: // N, Channel Volume: Slide
		{
			if (value == 0)
			{
				//N00 repeat last slide
				Pattern_AddEffect_ChannelVolume(pSong, cmd_last_slide, 0);
			}
			else if (!(value & 0x0f))
			{
				// Nx0 slide up
				effect = cmd_upmem1 | flag_cmd_slide_frameN0;
				value &= 0xf0;
				Pattern_AddEffect_ChannelVolume(pSong, effect, value >> 2);
			}
			else if (!(value & 0xf0))
			{
				// N0y slide down
				effect = cmd_downmem1 | flag_cmd_slide_frameN0;
				value &= 0x0f;
				Pattern_AddEffect_ChannelVolume(pSong, effect, value << 2);
			}
			else if ((value & 0x0f) == 0x0f)
			{
				// NxF up
				effect = cmd_upmem1 | flag_cmd_slide_frame0;
				value &= 0xf0;
				Pattern_AddEffect_ChannelVolume(pSong, effect, value >> 2);
			}
			else if ((value & 0xf0) == 0xf0)
			{
				// NFy down
				effect = cmd_downmem1 | flag_cmd_slide_frame0;
				value &= 0x0f;
				Pattern_AddEffect_ChannelVolume(pSong, effect, value << 2);
			}
			else
				SysLog_BadEffectValue(pSong, 0, oeffect, ovalue);
		}
		break;
		case 0xf: // O, Note: Set sample offset
		{
			if (value)
			{
				// 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 | value);
			}

			// only if a note is defined at the same time
			if (pConfig->Note) Pattern_AddEffect_Instrument(pSong, cmd_sample_set_offset, 0xf00);
		}
		break;
		case 0x10: // P, Panning: Slide (as if panning in range [0-64])
		{
			if (value == 0)
			{
				// P00 repeat last slide
				Pattern_AddEffect_Panning(pSong, cmd_last_slide, 0);
			}
			else if (!(value & 0x0f))
			{
				// Px0 slide left
				effect = cmd_downmem1 | flag_cmd_slide_frameN0;
				value &= 0xf0;
				Pattern_AddEffect_Panning(pSong, effect, value >> 2);
			}
			else if (!(value & 0xf0))
			{
				// P0y slide right
				effect = cmd_upmem1 | flag_cmd_slide_frameN0;
				value &= 0x0f;
				Pattern_AddEffect_Panning(pSong, effect, value << 2);
			}
			else if ((value & 0x0f) == 0x0f)
			{
				// PxF left
				effect = cmd_downmem1 | flag_cmd_slide_frame0;
				value &= 0xf0;
				Pattern_AddEffect_Panning(pSong, effect, value >> 2);
			}
			else if ((value & 0xf0) == 0xf0)
			{
				// PFy right
				effect = cmd_upmem1 | flag_cmd_slide_frame0;
				value &= 0x0f;
				Pattern_AddEffect_Panning(pSong, effect, value << 2);
			}
			else
				SysLog_BadEffectValue(pSong, 0, oeffect, ovalue);
		}
		break;
		case 0x11: // Q, 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 = 1;
			Pattern_AddEffect_RetrigCounter(pSong, cmd_retrig_count_type_IT, value);
			Pattern_AddEffect_Instrument(pSong, cmd_sample_note_retrig, eff);
		}
		break;
		case 0x12: // R, Volume: Vibrato (alias Tremolo)
		{
			uint32_t speed;

			speed = value >> 4;
			value &= 0xf;
			// Vibrate between [-2*y,+2*y]
			value <<= 3; // << 2 for range conversion, << 1 for depth (same as S3M)

			if (pSong->Flags & Song_Flags_NewEffects)
				effect = cmd_vibrato_depth | flag_cmd_vibrato_frame0N0;
			else
				effect = cmd_vibrato_depth | flag_cmd_vibrato_frameN0;

			if (speed) Pattern_AddEffect_NoteVolume(pSong, cmd_vibrato_speed, speed << 2);
			Pattern_AddEffect_NoteVolume(pSong, effect, value);
		}
		break;
		case 0x13: // S, +- protracker effects E
		{
			effect = value >> 4;
			value &= 0xf;

			switch(effect)
			{
				case 3: // S3, Pitch: Vibrato type
				{
					switch(value)
					{
						case 1: value = vibrato_type_rampdown; break;
						case 2: value = vibrato_type_halfsquare; break;
						case 3: value = vibrato_type_random; break;
						default: value = vibrato_type_sin; break;
					}
					if (pSong->Flags & Song_Flags_NewEffects)
						value ^= vibrato_type_inverted;
					Pattern_AddEffect_Pitch(pSong, cmd_vibrato_type, value);
				}
				break;
				case 4: // S4, Volume: Vibrato type
				{
					switch(value)
					{
						case 1: value = vibrato_type_rampdown; break;
						case 2: value = vibrato_type_halfsquare; break;
						case 3: value = vibrato_type_random; break;
						default: value = vibrato_type_sin; break;
					}
					Pattern_AddEffect_NoteVolume(pSong, cmd_vibrato_type, value);
				}
				break;
				case 5: // S5, Panning: Vibrato type
				{
					switch(value)
					{
						case 1: value = vibrato_type_rampdown; break;
						case 2: value = vibrato_type_halfsquare; break;
						case 3: value = vibrato_type_random; break;
						default: value = vibrato_type_sin; break;
					}
					Pattern_AddEffect_Panning(pSong, cmd_vibrato_type, value);
				}
				break;
				case 6: // S6, Global: Delay row for x ticks
				{
					Pattern_AddGlbEffect_Frames(pSong, gcmd_frames_delay, value);
				}
				break;
				case 7: // S7, Note: actions and envelopes
				{
					switch(value)
					{
						case 0: // S70, Past note: Cut
						{
							Pattern_AddEffect_Instrument(pSong, cmd_sample_past_note_action_cut, 0);
						}
						break;
						case 1: // S71, Past note: Off
						{
							Pattern_AddEffect_Instrument(pSong, cmd_sample_past_note_action_off, 0);
						}
						break;
						case 2: // S72, Past note: Fade
						{
							Pattern_AddEffect_Instrument(pSong, cmd_sample_past_note_action_fade, 0);
						}
						break;
						case 3: // S73, New note: Cut
						{
							Pattern_AddEffect_Instrument(pSong, cmd_sample_new_note_action_cut, 0);
						}
						break;
						case 4: // S74, New note: Continue
						{
							Pattern_AddEffect_Instrument(pSong, cmd_sample_new_note_action_continue, 0);
							pSong->Flags |= Song_Flag_Virtual;
						}
						break;
						case 5: // S75, New note: Off
						{
							Pattern_AddEffect_Instrument(pSong, cmd_sample_new_note_action_off, 0);
							pSong->Flags |= Song_Flag_Virtual;
						}
						break;
						case 6: // S76, New note: Fade
						{
							Pattern_AddEffect_Instrument(pSong, cmd_sample_new_note_action_fade, 0);
							pSong->Flags |= Song_Flag_Virtual;
						}
						break;
						case 7: // S77, Volume: Envelope off
						case 8: // S78, volume: Envelope on
						{
							Pattern_AddEffect_NoteVolume(pSong, cmd_volume_envelope, value - 7);
						}
						break;
						case 9:   // S79, Panning: Envelope off
						case 0xa: // S7A, Panning: Envelope on
						{
							Pattern_AddEffect_Panning(pSong, cmd_panning_envelope, value - 9);
						}
						break;
						case 0xb: // S7B, Pitch/Filter: Envelope off
						case 0xc: // S7C, Pitch/Filter: Envelope on
						{
							Pattern_AddEffect_Pitch(pSong, cmd_pitch_envelope, value - 0xb);
							Pattern_AddEffect_Pitch(pSong, cmd_filter_envelope, value - 0xb);
						}
						break;
						default:
						{
							SysLog_BadEffect(pSong, 0, oeffect, ovalue);
						}
					}
				}
				break;
				case 8: // S8, Panning: Set
				{
					value += value << 4;
					if (value >= 128) value++;
					Pattern_AddEffect_Panning(pSong, cmd_set, value);
				}
				break;
				case 9: // S91, Panning: Surround
				{
					if (value == 1)
						Pattern_AddEffect_Panning(pSong, cmd_set, Panning_Surround);
					else
					{
						SysLog_BadEffect(pSong, 0, oeffect, ovalue);
					}
				}
				break;
				case 0xa: // SA, Note: Set sample high offset value
				{
					Pattern_AddEffect_Instrument(pSong, cmd_sample_set_offset, 0x200 | value);
				}
				break;
				case 0xb: // SB, Global: loop in column
				{
					Pattern_AddGlbEffect_Position(pSong, gcmd_pos_loop | value, pConfig->Channel);
				}
				break;
				case 0xc: // SC, Note: Note cut after x ticks
				{
					if (!value) value = 1; // 0 behaves as 1
					Pattern_AddEffect_Instrument(pSong, cmd_sample_note_action_cut, value);
				}
				break;
				case 0xd: // SD, Note: Delay note for x ticks
				{
					if (!value) value = 1; // 0 behaves as 1
					Pattern_AddEffect_Instrument(pSong, cmd_sample_note_delay, value);
				}
				break;
				case 0xe: // SE, Global: delay row for x rows, FIRST ont take precedence
				{
					Pattern_KeepFirstGlbEffect_Frames(pSong, gcmd_frames_repeat, value);
				}
				break;
				case 0xf: // SF, Midi
				{
					// Not treated here
				}
				break;
				default:
				{
					SysLog_BadEffect(pSong, 0, oeffect, ovalue);
				}
			}
		}
		break;
		case 0x14: // T, Global: Tempo
		{
			if (!value)
			{
				// T00 repeat last slide (Should be last T)
				Pattern_AddGlbEffect_Tempo(pSong, cmd_last_slide, 0);
			}
			else if (value >= 0x20)
			{
				// set
				Pattern_AddGlbEffect_Tempo(pSong, cmd_set, value);
				// should reset last slide to 0
			}
			else if (value >= 0x10)
			{
				// slide up, Memory
				if (value) Pattern_AddGlbEffect_Tempo(pSong, cmd_upmem1+flag_cmd_slide_frameN0, (value - 0x10));
				// else should reset last slide to 0
			}
			else
			{
				// slide down, Memory
				Pattern_AddGlbEffect_Tempo(pSong, cmd_downmem1+flag_cmd_slide_frameN0, value);
			}
		}
		break;
		case 0x15: // U, Pitch: Fine vibrato
		{
			uint32_t speed;

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

			// Vibrate between [-y,+y] (new) or [-2*y,+2*y] (old)
			if (pSong->Flags & Song_Flags_NewEffects)
				effect = cmd_vibrato_depth | flag_cmd_vibrato_frame0N0;
			else
			{
				effect = cmd_vibrato_depth | flag_cmd_vibrato_frameN0;
				value <<= 1;
			}

			if (speed) Pattern_AddEffect_Pitch(pSong, cmd_vibrato_speed, speed << 2);
			Pattern_AddEffect_Pitch(pSong, effect, value);
		}
		break;
		case 0x16: // V, Global Volume: Set
		{
			if (value <= 128)
				Pattern_AddGlbEffect_Volume(pSong, cmd_set, value << 1);
			else
				SysLog_BadEffectValue(pSong, 0, oeffect, ovalue);
		}
		break;
		case 0x17: // W, Global Volume: Slide
		{
			if (value == 0)
			{
				//W00 repeat last slide
				Pattern_AddGlbEffect_Volume(pSong, cmd_last_slide, 0);
			}
			else if (!(value & 0x0f))
			{
				// Wx0 slide up
				effect = cmd_upmem1 | flag_cmd_slide_frameN0;
				value &= 0xf0;
				// max global volume is 128, not 64
				Pattern_AddGlbEffect_Volume(pSong, effect, value >> 3);
			}
			else if (!(value & 0xf0))
			{
				// W0y slide down
				effect = cmd_downmem1 | flag_cmd_slide_frameN0;
				value &= 0x0f;
				// max global volume is 128, not 64
				Pattern_AddGlbEffect_Volume(pSong, effect, value << 1);
			}
			// Note: Bug in IT? In opposition to other slides WFF is slide down
			else if ((value & 0xf0) == 0xf0)
			{
				// WFy down
				effect = cmd_downmem1 | flag_cmd_slide_frame0;
				value &= 0x0f;
				// max global volume is 128, not 64
				Pattern_AddGlbEffect_Volume(pSong, effect, value << 1);
			}
			else if ((value & 0x0f) == 0x0f)
			{
				// WxF up
				effect = cmd_upmem1 | flag_cmd_slide_frame0;
				value &= 0xf0;
				// max global volume is 128, not 64
				Pattern_AddGlbEffect_Volume(pSong, effect, value >> 3);
			}
			else
				SysLog_BadEffectValue(pSong, 0, oeffect, ovalue);
		}
		break;
		case 0x18: // X, Panning: Set
		{
			if (value >= 128) value++;
			Pattern_AddEffect_Panning(pSong, cmd_set, value);
		}
		break;
		case 0x19: // Y, Panning: Vibrato (alias Panbrello)
		{
			uint32_t speed;

			effect = cmd_vibrato_depth | flag_cmd_vibrato_frame0N0;

			speed = value >> 4;
			// Vibrate between [-2*y,+2*y]
			value = (value & 0xf) << 3;

			if (speed) Pattern_AddEffect_Panning(pSong, cmd_vibrato_speed, speed); // 4x times slower
			Pattern_AddEffect_Panning(pSong, effect, value);
		}
		break;
		case 0x1a: // Z, Midi macro
		{
			// Not treated here
		}
		break;
		default:
		{
			SysLog_BadEffect(pSong, 0, oeffect, ovalue);
		}
	}
}

typedef struct
{
	uint8_t Channel;
	uint8_t Info;
	uint8_t Note;
	uint8_t Instrument;
	uint8_t VolumeCommand;
	uint8_t Effect;
	uint8_t Value;
	uint8_t SValue; // For S00 command
	uint8_t SFValue; // For SFx command
} ChannelInfo;

static const _kernel_oserror* Pattern_IT
	( SongHdr* pSong
	, uint32_t i
	, Pattern* pPattern
	, const uint8_t* pPos
	, const uint8_t* pEnd
	, LoaderChannelData* pConfig
	)
{
	const _kernel_oserror* err = NULL;
	uint32_t        val;
	int             j;
	const uint8_t*  pPos2;
	int             channel, lastchannel;
	uint32_t        Note, Inst, Info;
	ChannelInfo*    pChInfo;
	ChannelInfo     ChInfo[65];

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

	for (j = 0; j < pSong->Channels; j++)
	{
		pChInfo = &ChInfo[j];
		pChInfo->Channel = j;
		pChInfo->Info = 0;
		pChInfo->Note = 0;
		pChInfo->Instrument = 0;
		pChInfo->VolumeCommand = 0;
		pChInfo->Effect = 0;
		pChInfo->Value = 0;
		pChInfo->SValue = 0;
		pChInfo->SFValue = 0;
	}

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

		lastchannel = -1;

		while (pPos < pEnd)
		{
			// read channel nr and flag
			val = *pPos++;
			// next row mark?
			if (!val) break;
			channel = (val & 0x7f) - 1;

			// will we remain within pattern limits while reading channel info?
			if (val & 0x80)
			{
				if (pPos >= pEnd)
				{
					SysLog_FileCorrupted(SysLog_Medium, pSong, - 1, "Pattern truncated");
					break;
				}
				Info = *pPos++;
				pPos2 = pPos;
				if (Info & 0x01) pPos2++;
				if (Info & 0x02) pPos2++;
				if (Info & 0x04) pPos2++;
				if (Info & 0x08) pPos2 += 2;
				if (pPos2 > pEnd)
				{
					SysLog_FileCorrupted(SysLog_Medium, pSong, -1, "Pattern truncated");
					pPos = pEnd;
					break;
				}
			}
			else
			{
				Info = 0;
				pPos2 = pPos;
			}

			if ((channel <= lastchannel)
			||  (channel >= pSong->Channels))
			{
				if (channel <= lastchannel)
					SysLog_FileCorrupted(SysLog_High, pSong, - (pEnd - pPos + 1), "Invalid channel %d order in pattern", channel);
				else
					SysLog_FileCorrupted(SysLog_Low, pSong, - (pEnd - pPos + 1), "Invalid channel %d in pattern", channel);
				pPos = pPos2;
			}
			else
			{
				lastchannel = channel;
				pChInfo = &ChInfo[channel];
				// read channel info byte
				if (val & 0x80)
					pChInfo->Info = Info;
				else
					Info = pChInfo->Info;
				// Read channel values
				if (Info & 0x01) pChInfo->Note = *pPos++;
				if (Info & 0x02) pChInfo->Instrument = *pPos++;
				if (Info & 0x04) pChInfo->VolumeCommand = *pPos++;
				if (Info & 0x08) pChInfo->Effect = *pPos++;
				if (Info & 0x08) pChInfo->Value = *pPos++;
				// Treat note/Instrument
				if (Info & 0x11)
				{
					switch(pChInfo->Note)
					{
						case 255: Note = Note_Off; break;
						case 254: Note = Note_Cut; break;
						default:
						{
							if (pChInfo->Note >= 120)
							{
								SysLog_BadNote(pSong, 0, channel, pChInfo->Note);
								Note = 0;
							}
							else
								Note = pChInfo->Note + NoteDelta;
						}
					}
				}
				else Note = 0;
				if (Info & 0x22) Inst = pChInfo->Instrument;
				else Inst = 0;
				err = Loaders_StartChannel(pSong, channel, Note, Inst);
				if (err) return err;

				// Treat volume column before effect
				if (Info & 0x44)
				{
					val = pChInfo->VolumeCommand;
					if (val <= 64)
					{
						// [0-64] volume
						Pattern_AddEffect_NoteVolume(pSong, cmd_set, val << 2);
					}
					else if (val <= 74)
					{
						// [65-74] fine volume up
						Pattern_AddEffect_NoteVolume(pSong
							, cmd_upmem2+flag_cmd_slide_frame0
							, (val - 65) << 2);
					}
					else if (val <= 84)
					{
						// [75-84] fine volume down
						Pattern_AddEffect_NoteVolume(pSong
							, cmd_downmem2+flag_cmd_slide_frame0
							, (val - 75) << 2);
					}
					else if (val <= 94)
					{
						// [85-94] volume slide up
						Pattern_AddEffect_NoteVolume(pSong
							, cmd_upmem2+flag_cmd_slide_frameN0
							, (val - 85) << 2);
					}
					else if (val <= 104)
					{
						// [95-104] volume slide down
						Pattern_AddEffect_NoteVolume(pSong
							, cmd_downmem2+flag_cmd_slide_frameN0
							, (val - 95) << 2);
					}
					else if (val <= 114)
					{
						// [105-114] pitch slide down, 4x normal
						Pattern_AddEffect_Pitch(pSong
							, cmd_downmem1+flag_cmd_slide_frameN0
							, (val - 105) << 4);
					}
					else if (val <= 124)
					{
						// [115-124] pitch slide up, 4x normal
						Pattern_AddEffect_Pitch(pSong
							, cmd_upmem1+flag_cmd_slide_frameN0
							, (val - 115) << 4);
					}
					else if (val < 128)
					{
						SysLog_BadEffect(pSong, 0, 256, val);
					}
					else if (val <= 192)
					{
						// [128-192] panning
						Pattern_AddEffect_Panning(pSong, cmd_set, (val - 128) << 2);
					}
					else if (val <= 202)
					{
						// [193-202] portamento
						Pattern_AddEffect_Pitch(pSong
							, cmd_portamento+flag_cmd_slide_frameN0
							, PortamentoTable[val - 193] << 2);
					}
					else if (val <= 212)
					{
						uint32_t effect;
						val = (val - 203) << 2;

						// [203-212] vibrato with depth
						if (pSong->Flags & Song_Flags_NewEffects)
							effect = cmd_vibrato_depth | flag_cmd_vibrato_frame0N0;
						else
						{
							effect = cmd_vibrato_depth | flag_cmd_vibrato_frameN0;
							val <<= 1;
						}
						Pattern_AddEffect_Pitch(pSong, effect, val);
					}
					else
					{
						SysLog_BadEffect(pSong, 0, 256, val);
					}
				}

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

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

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

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

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

	return Loaders_EndPattern(pSong, pPattern);
}

typedef union
{
	uint32_t u;
	int s;
} tvalue;

static void Load_Packed_Sample_8bit(uint8_t* pOut, uint8_t* pOutEnd, const uint8_t* pIn, uint8_t* pInEnd, bool it215)
{
	uint32_t    bits = 9;
	tvalue      bvalue;
	int         svalue = 0;
	int         avalue = 0;
	uint32_t    read = 0;
	uint32_t    right = 32 - bits;

	pInEnd[0] = 0;

	while ((pOut < pOutEnd) && (pIn < pInEnd))
	{
		// read new value
		bvalue.u = pIn[0] + (pIn[1] << 8);
		bvalue.u >>= read;
		bvalue.u <<= right;
		// update pointer and nr of bits read
		read += bits;
		pIn += read >> 3;
		read &= 0x7;

		if (bits < 7)
		{
			// depths 1 to 6
			// change depth ?
			if (bvalue.u == 0x80000000)
			{
				// read new depth (3 bits)
				bvalue.u = pIn[0] + (pIn[1] << 8);
				bvalue.u >>= read;
				bvalue.u &= 0x7;
				bvalue.u++;
				if (bvalue.u < bits) bits = bvalue.u;
				else bits = bvalue.u + 1;
				right = 32 - bits;
				// update pointer and nr of bits read
				read += 3;
				pIn += read >> 3;
				read &= 0x7;

				// loop
				continue;
			}
			bvalue.s >>= 8 - bits;
		}
		else if (bits == 7)
		{
			if ((bvalue.u >= 0x78000000)
			&&  (bvalue.u <= 0x86000000))
			{
				bvalue.u >>= right;
				bits = bvalue.u - 0x3B;
				if (bits >= 7) bits++;
				right = 32 - bits;
				// loop
				continue;
			}
			bvalue.s >>= 1;
                }
                else if (bits == 8)
		{
			if ((bvalue.u >= 0x7C000000)
			&&  (bvalue.u <= 0x83000000))
			{
				bvalue.u >>= right;
				bits = bvalue.u - 0x7B;
				if (bits >= 8) bits++;
				right = 32 - bits;
				// loop
				continue;
			}
                }
                else // bits > 8
		{
			if (bvalue.u & 0x80000000)
			{
				bvalue.u >>= right;
				bits = bvalue.u & 0x7;
				bits++;
				right = 32 - bits;
				// loop
				continue;
			}
			bvalue.u <<= 1;
                }

		svalue += bvalue.s;
		avalue += svalue;
		*pOut++ = (it215 ? avalue : svalue) >> 24;
	}

	while (pOut < pOutEnd)
		*pOut++ = 0;
}

static void Load_Packed_Sample_16bit(uint8_t* pOut, uint8_t* pOutEnd, const uint8_t* pIn, uint8_t* pInEnd, bool it215)
{
	uint32_t    bits = 17;
	tvalue      bvalue;
	int         svalue = 0;
	int         avalue = 0;
	uint32_t    read = 0;
	uint32_t    right = 32 - bits;

	pInEnd[0] = 0;
	pInEnd[1] = 0;

	while ((pOut < pOutEnd) && (pIn < pInEnd))
	{
		// read new value
		bvalue.u = pIn[0] + (pIn[1] << 8) + (pIn[2] << 16);
		bvalue.u >>= read;
		bvalue.u <<= right;
		// update pointer and nr of bits read
		read += bits;
		pIn += read >> 3;
		read &= 0x7;

		if (bits < 7)
		{
			// depths 1 to 6
			// change depth ?
			if (bvalue.u == 0x80000000)
			{
				// read new depth (4 bits)
				bvalue.u = pIn[0] + (pIn[1] << 8);
				bvalue.u >>= read;
				bvalue.u &= 0xf;
				bvalue.u++;
				if (bvalue.u < bits) bits = bvalue.u;
				else bits = bvalue.u + 1;
				right = 32 - bits;
				// update pointer and nr of bits read
				read += 4;
				pIn += read >> 3;
				read &= 0x7;

				// loop
				continue;
			}
			bvalue.s >>= 16 - bits;
		}
		else if (bits < 17)
		{
			uint32_t val = 0x80000000 - (0x8 << right);
			if ((bvalue.u <= (0x80000000 + (0x7 << right)))
			&&  (bvalue.u >= val))
			{
				bvalue.u -= val;
				bvalue.u >>= right;
				bvalue.u++;
				if (bvalue.u < bits) bits = bvalue.u;
				else bits = bvalue.u + 1;
				right = 32 - bits;
				// loop
				continue;
			}
			bvalue.s >>= 16 - bits;
                }
                else
		{
			if (bvalue.u & 0x80000000)
			{
				bvalue.u >>= right;
				bits = bvalue.u & 0xf;
				bits++;
				right = 32 - bits;
				// loop
				continue;
			}
			bvalue.u <<= 1;
                }

		svalue += bvalue.s;
		avalue += svalue;
		if (it215)
		{
			*pOut++ = avalue >> 16;
			*pOut++ = avalue >> 24;
		}
		else
		{
			*pOut++ = svalue >> 16;
			*pOut++ = svalue >> 24;
		}
	}

	while (pOut < pOutEnd)
		*pOut++ = 0;
}

static const _kernel_oserror* Loader_ReadPackedSample(SongHdr* pSong, Sample* pSmp, bool it215)
{
	const _kernel_oserror* err;
	uint32_t Size = pSmp->Size;
	uint32_t BSize;
	uint32_t CSize;
	uint32_t RSize;
	uint8_t* pData;
	uint8_t* pCBlock = NULL;

	if (pSmp->Type & Smp_Type_Stereo) Size <<= 1;
	if (pSmp->Type & Smp_Type_16bit) Size <<= 1;

	err = Loaders_Alloc(pSong, (void**) &pSmp->Ptr, Size + 4);
	if (err) return err;

	pData = pSmp->Ptr;
	RSize = Size;

	while(Size > 0)
	{
		// loop on 32K chunks
		if (Size > 32*1024) BSize = 32*1024;
		else BSize = Size;

		// free previous packet
		Loaders_Free(pSong, pCBlock);
		pCBlock = NULL;

		// read packet length
		err = FileLoad_ReadInt(pSong, &CSize, 2);
		if (err) break;

		// allocate new packet
		err = Loaders_Alloc(pSong, (void**) &pCBlock, CSize + 4);
		if (err) return err;
		err = FileLoad_Read(pSong, pCBlock, CSize);
		if (err) break;

		// decode packet
		if (pSmp->Type & Smp_Type_16bit)
			Load_Packed_Sample_16bit(pData, pData + BSize, pCBlock, pCBlock + CSize, it215);
		else
			Load_Packed_Sample_8bit(pData, pData + BSize, pCBlock, pCBlock + CSize, it215);

		pData += BSize;
		Size -= BSize;
	}

	if (err)
	{
		SysLog_FileCorrupted(SysLog_High, pSong, FileLoad_GetPos(pSong), "Could not read sample");
	}

	// shorten sample size with number of bytes not read
	RSize -= Size;

	if (pSmp->Type & Smp_Type_Stereo) RSize >>= 1;
	if (pSmp->Type & Smp_Type_16bit) RSize >>= 1;
	pSmp->Size = RSize;

	// clear error
	err = NULL;

	// free last packet
	Loaders_Free(pSong, pCBlock);

	return err;
}

const _kernel_oserror* Loader_IT(SongHdr* pSong)
{
	const _kernel_oserror* err = NULL;
	SubSong*        pSubSong = Song_GetSubSong(pSong, 0);
	uint32_t*       pOffsetTable = NULL;
	LHdr*           pLHdr = NULL;
	LInst*          pLInst = NULL;
	LSmp*           pLSmp = NULL;
	uint8_t*        pLTrack = NULL;
	Pattern*        pPattern;
	Sample*         pSmp;
	Instrument*     pInstr;
	ChannelDefault* pDef;
	uint32_t*       pOffset;
	uint8_t*        pc;
	uint32_t        val, Size;
	int             i, j;
	LoaderChannelData   ChConfig;

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

	pSong->Flags = Song_Flag_NewNoteOnPortamento
	        //   | Song_Flag_ITEnvelopes, reused by loader for NewEffects, we be set at the end
	             | Song_Flag_InstNoNote_Swap
	             | Song_Flag_CutOnHighPitch
	             | Song_Flag_MoveLoopAfterLoop
	             | Song_Flag_KeyoffLosesPrevNote
	             | Song_Flag_LimitTremorToRow;
	pSong->MinPitch = Note_Central -  63; // Correspond more or less to S3M period 65535
	pSong->MaxPitch = Note_Central + 128; // Correspond more or less to S3M period 1
	// Unstored samples are initialised as empty sample with max volume
	pSong->pSampleDef->DefaultVolume = 256;

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

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

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

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

	// fill comments
	if (pLHdr->MsgLength && pLHdr->MsgOffset)
	{
		pSong->CommentsLen = pLHdr->MsgLength;
		err = FileLoad_SetPos(pSong, pLHdr->MsgOffset);
		if (!err) err = FileLoad_ReadString(pSong, &pSong->pComments, pSong->CommentsLen, false);
		if (err)
		{
			SysLog_FileCorrupted(SysLog_Low, pSong, FileLoad_GetPos(pSong), "Could not read comments");
			pSong->CommentsLen = 0;
			Loaders_Free(pSong, pSong->pComments);
			pSong->pComments = NULL;
		}
	}

	pSubSong->SeqLen = pLHdr->Orders;
	if (!pSubSong->SeqLen)
	{
		err = Loaders_InvalidSong(pSong, 32, &pSubSong->SeqLen, pSubSong->SeqLen);
		goto loader_err;
	}
	pSong->Instruments = pLHdr->Instruments;
	if (pSong->Instruments > 255) // cf. Inst in pattern
	{
		err = Loaders_InvalidSong(pSong, 34, &pSong->Instruments, pSong->Instruments);
		goto loader_err;
	}
	pSong->Samples = pLHdr->Samples; // cf. notemap in instrument
	if (!pSong->Samples || (pSong->Samples > 255))
	{
		err = Loaders_InvalidSong(pSong, 36, &pSong->Samples, pSong->Samples);
		goto loader_err;
	}
	pSong->Patterns = pLHdr->Patterns;
	if (!pSong->Patterns || (pSong->Patterns > 253)) // cf. sequence
	{
		err = Loaders_InvalidSong(pSong, 38, &pSong->Patterns, pSong->Patterns);
		goto loader_err;
	}
	if (pLHdr->Flags & IT_Flags_Linear) pSong->Flags |= Song_Flag_Linear;
	if (pLHdr->Flags & IT_Flags_Instruments) pSong->Flags |= Song_Flag_Instruments;
	// Seems to be handled in contradiction with ITTECH.txt
	if (!(pLHdr->Flags & IT_Flags_LinkEFG)) pSong->Flags |= Song_Flag_LinkSlideAndPorta;
	if (!(pLHdr->Flags & IT_Flags_OldEffects)) pSong->Flags |= Song_Flags_NewEffects;

	if (pLHdr->GlobalVolume > 128)
		 pSubSong->Defaults.Volume = 256;
	else pSubSong->Defaults.Volume = pLHdr->GlobalVolume << 1;
	if (pLHdr->Speed) pSubSong->Defaults.Speed = pLHdr->Speed;
	if (pLHdr->Tempo) pSubSong->Defaults.Tempo = pLHdr->Tempo;
	pSubSong->Defaults.VibratoType = vibrato_type_sin;
	if (pSong->Flags & Song_Flags_NewEffects)
		pSubSong->Defaults.VibratoType |= vibrato_type_inverted;
	pSubSong->Defaults.TremoloType = vibrato_type_sin;
	pSubSong->Defaults.PanbrelloType = vibrato_type_sin;


	// read number of channels + channel panning settings
	pSong->Channels  = 0;

	for (i = 0, pDef = pSubSong->Defaults.ChDef; i < 64; i++, pDef++)
	{
		val = pLHdr->ChannelsPanning[i];
		if (val & 0x80)
			pDef->Mute = 1;
		if (val != 0xff)
		{
			pSong->Channels  = i + 1;
			val &= 0x7f;
			if (val <= 64)
				pDef->Panning = val << 2;
			else
				pDef->Panning = Panning_Surround;
		}
	}

	// read channel volume settings
	for (i = 0, pDef = pSubSong->Defaults.ChDef; i < pSong->Channels; i++, pDef++)
	{
		val = pLHdr->ChannelsVolume[i] << 2;
		if (val > 256)
			pDef->Volume = 256;
		else
			pDef->Volume = val;
	}

	// fill the sequence
	err = FileLoad_SetPos(pSong, sizeof(LHdr));
	if (err) goto loader_err;

	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;
		if (val >= 254) val -= 256;
		pSubSong->pSeqs[i] = val;
	}

	// read offset tables
	val = (pSong->Instruments + pSong->Samples + pSong->Patterns) << 2;
	err = Loaders_Alloc(pSong, (void**) &pOffsetTable, val);
	if (err) goto loader_err;
	err = FileLoad_Read(pSong, pOffsetTable, val);
	if (err) goto loader_err;

	memset(&ChConfig, 0, sizeof(ChConfig));
	ChConfig.SFx[0] = 0x1ff;
	for (i = 0; i < 16; i++)
		ChConfig.Zxx[i] = 0x200 + 8*i;
	if (pLHdr->Flags & IT_Flags_EmbedMidiConfig)
	{
		char midi[32];

		// 2 + n*8 of unkown stuff
		err = FileLoad_ReadInt(pSong, &val, 2);
		if (err) goto loader_err;
		err = FileLoad_Skip(pSong, val*8);
		if (err) goto loader_err;
		// 9*32 Global midi config
		err = FileLoad_Skip(pSong, 9*32);
		if (err) goto loader_err;
		// 16*32 SFx midi config
		for (i = 0; i < 16; i++)
		{
			err = FileLoad_Read(pSong, &midi, 32);
			if (err) goto loader_err;

			if (!strncmp(midi, "F0 F0 00 ", 9) || !strncmp(midi, "F0 F0 01 ", 9))
			{
				ChConfig.SFx[i] = (midi[7] - '0' + 1) << 8;
				if ((midi[9] == 'Z') || (midi[9] == 'z'))
					ChConfig.SFx[i] += 0xff;
				else
				{
					if ((midi[9] >= '0') && (midi[9] <= '9'))
						ChConfig.SFx[i] += (midi[9] - '0') << 4;
					else if ((midi[9] >= 'A') && (midi[9] <= 'F'))
						ChConfig.SFx[i] += (midi[9] - 'A' + 10) << 4;
					else
						ChConfig.SFx[i] = 0;
					if ((midi[10] >= '0') && (midi[10] <= '9'))
						ChConfig.SFx[i] += (midi[10] - '0');
					else if ((midi[10] >= 'A') && (midi[10] <= 'F'))
						ChConfig.SFx[i] += (midi[10] - 'A' + 10);
					else
						ChConfig.SFx[i] = 0;
				}
			}
		}
		// 128*32 Fxx midi config
		for (i = 0; i < 128; i++)
		{
			err = FileLoad_Read(pSong, &midi, 32);
			if (err) goto loader_err;

			if (!strncmp(midi, "F0 F0 00 ", 9) || !strncmp(midi, "F0 F0 01 ", 9))
			{
				ChConfig.Zxx[i] = (midi[7] - '0' + 1) << 8;
				if ((midi[9] >= '0') && (midi[9] <= '9'))
					ChConfig.Zxx[i] += (midi[9] - '0') << 4;
				else if ((midi[9] >= 'A') && (midi[9] <= 'F'))
					ChConfig.Zxx[i] += (midi[9] - 'A' + 10) << 4;
				else
					ChConfig.Zxx[i] = 0;
				if ((midi[10] >= '0') && (midi[10] <= '9'))
					ChConfig.Zxx[i] += (midi[10] - '0');
				else if ((midi[10] >= 'A') && (midi[10] <= 'F'))
					ChConfig.Zxx[i] += (midi[10] - 'A' + 10);
				else
					ChConfig.Zxx[i] = 0;
			}
		}
	}

	// read instruments info
	pOffset = pOffsetTable;
	for (i = 0, pInstr = pSong->pInstruments; i < pSong->Instruments; i++, pInstr++)
	{
		uint32_t inst_volume;

		// move to instrument header start
		err = FileLoad_SetPos(pSong, *pOffset++);
		if (err) goto loader_err;

		err = FileLoad_Read(pSong, pLInst, sizeof(*pLInst));
		if (err) goto loader_err;

		// check if is really instrument header
		if (pLInst->TAG != TAG(IMPI))
		{
			err = Loaders_Error(pSong, pOffset[-1], "Expected IMPI tag");
			goto loader_err;
		}

		// read filename
		err = Loaders_String(pSong, &pInstr->pFilename, &pLInst->Filename[0], sizeof(pLInst->Filename), true);
		if (err) goto loader_err;

		// read name
		err = Loaders_String(pSong, &pInstr->pName, &pLInst->Name[0], sizeof(pLInst->Name), true);
		if (err) goto loader_err;

		if (pSong->Version < 200)
		{
			Envelope* pEnv = &pInstr->VolumeEnvelope;
			// Loader_IT_Instrument_V1
			inst_volume = 256;
			// Volume envelope info
			val = pLInst->Info.V1.EnvFlags;
			pEnv->Flags = 0;
			if (val & 1) pEnv->Flags |= Envelope_Flag_On;
			if (val & 2) pEnv->Flags |= Envelope_Flag_Loop;
			if (val & 4) pEnv->Flags |= Envelope_Flag_Sustain;
			pEnv->LoopStart = pLInst->Info.V1.EnvLoopStart;
			pEnv->LoopEnd = pLInst->Info.V1.EnvLoopEnd;
			pEnv->SustainStart = pLInst->Info.V1.EnvSustainStart;
			pEnv->SustainEnd = pLInst->Info.V1.EnvSustainEnd;

			// read volume fadeout
			pInstr->FadeoutVolume = pLInst->Info.V1.Fadeout << 7;
			// read New Note Action
			val = pLInst->Info.V1.NewNoteAction;
			if (val > 3) val = 0;
			if (val) pSong->Flags |= Song_Flag_Virtual;
			switch (val)
			{
				case 0: pInstr->NewNoteAction = note_action_cut; break;
				case 1: pInstr->NewNoteAction = note_action_continue; break;
				case 2: pInstr->NewNoteAction = note_action_off; break;
				case 3: pInstr->NewNoteAction = note_action_fade; break;
			}
			// read Duplicate Check Type
			val = pLInst->Info.V1.DuplicateCheck;
			if (val > 1) pInstr->DuplicateCheckType = 0;
			else pInstr->DuplicateCheckType = val;
			// read Duplicate Note Action
			pInstr->DuplicateNoteAction = 0;

			// read volume envelope points
			err = Loaders_Alloc(pSong, (void**) &pEnv->pTable, 25<<2);
			if (err) goto loader_err;
			pEnv->Points = 0;

			for (j = 0, pc = pLInst->Env.V1.VolEnvValues; j < 25; j++, pc += 2)
			{
				uint32_t vol = pc[1] << 2;

				if (pc[0] == 0xff)
					break;

				if (vol > 256) vol = 256;
				pEnv->pTable[j] = pc[0] + (vol << 16);
				pEnv->Points++;
			}
		}
		else
		{
			// Loader_IT_Instrument_V2
			// read New Note Action
			val = pLInst->Info.V2.NewNoteAction;
			if (val > 3) val = 0;
			if (val) pSong->Flags |= Song_Flag_Virtual;
			switch (val)
			{
				case 1: pInstr->NewNoteAction = note_action_continue; break;
				case 2: pInstr->NewNoteAction = note_action_off; break;
				case 3: pInstr->NewNoteAction = note_action_fade; break;
				default: pInstr->NewNoteAction = note_action_cut; break;
			}
			// read Duplicate Check Type
			val = pLInst->Info.V2.DuplicateCheckType;
			if (val > 3) pInstr->DuplicateCheckType = 0;
			else pInstr->DuplicateCheckType = val;
			// read Duplicate Note Action
			switch(pLInst->Info.V2.DuplicateCheckAction)
			{
				case 1: pInstr->DuplicateNoteAction = note_action_off; break;
				case 2: pInstr->DuplicateNoteAction = note_action_fade; break;
				default: pInstr->DuplicateNoteAction = note_action_cut; break;
			}
			// read volume fadeout
			pInstr->FadeoutVolume = pLInst->Info.V2.Fadeout << 6;
			// read pitch-pan separation
			// -32 +32 -> -127 + 127
			pInstr->PitchPanSep = pLInst->Info.V2.PitchPanSep << 2;
			// read pitch-pan center
			val = pLInst->Info.V2.PitchPanCenter;
			if (val >= 120) pInstr->PitchPanCenter = Note_Central;
			else pInstr->PitchPanCenter = val + NoteDelta;
			// read volume
			inst_volume = pLInst->Info.V2.Volume << 1;
			// read panning
			val = pLInst->Info.V2.Panning;
			if (!(val & 0x80)) pInstr->Flags |= Inst_Flag_Set_Panning;
			val &= 0x7f;
			if (val > 64) pInstr->Panning = Panning_Surround;
			else pInstr->Panning = val << 2;
			// read volume swing, convert from % to [0-255]
			val = (pLInst->Info.V2.VolumeSwing * 656) >> 8;
			if (val > 255) pInstr->VolumeSwing = 255;
			else pInstr->VolumeSwing = val;
			// read panning swing
			val = pLInst->Info.V2.PanningSwing << 2;
			if (val > 255) pInstr->PanningSwing = 255;
			else pInstr->PanningSwing = val;
			// read Resonance Filter Info
			pInstr->FilterCutoff = pLInst->FilterCutoff;
			pInstr->FilterResonance = pLInst->FilterResonance;
			if (((pInstr->FilterCutoff & 0x80) && (pInstr->FilterCutoff != 0xff))
			||  ((pInstr->FilterResonance & 0x80) && (pInstr->FilterResonance != 0x80)))
				pSong->Flags |= Song_Flag_Filters;

			// Volume Envelope
			{
				Envelope* pEnv = &pInstr->VolumeEnvelope;

				val = pLInst->Env.V2.VolEnvFlags;
				pEnv->Flags = 0;
				if (val & 1) pEnv->Flags |= Envelope_Flag_On;
				if (val & 2) pEnv->Flags |= Envelope_Flag_Loop;
				if (val & 4) pEnv->Flags |= Envelope_Flag_Sustain;
				if (val & 8) pEnv->Flags |= Envelope_Flag_Carry;
				val = pLInst->Env.V2.VolEnvPoints;
				if (val > 25) val = 0;
				pEnv->Points = val;
				if (pEnv->Points)
				{
					pEnv->LoopStart = pLInst->Env.V2.VolEnvLoopStart;
					pEnv->LoopEnd   = pLInst->Env.V2.VolEnvLoopEnd;
					pEnv->SustainStart = pLInst->Env.V2.VolEnvSustainStart;
					pEnv->SustainEnd   = pLInst->Env.V2.VolEnvSustainEnd;
					// read envelope points
					err = Loaders_Alloc(pSong, (void**) &pEnv->pTable, pEnv->Points << 2);
					if (err) goto loader_err;
					for (j = 0, pc = pLInst->Env.V2.VolEnvValues; j < pEnv->Points; j++, pc += 3)
					{
						uint32_t val = pc[0] << 2;

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

			// Panning Envelope
			{
				Envelope* pEnv = &pInstr->PanningEnvelope;

				val = pLInst->Env.V2.PanEnvFlags;
				pEnv->Flags = 0;
				if (val & 1) pEnv->Flags |= Envelope_Flag_On;
				if (val & 2) pEnv->Flags |= Envelope_Flag_Loop;
				if (val & 4) pEnv->Flags |= Envelope_Flag_Sustain;
				if (val & 8) pEnv->Flags |= Envelope_Flag_Carry;
				val = pLInst->Env.V2.PanEnvPoints;
				if (val > 25) val = 0;
				pEnv->Points = val;
				if (pEnv->Points)
				{
					pEnv->LoopStart = pLInst->Env.V2.PanEnvLoopStart;
					pEnv->LoopEnd   = pLInst->Env.V2.PanEnvLoopEnd;
					pEnv->SustainStart = pLInst->Env.V2.PanEnvSustainStart;
					pEnv->SustainEnd   = pLInst->Env.V2.PanEnvSustainEnd;
					// read envelope points
					err = Loaders_Alloc(pSong, (void**) &pEnv->pTable, pEnv->Points << 2);
					if (err) goto loader_err;
					for (j = 0, pc = pLInst->Env.V2.PanEnvValues; j < pEnv->Points; j++, pc += 3)
					{
						int val = pc[0] << 24;

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

			// Pitch/Filter Envelope
			{
				Envelope* pEnv;

				val = pLInst->Env.V2.PitchEnvFlags;
				if (val & 0x80)
					pEnv = &pInstr->FilterEnvelope;
				else
					pEnv = &pInstr->PitchEnvelope;

				pEnv->Flags = 0;
				if (val & 1) pEnv->Flags |= Envelope_Flag_On;
				if (val & 2) pEnv->Flags |= Envelope_Flag_Loop;
				if (val & 4) pEnv->Flags |= Envelope_Flag_Sustain;
				if (val & 8) pEnv->Flags |= Envelope_Flag_Carry;
				val = pLInst->Env.V2.PitchEnvPoints;
				if (val > 25) val = 0;
				pEnv->Points = val;
				if (pEnv->Points)
				{
					pEnv->LoopStart = pLInst->Env.V2.PitchEnvLoopStart;
					pEnv->LoopEnd   = pLInst->Env.V2.PitchEnvLoopEnd;
					pEnv->SustainStart = pLInst->Env.V2.PitchEnvSustainStart;
					pEnv->SustainEnd   = pLInst->Env.V2.PitchEnvSustainEnd;
					// read envelope points
					err = Loaders_Alloc(pSong, (void**) &pEnv->pTable, pEnv->Points << 2);
					if (err) goto loader_err;
					if (pEnv == &pInstr->FilterEnvelope)
					{
						pSong->Flags |= Song_Flag_Filters;
						for (j = 0, pc = pLInst->Env.V2.PitchEnvValues; j < pEnv->Points; j++, pc += 3)
						{
							int32_t val = pc[0] << 24;

							val >>= 22;
							val += 128;
							if (val < 0) val = 0;
							if (val > 256) val = 256;
							pEnv->pTable[j] = pc[1] + (pc[2] << 8) + (val << 16);
						}
					}
					else
					{
						for (j = 0, pc = pLInst->Env.V2.PitchEnvValues; j < pEnv->Points; j++, pc += 3)
						{
							int val = pc[0] << 24;

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

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

		if (inst_volume > 256) inst_volume = 256;
		if (inst_volume > 0) inst_volume--;

		NoteMap* pn;
		for (j = 0, pn = pInstr->pNotesMap + NoteDelta; j < 120; j++, pn++)
		{
			pn->Volume = 255;
			pn->SampleNr = pLInst->NotesMap[j][1];
			pn->Pitch = (pLInst->NotesMap[j][0] + NoteDelta) << 8;
			pn->Volume = inst_volume;
		}
	}

	// read samples info
	// pOffset is still up to date
	pSmp = pSong->pSamples;
	for (i = 0; i < pSong->Samples; i++, pSmp++)
	{
		// move to sample header start
		err = FileLoad_SetPos(pSong, *pOffset++);
		if (err) goto loader_err;

		err = FileLoad_Read(pSong, pLSmp, sizeof(*pLSmp));
		if (err) goto loader_err;

		// check if is really sample header, disabled to cope with some corrupted files
/*		if (pLInst->TAG != *TAG_IMPS)
		{
			err = Loader_Corrupted(pSong);
			goto loader_err;
		}
*/
		// read filename
		err = Loaders_String(pSong, &pSmp->pFilename, &pLSmp->Filename[0], sizeof(pLSmp->Filename), true);
		if (err) goto loader_err;

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

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

		// read name
		err = Loaders_String(pSong, &pSmp->pName, &pLSmp->Name[0], sizeof(pLSmp->Name), true);
		if (err) goto loader_err;

		pSmp->Type = 0;

		// read panning
		val = pLSmp->Panning;
		if (val & 0x80) pSmp->Type |= Smp_Type_Set_Panning;
		val &= 0x7f;
		if (val > 64) pSmp->Panning = Panning_Surround;
		else pSmp->Panning = val << 2;

		// read sizes
		pSmp->Size = pLSmp->Length;
		pSmp->LoopStart = pLSmp->LoopStart;
		pSmp->LoopEnd = pLSmp->LoopEnd;
		pSmp->Frequency = 8363;
		pSmp->RelTone = Convert_RelTone(pSmp->Frequency, pLSmp->Frequency);
		pSmp->SustainStart = pLSmp->SustainStart;
		pSmp->SustainEnd = pLSmp->SustainEnd;

		// read vibrato
		pSmp->Vibrato.Speed = pLSmp->VibratoSpeed;
		pSmp->Vibrato.Depth = pLSmp->VibratoDepth << 2;
		pSmp->Vibrato.Rate = pLSmp->VibratoRate;
		switch(pLSmp->VibratoType)
		{
			case 1: pSmp->Vibrato.Type = vibrato_type_rampdown; break;
			case 2: pSmp->Vibrato.Type = vibrato_type_halfsquare; break;
			case 3: pSmp->Vibrato.Type = vibrato_type_random; break;
			default: pSmp->Vibrato.Type = vibrato_type_sin; break;
		}

		// read sample type
		val = pLSmp->Flags;
		// sample stored in file?
		if (val & 0x01)
		{
			// sample stored in file
			if (val & 0x02) pSmp->Type |= Smp_Type_16bit;
//			if (val & 0x04) pSmp->Type |= Smp_Type_Stereo; // Stereo samples are treated as mono.
//			if (val & 0x08) pSmp->Type |= Smp_Type_Packed;
			if (val & 0x10) pSmp->Type |= Smp_Type_Loop;
			if (val & 0x20) pSmp->Type |= Smp_Type_Sustain;
			if (val & 0x40) pSmp->Type |= Smp_Type_Loop_Bidi;
			if (val & 0x80) pSmp->Type |= Smp_Type_Sustain_Bidi;

			// read unsigned flag
			if (!(pLSmp->Convert & 1)) pSmp->Type |= Smp_Type_Unsigned;

			// read sample data
			err = FileLoad_SetPos(pSong, pLSmp->DataPtr);
			if (err)
			{
				SysLog_FileCorrupted(SysLog_High, pSong, FileLoad_GetPos(pSong), "Could not read sample");
				pSmp->Size = 0;
			}
			else
			{
				if (pLSmp->Flags & 0x08)
				{
					err = Loader_ReadPackedSample(pSong, pSmp, (pSong->Version >= 215) && (pLSmp->Convert & 4));
					if (err) goto loader_err;
				}
				else
				{
					err = FileLoad_ReadSample(pSong, pSmp);
					if (err) goto loader_err;
				}
			}
		}
		else
		{
			// sample not stored in file
			pSmp->Size = 0;
		}
	}

	// fill the patterns
	// pOffset still up to date
	for (i = 0, pPattern = pSong->pPatterns; i < pSong->Patterns; i++, pPattern++, pOffset++)
	{
		if (!*pOffset)
		{
			// empty pattern of 64 rows
			pPattern->Rows = 64;
			err = Loaders_EmptyPattern(pSong, pPattern);
			if (err) goto loader_err;
		}
		else
		{
			err = FileLoad_SetPos(pSong, *pOffset);
			if (err) goto loader_err;
			err = FileLoad_ReadInt(pSong, &Size, 2);
			if (err) goto loader_err;
			// read pattern length
			err = FileLoad_ReadInt(pSong, &pPattern->Rows, 2);
			if (err) goto loader_err;
			if (!pPattern->Rows || (pPattern->Rows > song_max_rows))
			{
				err = Loaders_InvalidSong(pSong, -2, &pPattern->Rows, pPattern->Rows);
				goto loader_err;
			}
			// skip pattern header
			err = FileLoad_SetPos(pSong, 8 + *pOffset);
			if (err) goto loader_err;

			if (Size)
			{
				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_IT(pSong, i, pPattern, pLTrack, pLTrack + Size, &ChConfig);
			if (err) goto loader_err;

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

	pSong->Flags |= Song_Flag_ITEnvelopes;

loader_err:
	Loaders_Free(pSong, pOffsetTable);
	Loaders_Free(pSong, pLHdr);
	Loaders_Free(pSong, pLInst);
	Loaders_Free(pSong, pLSmp);
	Loaders_Free(pSong, pLTrack);

	return err;
}
