#include <stdio.h>
#include <stdlib.h>
#include <gccore.h>
#include <string.h>
//#include <sys/unistd.h>

#include "patchcode.h"

extern void patchhook(u32 address, u32 len);
//extern void patchhook2(u32 address, u32 len);
//extern void patchhook3(u32 address, u32 len);

//extern void multidolhook(u32 address);
//extern void langvipatch(u32 address, u32 len, u8 langbyte);
//extern void vipatch(u32 address, u32 len);

/*static const u32 vipatchcode[3] = {
0x4182000C,0x4180001C,0x48000018
};

const u32 langpatch[3] = {
	0x7C600775, 0x40820010, 0x38000000
};
*/

// Hook found in freedom source
const u32 vigchooks[4] = {
	0x906402E4, 0x38000000, 0x900402E0, 0x909E0004
};

// Hook found by Nicksasa
const u32 GC_gxdrawdone_hooks[8] = {
	0x28000000, 0x4182FFF0, 0x7FE3FB78, 0x4BFE7CE5, 0x8001001C, 0x83E10014, 0x38210018, 0x7C0803A6
};

// Hook found by Nicksasa
const u32 GC_OSSleepthread_hooks[6] = {
	0x7FE3FB78, 0x4BFFC449, 0x8001001C, 0x83E10014, 0x83C10010, 0x38210018
};

s32 check_hook(u32 *offset, u32 *hook, u32 hooksize)
{
	u32 count = 0;
	
	while (count < hooksize / 4)
	{
		if (*hook == 0x4BFE7CE5 || *hook == 0x83E10014 || *hook == 0x4BFFC449 || *offset == *hook)
		{
			hook++;
			offset++;
			count++;
		} else
		{
			return 1;
		}		
	}
	return 0;
}

u32 hookselect = 0;
u32 ocarinaselect = 0;
u32 debuggerselect = 0;

void set_codes_settings(u8 hook, u8 ocarina, u8 debugger)
{
	hookselect = hook;
	ocarinaselect = ocarina;
	debuggerselect = debugger;
}


//---------------------------------------------------------------------------------
bool dogamehooks(void *addr, u32 len)
//---------------------------------------------------------------------------------
{
	void *addr_start = addr;
	void *addr_end = addr+len;
	bool hookpatched = false;

	while(addr_start < addr_end)
	{
		switch (hookselect)
		{
			case 0x01:
				if (check_hook(addr_start, (u32 *)vigchooks, sizeof(vigchooks))==0)
				{
					patchhook((u32)addr_start, len);
					hookpatched = true;
				}
			break;

			case 0x02:
				if (check_hook(addr_start, (u32 *)GC_OSSleepthread_hooks, sizeof(GC_OSSleepthread_hooks)) == 0)
				{
					patchhook((u32)addr_start, len);
					hookpatched = true;
				}
			break;

			case 0x03:	
				if (check_hook(addr_start, (u32 *)GC_gxdrawdone_hooks, sizeof(GC_gxdrawdone_hooks)) == 0)
				{
					patchhook((u32)addr_start, len);
					hookpatched = true;
				}
			break;
		
			case 0x00:
			default:
				//hookpatched = true;
			break;

		}
		addr_start += 4;
	}
	return hookpatched;
}

/*
//---------------------------------------------------------------------------------
void langpatcher(void *addr, u32 len)
//---------------------------------------------------------------------------------
{
	u32 temp;
	switch(langselect)
	{
		case 1: 
			temp = 0x00; // Japanese
		break;

		case 2: 
			temp = 0x01; // English
		break;

		case 3: 
			temp = 0x02; // German
		break;

		case 4: 
			temp = 0x03; // French
		break;

		case 5: 
			temp = 0x04; // Spanish
		break;

		case 6: 
			temp = 0x05; // Italian
		break;

		case 7: 
			temp = 0x06; // Dutch
		break;

		case 8: 
			temp = 0x07; // S. Chinese
		break;

		case 9: 
			temp = 0x08; // T. Chinese
		break;

		case 10: 
			temp = 0x09; // Korean
		break;

		case 11: 
			switch ( *((char *)0x80000003) )
			{
				case 0x45:		// USA default
					temp = 0x01;
				break;

				case 0x4A:		// JAP default
					temp = 0x00;
				break;

				default:
					temp = 0xCD;
			}
		break;

		default:
		case 0:
			temp = 0xCD; // Default
		break;			
	}
	if (temp == 0xCD)
	{
		return;
	}

	void *addr_start = addr;
	void *addr_end = addr+len;

	while(addr_start < addr_end)
	{
		if(memcmp(addr_start, langpatch, sizeof(langpatch))==0) {
			langvipatch((u32)addr_start, len, temp);
		}
		addr_start += 4;
	}
}

*/
/*
//---------------------------------------------------------------------------------
void vidolpatcher(void *addr, u32 len)
//---------------------------------------------------------------------------------
{
	
	void *addr_start = addr;
	void *addr_end = addr+len;
	
	while(addr_start < addr_end)
	{
		if(memcmp(addr_start, vipatchcode, sizeof(vipatchcode))==0) {
			vipatch((u32)addr_start, len);
		}
		addr_start += 4;
	}
}
*/

