/* sod2, a player for polychannel .csf music files.
 * Copyright (C) 1995 Russell Marks. See sod2.c for license details.
 *
 * grokfile.c - parse a .csf file (derived from makesod.c).
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "sod2.h"
#include "tarfile.h"


char sample_path[1024],sample_file[1024];
int reading_tar=0;


/* this is a bit unpleasant, but it won't work otherwise */
extern int lines[MAX_NOTES][MAX_LINE_DEPTH];


/* (some) function prototypes */
void makecsffilename(char *new,char *org);
int getline(char *buf,FILE *in);
void processcsf(FILE *in);
void addline(int num,int mode,
	char *notes,char *octaves,char *sample,char *relvol,
	int sod_time,int sod_down);
void errorprint(char *str);
void load_sample(char *filename);
void sign(void);
void padline(char *str,int n);


void grokfile(char *filename)
{
FILE *in;
char inname[256],tmp[256];

makecsffilename(inname,filename);
if((in=fopen(inname,"r"))==NULL)
  {
  sprintf(tmp,"Couldn't open file '%s'.",filename);
  errorprint(tmp);
  exit(1);
  }
else
  {
  processcsf(in);
  fclose(in);
  }
}


void makecsffilename(char *new,char *org)
{
char *ptr;

strcpy(new,org);
if((ptr=strrchr(org,'.'))==NULL)
  strcat(new,".csf");
}


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

buf[0]=0;
do
  ptr=fgets(buf,256,in);
while(buf[0]=='#' && ((!reading_tar && !feof(in)) ||
		      ( reading_tar && ftell(in)-512<=tar_csf_file_size)));

if(reading_tar && ftell(in)-512>tar_csf_file_size) ptr=NULL;

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);
}


void processcsf(FILE *in)
{
char buf[256],buf2[256],buf3[256],vbuf[256],*ptr;
int f,g,i,mode;
int sod_time,sod_down;

/* see if it's a tar file */
if(is_tar_file(in))
  {
  tar_csf_setup(in);
  reading_tar=1;
  }

/* 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(!strncmp(buf,"*bsize",6))
  {
  bsize=atoi(buf+6);
  getline(buf,in);
  }

if(strcmp(buf,"*samples"))
  {
  errorprint("No '*samples' block (must be first).");
  exit(1);
  }

/* add all the sample descriptions */
next_sample=1;
getline(buf,in);
while(strcmp(buf,"*blocklist"))
  {
  while((ptr=strchr(buf,'\t'))!=NULL) *ptr=32;
  if((ptr=strchr(buf,' '))==NULL)
    {
    errorprint("Couldn't find sample rate");
    exit(1);
    }

  samples[next_sample].rate=atoi(ptr);
  *ptr=0;

  if(reading_tar)
    tar_load_sample(in,buf);
  else
    load_sample(buf);
  
  if(strcmp(buf+strlen(buf)-4,".ami")!=0)
    sign();
  
  next_sample++;
  if(getline(buf,in)==-1)
    {
    errorprint("Couldn't find '*blocklist'.");
    exit(1);
    }
  }

g=1;
getline(buf,in);
while(strcmp(buf,"*blocks"))
  {
  f=0;
  ptr=strrchr(buf,',');
  if(ptr!=NULL)
    {
    do
      {
      *ptr++=0;
      lines[g][f]=atoi(ptr);
      f++;
      }
    while((ptr=strrchr(buf,','))!=NULL);
    }
  lines[g][f]=atoi(buf);
    
  g++;    
  if(getline(buf,in)==-1)
    {
    errorprint("Couldn't find '*blocks'.");
    exit(1);
    }
  }

if(g==0)
  {
  errorprint("Need some patterns in the block list.");
  exit(1);
  }

last_line=g-1;


/* now interpret the patterns, and write them as we go along. */
f=1;
while(getline(buf,in)!=-1)
  {
  for(i=0;i<bsize;i++) vbuf[i]='9';	/* max relative vol. */
  vbuf[bsize]=0;
  
  sod_time=10000; sod_down=10;
  mode=DEFAULTMODE;
  while(buf[0]=='*')
    {
    switch(buf[1])
      {
      case 'p':
        mode=PIANO_MODE; break;
      case 'm':
        mode=MULTI_MODE; break;
      case 'r':
        mode=RETRIGGER_MODE; break;
      case 'o':
        mode=OFFSET_MODE; break;
      case 'e':
        ptr=strchr(buf,',');
        *ptr=0;
        sod_time=atoi(buf+3);
        ptr++;
        sod_down=atoi(ptr);
        break;
      case 'v':
        getline(vbuf,in);
        padline(vbuf,bsize);
        break;
      }
    getline(buf,in);
    }

  padline(buf ,bsize);
  getline(buf2,in); padline(buf2,bsize);
  getline(buf3,in);
  addline(f,mode,buf,buf2,buf3,vbuf,sod_time,sod_down);
  f++;
  }

last_pattern=f-1;
}


void addline(int num,int mode,
	char *notes,char *octaves,char *sample,char *relvol,
	int sod_time,int sod_down)
{
int note=0,f,gotnote,samplenum,vol;
char *ptr;

ptr=strchr(sample,',');
if(ptr==NULL)
  vol=DEFAULTVOLUME;
else
  {
  *ptr=0;
  ptr++;
  vol=atoi(ptr);
  }

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

patterns[num].mode=mode;

if(strlen(notes)==bsize)
  {
  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;
      }

    if(gotnote)
      {
      patterns[num].notes[f].notenum=note+12*(octaves[f]-'0'-OCTAVEFIX);
      patterns[num].notes[f].vol=vol*(relvol[f]-48)/9;
      if(samplenum==-1)
        {
        int tmp;
        tmp=sample[f];
        if(tmp>'Z') tmp-=32;
        tmp-='0';
        if(tmp>9) tmp-=7;
        patterns[num].notes[f].sample=tmp;
        }
      else
        patterns[num].notes[f].sample=samplenum;
      }
    else
      patterns[num].notes[f].vol=0;	/* no note */

    patterns[num].notes[f].pattern=num;
    patterns[num].notes[f].mode=mode;
    patterns[num].notes[f].sustain=sod_time;
    patterns[num].notes[f].release=sod_down;
    }
  }
}


void errorprint(char *str)
{
fprintf(stderr,"Error: %s\n",str);
}


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


void load_sample(char *filename)
{
FILE *in;
int len,lastone;
char *path,*pathptr,*ptr;

/* find out where the sample actually is. */

if((path=getenv("SOD2_SAMPLE_PATH"))!=NULL)
  {
  strcpy(sample_path,path);
  strcat(sample_path,":");
  }
else
  sample_path[0]=0;

strcat(sample_path,MINIMUM_SAMPLE_PATH);

pathptr=sample_path;
lastone=0;

do
  {
  if((ptr=strchr(pathptr,':'))!=NULL) 
    *ptr=0;
  else
    lastone=1;
  
  strcpy(sample_file,pathptr);
  strcat(sample_file,"/");
  strcat(sample_file,filename);
  
  pathptr+=strlen(pathptr)+1;
  }
while((in=fopen(sample_file,"rb"))==NULL && lastone==0);

if(in==NULL)			die("open sample file");

/* find out how big the sample is */

if(fseek(in,0,SEEK_END)==-1)	die("fseek on sample file");
if((len=ftell(in))==-1)		die("ftell on sample file");
rewind(in);

/* load the sample */

if((samples[next_sample].data=malloc(len))==NULL) die("malloc");
if(fread(samples[next_sample].data,1,len,in)!=len) die("fread");
fclose(in);
samples[next_sample].len=len;

if(verbose)
  fprintf(stderr,"%s %d\n",sample_file,len);
}


/* only 8-bit input samples are allowed, so this is ok for now */
void sign()
{
int f;
unsigned char *ptr;

ptr=samples[next_sample].data;
for(f=0;f<samples[next_sample].len;f++,ptr++)
  *ptr-=128;
}
