/*
 * decodej6s.c
 *
 * RICOH DC-1/2 DSC SoundFile(J6S) decoder sample code.
 *
 *		This code is in need of 
 *				"Sun Microsystems G721_G722_G723 C source Package."
 *      Thanks Sun Microsystems, Inc.
 *
 *
 * Usage : decodej6s [-u|l|a] < infile > outfile
 *
 *		I'm poor in English....But, I try to write comment by English.
 *
 *		1996.07.13		master@kani.com
 */
#include <stdio.h>
#include "g72x.h"

/*
I'm Macintosh Developer. need this define.
If your OS is command interface, please comment out this define.
*/
#define	USE_CCOMMAND

/*
If your CPU is little-endian, please define "LITTLEENDIANCPU".
*/
/*
#define	LITTLEENDIANCPU
*/

typedef struct {
	short	rec_data_endian;
	long	snd_data_start;	/* ADPCM Data start offset. usually 0x0100.*/
	long	snd_data_end;	
	char	rec_date[6];	/* YY/MM/DD/HH/MM/SS */
} dsc_data_info;

enum {
	big_endian = 1,
	little_endian,
};

void usage()
{
	fprintf(stderr, "RICOH DSC J6S Decoder sample code.\n\n-- usage:\n");
	fprintf(stderr, "\tdecode [-u|l|a] <infile >outfile\n\n");
	fprintf(stderr, "\t-u\toutput 8 bit u-law PCM file.(default)\n");

	fprintf(stderr, "\t-l\toutput 8 bit linear PCM file.\n");
	fprintf(stderr, "\t-a\toutput 8 bit a-law PCM file.\n");
}

size_t	read_in(void *in_buffer, size_t in_size, size_t in_mmemb)
{
	return fread(in_buffer, in_size, in_mmemb, stdin);
}

int	seek_in(long seek_offset)
{
	return fseek(stdin, seek_offset, SEEK_SET);
}

size_t	write_out(void *out_buffer, size_t out_size, size_t out_mmemb)
{
	return fwrite(out_buffer, out_size, out_mmemb, stdout);
}

int	seek_out(long seek_offset)
{
	return fseek(stdout, seek_offset, SEEK_SET);
}

void	close_out()
{
	fclose(stdout);
}

int	get_dsc_info(dsc_data_info *dsc_info)
{
	unsigned char	in_buf[4], swap;
	
	if (seek_in(74))
		return 1;

	seek_in(0);
	
	read_in(in_buf, 1, 1);
	if (in_buf[0] != 0x80)	/*	not dsc file.	*/
		return 1;
	
	seek_in(50);
	read_in(in_buf, 1, 1);
	if (in_buf[0] == 0x0)	/*	endian maker.	*/
		dsc_info->rec_data_endian = big_endian;
	else	/*	if (in_buf[0] == 0x0f)	*/
		dsc_info->rec_data_endian = little_endian;
	
	seek_in(56);
	read_in(in_buf, 4, 1);
	if (dsc_info->rec_data_endian == little_endian) {
		swap = in_buf[3];
		in_buf[3] = in_buf[0];
		in_buf[0] = swap;
		swap = in_buf[2];
		in_buf[2] = in_buf[1];
		in_buf[1] = swap;
	}
#ifdef	LITTLEENDIANCPU
	swap = in_buf[3];
	in_buf[3] = in_buf[0];
	in_buf[0] = swap;
	swap = in_buf[2];
	in_buf[2] = in_buf[1];
	in_buf[1] = swap;
#endif
	dsc_info->snd_data_start = ((long)in_buf[0] << 24);
	dsc_info->snd_data_start |= ((long)in_buf[1] << 16);
	dsc_info->snd_data_start |= ((long)in_buf[2] << 8);
	dsc_info->snd_data_start |= in_buf[3];

	read_in(in_buf, 4, 1);
	if (dsc_info->rec_data_endian == little_endian) {
		swap = in_buf[3];
		in_buf[3] = in_buf[0];
		in_buf[0] = swap;
		swap = in_buf[2];
		in_buf[2] = in_buf[1];
		in_buf[1] = swap;
	}
#ifdef	LITTLEENDIANCPU
	swap = in_buf[3];
	in_buf[3] = in_buf[0];
	in_buf[0] = swap;
	swap = in_buf[2];
	in_buf[2] = in_buf[1];
	in_buf[1] = swap;
#endif
	dsc_info->snd_data_end = ((long)in_buf[0] << 24);
	dsc_info->snd_data_end |= ((long)in_buf[1] << 16);
	dsc_info->snd_data_end |= ((long)in_buf[2] << 8);
	dsc_info->snd_data_end |= in_buf[3];

	seek_in(68);
	read_in(dsc_info->rec_date, 1, 6);
	
	return 0;
}

/*
 * Unpack input codes and pass them back as bytes.
 * Returns 1 if there is residual input, returns -1 if eof, else returns 0.
 */
int
unpack_input_adpcm(
	unsigned char		*code,
	int			bits)
{
	static unsigned int	in_buffer = 0;
	static int		in_bits = 0;
	unsigned char		in_byte;

	if (in_bits < bits) {
		if (read_in(&in_byte, sizeof (char), 1) != 1) {
			*code = 0;
			return (-1);
		}
		/*
		Y.Aoki patch code.1996.07.13
		*/
        {
            unsigned char        swap_byte;

            swap_byte = in_byte >> 4;
            swap_byte |= in_byte << 4;
            in_byte = swap_byte;
        }        		
		/*
		End of Y.Aoki patch code.1996.07.13
		*/
		in_buffer |= (in_byte << in_bits);
		in_bits += 8;
	}
	*code = in_buffer & ((1 << bits) - 1);
	in_buffer >>= bits;
	in_bits -= bits;
	return (in_bits > 0);
}

enum {
	eAiff = 1,
	eWave,
	ePCM8
};


#ifdef USE_CCOMMAND		/* command-line reader for Macintosh */
#ifdef __MWERKS__
#include <SIOUX.h>		/* MW */
#include <console.h>	/* MW */
#endif
#ifdef THINK_C
#include <console.h>	/* THINK-C */
#endif
#endif

main(
	int			argc,
	char			**argv)
{
	short			sample;
	unsigned char	code;
	int				n;
	struct g72x_state	state;
	int				out_coding;
	int				out_size;
	int				snd_form;
	int				(*dec_routine)();
	int				dec_bits;
	int				errcode;
	dsc_data_info	dsc_info;

	g72x_init_state(&state);
	out_coding = AUDIO_ENCODING_ULAW;
	out_size = sizeof (char);
	dec_routine = g721_decoder;
	dec_bits = 4;


#ifdef USE_CCOMMAND		/* command-line reader for Macintosh */
	argc = ccommand(&argv);
#endif

	if (argc < 1) {
		usage();
		exit(1);
	}

	errcode = get_dsc_info(&dsc_info);
	if (errcode) {
		usage();
		exit(1);
	}
	errcode = seek_in(dsc_info.snd_data_start);
	if (errcode) {
		usage();
		exit(1);
	}
	
	switch (argv[1][1]) {
		case 'a':
		case 'A':
			out_coding = AUDIO_ENCODING_ALAW;
			break;
			
		case 'l':
		case 'L':
			out_coding = AUDIO_ENCODING_LINEAR;
			break;
			
		default:
			break;
	}		
	
	
	while (unpack_input_adpcm(&code, dec_bits) >= 0) {
		sample = (*dec_routine)(code, out_coding, &state);
		if (out_size == 2) {
			write_out(&sample, out_size, 1);
		} else {
			if (out_coding == AUDIO_ENCODING_LINEAR)
				code = (unsigned char)(sample >> 8);
			else
				code = (unsigned char)(sample);
				
			write_out(&code, out_size, 1);
		}
	}
	close_out();
}

