#include <u.h>
#include <libc.h>

typedef struct Trk Trk;
struct Trk{
	u32int len;
	uchar *dat;
	uchar *p;
	uchar *end;
	uchar v[16];
	int done;
};
Trk t;
uchar *mcmd, *mp, *me;
int fd;

#define	PBIT16(p,v)	(p)[0]=(v);(p)[1]=(v)>>8
#define BBIT32(p,v)	(p)[3]=(v);(p)[2]=(v)>>8;(p)[1]=(v)>>16;(p)[0]=(v)>>24

void
eread(int fd, void *u, long n)
{
	if(readn(fd, u, n) != n)
		sysfatal("readn: %r");
}

uchar
r8(void)
{
	return *t.p++;
}

void
delay(void)
{
	uchar v;

	do{
		v = r8();
		*mp++ = v;
	}while(v & 0x80);
}

void
putcmd(uchar *cmd, int n)
{
	if(mp + n >= me){
		me += 8192;
		mcmd = realloc(mcmd, me - mcmd);
		if(mcmd == nil)
			sysfatal("realloc: %r");
	}
	memcpy(mp, cmd, n);
	mp += n;
}

void
ev(void)
{
	uchar e, v, cmd[3], *p;

	e = r8();
	p = cmd;
	switch(e >> 4 & 7){
	case 0:
		v = r8() & 0x7f;
		*p++ = e | 0x80;
		*p++ = v;
		*p++ = 0x40;
		break;
	case 1:
		v = r8();
		*p++ = e | 0x80;
		*p++ = v & 0x7f;
		if(v & 0x80)
			t.v[e & 15] = r8() & 0x7f;
		*p++ = t.v[e & 15];
		break;
	case 2:
		v = r8();
		*p++ = e | 0xc0;
		PBIT16(p, v << 7 & 0x7f7f);
		p += 2;
		break;
	case 3:
		v = r8();
		*p++ = 0xb | e & 15;
		switch(v){
		case 10: *p++ = 0x78; break;
		case 11: *p++ = 0x7b; break;
		case 12: *p++ = 0x7e; break;
		case 13: *p++ = 0x7f; break;
		case 14: *p++ = 0x79; break;
		default: sysfatal("unknown system event %ux", v);
		}
		*p++ = 0;
		break;
	case 4:
		v = r8();
		if(v > 9)
			sysfatal("unknown controller %ux", v);
		*p++ = 0xb0 | e & 15;
		switch(v){
		case 1: *p++ = 0x00; break;
		case 2: *p++ = 0x01; break;
		case 3: *p++ = 0x07; break;
		case 4: *p++ = 0x0a; break;
		case 5: *p++ = 0x0b; break;
		case 6: *p++ = 0x5b; break;
		case 7: *p++ = 0x5d; break;
		case 8: *p++ = 0x40; break;
		case 9: *p++ = 0x43; break;
		}
		*p++ = r8() & 0x7f;
		if(v == 0)
			cmd[0] += 0x10;
		break;
	case 6:
		*p++ = 0xff;
		*p++ = 0x2f;
		e = 0;
		t.done++;
		break;
	default:
		sysfatal("unknown event %ux", e >> 4 & 7);
	}
	if((e & 15) == 9)
		cmd[0] |= 6;
	if((e & 15) == 15)
		cmd[0] &= ~6;
	putcmd(cmd, p-cmd);
	if(e & 0x80)
		delay();
	else
		*mp++ = 0;
}

void
reset(void)
{
	memset(t.v, 0x7f, sizeof t.v);
	mcmd = mallocz(t.len * 2, 1);
	if(mcmd == nil)
		sysfatal("mallocz: %r");
	mp = mcmd;
	me = mcmd + t.len * 2;
}

void
barf(void)
{
	static uchar hdr[] = {
		'M', 'T', 'h', 'd',
		0x00, 0x00, 0x00, 0x06,
		0x00, 0x00,
		0x00, 0x01,
		0x01, 0x01,
		'M', 'T', 'r', 'k',
		0x00, 0x00, 0x00, 0x00,
		0x00, 0xb0, 0x07, 0x7f,
		0x00, 0xb1, 0x07, 0x7f,
		0x00, 0xb2, 0x07, 0x7f,
		0x00, 0xb3, 0x07, 0x7f,
		0x00, 0xb4, 0x07, 0x7f,
		0x00, 0xb5, 0x07, 0x7f,
		0x00, 0xb6, 0x07, 0x7f,
		0x00, 0xb7, 0x07, 0x7f,
		0x00, 0xb8, 0x07, 0x7f,
		0x00, 0xb9, 0x07, 0x7f,
		0x00, 0xba, 0x07, 0x7f,
		0x00, 0xbb, 0x07, 0x7f,
		0x00, 0xbc, 0x07, 0x7f,
		0x00, 0xbd, 0x07, 0x7f,
		0x00, 0xbe, 0x07, 0x7f,
		0x00, 0xbf, 0x07, 0x7f,
		0x00, 0xff, 0x51, 0x03, 0x1b, 0x8a, 0x06,
		0x00
	};
	int n;

	n = sizeof(hdr) - 22 + mp - mcmd;
	BBIT32(hdr + 18, n);
	write(1, hdr, sizeof hdr);
	write(1, mcmd, mp - mcmd);
}

void
main(int argc, char *argv[])
{
	int n, ofs;
	uchar s[8], b[1024];

	if(argc > 1){
		fd = open(argv[1], OREAD);
		if(fd < 0)
			sysfatal("open: %r");
	}
	eread(fd, s, sizeof s);
	if(memcmp(s, "MUS\x1a", 4) != 0)
		sysfatal("invalid mus file: %r");
	t.len = s[5] << 8 | s[4];
	ofs = (s[7] << 8 | s[6]) - 8;
	while(ofs > 0){
		n = ofs > sizeof b ? sizeof b : ofs;
		eread(fd, b, n);
		ofs -= n;
	}
	t.dat = malloc(t.len);
	if(t.dat == nil)
		sysfatal("malloc: %r");
	t.p = t.dat;
	t.end = t.dat + t.len;
	eread(fd, t.dat, t.len);
	reset();
	while(!t.done && t.p < t.end)
		ev();
	barf();
	exits(nil);
}
