#include "YaRRIncludes.h"

/************************************** Polite Request ***************************************
** I have put a lot of time and effort into YaRR. If you want to use some of the source     **
** please tell me. Anything used from YaRR must be open source, as is scripts.dll.          **
***************************************** Thank you *****************************************/


bool YaRRHooking::Locked = 0;
#pragma warning(disable: 4245)
int YaRRHooking::RunGame = 1;

void __stdcall Process_Connection_Request(WideStringClass *wcs)
{
	DLOG;
	unsigned int ret = 1;
	if(YaRRHooking::Locked)
	{
		WriteMemory(0x00458741, (void *)&ret, 4);
		return;
	}
	const char *Nick = WideCharToChar((const wchar_t *)*wcs);
	
	bool IsReserved = 0;

	int i = 0;
	for(;YaRRSettings::SlotList[i] != 0; i++)
	{
		if(strcmp(YaRRSettings::SlotList[i], Nick) == 0)
		{
			IsReserved = 1;
			ret = 0;
		}
	}
	
	for(int z = 0; Players[z] != 0; z++)
	{
		if(strcmp(Nick, Players[z]->Nick) == 0)
		{
			i--;
		}
	}


	if((The_Game()->MaxPlayers-(i) > (unsigned)Get_Player_Count()) && ret)
	{
		ret = 0;
	}

	if(!ret)	
	{
		YaRRPlayers::LoadingMap(Nick, IsReserved);
	}
	WriteMemory(0x00458741, (void *)&ret, 4);
	delete []Nick;
}

bool _stdcall Damaged(int PlayerID, int ObjID, int VictimID, int WarheadID, float Damage)
{
	GameObject *Damager = Commands->Find_Object(ObjID);
	GameObject *Victim = Commands->Find_Object(VictimID);

	if(Damager && Victim)
	{
		if(Commands->Is_A_Star(Damager) && Commands->Is_A_Star(Victim))
		{
			Player *p = YaRRPlayers::Find(Get_Player_ID(Damager));
			if(!p)
			{
				return 0;
			}

			if(p->Rooted)
			{
				return 0;
			}

			p = YaRRPlayers::Find(Get_Player_ID(Victim));
			if(!p)
			{
				return 0;
			}

			if(p->Protected)
			{
				return 0;
			}
			
		}
	}
	return 1;
}

void __declspec(naked) Prejoin_Glue()
{
	__asm
	{
		push ecx;
		push edx;
		lea edx, [esp + 10h];
		push edx;
		call Process_Connection_Request;
		pop edx;
		pop ecx;
		mov eax, 0;
		retn
	}
}

int __cdecl StartButton(const char *Source, int)
{
	YaRRPlayers::ThinkingJoin(Source);
	return 0;
}

void __declspec(naked) StartButton_Glue()
{
	__asm
	{
		jmp StartButton;
	}
}

void __declspec(naked) StopGame_Glue()
{
	__asm
	{
		mov eax, YaRRHooking::RunGame;
		retn;
	}
}

void __declspec(naked) NoGameover_Glue()
{
	__asm
	{
		mov eax, 0;
		retn;
	}
}

void __cdecl BIATCH_Hook(void *, const char *Format, ...)
{
	DLOG;
	char buffer[256];
	va_list va;
	_crt_va_start(va, Format);
	vsnprintf(buffer, 256, Format, va);
	va_end(va);
	
	IRC::SendC('b', "%s", buffer);
}
void __declspec(naked) BIATCH_Glue()
{
	__asm
	{
		mov eax, BIATCH_Hook;
		jmp eax;
	}
}

class cSuicideEvent
{
	char stub[0x6B4];
public:
	int ID;
	void Act()
	{
		Player *p = YaRRPlayers::Find(ID);
		if(!p)
		{
			return;
		}
		if(YaRRSettings::SuicideAllowed && !p->Rooted)
		{
			IRC::SendC('b', "%S committed suicide", Get_Wide_Player_Name_By_ID(ID));
			if(YaRRSettings::SuicideTakeMoney)
			{
				DWORD loc = 0x4BA140;
				__asm
				{
					mov ecx, this;
					call loc;
				}
				return;
			}
			Commands->Destroy_Object(Get_GameObj(ID));
		}
		else
		{
			if(*YaRRSettings::SuicideMessage != 0)
			{
				YaRRFunctions::PPage(ID, "%s", YaRRSettings::SuicideMessage);
			}
		}
	}
};

class CSAnnouncement
{
	char stub[0x6B4];
public:
	int Team;
	int PlayerId;
	int a;
	int RadioId;
	int b;

	void Act()
	{
		Player *p = YaRRPlayers::Find(PlayerId);
		if(!p)
		{
			return;
		}
		if(p->Muted)
		{
			return;
		}
	
		DWORD loc = 0x4B3660;
		__asm
		{
			mov ecx, this;
			call loc;
		}
	}
};

class cBioEvent
{
	char stub[0x6B4];
	int ID;
	WideStringClass Nick;
	const char *a;
	char stub2[0x104];

public:

	void Act()
	{
		printf("%d - %S - %s\n", ID, (const wchar_t *)Nick, a);

		DWORD loc = 0x4B4400;
		__asm
		{
			mov ecx, this;
			call loc;
		}
	}
};

void Main_Think()
{
	YaRRThink::Think();
}


void __declspec(naked) main_loop_glue()
{
	__asm
	{
		call Main_Think;

		pop edi;
		pop esi;
		add esp, 0Ch;
		ret;
	}

}

void ROF_Callback(void *Data, int Number)
{
	((Player *)Data)->ROF_Callback();
	YaRRThink::Add_Timer((float)Number, ROF_Callback, Data, Number);
}


class cCsDamageEvent
{
	char stub[0x6B4];
	int PlayerId;
	int DamagerObjId;
	int TargetObjId;
	float Damage;
	unsigned int WarheadId;
	void Act()
	{
		Player *p = YaRRPlayers::Find(PlayerId);
		if(!p)
		{
			return;
		}
		p->ROF_Damage += Damage;

		if(!Damaged(PlayerId, DamagerObjId, TargetObjId, WarheadId, Damage))
		{
			return;
		}

		DWORD loc = 0x6F3F10;
		__asm
		{
			mov ecx, this;
			call loc;
		}
	}
};

class cCsTextObj
{
	char stub[0x6B4];
	int ID;
	int Type;
	WideStringClass Message;
	int Target;
public:

	void Act()
	{

		if(Target >= -1)
		{
			if(!YaRRCommands::GameChat(ID, Type, Message, Target))
			{
				return;
			}
		}
		else if(Target == -3)
		{
			//YaRRKeys::OnKey(ID, Message);
		}

		DWORD loc = 0x4B5C10;
		__asm
		{
			mov ecx, this;
			call loc;
		}
	}
};

class cCsPingRequestEvent
{
	char stub[0x6B4];

	int ID;
	int PingID;

	void Act()
	{
		

		DWORD loc = 0x4B5900;
		__asm
		{
			mov ecx, this;
			call loc;
		}
	}
};

class cLoadingEvent
{
	char stub[0x6B4];

	int ID;
	bool IsInGame;

	void Act()
	{

		DWORD loc = 0x4B7860;
		__asm
		{
			mov ecx, this;
			call loc;
		}
	}
};

/*
class cPlayerKill
{
	char stub[0x6BC];

	void Init(int ID, int Killer)
	{
		printf("ID: %d Killer %d\n", ID, Killer);
	}
};
*/
class cGameSpyCsChallengeResponseEvent
{
	char stub[0x6B4];
	int PlayerID;
	StringClass Serial;
	void Act()
	{
		printf("%d - %s\n", PlayerID, (const char *)Serial);

		DWORD loc = 0x4B6ED0;
		__asm
		{
			mov ecx, this;
			call loc;
		}
	}
};

void SendGameSpyChallenge(int ID, StringClass &_str)
{
	__asm
	{
		mov eax, 6B8h;
		push eax;

		mov eax, 0x672410;
		call eax;

		add esp, 4;

		mov ecx, eax;
		push ecx;

		mov eax, 0x4B7220;
		call eax;

		pop ecx;

		mov eax, _str;
		push eax;

		mov eax, ID;
		push ID;

		mov eax, 0x4B72C0;
		call eax;
	}
}


void YaRRHooking::Install()
{
	DLOG;
	char op = '\xE9';

	int set = (int)StopGame_Glue - 0x474CF0  - 5;
	WriteMemory(0x474CF0 , &op, 1);
	WriteMemory(0x474CF0+1, &set, 4);

	set = (int)BIATCH_Glue - 0x10009EE0  - 5;
	WriteMemory(0x10009EE0 , &op, 1);
	WriteMemory(0x10009EE0+1, &set, 4);

	set = (int)main_loop_glue - 0x0043BAC8 - 5;
	WriteMemory(0x0043BAC8 , &op, 1);
	WriteMemory(0x0043BAC8+1, &set, 4);

	/*set = (int)Kill_Glue - 0x004B7D30 - 5;
	WriteMemory(0x004B7D30 , &op, 1);
	WriteMemory(0x004B7D30+1, &set, 4);*/

	
	op = '\xE8';
	set = (int)Prejoin_Glue - 0x00458725 - 5;
	WriteMemory(0x00458725, &op, 1);
	WriteMemory(0x00458725 + 1, &set, 4);

	DWORD loc = 0;
	
	__asm
	{
		mov eax, cGameSpyCsChallengeResponseEvent::Act
		mov loc, eax
	}
	WriteMemory(0x007D4E30, (void *)&loc, 4);
	

	__asm
	{
		mov eax, cCsTextObj::Act;
		mov loc, eax;
	}
	WriteMemory(0x7D4B98, (void *)&loc, 4);

	__asm
	{
		mov eax, cCsDamageEvent::Act;
		mov loc, eax;
	}
	WriteMemory(0x7E4230, (void *)&loc, 4);
	
	__asm
	{
		mov eax, cSuicideEvent::Act;
		mov loc, eax;
	}
	WriteMemory(0x7D5468, (void *)&loc, 4);

	__asm
	{
		mov eax, CSAnnouncement::Act;
		mov loc, eax;
	}
	WriteMemory(0x7D45FC, (void *)&loc, 4);

	__asm
	{
		mov eax, cCsPingRequestEvent::Act;
		mov loc, eax;
	}
	WriteMemory(0x7D4B14, (void *)&loc, 4);

	__asm
	{
		mov eax, cLoadingEvent::Act;
		mov loc, eax;
	}
	WriteMemory(0x7D4FBC, (void *)&loc, 4);

	/*__asm
	{
		mov eax, cGameSpyScChallengeEvent::Act;
		mov loc, eax;
	}
	WriteMemory(0x7D4EB4, (void *)&loc, 4);*/

	/*__asm
	{
		mov eax, cBioEvent::Act;
		mov loc, eax;
	}
	WriteMemory(0x7D46FC, (void *)&loc, 4);
	*/

	
}

void YaRRHooking::Uninstall()
{
	DLOG;
}

//the following was written by the Black Intel team

bool ReadMemory(int Address, void* Buffer, int Size) {
	DLOG;
	bool ret = 1;
	DWORD OldProtect;
	HANDLE Process = OpenProcess(PROCESS_ALL_ACCESS, false, GetCurrentProcessId());
	VirtualProtectEx(Process, (LPVOID)Address, Size, PAGE_EXECUTE_READWRITE, &OldProtect);
	if (!ReadProcessMemory(Process, (LPVOID)Address, Buffer, Size, NULL)) {
		ret = 0;
	}
	VirtualProtectEx(Process, (LPVOID)Address, Size, OldProtect, NULL);
	CloseHandle(Process);
	return ret;
}

bool WriteMemory(int Address, const void* Buffer, int Size) {
	
	DLOG;
	bool ret = 1;
	DWORD OldProtect;
	HANDLE Process = OpenProcess(PROCESS_ALL_ACCESS, false, GetCurrentProcessId());
	VirtualProtectEx(Process, (LPVOID)Address, Size, PAGE_EXECUTE_READWRITE, &OldProtect);
	if (!WriteProcessMemory(Process, (LPVOID)Address, Buffer, Size, NULL)) {
		ret = 0;
	}
	VirtualProtectEx(Process, (LPVOID)Address, Size, OldProtect, NULL);
	CloseHandle(Process);
	return ret;
}