#include "gm6.h"

static char key_name[6];

// The Mark Overmas Sequence.
const uint8_t mark_overmars_seq[0x30] = { 0x01,0x07,0x11,0x17,0x19,0x1b,0x24,0x27,0x2b,0x31,0x33,0x3d,0x3f,0x4d,0x53,0x59,0x5e,0x63,0x68,0x69,0x76,0x7f,0x89,0x8b,0x95,0x97,0x9b,0x9e,0xa6,0xb9,0xbd,0xbf,0xc6,0xc9,0xd3,0xd6,0xde,0xe5,0xea,0xeb,0xf5,0xf9 };

const uint8_t blacklisted_keys[0x6][0x8] = {
	{0xFF, 0xDF, 0x0A, 0xFF, 0xD3, 0x05, 0x7F, 0x24},
	{0x04, 0x22, 0xDA, 0xEE, 0x19, 0x79, 0xEE, 0x61},
	{0x24, 0xB4, 0x48, 0x5C, 0x5B, 0xDB, 0x29, 0x22},
	{0xB6, 0x15, 0xEA, 0x32, 0xBC, 0x16, 0xFB, 0xAF},
	{0x1E, 0x9E, 0xCE, 0x56, 0x12, 0xAD, 0x9D, 0x48},
	{0x9E, 0x6E, 0x3F, 0x8C, 0xD4, 0x36, 0x22, 0xEA}
};

int is_blacklisted(uint8_t* key) {
	for (int i = 0; i < 0x6; i++) {
		if (memcmp(blacklisted_keys[i], key, 0x8) == 0)
			return 1;
	}

	if (key[0x00] == 0xE3 &&
		key[0x05] == 0x7A &&
		key[0x06] == 0xD9 &&
		key[0x07] == 0xE3 &&
		key[0x08] == 0x06) {
		return 1;
	}

	return 0;
}

int add_multiply(uint8_t* key_arr) {
	int sum = 0;
	for (int i = 0; i < KEY_LEN - 1; i++) {
		int c = key_arr[i];
		sum += (i + 1) * c;
	}
	return sum % 0x100;
}

int check_checksum(uint8_t* key_arr) {
	int c = key_arr[KEY_LEN - 1];
	int chksum = add_multiply(key_arr);
	if (c == chksum) {
		return 1;
	}
	else {
		return 0;
	}
}
int trunc_c(int c) {
	while (c > 0xFF)
		c -= 0x100;

	while (c < 0x100)
		c += 0x100;
	return c;
}

uint8_t CheckThing(int inp)
{
	int ret = 0;
	if (inp - 1 < 2)
		return 1;
	int max = inp - 3 + 1;
	for (int i = 2; inp % i; ++i)
	{
		if (!--max)
			return 1;
	}
	return ret;
}

int IterateCheckThing(int start)
{
	int i = 0;
	int ret = 2;
	do
	{
		if (CheckThing(++ret))
			++i;
	} while (start > i);
	return ret;
}

void gen_checksum(uint8_t* key_arr) {
	int curchk = key_arr[KEY_LEN - 1];
	int chksum = add_multiply(key_arr);
	if (curchk != chksum) {
		key_arr[KEY_LEN - 1] = (uint8_t)chksum;

	}
}

void deobfuscate_key_pt1(uint8_t* key_arr, uint8_t* out_key, int chk2) {
	int start = 0x11;
	memcpy(out_key, key_arr, KEY_LEN);
	for (int i = 0; i < KEY_LEN; i++) {
		int32_t c = (key_arr[i] + 0x100);
		c -= (start * chk2);
		c = trunc_c(c);
		out_key[i] = (uint8_t)c;
		start += 0x1F;
	}
}

void obfuscate_key_pt1(uint8_t* key_arr, uint8_t* out_key, int chk2) {
	int start = 0x11;
	memcpy(out_key, key_arr, KEY_LEN);
	for (int i = 0; i < KEY_LEN; i++) {
		int32_t c = (key_arr[i] + 0x100);
		c += (start * chk2);
		c = trunc_c(c);
		out_key[i] = (uint8_t)c;
		start += 0x1F;
	}
}

void obfuscate_key_pt2(uint8_t* key_arr, uint8_t* out_key, uint8_t chk2) {

	memcpy(out_key, key_arr, KEY_LEN);
	for (int i = 0; i < KEY_LEN; i++) {
		int res = IterateCheckThing(chk2 + ((i + 1) * (i + 1)));
		int c = (key_arr[i] + res);
		c = trunc_c(c);
		out_key[i] = (uint8_t)c;
	}
}

void deobfuscate_key_pt2(uint8_t* key_arr, uint8_t* out_key, uint8_t chk2) {

	memcpy(out_key, key_arr, KEY_LEN);
	for (int i = 0; i < KEY_LEN; i++) {
		int res = IterateCheckThing(chk2 + ((i + 1) * (i + 1)));
		int c = (key_arr[i] - res);
		c = trunc_c(c);
		out_key[i] = (uint8_t)c;
	}
}

void obfuscate_key_pt3(uint8_t* key_arr, uint8_t* out_key, uint8_t chk2) {

	memcpy(out_key, key_arr, KEY_LEN);

	for (int i = 0; i < KEY_LEN - 1; i++) {
		int c = key_arr[i + 1] - (chk2 * key_arr[i]);
		c = trunc_c(c);
		out_key[i + 1] = (uint8_t)c;
	}
}

void deobfuscate_key_pt3(uint8_t* key_arr, uint8_t* out_key, uint8_t chk2) {

	memcpy(out_key, key_arr, KEY_LEN);

	for (int i = 0; i < KEY_LEN - 1; i++) {
		int c = (chk2 * out_key[i]) + out_key[i + 1];
		c = trunc_c(c);
		out_key[i + 1] = (uint8_t)c;
	}
}

void deobfuscate_key_pt4(uint8_t* key_arr, uint8_t* out_key, uint8_t chk1) {

	memcpy(out_key, key_arr, KEY_LEN);

	int seed = 1;
	for (int multi = 0x64; seed % 9 == 1; ++multi)
		seed = IterateCheckThing(multi + chk1);


	for (int i = 0x0; i < 0x9; i++) {
		int c = key_arr[seed * (i + 1) % 9];
		out_key[i] = (uint8_t)c;
	}

}

void deobfuscate_key_pt5(uint8_t* key_arr, uint8_t* out_key, uint8_t chk1) {

	memcpy(out_key, key_arr, KEY_LEN);
	int multi = 0xD;

	for (int i = 0; i < KEY_LEN; i++) {
		int c = (key_arr[i] + 0x100) - (chk1 * multi);
		multi += 0x35;
		c = trunc_c(c);

		out_key[i] = (uint8_t)c;
	}
}

void obfuscate_key_pt5(uint8_t* key_arr, uint8_t* out_key, uint8_t chk1) {

	memcpy(out_key, key_arr, KEY_LEN);
	int multi = 0xD;

	for (int i = 0; i < KEY_LEN; i++) {
		int c = (key_arr[i] + 0x100) + (chk1 * multi);
		multi += 0x35;

		c = trunc_c(c);

		out_key[i] = (uint8_t)c;
	}
}

void obfuscate_key_pt4(uint8_t* key_arr, uint8_t* out_key, uint8_t chk1) {
	deobfuscate_key_pt4(key_arr, out_key, chk1);
}


char* decode_name(int encoded_name)
{
	memset(key_name, 0x00, 6);
	for (int i = 4; i >= 0; i--) {
		int c = (encoded_name % 0x1A) + 0x41;
		key_name[i] = c;
		encoded_name /= 0x1A;
	}
	return key_name;
}

void sanitize_name(char* namein, char* nameout) {
	memset(nameout, 0, 6);
	memcpy(nameout, namein, 5);

	if (strlen(namein) != 5) {
		int sz = strlen(namein);

		strncpy(nameout, "XXXXX", 5);

		if (sz < 5) {
			memcpy(&nameout[5 - sz], namein, sz);
		}
		else if (sz > 5) {
			memcpy(nameout, &namein[sz - 5], 5);
		}

	}
	else {
		strncpy(nameout, namein, 5);
	}


	for (int i = 0; i < 5; i++) {
		if (!(namein[i] >= 'A' && namein[i] <= 'Z')) {
			nameout[i] = 'X';
		}
		else {
			nameout[i] = namein[i];
		}
	}

}

int get_current_date_delphi() {
	double t = (((((double)time(0)) + 0.5) / 86400.0) + 25569.0);
	return (int)t;
}

int encode_name(char* name) {
	int total = 0;
	int multi = 1;

	for (int i = strlen(name) - 1; i >= 0; i--) {
		total += (name[i] - 0x41) * multi;
		multi *= 0x1A;
	}
	return total;
}


int final_key_check(uint8_t* key_arr, char* name, uint8_t chk2, uint8_t chk1, uint32_t exp_date, uint32_t timed) {

	int encoded_name = 0;
	((uint8_t*)(&encoded_name))[0] = key_arr[0x5];
	((uint8_t*)(&encoded_name))[1] = key_arr[0x4];
	((uint8_t*)(&encoded_name))[2] = key_arr[0x3];


	int encoded_time = 0;
	((uint8_t*)(&encoded_time))[0] = key_arr[0x1];
	((uint8_t*)(&encoded_time))[1] = key_arr[0x0];
	encoded_time += 0x8EAE;

	if (timed) {
		if (encoded_time != exp_date + 0x8EAE) {
			return 0;
		}
	}


	if (encoded_time - 0xA > get_current_date_delphi()) {
		return 0;
	}

	int is_timed = key_arr[0x2];

	if (!(is_timed >= 0x1 && is_timed <= 0x20)) {
		return 0;
	}

	if (is_timed >= 0xA && is_timed <= 0xF) {
		if (!timed) {
			return 0;
		}
	}

	if (strcmp(decode_name(encoded_name), name) != 0) {
		return 0;
	}

	uint8_t chk1chk = key_arr[0x7];
	uint8_t chk2chk = key_arr[0x8];

	if (chk1 == chk1chk && chk2 == chk2chk) {
		return 1;
	}
	else {

		return 0;
	}
}

void deobfuscate_key(uint8_t* key_arr, uint8_t* out_key, uint8_t* out_chk2, uint8_t* out_chk1) {
	char pt1_key[KEY_LEN];
	deobfuscate_key_pt1(key_arr, pt1_key, 0xD);
	uint8_t chk2 = pt1_key[0xA];

	char pt2_key[KEY_LEN];
	deobfuscate_key_pt2(pt1_key, pt2_key, chk2);


	char pt3_key[KEY_LEN];
	deobfuscate_key_pt3(pt2_key, pt3_key, chk2);


	uint8_t chk1 = pt3_key[0x9];
	char pt4_key[KEY_LEN];
	deobfuscate_key_pt4(pt3_key, pt4_key, chk1);


	char pt5_key[KEY_LEN];
	deobfuscate_key_pt5(pt4_key, pt5_key, chk1);


	memcpy(out_key, pt5_key, KEY_LEN);

	*out_chk2 = chk2;
	*out_chk1 = chk1;


}

void obfuscate_key(uint8_t* key_arr, uint8_t* out_key, int chk2, int chk1) {

	char pt5_key[KEY_LEN];
	obfuscate_key_pt5(key_arr, pt5_key, chk1);

	char pt4_key[KEY_LEN];
	obfuscate_key_pt4(pt5_key, pt4_key, chk1);

	pt4_key[0x9] = chk1;
	char pt3_key[KEY_LEN];
	obfuscate_key_pt3(pt4_key, pt3_key, chk2);

	char pt2_key[KEY_LEN];
	obfuscate_key_pt2(pt3_key, pt2_key, chk2);
	pt2_key[0xA] = chk2;

	char pt1_key[KEY_LEN];
	obfuscate_key_pt1(pt2_key, pt1_key, 0xD);

	memcpy(out_key, pt1_key, KEY_LEN);

}

int check_key_6(uint8_t* key_arr, char* name, uint32_t exp_date, uint32_t timed) {
	// Did we win the lottery?
	if (is_blacklisted(key_arr)) { 
		return 0;
	}

	if (check_checksum(key_arr)) {

		uint8_t chk1;
		uint8_t chk2;
		uint8_t out_key[KEY_LEN];

		deobfuscate_key(key_arr, out_key, &chk2, &chk1);

		return final_key_check(out_key, name, chk2, chk1, exp_date, timed);

	}
	return 0;
}

void gen_key_6(char* name, uint8_t chk1, uint8_t chk2, uint16_t exp_date, uint32_t timed, uint8_t* out_key) {

	uint8_t keydata[KEY_LEN];

	for (int i = 0; i < KEY_LEN; i++) {
		keydata[i] = rand() % 0x100;
	}

	keydata[0x7] = chk1;
	keydata[0x8] = chk2;

	int encoded_name = encode_name(name);

	keydata[0x5] = ((uint8_t*)(&encoded_name))[0];
	keydata[0x4] = ((uint8_t*)(&encoded_name))[1];
	keydata[0x3] = ((uint8_t*)(&encoded_name))[2];



	uint16_t encoded_time = exp_date;
	keydata[0x1] = ((uint8_t*)(&encoded_time))[0];
	keydata[0x0] = ((uint8_t*)(&encoded_time))[1];

	int is_timed = rand();
	if (timed) {
		is_timed = (rand() % 0x6) + 0xA;
	}
	else {
		do {
			is_timed = (rand() % 0x20) + 1;
		} while (is_timed >= 0xA && is_timed <= 0xF);
	}

	keydata[0x2] = is_timed;

	uint8_t obfus_key[KEY_LEN];
	obfuscate_key(keydata, obfus_key, chk2, chk1);
	gen_checksum(obfus_key);

	memcpy(out_key, obfus_key, KEY_LEN);
}

void key_to_string(uint8_t* key, char* out_str, int buffer_sz) {
	snprintf(out_str, buffer_sz, "%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X", key[0x0], key[0x1], key[0x2], key[0x3], key[0x4], key[0x5], key[0x6], key[0x7], key[0x8], key[0x9], key[0xA], key[0xB]);
}

void bruteforce_mark_seq(void* name) {
	char checked_name[6];
	sanitize_name(name, checked_name);
	uint8_t keybuf[KEY_LEN];

	for (int chk1 = 0; chk1 < 0xFF; chk1++) {
		int chk1_valid = 0;

		for (int chk2 = 0; chk2 < 0xFF; chk2++) {
			uint32_t exp_date = (get_current_date_delphi() - 0xA) - 0x8EAE;

			gen_key_6(checked_name, chk1, chk2, exp_date, 0, keybuf);
			if (check_key_6(keybuf, checked_name, exp_date, 0)) {
				chk1_valid++;
			}
		}
		if (chk1_valid == 0xFF)
			printf("found part of seq; 0x%x c = 0x%x\n", chk1, chk1_valid);

	}
}


void keygen_gm6(char* name, uint8_t* out_key) {
	char checked_name[6];
	sanitize_name(name, checked_name);
	uint8_t keybuf[KEY_LEN];

	int chk1 = mark_overmars_seq[rand() % sizeof(mark_overmars_seq)];
	int chk2 = rand() % 0xFF;
	uint32_t exp_date = (get_current_date_delphi() - 0xA) - 0x8EAE;

	gen_key_6(checked_name, chk1, chk2, exp_date, 0, keybuf);
	
	// Sanity check ...
	if (check_key_6(keybuf, checked_name, exp_date, 0)) { 
		memcpy(out_key, keybuf, KEY_LEN);
	}
	else {
		keygen_gm6(name, out_key);
	}
}
#ifdef STANDALONE
int main(int argc, char* argv[]) {
	srand(time(0));

	char keystr[31];
	uint8_t out_key[KEY_LEN];
	keygen_gm6(argv[1], out_key);
	key_to_string(out_key, keystr, sizeof(keystr));
	printf("Key found: %s", keystr);
	return 0;
}
#endif


// GM6 Keys
// Freeware
// 6f58-0ac7-90f2-533f-22f9-343a
// Pyrofol
// 6f58-0a44-6806-223f-22f9-3487
// Silica
// d655-6db3-4f53-43d4-a75f-6571