/* =========================================
     zmd2mid 共通コマンド部をMIDファイルに
   ========================================= */

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

#include "structs.h"
#include "zmscinit.h"
#include "MThdINFO.h"
#include "VarLen.h"
#include "Putcode.h"
#include "etc.h"
#include "bulkdump.h"
#include "tempo.h"

extern DWORD wavedatanum[],wavelooppoint[];
extern WORD *wavetable[];
extern BYTE wavelooptype[];

#include "config.h"
MThdINFO makecommon(UBYTE *zbuf,UBYTE **xxMTrk,DWORD *midc_,
			DWORD *zmdc_,DWORD bufs,MThdINFO rc,WORD *envdata,char *copyright,
			DWORD *MDDdelay,DWORD zmsc_delta);

MThdINFO makecommon(UBYTE *zbuf,UBYTE **xxMTrk,DWORD *midc_,
			DWORD *zmdc_,DWORD bufs,MThdINFO rc,WORD *envdata,char *copyright,
			DWORD *MDDdelay,DWORD zmsc_delta)
{
	UBYTE	*MTrk = *xxMTrk;	/* XXX: modified */
	UBYTE *MTrk_ = MTrk;
	DWORD zmdc = 8,val,i,j,k,l;
	DWORD COMdelta = 0, COMdelta_step = *MDDdelay;
	DWORD r;
#ifdef DEBUG
	FILE *fp;
#endif
	putDwordAlign(MTrk,0x4D54726B);		/* == 'MTrk' */
	/* *(unsigned int*)MTrk = 0x4D54726B; */	/* == 'MTrk' */
	MTrk += 4 + 4;

	status_init( NOUSERUNSTAT );		/* ランニングステータス 使用しない */

	while (zbuf[zmdc] != 0xFF) {
		if (MTrk - MTrk_ > (bufs << 10) - 4096) {
			DWORD	offset = MTrk - MTrk_;	/* XXX: modified */
			bufs += 16;
			MTrk_ = (UBYTE*)erealloc((void *)MTrk_,bufs*1024, "MTrk_");
			MTrk = MTrk_ + offset;
		}
		switch (zbuf[zmdc++]) {
			DWORD i,j;
			DWORD val;
			case 0x04:		/* FM音源音色指定(pass) */
				zmdc += 56;
				break;
			case 0x05:		/* テンポ指定 (TIMER-B) */
				rc.tempo = zbuf[zmdc] * 0x100 + zbuf[zmdc + 1];
				zmdc += 2;
				/* printf("TEMPO:%d\n",rc.tempo); */
				i = 0;
				MTrk += tempochg(MTrk,&i,rc.tempo);
				break;
			case 0x15:		/* ベースチャンネル設定 */
				/* FM基準(0):MIDIは10から MIDI基準(1):MIDIは1から */
				/* rc.base = (zbuf[zmdc++]==1)? 1:10; */
				/* printf( "BASE :%d\n",zbuf[zmdc++]); */
				break;
			case 0x18:		/* Exclusive */
				i = zbuf[zmdc] * 0x100 + zbuf[zmdc + 1];
				zmdc += 2;		/* skip size*2 */
				do {
					DWORD val,k,l = 0;
					/* printf( "Excl.:F0 " ); */
					k = zmdc;
					MTrk += PutVarLen(MTrk,COMdelta_step);
					COMdelta += COMdelta_step;
					if (zbuf[zmdc] == 0xF0) {
						zmdc++;
						l = 1;
					}
					*MTrk++ = 0xF0;					/* exclusive */
					val = zmdc;
					while (zbuf[zmdc] != 0xF7 && i > zmdc - k) {
						zmdc++;
					}
					if (zbuf[zmdc] == 0xF7) {
						zmdc++;						/* for F7 */
					}
					val = zmdc - val;
					MTrk += PutVarLen(MTrk,val);
					zmdc -= val;
					/* do { */
						/* printf( "%.2X ",zbuf[zmdc]); */
					/* } while ( (MTrk[midc++] = zbuf[zmdc++]) != 0xF7); */
					for ( j = val - 1; j >= 0; j--) {
						*MTrk++ = zbuf[zmdc++];
					}
						/* printf("\n"); */
					i -= val + l;	/* skip 0xF0分も引く */
					k = zmdc;
				} while (i > 0);
				break;
			case 0x1B:		/* FM音源音色指定(pass) */
				zmdc += 56;
				break;
			case 0x40:		/* ADPCM configrataion */
				zmdc += 19;
				val = zbuf[zmdc] * 0x100 + zbuf[zmdc + 1];
				zmdc += 2;
				if (val) {
					while (zbuf[zmdc++])
						;
				}
				ZTOMFormatMessage( stderr,"skip[ADPCMconfig]\n");
				break;
			case 0x42:		/* 全音符の分解能指定 */
				rc.Z = zbuf[zmdc];
				if (rc.Z == 1) {
					rc.Z = zbuf[zmdc + 3] * 0x100 + zbuf[zmdc + 4];
				}
				zmdc += 5;
				break;
			case 0x4A:		/* 波形メモリデータの登録 */
				i = zbuf[zmdc] * 0x100 + zbuf[zmdc + 1];
				j = zbuf[zmdc + 2];
				if ( j > 31 ) {
#ifndef ENGLISH
					fatal(1,"%d:波形メモリ登録番号が大きすぎます。\n",j);
#else
					fatal(1,"%d: Too large the wave# is.\n",j);
#endif
				} else if (j < 8) {
#ifndef ENGLISH
					fatal(1,"%d:指定の波形メモリ登録番号は予約済みです。\n",j);
#else
					fatal(1,"%d: This wave# is reserved.\n",j);
#endif
				}
				wavedatanum[j] = i;
				wavelooptype[j] = zbuf[zmdc + 3];
				wavelooppoint[j] = zbuf[zmdc + 4] * 0x100 + zbuf[zmdc + 5];
				zmdc += 6;
				wavetable[j] = (WORD *)emalloc(i * sizeof(short), "wavetable[j]");
				for (i = 0; i <= wavedatanum[j] - 1; i++) {
					wavetable[j][i] = (WORD)(zbuf[zmdc] * 0x100 + zbuf[zmdc + 1]);
					zmdc += 2;
				}
				break;
			case 0x60:		/* ADPCM_LIST */
				while (zbuf[zmdc++])
					;
				ZTOMFormatMessage( stderr,"skip[ADPCM_LIST]\n");
				break;
			case 0x61:		/* PRINTコマンド */
				*MTrk++ = 0;	/* delta */
				*MTrk++ = 0xFF;	/* text */
				*MTrk++ = 0x01;
				MTrk += PutVarLen(MTrk,strlen(&zbuf[zmdc]));
				while (*MTrk++ = zbuf[zmdc++])
					;
				MTrk--;		/* \0 を取り除く */
				break;
			case 0x62:		/* MIDI DUMPデータ送信 */
				if (!*MDDdelay) {		/* DUMP送信時Exclusive waitが0なら適当に設定 */
					*MDDdelay = 10;
				}
				i = MTrk - MTrk_;
				i += bulkdump(MTrk,MTrk_,MDDdelay,(BYTE *)&zbuf[zmdc],&bufs);
				MTrk = MTrk_ + i;
				while (zbuf[zmdc++])
					;
				/* printf( "skip[MIDI_DUMP]\n"); */
				break;
			case 0x63:		/* ZPDデータロード */
				while (zbuf[zmdc++])
					;
				ZTOMFormatMessage( stderr,"skip[ADPCM_BLOCK_DATA]\n");
				break;
			case 0x7F:		/* .comment */
				ZTOMFormatMessage( stderr,"TITLE:%s\n",&zbuf[zmdc] );
				*MTrk++ = 0;	/* delta */
				*MTrk++ = 0xFF;	/* sequence name */
				*MTrk++ = 0x03;
				val = zmdc;
				while (zbuf[zmdc++])
					;
				val = zmdc - val;
				MTrk += PutVarLen(MTrk,val - 1);
				zmdc -= val;
				while (*MTrk++ = zbuf[zmdc++])
					;
				MTrk--;		/* \0 を取り除く */
				break;
			default:
#ifndef ENGLISH
				ZTOMFormatMessage( stderr,"makecommon:%ldでの%xはZ-MUSICでは未定義のコマンドです。\n",zmdc - 1,zbuf[zmdc - 1]);
#else
				ZTOMFormatMessage( stderr,"makecommon: undefined code %x in %x.\n",zbuf[zmdc - 1],zmdc - 1);
#endif
				break;
		}
	}

	if (copyright[0]) {		/* 著作権表示 */
		UBYTE *cptmp,*c,*str = copyright;

		c = cptmp = emalloc(strlen(copyright) + 7, "cptmp");
		*c++ = 0;
		*c++ = 0xFF;
		*c++ = 0x02;
		c += PutVarLen(c,strlen(copyright));
		for (i = strlen(copyright) - 1; i >= 0; i--) {
			*c++ = *str++;
		}

		/* 著作権表示は無条件に第１トラックの最初のイベント */
		memmove(MTrk_ + 8 + (c - cptmp), MTrk_ + 8, MTrk - MTrk_ - 8);
		memcpy(MTrk_ + 8,cptmp,c - cptmp);
		MTrk += (c - cptmp);
		efree(cptmp, "cptmp");
	}

	val = 0;
	while (envdata[val]!=-1) {
		val += 2;	/* 最大値 */
	}

	for (i = 0; i <= val / 2 - 1 - 1; i++) {	/* bubble sort */
		int j;
		for (j = i + 1; j <= val / 2 - 1; j++) {
			if (envdata[j * 2] < envdata[i * 2]) {
				WORD temp = envdata[j * 2];
				envdata[j * 2] = envdata[i * 2];
				envdata[i * 2] = temp;
				temp = envdata[j * 2 + 1];
				envdata[j * 2 + 1] = envdata[i * 2 + 1];
				envdata[i * 2 + 1] = temp;
			}
		}
	}


	i = 0;				/* 以下、time signatureを同tick内で先に回す */
	do {
		DWORD i_bar;

		while (envdata[i + 1] / 0x2000 == 1) {	/* time sigantureである間 */
			i += 2;
			if (i >= val) {
				break;
			}
		}
		if (i >= val) {
			break;
									/* 現在 i は調号イベント上 */
		}

		i_bar = envdata[i];			/* 調号の小節番号 */
		j = i + 2;

		while (envdata[j] == i_bar) {
			if (j >= val) {
				break;
			}
			if (envdata[j + 1] / 0x2000 == 1) {	/* もし同tickにTimeSigがあれば */
				WORD temp = envdata[j];
				envdata[j] = envdata[i];
				envdata[i] = temp;
				temp = envdata[j + 1];
				envdata[j + 1] = envdata[i + 1];
				envdata[i + 1] = temp;
				i = j;
			}
			j += 2;
		}
		i += 2;
	} while (i < val);
	/* printf("[sort end]"); */

	/* if (val != 0) { */
		/* int r; */

		i = COMdelta;
		if (*MDDdelay > COMdelta_step) {
			i += *MDDdelay;
		}
		*MDDdelay = i;
		if (zmsc_delta > 0)
			i += zmscinit_delta(zmsc_delta);	/* + *MDDdelay; */
		r = (i)%(rc.Z);
		if (r) {
			r = rc.Z - r;
		}
		*MDDdelay += r;
		if (zmsc_delta > 0)
			r += zmscinit_delta(zmsc_delta);	/* + *MDDdelay; */
	/* } */
	i = 1;					/* 小節 */
	l = k = 4;				/* 何分の何拍子か */
	for (j = 0; j <= val - 1; j += 2) {
		DWORD r = 0;
		MTrk += PutVarLen(MTrk, (envdata[j] - i) * (k * rc.Z) / l + r );
		i = envdata[j];
		switch (envdata[j + 1] / 0x2000) {
			case 0:						/* 調 */
				*MTrk++ = 0xFF;
				*MTrk++ = 0x59;
				*MTrk++ = 0x02;
				*MTrk++ = ((envdata[j + 1] / 2) & 0xFF) - 7;
				*MTrk++ = envdata[j + 1] & 1;
				break;
			case 1:						/* 拍 */
				*MTrk++ = 0xFF;
				*MTrk++ = 0x58;
				*MTrk++ = 0x04;
				*MTrk++ = k = (envdata[j + 1] / 0x20) & 0xFF;
				*MTrk++ = l = envdata[j + 1] & 0x1F;
#ifdef AAA
				l = (1 << l);
				*MTrk++ = rc.Z / l;		/* 自信なし */
#endif
				*MTrk++ = 0x18;			/* v1.53 */
				*MTrk++ = 8;
				break;
			default:
				break;
		}
		/* printf("[小節]"); */
	}
	*MTrk++ = 0x00;
	*MTrk++ = 0xFF;	/* トラックの終わり */
	*MTrk++ = 0x2F;
	*MTrk++ = 0x00;
	*zmdc_ = zmdc;
	*midc_ = MTrk - MTrk_;

	/* *(int *)(MTrk_ + 4) = *midc_ - 8; */
	putDwordAlign(MTrk_ + 4, *midc_ - 8);

	*xxMTrk = MTrk_;

#ifdef DEBUG
	*midc_ -= 4;
	fp=fopen("MTr0","wb");		/* デバッグ用 */
	fwrite(MTrk_+4,sizeof(unsigned char),*midc_,fp);
	fclose(fp);
#endif
	return rc;
}
