/*
 *	csxjag.c
 *
 * 	-------------------------------------------------------------------------
 *
 *	Utility for compressing pieces of GameFilm audio using the CSX method.
 *	This program uses Len Ken's CSX compression code. Hacked his main
 *	function so that it can be called from this shell. His compressor is fed
 *	by memory blocks now rather than reading from disk. The memory blocks are
 *	provided by this shell.
 *
 * 	-------------------------------------------------------------------------
 *
 *	Version: 1.01
 *	Date:	 3/4/96
 *	Prg.:	 Hans-Martin Krober
 *
 *	Copyright 1996 ATARI Corp.
 *	All rights reserved.
 *	This material is secret & confidential.
 *
 * 	------------------------------------------------------------------------- 
 *
 *	100	03/04/96 hmk	Initial Version.
 *	101	03/08/96 hmk	Output header reflects compression mode in Audio
 *				Data Decription.
 */

 /*
  * ---	Include Files -----------------------------------------------------------
  */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "jagfilm.h"
#include "hdef.h"	/* Len's defs */

 /*
  * ---	Externals ---------------------------------------------------------------
  */

extern int optind;	/* Option Index		*/
  
 /*
  * ---	Prototypes --------------------------------------------------------------
  */

long  endian_swap_long( long );
short endian_swap_word( short );
void  CsxAudio(void);
void  usage(void);

 /*
  * ---	Data Structures ---------------------------------------------------------
  */    

#define bSTEREO		0
#define	b16BIT		1
#define bCOMPR		2

#define AUDIO_STEREO 	 (1 << bSTEREO)
#define AUDIO_16BIT	 (1 << b16BIT)
#define AUDIO_COMPRESSED (1 << bCOMPR)

#define	AUDIO_FORMAT	(AUDIO_16BIT | AUDIO_STEREO)
#define STEREO_16BIT	(AUDIO_16BIT | AUDIO_STEREO)
  
typedef struct _audioFormat {
	int	stereo;			/* 1 = stereo, 0 = mono 	*/
	int	bit16;			/* 1 = 16 bit, 0 = 8 bit	*/
	int	compressed;		/* 1 = compr., 0 = uncompressed */
} AFRMT;

 /*
  * ---	Global Data -------------------------------------------------------------
  */    

 /*
  * Input Buffer for data read from disk for the CSX compressor
  */
short	*iobuf;			/* Begin of i/o buffer 				*/
short	*iobufp;		/* Pointer to current position in buffer	*/
long	iobuf_size;		/* Size of the i/o buffer			*/

 /*
  * ---	Data --------------------------------------------------------------------
  */    

char	version[] = "Version 1.01";

char	*name;			/* Program's name			*/

char	*iname;			/* Input file name			*/
FILE	*ifp;			/* Input file handle			*/

char	aname[256];		/* Ext Audio file name			*/
FILE	*afp;			/* Ext Audio file handle 		*/


FILM	film;			/* Jaguar Film Header 			*/
AFRMT	audioFormat;		/* Audio Format Description		*/

/*
 * 	CSX configuration stuff
 */

static short stereomode;	/* stereo = 2, mono = 1			*/
static short compmode;		/* compression mode, CSXB or CSXC	*/
static short threshold;		/* threshold for compression algorithm	*/
static short intelmode;		/* mode for intel byte order		*/

 /*
  * ---	Functions ---------------------------------------------------------------
  */    


/*
 *	:main()
 */

	void
main(argc, argv)
	int	argc;
	char	**argv;
{
	char	c;
	long	id;
	long	audioDataDescription;
	
	/*
	 * Get program's name
	 */
	name = argv[0];


	if (argc < 2) {
		usage();
		exit(0);
	}
	
	/*
	 * 	Init CSX variables
	 */	 
	compmode   = HCSXC;
	threshold  = 11; 
	stereomode = -1;		/* use audio header in file */
	intelmode  = 0;
	
	/*
	 * Check arguments
	 */

	while ((c = getopt(argc, argv, "vsmli123456789abcdef")) != (char) -1) {
		switch (c) {
		case 'v':
			printf("CSX GameFilm Audio Compressor, %s\n", version);
			break;
		case 's':			/* s - stereo */
			stereomode = 2;
			break;
		case 'm':			/* m - mono */
			stereomode = 1;
			break;

		case 'l':			/* l - lossless */
			compmode = HCSXB;		
			break;
		case 'i':			/* i - intel byte format */
			intelmode = 1;
			break;
		case  '1':			/* 1-f: threshold */
		case  '2':
		case  '3':
		case  '4':
		case  '5':
		case  '6':
		case  '7':
		case  '8':
		case  '9':
		case  'a':
		case  'b':
		case  'c':
		case  'd':
		case  'e':
		case  'f':
			if (c < 'a')
				threshold = (1 << ((c - '0'))) - 1;
			else
 				threshold = (1 << ((c - 'a' + 10))) - 1;
 			break;
 		default:
 			usage();
 			break;
 		}
	}

	if (optind >= argc) {
		usage();
		exit(0);
	}
	
	/*
	 *	Open Jaguar Movie Audio File
	 */
	iname = argv[optind];
	if ((ifp = fopen(iname, "rb")) == NULL) {
		perror(iname);
		exit(-1);
	}
	/*
	 *	Read Audio ID 
	 */
	if (fread(&id, sizeof(long), 1, ifp) != 1) {
		perror(iname);
		exit(-1);
	}
	if (endian_swap_long(id) != XAUDIO_ID) {
		printf("%s: %s is not an extended jaguar movie audio file.\n", name, iname);
		exit(-2);
	}	
	/*
	 *	Read Film Header
	 */
	if (fread(&film, sizeof(FILM), 1, ifp) != 1) {
		perror(iname);
		exit(-1);
	}
	if (endian_swap_long(film.frameHeader.id) != FILM_ID) {
		printf("%s: %s is not a jaguar movie.\n", name, iname);
		exit(-2);
	}
	if (endian_swap_long(film.audioDescriptionAtom.id) != ADSC_ID) {
		printf("%s: %s has not audio description atom.\n", name, iname);
		exit(-2);
	}
	/*
	 *	Check audio format
	 */
	audioDataDescription = endian_swap_long(film.audioDescriptionAtom.dataDescription);
	if (((audioDataDescription & AUDIO_FORMAT) == STEREO_16BIT) &&
	((audioDataDescription & AUDIO_COMPRESSED) == 1)) {
		printf("%s: %s is already 16 Bit compressed stereo.\n", name, iname);
		exit(0);
	}
	/*
	 *	get audio format settings
	 */
	if (audioDataDescription & AUDIO_STEREO)
		audioFormat.stereo = 1;
	else
		audioFormat.stereo = 0;

	if (audioDataDescription & AUDIO_16BIT)
		audioFormat.bit16 = 1;
	else
		audioFormat.bit16 = 0;
		
	if (audioDataDescription & AUDIO_COMPRESSED) 
		audioFormat.compressed = 1;
	else
		audioFormat.compressed = 0;

	if (stereomode == -1) /* if not set on command line, set it to
			       * what's in the header
			       */
		stereomode = audioFormat.stereo + 1;
	/*
	 * Do the actual compressing of audio...
	 */
	 
	if (audioFormat.compressed) {
		printf("%s: Audio is already compressed.\n", iname);
		exit(0);
	}
	audioFormat.compressed = 2;	 /* It's going to be CSX compressed */
	CsxAudio();

	fclose(ifp);
	
	exit(0);
}

/*
 * 	Compress the audio portion of a Jaguar Movie Audio file
 */

	void
CsxAudio()
{
	long	i, k, n;
	long	n_audio;
	long	audio_offset, audio_size;
	long	ext_size;
	long	orgEndSampleTableArray;
	long	sampleTableArrayPos;
	long	sampleTableArraySize;	
	long	size;
	long 	count;
	long	step;
	STAR	*p, *a;
	STAR	*orgSampleTableArray, *audSampleTableArray, *vidSampleTableArray;
	char	*s, *bufp;
	
	/*
	 * Get size in bytes of Sample Table Array and allocate memory for it
	 */
	size = endian_swap_long(film.sampleTableAtom.count) * sizeof(STAR);
	if ((orgSampleTableArray = (STAR *) malloc(size)) == NULL) {
		fprintf(stderr, "%s: Out of memory (1).\n", name);
		exit(-3);
	}
	/*
	 * 	Get the Sample Table Array File Position and its Size
	 */
	sampleTableArrayPos  = ftell(ifp);
	sampleTableArraySize = size;
	/*
	 * Read Sample Table Array into buffer
	 */
	if (fread(orgSampleTableArray, size, 1, ifp) != 1) {
			perror(iname);
			exit(-3);
	}
	/*
	 * Get file position of end of Sample Table Array.
	 * This will be used as offset, since all entries in the array are based
	 * on the end of the array.
	 */
	orgEndSampleTableArray = ftell(ifp);

	/*
	 * Find out how many audio samples we have 
	 */
	count = endian_swap_long(film.sampleTableAtom.count);
	p = orgSampleTableArray;
	n_audio = 0;
	for (i = 0; i < count; i++, p++) {
		if ((endian_swap_long(p->start) & 0x7fffffffL) == 0x7fffffffL) {
			n_audio++;
		} else {
			printf("%s: %s contains non audio samples as well.\n", name, iname);
		}
	}
	if (n_audio == 0) {
		printf("%s: No audio sections found.\n", name);
		return;
	}

	if ((audSampleTableArray = malloc(size)) == NULL) {
		fprintf(stderr, "%s: Out of memory (2).\n", name);
		exit(-3);
	}
	p = orgSampleTableArray;
	a = audSampleTableArray;

	/*
	 * Copy original Sample Table Array
	 */
	memcpy(a, p, size);
	
	/*
	 * Build output name and open file for binary writing
	 */
	strcpy(aname, iname);

	if ((s = strrchr(aname, '.')) != NULL)
		*s = '\0';
	strcat(aname, ".csa");

	if ((afp = fopen(aname, "wb")) == NULL) {
		perror(aname);

		exit(-3);
	}	

	/*
	 * Write tags first
	 */

	if (fwrite("CSXA", 4, 1, afp) != 1) {
		perror(aname);
		exit(-3);
	}	

	/*
	 * Now write all data structures from the original.
	 * We write the sample table array only because we want to
	 * reserve space for it. At the end when it's updated with
	 * the new sizes and offsets it gets written again.
	 */
	 
	film.audioDescriptionAtom.dataDescription &= ~0xFC; /* clear compression code */
	film.audioDescriptionAtom.dataDescription |= (2 << 2);	/* CSX Compression    */
	
	if (fwrite(&film, sizeof(FILM), 1, afp) != 1) {
		perror(aname);
		exit(-3);
	}


	if (fwrite(audSampleTableArray, size, 1, afp) != 1) {
		perror(aname);
		exit(-3);
	}


	/*
	 * Do the actual job, read the audio sections, compress them and
	 * write them back to the new file
	 */

	p = orgSampleTableArray;
	a = audSampleTableArray;
	audio_offset = 0L;

	for (i = 0; i < count; i++, p++, a++) {
		size = endian_swap_long(p->size);
		if ((bufp = malloc(size)) == NULL) {
			fprintf(stderr, "%s: Out of memory (4).\n", name);
			exit(-3);
		}

		/*
		 * Go to the right spot
		 */
		fseek(ifp, orgEndSampleTableArray + endian_swap_long(p->offset), 0);
		/* printf("X POS=%d\n", ftell(ifp)); */

		/*
		 * Read data
		 */
		if (fread(bufp, size, 1, ifp) != 1) {
			perror(iname);
			exit(-3);
		}
/*
 *  FOR DEBUGGING ONLY!!!!!!!!!!!!!!!!!!!!!!!!!!
 */
#if 0
		if (i==1) {
			FILE *fp;
			fp = fopen("atst.raw", "wb");
			fwrite(bufp, size, 1, fp);
			fclose(fp);
		}
#endif
		/*
		 * Set up I/O buffer information for the compressor
		 */
		iobuf 	   = (short*) bufp;		/* Beginning of buffer	*/
		iobufp	   = (short*) bufp;
		iobuf_size = size / sizeof(short);	/* In Words 		*/
		
		/*
		 * 	Do Audio CSX Compression
		 */
		audio_size   = do_csx(afp, stereomode, compmode, threshold, intelmode);

		/*
		 * 	Update Sample Array
		 */
		a->size      = endian_swap_long(audio_size);
		a->offset    = endian_swap_long(audio_offset);
		audio_offset += audio_size;	

		
		free(bufp);
	}

	/*
	 * Finally, go back and write the updated Sample Table Array
	 * 
	 */
	
	fseek(afp, sampleTableArrayPos, 0);


	if (fwrite(audSampleTableArray, sampleTableArraySize, 1, afp) != 1) {
		perror(aname);
		exit(-3);
	}

	
	free(orgSampleTableArray);
	free(audSampleTableArray);	
	fclose(afp);
}



	void
usage()
{
	printf("Usage: %s [-vsmli123456789abcdef] JaguarXADfileName\n", name);
	printf("\t%s\n", version);
	printf("\tCompresses GameFilm audio pieces.\n");
	printf("\tThe output will be a \'.CSA\' file.\n");
	printf("\tOptions are:\n");
	printf("\t\t-v\tPrint version number\n");
	printf("\t\t-s\tInput file is stereo (option overrides descr. in file)\n");
	printf("\t\t-m\tInput file is mono   (option overrides descr. in file)\n");
	printf("\t\t-l\tLoseless compression\n");
	printf("\t\t-i\tIntel Byte Order (default is Motorola/Big Endian)\n");
	printf("\t\t-1..f\tSpecifies Threshold (2**x - 1, default is 4)\n");
	
	
}


long endian_swap_long( long l )
{
unsigned long a, b, c, d;

/* 'abcd' becomes 'dcba' pr vice versa */

	a = (l >> 24L) & 0x000000ffL;
	b = (l >> 8L) & 0x0000ff00L;
	c = (l << 8L) & 0x00ff0000L;
	d = (l << 24L) & 0xff000000L;
	return( a | b | c | d );
}

short endian_swap_word( short i )
{
unsigned short a, b;

	a = (i >> 8) & 0x00ff;
	b = (i << 8) & 0xff00;
	return( a | b );
}




