/* ------------------------------------------------------------------
                        MML compiler [M2]
               programmed by S.Yamamoto (SHINNOSUKE)
                   m2ctrl.c  --  control line
------------------------------------------------------------------ */

#include	<stdio.h>
#include	<stdlib.h>
#include	<ctype.h>
#include	<string.h>
#include	<limits.h>
#include	"m2.h"

#define	SUCCSESS	0
#define	FAILURE	(-1)
#define	TRUE	1
#define	FALSE	0
#define	ERR	(-1)
#define	YES	1
#define	NO	0

	/* ROLAND EXCLUSIVE SUM 計算 */
#define	CHECKSUM(x)	(((x)%128!=0)?128-(x)%128:0)

static	char	*ctrlCommand[] = {
	"title",
	"copyright",
	"compile",
	"begin",
	"tempo",
	"signature",
	"malloc",
	"division",
	"part",
	"rhmPart",
	"rhmNote",
	"program",
	"memo",
	"\0"
};

void	makeHeader(int d)	/* ＳＭＦヘッダ */
{
	int	d1;
	int	d2;

	d1 = (d & 0xff00) >> 8;
	d2 = d & 0xff;

	putData( 14 ,'M' ,'T' ,'h'
		,'d' ,0 ,0 ,0 ,6 ,0 ,0 ,0 ,1 ,d1 ,d2);
	putData( 8 ,'M' ,'T' ,'r' ,'k' ,0 ,0 ,0 ,0);
	return;
}

void	varNum( unsigned long n )	/* 可変長数値表現 */
{
	int	i;
	int	flags = NO;
	int	d[4];	/* 上限４バイト */

	for( i=0;i<4;i++ )
		d[i] = (int)(( n >> (i*7)) & 0x7f );

	for( i=3;i>=0;i-- ) {
		if( flags == NO && d[i] == 0 && i != 0)
			continue;
		flags = YES;
		putData( 1,(( i==0 ) ? d[i] : d[i] | 0x80));
	}
	return;
}

void	putText( char *t )	/* テキスト情報を書き込む */
{
	int	i;
	int	l;

	l = strlen(t);
	varNum( l );
	for( i=0;i<l;i++ )
		putData( 1 ,*(t+i) );
	return;
}

void	putTempo( int t )	/* テンポを書き込む */
{
	int	tempo1;
	int	tempo2;
	int	tempo3;
	unsigned long	tempo;

	tempo = 60000000 / (unsigned long) t;

	tempo1 = (int) (( tempo & 0xff0000 ) >> 16 );
	tempo2 = (int) (( tempo & 0xff00 ) >> 8 );
	tempo3 = (int) ( tempo & 0xff );

	putData( 6 ,0xff ,0x51 ,0x03 ,tempo1 ,tempo2 ,tempo3 );
	return;
}

void	putSignature( int n,int d )	/* 拍子を書き込む */
{
	int	i;
	int	dummy;
	int	denom=ERR;

	for( i=0;i<7;i++ ) {
		dummy = power( 2 ,i );
		if( d == dummy ) {
			denom = i;
			break;
		}
	}

	if( denom == ERR)	denom = 2;
	putData( 7 ,0xff ,0x58 ,0x04 ,n ,denom ,0x18 ,0x08 );
	return;
}

FLGDAT	ctrlLine( void )	/* コントロール行メイン */
{
	char	*param = "param";
	char	*buf;
	char	*rp;
	char	text[3][128];

	int	i;
	int	j;
	int	dummy;
	int	n;
	int	endFlags = NO;

	int	sigNum = ERR;
	int	sigDen;

	FLGDAT	fgset;

	strcpy( text[0] ,"no title" );
	strcpy( text[1] ,"no copyright" );
	strcpy( text[2] ,"created by MML compiler [M2] V2.X" );

	fgset.tempo = 120;	/* 初期状態 テンポ */
	fgset.wksize = 2048;	/* イベント数 */
	fgset.rpart = -1;
	fgset.div = 48;	/* 分解能 */
	fgset.signature[0] = 4;
	fgset.signature[1] = 4;

	for( i=0;i<16;i++ )	fgset.part[i] = i;
	for( i=0;i<=128;i++ )	fgset.program[i] = i;

	for( j=0;j<2;j++ )
		for( i=0;i<26;i++ )
			fgset.rnote[i][j] = ERR;

	do {
		if(( buf=Pre_fgets() ) == NULL )
			errMsg( MSG_freerr ,"No MML data" );
		rp = skipSpace( buf );
		if( *rp == '\0' )
			continue;
		if( *rp != '.' )
			errMsg( MSG_syntax ,"control" );
		rp = skipSpace( ++rp );
		n = comSearch( ctrlCommand ,rp );
		if( n == ERR )
			errMsg( MSG_undctl ,NULL );
		rp += strlen( ctrlCommand[n] );
		rp = skipSpace( rp );

		switch( n ) {
			case 0:
			case 1:
				strncpy( text[n] ,rp ,127);
				break;
			case 2:
			case 3:
				endFlags = YES;
				break;
			case 4:
				fgset.tempo = strToInt( &rp,30,600,0,NULL );
				break;
			case 5:
				sigNum = strToInt( &rp,1,64,-1,NULL );
				rp = skipSpace( rp );
				if( *rp == '/' )	rp++;
				sigDen = strToInt( &rp,1,64,-1,NULL );
				break;
			case 6:
				fgset.wksize = strToInt( &rp,1,INT_MAX,0,NULL );
				break;
			case 7:
				fgset.div = strToInt( &rp,48,480,0,NULL );
				break;
			case 8:
				rp = skipSpace( rp );
				dummy = strToInt( &rp,1,16,-1,NULL ) - 1;
				rp = skipSpace( rp );
				if( *rp == ',' )	rp++;
				fgset.part[dummy] = strToInt( &rp,1,16,-1,NULL ) - 1;
				break;
			case 9:
				fgset.rpart = strToInt( &rp,1,16,-1,NULL ) - 1;
				break;
			case 10:
				dummy = toupper( *rp );
				if( dummy<'A' || dummy>'Z' || dummy == 'R' )
					errMsg(MSG_misopd,param);
				dummy -= 'A';
				rp++;
				for( i=0;i<2;i++ ) {
					rp = skipSpace( rp );
					if( *rp==',' )	rp++;
					fgset.rnote[dummy][i] = strToInt( &rp,0,127,-1,NULL );
				}
				break;
			case 11:
				rp = skipSpace( rp );
				dummy = strToInt( &rp,0,128,-1,NULL );
				rp = skipSpace( rp ); 
				if( *rp == ',' )	rp++;
				fgset.program[dummy] = strToInt( &rp,1,128,-1,NULL );
				break;
		}
	} while( endFlags == NO );

	makeHeader( fgset.div );

	for( i=0;i<3;i++ ) {
		putData( 3,0,0xff,i+1 );
		putText( text[i] );
	}

	putDisp( "Title         :%s\n",text[0] );
	putDisp( "Copyright     :%s\n",text[1] );
	putDisp( "Division      :%d\n",fgset.div );
	putDisp( "Rhythm MML    :" );
	if( fgset.rpart == -1 )
		putDisp( "OFF\n" );
	else putDisp( "ON (part %d)\n",fgset.rpart + 1 );

	putData( 1,0 );
	putTempo( fgset.tempo );

	putDisp( "Top tempo     :%d\n",fgset.tempo );

	if( sigNum != ERR ) {
		putData( 1,0 );
		putSignature( sigNum,sigDen );
		fgset.signature[0] = sigNum;
		fgset.signature[1] = sigDen;
		putDisp( "Signature     :%d/%d\n",sigNum,sigDen );
	}


	return( fgset );
}
