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

        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.

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



#if defined (_MSC_VER)
#pragma warning (4 : 4786) // "identifier was truncated to '255' characters in the debug information"
#endif



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

#include	<algorithm>
#include	<map>
#include	<string>
#include	<vector>

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

#include	"archi.h"
#include	"base.h"
#include	"file.h"
#include	"inst.h"
#include	"log.h"
#include	"mod_dtm.h"
#include	"mod_mod.h"
#include	"modstruc.h"
#include	"patt.h"
#include	"Pattern.h"
#include	"player.h"
#include	"samp.h"
#include	"spl_raw.h"
#include	"splstruc.h"
#include	"song.h"
#include	"WaveForm.h"



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

const int	MODDTM_SONGNAME_LEN = 20;
const int	MODDTM_INSTRNAME_LEN = 22;



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

struct MODDTM_CHK_INF
{
	char           id [4];
	int64_t        file_pos;
	long           length;
};



#if defined (_MSC_VER)
#pragma pack (push, 1)
#endif

struct MODDTM_CHUNK_HEADER
{
	char		id [4];
	LWORD		length;
};

struct MODDTM_MAIN_CHUNK			// "D.T."
{
	WORD		file_type;				// 0 = module
	BYTE		reserved [4];
	WORD		speed;
	WORD		tempo;
	BYTE		unknown_2 [4];
	char		songname [MODDTM_SONGNAME_LEN];
};

struct MODDTM_SONG_CHUNK_HEADER	// "S.Q."
{
	WORD		length;
	WORD		repeat;
	BYTE		reserved [4];
};

struct MODDTM_PATT_CHUNK			// "PATT"
{
	WORD		nbr_tracks;
	WORD		nbr_patterns;
	char		version [4];
};

struct MODDTM_DAPT_CHUNK_HEADER
{
	ULWORD	track_field;			// bit set for each saved track
	WORD		pattern_nbr;
	WORD		nbr_lines;
};

struct MODDTM_INST_CHUNK_HEADER
{
	WORD		nbr_instr;
};

struct MODDTM_INST_CHUNK_DATA
{
	BYTE		reserved [4];
	LWORD		length;					// Bytes
	UBYTE		finetune;				// 8..F, 0, 1..7
	UBYTE		volume;					// 0 - 64
	LWORD		reppos;					// Bytes
	LWORD		replen;					// Bytes
	char		name [MODDTM_INSTRNAME_LEN];
	UBYTE		flags;					// Bit 0 = mono/stereo
	BYTE		bits_per_sample;
	BYTE		unknown_2 [1];
	UBYTE		midi_note;				// Middle C = 48
	BYTE		unknown_3 [2];
	LWORD		freq;
};

struct MODDTM_DAIT_CHUNK_HEADER
{
	WORD		number;
};


struct MODDTM_NOTE
{
	// Octave  : 4 bits
	// Note    : 4 bits
	// Volume  : 6 bits
	// Instr   : 6 bits
	// FX cmd  : 4 bits
	// FX param: 8 bits
	UBYTE		note;
	UBYTE		vol_instr;
	UBYTE		instr_fx;
	UBYTE		fx;

	int		get_octave () const
	{
		return (note >> 4);
	}

	int		get_note () const
	{
		return (note & 0x0F);
	}

	int		get_vol () const
	{
		return (vol_instr >> 2);
	}

	int		get_instr () const
	{
		return (  ((vol_instr << 4) & 0x30)
		        | ((instr_fx  >> 4) & 0x0F));
	}

	int		get_fx_cmd () const
	{
		return (instr_fx & 0x0F);
	}

	int		get_fx_param () const
	{
		return (fx);
	}
};

#if defined (_MSC_VER)
#pragma pack (pop)
#endif



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

signed int	MODDTM_get_chunk_info (FILE *file_ptr, std::vector <MODDTM_CHK_INF> &chk_arr);
long	MODDTM_find_chunk (std::vector <MODDTM_CHK_INF> &chk_arr, const char *id_0, long cur_chk, FILE *file_ptr);
void	MODDTM_nativepat_2_gtpat (int pattern, const std::vector <MODDTM_NOTE> &data);
void	MODDTM_nativecmd_2_gtcmd (MODS_GT2_SPL_NOTE &gt2_note, int cmd, int param);



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



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



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



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

signed int	MODDTM_load_module (FILE *file_ptr, BYTE temp_info [MODS_TEMP_INFO_LEN])
{
	using namespace std;

	Player &			player = Player::use_instance ();

	int		dtm_version = 0;
	map <int, SPLS_PRIMARY_INFO>	sample_info_list;

	/* Liste tous les chunks du fichier */
	LOG_printf ("Making chunk inventory... ");
	vector <MODDTM_CHK_INF>	chk_arr;
	if (MODDTM_get_chunk_info (file_ptr, chk_arr))
	{
		LOG_printf ("MODDTM_load_module: Error: couldn't read chunk info.\n");
		return (-1);
	}
	const long		nbr_chk = long (chk_arr.size ());
	LOG_printf ("%ld chunks.\n", nbr_chk);

	long		chk;

/*______________________________________________
 *
 * Chunk principal
 *______________________________________________
 */

	LOG_printf ("Reading header chunk... ");
	chk = MODDTM_find_chunk (chk_arr, "D.T.", 0, file_ptr);
	if (chk < 0)
	{
		LOG_printf ("MODDTM_load_module: Error: couldn't find header chunk.\n");
		return (-1);
	}

	if (chk < nbr_chk)
	{
		MODDTM_MAIN_CHUNK	dt_chk;
		if (fread (&dt_chk, sizeof (dt_chk), 1, file_ptr) != 1)
		{
			LOG_printf ("MODDTM_load_module: Error: couldn't read header chunk.\n");
			return (-1);
		}

		/* Nom de la song */
		char		songname_0 [MODDTM_SONGNAME_LEN+1];
		memcpy (songname_0, dt_chk.songname, MODDTM_SONGNAME_LEN);
		SONG_set_song_name (songname_0);

		/* Vitesse */
		int		speed = BASE_moto_word (dt_chk.speed);
		if (speed > 0)
		{
			player.set_speed (speed);
		}

		/* Tempo */
		int		tempo = BASE_moto_word (dt_chk.tempo);
		if (tempo > 0)
		{
			player.set_tempo (tempo);
		}

		LOG_printf ("Done.\n");
	}

	else
	{
		LOG_printf ("Not found.\n");
	}

/*______________________________________________
 *
 * Song
 *______________________________________________
 */

	LOG_printf ("Reading song chunk... ");
	chk = MODDTM_find_chunk (chk_arr, "S.Q.", 0, file_ptr);
	if (chk < 0)
	{
		LOG_printf ("MODDTM_load_module: Error: couldn't find song chunk.\n");
		return (-1);
	}

	if (chk < nbr_chk)
	{
		MODDTM_SONG_CHUNK_HEADER	dt_chk;
		if (fread (&dt_chk, sizeof (dt_chk), 1, file_ptr) != 1)
		{
			LOG_printf ("MODDTM_load_module: Error: couldn't read song chunk header.\n");
			return (-1);
		}

		/* Song length, song repeat */
		const int	song_len = BASE_moto_word (dt_chk.length);
		SONG_set_song_length (song_len);
		SONG_set_song_repeat (BASE_moto_word (dt_chk.repeat));

		/* Sequence */
		const int	buffer_len = 256;
		UBYTE			song_data [buffer_len];
		int			song_pos = 0;
		while (song_pos < song_len)
		{
			const int	i_max = MIN (song_len - song_pos, buffer_len);
			if (fread (&song_data, sizeof (song_data), 1, file_ptr) != 1)
			{
				LOG_printf ("MODDTM_load_module: Error: couldn't read song chunk data.\n");
				return (-1);
			}

			for (int i = 0; i < i_max; ++i)
			{
				SONG_set_pattern_number (song_pos + i, song_data [i]);
			}
			song_pos += i_max;
		}

		LOG_printf ("%d positions.\n", song_len);
	}

	else
	{
		LOG_printf ("Not found.\n");
	}

/*______________________________________________
 *
 * Descripteur des patterns
 *______________________________________________
 */

	LOG_printf ("Reading pattern descriptor... ");
	chk = MODDTM_find_chunk (chk_arr, "PATT", 0, file_ptr);
	if (chk < 0)
	{
		LOG_printf ("MODDTM_load_module: Error: couldn't find pattern format chunk.\n");
		return (-1);
	}

	if (chk < nbr_chk)
	{
		MODDTM_PATT_CHUNK	dt_chk;
		if (fread (&dt_chk, sizeof (dt_chk), 1, file_ptr) != 1)
		{
			LOG_printf ("MODDTM_load_module: Error: couldn't read pattern format chunk.\n");
			return (-1);
		}

		/* Nombre de voies */
		const int	nbr_tracks = BASE_moto_word (dt_chk.nbr_tracks);
		if (PAT_set_nbr_tracks (Pattern_TYPE_SPL, nbr_tracks))
		{
			LOG_printf ("MODDTM_load_module: Error: couldn't set number of tracks.\n");
			return (-1);
		}

		char		version_0 [4+1];
		memcpy (version_0, dt_chk.version, 4);
		version_0 [4] = '\0';
		dtm_version = static_cast <int> (atof (version_0) * 100);

		LOG_printf ("%d tracks.\n", nbr_tracks);
	}

	else
	{
		LOG_printf ("Not found.\n");
	}

/*______________________________________________
 *
 * Nom des pistes
 *______________________________________________
 */

	LOG_printf ("Reading track name chunk... ");
	chk = MODDTM_find_chunk (chk_arr, "TRKN", 0, file_ptr);
	if (chk < 0)
	{
		LOG_printf ("MODDTM_load_module: Error: couldn't find track name chunk.\n");
		return (-1);
	}

	if (chk < nbr_chk)
	{
		const long		length = chk_arr [chk].length;
		vector <char>	data (length);
		char *const		data_0 = &data [0];
		if (fread (data_0, length, 1, file_ptr) != 1)
		{
			LOG_printf ("MODDTM_load_module: Error: couldn't read track name chunk.\n");
			return (-1);
		}

		int				track = 0;
		long				index = 0;
		while (   index < static_cast <long> (data.size ())
		       && track < GTK_nbr_tracks [Pattern_TYPE_SPL])
		{
			long		end = index;

			while (   end < static_cast <long> (data.size ())
			       && data [end] != '\0')
			{
				++ end;
			}

			const string	temp (&data [index], end - index);
			player.set_track_name (Pattern_TYPE_SPL, track, temp.c_str ());

			++ track;
			index = end + 1;
		}

		LOG_printf ("Done.\n");
	}

	else
	{
		LOG_printf ("Not found.\n");
	}

/*______________________________________________
 *
 * Patterns
 *______________________________________________
 */

	LOG_printf ("Reading pattern chunks... ");
	int		nbr_patterns_read = 0;
	for (chk = 0; chk < nbr_chk; ++chk)
	{
		chk = MODDTM_find_chunk (chk_arr, "DAPT", chk, file_ptr);
		if (chk < 0)
		{
			LOG_printf ("MODDTM_load_module: Error: couldn't find pattern chunk.\n");
			return (-1);
		}

		if (chk < nbr_chk)
		{
			MODDTM_DAPT_CHUNK_HEADER	dt_chk;
			if (fread (&dt_chk, sizeof (dt_chk), 1, file_ptr) != 1)
			{
				LOG_printf ("MODDTM_load_module: Error: couldn't read pattern chunk header.\n");
				return (-1);
			}

			const int	pattern = BASE_moto_word (dt_chk.pattern_nbr);
			const int	nbr_lines = BASE_moto_word (dt_chk.nbr_lines);
			const int	nbr_tracks = PAT_get_pattern_nbr_tracks (Pattern_TYPE_SPL, pattern);
			const long	nbr_notes = long (nbr_lines) * nbr_tracks;

			assert (nbr_lines > 0);
			if (PAT_set_pattern_height (pattern, nbr_lines))
			{
				LOG_printf ("MODDTM_load_module: Error: couldn't change pattern height.\n");
				return (-1);
			}

			/* Version < 2.04 */
			if (dtm_version < 204)
			{
				std::vector <MODMOD_NOTE>	pattern_data (nbr_notes);
				MODMOD_NOTE *pattern_ptr = &pattern_data [0];

				if (fread (pattern_ptr, nbr_notes * sizeof (*pattern_ptr), 1, file_ptr) != 1)
				{
					LOG_printf ("MODDTM_load_module: Error: couldn't read pattern chunk data.\n");
					return (-1);
				}

				MODMOD_modpat_2_gtpat (pattern, pattern_ptr);
			}

			/* Version >= 2.04 */
			else
			{
				std::vector <MODDTM_NOTE>	pattern_data (nbr_notes);
				MODDTM_NOTE *pattern_ptr = &pattern_data [0];

				if (fread (pattern_ptr, nbr_notes * sizeof (*pattern_ptr), 1, file_ptr) != 1)
				{
					LOG_printf ("MODDTM_load_module: Error: couldn't read pattern chunk data.\n");
					return (-1);
				}

				MODDTM_nativepat_2_gtpat (pattern, pattern_data);
			}

			++ nbr_patterns_read;
		}
	}
	LOG_printf ("%d patterns read.\n", nbr_patterns_read);

/*______________________________________________
 *
 * Nom des patterns
 *______________________________________________
 */

	LOG_printf ("Reading pattern name chunk... ");
	chk = MODDTM_find_chunk (chk_arr, "PATN", 0, file_ptr);
	if (chk < 0)
	{
		LOG_printf ("MODDTM_load_module: Error: couldn't find pattern name chunk.\n");
		return (-1);
	}

	if (chk < nbr_chk)
	{
		const long		length = chk_arr [chk].length;
		vector <char>	data (length);
		char *const		data_0 = &data [0];
		if (fread (data_0, length, 1, file_ptr) != 1)
		{
			LOG_printf ("MODDTM_load_module: Error: couldn't read pattern name chunk.\n");
			return (-1);
		}

		int				pattern = 0;
		long				index = 0;
		while (   index < static_cast <long> (data.size ())
		       && pattern < PAT_NBRPATTERNS_MAXI)
		{
			long		end = index;

			while (   end < static_cast <long> (data.size ())
			       && data [end] != '\0')
			{
				++ end;
			}

			const string	temp (&data [index], end - index);
			PAT_set_pattern_name (pattern, temp.c_str ());

			++ pattern;
			index = end + 1;
		}

		LOG_printf ("Done.\n");
	}

	else
	{
		LOG_printf ("Not found.\n");
	}

/*______________________________________________
 *
 * Descripteur d'instruments
 *______________________________________________
 */

	LOG_printf ("Reading instrument descriptor... ");
	chk = MODDTM_find_chunk (chk_arr, "INST", 0, file_ptr);
	if (chk < 0)
	{
		LOG_printf ("MODDTM_load_module: Error: couldn't find instrument descriptor chunk.\n");
		return (-1);
	}

	if (chk < nbr_chk)
	{
		MODDTM_INST_CHUNK_HEADER	dt_chk;
		if (fread (&dt_chk, sizeof (dt_chk), 1, file_ptr) != 1)
		{
			LOG_printf ("MODDTM_load_module: Error: couldn't read instrument descriptor chunk.\n");
			return (-1);
		}

		int		nbr_instr = BASE_moto_word (dt_chk.nbr_instr);
		for (int instr_cnt = 0; instr_cnt < nbr_instr; ++instr_cnt)
		{
			const int	instr = instr_cnt + 1;
			const int	sample = instr;

			MODDTM_INST_CHUNK_DATA	instr_data;
			if (fread (&instr_data, sizeof (instr_data), 1, file_ptr) != 1)
			{
				LOG_printf ("MODDTM_load_module: Error: couldn't read descriptor for instrument # %d.\n", instr);
				return (-1);
			}

			SPLS_PRIMARY_INFO			sample_info;
			SPLS_init_primary_info (&sample_info, sample);

			/* Name */
			char			name_0 [MODDTM_INSTRNAME_LEN+1];
			memcpy (name_0, instr_data.name, MODDTM_INSTRNAME_LEN);
			INST_set_instr_name (instr, name_0);
			SAMP_set_sample_name (sample, name_0);

			/* Resolution */
			const int	bits_per_sample = instr_data.bits_per_sample;

			/* Stereo */
			const int	stereo = (instr_data.flags & 0x01) ? 2 : 1;
			const int	bytes_per_frame = (bits_per_sample >> 3) * stereo;

			/* Longueur */
			const long	length = BASE_moto_lword (instr_data.length) / bytes_per_frame;

			/* Boucle */
			const long	reppos = BASE_moto_lword (instr_data.reppos) / bytes_per_frame;
			const long	replen = BASE_moto_lword (instr_data.replen) / bytes_per_frame;
			const int	loop_type =   (replen > 1)
							            ? WaveForm_LOOP_TYPE_FWD
							            : WaveForm_LOOP_TYPE_NONE;

			/* Frequence */
			const long	freq = BASE_moto_lword (instr_data.freq);
			SAMP_set_sample_freq (sample, freq);

			/* MIDI note */
			const int	note = instr_data.midi_note;
			SAMP_set_sample_midi_note (sample, note);

			/* Finetune */
			int			finetune = instr_data.finetune;
			if (finetune > 7)
			{
				finetune -= 16;
			}
			SAMP_set_sample_finetune (sample, finetune);

			/* Volume */
			const int	volume = instr_data.volume << 2;
			INST_set_instr_volume (instr, volume);

			sample_info.byte_order = 0;
			sample_info.tracks     = stereo;
			sample_info.nbits      = bits_per_sample;
			sample_info.length     = length;
			sample_info.repeat     = reppos;
			sample_info.replen     = replen;
			sample_info.loop_type  = loop_type;

			sample_info.midi_note  = note;
			sample_info.freq       = freq;
			sample_info.finetune   = finetune;

			sample_info_list [sample] = sample_info;
		}

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

	else
	{
		LOG_printf ("Not found.\n");
	}

/*______________________________________________
 *
 * Samples
 *______________________________________________
 */

	LOG_printf ("Reading sample chunks... ");
	int		nbr_samples_read = 0;

	for (chk = 0; chk < nbr_chk; ++chk)
	{
		chk = MODDTM_find_chunk (chk_arr, "DAIT", chk, file_ptr);
		if (chk < 0)
		{
			LOG_printf ("MODDTM_load_module: Error: couldn't find sample chunk.\n");
			return (-1);
		}

		if (chk < nbr_chk)
		{
			MODDTM_DAIT_CHUNK_HEADER	dt_chk;
			if (fread (&dt_chk, sizeof (dt_chk), 1, file_ptr) != 1)
			{
				LOG_printf ("MODDTM_load_module: Error: couldn't read sample chunk header.\n");
				return (-1);
			}

			const int	sample = BASE_moto_word (dt_chk.number) + 1;

			if (sample_info_list.find (sample) != sample_info_list.end ())
			{
				SPLS_PRIMARY_INFO	&sample_info = sample_info_list [sample];
				sample_info.data_offset =   chk_arr [chk].file_pos
				                          + sizeof (MODDTM_DAIT_CHUNK_HEADER);

				if (SPLRAW_load_sample (file_ptr, sample, &sample_info))
				{
					LOG_printf ("MODDTM_load_module: Error: couldn't read data of sample # %d.\n",
					            sample);
					return (-1);
				}

				SAMP_set_sample_loop (sample,
				                      sample_info.loop_type,
				                      sample_info.repeat,
				                      sample_info.replen);

				if (SAMP_make_buffers (sample))
				{
					LOG_printf ("MODDTM_load_module: Error: couldn't make loop buffer for sample # %d.\n",
					            sample);
					return (-1);
				}

				++ nbr_samples_read;
			}
		}
	}

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

/*______________________________________________
 *
 * Divers
 *______________________________________________
 */

	/* Panning des tracks */
	for (int track = 0; track < GTK_nbr_tracks [Pattern_TYPE_SPL]; ++track)
	{
		player.set_track_panning (Pattern_TYPE_SPL, track, false, 0x3000L + (track & 1) * 0xA000L);
	}

	return (0);
}



signed int	MODDTM_get_chunk_info (FILE *file_ptr, std::vector <MODDTM_CHK_INF> &chk_arr)
{
	int		ret_val = 0;

	/* Save file postition */
	int64_t        old_file_pos = FILE_ftell (file_ptr);
	if (old_file_pos < 0)
	{
		LOG_printf ("MODDTM_get_chunk_info: Error: couldn't save current file position.\n");
		return (-1);
	}

	const int64_t  file_length = FILE_get_file_length (file_ptr);
	if (file_length < 0)
	{
		LOG_printf ("MODDTM_get_chunk_info: Error: couldn't know file length.\n");
		return (-1);
	}

	int64_t        cur_pos = 0;
	bool           exit_flag = false;
	while (ret_val == 0 && ! exit_flag)
	{
		if (FILE_fseek (file_ptr, cur_pos, SEEK_SET) != 0)
		{
			LOG_printf ("MODDTM_get_chunk_info: Error: couldn't reach file postition %ld.\n",
			            cur_pos);
			ret_val = -1;
		}

		MODDTM_CHUNK_HEADER	header;

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

		if (ret_val == 0)
		{
			cur_pos += 8;

			long		length = BASE_moto_lword (header.length);
			MODDTM_CHK_INF		info;

			memcpy (info.id, header.id, 4);
			info.length = length;
			info.file_pos = cur_pos;
			chk_arr.push_back (info);

			cur_pos += length;
			exit_flag = (cur_pos >= file_length);
		}
	}

	/* Restore file position */
	if (old_file_pos >= 0)
	{
		if (FILE_fseek (file_ptr, old_file_pos, SEEK_SET) != 0)
		{
			LOG_printf ("MODDTM_get_chunk_info: Error: couldn't restore old file postition.\n");
			ret_val = -1;
		}
	}

	return (ret_val);
}



long	MODDTM_find_chunk (std::vector <MODDTM_CHK_INF> &chk_arr, const char *id_0, long chk, FILE *file_ptr)
{
	assert (strlen (id_0) >= 4);

	const long		size = long (chk_arr.size ());

	for ( ; chk < size; ++chk)
	{
		if (BASE_compare_id4 (chk_arr [chk].id, id_0))
		{
			break;
		}
	}

	if (chk < size)
	{
		if (FILE_fseek (file_ptr, chk_arr [chk].file_pos, SEEK_SET) != 0)
		{
			LOG_printf (
				"MODDTM_get_chunk_info: Error: couldn't reach file postition %ld.\n",
			   long (chk_arr [chk].file_pos)
			);
			return (-1);
		}
	}

	return (chk);
}



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

bool	MODDTM_detect_format (FILE *file_ptr, const void *header_ptr, long header_length, BYTE temp_info [MODS_TEMP_INFO_LEN])
{
	return (BASE_compare_id4 (header_ptr, "D.T."));
}



/*==========================================================================*/
/*      Nom: MODDTM_nativepat_2_gtpat                                       */
/*      Description: Convertit un pattern Digital Tracker en pattern GT.    */
/*                   Le format du pattern doit deja etre fixe; seules les   */
/*                   donnees sont converties.                               */
/*      Parametres en entree:                                               */
/*        - pattern: numero du pattern a convertir.                         */
/*        - data: Vecteur des donnees brutes du pattern a convertir.        */
/*==========================================================================*/

void	MODDTM_nativepat_2_gtpat (int pattern, const std::vector <MODDTM_NOTE> &data)
{
	const int	nbr_lines = PAT_get_pattern_height (pattern);
	const int	nbr_tracks = PAT_get_pattern_nbr_tracks (Pattern_TYPE_SPL, pattern);
	long			index = 0;

	for (int line_cnt = 0; line_cnt < nbr_lines; line_cnt ++)
	{
		for (int trk_cnt = 0; trk_cnt < nbr_tracks; trk_cnt ++)
		{
			const MODDTM_NOTE	&nat_note = data [index];
			MODS_GT2_SPL_NOTE &gt_note =
				*PAT_get_spl_note_adr_pat (pattern, line_cnt, trk_cnt);

			/* Conversion de la note */
			const int	octave = nat_note.get_octave ();
			const int	note   = nat_note.get_note ();
			if (note == 0 && octave == 0)
			{
				gt_note.note = 0;
			}
			else
			{
				gt_note.note = octave * 12 + note - 1;
			}

			/* Instrument */
			gt_note.instr = nat_note.get_instr ();

			/* Volume */
			const int	vol = nat_note.get_vol ();
			if (vol > 0)
			{
				gt_note.volume = vol + 0x10;
			}

			/* Effet */
			MODDTM_nativecmd_2_gtcmd (gt_note,
			                          nat_note.get_fx_cmd (),
			                          nat_note.get_fx_param ());

			++ index;
		}
	}
}



/*==========================================================================*/
/*      Nom: MODDTM_nativecmd_2_gtcmd                                       */
/*      Description: Convertit un effet Digital Tracker 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	MODDTM_nativecmd_2_gtcmd (MODS_GT2_SPL_NOTE &gt2_note, int cmd, int param)
{
	switch (cmd)
	{
	/* Arpeggio */
	case	0x0:
		if (param != 0)
		{
			gt2_note.fxnum = 0x10;
			gt2_note.fxparam =   ((param >> 4) & 0x0F)
			                   | ((param << 4) & 0xF0);
		}
		break;

	/* Effet type protracker */
	default:
		MODMOD_modcmd_2_gtcmd (gt2_note, (cmd << 8) | param);
		break;

	}
}



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



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