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

        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	"archi.h"
#include	"base.h"
#include	"base_ct.h"
#include	"fnames.h"
#include	"file.h"
#include	"FileSelector.h"
#include	"gtracker.h"
#include	"intrface.h"
#include	"log.h"
#include	"memory.h"
#include	"mods_ct.h"
#include	"Popup.h"
#include	"player.h"
#include	"samp.h"
#include	"spl_au.h"
#include	"spl_avr.h"
#include	"spl_raw.h"
#include	"spl_wav.h"
#include	"splstruc.h"
#include	"String.h"
#include	"WaveForm.h"



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

#define	SPLS_DETECT_HEADER_LENGTH	4096	/* Taille du header sur lequel on fait porter l'autodetection */



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



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



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

int	SPLS_export_format = SPLS_FORMAT_WAV;	/* Format d'export des samples */
bool	SPLS_signed_flag = true;	/* Signature des samples a l'import et a l'export */

SPLS_FORMAT_ROUTINES	SPLS_format_routines [] =
{
	/* Raw data */
	{
		SPLRAW_sample_name_0,
		SPLRAW_sample_extension_0,
		SPLRAW_save_sample,
		SPLRAW_save_sample_header,
		SPLRAW_save_sample_data,
		SPLRAW_save_sample_tail,
		SPLRAW_get_primary_info,
		SPLRAW_load_sample,
		true, true
	},

	/* Audio Visual Research (AVR) */
	{
		SPLAVR_sample_name_0,
		SPLAVR_sample_extension_0,
		SPLAVR_save_sample,
		SPLAVR_save_sample_header,
		SPLAVR_save_sample_data,
		SPLAVR_save_sample_tail,
		SPLAVR_get_primary_info,
		SPLAVR_load_sample,
		true, true
	},

	/* Wave (WAV) */
	{
		SPLWAV_sample_name_0,
		SPLWAV_sample_extension_0,
		SPLWAV_save_sample,
		SPLWAV_save_sample_header,
		SPLWAV_save_sample_data,
		SPLWAV_save_sample_tail,
		SPLWAV_get_primary_info,
		SPLWAV_load_sample,
		true, true
	},

	/* Sun/NeXT (AU) */
	{
		SPLAU_sample_name_0,
		SPLAU_sample_extension_0,
		SPLAU_save_sample,
		SPLAU_save_sample_header,
		SPLAU_save_sample_data,
		SPLAU_save_sample_tail,
		SPLAU_get_primary_info,
		SPLAU_load_sample,
		true, true
	},

	/* Termine la liste */
	{
		NULL
	}
};

int	SPLS_nbr_formats;	/* Nombre de formats repertories */

Popup	*SPLS_import_format_popup_menu_ptr;	/* Menu des formats en chargement */
Popup	*SPLS_export_format_popup_menu_ptr;	/* Menu des formats en sauvegarde */



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




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


signed int	SPLS_init (void)
{
	char		string_0 [1023+1];

	/* Compte le nombre de formats internes */
	SPLS_nbr_formats = 0;
	while (SPLS_format_routines [SPLS_nbr_formats].name_0 != NULL)
	{
		SPLS_nbr_formats ++;
	}

	/* Construit les menus pop-up des formats */
	SPLS_import_format_popup_menu_ptr = new Popup;
	if (SPLS_import_format_popup_menu_ptr == NULL)
	{
		LOG_printf ("SPLS_init: Error: couldn't create import file format pop-up menu.\n");
		return (-1);
	}

	SPLS_export_format_popup_menu_ptr = new Popup;
	if (SPLS_export_format_popup_menu_ptr == NULL)
	{
		LOG_printf ("SPLS_init: Error: couldn't create export file format pop-up menu.\n");
		return (-1);
	}

	for (int count = 0; count < SPLS_nbr_formats; count ++)
	{
		sprintf (string_0, "%s (%s)",
		         SPLS_format_routines [count].name_0,
		         SPLS_format_routines [count].ext_0);
		if (SPLS_format_routines [count].import_flag)
		{
			SPLS_import_format_popup_menu_ptr->add_line (string_0, count);
		}
		if (SPLS_format_routines [count].export_flag)
		{
			SPLS_export_format_popup_menu_ptr->add_line (string_0, count);
		}
	}
	SPLS_import_format_popup_menu_ptr->add_line ("All sample files", 998);
	SPLS_import_format_popup_menu_ptr->add_line ("All files (*.*)", 999);

	return (0);
}



void	SPLS_restore (void)
{
	if (SPLS_export_format_popup_menu_ptr != NULL)
	{
		delete SPLS_export_format_popup_menu_ptr;
		SPLS_export_format_popup_menu_ptr = NULL;
	}

	if (SPLS_import_format_popup_menu_ptr != NULL)
	{
		delete SPLS_import_format_popup_menu_ptr;
		SPLS_import_format_popup_menu_ptr = NULL;
	}
}



/*==========================================================================*/
/*      Nom: SPLS_select_sample_filename                                    */
/*      Description: Choisit a l'aide du selecteur un fichier de sample.    */
/*      Parametres en entree:                                               */
/*        - title_0: pointeur sur la chaine du titre du selecteur de        */
/*                   fichiers, terminee par 0.                              */
/*        - save_flag: true si on va sauver le sample.                      */
/*      Parametres en sortie:                                               */
/*        - total_name: nom choisi, chaine vide si on a annule.             */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	SPLS_select_sample_filename (String &total_name, const char *title_0, bool save_flag)
{
	int		exit_button;
	String	path;
	String	filename;
	String	old_total_name;
	int		format_table [256];
	String	format_string;
	String	ext_temp;
	bool		first_of_all_flag;
	bool		preloaded_flag;
	int		format_cnt;
	int		format_list_size;
	int		track;
	FileSelector	*file_selector_ptr;

	total_name = "";
	old_total_name = "";

	INTR_stop_song ();
	SAMP_kill_sample (GTK_SAMPLE_TEMP);

	/* Construit la chaine de format */
	ext_temp = "";
	format_string = "";
	format_cnt = 0;
	format_list_size = 0;
	first_of_all_flag = true;
	for (format_cnt = 0; format_cnt < SPLS_nbr_formats; format_cnt ++)
	{
		if (   (! save_flag && SPLS_format_routines [format_cnt].import_flag)
		    || (save_flag && SPLS_format_routines [format_cnt].export_flag))
		{
			format_string +=   String (SPLS_format_routines [format_cnt].name_0) + "\n"
			                 + SPLS_format_routines [format_cnt].ext_0 + "\n";
			format_table [format_list_size] = format_cnt;
			format_list_size ++;

			if (! save_flag)
			{
				if (! first_of_all_flag)
				{
					ext_temp += ",";
				}
				ext_temp += SPLS_format_routines [format_cnt].ext_0;
				first_of_all_flag = false;
			}
		}
	}
	if (! save_flag)
	{
		format_string += "All sample files\n";
		format_string += ext_temp + "\n";
	}

	/* Recupere chemin et nom de fichier */
	path = FNAM_get_full_path (FILE_sample_filename) + FILE_sample_maskname;
	filename = FNAM_get_full_filename (FILE_sample_filename);

	file_selector_ptr = new FileSelector (path.c_str (), filename.c_str (), title_0, format_string.c_str ());

	if (save_flag)
	{
		for (format_cnt = 0; format_cnt < format_list_size; format_cnt ++)
		{
			if (format_table [format_cnt] == SPLS_export_format)
			{
				break;
			}
		}
		if (format_cnt >= format_list_size)
		{
			file_selector_ptr->set_filter_nbr (SPLS_FORMAT_RAW);
		}
		else
		{
			file_selector_ptr->set_filter_nbr (format_table [format_cnt]);
		}
	}

	do
	{
		exit_button = file_selector_ptr->manage ();

		/* Recompose le nom de fichier entier */
		total_name = file_selector_ptr->get_pathname () + file_selector_ptr->get_filename ();

		/* Le nom du fichier est different du dernier memorise ? */
		if (old_total_name != total_name)
		{
			GTK_stop_all ();
			SAMP_kill_sample (GTK_SAMPLE_TEMP);
			/* Verifie l'existence du fichier */
			if (FILE_file_exist (total_name.c_str ()))
			{
				preloaded_flag = false;

				SAMP_set_sample_resolution (GTK_SAMPLE_TEMP, SAMP_get_sample_resolution (GTK_sample_nbr));
				SAMP_set_sample_stereo (GTK_SAMPLE_TEMP, SAMP_get_sample_stereo (GTK_sample_nbr));
				SAMP_set_sample_freq (GTK_SAMPLE_TEMP, SAMP_get_sample_freq (GTK_sample_nbr));

				/* Essaie de le passer en D2D s'il est trop gros (256 KB) */
				if (FILE_get_file_length (total_name.c_str ()) >= 256L*1024)
				{
					if (SAMP_use_file_for_d2d (GTK_SAMPLE_TEMP, total_name.c_str ()) == 0)
					{
						preloaded_flag = true;
					}
				}

				/* On essaie de le charger en memoire */
				if (! preloaded_flag)
				{
					if (SPLS_load_sample_direct (GTK_SAMPLE_TEMP, total_name.c_str ()) == 0)
					{
						preloaded_flag = true;
					}
				}

				/* Joue le sample si on l'a charge sans probleme */
				if (preloaded_flag)
				{
					track = TRK_preset_data [TRK_preset_nbr].track_nbr [TRK_cursor_col + TRK_cursor_offset];
					Player &			player = Player::use_instance ();
					player.play_sound (track, GTK_SAMPLE_TEMP, 0);
				}
			}

			old_total_name = total_name;
		}
	}
	while (exit_button == FileSelector::BUTTON_NONE);

	/* Efface le sample qu'on aurait pu charger pour la pre-ecoute */
	GTK_stop_all ();
	SAMP_kill_sample (GTK_SAMPLE_TEMP);

	/* Annuler ? */
	if (exit_button != FileSelector::BUTTON_OK)
	{
		total_name = "";
		delete file_selector_ptr;
		return (0);
	}

	/* Mise a jour des noms de fichiers et de chemins */
	FILE_sample_filename = total_name;

	if (save_flag)
	{
		format_cnt = file_selector_ptr->get_last_valid_filter_nbr ();

		if (format_cnt < 0)
		{
			SPLS_export_format = SPLS_FORMAT_RAW;
		}

		else
		{
			SPLS_export_format = format_table [format_cnt];
		}
	}

	delete file_selector_ptr;

	return (0);
}



/*==========================================================================*/
/*      Nom: SPLS_save_sample                                               */
/*      Description: Fonction generique de sauvegarde de sample. Appelle un */
/*                   selecteur de fichiers et sauve le sample au format     */
/*                   courant, apres avoir demande la confirmation d'un      */
/*                   erasement eventuel. Mets ensuite a jour les chemins.   */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	SPLS_save_sample (void)
{
	int		button;
	signed int	ret;
	String	filename;
	String	message;

	if (SPLS_select_sample_filename (filename, "Save sample", true))
	{
		LOG_printf ("SPLS_save_sample: Error: sample filename selection failed.\n");
		return (-1);
	}
	if (filename == String (""))
	{
		return (0);
	}

	/* Le fichier existe deja ? */
	if (FILE_file_exist (filename.c_str ()))
	{
		message =   String (filename)
		          + "\nalready exists.\nShould I overwrite existing file \?";
		button = INTR_dialog_box ("SAVE", message.c_str (), "OK\nCancel", 0, 1);
		if (button != 0)
		{
			return (0);
		}
	}

	/* Sauvegarde suivant le format demande */
	INTR_graph_ptr->mouse_bee ();
	ret = SPLS_save_sample_direct (GTK_sample_nbr, filename.c_str ());
	INTR_graph_ptr->mouse_arrow ();

	if (ret)
	{
		INTR_dialog_box ("SAVE SAMPLE",
		                 "Error: couldn't save sample.",
		                 "Cancel", 0, 0);
	}
	else
	{
		SAMP_set_sample_d2d_filename (GTK_sample_nbr, filename.c_str ());
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: SPLS_save_sample_direct                                        */
/*      Description: Sauve un sample dans le format par defaut.             */
/*      Parametres en entree:                                               */
/*        - sample: numero du sample concerne.                              */
/*        - filename_0: pointeur sur le nom du fichier.                     */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	SPLS_save_sample_direct (int sample, const char *filename_0)
{
	signed int	ret;
	FILE		*file_ptr;

	/* Ouverture du fichier */
	file_ptr = FILE_fopen (filename_0, "wb");
	if (file_ptr == NULL)
	{
		LOG_printf ("SPLS_save_sample_direct: Error: couldn't open sample file.\n");
		return (-1);
	}

	/* Sauvegarde */
	ret = SPLS_format_routines [SPLS_export_format].save_ptr (file_ptr, sample);

	/* Fermeture */
	fclose (file_ptr);

	return (ret);
}



signed int	SPLS_save_sample_header (FILE *file_ptr, SPLS_PRIMARY_INFO *sample_info_ptr)
{
	return (SPLS_format_routines [SPLS_export_format].save_header_ptr (file_ptr, sample_info_ptr));
}



signed int	SPLS_save_sample_data (FILE *file_ptr, SPLS_PRIMARY_INFO *sample_info_ptr, void *mem_data_ptr, FILE *file_data_ptr)
{
	return (SPLS_format_routines [SPLS_export_format].save_data_ptr (file_ptr,
	                                                                 sample_info_ptr,
	                                                                 mem_data_ptr,
	                                                                 file_data_ptr));
}



signed int	SPLS_save_sample_tail (FILE *file_ptr, const SPLS_PRIMARY_INFO *sample_info_ptr)
{
	return (SPLS_format_routines [SPLS_export_format].save_tail_ptr (file_ptr, sample_info_ptr));
}



/*==========================================================================*/
/*      Nom: SPLS_save_raw_sample_block                                     */
/*      Description: Sauve les donnees d'un morceau de sample.              */
/*      Parametres en entree:                                               */
/*        - sample_info_ptr: pointeur sur la structure d'informations sur   */
/*                           le sample. Les champs position et block_len    */
/*                           indiquent la partie du sample a sauver, ainsi  */
/*                           que file_position qui indique l'endroit ou     */
/*                           sauver le bloc dans le fichier.                */
/*        - mem_data_ptr: pointeur sur le bloc a sauver. NULL si le sample  */
/*                        est sur disque.                                   */
/*        - file_data_ptr: pointeur sur le fichier qui contient le bloc     */
/*                         a sauver, positionne au debut du bloc. NULL si   */
/*                         le sample est en memoire.                        */
/*      Parametres en entree/sortie:                                        */
/*        - file_ptr: pointeur sur le fichier a sauver. Celui-ci est        */
/*                    place automatiquement au bon endroit.                 */
/*      Retour: 0 si aucun probleme.                                        */
/*==========================================================================*/

signed int	SPLS_save_raw_sample_block (FILE *file_ptr, const SPLS_PRIMARY_INFO *sample_info_ptr, void *mem_data_ptr, FILE *file_data_ptr)
{
	int            sample_stereo = sample_info_ptr->tracks;
	int            sample_resol  = sample_info_ptr->nbits;
	int            byte_width    = (sample_resol + 7) >> 3;
	long           sample_mul    = sample_stereo * byte_width;
	long           sample_len    = sample_info_ptr->block_len;
	int            sample_type   = (file_data_ptr != NULL) ? 1 : 0;
	int            byte_order    = sample_info_ptr->byte_order;

	int64_t        file_pos      = sample_info_ptr->file_position;

	/* Si le fichier est trop court, on l'agrandit */
	if (FILE_get_file_length (file_ptr) < file_pos)
	{
		if (FILE_change_file_length (file_ptr, file_pos))
		{
			LOG_printf ("SPLRAW_save_sample_data: Error: couldn't change file length.\n");
			return (-1);
		}
	}

	/* Placement au bon endroit du fichier */
	if (FILE_fseek (file_ptr, file_pos, SEEK_SET))
	{
		LOG_printf ("SPLRAW_save_sample_data: Error: couldn't position on sample data.\n");
		return (-1);
	}

	/* Allocation d'un buffer de conversion */
	long           buffer_blen;
	void *         buffer_ptr =
		MEM_max_malloc (sample_len * sample_mul, 4096, &buffer_blen);
	if (buffer_ptr == NULL)
	{
		LOG_printf ("SPLRAW_save_sample_data: Error: couldn't allocate buffer for sample conversion.\n");
		return (-1);
	}
	long           buffer_len = buffer_blen / sample_mul;

	/* Signature et sauvegarde */
	long           sample_pos = 0;
	while (sample_pos < sample_len)
	{
		long           proc_len = MIN (sample_len - sample_pos, buffer_len);

		/* Chargement du buffer de travail */
		if (sample_type == 1)
		{
			if (fread (buffer_ptr, sample_mul * proc_len, 1, file_data_ptr) != 1)
			{
				LOG_printf ("SPLRAW_save_sample_data: Error: couldn't read data on Direct-to-Disk file.\n");
				FREE (buffer_ptr);
				return (-1);
			}
		}
		else
		{
			memcpy (buffer_ptr, mem_data_ptr, proc_len * sample_mul);
		}

		long           proc_len2 = proc_len * sample_stereo;

		/* Conversions little/Big endian */
		if (   byte_width > 1
		    && (byte_order ^ OS_BYTE_ORDER) != 0)
		{
			/* 16 bits (optimisation) */
			if (byte_width == 2)
			{
				UWORD *        word_ptr = (UWORD *) buffer_ptr;
				for (long sample_count = 0; sample_count < proc_len2; sample_count ++)
				{
					BASE_invert_word (word_ptr + sample_count);
				}
			}

			/* 24 bits ou plus */
			else
			{
				UWORD *        word_ptr = (UWORD *) buffer_ptr;
				for (long sample_count = 0; sample_count < proc_len2; sample_count ++)
				{
					MEM_invert_memory_byte (word_ptr + sample_count * byte_width,
					                        byte_width);
				}
			}
		}

		/* Signature */
		if (! sample_info_ptr->signed_flag)
		{
			UBYTE *        byte_ptr = (UBYTE *) buffer_ptr;
			for (long sample_count = 0; sample_count < proc_len2; sample_count ++)
			{
				#if (OS_BYTE_ORDER == 0)
					byte_ptr [sample_count * byte_width] += 0x80;
				#else
					byte_ptr [sample_count * byte_width + byte_width - 1] += 0x80;
				#endif
			}
		}

		/* Sauvegarde du buffer de travail */
		if (fwrite (buffer_ptr, sample_mul * proc_len, 1, file_ptr) != 1)
		{
			LOG_printf ("SPLRAW_save_sample_data: Error: couldn't write sample data.\n");
			FREE (buffer_ptr);
			return (-1);
		}

		/* Bloc suivant */
		if (sample_type == 0)
		{
			mem_data_ptr = (BYTE *)mem_data_ptr + proc_len * sample_mul;
		}
		sample_pos += proc_len;
	}

	FREE (buffer_ptr);

	return (0);
}



/*==========================================================================*/
/*      Nom: SPLS_get_file_format                                           */
/*      Description: Identifie et renvoie le numero du format d'un fichier. */
/*      Parametres en entree/sortie:                                        */
/*        - file_ptr: pointeur du fichier en question                       */
/*      Retour: Numero du format de fichier, ou -1 si erreur.               */
/*==========================================================================*/

signed int	SPLS_get_file_format (FILE *file_ptr)
{
	int		format;
	int64_t	file_length;
	size_t	header_length;
	BYTE		*header_ptr;

	/* Lecture du header */
	header_ptr = (BYTE *) MALLOC (SPLS_DETECT_HEADER_LENGTH);
	if (header_ptr == NULL)
	{
		LOG_printf ("SPLS_get_file_format: Error: couldn't allocate memory for header.\n");
		return (-1);
	}
	memset (header_ptr, 0, SPLS_DETECT_HEADER_LENGTH);
	file_length = FILE_get_file_length (file_ptr);
	header_length = size_t (MIN (SPLS_DETECT_HEADER_LENGTH, file_length));
	if (FILE_fseek (file_ptr, 0, SEEK_SET))
	{
		FREE (header_ptr);
		LOG_printf ("SPLS_get_file_format: Error: couldn't position on file header.\n");
		return (-1);
	}
	if (fread (header_ptr, 1, header_length, file_ptr) != header_length)
	{
		LOG_printf ("SPLS_get_file_format: Error: couldn't read file header.\n");
		FREE (header_ptr);
		return (-1);
	}

	/* Reconaissance du format */
	format = SPLS_FORMAT_RAW;
	
	if (BASE_compare_id4 (header_ptr, "2BIT"))
	{
		format = SPLS_FORMAT_AVR;
	}
	
	else if (   BASE_compare_id4 (header_ptr, "RIFF")
				&& BASE_compare_id4 (header_ptr + 8, "WAVE"))
	{
		format = SPLS_FORMAT_WAV;
	}

	else if (BASE_compare_id4 (header_ptr, ".snd"))
	{
		format = SPLS_FORMAT_AU;
	}

	/* Fin */
	FREE (header_ptr);

	return (format);
}



/*==========================================================================*/
/*      Nom: SPLS_load_sample                                               */
/*      Description: Fonction generique de chargement d'un sample.          */
/*                   Selectionne un fichier, et le charge en memoire ou     */
/*                   sur disque.                                            */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	SPLS_load_sample (void)
{
	/*
	Choisir nom de fichier
	Si pas D2D
		Lire fichier
	Sinon
		Utiliser le fichier ou le dupliquer ?
		Si utiliser
			Lire infos du fichier
			Proposer l'effacement de l'ancien
		Sinon
			Copie du fichier dans l'ancien
		Finsi
	Finsi
	*/

	signed int	result;
	int		button;
	String	filename;
	char		*old_filename_0;
	FILE		*file_ptr;

	if (SPLS_select_sample_filename (filename, "Load sample", false))
	{
		LOG_printf ("SPLS_load_sample: Error: sample filename selection failed.\n");
		return (-1);
	}
	if (filename == String (""))
	{
		return (0);
	}

	/* Le fichier n'existe pas ? */
	if (!FILE_file_exist (filename.c_str ()))
	{
		INTR_dialog_box ("LOAD SAMPLE", (String (filename) + "\ndoesn't exist.").c_str (),
		                 "Cancel", 0, 0);
		return (0);
	}

	/* Memoire */
	if (SAMP_get_sample_type (GTK_sample_nbr) == 0)
	{
		INTR_graph_ptr->mouse_bee ();
		result = SPLS_load_sample_direct (GTK_sample_nbr, filename.c_str ());
		INTR_graph_ptr->mouse_arrow ();
	}

	/* Sample sur disque */
	else
	{
		button = INTR_dialog_box ("LOAD SAMPLE",
										  "Do you want to:\n"
										  "- Duplicate the selected sample by\n"
										  "copying it on the current sample,\n"
										  "- Use the selected sample instead\n"
										  "of the current one,\n"
										  "- Or cancel the operation \?",
										  "Duplicate\nUse\nCancel", 1, 2);

		/* Duplicate */
		if (button == 0)
		{
			INTR_graph_ptr->mouse_bee ();
			result = SPLS_load_sample_direct (GTK_sample_nbr, filename.c_str ());
			INTR_graph_ptr->mouse_arrow ();
		}

		/* Use */
		else if (button == 1)
		{
			file_ptr = FILE_fopen (filename.c_str (), "rb");
			if (file_ptr == NULL)
			{
				LOG_printf ("SPLS_load_sample: Error: couldn't open file.\n");
				INTR_dialog_box ("LOAD SAMPLE",
				                 "Error: couldn't open file.",
				                 "Cancel", 0, 0);
				return (-1);
			}

			GTK_stop_all ();

			old_filename_0 = STRDUP (SAMP_get_sample_d2d_filename (GTK_sample_nbr));
			fclose (SAMP_get_sample_file_ptr (GTK_sample_nbr));
			SAMP_set_sample_file_ptr (GTK_sample_nbr, file_ptr);

			SAMP_set_sample_balance (GTK_sample_nbr, -1);
			SAMP_set_sample_volume (GTK_sample_nbr, 0x100);
			SAMP_set_sample_finetune (GTK_sample_nbr, 0);

			if (SAMP_read_d2d_header (GTK_sample_nbr))
			{
				INTR_dialog_box ("LOAD SAMPLE",
									  "Sorry, can't use this file\n"
									  "for Direct-2-Disk.",
									  "Cancel", 0, 0);
				SAMP_kill_sample (GTK_sample_nbr);
			}

			else
			{
				SAMP_set_sample_d2d_filename (GTK_sample_nbr, filename.c_str ());

				/* Destruction du fichier precedent */
				if (INTR_dialog_box ("LOAD SAMPLE",
											"Delete the previous sample file \?",
											"OK\nCancel", 1, 1) == 0)
				{
					if (remove (old_filename_0))
					{
						INTR_dialog_box ("DELETE FILE",
											  "Can't delete the previous sample file.",
											  "Cancel", 0, 0);
					}
				}
			}
			FREE (old_filename_0);
		}
	}

	if (result == -2)
	{
		INTR_dialog_box ("LOAD SAMPLE",
							  "Format not recognized.",
							  "Cancel", 0, 0);
	}

	else if (result)
	{
		INTR_dialog_box ("LOAD SAMPLE",
							  "Error: couldn't load sample.",
							  "Cancel", 0, 0);
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: SPLS_load_sample_direct                                        */
/*      Description: Charge un sample.                                      */
/*      Parametres en entree:                                               */
/*        - sample: numero du sample concerne.                              */
/*        - filename_0: pointeur sur le nom du fichier.                     */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	SPLS_load_sample_direct (int sample, const char *filename_0)
{
	signed int	format;
	signed int	result;
	FILE		*file_ptr;
	SPLS_PRIMARY_INFO	sample_info;

	/* Ouverture du fichier */
	file_ptr = FILE_fopen (filename_0, "rb");
	if (file_ptr == NULL)
	{
		LOG_printf ("SPLS_load_sample_direct: Error: couldn't open file.\n");
		return (-1);
	}

	/* Examen du format du sample */
	format = SPLS_get_file_format (file_ptr);

	/* Demande plus de precisions */
	SPLS_init_primary_info (&sample_info, sample);
	result = SPLS_get_primary_info (file_ptr, format, &sample_info);
	if (result == 1)
	{
		LOG_printf ("Unknown sample format.\n");
		return (-2);
	}
	else if (result)
	{
		LOG_printf ("SPLS_load_sample_direct: Error: couldn't get file information.\n");
		return (-1);
	}

	/* On ne peut que charger des samples de 8 ou 16 bits */
	if (   sample_info.nbits != 16
	    && sample_info.nbits != 8)
	{
		/* N'affiche pas de message d'erreur pour cela */
		return (-1);
	}

	GTK_stop_all ();

	SAMP_set_sample_loop (sample, WaveForm_LOOP_TYPE_NONE, 0, 0);
	SAMP_set_sample_balance (sample, -1);
	SAMP_set_sample_volume (sample, 0x100);
	SAMP_set_sample_finetune (sample, 0);
	SAMP_set_sample_freq (sample, sample_info.freq);
	SAMP_set_sample_midi_note (sample, sample_info.midi_note);

	/* Chargement des donnees */
	result = SPLS_format_routines [format].load_ptr (file_ptr, sample, &sample_info);

	/* Fermeture */
	fclose (file_ptr);

	if (result)
	{
		LOG_printf ("SPLS_load_sample_direct: Error: couldn't load sample.\n");
		SAMP_kill_sample (sample);
		return (-1);
	}

	/* Memorise le nom s'il ne s'agissait pas d'un sample de D2D */
	if (SAMP_get_sample_type (sample) == 0)
	{
		SAMP_set_sample_d2d_filename (sample, filename_0);
	}

	/* Recalcule les buffers */
	SAMP_set_sample_loop (sample, sample_info.loop_type,
	                              sample_info.repeat,
	                              sample_info.replen);
	SAMP_make_buffers (sample);

	return (0);
}



/*==========================================================================*/
/*      Nom: SPLS_init_primary_info                                         */
/*      Description: Initialisation du bloc d'informations sur un sample    */
/*                   avec les donnees d'un sample en memoire.               */
/*      Parametres en entree:                                               */
/*        - sample: numero du sample. Peut etre 0 si on desire initialiser  */
/*                  la structure avec les valeurs par defaut.               */
/*      Parametres en sortie:                                               */
/*        - sample_info_ptr: pointeur sur la structure a remplir.           */
/*==========================================================================*/

void	SPLS_init_primary_info (SPLS_PRIMARY_INFO *sample_info_ptr, int sample)
{
	sample_info_ptr->nbits = SAMP_get_sample_resolution (sample);
	sample_info_ptr->tracks = SAMP_get_sample_stereo (sample);
	sample_info_ptr->freq = SAMP_get_sample_freq (sample);
	sample_info_ptr->length = SAMP_get_sample_length (sample);
	sample_info_ptr->repeat = SAMP_get_sample_repeat (sample);
	sample_info_ptr->replen = SAMP_get_sample_replen (sample);
	sample_info_ptr->finetune = SAMP_get_sample_finetune (sample);
	sample_info_ptr->balance = SAMP_get_sample_balance (sample);
	sample_info_ptr->midi_note = SAMP_get_sample_midi_note (sample);
	sample_info_ptr->volume = SAMP_get_sample_volume (sample);
	sample_info_ptr->signed_flag = SPLS_signed_flag;
	sample_info_ptr->loop_type = SAMP_get_sample_loop (sample);
	sample_info_ptr->pack_type = SPLS_PACK_TYPE_PCM;
	sample_info_ptr->acm_flag = false;
	sample_info_ptr->byte_order = OS_BYTE_ORDER;
	sample_info_ptr->special = 0;
	sample_info_ptr->position = 0;
	sample_info_ptr->block_len = sample_info_ptr->length;
	if (SAMP_get_sample_type (sample) == 1)
	{
		sample_info_ptr->data_offset = SAMP_get_sample_file_data_offset (sample);
	}
	else
	{
		sample_info_ptr->data_offset = 0;
	}
	sample_info_ptr->file_position = 0;
}



/*==========================================================================*/
/*      Nom: SPLS_get_primary_info                                          */
/*      Description: Recupere les informations de base sur un sample.       */
/*                   Ne modifie que ce qui est donne par le header mais     */
/*                   peut utiliser le contenu entrant du bloc d'infos (ex:  */
/*                   RAW).                                                  */
/*      Parametres en entree:                                               */
/*        - file_ptr: pointeur de fichier du sample.                        */
/*        - format: Numero du format du sample.                             */
/*      Parametres en entree/sortie:                                        */
/*        - sample_info_ptr: pointeur sur le bloc d'information a	remplir.  */
/*      Retour: 0 si tout s'est bien passe,                                 */
/*              1 si le sous-format trouve n'est pas supporte,              */
/*              2 si le fichier a l'air corrompu,                           */
/*              un nombre negatif en cas d'erreur lors de l'operation.      */
/*==========================================================================*/

signed int	SPLS_get_primary_info (FILE *file_ptr, int format, SPLS_PRIMARY_INFO	*sample_info_ptr)
{
	return (SPLS_format_routines [format].get_primary_info_ptr (file_ptr, sample_info_ptr));
}



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

