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

        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	<string.h>

#include "archi.h"
#include "base.h"
#include "base_ct.h"
#include	"file.h"
#include "log.h"
#include "samp.h"
#include	"spl_au.h"
#include "spl_raw.h"
#include "splstruc.h"



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



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

typedef struct
{
	char		header_id [4];		/* ".snd" */
	LWORD		data_offset;
	LWORD		sample_length;
	LWORD		data_type;
	LWORD		freq;
	LWORD		channels;
} SPLAU_AU_HEADER;



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



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

const char	*SPLAU_sample_name_0 = "Sun/NeXT";
const char	*SPLAU_sample_extension_0 = "*.au,*.snd";



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



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



/*==========================================================================*/
/*      Nom: SPLAU_save_sample                                              */
/*      Description: Sauve un sample en AVR.                                */
/*      Parametres en entree:                                               */
/*        - file_ptr: pointeur du fichier a sauver.                         */
/*        - sample: numero du sample concerne.                              */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	SPLAU_save_sample (FILE *file_ptr, int sample)
{
	void		*mem_data_ptr;
	FILE		*file_data_ptr;
	SPLS_PRIMARY_INFO	sample_info;

	SPLS_init_primary_info (&sample_info, sample);

	if (SAMP_get_sample_type (sample) == 1)
	{
		mem_data_ptr = NULL;
		file_data_ptr = SAMP_get_sample_file_ptr (sample);
		if (FILE_fseek (file_data_ptr, SAMP_get_sample_file_data_offset (sample), SEEK_SET) != 0)
		{
			LOG_printf ("SPLAU_save_sample: Error: couldn't set file position.\n");
			return (-1);
		}
	}
	else
	{
		mem_data_ptr = SAMP_get_sample_data_adr (sample);
		file_data_ptr = NULL;
	}

	if (SPLAU_save_sample_header (file_ptr, &sample_info))
	{
		LOG_printf ("SPLAU_save_sample: Error: couldn't save sample header.\n");
		return (-1);
	}

	sample_info.position = 0;
	sample_info.block_len = sample_info.length;

	if (SPLAU_save_sample_data (file_ptr, &sample_info, mem_data_ptr, file_data_ptr))
	{
		LOG_printf ("SPLAU_save_sample: Error: couldn't save sample data.\n");
		return (-1);
	}

	if (SPLAU_save_sample_tail (file_ptr, &sample_info))
	{
		LOG_printf ("SPLAU_save_sample: Error: couldn't save sample tail.\n");
		return (-1);
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: SPLAU_save_sample_header                                       */
/*      Description: Sauve le header du sample.                             */
/*      Parametres en entree/sortie:                                        */
/*        - file_ptr: pointeur sur le fichier a sauver.                     */
/*        - sample_info_ptr: pointeur sur les informations sur le sample,   */
/*                           qui doivent etre deja initialisees. Cette      */
/*                           routine fixe le bloc a sauver a la totalite du */
/*                           sample.                                        */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	SPLAU_save_sample_header (FILE *file_ptr, SPLS_PRIMARY_INFO *sample_info_ptr)
{
	SPLAU_AU_HEADER	header;

	if (FILE_fseek (file_ptr, 0, SEEK_SET) != 0)
	{
		LOG_printf ("SPLAU_save_sample_header: Error: couldn't position on header.\n");
		return (-1);
	}

	memcpy (header.header_id, ".snd", 4);
	header.data_offset = BASE_moto_lword (sizeof (header));
	header.sample_length = BASE_moto_lword (sample_info_ptr->length);
	header.data_type = BASE_moto_lword ((sample_info_ptr->nbits == 16) ? 3 : 2);
	header.freq = BASE_moto_lword (sample_info_ptr->freq);
	header.channels = BASE_moto_lword (sample_info_ptr->tracks);

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

	sample_info_ptr->data_offset = sizeof (header);
	sample_info_ptr->signed_flag = true;
	sample_info_ptr->byte_order = 0;

	return (0);
}



/*==========================================================================*/
/*      Nom: SPLAU_save_sample_data                                         */
/*      Description: Sauve les donnees d'un sample                          */
/*      Parametres en entree:                                               */
/*        - 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.                 */
/*        - 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.        */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	SPLAU_save_sample_data (FILE *file_ptr, SPLS_PRIMARY_INFO *sample_info_ptr, void *mem_data_ptr, FILE *file_data_ptr)
{
	return (SPLRAW_save_sample_data (file_ptr, sample_info_ptr, mem_data_ptr, file_data_ptr));
}



/*==========================================================================*/
/*      Nom: SPLAU_save_sample_tail                                         */
/*      Description: Sauve les informations finales d'un sample.            */
/*      Parametres en entree:                                               */
/*        - sample_info_ptr: pointeur sur la structure d'informations sur   */
/*                           le sample.                                     */
/*      Parametres en entree/sortie:                                        */
/*        - file_ptr: pointeur sur le fichier. Celui-ci est positionne au   */
/*                    bon endroit.                                          */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	SPLAU_save_sample_tail (FILE *file_ptr, const SPLS_PRIMARY_INFO *sample_info_ptr)
{
	if (fflush (file_ptr))
	{
		LOG_printf ("SPLAU_save_sample_tail: Error: couldn't flush file buffer.\n");
		return (-1);	/* Erreur d'ecriture */
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: SPLAU_get_primary_info                                         */
/*      Description: Recupere les informations de base sur un sample AVR.   */
/*                   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.                        */
/*      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 format n'est pas supporte.                          */
/*              -1 si erreur.                                               */
/*==========================================================================*/

signed int	SPLAU_get_primary_info (FILE *file_ptr, SPLS_PRIMARY_INFO *sample_info_ptr)
{
	LWORD		data_type;

	/* Lecture du header */
	SPLAU_AU_HEADER		header;
	memset (&header, 0, sizeof (header));
	int64_t        file_length = FILE_get_file_length (file_ptr);
	size_t         header_length = size_t (MIN (sizeof (header), file_length));
	if (FILE_fseek (file_ptr, 0, SEEK_SET))
	{
		LOG_printf ("SPLAU_get_primary_info: Error: couldn't position on file header.\n");
		return (-1);
	}
	if (fread (&header, header_length, 1, file_ptr) != 1)
	{
		LOG_printf ("SPLAU_get_primary_info: Error: couldn't read file header.\n");
		return (-1);
	}

	/* Recuperation des informations */
	sample_info_ptr->signed_flag = true;
	data_type = BASE_moto_lword (header.data_type);
	if (data_type == 2)
	{
		sample_info_ptr->nbits = 8;
		sample_info_ptr->pack_type = SPLS_PACK_TYPE_PCM;
	}
	else if (data_type == 3)
	{
		sample_info_ptr->nbits = 16;
		sample_info_ptr->pack_type = SPLS_PACK_TYPE_PCM;
	}
	else
	{
		return (1);
	}
	sample_info_ptr->data_offset = BASE_moto_lword (header.data_offset);
	sample_info_ptr->tracks = BASE_moto_lword (header.channels);
	sample_info_ptr->freq = BASE_moto_lword (header.freq);
	sample_info_ptr->length = BASE_moto_lword (header.sample_length);
	sample_info_ptr->length = long (MIN (
		sample_info_ptr->length,
		  (file_length - sample_info_ptr->data_offset)
		/ (sample_info_ptr->tracks * (sample_info_ptr->nbits >> 3))
	));
	sample_info_ptr->byte_order = 0;
	sample_info_ptr->special = 0;

	sample_info_ptr->position = 0;
	sample_info_ptr->block_len = sample_info_ptr->length;

	return (0);
}



/*==========================================================================*/
/*      Nom: SPLAU_load_sample                                              */
/*      Description: Charge un sample en AVR.                               */
/*      Parametres en entree:                                               */
/*        - file_ptr: pointeur du fichier a charger.                        */
/*        - sample: numero du sample concerne.                              */
/*        - sample_info_ptr: pointeur sur les informations du sample.       */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	SPLAU_load_sample (FILE *file_ptr, int sample, SPLS_PRIMARY_INFO	*sample_info_ptr)
{
	if (SPLRAW_load_sample (file_ptr, sample, sample_info_ptr))
	{
		LOG_printf ("SPLAU_load_sample: Error: couldn't load sample data.\n");
		return (-1);
	}

	return (0);
}



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



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