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

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

--- Legal stuff ---

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

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



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

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

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



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



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



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



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

const char	*SPLRAW_sample_name_0 = "Raw data";
const char	*SPLRAW_sample_extension_0 = "*.raw";



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



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



/*==========================================================================*/
/*      Nom: SPLRAW_save_sample                                             */
/*      Description: Sauve un sample sous forme brute.                      */
/*      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	SPLRAW_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);
	sample_info.data_offset = 0;

	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 ("SPLRAW_save_sample: Error: couldn't position on sample data (direct-to-disk).\n");
			return (-1);
		}
	}
	else
	{
		mem_data_ptr = SAMP_get_sample_data_adr (sample);
		file_data_ptr = NULL;
	}
	
	if (SPLRAW_save_sample_header (file_ptr, &sample_info))
	{
		LOG_printf ("SPLRAW_save_sample: Error: couldn't save sample header.\n");
		return (-1);
	}

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

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

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

	return (0);
}



/*==========================================================================*/
/*      Nom: SPLRAW_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	SPLRAW_save_sample_header (FILE *file_ptr, SPLS_PRIMARY_INFO *sample_info_ptr)
{
	return (0);
}



/*==========================================================================*/
/*      Nom: SPLRAW_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 aucun probleme.                                        */
/*==========================================================================*/

signed int	SPLRAW_save_sample_data (FILE *file_ptr, SPLS_PRIMARY_INFO *sample_info_ptr, void *mem_data_ptr, FILE *file_data_ptr)
{
	LWORD		sample_mul;
	int		sample_stereo;
	int		sample_resol;

	sample_stereo = sample_info_ptr->tracks;
	sample_resol = sample_info_ptr->nbits;
	sample_mul = sample_stereo * sample_resol >> 3;

	sample_info_ptr->file_position =   sample_info_ptr->data_offset
	                                 + sample_info_ptr->position * sample_mul;

	return (SPLS_save_raw_sample_block (file_ptr, sample_info_ptr,
	                                    mem_data_ptr, file_data_ptr));
}



/*==========================================================================*/
/*      Nom: SPLRAW_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	SPLRAW_save_sample_tail (FILE *file_ptr, const SPLS_PRIMARY_INFO *sample_info_ptr)
{
	if (fflush (file_ptr))
	{
		LOG_printf ("SPLRAW_save_sample_tail: Error: couldn't flush file buffer.\n");
		return (-1);	/* Erreur d'ecriture */
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: SPLRAW_get_primary_info                                        */
/*      Description: Recupere les informations de base sur un sample RAW.   */
/*                   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.                                 */
/*==========================================================================*/

signed int	SPLRAW_get_primary_info (FILE *file_ptr, SPLS_PRIMARY_INFO *sample_info_ptr)
{
	int64_t		file_length;

	file_length = FILE_get_file_length (file_ptr);
	sample_info_ptr->data_offset = MIN (sample_info_ptr->data_offset, file_length);
	sample_info_ptr->length = long (
		  (file_length - sample_info_ptr->data_offset)
		/ (sample_info_ptr->tracks * (sample_info_ptr->nbits >> 3))
	);
	sample_info_ptr->byte_order = OS_BYTE_ORDER;
	sample_info_ptr->position = 0;
	sample_info_ptr->block_len = sample_info_ptr->length;

	return (0);
}



/*==========================================================================*/
/*      Nom: SPLRAW_load_sample                                             */
/*      Description: Charge un sample, donnees brutes.                      */
/*                   Le chunk est mis a la bonne taille, et seuls les       */
/*                   parametres de longueur et tracks/nbits sont modifies.  */
/*                   Pour les samples en D2D, les donnees sources sont      */
/*                   recopiees sur le fichier de D2D a l'endroit specifie   */
/*                   par type1.file_data_offset.                            */
/*      Parametres en entree:                                               */
/*        - file_ptr: pointeur du fichier a charger.                        */
/*        - sample: numero du sample concerne.                              */
/*        - sample_info_ptr: pointeur sur les informations du sample.       */
/*                           parametres utilises: nbits, tracks, length,    */
/*                                                data_offset, byte_order,  */
/*                                                signed_flag               */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	SPLRAW_load_sample (FILE *file_ptr, int sample, SPLS_PRIMARY_INFO	*sample_info_ptr)
{
	int		byte_order;
	int		byte_width;
	FILE		*file2_ptr;
	void		*buffer_ptr;
	long		length;
	long		buffer_len;
	long		buffer_blen;
	long		sample_pos;
	long		sample_mul;
	long		sample_count;
	void		*data_ptr;
	UBYTE		*byte_ptr;
	UWORD		*word_ptr;
	long		proc_len;
	long		proc_len2;

	/* On se positionne au bon endroit */
	if (FILE_fseek (file_ptr, sample_info_ptr->data_offset, SEEK_SET) != 0)
	{
		LOG_printf ("SPLRAW_load_sample: Error: couldn't position on data.\n");
		return (-1);
	}

	/* Change la taille du sample */
	length = sample_info_ptr->length;
	byte_order = sample_info_ptr->byte_order;
	byte_width = (sample_info_ptr->nbits + 7) >> 3;
	sample_mul = sample_info_ptr->tracks * byte_width;
	SAMP_set_sample_stereo (sample, sample_info_ptr->tracks);
	SAMP_set_sample_resolution (sample, sample_info_ptr->nbits);
	if (SAMP_set_sample_length (sample, length))
	{
		LOG_printf ("SPLRAW_load_sample: Error: couldn't set file length.\n");
		return (-1);
	}

	/* Sample sur disque */
	if (SAMP_get_sample_type (sample) == 1)
	{
		file2_ptr = SAMP_get_sample_file_ptr (sample);
		if (FILE_fseek (file2_ptr, SAMP_get_sample_file_data_offset (sample), SEEK_SET) != 0)
		{
			LOG_printf ("SPLRAW_load_sample: Error: couldn't position on data of Direct-to-Disk file.\n");
			return (-1);
		}
	}

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

	/* Signature, byte re-order et chargement */
	data_ptr = SAMP_get_sample_data_adr (sample);
	sample_pos = 0;
	while (sample_pos < length)
	{
		proc_len = MIN (length - sample_pos, buffer_len);

		/* Chargement du buffer de travail avec le sample source */
		if ((LWORD) fread (buffer_ptr, sample_mul, proc_len, file_ptr) != proc_len)
		{
			LOG_printf ("SPLRAW_load_sample: Error: couldn't read data.\n");
			FREE (buffer_ptr);
			return (-1);
		}

		proc_len2 = proc_len * sample_info_ptr->tracks;

		/* Signature */
		if (! sample_info_ptr->signed_flag)
		{
			byte_ptr = (UBYTE *) buffer_ptr;
			for (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
			}
		}

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

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

		/* On vide le buffer de travail dans le sample destination */
		if (SAMP_get_sample_type (sample) == 1)
		{
			if ((LWORD) fwrite (buffer_ptr, sample_mul, proc_len, file2_ptr) != proc_len)
			{
				LOG_printf ("SPLRAW_load_sample: Error: couldn't write data on Direct-to-Disk file.\n");
				FREE (buffer_ptr);
				return (-1);
			}
		}
		else
		{
			memcpy (data_ptr, buffer_ptr, proc_len * sample_mul);
		}

		/* Bloc suivant */
		data_ptr = (BYTE *)data_ptr + proc_len * sample_mul;
		sample_pos += proc_len;
	}

	FREE (buffer_ptr);
	if (SAMP_get_sample_type (sample) == 1)
	{
		if (fflush (file2_ptr))
		{
			LOG_printf ("SPLRAW_load_sample: Error: couldn't flush file buffer.\n");
			return (-1);
		}
	}

	return (0);
}



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



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