%{
/*
 * Cfg file reader, Greg Lee, 8/93.
 * Adapted from Adagio for KMidi 12/99.
 *	$Id: cfg.l,v 1.8 2000/08/08 17:32:37 faure Exp $
 */
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "gtim.h"
#include "common.h"
#include "instrum.h"
#include "playmidi.h"
#include "readmidi.h"
#include "output.h"
#include "controls.h"
#include "tables.h"

#include "sbk.h"
#include "sflayer.h"

#define YY_ALWAYS_INTERACTIVE 0
#define YY_NEVER_INTERACTIVE 1

#define YY_INPUT(buf,result,max_size) \
	 result = fread( buf, 1, max_size, yyin );

extern int set_play_mode(char *cp);
static int prescanning;
static char doing_drums = 0;
static char doing_sf = 0;
static int patchno = -1;
static char *patchname;
static char cfg_flag = 1;
static int tone_bank;
static void set_patchno(char *m);

static char *s_dirname = 0;
static char *sfname = 0;

static int current_toneset = 0;
static int current_drumset = 0;

static int cfg_condition = -1;

static ToneBank *bank=0;
static int banknum=0;
static int rcf_count=1;
static int font_type=FONT_GUS;

#define MAX_SF_COUNT 4096
static struct cfg_type a_sf[MAX_SF_COUNT];
static int sf_count = 0;


#define MAX_INCLUDE_DEPTH 40
static YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH];
static int include_stack_ptr = 0;
static char *this_fname[MAX_INCLUDE_DEPTH] = { NULL };
static int this_line[MAX_INCLUDE_DEPTH] = { 0 };

static void new_toneset(int n, int highest);
static void new_patch(const char *vname, int num);
static int sf2tab(char *sffname);
static void interpret_sf(void);

%}

%option noyywrap
%option nounput
%option outfile = "cfg.c"

%s P K S

wh	[ \t]+
owh	[ \t]*
lwh	([ \t]*"\\"[ \t]*\n)?[ \t]*
nm	([^ \t\n\r\"#]+|\"[^\"\n]+\")
cm	[ \t]*("#".*)?\n

%%

^{owh}"if"{wh}[0-9]+ {
	int i = 0;
	while (isspace(yytext[i])) i++;
	i += 2;
	cfg_condition = atoi(yytext+i);
}
^{owh}"else" {
	cfg_condition = 0;
}
^{owh}("sf"|"sbk"){wh}{nm}{owh}[0-9]* {
	int sf_oldbank, sf_newbank = banknum;
	int i = 0, j;
	unsigned namelen;
	while (isspace(yytext[i])) i++;
	if (yytext[i+1] == 'f') i += 2;
	else i += 3;
	while (isspace(yytext[i])) i++;
	if (yytext[i] == '"') {
		i++;
		for (j = i; j < yyleng && yytext[j] != '"'; j++) ;
		/*j--;*/
	}
	else for (j = i; j < yyleng && !isspace(yytext[j]); j++) ;
	namelen = (unsigned)(j - i + 1);
	sfname = strncpy( (char *)malloc(namelen), yytext+i, (unsigned)(j-i) );
	sfname[j-i] = '\0';
	if (doing_drums) sf_newbank += 256;
	sf_oldbank = sf_newbank;
	if (j < yyleng) {
		while (j < yyleng && isspace(yytext[j])) j++;
		if (j < yyleng && isdigit(yytext[j])) sf_oldbank = atoi(yytext+j);
	}
	init_soundfont(sfname, sf_oldbank, sf_newbank, rcf_count);
}
^{owh}"soundfont"{wh}{nm} {
	int i = 0, j;
	unsigned namelen;
	while (isspace(yytext[i])) i++;
	i += 9;
	while (isspace(yytext[i])) i++;
	if (yytext[i] == '"') {
		i++;
		for (j = i; j < yyleng && yytext[j] != '"'; j++) ;
	}
	else for (j = i; j < yyleng && !isspace(yytext[j]); j++) ;
	namelen = (unsigned)(j - i + 1);

	if (prescanning && cfg_condition >= 0 && cfg_condition < 30 &&
		rcf_count==1 && !cfg_names[cfg_condition]) {
	    cfg_names[cfg_condition] = strncpy( (char *)malloc(namelen), yytext+i, (unsigned)(j-i) );
	    cfg_names[cfg_condition][j-i] = '\0';
	}

        if (!prescanning && (cfg_condition < 0 || cfg_condition == cfg_select)) {
		sfname = strncpy( (char *)malloc(namelen), yytext+i, (unsigned)(j-i) );
		sfname[j-i] = '\0';
		sf_count = 0;
		if (sf2tab(sfname) && sf_count) interpret_sf();
	}
	cfg_condition = -1;
}
^{owh}("dir"|"PatchDir:"){wh}{nm} {
	int i = 0, j;
	unsigned namelen;
	while (isspace(yytext[i])) i++;
	if (yytext[i] == 'd') i += 3;
	else i += 9;
	while (isspace(yytext[i])) i++;

	if (yytext[i] == '"') {
		i++;
		for (j = i; j < yyleng && yytext[j] != '"'; j++) ;
	}
	else for (j = i; j < yyleng && !isspace(yytext[j]); j++) ;
	namelen = (unsigned)(j - i + 1);

	if (!prescanning) {
		s_dirname = strncpy( (char *)malloc(namelen), yytext+i, namelen-1 );
		s_dirname[j-i] = '\0';
		add_to_pathlist(s_dirname, rcf_count);
		free(s_dirname);
	}
}
^{owh}"source"{wh}{nm} {
	char *fname;
	int i = 0, j;
	unsigned namelen;
	FILE *save_yyin;

	while (isspace(yytext[i])) i++;
	i += 6;
	while (isspace(yytext[i])) i++;

	if (yytext[i] == '"') {
		i++;
		for (j = i; j < yyleng && yytext[j] != '"'; j++) ;
	}
	else for (j = i; j < yyleng && !isspace(yytext[j]); j++) ;
	namelen = (unsigned)(j - i + 1);

	 if (prescanning && cfg_condition >= 0 && cfg_condition < 30 &&
		rcf_count==1 && !cfg_names[cfg_condition])
	  {
	    cfg_names[cfg_condition] = (char *)safe_malloc(namelen);
	    strcpy(cfg_names[cfg_condition], yytext+i);
	  }

    if (!prescanning && (cfg_condition < 0 || cfg_condition == cfg_select)) {


        if ( include_stack_ptr >= MAX_INCLUDE_DEPTH ) {
		ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
		 "Probable source loop in configuration files");
		return (-1);
        }
        include_stack[include_stack_ptr++] =
                YY_CURRENT_BUFFER;

	fname = strcpy( (char *)malloc(namelen), yytext+i );
	if (this_fname[include_stack_ptr]) free(this_fname[include_stack_ptr]);
	this_fname[include_stack_ptr] = strdup(fname);

	save_yyin = yyin;
	yyin = NULL;

	yyin = open_file(fname, 1, OF_VERBOSE, rcf_count);

	if (yyin == NULL) {
		ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
		 "Source file %s not found\n", fname);
		include_stack_ptr--;
	}

	if (yyin == NULL) yyin = save_yyin;
	else {
            cfg_condition = -1;
	    rcf_count++;
            yy_switch_to_buffer(
                yy_create_buffer( yyin, YY_BUF_SIZE ) );
	}
    }
    cfg_condition = -1;
}  

<<EOF>> {
        if ( --include_stack_ptr < 0 ) {
                yyterminate();
        }
        else {
		if (yyin) close_file(yyin);
                yy_delete_buffer( YY_CURRENT_BUFFER );
		rcf_count--;
                yy_switch_to_buffer(
                     include_stack[include_stack_ptr] );
        }
}

^{owh}"drumset"{wh}[0-9]+{wh}("sf"|"sbk"){wh}("#N"{wh})?{nm}	{
	char *bank_name = NULL;
	int i = 0;
	while (isspace(yytext[i])) i++;
	i += 7;
	current_drumset = atoi(yytext+i);
	doing_drums = 1;
	doing_sf = 1;
	new_toneset(current_drumset, MAXPROG);
	font_type=FONT_SF;
    if (bank && !bank->name) {
	while (isspace(yytext[i])) i++;
	while (isdigit(yytext[i])) i++;
	while (isspace(yytext[i])) i++;
	if (yytext[i+1] == 'f') i += 2; else i += 3;
	while (isspace(yytext[i])) i++;
	if (yytext[i] == '#') i += 2;
	while (isspace(yytext[i])) i++;
	patchname = yytext + i;
	if (patchname[0] == '"') {
		bank_name = strncpy( (char *)malloc(strlen(patchname)-1), patchname + 1, strlen(patchname)-1 );
		bank_name[strlen(patchname)-2] = '\0';
	}
	else bank_name = strcpy( (char *)malloc(strlen(patchname)+1), patchname );
	bank->name = bank_name;
    }

}
^{owh}"drumset"{wh}[0-9]+{wh}("sf"|"sbk")	{
	int i = 0;
	while (isspace(yytext[i])) i++;
	i += 7;
	current_drumset = atoi(yytext+i);
	doing_drums = 1;
	doing_sf = 1;
	new_toneset(current_drumset, MAXPROG);
	font_type=FONT_SF;
}
^{owh}"drums"("et"|"fx"){wh}[0-9]+{wh}("#N"{wh})?{nm}	{
	char *bank_name = NULL;
	int i = 0;
	int is_sfx = 0;
	while (isspace(yytext[i])) i++;
	i += 7;
	if (yytext[i-1] == 'x') is_sfx = 1;
	current_drumset = atoi(yytext+i);
	if (is_sfx) current_drumset += SFXBANK;
	doing_drums = 1;
	doing_sf = 0;
	new_toneset(current_drumset, is_sfx? SFXBANK+MAXPROG : MAXPROG);
	font_type=FONT_GUS;
    if (bank && !bank->name) {
	while (isspace(yytext[i])) i++;
	while (isdigit(yytext[i])) i++;
	while (isspace(yytext[i])) i++;
	if (yytext[i] == '#') i += 2;
	while (isspace(yytext[i])) i++;
	patchname = yytext + i;
	if (patchname[0] == '"') {
		bank_name = strncpy( (char *)malloc(strlen(patchname)-1), patchname + 1, strlen(patchname)-1 );
		bank_name[strlen(patchname)-2] = '\0';
	}
	else bank_name = strcpy( (char *)malloc(strlen(patchname)+1), patchname );
	bank->name = bank_name;
    }
}
^{owh}"drums"("et"|"fx"){wh}[0-9]+	{
	char *bank_name = NULL;
	int i = 0;
	int is_sfx = 0;
	while (isspace(yytext[i])) i++;
	i += 7;
	if (yytext[i-1] == 'x') is_sfx = 1;
	current_drumset = atoi(yytext+i);
	if (is_sfx) current_drumset += SFXBANK;
	doing_drums = 1;
	doing_sf = 0;
	new_toneset(current_drumset, is_sfx? SFXBANK+MAXPROG : MAXPROG);
	font_type=FONT_GUS;
    if (bank && !bank->name) switch (current_drumset) {
	case SFXDRUM1:
	case SFXDRUM2:
	    bank_name = "rhythm sfx";
	    break;
	case TECHNO_KS:
	    bank_name = "techno k/s";
	    break;
	case TECHNO_HI:
	    bank_name = "techno hi";
	    break;
	case TECHNO_LO:
	    bank_name = "techno lo";
	    break;
	case SAKURA:
	    bank_name = "sakura";
	    break;
	case SMALL_LATIN:
	    bank_name = "latin";
	    break;
	default:
	    break;
    }
        if (bank_name && bank)
	    bank->name = strcpy( (char *)malloc(strlen(bank_name)+1), bank_name );
}
^{owh}"bank"{wh}[0-9]+{wh}("sf"|"sbk")	{
	int i = 0;
	while (isspace(yytext[i])) i++;
	i += 4;
	current_toneset = atoi(yytext+i);
	doing_drums = 0;
	doing_sf = 1;
	new_toneset(current_toneset, MAXPROG);
	font_type=FONT_SF;
}
^{owh}"bank"{wh}[0-9]+	{
	int i = 0;
	while (isspace(yytext[i])) i++;
	i += 4;
	current_toneset = atoi(yytext+i);
	doing_drums = 0;
	doing_sf = 0;
	new_toneset(current_toneset, MAXPROG);
	font_type=FONT_GUS;
}
^{owh}"mu100"{wh}[0-9]+	{
	int i = 0;
	while (isspace(yytext[i])) i++;
	i += 5;
	current_toneset = atoi(yytext+i) + MU100BANK;
	doing_drums = 0;
	doing_sf = 0;
	new_toneset(current_toneset, MU100BANK+MAXPROG);
	font_type=FONT_GUS;
}
^{owh}"vl"{wh}[0-9]+	{
	int i = 0;
	while (isspace(yytext[i])) i++;
	i += 2;
	current_toneset = atoi(yytext+i) + VLBANK;
	doing_drums = 0;
	doing_sf = 0;
	new_toneset(current_toneset, VLBANK+7);
	font_type=FONT_GUS;
}
^{owh}("sfx"|"bankxg"){wh}[0-9]+	{
	int i = 0;
	while (isspace(yytext[i])) i++;
	if (yytext[i] == 's') i += 3;
	else i += 6;
	current_toneset = atoi(yytext+i) + SFXBANK;
	doing_drums = 0;
	doing_sf = 0;
	new_toneset(current_toneset, SFXBANK+MAXVAR);
	font_type=FONT_GUS;
}
^{owh}"sfx"	{
	current_toneset = SFXBANK;
	doing_drums = 0;
	doing_sf = 0;
	new_toneset(current_toneset, SFXBANK+1);
	font_type=FONT_GUS;
}
^{owh}"drumsfx1"	{
	current_drumset = SFXDRUM1;
	doing_drums = 1;
	doing_sf = 0;
	new_toneset(current_toneset, SFXDRUM1+1);
	font_type=FONT_GUS;
	if (bank) bank->name = "drumsfx1";
}
^{owh}"drumsfx2"	{
	current_drumset = SFXDRUM2;
	doing_drums = 1;
	doing_sf = 0;
	new_toneset(current_toneset, SFXDRUM2+1);
	font_type=FONT_GUS;
	if (bank) bank->name = "drumsfx2";
}

^{owh}"[Melodic Patches]" {
	doing_drums = 0;
}
^{owh}"[Drum Patches]" {
	doing_drums = 1;
	current_drumset = current_toneset;
}

^{owh}[0-9,]+("="|{wh}){nm} {
	const char *gm_name = NULL;
	char *vc_name = NULL;
	patchno = atoi(yytext);
	tone_bank = 0;
	if (patchno >= 0) {
		int i = 0;
		while (isspace(yytext[i])) i++;
		while (isdigit(yytext[i])) i++;
		if (yytext[i] == ',') {
			i++;
			tone_bank = atoi(yytext + i);
			while (isdigit(yytext[i])) i++;
		}
		else {
			if (doing_drums) tone_bank = current_drumset;
			else tone_bank = current_toneset;
		}
		new_toneset(tone_bank, 0);

		while (isspace(yytext[i])) i++;
		if (!cfg_flag && yytext[i] == '=') i++;
		patchname = yytext + i;
		if (patchname[0] == '"') {
			vc_name = strncpy( (char *)malloc(strlen(patchname)-1), patchname + 1, strlen(patchname)-1 );
			vc_name[strlen(patchname)-2] = '\0';
		}
		else vc_name = strcpy( (char *)malloc(strlen(patchname)+1), patchname );
		if (patchno < 128) gm_name = gm_voice[doing_drums? patchno+128 : patchno].vname;
		else {
		    ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Bad patch number %d in cfg file.", patchno);
		}
		if (gm_name == NULL) gm_name = (doing_drums)? "drum":"instr";

	        ctl->cmsg(CMSG_INFO, VERB_DEBUG, "config: voice %s[%d,%d] = patch %s",
			gm_name, patchno, tone_bank, vc_name);

		new_patch(vc_name, patchno);

	}
	BEGIN(P);
}

^{owh}[A-Z][^\n\r\t\=,#]+(","{owh}[0-9]+)?{owh}"="{owh}{nm} {
	const char *gm_name = NULL;
	char *vc_name = NULL;
	int i;
	set_patchno(yytext);
	tone_bank = 0;
	if (patchno < 256) {
		i = 0;
		while (yytext[i] != '=' && yytext[i] != ',') i++;
		if (yytext[i] == ',') {
			i++;
			tone_bank = atoi(yytext + i);
		}
		else {
			if (doing_drums) tone_bank = current_drumset;
			else tone_bank = current_toneset;
		}

		while (yytext[i] != '=') i++;
		i++;
		while (isspace(yytext[i])) i++;
		patchname = yytext + i;
		if (patchname[0] == '"') {
			vc_name = strncpy( (char *)malloc(strlen(patchname)-1), patchname + 1, strlen(patchname)-1 );
			vc_name[strlen(patchname)-2] = '\0';
		}
		else vc_name = strcpy( (char *)malloc(strlen(patchname)+1), patchname );
		gm_name = gm_voice[patchno].vname;
		if (gm_name == NULL) gm_name = (doing_drums)? "drum":"instr";

	        ctl->cmsg(CMSG_INFO, VERB_DEBUG, "config: voice %s[%d,%d] = patch %s",
			gm_name, patchno, tone_bank, vc_name);

	}
	else {
		ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Bad line \"%s ...\" in cfg file.", yytext);
	}
	new_patch(vc_name, patchno);
	
	BEGIN(P);
}

^{owh}("#option"{wh})?"-p"{owh}[0-9]+	{ if (prescanning) {
		int i, j;
		i=0; while(isspace(yytext[i])) i++;
		if (yytext[i] == '#') {
			i += 7;
			while(isspace(yytext[i])) i++;
		}
		j = atoi(yytext+i+2);
		if (j > 1 && j < MAX_VOICES) voices_ceiling = j;
	} }
^{owh}("#option"{wh})?"-A"{wh}[0-9]+	{ if (prescanning) {
		int i, j;
		i=0; while(isspace(yytext[i])) i++;
		if (yytext[i] == '#') {
			i += 7;
			while(isspace(yytext[i])) i++;
		}
		j = atoi(yytext+i+2);
		if (j > 1 && j < MAX_AMPLIFICATION) amplification = j;
	} }
^{owh}("#option"{wh})?"-C"{owh}[0-9]+	{ if (prescanning) {
		int i, j;
		i=0; while(isspace(yytext[i])) i++;
		if (yytext[i] == '#') {
			i += 7;
			while(isspace(yytext[i])) i++;
		}
		j = atoi(yytext+i+2);
		if (j > 1 && j < MAX_CONTROL_RATIO) control_ratio = j;
	} }
^{owh}("#option"{wh})?"-s"{owh}[0-9]+	{ if (prescanning) {
		int i, j;
		i=0; while(isspace(yytext[i])) i++;
		if (yytext[i] == '#') {
			i += 7;
			while(isspace(yytext[i])) i++;
		}
		j = atoi(yytext+i+2);
		if (j < 100) j *= 1000;
		if (j > MIN_OUTPUT_RATE && j < MAX_OUTPUT_RATE) play_mode->rate = j;
	} }
^{owh}("#option"{wh})?"-r"{owh}[0-9]+	{ if (prescanning) {
		int i, j;
		i=0; while(isspace(yytext[i])) i++;
		if (yytext[i] == '#') {
			i += 7;
			while(isspace(yytext[i])) i++;
		}
		j = atoi(yytext+i+2);
		max_patch_memory = j * 1000000;
	} }
^{owh}("#option"{wh})?"-k"{owh}[0-9]+	{ if (prescanning) {
		int i, j;
		i=0; while(isspace(yytext[i])) i++;
		if (yytext[i] == '#') {
			i += 7;
			while(isspace(yytext[i])) i++;
		}
		j = atoi(yytext+i+2);
		if (j >= 0 && j <= 3)
	    	  switch (j) {
		    case 0: current_interpolation = INTERPOLATION_LINEAR;
			    break;
		    case 1: current_interpolation = INTERPOLATION_CSPLINE;
			    break;
		    case 2: current_interpolation = INTERPOLATION_LAGRANGE;
			    break;
		    default:
		    case 3: current_interpolation = INTERPOLATION_CSPLINE | INTERPOLATION_BUTTERWORTH;
			    break;
	    	  }
	} }
^{owh}("#option"{wh})?"-X"{owh}[0-9]+	{ if (prescanning) {
		int i, j;
		i=0; while(isspace(yytext[i])) i++;
		if (yytext[i] == '#') {
			i += 7;
			while(isspace(yytext[i])) i++;
		}
		j = atoi(yytext+i+2);
		if (j >= 0 && j <= 2) opt_expression_curve = j;
	} }
^{owh}("#option"{wh})?"-V"{owh}[0-9]+	{ if (prescanning) {
		int i, j;
		i=0; while(isspace(yytext[i])) i++;
		if (yytext[i] == '#') {
			i += 7;
			while(isspace(yytext[i])) i++;
		}
		j = atoi(yytext+i+2);
		if (j >= 0 && j <= 2) opt_volume_curve = j;
	} }
^{owh}("#option"{wh})?"-O"{owh}[a-zA-Z]	{ if (prescanning) {
		int i;
		i=0; while(isspace(yytext[i])) i++;
		if (yytext[i] == '#') {
			i += 7;
			while(isspace(yytext[i])) i++;
		}
		i+=2;
		while(isspace(yytext[i])) i++;
		set_play_mode(yytext+i);
	} }
^{owh}("#option"{wh})?"-F"{owh}[01]	{ if (prescanning) {
		int i;
		i=0; while(isspace(yytext[i])) i++;
		if (yytext[i] == '#') {
			i += 7;
			while(isspace(yytext[i])) i++;
		}
		i+=2;
		fast_load = atoi(yytext+i);
	} }

{owh}"#".*	;
^#.*\n		this_line[include_stack_ptr]++;

<P>{lwh}"amp"{owh}"="{owh}[0-9]+	{
	int i = 0;
	while (yytext[i] != '=') if (yytext[i++]=='\n') this_line[include_stack_ptr]++;
	if (bank) bank->tone[patchno].amp = atoi(yytext+i+1);
	BEGIN(P);
}
<P>{lwh}"note"{owh}"="{owh}[0-9]+	{
	int i = 0;
	while (yytext[i] != '=') if (yytext[i++]=='\n') this_line[include_stack_ptr]++;
	if (bank) bank->tone[patchno].note = atoi(yytext+i+1);
	BEGIN(P);
}
<P>{lwh}"cutoff"{owh}"="{owh}[0-9]+	{
	int i = 0;
	while (yytext[i] != '=') if (yytext[i++]=='\n') this_line[include_stack_ptr]++;
	if (bank) bank->tone[patchno].cutoff = atoi(yytext+i+1);
	BEGIN(P);
}
<P>{lwh}"tuning"{owh}"="{owh}("+"|"-")[0-9]+	{
	int i = 0;
	while (yytext[i] != '=') if (yytext[i++]=='\n') this_line[include_stack_ptr]++;
	if (bank) bank->tone[patchno].tuning = atoi(yytext+i+1);
	BEGIN(P);
}
<P>{lwh}"keep"{owh}"="	BEGIN(K);
<P>{lwh}"strip"{owh}"="	BEGIN(S);

<K>{owh}"loop"	{
		  if (bank) bank->tone[patchno].strip_loop=0;
	BEGIN(P);
}
<K>{owh}"tail"	{
		  if (bank) bank->tone[patchno].strip_tail=0;
	BEGIN(P);
}
<K>{owh}"env"	{
		  if (bank) bank->tone[patchno].strip_envelope=0;
	BEGIN(P);
}
<K>{owh}"sustain"	{
		  if (bank) bank->tone[patchno].sustain=0;
	BEGIN(P);
}
<S>{owh}"loop"	{
		  if (bank) bank->tone[patchno].strip_loop=1;
	BEGIN(P);
}
<S>{owh}"tail"	{
		  if (bank) bank->tone[patchno].strip_tail=1;
	BEGIN(P);
}
<S>{owh}"env"	{
		  if (bank) bank->tone[patchno].strip_envelope=1;
	BEGIN(P);
}
<S>{owh}"sustain"	{
		  if (bank) bank->tone[patchno].sustain=1;
	BEGIN(P);
}
^{owh}[A-Z][^\n\r\t\:#]+{owh}":"{owh}"poly"  {
	set_patchno(yytext);
	if (patchno < 256) gm_voice[patchno].flags &= ~SOLO_MASK;
}
^{owh}[A-Z][^\n\r\t\:#]+{owh}":"{owh}"solo"  {
	set_patchno(yytext);
	if (patchno < 256) gm_voice[patchno].flags |= SOLO_MASK;
}

\n	{
	patchno = -1;
	this_line[include_stack_ptr]++;
	BEGIN(0);
}
.	;

%%

static void new_toneset(int n, int highest)
{
	banknum=n;

	if (!highest) highest = MAXBANK;
	if (n < 0 || n >= MAXBANK || n >= highest) {
		bank = NULL;
		ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
		 "Bank index %d out of range (max %d) in file %s line %d",
			 n, highest, this_fname[include_stack_ptr], this_line[include_stack_ptr]);
		return;
	}
	if (!doing_drums) {
	  if (!tonebank[n]) {
		tonebank[n]=(ToneBank *)safe_malloc(sizeof(ToneBank));
	      	memset(tonebank[n], 0, sizeof(ToneBank));
	  }
	  bank=tonebank[n];
	}
	else {
	  if (!drumset[n]) {
		drumset[n]=(ToneBank *)safe_malloc(sizeof(ToneBank));
	      	memset(drumset[n], 0, sizeof(ToneBank));
	  }
	  bank=drumset[n];
	}
}

static void new_patch(const char *vname, int num)
{
	if (!bank) return;
	if (num < 0 || num > 127) {
		ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
		 "Patch index %d out of range in file %s line %d",
			 num, this_fname[include_stack_ptr], this_line[include_stack_ptr]);
		return;
	}
	if (bank->tone[num].name) return;
	bank->tone[num].name=vname;
	bank->tone[num].note=bank->tone[num].amp=bank->tone[num].pan=
	  bank->tone[num].strip_loop=bank->tone[num].strip_envelope=
	    bank->tone[num].sf_ix=
	    bank->tone[num].cutoff=
	    bank->tone[num].sustain=
	    bank->tone[num].strip_tail=bank->tone[num].last_used=-1;
	bank->tone[num].font_type=font_type;
	bank->tone[num].tuning=0;
	bank->tone[num].layer=0;
}

static void set_patchno(char *m)
{
	int i,j,w,y;
	const char *vn;
	y = w = 0;
	while (isspace(m[w])) w++;
	for (i = 0; i < 256; i++) {
		vn = gm_voice[i].vname;
		if (vn == NULL) continue;
		for (j = 0; m[j+w] && vn[j+y] && m[j+w] == vn[j+y]; j++)
		    if (vn[j+y+1] && isspace(vn[j+y+1]) && m[j+w+1] && !isspace(m[j+w+1]) ) y++ ;
		if (!m[j+w] || m[j+w] == '=' || m[j+w] == ',' || m[j+w] == ':') break;
		if (!vn[j+y] && isspace(m[j+w])) break;
	}
	if (i < 256 && vn != NULL) patchno = i;
	else patchno = 256;
}


char *current_config_file = 0;

int read_config_file(const char *name, int prescan)
{
	int retvalue = 0;
	prescanning = prescan;
	include_stack_ptr = 0;
	rcf_count = 1;

	if (prescanning) {
	   current_config_file = (char *)safe_malloc(strlen(name)+1);
	   strcpy(current_config_file, name);
	}

	yyin = open_file(name, 1, OF_VERBOSE, rcf_count);
	if (this_fname[include_stack_ptr]) free(this_fname[include_stack_ptr]);
	this_fname[include_stack_ptr] = strdup(name);

	if (!yyin) return -1;
	current_toneset = current_drumset = 0;
	doing_drums = doing_sf = 0;
	BEGIN(0);
	if (!(retvalue = yylex())) {
	   if (prescan) got_a_configuration = 1;
	   else got_a_configuration = 2;
	}
	close_file(yyin);
	return retvalue;
}

static SFInfo sfinfo;

static char *getname(char *p);
static void print_sbk(SFInfo *sf);

static int check_sbk(SFInfo *sf);

static int sf2tab(char *sffname)
{
	FILE *fp;

	if ((fp = open_file(sffname, 1, OF_VERBOSE, 0)) == NULL) {
		return 0;
	}

	load_sbk(fp, &sfinfo);
	fclose(fp);

	if (!check_sbk(&sfinfo)) return 0;

	print_sbk(&sfinfo);
	if (!sf_count) return 0;

	a_sf[sf_count].font_code = FONT_SBK;
	a_sf[sf_count].num = 0;
	a_sf[sf_count].name = strdup( sffname );
	sf_count++;

	free_sbk(&sfinfo);
	return 1;
}


static char *getname(char *p)
{
	int i;
	static char buf[21];
	strncpy(buf, p, 20);
	buf[20] = 0;
	for (i = 19; i > 4 && buf[i]==' '; i--) {
	  buf[i] = 0;
	}
	for (i = 0; buf[i]; i++) {
	  if (buf[i] == ' ') buf[i] = '_';
	  if (buf[i] == '#') buf[i] = '@';
	}
	i = strlen(buf);
	if (i > 4 && buf[i-3] == '(' && buf[i-2] == 'L' && buf[i-1] == ')')
		buf[i-3] = '\0';
	return buf;
}

static int check_sbk(SFInfo *sf) {
	if (sf->version != 2) return 0;
	return 1;
}


static void print_sbk(SFInfo *sf)
{
    int i, b_nk, preset, lastpatch;
    int lastbank = -1;
    tpresethdr *ip;
    tinsthdr *tp;

    sf_count = 0;

    for (b_nk = 0; b_nk <= 128; b_nk++) {
      for (preset = 0; preset <= 127; preset++) {
      lastpatch = -1;
	for (i = 0, ip = sf->presethdr; i < sf->nrpresets-1; i++) {
	    int b, g, inst, sm_idx;
	    if (ip[i].bank != b_nk) continue;
	    if (ip[i].preset != preset) continue;
	    inst = -1;
	    for (b = ip[i].bagNdx; b < ip[i+1].bagNdx; b++) {
		for (g = sf->presetbag[b]; g < sf->presetbag[b+1]; g++)
		   if (sf->presetgen[g].oper == SF_instrument) {
			inst = sf->presetgen[g].amount;
			break;
		   }
	    }
	    if (inst < 0) continue;
	    tp = sf->insthdr;
	    if (b_nk != 128) {
		int realwaves = 0;
		if (b_nk != lastbank) {
			a_sf[sf_count].font_code = FONT_TONESET;
			a_sf[sf_count].num = b_nk;
			a_sf[sf_count].name = NULL;
			if (sf_count < MAX_SF_COUNT-2)
			sf_count++;
			lastbank = b_nk;
		}
		for (b = tp[inst].bagNdx; b < tp[inst+1].bagNdx; b++) {
		    sm_idx = -1;
		    for (g = sf->instbag[b]; g < sf->instbag[b+1]; g++) {
			if (sf->instgen[g].oper == SF_sampleId) sm_idx = sf->instgen[g].amount;
		    }
		    if (sm_idx >= 0 && sf->sampleinfo[sm_idx].sampletype < 0x8000) realwaves++;
		}
		if (realwaves && ip[i].preset != lastpatch) {
			a_sf[sf_count].font_code = FONT_PRESET;
			a_sf[sf_count].num = ip[i].preset;
			a_sf[sf_count].name = strdup( getname(ip[i].name) );
			if (sf_count < MAX_SF_COUNT-2)
			sf_count++;
		    lastpatch = ip[i].preset;
		}
	    }
	    else {
		int keynote, c, dpreset;
			a_sf[sf_count].font_code = FONT_DRUMSET;
			a_sf[sf_count].num = ip[i].preset;
			a_sf[sf_count].name = strdup( getname(ip[i].name) );
			if (sf_count < MAX_SF_COUNT-2)
			sf_count++;

		for (dpreset = 0; dpreset < 128; dpreset++)
	        for (c = ip[i].bagNdx; c < ip[i+1].bagNdx; c++) {
		  inst = -1;
		  for (g = sf->presetbag[c]; g < sf->presetbag[c+1]; g++)
		   if (sf->presetgen[g].oper == SF_instrument) {
			inst = sf->presetgen[g].amount;
			break;
		   }
		  if (inst >= 0) for (b = tp[inst].bagNdx; b < tp[inst+1].bagNdx; b++) {
		    int hikeynote = -1;
		    sm_idx = keynote = -1;
		    for (g = sf->instbag[b]; g < sf->instbag[b+1]; g++) {
			if (sf->instgen[g].oper == SF_sampleId) sm_idx = sf->instgen[g].amount;
			else if (sf->instgen[g].oper == SF_keyRange) {
			    keynote = sf->instgen[g].amount & 0xff;
			    hikeynote = (sf->instgen[g].amount >> 8) & 0xff;
			}
		    }
		    if (sm_idx < 0) continue;
		    if (sf->sampleinfo[sm_idx].sampletype >= 0x8000) continue;
		    /*if (keynote != dpreset) continue;*/
		    if (dpreset < keynote) continue;
		    if (dpreset > hikeynote) continue;
		    if (sm_idx >= 0 && keynote >= 0 && dpreset != lastpatch) {
			a_sf[sf_count].font_code = FONT_PRESET;
			a_sf[sf_count].num = dpreset;
			a_sf[sf_count].name = strdup( getname( sf->samplenames[sm_idx].name ) );
			if (sf_count < MAX_SF_COUNT-2)
			sf_count++;
			lastpatch = dpreset;
		    }
		  }
	        }

	    }
	}
      }
    }
}


static void
interpret_sf(void)
{
	int i = 0;

	while (1) {
		int action, which;
		const char *sf_name;
		//char *vc_name = NULL;

		action = a_sf[i].font_code;
		which = a_sf[i].num;
		sf_name = a_sf[i].name;

		switch (action) {
			case FONT_TONESET:
				current_toneset = which;
				doing_drums = 0;
				doing_sf = 1;
				new_toneset(current_toneset, MAXPROG);
				font_type=FONT_SBK;
				break;
			case FONT_DRUMSET:
				current_drumset = which;
				doing_drums = 1;
				doing_sf = 1;
				new_toneset(current_drumset, MAXPROG);
				font_type=FONT_SBK;
    				if (!bank->name) bank->name = sf_name;
				break;
			case FONT_PRESET:
				patchno = which;
				if (doing_drums) tone_bank = current_drumset;
				else tone_bank = current_toneset;
				new_toneset(tone_bank, MAXPROG);
				new_patch(sf_name, patchno);
				break;
			case FONT_SBK:
				init_soundfont(sf_name, which, which, 1);
				break;
		}

		if (action == FONT_SBK) break;
		i++;
		if (i >= sf_count) break;
	}

}

