#include <exec/exec.h>

#include <intuition/intuition.h>

#include <libraries/asl.h>
#include <libraries/iffparse.h>

#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/asl.h>
#include <proto/dos.h>

#include <string.h>

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stddef.h>
#include <string.h>

#include <clib/macros.h>

int	ulaw2linear(unsigned char);
void	maketable(signed char *, int);
int	getscale(void);
void	CloseAll(void);

/*----------------------------------------------------------------------*/
/*			      IFF 8SVX Stuff				*/
/*----------------------------------------------------------------------*/

#define BUFSIZE	64000
#define ID_8SVX	MAKE_ID('8','S','V','X')
#define ID_VHDR	MAKE_ID('V','H','D','R')
#define ID_BODY	MAKE_ID('B','O','D','Y')

typedef LONG Fixed;	
	/* A fixed-point value, 16 bits to the left of the point and 16 */
	/* to the right. A Fixed is a number of 216ths, i.e. 65536ths.	*/

#define Unity 0x10000L		/* Unity = Fixed 1.0 = maximum volume	*/

/* sCompression: Choice of compression algorithm applied to the samples */ 

#define sCmpNone       0	/* not compressed			*/
#define sCmpFibDelta   1	/* Fibonacci-delta encoding		*/ 

				/* Can be more kinds in the future.	*/

typedef struct {
	ULONG oneShotHiSamples,	/* # samples in the high octave 1-shot part 	*/
              repeatHiSamples,	/* # samples in the high octave repeat part 	*/
              samplesPerHiCycle;/* # samples/cycle in high octave, else 0   	*/
	UWORD samplesPerSec;	/* data sampling rate				*/
	UBYTE ctOctave,		/* # octaves of waveforms			*/
      	      sCompression;	/* data compression technique used		*/
	Fixed volume;		/* playback volume from 0 to Unity (full	*/
				/* volume). Map this value into the output	*/
				/* hardware's dynamic range.			*/
	} Voice8Header;

Voice8Header V8H = { 0, 0, 32, 8363, 1, sCmpNone, Unity };

typedef ULONG ID;

typedef struct {
	ID	ckID;
	LONG	ckSize;			/* sizeof(ckData) */
	} Chunk;

Chunk Form, V8Hdr, Body;

/********************************************************************************/

BPTR			fpi, fpo;
int			size;
unsigned char		*buf;
struct	IntuitionBase	*IntuitionBase;
struct	Library		*AslBase;
struct	FileRequester	*FR;

struct EasyStruct ES1 = {
	sizeof(struct EasyStruct),
	0,
	"Error",
	"Can't %s",
	"EXIT"
};

/*--------------------------------------------------------------------------*/
/* The following routine was extracted from posting by Brian Foley.         */
/* Brian Foley		email: bfoley@greatlakes.Central.Sun.COM            */
/* Systems Engineer	smail:	1000 Town Center                            */
/* Sun Microsystems		Suite 1700                                  */
/* GreatLakes Region		Southfield, MI 48075   (313) 352-7070       */
/*--------------------------------------------------------------------------*/

int ulaw2linear(unsigned char ulawbyte)
{
	static int exp_lut[8] = { 0, 132, 396, 924, 1980, 4092, 8316, 16764 };
	       int sign, exponent, mantissa, sample;

	ulawbyte	= ~ulawbyte;
	sign		=  ulawbyte & 0x80;
	exponent	= (ulawbyte >> 4) & 0x07;
	mantissa	=  ulawbyte & 0x0F;
	sample		= exp_lut[exponent] + (mantissa << (exponent + 3));
	if ( sign ) sample = -sample;
	return sample;
}

int getscale(void)
{
	int count, max = 0, i;

	size	= 0;

	do {
		count	 = Read(fpi, buf, BUFSIZE);
		size	+= count;
		for ( i = 0; i < count; i++ ) {
			max	= MAX(ABS(ulaw2linear(buf[i])), max);
		}
	} while ( count == BUFSIZE );

	return max;
}

void maketable(signed char *logs, int max)
{
	int i, c, d;

	for ( i = 0; i < 256; i++ ) {
		c = ( ulaw2linear(i) * ulaw2linear(0) ) / max;
		d = ABS(c) & 0xFF;
		if ( d > 0x7F ) {
			if ( c > 0 ) {
				logs[i] = (signed char) ( c / 256 + 1 );
			}
			else {
				logs[i] = (signed char) ( c / 256 - 1 );
			}
		}
		else {
			logs[i] = (signed char) ( c / 256 );
		}
	}
}


void CloseAll(void)
{
	if ( FR  )	FreeAslRequest(FR);
	if ( buf )	FreeMem(buf, BUFSIZE);
	if ( fpi )	Close(fpi);
	if ( fpo )	Close(fpo);

	if ( IntuitionBase ) {
		DisplayBeep(NULL);
		CloseLibrary((struct Library *) IntuitionBase);
	}

	if ( AslBase )	CloseLibrary((struct Library *) AslBase);
	exit(0);
}

void main(int argc, char **argv)
{
	int		i, count, max;
	char		ifile[80], ofile[80];
	signed char	logs[256];
	
	IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library", 37);
	if ( ! IntuitionBase ) exit(10);

	AslBase = OpenLibrary(AslName, 0);
	if ( ! AslBase ) CloseAll();

	if ( argc == 0 ) {
		FR = AllocFileRequest();
		if ( ! FR ) {
			CloseAll();
		}

		if ( AslRequestTags(FR, ASL_Hail, "Input File", TAG_DONE) ) {
			strmfp(ifile, FR->rf_Dir, FR->rf_File);
		}
		else CloseAll();

		if ( AslRequestTags(FR, ASL_Hail, "Output File", TAG_DONE) ) {
			strmfp(ofile, FR->rf_Dir, FR->rf_File);
		}
		else CloseAll();
	}
	else if ( argc != 3 ) {
		FPuts(Output(), "Usage: ");
		FPuts(Output(), argv[0]);
		FPuts(Output(), " infile outfile\n"
				"Converts from SPARC ULAW to RAW sound format\n");
		exit(0);
	}
	else {
		strcpy(ifile, argv[1]);
		strcpy(ofile, argv[2]);
	}

	fpi = Open(ifile, MODE_OLDFILE);
	if ( ! fpi ) {
		EasyRequest(NULL, &ES1, NULL, "open infile");
		CloseAll();
	}

	fpo = Open(ofile, MODE_NEWFILE);
	if ( ! fpo ) {
		EasyRequest(NULL, &ES1, NULL, "open outfile");
		CloseAll();
	}

	buf = (UBYTE *) AllocMem(BUFSIZE, MEMF_PUBLIC);
	if ( ! buf ) {
		EasyRequest(NULL, &ES1, NULL, "alloc buffer");
		CloseAll();
	}

	max = getscale();
	Seek(fpi, 0, OFFSET_BEGINNING);
	maketable(logs, max);

	Form.ckID	= ID_FORM;
	Form.ckSize	= 4 + sizeof(Chunk) * 2 + sizeof(Voice8Header) + size;
	Write(fpo, &Form, 8);

	i = ID_8SVX;
	Write(fpo, &i, 4);

	V8Hdr.ckID		= ID_VHDR;
	V8Hdr.ckSize		= sizeof(Voice8Header);
	Write(fpo, &V8Hdr, 8);

	V8H.oneShotHiSamples	= size;
	Write(fpo, &V8H, sizeof(Voice8Header));

	Body.ckID		= ID_BODY;
	Body.ckSize		= size;
	Write(fpo, &Body, 8);

	do {
		count = Read(fpi, buf, BUFSIZE);
		for ( i = 0; i < count; i++ ) {
			buf[i] = logs[buf[i]];
		}
		if ( count ) Write(fpo, buf, count);
	} while ( count == BUFSIZE );

	CloseAll();
}
