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

        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	"archi.h"
#include	"base.h"
#include	"base_ct.h"
#include	"env.h"
#include	"Envelope.h"
#include	"inst.h"
#include	"Instrument.h"
#include	"intrface.h"
#include	"errors.h"
#include	"file.h"
#include	"FileSelector.h"
#include	"fnames.h"
#include	"gtracker.h"
#include	"log.h"
#include	"memory.h"
#include	"MixPreset.h"
#include	"mod_dtm.h"
#include	"mod_gt2.h"
#include	"mod_gtk.h"
#include	"mod_mmd.h"
#include	"mod_mod.h"
#include	"mod_s3m.h"
#include	"mod_xm.h"
#include	"modstruc.h"
#include	"os.h"
#include	"Pattern.h"
#include	"player.h"
#include	"samp.h"
#include	"String.h"



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

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



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



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



void	MODS_insert_file_into_mru (std::string path);



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

MODS_FORMAT_ROUTINES	MODS_format_routines [] =
{
	/* Graoumf Tracker */
	{
		"Graoumf Tracker",
		"*.gt2",
		"*.sng",
		MODGT2_save_module,
		MODGT2_save_song,
		MODGT2_load_module,
		MODGT2_detect_format,
		true, true, true
	},

	/* ProTracker */
	{
		"Protracker",
		"*.mod",
		NULL,
		MODMOD_save_module,
		NULL,
		MODMOD_load_module,
		MODMOD_detect_format,
		false, false, true
	},

	/* FastTracker 2 */
	{
		"FastTracker 2",
		"*.xm",
		NULL,
		MODXM_save_module,
		NULL,
		MODXM_load_module,
		MODXM_detect_format,
		false, false, true
	},

	/* Digital Tracker */
	{
		"Digital Tracker",
		"*.dtm",
		NULL,
		NULL,
		NULL,
		MODDTM_load_module,
		MODDTM_detect_format,
		false, false, true
	},

	/* OctaMED */
	{
		"OctaMED",
		"*.mmd",
		NULL,
		NULL,
		NULL,
		MODMMD_load_module,
		MODMMD_detect_format,
		false, false, true
	},

	{
		NULL
	}
};

int	MODS_export_format = MODS_FORMAT_GT2;	/* Format d'export des modules */

/* Nombre de backup pour chaque module */
int	MODS_nbr_backups = 0;


const char	MODS_patset_chunk_id [Pattern_NBR_TYPES] [4] =
{
	{ 'P', 'A', 'T', 'S' },
	{ 'P', 'I', 'N', 'S' },
	{ 'P', 'F', 'X', 'S' },
	{ 'P', 'M', 'I', 'S' }
};
const char	MODS_pattern_chunk_id [Pattern_NBR_TYPES] [4] =
{
	{ 'P', 'A', 'T', 'D' },
	{ 'P', 'A', 'I', 'N' },
	{ 'P', 'A', 'F', 'X' },
	{ 'P', 'A', 'M', 'I' }
};
const char	MODS_envelope_chunk_id [Envelope_NBR_TYPES] [4] =
{
	{ 'V', 'E', 'N', 'V' },
	{ 'T', 'E', 'N', 'V' },
	{ 'P', 'E', 'N', 'V' },
	{ 'C', 'E', 'N', 'V' },
	{ 'R', 'E', 'N', 'V' }
};
const char	*MODS_track_type_name_0_ptr [] =
{
	"Sample", "Audio In", "Effect", "MIDI", "AudioOut"
};



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



// Contains full paths (UTF-8).
// First index is the most recently used.
std::vector <std::string> MODS_mru_file_list;



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



void	MODS_insert_file_into_mru (std::string path)
{
	// Checks if the path is already here. If it is, erase it.
	auto           it =
		std::find (MODS_mru_file_list.begin (), MODS_mru_file_list.end (), path);
	if (it != MODS_mru_file_list.end ())
	{
		MODS_mru_file_list.erase (it);
	}

	MODS_mru_file_list.insert (MODS_mru_file_list.begin (), path);

	OS_save_mru_mod_list (MODS_mru_file_list);
}



/*==========================================================================*/
/*      Nom: MODS_select_module_filename                                    */
/*      Description: Choisit a l'aide du selecteur un fichier de module.    */
/*      Parametres en entree:                                               */
/*        - title_0: pointeur sur la chaine du titre du selecteur de        */
/*                   fichiers, terminee par 0.                              */
/*        - song_flag: true s'il s'agit d'une song (module sans les         */
/*                     samples).                                            */
/*        - save_flag: true si on va sauver le module.                      */
/*      Parametres en sortie:                                               */
/*        - total_name: le nom choisi, chaine vide si on a annule.          */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

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

	/* Construit la chaine de format */
	ext_temp = "";
	format_string = "";
	format_cnt = 0;
	format_list_size = 0;
	first_of_all_flag = true;
	while (MODS_format_routines [format_cnt].name_0 != NULL)
	{
		if (   (!save_flag && MODS_format_routines [format_cnt].load_module_flag)
		    || (save_flag && !song_flag && MODS_format_routines [format_cnt].save_module_flag)
		    || (save_flag && song_flag && MODS_format_routines [format_cnt].save_song_flag))
		{
			format_string += String (MODS_format_routines [format_cnt].name_0) + "\n";
			if (! ((song_flag && MODS_format_routines [format_cnt].save_song_flag) || save_flag))
			{
				format_string += MODS_format_routines [format_cnt].ext_0;
				if (MODS_format_routines [format_cnt].save_song_flag)
				{
					format_string += String (",") + MODS_format_routines [format_cnt].sng_ext_0;
				}
			}
			else if (song_flag && MODS_format_routines [format_cnt].save_song_flag)
			{
				format_string += MODS_format_routines [format_cnt].sng_ext_0;
			}
			else
			{
				format_string += MODS_format_routines [format_cnt].ext_0;
			}
			format_string += "\n";
			format_table [format_list_size] = format_cnt;
			format_list_size ++;

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

	/* Recupere chemin et nom de fichier */
	path = FNAM_get_full_path (FILE_module_filename);
	if (! save_flag)
	{
		path += ext_temp;
	}
	else if (song_flag)
	{
		path += FILE_song_maskname;
	}
	else
	{
		path += FILE_module_maskname;
	}

	filename = FNAM_get_full_filename (FILE_module_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] == MODS_export_format)
			{
				break;
			}
		}
		if (format_cnt >= format_list_size)
		{
			file_selector_ptr->set_filter_nbr (MODS_FORMAT_GT2);
		}
		else
		{
			file_selector_ptr->set_filter_nbr (format_table [format_cnt]);
		}
	}

	do
	{
		exit_button = file_selector_ptr->manage ();
	}
	while (exit_button == FileSelector::BUTTON_NONE);

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

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

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

	if (song_flag)
	{
		FILE_song_maskname = file_selector_ptr->get_mask ();
	}
	else
	{
		FILE_module_maskname = file_selector_ptr->get_mask ();
	}

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

		if (format_cnt < 0)
		{
			MODS_export_format = MODS_FORMAT_GT2;
		}

		else
		{
			MODS_export_format = format_table [format_cnt];
		}
	}

	delete file_selector_ptr;

	return (0);
}



void	MODS_load_mru_file_list ()
{
	MODS_mru_file_list = OS_load_mru_mod_list ();
}



std::vector <std::string> MODS_get_mru_file_list ()
{
	return MODS_mru_file_list;
}



void  MODS_remove_from_mru_file_list (int index)
{
	if (index >= 0 && index < int (MODS_mru_file_list.size ()))
	{
		MODS_mru_file_list.erase (MODS_mru_file_list.begin () + index);
	}
	OS_save_mru_mod_list (MODS_mru_file_list);
}



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



/*==========================================================================*/
/*      Nom: MODS_save_module                                               */
/*      Description: Fonction generique de sauvegarde de module. Appelle un */
/*                   selecteur de fichiers et sauve le module au format     */
/*                   courant, apres avoir demande la confirmation d'un      */
/*                   ecrasement eventuel. Mets a jour les chemins and the   */
/*                   MRU.                                                   */
/*      Parametres en entree:                                               */
/*        - 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	MODS_save_module (bool song_flag)
{
	signed int	ret;
	int		button;
	String	total_name;
	String	message;

	/* Selection du nom du fichier */
	if (MODS_select_module_filename (total_name, song_flag ? "Save song" : "Save module", song_flag, true))
	{
		LOG_printf ("MODS_save_module: Error: couldn't get module filename.\n");
		return (-1);	/* Erreur lors de la selection du nom de fichier */
	}
	if (total_name == String (""))
	{
		return (0);
	}

	/* Le fichier existe deja ? */
	if (FILE_file_exist (total_name.c_str ()))
	{
		message =   String (total_name)
		          + "\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 = MODS_save_module_direct (total_name.c_str (), song_flag);
	INTR_graph_ptr->mouse_arrow ();

	if (ret)
	{
		INTR_dialog_box (song_flag ? "SAVE SONG" : "SAVE MODULE",
		                 "Error: couldn't save module.",
		                 "Cancel", 0, 0);
	}
	else
	{
		MODS_insert_file_into_mru (total_name.c_str ());
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: MODS_save_module_direct                                        */
/*      Description: Sauve un module dans le format par defaut.             */
/*      Parametres en entree:                                               */
/*        - filename_0: pointeur sur le nom 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	MODS_save_module_direct (const char *filename_0, bool song_flag)
{
	signed int	ret;
	FILE		*file_ptr;

	if (FILE_backup (filename_0, MODS_nbr_backups))
	{
		LOG_printf ("MODS_save_module_direct: Warning: backup of \"%s\" module failed.\n",
		            filename_0);
	}

	LOG_printf ("Saving module %s\n", filename_0);

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

	if (song_flag)
	{
		ret = MODS_format_routines [MODS_export_format].save_song_ptr (file_ptr);
	}

	else
	{
		ret = MODS_format_routines [MODS_export_format].save_module_ptr (file_ptr);
	}

	/* Fermeture */
	fclose (file_ptr);

	if (ret)
	{
		LOG_printf ("Operation aborted.\n");
	}
	else
	{
		GTK_modified_flag = false;
	}

	return (ret);
}



signed int	MODS_save_fx_preset (int preset)
{
	signed int	ret;
	int		button;
	String	total_name;
	String	message;

	/* Selection du fichier */
	total_name = INTR_file_selector ((FNAM_get_full_path (FILE_misc_filename) + "*.fxp").c_str (),
	                                 FNAM_get_full_filename (FILE_misc_filename).c_str (),
	                                 "Save effect", "Graoumf Tracker effect preset\n*.fxp\nAll files\n*.*\n");
	if (total_name == String (""))
	{
		return (0);
	}
	FILE_misc_filename = total_name;

	/* Le fichier existe deja ? */
	if (FILE_file_exist (total_name.c_str ()))
	{
		message =   String (total_name)
		          + "\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 = MODS_save_fx_preset_direct (total_name.c_str (), preset);
	INTR_graph_ptr->mouse_arrow ();

	if (ret)
	{
		INTR_dialog_box ("SAVE EFFECT PRESET",
		                 "Error: couldn't save preset.",
		                 "Cancel", 0, 0);
	}

	return (0);
}



signed int	MODS_save_fx_preset_direct (const char *filename_0, int preset)
{
	signed int	ret;
	FILE		*file_ptr;

	LOG_printf ("Saving effect %s\n", filename_0);

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

	/* Sauvegarde */
	ret = MODGT2_save_fx_preset (file_ptr, preset, false);

	/* Fermeture */
	fclose (file_ptr);

	if (ret)
	{
		LOG_printf ("Operation aborted.\n");
	}

	return (ret);
}



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



/*==========================================================================*/
/*      Nom: MODS_load_module                                               */
/*      Description: Fonction generique de chargement de module. Appelle un */
/*                   selecteur de fichiers et charge le module au format    */
/*                   courant, apres avoir demande si on devait sauver le    */
/*                   module precedent. Mets ensuite a jour les chemins and  */
/*                   the MRU.                                               */
/*      Parametres en entree:                                               */
/*        - song_flag: true indique qu'on desire charger une song.          */
/*        - filename_0: nom du fichier a charger. NULL fait apparaitre le   */
/*                      selecteur de fichier.                               */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	MODS_load_module (bool song_flag, const char *filename_0)
{
	signed int	ret;
	int		button;
	String	total_name;

	/* Demande si on doit sauver le module actuellement en memoire */
	if (GTK_mod_is_modified ())
	{
		button = INTR_dialog_box (song_flag ? "LOAD SONG" : "LOAD MODULE",
		                          "Current module modified.\nSave changes ?",
		                          "Ignore\nSave module\nSave song\nCancel", 0, 3);

		/* Abandon */
		if (button == 3)
		{
			return (0);
		}

		/* Sauvegarde de l'ancien module */
		else if (button == 1)
		{
			MODS_save_module (false);
		}
		else if (button == 2)
		{
			MODS_save_module (true);
		}
	}

	if (filename_0 == NULL)
	{
		/* Selection du nom du fichier */
		if (MODS_select_module_filename (total_name, song_flag ? "Load song" : "Load module", song_flag, false))
		{
			LOG_printf ("MODS_load_module: Error: couldn't select module filename.\n");
			return (-1);
		}
	}
	else
	{
		total_name = filename_0;
	}

	if (total_name == String (""))
	{
		return (0);
	}

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

	/* Chargement */
	INTR_stop_song ();
	INTR_graph_ptr->mouse_bee ();
	ret = MODS_load_module_direct (total_name.c_str ());
	INTR_graph_ptr->mouse_arrow ();

	if (ret == -2)
	{
		INTR_dialog_box ("LOAD MODULE",
							  "Format not recognized.",
							  "Cancel", 0, 0);
	}
	else if (ret)
	{
		INTR_dialog_box ("LOAD MODULE",
							  "Error: couldn't load module.",
							  "Cancel", 0, 0);
	}
	else
	{
		MODS_insert_file_into_mru (total_name.c_str ());
	}

	/* On verifie qu'on part sur des bases saines */
	if (ERR_check_all ())
	{
		LOG_printf ("MODS_load_module: Error: Check failed.\n");
		return (-1);
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: MODS_load_module_direct                                        */
/*      Description: Charge un module.                                      */
/*      Parametres en entree:                                               */
/*        - filename_0: pointeur sur le nom du fichier a charger.           */
/*      Retour: 0 si tout s'est bien passe,                                 */
/*              -1 si une erreur s'est produite,                            */
/*              -2 si le format n'est pas reconnu.                          */
/*==========================================================================*/

signed int	MODS_load_module_direct (const char *filename_0)
{
	signed int	format;
	signed int	ret;
	FILE		*file_ptr;
	BYTE		temp_info [MODS_TEMP_INFO_LEN];

	Player &			player = Player::use_instance ();

	LOG_printf ("Loading module %s\n", filename_0);

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

	/* Detection du format */
	format = MODS_get_module_file_format (file_ptr, temp_info);
	if (format == MODS_IMPORT_FORMAT_UNKNOWN)
	{
		LOG_printf ("MODS_load_module_direct: Unknown module format.\n");
		return (-2);
	}

	GTK_stop_all ();
	player.clear_all_effects ();

	if (GTK_clear_all ())
	{
		LOG_printf ("MODS_load_module_direct: Error: couldn't clear module.\n");
		return (-1);
	}
	player.set_linear_period_flag (false);
	FILE_module_filename = filename_0;

	ret = MODS_format_routines [format].load_module_ptr (file_ptr, temp_info);
	player.default_interpolation ();

	/* Fermeture */
	fclose (file_ptr);

	if (ret)
	{
		LOG_printf ("Operation aborted.\n");
	}

	return (ret);
}



/*==========================================================================*/
/*      Nom: MODS_get_module_file_format                                    */
/*      Description: Cherche le format d'un fichier de module. La position  */
/*                   courante du fichier n'est pas deplacee.                */
/*      Parametres en entree/sortie:                                        */
/*        - file_ptr: pointeur sur le fichier du module.                    */
/*        - temp_info: tableau d'informations recueillies par la routine de */
/*                     detection de module.                                 */
/*      Retour: Un des formats d'import, ou -1 en cas d'erreur.             */
/*==========================================================================*/

signed int	MODS_get_module_file_format (FILE *file_ptr, BYTE temp_info [MODS_TEMP_INFO_LEN])
{
	signed int	format;
	BYTE		buffer [MODS_DETECT_HEADER_LENGTH];

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

	/* Charge le header du fichier */
	memset (buffer, 0, MODS_DETECT_HEADER_LENGTH);
	long           length =
		long (FILE_get_file_length (file_ptr) - old_file_pos);
	length = MIN (length, MODS_DETECT_HEADER_LENGTH);
	length = MAX (length, 0);
	if (fread (buffer, length, 1, file_ptr) != 1)
	{
		LOG_printf ("MODS_get_module_file_format: Error: couldn't read file header.\n");
		return (-1);
	}

	/* Detection du format */
	memset (temp_info, 0, MODS_TEMP_INFO_LEN);
	format = 0;
	while (MODS_format_routines [format].name_0 != NULL)
	{
		if (MODS_format_routines [format].load_module_flag)
		{
			if (FILE_fseek (file_ptr, old_file_pos, SEEK_SET) != 0)
			{
				LOG_printf ("MODS_get_module_file_format: Error: couldn't restore saved file position.\n");
				return (-1);
			}

			if (MODS_format_routines [format].detect_format_ptr (file_ptr, buffer, length, temp_info))
			{
				break;
			}
		}

		format ++;
	}

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

	if (MODS_format_routines [format].name_0 != NULL)
	{
		return (format);
	}
	
	return (MODS_IMPORT_FORMAT_UNKNOWN);
}



/*==========================================================================*/
/*      Nom: MODS_get_real_sample_path                                      */
/*      Description: Recupere le chemin + nom de fichier d'un sample a      */
/*                   partir de son chemin relatif. Si celui-ci n'est pas    */
/*                   trouve immediatement, plusieurs  chemins alternatifs   */
/*                   sont essayes.                                          */
/*      Parametres en entree/sortie:                                        */
/*        - text_0: Chemin relatif du fichier, qui va se transformer en     */
/*                  chemin complet. La chaine de retour est vide si le      */
/*                  sample n'a pas ete trouve sur le disque alors qu'on en  */
/*                  avait besoin.                                           */
/*        - sample_needed_flag: true si on a vraiment besoin du sample      */
/*                              (teste plusieurs chemins en cas d'echec.    */
/*      Retour: 0 si aucune erreur.                                         */
/*==========================================================================*/

signed int	MODS_get_real_sample_path (char *text_0, bool sample_needed_flag)
{
	if (strlen (text_0) == 0)
	{
		return (0);
	}

	String	abs_path (FNAM_get_abs_path_from_rel (FILE_module_filename, text_0));

	if (sample_needed_flag)
	{
		if (! FILE_file_exist (abs_path.c_str ()))
		{
			abs_path = FNAM_get_abs_path_from_rel (FILE_sample_filename, text_0);
			if (! FILE_file_exist (abs_path.c_str ()))
			{
				String	filename (FNAM_get_full_filename (text_0));

				abs_path = FNAM_get_abs_path_from_rel (FILE_module_filename, filename);
				if (! FILE_file_exist (abs_path.c_str ()))
				{
					abs_path = FNAM_get_abs_path_from_rel (FILE_sample_filename, filename);
					if (! FILE_file_exist (abs_path.c_str ()))
					{
						abs_path = "";
					}
				}
			}
		}
	}

	strcpy (text_0, abs_path.c_str ());

	return (0);
}



/****************************************************************************/
/*                                                                          */
/*      TESTS DES ENTITES UTILISEES                                         */
/*                                                                          */
/****************************************************************************/



/*==========================================================================*/
/*      Nom: MODS_pattern_filled                                            */
/*      Description: Cherche si un pattern a ete utilise ou pas.            */
/*      Parametres en entree:                                               */
/*        - track_type: type de piste.                                      */
/*        - pattern: numero du pattern examine.                             */
/*      Retour: true si le pattern est utilise, false sinon.                */
/*==========================================================================*/

bool	MODS_pattern_filled (int track_type, int pattern)
{
	int		count;
	int		line_size;
	UBYTE		*data_ptr;

	if (PAT_get_pattern_height (pattern) != 1)
	{
		return (true);
	}

	line_size =   PAT_get_pattern_nbr_tracks (track_type, pattern)
	            * Pattern::NOTE_SIZE [track_type];
	data_ptr = (UBYTE *) PAT_get_note_adr_pat (track_type, pattern, 0, 0);
	for (count = 0; count < line_size; count ++)
	{
		if (data_ptr [count] != 0)
		{
			return (true);
		}
	}

	return (false);
}



/*==========================================================================*/
/*      Nom: MODS_instr_filled                                              */
/*      Description: Cherche si un instrument a ete utilise ou pas.         */
/*      Parametres en entree:                                               */
/*        - pattern: numero de l'instrument examine.                        */
/*      Retour: true si l'instrument est utilise, false sinon.              */
/*==========================================================================*/

bool	MODS_instr_filled (int instr)
{
	int		note;
	int		flags;
	char		name_0 [Instrument_NAME_LEN+1];
	char		*test_0;

	/* Teste si on a change les samples ou les transpositions */
	for (note = 0; note < GTK_NBRNOTES_MAXI; note ++)
	{
		if (   INST_get_instr_sample (instr, note) != instr
		    || INST_get_instr_transp (instr, note) != 0)
		{
			return (true);
		}
	}

	/* Teste si le nom a ete change */
	INST_get_instr_name (instr, name_0);
	test_0 = name_0;
	while (*test_0 != '\0')
	{
		if (*test_0++ != ' ')
		{
			return (true);
		}
	}

	/* Teste les autres variables */
	if (   INST_get_instr_volume (instr) != 0x100
	    || INST_get_instr_autobal (instr) >= 0)
	{
		return (true);
	}

	for (int env_type_cnt = 0; env_type_cnt < Envelope_NBR_TYPES; env_type_cnt ++)
	{
		flags = ENV_get_flags  (env_type_cnt, instr);
		if (   INST_get_instr_env (instr, env_type_cnt) != 0
		    || ENV_get_nbr_points (env_type_cnt, instr) > 0
		    || (flags & (Envelope_FLAG_FADEOUT | Envelope_FLAG_LFO)) != 0)
		{
			return (true);
		}
	}

	return (false);
}



/*==========================================================================*/
/*      Nom: MODS_sample_filled                                             */
/*      Description: Cherche si un certain sample est vide ou pas.          */
/*      Parametres en entree:                                               */
/*        - sample: numero du sample.                                       */
/*      Retour: true si le sample n'est pas vide, false sinon.              */
/*==========================================================================*/

bool	MODS_sample_filled (int sample)
{
	char		name_0 [Instrument_NAME_LEN+1];
	char		*test_0;

	/* Regarde si la longueur est non-nulle */
	if (SAMP_get_sample_length (sample) > 0)
	{
		return (true);
	}
	
	/* Teste le nom */
	SAMP_get_sample_name (sample, name_0);
	test_0 = name_0;
	while (*test_0 != '\0')
	{
		if (*test_0++ != ' ')
		{
			return (true);
		}
	}

	return (false);
}



/*==========================================================================*/
/*      Nom: MODS_mix_preset_filled                                         */
/*      Description: Cherche si un certain preset de mixage est vide ou pas */
/*      Parametres en entree:                                               */
/*        - preset: numero de preset.                                       */
/*      Retour: true si le preset n'est pas vide.                           */
/*==========================================================================*/

bool	MODS_mix_preset_filled (int preset)
{
	char		name_0 [MixPreset_NAME_LEN+1];
	char		*test_0;
	PLAY_SOURCE_TRACK_CONF_BLOCK	track_conf;

	/* Regarde le nombre d'entrees dans la table, le type par
	   defaut de la piste destination, le numero par defaut de
		la piste destination, les volumes destination wet et dry
		et les balances destination wet et dry. */
	if (   MIXP_get_nbr_source_tracks (preset) != 1
	    || MIXP_get_track_type (preset) != GTK_TRACK_TYPE_AOU
	    || MIXP_get_track_number (preset) != 0
	    || MIXP_get_volume (preset, false) != 0x1000
	    || MIXP_get_panning (preset, false) != 0x8000
	    || MIXP_get_volume (preset, true) != 0x1000
	    || MIXP_get_panning (preset, true) != 0x8000
	    || MIXP_get_stereo (preset) != 1)
	{
		return (true);
	}

	/* Regrarde la configuration de la premiere et unique piste source. */
	MIXP_get_source_track (preset, 0, &track_conf);
	if (   track_conf.track_type != Pattern_TYPE_SPL
	    || ! track_conf.wet_flag
	    || track_conf.track_nbr != 0
	    || track_conf.inpvol != 0x1000
	    || track_conf.inppan != 0x8000)
	{
		return (true);
	}

	/* Teste le nom */
	MIXP_get_preset_name (preset, name_0);
	test_0 = name_0;
	while (*test_0 != '\0')
	{
		if (*test_0++ != ' ')
		{
			return (true);
		}
	}

	return (false);
}



/*==========================================================================*/
/*      Nom: MODS_fx_preset_filled                                          */
/*      Description: Cherche si un certain preset d'effet est vide ou pas.  */
/*      Parametres en entree:                                               */
/*        - preset: numero de preset.                                       */
/*      Retour: true si le preset n'est pas vide.                           */
/*==========================================================================*/

bool	MODS_fx_preset_filled (int preset)
{
	char		name_0 [FxPreset_NAME_LEN+1];
	char		*test_0;

	if (FXP_get_effect_type (preset) != FxPreset_TYPE_NONE)
	{
		return (true);
	}

	/* Teste le nom */
	FXP_get_name (preset, name_0);
	test_0 = name_0;
	while (*test_0 != '\0')
	{
		if (*test_0++ != ' ')
		{
			return (true);
		}
	}

	return (false);
}



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



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

