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

/*
 * Channels: 4
 * Patterns: [1...128] single channel patterns
 * Rows    : 32
 * Orders  : [1...255]
 * Samples : 10 + 80 synths + 10*10 packs
 * Instr.  : 0
 * Tempo   : 50 frames per second
 * Speed   : [1...255].
 * Notes   : [C-0...C-3...B-4]
 * Pitch   : Amiga periods mode, base is 428 units, period is divided by 2 each octave up.
 *           Slides as x/1 units per frame.
 * Volume  : [0...3F], linear.
 * Panning : None.
 * Sampling: 8287 Hz.
 *
 * Notes:
 * Samples are not samples but volume sequence + freq sequence
 * where the sequences are a mix of commands and envelopes.
 * Impossible to translate into normal patterns so we write a player
 * which do the decoding and from it we write for each played frame a row
 * played at speed one.
 *
 * Vibrato, the code doubles vibrato according to note octaves
 * so I guess it's sort of a vibrato in linear mode.
 *
 * Vibrato, portamento (on 1.4 only) and slides seem to occur only every 2 frames (cf. NOT.B in assembler)
 */

#define NoteDelta   (Note_Central-12*4)

static const uint8_t Tag_SMOD[] = "SMOD";
static const uint8_t Tag_FC14[] = "FC14";
static const uint8_t Tag_SSMP[] = "SSMP";
static const uint8_t Typestr_FC[] = "Future Composer";

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

// Synthetic sounds
typedef struct
{
	uint16_t	offset;
	uint8_t     size;
	uint8_t     dummy;
} SynthDef;

static const SynthDef v1_synths[47] =
{ {0x0000, 0x20}, {0x0020, 0x20}, {0x0040, 0x20}, {0x0060, 0x20}
, {0x0080, 0x20}, {0x00a0, 0x20}, {0x00c0, 0x20}, {0x00e0, 0x20}
, {0x0100, 0x20}, {0x0120, 0x20}, {0x0140, 0x20}, {0x0160, 0x20}
, {0x0180, 0x20}, {0x01a0, 0x20}, {0x01c0, 0x20}, {0x01e0, 0x20}
, {0x0200, 0x20}, {0x0220, 0x20}, {0x0240, 0x20}, {0x0260, 0x20}
, {0x0280, 0x20}, {0x02a0, 0x20}, {0x02c0, 0x20}, {0x02e0, 0x20}
, {0x0300, 0x20}, {0x0320, 0x20}, {0x0340, 0x20}, {0x0360, 0x20}
, {0x0380, 0x20}, {0x03a0, 0x20}, {0x03c0, 0x20}, {0x03e0, 0x20}
, {0x0400, 0x10}, {0x0410, 0x10}, {0x0420, 0x10}, {0x0430, 0x10}
, {0x0440, 0x10}, {0x0450, 0x10}, {0x0460, 0x10}, {0x0470, 0x10}
, {0x0480, 0x20}, {0x04a0, 0x10}, {0x04b0, 0x20}, {0x04d0, 0x20}
, {0x04f0, 0x10}, {0x0500, 0x10}, {0x0510, 0x30}
};

static const uint8_t v1_waves[0x540] =
{ 0xc0, 0xc0, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0x00, 0xf8, 0xf0, 0xe8, 0xe0, 0xd8, 0xd0, 0xc8
, 0x3f, 0x37, 0x2f, 0x27, 0x1f, 0x17, 0x0f, 0x07, 0xff, 0x07, 0x0f, 0x17, 0x1f, 0x27, 0x2f, 0x37
, 0xc0, 0xc0, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0x00, 0xf8, 0xf0, 0xe8, 0xe0, 0xd8, 0xd0, 0xc8
, 0xc0, 0x37, 0x2f, 0x27, 0x1f, 0x17, 0x0f, 0x07, 0xff, 0x07, 0x0f, 0x17, 0x1f, 0x27, 0x2f, 0x37
, 0xc0, 0xc0, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0x00, 0xf8, 0xf0, 0xe8, 0xe0, 0xd8, 0xd0, 0xc8
, 0xc0, 0xb8, 0x2f, 0x27, 0x1f, 0x17, 0x0f, 0x07, 0xff, 0x07, 0x0f, 0x17, 0x1f, 0x27, 0x2f, 0x37
, 0xc0, 0xc0, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0x00, 0xf8, 0xf0, 0xe8, 0xe0, 0xd8, 0xd0, 0xc8
, 0xc0, 0xb8, 0xb0, 0x27, 0x1f, 0x17, 0x0f, 0x07, 0xff, 0x07, 0x0f, 0x17, 0x1f, 0x27, 0x2f, 0x37
, 0xc0, 0xc0, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0x00, 0xf8, 0xf0, 0xe8, 0xe0, 0xd8, 0xd0, 0xc8
, 0xc0, 0xb8, 0xb0, 0xa8, 0x1f, 0x17, 0x0f, 0x07, 0xff, 0x07, 0x0f, 0x17, 0x1f, 0x27, 0x2f, 0x37
, 0xc0, 0xc0, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0x00, 0xf8, 0xf0, 0xe8, 0xe0, 0xd8, 0xd0, 0xc8
, 0xc0, 0xb8, 0xb0, 0xa8, 0xa0, 0x17, 0x0f, 0x07, 0xff, 0x07, 0x0f, 0x17, 0x1f, 0x27, 0x2f, 0x37
, 0xc0, 0xc0, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0x00, 0xf8, 0xf0, 0xe8, 0xe0, 0xd8, 0xd0, 0xc8
, 0xc0, 0xb8, 0xb0, 0xa8, 0xa0, 0x98, 0x0f, 0x07, 0xff, 0x07, 0x0f, 0x17, 0x1f, 0x27, 0x2f, 0x37
, 0xc0, 0xc0, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0x00, 0xf8, 0xf0, 0xe8, 0xe0, 0xd8, 0xd0, 0xc8
, 0xc0, 0xb8, 0xb0, 0xa8, 0xa0, 0x98, 0x90, 0x07, 0xff, 0x07, 0x0f, 0x17, 0x1f, 0x27, 0x2f, 0x37
, 0xc0, 0xc0, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0x00, 0xf8, 0xf0, 0xe8, 0xe0, 0xd8, 0xd0, 0xc8
, 0xc0, 0xb8, 0xb0, 0xa8, 0xa0, 0x98, 0x90, 0x88, 0xff, 0x07, 0x0f, 0x17, 0x1f, 0x27, 0x2f, 0x37
, 0xc0, 0xc0, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0x00, 0xf8, 0xf0, 0xe8, 0xe0, 0xd8, 0xd0, 0xc8
, 0xc0, 0xb8, 0xb0, 0xa8, 0xa0, 0x98, 0x90, 0x88, 0x80, 0x07, 0x0f, 0x17, 0x1f, 0x27, 0x2f, 0x37
, 0xc0, 0xc0, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0x00, 0xf8, 0xf0, 0xe8, 0xe0, 0xd8, 0xd0, 0xc8
, 0xc0, 0xb8, 0xb0, 0xa8, 0xa0, 0x98, 0x90, 0x88, 0x80, 0x88, 0x0f, 0x17, 0x1f, 0x27, 0x2f, 0x37
, 0xc0, 0xc0, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0x00, 0xf8, 0xf0, 0xe8, 0xe0, 0xd8, 0xd0, 0xc8
, 0xc0, 0xb8, 0xb0, 0xa8, 0xa0, 0x98, 0x90, 0x88, 0x80, 0x88, 0x90, 0x17, 0x1f, 0x27, 0x2f, 0x37
, 0xc0, 0xc0, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0x00, 0xf8, 0xf0, 0xe8, 0xe0, 0xd8, 0xd0, 0xc8
, 0xc0, 0xb8, 0xb0, 0xa8, 0xa0, 0x98, 0x90, 0x88, 0x80, 0x88, 0x90, 0x98, 0x1f, 0x27, 0x2f, 0x37
, 0xc0, 0xc0, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0x00, 0xf8, 0xf0, 0xe8, 0xe0, 0xd8, 0xd0, 0xc8
, 0xc0, 0xb8, 0xb0, 0xa8, 0xa0, 0x98, 0x90, 0x88, 0x80, 0x88, 0x90, 0x98, 0xa0, 0x27, 0x2f, 0x37
, 0xc0, 0xc0, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0x00, 0xf8, 0xf0, 0xe8, 0xe0, 0xd8, 0xd0, 0xc8
, 0xc0, 0xb8, 0xb0, 0xa8, 0xa0, 0x98, 0x90, 0x88, 0x80, 0x88, 0x90, 0x98, 0xa0, 0xa8, 0x2f, 0x37
, 0xc0, 0xc0, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0x00, 0xf8, 0xf0, 0xe8, 0xe0, 0xd8, 0xd0, 0xc8
, 0xc0, 0xb8, 0xb0, 0xa8, 0xa0, 0x98, 0x90, 0x88, 0x80, 0x88, 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0x37
, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81
, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f
, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81
, 0x81, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f
, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81
, 0x81, 0x81, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f
, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81
, 0x81, 0x81, 0x81, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f
, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81
, 0x81, 0x81, 0x81, 0x81, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f
, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81
, 0x81, 0x81, 0x81, 0x81, 0x81, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f
, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81
, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f
, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81
, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f
, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81
, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f
, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81
, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f
, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81
, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f
, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81
, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f
, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81
, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x7f, 0x7f, 0x7f, 0x7f
, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81
, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x7f, 0x7f, 0x7f
, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80
, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7f, 0x7f
, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80
, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7f
, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f
, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f
, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f
, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f
, 0x80, 0x80, 0x80, 0x80, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f
, 0x80, 0x80, 0x80, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f
, 0x80, 0x80, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f
, 0x80, 0x80, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f
, 0x80, 0x80, 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0, 0xc8, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8
, 0x00, 0x08, 0x10, 0x18, 0x20, 0x28, 0x30, 0x38, 0x40, 0x48, 0x50, 0x58, 0x60, 0x68, 0x70, 0x7f
, 0x80, 0x80, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70
, 0x45, 0x45, 0x79, 0x7d, 0x7a, 0x77, 0x70, 0x66, 0x61, 0x58, 0x53, 0x4d, 0x2c, 0x20, 0x18, 0x12
, 0x04, 0xdb, 0xd3, 0xcd, 0xc6, 0xbc, 0xb5, 0xae, 0xa8, 0xa3, 0x9d, 0x99, 0x93, 0x8e, 0x8b, 0x8a
, 0x45, 0x45, 0x79, 0x7d, 0x7a, 0x77, 0x70, 0x66, 0x5b, 0x4b, 0x43, 0x37, 0x2c, 0x20, 0x18, 0x12
, 0x04, 0xf8, 0xe8, 0xdb, 0xcf, 0xc6, 0xbe, 0xb0, 0xa8, 0xa4, 0x9e, 0x9a, 0x95, 0x94, 0x8d, 0x83
, 0x00, 0x00, 0x40, 0x60, 0x7f, 0x60, 0x40, 0x20, 0x00, 0xe0, 0xc0, 0xa0, 0x80, 0xa0, 0xc0, 0xe0
, 0x00, 0x00, 0x40, 0x60, 0x7f, 0x60, 0x40, 0x20, 0x00, 0xe0, 0xc0, 0xa0, 0x80, 0xa0, 0xc0, 0xe0
, 0x80, 0x80, 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0, 0xc8, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8
, 0x00, 0x08, 0x10, 0x18, 0x20, 0x28, 0x30, 0x38, 0x40, 0x48, 0x50, 0x58, 0x60, 0x68, 0x70, 0x7f
, 0x80, 0x80, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70
};

static const uint8_t NotesMap[128] =
{ 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35
, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47
, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59
, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71
, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72
, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23
,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11
, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35
, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47
, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59
, 60, 61, 62, 63, 64, 65, 66, 67
};

typedef __packed struct
{
	uint8_t PatternNr0;
	int8_t  NoteTranspose0;
	int8_t  InstTranspose0;
	uint8_t PatternNr1;
	int8_t  NoteTranspose1;
	int8_t  InstTranspose1;
	uint8_t PatternNr2;
	int8_t  NoteTranspose2;
	int8_t  InstTranspose2;
	uint8_t PatternNr3;
	int8_t  NoteTranspose3;
	int8_t  InstTranspose3;
	uint8_t Speed;
} LSeq;

typedef struct
{
	const uint8_t	*pLPattern;
	const uint8_t	*pLVolumeSeq;
	const uint8_t	*pLFreqSeq;
	int8_t      NoteTranspose;
	int8_t      VolumeTranspose;
	int8_t      Note;
	uint8_t     Info;
	struct
	{
		int32_t Value;
		int32_t LastValue;
		int32_t Index;
		uint8_t Pos;
		uint8_t Count;
		uint8_t Speed;
		uint8_t Sustain;
		int8_t  SlideStep;
		uint8_t SlideDuration;
	}           Volume;
	struct
	{
		uint8_t Speed;
		uint8_t Depth;
		uint8_t Value;
		uint8_t Delay;
	}           Vibrato;
	struct
	{
		int32_t Value;
		int32_t Index;
		uint8_t Pos;
		uint8_t Sustain;
		int8_t  SlideStep;
		uint8_t SlideDuration;
	}           Freq;
	uint8_t     AutoSlide;
} FCVoice;

typedef struct
{
	uint8_t     SeqPos;
	uint8_t     RowPos;
	uint8_t     FrameCount;
	uint8_t     Speed;
	uint8_t     Seqs;
	uint32_t    Patterns;
	uint32_t    VolumeSeqs;
	uint32_t    FreqSeqs;
	uint8_t*    pLSeqs;
	uint8_t*    pLPatterns;
	uint8_t*    pLVolumeSeqs;
	uint8_t*    pLFreqSeqs;
	uint32_t    MultiIds[10];
	FCVoice     voices[4];
} FCSong;

#define LROWS 32

static const uint8_t SilentSeq[] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe1};

static const _kernel_oserror* Play_FC(SongHdr* pSong, FCSong* pLSong)
{
	const _kernel_oserror* err = NULL;
	Pattern* pPattern = NULL;
	FCVoice* pVoice;
	int k;
	uint8_t* pc;

	pSong->Patterns = 0;

	pLSong->SeqPos = -1;
	pLSong->RowPos = LROWS;
	pLSong->FrameCount = 3;
	pLSong->Speed = 3;

	for (k = 0; k < 4; k++)
	{
		pVoice = &pLSong->voices[k];

		pVoice->Volume.Index = 255;
		pVoice->pLVolumeSeq = SilentSeq;
		pVoice->Volume.Value = 0;
		pVoice->Volume.Pos = 5;
		pVoice->Volume.Count = 0;
		pVoice->Volume.Speed = 1;
		pVoice->Volume.Sustain = 0;
		pVoice->Volume.SlideStep = 0;
		pVoice->Volume.SlideDuration = 0;
		pVoice->Vibrato.Speed = 0;
		pVoice->Vibrato.Depth = 0;
		pVoice->Vibrato.Value = 0;
		pVoice->Vibrato.Delay = 0;

		// Decode freq sequence
		pVoice->Freq.Index = 255;
		pVoice->pLFreqSeq = NULL;
		pVoice->Freq.Pos = 64;
		pVoice->Freq.Value = 0;
		pVoice->Freq.Sustain = 0;
		pVoice->Freq.SlideStep = 0;
		pVoice->Freq.SlideDuration = 0;
		pVoice->AutoSlide = 0;
	}

	while (true)
	{
		// New frame
		pLSong->FrameCount++;

		// New row?
		if (pLSong->FrameCount >= pLSong->Speed)
		{
			pLSong->FrameCount = 0;
			pLSong->RowPos++;

			// Check voices for pattern breaks?
			if (pLSong->RowPos < LROWS)
			{
				for (k = 0; k < 4; k++)
				{
					if ((pLSong->voices[k].pLPattern != NULL)
					&&  (pLSong->voices[k].pLPattern[2 * pLSong->RowPos] == 73))
					{
						pLSong->RowPos = LROWS;
						break;
					}
				}
			}

			// New pattern?
			if (pLSong->RowPos >= LROWS)
			{
				// Close current pattern if one defined yet
				if (pSong->Patterns > 0)
				{
					err = Loaders_EndPattern(pSong, pPattern);
					if (err) return err;
				}

				// Move to start of next pattern
				pLSong->RowPos = 0;
				pLSong->SeqPos++;

				// End of song?
				if (pLSong->SeqPos >= pLSong->Seqs)
					break;

				// Start a new pattern
				err = Loaders_StartPattern(pSong, pSong->Patterns);
				if (err) return err;
				pPattern = pSong->pPatterns + pSong->Patterns;
				pSong->Patterns++;
				pPattern->Rows = 0;

				// Get sequence info
				pc = pLSong->pLSeqs + 13*pLSong->SeqPos;

				// Setup voices and speed
				pVoice = &pLSong->voices[0];
				if (pc[0] >= pLSong->Patterns)
					pVoice->pLPattern = NULL;
				else
					pVoice->pLPattern = pLSong->pLPatterns + 64 * pc[0];
				pVoice->NoteTranspose = pc[1];
				pVoice->VolumeTranspose = pc[2];

				pVoice = &pLSong->voices[1];
				if (pc[3] >= pLSong->Patterns)
					pVoice->pLPattern = NULL;
				else
					pVoice->pLPattern = pLSong->pLPatterns + 64 * pc[3];
				pVoice->NoteTranspose = pc[4];
				pVoice->VolumeTranspose = pc[5];

				pVoice = &pLSong->voices[2];
				if (pc[6] >= pLSong->Patterns)
					pVoice->pLPattern = NULL;
				else
					pVoice->pLPattern = pLSong->pLPatterns + 64 * pc[6];
				pVoice->NoteTranspose = pc[7];
				pVoice->VolumeTranspose = pc[8];

				pVoice = &pLSong->voices[3];
				if (pc[9] >= pLSong->Patterns)
					pVoice->pLPattern = NULL;
				else
					pVoice->pLPattern = pLSong->pLPatterns + 64 * pc[9];
				pVoice->NoteTranspose = pc[10];
				pVoice->VolumeTranspose = pc[11];

				if (pc[12]) pLSong->Speed = pc[12];
				if (pLSong->Speed >= 128) pLSong->Speed = 1;
			}
		}

		// Start new row for every frame
/*
		// but limit to 256 rows per pattern
		if (pPattern->Rows >= 256)
		{
			// Close pattern
			err = Loaders_EndPattern(pSong, pPattern);
			if (err) return err;
			// Start a new pattern
			err = Loaders_StartPattern(pSong, pSong->Patterns);
			if (err) return err;
			pPattern = pSong->pPatterns + pSong->Patterns;
			pSong->Patterns++;
			pPattern->Rows = 0;
		}
*/
		err = Loaders_StartRow(pSong, pPattern->Rows);
		if (err) return err;
		pPattern->Rows++;

		for (k = 0; k < 4; k++)
		{
			int Note = 0, Inst = 0;
			bool bTrigger = false;
			bool bFreq = false;

			pVoice = &pLSong->voices[k];

			if (!pLSong->FrameCount)
			{
				// Decode row info
				if (pVoice->pLPattern)
				{
					Note = pVoice->pLPattern[2 * pLSong->RowPos];
					pVoice->Info = pVoice->pLPattern[2 * pLSong->RowPos + 1];
				}
				else // Invalid pattern
				{
					Note = 0;
					pVoice->Info = 0;
				}

				if (pVoice->Info & 0x80)
				{
					if (pLSong->RowPos < 31)
						pVoice->AutoSlide = 0x80 | (pVoice->pLPattern[2 * pLSong->RowPos + 3] & 0x3f);
					else
						SysLog_Log(SysLog_Medium, "[%03d:%03d:%d] Autoslide in last row of pattern ignored"
						         , pSong->Patterns-1, pPattern->Rows - 1, k);
				}
				else if (pVoice->Info & 0x40)
					pVoice->AutoSlide = 0x40;
				else
					pVoice->AutoSlide = 0;

				Note = Note & 0x7f;
				if (Note)
				{
					pVoice->Note = Note;
					// Decode volume sequence
					int seq = (pVoice->Info & 0x3f) + pVoice->VolumeTranspose;

					pVoice->Volume.Index = seq;
					if ((seq < 0) || (seq >= pLSong->VolumeSeqs))
					{
						pVoice->pLVolumeSeq = SilentSeq;
						SysLog_Log(SysLog_Medium, "[%03d:%03d:%d] Invalid volume sequence %d, transposed by %d"
						         , pSong->Patterns-1, pPattern->Rows - 1, k, (pVoice->Info & 0x3f), pVoice->VolumeTranspose);
					}
					else
					{
						pVoice->pLVolumeSeq = pLSong->pLVolumeSeqs + 64*seq;
					}

					pVoice->Volume.Value = 0;
					pVoice->Volume.Pos = 5;
					pVoice->Volume.Count = 0;
					pVoice->Volume.Speed = pVoice->pLVolumeSeq[0];
					pVoice->Volume.Sustain = 0;
					pVoice->Volume.SlideStep = 0;
					pVoice->Volume.SlideDuration = 0;
					pVoice->Vibrato.Speed = pVoice->pLVolumeSeq[2];
					pVoice->Vibrato.Depth =
					pVoice->Vibrato.Value = pVoice->pLVolumeSeq[3];
					pVoice->Vibrato.Delay = pVoice->pLVolumeSeq[4];
					seq = pVoice->pLVolumeSeq[1];

					// Decode freq sequence
					pVoice->Freq.Index = seq;
					if (seq >= pLSong->FreqSeqs)
					{
						SysLog_Log(SysLog_Medium, "[%03d:%03d:%d] Invalid frequency sequence %d"
						         , pSong->Patterns-1, pPattern->Rows - 1, k, seq);
						pVoice->pLFreqSeq = NULL;
						pVoice->Freq.Pos = 64;
					}
					else
					{
						pVoice->pLFreqSeq = pLSong->pLFreqSeqs + 64*seq;
						pVoice->Freq.Pos = 0;
					}
					pVoice->Freq.Value = 0;
					pVoice->Freq.Sustain = 0;
					pVoice->Freq.SlideStep = 0;
					pVoice->Freq.SlideDuration = 0;
				}
			}
			else pVoice->AutoSlide = 0;

			// Parse frequency sequence
			while(pVoice->Freq.Pos < 64)
			{
				// Sustain?
				if (pVoice->Freq.Sustain > 0)
				{
					pVoice->Freq.Sustain--;
					break;
				}

				// Note: Pitch Slide is not part of the loop

				uint8_t val = pVoice->pLFreqSeq[pVoice->Freq.Pos++];

				switch(val)
				{
					case 0xe0: // Loop
					{
						val = pVoice->Freq.Pos - 1;
						if (pVoice->Freq.Pos < 64)
						{
							pVoice->Freq.Pos = pVoice->pLFreqSeq[pVoice->Freq.Pos] & 0x3f;
							if (val != pVoice->Freq.Pos)
								continue; // loop if no infinite loop
						}
					}
					break;
					case 0xe1: // End of sequence
					{
						pVoice->Freq.Pos = 64;
					}
					break;
					case 0xe2: // Set Waveform
					{
						if (pVoice->Freq.Pos < 64)
						{
							Inst = 1 + pVoice->pLFreqSeq[pVoice->Freq.Pos++];
							bTrigger = true;
							continue;
						}
					}
					break;
					case 0xe3: // Vibrato
					{
						if (pVoice->Freq.Pos < 64)
						{
							pVoice->Vibrato.Speed = pVoice->pLFreqSeq[pVoice->Freq.Pos++];
							if (pVoice->Freq.Pos < 64)
							{
								pVoice->Vibrato.Depth = pVoice->pLFreqSeq[pVoice->Freq.Pos++];
								continue; // loop to decount once
							}
						}
					}
					break;
					case 0xe4: // Change Waveform
					{
						if (pVoice->Freq.Pos < 64)
						{
							Inst = 1 + pVoice->pLFreqSeq[pVoice->Freq.Pos++];
							continue;
						}
					}
					break;
					case 0xe7: // New sequence
					{
						if (pVoice->Freq.Pos < 64)
						{
							val = pVoice->pLFreqSeq[pVoice->Freq.Pos];
							pVoice->Freq.Index = val;
							if (val >= pLSong->FreqSeqs)
							{
								pVoice->pLFreqSeq = NULL;
								pVoice->Freq.Pos = 64;
							}
							else
							{
								pVoice->pLFreqSeq = pLSong->pLFreqSeqs + 64*val;
								pVoice->Freq.Pos = 0;
							}
							continue;
						}
					}
					break;
					case 0xe8: // Sustain
					{
						if (pVoice->Freq.Pos < 64)
						{
							pVoice->Freq.Sustain = pVoice->pLFreqSeq[pVoice->Freq.Pos++];
							continue; // loop to decount once
						}
					}
					break;
					case 0xe9: // Set Wave pack
					{
						if (pVoice->Freq.Pos < 64)
						{
							val = pVoice->pLFreqSeq[pVoice->Freq.Pos++];
							if (val < 10)
								Inst = 1 + pLSong->MultiIds[val];
							else
								Inst = 1;
							if (pVoice->Freq.Pos < 64)
							{
								Inst += pVoice->pLFreqSeq[pVoice->Freq.Pos++];
								bTrigger = true;
								continue;
							}
						}
					}
					break;
					case 0xea: // Slide
					{
						if (pVoice->Freq.Pos < 64)
						{
							pVoice->Freq.SlideStep = pVoice->pLFreqSeq[pVoice->Freq.Pos++];
							if (pVoice->Freq.Pos < 64)
							{
								pVoice->Freq.SlideDuration = pVoice->pLFreqSeq[pVoice->Freq.Pos++] << 1;
								continue;
							}
						}
					}
					break;
					default:
					{
						pVoice->Freq.Value = val;
						bFreq = true;
					}
				}

				break; // exit loop
			}

			// Parse volume sequence
			while(pVoice->Volume.Pos < 64)
			{
				// Sustain?
				if (pVoice->Volume.Sustain > 0)
				{
					pVoice->Volume.Sustain--;
					break;
				}

				// Slide?
				if (pVoice->Volume.SlideDuration > 0)
				{
					pVoice->Volume.SlideDuration--;
					pVoice->Volume.Value += pVoice->Volume.SlideStep << 1;
					if (pVoice->Volume.Value < 0)
						pVoice->Volume.Value = 0;
					else if (pVoice->Volume.Value > 252)
						pVoice->Volume.Value = 252;

					break;
				}

				// Speed?
				if (pVoice->Volume.Count < pVoice->Volume.Speed)
				{
					pVoice->Volume.Count++;
					break;
				}

				// Parse next
				pVoice->Volume.Count = 0;

				uint8_t val = pVoice->pLVolumeSeq[pVoice->Volume.Pos++];

				switch(val)
				{
					case 0xe0: // Loop
					{
						val = pVoice->Volume.Pos - 1;
						if (pVoice->Volume.Pos < 64)
						{
							pVoice->Volume.Pos = pVoice->pLVolumeSeq[pVoice->Volume.Pos] & 0x3f;
							if (val != pVoice->Volume.Pos)
								continue; // loop if no infinite loop
						}
					}
					break;
					case 0xe1: // End of sequence
					{
						pVoice->Volume.Pos = 64;
					}
					break;
					case 0xe8: // Sustain
					{
						if (pVoice->Volume.Pos < 64)
						{
							pVoice->Volume.Sustain = pVoice->pLVolumeSeq[pVoice->Volume.Pos++];
							// continue; // do NOT to decount once
						}
					}
					break;
					case 0xea: // Slide
					{
						if (pVoice->Volume.Pos < 64)
						{
							pVoice->Volume.SlideStep = pVoice->pLVolumeSeq[pVoice->Volume.Pos++];
							if (pVoice->Volume.Pos < 64)
							{
								pVoice->Volume.SlideDuration = pVoice->pLVolumeSeq[pVoice->Volume.Pos++] << 1;
								continue; // loop to decount once
							}
						}
					}
					break;
					default:
					{
						pVoice->Volume.Value = (val & 0x3f) << 2; // Paula uses 6 bits
					}
				}

				break; // exit loop
			}

			// bit 0x80 -> use as note else -> transpose
			Note = pVoice->Freq.Value;
			if (!(Note & 0x80))
			{
				if (pVoice->Note)
					Note += pVoice->NoteTranspose + pVoice->Note;
				else
					Note = 0;
			}
			Note &= 0x7f;
			if (Note) Note = NoteDelta + NotesMap[Note];

			if (bTrigger)
			{
				Loaders_StartChannel(pSong, k, Note, Inst);
				Pattern_AddEffect_NoteVolume(pSong, cmd_set, pVoice->Volume.Value);
				pVoice->Volume.LastValue = pVoice->Volume.Value;
			}
			else
			{
				Loaders_StartChannel(pSong, k, 0, Inst);
				// set pitch to last (current) note value
				if (bFreq && Note) Pattern_AddEffect_Pitch(pSong, cmd_pitch_semitoneset, Note);
				if ((pVoice->Volume.LastValue != pVoice->Volume.Value) || Inst)
				{
					Pattern_AddEffect_NoteVolume(pSong, cmd_set, pVoice->Volume.Value);
					pVoice->Volume.LastValue = pVoice->Volume.Value;
				}
			}

			// Portamento?
			if (pVoice->AutoSlide)
			{
				if (pVoice->AutoSlide & 0x80)
				{
					int step = pVoice->AutoSlide & 0x1f;
					step = (pSong->Version >= 140) ? (step << 1) : (step << 2);
					if (pVoice->AutoSlide & 0x20)
						Pattern_AddEffect_Pitch(pSong, cmd_pitch_auto_down+flag_cmd_slide_frame0N0, step);
					else
						Pattern_AddEffect_Pitch(pSong, cmd_pitch_auto_up+flag_cmd_slide_frame0N0, step);
				}
				else
				{
					Pattern_AddEffect_Pitch(pSong, cmd_pitch_auto_down+flag_cmd_slide_frame0N0, 0);
				}
			}

			// Vibrato?
			if (pVoice->Vibrato.Delay > 0)
				pVoice->Vibrato.Delay--;
			else if (pVoice->Vibrato.Speed && pVoice->Vibrato.Depth)
			{
				int speed = 128*pVoice->Vibrato.Speed / pVoice->Vibrato.Depth;
				if (!speed) speed = 1;
				// Note: depth seems in semitones not period fractions
				Pattern_AddEffect_Pitch(pSong, cmd_vibrato_speed, speed);
				Pattern_AddEffect_Pitch(pSong, cmd_vibrato_depth | flag_cmd_vibrato_frame0N0, pVoice->Vibrato.Depth << 2);
			}

			// Slide?
			if (pVoice->Freq.SlideDuration > 0)
			{
				pVoice->Freq.SlideDuration--;
				if (pVoice->Freq.SlideStep < 0)
					Pattern_AddEffect_Pitch(pSong, cmd_down+flag_cmd_slide_frame0N0, (-pVoice->Freq.SlideStep) << 1);
				else
					Pattern_AddEffect_Pitch(pSong, cmd_up+flag_cmd_slide_frame0N0, pVoice->Freq.SlideStep << 1);
			}

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

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

	return err;
}

const _kernel_oserror* Loader_FC(SongHdr* pSong)
{
	const _kernel_oserror* err = NULL;
	SubSong*  pSubSong = Song_GetSubSong(pSong, 0);
	FCSong    LSong;
	Sample*   pSmp;
	Sample*   pMulSmp;
	int       i, j;
	uint32_t  Tag;
	uint32_t  SizeSeqs;
	uint32_t  OffsetPatterns;
	uint32_t  SizePatterns;
	uint32_t  OffsetFreqs;
	uint32_t  SizeFreqs;
	uint32_t  OffsetVolumes;
	uint32_t  SizeVolumes;
	uint32_t  OffsetSamples;
	uint32_t  SizeSamples;
	uint32_t  MultiOffsets[20];
	uint32_t  PosSamples;

	memset(&LSong, 0, sizeof(LSong));

	// load header
	err = FileLoad_SetPos(pSong, 0);
	if (err) goto loader_err;
	err = FileLoad_ReadInt(pSong, &Tag, 4);
	if (err) goto loader_err;

	// check if really Farandole Module?
	if (Tag == TAG(SMOD))
	{
		pSong->pType = Typestr_FC;
		pSong->Version = 100;
		pSong->MinPitch = NoteDelta + 24;
		pSong->MaxPitch = NoteDelta + 12 * 6 - 1;
	}
	else if (Tag == TAG(FC14))
	{
		pSong->pType = Typestr_FC;
		pSong->Version = 140;
		pSong->MinPitch = NoteDelta + 12; // Cf period 1 octave higher than highest period in table
		pSong->MaxPitch = NoteDelta + 12 * 6 - 1;
	}
	else
	{
		err = Loaders_NotThisType;
		goto loader_err;
	}

	if (!err) err = FileLoad_ReadReverseInt(pSong, &SizeSeqs, 4);
	if (!err) err = FileLoad_ReadReverseInt(pSong, &OffsetPatterns, 4);
	if (!err) err = FileLoad_ReadReverseInt(pSong, &SizePatterns, 4);
	if (!err) err = FileLoad_ReadReverseInt(pSong, &OffsetFreqs, 4);
	if (!err) err = FileLoad_ReadReverseInt(pSong, &SizeFreqs, 4);
	if (!err) err = FileLoad_ReadReverseInt(pSong, &OffsetVolumes, 4);
	if (!err) err = FileLoad_ReadReverseInt(pSong, &SizeVolumes, 4);
	if (!err) err = FileLoad_ReadReverseInt(pSong, &OffsetSamples, 4);
	if (!err) err = FileLoad_ReadReverseInt(pSong, &SizeSamples, 4);

	// Read samples info
	for (i = 0, pSmp = pSong->pSamples; i < 10; i++, pSmp++)
	{
		pSmp->Type = 0;
		pSmp->Frequency = 8287;
		if (!err) err = FileLoad_ReadReverseInt(pSong, &pSmp->Size, 2);
		if (!err) err = FileLoad_ReadReverseInt(pSong, &pSmp->LoopStart, 2);
		if (!err) err = FileLoad_ReadReverseInt(pSong, &pSmp->LoopEnd, 2);
		pSmp->Size <<= 1;
		if (pSmp->LoopEnd > 1)
		{
			pSmp->Type |= Smp_Type_Loop;
			pSmp->LoopEnd = pSmp->LoopStart + (pSmp->LoopEnd << 1);
		}
		else
			pSmp->LoopEnd = 0;
	}

	// Read wavetables info
	if (!err)
	{
		if (pSong->Version >= 140)
		{
			for (; i < 90; i++, pSmp++)
			{
				pSmp->Type = Smp_Type_Loop;
				pSmp->Frequency = 8287;
				if (!err) err = FileLoad_ReadByte(pSong, &pSmp->Size);
				pSmp->Size <<= 1;
				pSmp->LoopStart = 0;
				pSmp->LoopEnd = pSmp->Size;
			}
		}
		else
		{
			for (i = 0; i < 47; i++, pSmp++)
			{
				pSmp->Type = Smp_Type_Loop;
				pSmp->Frequency = 8287;
				pSmp->Size = v1_synths[i].size;
				err = Loaders_Alloc(pSong, &pSmp->Ptr, pSmp->Size);
				if (err) goto loader_err;
				memcpy(pSmp->Ptr, &v1_waves[v1_synths[i].offset], pSmp->Size);
				pSmp->LoopStart = 0;
				pSmp->LoopEnd = pSmp->Size;
			}
		}
	}
	if (err) goto loader_err;
	pMulSmp = pSmp;

	// Bytes to counters
	LSong.Seqs = SizeSeqs / 13;
	LSong.Patterns = SizePatterns / 64;
	LSong.VolumeSeqs = SizeVolumes / 64;
	LSong.FreqSeqs = SizeFreqs / 64;

	if (!LSong.Seqs || (LSong.Seqs > 254))
	{
		err = Loaders_InvalidSong(pSong, 4, &pSubSong->SeqLen, LSong.Seqs);
		goto loader_err;
	}
	if (!LSong.Patterns || (LSong.Patterns > 128))
	{
		err = Loaders_InvalidSong(pSong, 12, &pSong->Patterns, LSong.Patterns);
		goto loader_err;
	}
	if (!LSong.VolumeSeqs || LSong.VolumeSeqs > 64)
	{
		err = Loaders_Error(pSong, 28, "Invalid nr (%d) of Volume sequences", LSong.VolumeSeqs);
		goto loader_err;
	}
	if (!LSong.FreqSeqs || LSong.FreqSeqs > 128)
	{
		err = Loaders_Error(pSong, 20, "Invalid nr (%d) of Frequency sequences", LSong.FreqSeqs);
		goto loader_err;
	}

	pSong->Flags = Song_Flag_InstNoNote_Swap;
	pSong->Patterns = pSubSong->SeqLen
	                = LSong.Seqs;
	pSong->Channels = 4;
	pSong->TempoBase = Tempo_Base_2d5Hz;
	pSubSong->Defaults.Tempo = 125;
	pSubSong->Defaults.Speed = 1; // We create a row for every FC frame

	if (!pSubSong->SeqLen)
	{
		err = Loaders_InvalidSong(pSong, -258, &pSubSong->SeqLen, pSubSong->SeqLen);
		goto loader_err;
	}
	err = Loaders_AllocSeq(pSong, pSubSong);
	if (err) goto loader_err;

	// fill the pattern sequence
	for (i = 0; i < pSubSong->SeqLen; i++)
		pSubSong->pSeqs[i] = i;
	err = Loaders_Alloc(pSong, (void**) &LSong.pLSeqs, LSong.Seqs * 13);
	if (err) goto loader_err;
	err = FileLoad_Read(pSong, LSong.pLSeqs, LSong.Seqs * 13);
	if (err) goto loader_err;

	// fill the patterns
	err = Loaders_Alloc(pSong, (void**) &LSong.pLPatterns, LSong.Patterns * 64);
	if (err) goto loader_err;
	err = FileLoad_SetPos(pSong, OffsetPatterns);
	if (err) goto loader_err;
	err = FileLoad_Read(pSong, LSong.pLPatterns, LSong.Patterns * 64);
	if (err) goto loader_err;

	// fill the frequencies
	err = Loaders_Alloc(pSong, (void**) &LSong.pLFreqSeqs, LSong.FreqSeqs * 64);
	if (err) goto loader_err;
	err = FileLoad_SetPos(pSong, OffsetFreqs);
	if (err) goto loader_err;
	err = FileLoad_Read(pSong, LSong.pLFreqSeqs, LSong.FreqSeqs * 64);
	if (err) goto loader_err;

	// fill the volumes
	err = Loaders_Alloc(pSong, (void**) &LSong.pLVolumeSeqs, LSong.VolumeSeqs * 64);
	if (err) goto loader_err;
	err = FileLoad_SetPos(pSong, OffsetVolumes);
	if (err) goto loader_err;
	err = FileLoad_Read(pSong, LSong.pLVolumeSeqs, LSong.VolumeSeqs * 64);
	if (err) goto loader_err;

	// Rewrite patterns from sequence
	err = Play_FC(pSong, &LSong);
	if (err) goto loader_err;

	err = FileLoad_SetPos(pSong, OffsetSamples);
	if (err) goto loader_err;
	PosSamples = OffsetSamples;

	for (i = 0, pSmp = pSong->pSamples; i < 10; i++, pSmp++)
	{
		err = FileLoad_SetPos(pSong, PosSamples);
		if (err) goto loader_skipsamples;

		if (pSmp->Size)
		{
			PosSamples += pSmp->Size;
			if (pSong->Version >= 140)
				PosSamples += 2;

			// Read data
			err = FileLoad_ReadInt(pSong, &Tag, 4);
			if (err) goto loader_skipsamples;
			if (Tag == TAG(SSMP))
			{
				// Multi sample
				pSmp->Size = 0;
				LSong.MultiIds[i] = 1 + (pMulSmp - pSong->pSamples);
				// Read definitions
				for (j = 0; j < 10; j++, pMulSmp++)
				{
					pMulSmp->Type = 0;
					pMulSmp->Frequency = 8287;
					err = FileLoad_ReadReverseInt(pSong, &MultiOffsets[j], 4);
					if (!err) err = FileLoad_ReadReverseInt(pSong, &pMulSmp->Size, 2);
					if (!err) err = FileLoad_ReadReverseInt(pSong, &pMulSmp->LoopStart, 2);
					if (!err) err = FileLoad_ReadReverseInt(pSong, &pMulSmp->LoopEnd, 2);
					if (!err) err = FileLoad_Skip(pSong, 6);
					if (err) goto loader_skipsamples;
					pMulSmp->Size <<= 1;
					if (pMulSmp->LoopEnd > 1)
						pMulSmp->LoopEnd = pMulSmp->LoopStart + (pMulSmp->LoopEnd << 1);
					else
						pMulSmp->LoopEnd = 0;
				}
				// Read names
				for (j = 0,pMulSmp -= 10; j < 10; j++, pMulSmp++)
				{
					err = FileLoad_ReadString(pSong, &pMulSmp->pName, 16, false);
					if (err) goto loader_skipsamples;
				}

				uint32_t StartOffset = FileLoad_GetPos(pSong);

				// Read data
				for (j = 0,pMulSmp -= 10; j < 10; j++, pMulSmp++)
				{
					FileLoad_SetPos(pSong, StartOffset + MultiOffsets[j]);
					if (pMulSmp->Size)
					{
						err = FileLoad_ReadSample(pSong, pMulSmp);
						if (err) goto loader_err;
					}
				}
			}
			else
			{
				LSong.MultiIds[i] = 0;
				FileLoad_SetPos(pSong, FileLoad_GetPos(pSong) - 4);
				err = FileLoad_ReadSample(pSong, pSmp);
				if (err) goto loader_err;
			}
		}
	}

	if (pSong->Version < 140)
	{
		if (PosSamples != (OffsetSamples + SizeSamples))
		{
			SysLog_FileCorrupted
				( SysLog_Medium
				, pSong
				, PosSamples
			    , "Samples end should be %d bytes later"
			    , (OffsetSamples + SizeSamples) - PosSamples
			    );
		}
	}
	else
	{
		if (PosSamples != SizeSamples)
		{
			SysLog_FileCorrupted
				( SysLog_Medium
				, pSong
				, PosSamples
			    , "Samples end should be %d bytes later"
			    , SizeSamples - PosSamples
			    );
		}
	}

	// Read wavetables info
	if (pSong->Version >= 140)
	{
		FileLoad_SetPos(pSong, SizeSamples);
		for (; i < 90; i++, pSmp++)
		{
			err = FileLoad_ReadSample(pSong, pSmp);
			if (err) goto loader_err;
		}
	}

loader_skipsamples:
	pSong->Samples = pMulSmp - pSong->pSamples;
	err = NULL;

loader_err:
	Loaders_Free(pSong, LSong.pLSeqs);
	Loaders_Free(pSong, LSong.pLPatterns);
	Loaders_Free(pSong, LSong.pLVolumeSeqs);
	Loaders_Free(pSong, LSong.pLFreqSeqs);

	return err;
}
