/*****************************************************************************

GRAOUMF TRACKER 2

Copyright (c) 1996 - 2002 Laurent de Soras

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

Contact the author : laurent@ohmforce.com
More information about this license : http://www.gnu.org/licenses/gpl.html

*****************************************************************************/



/*\\\ FICHIERS INCLUDE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

#include	"archi.h"
#include	"base.h"
#include	"base_ct.h"
#include	"env.h"
#include	"Envelope.h"
#include	"file.h"
#include	"filter.h"
#include	"fnames.h"
#include	"gtracker.h"
#include	"inst.h"
#include	"Instrument.h"
#include	"log.h"
#include	"memory.h"
#include	"MixPreset.h"
#include	"mod_gt2.h"
#include	"modstruc.h"
#include	"player.h"
#include	"samp.h"
#include	"Sample.h"
#include	"song.h"
#include	"spl_raw.h"
#include	"splstruc.h"
#include	"String.h"
#include	"tracks.h"
#include	"WaveForm.h"

#include	<vector>

#include	<assert.h>
#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>
#include	<time.h>
#include	<math.h>

/*\\\ CONSTANTES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

#define	MODGT2_NBR_CHUNKS_MAXI		8192	/* Nombre maxi de chunks dans un module */

#define	MODGT2_FILE_VNUM				9		/* Numero de version du format .GT2 */
#define	MODGT2_TCN2_VNUM				1		/* Version du chunk de config */
#define	MODGT2_PAT_VNUM				1		/* Version de codage des patterns */
#define	MODGT2_INSTR_VNUM				2		/* Version de codage des instruments */
#define	MODGT2_SPL_ADDDATA_VNUM		1		/* Version de codage du chunk des donnees additionnelles dans les samples */

#define	MODGT2_MOD_NAME_LEN			32		/* Longueur du nom du module */
#define	MODGT2_INS_NAME_LEN			28		/* Longueur du nom d'un instrument */
#define	MODGT2_SPL_NAME_LEN			28		/* Longueur du nom d'un sample */
#define	MODGT2_PAT_NAME_LEN			16		/* Longueur du nom d'un pattern */
#define	MODGT2_PRE_NAME_LEN			16		/* Longueur du nom d'un preset */
#define	MODGT2_TRA_NAME_LEN			24		/* Longueur du nom du tracker de composition */
#define	MODGT2_MIX_NAME_LEN			32		/* Longueur du nom d'un preset de mixage */
#define	MODGT2_FX_NAME_LEN			32		/* Longueur du nom d'un preset d'effet */
#define	MODGT2_TRK_NAME_LEN			32		/* Longueur du nom d'une piste */
#define	MODGT2_SMALLCOM_LEN			160	/* Longueur du commentaire du header */
#define	MODGT2_FX_MTDELAY_TAPS		4		/* Nombre maxi de taps pour le delay multi-tap */
#define	MODGT2_INS_ENV_MAX_PT		64		/* Nombre maxi de points par envelope */
#define	MODGT2_INS_ENV_MAX_LP		2		/* Nombre maxi de boucles par envelope */

#define	MODGT2_SAMPLE2_INITLOOPBUF	512	/* Taille des buffers de bouclage initiaux en samples */
#define	MODGT2_SAMPLE2_INITTRK		1		/* Samples Mono par defaut */
#define	MODGT2_SAMPLE2_INITBYTES	1		/* Samples 8 bits par defaut */



/*\\\ TYPES & STRUCTURES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

/*--------------------------------------------------------------------------*/
/*      Header                                                              */
/*--------------------------------------------------------------------------*/

typedef struct
{
	UBYTE		day;			/* 1-31 */
	UBYTE		month;		/* 1-12 */
	UWORD		year;			/* 1994-... */
} MODGT2_HEADER_DATE;

typedef struct
{
	char		id [3];		/* "GT2" */
	UBYTE		version;		/* Numero de version du format */
	LWORD		chunksz;
	char		name [MODGT2_MOD_NAME_LEN];
	char		smallcomment [MODGT2_SMALLCOM_LEN];
	MODGT2_HEADER_DATE	date;
	char		tracker [MODGT2_TRA_NAME_LEN];
} MODGT2_HEADER;

typedef struct
{
	WORD		speed;		/* init speed */
	WORD		tempo;		/* init BPM */
	WORD		master_volume;	/* 000...FFF */
	WORD		pan_section_len;
	WORD		pan [1];		/* 000...FFF */
} MODGT2_EXTRA_HEADER;	/* Anciens modules (v < 6), place juste apres le header. */

/*--------------------------------------------------------------------------*/
/*      Extra-Comment                                                       */
/*--------------------------------------------------------------------------*/

typedef struct
{
	char		chunkid [4];	/* "XCOM" */
	LWORD		chunksz;
	UWORD		length;			/* Taille du texte */
} MODGT2_XCOM;

/* Le meme mais avec la section de donnees */
typedef struct
{
	char		chunkid [4];
	LWORD		chunksz;
	UWORD		length;
	char		comment [1];	/* Donnees */
} MODGT2_XCOM_D;

/*--------------------------------------------------------------------------*/
/*      Configuration                                                       */
/*--------------------------------------------------------------------------*/

typedef struct
{
	char		chunkid [4];		/* "TCN1" */
	LWORD		chunksz;
	WORD		version;				/* Version du chunk (0) */
	WORD		songpos;
	WORD		linepos;
	WORD		preset;
	WORD		display;				/* Type d'affichage (0-2) */
	WORD		curspos1;			/* Numero de colonne (0-...) */
	WORD		curspos2;			/* Position dans la colonne (0, 1-2, 3-6, 7-8) */
	WORD		flags;				/* Bit 0-1: 00 = stop, 01 = play song, 10 = play pattern */
	WORD		octave;				/* Bas du clavier (0-6) */
	WORD		linestep;
	WORD		instrument;
	WORD		sample;
	WORD		disptracks [32];	/* Nombre de pistes affichees pour chaque preset */
	char		presetname [32] [MODGT2_PRE_NAME_LEN];	/* Nom de chaque preset */
	WORD		tracknum [32] [32];	/* 32 numeros de pistes pour 32 presets */
	UBYTE		mute [(32+7)>>3];	/* Bits 0-31: 1 = track on, 0 = mute */
} MODGT2_TCN1;

typedef struct
{
	char		chunkid [4];		// "TCN2"
	LWORD		chunksz;
	WORD		version;				// Version du chunk (0-1)
	WORD		tempo;				// 32..999, 125 par defaut
	UWORD		fine_tempo;			// 0..FFFF -> [0;1[
	WORD		speed;				// 1..255, 6 par defaut
	WORD		time_high;			// Mesure, numerateur 1..16 (4 par defaut)
	WORD		time_low;			// Mesure, denominateur 1, 2, 4, 8, 16 (4 par defaut)
	/* Fin de la version 0 */
	WORD		period_mode;		// 0 = Linear, 1 = Amiga
	/* Fin de la version 1 */
} MODGT2_TCN2;

/*--------------------------------------------------------------------------*/
/*      Song                                                                */
/*--------------------------------------------------------------------------*/

typedef struct
{
	char		chunkid [4];	/* "SONG" */
	LWORD		chunksz;
	WORD		length;			/* Nombre de positions */
	WORD		repeat;			/* Position de repetition */
} MODGT2_SONG;

/* Le meme mais avec la section de donnees */
typedef struct
{
	char		chunkid [4];
	LWORD		chunksz;
	WORD		length;
	WORD		repeat;
	WORD		song [1];		/* Donnees: Numero de pattern pour chaque position */
} MODGT2_SONG_D;

/*--------------------------------------------------------------------------*/
/*      Pattern set                                                         */
/*--------------------------------------------------------------------------*/

typedef struct
{
	char		chunkid [4];	/* "PATS", "PINS", "PFXS" ou "PMIS" */
	LWORD		chunksz;
	WORD		tracks;			/* Nombre maxi de pistes dans un pattern */
	WORD		npat;				/* Nombre de patterns sauves */
} MODGT2_PAT_SET;

/*--------------------------------------------------------------------------*/
/*      Samples                                                             */
/*--------------------------------------------------------------------------*/

typedef struct
{
	char		chunkid [4];	/* "SAMP" */
	LWORD		chunksz;
	WORD		number;			/* 1-255 */
	char		name [MODGT2_SPL_NAME_LEN];
	WORD		stereo;			/* Bit 0 = mono/stereo, bit 1 = normal/ping-pong loop */
	SWORD		autobal;			/* 0..800..FFF, -1 = pas d'auto */
	WORD		nbits;			/* 8 ou 16 */
	UWORD		freq;				/* Frequence d'echantillonnage */
	LWORD		length;			/* En octets, pair */
	LWORD		repeat;			/* En octets, pair */
	LWORD		replen;			/* En octets, pair */
	WORD		volume;			/* 0-255 */
	SWORD		finetune;		/* -8..+7 : +/- 1/2 ton */
	WORD		codagev;			/* 0 */
} MODGT2_SAMPLE;

/* Le meme mais avec la section de donnees */
typedef struct
{
	char		chunkid [4];
	LWORD		chunksz;
	WORD		number;
	char		name [MODGT2_SPL_NAME_LEN];
	WORD		stereo;
	WORD		autobal;
	WORD		nbits;
	UWORD		freq;
	LWORD		length;
	LWORD		repeat;
	LWORD		replen;
	WORD		volume;
	SWORD		finetune;
	WORD		codagev;
	SBYTE		sample [1];	/* Donnees */
} MODGT2_SAMPLE_D;

typedef struct
{
	char		chunkid [4];	/* "SAM2" */
	LWORD		chunksz;
	WORD		number;			/* >= 1 */
	char		name [MODGT2_SPL_NAME_LEN];
	WORD		type;				/* Position du sample: 0 = memoire, 1 = D2D */
	WORD		nbits;			/* Nombre de bits: 8 ou 16 */
	WORD		byte_order;		/* Format des mots: 0 = Motorola, 1 = Intel */
	WORD		tracks;			/* Nombre de voies: 1 = mono, 2 = stereo, >2 = multi-piste */
	SWORD		autobal;			/* Balance automatique: 0..800..FFF, -1 = pas de balance */
	WORD		volume;			/* 0..100..FFF */
	SWORD		finetune;		/* -8 = -1/2 ton, a +7 = +7/16 ton */
	WORD		loop;				/* Bits 0-1: 00 = pas de boucle, 01 = boucle normale, 11 = ping-pong */
	WORD		midi_note;		/* Reference: 48 = C2. ?? Ne pas tenir compte de ce parametre ? */
	WORD		codagev;			/* Version de codage: 0  = PCM, 1 = sample sur disque */
	WORD		filename_len;	/* Longueur du nom de fichier */
	SWORD		autopan2;		/* Balance de profondeur. 0..800..FFF. -1 = pas de balance. Ce champ doit etre ignore pour le format de module < 8 */
	LWORD		freq;				/* Frequence pour le C2: 2000 a 240000 Hz */
	LWORD		length;			/* Longueur, en samples */
	LWORD		repeat;			/* Point de bouclage, en samples */
	LWORD		replen;			/* Longueur de la boucle, en samples */
	LWORD		loopbuf;			/* Longueur du buffer de bouclage, en samples */
	LWORD		mem_data_offset;	/* Offset des donnees en memoire (par rapport au debut du chunk) */
} MODGT2_SAMPLE2_HEADER;

typedef struct
{
	LWORD		buffer_len [3];	/* Longueur des buffers en samples: 0 = debut, 1,2 = normal */
	WORD		header_flag;		/* 0 = ne pas lire de header, 1 = lire le header du fichier a l'initialisation */
	LWORD		file_data_offset;	/* Offset des donnees dans le fichier */
} MODGT2_SAMPLE2_TYPE1;

typedef struct
{
	WORD		version;			/* Version des donnees additionnelles */
	WORD		group;			/* Numero de groupe de sample. 0 = aucun */
	// Fin version 1
} MODGT2_SAMPLE2_ADDDATA;

typedef struct
{
	MODGT2_SAMPLE2_HEADER	header;
	MODGT2_SAMPLE2_TYPE1	type1;
/*
Suivent dans l'ordre les donnees:
	- type0: le sample, sauf si SPLS_PACK_TYPE_DISK
	  type1: buffer de depart (sauf sur disque)
	- le buffer de repetition (sauf sur disque)
	- le nom de fichier
	- Version >= 9: les donnees additionnelles, alignee sur un multiple de 4 par rapport au debut du chunk.
*/
} MODGT2_SAMPLE2;

/*--------------------------------------------------------------------------*/
/*      Instrument set                                                      */
/*--------------------------------------------------------------------------*/

typedef struct
{
	char		chunkid [4];	/* "ORCH" */
	LWORD		chunksz;
	WORD		ninstr;			/* Nombre d'instruments sauves */
} MODGT2_INS_SET;

/*--------------------------------------------------------------------------*/
/*      Instruments                                                         */
/*--------------------------------------------------------------------------*/

typedef struct
{
	UBYTE		splnum;			/* Numero de sample associe a la note */
	SBYTE		transp;			/* Transposition de ce sample */
} MODGT2_INSTR_NOTE;

typedef struct
{
	UWORD		start;
	UWORD		end;
	UWORD		nbr_loops;		// 1...999, FFFF = infini
} MODGT2_INSTR_ENV_LOOP;

typedef struct
{
	UWORD		time;
	SWORD		val;
} MODGT2_INSTR_ENV_POINT;

typedef struct
{
	UWORD		nbr_points;
	UWORD		lfo_depth;
	UWORD		lfo_speed;
	UWORD		lfo_sweep;
	WORD		lfo_waveform;
	UWORD		fadeout;			// Bit 15: fadeout logarithmique
	WORD		flags;
	MODGT2_INSTR_ENV_LOOP	loop [MODGT2_INS_ENV_MAX_LP];
	MODGT2_INSTR_ENV_POINT	point [MODGT2_INS_ENV_MAX_PT];
} MODGT2_INSTR_ENV;

typedef struct
{
	char		chunkid [4];	/* "INST" */
	LWORD		chunksz;
	WORD		number;			/* 1-... */
	char		name [MODGT2_INS_NAME_LEN];
	WORD		type;				/* 0 = sample */
	WORD		volume;			/* 0..100 */
	SWORD		autobal;			/* 0..800..FFF, -1 = pas d'auto */
	WORD		env_flags [5];	/* Numero d'enveloppe / Flags de chaque envelope: bit 0 = actif, bit 1 = sensibilite accent. A tirer au clair... */
	UBYTE		reserved [4];
	WORD		version;			/* 0, 1, 2 */
	MODGT2_INSTR_NOTE	note [GTK_NBRNOTES_MAXI];	/* Pour chaque note MIDI de 0 a 127 */

	/* Version 1 et + */
	WORD		filter_flags;	// Bit 0: filtre on/off,
									// Bit 1: freq fnc de la velocite,
									// Bit 2: reso fnc de la velocite,
									// Bit 3-4: type du filtre [LP, HP, BP, BC]
	ASCIIDBL	filter_freq [2];	// Freq pour les velo max et min, en Hz si > 20.0, en multiple de la freq de la note si <= 20.0
	ASCIIDBL	filter_reso [2];	// Reso pour les velo max et min, en valeur de Q
	
	/* Version 2 et + */
	UBYTE		pitch_pan_note;	// 0...Sample_REF_C2...127
	SBYTE		pitch_pan_separation;	// -??...0...??
	UBYTE		accent;			// Accent par defaut: 0...40...FF
	UBYTE		nbr_env;			// 5
	MODGT2_INSTR_ENV	env [5];
} MODGT2_INSTR;

/*--------------------------------------------------------------------------*/
/*      Patterns                                                            */
/*--------------------------------------------------------------------------*/

typedef struct
{
	UBYTE		note;
	UBYTE		instr;
	UBYTE		fxnum;
	UBYTE		fxparam;
	UBYTE		volume;
} MODGT2_SPL_NOTE;			/* 5 octets */

typedef struct
{
	UBYTE		fxnum;
	UBYTE		fxparam;
	UBYTE		volume;
} MODGT2_AIN_NOTE;			/* 3 octets */

typedef struct
{
	UBYTE		comnum1;
	UBYTE		comnum2;
	UBYTE		comparam_msb;
	UBYTE		comparam_lsb;
	UBYTE		fxnum;
	UBYTE		fxparam;
	UBYTE		volume;
} MODGT2_FX_NOTE;			/* 7 octets */

typedef struct
{
	union
	{
		struct					/* Commande normale */
		{
			UBYTE		command;		/* Numero de commande normale */
			UBYTE		data [3];	/* Donnees des commandes normales */
		}			normal;
		LWORD		sysex_num;	/* Numero (0xF0 + 24 bits!) dans la table des System Exclusives */
	} event;
} MODGT2_MID_NOTE;			/* 4 octets */

/* Plutot utiliser ca a la place du sizeof () */
#define	MODGT2_SPL_NOTE_LEN		5
#define	MODGT2_AIN_NOTE_LEN		3
#define	MODGT2_FX_NOTE_LEN		7
#define	MODGT2_MID_NOTE_LEN		4

typedef struct
{
	char		chunkid [4];	/* "PATD", "PAIN", "PAFX", "PAMI" */
	LWORD		chunksz;
	WORD		number;			/* 0-... */
	char		name [MODGT2_PAT_NAME_LEN];
	WORD		codagev;			/* 0 a 1 */
	WORD		nbrlines;		/* Nombre de lignes (1-256) */
	WORD		nbrtracks;		/* Nombre de pistes */
	union							/* Donnees */
	{
		MODGT2_SPL_NOTE	spl [1];
		MODGT2_AIN_NOTE	ain [1];
		MODGT2_FX_NOTE	fx [1];
		MODGT2_MID_NOTE	mid [1];
		BYTE		data [1];
	}			pattern;
} MODGT2_PATTERN;

/* Le meme mais sans la section de donnees */
typedef struct
{
	char		chunkid [4];
	LWORD		chunksz;
	WORD		number;
	char		name [MODGT2_PAT_NAME_LEN];
	WORD		codagev;
	WORD		nbrlines;
	WORD		nbrtracks;
} MODGT2_PATTERN_ND;

/*--------------------------------------------------------------------------*/
/*      Nom des pistes                                                      */
/*--------------------------------------------------------------------------*/

typedef struct
{
	WORD		track_type;		/* Type de la piste */
	WORD		track_nbr;		/* Numero de la piste */
	char		name [TRK_NAME_LEN];
} MODGT2_TRKNAME_TRACK;

typedef struct
{
	char		chunkid [4];	/* "TNAM" */
	LWORD		chunksz;
	WORD		nbr_names;		/* Nombre de noms stoques dans la table */
} MODGT2_TRKNAME;



/*--------------------------------------------------------------------------*/
/*      Mix                                                                 */
/*--------------------------------------------------------------------------*/

typedef struct
{
	char		chunkid [4];			/* "TVOL" */
	LWORD		chunksz;
	WORD		nbrtracks;				/* Nombre de pistes dont le volume est defini dans cette section */
	UWORD		volume [1];				/* Volumes 0..1000..FFFF */
} MODGT2_TVOL;

typedef struct
{
	BYTE		track_type;		/* Numero du type de piste source */
	BYTE		dry_flag;		/* Pour les pistes d'effet, 0 = wet, 1 = dry */
	WORD		track;			/* Voie d'entree */
	UWORD		volume;			/* 0..1000..FFFF */
	WORD		balance;			/* 0..800..FFF */
} MODGT2_TRACK_MIX;

typedef struct
{
	char		chunkid [4];			/* "MIXP" */
	LWORD		chunksz;
	WORD		number;					/* 1-... Bit 15 a 1: config actuelle d'un canal */
	char		name [MODGT2_MIX_NAME_LEN];
	WORD		track_type;				/* Type de piste (FX ou AOU) */
	WORD		track_nbr;				/* Piste d'application (par defaut) */
	WORD		nbr_in;					/* Nombre de voies en entree */
	UWORD		vol_wet;					/* 0..1000..FFFF */
	WORD		pan_wet;					/* 0..800..FFF */
	UWORD		vol_dry;
	WORD		pan_dry;
	WORD		version;					/* 0x101 */
	WORD		stereo;					/* Pour les pistes qui peuvent */
	BYTE		reserved [4];
	MODGT2_TRACK_MIX	in [1];	/* Pour chaque voie en entree */
} MODGT2_MIXP;

/* Le meme sans la section de donnees */
typedef struct
{
	char		chunkid [4];
	LWORD		chunksz;
	WORD		number;
	char		name [MODGT2_MIX_NAME_LEN];
	WORD		track_type;
	WORD		track_nbr;
	WORD		nbr_in;
	UWORD		vol_wet;
	WORD		pan_wet;
 	UWORD		vol_dry;
	WORD		pan_dry;
	WORD		version;
	WORD		stereo;
	BYTE		reserved [4];
}
MODGT2_MIXP_ND;

/*--------------------------------------------------------------------------*/
/*      Effets                                                              */
/*--------------------------------------------------------------------------*/

typedef struct
{
	ASCIIDBL	time;				// Secondes
	ASCIIDBL	feedback;		// Normalise
} MODGT2_FX_DELAY;

typedef struct
{
	ASCIIDBL	q;					// q
	ASCIIDBL	freq;				// Frequence centrale, Hz
	ASCIIDBL	band_gap;		// Nombre de 1/2 tons separant la premiere et la deuxieme cellule
	ASCIIDBL	lfo_freq;		// Frequence en Hz d'oscillation de la frequence centrale
	ASCIIDBL	lfo_depth;		// Amplitude des oscillation: en demi-tons
	ASCIIDBL	lfo_phase;		// Phase [0...1[
	WORD	cell_order;			// Nombre de biquads par cellule 1...???
	WORD	nbr_cells;			// Nombre de cellules additionnelles 0...???
	UBYTE	lfo_waveform;		// Type de courbe de l'onde du LFO
	UBYTE	filter_type;		// Type: Low-pass, High-pass, Band-pass, Band-reject
	UBYTE	lfo_flag;			// 1: indique qu'il doit y avoir plus d'une cellule. Uniquement pour Band Pass et Notch.
	UBYTE	multiband_flag;	// 1: LFO actif.
	WORD	nbr_fir_coef;		// > 0: indique que le filtre doit etre convertit en FIR. C'est le nombre des coefs.
} MODGT2_FX_RESFILT;

typedef struct
{
	WORD		type;				// 0 (Boost) ou 1 (Punch)
	ASCIIDBL	gain;				// dB
	ASCIIDBL	threshold;		// dB
	ASCIIDBL	sec_gain;		// dB
} MODGT2_FX_DISTO;

typedef union
{
	MODGT2_FX_DELAY	delay;		// 0x01
	MODGT2_FX_RESFILT	resfilt;		// 0x02
	MODGT2_FX_DISTO	disto;		// 0x03
} MODGT2_FX_UNION;

typedef struct
{
	char		chunkid [4];			// "FX_P"
	LWORD		chunksz;
	WORD		number;					// 1-... Bit 15 a 1: config actuelle d'une
											// piste (le numero de la piste est dans les
											// bits 0 a 13, de 0 a ...).
											// Bit 14: bypass mode
	char		name [MODGT2_FX_NAME_LEN];
	WORD		version;					// 0x100
	WORD		effect_type;			// 0-...
	MODGT2_FX_UNION	fx;			// La config de l'effet
} MODGT2_FXP;

/* Le meme sans toutes les donnees */
typedef struct
{
	char		chunkid [4];			// "FX_P" */
	LWORD		chunksz;
	WORD		number;					// 1-... Bit 15 a 1: config actuelle d'une
											// piste (le numero de la piste est dans les
											// bits 0 a 14, de 0 a ...).
	char		name [MODGT2_FX_NAME_LEN];
	WORD		version;					// 0x100
	WORD		effect_type;			// 0-... Bit 15: bypass mode.
} MODGT2_FXP_ND;

/*--------------------------------------------------------------------------*/
/*      MIDI System Exclusive Table                                         */
/*--------------------------------------------------------------------------*/

typedef struct
{
	char		chunkid [4];		/* "MSET" */
	LWORD		chunksz;
	LWORD		nbr_se;				/* Nombre de System Exclusive */
	union
	{
		LWORD		offset [1];		/* Offset de chaque S.E. dans  */
		UBYTE		data [1];		/* la table, relativement a se.data. */
	}			se;
} MODGT2_MIDI_SYSEX_TABLE;

/*--------------------------------------------------------------------------*/
/*      Temporaire                                                          */
/*--------------------------------------------------------------------------*/

typedef struct
{
	char		chunkid [4];		/* "TEMP" */
	LWORD		chunksz;
} MODGT2_TEMP;

/*--------------------------------------------------------------------------*/
/*      End                                                                 */
/*--------------------------------------------------------------------------*/

typedef struct
{
	char		chunkid [4];
	LWORD		chunksz;
	LWORD		modulesize;		/* Taille totale du module */
} MODGT2_END;



/*\\\ PROTOTYPES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

signed int	MODGT2_save_module_or_song (FILE *file_ptr, bool song_flag);
signed int	MODGT2_save_gt2_pattern (FILE *file_ptr, int track_type, int pattern);
signed int	MODGT2_save_gt2_instrument (FILE *file_ptr, int instr);
signed int	MODGT2_save_gt2_sample (FILE *file_ptr, int sample, bool song_flag);
signed int	MODGT2_save_mix_preset (FILE *file_ptr, int preset);

signed int	MODGT2_get_chunk_offsets (MODS_CHUNK_OFFSET chunk [MODGT2_NBR_CHUNKS_MAXI], int *nbr_chunk_ptr, FILE *file_ptr);
signed int	MODGT2_get_next_chunk (FILE *file_ptr, MODS_CHUNK_OFFSET chunk_list [MODGT2_NBR_CHUNKS_MAXI], int nbr_chunks, int current_chunk, const char id [4], LWORD mask);
signed int	MODGT2_load_gt2_instr (FILE *file_ptr, signed int &instr, bool &not_loaded_flag);
signed int	MODGT2_load_gt2_mix_preset (FILE *file_ptr, signed int &preset, bool &not_loaded_flag);
void	MODGT2_fix_old_gt2_sample_offset_all ();
bool	MODGT2_fix_old_gt2_sample_offset_single_cmd (MODS_GT2_SPL_NOTE &step, int cur_note, int cur_instr);



/*\\\ VARIABLES EXTERNES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ VARIABLES PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

/* Taille de la partie des chunks d'effets qui est specifique a chaque type */
size_t	MODGT2_fx_chunk_size [FxPreset_NBR_TYPES] =
{
	0,
	sizeof (MODGT2_FX_DELAY),
	sizeof (MODGT2_FX_RESFILT),
	sizeof (MODGT2_FX_DISTO)
};



/*\\\ FONCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/****************************************************************************/
/*                                                                          */
/*      SAUVEGARDE                                                          */
/*                                                                          */
/****************************************************************************/



signed int	MODGT2_save_module (FILE *file_ptr)
{
	return (MODGT2_save_module_or_song (file_ptr, false));
}



signed int	MODGT2_save_song (FILE *file_ptr)
{
	return (MODGT2_save_module_or_song (file_ptr, true));
}



/*==========================================================================*/
/*      Nom: MODGT2_save_module_or_song                                     */
/*      Description: Sauve un module en GT2.                                */
/*      Parametres en entree/sortie:                                        */
/*        - file_ptr: pointeur du fichier a sauver.                         */
/*        - song_flag: true si on ne doit sauver que la song (module sans   */
/*                     les samples).                                        */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	MODGT2_save_module_or_song (FILE *file_ptr, bool song_flag)
{
	LWORD		chunk_len;
	int		list;
	int		pattern;
	int		track_type;
	int		pattern_nbr;
	int		instr;
	int		sample;
	int		preset;
	double	tempo;
	int		time_high;
	int		time_low;
	int		song_len;
	int		*pattern_list_ptr;
	time_t	current_time;
	struct tm	date;
	MODGT2_HEADER	header_chunk;
	MODGT2_XCOM		xcom_chunk;
	MODGT2_SONG_D	*song_ptr;
	MODGT2_PAT_SET	patset_chunk;
	MODGT2_TCN2		tcn2_chunk;
	const char	patset_id [Pattern_NBR_TYPES] [4] =
	{
		{ 'P', 'A', 'T', 'S' },
		{ 'P', 'I', 'N', 'S' },
		{ 'P', 'F', 'X', 'S' },
		{ 'P', 'M', 'I', 'S' }
	};
	char		text_0 [1023+1];

	Player &			player = Player::use_instance ();

/*______________________________________________
 *
 * Header
 *______________________________________________
 */

	LOG_printf ("Writing header... ");

	chunk_len = sizeof (header_chunk);

	memcpy (header_chunk.id, "GT2", 3);
	header_chunk.version = MODGT2_FILE_VNUM;
	header_chunk.chunksz = BASE_moto_lword (chunk_len);
	SONG_get_song_name (text_0);
	BASE_copy_string (text_0, header_chunk.name, strlen (text_0), MODGT2_MOD_NAME_LEN);

	/*** A faire ***/
	memset (header_chunk.smallcomment, '\0', MODGT2_SMALLCOM_LEN);

	current_time = time (NULL);
	date = *gmtime (&current_time);
	header_chunk.date.day = (UBYTE) date.tm_mday;
	header_chunk.date.month = (UBYTE) (date.tm_mon + 1);
	header_chunk.date.year = BASE_moto_word (date.tm_year + 1900);
	sprintf (text_0, "Graoumf Tracker v%1.4f", (double)GTK_VERSION_NUMBER);
	BASE_copy_string (text_0, header_chunk.tracker, strlen (text_0), MODGT2_TRA_NAME_LEN);

	if (fwrite (&header_chunk, chunk_len, 1, file_ptr) != 1)
	{
		LOG_printf ("MODGT2_save_module: Error: couldn't write header chunk.\n");
		return (-1);	/* Erreur d'ecriture */
	}

	LOG_printf ("Done.\n");

/*______________________________________________
 *
 * Extra Comment
 *______________________________________________
 */

	LOG_printf ("Writing extra comment... ");

	chunk_len = sizeof (xcom_chunk);

	memcpy (xcom_chunk.chunkid, "XCOM", 4);
	xcom_chunk.chunksz = BASE_moto_lword (chunk_len);

	/*** A faire ***/
	xcom_chunk.length = BASE_moto_word (0);

	if (fwrite (&xcom_chunk, chunk_len, 1, file_ptr) != 1)
	{
		LOG_printf ("MODGT2_save_module: Error: couldn't write Extra Comment chunk.\n");
		return (-1);	/* Erreur d'ecriture */
	}

	LOG_printf ("Done.\n");

/*______________________________________________
 *
 * Song
 *______________________________________________
 */

	LOG_printf ("Writing song... ");

	song_len = SONG_get_song_length ();
	chunk_len = sizeof (MODGT2_SONG) + song_len * sizeof (song_ptr->song [0]);

	song_ptr = (MODGT2_SONG_D *) MALLOC (chunk_len);
	if (song_ptr == NULL)
	{
		LOG_printf ("MODGT2_save_module: Error: couldn't allocate memory for song chunk.\n");
		return (-1);	/* Erreur d'allocation memoire */
	}

	memcpy (song_ptr->chunkid, "SONG", 4);
	song_ptr->chunksz = BASE_moto_lword (chunk_len);
	song_ptr->length = BASE_moto_word (song_len);
	song_ptr->repeat = BASE_moto_word (SONG_get_song_repeat ());
	for (int cnt = 0; cnt < song_len; cnt ++)
	{
		song_ptr->song [cnt] = BASE_moto_word (SONG_get_pattern_number (cnt));
	}

	if (fwrite (song_ptr, chunk_len, 1, file_ptr) != 1)
	{
		LOG_printf ("MODGT2_save_module: Error: couldn't write song chunk.\n");
		return (-1);	/* Erreur d'ecriture */
	}

	FREE (song_ptr);

	LOG_printf ("Done.\n");

/*______________________________________________
 *
 * Pattern set
 *______________________________________________
 */

	LOG_printf ("Writing pattern sets... ");

	/* Reserve de la memoire pour la liste des patterns a sauver */
	pattern_list_ptr = (int *) MALLOC (PAT_NBRPATTERNS_MAXI * sizeof (*pattern_list_ptr));
	if (pattern_list_ptr == NULL)
	{
		LOG_printf ("MODGT2_save_module: Error: couldn't allocate memory for pattern list.\n");
		return (-1);	/* Erreur d'allocation memoire */
	}

	/* Scanne les patterns a sauver et en compte le nombre */
	pattern_nbr = 0;
	for (pattern = 0; pattern < PAT_NBRPATTERNS_MAXI; pattern ++)
	{
		for (track_type = 0; track_type < Pattern_NBR_TYPES; track_type ++)
		{
			if (MODS_pattern_filled (track_type, pattern))
			{
				pattern_list_ptr [pattern_nbr] = pattern;
				pattern_nbr ++;
				break;
			}
		}
	}

	/* Reduit l'espace reserve au strict minimum */
	pattern_list_ptr = (int *) REALLOC (pattern_list_ptr, MAX (pattern_nbr, 1) * sizeof (*pattern_list_ptr));
	if (pattern_list_ptr == NULL)
	{
		LOG_printf ("MODGT2_save_module: Error: couldn't reallocate memory for pattern list.\n");
		return (-1);	/* Erreur de reallocation memoire */
	}

	for (track_type = 0; track_type < Pattern_NBR_TYPES; track_type ++)
	{
		chunk_len = sizeof (patset_chunk);

		/* Construit le chunk */
		memcpy (patset_chunk.chunkid, patset_id [track_type], 4);
		patset_chunk.chunksz = BASE_moto_lword (chunk_len);
		patset_chunk.tracks = BASE_moto_word (GTK_nbr_tracks [track_type]);
		patset_chunk.npat = BASE_moto_word (pattern_nbr);

		/* Sauve le chunk */
		if (fwrite (&patset_chunk, chunk_len, 1, file_ptr) != 1)
		{
			LOG_printf ("MODGT2_save_module: Error: couldn't write %s pattern set chunk.\n",
						   MODS_track_type_name_0_ptr [track_type]);
			return (-1);
		}
	}

	LOG_printf ("Done.\n");

/*______________________________________________
 *
 * Sauvegarde des noms des pistes
 *______________________________________________
 */

	{
		MODGT2_TRKNAME	tnam_chunk;
		MODGT2_TRKNAME_TRACK	tnam_data;
		char		name_0 [TRK_NAME_LEN+1];
		int		nbr_names;
		int		track;

		/* Cherche d'abord le nombre de noms qu'on va devoir sauver */
		nbr_names = 0;
		for (track_type = 0; track_type < GTK_NBR_MIX_TRACK_TYPE; track_type ++)
		{
			for (track = 0; track < GTK_nbr_tracks [track_type]; track ++)
			{
				player.get_track_name (track_type, track, name_0);
				BASE_trim_string (name_0);
				if (strlen (name_0) > 0)
				{
					nbr_names ++;
				}
			}
		}

		/* Si on a des noms a sauver */
		if (nbr_names > 0)
		{
			LOG_printf ("Writing track names... ");

			/* Construction du heade du chunk */
			memcpy (tnam_chunk.chunkid, "TNAM", 4);
			tnam_chunk.chunksz = BASE_moto_lword (  sizeof (tnam_chunk)
			                                      + sizeof (tnam_data) * nbr_names);
			tnam_chunk.nbr_names = BASE_moto_word (nbr_names);

			/* Ecriture du header */
			if (fwrite (&tnam_chunk, sizeof (tnam_chunk), 1, file_ptr) != 1)
			{
				LOG_printf ("MODGT2_save_module: Error: couldn't write track name chunk header.\n");
				return (-1);
			}

			/* Maintenant sauve toutes les noms utilises */
			for (track_type = 0; track_type < GTK_NBR_MIX_TRACK_TYPE; track_type ++)
			{
				for (track = 0; track < GTK_nbr_tracks [track_type]; track ++)
				{
					player.get_track_name (track_type, track, name_0);
					memcpy (tnam_data.name, name_0, TRK_NAME_LEN);
					tnam_data.track_type = BASE_moto_word (track_type);
					tnam_data.track_nbr = BASE_moto_word (track);

					BASE_trim_string (name_0);
					if (strlen (name_0) > 0)
					{
						if (fwrite (&tnam_data, sizeof (tnam_data), 1, file_ptr) != 1)
						{
							LOG_printf ("MODGT2_save_module: Error: couldn't write track name.\n");
							return (-1);
						}
					}
				}
			}

			LOG_printf ("Done.\n");
		}
	}

/*______________________________________________
 *
 * Sauvegarde des patterns
 *______________________________________________
 */

	LOG_printf ("Writing patterns data... ");

	for (list = 0; list < pattern_nbr; list ++)
	{
		pattern = pattern_list_ptr [list];

		for (track_type = 0; track_type < Pattern_NBR_TYPES; track_type ++)
		{
			if (GTK_nbr_tracks [track_type] > 0)
			{
				if (MODGT2_save_gt2_pattern (file_ptr, track_type, pattern))
				{
					LOG_printf ("MODGT2_save_module: Error: couldn't save %s pattern # %d.\n",
					            MODS_track_type_name_0_ptr [track_type], pattern);
					FREE (pattern_list_ptr);
					return (-1);
				}
			}
		}
	}

	FREE (pattern_list_ptr);

	LOG_printf ("Done.\n");

/*______________________________________________
 *
 * Sauvegarde des instruments
 *______________________________________________
 */

	LOG_printf ("Writing instruments... ");

	for (instr = 1; instr <= INST_NBRINSTR_MAXI; instr ++)
	{
		/* Teste si l'instrument est utilise ou pas */
		if (MODS_instr_filled (instr))
		{
			/* Oui, on le sauve */
			if (MODGT2_save_gt2_instrument (file_ptr, instr))
			{
				LOG_printf ("MODGT2_save_module: Error: couldn't save instrument # %d.\n",
					         instr);
				return (-1);	/* Erreur dans la sauvegarde des instruments */
			}
		}
	}

	LOG_printf ("Done.\n");

/*______________________________________________
 *
 * Sauvegarde des mix
 *______________________________________________
 */

	LOG_printf ("Writing mix presets... ");

	/* Presets */
	for (preset = 1; preset < MIXP_NBRMIXP_MAXI; preset ++)
	{
		if (MODS_mix_preset_filled (preset))
		{
			if (MODGT2_save_mix_preset (file_ptr, preset))
			{
				LOG_printf ("MODGT2_save_module: Error: couldn't save mix preset # %d.\n",
					         preset);
				return (-1);
			}
		}
	}

	/* Config actuelle des pistes */
	preset = 1;
	for (int track_type_cnt = 0; track_type_cnt < 2; track_type_cnt ++)
	{
		const int	track_type [2] =
		{
			Pattern_TYPE_FX,
			GTK_TRACK_TYPE_AOU
		};

		for (int track = 0; track < GTK_nbr_tracks [track_type [track_type_cnt]]; track ++)
		{
			/* Capture la config de la piste dans le preset temporaire */
			if (player.save_mix_preset (track_type [track_type_cnt], track, GTK_MIXP_TEMP))
			{
				LOG_printf ("MODGT2_save_module: Error: couldn't get mix config of track # %d, type %d.\n",
					         track, track_type [track_type_cnt]);
				return (-1);
			}

			/* Sauve le preset temporaire */
			if (MODGT2_save_mix_preset (file_ptr, preset | 0x8000))
			{
				LOG_printf ("MODGT2_save_module: Error: couldn't save mix config of track # %d, type %d.\n",
					         track, track_type [track_type_cnt]);
				return (-1);
			}

			preset ++;
		}
	}

	LOG_printf ("Done.\n");

/*______________________________________________
 *
 * Sauvegarde des effets
 *______________________________________________
 */

	LOG_printf ("Writing effect presets... ");

	/* Presets */
	for (preset = 1; preset < FXP_NBRFXP_MAXI; preset ++)
	{
		if (MODS_fx_preset_filled (preset))
		{
			if (MODGT2_save_fx_preset (file_ptr, preset, false))
			{
				LOG_printf ("MODGT2_save_module: Error: couldn't save effect preset # %d.\n",
					         preset);
				return (-1);
			}
		}
	}

	/* Config actuelle des pistes */
	for (int track = 0; track < GTK_nbr_tracks [Pattern_TYPE_FX]; track ++)
	{
		FXP_set_name (GTK_FXP_TEMP, "");

		/* Capture la config de la piste dans le preset temporaire */
		if (player.get_fx_preset (track, GTK_FXP_TEMP) == 0)
		{
			/* Sauve le preset temporaire */
			if (MODGT2_save_fx_preset (file_ptr, track | 0x8000,
			                           player.get_bypass_flag (track)))
			{
				LOG_printf ("MODGT2_save_module: Error: couldn't save effect config of track # %d.\n",
						      track);
				return (-1);
			}
		}
	}

	LOG_printf ("Done.\n");

/*______________________________________________
 *
 * Sauvegarde des SysEx
 *______________________________________________
 */





	/*** A Finir ***/





/*______________________________________________
 *
 * Sauvegarde des samples
 *______________________________________________
 */

	LOG_printf ("Writing samples... ");

	for (sample = 1; sample <= SAMP_NBRSAMPLES_MAXI; sample ++)
	{
		/* Teste si le sample est utilise ou pas */
		if (MODS_sample_filled (sample))
		{
			/* Oui, on le sauve */
			if (MODGT2_save_gt2_sample (file_ptr, sample, song_flag))
			{
				LOG_printf ("MODGT2_save_module: Error: couldn't save sample # %d.\n",
					         sample);
				return (-1);	/* Erreur dans la sauvegarde des samples */
			}
		}
	}

	LOG_printf ("Done.\n");

/*______________________________________________
 *
 * Configuration
 *______________________________________________
 */

	LOG_printf ("Writing configuration... ");

	chunk_len = sizeof (tcn2_chunk);
	memcpy (tcn2_chunk.chunkid, "TCN2", 4);
	tcn2_chunk.chunksz = BASE_moto_lword (chunk_len);
	tcn2_chunk.version = BASE_moto_word (MODGT2_TCN2_VNUM);
	tempo = player.get_tempo ();
	player.get_bar_time (time_high, time_low);
	tcn2_chunk.tempo = BASE_moto_word ((WORD)tempo);
	tcn2_chunk.fine_tempo = BASE_moto_word ((UWORD) ((tempo - floor (tempo)) * 65536));
	tcn2_chunk.speed = BASE_moto_word (player.get_speed ());
	tcn2_chunk.time_high = BASE_moto_word (time_high);
	tcn2_chunk.time_low = BASE_moto_word (time_low);
	tcn2_chunk.period_mode = BASE_moto_word (player.get_linear_period_flag () ? 0 : 1);

	/* Sauve le chunk */
	if (fwrite (&tcn2_chunk, chunk_len, 1, file_ptr) != 1)
	{
		LOG_printf ("MODGT2_save_module: Error: couldn't write configuration chunk.\n");
		return (-1);	/* Erreur d'ecriture */
	}
	
	LOG_printf ("Done.\n");


	
	LOG_printf ("Module saved.\n\n");

	return (0);
}



/*==========================================================================*/
/*      Nom: MODGT2_save_gt2_pattern                                        */
/*      Description: Sauve un pattern.                                      */
/*      Parametres en entree:                                               */
/*        - track_type: type du pattern a sauver.                           */
/*        - pattern: Numero du pattern a sauver.                            */
/*      Parametres en entree/sortie:                                        */
/*        - file_ptr: pointeur du fichier a sauver.                         */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	MODGT2_save_gt2_pattern (FILE *file_ptr, int track_type, int pattern)
{
	int		nbr_tracks;
	int		nbr_lines;
	long		data_len;
	char		name_0 [Pattern_NAME_LEN+1];
	MODGT2_PATTERN_ND	*pattern_ptr;

	pattern_ptr = (MODGT2_PATTERN_ND *) MALLOC (sizeof (MODGT2_PATTERN_ND));
	if (pattern_ptr == NULL)
	{
		LOG_printf ("MODGT2_save_gt2_pattern: Error: couldn't allocate memory for pattern chunk.\n");
		return (-1);
	}

	nbr_lines = PAT_get_pattern_height (pattern);
	nbr_tracks = PAT_get_pattern_nbr_tracks (track_type, pattern);
	data_len = (long)nbr_lines * nbr_tracks * Pattern::NOTE_SIZE [track_type];

	memcpy (pattern_ptr->chunkid, MODS_pattern_chunk_id [track_type] , 4);
	pattern_ptr->chunksz = BASE_moto_lword (sizeof (MODGT2_PATTERN_ND) + data_len);
	pattern_ptr->number = BASE_moto_word (pattern);
	pattern_ptr->codagev = BASE_moto_word (MODGT2_PAT_VNUM);
	pattern_ptr->nbrlines = BASE_moto_word (nbr_lines);
	pattern_ptr->nbrtracks = BASE_moto_word (nbr_tracks);
	PAT_get_pattern_name (pattern, name_0);
	BASE_copy_string (name_0, pattern_ptr->name, strlen (name_0), MODGT2_PAT_NAME_LEN);

	if (fwrite (pattern_ptr, sizeof (MODGT2_PATTERN_ND), 1, file_ptr) != 1)
	{
		LOG_printf ("MODGT2_save_gt2_pattern: Error: couldn't write pattern header.\n");
		FREE (pattern_ptr);
		return (-1);
	}

	FREE (pattern_ptr);

	if (fwrite (PAT_get_note_adr_pat (track_type, pattern, 0, 0),
	            data_len, 1, file_ptr) != 1)
	{
		LOG_printf ("MODGT2_save_gt2_pattern: Error: couldn't write pattern data.\n");
		return (-1);
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: MODGT2_save_gt2_instrument                                     */
/*      Description: Sauve un instrument, sans ses samples.                 */
/*      Parametres en entree:                                               */
/*        - instr: Numero de l'instrument a sauver.                         */
/*      Parametres en entree/sortie:                                        */
/*        - file_ptr: pointeur du fichier a sauver.                         */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	MODGT2_save_gt2_instrument (FILE *file_ptr, int instr)
{
	int		note;
	int		cnt;
	int		cnt_2;
	WORD		filter_flags;
	char		name_0 [Instrument_NAME_LEN+1];
	MODGT2_INSTR	instr_chunk;
	Envelope_LOOP	loop;
	Envelope_POINT	point;

	memcpy (instr_chunk.chunkid, "INST" , 4);
	instr_chunk.chunksz = BASE_moto_lword (sizeof (instr_chunk));
	instr_chunk.number = BASE_moto_word (instr);
	instr_chunk.type = BASE_moto_word (0);
	instr_chunk.volume = BASE_moto_word (INST_get_instr_volume (instr));
	instr_chunk.autobal = BASE_moto_word (INST_get_instr_autobal (instr));
	assert (Envelope_NBR_TYPES <= 5);
	memset (instr_chunk.reserved, 0, 4);
	for (cnt = 0; cnt < Envelope_NBR_TYPES; cnt ++)
	{
		/*** A modifier ***/
		instr_chunk.env_flags [cnt] = BASE_moto_word (INST_get_instr_env (instr, cnt));
	}
	instr_chunk.version = BASE_moto_word (MODGT2_INSTR_VNUM);
	INST_get_instr_name (instr, name_0);
	BASE_copy_string (name_0, instr_chunk.name, strlen (name_0), MODGT2_INS_NAME_LEN);
	for (note = 0; note < GTK_NBRNOTES_MAXI; note ++)
	{
		instr_chunk.note [note].splnum = INST_get_instr_sample (instr, note);
		instr_chunk.note [note].transp = INST_get_instr_transp (instr, note);
	}

	filter_flags = INST_get_filter_flag (instr) ? 0x01 : 0;
	filter_flags += INST_get_filter_freq_vol_flag (instr) ? 0x02 : 0;
	filter_flags += INST_get_filter_q_vol_flag (instr) ? 0x04 : 0;
	filter_flags += FILT_BIQUAD_LOWP << 3;
	instr_chunk.filter_flags = BASE_moto_word (filter_flags);
	for (cnt = 0; cnt < 2; cnt ++)
	{
		BASE_double_to_asciidbl (INST_get_filter_freq (instr, cnt), instr_chunk.filter_freq [cnt]);
		BASE_double_to_asciidbl (INST_get_filter_q (instr, cnt), instr_chunk.filter_reso [cnt]);
	}

	instr_chunk.pitch_pan_note = Sample_REF_C2;
	instr_chunk.pitch_pan_separation = 0;
	instr_chunk.accent = 0x40;
	instr_chunk.nbr_env = Envelope_NBR_TYPES;

	/* Enveloppes */
	for (cnt = 0; cnt < Envelope_NBR_TYPES; cnt ++)
	{
		/* Parametres generaux */
		instr_chunk.env [cnt].nbr_points = BASE_moto_word (ENV_get_nbr_points (cnt, instr));
		instr_chunk.env [cnt].lfo_depth = BASE_moto_word (ENV_get_lfo_depth (cnt, instr));
		instr_chunk.env [cnt].lfo_speed = BASE_moto_word (ENV_get_lfo_speed (cnt, instr));
		instr_chunk.env [cnt].lfo_sweep = BASE_moto_word (ENV_get_lfo_sweep (cnt, instr));
		instr_chunk.env [cnt].lfo_waveform = BASE_moto_word (ENV_get_lfo_waveform (cnt, instr));
		instr_chunk.env [cnt].fadeout = BASE_moto_word (ENV_get_fadeout (cnt, instr));
		instr_chunk.env [cnt].flags = BASE_moto_word (ENV_get_flags (cnt, instr));

		/* Boucles */
		for (cnt_2 = 0; cnt_2 < MIN (Envelope_NBR_LOOPS, MODGT2_INS_ENV_MAX_LP); cnt_2 ++)
		{
			loop = ENV_get_loop (cnt, instr, cnt_2);
			instr_chunk.env [cnt].loop [cnt_2].start = BASE_moto_word (loop.start);
			instr_chunk.env [cnt].loop [cnt_2].end = BASE_moto_word (loop.end);
			instr_chunk.env [cnt].loop [cnt_2].nbr_loops = BASE_moto_word (loop.nbr_loops);
		}

		/* Points */
		for (cnt_2 = 0; cnt_2 < MIN (Envelope_MAX_NBR_POINTS, MODGT2_INS_ENV_MAX_PT); cnt_2 ++)
		{
			point = ENV_get_point (cnt, instr, cnt_2);
			instr_chunk.env [cnt].point [cnt_2].time = BASE_moto_word (point.time);
			instr_chunk.env [cnt].point [cnt_2].val = BASE_moto_word (point.val);
		}
	}

	/* Sauvegarde */
	if (fwrite (&instr_chunk, sizeof (instr_chunk), 1, file_ptr) != 1)
	{
		LOG_printf ("MODGT2_save_gt2_instrument: Error: couldn't write instrument.\n");
		return (-1);	/* Erreur d'ecriture */
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: MODGT2_save_gt2_sample                                         */
/*      Description: Sauve un sample.                                       */
/*      Parametres en entree:                                               */
/*        - sample: Numero du sample a sauver.                              */
/*        - song_flag: indique qu'on ne doit sauver que le nom de fichier   */
/*                     du sample charge, avec son chemin.                   */
/*      Parametres en entree/sortie:                                        */
/*        - file_ptr: pointeur du fichier a sauver.                         */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	MODGT2_save_gt2_sample (FILE *file_ptr, int sample, bool song_flag)
{
	LWORD		length;
	int		filename_len;
	int		sample_mul;
	void		*data_ptr;
	const char	*filename_0;
	String	rel_path;
	char		name_0 [Sample_NAME_LEN+1];
	MODGT2_SAMPLE2	sample_chunk;

	/* Nom du fichier du sample */
	filename_0 = SAMP_get_sample_d2d_filename (sample);

	/* On le transforme en fichier relatif par rapport au chemin du module */
	if (strlen (filename_0) > 0)
	{
		rel_path = FNAM_get_rel_path_from_abs (FILE_module_filename, filename_0);
	}
	else
	{
		rel_path = "";
	}

	filename_len = (int)rel_path.get_len ();

	length = SAMP_get_sample_length (sample);

	// Computes the chunk length
	long				chunk_len = 0;
	if (SAMP_get_sample_type (sample) == 1)
	{
		sample_chunk.header.mem_data_offset = BASE_moto_lword (
			sizeof (MODGT2_SAMPLE2_HEADER) + sizeof (MODGT2_SAMPLE2_TYPE1)
		);
		chunk_len =
			  BASE_moto_lword (sample_chunk.header.mem_data_offset)
			+ filename_len;
	}
	else
	{
		sample_chunk.header.mem_data_offset = BASE_moto_lword (
			sizeof (MODGT2_SAMPLE2_HEADER)
		);
		if (song_flag)
		{
			chunk_len =
				  BASE_moto_lword (sample_chunk.header.mem_data_offset)
				+ filename_len;
		}
		else
		{
			sample_mul = SAMP_get_sample_bytes_per_sample (sample);
			chunk_len =
				  BASE_moto_lword (sample_chunk.header.mem_data_offset)
				+ length * sample_mul
				+ filename_len;
		}
	}

	const int		filename_pad = ((chunk_len + 3) & -4) - chunk_len;
	chunk_len += filename_pad;
	chunk_len += sizeof (MODGT2_SAMPLE2_ADDDATA);
	sample_chunk.header.chunksz = BASE_moto_lword (chunk_len);

	if (song_flag && SAMP_get_sample_type (sample) == 0)
	{
		sample_chunk.header.codagev = BASE_moto_word (SPLS_PACK_TYPE_DISK);
	}
	else
	{
		sample_chunk.header.codagev = BASE_moto_word (SPLS_PACK_TYPE_PCM);
	}

	memcpy (sample_chunk.header.chunkid, "SAM2", 4);
	sample_chunk.header.number = BASE_moto_word (sample);
	sample_chunk.header.type = BASE_moto_word (SAMP_get_sample_type (sample));
	sample_chunk.header.nbits = BASE_moto_word (SAMP_get_sample_resolution (sample));
	sample_chunk.header.byte_order = BASE_moto_word (OS_BYTE_ORDER);
	sample_chunk.header.tracks = BASE_moto_word (SAMP_get_sample_stereo (sample));
	sample_chunk.header.autobal = BASE_moto_word (SAMP_get_sample_balance (sample));
	sample_chunk.header.volume = BASE_moto_word (SAMP_get_sample_volume (sample));
	sample_chunk.header.finetune = BASE_moto_word (SAMP_get_sample_finetune (sample));
	sample_chunk.header.loop = BASE_moto_word ((WORD) SAMP_get_sample_loop (sample));
	sample_chunk.header.midi_note = BASE_moto_word (SAMP_get_sample_midi_note (sample));
	sample_chunk.header.filename_len = BASE_moto_word (filename_len);
	sample_chunk.header.autopan2 = BASE_moto_word (-1);
	sample_chunk.header.freq = BASE_moto_lword (SAMP_get_sample_freq (sample));
	sample_chunk.header.length = BASE_moto_lword (length);
	sample_chunk.header.repeat = BASE_moto_lword (SAMP_get_sample_repeat (sample));
	sample_chunk.header.replen = BASE_moto_lword (SAMP_get_sample_replen (sample));
	sample_chunk.header.loopbuf = BASE_moto_lword (SAMP_get_sample_loopbuf (sample));
	SAMP_get_sample_name (sample, name_0);
	BASE_copy_string (name_0, sample_chunk.header.name, strlen (name_0), MODGT2_SPL_NAME_LEN);

	if (fwrite (&sample_chunk.header, sizeof (sample_chunk.header), 1, file_ptr) != 1)
	{
		LOG_printf ("MODGT2_save_gt2_sample: Error: couldn't write sample header.\n");
		return (-1);	/* Erreur d'ecriture */
	}

	/* informations specifiques au D2D */
	if (SAMP_get_sample_type (sample) == 1)
	{
		sample_chunk.type1.buffer_len [0] = BASE_moto_lword (SAMP_get_sample_buffer_len (sample, 0));
		sample_chunk.type1.buffer_len [1] = BASE_moto_lword (SAMP_get_sample_buffer_len (sample, 1));
		sample_chunk.type1.buffer_len [2] = BASE_moto_lword (SAMP_get_sample_buffer_len (sample, 2));
		sample_chunk.type1.header_flag = BASE_moto_word (SAMP_get_read_d2d_header_flag (sample));
		sample_chunk.type1.file_data_offset = BASE_moto_lword (SAMP_get_sample_file_data_offset (sample));

		if (fwrite (&sample_chunk.type1, sizeof (sample_chunk.type1), 1, file_ptr) != 1)
		{
			LOG_printf ("MODGT2_save_gt2_sample: Error: couldn't write Direct-To-Disk sample information.\n");
			return (-1);	/* Erreur d'ecriture */
		}
	}

	/* Les donnees du sample */
	else if (! song_flag)
	{
		data_ptr = SAMP_get_sample_data_adr (sample);
		if (fwrite (data_ptr, sample_mul * length, 1, file_ptr) != 1)
		{
			LOG_printf ("MODGT2_save_gt2_sample: Error: couldn't write sample data.\n");
			return (-1);
		}
	}

	/* Nom du fichier du sample */
	if (filename_len > 0)
	{
		if (fwrite (rel_path.c_str (), filename_len, 1, file_ptr) != 1)
		{
			LOG_printf ("MODGT2_save_gt2_sample: Error: couldn't write sample filename.\n");
			return (-1);	/* Erreur d'ecriture */
		}
	}

	// Donnees additionnelles
	if (filename_pad > 0)
	{
		char			pad [4] = { '\0' };
		if (fwrite (pad, filename_pad, 1, file_ptr) != 1)
		{
			LOG_printf ("MODGT2_save_gt2_sample: Error: couldn't write filename padding.\n");
			return (-1);	/* Erreur d'ecriture */
		}
	}
	MODGT2_SAMPLE2_ADDDATA		add_data;
	add_data.version = BASE_moto_word (MODGT2_SPL_ADDDATA_VNUM);
	add_data.group   = BASE_moto_word (SAMP_get_sample_group (sample));
	if (fwrite (&add_data, sizeof (add_data), 1, file_ptr) != 1)
	{
		LOG_printf ("MODGT2_save_gt2_sample: Error: couldn't write extra data.\n");
		return (-1);	/* Erreur d'ecriture */
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: MODGT2_save_mix_preset                                         */
/*      Description: Sauve un preset de mixage.                             */
/*      Parametres en entree:                                               */
/*        - preset: numero du preset a sauver. Si le bit 15 est mis, le     */
/*                  preset temporaire (GTK_MIXP_TEMP) est sauve avec le     */
/*                  numero donne.                                           */
/*      Parametres en entree/sortie:                                        */
/*        - file_ptr: pointeur sur le fichier du module.                    */
/*==========================================================================*/

signed int	MODGT2_save_mix_preset (FILE *file_ptr, int preset)
{
	int		saved_preset_nbr;
	int		nbr_sources;
	int		src_cnt;
	MODGT2_MIXP_ND	header;
	char		name_0 [MixPreset_NAME_LEN+1];
	MODGT2_TRACK_MIX	mix_conf;
	PLAY_SOURCE_TRACK_CONF_BLOCK	int_conf;
	LWORD		chunk_len;

	saved_preset_nbr = preset;
	if (preset & 0x8000)
	{
		preset = GTK_MIXP_TEMP;
	}

	nbr_sources = MIXP_get_nbr_source_tracks (preset);
	chunk_len = sizeof (header) + sizeof (mix_conf) * nbr_sources;

	/* Le header */
	memcpy (&header.chunkid, "MIXP", 4);
	header.chunksz = BASE_moto_lword (chunk_len);
	header.number = BASE_moto_word (saved_preset_nbr);
	MIXP_get_preset_name (preset, name_0);
	BASE_copy_string (name_0, header.name, strlen (name_0), MODGT2_MIX_NAME_LEN);
	header.track_type = BASE_moto_word (MIXP_get_track_type (preset));
	header.track_nbr = BASE_moto_word (MIXP_get_track_number (preset));
	header.nbr_in = BASE_moto_word (nbr_sources);
	header.vol_wet = BASE_moto_word ((UWORD)MIXP_get_volume (preset, false));
	header.pan_wet = BASE_moto_word ((WORD)MIXP_get_panning (preset, false));
	header.vol_dry = BASE_moto_word ((UWORD)MIXP_get_volume (preset, true));
	header.pan_dry = BASE_moto_word ((WORD)MIXP_get_panning (preset, true));
	header.stereo = BASE_moto_word (MIXP_get_stereo (preset));
	header.version = BASE_moto_word (0x101);

	if (fwrite (&header, sizeof (header), 1, file_ptr) != 1)
	{
		LOG_printf ("MODGT2_save_mix_preset: Error: couldn't write mix preset chunk header.\n");
		return (-1);	/* Erreur d'ecriture */
	}

	/* Chaque source */
	for (src_cnt = 0; src_cnt < nbr_sources; src_cnt ++)
	{
		MIXP_get_source_track (preset, src_cnt, &int_conf);
		mix_conf.track_type = int_conf.track_type;
		mix_conf.dry_flag = ! int_conf.wet_flag;
		mix_conf.track = BASE_moto_word (int_conf.track_nbr);
		mix_conf.volume = BASE_moto_word (int_conf.inpvol);
		mix_conf.balance = BASE_moto_word ((int_conf.inppan >> 4) & 0xFFF);

		if (fwrite (&mix_conf, sizeof (mix_conf), 1, file_ptr) != 1)
		{
			LOG_printf ("MODGT2_save_mix_preset: Error: couldn't write configuration of source track # %d.\n", src_cnt);
			return (-1);
		}
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: MODGT2_save_fx_preset                                          */
/*      Description: Sauve un preset d'effet.                               */
/*      Parametres en entree:                                               */
/*        - preset: numero du preset a sauver. Si le bit 15 est mis, le     */
/*                  preset temporaire (GTK_FXP_TEMP) est sauve avec le      */
/*                  numero donne. (qui contient eventuellement le bit 14    */
/*                  correspondant au bypass de l'effet).                    */
/*        - bypass: indique si l'effet est court-circuite.                  */
/*      Parametres en entree/sortie:                                        */
/*        - file_ptr: pointeur sur le fichier du module.                    */
/*==========================================================================*/

signed int	MODGT2_save_fx_preset (FILE *file_ptr, int preset, bool bypass_flag)
{
	int		type;
	int		saved_preset_nbr;
	MODGT2_FXP_ND	header;
	char		name_0 [FxPreset_NAME_LEN+1];
	LWORD		chunk_len;
	FxPreset_EFFECT_CONF	conf;
	MODGT2_FX_UNION	fx;

	saved_preset_nbr = preset;
	if (preset & 0x8000)
	{
		preset = GTK_FXP_TEMP;
	}

	if (bypass_flag)
	{
		saved_preset_nbr |= 0x4000;
	}

	type = FXP_get_effect_type (preset);
	chunk_len = LWORD (sizeof (header) + MODGT2_fx_chunk_size [type]);

/*______________________________________________
 *
 * Sauvegarde du header
 *______________________________________________
 */

	memcpy (header.chunkid, "FX_P", 4);
	header.chunksz = BASE_moto_lword (chunk_len);
	header.number = BASE_moto_word (saved_preset_nbr);
	FXP_get_name (preset, name_0);
	BASE_copy_string (name_0, header.name, strlen (name_0), MODGT2_FX_NAME_LEN);
	header.version = BASE_moto_word (0x100);		// Version de l'effet
	header.effect_type = BASE_moto_word (type);

	/* Sauvegarde */
	if (fwrite (&header, sizeof (header), 1, file_ptr) != 1)
	{
		LOG_printf ("MODGT2_save_fx_preset: Error: couldn't write header of Effect Preset # %d.\n", preset);
		return (-1);
	}

/*______________________________________________
 *
 * Sauvegarde de la partie propre a chaque
 * type d'effet
 *______________________________________________
 */

	/* On recupere la configuration */
	conf = FXP_get_parameters (preset);

	switch (type)
	{

	/* Delay */
	case	FxPreset_TYPE_DELAY:
		BASE_double_to_asciidbl (conf.delay.delay, fx.delay.time);
		BASE_double_to_asciidbl (conf.delay.feedback, fx.delay.feedback);
		break;
	
	/* Filtre resonnant */
	case	FxPreset_TYPE_RESFILT:
		BASE_double_to_asciidbl (conf.resfilt.q, fx.resfilt.q);
		BASE_double_to_asciidbl (conf.resfilt.freq, fx.resfilt.freq);
		BASE_double_to_asciidbl (conf.resfilt.cell_ratio, fx.resfilt.band_gap);
		BASE_double_to_asciidbl (conf.resfilt.lfo_freq, fx.resfilt.lfo_freq);
		BASE_double_to_asciidbl (conf.resfilt.lfo_depth, fx.resfilt.lfo_depth);
		BASE_double_to_asciidbl (conf.resfilt.lfo_phase, fx.resfilt.lfo_phase);
		fx.resfilt.nbr_cells = BASE_moto_word ((WORD) (conf.resfilt.nbr_cells));
		fx.resfilt.cell_order = BASE_moto_word ((WORD) (conf.resfilt.nbr_biquads * 2));
		fx.resfilt.lfo_waveform = (UBYTE) conf.resfilt.lfo_waveform;
		fx.resfilt.filter_type = (UBYTE) conf.resfilt.type;
		fx.resfilt.lfo_flag = conf.resfilt.lfo_flag ? 1 : 0;
		fx.resfilt.multiband_flag = conf.resfilt.cell_flag ? 1 : 0;
		fx.resfilt.nbr_fir_coef = BASE_moto_word (0);	// conf.resfilt.nbr_fir_coef ?
		break;

	/* Distorsion */
	case	FxPreset_TYPE_DISTO:
		fx.disto.type = BASE_moto_word ((WORD) (conf.disto.type));
		BASE_double_to_asciidbl (conf.disto.gain, fx.disto.gain);
		BASE_double_to_asciidbl (conf.disto.threshold, fx.disto.threshold);
		BASE_double_to_asciidbl (conf.disto.sec_gain, fx.disto.sec_gain);
		break;
	}

	/* Sauvegarde */
	if (type != FxPreset_TYPE_NONE)
	{
		if (fwrite (&fx, MODGT2_fx_chunk_size [type], 1, file_ptr) != 1)
		{
			LOG_printf ("MODGT2_save_fx_preset: Error: couldn't write Effect Preset # %d.\n", preset);
			return (-1);
		}
	}

	return (0);
}



/****************************************************************************/
/*                                                                          */
/*      CHARGEMENT                                                          */
/*                                                                          */
/****************************************************************************/



/*==========================================================================*/
/*      Nom: MODGT2_load_module                                             */
/*      Description: Charge un module au format GT2.                        */
/*      Parametres en entree/sortie:                                        */
/*        - file_ptr: pointeur sur le fichier a charger.                    */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	MODGT2_load_module (FILE *file_ptr, BYTE temp_info [MODS_TEMP_INFO_LEN])
{
	int		chunk;
	int		nbr_chunks;
	MODS_CHUNK_OFFSET	chunk_list [MODGT2_NBR_CHUNKS_MAXI];
	int		file_version;
	void		*data_ptr;
	MODGT2_HEADER	*gt2_chunk_ptr;
	MODGT2_EXTRA_HEADER	*extra_header_ptr;
	MODGT2_XCOM	xcom_chunk;
	MODGT2_SONG_D	*song_chunk_ptr;
	MODGT2_PAT_SET	*patset_chunk_ptr;
	MODGT2_PATTERN_ND	*pattern_chunk_ptr;
	MODGT2_SAMPLE2	*sam2_chunk_ptr;
	MODGT2_SAMPLE	*samp_chunk_ptr;
	MODGT2_TCN2		tcn2_chunk;
	char		text_0 [1024+1];
	bool		not_loaded_flag;
	bool		reverse_byte_order_flag;
	bool		bypass_flag;
	long		length;
	long		count;
	LWORD		data_pos;
	LWORD		reppos;
	LWORD		replen;
	int		track_type;
	int		pattern;
	int		codagev;
	int		line;
	int		nbr_lines;
	int		nbr_tracks;
	int		nbr_tracks_2;
	int		nbr_tracks_tab [Pattern_NBR_TYPES];
	int		instr;
	int		sample;
	int		sample_type;
	int		sample_mul;
	int		loop_type;
	int		pan_length;
	signed int	preset;
	int		track;
	WORD		*pan_ptr;
	SPLS_PRIMARY_INFO	sample_info;
	String	xcom;
	int		inst_count;
	int		mix_preset_count;
	int		mix_cfg_count;
	int		fx_preset_count;
	int		fx_cfg_count;
	int		sample_count;

	Player &			player = Player::use_instance ();

	/* Repere la position de chaque chunk dans le fichier */
	if (MODGT2_get_chunk_offsets (chunk_list, &nbr_chunks, file_ptr))
	{
		LOG_printf ("MODGT2_load_module: Error: couldn't get chunk offsets.\n");
		return (-1);	/* Erreur lors du reperage de la position des chunks */
	}

/*______________________________________________
 *
 * GT2x
 *______________________________________________
 */

	chunk = MODGT2_get_next_chunk (file_ptr, chunk_list, nbr_chunks, 0, "GT2 ", 0xFFFFFF00L);
	if (chunk < 0)
	{
		LOG_printf ("MODGT2_load_module: Error: couldn't get header chunk.\n");
		return (-1);	/* Erreur lors de la recherche du chunk */
	}
	if (chunk < nbr_chunks)
	{
		/* Version precise */
		file_version = chunk_list [chunk].id [3];
		LOG_printf ("Format GT2 (Graoumf Tracker), version %d.\n", file_version);

		/* Chargement des donnees */
		gt2_chunk_ptr = (MODGT2_HEADER *) MALLOC (chunk_list [chunk].size);
		if (gt2_chunk_ptr == NULL)
		{
			LOG_printf ("MODGT2_load_module: Error: couldn't allocate memory for header chunk.\n");
			return (-1);	/* Erreur d'allocation memoire */
		}
		if (fread (gt2_chunk_ptr, chunk_list [chunk].size, 1, file_ptr) != 1)
		{
			LOG_printf ("MODGT2_load_module: Error: couldn't read header.\n");
			FREE (gt2_chunk_ptr);
			return (-1);	/* Erreur de lecture */
		}

		/* Nom du module */
		BASE_copy_string (gt2_chunk_ptr->name, text_0,
								MODGT2_MOD_NAME_LEN, SONG_NAME_LEN);
		text_0 [SONG_NAME_LEN] = 0;
		SONG_set_song_name (text_0);
		LOG_printf ("Module name: %s\n", text_0);

		/* Commentaire */
		BASE_copy_string (gt2_chunk_ptr->smallcomment, text_0,
								MODGT2_SMALLCOM_LEN, SONG_SMALLCOM_LEN);
		text_0 [SONG_SMALLCOM_LEN] = 0;
		SONG_set_small_comment (text_0);
		BASE_trim_string (text_0);
		if (strlen (text_0) > 0)
		{
			LOG_printf ("Comment:\n%s\n", text_0);
		}

		/* Date de la derniere modif */
		LOG_printf ("Last modified: %04d.%02d.%02d\n",
		            (int) BASE_moto_word ((WORD) gt2_chunk_ptr->date.year),
		            (int) gt2_chunk_ptr->date.month,
		            (int) gt2_chunk_ptr->date.day);

		/* Tracker de creation */
		BASE_copy_string (gt2_chunk_ptr->tracker, text_0,
								MODGT2_TRA_NAME_LEN, SONG_TRACKER_NAME_LEN);
		text_0 [SONG_TRACKER_NAME_LEN] = 0;
		LOG_printf ("Made with: %s\n", text_0);

		/* Information des versions < 6 */
		if (file_version < 6)
		{
			extra_header_ptr = (MODGT2_EXTRA_HEADER *) (gt2_chunk_ptr + 1);

			player.set_tempo (BASE_moto_word (extra_header_ptr->tempo));
			player.set_speed (BASE_moto_word (extra_header_ptr->speed));
			player.set_lin_master_volume (BASE_moto_word (extra_header_ptr->master_volume));

			/* Panning */
			pan_length = BASE_moto_word (extra_header_ptr->pan_section_len);
			pan_ptr = (WORD *) MALLOC (pan_length * sizeof (*pan_ptr));
			if (pan_ptr == NULL)
			{
				LOG_printf ("MODGT2_load_module: Error: couldn't allocate memory for panning table.\n");
				FREE (gt2_chunk_ptr);
				return (-1);
			}
			for (count = 0; count < pan_length; count ++)
			{
				pan_ptr [count] = BASE_moto_word (extra_header_ptr->pan [count]);
			}
		}

		FREE (gt2_chunk_ptr);
	}

/*______________________________________________
 *
 * XCOM
 *______________________________________________
 */

	chunk = MODGT2_get_next_chunk (file_ptr, chunk_list, nbr_chunks, 0, "XCOM", 0xFFFFFFFFL);
	if (chunk < 0)
	{
		LOG_printf ("MODGT2_load_module: Error: couldn't get Extra Comment chunk.\n");
		return (-1);
	}
	if (chunk < nbr_chunks)
	{
		LOG_printf ("Reading extra-comment chunk... ");

		/* Chargement de l'en-tete */
		if (fread (&xcom_chunk, sizeof (xcom_chunk), 1, file_ptr) != 1)
		{
			LOG_printf ("MODGT2_load_module: Error: couldn't read Extra Comment header.\n");
			return (-1);
		}

		length = (UWORD) BASE_moto_word ((WORD) xcom_chunk.length);
		std::vector <char>	tmp (length + 1);
		tmp [length] = '\0';

		/* Chargement des donnees */
		if (length > 0)
		{
			if (fread (&tmp [0], length, 1, file_ptr) != 1)
			{
				LOG_printf ("MODGT2_load_module: Error: couldn't read Extra Comment data.\n");
				return (-1);
			}
			else
			{
				xcom = String (&tmp [0]);
			}
		}

		LOG_printf ("Done.\n");
	}

/*______________________________________________
 *
 * Song
 *______________________________________________
 */


	chunk = MODGT2_get_next_chunk (file_ptr, chunk_list, nbr_chunks, 0, "SONG", 0xFFFFFFFFL);
	if (chunk < 0)
	{
		LOG_printf ("MODGT2_load_module: Error: couldn't get song chunk.\n");
		return (-1);
	}
	if (chunk < nbr_chunks)
	{
		LOG_printf ("Reading song chunk... ");

		/* Chargement des donnees */
		song_chunk_ptr = (MODGT2_SONG_D *) MALLOC (chunk_list [chunk].size);
		if (song_chunk_ptr == NULL)
		{
			LOG_printf ("MODGT2_load_module: Error: couldn't allocate memory for song chunk.\n");
			return (-1);
		}
		if (fread (song_chunk_ptr, chunk_list [chunk].size, 1, file_ptr) != 1)
		{
			LOG_printf ("MODGT2_load_module: Error: couldn't read song chunk.\n");
			FREE (song_chunk_ptr);
			return (-1);
		}

		/* Conversion */
		length = BASE_moto_word (song_chunk_ptr->length);
		SONG_set_song_length ((int)length);
		SONG_set_song_repeat (BASE_moto_word (song_chunk_ptr->repeat));
		for (count = 0; count < length; count ++)
		{
			pattern = BASE_moto_word (song_chunk_ptr->song [count]);
			if (pattern >= PAT_NBRPATTERNS_MAXI)
			{
				pattern = 0;
			}
			SONG_set_pattern_number (count, pattern);
		}

		LOG_printf ("Done.\n");

		FREE (song_chunk_ptr);
	}

/*______________________________________________
 *
 * Pattern sets
 *______________________________________________
 */

	patset_chunk_ptr = (MODGT2_PAT_SET *) MALLOC (sizeof (MODGT2_PAT_SET));
	if (patset_chunk_ptr == NULL)
	{
		LOG_printf ("MODGT2_load_module: Error: couldn't allocate memory for pattern set chunk.\n");
		return (-1);
	}

	/* Pour chaque type de piste */
	count = 0;
	for (track_type = 0; track_type < Pattern_NBR_TYPES; track_type ++)
	{
		nbr_tracks_tab [track_type] = 0;

		chunk = MODGT2_get_next_chunk (file_ptr, chunk_list, nbr_chunks, 0,
		                             MODS_patset_chunk_id [track_type], 0xFFFFFFFFL);
		if (chunk < 0)
		{
			LOG_printf ("MODGT2_load_module: Error: couldn't get %s pattern set chunk.\n",
			            MODS_track_type_name_0_ptr [track_type]);
			FREE (patset_chunk_ptr);
			return (-1);
		}
		if (chunk < nbr_chunks)
		{
			LOG_printf ("Reading %s pattern set chunk... ", MODS_track_type_name_0_ptr [track_type]);

			/* Chargement des donnees */
			if (fread (patset_chunk_ptr, chunk_list [chunk].size, 1, file_ptr) != 1)
			{
				LOG_printf ("MODGT2_load_module: Error: couldn't read %s pattern set chunk.\n",
				            MODS_track_type_name_0_ptr [track_type]);
				FREE (patset_chunk_ptr);
				return (-1);
			}

			/* Nombre de pistes */
			nbr_tracks_tab [track_type] = BASE_moto_word (patset_chunk_ptr->tracks);

			LOG_printf ("%d tracks.\n", nbr_tracks_tab [track_type]);
		}

		count += nbr_tracks_tab [track_type];
	}

	FREE (patset_chunk_ptr);

	/* Verifie qu'on est dans les limites */
	if (count > GTK_NBRTRACKS_MAXI)
	{
		LOG_printf ("Too many tracks.\n");
		return (-1);
	}

	/* Change le nombre de pistes */
	for (count = 0; count < 2; count ++)
	{
		for (track_type = 0; track_type < Pattern_NBR_TYPES; track_type ++)
		{
			/* Le premier tour, on ne change que les patterns qui doivent etre
			   diminuees, et le deuxieme on change ceux qui augmente. On est
			   alors sur de ne jamais depasser le nombre maximum de pistes
				autorisees. */
			if (  (signed int)(nbr_tracks_tab [track_type] - GTK_nbr_tracks [track_type])
			    * ((signed int)count * 2 - 1) > 0)
			{
				if (track_type != Pattern_TYPE_AIN)
				{
					if (PAT_set_nbr_tracks (track_type, nbr_tracks_tab [track_type]))
					{
						LOG_printf ("Error: couldn't set number of tracks of %s patterns to %d.\n",
						            MODS_track_type_name_0_ptr [track_type], nbr_tracks_tab [track_type]);
						return (-1);
					}
				}

				/* On ne peut pas changer le nombre de pistes Audio In, donc on
				   avertit s'il y en a plus que ce qu'on peut accueillir. */
				else if (nbr_tracks_tab [track_type] > GTK_nbr_tracks [track_type])
				{
					LOG_printf ("Warning: %d Audio In tracks can't be loaded (max: %d).\n",
					            nbr_tracks_tab [track_type] - GTK_nbr_tracks [track_type],
					            GTK_nbr_tracks [track_type]);
				}
			}
		}
	}

/*______________________________________________
 *
 * Noms des pistes
 *______________________________________________
 */

	{
		int		nbr_names;
		int		track_type;
		int		track_nbr;
		MODGT2_TRKNAME	tnam_chunk;
		MODGT2_TRKNAME_TRACK	tnam_data;
		char		name_0 [TRK_NAME_LEN+1];

		chunk = MODGT2_get_next_chunk (file_ptr, chunk_list, nbr_chunks, 0, "TNAM", 0xFFFFFFFFL);
		if (chunk < 0)
		{
			LOG_printf ("MODGT2_load_module: Error: couldn't get track name chunk.\n");
			return (-1);
		}
		if (chunk < nbr_chunks)
		{
			LOG_printf ("Reading track name chunk... ");

			if (fread (&tnam_chunk, sizeof (tnam_chunk), 1, file_ptr) != 1)
			{
				LOG_printf ("MODGT2_load_module: Error: couldn't read track name chunk.\n");
				return (-1);
			}

			for (nbr_names = BASE_moto_word (tnam_chunk.nbr_names); nbr_names > 0; -- nbr_names)
			{
				if (fread (&tnam_data, sizeof (tnam_data), 1, file_ptr) != 1)
				{
					LOG_printf ("MODGT2_load_module: Error: couldn't read track name.\n");
					return (-1);
				}

				track_type = BASE_moto_word (tnam_data.track_type);
				track_nbr = BASE_moto_word (tnam_data.track_nbr);

				if (   track_type < GTK_NBR_MIX_TRACK_TYPE
				    && track_nbr < GTK_nbr_tracks [track_type])
				{
					memcpy (name_0, tnam_data.name, TRK_NAME_LEN);
					name_0 [TRK_NAME_LEN] = '\0';
					player.set_track_name (track_type, track_nbr, name_0);
				}
			}

			LOG_printf ("Done.\n");
		}
	}



/*______________________________________________
 *
 * Patterns
 *______________________________________________
 */

	pattern_chunk_ptr = (MODGT2_PATTERN_ND *) MALLOC (sizeof (MODGT2_PATTERN_ND));
	if (pattern_chunk_ptr == NULL)
	{
		LOG_printf ("MODGT2_load_module: Error: couldn't allocate memory for pattern chunk.\n");
		return (-1);
	}

	not_loaded_flag = false;
	for (track_type = 0; track_type < Pattern_NBR_TYPES; track_type ++)
	{
		for (chunk = 0 ; ; chunk ++)
		{
			chunk = MODGT2_get_next_chunk (file_ptr, chunk_list,
			                             nbr_chunks, chunk,
			                             MODS_pattern_chunk_id [track_type],
			                             0xFFFFFFFFL);
			if (chunk < 0)
			{
				LOG_printf ("MODGT2_load_module: Error: couldn't get next %s pattern chunk.\n",
				            MODS_track_type_name_0_ptr [track_type]);
				FREE (pattern_chunk_ptr);
				return (-1);
			}

			if (chunk >= nbr_chunks)
			{
				break;
			}

			/* Charge l'en-tete du pattern */
			if (fread (pattern_chunk_ptr, sizeof (*pattern_chunk_ptr), 1, file_ptr) != 1)
			{
				LOG_printf ("MODGT2_load_module: Error: couldn't read pattern chunk header.\n");
				FREE (pattern_chunk_ptr);
				return (-1);
			}

			/* Numero du pattern */
			pattern = BASE_moto_word (pattern_chunk_ptr->number);

			/* Verifie que le numero de pattern n'est pas trop haut */
			if (pattern < PAT_NBRPATTERNS_MAXI)
			{
				/* Nom du pattern */
				memcpy (text_0, pattern_chunk_ptr->name, MODGT2_PAT_NAME_LEN);
				text_0 [MODGT2_PAT_NAME_LEN] = '\0';
				PAT_set_pattern_name (pattern, text_0);

				/* Nombre de lignes */
				nbr_lines = BASE_moto_word (pattern_chunk_ptr->nbrlines);
				if (PAT_set_pattern_height (pattern, nbr_lines))
				{
					LOG_printf ("MODGT2_load_module: Error: couldn't set pattern height # %d to %d lines.\n",
					            pattern, nbr_lines);
					FREE (pattern_chunk_ptr);
					return (-1);
				}

				/* Version de codage des patterns */
				codagev = BASE_moto_word (pattern_chunk_ptr->codagev);

				/* Chargement des donnees */
				if (   codagev >= 0
				    && codagev <= MODGT2_PAT_VNUM)
				{
					nbr_tracks = BASE_moto_word (pattern_chunk_ptr->nbrtracks);
					nbr_tracks_2 = MIN (nbr_tracks, GTK_nbr_tracks [track_type]);
					length = nbr_tracks_2 * Pattern::NOTE_SIZE [track_type];
					for (line = 0; line < nbr_lines; line ++)
					{
						data_ptr = PAT_get_note_adr_pat (track_type, pattern, line, 0);
						if (fread (data_ptr, length, 1, file_ptr) != 1)
						{
							LOG_printf ("MODGT2_load_module: Error: couldn't read pattern line.\n");
							FREE (pattern_chunk_ptr);
							return (-1);
						}

						/* On est oblige de sauter des pistes */
						if (nbr_tracks > nbr_tracks_2)
						{
							if (fseek (file_ptr,
											 (nbr_tracks - nbr_tracks_2)
										  * Pattern::NOTE_SIZE [track_type],
										  SEEK_CUR) != 0)
							{
								LOG_printf ("MODGT2_load_module: Error: couldn't get next pattern line.\n");
								FREE (pattern_chunk_ptr);
								return (-1);
							}
						}
					}

					/* Mets la colonne de volume au format FT2 */
					if (codagev < 1)
					{
						if (track_type == Pattern_TYPE_SPL)
						{
							for (line = 0; line < nbr_lines; line ++)
							{
								for (track = 0; track < nbr_tracks_2; track ++)
								{
									data_ptr = PAT_get_note_adr_pat (track_type, pattern, line, track);
									if ((*((MODS_GT2_SPL_NOTE *) data_ptr)).volume > 0)
									{
										(*((MODS_GT2_SPL_NOTE *) data_ptr)).volume =
											(((*((MODS_GT2_SPL_NOTE *) data_ptr)).volume + 2) >> 2) + 0x10;
									}
								}
							}
						}
						else if (track_type == Pattern_TYPE_AIN)
						{
							for (line = 0; line < nbr_lines; line ++)
							{
								for (track = 0; track < nbr_tracks_2; track ++)
								{
									data_ptr = PAT_get_note_adr_pat (track_type, pattern, line, track);
									if ((*((MODS_GT2_AIN_NOTE *) data_ptr)).volume > 0)
									{
										(*((MODS_GT2_AIN_NOTE *) data_ptr)).volume =
											(((*((MODS_GT2_AIN_NOTE *) data_ptr)).volume + 2) >> 2) + 0x10;
									}
								}
							}
						}
						else if (track_type == Pattern_TYPE_FX)
						{
							for (line = 0; line < nbr_lines; line ++)
							{
								for (track = 0; track < nbr_tracks_2; track ++)
								{
									data_ptr = PAT_get_note_adr_pat (track_type, pattern, line, track);
									if ((*((MODS_GT2_FX_NOTE *) data_ptr)).volume > 0)
									{
										(*((MODS_GT2_FX_NOTE *) data_ptr)).volume =
											(((*((MODS_GT2_FX_NOTE *) data_ptr)).volume + 2) >> 2) + 0x10;
									}
								}
							}
						}
					}
				}

				/* Version de codage inconnue */
				else
				{
					LOG_printf ("Warning: Pattern %d, type %s, codage version %n is unsupported. Pattern not loaded.\n",
								   pattern, MODS_track_type_name_0_ptr [track_type], codagev);
				}
			}

			/* Numero de pattern trop grand */
			else
			{
				not_loaded_flag = true;
			}
		}
	}

	FREE (pattern_chunk_ptr);

	if (not_loaded_flag)
	{
		LOG_printf ("Warning: Some patterns were not loaded (number too high).\n");
	}

/*______________________________________________
 *
 * Instruments
 *______________________________________________
 */

	LOG_printf ("Reading instrument chunks... ");

	not_loaded_flag = false;
	inst_count = 0;
	for (chunk = 0 ; ; chunk ++)
	{
		chunk = MODGT2_get_next_chunk (file_ptr, chunk_list,
			                          nbr_chunks, chunk,
											  "INST",
			                          0xFFFFFFFFL);
		if (chunk < 0)
		{
			LOG_printf ("MODGT2_load_module: Error: couldn't get next instrument chunk.\n");
			return (-1);
		}

		if (chunk >= nbr_chunks)
		{
			break;
		}

		instr = -1;
		if (MODGT2_load_gt2_instr (file_ptr, instr, not_loaded_flag))
		{
			LOG_printf ("MODGT2_load_module: Error: couldn't read instrument chunk.\n");
			return (-1);
		}

		inst_count ++;
	}

	LOG_printf ("%d instruments.\n", inst_count);

	if (not_loaded_flag)
	{
		LOG_printf ("Warning: Some instruments were not loaded (number too high).\n");
	}

/*______________________________________________
 *
 * Mix
 *______________________________________________
 */

	LOG_printf ("Reading mixing presets and configurations... ");

	not_loaded_flag = false;
	mix_preset_count = 0;
	mix_cfg_count = 0;
	for (chunk = 0 ; ; chunk ++)
	{
		chunk = MODGT2_get_next_chunk (file_ptr, chunk_list,
		                               nbr_chunks, chunk,
		                               "MIXP", 0xFFFFFFFFL);
		if (chunk < 0)
		{
			LOG_printf ("MODGT2_load_module: Error: couldn't get next mix preset chunk.\n");
			return (-1);
		}

		if (chunk >= nbr_chunks)
		{
			break;
		}

		preset = -1;
		if (MODGT2_load_gt2_mix_preset (file_ptr, preset, not_loaded_flag))
		{
			LOG_printf ("MODGT2_load_module: Error: couldn't read mix preset chunk.\n");
			return (-1);
		}

		/* Si le chunk a ete charge dans l'espace temporaire,
		   alors c'est un chunk de configuration */
		if (preset == GTK_MIXP_TEMP)
		{
			if (player.load_mix_preset (-1, -1, preset, Player::MIX_PRESET_REPL))
			{
				LOG_printf ("MODGT2_load_module: Warning: couldn't apply configuration of mix preset.\n");
			}
			mix_cfg_count ++;
		}
		else
		{
			mix_preset_count ++;
		}
	}

	LOG_printf ("%d presets, %d configurations.\n", mix_preset_count, mix_cfg_count);

	if (not_loaded_flag)
	{
		LOG_printf ("Warning: Some mix presets were not loaded (number too high).\n");
	}

/*______________________________________________
 *
 * Effets
 *______________________________________________
 */

	LOG_printf ("Reading effect presets and configurations... ");

	not_loaded_flag = false;
	fx_preset_count = 0;
	fx_cfg_count = 0;
	for (chunk = 0 ; ; chunk ++)
	{
		chunk = MODGT2_get_next_chunk (file_ptr, chunk_list,
		                               nbr_chunks, chunk,
		                               "FX_P", 0xFFFFFFFFL);
		if (chunk < 0)
		{
			LOG_printf ("MODGT2_load_module: Error: couldn't get next effect preset chunk.\n");
			return (-1);
		}

		if (chunk >= nbr_chunks)
		{
			break;
		}

		preset = -1;
		if (MODGT2_load_fx_preset (file_ptr, preset, track, bypass_flag, not_loaded_flag))
		{
			LOG_printf ("MODGT2_load_module: Error: couldn't read effect preset chunk.\n");
			return (-1);
		}

		if (preset == GTK_FXP_TEMP)
		{
			/* Si le chunk a ete charge dans l'espace temporaire,
				alors c'est un chunk de configuration */
			if (player.set_fx_preset (track, GTK_FXP_TEMP, 0xFFFFFFFFL))
			{
				LOG_printf ("MODGT2_load_module: Warning: couldn't apply configuration of effect preset.\n");
			}

			player.set_bypass_flag (track, bypass_flag);

			fx_cfg_count ++;
		}
		else
		{
			fx_preset_count ++;
		}
	}

	LOG_printf ("%d presets, %d configurations.\n", fx_preset_count, fx_cfg_count);

	if (not_loaded_flag)
	{
		LOG_printf ("Warning: Some effect presets were not loaded (number too high).\n");
	}

/*______________________________________________
 *
 * SysEx
 *______________________________________________
 */





	/*** A Finir ***/





/*______________________________________________
 *
 * Samples SAM2
 *______________________________________________
 */

	LOG_printf ("Reading samples... ");

	not_loaded_flag = false;
	sample_count = 0;

	sam2_chunk_ptr = (MODGT2_SAMPLE2 *) MALLOC (sizeof (*sam2_chunk_ptr));
	if (sam2_chunk_ptr == NULL)
	{
		LOG_printf ("MODGT2_load_module: Error: couldn't allocate memory for sample chunk.\n");
		return (-1);
	}

	for (chunk = 0 ; ; chunk ++)
	{
		chunk = MODGT2_get_next_chunk (file_ptr, chunk_list,
			                          nbr_chunks, chunk,
											  "SAM2",
			                          0xFFFFFFFFL);
		if (chunk < 0)
		{
			LOG_printf ("MODGT2_load_module: Error: couldn't get next sample chunk.\n");
			FREE (sam2_chunk_ptr);
			return (-1);
		}

		if (chunk >= nbr_chunks)
		{
			break;
		}

		const size_t		chunk_pos = ftell (file_ptr);

		/* Charge l'en-tete du sample */
		if (fread (sam2_chunk_ptr, sizeof (*sam2_chunk_ptr), 1, file_ptr) != 1)
		{
			LOG_printf ("MODGT2_load_module: Error: couldn't read sample chunk header.\n");
			FREE (sam2_chunk_ptr);
			return (-1);
		}

		/* Numero du sample */
		sample = BASE_moto_word (sam2_chunk_ptr->header.number);

		/* Verifie que le numero du sample n'est pas trop haut */
		if (sample <= INST_NBRINSTR_MAXI)
		{
			/* Placement sur les donnees */
			data_pos =   chunk_list [chunk].offset
			           + BASE_moto_lword (sam2_chunk_ptr->header.mem_data_offset);
			if (fseek (file_ptr, data_pos, SEEK_SET) != 0)
			{
				LOG_printf ("MODGT2_load_module: Error: couldn't reach data of sample # %d.\n",
				            sample);
				FREE (sam2_chunk_ptr);
				return (-1);
			}

			/* Version de codage */
			codagev = BASE_moto_word (sam2_chunk_ptr->header.codagev);

			/* Type du sample */
			sample_type = BASE_moto_word (sam2_chunk_ptr->header.type);

			/* Byte order */
			reverse_byte_order_flag =   (BASE_moto_word (sam2_chunk_ptr->header.byte_order) != 0)
			                          ^ (OS_BYTE_ORDER != 0);

			/* Charge le nom du fichier */
			length = MIN (BASE_moto_word (sam2_chunk_ptr->header.filename_len) , 1024);
			length = MAX (length, 0);
			if (length > 0)
			{
				if (fread (text_0, length, 1, file_ptr) != 1)
				{
					LOG_printf ("MODGT2_load_module: Error: couldn't read sample filename.\n");
					FREE (sam2_chunk_ptr);
					return (-1);
				}
			}
			text_0 [length] = '\0';
			if (MODS_get_real_sample_path (text_0,    sample_type == 1
			                                       || codagev == SPLS_PACK_TYPE_DISK))
			{
				text_0 [0] = '\0';
				LOG_printf ("Error: couldn't get filename of sample # %d.\n", sample);
			}

			/* Direct-2-Disk */
			if (sample_type == 1)
			{
				/* Probleme avec le nom du fichier ? */
				if (strlen (text_0) == 0)
				{
					LOG_printf ("Warning: No sample file for sample # %d. Sample not loaded.\n",
					            sample);
					continue;
				}

				/* Teste si le byte order est le meme que celui de la machine */
				if (reverse_byte_order_flag)
				{
					/*** Voir comment on peut resoudre simplement le probleme ***/
					LOG_printf ("Warning: sample data file \"%s\" has an inverted byte order. Sample # %d not loaded.\n",
					            text_0, sample);
					continue;
				}

				/* Si le fichier n'existe pas, on passe directement au sample suivant. */
				if (! FILE_file_exist (text_0))
				{
					LOG_printf ("Warning: sample data file \"%s\" not found. Sample # %d not loaded.\n",
					            text_0, sample);
					continue;
				}
			}

			/* Resolution */
			SAMP_set_sample_resolution (sample, BASE_moto_word (sam2_chunk_ptr->header.nbits));
			
			/* Pistes */
			SAMP_set_sample_stereo (sample, BASE_moto_word (sam2_chunk_ptr->header.tracks));

			/* Fichier separe a charger en memoire */
			if (codagev == SPLS_PACK_TYPE_DISK)
			{
				/* Probleme avec le nom du fichier ? */
				if (strlen (text_0) == 0)
				{
					LOG_printf ("Warning: No sample file for sample # %d. Sample not loaded.\n",
					            sample);
					continue;
				}

				/* Chargement du sample */
				if (SPLS_load_sample_direct (sample, text_0))
				{
					LOG_printf ("Warning: Couldn't load sample # %d.\n",
					            sample);
					continue;
				}
			}

			/* MIDI note */
			SAMP_set_sample_midi_note (sample, BASE_moto_word (sam2_chunk_ptr->header.midi_note));

			/* Frequence */
			SAMP_set_sample_freq (sample, BASE_moto_lword (sam2_chunk_ptr->header.freq));

			/* Balance */
			SAMP_set_sample_balance (sample, (SWORD) BASE_moto_word (sam2_chunk_ptr->header.autobal));

			/* Volume */
			SAMP_set_sample_volume (sample, BASE_moto_word (sam2_chunk_ptr->header.volume));

			/* Finetune */
			SAMP_set_sample_finetune (sample, (SWORD) BASE_moto_word (sam2_chunk_ptr->header.finetune));

			/* Direct-2-Disk */
			if (sample_type == 1)
			{
				/* Offset des donnees dans le fichier */
				SAMP_set_sample_file_data_offset (sample, BASE_moto_lword (sam2_chunk_ptr->type1.buffer_len [0]));

				/* Utilise le fichier pour le D2D */
				if (SAMP_use_file_for_d2d (sample, text_0))
				{
					LOG_printf ("MODGT2_load_module: Error: couldn't associate file to sample # %d for Direct-To-Disk.\n",
					            sample);
					FREE (sam2_chunk_ptr);
					return (-1);
				}

				/* Taille des buffers */
				if (SAMP_set_sample_d2d_buffers (sample,
				                                 BASE_moto_lword (sam2_chunk_ptr->type1.buffer_len [0]),
															BASE_moto_lword (sam2_chunk_ptr->type1.buffer_len [1]),
															BASE_moto_lword (sam2_chunk_ptr->type1.buffer_len [2])))
				{
					FREE (sam2_chunk_ptr);
					return (-1);
				}
			}

			/* Memoire */
			else if (codagev != SPLS_PACK_TYPE_DISK)
			{
				/* Longueur */
				if (SAMP_set_sample_length (sample, BASE_moto_lword (sam2_chunk_ptr->header.length)))
				{
					LOG_printf ("MODGT2_load_module: Error: couldn't set length of sample # %d.\n",
					            sample);
					FREE (sam2_chunk_ptr);
					return (-1);
				}

				/* Lecture des donnees */
				if (codagev == SPLS_PACK_TYPE_PCM)
				{
					SPLS_init_primary_info (&sample_info, sample);
					sample_info.data_offset = data_pos;
					if (SPLRAW_load_sample (file_ptr, sample, &sample_info))
					{
						LOG_printf ("MODGT2_load_module: Error: couldn't load data of sample # %d.\n",
						            sample);
						FREE (sam2_chunk_ptr);
						return (-1);
					}
				}

				/* Version de codage inconnue */
				else
				{
					LOG_printf ("Warning: Sample %d, codage version %n is unsupported. Sample not loaded.\n",
								   sample, codagev);
					continue;
				}

			}

			/* Bouclage */
			SAMP_set_sample_loop (sample,
			                      BASE_moto_word (sam2_chunk_ptr->header.loop),
			                      BASE_moto_lword (sam2_chunk_ptr->header.repeat),
			                      BASE_moto_lword (sam2_chunk_ptr->header.replen));
			SAMP_make_buffers (sample);

			/* Nom du sample */
			memcpy (text_0, sam2_chunk_ptr->header.name, MODGT2_SPL_NAME_LEN);
			text_0 [MODGT2_SPL_NAME_LEN] = '\0';
			SAMP_set_sample_name (sample, text_0);

			// Additionnal data
			if (file_version >= 9)
			{
				const size_t		cur_pos = ftell (file_ptr);
				const size_t		rel_pos = cur_pos - chunk_pos;
				const int			pad = int (((rel_pos + 3) & -4) - rel_pos);
				if (fseek (file_ptr, pad, SEEK_CUR) != 0)
				{
					LOG_printf ("MODGT2_load_module: Error: couldn't seek to additionnal sample data.\n");
					FREE (sam2_chunk_ptr);
					return (-1);
				}

				MODGT2_SAMPLE2_ADDDATA	add_data;
				long				max_read_len = sizeof (add_data);
				if (fread (&add_data, sizeof (add_data), 1, file_ptr) != 1)
				{
					LOG_printf ("MODGT2_load_module: Error: couldn't read additionnal sample data.\n");
					FREE (sam2_chunk_ptr);
					return (-1);
				}

				const int		add_data_version = BASE_moto_word (add_data.version);
				const int		group = BASE_moto_word (add_data.group);
				if (group < 0 || group > SAMP_NBRGROUPS_MAXI)
				{
					LOG_printf ("MODGT2_load_module: Error: sample group value out of range.\n");
					FREE (sam2_chunk_ptr);
					return (-1);
				}

				SAMP_set_sample_group (sample, group);
			}

			if (sample_type == 1)
			{
				if (SAMP_get_read_d2d_header_flag (sample))
				{
					if (SAMP_read_d2d_header (sample))
					{
						LOG_printf ("MODGT2_load_module: Error: couldn't read Direct-To-Disk sample header.\n");
						FREE (sam2_chunk_ptr);
						return (-1);
					}
				}
			}

			sample_count ++;
		}

		/* Numero du sample trop grand */
		else
		{
			not_loaded_flag = true;
		}
	}

	FREE (sam2_chunk_ptr);

/*______________________________________________
 *
 * Samples SAMP (modules GT2 version < 6)
 *______________________________________________
 */

	samp_chunk_ptr = (MODGT2_SAMPLE *) MALLOC (sizeof (*samp_chunk_ptr));
	if (samp_chunk_ptr == NULL)
	{
		LOG_printf ("MODGT2_load_module: Error: couldn't allocate memory for sample (old) chunk.\n");
		return (-1);
	}

	for (chunk = 0 ; ; chunk ++)
	{
		chunk = MODGT2_get_next_chunk (file_ptr, chunk_list,
			                          nbr_chunks, chunk,
											  "SAMP",
			                          0xFFFFFFFFL);
		if (chunk < 0)
		{
			LOG_printf ("MODGT2_load_module: Error: couldn't get next (old) sample chunk.\n");
			FREE (samp_chunk_ptr);
			return (-1);
		}

		if (chunk >= nbr_chunks)
		{
			break;
		}

		/* Charge l'en-tete du sample */
		if (fread (samp_chunk_ptr, sizeof (*samp_chunk_ptr), 1, file_ptr) != 1)
		{
			LOG_printf ("MODGT2_load_module: Error: couldn't read sample chunk header (old).\n");
			FREE (samp_chunk_ptr);
			return (-1);
		}

		/* Numero du sample */
		sample = BASE_moto_word (samp_chunk_ptr->number);

		/* Verifie que le numero du sample n'est pas trop haut */
		if (sample <= INST_NBRINSTR_MAXI)
		{
			SPLS_init_primary_info (&sample_info, sample);
			sample_info.byte_order = 0;
			sample_info.signed_flag = true;
			sample_info.nbits = BASE_moto_word (samp_chunk_ptr->nbits);
			sample_info.tracks = (BASE_moto_word (samp_chunk_ptr->stereo) & 0x0001) + 1;
			length =   BASE_moto_lword (samp_chunk_ptr->length)
			         / (  sample_info.tracks
			            * (sample_info.nbits >> 3));
			sample_info.length = length;
			sample_info.data_offset =   chunk_list [chunk].offset
					                    + sizeof (*samp_chunk_ptr);

			/* Lecture des donnees */
			sample_info.pack_type = BASE_moto_word (samp_chunk_ptr->codagev);
			if (sample_info.pack_type == SPLS_PACK_TYPE_PCM)
			{
				if (SPLRAW_load_sample (file_ptr, sample, &sample_info))
				{
					LOG_printf ("MODGT2_load_module: Error: couldn't load data of sample # %d (old).\n",
					            sample);
					FREE (samp_chunk_ptr);
					return (-1);
				}
			}

			/* Version de codage inconnue */
			else
			{
				LOG_printf ("Warning: Sample %d, codage version %n is unsupported. Sample not loaded.\n",
				            sample, sample_info.pack_type);
				continue;
			}

			SAMP_set_sample_freq (sample, (UWORD) BASE_moto_word (samp_chunk_ptr->freq));
			SAMP_set_sample_balance (sample, (SWORD) BASE_moto_word (samp_chunk_ptr->autobal));
			SAMP_set_sample_volume (sample, BASE_moto_word (samp_chunk_ptr->volume));
			SAMP_set_sample_finetune (sample, (SWORD) BASE_moto_word (samp_chunk_ptr->finetune));

			/* Bouclage */
			sample_mul = SAMP_get_sample_bytes_per_sample (sample);
			reppos = BASE_moto_lword (samp_chunk_ptr->repeat) / sample_mul;
			replen = BASE_moto_lword (samp_chunk_ptr->replen) / sample_mul;
			loop_type = BASE_moto_word (samp_chunk_ptr->stereo) & 0x0002;
			if (loop_type != 0)
			{
				loop_type = WaveForm_LOOP_TYPE_PP;
			}
			else
			{
				loop_type = WaveForm_LOOP_TYPE_FWD;
			}
			if (reppos == 0 && replen * sample_mul <= 2)
			{
				loop_type = WaveForm_LOOP_TYPE_NONE;
				reppos = 0;
				replen = 0;
			}
			SAMP_set_sample_loop (sample, loop_type, reppos, replen);
			SAMP_make_buffers (sample);

			/* Nom du sample */
			memcpy (text_0, samp_chunk_ptr->name, MODGT2_SPL_NAME_LEN);
			text_0 [MODGT2_SPL_NAME_LEN] = '\0';
			SAMP_set_sample_name (sample, text_0);

			sample_count ++;
		}

		/* Numero du sample trop grand */
		else
		{
			not_loaded_flag = true;
		}
	}

	FREE (samp_chunk_ptr);

	LOG_printf ("%d samples.\n", sample_count);

	if (not_loaded_flag)
	{
		LOG_printf ("Warning: Some samples were not loaded (number too high).\n");
	}

/*______________________________________________
 *
 * Initialisations diverses
 *______________________________________________
 */

	/* Reglage des pannings pour les modules de version < 6 */
	if (file_version < 6)
	{
		pan_length = MIN (pan_length, GTK_nbr_tracks [Pattern_TYPE_SPL]);
		for (count = 0; count < pan_length; count ++)
		{
			player.set_track_panning (Pattern_TYPE_SPL, count, false,
			                        ((LWORD) pan_ptr [count]) << 4);
		}

		FREE (pan_ptr);
	}

	/* Changement de l'unite pour la commande Sample Offset (9xxx) */
	if (file_version < 6)
	{
		MODGT2_fix_old_gt2_sample_offset_all ();
	}

	/* Reinitialisation des presets */
	TRK_init_presets ();

/*______________________________________________
 *
 * Configuration
 *______________________________________________
 */

	chunk = MODGT2_get_next_chunk (file_ptr, chunk_list, nbr_chunks, 0, "TCN2", 0xFFFFFFFFL);
	if (   chunk >= 0
	    && chunk < nbr_chunks)
	{
		LOG_printf ("Reading configuration chunk... ");

		length = MIN (sizeof (tcn2_chunk), chunk_list [chunk].size);
		if (fread (&tcn2_chunk, length, 1, file_ptr) != 1)
		{
			LOG_printf ("MODGT2_load_module: Error: couldn't read configuration chunk.\n");
			return (-1);	/* Erreur de lecture */
		}

		player.set_tempo (  BASE_moto_word (tcn2_chunk.tempo)
		                + (double)((UWORD)BASE_moto_word (tcn2_chunk.fine_tempo)) / 65536);
		player.set_speed (BASE_moto_word (tcn2_chunk.speed));
		player.set_bar_time (BASE_moto_word (tcn2_chunk.time_high),
		                   BASE_moto_word (tcn2_chunk.time_low));

		int	version = BASE_moto_word (tcn2_chunk.version);

		/* Version 1 */
		if (version >= 1)
		{
			player.set_linear_period_flag (BASE_moto_word (tcn2_chunk.period_mode) == 0);
		}

		LOG_printf ("Done.\n");
	}



	LOG_printf ("Module loaded.\n\n");

	return (0);
}



/*==========================================================================*/
/*      Nom: MODGT2_detect_format                                           */
/*      Description: Teste si un module est au format GT2.                  */
/*      Parametres en entree:                                               */
/*        - header_ptr: les premiers octets du module.                      */
/*        - header_length: taille du header sus-cite.                       */
/*      Parametres en entree/sortie:                                        */
/*        - file_ptr: pointeur sur le fichier du module. Il n'y a pas       */
/*                    besoin de sauver la position.                         */
/*      Retour: true si le module est un GT2.                               */
/*==========================================================================*/

bool	MODGT2_detect_format (FILE *file_ptr, const void *header_ptr, long header_length, BYTE temp_info [MODS_TEMP_INFO_LEN])
{
	if (   ((const BYTE *)header_ptr) [0] == 'G'
	    && ((const BYTE *)header_ptr) [1] == 'T'
	    && ((const BYTE *)header_ptr) [2] == '2')
	{
		return (true);
	}

	return (false);
}



/*==========================================================================*/
/*      Nom: MODGT2_get_chunk_offsets                                       */
/*      Description: Recherche dans un fichier GT2 la position de tous les  */
/*                   chunks qui le composent. La recherche commence a la    */
/*                   position courante du fichier. Cette position est       */
/*                   normalement inchangee a la fin de la recherche.        */
/*      Parametres en sortie:                                               */
/*        - chunk: tableau des offsets des chunks.                          */
/*        - nbr_chunk_ptr: pointeur sur le nombre de chunks trouves (taille */
/*                         du tableau).                                     */
/*      Parametres en entree/sortie:                                        */
/*        - file_ptr: pointeur sur le fichier a scanner.                    */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	MODGT2_get_chunk_offsets (MODS_CHUNK_OFFSET chunk [MODGT2_NBR_CHUNKS_MAXI], int *nbr_chunk_ptr, FILE *file_ptr)
{
	signed long	old_file_pos;
	long		file_pos;
	long		file_len;
	struct
	{
		char		id [4];
		LWORD		length;
	}			header;

	old_file_pos = ftell (file_ptr);
	if (old_file_pos == -1L)
	{
		LOG_printf ("MODGT2_get_chunk_offsets: Error: couldn't get current file position.\n");
		return (-1);
	}

	file_pos = old_file_pos;
	file_len = FILE_get_file_length (file_ptr);
	*nbr_chunk_ptr = 0;

	while (   file_pos + 8 <= file_len
	       && *nbr_chunk_ptr <= MODGT2_NBR_CHUNKS_MAXI)
	{
		if (fseek (file_ptr, file_pos, SEEK_SET) != 0)
		{
			LOG_printf ("MODGT2_get_chunk_offsets: Error: couldn't set current file position.\n");
			return (-1);
		}
		if (fread (&header, 8, 1, file_ptr) != 1)
		{
			LOG_printf ("MODGT2_get_chunk_offsets: Error: couldn't read chunk header.\n");
			return (-1);
		}
		
		#if (OS_BYTE_ORDER != 0)
		{
			BASE_invert_lword (&header.length);
		}
		#endif

		/* Le chunk est coupe par la fin du fichier: on arrete */
		if (file_pos + header.length > file_len)
		{
			break;
		}

		/* Le chunk a une longueur incorrecte: on arrete */
		if (header.length < 8)
		{
			break;
		}

		/* On memorise ce chunk */
		chunk [*nbr_chunk_ptr].id [0] = header.id [0];
		chunk [*nbr_chunk_ptr].id [1] = header.id [1];
		chunk [*nbr_chunk_ptr].id [2] = header.id [2];
		chunk [*nbr_chunk_ptr].id [3] = header.id [3];
		chunk [*nbr_chunk_ptr].offset = file_pos;
		chunk [*nbr_chunk_ptr].size = header.length;
		(*nbr_chunk_ptr) ++;

		/* Sortie de la liste des chunks */
		#if 0
		{
			LOG_printf ("Chunk %c%c%c%c - Pos %08X - Len %08X\n",
						   header.id [0], header.id [1], header.id [2], header.id [3],
						   (int)file_pos, (int)header.length);
		}
		#endif

		file_pos += header.length;
	}

	if (fseek (file_ptr, old_file_pos, SEEK_SET) != 0)
	{
		LOG_printf ("MODGT2_get_chunk_offsets: Error: couldn't restore saved file position.\n");
		return (-1);
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: MODGT2_get_next_chunk                                          */
/*      Description: Repere le prochain chunk qui correspond a l'ID demande */
/*                   dans la liste des chunks. Place le fichier sur la      */
/*                   position trouvee.                                      */
/*      Parametres en entree:                                               */
/*        - chunk_list: liste des id des chunks avec leurs offsets.         */
/*        - nbr_chunks: nombre de chunks dans la liste.                     */
/*        - current_chunk: numero du chunk a partir duquel on veut faire la */
/*                         recherche (inclu).                               */
/*        - id: chaine de 4 caractere specifiant l'id du chunk demande.     */
/*        - mask: masque a utiliser lors de la recherche de l'id. Un bit a  */
/*                1 indique qu'il faut comparer le bit, 0 signifie qu'il    */
/*                faut l'ignorer.                                           */
/*      Parametres en entree/sortie:                                        */
/*        - file_ptr: pointeur sur le fichier du module.                    */
/*      Retour: - Le numero du chunk s'il a ete trouve,                     */
/*              - Un nombre >= nbr_chunks s'il n'y avait plus de chunk de   */
/*                ce type,                                                  */
/*              - -1 si erreur pendant l'operation.                         */
/*==========================================================================*/

signed int	MODGT2_get_next_chunk (FILE *file_ptr, MODS_CHUNK_OFFSET chunk_list [MODGT2_NBR_CHUNKS_MAXI], int nbr_chunks, int current_chunk, const char id [4], LWORD mask)
{
	LWORD		id_1;
	LWORD		id_2;

	mask = BASE_moto_lword (mask);
	id_1 = *((LWORD *)id) & mask;

	for ( ; current_chunk < nbr_chunks; current_chunk ++)
	{
		id_2 = *((LWORD *) chunk_list [current_chunk].id) & mask;
		if (BASE_compare_id4 (&id_1, &id_2))
		{
			if (fseek (file_ptr, chunk_list [current_chunk].offset, SEEK_SET) != 0)
			{
				LOG_printf ("MODGT2_get_next_chunk: Error: couldn't reach chunk.\n");
				return (-1);
			}
			break;
		}
	}

	return (current_chunk);
}



/*==========================================================================*/
/*      Nom: MODGT2_load_gt2_instr                                          */
/*      Description: Charge un instrument situe dans un fichier.            */
/*      Parametres en entree/sortie:                                        */
/*        - file_ptr: pointeur sur le fichier. le pointeur doit se trouver  */
/*                    au debut du chunk a charger.                          */
/*        - instr: numero de l'instr a charger (-1 si on veut une detection */
/*                 automatique du numero de l'instrument). Au retour, la    */
/*                 contient le numero effectif de l'instrument dans lequel  */
/*                 ont ete charge les donnees.                              */
/*        - not_loaded_flag: est mis a true si l'instrument n'a pas pu etre */
/*                           charge, le numero etant trop haut.             */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	MODGT2_load_gt2_instr (FILE *file_ptr, signed int &instr, bool &not_loaded_flag)
{
	int		env_type;
	int		note;
	MODGT2_INSTR	instr_chunk;
	char		text_0 [MODGT2_INS_NAME_LEN+1];

	/* Charge l'en-tete de l'instrument */
	if (fread (&instr_chunk, sizeof (instr_chunk), 1, file_ptr) != 1)
	{
		LOG_printf ("MODGT2_load_module: Error: couldn't read instrument chunk.\n");
		return (-1);
	}

	/* Numero de l'instrument */
	if (instr < 0)
	{
		instr = BASE_moto_word (instr_chunk.number);

		/* Verifie que le numero de l'instrument n'est pas trop haut */
		if (instr > INST_NBRINSTR_MAXI)
		{
			not_loaded_flag = true;
		}
	}

	/* Nom de l'instrument */
	memcpy (text_0, instr_chunk.name, MODGT2_INS_NAME_LEN);
	text_0 [MODGT2_INS_NAME_LEN] = '\0';
	INST_set_instr_name (instr, text_0);

	/* Donnees diverses */
	INST_set_instr_volume (instr, BASE_moto_word (instr_chunk.volume));
	INST_set_instr_autobal (instr, (SWORD) BASE_moto_word (instr_chunk.autobal));
	INST_set_instr_volume (instr, BASE_moto_word (instr_chunk.volume));
	for (env_type = 0; env_type < Envelope_NBR_TYPES; env_type ++)
	{
		INST_set_instr_env (instr, env_type, BASE_moto_word (instr_chunk.env_flags [env_type]));
	}

	/* Samples et transpositions pour chaque note */
	for (note = 0; note < GTK_NBRNOTES_MAXI; note ++)
	{
		INST_set_instr_sample (instr, note, instr_chunk.note [note].splnum);
		INST_set_instr_transp (instr, note, instr_chunk.note [note].transp);
	}

	/* Version 1 et + */
	if (BASE_moto_word (instr_chunk.version) >= 1)
	{
		int		filter_flags;
		int		cnt;

		filter_flags = BASE_moto_word (instr_chunk.filter_flags);
		INST_set_filter_flag (instr, (filter_flags & 0x01) != 0);
		INST_set_filter_freq_vol_flag (instr, (filter_flags & 0x02) != 0);
		INST_set_filter_q_vol_flag (instr, (filter_flags & 0x04) != 0);
		for (cnt = 0; cnt < 2; cnt ++)
		{
			INST_set_filter_freq (instr, cnt, BASE_asciidbl_to_double (instr_chunk.filter_freq [cnt]));
			INST_set_filter_q (instr, cnt, BASE_asciidbl_to_double (instr_chunk.filter_reso [cnt]));
		}

		/* Version 2 et + */
		if (BASE_moto_word (instr_chunk.version) >= 2)
		{
			int		nbr_types;
			int		type_cnt;
			int		cnt_2;
			Envelope_LOOP	loop;
			Envelope_POINT	point;

			/* Enveloppes */
			nbr_types = MIN (instr_chunk.nbr_env, Envelope_NBR_TYPES);
			for (type_cnt = 0; type_cnt < nbr_types; type_cnt ++)
			{
				/* Parametres generaux */
				ENV_set_nbr_points (type_cnt, instr, BASE_moto_word (instr_chunk.env [type_cnt].nbr_points));
				ENV_set_lfo_depth (type_cnt, instr, BASE_moto_word (instr_chunk.env [type_cnt].lfo_depth));
				ENV_set_lfo_speed (type_cnt, instr, BASE_moto_word (instr_chunk.env [type_cnt].lfo_speed));
				ENV_set_lfo_sweep (type_cnt, instr, BASE_moto_word (instr_chunk.env [type_cnt].lfo_sweep));
				ENV_set_lfo_waveform (type_cnt, instr, BASE_moto_word (instr_chunk.env [type_cnt].lfo_waveform));
				ENV_set_fadeout (type_cnt, instr, BASE_moto_word (instr_chunk.env [type_cnt].fadeout));
				ENV_set_flags (type_cnt, instr, BASE_moto_word (instr_chunk.env [type_cnt].flags));

				/* Boucles */
				for (cnt_2 = 0; cnt_2 < MIN (Envelope_NBR_LOOPS, MODGT2_INS_ENV_MAX_LP); cnt_2 ++)
				{
					loop.start = BASE_moto_word (instr_chunk.env [type_cnt].loop [cnt_2].start);
					loop.end = BASE_moto_word (instr_chunk.env [type_cnt].loop [cnt_2].end);
					loop.nbr_loops = BASE_moto_word (instr_chunk.env [type_cnt].loop [cnt_2].nbr_loops);
					ENV_set_loop (type_cnt, instr, cnt_2, loop);
				}

				/* Points */
				for (cnt_2 = 0; cnt_2 < MIN (Envelope_MAX_NBR_POINTS, MODGT2_INS_ENV_MAX_PT); cnt_2 ++)
				{
					point.time = BASE_moto_word (instr_chunk.env [type_cnt].point [cnt_2].time);
					point.val = (SWORD)BASE_moto_word (instr_chunk.env [type_cnt].point [cnt_2].val);
					ENV_set_point (type_cnt, instr, cnt_2, point);
				}
			}
		}
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: MODGT2_load_gt2_mix_preset                                     */
/*      Description: Charge un preset de mixage situe dans un fichier.      */
/*      Parametres en entree/sortie:                                        */
/*        - file_ptr: pointeur sur le fichier. le pointeur doit se trouver  */
/*                    au debut du chunk a charger.                          */
/*        - preset: numero du preset a charger (-1 si on veut une detection */
/*                  automatique du numero de preset). Au retour, la         */
/*                  contient le numero effectif du preset dans lequel ont   */
/*                  ete charge les donnees.                                 */
/*        - not_loaded_flag: est mis a true si le preset n'a pas pu etre    */
/*                           charge, le numero etant trop haut.             */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	MODGT2_load_gt2_mix_preset (FILE *file_ptr, signed int &preset, bool &not_loaded_flag)
{
	int		nbr_sources;
	long		supposed_len;
	long		real_len;
	MODGT2_MIXP_ND	mixp_chunk;
	MODGT2_TRACK_MIX	mixp_chunk_conf;
	PLAY_SOURCE_TRACK_CONF_BLOCK	mixp_int_conf;
	char		name_0 [MODGT2_MIX_NAME_LEN+1];

	/* Charge l'en-tete du preset */
	if (fread (&mixp_chunk, sizeof (mixp_chunk), 1, file_ptr) != 1)
	{
		LOG_printf ("MODGT2_load_gt2_mix_preset: Error: couldn't read mix preset chunk.\n");
		return (-1);
	}

	if (preset < 0)
	{
		/* Numero du preset */
		preset = BASE_moto_word (mixp_chunk.number);
		
		/* Dans les presets temporaires */
		if (preset & 0x8000)
		{
			preset = GTK_MIXP_TEMP;
		}

		/* Verifie que le numero du preset n'est pas trop haut */
		else if (preset > MIXP_NBRMIXP_MAXI)
		{
			not_loaded_flag = true;
			return (0);
		}
	}

	/* Nom du preset */
	BASE_copy_string (mixp_chunk.name, name_0,
	                  MODGT2_MIX_NAME_LEN, MODGT2_MIX_NAME_LEN);
	MIXP_set_preset_name (preset, name_0);

	MIXP_set_track_type (preset, BASE_moto_word (mixp_chunk.track_type));
	MIXP_set_track_number (preset, BASE_moto_word (mixp_chunk.track_nbr));
	MIXP_set_volume (preset, false, BASE_moto_word (mixp_chunk.vol_wet));
	MIXP_set_volume (preset, true, BASE_moto_word (mixp_chunk.vol_dry));
	MIXP_set_panning (preset, false, BASE_moto_word (mixp_chunk.pan_wet));
	MIXP_set_panning (preset, true, BASE_moto_word (mixp_chunk.pan_dry));
	MIXP_set_stereo (preset, BASE_moto_word (mixp_chunk.stereo));

	nbr_sources = BASE_moto_word (mixp_chunk.nbr_in);
	MIXP_set_nbr_source_tracks (preset, nbr_sources);

	/*** Tenir compte de la version ***/

	/* Bidouille pour faire en sorte que les anciennes version soient chargees
	   correctement. Ne pas garder ca trop longtemps. */
	real_len = BASE_moto_lword (mixp_chunk.chunksz);
	supposed_len = sizeof (mixp_chunk) + nbr_sources * sizeof (mixp_chunk_conf);
	if (real_len != supposed_len)
	{
		if (fseek (file_ptr, real_len - supposed_len, SEEK_CUR) != 0)
		{
			LOG_printf ("MODGT2_load_gt2_mix_preset: Error: couldn't set file position for track config reading.\n");
			return (-1);
		}
	}

	for (int src_cnt = 0; src_cnt < nbr_sources; src_cnt ++)
	{
		if (fread (&mixp_chunk_conf, sizeof (mixp_chunk_conf), 1, file_ptr) != 1)
		{
			LOG_printf ("MODGT2_load_gt2_mix_preset: Error: couldn't read source track config # %d in the mix preset chunk.\n",
			            src_cnt);
			return (-1);
		}

		mixp_int_conf.track_type = mixp_chunk_conf.track_type;
		mixp_int_conf.wet_flag = ! mixp_chunk_conf.dry_flag;
		mixp_int_conf.track_nbr = BASE_moto_word (mixp_chunk_conf.track);
		mixp_int_conf.inpvol = BASE_moto_word (mixp_chunk_conf.volume);
		mixp_int_conf.inppan = BASE_moto_word (mixp_chunk_conf.balance) << 4;

		MIXP_set_source_track (preset, src_cnt, &mixp_int_conf);
	}

	return (0);
}



/*
==============================================================================
Name: MODGT2_fix_old_gt2_sample_offset_all
Description:
	Fixes Sample Offset command problems (9xxx effect) after loading. There are
	two reasons to do so:
	- GT2 modules created with Graoumf Tracker on Falcon 030 measured the
		position of Sample Offset effect in bytes, whereas Graoumf Tracker 2
		measures position in samples (it makes difference with 16-bit samples).
	- Triggering a sample past its loop end made the sample play from its loop
		start position in GT/Falcon 030. New Graoumf Tracker simply stops the
		sample. The 9xxx value is fixed to the closest point to the start of the
		loop.
		
==============================================================================
*/

void	MODGT2_fix_old_gt2_sample_offset_all ()
{
	long				nbr_fix = 0;

	const int		nbr_tracks = GTK_nbr_tracks [Pattern_TYPE_SPL];
	for (int track = 0; track < nbr_tracks; ++ track)
	{
		int				cur_instr = 1;
		int				cur_note = Sample_REF_C2;

		for (int pat = 0; pat < PAT_NBRPATTERNS_MAXI; ++ pat)
		{
			const int		nbr_lines = PAT_get_pattern_height (pat);

			for (int line = 0; line < nbr_lines; ++ line)
			{
				MODS_GT2_SPL_NOTE &	step = *reinterpret_cast <MODS_GT2_SPL_NOTE *> (
					PAT_get_note_adr_pat (Pattern_TYPE_SPL, pat, line, track)
				);

				if (step.note != 0)
				{
					cur_note = step.note;
				}
				if (step.instr != 0)
				{
					cur_instr = step.instr;
				}

				const bool		modified_flag = MODGT2_fix_old_gt2_sample_offset_single_cmd (
					step, cur_note, cur_instr
				);
				if (modified_flag)
				{
					++ nbr_fix;
				}
			}
		}
	}

	if (nbr_fix > 0)
	{
		LOG_printf ("Fixed %ld 9xxx effect parameters.\n", nbr_fix);
	}
}



bool	MODGT2_fix_old_gt2_sample_offset_single_cmd (MODS_GT2_SPL_NOTE &step, int cur_note, int cur_instr)
{
	bool				modified_flag = false;
	long				total = (static_cast <long> (step.fxnum) << 8) + step.fxparam;
	int				cmd = (total >> 12) & 0x0F;
	int				param = total & 0x0FFF;
	if (cmd == 0x9)
	{
		if (param == 0)
		{
			total = 0xBB01;
			modified_flag = true;
		}

		else
		{
			const int		sample = INST_get_instr_sample (cur_instr, cur_note);
			const int		nbr_bits = SAMP_get_sample_resolution (sample);
			const int		nbr_bytes = (nbr_bits + 7) / 8;

			// Position / 16 in samples, and 9xxx position * 16
			long				offset_x16 = (static_cast <long> (param) * 16) / nbr_bytes;

			// Fix for 16-bit samples
			if (nbr_bytes > 1)
			{
				modified_flag = true;
			}

			// Fix for post-loop triggering
			if (SAMP_get_sample_loop (sample) != WaveForm_LOOP_TYPE_NONE)
			{
				const long		max_pos =   SAMP_get_sample_repeat (sample)
												 + SAMP_get_sample_replen (sample);
				long				trig_pos = offset_x16 << 4;
				if (trig_pos >= max_pos)
				{
					const long		loop_start = SAMP_get_sample_repeat (sample);
					trig_pos = loop_start;

					// Rounds to nearest
					offset_x16 = (trig_pos + 8) / 16;

					// Check if the rounded value stays into the loop.
					// If max_pos == 0, it doesn't generate a negative number.
					const long		last_offset = (max_pos - 1) >> 4;
					if (offset_x16 >= last_offset)
					{
						offset_x16 = last_offset;
					}

					modified_flag = true;
				}
			}

			// Transforms the new offset into a command.
			if (modified_flag)
			{
				if (offset_x16 <= 0xFF && (offset_x16 & 15) != 0)
				{
					cmd = 0xBA;
					param = offset_x16;
					total = (static_cast <long> (cmd) << 8) + param;
				}
				else
				{
					const int		offset_x256 =
						static_cast <int> (offset_x16 / 16);
					param = offset_x256;
					total = (static_cast <long> (cmd) << 12) + param;
				}
			}
		}

		step.fxnum = static_cast <UBYTE> ((total >> 8) & 0xFF);
		step.fxparam = static_cast <UBYTE> (total & 0xFF);
	}

	return (modified_flag);
}



/*==========================================================================*/
/*      Nom: MODGT2_load_fx_preset                                          */
/*      Description: Charge un preset d'effet situe dans un fichier.        */
/*      Parametres en entree/sortie:                                        */
/*        - file_ptr: pointeur sur le fichier. le pointeur doit se trouver  */
/*                    au debut du chunk a charger.                          */
/*        - preset: numero du preset a charger (-1 si on veut une detection */
/*                  automatique du numero de preset). Au retour, la var     */
/*                  contient le numero effectif du preset dans lequel ont   */
/*                  ete charge les donnees.                                 */
/*        - not_loaded_flag: est mis a true si le preset n'a pas pu etre    */
/*                           charge, le numero etant trop haut.             */
/*      Parametres en sortie:                                               */
/*        - track: dans le cas ou le preset serait une configuration de     */
/*                 piste, cette variable contient le numero de cette piste. */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	MODGT2_load_fx_preset (FILE *file_ptr, signed int &preset, int &track, bool &bypass_flag, bool &not_loaded_flag)
{
	int		type;
	long		fx_len;
	MODGT2_FXP_ND	header;
	FxPreset_EFFECT_CONF	conf;
	char		name_0 [MODGT2_FX_NAME_LEN+1];
	MODGT2_FX_UNION	fx;

/*______________________________________________
 *
 * Header du preset
 *______________________________________________
 */

	/* Chargement du header */
	if (fread (&header, sizeof (header), 1, file_ptr) != 1)
	{
		LOG_printf ("MODGT2_load_fx_preset: Error: couldn't read effect preset header.\n");
		return (-1);
	}

	/* Autodetection du numero de preset */
	bypass_flag = false;
	if (preset < 0)
	{
		preset = BASE_moto_word (header.number);
		bypass_flag = ((preset & 0x4000) != 0);

		if (preset & 0x8000)
		{
			track = preset & 0x3FFF;
			preset = GTK_FXP_TEMP;
		}

		/* Verifie que le numero du preset n'est pas trop haut */
		else if (preset > FXP_NBRFXP_MAXI)
		{
			not_loaded_flag = true;
			return (0);
		}
	}

	/* Initialisation des parametres generaux */
	BASE_copy_string (header.name, name_0,
	                  MODGT2_FX_NAME_LEN, MODGT2_FX_NAME_LEN);
	type = BASE_moto_word (header.effect_type);
	fx_len = BASE_moto_lword (header.chunksz) - sizeof (header);
	fx_len = MIN (fx_len, sizeof (fx));

	FXP_set_name (preset, name_0);
	FXP_set_effect_type (preset, type);

/*______________________________________________
 *
 * Parametres particuliers a chaque type d'effet
 *______________________________________________
 */

	/* Chargement */
	if (fx_len > 0 && type != FxPreset_TYPE_NONE)
	{
		if (fread (&fx, fx_len, 1, file_ptr) != 1)
		{
			LOG_printf ("MODGT2_load_fx_preset: Error: couldn't read effect preset parameters.\n");
			return (-1);
		}
	}

	/* Decodage des parametres */
	switch (type)
	{

	case	FxPreset_TYPE_DELAY:
		conf.delay.delay = BASE_asciidbl_to_double (fx.delay.time);
		conf.delay.feedback = BASE_asciidbl_to_double (fx.delay.feedback);
		break;

	case	FxPreset_TYPE_RESFILT:
		conf.resfilt.q = BASE_asciidbl_to_double (fx.resfilt.q);
		conf.resfilt.freq = BASE_asciidbl_to_double (fx.resfilt.freq);
		conf.resfilt.cell_ratio = BASE_asciidbl_to_double (fx.resfilt.band_gap);
		conf.resfilt.lfo_freq = BASE_asciidbl_to_double (fx.resfilt.lfo_freq);
		conf.resfilt.lfo_depth = BASE_asciidbl_to_double (fx.resfilt.lfo_depth);
		conf.resfilt.lfo_phase = BASE_asciidbl_to_double (fx.resfilt.lfo_phase);
		conf.resfilt.nbr_biquads = BASE_moto_word (fx.resfilt.cell_order) >> 1;
		conf.resfilt.nbr_cells = BASE_moto_word (fx.resfilt.nbr_cells);
		conf.resfilt.lfo_waveform = fx.resfilt.lfo_waveform;
		conf.resfilt.type = fx.resfilt.filter_type;
		conf.resfilt.lfo_flag = (fx.resfilt.lfo_flag != 0);
		conf.resfilt.cell_flag = (fx.resfilt.multiband_flag != 0);
		// conf.resfilt.nbr_fir_coef = BASE_moto_word (fx.resfilt.nbr_fir_coef);
		break;

	/* Distorsion */
	case	FxPreset_TYPE_DISTO:
		conf.disto.type = BASE_moto_word (fx.disto.type);
		conf.disto.gain = BASE_asciidbl_to_double (fx.disto.gain);
		conf.disto.threshold = BASE_asciidbl_to_double (fx.disto.threshold);
		conf.disto.sec_gain = BASE_asciidbl_to_double (fx.disto.sec_gain);
		break;
	}

	FXP_set_parameters (preset, conf);

	return (0);
}



/****************************************************************************/
/*                                                                          */
/*                                                                          */
/*                                                                          */
/****************************************************************************/



/*==========================================================================*/
/*      Nom:                                                                */
/*      Description:                                                        */
/*      Parametres en entree:                                               */
/*      Parametres en sortie:                                               */
/*      Parametres en entree/sortie:                                        */
/*      Retour:                                                             */
/*==========================================================================*/

