// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// WAV -> RAW converter/dumper for DBoy
// v0.0
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// (w)2011 by lsl/checkpoint
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// http://checkpoint.atari.org
// http://www.myspace.com/_lsl_
// http://www.myspace.com/nintendohardcorelive
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

#include <direct.h>
#include <io.h>


#include "_conio.h"
#include "datatypes.h"
#include "filestuff.h"
#include "readdir.h"

#include "wav_struct.h"
#include "wav.h"


#define PCM			0x0001
#define IEEE_FLOAT	0x0003


int normalise_samples = 1;

char start_directory[512];
char samples_directory[512]="samples";


char template_fname[]= "dboy_template.nds";	// template filename
char out_fname[]=      "dboy.nds";			// output filename


int search_len;

int template_size;
u8 *template_ptr;

u8 *ptr;

int sample_bytes_total;

u8 *samples_start;
u32 sample_list;
u32 sample_list_names;
u32 sample_data;



#define NULL_BUFFER_SIZE 4096
u8 null_buffer[NULL_BUFFER_SIZE];


u32 readID(u8 *ptr)
{
	return (ptr[0]<<24)|(ptr[1]<<16)|(ptr[2]<<8)|ptr[3];
}


u32 read32(u8 *ptr)
{
	return (ptr[3]<<24)|(ptr[2]<<16)|(ptr[1]<<8)|ptr[0];
}


u32 read32_BE(u8 *ptr)
{
	return (ptr[3]<<24)|(ptr[2]<<16)|(ptr[1]<<8)|ptr[0];
}


u32 read16(u8 *ptr)
{
	u32 value = (ptr[1]*256);
	value = value | ((u8)ptr[0]);
	return value;
}

void write32(u8 *ptr,u32 value)
{
	ptr[0] = value;
	ptr[1] = value>>8;
	ptr[2] = value>>16;
	ptr[3] = value>>24;
}




u8 *WAVsrc;

int WAVsize, RIFFsize;

int file_format_tag;
int file_channels;
int file_sample_rate;
int file_bytes_second;
int file_block_align;
int file_bits_sample;
int file_data_length;

int num_samples;

u32 ID;


int read_WAV_header()
{
	int i;

	// check 'RIFF'
	ID = readID(WAVsrc + RIFF_start);
	printf("\nstartID: 0x%X",ID);
	if(ID != 'RIFF')
	{
		printf("\n\nERROR: no 'RIFF' header!");
		return -1;
	}

	RIFFsize = read32(WAVsrc + filesize);
	printf("\nRIFFsize: %i",RIFFsize);

	// check 'WAVE'
	ID = readID(WAVsrc + WAVE_start);
	printf("\n0x%X",ID);
	if(ID != 'WAVE')
	{
		printf("\n\nERROR: no 'WAVE' header!");
		return -1;		
	}

	// check fmt_start
	ID = readID(WAVsrc + fmt_start);
	printf("\n0x%X",ID);
	if(ID != 'fmt ')
	{
		printf("\n\nERROR: no 'fmt ' header!");
		return -1;		
	}

	// fmt_length
	i = read32(WAVsrc + fmt_length);
	printf("\nfmt_length: %i",i);
	if(i!=16)
	{
		printf("\n\nERROR: fmt_length != 16 !");
		return -1;
	}

	// format tag
	file_format_tag	= read16(WAVsrc + format_tag);
	printf("\nformat_tag: %X",file_format_tag);
	if( (file_format_tag != PCM) && (file_format_tag != IEEE_FLOAT))
	{
		printf("\n\nERROR: only PCM and IEEE_FLOAT supported !");
		return -1;
	}

	// channels
	file_channels = read16(WAVsrc + channels);
	printf("\nchannels: %i",file_channels);
	if(file_channels > 2)
	{
		printf("\n\nERROR: only 1 or 2 channels supported !");
		return -1;
	}

	// sample rate
	file_sample_rate = read32(WAVsrc + sample_rate);
	printf("\nsamplerate:   %i Hz",file_sample_rate);

	// bytes_second
	file_bytes_second = read32(WAVsrc + bytes_second);
	printf("\nbytes_second: %i",file_bytes_second);

	// block_align
	file_block_align = read16(WAVsrc + block_align);
	printf("\nblock_align:  %i",file_block_align);

	// bits_sample
	file_bits_sample = read16(WAVsrc + bits_sample);
	printf("\nbits_sample:  %i",file_bits_sample);
	if( (file_bits_sample!=8)&&(file_bits_sample!=16)&&(file_bits_sample!=32))
	{
		printf("\n\nERROR: only 8 or 16 or 32 bits per sample !");
		return -1;
	}

	// check 'data'
	ID = readID(WAVsrc + data_start);
	printf("\n0x%X",ID);
	if(ID != 'data')
	{
		printf("\n\nERROR: no 'data' header!");
		waitkey();
		return -1;		
	}

	// data_length
	file_data_length = read32(WAVsrc + data_length);
	printf("\ndata_length:  %i",file_data_length);

	num_samples = file_data_length / file_channels;
	switch (file_bits_sample)
	{
		case 8:
			break;
		case 16:
			num_samples = num_samples/2;
			break;
		case 32:
			num_samples = num_samples/4;
			break;
		default:
			break;
	}

	printf("\nnum_samples:  %i",num_samples);

	if(num_samples > 4194303)
	{
		printf("\n\nERROR: sample too big !");
		printf("\nmax. samplesize is 4194303 --> app. 3:29 minutes");
		waitkey();
		return -1;
	}

	//getch();
 
	return 0;
}


s8 *RAWout;

s32 read_sample()
{
	s32 spl;
	float spl_float;

	if(file_format_tag == IEEE_FLOAT)
	{
		spl_float = *(float *)(ptr);
		//printf("\n%f",spl_float);
		spl = 32767*spl_float;
		ptr+=4;
		return spl;
	}

	switch (file_bits_sample)
	{
		case 8:
			spl = *ptr++;
			break;
		case 16:
			spl = read16(ptr);
			spl = (spl<<16)>>16;	// sign extension to 32 bit
			ptr+=2;
			break;
		default:
			break;
	}
	return spl;
}


//if(normalise_samples)

s32 min_s;
s32 max_s;

s32 max_amplitude = 220;

#define MAX 128

int convert_WAV_to_8bitSignedMonoRAW()
{
	int i;

	s32 spl0,spl1;

	s32 a0,a1,dc_shift,dy;

	double num_samples_32KHz_f;
	int    num_samples_32KHz;
	double resample_freq_f;
	double resample_pos;

	FILE *dump;
	s32 *RAWout_temp;

	num_samples_32KHz_f = ( ((double)num_samples)*32768.0f) / ((double)file_sample_rate);
	num_samples_32KHz   = num_samples_32KHz_f;

	resample_freq_f		= ((double)file_sample_rate) / 32768.0f;
	resample_pos		= 0;
	
	/*
	printf("\nnum_samples: %i",num_samples);
	printf("\nfile_sample_rate: %i",file_sample_rate);
	printf("\nnum_samples_32KHz: %f",num_samples_32KHz_f);
	getch();
	*/


	ptr = WAVsrc + SAMPLE_DATA;

	RAWout = sample_output_pointer;		// malloc(num_samples+1024);
	
	RAWout_temp = malloc(num_samples*4+1024);

	min_s = +(1<<26);
	max_s = -(1<<26);

	for(i=0;i<num_samples;i++)
	{
		switch (file_channels)
		{
			case 1:						// 1 channel
				spl0 = read_sample();
				break;
			case 2:						// 2 channels
				spl0 = read_sample();
				spl1 = read_sample();
				spl0 = (spl0+spl1)/2;	// mix channels
				break;
			default:
				break;
		}

		if(file_format_tag == IEEE_FLOAT)
		{
			spl0=spl0>>8;
		}
		else
		{
			switch (file_bits_sample)
			{
				case 8:						// 8 bit input is always unsigned
					spl0 = spl0-128;		// convert to signed
					break;
				case 16:
					spl0 = spl0>>8;			// 8
					break;
				default:
					break;
			}
		}

		RAWout_temp[i] = spl0;				// 8 bit, signed, mono output

		if(spl0<min_s) min_s = spl0;
		if(spl0>max_s) max_s = spl0;
	}

	a0 = -MAX - min_s;
	a1 =  MAX - max_s;
	dy =  max_s - min_s;
	dc_shift = (a0+a1)/2;

	//printf("\nmin: %i ,max: %i",min_s,max_s);
	//waitkey();

	
	//---------------------------------------------------------------------------------
	// no resampling
	for(i=0;i<num_samples;i++)
	{
		spl0 = RAWout_temp[i];
		
		spl0 = spl0+dc_shift;
		spl0 = (spl0*max_amplitude)/dy;

		if(spl0 < (-MAX)  )  spl0 = -MAX;
		if(spl0 > (MAX-1) )  spl0 =  MAX-1;
		
		RAWout[i] = spl0;
	}
	//---------------------------------------------------------------------------------

	//---------------------------------------------------------------------------------
	// resampling
	/*
	num_samples = num_samples_32KHz;
	for(i=0;i<num_samples;i++)
	{
		spl0 = RAWout_temp[ (int)resample_pos ];
		
		spl0 = spl0+dc_shift;
		spl0 = (spl0*max_amplitude)/dy;

		if(spl0 < (-MAX)  )  spl0 = -MAX;
		if(spl0 > (MAX-1) )  spl0 =  MAX-1;
		
		RAWout[i] = spl0;

		resample_pos+=resample_freq_f; 
	}
	*/
	//---------------------------------------------------------------------------------
	

	/*
	dump = fopen("output.spl","wb");
	fwrite(RAWout, num_samples, 1,dump);
	fclose(dump);
	*/

	free(RAWout_temp);

	return 0;
}



int load_and_convert_WAV(char *WAVfname)
{
	WAVsize = get_filesize(WAVfname);
	if ( WAVsize == -1)
	{
		printf("\n\n*** ERROR opening >%s< ***",WAVfname);
		waitkey();
		return -1;
	}

	WAVsrc = malloc(WAVsize);

	load_file(WAVfname,WAVsrc);


	printf("\n%s",WAVfname);
	printf("\n%i bytes",WAVsize);

	if (read_WAV_header())
	{
		free(WAVsrc);
		return -1;
	}
	
	sample_bytes_total+=num_samples;
	if( sample_bytes_total > MAX_SAMPLESIZE)
	{
		printf("\n\n***ERROR*** MAX_SAMPLESIZE (%i) EXCEEDED !!!\n\n",MAX_SAMPLESIZE);
		waitkey();
		free(WAVsrc);
		return -1;
	}

	convert_WAV_to_8bitSignedMonoRAW();

	free(WAVsrc);
	return 0;
}


int load_WAV_header(char *WAVfname)
{
	FILE *file;

	WAVsize = get_filesize(WAVfname);
	if ( WAVsize == -1)
	{
		printf("\n\nERROR opening >%s<",WAVfname);
		return -1;
	}

	WAVsrc = malloc(SAMPLE_DATA+16);

	file=fopen(WAVfname,"rb");
	if(file)
	{
		fread(WAVsrc, SAMPLE_DATA, 1, file);
		fclose(file);
	}
	else
	{
		free(WAVsrc);
		return -1;
	}


	printf("\n\n%s",WAVfname);
	printf("\n%i bytes",WAVsize);

	if (read_WAV_header())
	{
		free(WAVsrc);
		return -1;
	}

	free(WAVsrc);
	return 0;
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
int search_id(u32 id)
{
	do
	{
		if( readID(ptr) == id)
		{
			return ptr-template_ptr;
		}
		else ptr++;
	
	}while(search_len--);
	return -1;
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
int main(int argc,char **argv)
{
	int i;
	FILE *out_file;

	char cwd_buff[512];

	char header[]="DBoy";

	printf("\ndboy sample includer");
	printf("\n--------------------\n\n");

	getcwd(start_directory,512);

	for(i=0;i<NULL_BUFFER_SIZE;i++) null_buffer[i] = 0x00;

	template_size = get_filesize(template_fname);
	
	if(template_size == -1)
	{
		printf("\n*** ERRROR OPENING: %s",template_fname);
		waitkey();
		return -1;
	}

	template_ptr = (u8*)malloc(template_size+65536);

	load_file(template_fname, template_ptr );


	ptr = template_ptr;

	search_len	= template_size;

	i				= search_id('CPT!');		ptr+=4;   // start of sample bank
	samples_start   = template_ptr + i;

	i				= search_id(0xDEADC0DE);	ptr+=4;

	if(i==-1)
	{
		printf("\n\n*** ERROR: ID NOT FOUND ***");
		waitkey();
		free(template_ptr);
		return -1;
	}

	printf("\n<samples_start> ID found at: 0x%X",samples_start);

	sample_list			= read32_BE(ptr);
	sample_list_names	= read32_BE(ptr+4);
	sample_data			= read32_BE(ptr+8);

	printf("\nsample_list:      %X",sample_list);
	printf("\nsample_list_names:%X",sample_list_names);
	printf("\nsample_data:		%X",sample_data);


	if ( chdir(samples_directory) )
	{
		printf("\n\n***ERROR OPENING SAMPLE-DIRECTORY >%s< ***",samples_directory);
		waitkey();
		free(template_ptr);
		return -1;
	}

	getcwd(cwd_buff,512);
	printf("\n\n%s\n\n",cwd_buff);

	if (read_directory()==-1)
	{
		printf("\n\n*** ERROR WHILE CONVERTING WAV-FILES ***");
		waitkey();
		free(template_ptr);
		return -1;
	}

	// write output file
	chdir(start_directory);
	out_file = fopen(out_fname,"wb");
	fwrite(template_ptr, template_size, 1, out_file);
	fclose(out_file);

	printf("\n\n\nALL SAMPLES SUCCESSFUL ADDED!\n");
	printf("Use %s on your NDS or emulator now!\n",out_fname);
	printf("                                       press a key...\n\n");

	waitkey();

	free(template_ptr);
	return 0;
}