/*
 *	sndext.c
 *
 * 	-------------------------------------------------------------------------
 *
 *	Utility for extending the Audio portion of a Jaguar Movie 
 *	(8 bit mono to 16 bit stereo).
 *
 * 	-------------------------------------------------------------------------
 *
 *	Version: 1.0
 *	Date:	 2/21/96
 *	Prg.:	 Hans-Martin Krober
 *
 *	Copyright 1996 ATARI Corp.
 *	All rights reserved.
 *	This material is secret & confidential.
 *
 * 	------------------------------------------------------------------------- 
 *
 *	100	02/21/96 hmk	Initial Version.
 *
 */

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

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

 /*
  * ---	Prototypes --------------------------------------------------------------
  */
long  endian_swap_long( long );
short endian_swap_word( short );

 /*
  * ---	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;

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

char	*version = "Version 1.0";

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		*/
 /*
  * ---	Functions ---------------------------------------------------------------
  */    


/*
 *	:main()
 */

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

	/*
	 * Check arguments
	 */
	if (argc != 2) {
		usage();
		exit(0);
	}
	/*
	 *	Open Jaguar Movie Audio File
	 */
	iname = argv[1];
	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) != AUDIO_ID) {
		printf("%s: %s is not a 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) == 0)) {
		printf("%s: %s is already 16 Bit uncompressed 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;
		
	/*
	 * Do the actual extending of audio...
	 */
	ExtendAudio();

	fclose(ifp);
	
	exit(0);
}

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

	void
ExtendAudio()
{
	long	i, k, n;
	long	n_audio;
	long	audio_offset, audio_size;
	long	ext_size;
	long	orgEndSampleTableArray;
	long	size;
	long 	count;
	long	step;
	short	*newP, *wordP;
	char	*byteP;
	STAR	*p, *a;
	STAR	*orgSampleTableArray, *audSampleTableArray, *vidSampleTableArray;
	char	*s, *bufp, *newbufp;
	
	/*
	 * 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);
	}

	/*
	 * 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/video samples we have and then build new array tables
	 */
	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(n_audio * sizeof(STAR))) == NULL) {
		fprintf(stderr, "%s: Out of memory (2).\n", name);
		exit(-3);
	}
	p = orgSampleTableArray;
	a = audSampleTableArray;
	audio_offset = 0L; 
	audio_size = 0L;

	/*
	 *	Calculate factor by which audio size will increase
	 */
	ext_size = 1;
	if (audioFormat.stereo)
		ext_size *= 1;
	else
		ext_size *= 2;
	if (audioFormat.bit16)
		ext_size *= 1;
	else
		ext_size *= 2;
	if (audioFormat.compressed)
		ext_size *= 2;
	else
		ext_size *= 1;		

	/*
	 *	Now build the new table with newly calculated sample sizes
	 */
	for (i = 0; i < count; i++, p++) {
		if ((endian_swap_long(p->start) & 0x7fffffffL) == 0x7fffffffL) {
			/*
			 * Build audio sample table array
			 */
			a->offset   = endian_swap_long(audio_offset + audio_size); /* patch start */
			n           = endian_swap_long(p->size) * ext_size;
			a->size     = endian_swap_long(n);
			a->start    = p->start;
			a->duration = p->duration; 
			audio_size += n;
			a++;
		} 
	}
		
	/*
	 * Build output names and open files for binary writing
	 */
	strcpy(aname, iname);

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

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

		exit(-3);
	}	

	/*
	 * Write tags first
	 */

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

	/*
	 * Now write all data structures from the original
	 * plus the new audio sample table arrays
	 */

	n = film.sampleTableAtom.count;	      /* Backup original number */
	film.sampleTableAtom.size  = endian_swap_long(sizeof(STAB) + n_audio * sizeof(STAR)); 	
	film.frameHeader.size =
	 endian_swap_long(sizeof(FRAME) + sizeof(FDSC) + sizeof(ADSC) +
		sizeof(STAB) + n_audio * sizeof(STAR));
	film.sampleTableAtom.count = endian_swap_long(n_audio); /* HACK: patch in number of audio samples */	

	film.audioDescriptionAtom.dataDescription = endian_swap_long(STEREO_16BIT);

	if (fwrite(&film, sizeof(FILM), 1, afp) != 1) {
		perror(aname);
		exit(-3);
	}
	film.sampleTableAtom.count = n; /* Restore original number */


	if (fwrite(audSampleTableArray, sizeof(STAR), n_audio, afp) != n_audio) {
			perror(aname);
			exit(-3);
	}

	free(audSampleTableArray);
	/*
	 * Do the actual job, read the audio sections, extend them and
	 * write them back to the new file
	 */

	p = orgSampleTableArray;
	for (i = 0; i < count; i++, p++) {
		size = endian_swap_long(p->size);
		if ((bufp = malloc(size)) == NULL) {
			fprintf(stderr, "%s: Out of memory (4).\n", name);
			exit(-3);
		}
		if ((newbufp = malloc(size * ext_size)) == NULL) {
			fprintf(stderr, "%s: Out of memory (5).\n", name);
			exit(-3);
		}
		/*
		 * Go to the right spot
		 */

		fseek(ifp, orgEndSampleTableArray + endian_swap_long(p->offset), 0);
		
		/*
		 * Read data
		 */
		if ((k=fread(bufp, size, 1, ifp)) != 1) {
			perror(iname);
			exit(-3);
		}

		/*
		 * 	Audio Extension
		 */
		newP = (short *) newbufp;
		if (audioFormat.compressed | (!audioFormat.bit16)) {
			byteP = bufp;
			step = 1;
		} else {
			wordP = (short *) bufp;
			step = 2;
		}
		for (k = 0; k < size; k += step) {
			if (audioFormat.compressed)
				*newP = (short) *byteP * (short) *byteP++;
			else if (!audioFormat.bit16)
				*newP = (short)(*byteP++ << 8);
			else
				*newP = *wordP++;
			*newP = endian_swap_word(*newP);
			if (!audioFormat.stereo) {
				*(newP+1) = *newP;
				newP++;				
			}
			newP++;
		}
		/*
		 * Write Extended Audio Data
		*/
		if (fwrite(newbufp, size * ext_size, 1, afp) != 1) {
			perror(aname);
			exit(-3);
		}

		free(bufp);
		free(newbufp);			
	}
	free(orgSampleTableArray);			
	fclose(afp);
}



	void
usage()
{
	printf("Usage: %s JaguarMovieFileName\n", name);
	printf("\t%s\n", version);
	printf("\tSplits a Jaguar movie into an audio and a video section.\n");
	printf("\tThe output will be a \'.AUD\' and a \'.VID\' file.\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 );
}




