/* tuneconv - convert pseudo-csf to tune.z data. based on sod2's grokfile.c
 * PD by RJM
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>


/*#define AY_CLOCK	1773400*/	/* speccy */

#define AY_CLOCK	1625000		/* Zon X */



#define DEFAULTVOLUME	15
#define OCTAVEFIX	3


static int curline=0;

int bsize=64;


/* (some) function prototypes */
void makecsffilename(char *new,char *org);
int getline(char *buf,FILE *in);
void check_linelen(char *buf,int bsize,int has_comma);
void processcsf(FILE *in);
void addline(int num,int mode,
	char *notes,char *octaves,char *sample,
        char *stereo1,char *stereo2,char *relvol,
	int sod_time,int sod_down,int sod_pos);
void padline(char *str,int n);


int main(void)
{
processcsf(stdin);
exit(0);
}


int getline(char *buf,FILE *in)
{
char *ptr;

buf[0]=0;
do
  curline++,ptr=fgets(buf,256,in);
while(buf[0]=='#' && !feof(in));

if(ptr!=NULL)
  {
  if(buf[0]!=0 && buf[strlen(buf)-1]=='\n') buf[strlen(buf)-1]=0;
  if(buf[0]!=0 && buf[strlen(buf)-1]=='\r') buf[strlen(buf)-1]=0;
  }
else
  buf[0]=0;
return((ptr==NULL)?-1:0);
}


/* check line len, and fix it if too long (giving warning) */
void check_linelen(char *buf,int bsize,int has_comma)
{
char *cptr=NULL;

/* if we allow a comma, see if we have one or not. */
if(has_comma)
  cptr=strchr(buf,',');

if((cptr==NULL && strlen(buf)>bsize) ||
   (cptr!=NULL && (cptr-buf)>bsize))
  {
  buf[bsize]=0;
  fprintf(stderr,"sod2: line %d too long.\n",curline);
  exit(1);
  }
}


void processcsf(FILE *in)
{
char buf[256],buf2[256],buf3[256],vbuf[256],sbuf[256],sbuf2[256],*ptr;
int f,g,i;
int sod_time,sod_down,sod_pos;
int tempo=125;

/* look for '*samples' (and possibly '*tempo', '*bsize' before that) */
getline(buf,in);
if(!strncmp(buf,"*tempo",6))
  {
  tempo=atoi(buf+6);
  getline(buf,in);
  }

if(tempo<100) tempo=100;
if(tempo>240) tempo=240;

printf("intnum: defb %d\n\n",750/tempo);

if(!strncmp(buf,"*bsize",6))
  {
  /* ignored */
  getline(buf,in);
  }

if(strcmp(buf,"*samples"))
  {
  fprintf(stderr,"sod2: missing `*samples' block (must be first).\n");
  exit(1);
  }

/* add all the sample descriptions */
getline(buf,in);
while(strcmp(buf,"*blocklist"))
  {
  if(getline(buf,in)==-1)
    {
    fprintf(stderr,"sod2: missing `*blocklist' section.\n");
    exit(1);
    }
  }

printf("blocklist:\n");

g=1;
getline(buf,in);
while(strcmp(buf,"*blocks"))
  {
  ptr=strrchr(buf,',');
  f=1;
  if(ptr!=NULL)
    {
    do
      {
      f++;
      *ptr++=0;
      if(f<=3)
        printf("defb %s\n",ptr);
      }
    while((ptr=strrchr(buf,','))!=NULL);
    }
  printf("defb %s\n",buf);
  if(f>3)
    fprintf(stderr,
            "tuneconv: warning: "
            "%d: (%d) attempt to use %d channels\n",curline,g,f);
  for(;f<3;f++)
    printf("defb 0\n");
  
  g++;    
  if(getline(buf,in)==-1)
    {
    fprintf(stderr,"sod2: missing `*blocks' section.\n");
    exit(1);
    }
  }

if(g==0)
  {
  fprintf(stderr,"sod2: must be some patterns in the block list!\n");
  exit(1);
  }

printf("defb 255\n\nblocks:\n");


/* now interpret the patterns, and write them as we go along. */
f=1;
while(getline(buf,in)!=-1)
  {
  printf(";p%d\n",f);
  check_linelen(buf,bsize,0);
  
  for(i=0;i<bsize;i++) vbuf[i]='9';	/* max relative vol. */
  vbuf[bsize]=0;
  for(i=0;i<bsize;i++) sbuf[i]=' ';
  sbuf[bsize]=0;
  for(i=0;i<bsize;i++) sbuf2[i]=' ';	/* space=use sod_pos */
  sbuf2[bsize]=0;
  
  sod_time=10000; sod_down=10; sod_pos=0;
  while(buf[0]=='*')
    {
    switch(buf[1])
      {
      case 'e':
        ptr=strchr(buf,',');
        *ptr=0;
        sod_time=atoi(buf+3);
        ptr++;
        sod_down=atoi(ptr);
        break;
      case 'v':
        getline(vbuf,in);
        check_linelen(vbuf,bsize,0);
        padline(vbuf,bsize);
        break;
      case 's':		/* stereo position */
        sod_pos=atoi(buf+3);
        if(sod_pos<-16) sod_pos=-16;
        if(sod_pos>16) sod_pos=16;
        break;
      case 'S':		/* stereo position like *v */
        getline(sbuf,in);
        check_linelen(sbuf,bsize,0);
        padline(sbuf,bsize);
        getline(sbuf2,in);
        check_linelen(sbuf2,bsize,0);
        padline(sbuf2,bsize);
        break;
      }
    getline(buf,in);
    check_linelen(buf,bsize,0);
    }

  padline(buf ,bsize);
  getline(buf2,in); check_linelen(buf2,bsize,0); padline(buf2,bsize);
  getline(buf3,in); check_linelen(buf3,bsize,1);
  addline(f,0,buf,buf2,buf3,sbuf,sbuf2,vbuf,sod_time,sod_down,sod_pos);
  f++;
  }
}


void addline(int num,int mode,
	char *notes,char *octaves,char *sample,
        char *stereo1,char *stereo2,char *relvol,
	int sod_time,int sod_down,int sod_pos)
{
int note=0,f,gotnote,vol,i,tmp,noisebit;
char *ptr;
double etime=0.,eincr=8./6.;
int samplenum=-1;

ptr=strchr(sample,',');
if(ptr==NULL)
  vol=DEFAULTVOLUME;
else
  {
  *ptr=0;
  ptr++;
  vol=atoi(ptr);
  /* so they have to *ask* for 16 (env) */
  if(vol>16) vol=15;
  }

if(strlen(sample)>10)
  {
  samplenum=-1;
  }
else
  {
  tmp=sample[0];
  if(tmp>'Z') tmp-=32;
  tmp-='0';
  if(tmp>9) tmp-=7;
  
  samplenum=tmp;
  }

if(strlen(notes)==bsize)
  {
  int freq;
  
  for(f=0;f<bsize;f++)
    {
    gotnote=1;
    switch(notes[f])
      {
      case 'c': note= 0; break;
      case 'C': note= 1; break;
      case 'd': note= 2; break;
      case 'D': note= 3; break;
      case 'e': note= 4; break;
      case 'f': note= 5; break;
      case 'F': note= 6; break;
      case 'g': note= 7; break;
      case 'G': note= 8; break;
      case 'a': note= 9; break;
      case 'A': note=10; break;
      case 'b': note=11; break;
      default:  gotnote=0;
      }

    noisebit=0;
    if(gotnote)
      {
      etime=0.;
      freq=(int)(440.*pow(2.,octaves[f]-'0'-OCTAVEFIX+(note-9.)/12.));
      
      if(samplenum==-1)
        {
        tmp=sample[f];
        if(tmp>'Z') tmp-=32;
        tmp-='0';
        if(tmp>9) tmp-=7;
        }
      else
        tmp=samplenum;

      /* noise stuff, based on sample number */
      switch(tmp)
        {
        case 3: noisebit=8+4; break;	/* bass */
        case 4: noisebit=8+2; break;	/* snare */
        case 5:
        case 6: noisebit=8+1; break;	/* hihat */
        }
      }
    else
      freq=0;

    printf("defw %d\n",freq?(noisebit<<12)+((AY_CLOCK/16)/freq):0);

    printf("defb ");
    /* XXX this assumes volume is linear */
    for(i=0;i<6;i++)
      {
      int c=(i==5)?'\n':',';
      double dvol=vol*(relvol[f]-48)/9.;
      
      if(etime<sod_time)
        printf("%d%c",(int)dvol,c);
      else
        {
        if(etime<sod_time+sod_down)
          printf("%d%c",(int)(dvol*(1.-(etime-sod_time)/sod_down)),c);
        else
          printf("%d%c",0,c);
        }
        
      etime+=eincr;
      }
      
    /* patterns[num].notes[f].vol=vol*(relvol[f]-48)/9; or
       patterns[num].notes[f].vol=0;	for no note */
    /* patterns[num].notes[f].sustain=sod_time; */
    /* patterns[num].notes[f].release=sod_down; */
    }
  }
}


/* well, it works :) */
void padline(char *str,int n)
{
while(strlen(str)<n)
  strcat(str," ");
}
