/************************************************************************/
/* BattlEye Client code                                                 */
/************************************************************************/

#include "../client/client.h"
#include "winquake.h"


#ifdef BATTLEYE

static qbyte newBEPacket[BE_MAX_PACKET_SIZE];
static short newBEPacketLen = 0;


/*
short GetBEPacket(void*, short) (1st parameter for Run()): 
This function first checks if cls.runBattlEye = true - if not, it returns -1. 
Then it checks if there is a new/last packet - if not, it returns 0. 
If there is one, it checks if its length is <= buffer length (2nd parameter) 
(fits into the buffer pointed to by the 1st parameter) - if not, the server tries to illegally 
flood the client with impossibly huge BE packets and thus it should disconnect immediately; 
after doing this, it returns -1.
Otherwise it copies the last BE packet into the buffer and returns its length/size. 
The (last) packet data and length are stored globally (variable(s): e.g. a BYTE[1024] buffer 
for the data and an int for the length; or a wsw msg structure?) each time the engine receives 
a packet starting with the unique BE header (e.g. "BE"; note: store the packet/length globally 
without/minus it). 
After doing this, the function clears/cleans up/whatever the global lastBEPacket data and 
length (e.g. fills them with 0), so the function later knows and returns that there is no new 
BE packet when called by BE/Run() immediately again.
BE/Run() calls this function very frequently (in its loop) to check for new incoming packets.
*/
static short GetBEPacket(void* buf, short len)
{
	short thisPacketLen;

	// may we run and thus perform this action ?
	if(!cls.runBattlEye)
		return -1;

	// no new packet
	if(newBEPacketLen == 0)
		return 0;

	// illegally huge packets
	if(newBEPacketLen > len)
	{
		Com_Error( ERR_DROP, "Invalid BattlEye packet size from server" );
		return -1;
	}

	thisPacketLen = newBEPacketLen;
	memcpy(buf, newBEPacket, thisPacketLen);
	newBEPacketLen = 0;

	return thisPacketLen;
}

/*
short SendBEPacket(void*, short) (2nd parameter for Run()): 
This function first checks if cls.runBattlEye = true - if not, it returns -1. 
Otherwise it simply sends a BE packet to the connected server. 
It appends the unique BE header in front of the packet/buffer pointed to by the 1st 
parameter and sends a packet of [2nd parameter]+2 (2 for "BE" as the header) bytes. 
If the packet was sent successfully, it returns 0, otherwise -1.
BE/Run() calls this function to send BE packets to the server.
*/
static short SendBEPacket(void* buf, short len)
{
	// may we run and thus perform this action ?
	if(!cls.runBattlEye)
		return -1;

	memcpy(cls.BE.packets[cls.BE.headPacket & BE_UPDATE_MASK], buf, len);
	cls.BE.packetLens[cls.BE.headPacket & BE_UPDATE_MASK] = len;
	cls.BE.headPacket++;

	return 0;
}

static DWORD WINAPI BattlEyeThread(LPVOID lpParameter)
{
	HMODULE BEModule;
	while(BEModule=LoadLibrary("BattlEye.dll"))
	{
		// the BE "Run" export
		typedef qbyte (*BE_RUN)(short (*)(void*, short), short (*)(void*, short));
		BE_RUN BERun;
		qbyte Result = qfalse;
		if (BERun = (BE_RUN)GetProcAddress(BEModule, "Run"))
		{
			Com_Printf("BattlEye started\n");
			Result = BERun(&GetBEPacket, &SendBEPacket);
		}
		else
			Cbuf_ExecuteText(EXEC_APPEND, "disconnect;menu_msgbox \"Failed to get BattlEye.dll procedure\"");

		// note: Run() is like an application (has its own "infinite" loop) - 
		// it returns only when the below happens

		FreeLibrary(BEModule); // unload the module each time Run() returns 
		// (or if GetProcAddress() fails)

		// if Result = true, everything is ok, BE just wants to be reloaded 
		// (used after auto-updating/downloading) ...

		// if Result = false, BE is done (most likely GetBEPacket failed due to closed connection), 
		// so quit the thread
		if (!Result)
			return 0;
	}

	// this code is reached only if LoadLibrary() failed
	Cbuf_ExecuteText(EXEC_APPEND, "disconnect;menu_msgbox \"Failed to load BattlEye.dll\"");

	return 0;
}

void CL_BE_Start(void)
{
	// static variable holding the handle to the current/last BE thread - it is initially set to NULL
	static HANDLE BEThread=NULL;
	DWORD BEThreadID;

	if(BEThread!=NULL)
	{
		WaitForSingleObject(BEThread, INFINITE);

		CloseHandle(BEThread);
		BEThread=NULL;
	}

	cls.runBattlEye=qtrue;

	// create the thread
	BEThread=CreateThread(NULL, 0, BattlEyeThread, NULL, 0, &BEThreadID);
}

void CL_BE_NewIncomingPacket(void* buf, int len)
{
	memcpy(newBEPacket, buf, len);
	newBEPacketLen = len;
}

#endif // BATTLEYE
