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

        GRAOUMF TRACKER 2
        Author: Laurent de Soras, 1996-2016

--- Legal stuff ---

This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.

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



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

#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>

#include	"archi.h"
#include	"base.h"
#include	"env.h"
#include	"Envelope.h"
#include	"file.h"
#include	"gt_limit.h"
#include	"inst.h"
#include	"log.h"
#include	"memory.h"
#include	"mod_mod.h"
#include	"mod_xm.h"
#include	"mods_ct.h"
#include	"patt.h"
#include	"Pattern.h"
#include	"player.h"
#include	"samp.h"
#include	"song.h"
#include "splstruc.h"
#include "spl_raw.h"
#include	"Waveform.h"



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

#define	MODXM_ID_LEN					17
#define	MODXM_MODULE_NAME_MAX_LEN	20
#define	MODXM_TRACKER_NAME_MAX_LEN	20
#define	MODXM_INSTR_NAME_MAX_LEN	22
#define	MODXM_SAMPLE_NAME_MAX_LEN	22
#define	MODXM_SONG_MAX_LEN			256



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

typedef struct
{
	char		id [MODXM_ID_LEN];	// "Extended Module: "
	char		module_name [MODXM_MODULE_NAME_MAX_LEN];
	char		dummy;					// 0x1A
	char		tracker_name [MODXM_TRACKER_NAME_MAX_LEN];
	WORD		version;
} MODXM_HEADER;

typedef struct
{
	LWORD		size;
	WORD		song_len;
	WORD		repeat_pos;
	WORD		nbr_channels;
	WORD		nbr_patterns;
	WORD		nbr_instruments;
	WORD		flags;
	WORD		speed;
	WORD		tempo;
	UBYTE		song [MODXM_SONG_MAX_LEN];
} MODXM_SONG;

typedef struct
{
	LWORD		size;
	BYTE		pack_type;
	WORD		nbr_lines;
	WORD		packed_size;
} MODXM_PAT_HEADER;

typedef struct
{
	LWORD		size;						// Jusqu'aux sample headers
	char		name [MODXM_INSTR_NAME_MAX_LEN];
	BYTE		type;
	WORD		nbr_samples;
} MODXM_INSTR_HEADER;

typedef struct
{
	WORD		time;
	WORD		value;
} MODXM_ENV_POINT;

typedef struct
{
	UBYTE		sustain;
	UBYTE		loop_start;
	UBYTE		loop_end;
} MODXM_ENV_CTRL;

typedef struct
{
	LWORD		sample_header_size;
	UBYTE		samples [96];
	MODXM_ENV_POINT	env [2] [12];	// 0: Vol, 0..64, 1: Pan, 0..32..63
	UBYTE		nbr_points [2];
	MODXM_ENV_CTRL	control [2];
	UBYTE		type [2];
	BYTE		vib_type;
	BYTE		vib_sweep;
	BYTE		vib_depth;
	BYTE		vib_rate;
	WORD		vol_fadeout;
	WORD		reserved;
} MODXM_INSTR_BODY;

typedef struct
{
	LWORD		sample_len;
	LWORD		loop_start;
	LWORD		loop_len;
	UBYTE		volume;
	SBYTE		finetune;
	BYTE		type;
	UBYTE		panning;
	SBYTE		transpose;
	BYTE		reserved;
	char		name [MODXM_SAMPLE_NAME_MAX_LEN];
} MODXM_SAMPLE_HEADER;



/*\\\ PROTOTYPES DES FONCTIONS PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

void	MODXM_modcmd_2_gtcmd (MODS_GT2_SPL_NOTE &gt2_note, int cmd, int param);



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



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



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



/*==========================================================================*/
/*      Nom: MODXM_save_module                                              */
/*      Description: Sauve un module en XM.                                 */
/*      Parametres en entree:                                               */
/*        - file_ptr: pointeur du fichier a sauver.                         */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	MODXM_save_module (FILE *file_ptr)
{

	/*** A faire ***/

	return (0);
}



/*==========================================================================*/
/*      Nom: MODXM_load_module                                              */
/*      Description: Charge un module en XM.                                */
/*      Parametres en entree:                                               */
/*        - file_ptr: pointeur du fichier a charger.                        */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	MODXM_load_module (FILE *file_ptr, BYTE temp_info [MODS_TEMP_INFO_LEN])
{
	int		pos_cnt;
	int		pat_cnt;
	int		trk_cnt;
	int		line_cnt;
	int		instr_cnt;
	int		nbr_channels;
	int		nbr_lines;
	int		nbr_patterns;
	int		nbr_instruments;
	long		pat_packed_size;
	long		ft_note_offset;
	UBYTE		*pattern_data_ptr;
	char		text_0 [31+1];
	MODXM_HEADER	header;
	MODXM_SONG	song;
	MODXM_PAT_HEADER	pat_header;

	Player &			player = Player::use_instance ();

/*______________________________________________
 *
 * Header du module
 *______________________________________________
 */

	if (fread (&header, sizeof (header), 1, file_ptr) != 1)
	{
		LOG_printf ("MODMX_load_module: Error: couldn't read module header.\n");
		return (-1);
	}

	memcpy (text_0, header.module_name, MODXM_MODULE_NAME_MAX_LEN);
	text_0 [MODXM_MODULE_NAME_MAX_LEN] = '\0';
	SONG_set_song_name (text_0);

/*______________________________________________
 *
 * Sequence
 *______________________________________________
 */

	if (fread (&song, sizeof (song), 1, file_ptr) != 1)
	{
		LOG_printf ("MODMX_load_module: Error: couldn't read song.\n");
		return (-1);
	}

	/* La song */
	SONG_set_song_length (MODXM_SONG_MAX_LEN);
	for (pos_cnt = 0; pos_cnt < MODXM_SONG_MAX_LEN; pos_cnt ++)
	{
		SONG_set_pattern_number (pos_cnt, song.song [pos_cnt]);
	}
	SONG_set_song_length (BASE_intel_word (song.song_len));
	SONG_set_song_repeat (BASE_intel_word (song.repeat_pos));

	/* Change le nombre de pistes */
	nbr_channels = BASE_intel_word (song.nbr_channels);
	if (PAT_set_nbr_tracks (Pattern_TYPE_SPL, nbr_channels))
	{
		LOG_printf ("MODXM_load_module: Error: couldn't set number of tracks to %d.\n",
		            nbr_channels);
		return (-1);
	}

	player.set_tempo (BASE_intel_word (song.tempo));
	player.set_speed (BASE_intel_word (song.speed));
	player.set_linear_period_flag ((BASE_intel_word (song.flags) & 0x01) != 0);

	nbr_patterns = BASE_intel_word (song.nbr_patterns);
	nbr_instruments = BASE_intel_word (song.nbr_instruments);

	int64_t        cur_file_pos = sizeof (header) + BASE_intel_lword (song.size);

/*______________________________________________
 *
 * Patterns
 *______________________________________________
 */

	for (pat_cnt = 0; pat_cnt < nbr_patterns; pat_cnt ++)
	{
		/* On se positionne */
		if (FILE_fseek (file_ptr, cur_file_pos, SEEK_SET) != 0)
		{
			LOG_printf ("MODMX_load_module: Error: couldn't set file position.\n");
			return (-1);
		}

		/* Lecture du header */
		if (fread (&pat_header, sizeof (pat_header), 1, file_ptr) != 1)
		{
			LOG_printf ("MODMX_load_module: Error: couldn't read header of pattern # %d.\n",
			            pat_cnt);
			return (-1);
		}
		nbr_lines = BASE_intel_word (pat_header.nbr_lines);
		pat_packed_size = (UWORD)BASE_intel_word (pat_header.packed_size);

		/* Change le nombre de lignes du pattern */
		if (PAT_set_pattern_height (pat_cnt, nbr_lines))
		{
			LOG_printf ("MODXM_load_module: Error: couldn't set number of lines of pattern # %d to %d.\n",
			            pat_cnt, nbr_lines);
			return (-1);
		}

		if (pat_packed_size > 0)
		{
			/* Lecture des donnees */
			pattern_data_ptr = (UBYTE *) MALLOC (pat_packed_size);
			if (pattern_data_ptr == NULL)
			{
				LOG_printf ("MODMX_load_module: Error: couldn't allocate memory for pattern conversion.\n");
				return (-1);
			}
			if (fread (pattern_data_ptr, pat_packed_size, 1, file_ptr) != 1)
			{
				LOG_printf ("MODMX_load_module: Error: couldn't read data of pattern # %d.\n",
				            pat_cnt);
				FREE (pattern_data_ptr);
				return (-1);
			}

			ft_note_offset = 0;
			for (line_cnt = 0; line_cnt < nbr_lines; line_cnt ++)
			{
				for (trk_cnt = 0; trk_cnt < nbr_channels; trk_cnt ++)
				{
					int		content;
					UBYTE		note;
					UBYTE		fxnum;
					UBYTE		fxparam;
					MODS_GT2_SPL_NOTE	*note_ptr;

					note_ptr = PAT_get_spl_note_adr_pat (pat_cnt, line_cnt, trk_cnt);
					fxnum = 0;
					fxparam = 0;
					content = pattern_data_ptr [ft_note_offset];
					if ((content & 0x80) == 0)
					{
						content = 0x1F;	// Par defaut tout y est
					}
					else
					{
						ft_note_offset ++;
					}

					/* Note */
					if (content & 0x01)
					{
						note = pattern_data_ptr [ft_note_offset];
						ft_note_offset ++;
						if (note == 97)
						{
							note_ptr->fxnum = 0x0A;
							note_ptr->fxparam = 0x00;
						}
						else
						{
							note_ptr->note = note - 1;
						}
					}

					/* Instrument */
					if (content & 0x02)
					{
						note_ptr->instr = pattern_data_ptr [ft_note_offset];
						ft_note_offset ++;
					}

					/* Volume */
					if (content & 0x04)
					{
						note_ptr->volume = pattern_data_ptr [ft_note_offset];
						ft_note_offset ++;
					}

					/* Effet (commande) */
					if (content & 0x08)
					{
						fxnum = pattern_data_ptr [ft_note_offset];
						ft_note_offset ++;
					}

					/* Effet (parametre) */
					if (content & 0x10)
					{
						fxparam = pattern_data_ptr [ft_note_offset];
						ft_note_offset ++;
					}

					/* Interpretation de l'effet */
					MODXM_modcmd_2_gtcmd (*note_ptr, fxnum, fxparam);
				}
			}

			FREE (pattern_data_ptr);

			/* On repasse sur le pattern pour corriger les montees et
			   descentes de volume. */
			for (trk_cnt = 0; trk_cnt < nbr_channels; trk_cnt ++)
			{
				signed int	direction = 0;

				for (line_cnt = 0; line_cnt < nbr_lines; line_cnt ++)
				{
					MODS_GT2_SPL_NOTE	*const note_ptr = PAT_get_spl_note_adr_pat (pat_cnt, line_cnt, trk_cnt);

					if (   (   note_ptr->fxnum == 0x14
					        || note_ptr->fxnum == 0x15))
					{
						if (note_ptr->fxparam != 0)
						{
							if (note_ptr->fxnum == 0x14)
							{
								direction = 1;
							}
							else
							{
								direction = -1;
							}
						}

						else
						{
							if (direction > 0)
							{
								note_ptr->fxnum = 0x14;
							}
							else if (direction < 0)
							{
								note_ptr->fxnum = 0x15;
							}
						}
					}
				}
			}
		}

		cur_file_pos += BASE_intel_lword (pat_header.size) + pat_packed_size;
	}

/*______________________________________________
 *
 * Instruments
 *______________________________________________
 */

	if (FILE_fseek (file_ptr, cur_file_pos, SEEK_SET) != 0)
	{
		LOG_printf ("MODMX_load_module: Error: couldn't set file position.\n");
		return (-1);
	}

	for (instr_cnt = 1; instr_cnt <= nbr_instruments; instr_cnt ++)
	{
		if (MODXM_load_instrument (file_ptr, instr_cnt))
		{
			LOG_printf ("MODMX_load_module: Error: couldn't load instrument # %d.\n",
			            instr_cnt);
			return (-1);
		}
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: MODXM_detect_format                                            */
/*      Description: Teste si un module est au format XM.                   */
/*      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 XM.                                */
/*==========================================================================*/

bool	MODXM_detect_format (FILE *file_ptr, const void *header_ptr, long header_length, BYTE temp_info [MODS_TEMP_INFO_LEN])
{
	char		id_0 [MODXM_ID_LEN+1];

	memcpy (id_0, header_ptr, MODXM_ID_LEN);
	id_0 [MODXM_ID_LEN] = '\0';
	if (strcmp (id_0, "Extended Module: ") == 0)
	{
		return (true);
	}

	return (false);
}



/*==========================================================================*/
/*      Nom: MODXM_load_instrument                                          */
/*      Description: Charge un instrument Fast Tracker 2                    */
/*      Parametres en entree:                                               */
/*        - instr: numero de l'instrument a charger.                        */
/*      Parametres en entree/sortie:                                        */
/*        - file_ptr: pointeur sur le fichier. Il doit etre positionne au   */
/*                    debut de l'instrument. Le fichier pointe sur          */
/*                    instrument suivant a la sortie de la routine.         */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	MODXM_load_instrument (FILE *file_ptr, int instr)
{
	int		sample;
	int		sample_cnt;
	int		nbr_samples;
	int		note_cnt;
	int		bytes_per_sample;
	int		env_type;
	signed int	transp;
	long		instr_header_size;
	long		sample_header_size;
	long		data_length;
	long		sample_length;
	long		spl_data_cnt;
	char		text_0 [31+1];
	int		sample_index [96];
	int		nbr_points;
	int		point_cnt;
	int		old_time;
	int		env_flags;
	Envelope_POINT	point;
	Envelope_LOOP	loop;
	const int	loop_type_table [4] =
	{
		WaveForm_LOOP_TYPE_NONE,
		WaveForm_LOOP_TYPE_FWD,
		WaveForm_LOOP_TYPE_PP,
		WaveForm_LOOP_TYPE_FWD	// Undocumented
	};
	const struct
	{
		int		type;
		SLWORD	offset;
		double	scale;
	}			env_type_table [2] =
	{
		{
			Envelope_TYPE_VOL, 0, 0x1000 / 64
		},
		{
			Envelope_TYPE_PAN, -0x80, 0x100 / 64
		}
	};
	MODXM_INSTR_HEADER	header;
	MODXM_INSTR_BODY		body;
	MODXM_SAMPLE_HEADER	*sample_header_ptr;
	SPLS_PRIMARY_INFO		sample_info;

	int64_t        cur_file_pos = FILE_ftell (file_ptr);

/*______________________________________________
 *
 * Chargement du header
 *______________________________________________
 */

	if (fread (&header, sizeof (header), 1, file_ptr) != 1)
	{
		LOG_printf ("MODXM_load_instrument: Error: couldn't read header.\n");
		return (-1);
	}

	memcpy (text_0, header.name, MODXM_INSTR_NAME_MAX_LEN);
	text_0 [MODXM_INSTR_NAME_MAX_LEN] = '\0';
	INST_set_instr_name (instr, text_0);
	nbr_samples = BASE_intel_word (header.nbr_samples);
	instr_header_size = BASE_intel_lword (header.size);
	cur_file_pos += instr_header_size;

	if (nbr_samples > 0)
	{

/*______________________________________________
 *
 * Corps de l'instrument
 *______________________________________________
 */

		if (fread (&body, sizeof (body), 1, file_ptr) != 1)
		{
			LOG_printf ("MODXM_load_instrument: Error: couldn't read body.\n");
			return (-1);
		}

		/* Cherche des emplacements de samples libres */
		sample = 1;
		for (sample_cnt = 0; sample_cnt < nbr_samples; sample_cnt ++)
		{
			while (sample <= SAMP_NBRSAMPLES_MAXI)
			{
				if (! MODS_sample_filled (sample))
				{
					break;
				}
				sample ++;
			}

			if (sample > SAMP_NBRSAMPLES_MAXI)
			{
				LOG_printf ("MODXM_load_instrument: Error: too much free sites to load all the samples.\n");
				return (-1);
			}

			sample_index [sample_cnt] = sample;
			sample ++;
		}

		/* Affecte les samples a chaque note de l'instrument */
		for (note_cnt = 0; note_cnt < GTK_NBRNOTES_MAXI; note_cnt ++)
		{
			if (note_cnt < 96)
			{
				sample = sample_index [body.samples [note_cnt]];
			}
			INST_set_instr_sample (instr, note_cnt, sample);
		}

/*______________________________________________
 *
 * Enveloppes
 *______________________________________________
 */

		for (env_type = 0; env_type < 2; env_type ++)
		{
			env_flags = ENV_get_flags (env_type_table [env_type].type, instr);

			/* Recupere les points */
			if (body.nbr_points [env_type] > 0)
			{
				nbr_points = body.nbr_points [env_type];
				ENV_set_nbr_points (env_type_table [env_type].type, instr, nbr_points);
				old_time = 0;
				for (point_cnt = 0; point_cnt < nbr_points; point_cnt ++)
				{
					if ( point_cnt < nbr_points - 1)
					{
						point.time =   body.env [env_type] [point_cnt+1].time
						             - body.env [env_type] [point_cnt].time;
					}
					else
					{
						point.time = 0;
					}
					point.val = (SWORD) (  body.env [env_type] [point_cnt].value
					                     * env_type_table [env_type].scale
					                     + env_type_table [env_type].offset);
					ENV_set_point (env_type_table [env_type].type, instr, point_cnt, point);
				}
			}

			/* Fadeout */
			if (env_type_table [env_type].type == Envelope_TYPE_VOL)
			{
				ENV_set_fadeout (env_type_table [env_type].type, instr,
				                 BASE_intel_word (body.vol_fadeout));
				env_flags |= Envelope_FLAG_FADEOUT;
			}

			/* Vibrato */
			if (env_type_table [env_type].type == Envelope_TYPE_TON)
			{
				ENV_set_lfo_speed (env_type_table [env_type].type, instr,
				                   (UWORD)body.vib_rate << 8);
				ENV_set_lfo_depth (env_type_table [env_type].type, instr,
				                   (UWORD) (  body.vib_depth
				                            * env_type_table [env_type].scale));
				ENV_set_lfo_sweep (env_type_table [env_type].type, instr,
				                   body.vib_sweep);
				if (   body.vib_rate > 0
				    && body.vib_depth > 0)
				{
					env_flags |= Envelope_FLAG_LFO;
				}
			}

			/* Loop */
			loop.start = body.control [env_type].loop_start;
			loop.end = body.control [env_type].loop_end;
			loop.nbr_loops = Envelope_LOOP_INFINITE;
			ENV_set_loop (env_type_table [env_type].type, instr,
			              Envelope_LOOP_NORMAL, loop);
			if (body.type [env_type] & 0x04)
			{
				env_flags |= Envelope_FLAG_ENV_LOOP;
			}

			/* Sustain */
			loop.start = body.control [env_type].sustain;
			loop.end = loop.start;
			loop.nbr_loops = Envelope_LOOP_INFINITE;
			ENV_set_loop (env_type_table [env_type].type, instr,
			              Envelope_LOOP_SUSTAIN, loop);
			if (body.type [env_type] & 0x02)
			{
				env_flags |= Envelope_FLAG_SUS_LOOP;
			}

			ENV_set_flags (env_type_table [env_type].type, instr, env_flags);

			/* Envelope active */
			if (body.type [env_type] & 0x01)
			{
				INST_set_instr_env (instr, env_type_table [env_type].type, instr);
			}
		}

/*______________________________________________
 *
 * Headers des samples
 *______________________________________________
 */

		sample_header_size = BASE_intel_lword (body.sample_header_size);

		sample_header_ptr = (MODXM_SAMPLE_HEADER *) MALLOC (nbr_samples * sizeof (*sample_header_ptr));
		if (sample_header_ptr == NULL)
		{
			LOG_printf ("MODXM_load_instrument: Error: couldn't allocate memory for sample headers.\n");
			return (-1);
		}

		for (sample_cnt = 0; sample_cnt < nbr_samples; sample_cnt ++)
		{
			/* Placement sur le header */
			if (FILE_fseek (file_ptr, cur_file_pos, SEEK_SET) != 0)
			{
				LOG_printf ("MODXM_load_instrument: Error: couldn't set file position.\n");
				FREE (sample_header_ptr);
				return (-1);
			}

			/* Lecture du header */
			if (fread (&sample_header_ptr [sample_cnt], sizeof (*sample_header_ptr), 1, file_ptr) != 1)
			{
				LOG_printf ("MODXM_load_instrument: Error: couldn't read header of sample # %d.\n",
				            sample_cnt);
				FREE (sample_header_ptr);
				return (-1);
			}

			cur_file_pos += sample_header_size;
		}

		if (FILE_fseek (file_ptr, cur_file_pos, SEEK_SET) != 0)
		{
			LOG_printf ("MODXM_load_instrument: Error: couldn't set file position.\n");
			FREE (sample_header_ptr);
			return (-1);
		}

/*______________________________________________
 *
 * Donnees des samples
 *______________________________________________
 */

		for (sample_cnt = 0; sample_cnt < nbr_samples; sample_cnt ++)
		{
			data_length = BASE_intel_lword (sample_header_ptr [sample_cnt].sample_len);
			sample = sample_index [sample_cnt];

			memcpy (text_0, sample_header_ptr [sample_cnt].name, MODXM_SAMPLE_NAME_MAX_LEN);
			text_0 [MODXM_SAMPLE_NAME_MAX_LEN] = '\0';
			SAMP_set_sample_name (sample, text_0);
			SPLS_init_primary_info (&sample_info, sample);
			sample_info.byte_order = 1;
			sample_info.signed_flag = true;
			sample_info.nbits = ((sample_header_ptr [sample_cnt].type & 0x10) != 0) ? 16 : 8;
			sample_info.tracks = 1;
			bytes_per_sample = ((sample_info.nbits + 7) >> 3) * sample_info.tracks;
			sample_info.length = data_length / bytes_per_sample;
			sample_info.repeat = BASE_intel_lword (sample_header_ptr [sample_cnt].loop_start) / bytes_per_sample;
			sample_info.replen = BASE_intel_lword (sample_header_ptr [sample_cnt].loop_len) / bytes_per_sample;
			sample_info.loop_type = loop_type_table [sample_header_ptr [sample_cnt].type & 0x03];
			sample_info.data_offset = cur_file_pos;

			/* Charge les donnees */
			if (SPLRAW_load_sample (file_ptr, sample, &sample_info))
			{
				LOG_printf ("MODXM_load_instrument: Error: couldn't load data of sample # %d.\n",
				            sample_cnt);
				return (-1);
			}
			sample_length = SAMP_get_sample_length (sample);

			/* Conversion en mode absolu */
			if (sample_info.nbits == 16)
			{
				SWORD		old;
				SWORD		*data_ptr;

				data_ptr = (SWORD *)SAMP_get_sample_data_adr (sample);
				old = 0;
				for (spl_data_cnt = 0; spl_data_cnt < sample_length; spl_data_cnt ++)
				{
					old += data_ptr [spl_data_cnt];
					data_ptr [spl_data_cnt] = old;
				}
			}
			else
			{
				SBYTE		old;
				SBYTE		*data_ptr;

				data_ptr = (SBYTE *)SAMP_get_sample_data_adr (sample);
				old = 0;
				for (spl_data_cnt = 0; spl_data_cnt < sample_length; spl_data_cnt ++)
				{
					old += data_ptr [spl_data_cnt];
					data_ptr [spl_data_cnt] = old;
				}
			}

			SAMP_set_sample_freq (sample, 8363);
			SAMP_set_sample_finetune (sample, sample_header_ptr [sample_cnt].finetune / 16);
			INST_set_instr_volume (instr, (int)sample_header_ptr [sample_cnt].volume << 2);
			SAMP_set_sample_balance (sample, (int)sample_header_ptr [sample_cnt].panning << 4);
			SAMP_set_sample_loop (sample,
			                      sample_info.loop_type,
			                      sample_info.repeat,
			                      sample_info.replen);
			SAMP_make_buffers (sample);

			/* Transpose le sample dans l'instrument */
			transp = sample_header_ptr [sample_cnt].transpose;
			SAMP_set_sample_midi_note (sample, Sample_REF_C2 + transp);
			for (note_cnt = 0; note_cnt < GTK_NBRNOTES_MAXI; note_cnt ++)
			{
				if (INST_get_instr_sample (instr, note_cnt) == sample)
				{
					INST_set_instr_transp (instr, note_cnt, transp);
				}
			}

			cur_file_pos += data_length;
		}

		FREE (sample_header_ptr);
	}

	/* Placement final sur l'instrument suivant (s'il existe) */
	if (FILE_fseek (file_ptr, cur_file_pos, SEEK_SET) != 0)
	{
		LOG_printf ("MODXM_load_instrument: Error: couldn't set file position.\n");
		return (-1);
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: MODXM_ftcmd_2_gtcmd                                            */
/*      Description: Convertit un effet FastTracker en effet GT.            */
/*      Parametres en entree:                                               */
/*        - cmd: commande a convertir.                                      */
/*        - param: parametre de la commande.                                */
/*      Parametres en sortie:                                               */
/*      Parametres en entree/sortie:                                        */
/*        - gt2_note: note qui recoit l'effet converti. Si l'effet ne peut  */
/*                    etre converti, la zone correspondante est laissee     */
/*                    telle quelle.                                         */
/*==========================================================================*/

void	MODXM_modcmd_2_gtcmd (MODS_GT2_SPL_NOTE &gt2_note, int cmd, int param)
{
	/* Commande Protracker standard */
	if (cmd <= 0xF)
	{
		MODMOD_modcmd_2_gtcmd (gt2_note, (cmd << 8) + param, true);
	}

	/* Commande FastTracker */
	else
	{
		switch (cmd)
		{

		/* Key off */
		case	0x14:
			gt2_note.fxnum = 0x0A;
			gt2_note.fxparam = 0x00;
			break;

		/* Panning slide */
		case	0x19:
			param = (param >> 4) - (param & 0xF);
			if (param < 0)
			{
				gt2_note.fxnum = 0xAE;
				param = -param;
			}
			else
			{
				gt2_note.fxnum = 0xAF;
			}
			gt2_note.fxparam = param << 4;
			break;

		/* Multi-retrig note */
		case	0x1B:
			gt2_note.fxnum = 0x13;
			gt2_note.fxparam = param;
			break;

		/* Tremor */
		case	0x1D:
			gt2_note.fxnum = 0xB0;
			gt2_note.fxparam = param;
			break;

		/* Effets etendus */
		case	0x21:
			cmd = param >> 8;
			param &= 0xF;
			switch (cmd)
			{

			/* Extra fine protamento up */
			case	0x1:
				gt2_note.fxnum = 0xAC;
				gt2_note.fxparam = param << 2;
				break;

			/* Extra fine portamento down */
			case	0x2:
				gt2_note.fxnum = 0xAD;
				gt2_note.fxparam = param << 2;
				break;

			}
			break;

		}
	}
}



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



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