/*
 *	mergejag.c
 *
 * 	-------------------------------------------------------------------------
 *
 *	Utility for merging a video and audio data stream
 *	into a Jaguar Movie.
 *
 * 	-------------------------------------------------------------------------
 *
 *	Version: 1.0
 *	Date:	 3/07/96
 *	Prg.:	 Hans-Martin Krober
 *
 *	Copyright 1996 ATARI Corp.
 *	All rights reserved.
 *	This material is secret & confidential.
 *
 * 	------------------------------------------------------------------------- 
 *
 *	100	03/07/96 hmk	Initial Version.
 *	101	03/28/96 hmk	Added padding for audio data so it's long aligned
 */

 /*
  * ---	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);
void	mergeJag(void);
void	usage(void);
 /*
  * ---	Data --------------------------------------------------------------------
  */    

char	*version = "Version 1.01";

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

char	oname[256];			/* Output file name			*/
FILE	*ofp;			/* Output file handle			*/

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

char	vname[256];		/* Video file name			*/
FILE	*vfp;			/* Video file handle 			*/

FILM	video;			/* Jaguar Film Header 			*/
FILM	audio;

long	videoid;
long	audioid;

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


/*
 *	:main()
 */

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

	/*
	 * Check arguments
	 */
	if (argc != 2) {
		usage();
		exit(0);
	}
	/*	-------------------------------
	 *	Open Jaguar Video/Audio Streams
	 *	-------------------------------
	 *	Generate file names
	 */
	s = argv[1];
	if (strstr(s, ".flm")) {
		strcpy(vname, s);
		strcpy(aname, s);		
		s  = strrchr(aname, '.');
		*s = '\0';
		strcat(aname, ".csa");
	} else 	if (strstr(s, ".csa")) {
		strcpy(aname, s);
		strcpy(vname, s);		
		s  = strrchr(vname, '.');
		*s = '\0';
		strcat(vname, ".flm");
	} else  {
		strcpy(aname, s);
		strcpy(vname, s);
		strcat(aname, ".csa");		
		strcat(vname, ".flm");
	}

	/*
	 * 	Open video stream
	 */
	if ((vfp = fopen(vname, "rb")) == NULL) {
		perror(vname);
		exit(-1);
	}
	/*
	 *	Open audio stream
	 */
	if ((afp = fopen(aname, "rb")) == NULL) {
		perror(aname);
		exit(-1);
	}	
	/*
	 *	Read Film Header
	 */
	if (fread(&audioid, sizeof(long), 1, afp) != 1) {
		perror(aname);
		exit(-1);
	}
	if (endian_swap_long(audioid) != CSXA_ID) {
		printf("%s: %s is not a \'CSXA\' compressed audio stream.\n", name, aname);
		exit(0);
	}	

	
	if (fread(&video, sizeof(FILM), 1, vfp) != 1) {
		perror(vname);
		exit(-1);
	}
	if (endian_swap_long(video.frameHeader.id) != FILM_ID) {
		printf("%s: %s is not in jaguar movie format.\n", name, vname);
		exit(-2);
	}

	if (fread(&audio, sizeof(FILM), 1, afp) != 1) {
		perror(aname);
		exit(-1);
	}
	if (endian_swap_long(audio.frameHeader.id) != FILM_ID) {
		printf("%s: %s is not in jaguar movie format.\n", name, aname);
		exit(-2);
	}	


	
	if (endian_swap_long(audio.audioDescriptionAtom.id) != ADSC_ID) {
		printf("%s: %s has not audio description atom.\n", name, aname);
		exit(-2);
	}
	/*
	 * Do the actual file splitting...
	 */
	mergeJag();

	fclose(afp);
	fclose(vfp);	
	
	exit(0);
}

/*
 *	Merge a '.FLM' file and a '.CSA' file.
 *	YOU CANNOT MERGE WITH A '.VID' file since it's missing information!
 *
 *	This opens the '.FLM' file and '.CSA' file, builds a new sample table based
 *	on the CSA audio data from the '.CSA' file and replaces the existing audio
 *	data of the '.FLM' file with the CSX audio data from the '.CSA' file. The
 *	new file will have the '.JAX' extention.
 *
 *	NOTE: Sections have to be long alligned. In order to achive that the CSX
 *	audio data will be preceeded by a long word containing the actual size of
 *	the CSX audio data. THe data will then be long padded and that new size
 *	([size_header = 4 bytes] + [CSX Audio Data Size] + [Padding Amount]) will
 *	be entered into the sample table.
 */
 
	void
mergeJag()
{
	long	i, n;
	long	a_n_video, a_n_audio;
	long	v_n_video, v_n_audio;	
	long	new_offset, new_size;
	long	vidEndSampleTableArray;
	long	audEndSampleTableArray;	
	long	size;
	long 	count;
	long	l;
	STAR	*p, *a, *v;
	STAR	*newSampleTableArray, *audSampleTableArray, *vidSampleTableArray;
	char	*s, *bufp;
	
	/*
	 * Get size in bytes of Sample Table Array og Jaguar Movie
	 * and allocate memory for it
	 */
	size = endian_swap_long(video.sampleTableAtom.count) * sizeof(STAR);
	if ((vidSampleTableArray = (STAR *) malloc(size)) == NULL) {
		fprintf(stderr, "%s: Out of memory (1).\n", name);
		exit(-3);
	}

	/*
	 * Read Sample Table Array of '.FLM' file into buffer
	 */
	if (fread(vidSampleTableArray, size, 1, vfp) != 1) {
		perror(vname);
		exit(-3);
	}

	/*
	 * Get size in bytes of Sample Table Array of Audio File
	 * and allocate memory for it
	 */
	size = endian_swap_long(audio.sampleTableAtom.count) * sizeof(STAR);
	if ((audSampleTableArray = (STAR *) malloc(size)) == NULL) {
		fprintf(stderr, "%s: Out of memory (1).\n", name);
		exit(-3);
	}

	/*
	 * Read Sample Table Array of '.CSA' into buffer
	 */
	if (fread(audSampleTableArray, size, 1, afp) != 1) {
		perror(aname);
		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.
	 */
	vidEndSampleTableArray = ftell(vfp);
	audEndSampleTableArray = ftell(afp);	

	/* -----------------------------------------------------------------------------
	 * Find out how many audio/video samples we have and then build new array tables
	 * -----------------------------------------------------------------------------
	 */

	/*
	 *	First, look in the Jaguar Movie (Video) File (.FLM)
	 */
	count = endian_swap_long(video.sampleTableAtom.count);
	p = vidSampleTableArray;
	v_n_audio = 0;
	for (i = 0; i < count; i++, p++) {
		if ((endian_swap_long(p->start) & 0x7fffffffL) == 0x7fffffffL) {
			v_n_audio++;
		}
	}
	if (v_n_audio == 0) {
		printf("%s: No audio sections found in %s.\n", name, vname);
		return;
	}
	v_n_video = count - v_n_audio;

	/*
	 *	Next, check the compressed Audio File
	 */
	count = endian_swap_long(audio.sampleTableAtom.count);
	p = audSampleTableArray;
	a_n_audio = 0;
	for (i = 0; i < count; i++, p++) {
		if ((endian_swap_long(p->start) & 0x7fffffffL) == 0x7fffffffL) {
			a_n_audio++;
		}
	}
	if (a_n_audio == 0) {
		printf("%s: No audio sections found in %s.\n", name, aname);
		return;
	}

	/*
	 *	Make sure bot files have the same number of audio sections
	 */
	if (a_n_audio != v_n_audio) {
		printf("%s: different number of audio sections found in %s and %s.\n",
			vname, aname);
		return;
	}

	/*
	 *	Allocate memory for new sample table.
	 */
	count = v_n_audio + v_n_video;
	if ((newSampleTableArray = malloc(count * sizeof(STAR))) == NULL) {
		fprintf(stderr, "%s: Out of memory (2).\n", name);
		exit(-3);
	}

	/*
	 *	BUILD NEW SAMPLE TABLE
	 */
	p = newSampleTableArray;
	v = vidSampleTableArray;
	a = audSampleTableArray;
	new_offset = 0L; 
	new_size   = 0L;
	for (i = 0; i < count; i++, p++, v++) {
		if ((endian_swap_long(v->start) & 0x7fffffffL) == 0x7fffffffL) {
			/*
			 * Build audio sample table array
			 */
			p->offset   = endian_swap_long(new_offset + new_size);
									/* patch start */

			l = endian_swap_long(a->size) + sizeof(long);
							     /* + 4 for the size field */
			if ((l % 4) != 0)
				l += (4 - (l % 4));	 	  /* DO LONG PADDING!! */

			p->size     = endian_swap_long(l);
			p->start    = a->start;
			p->duration = a->duration; 
			new_size   += l;
			a++;
		} else {
			/*
			 * Build video sample table array
			 */			
			p->offset   = endian_swap_long(new_offset + new_size); /* patch start */
			p->size     = v->size;
			p->start    = v->start;
			p->duration = v->duration; 
			new_size   += endian_swap_long(v->size);
		}
	}
		
	/*
	 * Build output name and open file for binary writing
	 */
	strcpy(oname, vname);

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


	if ((ofp = fopen(oname, "wb")) == NULL) {
		perror(oname);
		exit(-3);
	}

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

	video.audioDescriptionAtom.dataDescription =
	 audio.audioDescriptionAtom.dataDescription =
	 /* HACK */ endian_swap_long(0x0BL); /* 16 bit stereo scsx */

	if (fwrite(&video, sizeof(FILM), 1, ofp) != 1) {
		perror(oname);
		exit(-3);
	}
	
	if (fwrite(newSampleTableArray, sizeof(STAR), count, ofp) != count) {
			perror(vname);
			exit(-3);
	}

	/*
	 * Do the actual job, read the video section from the orginal and read the
	 * audio section from the file with the compressed audio and then
	 * write them into one merged file
	 */

	p = newSampleTableArray;
	v = vidSampleTableArray;
	a = audSampleTableArray;
	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);
		}
		/*
		 * Audio or Video?
		 */
		if ((endian_swap_long(p->start) & 0x7fffffffL) == 0x7fffffffL) {
			/*
			 * Audio Data
			 */

			/*
			 * Go to the right spot
			 */
			fseek(afp, audEndSampleTableArray + endian_swap_long(a->offset), 0);

			/*
			 * Read data
			 */
			if (fread(bufp, endian_swap_long(a->size), 1, afp) != 1) {
				perror(aname);
				exit(-3);
			}
			/*
			 * Write actual (not padded!!!) size of audio data in front of
			 * the actual data!!!!
			 */
			if (fwrite(&a->size, sizeof(long), 1, ofp) != 1) {
				perror(oname);
				exit(-3);
			}
			size -= sizeof(long);	/* since we already wrote four bytes */

			a++;
			v++;	/* !!! */
		} else {
			/*
			 * Video Data
			 */
			/*
			 * Go to the right spot
			 */
			fseek(vfp, vidEndSampleTableArray + endian_swap_long(v->offset), 0);

			/*
			 * Read data
			 */
			if (fread(bufp, size, 1, vfp) != 1) {
				perror(vname);
				exit(-3);
			}
			
			v++;
		}
		/*
		 *	Write data
		 */
		if (fwrite(bufp, size, 1, ofp) != 1) {
			perror(oname);
			exit(-3);
		}		
		free(bufp);	
	}
	free(audSampleTableArray);
	free(vidSampleTableArray);
	free(newSampleTableArray);
	fclose(ofp);
}


	void
usage()
{
	printf("Usage: %s FileName\n", name);
	printf("%s\n", version);
	printf("Mergejag reads a \'.FLM\' and a \'.CSA\' file and merges them together.\n");
	printf("The audio chunks in the '.FLM' file will be replaced by the CSX\n");
	printf("audio from the \'.CSA\' file.\n");
	printf("The input file name can have no extention, \'.CSA\' or \'.FLM\' extentions.\n");
	printf("The base name has to be the same. The output will be a '.JAX' file.\n");
	printf("For example:\n");
	printf("\t\"lout006.flm\" and \"lout006.csa\" shall be merged:\n");
	printf("\t\"mergejag lout006.flm\" or \"mergejag lout006.csa\" or\n");
	printf("\t\"mergejag lout006\"\n");
	printf("\tThis will produce a Jaguar Film called \"lout006.jax\" which\n");
	printf("\tcontains CSX audio.\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 );
}

