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

static char cart[] = {
	/* entry point */
	0x00, 0x00, 0x00, 0x00,

	/* nintendo logo */
	0x24,0xFF,0xAE,0x51,0x69,0x9A,0xA2,0x21,
	0x3D,0x84,0x82,0x0A,0x84,0xE4,0x09,0xAD,
	0x11,0x24,0x8B,0x98,0xC0,0x81,0x7F,0x21,
	0xA3,0x52,0xBE,0x19,0x93,0x09,0xCE,0x20,
	0x10,0x46,0x4A,0x4A,0xF8,0x27,0x31,0xEC,
	0x58,0xC7,0xE8,0x33,0x82,0xE3,0xCE,0xBF,
	0x85,0xF4,0xDF,0x94,0xCE,0x4B,0x09,0xC1,
	0x94,0x56,0x8A,0xC0,0x13,0x72,0xA7,0xFC,
	0x9F,0x84,0x4D,0x73,0xA3,0xCA,0x9A,0x61,
	0x58,0x97,0xA3,0x27,0xFC,0x03,0x98,0x76,
	0x23,0x1D,0xC7,0x61,0x03,0x04,0xAE,0x56,
	0xBF,0x38,0x84,0x00,0x40,0xA7,0x0E,0xFD,
	0xFF,0x52,0xFE,0x03,0x6F,0x95,0x30,0xF1,
	0x97,0xFB,0xC0,0x85,0x60,0xD6,0x80,0x25,
	0xA9,0x63,0xBE,0x03,0x01,0x4E,0x38,0xE2,
	0xF9,0xA2,0x34,0xFF,0xBB,0x3E,0x03,0x44,
	0x78,0x00,0x90,0xCB,0x88,0x11,0x3A,0x94,
	0x65,0xC0,0x7C,0x63,0x87,0xF0,0x3C,0xAF,
	0xD6,0x25,0xE4,0x8B,0x38,0x0A,0xAC,0x72,
	0x21,0xD4,0xF8,0x07,

	/* title */
	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
	0x20, 0x20, 0x20, 0x20, 0x20,

	/* game code */
	'A', 'P', '9', 'E',

	/* maker code */
	'P', '9',

	/* fixed */
	0x96,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,

	/* version */
	0x00,

	/* compliment */
	0x00,

	/* reserved */
	0x00, 0x00,
};

static int infd, outfd;

static void
io(ulong count)
{
	static char buf[8192];
	long n, n2;

	do {
		n2 = count > sizeof buf ? sizeof buf : count;
		n = read(infd, buf, n2);
		if(n <= 0)
			sysfatal("read: %r");
		if(write(outfd, buf, n) != n)
			sysfatal("write: %r");
		count -= n;
	} while(count != 0);
}

static void
usage(void)
{
	fprint(2, "usage: %s a.out\n", argv0);
	exits("usage");
}

void
main(int argc, char **argv)
{
	Fhdr fhdr;
	u32int rzero;
	ulong ep;
	char *ofile, *iname;
	char *p, *e;
	int n;
	char c;

	ofile = nil;
	rzero = 0x08000000;
	ARGBEGIN {
	case 'Z': rzero = strtoull(EARGF(usage()), 0, 0); break;
	case 'o': ofile = strdup(EARGF(usage())); break;
	default: usage();
	} ARGEND;

	if(argc != 1)
		usage();
	infd = open(argv[0], OREAD);
	if(infd < 0)
		sysfatal("infd: %r");
	if(crackhdr(infd, &fhdr) == 0)
		sysfatal("crackhdr: %r");
	if(fhdr.type != FARM && fhdr.type != FARMB)
		sysfatal("not an arm32 a.out");
	iname = strrchr(argv[0], '/');
	if(iname != nil)
		iname++;
	else
		iname = argv[0];
	if(ofile == nil)
		ofile = smprint("%s.gba", iname);
	outfd = create(ofile, OWRITE|OTRUNC, 0666);
	if(outfd < 0)
		sysfatal("create: %r");

	ep = 0xea000000; //B main
	ep += (fhdr.entry - rzero - 8)/4;
	cart[3] = ep>>24;
	cart[2] = ep>>16;
	cart[1] = ep>>8;
	cart[0] = ep;

	p = cart + 0xA0;
	n = strlen(iname);
	if(n > 12)
		sysfatal("title too long");
	memcpy(p, iname, n); //no null

	e = cart + 0xBD;
	for(c = 0; p < e; p++)
		c += *p;
	*p = -(0x19+c);

	if(write(outfd, cart, sizeof cart) != sizeof cart)
		sysfatal("write: %r");

	seek(infd, fhdr.txtoff, 0);
	io(fhdr.txtsz);

	/* allignment */
	assert(fhdr.datoff >= fhdr.txtoff + fhdr.txtsz);
	seek(outfd, fhdr.datoff - (fhdr.txtoff + fhdr.txtsz), 1);

	seek(infd, fhdr.datoff, 0);
	io(fhdr.datsz);
	exits(nil);
}
