// ACHooks.cpp : Implementation of CDecalApp and DLL registration.

#include "stdafx.h"
#include "ACHooks.h"
#include <math.h>
#include <time.h> /* Heyus 23 March 2003 */
#include ".\achooks.h"

/*
I believe in VC6, the limit of unnamed variables (_bstr_t("foo")) has been reached,
which caused the following error:
F:\Decal\source\Decal\ACHooks.cpp(467) : fatal error C1001: INTERNAL COMPILER ERROR
  (compiler file 'E:\8168\vc98\p2\src\P2\main.c', line 494)
    Please choose the Technical Support command on the Visual C++
    Help menu, or open the Technical Support help file for more information
This macro allows us to use a single variable, while keeping more or less the same syntax.
A variable of type _bstr_t named _Unnamed must be declared in any procedure this is used.

Note - This only works with inline strings -> L"Foo"
*/
#define BSTRT(x) (_Unnamed = L##x, _Unnamed)

/////////////////////////////////////////////////////////////////////////////
cACHooks* cACHooks::s_pACHooks = NULL;
long g_lObjectDestroyedProc = 0;
long g_lSelectItemHijackProc = 0;
long g_lIdentifyHijackProc = 0;
long g_lToolTextProc = 0;
long g_lToolText2Proc = 0;

void (__fastcall *pfnOldChatMessage)(int _ecx, int _edx, char *, DWORD ) = NULL;
void (*pfnOldChatText)() = NULL;
void (*pfnUseItem)( DWORD, DWORD ) = NULL;
void (*pfnMoveItemEx)( DWORD, DWORD, DWORD ) = NULL;
void ( __fastcall *pfnInternalStringDestructor)( qString * ) = NULL;
long (*pfnSendMessageToID)( qString *, long ) = NULL;
long (*pfnSendMessageToName)( qString *, qString * ) = NULL;
long (*pfnSendMessageByMask)( long, qString * ) = NULL;
long (*pfnLocalChatText)( qString * ) = NULL;
long (*pfnLocalChatEmote)( qString * ) = NULL;
qString* ( __fastcall *pfnInternalStringConstructor)( qString *, long, char * ) = NULL;

bool g_bTimestamp;
char *g_szTimestampFormat;
DWORD g_lTimestampColor;
char g_charBufferPreviousCall;   

extern void __fastcall OnChatMessage( int _ecx, int _edx, char* pText, long dwColor ); 
extern void CatchToolText();
extern void CatchToolTextAppend();
extern void OnChatText();

extern void IdentifyShortcircuit();
extern void SelectItemHook();
extern void ObjectDestroyedHook();
extern DWORD HookCall (DWORD dwCallAddress, DWORD dwReplacement);

cACHooks::cACHooks()
{
	g_bTimestamp = false;
	s_pACHooks = this;
	m_bDecalRef = false;
	m_bIdQueueRef = false;
	m_bMismatch = true;
	m_bPrevSelect = false;
	m_bCurrentSelect = false;
	m_bMouse = false;
	m_bCastSpell = false;
	m_bMoveItem = false;
	m_bSelectItem = false;
	m_bUseItem = false;
	m_bAllYourBase = false;
	m_bSetCombatState = false;
	m_bCombatStateOffset = false;
	m_bChatState = false;
	m_bRequestID = false;
	m_bStackCount = false;
	m_bTestFormula = false;
	m_bVendorID = false;
	m_bBusyState = false;
	m_bPointerState = false;
	m_bMoveItemEx = false;
	m_bFaceHeading = false;
	m_bChatMessageAddy = false;
	m_bSendMessageToID = false;
	m_bSendMessageToName = false;
	m_bSendMessageToMask = false;
	m_bLocalChatText = false;
	m_bLocalChatEmote = false;
	m_bSetAutorun = false;
	m_bGetVital = false;
	m_bGetAttribute = false;
	m_bGetSkill = false;
	m_bLogout = false;
	m_bSecureTrade_Add = false;   
	m_bSecureTrade_Add_Off1 = false;   
	m_bSecureTrade_Add_Off2 = false;   
	m_bGetSkillInfo = false;   
	m_bGetAttributeInfo = false;   
	m_bGetVitalInfo = false;
	m_bSelectItemHook = false;
	m_bUstAddItem_Useable = false;
	m_bRequestShortcircuit = false;
	m_bToolTextHook = false;
	m_bToolText2Hook = false;
	m_bIdleLoc = false;

	m_Hooks = 0;
	memset(m_HooksEx, 0, sizeof(m_HooksEx));
	m_HookCount = 0;


	USES_CONVERSION;
	_bstr_t _Unnamed;


	char szPath[ MAX_PATH ];
	DWORD dwCount = MAX_PATH;


	g_charBufferPreviousCall = '\n';
	DWORD dwTimestamp = 0;
	char szTimestampFormat[ MAX_PATH ];
	DWORD lTimestampFormatLength = MAX_PATH;

	RegKey key;
	if( key.Open( HKEY_LOCAL_MACHINE, "SOFTWARE\\Decal", KEY_READ ) == ERROR_SUCCESS )
	{
		if( key.QueryDWORDValue( _T("Timestamp"), dwTimestamp ) != ERROR_SUCCESS )
			dwTimestamp = 0;

		if( key.QueryStringValue( _T("TimestampFormat"), szTimestampFormat, &lTimestampFormatLength ) != ERROR_SUCCESS ) /* Heyus 21 March 2003 */
			memset( szTimestampFormat, 0, sizeof(szTimestampFormat) ), strncpy( szTimestampFormat, "[%H:%M]", sizeof( szTimestampFormat ) );

		if( key.QueryDWORDValue( _T("TimestampColor"), g_lTimestampColor  ) != ERROR_SUCCESS )
			g_lTimestampColor = 12; // Grey

		key.Close();
	}

	int iStrLen = strlen( szTimestampFormat ) + 2;
	g_szTimestampFormat = new char[ iStrLen ];
	memset( g_szTimestampFormat, 0, iStrLen );
	_snprintf( g_szTimestampFormat, iStrLen, "%s ", szTimestampFormat );

	// 0xFFFFFFFF = same color as chat text, for those that want time stamps but don't
	// want to infringe upon their 300 max chat window items

	if( g_lTimestampColor < 0 || g_lTimestampColor > 22 && g_lTimestampColor != 0xFFFFFFFF )
		g_lTimestampColor = 12; // Grey

	g_bTimestamp = dwTimestamp ? true : false ;




	TCHAR szFilename[ MAX_PATH ];
	::GetModuleFileName( NULL, szFilename, MAX_PATH );
	LPTSTR strProcessName = ::_tcsrchr( szFilename, _T( '\\' ) );

	strProcessName[ 7 ] = _T( '\0' );

	if( ::_tcsicmp( strProcessName + 1, _T( "client" ) ) != 0 )
		return;




	key.Open( HKEY_LOCAL_MACHINE, "SOFTWARE\\Decal\\Agent", KEY_READ );   
	key.QueryStringValue( _T("AgentPath"), szPath, &dwCount ); 

	strncat( szPath, "\\memlocs.xml", MAX_PATH );

	MSXML::IXMLDOMDocumentPtr pMemLocDoc;

	pMemLocDoc.CreateInstance( __uuidof( MSXML::DOMDocument ) );
	pMemLocDoc->async = false;

	VARIANT_BOOL bSuccess = pMemLocDoc->load( szPath );
	if( ! bSuccess )
	{
		std::string szXML;
		DecryptXML( szPath, szXML );

		if( szXML != "" )
			bSuccess = pMemLocDoc->loadXML( _bstr_t( szXML.c_str() ) );
	}

	if( bSuccess )
	{
		MSXML::IXMLDOMElementPtr pNode = pMemLocDoc->selectSingleNode( BSTRT( "locations" ) );

		long lVerMajor = 0, lVerMinor = 0, lVerRelease = 0;
		_variant_t vVersion = pNode->getAttribute( BSTRT( "version" ) );

		char *szTemp, *szVersion = OLE2A( vVersion.bstrVal );

		// I'm tired and sscanf is crashing - this works even if it's lame
		lVerMajor = atol( szVersion );
		szTemp = strstr( szVersion, "." ) + 1;
		lVerMinor = atol( szTemp );
		szTemp = strstr( szTemp, "." ) + 1;
		lVerRelease = atol( szTemp );

		RegKey rk;
		if( rk.Open( HKEY_LOCAL_MACHINE, _T( "Software\\Microsoft\\Microsoft Games\\Asheron's Call\\1.00" ) ) == ERROR_SUCCESS )
		{
			TCHAR szClientPath[ MAX_PATH ];
			DWORD dwPathLength = MAX_PATH;
			if( rk.QueryStringValue ( _T( "path" ), szClientPath, &dwPathLength ) == ERROR_SUCCESS )
			{
				::_tcscpy( szClientPath + ( dwPathLength - 1 ), _T( "\\client.exe" ) );

				DWORD dwDummy, 
					dwVerSize = ::GetFileVersionInfoSize( const_cast< LPTSTR > ( szClientPath ), &dwDummy );
				if( dwVerSize != 0 )
				{
					BYTE *pbVersionInfo = reinterpret_cast< BYTE * >( ::_alloca( dwVerSize ) );

					::GetFileVersionInfo( const_cast< LPTSTR > ( szClientPath ), 0, dwVerSize, pbVersionInfo );

					VS_FIXEDFILEINFO *vffi;
					UINT nLength = sizeof( VS_FIXEDFILEINFO );
					if( ::VerQueryValue( pbVersionInfo, _T( "\\" ), reinterpret_cast< LPVOID * >( &vffi ), &nLength ) )
					{
						if(	(static_cast< long >( HIWORD( vffi->dwFileVersionMS ) ) == lVerMajor) &&
							(static_cast< long >( LOWORD( vffi->dwFileVersionMS ) ) == lVerMinor) &&
							(static_cast< long >( HIWORD( vffi->dwFileVersionLS ) ) == lVerRelease) )
						{
							m_bMismatch = false;
						}
					}
				}
			}
		}

		MSXML::IXMLDOMNodeListPtr pNodes = pMemLocDoc->selectNodes( BSTRT( "/locations/memloc" ) );

		for( pNode = pNodes->nextNode(); pNode.GetInterfacePtr() != NULL; pNode = pNodes->nextNode() )
		{
			_variant_t vName = pNode->getAttribute( _T( "name" ) ), vValue = pNode->getAttribute( _T( "value" ) );

			sMemoryLocation loc;

			loc.Name = OLE2A( vName.bstrVal );
			loc.Location = wcstoul( vValue.bstrVal, 0, 16 );

			m_mLocationList[ loc.Name ] = loc;
		}
	}

	long Val;
	if( QueryMemLoc( BSTRT( "PrevItem1" ), &Val ) == S_OK )
		m_lPrevSelect[0] = Val;
	if( QueryMemLoc( BSTRT( "PrevItem2" ), &Val ) == S_OK )
	{
		m_lPrevSelect[1] = Val;
		m_bPrevSelect = true;
		m_Hooks |= ePrevSelect ;
	}

	if( QueryMemLoc( BSTRT( "CurrentItem1" ), &Val ) == S_OK )
		m_lCurrentSelect[0] = Val;
	if( QueryMemLoc( BSTRT( "CurrentItem2" ), &Val ) == S_OK )
	{
		m_lCurrentSelect[1] = Val;
		m_bCurrentSelect = true;
		m_Hooks |= eCurrentSelect ;
	}

	if( QueryMemLoc( BSTRT( "ChatMessage" ), &Val ) == S_OK )
		pfnOldChatMessage = reinterpret_cast< void (__fastcall*)( int, int, char *, DWORD ) >( Val );
	if( QueryMemLoc( BSTRT( "ChatMessageAddy" ), &Val ) == S_OK )
	{
		m_lChatMessageAddy = Val;
		m_bChatMessageAddy = true;
	}
	if( QueryMemLoc( BSTRT( "ChatText" ), &Val ) == S_OK )
		pfnOldChatText = reinterpret_cast< void (*)() >( Val );


	if( QueryMemLoc( BSTRT( "Mouse1X" ), &Val ) == S_OK )
		m_lMouse[0] = Val;
	if( QueryMemLoc( BSTRT( "Mouse2X" ), &Val ) == S_OK )
		m_lMouse[1] = Val;
	if( QueryMemLoc( BSTRT( "Mouse3X" ), &Val ) == S_OK )
		m_lMouse[2] = Val;
	if( QueryMemLoc( BSTRT( "Mouse4X" ), &Val ) == S_OK )
	{
		m_bMouse = true;
		m_lMouse[3] = Val;
		m_Hooks |= eMouse ;
	}

	if( QueryMemLoc( BSTRT( "CastSpell" ), &Val ) == S_OK )
	{
		m_bCastSpell = true;
		m_Hooks |= eCastSpell ;
		m_lCastSpell = Val;
	}

	if( QueryMemLoc( BSTRT( "MoveItem" ), &Val ) == S_OK )
	{
		m_bMoveItem = true;
		m_Hooks |= eMoveItem ;
		m_lMoveItem = Val;
	}

	if( QueryMemLoc( BSTRT( "SelectItem" ), &Val ) == S_OK )
	{
		m_bSelectItem = true;
		m_Hooks |= eSelectItem ;
		m_lSelectItem = Val;
	}

	if( QueryMemLoc( BSTRT( "UseItem" ), &Val ) == S_OK )
	{
		m_bUseItem = true;
		m_Hooks |= eUseItem ;
		SetHookEx( eUseItemRaw );
		SetHookEx( eUseFociSpell );
		m_lUseItem = Val;
		pfnUseItem = reinterpret_cast< void (*)( DWORD, DWORD ) >( Val );
	}

	if( QueryMemLoc( BSTRT( "AllYourBase" ), &Val ) == S_OK )
	{
		// The memloc formerly known as CombatState
		m_bAllYourBase = true;
		m_Hooks |= eCombatState ;
		m_lAllYourBase = Val;
	}

	if( QueryMemLoc( BSTRT( "SetCombatState" ), &Val ) == S_OK )
	{
		m_bSetCombatState = true;
		m_Hooks |= eSetCombatState ;
		m_lSetCombatState = Val;
	}

	if( QueryMemLoc( BSTRT( "CombatStateOffset" ), &Val ) == S_OK )
	{
		m_bCombatStateOffset = true;
		m_Hooks |= eCombatState ;
		m_lCombatStateOffset = Val;
	}

	if( QueryMemLoc( BSTRT( "ChatState" ), &Val ) == S_OK )
	{
		m_bChatState = true;
		m_Hooks |= eChatState ;
		m_lChatState = Val;
	}

	if( QueryMemLoc( BSTRT( "GetFellowStats" ), &Val ) == S_OK )
	{
		m_bRequestID = true;
		m_lRequestID = Val;
		m_Hooks |= eGetFellowStats;
		SetHookEx( eRequestID );
	}

	if( QueryMemLoc( BSTRT( "RequestID" ), &Val ) == S_OK )
	{
		m_bRequestID = true;
		m_lRequestID = Val;
		m_Hooks |= eGetFellowStats;
		SetHookEx( eRequestID );
	}

	if( QueryMemLoc( BSTRT( "NumStackItemsSelected" ), &Val ) == S_OK )
	{
		m_bStackCount = true;
		m_Hooks |= eStackCount ;
		m_lStackCount = Val;
	}

	if( QueryMemLoc( BSTRT( "TestFormula" ), &Val ) == S_OK )
		m_lTestFormula = Val;
	if( QueryMemLoc( BSTRT( "TestFormulavtp" ), &Val ) == S_OK )
	{
		m_bTestFormula = true;
		m_Hooks |= eTestFormula ;
		m_lTestFormulaVTable = Val;
	}

	if( QueryMemLoc( BSTRT( "VendorID" ), &Val ) == S_OK )
	{
		m_bVendorID = true;
		m_Hooks |= eVendorID ;
		m_lVendorID = Val;
	}

	if( QueryMemLoc( BSTRT( "ItemBusyState" ), &Val ) == S_OK )
	{
		m_bBusyState = true;
		m_Hooks |= eBusyState ;
		m_lBusyState = Val;
	}
	if( QueryMemLoc( BSTRT( "ItemBusyGUID" ), &Val ) == S_OK )
	{
		m_bBusyStateID = true;
		m_Hooks |= eBusyStateID ;
		m_lBusyStateID = Val;
	}

	if( QueryMemLoc( BSTRT( "PointerState" ), &Val ) == S_OK )
	{
		m_bPointerState = true;
		m_Hooks |= ePointerState ;
		m_lPointerState = Val;
	}

	if( QueryMemLoc( BSTRT( "MoveItemEx" ), &Val ) == S_OK )
	{
		m_bMoveItemEx = true;
		m_Hooks |= eMoveItemEx ;
		m_lMoveItemEx = Val;
		pfnMoveItemEx = reinterpret_cast< void (*)(DWORD, DWORD, DWORD) >( Val );
	}

	if( QueryMemLoc( BSTRT( "3DAreaWidth" ), &Val ) == S_OK )
	{
		m_bArea3DWidth = true;
		m_Hooks |= eArea3DWidth ;
		m_lpArea3DWidth = reinterpret_cast< long * >( Val );
	}

	if( QueryMemLoc( BSTRT( "3DAreaHeight" ), &Val ) == S_OK )
	{
		m_bArea3DHeight = true;
		m_Hooks |= eArea3DHeight ;
		m_lpArea3DHeight = reinterpret_cast< long * >( Val );
	}

	if( QueryMemLoc( BSTRT( "ObjectFromGUID" ), &Val ) == S_OK ) {
		m_lObjectFromGuid = Val;
		SetHookEx( eObjectFromGUID );
		if( QueryMemLoc( BSTRT( "ObjectFromGUIDClass" ), &Val ) == S_OK )
		{
			m_lObjectFromGuidClass = Val;
			SetHookEx( eObjectFromGUIDClass );
			m_bObjectFromGuid = true;
		}
	}
	m_lMovementThingyParent = m_lMovementThingyOffset = m_lFaceHeading = 0;

	if( QueryMemLoc( BSTRT( "MovementThingyParent" ), &Val ) == S_OK)
	{
		m_lMovementThingyParent = Val;
	}

	if( QueryMemLoc( BSTRT( "MovementThingyOffset" ), &Val ) == S_OK)
	{
		m_lMovementThingyOffset = Val;
	}

	if( QueryMemLoc( BSTRT( "FaceHeading" ), &Val ) == S_OK)
	{
		m_lFaceHeading = Val;
	}

	if( QueryMemLoc( BSTRT( "SetAutorun" ), &Val ) == S_OK)
	{
		m_lSetAutorun = Val;
	}

	if (m_lMovementThingyParent && m_lMovementThingyOffset && m_lFaceHeading)
	{
		m_bFaceHeading = true;

		m_Hooks |= eFaceHeading;
	}

	if (m_lMovementThingyParent && m_lMovementThingyOffset && m_lSetAutorun)
	{
		m_bSetAutorun = true;

		m_Hooks |= eSetAutorun;
	}

	/*
	// While the address of ObjectDestroyed is interesting, it isn't actually needed.
	// We can't assume that the calls we're hooking call to this address, as they
	// might have already been hooked!
	if( QueryMemLoc( BSTRT( "ObjectDestroyed"), &Val ) == S_OK )
	{
	}
	*/

	long lCall1 = 0, lCall2 = 0;
	if( QueryMemLoc( BSTRT( "ObjectDestroyed_Call1"), &Val ) == S_OK )
	{
		lCall1 = Val;
	}

	if( QueryMemLoc( BSTRT( "ObjectDestroyed_Call2"), &Val ) == S_OK )
	{
		lCall2 = Val;
	}

	if (lCall1 && lCall2)
	{
		DWORD dwFunc1 = HookCall (lCall1, (DWORD) ObjectDestroyedHook);
		DWORD dwFunc2 = HookCall (lCall2, (DWORD) ObjectDestroyedHook);

		_ASSERTE (dwFunc1 && (dwFunc1 == dwFunc2));

		if (dwFunc1 && (dwFunc1 != dwFunc2))
		{
			// Doh, okay put them back, something's wrong.
			HookCall (lCall1, dwFunc1);
			HookCall (lCall2, dwFunc2);
		}
		else
		{
			m_Hooks |= eObjectDestroyed;
			g_lObjectDestroyedProc = dwFunc1;
		}
	}

	if( QueryMemLoc( BSTRT( "InternalStringConstructor" ), &Val ) == S_OK)
	{
		m_bInternalStringConstructor = true;
		m_lInternalStringConstructor = Val;

		pfnInternalStringConstructor = reinterpret_cast< qString *(__fastcall*)(qString *, long, char *) >( Val );
	}

	if( QueryMemLoc( BSTRT( "InternalStringDestructor" ), &Val ) == S_OK)
	{
		m_bInternalStringDestructor = true;
		m_lInternalStringDestructor = Val;

		pfnInternalStringDestructor = reinterpret_cast< void(__fastcall *)(qString *) >( Val );
	}

	if( QueryMemLoc( BSTRT( "SendMessageToID" ), &Val ) == S_OK)
	{
		m_bSendMessageToID = true;
		m_lSendMessageToID = Val;

		pfnSendMessageToID = reinterpret_cast< long(*)(qString *, long) >( Val );
	}

	if( QueryMemLoc( BSTRT( "SendMessageToName" ), &Val ) == S_OK)
	{
		m_bSendMessageToName = true;
		m_lSendMessageToName = Val;

		pfnSendMessageToName = reinterpret_cast< long(*)(qString *, qString *) >( Val );
	}

	if( QueryMemLoc( BSTRT( "SendMessageToMask" ), &Val ) == S_OK)
	{
		m_bSendMessageToMask = true;
		m_lSendMessageToMask = Val;

		pfnSendMessageByMask = reinterpret_cast< long(*)( long, qString * ) >( Val );
	}

	if( QueryMemLoc( BSTRT( "LocalChatText" ), &Val ) == S_OK)
	{
		m_bLocalChatText = true;
		m_lLocalChatText = Val;

		pfnLocalChatText = reinterpret_cast< long(*)(qString *) >( Val );
	}

	if( QueryMemLoc( BSTRT( "LocalChatEmote" ), &Val ) == S_OK)
	{
		m_bLocalChatEmote = true;
		m_lLocalChatEmote = Val;

		pfnLocalChatEmote = reinterpret_cast< long(*)(qString *) >( Val );
	}

	if( m_bInternalStringConstructor && m_bInternalStringDestructor && m_bSendMessageToID )
		m_Hooks |= eSendTell;

	if( m_bInternalStringConstructor && m_bInternalStringDestructor && m_bSendMessageToName )
		m_Hooks |= eSendTellEx;

	if( m_bInternalStringConstructor && m_bInternalStringDestructor && m_bLocalChatText )
		m_Hooks |= eLocalChatText;

	if( m_bInternalStringConstructor && m_bInternalStringDestructor && m_bLocalChatEmote )
		m_Hooks |= eLocalChatEmote;

	if( m_bInternalStringConstructor && m_bInternalStringDestructor && m_bSendMessageToMask )
		SetHookEx( eSendMessageByMask );

	if( QueryMemLoc( BSTRT( "VitalBase" ), &Val ) == S_OK )
	{
		m_lVitalBase = Val;
	}

	if( QueryMemLoc( BSTRT( "GetVital" ), &Val ) == S_OK )
	{
		m_lGetVital = Val;
		m_bGetVital = true;
	}

	if( QueryMemLoc( BSTRT( "GetAttribute" ), &Val ) == S_OK )
	{
		m_lGetAttribute = Val;
		m_bGetAttribute = true;
	}

	if( QueryMemLoc( BSTRT( "GetSkill" ), &Val ) == S_OK )
	{
		m_lGetSkill = Val;
		m_bGetSkill = true;
	}

	if( m_lVitalBase )
	{
		if( m_bGetVital )
			m_Hooks |= eGetVital;

		if( m_bGetAttribute )
			m_Hooks |= eGetAttribute;

		if( m_bGetSkill )
			m_Hooks |= eGetSkill;
	}

	if( QueryMemLoc( BSTRT( "Logout" ), &Val ) == S_OK )
	{
		m_lLogout = Val;
		m_bLogout = true;
		SetHookEx(eLogout);
	}

	if (QueryMemLoc(BSTRT( "SecureTrade_Add" ), &Val) == S_OK)   
	{   
		m_lSecureTrade_Add = Val;   
		m_bSecureTrade_Add = true;   
	}   

	if (QueryMemLoc(BSTRT( "SecureTrade_Add_Off1" ), &Val) == S_OK)   
	{   
		m_lSecureTrade_Add_Off1 = Val;   
		m_bSecureTrade_Add_Off1 = true;   
	}   

	if (QueryMemLoc(BSTRT( "SecureTrade_Add_Off2" ), &Val) == S_OK)   
	{   
		m_lSecureTrade_Add_Off2 = Val;   
		m_bSecureTrade_Add_Off2 = true;   
	}   

	if (m_bSecureTrade_Add && m_bSecureTrade_Add_Off1 && m_bSecureTrade_Add_Off2 && m_bAllYourBase)   
	{   
		SetHookEx(eSecureTrade_Add);   
	}   


	if (QueryMemLoc(BSTRT( "GetSkillInfo" ), &Val) == S_OK)   
	{   
		m_lGetSkillInfo = Val;   
		m_bGetSkillInfo = true;   
	}   

	if (QueryMemLoc(BSTRT( "GetVitalInfo" ), &Val) == S_OK)   
	{   
		m_lGetVitalInfo = Val;   
		m_bGetVitalInfo = true;   
	}   

	if (QueryMemLoc(BSTRT( "GetAttributeInfo" ), &Val) == S_OK)   
	{   
		m_lGetAttributeInfo = Val;   
		m_bGetAttributeInfo = true;   
	}   


	/* I don't believe the vTable is used for any of these, however, if it becomes necessary   
	the functionality is here to a) detect the problem (crash at 0xFF......), and fix it   
	without requiring a complete update to Decal */   
	if (QueryMemLoc(BSTRT( "GetSkillInfo_vT" ), &Val) == S_OK)   
	{   
		m_lGetSkillInfo_vT = Val;   
	}   
	else   
	{   
		m_lGetSkillInfo_vT = 0xFF111111;   
	}   

	if (QueryMemLoc(BSTRT( "GetVitalInfo_vT" ), &Val) == S_OK)   
	{   
		m_lGetVitalInfo_vT = Val;   
	}   
	else   
	{   
		m_lGetVitalInfo_vT = 0xFF222222;   
	}   

	if (QueryMemLoc(BSTRT( "GetAttributeInfo_vT" ), &Val) == S_OK)   
	{   
		m_lGetAttributeInfo_vT = Val;   
	}   
	else   
	{   
		m_lGetAttributeInfo_vT = 0xFF333333;   
	}   

	if (m_lVitalBase) {   
		if (m_bGetSkillInfo) {   
			SetHookEx(eSkillInfo);   
		}   
		if (m_bGetVitalInfo) {   
			SetHookEx(eVitalInfo);   
		}   
		if (m_bGetAttributeInfo) {   
			SetHookEx(eAttributeInfo);   
		}   
	} 

	if (QueryMemLoc(BSTRT( "ToolTextHJ" ), &Val) == S_OK)
	{
		m_lToolTextHJ = Val;

		g_lToolTextProc = HookCall( m_lToolTextHJ, (DWORD)CatchToolText );

		SetHookEx( eToolText );
		m_bToolTextHook = true;
	}

	if (QueryMemLoc(BSTRT( "ToolText2HJ" ), &Val) == S_OK)
	{
		m_lToolText2HJ = Val;

		g_lToolText2Proc = HookCall( m_lToolText2HJ, (DWORD)CatchToolTextAppend );

		SetHookEx( eToolText2 );
		m_bToolText2Hook = true;
	}

	DWORD	dwOldProtect;
	if( QueryMemLoc( BSTRT( "OnChatText" ), &Val ) == S_OK )
	{
		VirtualProtect( reinterpret_cast< void * >( Val ), 4, PAGE_EXECUTE_READWRITE, &dwOldProtect );
		*reinterpret_cast< long * >( Val ) = ( reinterpret_cast< long >( OnChatText ) - ( Val + 0x4 ) );
	}

	if( QueryMemLoc( BSTRT( "OnChatMessage" ), &Val ) == S_OK )
	{
		VirtualProtect( reinterpret_cast< void * >( Val ), 4, PAGE_EXECUTE_READWRITE, &dwOldProtect );
		*reinterpret_cast< long * >( Val ) = ( reinterpret_cast< long >( OnChatMessage ) - ( Val + 0x4 ) );
	}

	m_Hooks |= eHooksAvailEx;

	if( QueryMemLoc( BSTRT( "SelectItemHook" ), &Val ) == S_OK )
	{
		m_lSelectItemHook = Val;
		m_bSelectItemHook = true;
		g_lSelectItemHijackProc = HookCall( m_lSelectItemHook, (DWORD) SelectItemHook );
		SetHookEx( eOnSelectItemEvent );
	}


	bool m_bUstAddItem = false;
	bool m_bUstAddItem_Off1 = false;
	bool m_bUstAddItem_Off2 = false;
	if (QueryMemLoc(BSTRT( "UstAddItem" ), &Val) == S_OK)
	{
		m_lUstAddItem = Val;
		m_bUstAddItem = true;
	}

	if (QueryMemLoc(BSTRT( "UstAddItem_Off1" ), &Val) == S_OK)
	{
		m_lUstAddItem_Off1 = Val;
		m_bUstAddItem_Off1 = true;
	}

	if (QueryMemLoc(BSTRT( "UstAddItem_Off2" ), &Val) == S_OK)
	{
		m_lUstAddItem_Off2 = Val;
		m_bUstAddItem_Off2 = true;
	}

	if (m_bUstAddItem && m_bUstAddItem_Off1 && m_bUstAddItem_Off2 && m_bChatMessageAddy)
	{
		SetHookEx(eUstAddItem);
		m_bUstAddItem_Useable = true;
	}

	m_lRequestShortcircuit1 = 0;
	m_lRequestShortcircuit2 = 0;
	m_lRequestShortcircuit3 = 0;

	if( QueryMemLoc( BSTRT( "RequestShortcircuit1" ), &Val ) == S_OK )
		m_lRequestShortcircuit1 = Val;
	if( QueryMemLoc( BSTRT( "RequestShortcircuit2" ), &Val ) == S_OK )
		m_lRequestShortcircuit2 = Val;
	if( QueryMemLoc( BSTRT( "RequestShortcircuit3" ), &Val ) == S_OK )
		m_lRequestShortcircuit3 = Val;

	if( m_lRequestShortcircuit1 && m_lRequestShortcircuit2 && m_lRequestShortcircuit3 )
	{
		DWORD dwHook1 = HookCall( m_lRequestShortcircuit1, (DWORD) IdentifyShortcircuit );
		DWORD dwHook2 = HookCall( m_lRequestShortcircuit2, (DWORD) IdentifyShortcircuit );
		DWORD dwHook3 = HookCall( m_lRequestShortcircuit3, (DWORD) IdentifyShortcircuit );

		if( dwHook1 && dwHook2 && dwHook3 && (dwHook1 == dwHook2) && (dwHook2 == dwHook3) )
		{
			m_bRequestShortcircuit = true;
			g_lIdentifyHijackProc = dwHook1;
		}

		else
		{
			// Something's wrong.
			HookCall( m_lRequestShortcircuit1, dwHook1 );
			HookCall( m_lRequestShortcircuit2, dwHook2 );
			HookCall( m_lRequestShortcircuit3, dwHook3 );
		}
	}

	if( QueryMemLoc( BSTRT( "IdleTime" ), &Val ) == S_OK )
	{
		m_lIdleLoc = Val;
		m_bIdleLoc = true;
		SetHookEx( eSetIdleTime );
	}

	if( QueryMemLoc( BSTRT( "SlashDay" ), &Val ) == S_OK )
	{
		m_lSlashDay = Val;
		m_bSlashDay = true;
		SetHookEx( eSetDay );
	}
}

cACHooks::~cACHooks()
{
	_bstr_t _Unnamed;

	delete [] g_szTimestampFormat;

	long Val, lCall1 = 0, lCall2 = 0;
	if( QueryMemLoc( BSTRT( "ObjectDestroyed_Call1"), &Val ) == S_OK )
	{
		lCall1 = Val;
	}

	if( QueryMemLoc( BSTRT( "ObjectDestroyed_Call2"), &Val ) == S_OK )
	{
		lCall2 = Val;
	}

	if (lCall1 && lCall2)
	{
		HookCall( lCall1, g_lObjectDestroyedProc );
		HookCall( lCall2, g_lObjectDestroyedProc );
	}

	if( m_bRequestShortcircuit )
	{
		HookCall( m_lRequestShortcircuit1, g_lIdentifyHijackProc );
		HookCall( m_lRequestShortcircuit2, g_lIdentifyHijackProc );
		HookCall( m_lRequestShortcircuit3, g_lIdentifyHijackProc );
	}

	if( m_bToolTextHook )
	{
		HookCall( m_lToolTextHJ, g_lToolTextProc );
	}

	if( m_bToolText2Hook )
	{
		HookCall( m_lToolText2HJ, g_lToolText2Proc );
	}

	s_pACHooks = NULL;
	g_lObjectDestroyedProc = 0;

	if( m_bDecalRef )
		m_pDecal.Release();

	if( m_bIdQueueRef )
		m_pIdQueue.Release();
}

void cACHooks::DecryptXML( const char *szPath, std::string &szXML )
{
	if( szPath == NULL )
	{
		szXML = "";
		return;
	}

	FILE *f = fopen( szPath, "rb" );
	if( f == NULL )
	{
		szXML = "";
		return;
	}

	szXML.clear();
	unsigned char szBuffer[1025];

	try
	{
		CCryptProv crypt;
		if( crypt.Initialize( PROV_RSA_FULL, "Decal_Memlocs", MS_DEF_PROV ) == NTE_BAD_KEYSET )
			crypt.Initialize( PROV_RSA_FULL, "Decal_Memlocs", MS_DEF_PROV, CRYPT_NEWKEYSET );

		CCryptMD5Hash hash;
		hash.Initialize( crypt );
		hash.AddString( DECAL_KEY );

		CCryptDerivedKey key;
		key.Initialize( crypt, hash );

		DWORD dwDecLen = 0;

		while( ! feof(f) )
		{
			memset( szBuffer, 0, sizeof( szBuffer ) );
			dwDecLen = fread( szBuffer, 1, 1024, f );
			key.Decrypt( feof(f), (BYTE *) szBuffer, &dwDecLen );
			szXML += (char *)szBuffer;
		}

		key.Destroy();
		hash.Destroy();
		crypt.Release();
	}

	catch( ... )
	{
		// crap...
		szXML = "";
	}

	fclose( f );
}

STDMETHODIMP cACHooks::SetDecal( IUnknown *pDecal )
{
	if( m_bDecalRef )
	{
		m_pDecal.Release();
		m_bDecalRef = false;
	}

	if( pDecal->QueryInterface( __uuidof( IDecal ), reinterpret_cast< void ** >( &m_pDecal ) ) == S_OK )
		m_bDecalRef = true;

	return S_OK;
}

STDMETHODIMP cACHooks::QueryMemLoc(BSTR bstrName, long *pVal)
{
	USES_CONVERSION;

	if( m_bMismatch )
	{
		*pVal = NULL;
		return S_FALSE;
	}

	std::string queryName = OLE2A( bstrName );

	LocMap::iterator i = m_mLocationList.find( queryName );
	if( i != m_mLocationList.end() )
	{
		*pVal = i->second.Location;
		return S_OK;
	}

	return S_FALSE;
}

STDMETHODIMP cACHooks::get_CurrentSelection( long *pVal )
{
	if( !m_bCurrentSelect )
		return S_FALSE;

	*pVal = *( reinterpret_cast< long * >( m_lCurrentSelect[0] ) );

	return S_OK;
}

STDMETHODIMP cACHooks::put_CurrentSelection(long newVal)
{
	if( !m_bCurrentSelect )
		return S_FALSE;

	*( reinterpret_cast< long * >( m_lCurrentSelect[0] ) ) = newVal;
	*( reinterpret_cast< long * >( m_lCurrentSelect[1] ) ) = newVal;

	return S_OK;
}

STDMETHODIMP cACHooks::get_PreviousSelection(long *pVal)
{
	if( !m_bPrevSelect )
		return S_FALSE;

	*pVal = *( reinterpret_cast< long * >( m_lPrevSelect[0] ) );

	return S_OK;
}

STDMETHODIMP cACHooks::put_PreviousSelection(long newVal)
{
	if( !m_bPrevSelect )
		return S_FALSE;

	*( reinterpret_cast< long * >( m_lPrevSelect[0] ) ) = newVal;
	*( reinterpret_cast< long * >( m_lPrevSelect[1] ) ) = newVal;

	return S_OK;
}

/* Heyus - 23 March 2003 */
// Should be the method by which all functions write text to the chat window
// Writes raw text (does not append \n).  chatMessageArg1 and 2 are the first two
// arguments you normally pass to pfnOldChatMessage.  THIS SHOULD NOT BE CALLED BY
// PLUGINS/FILTERS UNLESS ABSOLUTELY NECESSARY.
// Will implement IKitchenSink later
void InternalRawWriteToChat( char* pText, long lColor, int chatMessageArg1, int chatMessageArg2 )
{
	if( pText == NULL )
		return;

	if( g_bTimestamp )
	{
		bool bDoTimeStamp = (g_charBufferPreviousCall == '\n') ? true : false;
		int iStrLen = strlen( pText );

		char *szTimeStamp = new char[ iStrLen + 2 ];
		memset( szTimeStamp, 0, iStrLen + 2 );

		// Copy into a larger buffer to accomodate a possible \n
		// If i were more awake, I'd see if this could just be replaced with
		// g_charBufferPreviousCall = pText[strlen(pText)]
		int iOffset = 0;
		char *szTemp = pText;
		while( *szTemp != 0 )
		{
			szTimeStamp[ iOffset++ ] = *szTemp;
			g_charBufferPreviousCall = *szTemp;
			szTemp++;
		}

		// Format a time stamp
		char szFormattedTimestamp[ MAX_PATH ];
		memset( szFormattedTimestamp, 0, MAX_PATH );

		time_t stTmpTime;
		time( &stTmpTime );

		tm *tmTime = localtime( &stTmpTime );
		long lFormattedStampLength = (long) strftime( szFormattedTimestamp, MAX_PATH, g_szTimestampFormat, tmTime );

		while( *szTimeStamp != NULL && *szTimeStamp == '\n' )
		{
			pfnOldChatMessage( chatMessageArg1, chatMessageArg2, "\n", lColor );
			g_charBufferPreviousCall = '\n';
			*szTimeStamp++;
		}

		char* szToken = strtok( szTimeStamp, _T( "\n" ) );

		char* toWrite;

		if( g_lTimestampColor != 0xFFFFFFFF )
			lFormattedStampLength = 0;

		while( szToken != NULL )
		{
			int len = strlen( szToken ) + lFormattedStampLength + 3;
			toWrite = new char[ len ];
			memset( toWrite, 0, len );

			if( bDoTimeStamp )
			{
				if( g_lTimestampColor == 0xFFFFFFFF )
					toWrite = strcat( toWrite, szFormattedTimestamp );
				else
					pfnOldChatMessage( chatMessageArg1, chatMessageArg2, szFormattedTimestamp, g_lTimestampColor );
			}

			toWrite = strcat( toWrite, szToken );

			char* nextToken = strtok( NULL, "\n" );
			if( nextToken != NULL || pText[strlen(pText)-1] == '\n' )
				toWrite = strcat( toWrite, "\n" );

			pfnOldChatMessage( chatMessageArg1, chatMessageArg2, toWrite, lColor );
			szToken = nextToken;

			delete[] toWrite;
		}

		delete[] szTimeStamp;
	}

	else
		pfnOldChatMessage( chatMessageArg1, chatMessageArg2, pText, lColor );

	return;
}

STDMETHODIMP cACHooks::ChatOut(BSTR szText, long lColor)
{
	if( pfnOldChatMessage == NULL )
		return S_FALSE;

	DWORD *Addy = reinterpret_cast< DWORD * > ( m_lChatMessageAddy );
	if( *Addy == NULL )
		return S_FALSE;

	USES_CONVERSION;

	char *strText = OLE2A( szText );

	if( strText == NULL )
		return S_FALSE;

	int iStrLen = strlen( strText );

	int iOffset = 0;
	char* szTemp = new char[iStrLen + 2];
	memset( szTemp, 0, iStrLen+2 );
	strncpy( szTemp, strText, iStrLen + 2 );
	strncat( szTemp, "\n", iStrLen + 2 );
	InternalRawWriteToChat( szTemp, lColor, *Addy, 0 );

	return S_OK;
}

/* Heyus - 23 Mar 2003 - Fixed to point to the new universal function */
STDMETHODIMP cACHooks::RawChatOut(BSTR szText, long lColor)
{
	if( pfnOldChatMessage == NULL )
		return S_FALSE;

	DWORD *Addy = reinterpret_cast< DWORD * > ( m_lChatMessageAddy );
	if( *Addy == NULL )
		return S_FALSE;

	USES_CONVERSION;

	char *strText = OLE2A( szText );

	InternalRawWriteToChat( strText, lColor, *Addy, 0 );
	return S_OK;
}

STDMETHODIMP cACHooks::SetCursorPosition(long lX, long lY)
{
	if( !m_bMouse )
		return S_FALSE;

	for( int i = 0; i < 4; i++ )
	{
		*(reinterpret_cast< long * >( m_lMouse[i] ) ) = lX;
		*(reinterpret_cast< long * >( m_lMouse[i] + 4 ) ) = lY;
	}

	SetCursorPos( lX, lY );

	for( i = 0; i < 4; i++ )
	{
		*(reinterpret_cast< long * >( m_lMouse[i] ) ) = lX;
		*(reinterpret_cast< long * >( m_lMouse[i] + 4 ) ) = lY;
	}

	return S_OK;
}

STDMETHODIMP cACHooks::CastSpell(long lSpellID, long lObjectID)
{
	if( !m_bCastSpell )
		return S_FALSE;	

	long lCombatState = 4; 
	if( get_CombatState( &lCombatState ) == S_OK )
	{
		if( lCombatState != 4 ) //Must be in MAGIC MODE
			return S_FALSE;
	}

	long lCurrentSelect;
	get_CurrentSelection( &lCurrentSelect );
	put_CurrentSelection( lObjectID );

	typedef void(*CastPtr)( long );
	CastPtr pCast = reinterpret_cast< CastPtr >( m_lCastSpell );
	pCast( lSpellID );

	put_CurrentSelection( lCurrentSelect );

	return S_OK;
}

STDMETHODIMP cACHooks::MoveItem(long lObjectID, long lPackID, long lSlot, VARIANT_BOOL bStack)
{
	if( !m_bMoveItem )
		return S_FALSE;

	long lStack = 0;
	if( bStack == VARIANT_TRUE )
		lStack = 1;

 	typedef void(*MovePtr)( long, long, long, long );
 	MovePtr pMove = reinterpret_cast< MovePtr >( m_lMoveItem );
	pMove( lObjectID, lPackID, lSlot, lStack );
	
	return S_OK;
}

STDMETHODIMP cACHooks::SelectItem(long lObjectID)
{
	if( !m_bSelectItem )
		return S_FALSE;

 	typedef void(*SelectPtr)( long );
 	SelectPtr pSelect = reinterpret_cast< SelectPtr >( m_lSelectItem );
	pSelect( lObjectID );

	return S_OK;
}

STDMETHODIMP cACHooks::UseItem(long lObjectID, long lUseState)
{
	if( !m_bUseItem )
		return S_FALSE;

	__asm
	{
		push 0x0
		push lUseState
		push lObjectID
		call pfnUseItem
		add esp, 0x0C
	}

	return S_OK;
}

STDMETHODIMP cACHooks::UseItemRaw(long lObjectID, long lUseState, long lUseMethod)
{
	if( !m_bUseItem )
		return S_FALSE;

	__asm
	{
		push lUseMethod
		push lUseState
		push lObjectID
		call pfnUseItem
		add esp, 0x0C
	}

	return S_OK;
}

STDMETHODIMP cACHooks::UseFociSpell(long UseThis, long OnThis)
{
	if( !m_bUseItem )
		return S_FALSE;

	long lCurrentSelected;
	get_CurrentSelection( &lCurrentSelected );
	put_CurrentSelection( OnThis );

	__asm
	{
		push 0x1
		push 0x1
		push UseThis
		call pfnUseItem
		add esp, 0x0C
	}

	put_CurrentSelection( lCurrentSelected );

	return S_OK;
}

STDMETHODIMP cACHooks::get_CombatState(long *pVal)
{
	if( !m_bAllYourBase )
		return S_FALSE;

	if( !m_bCombatStateOffset )
		return S_FALSE;

	*pVal = *(reinterpret_cast< long * >( (*reinterpret_cast< char ** >( m_lAllYourBase )) + m_lCombatStateOffset ));

	return S_OK;
}

STDMETHODIMP cACHooks::SetCombatState(long pVal)
{
	if( !m_bSetCombatState )
		return S_FALSE;

	if( (pVal < 1) || (pVal > 4) )
		return E_INVALIDARG;

	typedef void(__fastcall *SetCombatStatePtr)( long, long, long, long );
	SetCombatStatePtr pSetCState = reinterpret_cast< SetCombatStatePtr >( m_lSetCombatState );
	pSetCState( *( reinterpret_cast< long * >( m_lAllYourBase ) ), 0, pVal, 1 );

	return S_OK;
}

STDMETHODIMP cACHooks::get_ChatState(VARIANT_BOOL *pVal)
{
	if( !m_bChatState )
		return S_FALSE;

	long *pChatState = reinterpret_cast< long * >( m_lChatState );
	if( m_bDecalRef )
	{
		CComPtr< IPSite > pSite;
		if( m_pDecal->get_Object( _bstr_t( "services\\DecalPlugins.InjectService\\site" ), __uuidof( IPSite ), reinterpret_cast< void ** >( &pSite ) ) == S_OK ) 
		{
			if( pSite->get_Focus( pVal ) != S_OK )
				*pVal = *pChatState ? VARIANT_TRUE : VARIANT_FALSE;
			else
				*pVal = (*pChatState || (*pVal == VARIANT_TRUE) ) ? VARIANT_TRUE : VARIANT_FALSE;

			return S_OK;
		}

		else
			*pVal = *pChatState ? VARIANT_TRUE : VARIANT_FALSE;

		return S_OK;
	}

	*pVal = *pChatState ? VARIANT_TRUE : VARIANT_FALSE;

	return S_OK;
}

STDMETHODIMP cACHooks::UseItemEx(long UseThis, long OnThis)
{
	if( !m_bUseItem )
		return S_FALSE;

	long lCurrentSelected;
	get_CurrentSelection( &lCurrentSelected );
	put_CurrentSelection( OnThis );

	UseItem( UseThis, 1 );

	put_CurrentSelection( lCurrentSelected );

	return S_OK;
}

STDMETHODIMP cACHooks::GetFellowStats(long lCharID)
{
	return IDQueueAdd( lCharID );
}

STDMETHODIMP cACHooks::get_SelectedStackCount(long lStackCount, long *pVal)
{
	if( !m_bStackCount )
		return S_FALSE;

	*pVal = *( reinterpret_cast< long * >( m_lStackCount ) );

	return S_OK;
}

STDMETHODIMP cACHooks::put_SelectedStackCount(long lStackCount, long newVal)
{
	if( !m_bStackCount )
		return S_FALSE;

	*( reinterpret_cast< long * >( m_lStackCount ) ) = newVal;

	return S_OK;
}

STDMETHODIMP cACHooks::get_VendorID(long *pVal)
{
	if( !m_bVendorID )
		return S_FALSE;

	*pVal = *( reinterpret_cast< long * >( m_lVendorID ) );

	return S_OK;
}

STDMETHODIMP cACHooks::get_BusyState(long *pVal)
{
	if( !m_bBusyState )
		return S_FALSE;

	*pVal = *( reinterpret_cast< long * >( m_lBusyState ) );

	return S_OK;
}

STDMETHODIMP cACHooks::get_BusyStateID(long *pVal)
{
	if( !m_bBusyStateID )
		return S_FALSE;

	*pVal = *( reinterpret_cast< long * >( m_lBusyStateID ) );

	return S_OK;
}

STDMETHODIMP cACHooks::get_PointerState(long *pVal)
{
	if( !m_bPointerState )
		return S_FALSE;

	*pVal = *( reinterpret_cast< long * >( m_lPointerState ) );

	return S_OK;
}

STDMETHODIMP cACHooks::MoveItemEx(long lObjectID, long lDestinationID)
{
	if( !m_bMoveItemEx )
		return S_FALSE;

	__asm
	{
		push 0x0
		push lDestinationID
		push lObjectID
		call pfnMoveItemEx
		add esp, 0x0C
	}

	return S_OK;
}

long* cACHooks::GetCs(long offs)
{
	if (!m_bAllYourBase) {
		return NULL ;
	}
	// follow multiple indirection from m_lCombatState to the 
	long *rv = reinterpret_cast< long * >( m_lAllYourBase );
	if (*rv)	rv = reinterpret_cast< long * >( *rv + 0xE4 );
	if (*rv)	rv = reinterpret_cast< long * >( *rv + 0x160 );
	if (*rv)	rv = reinterpret_cast< long * >( *rv + offs);
	return rv ;
}

STDMETHODIMP cACHooks::get_Heading(double *pVal)
{
	HRESULT rv = S_OK ;
	if (!m_bAllYourBase) {
		*pVal = 0.0 ;
		return S_FALSE ;
	}
	long* pX = GetCs(0x6C) ;
	long* pY = GetCs(0x70) ;
	float fHeadingX(0) ;
	float fHeadingY(0) ;
	if (pX) {
		fHeadingX = *(reinterpret_cast< float * >(pX)) ;
	} else {
		rv = S_FALSE ;
	}
	if (pY) {
		fHeadingY = *(reinterpret_cast< float * >(pY)) ;
	} else {
		rv = S_FALSE ;
	}
	
	long lCardinal = 0;

	if( (fHeadingX > 0) && (fHeadingY > 0) )
		lCardinal = 90;
	if( (fHeadingX < 0) && (fHeadingY < 0) )
		lCardinal = 270;
	if( (fHeadingX < 0) && (fHeadingY > 0) )
		lCardinal = 270;
	if( (fHeadingX > 0) && (fHeadingY < 0) )
		lCardinal = 450;

	*pVal = fabs( atan( fHeadingY / fHeadingX ) * 180 / 3.14159265358979323846 - 450 + lCardinal ); 

	return rv;
}

STDMETHODIMP cACHooks::get_Landblock(long *pVal)
{
	HRESULT rv = S_OK ;
	*pVal = 0 ;
	if (!m_bAllYourBase) {
		return S_FALSE ;
	}
	long *cs = GetCs(0x4C) ;

	if (cs) {
		*pVal = *cs ;
	} else {
		*pVal = rv ;
	}

	return rv;
}

STDMETHODIMP cACHooks::get_LocationX(double *pVal)
{
	HRESULT rv = S_OK ;
	if (!m_bAllYourBase) {
		*pVal = 0 ;
		return S_FALSE ;
	}
	long* cs = GetCs(0x50) ;
	float val(0) ;

	if (cs) {
		val = *(reinterpret_cast< float * >(cs));
	} else {
		rv = S_FALSE ;
	}
	*pVal = double(val) ;

	return rv;
}

STDMETHODIMP cACHooks::get_LocationY(double *pVal)
{
	HRESULT rv = S_OK ;
	if (!m_bAllYourBase) {
		*pVal = 0 ;
		return S_FALSE ;
	}
	long* cs = GetCs(0x54) ;
	float val(0) ;

	if (cs) {
		val = *(reinterpret_cast< float * >(cs));
	} else {
		rv = S_FALSE ;
	}
	*pVal = double(val) ;

	return rv;
}

STDMETHODIMP cACHooks::get_LocationZ(double *pVal)
{
	HRESULT rv = S_OK ;
	if (!m_bAllYourBase) {
		*pVal = 0 ;
		return S_FALSE ;
	}
	long* cs = GetCs(0x58) ;
	float val(0) ;

	if (cs) {
		val = *(reinterpret_cast< float * >(cs));
	} else {
		rv = S_FALSE ;
	}
	*pVal = double(val) ;

	return rv ;
}

STDMETHODIMP cACHooks::get_HooksAvail(long *pVal) {
	*pVal = m_Hooks ;
	return S_OK ;
}

STDMETHODIMP cACHooks::DropItem(long lObjectID)
{
	if( !m_bMoveItemEx )
		return S_FALSE;

	__asm
	{
		push 0x1
		push 0x0
		push lObjectID
		call pfnMoveItemEx
		add esp, 0x0C
	}

	return S_OK;
}

// Turn to a particular heading, discovered by Madar on 8/25/2002
STDMETHODIMP cACHooks::FaceHeading (float fHeading, VARIANT_BOOL bUnknown, VARIANT_BOOL *pRetval)
{
	if (!m_bFaceHeading)
	{
		*pRetval = VARIANT_FALSE;
		return S_FALSE;
	}

	DWORD dwTemp = m_lFaceHeading;

	DWORD ** pTemp = (DWORD **) m_lMovementThingyParent;

	DWORD dwMovementThingy = pTemp[0][m_lMovementThingyOffset];
	long lResult = 0;

	DWORD dwSecondArg = (bUnknown == VARIANT_TRUE) ? 1 : 0;

	__asm
	{
		mov ecx,dwMovementThingy
		push dwSecondArg
		push fHeading
		call dwTemp
		mov lResult, eax
	}

	*pRetval = lResult ? VARIANT_TRUE : VARIANT_FALSE;

	return S_OK;
}

// Set auto-run, discovered by Madar on 9/16/2002
STDMETHODIMP cACHooks::SetAutorun (VARIANT_BOOL bOnOff)
{
	if (!m_bSetAutorun)
	{
		return S_FALSE;
	}

	DWORD dwTemp = m_lSetAutorun;

	DWORD ** pTemp = (DWORD **) m_lMovementThingyParent;

	DWORD dwMovementThingy = pTemp[0][m_lMovementThingyOffset];

	long lFirstArg = (bOnOff == VARIANT_TRUE) ? 1 : 0;
	long lUnknown = 1;

	__asm
	{
		mov ecx,dwMovementThingy
		push lUnknown
		push lFirstArg
		call dwTemp
	}

	return S_OK;
}

STDMETHODIMP cACHooks::ItemIsKnown(long lGUID, VARIANT_BOOL* pRetval)
{
	if (!m_bObjectFromGuid)
	{
		*pRetval = VARIANT_FALSE;
		return S_FALSE;
	}

	DWORD	dwTemp = m_lObjectFromGuid ;
	DWORD	dwClass = *((DWORD*) m_lObjectFromGuidClass);

	long lResult = 0;

	__asm
	{
		mov ecx,dwClass
		push lGUID
		call dwTemp
		mov lResult, eax
	}

	*pRetval = lResult ? VARIANT_TRUE : VARIANT_FALSE;
	return S_OK;
}



STDMETHODIMP cACHooks::get_Area3DWidth(long *pVal)
{
	if( !m_bArea3DWidth )
		return S_FALSE;

	*pVal = *m_lpArea3DWidth;

	return S_OK;
}

STDMETHODIMP cACHooks::get_Area3DHeight(long *pVal)
{
	if( !m_bArea3DHeight )
		return S_FALSE;

	*pVal = *m_lpArea3DHeight;

	return S_OK;
}

STDMETHODIMP cACHooks::SendTell( long lPlayerID, BSTR Message )
{
	if( !(m_bInternalStringConstructor && m_bInternalStringDestructor && m_bSendMessageToID) )
		return S_FALSE;

	USES_CONVERSION;
	char *szText = OLE2A( Message );

	long retval;
	qString Msg;
	pfnInternalStringConstructor( &Msg, 0, szText );
	retval = pfnSendMessageToID( &Msg, lPlayerID );
	pfnInternalStringDestructor( &Msg );

	return S_OK;
}

STDMETHODIMP cACHooks::SendTellEx( BSTR Name, BSTR Message )
{
	if( !(m_bInternalStringConstructor && m_bInternalStringDestructor && m_bSendMessageToName) )
		return S_FALSE;

	USES_CONVERSION;
	char *szName = OLE2A( Name );
	char *szText = OLE2A( Message );

	long retval;
	qString qSName;
	qString qSMsg;
	pfnInternalStringConstructor( &qSMsg, 0, szText );
	pfnInternalStringConstructor( &qSName, 0, szName );
	retval = pfnSendMessageToName( &qSMsg, &qSName );
	pfnInternalStringDestructor( &qSMsg );
	pfnInternalStringDestructor( &qSName );

	return S_OK;
}

STDMETHODIMP cACHooks::LocalChatText(BSTR Text)
{
	if( !(m_bInternalStringConstructor && m_bInternalStringDestructor && m_bLocalChatText) )
		return S_FALSE;

	USES_CONVERSION;
	char *szText = OLE2A( Text );

	qString qSText;

	pfnInternalStringConstructor( &qSText, 0, szText );
	pfnLocalChatText( &qSText );
	pfnInternalStringDestructor( &qSText );

	return S_OK;
}

STDMETHODIMP cACHooks::LocalChatEmote(BSTR EmoteText)
{
	if( !(m_bInternalStringConstructor && m_bInternalStringDestructor && m_bLocalChatEmote) )
		return S_FALSE;

	USES_CONVERSION;
	char *szText = OLE2A( EmoteText );

	qString qSText;

	pfnInternalStringConstructor( &qSText, 0, szText );
	pfnLocalChatEmote( &qSText );
	pfnInternalStringDestructor( &qSText );

	return S_OK;
}

STDMETHODIMP cACHooks::get_Vital(long Vital, long* pVal)
{
	if( !(m_bGetVital && m_lVitalBase) )
		return S_FALSE;

	if( (Vital < 1) || (Vital > 9) )
		return E_INVALIDARG;

	long lBaseOrBuffed = (Vital > 6) ? 1 : 0;
	if( lBaseOrBuffed )
	{
		if( Vital == 7 ) // health
			Vital = 2;
		else if( Vital == 8 ) // stamina
			Vital = 4;
		else if( Vital == 9 ) // mana
			Vital = 6;
	}

	long ( __fastcall *Internal_GetStat )( qPointerList *, long, long, long *, long ) = reinterpret_cast< long ( __fastcall * )( qPointerList *, long, long, long *, long ) >( m_lGetVital );

	qPointerList *p = reinterpret_cast< qPointerList * >( m_lVitalBase );
	p = p->dd[0];

	Internal_GetStat( p, 0, Vital, pVal, lBaseOrBuffed );

	return S_OK;
}

STDMETHODIMP cACHooks::get_Attribute(long Attribute, long* pVal)
{
	if( !(m_bGetAttribute && m_lVitalBase) )
		return S_FALSE;

	if( (Attribute < 1) || (Attribute > 12) )
		return E_INVALIDARG;

	long lBaseOrBuffed = (Attribute > 6) ? 1 : 0;
	if( lBaseOrBuffed )
		Attribute -= 6;

	qPointerList *p = reinterpret_cast< qPointerList * >( m_lVitalBase );
	p = p->dd[0];

	long ( __fastcall *Internal_GetAttribute )( qPointerList *, long, long, long *, long ) = reinterpret_cast< long ( __fastcall * )( qPointerList *, long, long, long *, long ) >( m_lGetAttribute );

	Internal_GetAttribute( p, 0, Attribute, pVal, lBaseOrBuffed );

	return S_OK;
}

STDMETHODIMP cACHooks::get_Skill(long Skill, long* pVal)
{
	if( !(m_bGetSkill && m_lVitalBase) )
		return S_FALSE;

	// error on out of range, the unused range for added skills, and other skill ids that Turbine skips
	if((Skill < 1) || (Skill > 89) || ((Skill > 39) && (Skill < 50)) || (Skill == 8 ) || (Skill == 58) || (Skill == 17) || (Skill == 67) || (Skill == 25) || (Skill == 75) || (Skill == 26) || (Skill == 76))
		return E_INVALIDARG;

	long lBaseOrBuffed = (Skill > 50) ? 1 : 0;
	if( lBaseOrBuffed )
		Skill -= 50;

	qPointerList *p = reinterpret_cast< qPointerList * >( m_lVitalBase );
	p = p->dd[0];

	long ( __fastcall *Internal_GetSkill )( qPointerList *, long, long, long *, long ) = reinterpret_cast< long ( __fastcall * )( qPointerList *, long, long, long *, long ) >( m_lGetSkill );

	Internal_GetSkill( p, 0, Skill, pVal, lBaseOrBuffed );

	return S_OK;
}

STDMETHODIMP cACHooks::get_HooksAvailEx(eAvailableHooksEx HookID, VARIANT_BOOL *pVal)
{
	unsigned int ID;
	unsigned int Index, Offset;

	// Default to not being valid
	*pVal = VARIANT_FALSE;

	// Convert the enum to an integer so we can do maths on it
	ID = static_cast< unsigned int >( HookID );

	// Ensure the value is within range
	if( ID > m_HookCount )
	{
		return S_OK;
	}

	// Extract the index and bit offset
	Index = ID >> 5; // 32 -> 5 bits
	Offset = ID & 0x1F; // 1F -> 5 bits worth of 1s

	if( m_HooksEx[Index] & (1 << Offset) )
	{
		*pVal = VARIANT_TRUE;
	}

	return S_OK;
}

STDMETHODIMP cACHooks::Logout()
{
	if( m_bLogout )
	{
		long ( __cdecl *Internal_Logout )( long );
		Internal_Logout = reinterpret_cast< long ( __cdecl * )( long ) >( m_lLogout );
		Internal_Logout(0);
		return S_OK;
	}

	return S_FALSE;
}

STDMETHODIMP cACHooks::ToolText(BSTR Text, VARIANT_BOOL bError)
{
  /*
  Moputu - 09012003: Added to allow plugins to display text 
  in the upper left hand corner as either white text w/o sound or
  as yellow text with the busy/error sound.

  Either CombatState or ObjectBase can be used as the starting 
  memloc as both always seems to have the same value and the 
  function is called using one or the other in the client.exe.
  You can only find the function address after the player is logged in.
  */

	if( !m_bAllYourBase ) //the combat state memloc is necessary
		return S_FALSE;

	USES_CONVERSION;
	char *szText = OLE2A( Text ); //pointer to the text being passed
	DWORD cstate = *((long *)m_lAllYourBase);

	long lErr;
	if (bError)
		lErr = 1; // display text as yellow w/sound
	else
		lErr = 0; // display text as white w/o sound

	__asm
	{
		mov		ecx, cstate

		push	lErr
		push	szText

		mov		edx, dword ptr [ecx]
		call	dword ptr [edx+114h]
		add		esp, 8
	}

	return S_OK;
}

STDMETHODIMP cACHooks::ToolTextAppend(BSTR Text, VARIANT_BOOL bError)
{
	if( !m_bAllYourBase ) //the combat state memloc is necessary
		return S_FALSE;

	USES_CONVERSION;
	char *szText = OLE2A( Text ); //pointer to the text being passed
	DWORD cstate = *((long *)m_lAllYourBase);

	long lErr;
	if (bError)
		lErr = 1; // display text as yellow w/sound
	else
		lErr = 0; // display text as white w/o sound

	__asm
	{
		mov		ecx, cstate

		push	lErr
		push	szText

		mov		edx, dword ptr [ecx]
		call	dword ptr [edx+118h]
	}

	return S_OK;
}

STDMETHODIMP cACHooks::SecureTrade_Add(long ItemID, VARIANT_BOOL *pVal)   
{   
	void ( __fastcall *Internal_SecureTrade_Add )( struct qPointerList *, int, long );   
	struct qPointerList *p;
	VARIANT_BOOL ItemExists;   

	if (pVal) /* Should probably return an error code.  Tough. */   
	{   
		*pVal = VARIANT_FALSE;
	}

	else
		return E_FAIL;

	if ( m_bSecureTrade_Add && m_bSecureTrade_Add_Off1 && m_bSecureTrade_Add_Off2 && m_bAllYourBase )   
	{
		if( ItemIsKnown(ItemID, &ItemExists) != S_OK )
			return E_FAIL;

		if (ItemExists == VARIANT_FALSE)   
		{   
			return S_FALSE;   
		}   

		p = (qPointerList *)m_lAllYourBase;   
		if (!p)   
		{   
			return S_FALSE;   
		}   

		p = p->dd[0];   
		if (!p)   
		{   
			return S_FALSE;   
		}   

		p = p->dd[m_lSecureTrade_Add_Off1 >> 2];   
		if (!p)   
		{   
			return S_FALSE;   
		}   

		p = p->dd[m_lSecureTrade_Add_Off2 >> 2];   
		if (!p)   
		{   
			return S_FALSE;   
		}   

		Internal_SecureTrade_Add = reinterpret_cast< void ( __fastcall *)( struct qPointerList *, int, long ) >( m_lSecureTrade_Add );   
		Internal_SecureTrade_Add(p, 0, ItemID);   
		if (pVal)   
		{   
			*pVal = VARIANT_TRUE;   
		}   
		return S_OK;   
	}   
	return S_FALSE;   
}

HRESULT cACHooks::GetSkillInfo(eSkill SkillID, struct qSkill *Skill) {   
	int ( __fastcall *Internal_GetSkillInfo )( void *, int, int, struct qSkill * );   

	if (!m_bGetSkillInfo) {   
		return E_FAIL;   
	}   

	if (SkillID > 50) {   
		SkillID = (eSkill)(SkillID - 50);   
	}   
	if ((SkillID < 1) || (SkillID > 50)) {   
		return E_INVALIDARG;   
	}   


	Skill->vTable = m_lGetSkillInfo_vT;   

	Internal_GetSkillInfo = reinterpret_cast< int (__fastcall*)(void *, int, int, struct qSkill *) >(m_lGetSkillInfo);   
	if (Internal_GetSkillInfo(*(void **)m_lVitalBase, 0, SkillID, Skill)) {   
		return S_OK;   
	}   
	return E_FAIL;   
}   

HRESULT cACHooks::GetAttributeInfo(eAttribute AttributeID, struct qAttribute *Attribute) {   
	int (__fastcall *Internal_GetAttributeInfo)(void *, int, int, struct qAttribute *);   

	if (!m_bGetAttributeInfo) {   
		return E_FAIL;   
	}   

	if (AttributeID > 6) {   
		AttributeID = (eAttribute)(AttributeID - 6);   
	}   
	if ((AttributeID < 1) || (AttributeID > 6)) {   
		return E_INVALIDARG;   
	}   

	Attribute->vTable = m_lGetAttributeInfo_vT;   

	Internal_GetAttributeInfo = reinterpret_cast< int (__fastcall*)(void *, int, int, struct qAttribute *) >(m_lGetAttributeInfo);   
	if (Internal_GetAttributeInfo(*(void **)m_lVitalBase, 0, AttributeID, Attribute)) {   
		return S_OK;   
	}   
	return E_FAIL;   
}   

HRESULT cACHooks::GetVitalInfo(eVital VitalID, struct qVital *Vital) {   
	int (__fastcall *Internal_GetVitalInfo)(void *, int, int, struct qVital *);   

	if (!m_bGetVitalInfo) {   
		return E_FAIL;   
	}   

	switch (VitalID) {   
	 case eCurrentHealth:   
	 case eMaximumHealth:   
	 case eBaseHealth:   
		 VitalID = eCurrentHealth;   
		 break;   
	 case eCurrentStamina:   
	 case eMaximumStamina:   
	 case eBaseStamina:   
		 VitalID = eCurrentStamina;   
		 break;   
	 case eCurrentMana:   
	 case eMaximumMana:   
	 case eBaseMana:   
		 VitalID = eCurrentMana;   
		 break;   
	 default:   
		 return E_INVALIDARG;   
	}   


	Vital->vTable = m_lGetVitalInfo_vT;   

	Internal_GetVitalInfo = reinterpret_cast< int (__fastcall*)(void *, int, int, struct qVital *) >(m_lGetVitalInfo);   
	if (Internal_GetVitalInfo(*(void **)m_lVitalBase, 0, VitalID, Vital)) {   
		return S_OK;   
	}   
	return E_FAIL;   
}

STDMETHODIMP cACHooks::get_SkillTrainLevel( eSkill SkillID, eTrainLevel *pVal )   
{   
	struct qSkill Skill;   
	HRESULT hr;   
	hr = GetSkillInfo(SkillID, &Skill);   
	if (FAILED(hr)) {   
		return hr;   
	}   

	*pVal = Skill.Trained;   

	return S_OK;   
}   

STDMETHODIMP cACHooks::get_SkillTotalXP( eSkill SkillID, int *pVal )   
{   
	struct qSkill Skill;   
	HRESULT hr;   
	hr = GetSkillInfo(SkillID, &Skill);   
	if (FAILED(hr)) {   
		return hr;   
	}   

	*pVal = Skill.TotalXP;   

	return S_OK;   
}   

STDMETHODIMP cACHooks::get_SkillFreePoints( eSkill SkillID, int *pVal )   
{   
	struct qSkill Skill;   
	HRESULT hr;   
	hr = GetSkillInfo(SkillID, &Skill);   
	if (FAILED(hr)) {   
		return hr;   
	}   

	*pVal = Skill.FreePoints;   

	return S_OK;   
}   


STDMETHODIMP cACHooks::get_SkillClicks( eSkill SkillID, int *pVal )   
{   
	struct qSkill Skill;   
	HRESULT hr;   
	hr = GetSkillInfo(SkillID, &Skill);   
	if (FAILED(hr)) {   
		return hr;   
	}   

	*pVal = Skill.Clicks;   

	return S_OK;   
}   

STDMETHODIMP cACHooks::get_AttributeClicks( eAttribute AttributeID, int *pVal )   
{   
	struct qAttribute Attribute;   
	HRESULT hr;   
	hr = GetAttributeInfo(AttributeID, &Attribute);   
	if (FAILED(hr)) {   
		return hr;   
	}   

	*pVal = Attribute.Clicks;   

	return S_OK;   
}   

STDMETHODIMP cACHooks::get_AttributeTotalXP( eAttribute AttributeID, int *pVal )   
{   
	struct qAttribute Attribute;   
	HRESULT hr;   
	hr = GetAttributeInfo(AttributeID, &Attribute);   
	if (FAILED(hr)) {   
		return hr;   
	}   

	*pVal = Attribute.TotalXP;   

	return S_OK;   
}   


STDMETHODIMP cACHooks::get_AttributeStart( eAttribute AttributeID, int *pVal )   
{   
	struct qAttribute Attribute;   
	HRESULT hr;   
	hr = GetAttributeInfo(AttributeID, &Attribute);   
	if (FAILED(hr)) {   
		return hr;   
	}   

	*pVal = Attribute.Start;   

	return S_OK;   
}   

STDMETHODIMP cACHooks::get_VitalClicks( eVital VitalID, int *pVal )   
{   
	struct qVital Vital;   
	HRESULT hr;   
	hr = GetVitalInfo(VitalID, &Vital);   
	if (FAILED(hr)) {   
		return hr;   
	}   

	*pVal = Vital.Clicks;   

	return S_OK;   
}   

STDMETHODIMP cACHooks::get_VitalTotalXP( eVital VitalID, int *pVal )   
{   
	struct qVital Vital;   
	HRESULT hr;   
	hr = GetVitalInfo(VitalID, &Vital);   
	if (FAILED(hr)) {   
		return hr;   
	}   

	*pVal = Vital.TotalXP;   

	return S_OK;   
}   

void cACHooks::SetHookEx( eAvailableHooksEx HookID )
{
	unsigned int ID;
	unsigned int Index, Offset;

	ID = static_cast< unsigned int >( HookID );

	Index = ID >> 5;
	Offset = ID & 0x1F;

	_ASSERTE( Index < (sizeof(m_HooksEx) / sizeof(m_HooksEx[0])) );

	m_HooksEx[Index] |= (1 << Offset);

	if( ID > m_HookCount )
		m_HookCount = ID;

	return;
}

STDMETHODIMP cACHooks::SetIdleTime( double dIdleTimeout )
{
	if( !m_bIdleLoc )
		return S_FALSE;

	DWORD dwOldProtect = 0;
	DWORD dwNewProtect = PAGE_READWRITE;
	VirtualProtect( reinterpret_cast< void * >( m_lIdleLoc ), 8 /* sizeof double */, dwNewProtect, &dwOldProtect );
	*( reinterpret_cast< double * >( m_lIdleLoc ) ) = dIdleTimeout;
	VirtualProtect( reinterpret_cast< void * >( m_lIdleLoc ), 8 /* sizeof double */, dwOldProtect, &dwNewProtect );

	return S_OK;
}

void cACHooks::InternalObjectDestroyed (DWORD dwGuid)
{
	Fire_ObjectDestroyed( dwGuid );
}

void cACHooks::InternalSelectItem( DWORD dwID )
{
	Fire_OnSelectItem( dwID );
}

void cACHooks::InternalShortcircuit( DWORD dwID )
{
	if( m_bIdQueueRef )
		m_pIdQueue->ShortcircuitID( dwID );
}

void OnObjectDestroyed (DWORD dwGuid)
{
	if( cACHooks::s_pACHooks )
		cACHooks::s_pACHooks->InternalObjectDestroyed( dwGuid );
}

void OnSelectItem( DWORD dwID )
{
	if( cACHooks::s_pACHooks )
		cACHooks::s_pACHooks->InternalSelectItem( dwID );
}

void Shortcircuit( DWORD dwID )
{
	if( cACHooks::s_pACHooks )
		cACHooks::s_pACHooks->InternalShortcircuit( dwID );
}

void __declspec(naked) SelectItemHook()
{
	_asm
	{
		push eax
		call OnSelectItem
		add esp,4

		jmp g_lSelectItemHijackProc
	}
}

void __declspec(naked) ObjectDestroyedHook()
{
	_asm
	{
		mov eax,[esp+8]
		push eax
		push ecx

		push eax
		call OnObjectDestroyed

		add esp,4
		pop ecx
		pop eax
		mov [esp+8],eax

		jmp g_lObjectDestroyedProc
	}
}

void __declspec(naked) IdentifyShortcircuit()
{
	__asm
	{
		mov eax,[esp+4]
		push eax
		push ecx

		push eax
		call Shortcircuit
		add esp, 4

		pop ecx
		pop eax
		mov [esp+4], eax

		jmp g_lIdentifyHijackProc
	}
}

// Returns the function which previously was being called, after replacing it with the new call.
DWORD HookCall (DWORD dwCallAddress, DWORD dwReplacement)
{
	DWORD* pTemp = (DWORD *) (dwCallAddress + 1);

	HANDLE hProcess = OpenProcess
		(
		PROCESS_VM_WRITE | PROCESS_VM_OPERATION,
		FALSE,
		GetCurrentProcessId ()
		);

	DWORD dwOriginal = 0;

	if (hProcess)
	{
		dwOriginal = (*pTemp) + dwCallAddress + 5;

		DWORD dwTemp = dwReplacement - (dwCallAddress + 5);
		if (WriteProcessMemory
			(
			hProcess,
			pTemp,
			&dwTemp,
			sizeof (DWORD),
			NULL
			))
		{
		}
		else
		{
			dwOriginal = 0;
		}

		CloseHandle (hProcess);
	}

	return dwOriginal;
}

bool cACHooks::InternalChatMessage( char *szText, long lColor )
{
	return Fire_OnChatBoxMessage( _bstr_t( szText ), lColor ) == VARIANT_TRUE ?  true : false;
}

bool cACHooks::InternalChatText( char *szText )
{
	return Fire_OnCommandLineText( _bstr_t( szText ) ) == VARIANT_TRUE ? true : false;
}

bool DispatchChatMessage( char *pText, long pdwColor )
{
	if( cACHooks::s_pACHooks )
		return cACHooks::s_pACHooks->InternalChatMessage( pText, pdwColor );

	return false;
}

bool DispatchChatText( char *pText )
{
	if( cACHooks::s_pACHooks )
		return cACHooks::s_pACHooks->InternalChatText( pText );
	
	return false;
}

/* Heyus - 23 Mar 2003 - Fixed to point to the new universal function */
void __fastcall OnChatMessage( int _ecx, int _edx, char* pText, long lColor )
{
	if( !DispatchChatMessage( pText, lColor ) )
		InternalRawWriteToChat( pText, lColor, _ecx, _edx );

	return;
}

void __declspec(naked) OnChatText()
{
	__asm
	{
		push	ebp
		mov		ebp, esp
		sub		esp, 0xC

		call	pfnOldChatText

		mov		[ebp-4], ecx
		mov		[ebp-8], eax

		push	eax

		call	DispatchChatText

		mov		edx, eax
		mov		ecx, [ebp-4]
		mov		eax, [ebp-8]

		test	dl, dl
		jz		dont_eat_chat_text

		mov		byte ptr [eax], 0

dont_eat_chat_text:
		mov		esp, ebp
		pop		ebp
		ret
	}
}

void cACHooks::InternalToolText( char *szText, VARIANT_BOOL bError )
{
	USES_CONVERSION;
	Fire_OnToolText( T2BSTR( szText ), bError );
}

void OnToolText( char *szText, long lError )
{
	VARIANT_BOOL bError = lError;

	if( cACHooks::s_pACHooks )
		cACHooks::s_pACHooks->InternalToolText( szText, bError );
}

void __declspec(naked) CatchToolText()
{
	__asm
	{
		push  ecx
		push  edx
		push  edi
		push  eax
		call  OnToolText
		pop   eax
		pop   edi
		pop   edx
		pop   ecx
		jmp   g_lToolTextProc
	}
}

void cACHooks::InternalToolTextAppend( char *szText, VARIANT_BOOL bError )
{
	USES_CONVERSION;
	Fire_OnToolTextAppend( T2BSTR( szText ), bError );
}

void OnToolTextAppend( char *szText, long lError )
{
	VARIANT_BOOL bError = lError;

	if( cACHooks::s_pACHooks )
		cACHooks::s_pACHooks->InternalToolTextAppend( szText, bError );
}

void __declspec(naked) CatchToolTextAppend()
{
	__asm
	{
		push  ecx
		push  edx
		push  edi
		push  eax
		call  OnToolTextAppend
		pop   eax
		pop   edi
		pop   edx
		pop   ecx
		jmp   g_lToolText2Proc
	}
}

STDMETHODIMP cACHooks::RequestID(long lObjectID)
{
	if( !m_bRequestID )
		return S_FALSE;

	if( m_bIdQueueRef )
		return m_pIdQueue->AddToQueue( lObjectID );

	typedef void(*RequestPtr)( long );
	RequestPtr pRequest = reinterpret_cast< RequestPtr >( m_lRequestID );
	pRequest( lObjectID );

	return S_OK;
}

STDMETHODIMP cACHooks::IDQueueAdd(long lObjectID)
{
	if( !m_bIdQueueRef )
		return RequestID( lObjectID );

	return m_pIdQueue->AddToQueue( lObjectID );
}

STDMETHODIMP cACHooks::SetIDFilter(IKitchenSink* pIDFilter)
{
	if( !m_bIdQueueRef )
	{
		m_bIdQueueRef = true;
		m_pIdQueue = pIDFilter;
		SetHookEx( eIDQueueAdd );
	}

	return S_OK;
}

STDMETHODIMP cACHooks::UstAddItem(long lObjectID)
{   
	VARIANT_BOOL ItemExists;

	if ( !m_bUstAddItem_Useable )
		return S_FALSE;

	if( ItemIsKnown( lObjectID, &ItemExists ) != S_OK )
		return E_FAIL;

	if( ItemExists == VARIANT_FALSE )
		return S_FALSE;   

	//Calculate the THIS pointer  
	//DWORD thisPtr = *((long *)(*((long *)(*((long *)0x77A618) + 0x324)) + 0xE0));   
	//DWORD tmp;
	//DWORD func = 0x005556C0; 

	DWORD thisPtr = *((long *)(*((long *)(*((long *)m_lChatMessageAddy) + m_lUstAddItem_Off1)) + m_lUstAddItem_Off2));
	DWORD tmp;     
	DWORD func = (DWORD)m_lUstAddItem;

	__asm
	{
		mov tmp, ecx
		mov ecx, thisPtr
		push lObjectID

		call func

		mov ecx, tmp
	}

	return S_OK;
}

STDMETHODIMP cACHooks::SendMessageByMask( long lMask, BSTR szMessage )
{
	if( !(m_bInternalStringConstructor && m_bInternalStringDestructor && m_bSendMessageToMask) )
		return S_FALSE;

	USES_CONVERSION;
	char *szText = OLE2A( szMessage );

	long retval;
	qString Msg;
	pfnInternalStringConstructor( &Msg, 0, szText );
	retval = pfnSendMessageByMask( lMask, &Msg );
	pfnInternalStringDestructor( &Msg );

	return S_OK;
}

STDMETHODIMP cACHooks::SetDay( VARIANT_BOOL bDay )
{
	if( !m_bSlashDay )
		return S_FALSE;

	*reinterpret_cast< long * >( m_lSlashDay ) = bDay == VARIANT_TRUE ? 1 : 0;

	return S_OK;
}
