mul = 'OPQRSTUVWX`abcdefghijklmnopqrstuvwxy@F';
sin = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz :';
mask_par = Array(
	0xC6EF3720, 0x28B7BD67, 0x8A8043AE, 0xEC48C9F5, 0x4E11503C, 0xAFD9D683, 0x11A25CCA, 0x736AE311,
	0xD5336958, 0x36FBEF9F, 0x98C475E6, 0xFA8CFC2D, 0x5C558274, 0xBE1E08BB, 0x1FE68F02, 0x81AF1549,
	0xE3779B90, 0x454021D7, 0xA708A81E, 0x08D12E65, 0x6A99B4AC, 0xCC623AF3, 0x2E2AC13A, 0x8FF34781,
	0xF1BBCDC8, 0x5384540F, 0xB54CDA56, 0x1715609D, 0x78DDE6E4, 0xDAA66D2B, 0x3C6EF372, 0x9E3779B9
);
function toHex(i,l) {
	i = i.toString(16).slice(l - l * 2);
	while(i.length < l) { i = '0' + i; }
	return(i);
}
function toSingle(str) {
	var i,chr,p,single = '';
	for (i = 0; i < str.length; i++) {
		chr = str.charAt(i);
		p = mul.indexOf(chr);
		if(p >= 0) { chr = sin.charAt(p); }
		single += chr;
	}
	return(single);
}
function match_code(str) {
	var codes,i,code,parts,dec,len;
	str = toSingle(str).replace(/\r\n/g,"\n").replace(/\r/g,"\n").replace(/\n/g,' ');
	if(!(codes = str.match(/[0-9A-F]{8}[ :]{0,1}[0-9A-Z]{2,11}/gi))) {
		codes = '';
	}
	for(i = 0; i < codes.length; i++) {
		if(!codes[i].match(/[ :]/)) {
			codes[i] = codes[i].substr(0,8) + ' ' + codes[i].substr(8);
		}
		parts = codes[i].match(/^(.{8})(.{1})(.+)$/);
		if(dec = parts[3].match(/^([SML])([0-9]+)$/)) {
			switch(dec[1]) {
				case 'S': l = 2; break;
				case 'M': l = 4; break;
				case 'L': l = 8; break;
			}
			parts[3] = toHex(parseInt(dec[2]),l);
		}
		codes[i] = parts[1] + parts[2] + parts[3];
	}
	return(codes);
}
function crypt_gba(code,m) {
	var i,n;
	code = code.split(' ');
	code[0] = parseInt(code[0],16);
	code[1] = parseInt(code[1],16);
	switch(m) {
		case 1:
			for (i = 0; i <= 31; i++) {
				n = (code[0] + mask_par[i] & 0xFFFFFFFF) ^
					((code[0] << 4) + 0x352027E9 & 0xFFFFFFFF) ^
					((code[0] >>> 5) + 0xF3DEE5A7 & 0xFFFFFFFF);
				code[1] = code[1] - n & 0xFFFFFFFF;
				n = (code[1] + mask_par[i] & 0xFFFFFFFF) ^
					((code[1] << 4) + 0x09F4FBBD & 0xFFFFFFFF) ^
					((code[1] >>> 5) + 0x9681884A & 0xFFFFFFFF);
				code[0] = code[0] - n & 0xFFFFFFFF;
			}
			break;
		case 4:
			for(i = 1; i <= 32; i++){
				n = ((code[1] << 4) + 0x7AA9648F) ^
					((0x9E3779B9 * i) + code[1]) ^
					((code[1] >>> 5) + 0x7FAE6994);
				code[0] = code[0] + n & 0xFFFFFFFF;
				n = ((code[0] << 4) + 0xC0EFAAD5) ^
					((0x9E3779B9 * i) + code[0]) ^
					((code[0] >>> 5) + 0x42712C57);
				code[1] = code[1] + n & 0xFFFFFFFF;
			}
			break;
		case 5:
			for(i = 32; i >= 1; i--){
				n = ((code[0] << 4) + 0xC0EFAAD5) ^
					((0x9E3779B9 * i) + code[0]) ^
					((code[0] >>> 5) + 0x42712C57);
				code[1] = code[1] - n & 0xFFFFFFFF;
				n = ((code[1] << 4) + 0x7AA9648F) ^
					((0x9E3779B9 * i) + code[1]) ^
					((code[1] >>> 5) + 0x7FAE6994);
				code[0] = code[0] - n & 0xFFFFFFFF;
			}
			break;
		default:
			for(i = 31; i >= 0; i--) {
				n = (code[1] + mask_par[i] & 0xFFFFFFFF) ^
					((code[1] << 4) + 0x09F4FBBD & 0xFFFFFFFF) ^
					((code[1] >>> 5) + 0x9681884A & 0xFFFFFFFF);
				code[0] = code[0] + n & 0xFFFFFFFF;
				n = (code[0] + mask_par[i] & 0xFFFFFFFF) ^
					((code[0] << 4) + 0x352027E9 & 0xFFFFFFFF) ^
					((code[0] >>> 5) + 0xF3DEE5A7 & 0xFFFFFFFF);
				code[1] = code[1] + n & 0xFFFFFFFF;
			}
	}
	if(code[0] < 0) { code[0] += 4294967296; }
	if(code[1] < 0) { code[1] += 4294967296; }
	code[0] = toHex(code[0],8);
	code[1] = toHex(code[1],8);
	return code.join(' ');
}
function change(code,m) {
	var parts,parts_addr,head,head_ar3,int_addr,len,model,pause,i,n;
	if((m == 1 || m == 5) && !code.match(/[^0-9A-F ]/i)) {
		code = crypt_gba(code,m);
	}
	if(!(parts = code.match(/^(.{8})(.{1})(.{2,8})$/))) {
		return('INVALID_CODE');
	}
	head = parts[1].charAt(0);
	head_ar3 = parts[1].substr(0,2);
	model = parts[2];
	code = Array(parts[1].substr(1),parts[3]);
	int_addr = parseInt(code[0],16);
	len = code[1].length;
	switch(m) {
		// PAR -> VBA
		case 1:
			pause = ':';
			switch(head) {
				case '0':
					code[0] = '0' + code[0];
					code[1] = code[1].slice(-2);
					break;
				case '1':
					code[0] = '0' + code[0];
					code[1] = code[1].slice(-4);
					break;
				case '2':
					code[0] = '0' + code[0];
					break;
				default:
					pause = ' ';
					code[0] = parts[1];
			}
			break;
		// VBA -> XTA
		case 2:
			pause = ' ';
			if(model == ':') {
				switch(len) {
					case 2:
						code[0] = '3' + code[0];
						code[1] = '00' + code[1];
						break;
					case 4:
						code[0] = '8' + code[0];
						code[1] = code[1];
						break;
					case 8:
						code[0] = '8' + code[0] + pause + code[1].substr(4,4);
						code[1] = '8' + toHex(int_addr + 2,7) + pause + code[1].substr(0,4);
						pause = "\n";
						break;
					default:
						return('INVALID_CODE');
				}
			}
			else if(code[1] == '001DC0DE') {
				code[0] = '00000000';
				code[1] = '000A';
			}
			else if(head_ar3 == 'F8' || head_ar3 == 'C4') {
				code[0] = '10' + parts[1].slice(-6);
				code[1] = '0007';
			}
			else if(head_ar3 == '0A') {
				parts_addr = parts[1].match(/^(.{2})(.{1})(.{5})$/);
				code[0] = parts_addr[2] + '0' + parts_addr[3];
				switch(head_ar3) {
					case '0A':
						switch(code[1].substr(0,4)) {
							case '0000':
								code[0] = '7' + code[0];
								code[1] = code[1].slice(-4);
								break;
							case '0010':
								code[0] = 'A' + code[0];
								code[1] = code[1].slice(-4);
								break;
							default:
								code[0] = parts[1];
						}
						break;
					default:
						code[0] = parts[1];
				}
			}
			else if((head_ar3 == '64' && code[1].substr(0,4) == '0000') || (parts[1].charAt(2) == 0 && (head == 'D'))) {
				switch(head) {
					case '6':
						code[0] = '8' + toHex(int_addr * 2,7);
						code[1] = code[1].slice(-4);
						break;
					case 'D':
						switch(code[1].substr(0,4)) {
							case '0000':
								code[0] = '7' + code[0];
								code[1] = code[1].slice(-4);
								break;
							case '0010':
								code[0] = 'A' + code[0];
								code[1] = code[1].slice(-4);
								break;
							default:
								code[0] = parts[1];
						}
						break;
					default:
						code[0] = parts[1];
				}
			}
			else {
				code[0] = parts[1];
			}
			break;
		// XTA -> VBA
		case 3:
			pause = ':';
			switch(head) {
				case '3':
					code[0] = '0' + code[0];
					code[1] = code[1].slice(-2);
					break;
				case '8':
					code[0] = '0' + code[0];
					break;
				case '7':
					pause = ' ';
					code[0] = 'D' + code[0];
					code[1] = '0000' + code[1];
					break;
				case 'A':
					pause = ' ';
					code[0] = 'D' + code[0];
					code[1] = '0010' + code[1];
					break;
				default:
					pause = ' ';
					code[0] = parts[1];
			}
			break;
		// VBA -> AR3
		case 4:
			pause = ' ';
			parts_addr = code[0].match(/^(.{1})(.{1})(.{5})$/);
			code[0] = parts_addr[1] + parts_addr[3];
			if(model == ':') {
				switch(len) {
					case 2:
						code[0] = '00' + code[0];
						code[1] = '000000' + code[1];
						break;
					case 4:
						code[0] = '02' + code[0];
						code[1] = '0000' + code[1];
						break;
					case 8:
						code[0] = '04' + code[0];
						break;
					default:
						return('INVALID_CODE');
				}
			}
			else if(head_ar3 == 'F8' || (parts[1].charAt(2) == '0' && head == 'D')) {
				switch(head) {
					case 'D':
						code[0] = '0A' + code[0];
						break;
					case 'F':
						code[0] = 'C4' + parts[1].slice(-6);
						code[1] = '000084' + code[1].slice(-2);
						break;
					default:
						return(parts[1] + model + parts[3]);
				}
			}
			else {
				code[0] = parts[1];
			}
			if(!code[1].match(/[^0-9A-F]/i)) {
				code = crypt_gba(code.join(' '),m).split(' ');
			}
			break;
		// AR3 -> VBA
		case 5:
			pause = ':';
			parts_addr = parts[1].match(/^(.{2})(.{1})(.{5})$/);
			code[0] = '0' + parts_addr[2] + '0' + parts_addr[3];
			if(parts[1].charAt(2) != '0') {
				switch(head_ar3) {
					case '00':
						code[1] = code[1].slice(-2);
						break;
					case '02':
						code[1] = code[1].slice(-4);
						break;
					case '04':
						break;
					default:
						pause = ' ';
						code[0] = parts[1];
				}
			}
			else {
				pause = ' ';
				code[0] = parts[1];
			}
			break;
		// VBA -> PAR
		default:
			pause = '';
			if(model == ':') {
				if(int_addr >= 0x08000000) {
					code[0] = '6' + toHex(int_addr / 2,7);
					code[1] = '0000' + code[1].slice(-4);
				}
				else {
					switch(len) {
						case 2:
							code[0] = '0' + code[0];
							code[1] = '000000' + code[1];
							break;
						case 4:
							code[0] = '1' + code[0];
							code[1] = '0000' + code[1];
							break;
						case 8:
							code[0] = '2' + code[0];
							break;
						default:
							return('INVALID_CODE');
					}
				}
			}
			else if(head_ar3 == '0A' || head_ar3 == 'C4') {
				parts_addr = parts[1].match(/^(.{2})(.{1})(.{5})$/);
				code[0] = parts_addr[2] + '0' + parts_addr[3];
				switch(head_ar3) {
					case '0A':
						code[0] = 'D' + code[0];
						break;
					case 'C4':
						code[0] = 'F8' + parts[1].slice(-6);
						code[1] = '000001' + code[1].slice(-2);
						break;
					default:
						return(parts[1] + model + parts[3]);
				}
			}
			else {
				code[0] = parts[1];
			}
			if(!code[1].match(/[^0-9A-F]/i)) {
				code = crypt_gba(code.join(' '),m).split(' ');
			}
	}
	return code.join(pause);
}
function decompo(argue) {
	var i,codes = new Array;
	for(i = 0; i < argue[3]; i++) {
		if(argue[5] == 4 && argue[4] == 2 && argue[0] % 4 == 0 && i < argue[3] - 1) {
			codes[i] = toHex(argue[0],8) + ':' + toHex(argue[1] + argue[2],argue[5]) + toHex(argue[1],argue[5]);
			argue[0] += argue[4] * 2;
			argue[1] += argue[2] * 2;
			i++;
		}
		else {
			codes[i] = toHex(argue[0],8) + ':' + toHex(argue[1],argue[5]);
			argue[0] += argue[4];
			argue[1] += argue[2];
		}
	}
	return(match_code(codes.join(' ')));
}
function convert(str,m) {
	var i,codes = match_code(str),contract = 0,argue,int_addr,ras = 0;
	str = '';
	for(i = 0; i < codes.length; i++) {
		if(contract) {
			argue = Array(
				parseInt(contract.substr(1,7),16),
				parseInt(contract.substr(9),16),
				parseInt(codes[i].substr(0,4),16),
				parseInt(codes[i].substr(4,4),16),
				parseInt(codes[i].substr(9),16),
				contract.substr(9).length
			);
			str += decompo(argue).join("\n") + "\n";
			contract = 0;
		}
		else if(m == 3 && codes[i].charAt(0) == '4') {
			contract = codes[i];
		}
		else if(ras) {
			str += change('64' + ras.substr(11,6) + ' 0000' + codes[i].substr(4,4),m) + "\n";
			ras = 0;
		}
		else if(
			m == 0 &&
			codes[i].substr(0,10) == '00000000 1' &&
			codes[i + 1].substr(0,4) == '0000' &&
			codes[i + 1].substr(9,8) == '00000000'
		) {
			ras = codes[i];
		}
		else if(
			m == 4 &&
			(int_addr = parseInt(codes[i].substr(1,7),16)) >= 0x08000000 &&
			codes[i].charAt(8) == ':'
		) {
			str += change('00000000 18' + toHex(int_addr / 2,6),m) + "\n";
			str += change('0000' + codes[i].slice(-4) + ' 00000000',m) + "\n";
		}
		else if(m == 4 && codes[i].substr(0,2) == '64' && codes[i].substr(9,4) == '0000') {
			str += change('00000000 18' + codes[i].substr(2,6),m) + "\n";
			str += change('0000' + codes[i].slice(-4) + ' 00000000',m) + "\n";
		}
		else {
			str += change(codes[i],m) + "\n";
		}
	}
	return(str.toUpperCase());
}