/*
	wav2adv

	(c)2001 BERO

	http://www.geocities.co.jp/Playtown/2004/
	bero@geocities.co.jp

	adx info from: http://ku-www.ss.titech.ac.jp/~yatsushi/adx.html

*/

#include <stdio.h>
#include <string.h>
#include <fcntl.h>

void write_long(void *p0,long d)
{
	unsigned char *p = p0;
	p[0] = d>>24;
	p[1] = d>>16;
	p[2] = d>>8;
	p[3] = d;
}

typedef struct {
	int s1,s2;
} PREV;

//#define	BASEVOL	0x11e0
#define	BASEVOL	0x4000

int convert(unsigned char *adx,short *wav,PREV *prev)
{
	int scale;
	int i;
	int s0,s1,s2,d;
	int max=0;
	int min=0;
	int data[32];

	s1 = prev->s1;
	s2 = prev->s2;
	for(i=0;i<32;i++) {
		s0 = wav[i];
		d = ((s0<<14) - 0x7298*s1 + 0x3350*s2)/BASEVOL;
		data[i]=d;
		if (max<d) max=d;
		if (min>d) min=d;
		s2 = s1;
		s1 = s0;
	}
	prev->s1 = s1;
	prev->s2 = s2;

	/* -8..+7 */

	if (max==0 && min==0) {
		memset(adx,0,18);
		return;
	}

	if (max/7>-min/8) scale = max/7;
	else scale = -min/8;

	if (scale==0) scale=1;

	adx[0] = scale>>8;
	adx[1] = scale;

	for(i=0;i<16;i++) {
		adx[i+2] = ((data[i*2]/scale)<<4) | ((data[i*2+1]/scale)&0xf);
	}

//	if (over) putchar('.');
}

int wav2adx(char *infile,char *outfile)
{
	FILE *in,*out;
	unsigned char adxbuf[18*2];
	short wavbuf[32*2];
	int offset;
	int channel,freq,size,wsize;
	PREV prev[2];
	int i;

	struct {
		char hdr1[4];
		long totalsize;

		char hdr2[8];
		long hdrsize;
 		short format;
		short channel;
		long freq;
		long byte_per_sec;
		short blocksize;
		short bits;

		char hdr3[4];
		long datasize;
	} wavhdr;

	struct {
		long offset; /* 0x80000000 + sample start - 4 */
		unsigned char unknown1[3]; /* 03 12 04 */
		unsigned char channel; /* 1 or 2 */
		long freq;
		long size;
		long unknown2; /* 01 f4 03 00 */
		long unknown3; /* 00 00 00 00 */
		long unknown4; /* 00 00 00 00 */

	/* if loop
		unknown3 00 15 00 01
		unknown4 00 00 00 01
		long loop_start_sample;
		long loop_start_byte;
		long loop_end_sample;
		long loop_end_byte;
		long 
	*/
	} adxhdr; /* big endian */

	/* offset-6 "(c)CRI" */


	if (strcmp(infile,"-")==0)
		in = stdin;
	else
		in = fopen(infile,"rb");
	if (in==NULL) {
		printf("can't open infile %s\n",infile);
		return -1;
	}

	fread(&wavhdr,1,sizeof(wavhdr),in);
	if(memcmp(wavhdr.hdr1,"RIFF",4)
	|| memcmp(wavhdr.hdr2,"WAVEfmt ",8)
	|| wavhdr.hdrsize!=0x10
	|| wavhdr.format!=1
	|| wavhdr.bits!=16) {
		puts("unsupport!");
		return -1;
	}

	channel = wavhdr.channel;
	freq = wavhdr.freq;
	size = wavhdr.datasize/wavhdr.blocksize;

	offset = 0x20;

	write_long(&adxhdr.offset,offset|0x80000000);
	adxhdr.channel = wavhdr.channel;
	adxhdr.unknown1[0] = 0x03;
	adxhdr.unknown1[1] = 0x12;
	adxhdr.unknown1[2] = 0x04;

	write_long(&adxhdr.freq,freq);
	write_long(&adxhdr.size,size);

	write_long(&adxhdr.unknown2,0x01f40300);
	write_long(&adxhdr.unknown3,0x00000000);
	write_long(&adxhdr.unknown4,0x00000000);


	if (strcmp(outfile,"-")==0)
		out = stdout;
	else
		out = fopen(outfile,"wb");
	if (out==NULL) {
		printf("can't open outfile %s\n",outfile);
		return -1;
	}

	fwrite(&adxhdr,1,sizeof(adxhdr),out);

	/* padding */
	for(i=sizeof(adxhdr);i<offset+4-6;i++) {
		static char c=0;
		fputc(c,out);
	}

	fwrite("(c)CRI",1,6,out);


	prev[0].s1 = 0;
	prev[0].s2 = 0;
	prev[1].s1 = 0;
	prev[1].s2 = 0;


	if (channel==1)
	while(size) {
		fread(wavbuf,2,32,in);
		convert(adxbuf,wavbuf,prev);
		if (size>32) wsize=32; else wsize = size;
		size-=wsize;
		fwrite(adxbuf,1,18,out);
	}
	else if (channel==2)
	while(size) {
		short tmpbuf[32*2];
		int i;

		fread(tmpbuf,2,32*2,in);
		for(i=0;i<32;i++) {
			wavbuf[i] = tmpbuf[i*2];
			wavbuf[i+32] = tmpbuf[i*2+1];
		}

		convert(adxbuf,wavbuf,prev);
		convert(adxbuf+18,wavbuf+32,prev+1);
		if (size>32) wsize=32; else wsize = size;
		size-=wsize;
		fwrite(adxbuf,1,18*2,out);
	}

	memset(&adxhdr,0,sizeof(adxhdr));
	write_long(&adxhdr.offset,0x8001000e);

	fwrite(&adxhdr,1,0x12,out);

	fclose(in);
	fclose(out);

	return 0;
}

int main(int argc,char**argv)
{
	int i;
	if (argc<2) {
		puts("DC/SS wav to adx converter by bero\n wav2adx <infile>\n http://www.geocities.co.jp/Playtown/2004/");
		return -1;
	}

//	_fmode = O_BINARY;

	for(i=1;i<argc;i++) {
		char *infile = argv[i];
		char *p,outfile[256];

		p = strrchr(infile,'\\');
		if (p) p++; else p=infile;
		strcpy(outfile,p);

		p = strrchr(outfile,'.');
		if (!p) p = outfile+strlen(outfile);
		strcpy(p,".adx");

		printf("%s -> %s\n",infile,outfile);

		wav2adx(infile,outfile);
	}
	return 0;
}
