// 14bit stereo PCM to Id RoQ DPCM encoder 
// All code written by Lynx-EyED 

#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#define WAVESIZE (450*1024*1024)
#define WAVEMAX (32767-4096)
#define WAVEMIN (-32767+4096)

signed short wavebuf[WAVESIZE];
unsigned char adpbuf[WAVESIZE/4];
unsigned char headbuf[10+WAVESIZE/2];

void adp_encode(char *src,char *dst);

  
void encode(unsigned char *adp,signed short *wave, int size)
{
    unsigned char sign;			/* Current dpcm sign bit */

    int diff;					/* Difference between nowpcm and valprev */
    int diff2;					/* Difference between nowpcm and valprev */


    short oldpcm=0;				/* Predicted output value */
    short oldpcm2=0;				/* Predicted output value */
    unsigned int vpdiff;			/* Current change to oldpcm */
    int index=0;				/* Current step change index */
    int index2=0;				/* Current step change index */
    int i;
    


	for (i=0;i<size;i+=2) {
		/* compute difference with previous value */
		diff = ((*wave++) / 2) - oldpcm;	//clip to 10bit-length
		sign = (diff < 0) ? 0x80 : 0;
		if ( sign ) diff = (-diff);
		vpdiff = ((unsigned char)(sqrt(diff)));

		/* Update previous value */
		if( sign )
			oldpcm -= ((vpdiff)*(vpdiff));
		else
			oldpcm += ((vpdiff)*(vpdiff));


		/* Clamp previous value to 10 bits */
		if ( oldpcm > 16383){
			//vpdiff = ((unsigned char)(sqrt(1023 - oldpcm)));
			oldpcm = 16383;
		}
		else if ( oldpcm < -16384){
			//vpdiff = ((unsigned char)(sqrt(oldpcm)));
			oldpcm = -16384;
		}

		/*Assemble value */
		vpdiff |= sign;
		*adp++ = vpdiff;




		/* compute difference with previous value */
		diff2 = ((*wave++) / 2) - oldpcm2;
		sign = (diff2 < 0) ? 0x80 : 0;
		if ( sign ) diff2 = (-diff2);


		vpdiff = ((unsigned char)(sqrt(diff2)));


		/* Update previous value */
		if ( sign )
		  oldpcm2 -= ((vpdiff)*(vpdiff));
		else
		  oldpcm2 += ((vpdiff)*(vpdiff));

		/* Clamp previous value to 10 bits */
		if ( oldpcm2 > 16383){
			//vpdiff = ((unsigned char)(sqrt(1023 - oldpcm2)));
			oldpcm2 = 16383;
		}
		else if ( oldpcm2 < -16384){
			//vpdiff = ((unsigned char)(sqrt(oldpcm2)));
			oldpcm2 = -16384;
		}
		/* Assemble value */
		vpdiff |= sign;
		*adp++ = vpdiff;
   	 }

}



int main(int argc, char * argv[])
{
  if((argc!=3)&&(argc!=4)){
    printf("%s:DPCM encoder based 'Id RoQ DPCM' for 16bit LPCM\n",argv[0]);
    printf("\t%s <source file name> <tag name>\n",argv[0]);
    printf("\t output file name : <tag name>.dat\n");
    exit(-1);
  }
  memset(wavebuf,0,sizeof(wavebuf));
  memset(adpbuf,0,sizeof(adpbuf));
  adp_encode(argv[1],argv[2]);
  return 0;
}

void adp_encode(char *src,char *dst)
{
  FILE *fp;
  int size;
  char nbuff[256];
  long fsize;
  if((fp=fopen(src,"rb"))==NULL){
    printf("can't open %s\n",src);
    exit(-1);
  }


	/* seek file size */ 
	fseek(fp,0,SEEK_END); 

	//fgetpos(fp,&fsize);
	fsize = ftell(fp);
	rewind(fp);
	if(fsize > sizeof(wavebuf))fsize=sizeof(wavebuf);

 
  fseek(fp,44,SEEK_SET); //Skip header
  
  size=fread(wavebuf,1,fsize,fp);
  fclose(fp);
  encode(adpbuf,wavebuf,size);
  if((fp=fopen( strcat( strcpy(nbuff,dst) ,".dat" ),"wb" ))==NULL){
    printf("can't open %s\n",dst);
    exit(-1);
  }
  fwrite(adpbuf,1,10+(fsize/2),fp);
  fclose(fp);
}

