// Inject.cpp : Implementation of DLL Exports.


// Note: Proxy/Stub Information
//      To build a separate proxy/stub DLL, 
//      run nmake -f Injectps.mk in the project directory.

#include "stdafx.h"
#include "resource.h"
#include <initguid.h>
#include "Inject.h"
#include "InjectApi.h"

#include "Inject_i.c"
#include "DirectDrawHook.h"
#include "Manager.h"

#include "Button.h"
#include "Pager.h"
#include "InjectService.h"
#include "PluginAdapterV1.h"
#include "RootLayer.h"

// one instance for all processes
#pragma data_seg( ".InjectDll" )
HHOOK g_hHook = NULL;
#pragma data_seg()

CComModule _Module;

LPFNDLLFUNCALPHABLEND AlphaBlendF;

BEGIN_OBJECT_MAP(ObjectMap)
   OBJECT_ENTRY(CLSID_Button, cButton)
   OBJECT_ENTRY(CLSID_Pager, cPager)
   OBJECT_ENTRY(CLSID_InjectService, cInjectService)
   OBJECT_ENTRY(CLSID_PluginAdapterV1, cPluginAdapterV1)
END_OBJECT_MAP()

// These functions and structures are used for hooking functions
struct cHookDescriptor;
bool hookFunctions( cHookDescriptor *pHook, DWORD nCount );

// Functions for hooking from ddraw.dll
HRESULT WINAPI DirectDrawCreateF( GUID *lpGuid, LPDIRECTDRAW *lplpDD, IUnknown *pUnkOut );

// Functions for hooking from kernel32.dll
HANDLE WINAPI CreateFileF( LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile );

// Function for dual logging
HANDLE WINAPI Replacement_CreateSemaphoreA( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount, LPCTSTR lpName );

// Function for no movies
HFILE WINAPI Replacement_OpenFile( LPCSTR lpFileName, LPOFSTRUCT lpReOpenBuff, UINT uStyle );

enum eAddressing
{
   eByName,
   eByOrdinal
};

struct cHookDescriptor
{
   eAddressing m_addr;
   LPCTSTR m_szModule,
      m_szFunction;
   DWORD m_dwOrdinal,
      m_pNewFunction,
      m_pOldFunction;
};

static bool bRegisteredInAC = false;
HANDLE _hUpdateEnabled = NULL;

static cHookDescriptor _hooks[] = {
	{ eByName, _T( "ddraw.dll" ), _T( "DirectDrawCreate" ), 0, reinterpret_cast< DWORD >( DirectDrawCreateF ), 0 },
	{ eByName, _T( "kernel32.dll" ), _T( "CreateFileA" ), 0, reinterpret_cast< DWORD >( CreateFileF ), 0 },
};

static bool bDualLog = false;
static bool bWindowed = false;
static bool bNoSplash = false;
static bool bNoMovies = false;

static cHookDescriptor _hooksDualLog[] = {
	{ eByName, _T( "kernel32.dll" ), _T( "CreateSemaphoreA" ), 102, reinterpret_cast< DWORD >( Replacement_CreateSemaphoreA ), 0 },
};

static cHookDescriptor _hooksNoMovies[] = {
	{ eByName, _T( "kernel32.dll" ), _T( "OpenFile" ), 630, reinterpret_cast< DWORD >( Replacement_OpenFile ), 0 },
};

bool CheckClientVersion( MSXML::IXMLDOMDocument *pDoc );
bool PatchWindowMode( MSXML::IXMLDOMDocument *pDoc );
bool PatchNoSplash( MSXML::IXMLDOMDocument *pDoc );

HINSTANCE hAlphaBlendDLL;

/////////////////////////////////////////////////////////////////////////////
// DLL Entry Point

extern "C"
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)
{
    if (dwReason == DLL_PROCESS_ATTACH)
    {
        _Module.Init(ObjectMap, hInstance, &LIBID_DecalPlugins);
        DisableThreadLibraryCalls(hInstance);

        // Check out the project we're in - if it's client.exe, hook stuff up
        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 )
        {
			hookFunctions( _hooks, 2 );
			bRegisteredInAC = true;

		    RegKey key;
			if( key.Open( HKEY_LOCAL_MACHINE, "SOFTWARE\\Decal" ) == ERROR_SUCCESS )
			{
				// Check if dual logging is enabled
				DWORD dwReg = 0;
				if( key.QueryDWORDValue( "AllowDualLog", dwReg ) == ERROR_SUCCESS )
				{
					if( dwReg )
					{
						bDualLog = true;
						hookFunctions( _hooksDualLog, 1 );
					}
				}

				// Check if movies are disabled
				dwReg = 0;
				if( key.QueryDWORDValue( "NoMovies", dwReg ) == ERROR_SUCCESS )
				{
					if( dwReg )
					{
						bNoMovies = true;
						hookFunctions( _hooksNoMovies, 1 );
					}
				}

			}

			key.Close();

			// Get decal path
			char szPath[ MAX_PATH ];
			DWORD dwCount = MAX_PATH;
			key.Open( HKEY_LOCAL_MACHINE, "SOFTWARE\\Decal\\Agent" );
			key.QueryStringValue( "AgentPath", szPath, &dwCount );
			key.Close();

			strncat( szPath, "clientpatches.xml", MAX_PATH );

			::CoInitialize( NULL );
			MSXML::IXMLDOMDocumentPtr pPatchesDoc;
			pPatchesDoc.CreateInstance( __uuidof( MSXML::DOMDocument ) );
			pPatchesDoc->async = false;

			bool bSuccess = (pPatchesDoc->load( szPath )) ? true : false;
			if( bSuccess )
			{
				if( CheckClientVersion( pPatchesDoc ) )
				{
					RegKey key;
					if( key.Open( HKEY_LOCAL_MACHINE, "SOFTWARE\\Decal" ) == ERROR_SUCCESS )
					{
						// Windowed Mode patch
						DWORD dwReg = 0;
						if( key.QueryDWORDValue( "AllowWindowed", dwReg ) == ERROR_SUCCESS )
						{
							if( dwReg )
								bWindowed = PatchWindowMode( pPatchesDoc );
						}


						// Splash hax patches
						dwReg = 0;
						if( key.QueryDWORDValue( "NoSplash", dwReg ) == ERROR_SUCCESS )
						{
							if( dwReg )
								bNoSplash = PatchNoSplash( pPatchesDoc );
						}


					}
				}
			}
		}

		FlushInstructionCache( GetCurrentProcess( ), NULL, NULL );

        _hUpdateEnabled = ::CreateMutex( NULL, FALSE, _T( "InjectEnablePlugins" ) );

		hAlphaBlendDLL = LoadLibrary("Msimg32.dll");

		if (hAlphaBlendDLL != NULL)
		{
			AlphaBlendF = (LPFNDLLFUNCALPHABLEND)GetProcAddress(hAlphaBlendDLL,"AlphaBlend");
			if (!AlphaBlendF)
				AlphaBlendF=NULL;
		}
	}
    else if (dwReason == DLL_PROCESS_DETACH)
    {
		::CloseHandle( _hUpdateEnabled );
		::CoUninitialize();
	   
		if( hAlphaBlendDLL )
			FreeLibrary( hAlphaBlendDLL );

		_Module.Term();
    }
    return TRUE;    // ok
}

/////////////////////////////////////////////////////////////////////////////
// Used to determine whether the DLL can be unloaded by OLE

STDAPI DllCanUnloadNow(void)
{
    return (_Module.GetLockCount()==0) ? S_OK : S_FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// Returns a class factory to create an object of the requested type

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
    return _Module.GetClassObject(rclsid, riid, ppv);
}

/////////////////////////////////////////////////////////////////////////////
// DllRegisterServer - Adds entries to the system registry

STDAPI DllRegisterServer(void)
{
    // registers object, typelib and all interfaces in typelib
    return _Module.RegisterServer(TRUE);
}

/////////////////////////////////////////////////////////////////////////////
// DllUnregisterServer - Removes entries from the system registry

STDAPI DllUnregisterServer(void)
{
    return _Module.UnregisterServer(TRUE);
}

// The hook function - all it does is continue calls
LRESULT CALLBACK hookCBTProc( int nCode, WPARAM wParam, LPARAM lParam )
{
   return ::CallNextHookEx( g_hHook, nCode, wParam, lParam );
}

// Functions from inject api
void InjectEnable()
{
   // Get the image path (path of parent executable)
   TCHAR szImagePath[ MAX_PATH ];
   ::GetModuleFileName( NULL, szImagePath, MAX_PATH );

   LPTSTR strProcessName = ::_tcsrchr( szImagePath, _T( '\\' ) );
   *( strProcessName + 1 ) = _T( '\0' );

   RegKey key;
   key.Create( HKEY_LOCAL_MACHINE, _T( "SOFTWARE\\Decal\\Agent" ) );
   key.SetStringValue( _T( "AgentPath" ), szImagePath );

   // Install the global hook, injecting this DLL into every other process
   g_hHook = ::SetWindowsHookEx( WH_CBT, hookCBTProc, _Module.m_hInst, 0 );
}

void InjectDisable()
{
   ::UnhookWindowsHookEx( g_hHook );
   g_hHook = NULL;
   // Send a broadcast message, this forces the hook to trigger, and thus unload
   SendMessage(HWND_BROADCAST,WM_NULL,0,0);
}

struct cPathRegistry
{
   eInjectPath ePathType;
   LPCTSTR szKey,
      szValue;
};

LPTSTR InjectMapPath( eInjectPath pathType, LPCTSTR szFilename, LPTSTR szBuffer )
{
   static cPathRegistry _paths[] = {
      { eInjectPathDatFile, _T( "SOFTWARE\\Microsoft\\Microsoft Games\\Asheron's Call\\1.00" ), _T( "Portal Dat" ) },
      { eInjectPathAgent, _T( "SOFTWARE\\Decal\\Agent" ), _T( "AgentPath" ) } };

   // Validate the szFilename path - If it is already complete, just pass it into the buffer and return
   // FIXME? : The will also "fall through" for files on our path(es)/working directory (which may later be changed...)

   OFSTRUCT fileDetails;

   if (OpenFile(szFilename, &fileDetails, OF_EXIST) != HFILE_ERROR) {
	   ::_tcscpy(szBuffer, szFilename);
   } else {
      DWORD dwCount = MAX_PATH;
      RegKey key;
      key.Open( HKEY_LOCAL_MACHINE, _paths[ pathType ].szKey );
      key.QueryStringValue( _paths[ pathType ].szValue, szBuffer, &dwCount );
      ::_tcscat( szBuffer, _T( "\\" ) );
      ::_tcscat( szBuffer, szFilename );
   }

   return szBuffer;
}

bool CheckForHardwareMode ()
{
	RegKey key;

	if (key.Open (HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Microsoft Games\\Asheron's Call\\1.00", KEY_READ) != ERROR_SUCCESS)
	{
		return false;
	}

	DWORD dwValue = 0;

	if (key.QueryDWORDValue ("UseHardware", dwValue) != ERROR_SUCCESS)
	{
		return false;
	}

	return dwValue ? true : false;
}

HRESULT WINAPI DirectDrawCreateF( GUID *lpGuid, LPDIRECTDRAW *lplpDD, IUnknown *pUnkOut )
{
   // Add another instance of the library
   TCHAR szModule[ MAX_PATH ];
   ::GetModuleFileName( _Module.GetModuleInstance(), szModule, MAX_PATH );
   ::LoadLibrary( szModule );

   if( cManager::_p == NULL )
   {
      CComObject< cManager >::CreateInstance( &cManager::_p );
      cManager::_p->AddRef();
   }

   CComPtr< IDirectDraw > pDD;
   HRESULT hRes = ::DirectDrawCreate( lpGuid, &pDD, pUnkOut );
   if( FAILED( hRes ) )
      // Boo - failure
      return hRes;

   CComObject< CDirectDrawHook > *pDDHook;
   CComObject< CDirectDrawHook >::CreateInstance( &pDDHook );
   pDDHook->setObject( pDD );

   pDDHook->QueryInterface( IID_IDirectDraw, reinterpret_cast< void ** >( lplpDD ) );
   
   if( !CheckForHardwareMode( ) )
	   cManager::_p->enableSoftwareMode( );

   return hRes;
}

// Just call to CreateFile but supplement the share modes to allow more handles to the file
HANDLE WINAPI CreateFileF( LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile )
{
   // ::MessageBox( NULL, _T( "CreateFileF" ), _T( "Inject.dll" ), MB_OK );
   HANDLE hFile = CreateFile( lpFileName, dwDesiredAccess, dwShareMode | FILE_SHARE_READ, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile );
   DWORD dwError = GetLastError();

   return hFile;
}

HANDLE __stdcall Replacement_CreateSemaphoreA( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount, LPCTSTR lpName )
{
	if( stricmp( lpName, "Empyrean Client" ) == 0 )
	{
		HANDLE hSem = CreateSemaphore( lpSemaphoreAttributes, lInitialCount, /* lMaximumCount */ 0x7F, lpName );
		SetLastError( 0 );
		return hSem;
	}

	else
		return CreateSemaphore( lpSemaphoreAttributes, lInitialCount, lMaximumCount, lpName );
}

HFILE WINAPI Replacement_OpenFile( LPCSTR lpFileName, LPOFSTRUCT lpReOpenBuff, UINT uStyle )
{
	if( strstr( lpFileName, "\\movies\\" ) != NULL )
		return HFILE_ERROR;

	else
		return OpenFile( lpFileName, lpReOpenBuff, uStyle );
}

bool CheckClientVersion( MSXML::IXMLDOMDocument *pDoc )
{
	USES_CONVERSION;

	MSXML::IXMLDOMElementPtr pNode = pDoc->selectSingleNode( _bstr_t( "patches" ) );

	long lVerMajor = 0, lVerMinor = 0, lVerRelease = 0;

	_variant_t vVersion = pNode->getAttribute( _bstr_t( "version" ) );
	char *szVersion = OLE2A( vVersion.bstrVal );

	sscanf( szVersion, "%d.%d.%d", &lVerMajor, &lVerMinor, &lVerRelease );

	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) )
					{
						return true;
					}
				}
			}
		}
	}

	return false;
}

bool PatchWindowMode( MSXML::IXMLDOMDocument *pDoc )
{
	if( pDoc == NULL )
		return false;

	USES_CONVERSION;

	MSXML::IXMLDOMElementPtr	pNode;
	MSXML::IXMLDOMNodeListPtr	pNodes = pDoc->selectNodes( _bstr_t( "/patches/patch" ) );

	bool				bWindowPattern = false,
						bWindowReplaceOffset = false,
						bWindowReplace = false,
						bWindowMaxOffset = false;

	char				*szPatchPattern;

	unsigned char		byteReplace;

	long				lReplaceOffset,
						lMaxOffset;

	for( pNode = pNodes->nextNode(); pNode.GetInterfacePtr() != NULL; pNode = pNodes->nextNode() )
	{
		if( bWindowPattern && bWindowReplace && bWindowReplaceOffset && bWindowMaxOffset )
			break;

		_variant_t vName = pNode->getAttribute( _bstr_t( "name" ) );
		char *szName = OLE2A( vName.bstrVal );

		if( stricmp( szName, "WindowedModePattern" ) == 0 )
		{
			_variant_t vPatchPattern = pNode->getAttribute( _bstr_t( "value" ) );
			szPatchPattern = OLE2A( vPatchPattern.bstrVal );
			bWindowPattern = true;
			continue;
		}

		if( stricmp( szName, "WindowedModeReplace" ) == 0 )
		{
			_variant_t vReplace = pNode->getAttribute( _bstr_t( "value" ) );
			byteReplace = wcstoul( vReplace.bstrVal, NULL, 16 );
			bWindowReplace = true;
			continue;
		}

		if( stricmp( szName, "WindowedModeReplaceOffset" ) == 0 )
		{
			_variant_t vReplaceOffset = pNode->getAttribute( _bstr_t( "value" ) );
			lReplaceOffset = wcstoul( vReplaceOffset.bstrVal, NULL, 16 ) - 1; // Arrays are 0 indexed in c++
			bWindowReplaceOffset = true;
			continue;
		}

		if( stricmp( szName, "WindowedModeMaxOffset" ) == 0 )
		{
			_variant_t vMaxOffset = pNode->getAttribute( _bstr_t( "value" ) );
			lMaxOffset = wcstoul( vMaxOffset.bstrVal, NULL, 16 );
			bWindowMaxOffset = true;
			continue;
		}
	}

	if( !(bWindowPattern && bWindowReplace && bWindowReplaceOffset && bWindowMaxOffset) )
		return false;

	unsigned char *pAddy = reinterpret_cast< unsigned char * >( 0x400000 );

	int iLen = strlen( szPatchPattern + 1 ) / 3 + 1;
	unsigned char *szPatternArray = new unsigned char[ iLen ];
	memset( szPatternArray, 0, iLen );

	int i;
	for( i = 0; i < iLen; ++i )
		szPatternArray[ i ] = strtoul( szPatchPattern + (i*3), NULL, 16 );

	bool bAbort = true;

	for( i = 0; i < lMaxOffset; ++i, ++pAddy )
	{
		// peek at current byte
		if( *pAddy == szPatternArray[ 0 ] )
		{
			// see if entire pattern matches.
			if( memcmp( reinterpret_cast< const char * >( szPatternArray ) + 1, reinterpret_cast< char * >( pAddy ) + 1, iLen - 1 ) == 0 )
			{
				// yahoo? ...
				bAbort = false;
				pAddy += lReplaceOffset;
				break;
			}
		}
	}

	if( bAbort )
		return false;

	DWORD dwOldProtect, dwNewProtect;
	VirtualProtect( reinterpret_cast< void * >( pAddy ), 1, PAGE_READWRITE, &dwOldProtect );
	pAddy[ 0 ] = byteReplace;
	VirtualProtect( reinterpret_cast< void * >( pAddy ), 1, dwOldProtect, &dwNewProtect );

	return true;
}

bool PatchNoSplash( MSXML::IXMLDOMDocument *pDoc )
{
	if( pDoc == NULL )
		return false;

	USES_CONVERSION;

	MSXML::IXMLDOMElementPtr	pNode;
	MSXML::IXMLDOMNodeListPtr	pNodes = pDoc->selectNodes( _bstr_t( "/patches/patch" ) );

	bool			bNoSplashPattern1 = false,
					bNoSplashPattern2 = false,
					bNoSplashReplaceOffset1 = false,
					bNoSplashReplaceOffset2 = false,
					bNoSplashReplace1 = false,
					bNoSplashReplace2 = false,
					bNoSplashMaxOffset1 = false,
					bNoSplashMaxOffset2 = false;

	char			*szPatchPattern1,
					*szPatchPattern2;

	unsigned char	byteReplace1,
					byteReplace2;

	long			lReplaceOffset1,
					lReplaceOffset2,
					lMaxOffset1,
					lMaxOffset2;

	for( pNode = pNodes->nextNode(); pNode.GetInterfacePtr() != NULL; pNode = pNodes->nextNode() )
	{
		if( bNoSplashPattern1 && bNoSplashPattern2 && bNoSplashReplaceOffset1 && bNoSplashReplaceOffset2 && bNoSplashReplace1 && bNoSplashReplace2 && bNoSplashMaxOffset1 && bNoSplashMaxOffset2 )
			break;

		_variant_t vName = pNode->getAttribute( _bstr_t( "name" ) );
		char *szName = OLE2A( vName.bstrVal );

		if( stricmp( szName, "SplashHax1Pattern" ) == 0 )
		{
			_variant_t vPatchPattern = pNode->getAttribute( _bstr_t( "value" ) );
			szPatchPattern1 = OLE2A( vPatchPattern.bstrVal );
			bNoSplashPattern1 = true;
			continue;
		}

		if( stricmp( szName, "SplashHax2Pattern" ) == 0 )
		{
			_variant_t vPatchPattern = pNode->getAttribute( _bstr_t( "value" ) );
			szPatchPattern2 = OLE2A( vPatchPattern.bstrVal );
			bNoSplashPattern2 = true;
			continue;
		}

		if( stricmp( szName, "SplashHax1Replace" ) == 0 )
		{
			_variant_t vReplace = pNode->getAttribute( _bstr_t( "value" ) );
			byteReplace1 = wcstoul( vReplace.bstrVal, NULL, 16 );
			bNoSplashReplace1 = true;
			continue;
		}

		if( stricmp( szName, "SplashHax2Replace" ) == 0 )
		{
			_variant_t vReplace = pNode->getAttribute( _bstr_t( "value" ) );
			byteReplace2 = wcstoul( vReplace.bstrVal, NULL, 16 );
			bNoSplashReplace2 = true;
			continue;
		}

		if( stricmp( szName, "SplashHax1ReplaceOffset" ) == 0 )
		{
			_variant_t vReplaceOffset = pNode->getAttribute( _bstr_t( "value" ) );
			lReplaceOffset1 = wcstoul( vReplaceOffset.bstrVal, NULL, 16 ) - 1; // Arrays are 0 indexed in c++
			bNoSplashReplaceOffset1 = true;
			continue;
		}

		if( stricmp( szName, "SplashHax2ReplaceOffset" ) == 0 )
		{
			_variant_t vReplaceOffset = pNode->getAttribute( _bstr_t( "value" ) );
			lReplaceOffset2 = wcstoul( vReplaceOffset.bstrVal, NULL, 16 ) - 1; // Arrays are 0 indexed in c++
			bNoSplashReplaceOffset2 = true;
			continue;
		}

		if( stricmp( szName, "SplashHax1MaxOffset" ) == 0 )
		{
			_variant_t vMaxOffset = pNode->getAttribute( _bstr_t( "value" ) );
			lMaxOffset1 = wcstoul( vMaxOffset.bstrVal, NULL, 16 );
			bNoSplashMaxOffset1 = true;
			continue;
		}

		if( stricmp( szName, "SplashHax2MaxOffset" ) == 0 )
		{
			_variant_t vMaxOffset = pNode->getAttribute( _bstr_t( "value" ) );
			lMaxOffset2 = wcstoul( vMaxOffset.bstrVal, NULL, 16 );
			bNoSplashMaxOffset2 = true;
			continue;
		}
	}

	if( !(bNoSplashPattern1 && bNoSplashPattern2 && bNoSplashReplaceOffset1 && bNoSplashReplaceOffset2 && bNoSplashReplace1 && bNoSplashReplace2 && bNoSplashMaxOffset1 && bNoSplashMaxOffset2) )
		return false;

	unsigned char *pAddy = reinterpret_cast< unsigned char * >( 0x400000 );

	int iLen = strlen( szPatchPattern1 + 1 ) / 3 + 1;
	unsigned char *szPatternArray1 = new unsigned char[ iLen ];
	memset( szPatternArray1, 0, iLen );

	int i;
	for( i = 0; i < iLen; ++i )
		szPatternArray1[ i ] = strtoul( szPatchPattern1 + (i*3), NULL, 16 );

	bool bAbort1 = true;

	for( i = 0; i < lMaxOffset1; ++i, ++pAddy )
	{
		// peek at current byte
		if( *pAddy == szPatternArray1[ 0 ] )
		{
			// see if entire pattern matches.
			if( memcmp( reinterpret_cast< const char * >( szPatternArray1 ) + 1, reinterpret_cast< char * >( pAddy ) + 1, iLen - 1 ) == 0 )
			{
				// yahoo? ...
				bAbort1 = false;
				pAddy += lReplaceOffset1;
				break;
			}
		}
	}

	if( bAbort1 )
		return false;

	DWORD dwOldProtect, dwNewProtect;
	VirtualProtect( reinterpret_cast< void * >( pAddy ), 1, PAGE_READWRITE, &dwOldProtect );
	pAddy[ 0 ] = byteReplace1;
	VirtualProtect( reinterpret_cast< void * >( pAddy ), 1, dwOldProtect, &dwNewProtect );


	pAddy = reinterpret_cast< unsigned char * >( 0x400000 );

	iLen = strlen( szPatchPattern2 + 1 ) / 3 + 1;
	unsigned char *szPatternArray2 = new unsigned char[ iLen ];
	memset( szPatternArray2, 0, iLen );

	for( i = 0; i < iLen; ++i )
		szPatternArray2[ i ] = strtoul( szPatchPattern2 + (i*3), NULL, 16 );

	bool bAbort2 = true;

	for( i = 0; i < lMaxOffset2; ++i, ++pAddy )
	{
		// peek at current byte
		if( *pAddy == szPatternArray2[ 0 ] )
		{
			// see if entire pattern matches.
			if( memcmp( reinterpret_cast< const char * >( szPatternArray2 ) + 1, reinterpret_cast< char * >( pAddy ) + 1, iLen - 1 ) == 0 )
			{
				// yahoo? ...
				bAbort2 = false;
				pAddy += lReplaceOffset2;
				break;
			}
		}
	}

	if( bAbort2 )
		return false;

	VirtualProtect( reinterpret_cast< void * >( pAddy ), 1, PAGE_READWRITE, &dwOldProtect );
	pAddy[ 0 ] = byteReplace2;
	VirtualProtect( reinterpret_cast< void * >( pAddy ), 1, dwOldProtect, &dwNewProtect );

	return true;
}

PIMAGE_IMPORT_DESCRIPTOR getNamedImportDescriptor( HMODULE hModule, LPCSTR szImportMod )
{
    PIMAGE_DOS_HEADER pDOSHeader = reinterpret_cast< PIMAGE_DOS_HEADER >( hModule );

    // Get the PE header.
    PIMAGE_NT_HEADERS pNTHeader = MakePtr( PIMAGE_NT_HEADERS, pDOSHeader, pDOSHeader->e_lfanew );

    // If there is no imports section, leave now.
    if( pNTHeader->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_IMPORT ].VirtualAddress == NULL )
        return NULL;

    // Get the pointer to the imports section.
    PIMAGE_IMPORT_DESCRIPTOR pImportDesc = MakePtr ( PIMAGE_IMPORT_DESCRIPTOR, pDOSHeader, 
       pNTHeader->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_IMPORT ].VirtualAddress );

    // Loop through the import module descriptors looking for the
    //  module whose name matches szImportMod.
    while( pImportDesc->Name != NULL )
    {
        PSTR szCurrMod = MakePtr( PSTR, pDOSHeader, pImportDesc->Name );
        if( stricmp( szCurrMod, szImportMod ) == 0 )
            // Found it.
            break;

        // Look at the next one.
        pImportDesc ++;
    }

    // If the name is NULL, then the module is not imported.
    if ( pImportDesc->Name == NULL )
        return ( NULL ) ;

    // All OK, Jumpmaster!
    return pImportDesc;

}

bool hookFunctions( cHookDescriptor *pHook, DWORD nCount )
{
   HMODULE hProcess = ::GetModuleHandle( NULL );
   for( cHookDescriptor *i = pHook; i != pHook + nCount; ++ i )
   {
      // Get the specific import descriptor.
      PIMAGE_IMPORT_DESCRIPTOR pImportDesc = getNamedImportDescriptor( hProcess, i->m_szModule );

      if ( pImportDesc == NULL )
         continue;

      // Get the original thunk information for this DLL.  I cannot use
      //  the thunk information stored in the pImportDesc->FirstThunk
      //  because the that is the array that the loader has already
      //  bashed to fix up all the imports.  This pointer gives us acess
      //  to the function names.
      PIMAGE_THUNK_DATA pOrigThunk = MakePtr( PIMAGE_THUNK_DATA, hProcess, pImportDesc->OriginalFirstThunk );

      // Get the array pointed to by the pImportDesc->FirstThunk.  This is
      //  where I will do the actual bash.
      PIMAGE_THUNK_DATA pRealThunk = MakePtr( PIMAGE_THUNK_DATA, hProcess, pImportDesc->FirstThunk );

      // Loop through and look for the one that matches the name.
      for( ; pOrigThunk->u1.Function != NULL; ++ pOrigThunk, ++ pRealThunk )
      {
         if( i->m_addr == eByName )
         {
            if( pOrigThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG )
               // Only look at those that are imported by name, not ordinal.
               continue;

            // Look get the name of this imported function.
            PIMAGE_IMPORT_BY_NAME pByName = MakePtr( PIMAGE_IMPORT_BY_NAME, hProcess, pOrigThunk->u1.AddressOfData );

            // If the name starts with NULL, then just skip out now.
            if( pByName->Name[ 0 ] == '\0' )
               continue;

            if ( ::_tcsicmp( reinterpret_cast< char * >( pByName->Name ), i->m_szFunction ) != 0 )
               // This name dosen't match
               continue;
         }
         else
         {
            if( !( pOrigThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG ) )
               // The import must be by ordinal
               continue;

            if( ( pOrigThunk->u1.Ordinal & ~IMAGE_ORDINAL_FLAG ) != i->m_dwOrdinal )
               // Ordinal does not match
               continue;
         }

         // I found it.  Now I need to change the protection to
         //  writable before I do the blast.  Note that I am now
         //  blasting into the real thunk area!
         MEMORY_BASIC_INFORMATION mbi_thunk;

         ::VirtualQuery( pRealThunk, &mbi_thunk, sizeof ( MEMORY_BASIC_INFORMATION ) );
         ::VirtualProtect( mbi_thunk.BaseAddress, mbi_thunk.RegionSize, PAGE_READWRITE, &mbi_thunk.Protect );

         // Save the original address if requested.
         i->m_pOldFunction = pRealThunk->u1.Function;
         pRealThunk->u1.Function = i->m_pNewFunction;

         DWORD dwOldProtect;

         ::VirtualProtect( mbi_thunk.BaseAddress, mbi_thunk.RegionSize, mbi_thunk.Protect, &dwOldProtect );

         break;
      }
   }

   return true;
}

void DisplayErrorMessage( DWORD dwError, char* szFrom )
{
	char szBuffer[1024];
	char *lpMsgBuf;

	FormatMessage(	FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError, 
				MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), reinterpret_cast< LPTSTR > ( &lpMsgBuf ), 0, NULL );
	
	wsprintf( szBuffer, "%s %ld: %s", szFrom, dwError, lpMsgBuf );

	MessageBox( 0, szBuffer, "Error", MB_OK | MB_ICONERROR );
	LocalFree( lpMsgBuf );
}

// Checksum - Added for my test container
void Container_Initialize( HWND hWnd, IDirectDraw4* pDD4, IDirectDrawSurface4 *pDDS4 )
{
	if( cManager::_p )
		return;

	HRESULT hRes = CComObject< cManager >::CreateInstance( &cManager::_p );
	if( FAILED( hRes ) )
	{
		DisplayErrorMessage( hRes, "Creating cManager Object" );
		return;
	}

	cManager::_p->AddRef( );

	cManager::_p->setWindow( hWnd );
	cManager::_p->m_pD = pDD4;
	cManager::_p->m_pPrimarySurface = pDDS4;
	cManager::_p->m_pDecal->InitGraphics( pDD4, NULL );
	cManager::_p->m_bContainer = true;
	cManager::_p->m_bXMLViewViewer = false;
}

void Container_StartPlugins( )
{
	if( !cManager::_p )
		return;

	cManager::_p->m_pDecal->StartPlugins( );
}

void Container_StopPlugins( )
{
	if( !cManager::_p )
		return;

	cManager::_p->m_pDecal->StopPlugins( );
}

void Container_Terminate( )
{
	if( !cManager::_p )
		return;
	
	Container_StopPlugins( );

	cManager::_p->Release( );
	cManager::_p = NULL;
}

void Container_SetSurface( IDirectDrawSurface4 *pDDS4 )
{
	if( !cManager::_p )
		return;

	cManager::_p->m_pPrimarySurface = pDDS4;
}

void Container_Draw( )
{
	if( !cManager::_p )
		return;

	if( cManager::_p->m_bInitialized )
		cManager::_p->m_pRootLayer->m_pBars->Reformat( );

	cManager::_p->draw2D( );
}

std::set< IView* > g_pViews;

void XMLViewer_Initialize( HWND hWnd, IDirectDraw4* pDD4, IDirectDrawSurface4 *pDDS4 )
{
	if( cManager::_p )
		return;

	HRESULT hRes = CComObject< cManager >::CreateInstance( &cManager::_p );
	if( FAILED( hRes ) )
	{
		DisplayErrorMessage( hRes, "Creating cManager Object" );
		return;
	}
	cManager::_p->AddRef( );

	cManager::_p->setWindow( hWnd );
	cManager::_p->m_pD = pDD4;
	cManager::_p->m_pPrimarySurface = pDDS4;
	cManager::_p->m_pDecal->InitGraphics( pDD4, NULL );
	cManager::_p->m_bContainer = true;
	cManager::_p->m_bXMLViewViewer = true;
	cManager::_p->init( );
}

void XMLViewer_LoadView( BSTR bstrSchema )
{
	if( !cManager::_p )
		return;

	static _bstr_t strSchema( 
		_T( "<?xml version=\"1.0\"?>" )

		_T( "<view icon=\"4150\" title=\"TestView\" width=\"550\" height=\"550\">" )						

		_T( "<control name=\"NoteBookTab\" progid=\"DecalControls.Notebook\" left=\"5\">"  )

		_T( "<page label=\"Test List\">"  )
		_T( "<control left=\"0\" top=\"0\" width=\"180\" height=\"206\" progid=\"DecalControls.List\" name=\"sux\">" )
		_T( "<column progid=\"DecalControls.IconColumn\"/>" )
		_T( "<column progid=\"DecalControls.IconColumn\"/>" )
		_T( "<column progid=\"DecalControls.TextColumn\" fixedwidth=\"80\"/>" )
		_T( "<column progid=\"DecalControls.TextColumn\" fixedwidth=\"50\"/>" )
		_T( "</control>" )
		_T( "</page>" )

		_T("<page label=\"whee!\">")
		_T( "<control progid=\"DecalControls.FixedLayout\" width=\"550\">"  )
		_T( "</control>" )
		_T("</page>")

		_T( "</control>" )
		_T( "</view>" )
		);

	IView* pView;
	cManager::_p->LoadView( bstrSchema ? bstrSchema : strSchema, &pView );
	g_pViews.insert( pView );
}

void XMLViewer_RemoveView( IView* pView  )
{
	try{
		if( !pView )
			return;

		g_pViews.erase( pView );

		pView->Release( );
		pView = NULL;
	}
	catch( ... )
	{
		pView = NULL;
	}
}

void XMLViewer_Terminate( )
{
	if( !cManager::_p )
		return;

	if( cManager::_p->m_bInitialized )
	{
		for( std::set< IView* >::iterator it = g_pViews.begin( ); it != g_pViews.end( ); ++it )
			XMLViewer_RemoveView( *it );
			
		g_pViews.clear( );

		cManager::_p->term( );
	}

	cManager::_p->Release( );
	cManager::_p = NULL;
}

void XMLViewer_Draw( )
{
	Container_Draw( );
}