/*
 * September 25, 1991
 * Copyright 1991 Guido van Rossum And Sundry Contributors
 * This source code is freely redistributable and may be used for
 * any purpose.  This copyright notice must be maintained. 
 * Guido van Rossum And Sundry Contributors are not responsible for 
 * the consequences of using this software.
 */

/*
 * Sound Tools Macintosh HCOM format.
 * These are really FSSD type files with Huffman compression,
 * in MacBinary format.
 * To do: make the MacBinary format optional (so that .data files
 * are also acceptable).  (How to do this on output?)
 */

#include "st.h"

#ifdef __STDC__
/*
#include <stdlib.h>
*/
#else
#include <string.h>
IMPORT char *malloc(), *realloc();
#endif

/* Dictionary entry for Huffman (de)compression */
typedef struct {
	long frequ;
	short dict_leftson;
	short dict_rightson;
} dictent;

/* Private data used by reader */
struct readpriv {
	/* Static data from the header */
	dictent *dictionary;
	long checksum;
	int deltacompression;
	/* Engine state */
	long huffcount;
	long cksum;
	int dictentry;
	int nrbits;
	unsigned long current;
	short sample;
};

void skipbytes(ft_t ft, int n);

int hcomstartread(ft_t ft)
{
	struct readpriv *p = (struct readpriv *) ft->priv;
	int i;
	char buf[4];
	unsigned long datasize, rsrcsize;
	unsigned long huffcount, checksum, compresstype, divisor;
	unsigned short dictsize;

	/* Skip first 65 bytes of header */
	skipbytes(ft, 65);

	/* Check the file type (bytes 65-68) */
	if (fread(buf, 1, 4, ft->fp) != 4 || strncmp(buf, "FSSD", 4) != 0)
		return 0;
//		fail("Mac header type is not FSSD");

	/* Skip to byte 83 */
	skipbytes(ft, 83-69);

	/* Get essential numbers from the header */
	datasize = rblong(ft); /* bytes 83-86 */
	rsrcsize = rblong(ft); /* bytes 87-90 */

	/* Skip the rest of the header (total 128 bytes) */
	skipbytes(ft, 128-91);

	/* The data fork must contain a "HCOM" header */
	if (fread(buf, 1, 4, ft->fp) != 4 || strncmp(buf, "HCOM", 4) != 0)
		return 0;
//		fail("Mac data fork is not HCOM");

	/* Then follow various parameters */
	huffcount = rblong(ft);
	checksum = rblong(ft);
	compresstype = rblong(ft);
	if (compresstype > 1)
		return 0;
//		fail("Bad compression type in HCOM header");
	divisor = rblong(ft);
	if (divisor == 0 || divisor > 4)
		return 0;
//		fail("Bad sampling rate divisor in HCOM header");
	dictsize = rbshort(ft);

	/* Translate to sox parameters */
	ft->info.style = UNSIGNED;
	ft->info.size = BYTE;
	ft->info.rate = 22050 / divisor;
	ft->info.channels = 1;

	/* Allocate memory for the dictionary */
	p->dictionary = (dictent *) malloc(511 * sizeof(dictent));
	if (p->dictionary == NULL)
		return 0;
//		fail("can't malloc memory for Huffman dictionary");

	/* Read dictionary */
	for(i = 0; i < dictsize; i++) {
		p->dictionary[i].dict_leftson = rbshort(ft);
		p->dictionary[i].dict_rightson = rbshort(ft);
		/*
		report("%d %d",
		       p->dictionary[i].dict_leftson,
		       p->dictionary[i].dict_rightson);
		       */
	}
	skipbytes(ft, 1); /* skip pad byte */

	/* Initialized the decompression engine */
	p->checksum = checksum;
	p->deltacompression = compresstype;
//	if (!p->deltacompression)
//		report("HCOM data using value compression");
	p->huffcount = huffcount;
	p->cksum = 0;
	p->dictentry = 0;
	p->nrbits = -1; /* Special case to get first byte */
	
	return 1;
}

void skipbytes(ft_t ft, int n)
{
	while (--n >= 0) {
		if (getc(ft->fp) == EOF)
			return;
//			fail("unexpected EOF in Mac header");
	}
}

int hcomread(ft_t ft, long *buf, int len)
{
	register struct readpriv *p = (struct readpriv *) ft->priv;
	int done = 0;

	if (p->nrbits < 0) {
		/* The first byte is special */
		if (p->huffcount == 0)
			return 0; /* Don't know if this can happen... */
		p->sample = getc(ft->fp);
		if (p->sample == EOF)
			return 0;
//			fail("unexpected EOF at start of HCOM data");
		*buf++ = (p->sample - 128) * 0x1000000;
		p->huffcount--;
		p->nrbits = 0;
		done++;
		len--;
		if (len == 0)
			return done;
	}

	while (p->huffcount > 0) {
		if(p->nrbits == 0) {
			p->current = rblong(ft);
			if (feof(ft->fp))
				return 0;
//				fail("unexpected EOF in HCOM data");
			p->cksum += p->current;
			p->nrbits = 32;
		}
		if(p->current & 0x80000000) {
			p->dictentry =
				p->dictionary[p->dictentry].dict_rightson;
		} else {
			p->dictentry =
				p->dictionary[p->dictentry].dict_leftson;
		}
		p->current = p->current << 1;
		p->nrbits--;
		if(p->dictionary[p->dictentry].dict_leftson < 0) {
			short datum;
			datum = p->dictionary[p->dictentry].dict_rightson;
			if (!p->deltacompression)
				p->sample = 0;
			p->sample = (p->sample + datum) & 0xff;
			p->huffcount--;
			if (p->sample == 0)
				*buf++ = -127 * 0x1000000;
			else
				*buf++ = (p->sample - 128) * 0x1000000;
			p->dictentry = 0;
			done++;
			len--;
			if (len == 0)
				break;
		}
	}

	return done;
}

/*void*/ hcomstopread(ft_t ft) 
{
	register struct readpriv *p = (struct readpriv *) ft->priv;

	if (p->huffcount != 0)
		return 0;
//		fail("not all HCOM data read");
	if(p->cksum != p->checksum)
		return 0;
//		fail("checksum error in HCOM data");
	free((char *)p->dictionary);
	p->dictionary = NULL;
}
