/**
* vim: set ts=4 :
* =============================================================================
* SourceMod
* Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
* =============================================================================
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 3.0, as published by the
* Free Software Foundation.
* 
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program.  If not, see .
*
* As a special exception, AlliedModders LLC gives you permission to link the
* code of this program (as well as its derivative works) to "Half-Life 2," the
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
* by the Valve Corporation.  You must obey the GNU General Public License in
* all respects for all other code used.  Additionally, AlliedModders LLC grants
* this exception to all derivative works.  AlliedModders LLC defines further
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or .
*
* Version: $Id$
*/
#include "detours.h"
#include 
ISourcePawnEngine *CDetourManager::spengine = NULL;
IGameConfig *CDetourManager::gameconf = NULL;
int CDetourManager::returnValue = 0;
void CDetourManager::Init(ISourcePawnEngine *spengine, IGameConfig *gameconf)
{
	CDetourManager::spengine = spengine;
	CDetourManager::gameconf = gameconf;
}
CDetour *CDetourManager::CreateDetour(void *callbackfunction, size_t paramsize, const char *signame)
{
	CDetour *detour = new CDetour(callbackfunction, paramsize, signame);
	if (detour)
	{
		if (!detour->Init(spengine, gameconf))
		{
			delete detour;
			return NULL;
		}
		return detour;
	}
	return NULL;
}
void CDetourManager::DeleteDetour(CDetour *detour)
{
	delete detour;
}
CBlocker * CDetourManager::CreateFunctionBlock( const char *signame, bool isVoid )
{
	CBlocker *block = new CBlocker(signame, isVoid);
	if (block)
	{
		if (!block->Init(spengine, gameconf))
		{
			delete block;
			return NULL;
		}
		return block;
	}
	return NULL;
}
void CDetourManager::DeleteFunctionBlock(CBlocker *block)
{
	delete block;
}
CDetour::CDetour(void *callbackfunction, size_t paramsize, const char *signame)
{
	enabled = false;
	detoured = false;
	detour_address = NULL;
	detour_callback = NULL;
	this->signame = signame;
	this->callbackfunction = callbackfunction;
	spengine = NULL;
	gameconf = NULL;
	this->paramsize = paramsize;
}
CDetour::~CDetour()
{
	DeleteDetour();
}
bool CDetour::Init(ISourcePawnEngine *spengine, IGameConfig *gameconf)
{
	this->spengine = spengine;
	this->gameconf = gameconf;
	if (!CreateDetour())
	{
		enabled = false;
		return enabled;
	}
	enabled = true;
	return enabled;
}
bool CDetour::IsEnabled()
{
	return enabled;
}
bool CDetour::CreateDetour()
{
	if (!gameconf->GetMemSig(signame, &detour_address))
	{
		g_pSM->LogError(myself, "Could not locate %s - Disabling detour", signame);
		return false;
	}
	if (!detour_address)
	{
		g_pSM->LogError(myself, "Sigscan for %s failed - Disabling detour to prevent crashes", signame);
		return false;
	}
	detour_restore.bytes = copy_bytes((unsigned char *)detour_address, NULL, OP_JMP_SIZE+1);
	/* First, save restore bits */
	for (size_t i=0; iExecAlloc(100);
	JitWriter wr;
	JitWriter *jit = ≀
	jit_uint32_t CodeSize = 0;
	
	wr.outbase = NULL;
	wr.outptr = NULL;
jit_rewind:
	/* Push all our params onto the stack */
	for (size_t i=0; iAllocatePageMemory(CodeSize);
		spengine->SetReadWrite(wr.outbase);
		wr.outptr = wr.outbase;
		detour_callback = wr.outbase;
		goto jit_rewind;
	}
	spengine->SetReadExecute(wr.outbase);
	return true;
}
void CDetour::EnableDetour()
{
	if (!detoured)
	{
		DoGatePatch((unsigned char *)detour_address, &detour_callback);
		detoured = true;
	}
}
void CDetour::DeleteDetour()
{
	if (detoured)
	{
		DisableDetour();
	}
	if (detour_callback)
	{
		/* Free the gate */
		spengine->FreePageMemory(detour_callback);
		detour_callback = NULL;
	}
}
void CDetour::DisableDetour()
{
	if (detoured)
	{
		/* Remove the patch */
		/* This may screw up */
		ApplyPatch(detour_address, 0, &detour_restore, NULL);
		detoured = false;
	}
}
CBlocker::CBlocker( const char *signame, bool isVoid )
{
	this->isVoid = isVoid;
	isEnabled = false;
	isValid = false;
	spengine = NULL;
	gameconf = NULL;
	block_address = NULL;
	block_sig = signame;
	if (isVoid)
	{
		/* Void functions we only patch in a 'ret' (1 byte) */
		block_restore.bytes = 1;
	}
	else
	{
		/* Normal functions need an mov eax, value */
		block_restore.bytes = 6;
	}
}
void CBlocker::EnableBlock( int returnValue )
{
	if (!isValid || isEnabled)
	{
		return;
	}
	/* First, save restore bits */
	for (size_t i=0; ispengine = spengine;
	this->gameconf = gameconf;
	if (!gameconf->GetMemSig(block_sig, &block_address))
	{
		g_pSM->LogError(myself, "Could not locate %s - Disabling blocker", block_sig);
		isValid = false;
		return false;
	}
	if (!block_address)
	{
		g_pSM->LogError(myself, "Sigscan for %s failed - Disabling blocker", block_sig);
		isValid = false;
		return false;
	}
	isValid = true;
	return true;
}